#include <ctype.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>

#ifndef FOXPRO 
#define STRICT
#define XVT_INCL_NATIVE
#include <xvt.h>
#endif

#if XVT_OS == XVT_OS_WIN || XVT_OS == XVT_OS_WIN32
#include <direct.h>
#include <io.h>
#define F_OK 0
#undef DIRECTORY
#else
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>

// @doc EXTERNAL

// @func Permette di rimuovere un file (chiama unlink)
//
// @rdesc Ritorna il risultato dell'operazione
//
// @flag 0 | Se l'operazione ha successo
// @flag -1 | Se l'operazione non e' riuscita. Viene inoltre settata la variabile
//            <p errno> con la causa dell'insuccesso
int remove(
  const char* path)  // @parm Nome del file da eliminare (non valgono i caratteri jolly)
{ return unlink(path); }

#endif

#define __UTILITY_CPP
#include <utility.h>

// @doc EXTERNAL

// @func Permette di copiare un file
//
// @rdesc Ritorna il risultato dell'operazione:
//
// @flag TRUE | Se l'operazione e' stata effettuata con successo
// @flag FALSE | Se l'operazione non e' stata effettuata con successo
bool fcopy(
  const char* orig,  // @parm Nome del file di origine
  const char* dest,  // @parm Nome del file di destinazione
  bool append)       // @parm Controllo per aggiungere il contenuto del
  //       file <p dest> in coda al file <p orig> (default FALSE)

  // @comm Nel caso vengano ravvisati degli errori durante l'operazione vengono
  //       creati dei box di comunicazione che indicano la causa del problema
{ 
  const char* wflag;
#if XVT_OS == XVT_OS_WIN || XVT_OS == XVT_OS_WIN32
  const char* const rflag = "rb";
  if (append)                    
    wflag = "ab";
  else
    wflag = "wb";
#else
  const char* const rflag = "r";
  if (append)
    wflag = "a";
  else
    wflag = "w";
#endif

  FILE* i = fopen(orig, rflag);
  if (!i) return error_box("Impossibile leggere il file %s", orig);

  FILE* o = fopen(dest, wflag);
  CHECKS(o, "Impossibile scrivere il file ", dest);

  const word size = 16*1024;
  TString buffer(size);
  
  bool ok = TRUE;          
  while (ok)
  {
    const word letti = fread((char*)(const char*)buffer, 1, size, i);
    ok = fwrite((char*)(const char*)buffer, 1, letti, o) == letti;
    if (letti < size) break;
  }    
  
  if (!ok) error_box("Errore di scrittura: probabile disco pieno!");

  fclose(o);
  fclose(i);

  return ok;
}

// @doc EXTERNAL

// @func Controlla l'esistenza di un file
//
// @rdesc Ritrona i seguenti valori:
//
// @flag TRUE | Se il file esiste
// @flag FALSE | Se il file non esiste
bool fexist(
  const char* file)  // @parm Nome del file di cui contrallarne l'esistenza
{
  return access(file, F_OK) == 0;
}

// @doc EXTERNAL

// @func Permette di creare una directory
//
// @rdesc Ritorna il risultato dell'operazione
//
// @flag TRUE | Se l'operazione e' avvenuta con successo
// @flag FALSE | Se l'operazione non e' riuscita
bool make_dir(
  const char* dir) // @parm Nome della directory da creare
{
  int res =
#if XVT_OS == XVT_OS_WIN || XVT_OS == XVT_OS_WIN32
    mkdir(dir);
#else
  mkdir(dir, 0777);
#endif       
  return res == 0;
}

#ifndef FOXPRO

// @doc EXTERNAL

// @func Ritorna la lista dei file il cui nome corrisponde alla stringa (con caratteri
//   jolly) passata.
//
// @rdesc Ritorna il numero di file che soddisfano la condizione passata (numero di elementi
//                              della token string)
int list_files(
  TToken_string& filelist) // @parm Stringa contenente la condizione di estrazione 
  //                            della lista dei file
  
  // @comm Per comodita' la lista desiderata vine anch'essa messa in <p filelist> in quanto e'
  //                     gia' costruita                                                                                                                  
{           
  TFilename dir(filelist.get(0));
  for (int i = dir.len()-1; i >= 0; i--)
    if (dir[i] == '/' || dir[i] == '\\') break;
  
  const TString16 mask(dir.mid(i+1));
  dir.cut(i > 0 ? i : 0);   
  
  xvt_fsys_save_dir();        
  DIRECTORY directory; xvt_fsys_convert_str_to_dir((char*)(const char*)dir, &directory);
  BOOLEAN ok = xvt_fsys_set_dir(&directory);
  if (!ok) fatal_box ("Impossibile entrare in %s", (const char*)dir);
  
  SLIST files = xvt_fsys_list_files("", (char*)(const char*)mask, FALSE);
  
  const int count = xvt_slist_count(files);
  filelist.spaces(count*12);
  filelist.cut(0);
  
  for (SLIST_ELT e = xvt_slist_get_first(files); e; e = xvt_slist_get_next(files, e))
  { 
    char* f = xvt_slist_get(files, e, NULL);
    if (dir.not_empty()) 
    {
      filelist.add(dir); 
      filelist << '/' << f;
    }
    else 
      filelist.add(f);  
  }
  
  xvt_slist_destroy(files);
  xvt_fsys_restore_dir();
  
  return count;
}

#endif

// Certified 99%
// @doc EXTERNAL

// @func Permette di ritornare una stringa formattata
//
// @rdesc Ritorna la stringa desiderata
char* format(
  const char* fmt, // @parm Formato che deve essere dato alla stringa
  ...)                     // @parmvar Uno o piu' parametri corrispondenti ai codici in <p fmt>

  // @comm Il funzionamento e' come la <f sprintf> del C, solo che non e' necessario passare la
  //                     stringa di destinazione alla funzione.
{
  va_list pars;

  va_start(pars, fmt);
  const int tot = vsprintf(__tmp_string, fmt, pars);
  va_end(pars);

  CHECK(tot >= 0 && tot < sizeof(__tmp_string)-1, "Ue'! Ma quanto scrivi?");
  return(__tmp_string);
}

// @doc EXTERNAL

// @func Converte la coppia nome-parametro in una stringa che identifica il programma
//
// @rdesc Ritorna la stringa identificante il programma
const char* cmd2name(
  const char* argv0,  // @parm Nome del programma
  const char* argv1)  // @parm Nome del parametro (default "")
{
  TFilename app(argv0);
  app = app.name(); 
  if (argv1 && *argv1) app << ' ' << argv1;
  else app << " -0";
  app.lower();

  const int par = app.find(" -");
  const int num = par > 0 ? atoi(app.mid(par+2))+1 : 1; 

  const char c = (num > 9) ? ('a'+num-10) : ('0'+num);
  app.cut(3);
  app << c << "00";

  return strcpy(__tmp_string, app);
}


///////////////////////////////////////////////////////////
// Conversione in cifre romane
///////////////////////////////////////////////////////////

HIDDEN const char * cifre_romane = "IVXLCDM@";
HIDDEN const int valori_cifre [] = { 1, 5, 10, 50, 100, 500, 1000, -1 };

HIDDEN int ctoi(char c)
{
  if (c == '\0') return 0;

  c = toupper(c);
  for (int i = 0; cifre_romane[i]; i++)
    if (cifre_romane[i] == c) return valori_cifre[i];

  return -1;
}

// @doc EXTERNAL

// @func Converte una cifra romana in intero normale
//
// @rdesc Ritorna l'equivalente in numeri della cifra romane
int rtoi(
  const char * val) // @parm Stringa contenente la cifra scritta in numeri romani
{
  if (val == NULL) return 0;

  int tot = 0;
  int value = ctoi (val[0]);
  for (int i = 1; value > 0; i++)
  {
    const int next_val = ctoi(val[i]);
    if (value < next_val) tot -= value;
    else                  tot += value;
    value = next_val;
  }

  return (value == 0) ? tot : -1;
}

// @doc EXTERNAL

// @func Converte un numero intero nell'equivalente cifra romana
//
// @rdesc Ritorna una stringa contenente la cifra romana
const char* itor(
  int num) // @parm Intero da convertire in cifra romana
{
  HIDDEN char roman_string[16];
  int cifra = 0;

  for (int pos = 7; pos--;)
  {
    int val = valori_cifre[pos];
    int quanti = num / val;
    if (quanti < 4)
    {
      if ((pos & 1) && quanti == 1 && (num/valori_cifre[pos-1]) == 9)
      {
        roman_string[cifra++] = cifre_romane[pos-1];
        roman_string[cifra++] = cifre_romane[pos+1];
        val = valori_cifre[pos-1];
        quanti = 9;
      }
      else for (int i = 0; i < quanti; i++)
        roman_string[cifra++] = cifre_romane[pos];
    }
    else
    {
      roman_string[cifra++] = cifre_romane[pos];
      roman_string[cifra++] = cifre_romane[pos+1];
    }

    num -= quanti * val;
  }

  roman_string[cifra] = '\0';
  return roman_string;
}

// @doc EXTERNAL

// @func Permette di codificare i caratteri di escape
//
// @rdesc Ritorna il carattere codificato
const char *esc(
  const char* s) // @parm Carattere da codificare

  // @comm Viene utilizzata soprattuto in UNIX.
{
  const char *s1 = s;
  char *s2 = __tmp_string;
  int base;

  while (*s1)
  {
    if (*s1 == '\\')
    {
      s1++;
      switch (tolower(*s1))
      {
      case 'b' : *s2++ = '\b'; break;
      case 'e' : *s2++ = '\033'; break;
      case 'f' : *s2++ = '\f'; break;
      case 'n' : *s2++ = '\n'; break;
      case 'r' : *s2++ = '\r'; break;
      case 't' : *s2++ = '\t'; break;
      default  :
      {
                 if (isdigit(*s1))
                 {
                   if (*s1 == '0')
                   {
                     s1++;
                     if (tolower(*s1) == 'x') 
                     {
                       s1++;
                       base = 16;
                     }
                     else base = 8;
                   }
                   else base = 10;
                   *s2 = 0;
                   char c = tolower(*s1);
                   while (isdigit(c) || (base == 16 && c >= 'a' && c <= 'f'))
                   {
                     *s2 *= base;
                     if (isdigit(*s1)) *s2 += (*s1 - 48);
                     else *s2 += (*s1 - 'a' + 10) & 0x0F;
                     s1++;
                     c = tolower(*s1);
                   }
                   s2++; s1--;
                 }
                 else *s2++ = *s1;
               }
               }
    }
    else
      if (*s1 == '^')
      {
        s1++;
        *s2++ = (tolower(*s1) - 'a' + 1);
      }
      else *s2++ = *s1 ;
    s1++;
  }
  *s2 = '\0';
  return(__tmp_string);
}

HIDDEN const char * const key = "QSECOFR-";

// @doc EXTERNAL

// @func Permette di criptare una parola
//
// @rdesc Ritorna la stringa criptata
const char * encode(
  const char * data) // @parm Stringa da criptare

  // @xref <f decode>
{
  for (int i = 0; data[i]; i++)
    __tmp_string[i] = data[i] + (i < 8 ? key[i] : data[i - 8]);
  __tmp_string[i] = '\0';
  return __tmp_string; 
}

// @doc EXTERNAL

// @func Permette di decodificare una stringa criptata
//
// @rdesc Ritorna la stringa in chiaro
const char * decode(
  const char * data) // @parm Stringa criptata da tradurre

  // @xref <f encode>
{
  for (int i = 0; data[i]; i++)
    __tmp_string[i] = data[i] - (i < 8 ? key[i] : __tmp_string[i - 8]);
  __tmp_string[i] = '\0';
  return __tmp_string; 
}


os_type get_os_type()
{
#if XVT_OS == XVT_OS_WIN 
  const WORD winver = LOWORD(GetVersion());
  const BYTE majver = LOBYTE(winver);
  const BYTE minver = HIBYTE(winver);
  if (majver > 3 || (majver == 3 && minver > 11))
    return os_Windows95;
  const DWORD winflags = GetWinFlags();
  if (winflags & 0x4000)  
    return os_WindowsNT;
  return os_Windows;  
#else
// #error Ricordati di Win32
  return os_WindowsNT;  
#endif
}

#ifdef __WATCOMC__

// @doc EXTERNAL

// @func Costruisce il percorso per un file temporaneo
//
// @rdesc Ritorna il nome assoluto del file temporaneo
char * tempnam(
  const char * dir ,   // @parm Nome della directory nel quale inserire il file temporaneo
  const char * prefix) // @parm Evenutale prefisso da dare al file temporaneo

  // @comm Il nome della directory viene letto dalla variabile d'ambiente chiamata
  //       TMP, solo nel caso questa non esista viene utilizzato il nome passato
  //       in <p dir>.
  //                     <nl>E' definita solo nel caso si utilizzi il WATCOM C/C++.

{
  TFilename tmpdir(getenv("TMP"));
  static word counter = 1;

  if (tmpdir.empty() || !fexist(tmpdir))
  {
    tmpdir = dir;
    if (tmpdir.empty() || !fexist(tmpdir))
      tmpdir = "";
  }
  if (is_not_slash(tmpdir.right(1)[0]))
    tmpdir << '/';
  tmpdir << prefix ;
  const int lastpos = tmpdir.len() ;
  tmpdir << format("%ud", counter);

  while (!fexist(tmpdir))
  {
    counter++;
    if (counter == 0) counter = 1;
    tmpdir.cut(lastpos);
    tmpdir << format("%ud", counter);
  }
  return strdup(tmpdir);
}
#endif