//
// 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 06/27/89 -- Initial design and implementation
// 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 08/03/89 -- Changed operator= argument to const
// Updated: LGO 08/09/89 -- Inherit from Generic
// Updated: MBN 09/06/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: MBN 12/15/89 -- Sprinkled "const" all over the place!
// Updated: LGO 01/05/90 -- Removed strchr, strrchr
// Updated: MBN 01/19/90 -- Made operator=(const char*) take a const char*
// Updated: MJF 06/15/90 -- Added inline strncpy(...,int) to remove ambiguity
//
// The String class provides dynamic, efficient  strings for a C++ application
// programmer.  The string private data consists of a  slot that maintains the
// length  of   the string  (ie.   number of characters),   a  size  slot that
// maintains the  actual number of  bytes allocated to  a string  object char*
// pointer, and a pointer to the first character of the string. In addition, a
// floating point slot can contain a non-negative percentage between 0 and 1.0
// that  indicates   the ratio by which  a   string object  should  grow  when
// necessary.  Finally,  a static for  the  entire  string class  contains the
// allocation size to be used  when a string  object needs to grow dynamically
// if the growth ratio for the particular instance has not been set.  This has
// a default value that may be over-ridden by the user when the constructor is
// invoked.
//
//              String               Virtual Function Table
//           +-----------+                +----------+
//           |  V_Table--+------------->  :Alloc_Size:
//           +-----------+                :  ....    :
//           | Length=16 |                :          :
//           +-----------+                +----------+
//           | size=100  |                
//           +-----------+   
//           | ratio=0.0 |
//           +-----------+       +--------------------- ... --+
//           |   str   --+------>|This is a string            |
//           +-----------+       +--------------------- ... --+
//
//                               \_____________  _____________/
//                                             \/
//                                     100 bytes allocated
//
// There are  several constructors  for  this class.  The   empty  constructor
// initializes a String object and allocates the default size block of memory.
// The second, third,  and fourth constructors  take char, char*,   and String
// arguments, respectively, and initialize the  String object accordingly. The
// fifth and sixth constructors take either a char* or String argument, and an
// integer argument that specifies the initial
//
// 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 STRINGH					// If no String class defined
#define STRINGH					// Indicate its done now

#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

#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

class String : public Generic {  
private:
  long length;					// Number of characters 
  long size;					// Allocated memory size
  char* str;					// Pointer to string
  float growth_ratio;				// If non-zero, grow by %
  static int alloc_size_s;			// Memory growth size

  void bracket_error (long) CONST;		// Raise exception
  void growth_error (int) CONST;		// Raise exception
  void ratio_error (float) CONST;		// Raise exception
  friend void update_memory (String&);		// Adjust memory size
  
public:
  String ();					// String x;
  String (char);				// String x = 'A';
  String (CONST char*);				// String x = "ABCDEFG";
  String (CONST String&);			// String x = y;
  String (CONST String&, long);			// String x = y; memory size
  String (CONST char*, long);			// String x = "ABCDEFG"; size

  ~String();					// Destructor for String class

  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 index
  void yank (String&, long, long);		// Delete/set to chars at index
  void sub_string (String&, long, long);	// Set to chars between indexes

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

  friend ostream& operator<< (ostream&, const String&);
  inline friend ostream& operator<< (ostream&, const String*);
  
  inline String& operator= (char);		// x = 'A';
  inline String& operator= (const char*);	// x = "ABCDEFG";
  inline String& operator= (const String&);	// x = y;
  
  String operator+ (char);			// Concatenation operators
  String operator+ (const char*);
  String operator+ (const String&);
  
  inline String& operator+= (char);		// Concatentation w/ assignment
  inline String& operator+= (const char*);
  inline String& operator+= (const String&);
  
#ifdef __cplusplus
  inline operator char*() CONST;		// Convert String to char*
#else
  inline char* operator char*();		// Convert String to char*
#endif  
  void reverse ();				// Reverse character order
  void clear ();				// Reset NULL terminator
  void resize (long);				// Allocate at least min size
  inline char operator[] (long i) CONST;	// Specific char from String

  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 String&) CONST; // Equality operator
  inline Boolean operator== (const char*) CONST;
  
  inline Boolean operator!= (const String&) CONST; // Inequality operator
  inline Boolean operator!= (const char*) CONST;
  
  inline Boolean operator< (const String&) CONST; // Lexical less than
  inline Boolean operator< (const char*) CONST;
  
  inline Boolean operator> (const String&) CONST; // Lexical greater than
  inline Boolean operator> (const char*) CONST;
  
  inline Boolean operator<= (const String&) CONST; // Lexical less than/equal
  inline Boolean operator<= (const char*) CONST;
  
  inline Boolean operator>= (const String&) CONST; // Lexical greater than/equal
  inline Boolean operator>= (const char*) CONST;
  
  inline friend long strlen (const String&);	// Return length of string
  friend String& strcat(String&, const String&);// Appends a copy of second
  friend String& strcat (String&, const char*);
  friend String& strcat (String&, char);
  
  friend String& strncat (String&, const String&, int); // Append "n" chars
  friend String& strncat (String&, const char*, int);
  
  friend String& strcpy (String&, char);	// String copy functions
  friend String& strcpy (String&, const char*);
  friend String& strcpy (String&, const String&); 
  
  friend long strtol(const String&, char** ptr=NULL, int radix=10); // to long
  friend long atol (const String&);		// Convert string to long
  friend int atoi (const String&);		// Convert string to int
  
  friend double strtod (const String&, char** ptr=NULL); // string to double
  inline friend double atof (const String&);	// Convert string to double
  
  friend String& trim (String&, const char*);	    // Trim characters 
  friend String& left_trim (String&, const char*);  // Trim prefix chars
  friend String& right_trim (String&, const char*); // Trim suffix chars
  
  friend String& upcase (String&);		// Convert string to upper 
  friend String& downcase (String&);		// Convert string to lower
  friend String& capitalize (String&);		// Capitalize each word
};


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

inline char 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 String::capacity() CONST {
  return (this->size-1);			// Allocated size -1 for NULL
}


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

inline void String::set_alloc_size (int n) {
#if ERROR_CHECKING
  if (n < 0)					// If negative growth size
    this->growth_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

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


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

inline ostream& operator<< (ostream& os, const String* s) {
  return operator<< (os, *s);
}


// operator char* -- Provide an accessor to the String character pointer
// Input:            this* String pointer
// Output:           this->str character pointer

#ifdef __cplusplus
inline String::operator char*() CONST {return this->str;}
#else
inline char* String::operator char*() {return this->str;}
#endif

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

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


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

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


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

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


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

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


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

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


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

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


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

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


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

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


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

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


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

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


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

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


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

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


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

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


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

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


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

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


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

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


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

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


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

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


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

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


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

inline double atof (const 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 String s, a char* source, and an int length.
// Output  -- The modified String s.

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

#endif						// End #ifdef of STRINGH


