// <copyright>
// 
// Copyright (c) 1993-1996
// Institute for Information Processing and Computer Supported New Media (IICM),
// Graz University of Technology, Austria.
// 
// </copyright>

//<file>
//
// Name:       str.C
//
// Purpose:    Character pointer with resource management
//
// Created:     1990    Gerald Pani
//
// Modified:   24 Aug 93    Gerald Pani
// Modified:   14 Sep 94    Gerald Pani
// 		o initLowTable
//
//
//
// Description:
//
//</file>

#include <string.h>
#include <memory.h>

#include <ctype.h>
#include <iostream.h>
#include <stdlib.h>

#include "str.h"

int class_RString_version_2_0;

static const int lineBufSize = 512;
//gorasche: for WIN32 this should be THREADSTATIC and makes problems...
//static char lineBuf[lineBufSize];

static inline char* mmemcpy(char* s1, const char* s2, int len) {
     return (char*)::memcpy(s1, s2, len);
}

static inline const char* mmemchr(const char* s1, char c, int len) {
     return (const char*)::memchr((const void*)s1, c, len);
}

static inline int mmemcmp(const char* s1, const char* s2, int len) {
     return ::memcmp(s1, s2, len);
}


// ---- Delimiter for sgmlIso2LowerAscii() -------
// ---- critical characters ------

// 26 '&', 

static char isoDelimTable[256] = {
     
//   0 1 2 3 4 5 6 7 8 9 a b c d e f
     1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, // 0
     1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, // 1
     0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0, // 2
     0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 3
     0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 4
     0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 5
     0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 6
     0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1, // 7
     1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, // 8
     1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, // 9
     1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, // a
     1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, // b
     1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, // c
     1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, // d
     1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, // e
     1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1  // f
     };

// allowed characters (those with "1") for direct SGML entity references ("&#digits;")
static char isoCharReferenceTable[256] = {
     
//   0 1 2 3 4 5 6 7 8 9 a b c d e f
     0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0, // 0
     0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 1
     1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, // 2
     1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, // 3
     1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, // 4
     1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, // 5
     1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, // 6
     1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0, // 7
     0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 8
     0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 9
     1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, // a
     1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, // b
     1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, // c
     1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, // d
     1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, // e
     1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1  // f
     };

static const char* lowTable[256];

// static const char* lowTable[256] = {
     
// //     0    1    2    3    4    5    6    7    8    9    a    b    c    d    e    f
//       nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, // 0
//       nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, // 1
//       nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, // 2
//       nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, // 3
//       nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, // 4
//       nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, // 5
//       nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, // 6
//       nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, // 7
//       nil,"ue", nil, nil,"ae", nil, nil, nil, nil, nil, nil, nil, nil, nil,"ae", nil, // 8
//       nil, nil, nil, nil,"oe", nil, nil, nil, nil,"oe","ue", nil, nil, nil, nil, nil, // 9
//       nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, // a
//       nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, // b
//       nil, nil, nil, nil,"ae", nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, // c
//       nil, nil, nil, "o", nil, nil,"oe", nil, nil, nil, nil, nil,"ue", nil, nil,"ss", // d
//       nil, "a", nil, nil,"ae", nil, nil, "c", nil, "e", nil, nil, nil, nil, nil, nil, // e
//       nil, nil, nil, nil, nil, nil,"oe", nil, nil, nil, "u", nil,"ue", nil, nil, nil  // f
//      };

// /* lowTable['ä'] = "ae";
//  * lowTable['ö'] = "oe";
//  * lowTable['ü'] = "ue";
//  * lowTable['Ä'] = "ae";
//  * lowTable['Ö'] = "oe";
//  * lowTable['Ü'] = "ue";
//  * lowTable['ß'] = "ss";
//  */

static char whiteTable[256];
static RString wsChars;


RString operator+( const RString & s1, const RString & s2) {
     RString ret( s1, s2);
     return ret;
}

int operator ==( const RString& s1, const char* s2) {
     if (!s2)
	  return (s1.length() ? 0 : 1);
     if(! RString::uchar_strcmp (s1.string(), s2))
	  return 1;
     return 0;
}

int operator ==( const char* s1, const RString & s2) {
     if (!s1)
	  return (s2.length() ? 0 : 1);
     if(! RString::uchar_strcmp (s1, s2.string()))
	  return 1;
     return 0;
}

int operator ==( const RString& s1, const RString& s2) {
     if (s1.ptr() == s2.ptr())  // pointer are the same
	  return 1;
     if (s1.length() != s2.length())
	  return 0;
     if(!::mmemcmp( s1.string(), s2.string(), s1.length())) 
     {
         // set s2 = s1
//	((RString&)s2).repres_ = s1.repres_;  // this cast is really ugly stuff
	return 1;
     }
     return 0;
}

int operator !=( const RString& s1, const char* s2) {
     if(!(s1 == s2))
	  return 1;
     return 0;
}

int operator !=( const char* s1, const RString & s2) {
     if(!(s1 == s2))
	  return 1;
     return 0;
}

int operator !=( const RString& s1, const RString & s2) {
     if(!(s1 == s2))
	  return 1;
     return 0;
}

int operator <( const RString& s1, const RString & s2) {
     if (s1.length() < s2.length()) {
	  if (::mmemcmp( s1.string(), s2.string(), s1.length() + 1) < 0)
	       return 1;
	  else 
	       return 0;
     }
     else {
	  if (::mmemcmp( s1.string(), s2.string(), s2.length() +1) < 0)
	       return 1;
	  else 
	       return 0;
     }
}

int operator <( const char* s1, const RString & s2) {
     if (!s1)
	  return (s2.length() ? 1 : 0);
     if ((RString::uchar_strcmp ((const char *)s1, s2.string())) < 0)
	  return 1;
     return 0;
}

int operator <( const RString& s1, const char* s2) {
     if (!s2)
	  return 0;
     if ((RString::uchar_strcmp (s1.string(), (const char*)s2)) < 0)
	  return 1;
     return 0;
}

StringRepPtr StringRep::lambda_;

StringRep::StringRep( const char *s) {
    write (s) ;
    write ('\0') ;
}

StringRep::StringRep( const char *s, int length) 
:OBuffer(length+1)
{
     if (length < 0)
         length = 0;
     write (s,length);
     write ('\0') ;
}
StringRep :: StringRep( const char* c1, const char * c2)
{
    write(c1).write(c2).write('\0');
}

StringRep :: StringRep( StringRep& s1 , StringRep& s2)
:OBuffer(s1.length()+s2.length()+1)
{
    write(s1.data(),s1.length()).write(s2.data(),s2.length()).write('\0');
}


struct ConvertEnt {
     const char* sgml;
     char iso;
     const char* low;
};

static const int numents = 100;

static ConvertEnt convertTable[numents] = {{ "AElig",	 16*12+6,	"ae"	},
				      { "Aacute",	 16*12+1,	"a"	},
				      { "Acirc",	 16*12+2,	"a"	},
				      { "Agrave",	 16*12+0,	"a"	},
				      { "Aring",	 16*12+5,	"a"	},
				      { "Atilde",	 16*12+3,	"a"	},
				      { "Auml",	 16*12+4,	"ae"	},
				      { "Ccedil",	 16*12+7,	"c"	},
				      { "Dstrok",	 16*13+0,	"d"	},
				      { "Eacute",	 16*12+9,	"e"	},
				      { "Ecirc",	 16*12+10,	"e"	},
				      { "Egrave",	 16*12+8,	"e"	},
				      { "Euml",	 16*12+11,	"e"	},
				      { "Iacute",	 16*12+13,	"i"	},
				      { "Icirc",	 16*12+14,	"i"	},
				      { "Igrave",	 16*12+12,	"i"	},
				      { "Iuml",	 16*12+15,	"i"	},
				      { "Ntilde",	 16*13+1,	"n"	},
				      { "Oacute",	 16*13+3,	"o"	},
				      { "Ocirc",	 16*13+4,	"o"	},
				      { "Ograve",	 16*13+2,	"o"	},
				      { "Oslash",	 16*13+8,	"o"	},
				      { "Otilde",	 16*13+5,	"o"	},
				      { "Ouml",	 16*13+6,	"oe"	},
				      { "THORN",	 16*13+14,	""	},
				      { "Uacute",	 16*13+10,	"u"	},
				      { "Ucirc",	 16*13+11,	"u"	},
				      { "Ugrave",	 16*13+9,	"u"	},
				      { "Uuml",	 16*13+12,	"ue"	},
				      { "Yacute",	 16*13+13,	"y"	},
				      { "aacute",	 16*14+1,	"a"	},
				      { "acirc",	 16*14+2,	"a"	},
				      { "acute",	 16*11+4,	""	},
				      { "aelig",	 16*14+6,	"ae"	},
				      { "agrave",	 16*14+0,	"a"	},
				      { "amp",	 16*2+6,	"&"	},
				      { "aring",	 16*14+5,	"a"	},
				      { "atilde",	 16*14+3,	"a"	},
				      { "auml",	 16*14+4,	"ae"	},
				      { "brkbar",	 16*10+6,	""	},
				      { "ccedil",	 16*14+7,	"c"	},
				      { "cedil",	 16*11+8,	""	},
				      { "cent",	 16*10+2,	""	},
				      { "copy",	 16*10+9,	""	},
				      { "curren",	 16*10+4,	""	},
				      { "deg",	 16*11+0,	""	},
				      { "divide",	 16*15+7,	""	},
				      { "eacute",	 16*14+9,	"e"	},
				      { "ecirc",	 16*14+10,	"e"	},
				      { "egrave",	 16*14+8,	"e"	},
				      { "eth",	 16*15+0,	""	},
				      { "euml",	 16*14+11,	"e"	},
				      { "frac12",	 16*11+13,	""	},
				      { "frac14",	 16*11+12,	""	},
				      { "frac34",	 16*11+14,	""	},
				      { "gt",	 16*3+14,	">"	},
				      { "hibar",	 16*10+15,	""	},
				      { "iacute",	 16*14+13,	"i"	},
				      { "icirc",	 16*14+14,	"i"	},
				      { "iexcl",	 16*10+1,	""	},
				      { "igrave",	 16*14+12,	"i"	},
				      { "iquest",	 16*11+15,	""	},
				      { "iuml",	 16*14+15,	"i"	},
				      { "laquo",	 16*10+11,	""	},
				      { "lt",		 16*3+12,	"<"	},
				      { "micro",	 16*11+5,	""	},
				      { "middot",	 16*11+7,	""	},
				      { "nbsp",	 16*10+0,	" "	},
				      { "not",	 16*10+12,	""	},
				      { "ntilde",	 16*15+1,	"n"	},
				      { "oacute",	 16*15+3,	"o"	},
				      { "ocirc",	 16*15+4,	"o"	},
				      { "ograve",	 16*15+2,	"o"	},
				      { "ordf",	 16*10+10,	""	},
				      { "ordm",	 16*11+10,	""	},
				      { "oslash",	 16*15+8,	"o"	},
				      { "otilde",	 16*15+5,	"o"	},
				      { "ouml",	 16*15+6,	"oe"	},
				      { "para",	 16*11+6,	""	},
				      { "plusmn",	 16*11+1,	""	},
				      { "pound",	 16*10+3,	""	},
				      { "quot",	 16*2+2,	"\""	},
				      { "raquo",	 16*11+11,	""	},
				      { "reg",	 16*10+14,	""	},
				      { "sect",	 16*10+7,	""	},
				      { "shy",	 16*10+13,	""	},
				      { "sup1",	 16*11+9,	""	},
				      { "sup2",	 16*11+2,	""	},
				      { "sup3",	 16*11+3,	""	},
				      { "szlig",	 16*13+15,	"ss"	},
				      { "thorn",	 16*15+14,	""	},
				      { "times",	 16*13+7,	""	},
				      { "uacute",	 16*15+10,	"u"	},
				      { "ucirc",	 16*15+11,	"u"	},
				      { "ugrave",	 16*15+9,	"u"	},
				      { "uml",	 16*10+8,	""	},
				      { "uuml",	 16*15+12,	"ue"	},
				      { "yacute",	 16*15+13,	"y"	},
				      { "yen",	 16*10+5,	""	},
				      { "yuml",	 16*15+15,	"y"	}
				      };



static boolean fdConvEnt( const RString& sgml, int& pos) {	
     int low = 0, high = numents;			      
     for (;;) {								      
	  if (low == high) {						      
	       pos = low;						      
	       return false;						      
	  }								      
	  int mid = (low + high)/2;					      
	  if (sgml == convertTable[mid].sgml) {					      
	       pos = mid;						      
	       return true;						      
	  }								      
	  if (sgml < convertTable[mid].sgml)					      
	       high = mid;						      
	  else								      
	       low = mid + 1;						      
     }									      
}

static boolean lowTableInitialized = false;
	
static void initLowTable() {	
     if (lowTableInitialized)
	  return;
     int i;
     for (i = 0; i < 256; i++) {
	  lowTable[i] = nil;
     }
     for (i = 0; i < numents; i++) {
	  if (convertTable[i].low && *(convertTable[i].low))
	       lowTable[(unsigned char)convertTable[i].iso] = convertTable[i].low;
     }
     lowTableInitialized = true;
}	


const RString RString::lambda_;

RString::RString():
StringRepPtr(StringRep::lambda())
{

}

RString::~RString() 
{
}


RString::RString( const RString &init) 
:StringRepPtr(init.ptr())
{
}

RString::RString( const char* s)
:StringRepPtr( (!s || !*s) ? (StringRep::lambda().ptr()):(HGNEW( StringRep(s))))
{
}

RString::RString( const char* s, int len) 
:StringRepPtr( (!s || !len) ? (StringRep::lambda().ptr()):(HGNEW(StringRep(s,len))))
{
}

RString::RString( const RString& s1, const RString& s2) 
:StringRepPtr(HGNEW(StringRep(*s1.ptr(),*s2.ptr())))
{
//     rep_ = new StringRep(*(s1.rep_), *(s2.rep_));
}

RString& RString::operator = ( const RString& str) {
     if (ptr() == str.ptr())
	  return *this;
     
     StringRepPtr::operator = (str);
     return *this;
}

RString& RString::operator = ( const char* s) {
    // free or not free ?
    if (!s || !*s)
        StringRepPtr::operator = (StringRep::lambda());
    else
    {
      if (!ptr()->usedOnlyOnce())
        StringRepPtr::operator= (HGNEW(StringRep(s, ::strlen(s))));
      else
      {
         ptr()->reset();
         ptr()->write(s);
         ptr()->write('\0');
      }
    }
    return *this;
    
//     RString tmp(s);
//     return operator=(tmp);
}

RString& RString::operator +=( const RString& str) {
    if (!ptr()->usedOnlyOnce())
        StringRepPtr::operator= (HGNEW(StringRep(ptr()->data(),ptr()->length())));
    // this is tricky because of the \0 at the end;
    ptr()->fixup(ptr()->length(),str.ptr()->data(),str.ptr()->count());
//    ptr()->write('\0') ; we dont need this, because count() is with \0
    return *this;
}

RString& RString::operator +=( const char * str) {
  if (!str || !*str)
    return *this;
  if (!ptr()->usedOnlyOnce())
    StringRepPtr::operator= (HGNEW(StringRep(ptr()->data(),ptr()->length())));
  // this is tricky because of the \0 at the end;
  ptr()->fixup(ptr()->length(),str,::strlen(str));
  ptr()->write('\0') ; 
  return *this;
}
   
int RString::index( char c) const {
     return index( 0, c);
}

int RString::index( int start, char c) const {
     if (start < 0)
	  start = 0;
     else if (start > length())
	  start = length();
     const char* p = ::mmemchr( &ptr()->data()[start], c, length() - start);
     if (!p)
	  return -1;
     return p - ptr()->data() ;
//     return p - rep_->str_;
}

int RString::indexa( const char* str) const {
     return indexa( 0, RString( str));
}

int RString::indexa( int start, const char* str) const {
     return indexa( start, RString( str));
}

int RString::indexa( const RString& str) const {
     return indexa( 0, str);
}

int RString::indexa( int start, const RString& str) const {
     if (start < 0)
	  start = 0;
     else if (start > length())
	  start = length();
     // 0 <= start <= length()

     // testing search area
     int len = length() - start;
     if (!len) 
	  return -1;
     const char* string = &ptr()->data()[start];
     // len > 0
     
     const char* word = str.string();
     int wordlen = str.length();
     if (!wordlen)
	  return -1;
     // wordlen > 0

     // search for str in search-area
     const char *s, *p;
     for(;;) {
	  // string ... pointer to search-area
	  // len    ... length of the search-area
	  // word   ... pointer tor the search string
	  if(!(p = ::mmemchr( string, *word, len))) // search for the first character of word
	       return -1;		      // no, does not occur
	  string = ++p;			      // new search-area
//	  len = length() - (string - rep_->str_); // adjust len
	  len = length() - (string - ptr()->data()); // adjust len
	  s = word + 1;			      // compare word
	  int i, j;
	  for (i = len, j = wordlen - 1;  // max. length to compare
	       i > 0 && j > 0 && *p == *s;    // compare and test length
	       i--, j--, s++, p++);	      // skip
	  if (j == 0)
	       return p - ptr()->data(); // ok, word found
//	       return p - rep_->str_; // ok, word found
	  if (i == 0)
	       return -1;	// no, end of search-area
     }
}

RString RString::gSubstrDelim( int index, char delim) const {
     if (index > ptr()->length()) 
	  return RString();
     if (index < 0)
	  index = 0;
     const char* subst = &ptr()->data()[index];
     const char* p = ::mmemchr( subst, delim, length() - index);
     if (p)
	  return RString( subst, p - subst);
     else
	  return RString( subst);
}

RString RString::gSubstrIndex(int begin, int end) const {
     if (!length())
	  return RString();
     if (begin > end)
	  return RString();
     if (begin >= ptr()->length()) 
	  return RString();
     if (end < 0)
	  return RString();
     if (begin < 0)
	  begin = 0;
     if (end >= ptr()->length())
	  end = ptr()->length() - 1;
     return RString( &ptr()->data()[begin], end - begin + 1);
}

RString RString::gRight(int begin) const {
     if (!length())
          return RString();
     if (begin >= ptr()->length()) 
	  return RString();
     if (begin < 0)
	  begin = 0;
      return RString( &ptr()->data()[begin], ptr()->length() - begin);
}

RString& RString::invert() {
    if (!ptr()->usedOnlyOnce())
        StringRepPtr::operator=(HGNEW(StringRep( ptr()->data(), ptr()->length())));
    int size = ptr()->size();
    int len = ptr()->count();
    char* buffer = ptr()->freeze();
    
    // start at len-1 because count counts from 1
    // we start at len-2 because len-1 is a null byte
    for (int i = len-2; i >= 0; i--)
        buffer[i] = ~(buffer[i]) + 1;	// 256 - *p
    
    ptr()->init(len,buffer,size);
    return *this;
}

RString& RString::toLower() {
    if (!ptr()->usedOnlyOnce())
        StringRepPtr::operator=(HGNEW(StringRep( ptr()->data(), ptr()->length())));
    int size = ptr()->size();
    int len = ptr()->count();
    char* buffer = ptr()->freeze();
    
     for(char* p=buffer; *p; p++)
         *p = tolower(*p);
    
    ptr()->init(len,buffer,size);
    return *this;
}

RString& RString::subst(char a, char b) { // replace all a with b
    if (!ptr()->usedOnlyOnce())
        StringRepPtr::operator=(HGNEW(StringRep( ptr()->data(), ptr()->length())));
    int size = ptr()->size();
    int len = ptr()->count();
    char* buffer = ptr()->freeze();

    // we don't care about the trailing null byte -> len-1
    int i = len-1;
     for(char* p = buffer; i; p++, i--)
	  if (*p == a)
              *p = b;
     
     ptr()->init(len,buffer,size);
     return *this;
}

// replace all occurences of "from" with "to"
RString& RString::subst( const RString& from, const RString& to, boolean caseInSens) {
     int fromlength = from.length();
     if (!length() || !fromlength) 
	  return *this;

     RString comparestring = *this;
     RString fromstring = from;
     if (caseInSens) {
	  comparestring.toLower();
	  fromstring.toLower();
     }
     int index = 0;
     int where;
     RString tmp;
     while ((where = comparestring.indexa( index, fromstring)) != -1) {
	  tmp += gSubstrIndex( index, where - fromlength - 1) + to;
	  index = where;
     }
     if (index <= length()) {
	  tmp += gRight( index);
     }
     *this = tmp;
     return *this;
}

// replace all occurences of "from" in "in" with "to" if from is surrounded by a delim or 
// the word is at the beginning or the end and a delim on the other side

RString& RString::subst( const RString& from, const RString& to,
			const RString& delim, boolean caseInSens) {
     int fromlength = from.length();
     if (!length() || !fromlength) 
	  return *this;
     
     RString comparestring = *this;
     RString fromstring = from;
     RString delimstring = delim;
     if (caseInSens) {
	  comparestring.toLower();
	  fromstring.toLower();
	  delimstring.toLower();
     }

     int index = 0;
     int where;
     RString tmp;
     while ((where = comparestring.indexa( index, fromstring)) != -1) {
	  // from found
	  // ("from..." || "delim from...") && ("...from" || "...from delim...")
	  if (((where - fromlength == 0) || 
	       (delimstring.index( comparestring[where-fromlength-1]) != -1)) &&
	      (((where == length()) || (delimstring.index( comparestring[where]) != -1)))) 
	  {
	       tmp += gSubstrIndex( index, where - fromlength - 1) + to;
	  }
	  else 
	  {
	       tmp += gSubstrIndex( index, where-1);
	  }
	  index = where;
     }
     if (index <= length())
     {
	  tmp += gRight( index);
     }
     *this = tmp;
     return *this;
}

RString& RString::ignMultChars( char c) {
     // we need this also in the for loop (after freeze() nothing is valid!)
     int reallen = length();
     if (!reallen)
	  return *this;
     if (!ptr()->usedOnlyOnce())
         StringRepPtr::operator=(HGNEW(StringRep( ptr()->data(), ptr()->length())));
     int size = ptr()->size();
     int len = ptr()->count();
     char* buffer = ptr()->freeze();
     
     register char* from = buffer;
     register char* to = from;
     boolean gotIt = false;
     for ( int i = reallen; i; i--) {
	  if (*from == c) {
	       if (!gotIt) {
		    *to++ = *from;
		    gotIt = true;
	       }
	  }
	  else {
	       *to++ = *from;
	       gotIt = false;
	  }
	  from++;
     }
     *to = '\0';
//     ptr()->count() = to - ptr()->data();
     // we need the +1 for the trailing null byte
     ptr()->init(to-buffer+1,buffer,size);
     return *this;
}

RString& RString::sgml2Iso() {
   if (index( '&') == -1) // no SGML entities;
      return *this;
   else {                   // replace SGML entities by ISO characters
      RString out, part, ent;
      int pos = 0;
      boolean word = true;
      do {
	 out += part;
	 if (operator[]( pos) == '&') {
	    // delimiters: ';' (to remove), ' ', '\n', '\t', '\0'
	    int ndx = pos + 1;
	    for ( ; operator[](ndx); ndx++) {
	       register char c = operator[](ndx);
	       if (c == ' ' || c == '\n' || c == '\t' || c == ';')
		  break;
	    }
	    ent = gSubstrIndex( pos + 1, ndx -1);
	    if (ent[0] == '#') {
	       // "&#digits;
	       char* end_ptr = (char*)(ent.string()) +1;
	       long tpos = strtol(ent.string() +1, &end_ptr, 10);
	       if (*end_ptr == '\0') {
		  if (tpos >= 0 && tpos < 256 && isoCharReferenceTable[tpos]) {
		     register char c = (char)tpos;
		     out += RString( &c, 1);
		     if (operator[](ndx) == ';')
			pos += ent.length() + 2;
		     else
			pos += ent.length() + 1;
		  }
		  else {
		     // wrong value
		     out += "&";
		     pos++;
		  }
	       }
	       else if (ent == "#TAB") {
		  out += RString("\t", 1);
		  if (operator[](ndx) == ';')
		     pos += ent.length() + 2;
		  else
		     pos += ent.length() + 1;
	       }
	       else if (ent == "#SPACE") {
		  out += RString(" ", 1);
		  if (operator[](ndx) == ';')
		     pos += ent.length() + 2;
		  else
		     pos += ent.length() + 1;
	       }
	       else {
		  // wrong value
		  out += "&";
		  pos++;
	       }
	    }
	    else {
	       // "&Uuml;"
	       int tpos;
	       if (fdConvEnt( ent, tpos)) {
		  out += RString( &(convertTable[tpos].iso), 1);
		  if (operator[](ndx) == ';')
		     pos += ent.length() + 2;
		  else
		     pos += ent.length() + 1;
	       }
	       else {
		  out += "&";
		  pos++;
	       }
	    }
	    part = lambda_;
	    continue;
	 }
	 word = gWordChar( pos, '&', part);
      } while (word);
      operator=( out);
   }
   return *this;
}

RString& RString::sgmlIso2LowerAscii() {
   initLowTable();
   RString out, part, ent;
   int pos = 0;
   boolean word = true;
   do {
      out += part.toLower();	// ignored if part is empty
      if (operator[]( pos) == '&') {
	 // entity reference ?
	 // delimiters: ';' (to remove), ' ', '\n', '\t', '\0'
	 int ndx = pos + 1;
	 for ( ; operator[](ndx); ndx++) {
	    register char c = operator[](ndx);
	    if (c == ' ' || c == '\n' || c == '\t' || c == ';')
	       break;
	 }
	 // ent should be the entity
	 ent = gSubstrIndex( pos + 1, ndx -1);
	 if (ent[0] == '#') {
	    // direct entity reference
	    // "&#digits;
	    char* end_ptr = (char*)(ent.string()) +1;
	    long tpos = strtol(ent.string() +1, &end_ptr, 10);
	    if (*end_ptr == '\0') {
	       if (tpos >= 0 && tpos < 256 && isoCharReferenceTable[tpos]) {
		  // valid character reference
		  if (lowTable[ tpos]) {
		     // with "lower" representation
		     out += lowTable[tpos];
		     if (operator[](ndx) == ';')
			pos += ent.length() + 2;
		     else
			pos += ent.length() + 1;
		     part = lambda_;
		     continue;	// test next character after reference
		  }
		  else if (isoDelimTable[tpos]) {
		     // without "lower" representation, not an ASCII
		     // ignore this reference
		     if (operator[](ndx) == ';')
			pos += ent.length() + 2;
		     else
			pos += ent.length() + 1;
		     part = lambda_;
		     continue;	// test next character after reference
		  }
		  else {
		     // without "lower" representation, ASCII
		     // insert lower character
		     register char c = (char)tpos;
		     RString one_char(&c,1);
		     out += one_char.toLower();
		     if (operator[](ndx) == ';')
			pos += ent.length() + 2;
		     else
			pos += ent.length() + 1;
		     part = lambda_;
		     continue;	// test next character after reference
		  }
	       }
	       else {
		  // invalid character reference
		  // insert "&"
		  out += "&";
		  pos++;
		  part = lambda_;
		  continue;	// test next character
	       }
	    }
	    else if (ent == "#TAB") {
	       out += RString("\t",1);
	       if (operator[](ndx) == ';')
		  pos += ent.length() + 2;
	       else
		  pos += ent.length() + 1;
	       part = lambda_;
	       continue;	// test next character after reference
	    }
	    else if (ent == "#SPACE") {
	       out += RString(" ",1);
	       if (operator[](ndx) == ';')
		  pos += ent.length() + 2;
	       else
		  pos += ent.length() + 1;
	       part = lambda_;
	       continue;	// test next character after reference
	    }
	    else {
	       // invalid character reference
	       // insert "&"
	       out += "&";
	       pos++;
	       part = lambda_;
	       continue;	// test next character
	    }
	 }
	 else {
	    // indirect entity reference
	    // e.g. "&Uuml;"
	    int tpos;
	    if (length() > ent.length() + pos + 1 && fdConvEnt( ent, tpos)) {
	       // "lower" representation found - insert it
	       out += convertTable[tpos].low;
	       if (operator[](ndx) == ';')
		  pos += ent.length() + 2;
	       else
		  pos += ent.length() + 1;
	    }
	    else {
	       // not a valid indirect reference - insert "&"
	       out += "&";
	       pos++;
	    }
	    part = lambda_;
	    continue;		// test next character (after reference)
	 }
      }
      else
	 // current character != '&'
	 if (operator[]( pos)) {
	    // current character != '\0'
	    if (lowTable[(unsigned char)operator[]( pos)]) {
	       // current character has a "lower" representation (ISO)
	       // insert "lower" representation
	       out += lowTable[(unsigned char)operator[]( pos)];
	       pos++;
	       part = lambda_;
	       continue;	// test next character
	    }
	    else if (isoDelimTable[(unsigned char)operator[]( pos)]) {
	       // current character is not an ASCII (excluding '&') -> ignore
	       pos++;
	       part = lambda_;
	       continue;	// test next character
	    }
	    // else: current character is an ASCII -> read next word (including current character)
	 }
      // Attention: call this function only when there are no leading isoDelimTable-characters
      word = gWordTable( pos, isoDelimTable, part);
   } while (word);
   *this = out;
   return *this;
}

RString& RString::toUpper() {
     if (!ptr()->usedOnlyOnce())
         StringRepPtr::operator=(HGNEW(StringRep( ptr()->data(), ptr()->length())));
     int size = ptr()->size();
     int len = ptr()->count();
     char* buffer = ptr()->freeze();
     for(char* p = buffer; *p; p++)
	  *p = toupper(*p);
     ptr()->init(len,buffer,size);
     return *this;
}

RString& RString::dos2Unix() {
     int i = length();
     int crCount = 0;
     for (const char* p = ptr()->data(); i; p++, i--)
	  if (*p == 13)
	       crCount++;
     if (crCount) {
	  char* out = new char[length() - crCount + 1];
	  char* q = out;
	  i = length();
	  for (const char* p = ptr()->data(); i; p++, i--)
	       if (*p != 13)
		    *(q++) = *p;
	  *q = '\0';
	  i = length();
          StringRepPtr::operator=(HGNEW(StringRep( out, i - crCount)));
          delete out;
     }
     return *this;
}     

RString& RString::unix2Dos() {
     int i = length();
     int nlCount = 0;
     for (const char* p = ptr()->data(); i; p++, i--)
	  if (*p == 10)
	       nlCount++;
     if (nlCount) {
	  char* out = new char[length() + nlCount + 1];
	  char* q = out;
	  i = length();
	  for (const char* p = ptr()->data(); i; p++, i--) {
	       if (*p == 10)
		    *(q++) = 13;
	       *(q++) = *p;
	  }
	  *q = '\0';
	  i = length();
          StringRepPtr::operator=(HGNEW(StringRep( out, i + nlCount)));
	  delete out;
     }
     return *this;
}

boolean RString::gWord( int& index, const RString& white, RString& word) const {
     if (white != wsChars) {
	  const char* p;
	  for (p = wsChars.string(); *p; p++) 
	       whiteTable[(unsigned char)*p] = 0;
	  for (p = white.string(); *p; p++) 
	       whiteTable[(unsigned char)*p] = 1;
	  wsChars = white;
     }	  

     return gWordTable( index, whiteTable, word);
}
	 
boolean RString::gWordTable( int& index, const char white[], RString& word) const {
     if ( (index >= length()) || (index < 0) )
	  return false;

     const char* p;
     for (p = ptr()->data() + index; *p; p++) {
	  if (!(white[(unsigned char)*p]))
	       break;
     }
 
     // p points to end of string or at the first non-whitespace
     if (!*p)
	  return false;
     // ok, p points to non-whitespace
     const char* beg = p; // mark the begin
     for (; *p; p++)
	  if (white[(unsigned char)*p])
	       break;

     // p points to end of string or at the first whitespace after the word.
     index = p - ptr()->data();
     word = RString( beg, p - beg);
     return true;
}

boolean RString::gWordChar( int& index, const char white, RString& word) const {
     if ( (index >= length()) || (index < 0) )
	  return false;

     const char* p;
     for (p = ptr()->data() + index; *p; p++) {
	  if (*p != white)
	       break;
     }
 
     // p points to end of string or at the first non-whitespace
     if (!*p)
	  return false;
     // ok, p points to non-whitespace
     const char* beg = p; // mark the begin
     for (; *p; p++)
	  if (white == *p)
	       break;

     // p points to end of string or at the first whitespace after the word.
     index = p - ptr()->data();
     word = RString( beg, p - beg);
     return true;
}
    
RString& RString::gLine( istream& inp, char delim) {
  char lineBuf[lineBufSize];
     RString line;
     while (inp) {
	  lineBuf[0] = '\0';
	  inp.get( lineBuf, lineBufSize - 1 , delim);
	  if (inp) {
	       char n = inp.get();
	       if (!inp || n == delim) {
		    line += lineBuf;
		    break;
	       }
	       else {
		    lineBuf[lineBufSize - 2] = n;
		    lineBuf[lineBufSize - 1] = '\0';
		    line += lineBuf;
	       }
	  }
	  else {
	       line += lineBuf;
	       break;
	  }
     }
     return operator=( line);
}

int RString :: uchar_strcmp (const char* s1, const char* s2) {
   int diff ;
   unsigned char c1, c2 ;
   while ((diff = (c1 = *s1) - (c2 = *s2)) == 0) {
      if (! (c1 || c2))
         return 0 ;
      s1++ ;
      s2++ ;
   }
   return diff ;
}

