#include <ctype.h>
#include <direct.h>
#include <stdio.h>
#include <stdarg.h>
#include <stdlib.h>
#if XVT_OS == XVT_OS_WIN || XVT_OS == XVT_OS_WIN32
#include <strstrea.h>
#else
#include <strstream.h>
#endif

#include <regexp.h>
#include <strings.h>
#include <prefix.h>
#include <utility.h>

#if XVT_OS == XVT_OS_WIN || XVT_OS == XVT_OS_WIN32
#include <process.h>  
#else
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#endif

// Most descriptions will fit in fifty characters
const int DEFAULT_SIZE = 50;

#ifdef FOXPRO
class TString512 : public TFixed_string
{
  char _str512[513];

public:
  TString512() : TFixed_string(_str512, 513) { }
  const TString& operator =(const char* s);
  const TString& operator =(const TString& s) { return operator =((const char*)s); }
  void strncpy(const char* s, int n);
};

#else

class TString512 : public TString
{
public:
  TString512() : TString(512) { }
  const TString& operator =(const char* s);
  const TString& operator =(const TString& s) { return operator =((const char*)s); }
  void strncpy(const char* s, int n);
};

#endif

const TString& TString512::operator =(const char* s) 
{ 
#ifdef FOXPRO
  if (_size == 0)
  {
    _str = _str512;
    _size = 512;
  }  
#endif
  return set(s); 
}

void TString512::strncpy(const char* s, int n)
{
#ifdef FOXPRO
  if (_size == 0)
  {
    _str = _str512;
    _size = 512;
  }  
  TFixed_string::strncpy(s, n);
#else
  if (n > _size) 
    resize(n, FALSE);
  ::strncpy(_str, s, n);
  _str[n] = '\0';
#endif
}

HIDDEN TString512 spark; 

inline bool is_space(char c)
{ return c >= '\t' && c <= ' '; }

// Certified 99%
// @doc EXTERNAL
//
// @mfunc Cambia la dimensione della stringa eventualmente preservandone il contenuto iniziale
void TString::resize(
  int size,  // @parm Nuova dimensione della stringa
  bool cpy)  // @parm Se TRUE mantiene il contenuto della stringa e alloca
  //       nuovo spazio

  // @comm Non funziona con le stringhe static e per valori negativi di <p size>
{                     
  CHECKD(size >= 0, "Invalid string resize ", size);
  char* s = new char[size+1];
  if (cpy && _str) strcpy(s, _str);
  else *s = '\0';

  if (_str)
    delete _str;

  _str  = s;
  _size = size;
}

// Certified 99% (uses resize)
// @doc EXTERNAL
//
// @mfunc Inizializza con la stringa puntata da char* di lunghezza size
//        (usa <mf TString::resize>)
TString& TString::set(
  const char* s) // @parm Stringa da inizializzare

  // @rdesc Ritorna l'indirizzo della stringa inizializzata
{
  if (s == NULL) s = "";
  const int sz = *s ? strlen(s) : 15;
  if (sz > size()) resize(sz, FALSE);
  strcpy(_str, s);

  return *this;
}
// @doc EXTERNAL

// @mfunc Espande la stringa per altri caratteri
int TString::make_room(
  int s) // @parm Numero di caratteri di cui si cuole aspandere la stringa

  // @comm La stringa viene espansa di un numero di caratteri doppio (per sicurezza) di 
  //       quello passato per parametro
  //
  // @rdesc Ritorna il numero di caratteri della stringa prima della chiamata
{
  const int lun = len();
  const int spare = size() - lun;
  if (spare < s)
    resize(3 * (size() + s) / 2, TRUE);
  return lun;
}

TString::TString(const char* s) : _str(NULL), _size(0)
{ set(s); }

TString::TString(const TString& s) : _str(NULL), _size(0)
{ set(s); }

TString::TString(int size, char c) : _str(NULL), _size(0)
{ 
/*
  resize(size, FALSE); 
  *(_str+size--)='\0';
  for (;c && size>=0; size--)
    *(_str+size)=c;
*/    
  if (size > 0)
    fill(c, size);   // Guy: Much simpler and faster (uses memset)
  else
    resize(DEFAULT_SIZE, FALSE);
}

TString::TString() : _str(NULL), _size(0)
{ resize(DEFAULT_SIZE, FALSE); }

TString::~TString()
{
  if (_str)
    delete _str;
}

char TString::shift(int n)
{
  CHECK(n>0,"Scorrimento a destra delle stringhe non ancora supportato");
  CHECK(n<_size,"Errore di scorrimento");
  char r=*(_str+n-1);
  if (n)
  {
    char *c=_str;
    while (*c)
    {
      *(c)=*(c+n);
      c++;
    }
  } 
  return r;
}


TString& TString::operator <<(const char* s)
{
  if (s && *s)
  {
    const int pos = make_room(strlen(s));
    strcpy(&_str[pos], s);
  }
  return *this;
}

TString& TString::operator <<(char c)
{
  int pos = make_room(1);
  _str[pos++] = c;
  _str[pos] = '\0';
  return *this;
}


TString& TString::operator <<(int n)
{                          
  char s[16];
  sprintf(s, "%d", n);
  return operator <<(s);
}


TString& TString::operator <<(long n)
{
  char s[16];
  sprintf(s, "%ld", n);
  return operator <<(s);
}

TString& TString::operator <<(double n)
{
  char s[32];
  sprintf(s, "%lf", n);
  return operator <<(s);
}

// Appends an object to the string
// Certified 50%
// The object should be completely storable in spark
TString& TString::operator <<(const TObject& obj)
{
  ostrstream out((char*)(const char*)spark, spark.size());
  obj.print_on(out);
  out << ends;
  return operator <<(spark);
}


TString& TString::operator <<(const TString& str)
{ return operator <<(str._str); }


// @doc EXTERNAL

// @mfunc Elimina tutti i caratteri contenuti in <p k>
TString& TString::strip(
  const char* k) // @parm Stringa da controllarne l'esitenza nell'oggetto

{
  int j = 0;
  for (int i = 0; _str[i]; i++)
  {
    char c = _str[i];
    if (strchr(k, c) == NULL) _str[j++] = c;
  }
  return cut(j);
}


bool TString::blank() const
{
  for (int i = 0; _str[i]; i++)
    if (!is_space(_str[i])) 
      return FALSE;
  return TRUE;
}

TString& TString::strip_spaces()
{
  char instring = '\0';
  int j = 0;
  for (int i = 0; _str[i]; i++)
  {
    char c = _str[i];
    if (is_space(c) && !instring) continue;
    if (c == '"' || c == '\'')
    {
      if (instring == c) instring = '\0';
      else
        if (instring == '\0') instring = c;
    }
    _str[j++] = c;
  }
  _str[j] = '\0';
  return *this;
}

TString& TString::strip_d_spaces()
{
  int j = 0;       
  bool spc = FALSE;
  for (int i = 0; _str[i]; i++)
  {
    char c = _str[i];
    if (is_space(c))
      if (spc)      
        continue;
      else
        spc = TRUE;
    else
      if (spc) spc = FALSE;
    _str[j++] = c;
  }
  _str[j] = '\0';
  return *this;
}

// Certified 100%
const char* TString::class_name() const
{ return "String"; }

// Certified 100%
word TString::class_id() const
{ return CLASS_STRING; }

// @doc EXTERNAL

// @mfunc Duplica una stringa
TObject* TString::dup() const

// @comm Alloca nuovo spazio per duplicare la stringa
//
// @rdesc Ritorna il puntatore alla stringa duplicata
{
  TString* s = new TString(size());
  s->set(_str);
  return s;
}


void TString::read_from(istream& in)
{
  in >> __tmp_string;
  set(__tmp_string);
}


// Certified 100%
void TString::print_on(ostream& out) const
{ out << _str; }


// Certified 100%
// @doc EXTERNAL

// @mfunc Ritorna la posizione del carattere o della stringa nell'oggetto TString
//
// @rdesc Ritorna i seguneti parametri:
//
// @flag <gt>= 0 | Posizione dell'elemento nella stringa
// @flag -1 | L'elemento non e' stato trovato
int TString::find(
  char c,          // @parm Carattere da cercare
  int from) const  // @parm Posizione da cui iniziare la ricerca
  // @parm const char* | s | Stringa da cercare

  // @syntax find(char c, int from);
  // @syntax find(const char* s, int from);
  //
  // @comm Cerca nella stringa, dalla posizione indicata, l'elemento passato.
  //       Nel caso <p from> sia maggiore della lunghezza della stringa manda
  //       un messaggio di errore
{
  CHECKD(from <= len(), "Trying to find past end of string", from);
  const char* p = strchr(_str + from, c);
  return p ? int(p - _str) : -1;
}


#if XVT_OS == XVT_OS_SCOUNIX
HIDDEN char* strstr(const char* string1, const char* string2)
{
  const int len = strlen(string2);
  while (*string1)
  {
    if (strncmp(string1, string2, len) == 0) return (char*) string1;
    string1++;
  }
  return NULL;
}
#endif


// Certified 100%
int TString::find(const char* s, int from) const
{
  CHECKD(from <= len(), "Trying to find past end of string", from);
  const char* p = strstr(_str + from, s);
  return p ? int(p - _str) : -1;
}

bool TString::match(const char* pat) const
{ 
  return ::match(pat, _str); 
}


int TString::replace(char find_char, char replace_char)
{
  const int l = len();
  int n = 0;
  for (int i = 0; i < l; i++)
    if (_str[i] == find_char)
    {
      _str[i] = replace_char;
      n++;
    }
  return n;
}


// Certified 99%
// @doc EXTERNAL

// @mfunc Ritorna l'oggetto TString composto dai <p count> caratteri da sinistra
const TString& TString::left(
  int count) const // @parm Indica fino quale carattere restituire la stringa

  // @rdesc Ritorna l'indirizzo della stringa contenente i <p count> caratteri da sinistra
{
  spark.strncpy(_str, count);
  return spark;
}

// Certified 99%
// @doc EXTERNAL

// @mfunc Ritorna l'oggetto TString composto dai <p count> caratteri da destra
const TString& TString::right(
  int count) const // @parm Indica da quale carattere restituire la stringa

  // @rdesc Ritorna l'indirizzo della stringa contenente i <p count> caratteri da destra
{
  int from = len()-count;
  if (from < 0) from = 0;
  spark = _str + from;
  return spark;
}


// Certified 100%
// @doc EXTERNAL

// @mfunc Ritorna l'oggetto TString composto dai <p count> caratteri a partire
//        da <p from>
const TString& TString::mid(
  int from,         // @parm Posizione dalla quale partire per l'estrazione
  int count) const  // @parm Numero di caratteri da estrarre

  // @rdesc Ritorna l'indirizzo della stringa contenente i <p count> cartteri da <p from>
{
  const int l = len();

#ifdef DBG
  if (from < 0)
  {
    NFCHECK("Ivalid MID parameter: from = %d", from);
    from = 0;
  }
#endif
  if (from > l) from = l;
  if (count < 0 || from+count>l) 
    count = l-from;
  spark.strncpy(&_str[from], count);
  return spark;
}


// Certified 100%
// @doc EXTERNAL

// @mfunc Ritorna la stringa da <p from> a <p to> (escluso)
const TString&  TString::sub(
  int from,     // @parm Posizione dalla quale estrarre la stringa
  int to) const // @parm Posizione fin alla quale estrarre la stringa

  // @rdesc Ritorna l'indirizzo della stringa da i <p from> a <p to>
{
  const int count = to-from;
  return mid(from, count);
}

// Certified 100%
TString& TString::cut(int n)
{                
  CHECKD(n >= 0, "Invalid TString::cut position ", n);
  if (n <= _size)
    _str[n] = '\0';
  else
    NFCHECK("Hai tagliato a %d la stringa '%s' lunga %d(%d)",
            n, _str, len(), size());
  return *this;
}

// @doc EXTERNAL
// @mfunc Espande la stringa fino alla lunghezza indicata, eventualmente aggiungendo spazi a destra
TString& TString::rpad(const int n,const char c)
{
  const int & l=  len();

  if (n>l)
  {
    if (n > size()) resize(n, TRUE);
    memset(_str+l, c, n-l);
    _str[n] = '\0';
  } 
  return *this;
}

// @doc EXTERNAL
// @mfunc Espande la stringa fino alla lunghezza indicata, eventualmente aggiungendo spazi a sinistra 
TString& TString::lpad(const int n,const char c)
{
  int l=len();
  const int nsp=n-l;
  if (n>l)
  {
    if (n > size()) resize(n, TRUE);
      for (l--; l>=0; l--)
        *(_str+l+nsp)=*(_str+l);
    memset(_str, c, nsp);
    _str[n] = '\0';
  } 
  return *this;
}

// @doc EXTERNAL

// @mfunc Elimina gli spazi da sinistra o i primi n caratteri (da sinistra).
TString& TString::ltrim(
  int count) // @parm Indica il numero di caratteri da eliminare.
  //       Se uguale a 0 elimina tutti gli spazi da sinistra

  // @comm Controlla se <p count> e' 0. Se non lo � ritorna l'indirizzo della
  //       stringa a partire dal <p count>-esimo carattere; altrimenti controlla
  //       se i primi caratteri sono spazi e li elimina fino a che non trova
  //       un carattere diverso da ' '
  //
  // @xref <mf TString::rtrim>
{
  const char* s;

  if (count > 0)
  {
    if (count >= len()) return cut(0);
    s = &_str[count];
  }
  else for (s = _str; *s && is_space(*s); s++);

  if (s != _str) strcpy(_str, s);
  return *this;
}

// @doc EXTERNAL

// @mfunc Elimina gli spazi da destra o i primi n caratteri (da destra).
TString& TString::rtrim(
  int count)  // @parm Indica il numero di caratteri da eliminare.
  //       Se uguale a 0 elimina tutti gli spazi da destra

  // @comm Controlla se <p count> e' 0. Se non lo � pone il fine stringa alla
  //       posizione <p count>; altrimenti controlla se gli ultimi caratteri
  //       sono spazi e li elimina fino a che non trova un carattere diverso da ' '
  //
  // @xref <mf TString::ltrim>
{
  if (count > 0)
  {
    int i = len() - count;
    if (i < 0) i = 0;
    cut(i);
  }
  else
  {
    char* good = _str-1;
    for (char* s = _str; *s; s++) 
      if (!is_space(*s)) good = s;
    *(good+1) = '\0';
  }
  return *this;
}

TString& TString::trim()
{
  char* last = _str;

  // Salta spazi iniziali
  for (const char* s = _str; *s && is_space(*s); s++);

  // Copia stringa
  if (s > _str)
  {
    for(char* c = _str; *s; s++)
    {
      *c++ = *s;
      if (!is_space(*s)) last = c;
    }
    // Elimina spazi finali
    *last = '\0';
  }
  else rtrim();

  return *this;
}

// Certified 50%
// @doc EXTERNAL

// @mfunc Compara due stringhe (o i primi <p max> caratteri)
//
// @rdesc Ritrna i seguenti valori:
//
// @flag 0 | Se le stringhe sono uguali
// @flag <gt><lt>0 | Se le stringhe sono diverse
int TString::compare(
  const char* s,          // @parm Stringa da comparare
  int max,                // @parm Numero di caratteri da conforntare (default tutta la stringa)
  bool ignorecase) const  // @parm Ignorare la differenza maiuscolo/minuscolo (default FALSE)
{ 
  int res; 
  if (ignorecase)
  {
    if (max < 0) 
      res = stricmp(_str, s);
    else
    {    
      for (int i = 0; i < max; i++)
      {
        res = toupper(_str[i]) - toupper(s[i]);
        if (res) break;
      }  
    }
  }  
  else
    res =  max < 0 ? strcmp(_str, s) : strncmp(_str, s, max);
  
  return res;
}

// Certified 100%
// @doc EXTERNAL

// @mfunc Riempe la stringa con n caratteri c
//
// @rdesc Ritorna l'indirizzo dell stringa
TString& TString::fill(
  char c,  // @parm Caratteri con cui riempire la stringa
  int n)   // @parm Numero di caratteri da inserire nella stringa
  //       (default per tutta la lungehzza)

  // @comm Se il paramatro <p n> e' maggiore della dimensione della stringa, la
  //       stessa viene ridimensionata (chaimata alla <mf TString::resize>).
  //       <nl>Nel caso non venga passato il parametro <p n> la stringa viene
  //       riempita col carattere <p c> per tutta la lunghezza.
  
{
  if (n < 0) n = size(); else
    if (n > size()) resize(n, FALSE);
  memset(_str, c, n);
  _str[n] = '\0';
  return *this;
}

// Certified 100% 
// @doc EXTERNAL

// @mfunc Giustifica l'oggetto stringa a destra
TString& TString::right_just(
  int n,  // @parm Numero di colonne alla quali allineare (defualt larghezza della stringa)
  char c) // @parm Carattere di riempimento (default ' ')

  // @comm Nel caso venga passato un carattere in <p c>, questo  viene inserito
  //       nel resto della stringa (a sinistra della stringa stessa)
  //
  // @xref <mf TString::left_just> <mf TString::center_just>
{
  if (n < 0) n = size();
  trim();
  spark = _str;
  fill(c, n);
  overwrite(spark, n-spark.len());

  return *this;
}

// Certified 100% 
// @doc EXTERNAL

// @mfunc Centra l'oggetto stringa
TString& TString::center_just(
  int n,  // @parm Numero di colonne alla quali allineare (defualt larghezza della stringa)
  char c) // @parm Carattere di riempimento (default ' ')

  // @comm Nel caso venga passato un carattere in <p c>, questo  viene inserito
  //       nel resto della stringa (a destra e a sinistra della stringa stessa)
  //
  // @xref <mf TString::left_just> <mf TString::right_just>
{
  if (n < 0) n = size();
  trim();
  spark = _str;
  fill(c, n);
  const int p = (n-spark.len()) >> 1;
  overwrite(spark, p);

  return *this;
}

// Certified 100% 
// @doc EXTERNAL

// @mfunc Giustifica l'oggetto stringa a sinistra
TString& TString::left_just(
  int n,   // @parm Numero di colonne alla quali allineare (defualt larghezza della stringa)
  char c)  // @parm Carattere di riempimento (default ' ')

  // @comm Nel caso venga passato un carattere in <p c>, questo  viene inserito
  //       nel resto della stringa (a destra della stringa stessa)
  //
  // @xref <mf TString::right_just> <mf TString::center_just>
{
  if (n < 0) n = size();
  trim();
  spark = _str;
  fill(c, n);
  overwrite(spark, 0);

  return *this;
}

// @doc EXTERNAL

// @mfunc Formatta una stringa usando il formato dato da <p pic>
TString& TString::picture(
  const char* pic, // @parm Formato della stringa
  const char* s)   // @parm Stringa da formattare 
{
  if (pic == NULL || *pic == '\0')
    return set(s);

  set(pic);

  int l = strlen(s)-1;  // Prossimo carattere da sostituire a # o @

  for (int i = len()-1; i >= 0; i--)
  {
    const char k = pic[i];
    if (k == '#') _str[i] = (l >= 0) ? s[l--] : ' ';
    else if (k == '@') _str[i] = (l >= 0) ? s[l--] : '0';
    else if (k == '^') { _str[i] = ' '; l--; }
  }

  return *this;
}

// Certified 90% (spark size limited)
// @doc EXTERNAL

// @mfunc Manda un output formattato alla stringa oggetto
TString& TString::format(
  const char* fmt,   // @parm Stringa da formattare
  ...)                           // @parmvar Uno o piu' parametri corrispondenti ai codici in <p fmt>

  // @comm Funziona come la funzione "sprintf" standard del C e ritorna la
  //       stringa formattata con i parametri passati.
{
  va_list pars;
  va_start(pars, fmt);
  const int tot = vsprintf((char*)(const char*)spark, fmt, pars);
  va_end(pars);

  CHECK(tot >= 0 && tot < spark.size(), "Ue'! Quanto scrivi?");
  if (tot > size()) resize(tot, FALSE);
  strcpy(_str, spark);

  return *this;
}

// Certified 99%
char* TString::get_buffer(int min_size)
{
  if (min_size > size())
    resize(min_size, TRUE);
  return _str;  
}

// Certified 99%
TString& TString::upper(int from, int to)
{
  for (int c=0; *(_str+c) && (to<0 || c<=to); c++) 
    if (c>=from)
      *(_str+c) = toupper(*(_str+c));
  return *this;
}

// Certified 99%
TString& TString::lower(int from, int to)
{
  for (int c=0; *(_str+c) && (to<0 || c<=to); c++) 
    if (c>=from)
      *(_str+c) = tolower(*(_str+c));
  return *this;
}

// Certified 50%  
// @doc EXTERNAL

// @mfunc Permette di ttrovare il plurale di una stringa
TString& TString::add_plural(
  long num,          // @parm Numero di elementi per sapere la desineneza
  const char* name)  // @parm Stringa da modificare

  // @comm A seconda del numero passato in <p num> permette di stabilire la
  //       corretta sintassi della stringa <p name> da scrivere nei messaggi
  //       dati all'utente. Nel caso <p num> sia 0 allora ritorna "nessuno".
{   
  const TFixed_string n(name);
  const char last = n[n.len()-1];                   

  if (num < 1)
  {
    *this << "nessun";
    if (toupper(last) == 'A' || toupper(n[0]) == 'Z' ||
        toupper(n[0]) == 'S' && strchr("aeiouAEIOU", n[1]) == NULL) 
      *this << tolower(last);
    *this << ' ' << name;
  } 
  else
  {
    *this << num << ' ' << name;
    if (num > 1)
      _str[len()-1] = (last == 'a') ? 'e' : 'i';
  }
  
  return *this;
}

// Certified 90%
// @doc EXTERNAL

// @mfunc Sovrascrive la stringa <p s> dalla posizione <p pos>
TString& TString::overwrite(
  const char* s, // @parm Stringa da inserire
  int pos)       // @parm Posizione dalla quale iniziare a sovrascrivere

  // @comm Sovrascrive dalla posizione <p pos> fino alla lunghezza di <p s> l'oggetto
  //       stringa. Nel caso la dimensione sia maggiore l'oggetto viene riallocato
  //       dinamicamente
  //
  // @xref <mf TString::insert>
{
  if (s) 
  {
    const int l = len();
    if (pos < 0) pos = l;
    const int max = pos+strlen(s);
    if (max > size()) resize(max, TRUE);          // resize needed?

    const bool over = max > l;                    // beyond end of string?
    for (int i = l; i < pos; i++) _str[i] = ' ';  // space padding
    for (; *s; s++) _str[pos++] = *s;             // write
    if (over) _str[pos] = '\0';                   // end of string
  }
  return *this;
}


// Certified 90%  
// @doc EXTERNAL

// @mfunc Inserisce la stringa s dalla posizione pos
TString& TString::insert(
  const char* s,  // @parm Stringa da inserire
  int pos)        // @parm Posizione dalla quale iniziare a inserire

  // @comm Inserisce dalla posizione <p pos> la stringa <p s> nell'oggetto
  //       stringa. Nel caso la dimensione sia maggiore l'oggetto viene riallocato
  //       dinamicamente
  //
  // @xref <mf TString::overwrite>
{
  if (s && *s)
  {
    const int l = strlen(s);
    make_room(l);
    mid(pos);                     // Scrivi in spark la stringa da pos in poi
    overwrite(s, pos);            // Aggiungi s
    strcpy(&_str[pos+l], spark);  // Aggiungi spark
  }
  return *this;
}

// Certified 90%
word TString::hash() const
{
/*
// Villa's megasmart hash function
  word h = 0x0000;
  for (int i = 0; _str[i]; i++)
    h ^= (i & 0x1) ? (_str[i] << 8) : _str[i];
*/                                     
  // Peter Weinberger's (PJW) generic hashing
  word h = 0;
  for (const char* s = _str; *s; s++)
  {
    h = (h << 2) + *s;
    const word i = h & 0xC000;
    if (i) h = (h ^ (i >> 12)) & 0x3FFF;
  }
  return h;
}

///////////////////////////////////////////////////////////
// TFixed_string
///////////////////////////////////////////////////////////

// Certified 100%
TFixed_string::TFixed_string(const char* str, int size)
: TString((char*)str, (size <= 0) ? strlen(str) : size-1)
{            
  CHECK(str, "NULL buffer for fixed string");
  if (size > 0 && memchr(str, '\0', size) == NULL)
    cut(0);
}


// Certified 100%
TFixed_string::~TFixed_string()
{ _str = NULL; }        // Impedisce la deallocazione


// Certified 100%
void TFixed_string::resize(int size, bool)
{
#ifdef DBG
  fatal_box("Impossibile ridimensionare una stringa fissa da %d a %d caratteri:\n'%s'", 
            _size, size, _str);
#endif
}


// Certified 99% (s != NULL)
void TFixed_string::strncpy(const char* s, int n)
{
  CHECKD(n <= _size, "Fixed string can't be strncpyed: lenght ", n);
  for (int i = 0; *s && i < n; i++) _str[i] = *s++;
  _str[i] = '\0';
}

// Certified 99%
// @doc EXTERNAL

// @mfunc Manda un output formattato alla stringa oggetto
TString& TFixed_string::format(
  const char* fmt, // @parm Formato della stringa
  ...)                     // @parmvar Uno o piu' parametri corrispondenti ai codici in <p fmt>

  // @comm Funziona come la funzione "sprintf" standard del C e ritorna la
  //       stringa formattata con i parametri passati.
  //       <nl>E' piu' efficiente di <mf TString::format> poiche' non usa spark
{
  va_list pars;
  va_start(pars, fmt);
  const int tot = vsprintf(_str, fmt, pars);
  va_end(pars);
  CHECK(tot >= 0 && tot <= size(), "Ue'! Quanto scrivi con 'sta format?");
  return *this;
}



///////////////////////////////////////////////////////////
// Filename
///////////////////////////////////////////////////////////

// Certified 90%
const char* TFilename::ext() const
{
  const char* d = strrchr(name(), '.');
  if (d && is_not_slash(*(++d))) return d;
  return "";
}

// Certified 90%
void TFilename::ext(const char* e)
{
  int start = find(' ')-1;
  if (start < 0)
    start = len()-1;
  for (int i = start; i > 0 && _str[i] != '.'; i--);

  if (i && is_not_slash(_str[i+1])) 
    cut(i);
  if (*e)
  {
    if (*e != '.') 
      *this << ".";
    *this << e;
  }  
}


// Certified 95%
const char* TFilename::name() const
{
  int start = find(' ')-1;
  if (start < 0)
    start = len()-1;
  for (int i = start; i >= 0; i--)
    if (_str[i] == '/' || _str[i] == '\\' || _str[i] == ':')
      break; 
  
  spark.strncpy(&_str[i+1], start-i);
  return spark;
}

// Certified 95%
const char* TFilename::path() const
{                        
  int start = find(' ')-1;
  if (start < 0)
    start = len()-1;
  for (int i = start; i >= 0; i--)
    if (_str[i] == '/' || _str[i] == '\\' || _str[i] == ':')
      break; 
  spark.strncpy(_str, i+1);
  return spark;
}


TFilename& TFilename::add(const char* n)
{
  if (not_empty() && is_not_slash(_str[len()-1]) && is_not_slash(*n))
    *this << SLASH;
  *this << n;
  return *this;
}


// @doc EXTERNAL

// @mfunc Controlla il formato del nome del file
//
// @rdesc Ritorna i seguenti valori
//
// @flag TRUE | Se il nome del file e' sintatticamente corretto
// @flag FALSE | Se il nome del file non e' sintatticamente corretto
bool TFilename::ok() const

// @comm Controlla tutti i casi per cui un nome di file puo' non essere valido.
//       <nl>Nel caso si lavori in Windows controlla anche che il nome del
//       disco sia corretto.
{         
  const int l = len();

  int len = 0;            // lunghezza ultima sottostringa
  bool ext = FALSE;       // trovata estensione
  
  for (int c = 0; c < l; c++)
  { 
    switch(_str[c])
    {
#if XVT_OS == XVT_OS_WIN || XVT_OS == XVT_OS_WIN32
   case ':':
      if (c != 1 || !isalpha(_str[0])) return FALSE;  // Nome disco errato
      len = 0;
      break;
    case '\\':
#endif      
    case '/':
      if (ext) return FALSE;                          // Slash dopo estensione
      if (len > 8) return FALSE;                      // Nome troppo lungo
      if (!isalnum(_str[++c])) return FALSE;
      len = 1;
      break;
    case '.':
      if (len == 0 || ext) return FALSE;              // Nome nullo o Doppia estensione
      ext = TRUE;
      len = 0;
      c++;
    default:
      if (isalnum(_str[c])) len++;
      else return FALSE;
      break; 
    }   
  }

  if (ext && len > 3) 
    return FALSE;
  
  return len > 0 && len < 9;
}

// Certified 70%
const TFilename& TFilename::tempdir()
{                                         
  static TFilename _tempdir;
  const bool create = _tempdir.empty() || user().compare(_tempdir.right(user().len()), -1, TRUE);
  
  if (create) 
  {
    const char* dirpref = getenv("TEMP");
    if (dirpref == NULL || *dirpref == '\0') dirpref = getenv("TMP");
    if (dirpref == NULL || *dirpref == '\0') 
#if XVT_OS == XVT_OS_WIN || XVT_OS == XVT_OS_WIN32
      dirpref = "\\tmp";
#else
    dirpref = "/tmp";
#endif      
    _tempdir = dirpref;
    
    const int last = len()-1;
    if (!is_not_slash(_str[last])) 
      _tempdir.cut(last);

    bool ok = TRUE;
    
    _tempdir.lower();
    if (!fexist(_tempdir))
      ok = make_dir(_tempdir);

    if (ok)
    {                
      TString16 theuser(user());
                      
      if (theuser.empty()) 
        theuser = "prassi";                
      theuser.lower();
      const int f = _tempdir.find(theuser);
      if (f < 0  || f != _tempdir.len() - theuser.len())
        _tempdir << SLASH << theuser;
      _tempdir.lower();
      if (!fexist(_tempdir))
        ok = make_dir(_tempdir);
    }

    if (!ok)
      fatal_box("Impossibile creare la directory '%s' per i file temporanei", (const char*)_tempdir);
    
    _tempdir.insert("TMP=", 0);
    putenv(_tempdir);
  }  
  
  set(_tempdir.mid(4));
  return *this;
}

// Certified 50%
// @doc EXTERNAL

// @mfunc Genera il nome di un file temporaneo
const TFilename& TFilename::temp(
  const char* prefix,      // @parm Eventuale prefisso da assegnare al file temporaneo
  const char* extension)   // @parm Eventuale estensione da assegnare al file temporaneo

  // @comm Nel generare il nome del file controlla se esistone dei caratteri jolly
  //       e li elimina.
{ 
  if (extension && *extension)
  {   
    TFilename mask(prefix);
    if (mask.empty())
      mask.tempdir();
    
    mask.add("*");
    mask.ext(extension);
    const int preflen = strlen(mask.path());
    
    TString_array list;
    const int count = list_files(mask, list);
    
    if (count > 0)
    {             
      for (int i = 0; i < count; i++)
      {
        const char* name = (const char*)list.row(i) + preflen;
        const long numero = atol(name) + 1;
        mask.cut(preflen); 
        mask << numero; 
        mask.ext(extension);
        if (list.find(mask) < 0)
          break;
      }
    }
    else
    {            
      const int star = mask.find('*');
      mask[star] = '1';
    }  
    set(mask);
  }
  else
  {
    tempdir();
    if (prefix && *prefix)
    {
      set(prefix);      // Copia prefisso e ...
      strip("$#*?.");   // ... toglie caratteri jolly
  
      const TFixed_string f(prefix);
#if XVT_OS == XVT_OS_WIN || XVT_OS == XVT_OS_WIN32
      if (f.find("$$") != -1)
      {
        char pid[8];
        sprintf(pid, "%d", getpid());
        pid[3] = '\0';
        *this << pid;
      }
#else
      if (f.find("$$") != -1) *this << getpid();
      if (f.find("##") != -1) *this << getuid();
#endif
    }
    else 
      cut(0);
  
    char* t = tempnam(NULL, (char*)_str);
    CHECK(t != NULL, "Can't execute tempnam");
    set(t);
    free(t);
  }
  
  CHECKS(!fexist(_str), "Il file temporaneo esiste gia': ", _str);
  return *this;
}


bool TFilename::is_absolute_path() const
{
  const char* s = _str;
  if (isalpha(*s) && s[1] == ':')
    s += 2;
  return *s == '/' || *s   == '\\';
}

const TFilename& TFilename::make_absolute_path()
{
  if (is_relative_path())
  {           
    const TString saved(_str);
    getcwd(get_buffer(), size());
    add(saved);
  }
  return *this;
}

bool TFilename::exist() const
{
  return ::fexist(_str);
}

bool TFilename::search_in_path(TFilename& path) const
{
  _searchenv(_str, "PATH", path.get_buffer());
  if (path.empty())
    _searchenv(_str, "path", path.get_buffer());
  return path.not_empty();
}


///////////////////////////////////////////////////////////
// Token string
///////////////////////////////////////////////////////////

// Certified 100%
TToken_string::~TToken_string()
{
}

// Certified 100%
TToken_string::TToken_string(const char* s, char separator)
: TString(s), _separator(separator)
{            
  CHECK(_separator, "NULL TToken_string separator");
  restart();
}

// Certified 100%
TToken_string::TToken_string(int n, char separator)
: TString(n), _separator(separator), _last(-1)
{
  CHECK(_separator, "NULL TToken_string separator");
}

// Certified 100%
TToken_string::TToken_string(const TToken_string& s)
: TString(s), _separator(s._separator), _last(s._last)
{
  CHECK(_separator, "NULL TToken_string separator");
}

void TToken_string::separator(char s)
{ 
  CHECK(s, "NULL TToken_string separator");
  _separator = s; 
}


// Certified 100%
TObject* TToken_string::dup() const
{
  return new TToken_string(_str, _separator);
}

// Certified 90%
const char* TToken_string::get()
{
  CHECK(_separator, "Corrupted TToken_string: NULL separator");
  
  if (_last < 0) return NULL;

  const int start = _last;

  if (_str[start] == '\0')
  {
    _last = -1;
    return NULL;
  }
  else
    if (_str[start] == _separator)
    {
      _last = start+1;
    }
    else
    {
      const int k = find(_separator, start);
      _last = (k >= 0) ? k+1 : -1;
      return sub(start, k);
    }
  return "";
}

// Certified 50%
// @doc EXTERNAL

// @mfunc Ritorna un Token
//
// @rdesc Ritorne la stringa identificata dal Token passato come parametro <p n>
const char* TToken_string::get(
  int n) // @parm Token da ritornare (0 = primo, -1 = prossimo, -2 = ultimo, n = n-simo)

  // @syntax const char* get(int n);
  // @syntax const char* get();
  //
  // @comm Se non viene passato il parametro <p n> ritorna il prossimo Token
  //       (come se <p n> == -1)
  //
  // @xref <mf TToken_string::get_char>
{
  CHECK(_separator, "Corrupted TToken_string: NULL separator");

  if (n < 0)
  {
    if (n == -2)
    {
      const char* sep = strrchr(_str, _separator);
      _last = -1;
      return sep ? sep+1 : _str;
    }
    else return get();
  }
  int sep = 0;
  for (const char* s = _str; sep < n && *s; s++)
    if (*s == _separator) sep++;

  if (sep >= n)
  {
    char* p = strchr(s, _separator);
    if (p == NULL)
    {
      spark = s;
      _last = -1;
    }
    else
    {
      *p = '\0';
      spark = s;
      *p = _separator;
      _last = (int)((const char*)p - _str) + 1;
    }
  }
  else
  {
    _last = -1;
    return NULL;
  }

  return spark;
}


// Certified 99%
// @doc EXTERNAL

// @mfunc Ritorna un carattere
//
// @rdesc Ritorna il primo carattere del Token richiesto
char TToken_string::get_char(
  int n) // @parm Token da ritornare

  // @comm Chiama la <mf TToken_string::get> con tutti i relativi significati per
  //       il parametro <p n>
{
  const char* const car = get(n);
  return car ? *car : '\0';
}

// Certified 99%
int TToken_string::get_int(int n)
{
  const char* const num = get(n);
  return num ? atoi(num) : 0;
}


// Certified 99%
long TToken_string::get_long(int n)
{
  const char* const num = get(n);
  return num ? atol(num) : 0L;
}


// Certified 70%
bool TToken_string::set_item(const char* v, int n)
{
  CHECK(_separator, "Corrupted TToken_string: NULL separator");

  int sep = 0;
  for (int i = 0; sep < n && _str[i]; i++)
    if (_str[i] == _separator) sep++;

  if (sep < n)  // Aggiunge items mancanti prima della posizione n
  {
    for (;sep < n; sep++) 
      *this << _separator;
    *this << v;
    return FALSE;
  }

  int e = find(_separator, i);
  if (e < 0) e = len();

  spark = &_str[e];           // Salva items seguenti
  cut(i);                     // Considera solo items precedenti
  *this << v << spark;        // Aggiunge item desiderato e seguenti
  return TRUE;
}


// Certified 80%
int TToken_string::get_pos(const char* s)
{
  const char* item;

  restart();
  for (int i = 0; (item = get()) != NULL; i++)
    if (strcmp(item, s) == 0) return i;

  return -1;
}

// Certified 80%
int TToken_string::get_pos(long n)
{
  char s[16]; sprintf(s, "%ld", n);
  return get_pos(s);
}



// Certified 90%
bool TToken_string::empty_items() const
{
  for (const char* c = _str; *c; c++)
    if (!is_space(*c) && *c != _separator) return FALSE;
  return TRUE;
}


// Certified 80%
int TToken_string::items() const
{
  int t = 0;
  if (not_empty())
  {
    t++;
    for (const char* s = _str; *s; s++) 
      if (*s == _separator) t++;
  }  
  return t;
}

// Certified 99%
// @doc EXTERNAL

// @mfunc Aggiunge un elemento alla token string
void TToken_string::add(
  const char* s, // @parm Stringa da aggiungere
  int pos)       // @parm Posizione nella quale aggiungere l'elemento
  //       (default aggiunge come ultimo elemento)
  // @parm char | c | Caratter da aggiungere
  // @parm long | n | Long da aggiungere
  // @parm int | n | Intero da aggiungere

  // @syntax void add(const char* s, int n = -1);
  // @syntax void add(char c, int pos = -1);
  // @syntax void add(long n, int pos = -1);
  // @syntax void add(int n, int pos = -1);
  //
  // @comm Permette, a seconda del parametro passato, di aggiungere alla Token
  //       string un nuovo elemnto gia' completo del carattere di separazione
{
  if (s == NULL || *s == '\0') s = " ";
  if (pos < 0)
  {
    if (not_empty()) *this << _separator;
    *this << s;
  }
  else
    set_item(s, pos);
  if (_last < 0) _last = 0;
}

// Certified 0%
void TToken_string::add(char c, int pos)
{
  const char s[2] = { c, '\0' };
  add(s, pos);
}

// Adds an integer value to the token string
// Certified 100%
void TToken_string::add(long n, int pos)
{
  char s[16];
  sprintf(s, "%ld", n);
  add(s, pos);
}

void TToken_string::add(int n, int pos)
{
  char s[16];
  sprintf(s, "%d", n);
  add(s, pos);
}

// Certified 50%
void TToken_string::destroy(int n)
{
  if (_last == -2) return ;

  if (n < 0)
  {
    char* s = strrchr(_str, _separator);
    if (s != NULL) *s = '\0';
  }
  else
  {
    int sep = 0;
    for (char* s = _str; sep < n && *s; s++)
      if (*s == _separator) sep++;

    if (sep >= n)
    {
      const char* p = strchr(s, _separator);
      *s = '\0';
      if (p != NULL) strcat(s,  p+1);
    }
  }
  restart();  
}

///////////////////////////////////////////////////////////
// Paragraph string
///////////////////////////////////////////////////////////

TParagraph_string::TParagraph_string(const char* s, int width)
: TToken_string(s, '|'), _width(width)
{ tokenize(); }

const TString& TParagraph_string::operator =(const char* s)
{
  TToken_string::operator=(s);
  tokenize();
  return *this;
}

void TParagraph_string::tokenize()
{                                  
  int last_space = 0, last_start = 0;
  int num_chars = 0;
  const int l = len();
  TString stmp,sstr(_str),swork; // stringhe di lavoro
  for (int start = 0; start < l;)
  {
    
    switch (_str[start])
    {
      case '\n': last_space = start; num_chars = _width; break;
      case ' ' : last_space = start; num_chars++; break;
      default  : num_chars++;  break;
    }
    
    if (num_chars == _width && (start < l-1))
    {
      int num_of_chars_in_row = (last_space+1) - last_start;
      if (num_of_chars_in_row <= 0) num_of_chars_in_row = _width; // Se non ci sono spazi tronca la stringa a 25
      swork=sstr.mid(last_start,num_of_chars_in_row);
      stmp << swork.trim();
      stmp << "|";
      last_space = num_chars = 0;
//      start = start - (_width - num_of_chars_in_row); // riporta indietro (toglie il numero di caratteri effettivi della riga)
      start = last_start + num_of_chars_in_row;
      last_start = start;
    }
    else 
      start++;
  }
  swork=sstr.mid(last_start,l-last_start);
  swork.trim();
  stmp << swork;
  if (stmp.empty()) stmp = sstr;
  TToken_string::operator=(stmp);
}

///////////////////////////////////////////////////////////
//  TString_array
///////////////////////////////////////////////////////////

int TString_array::add(const char* s, int n)
{
  if (objptr(n) == NULL) 
    n = TArray::add(new TToken_string(s), n);
  else row(n) = s;
  return n;
}

int TString_array::add(const TToken_string& s, int n)
{
  if (objptr(n) == NULL) 
    n = TArray::add(s, n);
  else 
    row(n) = s;
  return n;
}

const TString_array& TString_array::operator=(const TString_array& a)
{                      
  destroy();
  for (int i = a.last(); i >= 0; i = a.pred(i))
  {
    const TToken_string& s = a.row(i);
    add(s, i);
  }
  return a;  
}

// @doc EXTERNAL

// @mfunc Cerca una stringa nell'array
//
// @rdesc Ritorna la posizione nell'array in cui si trova la stringa (-1 se non
//        e' stata trovata)
int TString_array::find(
  const char* s,  // @parm Stringa da cercare
  int from) const // @parm Posizione dalla quale cercare la stringa
{   
  int found = -1;
  for (int i = from; i < items(); i++)
    if (row(i).compare(s, -1, TRUE) == 0)
    {
      found = i;
      break;
    }
  return found;  
}

HIDDEN int ascending_string(const TObject** o1, const TObject** o2)
{
  const TString* s1 = (const TString*)*o1;
  const TString* s2 = (const TString*)*o2;
  return strcmp(*s1, *s2);
}

HIDDEN int descending_string(const TObject** o1, const TObject** o2)
{
  return -ascending_string(o1, o2);
}

void TString_array::sort(bool ascending)
{
  TArray::sort(ascending ? ascending_string : descending_string);
}