#define __ISAM_CPP

#include <config.h>
#include <execp.h>
#include <expr.h>
#include <extcdecl.h>
#include <diction.h>
#include <dongle.h>
#include <mailbox.h>
#include <postman.h>
#include <prefix.h>
#include <progind.h>
#include <recarray.h>
#include <relation.h>
#include <scanner.h>
#include <tabmod.h>
#include <utility.h>
#include <tabutil.h>
#include <varrec.h>

#ifdef WIN32
#include <io.h>
#include <share.h>
#include <stdio.h>
#endif

#include <fcntl.h>
#include <stdlib.h>        
#include <sys/stat.h>

#include <codeb.h>  
#include <nditte.h>  

#define RECLOCKTYPES    0xFF00
#define READTYPES       0x00FF                                                                       
#define INVFLD          255

bool __field_changed = FALSE;

#define NOALLOC (char **) -1

HIDDEN bool __autoload = TRUE;   
                                    
// @doc INTERNAL

////////////////////////////////////////////////////////////////////////////////////////////////////
// Funzioni C 
////////////////////////////////////////////////////////////////////////////////////////////////////

int hashfun(const char *s) 
{
  int l = strlen(s);
  unsigned short  temp = 0, *pw = (unsigned short *) s;

  const unsigned short * end = (unsigned short *) (s + (l - 1));
  while ((char *) pw < (char *) end)
  {
    temp ^= *pw;
    pw++;
  }                       
  if (l & 1)
   temp ^= (unsigned short ) (8192 + s[l - 1]);
  l = (short) (temp % (MaxFields - 3));            
  CHECKS(l >= 0, "Negative remainder on ", s);
  return(l);
}

int findfld(const RecDes *recd, const char *s) 
{
  short stp = hashfun(s);

  for (byte i = 0 ; i < MaxFields; i++) 
  {                             
    if (stp + i >= MaxFields)
      stp -= MaxFields;
    byte p = stp + i;
    const int fp = recd->SortFd[p];
    if (fp == INVFLD) 
      return(FIELDERR);
    
    const int cmp = strcmp(recd->Fd[fp].Name, s);
    if (cmp == 0)
      return (int) fp;
    else
      if (cmp > 0) 
        return(FIELDERR);
  }
  return(FIELDERR);
}

bool __file_is_crypted(int logicnum)
{
	return logicnum == LF_TURNI;
}

void __getfieldbuff(byte l, byte t, const char * recin, TString& s, bool is_crypted)
{     
  CHECK(recin, "Can't read from a Null record");
  
  if (t != _alfafld && t != _datefld)
  {                   
    if (t == _intzerofld || t == _longzerofld)
    {             
      byte i = 0;
      for (char* c = (char*)recin; *c == ' ' && i < l; c++, i++)
        *c = '0';     
      if (i == l) 
        l = 0;  
    }
    else                            
    {
      while ((*recin == ' ') && (l))
      {
        recin++;
        l--;
      }
      if ((t != _realfld) && (t != _charfld))
      {
        while ((*recin == '0') && (l))
        {
          recin++;
          l--;
        }
      }
    }  
  }

  if (l)
  {
    while(l > 0 && recin[l - 1] == ' ') l--;
    if (l)
      s.strncpy(recin, l);
  }
  s.cut(l); 
  if (l)
  {
    if (t == _datefld)
    {
      const TDate dt(s);
      s = dt.string(full);
    }
    else
    {
      if (t == _boolfld)
      {
        const char ok = toupper(*s);
        if (ok == 'T' || ok == 'Y' || ok == 'S' || ok == 'X')
          s = "X";
        else
          s.spaces(1);
      }    
    }
  }
}

void  __putfieldbuff(byte l, byte d, byte t, const char* s, char* recout, bool is_crypted)
{
  CHECK(recout, "Can't write null record" );

  TString80 s2;
  if (t == _datefld)
  {
    if (s && *s && strlen(s) != 8)
    {
      const TDate dt(s);    
      s2 << dt.date2ansi();
      s = s2.get_buffer();
    }
  }
  else
    if (t == _boolfld)
    {           
      s2 = (*s && strchr("1STXY", toupper(*s)) != NULL) ? "T" : "F";
      s = s2.get_buffer();
    }
    else
      if (t == _realfld)
      {
        real r(s);
        s2 = r.string(l, d); 
        s = s2.get_buffer();
      }

  int len = strlen(s);
  const bool exceeded = len > l;

  if ((t == _intfld)     ||
      (t == _longfld)    ||
      (t == _wordfld)    ||
      (t == _realfld)    ||
      (t == _intzerofld) ||
      (t == _longzerofld)
      )
  {
    if (len == 0 || exceeded)
    {
      s2 = "0";
      s = s2.get_buffer();
      len = 1;
    } 
    
    __field_changed = FALSE;
    const char c = (t == _intzerofld || t == _longzerofld) ? '0' : ' ';
    for (int i = l - len - 1; i >= 0; i--) 
    {
      __field_changed |= (recout[i] != c);
      recout[i] = c;
    }
    if (!__field_changed)
    {
      __field_changed = memcmp(s, recout, l)  != 0;
      if (!__field_changed)
        return;
    }
    strncpy(&recout[l - len], s, len) ;
  }
  else
  {
    if (exceeded)
      len = l;

/*
    // Il codice seguente e' completamente errato in quanto tutti i prefissi ingannano il test!
    // La stringa vuota e' prefisso di qualsiasi stringa
    // per cui l'azzeramento di un campo risulta sempre impossibile!
    
    __field_changed = memcmp(s, recout, len) != 0;
    if (!__field_changed)
      return;
*/
    __field_changed = TRUE; // Per ora e' meglio cosi' 

    strncpy(recout, s, len) ;
    for  (int i = l - 1; i >= len; i--) 
      recout[i] = ' ';
  }
}      

struct TCallbackFileinfo
{
  TString16 _var;
  TFilename _app;
};

HIDDEN int find_relapp(TConfig& cfg, void* jolly)
{
  TCallbackFileinfo& info = *((TCallbackFileinfo*)jolly);
  if (cfg.exist(info._var))
  {
    info._app = cfg.get(info._var);
    return info._app.not_empty();
  }
  return FALSE;
}

bool get_relapp(int logicnum, TString& app)
{
  TConfig ini(CONFIG_GENERAL);
  TCallbackFileinfo fi;
  fi._var.format("Edit_%d", logicnum);
  ini.for_each_paragraph(find_relapp, &fi);
  if (fi._app.not_empty()) 
    app = fi._app;
  return app.not_empty();
}

struct TCallbackTableinfo
{
  TString16 _var1, _var2, _var3;
  TString4 _module;
  TFilename _tabapp, _relapp;
};

HIDDEN int find_tabapp(TConfig& cfg, void* jolly)
{
  TCallbackTableinfo& info = *((TCallbackTableinfo*)jolly);
  
  if (cfg.exist(info._var1))
  {
    info._tabapp = cfg.get(info._var1);
    if (info._tabapp.not_empty())
      return 1;
  }
  if (cfg.get_paragraph().compare(info._module, 2, TRUE) == 0)
  {
    if (cfg.exist(info._var2))
    {
      info._relapp = cfg.get(info._var2);
      if (info._relapp.not_empty())
        return 2;
    }
    if (info._var3.full() && cfg.exist(info._var3))
    {
      info._relapp = cfg.get(info._var3);
      if (info._relapp.not_empty())
        return 3;
    }
  }
  return 0;
}

bool get_tabapp(const char* tabname, TString& app)
{
  TConfig ini(CONFIG_GENERAL, "Main");
  TCallbackTableinfo fi;
  TTable t(tabname);
  fi._var1.format("Edit_%s", t.name());
  fi._var2.format("Edit_%d", t.num());
  fi._var3.format("Edit_%d", t.num() == LF_TABCOM ? LF_TAB : LF_TABCOM);
  fi._module = t.module();
  ini.for_each_paragraph(find_tabapp, &fi);
  
  app = fi._tabapp;
  if (app.empty())
    app = fi._relapp;

  if (app.empty())
  {
    app = "ba3 -0";
    if (!fi._module.starts_with("ba")) // Compatibility mode :-(
    {
      const TString& tp = ini_get_string(CONFIG_STUDIO, fi._module, "TabPrg");
      if (tp.full())
        app = tp;
    }
  }
  app << ' ' << tabname;
  return tabname && *tabname > ' ';
}        

////////////////////////////////////////////////////////////////////////////////////////////////////
// Funzioni implementate per la gestione file dati tramite Codebase
////////////////////////////////////////////////////////////////////////////////////////////////////
// Inizio(@)

// @doc INTERNAL

// @func Ritorna una Token_string con in nome dell'indice
int get_table_names(
     int logicnum,            // @parm Numero logico del file di cui riconoscere l'indice
     TToken_string& i_names,  // @parm Token_string in cui inserire il nome dell'indice
     int mode)

// @comm Ritorna il nome con il prefisso corrente
{
  TFilename f = prefix().get_filename(logicnum);
  i_names.cut(0);

  if (mode & 1)
    i_names = f;

  if (mode & 2)
  {
    f.ext("cdx");
    i_names.add(f);
  }

  if (mode & 4)
  {
    f.ext("fpt");
    if (f.exist())
      i_names.add(f);
  }

  return i_names.items();
}

// Converte un errore di codebase in un errore isam
#ifdef DBG      
HIDDEN  int cb_error = NOERR; 
#endif

int get_error(int err)
{   
//   Codici negativi
HIDDEN int error_codes_g[] = {-1,_isnotopen,-1,-1,-1,_islocked,-1,-1,-1,-1,-1,-1,_isfilefull,
                              -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,_iskeynotfound,_ispatherr,-1,-1,_isdupkey};
//   Codici da 0 a 9
HIDDEN int error_codes_ra[] = {NOERR,NOERR,_iskeynotfound,_iseof,_isbof,_isnrecerr}  ;
//   Codici da  10 a ...  
HIDDEN int error_codes_rb[] = {-1,-1,_isreinsert,-1,-1,_islocked,-1,_isalropen,_iskeyerr }      ;

  int isamerr = NOERR;

#ifdef DBG
  if (err <= 200 || err >= 230)
    cb_error = err;
#endif

  if (err > 0)
  {
    if (err >= 10)
    {
      if (err > 80 || error_codes_rb[err/10]==-1)
        isamerr = err;
      else
        isamerr = error_codes_rb[err/10];
    }
    else
      isamerr = error_codes_ra[err];
  }
  else
    if (err < 0)
    {
      if (err == -1)
			{
#ifdef DBG
        int ierr = DB_get_error();  
#endif
        isamerr = _isnotopen;
			}
      else
      {
        int ierr = DB_get_error();  
        if (ierr == 0) ierr = -err;  // DB_get_error already called
        if (ierr < 0) ierr = -ierr;
        if (ierr > 340 || error_codes_g[ierr/10]==-1)
          isamerr = -ierr;
        else
          isamerr = error_codes_g[ierr/10];
      }
    }
  DB_zero_error();
  return isamerr;
}

// Used also by varrec
bool rec_has_memo(const RecDes& rd)
{
  for( int i = rd.NFields - 1; i >= 0; i--)
    if (rd.Fd[i].TypeF == _memofld)
      return true;
  return false;
}

HIDDEN bool lf_has_memo(int lffile)
{
  return rec_has_memo(prefix().get_recdes(lffile));
}

HIDDEN void browse_null(char *start, int nc)
{
  for (int i = nc - 1; i >= 0 ; i--) // Anche il primo byte(deletion flag) deve essere cambiato. nc comprende il primo byte
    if (start[i] == '\0') start[i] = ' '; 
}
 
// Traduce l'espressione chiave di CodeBase

/*HIDDEN const char * translate_key(const char* key)
{
  // Trasforma l'espressione
  TToken_string t = key;
  TToken_string k(t.get(0),'+');
  TToken_string range("",',');
  TString ws;
  const bool is_dup = t.get(1)[0] == 'X';
  const int items = k.items();
  t = "";
  for (int i = 0; i<items; i++) // scorre i campi dell'espressione
  {
    ws = k.get(i); ws.upper(); // Primo campo
    const bool is_upper = ws.find("UPPER")  >= 0;
    const bool is_sub   = ws.find("SUBSTR") >= 0;
    int   paren1        = ws.find('('); // Trova la prima parentesi aperta
    int   paren2,last,from = 0,to = 0;
    
    if (paren1 >= 0 && is_sub && is_upper)
      paren1 = ws.find('('); // Trova la seconda parentesi (in questo caso c'e' per forza)

    if (paren1 >= 0) // Trova la prima virgola o parentesi chiusa (per qualsiasi espressione)
    {
      paren2 = ws.find(',');
      if (paren2 == -1) // se non ci sono virgole trova la parentesi chiusa
        paren2 = ws.find(')');
      CHECK(paren2 > paren1,"Something wrong happened translating CodeBase expressions.");
      if (is_sub) // Se e' una sottostringa estrae i campi DA e A
      {
        range = ws;
        last = ws.find(')');
        range.sub(paren2,last); // dalla virgola alla parentesi
        from = range.get_int(0);
        to   = range.get_int(1);
      }
      ws = ws.sub(paren1+1,paren2); // Nome del campo pulito pulito
      ws.upper();
    }
    
    if (is_upper)
      t << "UPPER(";
    
    t << ws; // aggiunge il nome del campo

    if (is_sub)
    {
      t << "[";
      t << from << ",";
      t << to << "]";
    }
    if (is_upper)
      t << ")";
    t << '+';
  }
  t.rtrim(1); // Toglie il + in piu'
  t.add(is_dup ? "X" : " "); 

  TString& tmp = get_tmp_string();
  tmp = t;
  return tmp;
} */

HIDDEN  int __build_key(const RecDes& recd, int numkey, const RecType recin, char *key, bool build_x_cb)
  /* *recd;       descrittore record               */
  /* numkey;      numero chiave                    */
  /* recin;       buffer contenente il record      */
  /* *key;        valore della chiave              */
  /* build_x_cb   flag di costruzione per codebase */
{           
  CHECKD(numkey > 0, "Can't build key ", numkey);
  
  const char null_char = -1;
  key[0] = '\0';
  if (numkey-- <= recd.NKeys)
  { 
    int l = 0;                                   
    for (int i = 0; i < recd.Ky[numkey].NkFields; i++)
    {                        
      const KeyDes& kd = recd.Ky[numkey];
      const bool upp = kd.FieldSeq[i] > MaxFields;
      const int nf = upp ? kd.FieldSeq[i] - MaxFields : kd.FieldSeq[i];
      const RecFieldDes& rf = recd.Fd[nf];  
      const TFieldtypes f = (TFieldtypes) rf.TypeF;
      
      int off, len;
      if (kd.FromCh[i] == 255)
      {
        off = rf.RecOff;
        len = rf.Len;
      }
      else
      { 
        off = rf.RecOff + kd.FromCh[i];
        len = kd.ToCh[i] - kd.FromCh[i] + 1;
      }
      if ((l + len) > 255)
      {
        key[0] = '\0';
        return(_iskeylenerr);
      }
      if (f == _boolfld) 
      {               
        const bool on = *(recin + off) > ' ' && strchr("STXY", *(recin + off)) != NULL;
        key[l] = on ? 'T' : 'F';
      }  
      else             
        strncpy((key + l), (recin + off), len);
        
      if (recin[off] == '\0')
      {
        memset(key + l, ' ', len);
        if ((f == _intfld) || (f == _longfld) || (f == _wordfld) || 
                            (f == _intzerofld) || (f == _longzerofld))
          key[l + len - 1] = build_x_cb ? '0' : null_char;
      }
      else
        if ((f == _intfld) || (f == _longfld) || (f == _wordfld) || (f == _intzerofld) || (f == _longzerofld))
        {   
          int w = l, j = l + len;
          while (w < j && key[w] == ' ') w++;
          while (w < j && key[w] == '0') key[w++] = ' ';
          if (w == j) key[w-1] = build_x_cb ? '0' : null_char;
        } 
      if (upp)
        for (int i = l+len-1; i >= l; i--)
          key[i] = toupper(key[i]);

      l += len;
    }
    
    // rtrim            
    if (build_x_cb)
    {
      for (l--; l>=0 && key[l] == ' '; l--);
      key[l + 1] = '\0';
    }
    else
    {
      for (l--; l>=0 && (key[l] == ' ' || key[l] == null_char); l--);
      key[l + 1] = '\0';
      for (;l >= 0; l--)
        if (key[l] == null_char)
          key[l] = '0';
    }
    
    return(NOERR);
  }
  return(_ispatherr);
}


HIDDEN int cisread(int fhnd, int knum, TRectype& record, int mode, TRecnotype& curr_recno)
{
  CHECKD(fhnd >= 0, "Can't use codebase handle ", fhnd);

  const int rmode = (mode & READTYPES);
  const int lmode = (mode & RECLOCKTYPES);

  // Non usare mai _isnextn o _isprevn, usare il metodo skip!  
  CHECK (rmode !=_isnextn && rmode !=_isprevn, "_isnextn and _isprevn not supported in cisread");

  TString256 keystr;
  char* key = keystr.get_buffer();
  
  int err = NOERR;
  do
  {
    if (rmode>=_isequal && rmode<=_isgteq)
    {   
      const RecDes& r = record.rec_des();
      err=__build_key(r, knum, record.string(),key,TRUE);
      if (err == NOERR)
      {
        err = DB_seek(fhnd,key); 
        if (err == NOERR && rmode == _isgreat)  
          err = DB_next(fhnd);
        if (err != NOERR) 
          err = get_error(err);
      }
      if (rmode != _isequal && err == _iskeynotfound) 
        err = NOERR;
    }
    else
    {
      if (rmode==_isfirst)   
        err=DB_first(fhnd);
      else
        if (rmode==_islast)
          err=DB_last(fhnd);
        else
          if (rmode==_isnext)
          {
            if (curr_recno != DB_recno(fhnd))
            {
              const RecDes& r = record.rec_des();
              err = __build_key(r, knum, record.string(),key,TRUE);
              if (err == NOERR)
              {
                err = DB_seek(fhnd,key);
                err = get_error(err);
                if (err != NOERR && err != _iskeynotfound && err != _iseof)
                  fatal_box("Errore nella next %d : non posso riposizionarmi", err);
                else
                  if (err == NOERR)
                    err=DB_next(fhnd);
              }                         
            }
            else
              err=DB_next(fhnd);
          }
          else
            if (rmode==_isprev)
            {
              if (curr_recno != DB_recno(fhnd))
              {
                const RecDes& r = record.rec_des();
                err = __build_key(r, knum, record.string(),key,TRUE);
                if (err == NOERR)
                {
                  err = DB_seek(fhnd,key);
                  err = get_error(err);
                  if (err != NOERR && err != _iskeynotfound &&  err != _iseof)
                    fatal_box("Errore nella prev %d : non posso riposizionarmi", err);
                  else
                    if (err == NOERR)
                      err=DB_prev(fhnd);
                }
              }
              else
                err=DB_prev(fhnd);
            }
            else
              if (rmode==_iscurr)
                err=DB_go(fhnd,DB_recno(fhnd));  
      if (err != NOERR) 
        err=get_error(err);       
    }
    
    if (err == _iseof)
      DB_last(fhnd);
    if (err == NOERR && (lmode == _lock || lmode == _testandlock))   // _lock e _testandlock
    {
      err=DB_lock(fhnd);
      if (err != NOERR) 
        err = get_error(err);
      if (err == _islocked && lmode == _testandlock) 
        break;
    }
    if (err == _islocked)
    {
      const RecDes& r = record.rec_des();
      record = (const char*)DB_getrecord(fhnd);
      __build_key(r, knum, record.string(), key, TRUE);
      message_box("Codice %s in uso da parte\ndi un altro utente.", key);
      if (lmode != _lock)          
        break;
    } 
  } while (err ==_islocked);  

  if (err == NOERR && lmode == _unlock)            
  {
    err=DB_unlock(fhnd);  
    if (err != NOERR) 
      err = get_error(err);
  }

  curr_recno = DB_recno(fhnd);
  record = (const char *)DB_getrecord(fhnd);

  return err;
}

////////////////////////////////////////////////////////////////////////////////////////////////////
// Funzioni implementate per la gestione file dati tramite Codebase
////////////////////////////////////////////////////////////////////////////////////////////////////
// Fine(@)

// @doc EXTERNAL

// @func Setta il valore della variabile <p __autoload>
void set_autoload_new_files(
     bool on) // @parm Valore da assegnare

// @comm Il valore di <p __autoload> indica il caricamento dei valori standard dei file

{
  __autoload = on;
}

///////////////////////////////////////////////////////////
// TBaseisamfile
///////////////////////////////////////////////////////////

TBaseisamfile::TBaseisamfile(int logicnum)
{
  _logicnum = logicnum;
  _isam_handle = 0;
  _curr_key = 0;
  _lasterr = NOERR;
  _current = new TRectype(logicnum);
}

// @doc EXTERNAL

// @mfunc Reperisce il tracciato dal file stesso.
//
TBaseisamfile::TBaseisamfile(
    const char* name, // @parm Indica il nome del file
    const char* descname) // @parm Indica la descrizione del file
// @comm Esiste la possibilita' di codificare i valori di pathpref e prefix in <p name>
// %  si riferisce ai dati comuni
// $  si riferisce ai dati di ditta corrente
// #  si riferisce al direttorio indicato da PATHPREF.INI
{
  if (descname && *descname)
  {
    TTrec r;
    ifstream f(descname);
    f >> r;       
    const int err=DB_build(name, &r.rec()) ;
    if (err != NOERR) 
      fatal_box("Non posso creare il file %s : errore n.ro %d", name, err);
  }
  _lasterr = NOERR;
  TFilename filename(name);
  CHECK(filename.full(),"Must define the file to open!");
  _logicnum = prefix().get_handle(filename);
  _current = new TRectype(this);

}

TBaseisamfile::~TBaseisamfile()
{
  if (_current)
    delete _current;
}

TCodeb_handle TBaseisamfile::handle(int key) const
{
  return prefix().get_handle(_isam_handle, key > 0 ? key : _curr_key);
}

TRecnotype TBaseisamfile::items() const
{                  
  return DB_reccount(handle());
}

const char* TBaseisamfile::name() const
{     
	TString& tmp = get_tmp_string(); 
  tmp.format("%d", num());
  return tmp;
}

const char* TBaseisamfile::filename() const
{                                     
  const int n = _isam_handle > 0 ? _isam_handle : num();
	TString& tmp = get_tmp_string(); 
  tmp = prefix().get_filename(n);
  tmp << ".dbf";
  return tmp;
}

const char* TBaseisamfile::description()
{
  const int n = _isam_handle > 0 ? _isam_handle : num();
  const FileDes& d = prefix().get_filedes(n);
  return d.Des;
}

TRecnotype TBaseisamfile::eod() const
{
  return items();
}                                         

void TBaseisamfile::set_curr(TRectype * curr)
{ 
  CHECK(curr != NULL, "You must set a valid current record");
  CHECK(num() == curr->num(), "You must set a coherent current record");
  if (_current != NULL)
    delete _current;
  _current = curr;                     
}

void TBaseisamfile::setkey(int nkey)
{
  _curr_key = nkey;
}

int TBaseisamfile::getkey() const
{
  return _curr_key;
}

int TBaseisamfile::first(word lockop)
{
  return TBaseisamfile::read(curr(), _isfirst, lockop);
}

int TBaseisamfile::last(word lockop)
{
  return TBaseisamfile::read(curr(), _islast, lockop);
}

int TBaseisamfile::next(word lockop)
{
  return TBaseisamfile::read(curr(), _isnext, lockop);
}

int TBaseisamfile::prev(word lockop)
{
  return TBaseisamfile::read(curr(), _isprev, lockop);
}

int TBaseisamfile::reread(word lockop)
 
{
  return TBaseisamfile::reread(curr(), lockop);
}

int TBaseisamfile::reread(TRectype& rec, word lockop)
{
  return rec.read(*this, _iscurr, lockop);
}

int TBaseisamfile::skip(TRecnotype nrec, word lockop)
{   
  if (nrec == 0) 
    return NOERR;
  
  curr().setdirty();  
  const int fhnd = handle();

  // controllo se mi sono spostato dall'ultima lettura
  if (_recno != DB_recno(fhnd))
  {
    _lasterr = DB_go(fhnd, _recno);
    if (_lasterr != NOERR)
    { 
      _lasterr = get_error(_lasterr);
      if (_lasterr != _islocked)
        fatal_box("Errore nella skip %d : non posso riposizionarmi", _lasterr);
    }
  }
  _lasterr=DB_skip(fhnd, nrec);
  if (_lasterr != NOERR) 
    _lasterr = get_error(_lasterr);
  while (_lasterr ==_islocked) 
  { 
    const RecDes& r = prefix().get_recdes(num());
    TString256 key;
    __build_key(r, _curr_key, curr().string(), key.get_buffer(), TRUE);
     message_box("Codice %s in uso da parte\ndi un altro utente.\nSkipping", (const char*)key);
    _lasterr = cisread(fhnd, getkey(), curr(),_iscurr + lockop, _recno);
  } 
  _recno = DB_recno(fhnd);
  if (curr().has_memo())
    curr().init_memo(_recno, _isam_handle);
  
  return _lasterr;
}

// funzione  di lettura dei file
int TBaseisamfile::_read(TRectype& rec, word op, word lockop)
{
  const TCodeb_handle fhnd = handle();
  rec.setdirty();

  _lasterr = cisread(fhnd, getkey(), rec, op + lockop, _recno);
   
  // _recno = DB_recno(fhnd); // Gia' fatto nella cisread
  
	if(rec.has_memo())
    rec.init_memo(_recno, _isam_handle);

  if (_lasterr == NOERR)
  {
    if (lockop == _lock || lockop == _testandlock)
      prefix().lock_record(_isam_handle, _recno); else
    if (lockop == _unlock)
      prefix().unlock_record(_isam_handle, _recno);
  }

  return _lasterr;
}

int TBaseisamfile::read(TRectype& rec, word op, word lockop)
{
  _lasterr=rec.read(*this, op, lockop);
  return _lasterr;
}

int TBaseisamfile::read(word op, word lockop)
{
  return TBaseisamfile::read(curr(), op, lockop);
}

int TBaseisamfile::readat(TRectype& rec, TRecnotype nrec, word lockop)
{
  return rec.readat(*this, nrec, lockop);
}

int TBaseisamfile::readat(TRecnotype nrec, word lockop)
{
  return TBaseisamfile::readat(curr(), nrec, lockop);
}

int TBaseisamfile::_readat(TRectype& rec, TRecnotype nrec, word lockop)
{
  const int fhnd = handle();
  rec.setdirty();
  _lasterr = DB_go(fhnd, nrec);                             
  if (_lasterr != NOERR) 
    _lasterr = get_error(_lasterr);
  else
    rec = (const char *) DB_getrecord(fhnd);
  _recno = DB_recno(fhnd);
  if (rec.has_memo())
    rec.init_memo(_recno, _isam_handle);
  return _lasterr;
}

int TBaseisamfile::_write(const TRectype& rec)
{
  CHECK(!rec.empty(), "Can't write an empty record");

  // Controlla che la chiave sia piena
  TString256 key;

  __build_key(rec.rec_des(), 1, rec.string(), key.get_buffer(), TRUE);
  if (key.blank())
    return _iskeyerr;

  if (dongle().demo())
  {
    const int logicnum = num();
    if ((logicnum > LF_COMUNI && logicnum < LF_ANALISI) || logicnum > LF_RELANA)
    {
      if (items() > 979L)
        return _isfilefull;
    }
  }

  // Forza l'uso della chiave principale (per chiavi duplicate?)
  const int fhnd = handle(_curr_key > 0 ? 1 : 0);
  
  const int dst_len = DB_reclen(fhnd);
  if (dst_len != rec.len())
    NFCHECK("Record size mismatch on file %d: RecDes=%d, DB_reclen=%d", 
            _logicnum, rec.len(), dst_len);

  browse_null(rec.string(), dst_len);
  memcpy(DB_getrecord(fhnd), rec.string(), dst_len);           
  _lasterr = DB_add(fhnd);
  _recno = DB_recno(fhnd);
  
  if (_lasterr == NOERR) 
  { 
    if (rec.has_memo())
      ((TRectype&)rec).write_memo(_isam_handle, _recno );
  }
  else
    _lasterr = get_error(_lasterr);
  
  return _lasterr;
}

int TBaseisamfile::write(const TRectype& rec)
{
  return rec.write(*this);
}

int TBaseisamfile::write()
{                 
  return TBaseisamfile::write(curr());
}

int TBaseisamfile::write_rewrite(const TRectype& rec)
{                 
  return rec.write_rewrite(*this);
}

int TBaseisamfile::write_rewrite()
{                 
  return TBaseisamfile::write_rewrite(curr());
}

int TBaseisamfile::rewrite_write(const TRectype& rec)
{                 
  return rec.rewrite_write(*this);
}

int TBaseisamfile::rewrite_write()
{                 
return TBaseisamfile::rewrite_write(curr());
}

int TBaseisamfile::_rewrite(const TRectype& rec)
{
  CHECK(!rec.empty(), "Can't write an empty record");

  TRectype save_rec(rec);

  // Forza l'uso della chiave principale (per chiavi duplicate?)
  const int fhnd = handle(1);
  _lasterr = cisread(fhnd, 1, save_rec, _isequal + _nolock, _recno); // Si Posiziona per sicurezza...
  
  if (_lasterr == NOERR)
  {
    const int len = DB_reclen(fhnd);
    if (len != save_rec.len())
      NFCHECK("Record size mismatch on file %d: RecDes=%d, DB_reclen=%d", 
              _logicnum, save_rec.len(), len);

    browse_null(rec.string(), len);
    if (memcmp(rec.string(), save_rec.string(), len) != 0)
    {
      memcpy(DB_getrecord(fhnd), rec.string(), len); 
      _lasterr = DB_rewrite(fhnd);
      if (_lasterr == NOERR) 
        rec_cache(_logicnum).notify_change();
      else
        _lasterr = get_error(_lasterr);
    }
    else
      DB_unlock(fhnd);  // non vale la pena farlo sempre ?
    _recno = DB_recno(fhnd);
    prefix().unlock_record(_isam_handle, _recno);

    if (_lasterr == NOERR)
    {
      if (curr().has_memo())
        ((TRectype&)rec).write_memo(_isam_handle, _recno);
    }
  }    

  return _lasterr;
}

int TBaseisamfile::rewrite(const TRectype& rec)
{
  return rec.rewrite(*this);
}

int TBaseisamfile::rewrite()
{
  return TBaseisamfile::rewrite(curr());
}

int TBaseisamfile::rewriteat(const TRectype& rec, TRecnotype nrec)
{
  const int fhnd = handle();

  if ((_lasterr=DB_go(fhnd,nrec))== NOERR)
  {                                      
    browse_null(rec.string(),DB_reclen(fhnd));
    memcpy(DB_getrecord(fhnd),rec.string(),DB_reclen(fhnd));
    _lasterr=DB_rewrite(fhnd);
    if (_lasterr != NOERR) _lasterr = get_error(_lasterr);
  } 
  else
    _lasterr = get_error(_lasterr);
  _recno = DB_recno(fhnd);

  if(_lasterr == NOERR)
  {
    if (curr().has_memo())
      ((TRectype &)rec).write_memo(_isam_handle, _recno);
    rec_cache(_logicnum).notify_change();
  }

  return _lasterr;
}

int TBaseisamfile::rewriteat(TRecnotype nrec)
{
  return TBaseisamfile::rewriteat(curr(),nrec);
}

int TBaseisamfile::_remove(const TRectype& rec)
{
  CHECK(!rec.empty(), "Can't remove an empty record");

  const int fhnd = handle(1); // Forza l'uso della chiave principale (per chiavi duplicate?)

  TRectype save_rec(rec);
  _lasterr = cisread(fhnd, 1, save_rec, _isequal + _nolock, _recno); // Si Posiziona per sicurezza...

  if (_lasterr == NOERR)
  {
    _lasterr = DB_delete(fhnd);   // Put only deletion flag on record, must remove keys too!
    if (_lasterr == NOERR)         
    {
      if (curr().has_memo( ))
        curr().init_memo();
      rec_cache(_logicnum).notify_change();
      DB_flush(fhnd);
    }
    else
      _lasterr = get_error(_lasterr);
  }
  
  if (_lasterr != NOERR)         
    DB_recall(fhnd);

  return _lasterr;
}

int TBaseisamfile::remove(const TRectype& rec)
{
  return rec.remove(*this);  
}

int TBaseisamfile::remove()
{
  return TBaseisamfile::remove(curr());
}

int TBaseisamfile::lock()
{
  const int fhnd = handle();
  _lasterr = DB_lockfile(fhnd);
  if (_lasterr != NOERR) 
    _lasterr = get_error(_lasterr);
  return _lasterr;
}

int TBaseisamfile::unlock()  
{
  const int fhnd = handle();
  _lasterr = DB_unlock(fhnd);
  if (_lasterr != NOERR) 
    _lasterr = get_error(_lasterr);
  return (_lasterr);
}

void TBaseisamfile::indexon()
{
}

void TBaseisamfile::indexoff()
{
}

bool TBaseisamfile::empty()
{ 
  return items() <= 0;
}

// @doc EXTERNAL

// @mfunc Apre il file isam di base con lock
//
// @rdesc Ritorna NOERR se e' riuscita ad aprire il file, altrimenti ritorna il numero di errore
//    generato (vedi <t TIsamerr>).
int TBaseisamfile::_open(
    unsigned int mode,  // @parm Indica il modo di apertura del file (default _manulock)
    bool index)         // @parm Indica se aprire con indici o meno  (default TRUE)

// @comm Il parametro <p mode> puo' assumere i valori:
//
// @flag _manulock | Il lock dei record viene fatto manualmente
// @flag _exclock | Il file viene aperte in modo esclusivo
// @flag _autolock | Il lock dei record viene fatto in modo automatico
// @comm Il parametro <p index> puo' assumere i valori:
//
// @flag TRUE  | Il file viene aperto con indici
// @flag FALSE | Il file viene aperto senza indici
  
{
  CHECKD(_isam_handle == 0, "Can't reopen file ", _logicnum);
  _curr_key = index ? 1 : 0;

  TFilename filename;
  _isam_handle = prefix().open_isamfile(_logicnum, filename, mode==_excllock, index);
  if (_isam_handle > 0)
  {                       
		TCodeb_handle cb_handle = prefix().get_handle(_isam_handle, _curr_key);
    const int dbfreclen = DB_reclen(cb_handle);
    const int trcreclen = prefix().get_reclen(_logicnum);
    const TRecnotype n = DB_reccount(cb_handle);
    
    if (dbfreclen != trcreclen)        
    {
      TString msg;
      msg.format("Lunghezza record incoerente sul file %d (%s): file=%d trc=%d",
                 num(), (const char*)filename, dbfreclen, trcreclen);
      if (n == 0)
      {                
        msg << "\nSi consiglia di eliminare il file ed i suoi indici.";
        error_box(msg);
      }
      else
        fatal_box(msg);
    }            
    if (prefix().get_recdes(_logicnum).NKeys <= 0)        
      fatal_box("Il file %d (%s) e' senza indici", num(), (const char*)filename);
    _recno = RECORD_NON_FISICO;
    setkey(_curr_key);
    _lasterr = NOERR;
  }  
  else            
  {   
    TString e_msg;
    _lasterr = get_error(_isam_handle);
    if (_lasterr == -60)
    {
      if (!filename.exist())
        e_msg.format("Il file %d(%s) non esiste, errore %d",num(),(const char*)filename,_lasterr);
      else
        e_msg.format("Il file %d(%s) e' aperto in modo esclusivo da un'altra applicazione",
                     num(), (const char*)filename);
    }
    if (e_msg.empty())
      e_msg.format("Il file %d(%s) non puo' essere aperto, errore %d",num(),(const char*)filename,_lasterr);
    fatal_box((const char*) e_msg);
  }
  return _lasterr;
}

int TBaseisamfile::_open_ex(
    unsigned int mode,  // @parm Indica il modo di apertura del file (default _manulock)
    bool index)         // @parm Indica se aprire con indici o meno  (default TRUE)

// @comm Il parametro <p mode> puo' assumere i valori:
//
// @flag _manulock | Il lock dei record viene fatto manualmente
// @flag _exclock | Il file viene aperte in modo esclusivo
// @flag _autolock | Il lock dei record viene fatto in modo automatico
// @comm Il parametro <p index> puo' assumere i valori:
//
// @flag TRUE  | Il file viene aperto con indici
// @flag FALSE | Il file viene aperto senza indici
  
{
  CHECKD(_isam_handle == 0, "Can't reopen file ", _logicnum);
  _curr_key = index ? 1 : 0;

  TFilename filename;
  _isam_handle = prefix().open_isamfile(_logicnum, filename, mode==_excllock, index);
  if (_isam_handle > 0)
  {                
    if (prefix().get_reclen(_logicnum) > 0)
    {
      const int fhnd = handle();
      if (fhnd >= 0)
      {
        _recno = RECORD_NON_FISICO;
        _lasterr = NOERR;
      }
      else
        _lasterr = get_error(fhnd);
    }
    else
      _lasterr = _isbadtrc;
  }  
  else            
  {
    _isam_handle = 0;
    _lasterr = get_error(_isam_handle);
  }
  return _lasterr;
}

int TBaseisamfile::_close()
{
  int err = NOERR;
  if (prefix_valid())
  {
    err = prefix().close_isamfile(_isam_handle);
    setstatus(err);
  }
  return err;
}

int TBaseisamfile::is_valid(bool exclusive)
{ // Ritorna NOERR se il file puo' essere aperto senza errori
  CHECKD(_isam_handle == 0, "Can't reopen file ", _logicnum);
  TFilename filename;
  TIsam_handle isam_handle = prefix().open_isamfile(_logicnum, filename, exclusive, TRUE);
  TCodeb_handle fhnd = isam_handle > 0 ? prefix().get_handle(isam_handle,1) : isam_handle;

  int err = NOERR;
  if (fhnd < 0)
    err = get_error(fhnd);
  else
  {
    const int trcreclen = prefix().get_reclen(_logicnum);
    if (trcreclen > 0)
		{
			if (DB_tagget(fhnd) == -1)
				err = _ispatherr;
	    else
		  {
		    const int dbfreclen = DB_reclen(fhnd);
			  if (dbfreclen != trcreclen)
				  err = _istrcerr;
			}    
		}
    else
      err = _isbadtrc;
  }
  if (isam_handle > 0)
    prefix().close_isamfile(isam_handle);
  return err;
}

bool TBaseisamfile::get_relapp(TString& app) const
{
   return curr().get_relapp(app);
}

bool TBaseisamfile::is_changed_since(long& last) const
{ 
  bool yes = false; 
     
#ifdef WIN32
  const int fh = ::_sopen(filename(), _O_RDONLY, _SH_DENYNO);
  if (fh > 0)
  {     
    struct stat stat;
    if (::fstat(fh, &stat) == 0)
    {
      const long tim = long(stat.st_mtime) ^ long(stat.st_size);
      yes = tim != last;
      last = tim;
    }
    ::_close(fh);
  }
#else
  struct stat stat;
  if (lstat(filename(), &stat) == 0)
  {
    const long tim = long(stat.st_mtime) ^ long(stat.st_size);
    yes = tim != last;
    last = tim;
  }
#endif
  
  return yes;
}

///////////////////////////////////////////////////////////
// TLocalisamfile
///////////////////////////////////////////////////////////

// @doc EXTERNAL

// @mfunc Costruttore
//
// @rdesc Ritorna l'oggetto <c TLocalisamfile>
TLocalisamfile::TLocalisamfile(
                int logicnum)     // @parm Numero del logico del file
              : TBaseisamfile(logicnum)
{             
  if (open() != NOERR)
    fatal_box("Impossibile aprire il file %d", logicnum);
}

// @mfunc Costruttore
//
// @rdesc Ritorna l'oggetto <c TLocalisamfile>
TLocalisamfile::TLocalisamfile(
                const char* name,     // @parm Nome del file esterno da aprire
                const char* descname) // @parm Indica la descrizione del file
: TBaseisamfile(name, descname)
{
}

TLocalisamfile::TLocalisamfile(int logicnum, bool tmpfile)
              : TBaseisamfile(logicnum)
{
  CHECK(tmpfile == TRUE, "Protected constructor badly used");
}
 

TLocalisamfile::~TLocalisamfile()
{         
  close();
}
             
int TLocalisamfile::close()
{
  return TBaseisamfile::_close();
}

int TLocalisamfile::open(unsigned int mode)
{
  return TBaseisamfile::_open(mode);
}

int TLocalisamfile::operator +=(const TRecnotype npos)
{ 
  skip(npos);
  return status();
}


int TLocalisamfile::operator -=(const TRecnotype npos)
{
  skip(-npos);
  return status();
}


int TLocalisamfile::operator ++()
{
  next();
  return status();
}


int TLocalisamfile::operator --()
{
  prev();
  return status();
}



TIsamfile::TIsamfile(int logicnum) 
         : TBaseisamfile(logicnum) 
{ }


TIsamfile::~TIsamfile()
{
  close();
}

///////////////////////////////////////////////////////////
// TIsamtempfile
///////////////////////////////////////////////////////////

TIsamtempfile::TIsamtempfile(int logicnum, const char* radix, bool create, bool autodel)
             : TLocalisamfile(logicnum, TRUE) 
{                             
  TRecnotype eod = 0;
  TRecnotype eox = 100;

  TFilename n; 
  
  if (radix && *radix)
  {
    if (*radix == '%') 
      n = radix + 1;
    else
    {
      n.tempdir(); 
      n.add(radix); 
    }
    n.ext("dbf");
  }  
  else
    n.temp(NULL, "dbf");
  
  if (!create)
  {      
    const long sz = fsize(n);
    if (sz == 0)
      fatal_box("Impossibile aprire il file %s: %s",(const char*)n, (const char*)_strerror(NULL));

    const TDir dir(logicnum);
    const long len = dir.len();  
    eod = eox = sz / len;
  }
  
  CHECK(create == FALSE || create == TRUE, "Il flag di autodel ora si setta con il terzo parametro del costruttore");
  _autodel = autodel || (create != FALSE && create != TRUE);

  n.insert("%", 0);

  open(n, create, eod, eox);
}


TIsamtempfile::~TIsamtempfile()
{
  close();
}

// @doc EXTERNAL

// @mfunc Apre il file
//
// @rdesc Ritorna NOERR se e' riuscita ad aprire il file, altrimenti ritorna il numero di errore
//    generato (vedi <t TIsamerr>).
int TIsamtempfile::open(
    const char* radix,  // @parm Radice del path del file
    bool create,        // @parm Indica se va creato un nuovo file (se FALSE il file esiste gia')
    TRecnotype eod,     // @parm Numero di record presenti nel file
    TRecnotype eox)     // @parm Numero di record da aggiungere al file

// @comm Nel case <p create> sia TRUE allora viene settato automaticamente il valore di <p _autodel>
//   a TRUE, cioe' viene abilitata l'eliminazione del file in chiusura.
  
{
  int err = NOERR;
  
  TFilename filename;
  if (radix[0] == '%')
    filename = radix+1;
  else
    filename.temp(radix);
  filename.ext("");  

  CHECKS(_isam_handle == 0, "File already open ", (const char*)filename);

  if (create)
  {
    RecDes* r = (RecDes*)&prefix().get_recdes(num());
    err = DB_build(filename, r) ;
    if (err != NOERR) 
    {                 
      err = get_error(err);     
// dalla 1.5 serve ?      relisfd(_isamfile);
      fatal_box("Can't create temp file '%s' num. %d: Error n. %d", (const char*)filename, num(), err);
    }
  }

  _isam_handle = prefix().open_isamfile(_logicnum, filename);
  TCodeb_handle fhnd = handle(_curr_key = 1);
  if (fhnd < 0) 
    err = get_error(fhnd);
  if (err != NOERR)
  {
    filename.ext("dbf");
    if (err == -60)
    {
      if (!filename.exist()) 
        fatal_box("Apertura file %s : errore n. %d. File non esistente.",(const char*)filename,err);
      else
        fatal_box("Apertura file %s : errore n. %d. File aperto in uso esclusivo da un'altra applicazione.",(const char*)filename,err);
    }
    else 
      fatal_box("Apertura file %s : errore n. %d ",(const char*)filename,err);
  }

  _recno = RECORD_NON_FISICO;
  setstatus(err);
  return err;
}


int TIsamtempfile::close()
{
  TFilename f = filename();
  int err = prefix().close_isamfile(_isam_handle);
  if (err == NOERR && _autodel)
  {
    const long c = DB_getconf();

    f.ext("dbf");
    ::remove(f);

    if (c & 1)
      f.ext("fpt");
    else
      f.ext("dbt");
    ::remove(f);

    if (c & 1)                                // FOXPRO format
      f.ext("cdx"); 
    if (c & 4)                                // DBIV format
      f.ext("mdx");
    if (c & 8 || c & 2)                       // CLIPPER and DBIII format
    {
      f.ext("cgp");
      FILE *fp=fopen(f,"r");
      char in[16];
      while (fgets(in,16,fp)!=NULL)
      {
        TFilename a(in);
        if (c & 8)                    // DBIII format
          a.ext("ndx");
        else
          a.ext("ntx");       // CLIPPER format
        ::remove((const char *)a);
      }
      fclose(fp);
    }
    ::remove(f);        
  
		if (curr().has_memo()) // Cancella eventuale file dei memo
    {
      f.ext("fpt");
      ::remove(f);        
    }
  }

  setstatus(err);
  return err;
}

///////////////////////////////////////////////////////////
// TExternisamfile
///////////////////////////////////////////////////////////
TExternisamfile::TExternisamfile(const char* name, bool exclusive, bool index)
: TLocalisamfile(name) 
{
  init(name, exclusive, index);                             
}

TExternisamfile::TExternisamfile(const char* name, const char* descname, bool exclusive, bool index)
: TLocalisamfile(name, descname)
{
  init(name, exclusive, index);                             
}

TExternisamfile::~TExternisamfile()
{
  close();
}

void TExternisamfile::init(const char* name, bool exclusive, bool index)
{
  _name = name;
  _name.ext("dbf");
  
// Espande il nome!
  const char c = _name[0];
  if (c == '%' || c == '$')
    _name = CAddPref(_name.get_buffer());
  else
    if (c == '#')
    {
      _name.ltrim(1);
      _name.format("%s/%s",__ptprf,(const char*)_name);
    } 
  open(exclusive, index);
}

int TExternisamfile::open(bool exclusive, bool index)
{
  _isam_handle = prefix().open_isamfile(_logicnum, _name, exclusive, index);
  if (_isam_handle > 0)
  {                       
    if (prefix().get_recdes(_logicnum).NKeys <= 0)        
      fatal_box("Il file %d (%s) e' senza indici", num(), (const char*)filename());
    _recno = RECORD_NON_FISICO;
    setkey(1);
    _lasterr = NOERR;
  }  
  else            
  {   
    TString e_msg;
    _lasterr = get_error(_isam_handle);
    if (_lasterr == -60)
    {
      if (!_name.exist())
        e_msg.format("Il file %d(%s) non esiste, errore %d",num(),(const char*)_name,_lasterr);
      else
        e_msg.format("Il file %d(%s) e' aperto in modo esclusivo da un'altra applicazione",
                     num(), (const char*)_name);
    }
    if (e_msg.empty())
      e_msg.format("Il file %d(%s) non puo' essere aperto, errore %d",num(),(const char*)_name,_lasterr);
    fatal_box((const char*) e_msg);
  }
  return _lasterr;
}

int TExternisamfile::close()
{
  return _close();
}

int TExternisamfile::zap()
{                       
  RecDes rd = curr().rec_des();

  int err = prefix().close_isamfile(_isam_handle);
  if (err == NOERR)
  {
    err = DB_packfile(TRUE, _name, 0);
    if (err == NOERR)
    {
      TRecnotype peod;
      err = DB_packindex(TRUE, _name, &rd, &peod, FALSE);
    }       
	  _isam_handle = prefix().open_isamfile(_logicnum, _name);
  }

  return err;
}

const char* TExternisamfile::name() const
{     
  return filename();
}

///////////////////////////////////////////////////////////
// TSystemisamfile
///////////////////////////////////////////////////////////

int TSystemisamfile::build(const TTrec& r)
{
  int err = NOERR;
  const TDir d(num());
  if (r.len() > 0)
  {
    TFilename f(filename());
    TFilename fname(f); fname.ext("");  // sostituto per _filename

    f = f.path(); if (!is_not_slash(f.right(1)[0])) f.rtrim(1);
    if (!fexist(f)) 
      make_dir(f);

    err=DB_build(fname, &r.rec());
    if (err != NOERR) 
      err = get_error(err);
    setstatus(err);
     
    if (err == NOERR && __autoload)
    {
      TFilename lf;  
      lf.format("%sstd/lf%04d.txt", __ptprf, num());
      if (fexist(lf))
        load(lf, '|', '\0', '\n', TRUE, TRUE);
    }
  }
  else
  {
    NFCHECK("Can't create a file with empty field info");
    setstatus(_isbadtrc);
  }
  return err;
}

int TSystemisamfile::build()
{
  TTrec r;
  r.get(num());
  return build(r);
}

long TSystemisamfile::size(TRecnotype eox)
{
  return 51200L;
}

#ifndef FOXPRO

// @doc INTERNAL

// @mfunc Esegue la conversione del file
//
// @rdesc Ritorna il rusultato dell'operazione
//
// @flag TRUE | Se la conversione e' stata effettuata correttamente
// @flag FALSE | Se h stato rilevato un errore durante la conversione (viene emesso un <f error_box>)
int TSystemisamfile::exec_convapp(
     long flev,   // @parm Livello a cui aggiornare l'archivio
     const bool before) // @parm Indica se viene chiamata prima o dopo la conversione
  
{
  const char * const v = before ? "BCNV" : "ACNV";
  int err = 0;

  if (flev == 0) flev = 199401;
  else flev++;
  
  TConfig conv(CONFIG_FCONV);
  TString16 paragraph; 
  
  TString_array paralist; 
  conv.list_paragraphs(paralist);
  
  for (unsigned int l = flev; err == 0 && l <= prefix().get_stdlevel(); l++)
  {
    paragraph.format("%06ld", l);
    if (paralist.find(paragraph) < 0)
      continue;
    if (conv.set_paragraph(paragraph) && conv.exist(v, num()))
    {
      TToken_string s(conv.get(v, NULL, num()), ' ');
      TFilename f(s.get(0));
      f.ext(".exe");
      s << ' ' << prefix().get_codditta();
      TExternal_app app(s);
      if (f.exist())
      {
        err = app.run(FALSE, 0x3);    // Synchronous Spawn with User
        TMailbox mail;
        TMessage* msg = mail.next(TRUE);
        if (err == 0 && msg != NULL)
          err = atoi(msg->body());
      }
      if (err && err != 8)
        return error_box("Impossibile eseguire il programma di %sconversione\ndel livello %ld/%ld\nErrore n.ro %d", before ? "pre" : "post", l / 100, l % 100, err); 
    }
  }
  return err;
}

// @doc INTERNAL

// @mfunc Recupera le conversioni logiche da effettuare sul file
//
// @rdesc Ritorna TRUE se occorre effettuare la conversione sul file
bool TSystemisamfile::getlcf(
     long flev)  // @parm livello archivi di partenza della convesione
     
// @comm Recupera le conversioni logiche da effettuare sul file per per passare dal
//       livello archivi <p flev> a quello attuale degli archivi standard.
  
{
  _flds.destroy();
  _exps.destroy();
  if (flev == 0) flev = 199401;
  else flev++;

  TConfig conv(CONFIG_FCONV);
  TString16 paragraph; 
  
  TString_array paralist; 
  conv.list_paragraphs(paralist);

  for (unsigned int l = flev; l <= prefix().get_stdlevel(); l++)
  {
    paragraph.format("%06ld", l);
    if (paralist.find(paragraph) < 0)
      continue;
    
    if (conv.set_paragraph(paragraph) && conv.exist("F", num()))
    {
      TToken_string exprline(conv.get("F", NULL, num()));
      
      if (exprline.empty()) return FALSE;

      TToken_string w("", '=');
      const char * wexprs = exprline.get();

      while (wexprs != NULL)
      {
        w = wexprs;
        TFixed_string fld(w.get());
        _flds.add(new TFieldref(fld, 0));
        _exps.add(new TExpression(w.get(), _strexpr));
        wexprs = exprline.get();
      }
    }
  }
  return _flds.items() > 0;
}

void TSystemisamfile::makelc(TRectype& rec)
{
  for (int i = 0 ; i < _flds.items(); i++)
  {
    TFieldref& f = (TFieldref&) _flds[i];
    TExpression& e = (TExpression & )_exps[i]; 

    for (int k = 0; k < e.numvar(); k++)
      e.setvar(k, get(e.varname(k)));
    f.write(e.as_string(), rec);
  }
}

// @doc EXTERNAL

// @mfunc Esegue la conversione del tracciato record del file
//
// @rdesc Ritorna il risulato della conversione, altrimenti il codice di errore generato 
//        (vedi <t TIsamerr>)

int TSystemisamfile::update(
    const TTrec& newrec, // @parm Nuovo tracciato record con cui aggiornare il file
    bool interactive)    // @parm Indica se riportare i campi personalizzati (!interactive
  
{        
  if (newrec.len() == 0)
  {
    error_box(FR("Il nuovo tracciato per il file %d e' vuoto"), num());
    setstatus(_istrcerr);
    return status();
  }
  
  int err = NOERR;
  
  TTrec wrec(newrec);
  TDir dir;

  dir.get(num(), _unlock, _nordir, _sysdirop);
  const bool is_com = prefix().is_com();
  const bool toconvert = is_com ? dir.is_com() : dir.is_firm();

  TTrec oldrec(num(), is_com ? _comdir : _nordir);
  if (oldrec.fields() < 0 || oldrec.fields() > MaxFields)
  {
    if (yesno_box(FR("Il file %d (%s)\n"
                     "ha %d campi ed una lunghezza record di %d"
                     "\nSi desidera azzerare il vecchio tracciato?"), 
                     num(), (const char*)filename(), oldrec.fields(), oldrec.len()))
    {
      oldrec.zero();
    }
    else
    {
      setstatus(_istrcerr);
      return status();
    }
  }

  int lenr = wrec.len();
  if (lenr != 0)
  {
    const long lev = prefix().filelevel();
    const bool lcf = getlcf(lev);

    if (toconvert)
    {
      err = exec_convapp(lev, TRUE);  // Pre-conversion
      if (err != NOERR)
      {
        setstatus(err);
        return err;
      }
    
      if (!lcf && wrec == oldrec)
      {
        err = exec_convapp(lev, FALSE); // Post-conversion (SOLO se il record e' rimasto uguale)
        setstatus(err);
        return err;
      }
    }
    else
    {
      if (dir.eox() > 0L)
        dir.reset_eox();
    }

		const int oldfields = oldrec.fields();	//numero campi del vecchio file
		const int wfields = wrec.fields();			//numero campi del nuovo file
		int newfields = wfields;								//numero campi del nuovo file compresi quelli personalizzati (_)
		TToken_string def;											//riga del file .trr nome|tipo|lungh|dec

		for (int j = 0; j < oldfields; j++)
    {
			def = oldrec.fielddef(j);
			const TString16 fname = def.get(0);		//nome e basta del campo
			if (!interactive && fname[0] == '_')
			{
	      if (wrec.field(fname) == FIELDERR)
        {
				  if (newfields < MaxFields)
					{
					  wrec.update_fielddef(newfields++, def);
      			wrec.set_fields(newfields);
			      wrec.rehash();
          }
				  else
          {
						if(!yesno_box("Il campo %s non verra' preservato, devo continuare?",
								     	  	(const char*)fname))
						  return NOERR;
          }
        }
			}
		}
		if (wfields < newfields)
		{
			wrec.set_fields(newfields);
			wrec.rehash();
	    lenr = wrec.len();
		}
    TFilename fname;
    if (toconvert)
      fname = filename();

    if (toconvert && (dir.eox() > 0L || fname.exist()))
    {
      TRecnotype ni = 0L;     
      TFilename tmpfname; tmpfname.temp("tf");  
      
      err=DB_build(tmpfname, &wrec.rec());
      
      if (err != NOERR) 
      {
        err=get_error(err);
        return (err);
      }

      if (dir.eod() > 0 && oldrec.len() > 0)
      {
        // Apro il file destinazione in modo esclusivo e senza indici
        //int tmpnum = num();
        //TIsam_handle ishandle = prefix().open_isamfile(tmpnum, tmpfname, true, false);
        // TCodeb_handle fhnd = ishandle > 0 ? prefix().get_handle(ishandle) : ishandle;
        TCodeb_handle fhnd = DB_open(tmpfname, 1, 0);
        if (fhnd < 0) 
        {
          err = get_error(fhnd);
          return err;
        }

        err = _open_ex(_excllock, false);
        if (err != NOERR) 
          return err;
    
        TString s(256); s << TR("Aggiornamento") << ' ' << fname;

        const TRecnotype nitems = items();
        TProgind p(nitems > 0 ? nitems : 1, s, is_power_station(), true);

        TExtrectype nrec(wrec);
  
        const int nflds = curr().items();
        TArray infld, outfld;
        int j;
        for (j = 0; j < nflds; j++)
        {
          const char* fld_name = curr().fieldname(j);
          infld.add(new TRecfield(curr(), fld_name), j);
          if (nrec.exist(fld_name))
            outfld.add(new TRecfield(nrec, fld_name), j);    
        }
        const bool memo_inside = rec_has_memo(nrec.rec_des()); 
        for (int errore = first(); errore == NOERR; errore = next())
        {
          if (!p.addstatus(1))
          {
            err = _iseof; // Simula errore in caso di interruzione
            break;
          }
          ni++;

          if (curr().isdeleted())
            continue;
          
          nrec.zero();
          for (j = outfld.last(); j >= 0; j = outfld.pred(j))
          {
            TRecfield* in_fld  = (TRecfield*)infld.objptr(j);
            TRecfield* out_fld = (TRecfield*)outfld.objptr(j);
            if (in_fld != NULL && out_fld != NULL)
            {
              char* fld_val = (char*)(const char*)*in_fld;
              if (out_fld->type() != _datefld && out_fld->type() != _memofld)
              {
                const int l1 = out_fld->len();
                const int l2 = strlen(fld_val);
                if (l1 < l2)
                {
                  if (out_fld->type() != _alfafld)
                    *fld_val = '\0';
                  else
                    fld_val[l1] = '\0';
                }
              }
              *out_fld = fld_val;
            }
          }
          if (lcf)
            makelc((TRectype &)nrec);
          browse_null(nrec.string(),lenr); 
          memcpy(DB_getrecord(fhnd),nrec.string(),lenr);  
          err=DB_add(fhnd);
          if ( err == NOERR && memo_inside)
          {
            // nrec.write_memo(ishandle, DB_recno(fhnd)); 
            for (j = outfld.last(); j >= 0; j = outfld.pred(j))
            {
              const TRecfield* in_fld  = (const TRecfield*)infld.objptr(j);
              const TRecfield* out_fld = (const TRecfield*)outfld.objptr(j);
              if (in_fld != NULL && out_fld != NULL && out_fld->type() == _memofld)
              {
                const TFixed_string val(*in_fld);
                if (val.full())
                  DB_memowrite(fhnd, out_fld->name(), val);
              }
            }
            DB_flush(fhnd);
          }
          if (err != NOERR) 
            err=get_error(err);
          setstatus(err);

        }
        close();
        //prefix().close_isamfile(ishandle);
        DB_close(fhnd);

        if (err != NOERR) 
          err = get_error(err);
        if (p.iscancelled())
          err = _iseof;

        p.setstatus(nitems);
      }

      if (err == NOERR)
      {           
        long c = DB_getconf();
        fname.ext("dbf");
        tmpfname.ext("dbf");
        fcopy(tmpfname, fname);
        ::remove(tmpfname);
        if (c & 1)
          tmpfname.ext("fpt");
        else
          tmpfname.ext("dbt");
        if (tmpfname.exist())
        {
          if (c & 1)
            fname.ext("fpt");
          else
            fname.ext("dbt");
          fcopy(tmpfname, fname);
          ::remove(tmpfname);
        }
        if (c & 1)                        // FOXPRO format
          tmpfname.ext("cdx");
        if (c & 4)                        // DBIV format
          tmpfname.ext("mdx");
        if (c & 8 || c & 2)
        {                   
          tmpfname.ext("cgp");
          TFilename a;    
          FILE *fp=fopen(tmpfname,"rb");
          while (fgets(a.get_buffer(),16,fp) != NULL)
          {
            a.rtrim(1); // Cut \n
            if (c & 8)                    // DBIII format
              a.ext("ndx");
            else
              a.ext("ntx");       // CLIPPER format
            ::remove(a);
          }
          fclose(fp);
        }
        ::remove(tmpfname);            
        dir.set_eod(ni);
      }
    }
    if (err==NOERR)
    {    
      dir.set_len(lenr);
      dir.put(num(), _nordir, _sysdirop);
      wrec.put(num());
      prefix().update_recdes(num());

      if (toconvert)
        packindex();
      if (err == NOERR)
        err = exec_convapp(lev, FALSE);  // Post - conversion
    }
  }

  setstatus(err);
  return err;
}

// @doc EXTERNAL

// @mfunc Rimuove fisicamente i record cancellati
//
// @rdesc Ritorna NOERR se l'operazione di compattamento e' riuscita, altrimenti il codice di
//    di errore generato (vedi <t TIsamerr>).
int TSystemisamfile::packfile(
    bool vis, // @parm Indica se visualizzare lo stato dell'operazione
    bool zap) // @parm Indica se distruggere tutti i records

// @xref <mf TSystemisamfile::packindex>
  
{
  TFilename fname = filename(); fname.ext("");

  TRecnotype new_eod = 0L;
  if (!zap)
  {
    TDir d(num()); // Chi commenta muore!
    d.get(num(),_nolock, (d.is_com()) ? _comdir : _nordir, _sysdirop);
    new_eod = d.eod();
  }
  
  int err = DB_packfile(vis, fname, new_eod);
  
  if (zap && err == NOERR)
    err = packindex(vis, FALSE);
  if (err == NOERR && curr().has_memo())
    err = DB_packmemo(vis,fname);
  
  if (err != NOERR) 
  {
    err = get_error(err);
    if (err != NOERR) 
      error_box("Errore in compattamento dati.\nFile %d : %d", num(), err);
  }    
  setstatus(err);
  return err;
}

int TSystemisamfile::zap()
{
  safely_close_closeable_isamfiles();
  return packfile(true, true);
}

// @doc EXTERNAL

// @mfunc Rimuove fisicamente gli indici cancellati
//
// @rdesc Ritorna NOERR se l'operazione di compattamento e' riuscita, altrimenti il codice di
//    di errore generato (vedi <t TIsamerr>).
int TSystemisamfile::packindex(
    bool vis, // @parm Indica se visualizzare lo stato dell'operazione
    bool ask) // @parm Indica se chiedere il recupero dei record duplicati

// @xref <mf TSystemisamfile::packfile>
  
{
  const TTrec r(num());                
  TDir d(num());
  const bool is_com = d.is_com();
  d.get(num(),_nolock, is_com ? _comdir : _nordir, _sysdirop);
  
  TFilename name = d.filename(); name.ext("");
  TRecnotype peod;
  int err=DB_packindex(vis,name,&r.rec(),&peod,ask);
  if (err != NOERR) 
    err = get_error(err);
  if (err != NOERR) 
  {
    if (vis)
      error_box("Errore in compattamento indici.\nFile %d : %d", num(),err);
  }
  else
    if (peod >= 0 && peod != d.eod())
    {
      d.set_eod(peod);
      d.put(num(), is_com ? _comdir : _nordir);
    }

  setstatus(err);
  return err;
}

void TSystemisamfile::update_file()
{
  TFilename fname;
  int logicnum = _logicnum;
  int isam_handle = prefix().open_isamfile(logicnum, fname, FALSE, 1);
  if (isam_handle > 0)
  {                       
		TCodeb_handle cb_handle = prefix().get_handle(isam_handle, 1);
    const TRecnotype n = DB_reccount(cb_handle);    //numero di elementi del file

    TDir dir;
    dir.get(_logicnum, _lock, _nordir, _sysdirop);
    dir.set_eox(n);
    dir.set_eod(n);
    dir.put(_logicnum, _nordir, _sysdirop);
		prefix().close_isamfile(isam_handle);
  }
}

int TSystemisamfile::pack(bool vis, bool ask)
{
  int err = packfile(vis);
  if (err == NOERR)
    err = packindex(vis, ask); 
  setstatus(err);       
  return err;
}

// @doc EXTERNAL

// @mfunc Importa un file ascii
//
// @rdesc Ritorna NOERR se l'operazione di lettura e' riuscita, altrimenti il codice di
//    di errore generato (vedi <t TIsamerr>).
int TSystemisamfile::load(
    const char* from, // @parm Nome del file da importare 
    char fs,          // @parm Carattere separatore di campo (default <pipe>)
    char fd,          // @parm Carattere delimitatore di campi (default '\\0')
    char rs,          // @parm Carattere separatore di record (default '\\n')
    bool vis,         // @parm Indica se visualizzare lo stato dell'operazione (default TRUE)
    bool extended,    // @parm Indica se interpretare alcune stringhe come macro (default FALSE)
    bool indexed)     // @parm Indica se indicizzare subito o alla fine
    
// @comm Se <p extended> e' TRUE e trova alcune stringhe col formato %stringa% (es. %frm%)
//       ne sostituisce i valori (es. ditta corrente).
                                      
// @xref <mf TSystemisamfile::dump>
  
{
  int err=NOERR;
  FILE* fl = NULL; fopen_s(&fl, from, "r");
  if (fl == NULL) 
  {
    cantread_box(from);
    setstatus(2);
    return 2;
  }
  TRecnotype r = 0, e = 0, nitems = 0;
  TString8 firm, year, attprev("00000");

  if (extended)
  {
    const TDate d(TODAY);
    year.format("%04d", d.year());
    firm.format("%05ld", prefix().get_codditta());
    attprev = cache().get(LF_NDITTE, firm, NDT_CODATTPREV);
  }

  fseek(fl, 0L, SEEK_END);
  nitems = ftell(fl);
  fclose(fl);

  err = _open_ex(_excllock, indexed);
  if (err != NOERR)
  {
    TString msg; msg << _logicnum << " (" << name() << ')';
    cantread_box(msg);
    return err;
  }
  
  TScanner f(from);
  const bool fixedlen = (fs == '\0');
  TToken_string s(1024, fixedlen ? char(255) : fs);
  int nflds = curr().items();
  TString_array fld(nflds);
  TPointer_array fldlen(nflds);
  int reclen = 0;
  TString sfd(3);
  TString s1(256);
  bool lcf = FALSE;

  if (f.paragraph("Header"))
  {       
    int equal;
    TString key;
    nflds = 0;
    while ((equal = f.line().find('=')) > 0)
    {
      key = f.token().left(equal);
      key.trim();
      if (key == "File")
      {
        const int logic = atoi(f.token().mid(equal+1));
        if (logic != num())
        {
          error_box("L'archivio %s e' stato generato dal file %d", from, logic);
          return _isbadtrc;
        }
      } else
      if (key == "Fields")
      {                
        TToken_string riga(f.token().mid(equal+1));
        TToken_string wfd(32, ',');
        FOR_EACH_TOKEN(riga, fd)
        {
          wfd = fd; wfd.strip_spaces();
          fld.add(wfd.get(0));
          const int l = wfd.get_int();
          fldlen.add_long(l, nflds++);
          reclen += l;
        }
      }  
    }
  }
  
  if (nflds == 0 || fld.items() == 0)
  {   
    nflds = curr().items();
    for (int j = 0; j < nflds; j++)
    {
      fld.add(curr().fieldname(j), j);
      const TString& wfld = fld.row(j);
      const int l = (curr().type(wfld) == _datefld) ? 10 : curr().length(wfld);
      fldlen.add_long(l, j);
    }
  }
  
  if (!f.paragraph("Data"))
  {
    error_box("Formato file non valido: manca il paragrafo [Data]");
    close();
    err = 1;
    setstatus(err);
    return err;
  }
  
  if (fd) sfd << fd;
  int last = NOERR;    

  const char* const fmt = FR("Importazione archivio %d da %s\n%6ld records %6ld errori - %3d");
  
  s1.format(fmt, _logicnum, from, r, e, last);
  TProgind p(nitems, s1);

  while(!f.eof())
  {
    if (fixedlen)
      f.getline(s.get_buffer(reclen+2), reclen+2);
    else
      s = f.line();
    if (s.empty())
      break;

    if (extended)
    {
      int p, i;

      while ((p = s.find("%yr%")) >= 0)
        for (i = 0; i < 4; i++) s[p + i] = year[i];
      while ((p = s.find("%frm%")) >= 0)
        for (i = 0; i < 5; i++) s[p + i] = firm[i];
      while ((p = s.find("%att%")) >= 0)
        for (i = 0; i < 5; i++) s[p + i] = attprev[i];
    }
    if ((r + e) % 100 == 0)
    {
      s1.format(fmt, _logicnum, from, r, e, last);
      p.set_text(s1);
    }

    if (!p.setstatus((long)f.tellg()))
      break;
    
    zero();
    if (fixedlen)
    {
      if (s.len() < reclen)
      {
        error_box(FR("Record di lunghezza errata: %d invece di %d"), s.len(), reclen);
        break;
      }
      int pos = 0;
      for (int j = 0; j < nflds; j++)
      {
        const int l = fldlen.get_int(j);
        s1 = s.mid(pos, l);
        s1.rtrim();
        put(fld.row(j), s1);
        pos += l;
      }
    }
    else
    {
      s.restart();
      for (int j = 0; j < nflds; j++)
      {
        s1 = s.get();
        if (fd) 
        {
		      s1.rtrim(1);
		      s1.ltrim(1);
        }
        if (curr().type((const TString&) fld[j]) == _memofld)
  		  {
	  	    TString s2 = s1;

          s1 = esc(s2);
        }

        put((const TString&) fld[j], s1);
      }
    }           
    
    int err = write();
      
    if (err == NOERR)  
      r++;
    else 
    {
      e++;
      last = status();
    }
  }
  s1.format(fmt, _logicnum, from, r, e, last);
  p.set_text(s1);
  
  close();
  
  setstatus(err);

 // Devo reindicizzare alla fine
  if (err == NOERR && !indexed)
    packindex(vis, false);

//aggiorna lo sheet con i nuovi valori di EOX EOD caricati
  if (err == NOERR)
    update_file();

  return err;
}

// @doc EXTERNAL

// @mfunc Importa un file ascii
//
// @rdesc Ritorna NOERR se l'operazione di lettura e' riuscita, altrimenti il codice di
//    di errore generato (vedi <t TIsamerr>).
int TSystemisamfile::overwrite(
    const char* from, // @parm Nome del file da importare 
    char fs,    // @parm Carattere separatore di campo (default <pipe>)
    char fd,            // @parm Carattere delimitatore di campi (default '\\0')
    char rs,            // @parm Carattere separatore di record (default '\\n')
    bool vis)     // @parm Indica se visualizzare lo stato dell'operazione (default TRUE)
    
// @comm Se <p extended> e' TRUE e trova alcune stringhe col formato %stringa% (es. %frm%)
//       ne sostituisce i valori (es. ditta corrente).
                                      
// @xref <mf TSystemisamfile::dump>
  
{
  int err=NOERR;
  FILE* fl = fopen(from, "r");
  if (fl == NULL) 
  {
    error_box("Impossibile aprire il file %s",from);
    clearerr(fl);
    setstatus(2);
    return 2;
  }
  TRecnotype r = 0, e = 0, nitems = 0;
  TString16 firm, year, attprev("00000");

  fseek(fl, 0L, SEEK_END);
  nitems = ftell(fl);
  fclose(fl);

  err = _open_ex(_excllock);
  if (err != NOERR)
  {
    error_box("Impossibile aprire il file %d in modo esclusivo", _logicnum);
    return err;
  }
  
  TScanner f(from);
  bool fixedlen = (fs == '\0');
  TToken_string s(1024, fixedlen ? char(255) : fs);
  int nflds = curr().items();
  TString_array fld(nflds);
  TString_array keyfld(nflds);
  TAssoc_array vals;
  int len[MaxFields];
  TString sfd(3);
  TString s1(256);
  bool lcf = FALSE;

  if (f.paragraph("Header"))
  {       
    int equal;
    TString key;
    nflds = 0;
    while ((equal = f.line().find('=')) > 0)
    {
      key = f.token().left(equal);
      key.trim();
      if (key == "Version")
      {
        const unsigned int level = atoi(f.token().mid(equal+1));
        if (level > prefix().filelevel())
        {
          const unsigned int stdlevel = prefix().get_stdlevel();
          error_box(FR("L'archivio %s e' stato generato con gli archivi di livello %ld%/%ld.\n Il livello attuale e' %ld/%ld.\n Convertire gli archivi e ripetere l' operazione."), 
                    from, level/100, level%100, stdlevel/100, stdlevel%100);
        }
        lcf = getlcf(level);
      } else
      if (key == "File")
      {
        const int logic = atoi(f.token().mid(equal+1));
        if (logic != num())
          error_box("L'archivio %s e' stato generato dal file %d",
                     from, logic);
      } else
      if (key == "Fields")
      {                
        TToken_string riga(f.token().mid(equal+1));
        TToken_string wfd(32, ',');
        FOR_EACH_TOKEN(riga, fd)
        {
          wfd = fd; wfd.strip_spaces();
          fld.add(wfd.get(0));
          len[nflds++] = wfd.get_int();
        }
				const RecDes& rd = curr().rec_des();
				const KeyDes& kd = rd.Ky[0];

				for (int i = 0 ; i < kd.NkFields; i++)
				{
					const int nf = kd.FieldSeq[i] % MaxFields;
					const RecFieldDes& rf = rd.Fd[nf];  
					keyfld.add(rf.Name);
				}
      }  
    }
  }
  
  if (nflds == 0 || fld.items() == 0)
  {   
    nflds = curr().items();
    for (int j = 0; j < nflds; j++)
    {
      fld.add(curr().fieldname(j), j);
      const TString & wfld = fld.row(j);
      int l = 0;
      switch (curr().type(wfld))
      {
      case _datefld: l = 10; break;
      default      : l = curr().length(wfld); break;
      }
      len[j] = l;
    }
  }
  
  if (!f.paragraph("Data"))
  {
    error_box("Formato file non valido: manca il paragrafo [Data]");
    close();
    err = 1;
    setstatus(err);
    return err;
  }
  
  if (fd) sfd << fd;
  int last = NOERR;    
  
  s1.format("Importazione archivio %d da %s\n%6ld records %6ld errori - %3d", 
            _logicnum, from, r, e, last);
  TProgind p(nitems, s1, true, true, 78);
  long pos = 16*nflds;
  for (s = f.line(); s.not_empty() && !p.iscancelled(); s = f.line())
  {
    if ((r + e) % 50 == 0)
    {
      s1.format("Importazione archivio %d da %s\n%6ld records %6ld errori - %3d", 
                _logicnum, from, r, e, last);
      p.set_text(s1);
    }

    pos += s.len()+2;
    p.setstatus(pos);
    zero();
		vals.destroy();
    if (fixedlen)
    {
      int pos = 0;
      for (int j = 0; j < nflds; j++)
      {
				const TString & fldname = fld.row(j);

        s1 = s.mid(pos,len[j]);
        s1.rtrim();
        vals.add(fldname, s1);
        pos += len[j];
      }
    }
    else
    {
      s.restart();
      for (int j = 0; j < nflds; j++)
      {
				const TString & fldname = fld.row(j);

        s1 = s.get();
        if (fd) 
        {
		      s1.rtrim(1);
		      s1.ltrim(1);
        }
        if (curr().type(fldname) == _memofld)
  		  {
	  	    TString s2 = s1;
          s1 = esc(s2);
        }
        vals.add(fldname, s1);
      }
    }           
    
		FOR_EACH_ARRAY_ROW(keyfld, r0, kfldname)
		{
			const TString * value = (const TString *) vals.objptr(*kfldname);

			if (value != NULL)
				put(*kfldname, *value);
		}

		const bool nuovo = read() != NOERR;

		if (nuovo)
			zero();

      
    FOR_EACH_ASSOC_STRING(vals, obj, fldname, value)
			put(fldname, value);
		
		const int err = nuovo ? write() : rewrite();

		if (err == NOERR)  
      r++;
    else 
    {
      e++;
      last = status();
    }
  }
  s1.format("Importazione archivio %d da %s\n%6ld records %6ld errori - %3d", 
            _logicnum, from, r, e, last);

  p.set_text(s1);
  close();
  setstatus(err);
  //aggiorna lo sheet con i nuovi valori di EOX EOD caricati
  if (err == NOERR)
    update_file();

  return err;
}

// @doc EXTERNAL

// @mfunc Esporta VERSO un file ascii. 
//
// @rdesc Ritorna NOERR se l'operazione di esportazione e' riuscita, altrimenti il codice di
//    di errore generato (vedi <t TIsamerr>).
int TSystemisamfile::dump(
    const char* to, // @parm Nome del file verso quale esportare
    int nkey,     // @parm Numero della chiave di ordinamento con cui scaricare i dati (defualt 1)
    char fs,    // @parm Carattere seperatore di campo (defualt <pipe>)
    char fd,            // @parm Carattere delimitatore di campo (default '\\0')
    char rs,            // @parm Carattere separatore di record (default '\\n')
    bool vis,     // @parm Indica se visualizzare lo stato dell'operazione (defualt TRUE)
    bool withdeleted,// @parm Indica se scaricare anche i record cancellati (dafault FALSE)
    const char * filter,  // @parm Indica l'espressione filtro 
		SELECTIONFUNCTION select_func, // @parm funzione filtro 
		TObject * filter_obj) // @parm oggetto di filtro 

// @xref <mf TSystemisamfile::load>

{
  FILE* f = fopen(to, "w");

  if (f == NULL) 
  {
    setstatus(2);
    return 2;
  }

  if (withdeleted) nkey = 0;
  int err = ferror(f);
  
  open(FALSE, nkey ? TRUE : FALSE);
  TString s(512);
  bool fixedlen = (fs == '\0');
  int nflds = curr().items();
  TString_array  fld(nflds);
  TBit_array rjust(nflds);
  int        len[MaxFields];
  int j;

  for (j = 0; j < nflds; j++)
  {
    fld.add(curr().fieldname(j), j);
    const TString & wfld = fld.row(j);
    const TFieldtypes t = curr().type(wfld);
    rjust.set(j, t == _intfld || t == _longfld || t == _realfld ||
              t == _wordfld || t == _intzerofld || t == _longzerofld);
    len[j] = (t == _datefld) ? 10 : curr().length(wfld);
		if (fixedlen && t == _memofld)
    {
			error_box(TR("Non e' possibile scaricare a lunghezza fissa un file con campi memo"));
      return _isbadtrc;
    }
  }
  TRecnotype i = 0;
  const TRecnotype nitems = items();
  
  s.format(FR("Esportazione %s"), filename());
  TProgind p(nitems, s, TRUE, TRUE, 70);
  TString s1, sfld;

  fprintf(f, "[Header]\nVersion=%ld\nFile=%d", 
          (long) prefix().filelevel(), num());
  for (int k = 0; k < nflds; k++)
  {
    if ((k % 10) == 0) fprintf(f, "\nFields=");
    else fprintf(f, "|");
    fprintf(f, "%s,%d", (const char *) (const TString&) fld[k], len[k]);
  }
  fprintf(f, "\n\n[Data]\n");
  if (nkey)
  {
    setkey(nkey);
    for ( first(); status() == NOERR && !p.iscancelled(); next(), i++)
    {
      p.setstatus(i + 1);
      if (filter && *filter)
      {
        TToken_string filter_str(filter);
        TString16 fname;
        bool skip = false;
        while (!skip && !(fname=filter_str.get()).empty())
        {
          const char* fval = filter_str.get();
          const TString& cmp = get(fname);
          skip = cmp != fval;
        }
				skip |= (select_func != NULL) && (select_func(curr(), filter_obj) == false);
        if (skip)
          continue;
      }
      s.cut(0);
      for (j = 0; j < nflds; j++)
      {
        const TString& fname = fld.row(j);
        if (fixedlen)
        {
          s1 = get(fname);
          if (rjust[j]) s1.right_just(len[j]);
          else s1.left_just(len[j]);
        }
        else
        {
          s1.cut(0);
          if (j && fs) s1 << fs;
          if (fd) s1 << fd;
          sfld = get(fname); 
          switch (curr().type(fname))    
          {
          case _intfld:
          case _longfld:
          case _intzerofld:
          case _longzerofld:
          case _realfld:
            if (real::is_null(sfld))
              sfld.cut(0);
            break;
          case _memofld:
            if (sfld.full())
            {
              int p = 0;
              while ((p = sfld.find('\n', 0)) >= 0)
              {
                sfld.overwrite("\\", p);
                sfld.insert("n", p+1);
              }
            }
            else
              sfld.cut(0);
            break;
          default: // strfld, charfld, boolfld
            if (sfld.blank())
              sfld.cut(0);
            break;
          }
          s1 << sfld;
          if (fd) s1 << fd;
        }
        s << s1;
      }
      if (fs) // Elimina tutti i campi vuoti a fine record
      {
        int i;
        for (i = s.len()-1; i >= 0 && s[i] == fs; i--);
        s.cut(i+1);
      }
      fprintf(f, "%s%c", (const char*) s, rs);
    }
  }
  else
  {
    for (i = 0; i < nitems && !p.iscancelled(); i++)
    {
      zero();
      p.setstatus(i + 1);
      readat(i + 1);

      if (filter && *filter)
      {
        TToken_string filter_str(filter);
        TString16 fname; 
        bool skip = FALSE;
        while (!skip && !(fname=filter_str.get()).empty())
        {
          const char* fval = filter_str.get();
          const TString& cmp = get(fname);
          skip = cmp != fval;
        }
        if (skip)
          continue;
      }

      s="";
      if (withdeleted || curr().valid())
      {
        for (j = 0; j < nflds; j++)
        {
          if (fixedlen)
          {
            s1 = get((const TString&)fld[j]);
            if (rjust[j]) s1.right_just(len[j]);
            else s1.left_just(len[j]);
          }
          else
          {
            s1 = "";
            if (j && fs) s1 << fs;
            if (fd) s1 << fd;
            sfld = get((const TString&)fld[j]); 
            if (curr().type((const TString&) fld[j]) == _memofld)
            {
              int p = 0;
              while ((p = sfld.find('\n', 0)) >= 0)
              {
                sfld.overwrite("\\", p);
                sfld.insert("n", p+1);
              }
            }
            s1 << sfld;
            if (fd) s1 << fd;
          }
          s << s1;
        }
        fprintf(f, "%s%c", (const char*) s, rs);
      }
    }
  }
  p.setstatus(nitems);
  close();
  fclose(f);
  setstatus(err);
  return err;
}       

int TSystemisamfile::dump(
    const char* to, // @parm Nome del file verso quale esportare
		TToken_string & field_list,   // @parm Lista dei campi
    int nkey,     // @parm Numero della chiave di ordinamento con cui scaricare i dati (defualt 1)
    char fs,    // @parm Carattere seperatore di campo (defualt <pipe>)
    char fd,            // @parm Carattere delimitatore di campo (default '\\0')
    char rs,            // @parm Carattere separatore di record (default '\\n')
    bool vis,     // @parm Indica se visualizzare lo stato dell'operazione (defualt TRUE)
    bool withdeleted,// @parm Indica se scaricare anche i record cancellati (dafault FALSE)
    const char * filter, // @parm Indica l'espressione filtro 
		SELECTIONFUNCTION select_func, // @parm funzione filtro 
		TObject * filter_obj) // @parm oggetto di filtro 

// @xref <mf TSystemisamfile::load>

{
  FILE* f = fopen(to, "w");

  if (f == NULL) 
  {
    setstatus(2);
    return 2;
  }

  if (withdeleted) nkey = 0;
  int err = ferror(f);
  
  open(FALSE, nkey ? TRUE : FALSE);
  TString s(512);
  bool fixedlen = (fs == '\0');
  int nflds = curr().items();
  TString_array  fld(nflds);
  TBit_array rjust(nflds);
  int        len[MaxFields];
	int j = 0;

	FOR_EACH_TOKEN(field_list, str)             
  {
		const TString16 fldname(str);

    fld.add(fldname);
    const TFieldtypes t = curr().type(fldname);
    rjust.set(j, t == _intfld || t == _longfld || t == _realfld ||
              t == _wordfld || t == _intzerofld || t == _longzerofld);
    len[j++] = (t == _datefld) ? 10 : curr().length(fldname);
		if (fixedlen && t == _memofld)
			return error_box(TR("Non e' possibile scaricare a lunghezza fissa un file con campi memo"));
  }
  TRecnotype i = 0;
  const TRecnotype nitems = items();
  
  s.format(FR("Esportazione %s"), filename());
  TProgind p(nitems, s, TRUE, TRUE, 70);
  TString s1, sfld;

  fprintf(f, "[Header]\nVersion=%ld\nFile=%d", 
          (long) prefix().filelevel(), num());
 	FOR_EACH_ARRAY_ROW(fld, k, el)             
  {
    if ((k % 10) == 0) fprintf(f, "\nFields=");
    else fprintf(f, "|");

		const TString & fldname =fld.row(k);

    fprintf(f, "%s,%d", (const char *) fldname, len[k]);
  }
  fprintf(f, "\n\n[Data]\n");
  if (nkey)
  {
    setkey(nkey);
    for ( first(); status() == NOERR && !p.iscancelled(); next(), i++)
    {
      bool skip = false;

			p.setstatus(i + 1);
      if (filter && *filter)
      {
        TToken_string filter_str(filter);
        TString16 fname;

        while (!skip && !(fname=filter_str.get()).empty())
        {
          const char* fval = filter_str.get();
          const TString& cmp = get(fname);
          skip = cmp != fval;
        }
      }
			skip |= (select_func != NULL) && (select_func(curr(), filter_obj) == false);
      if (skip)
	      continue;
      s = "";
			FOR_EACH_ARRAY_ROW(fld, j, el)             
      {
				const TString & fldname =fld.row(j);

	      if (fixedlen)
        {
          s1 = get(fldname);
          if (rjust[j]) s1.right_just(len[j]);
          else s1.left_just(len[j]);
        }
        else
        {
          s1 = "";
          if (j && fs) s1 << fs;
          if (fd) s1 << fd;
          sfld = get(fldname); 
          if (curr().type(fldname) == _memofld)
          {
            int p = 0;
            while ((p = sfld.find('\n', 0)) >= 0)
            {
              sfld.overwrite("\\", p);
              sfld.insert("n", p+1);
            }
          }
          s1 << sfld;
          if (fd) s1 << fd;
        }
        s << s1;
      }
      fprintf(f, "%s%c", (const char*) s, rs);
    }
  }
  else
  {
    for (i = 0; i < nitems && !p.iscancelled(); i++)
    {
      zero();
      p.setstatus(i + 1);
      readat(i + 1);

      if (filter && *filter)
      {
        TToken_string filter_str(filter);
        TString16 fname; 
        bool skip = FALSE;
        while (!skip && !(fname=filter_str.get()).empty())
        {
          const char* fval = filter_str.get();
          const TString& cmp = get(fname);
          skip = cmp != fval;
        }
        if (skip)
          continue;
      }

      s="";
      if (withdeleted || curr().valid())
      {
				FOR_EACH_ARRAY_ROW(fld, j, el)             
		    {
					const TString & fldname = fld.row(j);

          if (fixedlen)
          {
            s1 = get(fldname);
            if (rjust[j]) s1.right_just(len[j]);
            else s1.left_just(len[j]);
          }
          else
          {
            s1 = "";
            if (j && fs) s1 << fs;
            if (fd) s1 << fd;
            sfld = get(fldname); 
            if (curr().type(fldname) == _memofld)
            {
              int p = 0;
              while ((p = sfld.find('\n', 0)) >= 0)
              {
                sfld.overwrite("\\", p);
                sfld.insert("n", p+1);
              }
            }
            s1 << sfld;
            if (fd) s1 << fd;
          }
          s << s1;
        }
        fprintf(f, "%s%c", (const char*) s, rs);
      }
    }
  }
  p.setstatus(nitems);
  close();
  fclose(f);
  setstatus(err);
  return err;
}       

#endif // FOXPRO


void TBaseisamfile::recover()
{
}

////////////////////////////////////////////////////////////
// Memo data
////////////////////////////////////////////////////////////

void TMemo_data::init(TRecnotype recno, TIsam_handle file) 
{ 
  CHECK(file > 0 || recno < 0, "Valid memo recno with NULL memo file");  // verificare
  _recno = recno; 
  _isamfile = file; 
}

void TMemo_data::destroy()
{
  TString_array::destroy();
  _dirty.reset();
  _recno = RECORD_NON_FISICO;
  _isamfile = 0;
}

void TMemo_data::copy(const TMemo_data& m)
{
  TString_array::operator=(m);
  _dirty = m._dirty;
  _recno = m._recno;
  _isamfile = m._isamfile;
}

////////////////////////////////////////////////////////////
// TRectype
////////////////////////////////////////////////////////////

void TRectype::init(int logicnum)
{
  _logicnum = logicnum;
  CHECK(_logicnum > 0,"Impossibile costruire un record di un file esterno");

  _length = prefix().get_reclen(logicnum);

  _rec = new char [_length];
  *_tab = '\0';
  if (_length > 0)
    zero();
  else
    setempty(TRUE);

  if (_length > 0 && lf_has_memo(logicnum))
    init_memo(RECORD_NON_FISICO);
}

TRectype::TRectype(int logicnum)
         : _memo_data(NULL)
{
	init(logicnum);
}

TRectype::TRectype(const TBaseisamfile* i)
         : _memo_data(NULL)
{
  init(i->num());
}

TRectype::TRectype(const TRectype& r)
        : _memo_data(NULL)
  
{
	init(r._logicnum);
  if (r._memo_data)
  {
    init_memo(r._memo_data->recno(), r._memo_data->file());
    *_memo_data = *r._memo_data;
  }               
  memcpy(_rec, r._rec, _length);
  strcpy(_tab, r._tab);
  setempty(r.empty());
}

TRectype::~TRectype()
{
  if (_rec != NULL) 
    delete _rec;
  if (_memo_data != NULL ) 
    delete _memo_data;
}

void TRectype::unknown_field(const char* name) const 
{
  static int last_file = 0;
  if (_logicnum != last_file)
  {
    NFCHECK("Il campo '%s' non appartiene al file %d", name, _logicnum);
    last_file = _logicnum;
  }
}
                    
void TRectype::write_memo(TIsam_handle file, const TRecnotype recno)
{   
  const RecDes& r = rec_des( );
  TIsam_handle orig = _memo_data->file();
  if (orig && (file != orig || recno != _memo_data->recno()))
  {
		 TCodeb_handle cb_orig = prefix().get_handle(orig);
     DB_go(cb_orig, _memo_data->recno());
     for(int i = r.NFields - 1; i >= 0; i--)
     {
       if (r.Fd[i].TypeF == _memofld)
       {
         if (_memo_data->objptr(i) == NULL)
         {
           const char* memo = DB_memoptr(cb_orig, r.Fd[i].Name);
           if (memo && *memo) 
             _memo_data->add(memo, i);
         }
         _memo_data->set_dirty(i);
       }
     }
  }
  CHECKD(recno >= 0,  "Maiale! Non fare le GO con _recno < 0: ", recno);
  const TCodeb_handle cb_handle = prefix().get_handle(file);
  DB_go( cb_handle, recno);
  FOR_EACH_ARRAY_ROW_BACK((*_memo_data), i, memo)
	{
    if (_memo_data->is_dirty(i))
			DB_memowrite(cb_handle, r.Fd[i].Name, *memo);
	}
  DB_flush(cb_handle);
  *this = (const char*)DB_getrecord(cb_handle);
  init_memo(recno, file);
}

void TRectype::init_memo(TRecnotype recno, TIsam_handle file)
{  
  if (_memo_data == NULL)
    _memo_data = new TMemo_data;
  else
    _memo_data->destroy();
  _memo_data->init(recno, file);
}

void TRectype::settab(const char *tab)
{ 
  strcpy(_tab, tab);
  zero();  
}

TObject* TRectype::dup() const
{
  TRectype* o = new TRectype(*this);
  return o;
}

const char* TRectype::build_key(int num) const
{
  TString& tmp = get_tmp_string(256);
  __build_key(rec_des(), num, string(), tmp.get_buffer(), TRUE);
  return tmp;
}

const char* TRectype::last_key_field(int key) const
{
  const KeyDes& kd = rec_des().Ky[key];
  const int last = kd.NkFields-1;
  const bool upp = kd.FieldSeq[last] > MaxFields;
  const int nf = upp ? kd.FieldSeq[last] - MaxFields : kd.FieldSeq[last];
  const RecFieldDes& rf = rec_des().Fd[nf];  
  return rf.Name;
}

// @doc EXTERNAL

// @mfunc Confronta le chiavi di due record
//
// @rdesc Ritorna il risultato di una <f strcmp>:
//
// @flag 0 | Se le due chiavi sono uguali
// @flag <lt><gt>0 | Se le due chiavi sono diverse
int TRectype::compare_key(
    const TRectype& rec, // @parm Record di cui confrontare le chiavi
    int key,             // @parm Numero della chiave del presente record (defautl 1)
    int skip_last) const // @parm Numero di campi da ignorare nella comparazione a partire dall'ultimo

// @xref <mf TRectype::build_key>
{
/* Vecchio modo con molte operazioni su stringhe e molte chiamate a rec_des()!
  TString256 key1 = build_key(key);
  TString256 key2 = rec.build_key(key);
  if (skip_last > 0)
  {                
    const KeyDes& kd = rec_des()->Ky[key-1];
    const int last = kd.NkFields-1;
    CHECKD(last >= skip_last, "Can't ignore so many fields in key: ", skip_last);
    for (int l = 0; l < skip_last; l++)
    {
      int nf = kd.FieldSeq[last-l]; 
      if (nf > MaxFields) nf -= MaxFields;
      const RecFieldDes& rf = rec_des()->Fd[nf];  
      key1.rtrim(rf.Len);
      key2.rtrim(rf.Len);
    }  
  }
  const int res = strcmp(key1, key2);
*/
  const char* key1 = build_key(key);
  const char* key2 = rec.build_key(key);
  int res = 0;
  if (skip_last > 0)
  {                
    int maxlen = 0;
    const RecDes& rd = rec_des();
    const KeyDes& kd = rd.Ky[key-1];
    const int last = kd.NkFields-skip_last;
    for (int l = 0; l < last; l++)
    {
      const int nf = kd.FieldSeq[l] % MaxFields; 
      const RecFieldDes& rf = rd.Fd[nf];  
      maxlen += rf.Len;
    }  
    res = strncmp(key1, key2, maxlen);
  }
  else
    res = strcmp(key1, key2);

  return res;
}

HIDDEN bool fld_empty(const char* s, int len, bool number)
{
  if (s && *s)
  {
    for (; len; s++, len--)
      if (*s != ' ') return false;
  }
  return true;
}


HIDDEN int fld_cmp(const char* a, const char* b, int len, bool number)
{
	int i;
  for (i = 0; i < len && *a == *b; b++, a++, i++);
  if (i == len) return 0;
  int res = *a - *b;      
  if (number)
  {
    b -= i;
    i  = 0;
  }
  return fld_empty(b, len - i, number) ? 0 : res;
}


///////////////////////////////////////////////////////////
// TRectype (record di un file)
///////////////////////////////////////////////////////////

const RecDes& TRectype::rec_des() const  
{
  return prefix().get_recdes(_logicnum);
}

int TRectype::items() const
{
  return rec_des().NFields;
}

const char* TRectype::start(int nf) const
{
  return string() + rec_des().Fd[nf].RecOff;
}

// Confronto tra record: Attenzione i campi vuoti di s non vengono confrontati!
int TRectype::compare(const TSortable& s) const
{
  const TRectype& br = (const TRectype&) s;
  int res = 0;

  if (br.empty()) return UNDEFINED;
  
  const RecDes& rd = rec_des();
  for (int i = 0; i < rd.NFields; i++)
  {
    const char* b = br.start(i);
    const char* a = start(i);
    const byte typ = rd.Fd[i].TypeF;
    const int  sz  = rd.Fd[i].Len;
    const bool number = (typ == _intfld) || (typ == _realfld) ||
      (typ == _longfld) || (typ == _wordfld) ||
        (typ == _intzerofld) || (typ == _longzerofld) || (typ == _datefld) ;

    if (fld_empty(b, sz, number)) continue;
    res = ::fld_cmp(a, b, sz, number);
    if (res) return res;
  }
  return 0;
}

// Confronto stretto
bool TRectype::is_equal(const TRectype& r) const
{
  char* r1 = new char[_length];
  char* r2 = new char[_length];
  memcpy(r1, _rec, _length);
  memcpy(r2, r._rec, _length);
  browse_null(r1, _length);
  browse_null(r2, _length);

  if (has_memo())
  {
    const RecDes& rd = rec_des();
    for(int i = rd.NFields - 1; i >= 0; i--)
    {
      if (rd.Fd[i].TypeF == _memofld)
      {
        memset(r1+rd.Fd[i].RecOff, ' ', rd.Fd[i].Len);
        memset(r2+rd.Fd[i].RecOff, ' ', rd.Fd[i].Len);
      }
    }
  }
  bool yes = memcmp(r1, r2, _length) == 0;
  delete r1;
  delete r2;

  if (yes && has_memo())
  {
    const RecDes& rd = rec_des();
    for(int i = rd.NFields - 1; yes && i >= 0; i--)
    {
      if (rd.Fd[i].TypeF == _memofld)
        yes = get(rd.Fd[i].Name) == r.get(rd.Fd[i].Name);
    }
  }
  return yes;  
}

TFieldtypes TRectype::type(const char* fieldname) const
{
  const RecDes& recd = rec_des();
  const int p = findfld(&recd, fieldname);
  return p != FIELDERR ? (TFieldtypes)recd.Fd[p].TypeF : _nullfld;
}

int TRectype::length(const char* fieldname) const
{
  const RecDes& recd = rec_des();
  const int p = findfld(&recd, fieldname); 
  return p != FIELDERR ? recd.Fd[p].Len : 0;
}

int TRectype::ndec(const char* fieldname) const
{
  const RecDes& recd = rec_des();
  const int p = findfld(&recd, fieldname); 
  return p != FIELDERR ? recd.Fd[p].Dec : 0;
}

bool TRectype::exist(const char* fieldname) const
{                   
  return findfld(&rec_des(), fieldname) != FIELDERR;
}

const char* TRectype::fieldname(int i) const
{
  const RecDes& recd = rec_des();
  return i >= 0 && i < recd.NFields ? recd.Fd[i].Name : NULL;
}                            

const TString& TRectype::get_str(const char* fieldname) const
{     
  const RecDes& recd = rec_des();
  const int nf = findfld(&recd, fieldname);
  if (nf != FIELDERR)
  {
    const RecFieldDes& fd = recd.Fd[nf];
    TString& tmp = get_tmp_string(fd.Len + (fd.TypeF == _datefld ? 2 : 0));
    __getfieldbuff(fd.Len, fd.TypeF, _rec + fd.RecOff, tmp, __file_is_crypted(num()));
    return tmp;
  }
  else
    unknown_field(fieldname);
  return EMPTY_STRING;
}

const TString& TRectype::get(const char* fieldname) const
{
  if (_memo_data && type(fieldname) == _memofld)
  {
    const RecDes& recd = rec_des();
    const int index = findfld(&recd, fieldname);
    if ( _memo_data->objptr( index ))
      return _memo_data->row( index );    
    if (_memo_data->recno() >= 0L)
    {
      const TIsam_handle orig = _memo_data->file();
      if (orig)
      {
        CHECKD(orig >= LF_EXTERNAL || orig == num(), "Invalid isam handle ", orig);
	      TCodeb_handle cb_handle = prefix().get_handle(orig, -1);
	      CHECKD(cb_handle >= 0, "Can't read memo from file ", orig);
        if (DB_recno(cb_handle) != _memo_data->recno())
          DB_go(cb_handle, _memo_data->recno());
        _memo_data->add(DB_memoptr(cb_handle, fieldname), index);    
      }
      else
        NFCHECK("Valid memo recno with null memo file");
    }
    else
      _memo_data->add("", index);
    return _memo_data->row(index);
  }  
  return get_str(fieldname);
}

int TRectype::get_int(const char* fieldname) const
{
  return atoi(get_str(fieldname));
}

long TRectype::get_long(const char* fieldname) const
{
  return atol(get_str(fieldname));
}


word TRectype::get_word(const char* fieldname) const
{
  return (word)atoi(get_str(fieldname));
}

real TRectype::get_real(const char* fieldname) const
{
  real r(get_str(fieldname));
  return r;
}

char TRectype::get_char(const char* fieldname) const
{
  return get_str(fieldname)[0];
}

bool TRectype::get_bool(const char* fieldname) const
{
  return get_char(fieldname) == 'X';
}

TDate TRectype::get_date(const char* fieldname) const
{
  const TDate d(get_str(fieldname));
  return d;
}

void TRectype::put(const char* fieldname, int val)
{           
	char tmp[16];
  itoa(val, tmp, 10);
  put_str(fieldname, tmp);
}


void TRectype::put(const char* fieldname, long val)
{
	char tmp[16];
  ltoa(val, tmp, 10);
  put_str(fieldname, tmp);
}


void TRectype::put(const char* fieldname, word val)
{
  put(fieldname, (int)val);
}

void TRectype::put(const char* fieldname, const real& val)
{
  put_str( fieldname, val.string());
}

void TRectype::put(const char* fieldname, const TCurrency& val)
{
  put_str( fieldname, val.get_num().string());
}

void TRectype::put(const char* fieldname, const TDate& val)
{            
  put( fieldname, val.date2ansi());
}

void TRectype::put(const char* fieldname, char val)
{
  const char w[2] = { val, '\0' };
  put_str( fieldname, w);
}


void TRectype::put(const char* fieldname, bool val)
{
  put(fieldname, val ? 'X' : ' ');
}


void TRectype::put_str(const char* fieldname, const char* val)
{                                            
  const RecDes& recd = rec_des();
  const int nf = findfld(&recd, fieldname);
  if (nf == FIELDERR)
  {
    unknown_field(fieldname);
    return;
  }

  const RecFieldDes& fd = recd.Fd[nf];
  const TFieldtypes ft = TFieldtypes(fd.TypeF);
  
  if (val == NULL)
    val = "";
  if (ft == _boolfld)                            
    val = (*val && strchr("1STXY", toupper(*val)) != NULL) ? "T" : "F";

  if (*val == '\0')  // VERIFICARE COL REPOSITORY
  {
    TRecfield f(*this, fieldname);
    if (*f.pos() == '\0') return;
  }
  if(ft == _memofld)
  {
    _memo_data->add(val, nf); 
    _memo_data->set_dirty(nf);
  }  
  else
  {
    __putfieldbuff(fd.Len, fd.Dec, ft, val, _rec + fd.RecOff, __file_is_crypted(num()));
  }

  setempty(FALSE);
}

void TRectype::add(const char* fieldname, const real& val)
{
  if (!val.is_zero())
  {
    real k = get_real(fieldname);
    k += val;
    put(fieldname, k);
  }
}

void TRectype::zero(const char* fieldname)
{
  if (*_tab && strcmp(fieldname , "COD") == 0)
    put("COD", _tab);
  else 
  {
    const RecDes& recd = rec_des();
    const int nf = findfld(&recd, fieldname);
    if (nf == FIELDERR)
      unknown_field(fieldname);
    else
    {
      const int recoff = recd.Fd[nf].RecOff;
      char * p = _rec + recoff;
      const byte len = recd.Fd[nf].Len; 
      const byte dec = recd.Fd[nf].Dec; 
      const TFieldtypes type = (TFieldtypes) recd.Fd[nf].TypeF;
      switch (type)
      {
      case _datefld:
        __putfieldbuff(len, dec, _datefld, "", p, __file_is_crypted(num()));
        break;
      case _memofld:
        __putfieldbuff(len, dec, _memofld, "", p, __file_is_crypted(num()));
        _memo_data->add("", nf); 
        _memo_data->set_dirty(nf);
        break;  
      default:
        memset(p, ' ', len);
        if ((type == _intfld) || (type == _longfld) || (type == _wordfld))
          *(p + len - 1) = '0';
        else
          if (type == _realfld)
          {
            if (dec)
            {
              memset(p + len - dec - 2, '0', dec + 2);
              *(p + len - dec - 1) = '.';
            }
            else
              *(p + len - 1) = '0';
          }
        break;
      }
    }
  }
}

void TRectype::zero(char c)
{
  memset(_rec, c, len());
  recall();

  if (*_tab)
    put("COD", _tab);
  
  if(has_memo())
    init_memo( RECORD_NON_FISICO );
  setempty(TRUE);
}


// Certified 99%
TRectype& TRectype::operator =(const TRectype& rec)
  
{
  CHECK(num() == rec.num(), "Can't assign records of different file");

  memcpy(_rec, rec._rec, _length);       // Copy contents
  if (rec._memo_data)
  {    
    init_memo(rec._memo_data->recno(), rec._memo_data->file());
    *_memo_data = *rec._memo_data;
  }
  strcpy(_tab, rec._tab);
  setempty(rec.empty());                   // Copy emptiness status
  return *this;
}

// Certified 100%
TRectype& TRectype::operator =(const TBaseisamfile& f)
{
  return *this = f.curr();
}

// Certified  50%
int TRectype::read(TBaseisamfile& f, word op, word lockop)
{ 
  int err = f._read(*this,op,lockop);
  return err;
}

int TRectype::readat(TBaseisamfile& f, TRecnotype nrec, word lockop)
{
  return f._readat(*this,nrec,lockop);
}


// Certified 100%
int TRectype::next(TBaseisamfile& f,word lockop)
{ 
  return read(f, _isnext, lockop); 
}

// Certified 100%
int TRectype::prev(TBaseisamfile& f,word lockop)
{ 
  return read(f, _isprev, lockop); 
}

int TRectype::first(TBaseisamfile& f,word lockop)
{ 
  return read(f, _isfirst, lockop); 
}

int TRectype::last(TBaseisamfile& f,word lockop)
{ 
  return read(f, _islast, lockop); 
}

// Certified ??%
int TRectype::write(TBaseisamfile& f) const
{ return f._write(*this); }

// Certified ??%
int TRectype::rewrite(TBaseisamfile& f) const
{ 
  return f._rewrite(*this); 
}

int TRectype::write_rewrite(TBaseisamfile& f) const
{ 
  int err = write(f); 
  if (err != NOERR)
    err = rewrite(f);
  return err;
}

int TRectype::rewrite_write(TBaseisamfile& f) const
{ 
  int err = rewrite(f); 
if (err != NOERR)
    err = write(f);
return err;
}

// Certified ??%
int TRectype::remove(TBaseisamfile& f) const
{ 
  return f._remove(*this); 
}

void TRectype::renum_key(const char* field, const char* val)
{
  put(field, val);
}

// Certified 99%
TRectype& TRectype::operator =(const char* rec)
{
  if (rec && * rec)
  {
    memcpy(_rec, rec, _length);
    setempty(FALSE);
  }
  else
    zero();
  return *this;
}

const char* TRectype::key(int numkey) const
{
	TString& tmp = get_tmp_string(256);
  __build_key(rec_des(), numkey, _rec, tmp.get_buffer(), FALSE);
  return tmp;
}

void TRectype::fill_transaction(TConfig& cfg, int row) const
{                                   
  TString16 p; p << num();
  if (row > 0) p << ',' << row;
  cfg.set_paragraph(p);

  for (int f = items()-1; f >= 0; f--)
  {
    const char* name = fieldname(f);
    const char* value = get(name);
    cfg.set(name, value);
  }
}

bool TRectype::send_mail(const char* action) const
{
  TWait_cursor hourglass;
  bool ok = ::can_dispatch_transaction(*this);
  if (ok)
  {
    TFilename ininame; ininame.temp();
    if (ok) // Test qualunque per usare {}
    {
      TConfig ini(ininame, "Transaction");
      ini.set("Action", action);
      ini.set("Mode", "A");
      fill_transaction(ini);
    }
    ok = ::dispatch_transaction(*this, ininame);
    ::remove(ininame);
  }
  return ok;
}

bool TRectype::get_relapp(TString& app) const
{            
  if (*_tab)
  {                 
    TString4 cod(_tab);
    switch (num())
    {
    case LF_TABCOM: cod.insert("%"); break;
    case LF_TABGEN: cod.insert("^"); break;
    case LF_TABMOD:
      {
        TModule_table mod(_tab);
        return mod.get_relapp(app);
      }
      break;
    default       : break;
    }
    return ::get_tabapp(cod, app);
  }
  return ::get_relapp(num(), app);
}

bool TRectype::edit(int logicnum, const char* alternate_key_fields, const char* hint) const
{
  bool ok = false;
  if (logicnum <= 0)
    logicnum = num();
  
  TRectype r(logicnum);
  if (*_tab)
    r.settab(_tab);
  
  TString app = hint;
  if (app.blank())
    r.get_relapp(app);
  if (app.full())
  {
    const RecDes& recd = r.rec_des();    // Descrizione del record della testata
    const KeyDes& kd = recd.Ky[0];      // Elenco dei campi della chiave 1
    TToken_string key_labels;
    for (int i = 0; i < kd.NkFields; i++)
    {                        
      const int nf = kd.FieldSeq[i] % MaxFields;
      const RecFieldDes& rf = recd.Fd[nf];  
      key_labels.add(rf.Name);
    }
                 
    TToken_string key_fields(alternate_key_fields);
    if (key_fields.empty_items())
      key_fields = key_labels;
  
    TFilename ininame; ininame.temp("lnk", "ini");
    {
      TConfig ini(ininame, "Transaction");
      ini.set("Action", "LINK");
      TString8 p; p << logicnum;
      ini.set_paragraph(p);           
    
      FOR_EACH_TOKEN(key_labels, tok)
      {
        const TString16 name(tok);
        const TString& value = get(key_fields.get());
        ini.set(name, value);
      } 
    }

    app << " -i" << ininame;
    TExternal_app a(app);
    ok = a.run() == 0;

    if (ok)
    {
      TConfig ini(ininame, "Transaction");
      const TString& result = ini.get("Result");
      ok = result == "OK";
    }

    xvt_fsys_remove_file(ininame);

    if (ok)
      rec_cache(logicnum).notify_change();
  }      
  return ok;
}

bool TRectype::set_edit_info(const char* ut, const char* dt, const char* or)
{
  bool ok = false;
  if (num() > LF_TAB) // Inutile tentare di gestire le tabelle
  {
    // Se esiste un campo alfanumerioco UT*
    if (ut && *ut && type(ut) == _alfafld)   
    {
      put(ut, user()); // Scrivi utente corrente
      ok = true;

      // Se esiste un campo data DT*
      if (dt && *dt && type(dt) == _datefld) 
      {
        const TDate oggi(TODAY);
        put(dt, oggi); // Scrivi data odierna

        // Se esisnte un campo long OR*
        if (or && *or && (type(or) == _longfld || type(or) == _longzerofld))
          put(or, daytime()); // Scrivi ora attuale HHMMSS
      }
    }
  }
  return ok;
}

bool TRectype::set_modify_info()
{
  return set_edit_info("UTCREAZ", "DTCREAZ", "ORCREAZ");
}

bool TRectype::set_creation_info()
{
  return set_edit_info("UTULAGG", "DTULAGG", "ORULAGG");
}

///////////////////////////////////////////////////////////
// TRecfield (campo/sottocampo di un record)
///////////////////////////////////////////////////////////

TRecfield::TRecfield(TRectype& rec, const char* name, int from, int to)
{
  _rec = &rec;

  const TFixed_string fname(name);
  const int colon = fname.find(':');
  if (colon > 0)
  {
    _name.strncpy(name, colon);
    _sub_field = name+colon+1;
    _sub_field << '='; // ???????????????
  }
  else
    _name = name;

  const RecDes& rd = _rec->rec_des();

  const int nf = findfld(&rd, _name);

  if (nf == FIELDERR)
  {
		_sub_field = _name;   
    _p = _rec->string();
    _len = 0;
    _dec = 0;
    _type = _alfafld;
  }
  else
  {
    if (from < 0)
    {
      NFCHECK("Invalid Start %d", from);
      from = 0;
    }
    _p = _rec->string() + rd.Fd[nf].RecOff;
    _dec = rd.Fd[nf].Dec;
    _type = (TFieldtypes)rd.Fd[nf].TypeF;
		if (_sub_field.empty())
		{
			_p += from;
			if  (to >= 0)
			{
				CHECKS(_type == _memofld || (from <= to && to <= rd.Fd[nf].Len), "Invalid Range on field ", (const char *)_name);
				_len = to - from + 1;
			}
			else 
				_len = rd.Fd[nf].Len - from;
		}
		else
		{
			CHECK(_type == _memofld, "You can use Subfields only with Memo");
			_from = from;
			_to = to;
			if (_type == _memofld)
				_len = 0;
			else
				_len = rd.Fd[nf].Len;
		}
  }

  CHECKS(colon < 0 || _type == _memofld, "SubField on non memo field: ", name);
}

void TRecfield::put_subfield(const char* s)
{
	if (_name == _sub_field)
		return;
  const TString& str = _rec->get(_name);
  int p = str.find(_sub_field);
      
	if (p == 0 || (p > 0 && str[p - 1] < ' '))
  {
    p += _sub_field.len();
    
		int e = str.find('\n', p);

		if (_to > 0 && p + _to < e)
			e = p + _to;
		p += _from;
		if (p < e)
		{	
			TString val(s);

			if (_to > 0)
			{
				val.left(e - p + 1);
				val.rpad(e - p + 1);
			}
			TString out = str.left(p);
			out << val << str.mid(e); // ? e + 1
			_rec->put(_name, out);
		}
	}
}

int TRecfield::operator =(int i)
{
  TString16 buff; buff << i;
	if (_sub_field.empty())
		__putfieldbuff( _len, _dec, _type, buff, _p, __file_is_crypted(_rec->num()));
	else
		put_subfield(buff);
  _rec->setempty(FALSE);
  return i;
}


long TRecfield::operator =(long l)
{
  TString16 buff; buff << l;
	if (_sub_field.empty())
		__putfieldbuff( _len, _dec, _type, buff, _p, __file_is_crypted(_rec->num()));
	else
		put_subfield(buff);
  _rec->setempty(false);
  return l;
}

const real& TRecfield::operator =(const real& r)
{
  const char* buff = r.string();
	if (_sub_field.empty())
		__putfieldbuff( _len, _dec, _type, buff, _p, __file_is_crypted(_rec->num()));
	else
		put_subfield(buff);
  _rec->setempty(FALSE);
  return r;
}

const TDate& TRecfield::operator =(const TDate& d)
{ 
  const TString16 buff = d.stringa();
	if (_sub_field.empty())
		__putfieldbuff( _len, _dec, _type, buff, _p, __file_is_crypted(_rec->num()));
	else
		put_subfield(buff);
  _rec->setempty(FALSE);
  return d;
}

const char* TRecfield::operator =(const char* s)
{
	if (_sub_field.empty())
  {
		if (_type == _memofld)
			_rec->put(_name, s);
	  else
			__putfieldbuff( _len, _dec, _type, s, _p, __file_is_crypted(_rec->num()));
	}
	else
		put_subfield(s);
  _rec->setempty(FALSE);
  return s;
}

void TRecfield::setptr(TRecnotype r)
{
  if (_p == NULL) return;
  
  bool n = r < 0;
  unsigned char*  wp = (unsigned char*) _p;

  if (n) r = -r;
  while(wp - (unsigned char*) _p <= 3)
  {
    *wp = r && 0x000000FF;
    r >>= 8;
    wp++;
  }
  if (n) *wp += 128;
}

void TRecfield::get_subfield(TString& s) const
{
  const TString& str = _rec->get(_name);
	
	if (_name == _sub_field)
		s = str;
	else
	{
    int p = str.find(_sub_field);
      
	  if (p == 0 || (p > 0 && str[p - 1] < ' '))
    {
      p += _sub_field.len();
      
		  int e = str.find('\n', p);
      if (e < 0) e = str.len();    // L'ultima variabile non termina con \n
		  if (_to > 0 && p + _to < e)
			  e = p + _to;
		  p += _from;
		  if (p < e)
			  s = str.sub(p, e);
		  else 
        s.cut(0);
    }
	  else 
      s.cut(0);
  }
}

TRecfield::operator int() const
{
	TString16 tmp;
	if (_sub_field.empty())
	{
		if (_type == _intfld || _type == _intzerofld || _type == _longfld || _type == _longzerofld)
			tmp.strncpy(_p, _len);
		else
			__getfieldbuff( _len, _type, _p, tmp, __file_is_crypted(_rec->num()));
	}
	else
		get_subfield(tmp);
  return atoi(tmp);
}


TRecfield::operator long() const
{
	TString16 tmp;

	if (_sub_field.empty())
	{
		if (_type == _longfld || _type == _longzerofld || _type == _intfld || _type == _intzerofld)
			tmp.strncpy(_p, _len);
		else  
			__getfieldbuff( _len, _type, _p, tmp, __file_is_crypted(_rec->num()));
	}
	else
		get_subfield(tmp);
  
  return atol(tmp);
}


TRecfield::operator const real() const
{
  TString80 tmp;

	if (_sub_field.empty())
	{
		if (_type == _realfld)
			tmp.strncpy(_p, _len);
		else
			__getfieldbuff( _len, _type, _p, tmp, __file_is_crypted(_rec->num()));
	}
	else
		get_subfield(tmp);

  return real(tmp);
}


TRecfield::operator TDate() const
{    
  TString16 tmp;
	
	if (_sub_field.empty())
	{
		if (_type == _datefld)                          
		{
			tmp.strncpy(_p, 8);
			return TDate(atol(tmp));
		}  
		__getfieldbuff(_len, _type, _p, tmp, __file_is_crypted(_rec->num()));
	}
	else
		get_subfield(tmp);

  return TDate(tmp);
}


TRecfield::operator const char*() const
{    
  TString& tmp = get_tmp_string(max(_len, 50));

  if (_sub_field.empty())
  {
    if (_type == _memofld)
	    return _rec->get(_name);
		__getfieldbuff(_len, _type, _p, tmp, __file_is_crypted(_rec->num()));
  }
	else
		get_subfield(tmp);

  return tmp;
}


TRecnotype TRecfield::ptr() const
{
  if (_p == NULL) return(RECORD_NON_FISICO);
  unsigned char*  wp = (unsigned char*) _p + 3;
  TRecnotype   r  = *wp;
  bool         n = r > 127;

  if (n) r -= 128;
  while(wp-- > (unsigned char*) _p) 
	  r = (r << 8) + *wp;
  return n ? -r : r;
}