//
// 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: MBN 06/07/89 -- Added growth ratio slot and resize method
// Updated: MBN 06/30/89 -- Created Gen_String class with regular expression
// 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: LGO 08/09/89 -- Inherit from Generic
// Updated: MBN 09/07/89 -- Added conditional exception handling
// 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: LGO 11/07/89 -- Removed strcmp, strncmp
// Updated: LGO 11/07/89 -- Make check_memory strcpy and operator=
//                          take const arguments
// 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/05/90 -- Removed strchr, strrchr
// Updated: MJF 05/21/90 -- Moved delete from grow_memory to validate
// Updated: MJF 06/15/90 -- Added inline strncpy(...,int) to remove ambiguity
//
// The   Gen_String class   provides   dynamic,  efficient strings   for  a C++
// application programmer  with support for   reference counting, delayed copy,
// and  regular expression   pattern   matching/replacment.  The  private  data
// consists of a pointer to a structure of type String_Layout.   This structure
// is defined for use by the Gen_String class and  is public so that Gen_String
// constructors can  allocate correct   memory.  In addition, the  private data
// section contains a slot that maintains the length of the string (ie.  number
// of characters) and a pointer to the first character of the string.  Finally,
// a static for the entire Gen_String class  contains the allocation size to be
// used   when an  Gen_String object  needs  to grow  dynamically.   This has a
// default value that may be over-ridden  by the  user when the  constructor is
// invoked.
//
// The string layout structure has several fields.  The size variable maintains
// the actual  number of bytes allocated  to this string  object char* pointer.
// This may be different to the length slot in the Gen_String object due to the
// dynamic nature  of the  string  object  and/or  sub-string  operations.  The
// reference count variable  maintains  a  count  of  the number  of Gen_String
// pointers  that point  to  this  actual string.  This  is  utilized to reduce
// copying of  string objects  at   assignment, for example.   This  scheme  is
// similar to that used  in a virtual  memory management  system when a  parent
// process spawns a child process.  The  child process  inherits a copy  of the
// parents memory,  but  instead  of   copying,  a pointer  to these  pages,  a
// reference count is  kept.  If the child process  only reads a page, it never
// needs to be copied.  Only  when a change is  made  must a copy actually take
// place.   In  general, this  scheme drastically  cuts   down byte  copies and
// improves runtime efficieny.   The following diagram  displays two Gen_String
// objects that share a char* string.   Note  that the Gen_String2 object is in
// fact a sub-string of Gen_String1.
// 
//  +----------->-------------+
//  |                         |
//  |         Gen_String1     V          Virtual Function Table
//  |        +-----------+    |                +----------+
//  |        |  V_Table--+----+------------->  :Alloc_Size:
//  |        +-----------+                     :  ....    :
//  |        | Length=16 |                     :  ....    :       +----------+
//  |        +-----------+                     +----------+       |  Regular |
//  |        |  *rgexp --+--------------------------------------->|Expression|
//  |        +-----------+                                        |  Object  |
//  |        |rgexp_index|                                        +----------+
//  |        +-----------+
//  ^        |    p    --+---+
//  |        +-----------+   |
//  |        |   str   --+---)-------------->-------------+
//  |        +-----------+   |                            |
//  |                        |                            |
//  |                        |                            |
//  |     Gen_String2        |        Layout_Struct       |
//  |    +-----------+       +------->+-----------+       |
//  +----+--V_Table  |       |        | Ref_Cnt=2 |       |
//       +-----------+       |        +-----------+       |
//       | Length=6  |       |        |  Size=25  |       V
//       +-----------+       |        +-----------+      +--------------------+
//    +--+-- *rgexp  |       |        | str_start-+----->|This is a string    |
//    |  +-----------+       |        +-----------+      +--------------------+
//    |  |rgexp_index|       |                                      ^
//    |  +-----------+       |                                      |
//    |  |    p    --+-------+                                      |
//    |  +-----------+                                              |
//    |  |   str   --+--------------------->------------------------+
//    |  +-----------+   
//    |                  +----------+
//    |                  |  Regular |
//    +----------------->|Expression|
//                       |  Object  |
//                       +----------+
//
// There are  several   constructors  for this   class.  The empty  constructor
// initializes an Gen_String object  and allocates the  layout structure with a
// default allocation of  MEM_BLK_SZ bytes for  the char* pointer.   The second
// constructor takes a char* argument and provides for the initialization of an
// Gen_String object  to a single character or  character  string.    The third
// constructor provides  initialization  of an   Gen_String  Object to that  of
// another Gen_String  object.  Finally,  the two  constructors with an integer
// argument allow  the user  to initialize  a  Gen_String object with either  a
// char* or another Gen_String object and specify the initial memory allocation
// size for the char* pointer.  This is useful  for a user  who expect a String
// object to be fairly dynamic; allocating  a  larger chunk initially cuts down
// on the number of new() requests needed to grow the object later.
//
// The standard  ANSI "str____" function names are  all overloaded for use with
// both  char*  and  String   objects.  Operators  for  String   concatenation,
// assignment, and  comparison are also provided.   In addition,  functions for
// case conversion and string token trimming are provided.   Finally, note that
// the operator functions use corresponding functions with the  "case" flag set
// to SENSITIVE.  A user  can perform case-INSENSITIVE operations by explicitly
// calling the appropriate function.
//

#ifndef GEN_STRINGH				// If no Gen_String class
#define GEN_STRINGH				// Include the header file

#ifndef CHARH					// If extended char* not here
#include <cool/char.h>
#endif

#ifndef GENERIC_H				// If no definition for class
#include <cool/Generic.h>			// include definition file
#endif

#ifndef REGEXPH					// If no regular expressions
#include <cool/Regexp.h>			// Include definition
#endif

#if defined(DOS)
extern "C" {
#include <stdlib.h>		// Include standard c library support
}
#else
#include <stdlib.h>		// Include standard c library support
#endif

#define MEM_BLK_SZ 100

struct String_Layout {				// Internal layout structure 
  long size;					// Bytes allocated
  short ref_count;				// Pointers to other Gen_String
  char* start;					// Pointer to start of string
};

class Gen_String : public Generic {
private:
  long length;				       // Number of characters 
  char* str;				       // Pointer to string
  String_Layout *p;			       // Pointer to layout structure
  float growth_ratio;			       // If non-zero, grow by this %
  static int alloc_size_s;		       // Memory allocation growth size
  Regexp* rgexp;			       // Pointer to regular expression
  long rgexp_index;			       // Start index for regexp search

  void bracket_error (long) CONST;		// Raise exception
  void ratio_error (float) CONST;		// Raise exception
  void find_error () CONST;			// Raise exception
  void size_error (int) CONST;			// Raise exception
  
  void validate(Boolean dont_copy=FALSE);	// check ref_count and memory
  void update_ref_count();			// Don't share string
  void grow_memory (Boolean dont_copy);		// Grow string

//  friend void update_ref_count (Gen_String&);	// Adjust reference count
//  friend void check_memory (Gen_String&, const char*);// Adjust memory size
  
public:
  Gen_String ();			       // Gen_String x;
  Gen_String (char);			       // Gen_String x = 'A';
  Gen_String (CONST char*);		       // Gen_String x = "ABCDEFG";
  Gen_String (CONST Gen_String&);	       // Gen_String y = x;
  Gen_String (CONST Gen_String&, long);	       // Gen_String x = y; memory size
  Gen_String (CONST char*, long);	       // Gen_String x = "AB"; mem size
  
  ~Gen_String();			       // Destructor for Gen_String

  Boolean insert (const char*, long);	       // Insert chars at index
  Boolean remove (long, long);		       // Remove chars between indexes
  Boolean replace (const char*, long, long);   // Replace chars between indexes

  void yank (Gen_String&, long, long);	       // Delete and set the given 
                                               // Gen_String to the characters
                                               // at index

  void sub_string (Gen_String&, long, long);   // Set the given Gen_String to 
                                               // the chars between indexes

  friend Gen_String& strncpy (Gen_String&, const char*, long);// Copy "n" chars
  inline friend Gen_String& strncpy (Gen_String&, const char*, int);

  friend ostream& operator<< (ostream&, const Gen_String&);
  inline friend ostream& operator<< (ostream&, const Gen_String*);
  
  inline Gen_String& operator= (char);		// x = 'A';
  inline Gen_String& operator= (const char*);	// x = "ABCDEFG";
  inline Gen_String& operator= (const Gen_String&); // x = y;
  
  Gen_String operator+ (char);			// Concatenate a character
  Gen_String operator+ (const char*);		// Concatenate a char string
  Gen_String operator+ (const Gen_String&);	// Concatenate an Gen_String
  
  inline Gen_String& operator+= (char);		// Concat with assignment 
  inline Gen_String& operator+= (const char*);
  inline Gen_String& operator+= (const Gen_String&);
  
#ifdef __cplusplus
  inline operator char*() CONST ;		// Convert Gen_String to char*
#else
  inline char* operator char*();		// Convert Gen_String to char*
#endif
  void compile (char*);				// Compile regexp in argument
  Boolean find ();				// Search for first/next regexp
  inline long start () CONST;			// Return start index of match
  inline long end () CONST;			// Return end index of match
  inline Boolean is_valid () CONST;		// TRUE/FALSE validates regexp
  
  void reverse ();				// Reverse character order
  void clear ();				// Reset NULL terminator
  void resize (long);				// Allocate at least min size
  inline char operator[] (long i) CONST;	// x[4] --> fifth character

  inline long capacity() CONST;			// Returns maximum size string 
  inline void set_alloc_size (int);		// Set memory block alloc size
  inline void set_growth_ratio (float);		// Set growth percentage
  
  inline Boolean operator== (const Gen_String&) CONST; 
  inline Boolean operator== (const char*) CONST;	  
  
  inline Boolean operator!= (const Gen_String&) CONST; 
  inline Boolean operator!= (const char*) CONST;	  
  
  inline Boolean operator< (const Gen_String&) CONST;  
  inline Boolean operator< (const char*) CONST;	  
  
  inline Boolean operator> (const Gen_String&) CONST;  
  inline Boolean operator> (const char*) CONST;
  
  inline Boolean operator<= (const Gen_String&) CONST; 
  inline Boolean operator<= (const char*) CONST;
  
  inline Boolean operator>= (const Gen_String&) CONST; 
  inline Boolean operator>= (const char*) CONST;
  
  inline friend long strlen (const Gen_String&); // Return length of string
  
  friend Gen_String& strcat (Gen_String&, const Gen_String&);
  friend Gen_String& strcat (Gen_String&, const char*);
  friend Gen_String& strcat (Gen_String&, char);
  
  friend Gen_String& strncat (Gen_String&, const Gen_String&, int);
  friend Gen_String& strncat (Gen_String&, const char*, int);
  
  friend Gen_String& strcpy (Gen_String&, char);
  friend Gen_String& strcpy (Gen_String&, const char*);
  friend Gen_String& strcpy (Gen_String&, const Gen_String&); 
  
  friend long strtol(const Gen_String&, char** ptr=NULL, int radix=10);
  friend long atol (const Gen_String&);		  
  friend int atoi (const Gen_String&);	
  
  friend double strtod (const Gen_String&, char** ptr=NULL);
  inline friend double atof (const Gen_String&);
  
  friend Gen_String& trim (Gen_String&, const char*);
  friend Gen_String& left_trim (Gen_String&, const char*); 
  friend Gen_String& right_trim (Gen_String&, const char*);
  
  friend Gen_String& upcase (Gen_String&);
  friend Gen_String& downcase (Gen_String&);
  friend Gen_String& capitalize (Gen_String&);
};

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

inline ostream& operator<< (ostream& os, const Gen_String* s) {
  return os << s->str;				
}

// operator[] -- Return a single character element from string
// Input:        this*, index "i"
// Output:       The "ith-1" character from String

inline char Gen_String::operator[] (long i) CONST {
#if ERROR_CHECKING
  if (i >= this->length)			// If index out of range
    this->bracket_error (i);			// Raise exception
#endif
  return (this->str[i]);
}


// capacity -- Determine the maximum size string possible with growing
// Input:      None
// Output:     Maximum number of characters before growth is required

inline long Gen_String::capacity() CONST {
  return (this->p->size-1);			
}


// set_alloc_size -- Set the default allocation size growth rate
// Input:            Growth size in number of elements
// Output:           None

inline void Gen_String::set_alloc_size (int n) {
#if ERROR_CHECKING
  if (n < 0)					// If negative growth size
    this->size_error (n);			// Raise exception
#endif
  this->alloc_size_s = n;			// Set growth size
}


// set_growth_ratio -- Set the growth percentage for this instance of String
// Input:              Percentage growth rat
// Output:             None

void Gen_String::set_growth_ratio (float ratio) {
#if ERROR_CHECKING
  if (ratio <= 0.0)				// If positive growth factor
    this->ratio_error (ratio);			// Raise exception
#endif
  this->growth_ratio = ratio;			// Set growth size
}


// operator char* -- Provide an accessor to the character pointer
// Input:            this* 
// Output:           this->str character pointer
#ifdef __cplusplus
inline Gen_String::operator char*() CONST {return (this->str);}
#else
inline char* Gen_String::operator char*() {return (this->str);}
#endif

// start -- Return the zero-relative start index of regular expression match
// Input:   None
// Output:  Start index into string

inline long Gen_String::start () CONST {
  return (this->rgexp_index + this->rgexp->start ());
}


// End -- Return the zero-relative end index of the regular expression match
// Input:   None
// Output:  End index into string

inline long Gen_String::end () CONST {
  return (this->rgexp_index + this->rgexp->end ());
}


// is_valid -- Returns the validity of the regular expression
// Input:      None
// Output:     TRUE/FALSE

inline Boolean Gen_String::is_valid () CONST {
  return (this->rgexp != NULL) && 		// If a Regexp object
         this->rgexp->is_valid ();		// return Regexp status
}


// operator= -- Assignment to a single character: x = 'A';
// Input:       Single character
// Output:      Gen_String object containing character string

inline Gen_String& Gen_String::operator= (char c) {
  return (strcpy (*this, c));			
}


// operator= -- Assignment to a character string: x = "ABCDEFG";
// Input:       Character string
// Output:      Gen_String object containing character string

inline Gen_String& Gen_String::operator= (const char* c) {
  return (strcpy (*this, c));			
}


// operator= -- Assignment to another Gen_String: x = y;
// Input:       Reference to Gen_String object
// Output:      Gen_String object sharing memory with other Gen_String

inline Gen_String& Gen_String::operator= (const Gen_String& s) {
  return (strcpy (*this, s));			
}


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

inline Gen_String& Gen_String::operator+= (char c) {
  return (strcat (*this, c));			
}


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

inline Gen_String& Gen_String::operator+= (const char* c) {
  return (strcat (*this, c));			
}


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

inline Gen_String& Gen_String::operator+= (const Gen_String& s) {
  return (strcat (*this, s));			
}


// operator== -- Test for equality of two Gen_String objects
// Input:        this*, Gen_String reference
// Output:       Boolean TRUE/FALSE

inline Boolean Gen_String::operator== (const Gen_String& s) CONST {
  return (is_equal (*this, s, SENSITIVE));
}


// operator== -- Test for equality of an Gen_String and char*
// Input:        this*, char* pointer
// Output:       Boolean TRUE/FALSE

inline Boolean Gen_String::operator== (const char* c) CONST {
  return (is_equal (*this, c, SENSITIVE));
}


// operator!= -- Test for inequality of two Gen_String objects
// Input:        this*, Gen_String reference
// Output:       Boolean TRUE/FALSE

inline Boolean Gen_String::operator!= (const Gen_String& s) CONST {
  return (is_not_equal (*this, s, SENSITIVE));
}


// operator!= -- Test for inequality of two Gen_String objects
// Input:        this*, char* pointer
// Output:       Boolean TRUE/FALSE

inline Boolean Gen_String::operator!= (const char* c) CONST {
  return (is_not_equal (*this, c, SENSITIVE));
}


// operator< -- Test for lexical ordering before a Gen_String
// Input:       this*, Gen_String reference
// Output:      Boolean TRUE/FALSE

inline Boolean Gen_String::operator< (const Gen_String& s) CONST {
  return (is_lt (*this, s, SENSITIVE));
}


// operator< -- Test for lexical ordering before a Gen_String
// Input:       this*, char* pointer
// Output:      Boolean TRUE/FALSE

inline Boolean Gen_String::operator< (const char* c) CONST {
  return (is_lt (*this, c, SENSITIVE));
}


// operator> -- Test for lexical ordering after a Gen_String
// Input:       this*, Gen_String reference
// Output:      Boolean TRUE/FALSE

inline Boolean Gen_String::operator> (const Gen_String& s) CONST {
  return (is_gt (*this, s, SENSITIVE));
}


// operator> -- Test for lexical ordering after a Gen_String
// Input:       this*, char* pointer
// Output:      Boolean TRUE/FALSE

inline Boolean Gen_String::operator> (const char* c) CONST {
  return (is_gt (*this, c, SENSITIVE));
}


// operator<= -- Test for lexical ordering before or equal to a Gen_String
// Input:        this*, Gen_String reference
// Output:       Boolean TRUE/FALSE

inline Boolean Gen_String::operator<= (const Gen_String& s) CONST {
  return (is_le (*this, s, SENSITIVE));
}


// operator<= -- Test for lexical ordering before or equal to a Gen_String
// Input:        this*, char* pointer
// Output:       Boolean TRUE/FALSE

inline Boolean Gen_String::operator<= (const char* c) CONST {
  return (is_le (*this, c, SENSITIVE));
}


// operator>= -- Test for lexical ordering after or equal to a Gen_String
// Input:        this*, String reference
// Output:       Boolean TRUE/FALSE

inline Boolean Gen_String::operator>= (const Gen_String& s) CONST {
  return (is_ge (*this, s, SENSITIVE));
}


// operator>= -- Test for lexical ordering after or equal to a Gen_String
// Input:        this*, char* pointer
// Output:       Boolean TRUE/FALSE

inline Boolean Gen_String::operator>= (const char* c) CONST {
  return (is_ge (*this, c, SENSITIVE));
}




// strlen -- Return the number of characters in the string
// Input:    Gen_String reference
// Output:   Length of the string

inline long strlen (const Gen_String& s) {
  return (s.length);
}


// atof -- Equivalent to strtod (str, (char**)END_OF_STRING)
// Input:  Reference to Gen_String object
// Output: Double representing value contained in string

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


// 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 an int length.
// Output  -- The modified Gen_String s.

inline Gen_String& strncpy(Gen_String& s, const char* source, int n) {
  return strncpy(s, source, (long) n);
}


#endif						// End of GEN_STRINGH


