//
// Copyright (C) 1991 Texas Instruments Incorporated.
//
// Permission is granted to any individual or institution to use, copy, modify,
// and distribute this software, provided that this complete copyright and
// permission notice is maintained, intact, in all copies and supporting
// documentation.
//
// Texas Instruments Incorporated provides this software "as is" without
// express or implied warranty.
//
// Created: MBN 03/17/89 -- Initial design and implementation
// Updated: MBN 04/06/89 -- Performance enhancement by changing several
//                          methods and friends to inline, and addition
//                          of two private friend functions.
// Updated: LGO 06/03/89 -- Fix string growth algorithm.
// Updated: MBN 06/06/89 -- Performance enhancement by making reference count
//                          and memory check/adjustment inline, addition of a
//                          resize method and a growth ratio data slot, and
//                          change of constructors to set specific object size,
//                          not class as a whole
// Updated: DKM 06/23/89 -- To get around a bug in the Glockenspiel translator
//                          we had to explicitly typecast arguments for calls
//                          to the overloaded unix string functions, including
//                          strchr, strrchr, strcpy, strcmp, strncmp, strtod,
//                          and strtol.
// Updated: MNF 06/27/89 -- Added the insert, remove, replace, yank, and
//                          sub_string public member functions.
// Updated: MBN 06/30/89 -- Updated to include support for regular expressions
// Updated: DKM 07/07/89 -- To work around Xenix 31 char limit:
//                          Shortened is_less_than        to is_lt
//                                    is_greater_than     to is_gt
//                                    is_less_or_equal    to is_le
//                                    is_greater_or_equal to is_ge
//                          Removed is_equal_or_less and is_greater_or_less
// Updated: MBN 09/07/89 -- Added conditional exception handling
// Updated: MBN 09/26/89 -- Fixed bug to set size when growth size ratio given
// Updated: LGO 10/11/89 -- Removed operator>>
// Updated: LGO 10/28/89 -- Removed is_lt, is_gt, is_le, is_ge, is_equal
//                          and is_not_equal (use char* functions instead)
// Updated: MBN 12/15/89 -- Sprinkled "const" qualifier all over the place!
// Updated: MBN 01/02/90 -- Made find search for the first/next regexp match 
// Updated: LGO 01/03/90 -- Avoid use of strncat
// Updated: LGO 01/05/90 -- Partial re-write of most everything to simplify
//                          and speed-up the code
// Updated: MJF 03/12/90 -- Added group names to RAISE
// Updated: MJF 05/21/90 -- Moved delete from grow_memory to validate
//
// This  file contains member and  friend function  implementation code for the
// Gen_String class defined  in  the  Gen_String.h and Regexp.h header   files.
// Where appropriate and  possible, interfaces to, and  us  of, existing system
// functions   has been incorporated.  An overview    of the structure   of the
// Edit_String class, along with a synopsis of each member and friend function,
// can be found in the String.h header file.
//

#ifndef GEN_STRINGH				// If class not defined
#include <cool/Gen_String.h>			// Include class specification
#endif

#if defined(DOS)
extern "C" {
#include <ctype.h>		// Include character processing macros
}
#else
#include <ctype.h>		// Include character processing macros
#endif

// update_ref_count -- Gives "this" it's own (unshared) copy of a string
// Input:          None
// Output:	   None

void Gen_String::update_ref_count() {
  this->p->ref_count--;		     // Decrement reference count
  this->p = new String_Layout;	     // Allocate layout structure
  this->p->ref_count = 1;	     // One object at the moment
  this->p->size = 0;		     // No memory allocated yet
  this->p->start = this->str;	     // Pointer to start of string
}


// validate -- protected method called when a string is modified
// Input:        before calling, set this->length to new length.
//               dont_copy - set to TRUE If you don't want the old string
//               copied on a grow, or ref-count copy
// Output:       None
void Gen_String::validate(Boolean dont_copy) {
  if (this->p->ref_count > 1) {	  // If sharing memory with another Gen_String
    this->update_ref_count();	  // decrement reference count and 
    this->grow_memory(dont_copy); // allocate memory for own copy
  }
  else if (this->length >= this->p->size) { // If not long enough,
    char* ostr = this->str;		    // save old string to delete later
    this->grow_memory(dont_copy);	    // allocate memory,
    if (ostr != NULL) delete ostr;	    // and free old memory block
  }
}

// grow_memory -- Called when string isn't long enough, or to allocate memory
//                after update_ref_count inside validate.
// Input:         dont_copy - TRUE If you don't want the old string copied.
// Output:	  None

void Gen_String::grow_memory (Boolean dont_copy) {
  char* old_chars = this->str;
  long size = this->p->size;
  if (this->length >= size) { // Called from validate just to allocate memory
    if (this->growth_ratio != 0.0)     // If a growth ratio is set
      size += long(size * this->growth_ratio);
    else
      size += this->alloc_size_s;    // Increment by allocation size
    if (size < this->length)	     // But if still not big enough
      size = this->length+1;	     // Grow to big enough size
    this->p->size = size;
  }
  this->str = new char[size];	     // Allocate memory for string
  this->p->start = this->str;	     // Pointer to start of string
  if (old_chars != NULL) {
    if (!dont_copy)
      strcpy ((char *)this->str, old_chars);   // Copy initial string
  }
  else
    *this->str = END_OF_STRING;
}


// Gen_String() -- Simple constructor for a Gen_String object. Memory is
//                 allocated for the layout structure and char* pointers are
//                 set to the "alloc_size_s" value.
// Input:          None
// Output:         Gen_String reference


Gen_String::Gen_String () {		
  if (this->alloc_size_s == 0)		// If allocation size is uninitialized
    this->alloc_size_s = MEM_BLK_SZ;	// Set memory block allocation size
  this->growth_ratio = 0.0;		// Initialize growth ratio
  this->str = new char[alloc_size_s];	// Allocate block of memory
  this->length = 0;			// Zero characters in string
  this->p = new String_Layout;		// Allocate layout structure
  this->p->ref_count = 1;		// One String object at the moment
  this->p->size = alloc_size_s;		// Size of allocated block 
  this->p->start = str;			// Pointer to start of string 
  *this->str = END_OF_STRING;		// END_OF_STRING terminate for safety
  this->rgexp = NULL;			// No regular expression yet
}


// Gen_String(char) -- Constructor to initialize object with a char
// Input:              character
// Output:             Gen_String reference to new object

Gen_String::Gen_String (char c) {		
  if (this->alloc_size_s == 0)		// If allocation size is uninitialized
    this->alloc_size_s = MEM_BLK_SZ;	// Set memory block allocation size
  this->growth_ratio = 0.0;		// Initialize growth ratio
  this->length = 1;			// Length of character is 1
  this->str = new char[length+1];	// Allocate memory for string
  this->p = new String_Layout;		// Allocate layout structure
  this->p->ref_count = 1;		// One String object at the moment
  this->p->size = this->length+1;	// Size of allocated block
  this->p->start = this->str;		// Pointer to start of string
  *this->str = c;			// Assign character
  *(this->str+1) = END_OF_STRING;	// END_OF_STRING terminator for string
  this->rgexp = NULL;			// No regular expression yet
}


// Gen_String(char*) -- Constructor to initialize object with a char*.
// Input:               char* pointer
// Output:              Gen_String reference to new object

Gen_String::Gen_String (CONST char* c) {		
  if (this->alloc_size_s == 0)	        // If allocation size is uninitialized
    this->alloc_size_s = MEM_BLK_SZ;	// Set memory block allocation size
  this->growth_ratio = 0.0;		// Initialize growth ratio
  this->length = strlen (c);		// Determine length of character string
  this->str = new char[length+1];	// Allocate memory for string
  this->p = new String_Layout;		// Allocate layout structure
  this->p->ref_count = 1;		// One String object at the moment
  this->p->size = this->length+1;	// Size of allocated block
  this->p->start = this->str;		// Pointer to start of string
  strcpy ((char *)this->str, c);	// Copy characters
  this->rgexp = NULL;			// No regular expression yet
}


// Gen_String(Gen_String&) -- Constructor to initialize object to Gen_String
//                            Note that this involves use of delayed copy via 
//                            the reference count mechanism.
// Input:                     Gen_String reference
// Output:                    Gen_String reference to new object

Gen_String::Gen_String (CONST Gen_String& s) {
  if (this->alloc_size_s == 0)		// If allocation size is uninitialized
    this->alloc_size_s = MEM_BLK_SZ;	// Set memory block allocation size
  this->growth_ratio = s.growth_ratio;	// Initialize growth ratio
  this->length = s.length;		// Determine length of character string
  this->str = s.str;			// Point to same string memory block
  this->p = s.p;			// Point to same layout structure
  this->p->ref_count++;			// Increment reference count
  this->rgexp = NULL;			// No regular expression yet
}


// Gen_String(char*, size) -- Constructor to initialize object with a char*
//                            and control initial allocation size
// Input:                     char* pointer and size value
// Output:                    Gen_String reference to new object

Gen_String::Gen_String (CONST char* c, long sz) {
  if (this->alloc_size_s == 0)		// If allocation size is uninitialized
    this->alloc_size_s = MEM_BLK_SZ;	// Set memory block allocation size
  this->length = strlen (c);		// Determine length of character string
  this->p = new String_Layout;		// Allocate layout structure
  if (this->length > sz) {		// If initial string is larger
    this->str = new char[length+1];	// Allocate memory for string
    this->p->size = length+1;		// Size of allocated block
  }
  else {
    this->str = new char[sz+1];		// Allocate memory for string 
    this->p->size = sz+1;		// Size of allocated block
  }
  this->p->ref_count = 1;		// One String object at the moment
  this->p->start = this->str;		// Pointer to start of string
  strcpy ((char *)this->str, c);	// Copy characters
  this->rgexp = NULL;			// No regular expression yet
}


// Gen_String(Gen_String&, long) -- Constructor to initialize object to a
//                                  Gen_String and control initial alloc size
// Input:                           Gen_String reference and size value
// Output:                          Gen_String reference to new object

Gen_String::Gen_String (CONST Gen_String& s, long sz) { 
  if (this->alloc_size_s == 0)		// If allocation size is uninitialized
    this->alloc_size_s = MEM_BLK_SZ;	// Set memory block allocation size
  this->length = s.length;		// Determine length of character string
  this->p = new String_Layout;		// Allocate layout structure
  if (this->length > sz) {		// If initial string is larger
    this->str = new char[length+1];	// Allocate memory for string
    this->p->size = length+1;		// Size of allocated block
  }
  else {
    this->str = new char[sz+1];		// Allocate memory for string 
    this->p->size = sz+1;		// Size of allocated block
  }
  this->p->ref_count = 1;		// One String object at the moment
  this->p->start = this->str;		// Pointer to start of string
  strcpy ((char *)this->str, s.str);	// Copy characters over
  this->rgexp = NULL;			// No regular expression yet
}


// ~Gen_String() -- Gen_String object destructor frees up allocated memory and
//                  decrements reference count if necessary
// Input:           Gen_String object
// Output:          None

Gen_String::~Gen_String () {			
  if (this == NULL || this->p == NULL)
    cerr << form("Gen_String 0x%d deleted twice!\n", this);
   else {
    if (this->p->ref_count == 1) {      // If not sharing mem with other String
      delete this->str;			// then delete String object
      delete this->p;			// Delete layout structure
      this->str = NULL;
      this->p = NULL;
    }
    else				// Else decrement reference counter
      this->p->ref_count--;
    delete this->rgexp;			// Delete regular expression object
  }
}


// clear -- Flush the character string from the string object by setting
//          the char* pointer to NULL and the length to zero.
// Input:   this*
// Output:  None

void Gen_String::clear() {
  this->length = 0;			// Set new string length
  if (this->rgexp)			// If there is a Regexp object
    this->rgexp->set_invalid();		// Invalidate compiled expression
  if (this->p->ref_count == 1)		// If not sharing mem with other String
    *(this->str) = NULL;		// Clear this String by using NULL
  else {
    this->str = NULL;			// Invalidate pointer
    this->validate();			// Check for sharing memory and reset
  }
}


// strcpy -- String copy of a single character to an Gen_String object
// Input:    Gen_String reference and a character
// Output:   Gen_String object containing a character string

Gen_String& strcpy (Gen_String& s, char c) {
  s.length = 1;				// Character string length
  s.validate(TRUE);			// Ensure enough memory and not shared
  *(s.str) = c;				// Assign character
  *(s.str+1) = END_OF_STRING;		// END_OF_STRING terminator for string
  return s;				// Return Gen_String reference
}  


// strcpy -- String copy of a char* to a String object
// Input:    Gen_String reference and a char* 
// Output:   String object containing a character string

Gen_String& strcpy (Gen_String& s, const char *c) {
  s.length = strlen (c);		// Determine length of character string
  s.validate(TRUE);			// Ensure enough memory and not shared
  strcpy(s.str, c);			// Copy into string
  return s;				// Return Gen_String reference
}


// strcpy -- String copy of one String object to another. Note that the
//           copy is delayed via the reference count mechanism until it
//           is really needed.
// Input:    Two Gen_String references
// Output:   Gen_String object pointing to another Gen_String object

Gen_String& strcpy (Gen_String& s1, const Gen_String& s2) {
  if (s1.p->ref_count > 1)		// If sharing memory with other string
    s1.p->ref_count--;			// Decrement that reference count
  else {				// Else must be some memory to free
    delete s1.p;			// Free layout structure memory
    if (s1.str != END_OF_STRING)        // If pointing to a memory block
      delete s1.str;			// Free that up too
  }
  s1.length = s2.length;		// Determine length of character string
  s1.str = s2.str;			// Point to same string memory block
  s1.p = s2.p;				// Point to same layout structure
  s1.p->ref_count++;			// Increment reference count
  return s1;				// Return Gen_String reference
}


// operator+ -- String concatenation of a character: x = x + 'A';
// Input:       Character
// Output:      Gen_String object concatenated with character

Gen_String Gen_String::operator+ (char c) {
  this->length += 1;			// Determine length of new string
  this->validate();			// Ensure enough memory and not shared
  this->str[length-1] = c;		// Append new character
  this->str[length] = END_OF_STRING;	// END_OF_STRING terminator
  return *this;				// Return String
}


// strcat -- Concatenate a single character to a Gen_String object
// Input:    Gen_String reference and a character
// Output:   Gen_String object concatenated with character 

Gen_String& strcat (Gen_String& s, char c) {
  s.length += 1;			// Determine length of new string
  s.validate();				// Ensure enough memory and not shared
  s.str[s.length-1] = c;		// Append new character
  s.str[s.length] = END_OF_STRING;	// END_OF_STRING terminator
  return s;				// Return String
}


// operator+ -- String concatenation of a character string: x = x + "ABCDEFG";
// Input:       Character string
// Output:      Gen_String object concatenated with character string

Gen_String Gen_String::operator+ (const char* c) {
  long old_len = this->length;		// Save length for efficiency hack
  this->length = old_len + strlen (c);	// Determine length of new string
  this->validate();			// Ensure enough memory and not shared
  strcpy (this->str+old_len, c);	// Copy remaining characters 
  return *this;				// Return String
}


// strcat -- Concatenate a String and a char*
// Input:    Gen_String reference and a char*
// Output    Gen_String object concatentated with character string

Gen_String& strcat (Gen_String& s, const char* c) {	
  long old_len = s.length;		// Save length for efficiency hack
  s.length = old_len + strlen (c);	// Determine length of new string
  s.validate();				// Ensure enough memory and not shared
  strcpy (s.str+old_len, c);		// Concatenate characters   
  return s;				// Return String
}


// strncat -- Concatentate a String with "n" characters from char*
// Input:     Gen_String reference, char*, number of characters
// Output:    Gen_String object concatenedate with "n" characters

Gen_String& strncat (Gen_String& s, const char* c, int n) {
#if ERROR_CHECKING
  if (n < 0)				// If invalid length
    RAISE (Error, SYM(Gen_String), SYM(Negative_Length),
	   "Gen_String::strncat(): Negative length %d", n);
#endif
  long old_len = s.length;		// Save length for efficiency hack
  s.length = old_len + n;		// Determine length of new string
  s.validate();				// Ensure enough memory and not shared
  strncpy (s.str+old_len, c, n);	// Concatenate characters   
  return s;				// Return String
}


// operator+ -- String concatenation of another string: x = x + y;
// Input:       Gen_String reference
// Output:      Gen_String object concatenated with String contents

Gen_String Gen_String::operator+ (const Gen_String& s) {
  long old_len = this->length;		// Save length for efficiency hack
  this->length = old_len + s.length;	// Determine length of new string
  this->validate();			// Ensure enough memory and not shared
  strcpy (this->str+old_len, s.str);	// Copy remaining characters 
  return *this;				// Return String
}


// strcat -- Concatenate two Gen_String objects
// Input:    Two Gen_String references
// Output:   Gen_String object concatenated with Gen_String object

Gen_String& strcat (Gen_String& s1, const Gen_String& s2) {
  long old_len = s1.length;		// Save length for efficiency hack
  s1.length = old_len + s2.length;	// Determine length of new string
  s1.validate();			// Ensure enough memory and not shared
  strcpy (s1.str+old_len, s2.str);	// Copy remaining characters 
  return s1;				// Return String
}


// strncat -- Concatentate a String with "n" characters from a String
// Input:     Two Gen_String references and number of characters
// Output:    Gen_String object concatenedate with "n" characters

Gen_String& strncat (Gen_String& s1, const Gen_String& s2, int n) {
#if ERROR_CHECKING
  if (n < 0)				// If invalid length
    RAISE (Error, SYM(Gen_String), SYM(Negative_Length),
	   "Gen_String::strncat(): Negative length %d", n);
#endif
  long old_len = s1.length;		// Save length for efficiency hack
  s1.length = old_len + n;		// Determine length of new string
  s1.validate();			// Ensure enough memory and not shared
  strncpy (s1.str+old_len, s2.str, n);	// Copy remaining characters 
  return s1;				// Return String
}


// compile -- Compile a regular expression ready for pattern matching. If no
//            regexp object in string, create one
// Input:     Regular expression to compile
// Output:    None

void Gen_String::compile (char* s) {
  if (this->rgexp == NULL) 			// If no regexp object 
    this->rgexp = new Regexp;			// Allocate storage for regexp
  this->rgexp->compile (s);			// Compile regular expression
  this->rgexp_index = 0;			// Reset search index position
}


// find -- Search the string for the previously compiled regular expression
// Input:  None
// Output: Boolean TRUE/FALSE

Boolean Gen_String::find () {
#if ERROR_CHECKING
  if (this->rgexp == NULL || !this->rgexp->is_valid()) // Valid regexp?
    this->find_error ();		        // Raise exception
#endif
  this->rgexp_index += this->rgexp->end ();	// Use index from last search
  if (this->rgexp_index >= this->length)	// If at end of search
    return FALSE;				// Indicate failure
  Boolean result = this->rgexp->find (this->str + this->rgexp_index);
  return result;				// Return match result
}


// reverse -- Reverse the order of the characters in Gen_String object
// Input:     Gen_String object
// Output:    Gen_String object with character order reverse

void Gen_String::reverse () {
  char c;
  for (long i = 0, j = this->length-1;		// Counting from front and rear
       i < this->length / 2; i++, j--) {	// until we reach the middle
    c = this->str[i];				// Save front character
    this->str[i] = this->str[j];		// Switch with rear character
    this->str[j] = c;				// Copy new rear character
  }
}


// resize -- Adjust the memory size of a string to accomodate some size
// Input:    Gen_String object
// Output:   None

void Gen_String::resize (long sz) {
#if ERROR_CHECKING
  if (sz < 0)				// If invalid resize
    RAISE (Error, SYM(Gen_String), SYM(Negative_Size),
	   "Gen_String::resize(): Negative resize %d", sz);
#endif
  int shared_memory = FALSE;		// If TRUE, memory shared
  char* temp = this->str;		// Save pointer to existing string
  if (this->p->ref_count > 1) {	        // If sharing memory with other String
    this->update_ref_count();		// Adjust and update
    shared_memory = TRUE;		// Set flag for later use
  }
  this->str = new char[sz+1];		// Allocate memory for desired size
  this->p->start = str;			// Pointer to start of string
  this->p->size = sz+1;			// Save size of allocated memory
  if (temp != NULL)			// If original string had data
    strcpy ((char *)this->str, temp);	// Copy original string back
  if (shared_memory == FALSE)		// If did not share memory
    delete temp;			// Deallocate memory
}


// operator<< -- Overload output operator for Gen_String objects
// Input:        Gen_String object
// Output:       Formatted output and stream descriptor

ostream& operator<< (ostream& os, const Gen_String& s) {
  return os << s.str;				// Output char* and newline
}


// strtol -- Returns as a long integer the value represented by the
//           string pointer to by s, scanning upto the first character
//           that is inconsistent with the base. Leading white space is
//           ignored.
// Input:    Reference to Gen_String object
//           Radix of number
// Output:   Long integer representing value contained in the String

long strtol (const Gen_String& s, char** ptr, int radix) {
  return (strtol ((char *)s.str, ptr, radix)); 
}	


// atol -- Equivalent to: strtol (str, (char**)END_OF_STRING, 10)
// Input:  Reference to Gen_String object
// Output: Long integer representing value contained in the String

long atol (const Gen_String& s) {		
  return (strtol ((char *)s.str, (char **) END_OF_STRING, int(10)));
}


// atoi -- Equivalent to: (int)strtol (str, (char**)END_OF_STRING, 10)
// Input:  Reference to Gen_String object
// Output: Integer representing value contained in the String

int atoi (const Gen_String& s) {		
  return int(strtol ((char *)s.str, (char **) END_OF_STRING, int(10)));	
}


// strtod -- Returns as a double-precision floating-point number the
//           value represented by a Gen_String object. Characters are
//           scanned upto the first unrecognized character.
// Input:    Reference to Gen_String object
// Output:   Double representing value contained in String

double strtod (const Gen_String& s, char** ptr) {		
  return (strtod ((char *)s.str, ptr));
}


// trim -- Removes any occurrence of the character(s) in "c" from "s"
// Input:  Gen_String reference, character string
// Output: Modified String "s" object

Gen_String& trim (Gen_String& sr, const char* rem) {
  sr.validate();			// Ensure not shared
  char* s = sr.str;
  char* result = sr.str;
  long len = 0;
  register char c;
  while ((c=*s++) != END_OF_STRING) {
    register const char* r = rem;
    register char t;
    while ((t=*r++) != END_OF_STRING && t != c); // Scan for match
    if (t == END_OF_STRING)			 // If no match found
      *result++ = c, len++;
  }
  *result = END_OF_STRING;			// NULL terminate string
  sr.length = len;
  return sr;					// Return string
}


// left_trim -- Removes any occurrence of the character(s) in "c" from
//              "s" that appear as a prefix to the string. The first
//              non-matching character encountered terminates the remove
//              operation and the rest of the string is copied intact.
// Input:       Gen_String reference, character string
// Output:      Modified String "s" object

Gen_String& left_trim (Gen_String& sr, const char* rem) {
  sr.validate();			// Ensure not shared
  char* result = sr.str;
  char* s = sr.str;
  long len = 0;
  register char c;
  for (; (c=*s) != END_OF_STRING; s++) {
    register const char* r = rem;
    register char t;
    while ((t=*r++) != END_OF_STRING && t != c); // Scan for match
    if (t == END_OF_STRING)			 // If no match found
      break;
  }
  if (s != result)				  // when characters trimed
    while ((*result++ = *s++) != END_OF_STRING) len++; // shift string down
  sr.length = len;
  return sr;				// Return string reference
}


// right_trim -- Removes any occurrence of the character(s) in "c" from
//               "s" that appear as a suffix to the string. The first
//               non-matching character encountered terminates the remove
//               operation and the rest of the string is copied intact.
// Input:        Gen_String reference, character string
// Output:       Modified String "s" object

Gen_String& right_trim (Gen_String& sr, const char* rem) {	
  sr.validate();			// Ensure not shared
  char* str = sr.str;
  char* s = str + strlen(str) - 1;		// last character of string
  for (; s >= str; s--) {
    register const char* r = rem;
    register char t;
    register char c = *s;
    while ((t=*r++) != END_OF_STRING && t != c); // Scan for match
    if (t == END_OF_STRING)			 // If no match found
      break;
  }
  *(s+1) = END_OF_STRING;
  sr.length = s - str;
  return sr;				// Return String reference
}


// upcase -- Convert all alphabetical characters to uppercase
// Input:    Gen_String reference
// Output:   Updated string

Gen_String& upcase (Gen_String& s) {	// Convert entire string to upper case
  s.validate();				// Ensure not shared
  c_upcase(s.str);
  return s;				// Return reference to modified string
}


// downcase -- Convert all alphabetical characters to lowercase
// Input:      Gen_String reference
// Output:     Updated string

Gen_String& downcase (Gen_String& s) {	// Convert entire string to lower case
  s.validate();			// Ensure not shared
  c_downcase(s.str);
  return s;			// Return reference to modified string
}

// capitalize -- Capitalize all words in a String. A word is define as
//               a sequence of characters separated by white space
// Input:        Gen_String reference
// Output:       Updated string

Gen_String& capitalize (Gen_String& s) {	
  s.validate();			// Ensure not shared
  c_capitalize(s.str);
  return s;			// Return reference to modified string
}


// insert -- Insert a char* at the position specified by start.
// Input  -- A char* to be inserted and a long index.          
// Output -- A Boolean, true if insertion took place, false if error.

Boolean Gen_String::insert (const char* ins, long start) {
  if (start<0 || start>this->length) return(FALSE);  // Boundary check
  long len=strlen(ins);                 // length of char*
  this->length += len;  	        // Determine length of new string
  this->validate();			// Ensure not shared and enough memory
  register char* ptr = this->str;
  register char* st = ptr + start;
  register char* end = ptr + this->length;
  for (ptr = end - len; ptr >= st;)		// Make space for ins
    *end-- = *ptr--;
  for (end = st+len; st < end; *st++ = *ins++);	// Insert ins into that
  return(TRUE);				// Insert worked
}


// remove -- Removes everything between the start and end indexes of string
//           The character at start is removed and all characters up to but 
//           not including the character at end.
// Input  -- A start and end index into the string.
// Output -- A Boolean, true if remove worked, false if error.

Boolean Gen_String::remove (long start, long end) {
#if ERROR_CHECKING
  if (start<0 || start > this->length || end<0
      || end > this->length || end<=start)
    RAISE (Error, SYM(Gen_String), SYM(Invalid_Start_End),
	   "Gen_String::remove(): Start %d and/or end %d index invalid",
	   start, end);
#endif
  this->validate();			     // Ensure not shared
  this->length -= (end-start);		     // New (shorter) length
  for(; this->str[start]=this->str[end]; start++) end++; // Remove everything
  return(TRUE);                              // Remove worked
}


// replace -- Removes everything between start and end as in remove(), and
//            inserts the char* argument into the place of what was removed.
// Input   -- A start and end for remove and a char*, c, to insert.
// Output  -- A Boolean, true if replace worked, false if error.

Boolean Gen_String::replace (const char* c, long start, long end) {
#if ERROR_CHECKING
  if (start<0 || start > this->length || end<0            // Boundary check
      || end > this->length || end<=start)
    RAISE (Error, SYM(Gen_String), SYM(Invalid_Start_End),
	   "Gen_String::replace(): Start %d and/or end %d index invalid",
	   start, end);
#endif
  long len=strlen(c);                   // length of c
  long delta = len - end + start;	// find overall change in length
  this->length += delta;		// set new length
  this->validate();		        // Ensure not shared and enough memory
  long ind;
  if (delta > 0) {			          // If replacement is bigger
    for (ind=this->length; start+len<=ind; ind--) // than chacters replaced
      this->str[ind] = this->str[ind-delta];
  }
  else if (delta < 0) {  		// Replacement is shorter
    char* st = this->str + start + len;
    char* ptr = this->str + end;
    char* end = this->str + this->length;
    while (st<=end) *st++ = *ptr++;
  }
  { char* ptr = this->str+start;
    char ch;
    while ((ch = *c++) != END_OF_STRING) // replace characters
      *ptr++ = ch;
  }
  return(TRUE);
}


// yank   -- Removes everything between start and end, copies it into a 
//           a string and returns that string.
// Input  -- A reference to a Gen_String. A start and end for the remove.
// Output -- none.  the Gen_String passed in gets set.

void Gen_String::yank (Gen_String& s, long start, long end) {
#if ERROR_CHECKING
  if (start<0 || start > this->length || end<0	// Boundary check
      || end > this->length || end<=start) 
    RAISE (Error, SYM(Gen_String), SYM(Invalid_Start_End),
	   "Gen_String::yank(): Start %d and/or end %d index invalid",
	   start,end);
#endif
  this->validate();			// Ensure not shared
  long len = end - start;
  strncpy(s, this->str+start, len);	// Copy stuff to yank into s
  this->length -= len;			// set new length
  for(; this->str[start]=this->str[end]; start++) end++;  // Do the remove
}


// sub_string -- Returns a new string initialized to what is between the
//               start and end indexes.
// Input      -- A Gen_String reference.  Two longs, a start and an end
// Output     -- none.  Sets the given Gen_String.

void Gen_String::sub_string (Gen_String& s, long start, long end) {
#if ERROR_CHECKING
  if (start<0 || start > this->length || end<0	// Boundary check
      || end > this->length || end<=start)
    RAISE (Error, SYM(Gen_String), SYM(Invalid_Start_End),
	   "Gen_String::sub_string(): Start %d and/or end %d index invalid",
	   start, end);
#endif
  strncpy(s, this->str+start, end - start);	// Copy substring into s
}


// strncpy -- Returns s, with the first length characters of source copied
//            into it.  The old value of s is lost.
// Input   -- A reference to a Gen_String s, a char* source, and a long length.
// Output  -- The modified Gen_String s.

Gen_String& strncpy(Gen_String& s, const char* source, long length) {
#if ERROR_CHECKING  
  if (length<0) 				// Boundary check
    RAISE (Error, SYM(Gen_String), SYM(Negative_Length),
	   "Gen_String::strncpy(): Negative length %d", length);
#endif
  s.length = length;			// Set new string length
  s.validate(TRUE);		        // Ensure not shared and enough memory
  char* p = s.str;
  while (length-- > 0)			// Copy source into string
    *p++ = *source++;
  *p = END_OF_STRING;			// set the end byte
  return s;
}


// bracket_error -- Raise exception for operator[]
// Input:           Index
// Output:          None

void Gen_String::bracket_error (long n) CONST {
  RAISE (Error, SYM(Gen_String), SYM(Out_Of_Range),
	 "Gen_String::operator[](): Index %d out of range", n);
}


// ratio_error -- Raise exception for set_growth_ratio
// Input:         Ratio
// Output:        None

void Gen_String::ratio_error (float r) CONST {
  RAISE (Error, SYM(Gen_String), SYM(Negative_Ratio),
	 "Gen_String::set_growth_ratio(): Negative growth ratio %f", r);
}


// size_error -- Raise exception for set_alloc_size
// Input:        Growth size
// Output:       None

void Gen_String::size_error (int n) CONST {
  RAISE (Error, SYM(Gen_String), SYM(Negative_Size),
	 "Gen_String::set_alloc_size(): Negative growth size %d", n);
}


// find_error -- Raise exception for find()
// Input:        None
// Output:       None

void Gen_String::find_error () CONST {
  RAISE (Error, SYM(Gen_String), SYM(Invalid_Regexp),
	 "Gen_String::find(): Invalid regular expression");
}
