/*
   Attenzione!. Per costruire la DLL accertarsi che siano attivati i seguenti switch:
   1) S4DLL_BUILD definito da command line del compilatore
   2) Uno solo tra S4FOX S4CLIPPER S4NDX e S4MDX, definito in d4all.h
   
   Gli altri switch di configurazione generale sono a piacere (S4DEBUG, S4ERROR_HOOK, ecc.)
   
   Per costruire WINUNO.LIB Accertarsi che d4all.h non abbia definito S4DLL o S4UNIX,
   e tanto meno S4DLL_BUILD, altrimenti per costruire la library di UNIX e' necessario
   cambiare ogni volta. La definizione di tali simboli avviene qui dentro.
   Inoltre deve essere attivo uno solo degli switch di selezione formato database 
   (S4FOX, S4MDX ecc.); nel caso si faccia uso della DLL non importa quale si e' definito.
   E' importante solo in caso di utilizzo di una static Library.
   
   Per costruire l'aga.fll e' necessario togliere le funzioni progind* e definire S4DLL.
   
   */
#if XVT_OS == XVT_OS_SCOUNIX
#define S4UNIX        
#else 
#define S4DLL 
#endif

#ifdef XVT  /* This not recommended in Unix!*/
#undef S4UNIX 
#define S4DLL
#endif

#ifdef FOXPRO
#undef S4UNIX
#define S4DLL
#endif  

#include <d4all.h>
#include <x4filter.h> 
#include <i4chang.h>
#include <codeb.h>
#include <rectypes.h>    
#include <progind.h>
#define  MAXLEN         128     /* Lunghezza massima chiave  */

extern char* CUpString(char *); 

static CODE4 code_base;
static DATA4 *dbdata[CB4FILES]; 
static X4FILTER xdb[CB4FILES];       


static char * find_slash_backslash(char * str)
{                     
  int l=strlen(str);
  static char *  xstr ;
  xstr = str + l;
  
  while (xstr-- && l--)
    if (*xstr == '\\' || *xstr == '/')
      break;
  if (l == 0)
    return NULL;
  else     
    return xstr;
}                                                  


/*--------------------------------------------------------------------------
  funzione di filtro usata su tutti i database
  --------------------------------------------------------------------------*/
int S4CALL notdelete(void *fd)
{
  if(d4deleted(fd)) return r4ignore;
  else return r4keep;
}

/*--------------------------------------------------------------------------
  inizializzazione di CodeBase e delle variabili necessarie
  questa funzione deve essere chiamata SOLO una volta all'interno
  di ciascun eseguibile
  --------------------------------------------------------------------------*/
void DB_init(void)
{
  int i;
  for(i=0;i<CB4FILES;i++) {
    dbdata[i]=(DATA4 *) 0;
  }
  d4init(&code_base);
  code_base.read_lock=0;
  code_base.default_unique_error=e4unique;
  code_base.safety=0;
  code_base.lock_attempts=1;   
  //  code_base.optimize_write = 0;
  u4ncpy(code_base.date_format,"CCYYMMDD",sizeof(code_base.date_format));

}

/*--------------------------------------------------------------------------
  reset di CodeBase
  questa funzione dovrebbe essere chiamata prima di uscire da un eseguibile
  per una questione di correttezza formale. Non usandola comunque non acca-
  de niente di drammatico
  --------------------------------------------------------------------------*/
void DB_exit(void)
{
  d4init_undo(&code_base);
}

/*-------------------------------------------------------------------------
  apertura del file 'filename'. Il parametro mode consente se != 0 l'apertura
  esclusiva
  --------------------------------------------------------------------------*/
int  DB_open(const char *filename,int mode)
{
  int i,found;

  if (mode) code_base.exclusive=1;
  /* cerca il primo posto libero nel vettore dbdata */
  found=-1;   
  for(i=0;i<CB4FILES;i++) {
    if(dbdata[i]==(DATA4 *) 0) {
      found=i;
      break;
    }
  }
  /* se non ci sono posti liberi torna -1 */
  if(found==-1) return(found);
  code_base.error_code=0;
  dbdata[found]=d4open(&code_base,(char*)filename);
  if(dbdata[found]==0) return(-1);
  x4init(&xdb[found],dbdata[found],notdelete,dbdata[found]);
  code_base.exclusive=0;
  d4tag_select(dbdata[found],d4tag_default(dbdata[found]));
  if (d4reccount(dbdata[found]) > 0)
    x4top(&xdb[found]);                  
  return(found);
}


/*-------------------------------------------------------------------------
  chiusura del database inviduato da handle
  torna -1 se il database non puo essere chiuso
  --------------------------------------------------------------------------*/
int  DB_close(int handle)
{          
  if(dbdata[handle]==0) return(-1);
  d4close(dbdata[handle]);
  dbdata[handle]=(DATA4 *) 0; 
  code_base.error_code=0;   
  return(0);
}

/*-------------------------------------------------------------------------
  torna il puntatore al buffer del record del database individuato da
  handle. In caso di errore torna (char *) 0
  --------------------------------------------------------------------------*/
char *DB_getrecord(int handle)
{
  if(dbdata[handle]==0) return((char *) 0);
  return(d4record(dbdata[handle]));
}

/*-------------------------------------------------------------------------
  torna la lunghezza del record
  --------------------------------------------------------------------------*/
int  DB_reclen(int handle)
{
  if(dbdata[handle]==0) return(-1);
  return((int) d4record_width(dbdata[handle]));
}

/*-------------------------------------------------------------------------
  torna la lunghezza della chiave corrente
  --------------------------------------------------------------------------*/

int DB_keylen(int handle)
{   
  TAG4 *t;

  if (dbdata[handle]==0) return(-1);
  if ((t=d4tag_default(dbdata[handle]))== NULL) return (-1);
  return(expr4key_len(t->expr));
}

/*-------------------------------------------------------------------------
  torna il numero del record attuale
  --------------------------------------------------------------------------*/
long int DB_recno(int handle)
{
  if(dbdata[handle]==0) return(-1L);
  return(d4recno(dbdata[handle]));
}

/*-------------------------------------------------------------------------
  torna il numero complessivo dei records presenti nell'archivio
  --------------------------------------------------------------------------*/
long int DB_reccount(int handle)
{
  if(dbdata[handle]==0) return(-1L);
  return(d4reccount(dbdata[handle]));
}

/*-------------------------------------------------------------------------
  seleziona un indice sul database specificato
  torna -1 se errore, altrimenti 0
  --------------------------------------------------------------------------*/
int  DB_tagselect(int handle,int index_no)
{
  TAG4 *tt;
  int i;

  if(dbdata[handle]==0) return(-1);
  /* si posiziona sul primo indice */
  tt=d4tag_next(dbdata[handle],NULL);
  if(tt==NULL) return(-1);
  for(i=1;i<index_no;i++) {
    tt=d4tag_next(dbdata[handle],tt);
    if(tt==NULL) return(-1);
  }
  d4tag_select(dbdata[handle],tt);
  return(0);
}


/*-------------------------------------------------------------------------
  torna il numero dell'indice selezionato
  torna -1 se errore
  --------------------------------------------------------------------------*/
int DB_tagget(int handle)
{
  TAG4 *tt,*tt1;
  int i;

  if(dbdata[handle]==0) return(-1);
  /* si posiziona sul primo indice */
  tt=d4tag_selected(dbdata[handle]);
  if(tt==NULL) return(-1);

  tt1=d4tag_next(dbdata[handle],NULL);
  i=1;
  while(tt!=tt1 && tt1!=NULL) {
    tt1=d4tag_next(dbdata[handle],tt1);
    i++;
  }
  return(i);
}

/*-------------------------------------------------------------------------
  si posiziona sul primo record
  torna -1 se errore
  --------------------------------------------------------------------------*/
int DB_first(int handle)
{
  if(dbdata[handle]==0) return(-1);
  return(x4top(&xdb[handle]));
}


/*-------------------------------------------------------------------------
  si posiziona sull'ultimorecord
  torna -1 se errore
  --------------------------------------------------------------------------*/
int DB_last(int handle)
{
  if(dbdata[handle]==0) return(-1);
  return(x4bottom(&xdb[handle]));
}


/*-------------------------------------------------------------------------
  skip avanti di un record
  --------------------------------------------------------------------------*/
int DB_next(int handle)
{
  if(dbdata[handle]==0) return(-1);
  return(x4skip(&xdb[handle],1L));
}

/*-------------------------------------------------------------------------
  skip indietro di un record
  --------------------------------------------------------------------------*/
int DB_prev(int handle)
{
  if(dbdata[handle]==0) return(-1);
  return(x4skip(&xdb[handle],-1L));
}


/*-------------------------------------------------------------------------
  skip di n records
  --------------------------------------------------------------------------*/
int DB_skip(int handle,long int recno)
{
  if(dbdata[handle]==0) return(-1);
  return(x4skip(&xdb[handle],recno));
}

/*-------------------------------------------------------------------------
  locka il record attuale
  --------------------------------------------------------------------------*/
int DB_lock(int handle)
{
  if(dbdata[handle]==0) return(-1);
  return(d4lock(dbdata[handle],d4recno(dbdata[handle])));
}

/*-------------------------------------------------------------------------
  slocka il record attuale
  --------------------------------------------------------------------------*/
int DB_unlock(int handle)
{
  if(dbdata[handle]==0) return(-1);
  return(d4unlock(dbdata[handle]));
}

/*-------------------------------------------------------------------------
  cerca la chiave, torna r4after se not found ma si e' posizionato sul record
  successivo, se torna r4eof e' su eof
  --------------------------------------------------------------------------*/
int DB_seek(int handle,char *key)
{                                      
  int rc, len;  
  TAG4 * tt;  
  char * k;

  if(dbdata[handle]==0) return(-1);
  rc = x4seek(&xdb[handle],key);
  if (rc)
    return rc;                           
  tt = d4tag_selected(dbdata[handle]); 
  len = expr4key_len(tt->expr);
  k = t4key(tt);
  while (len > 0 && k[len-1] == ' ') len--;  
  rc = strncmp(key, k, len);
  if (rc == 0)
    return 0;
  else 
    if (rc < 0)    
      return r4after;
    else
      return r4eof;
}

/*-------------------------------------------------------------------------
  torna 1 se eof, 0 altrimenti
  --------------------------------------------------------------------------*/
int DB_eof(int handle)
{
  if(dbdata[handle]==0) return(-1);
  return(d4eof(dbdata[handle]));
}

/*-------------------------------------------------------------------------
  torna 1 se tof, 0 altrimenti
  --------------------------------------------------------------------------*/
int DB_bof(int handle)
{
  if(dbdata[handle]==0) return(-1);
  return(d4bof(dbdata[handle]));
}

/*-------------------------------------------------------------------------
  legge un record per numero record
  --------------------------------------------------------------------------*/
int DB_go(int handle,long int recno)
{
  if(dbdata[handle]==0) return(-1);
  return(x4go(&xdb[handle],recno));
}

/*-------------------------------------------------------------------------
  cancella il record attuale. Non cancella le chiavi.
  --------------------------------------------------------------------------*/
int DB_delete(int handle)
{   
  
  if(dbdata[handle]==0) return(-1);
  d4delete(dbdata[handle]);
  return(0);
}       

/*-------------------------------------------------------------------------
  ripristina il record attuale
  --------------------------------------------------------------------------*/
int DB_recall(int handle)
{   
  
  if(dbdata[handle]==0) return(-1);
  d4recall(dbdata[handle]);
  return(0);
}       


/*-------------------------------------------------------------------------
  cancella dall'indice corrente la chiave specificata
  --------------------------------------------------------------------------*/
int DB_delkey(int handle, char* key, long recno)                
{
  INDEX4* i;
  TAG4*   t;
  int rt=0;
  int is_locked = 0; /* Diverso da 0 se il file e' bloccato in modo esclusivo */             
  /* (dati o indice) da me stesso*/
  char fn[64];  
  
  if(dbdata[handle]==0) return(-1);
  t=d4tag_default(dbdata[handle]);
  
  if (u4switch() & 2 || u4switch()  & 8) /* Clipper & DBIII */
    u4name_piece(fn,64,dbdata[handle]->file.name,0,0); 
  else
    strcpy(fn,dbdata[handle]->file.name);   
  if ((i=d4index(dbdata[handle],fn)) == NULL)
    return(e4index); 
  is_locked = DB_file_locked(handle);
  if (is_locked == 0)   /* Se non e' stato bloccato in modo esclusivo */
  {
    while ((rt=i4lock(i)) == r4locked) 
#ifdef DBG
      yesnofatal_box("Sono in attesa nella DB_delkeys");
#else  
    u4delay_sec();
#endif
  }
  if (rt == 0)
    rt=t4remove_calc(t,recno);              
  if (is_locked == 0)  /* Siccome ho fatto il lock dell'indice, devo anche sbloccarlo */
    i4unlock(i);
  return(rt);
}

/*-------------------------------------------------------------------------
  cancella dall'indice corrente la chiave specificata
  --------------------------------------------------------------------------*/
int DB_flush(int handle)                
{
  int rt;
  
  while ((rt = d4flush(dbdata[handle])) == r4locked)
#ifdef DBG
    yesnofatal_box("Sono in attesa nella DB_flush");
#else  
  u4delay_sec();                                        
#endif      
  return rt;
}

/*-------------------------------------------------------------------------
  riscrive il record attuale
  --------------------------------------------------------------------------*/
int DB_rewrite(int handle)
{
  int rt;
  if(dbdata[handle]==0) return(-1);
  while ((rt=d4write(dbdata[handle],d4recno(dbdata[handle]))) == r4locked)
#ifdef DBG
    yesnofatal_box("Sono in attesa nella DB_rewrite");
#else  
  u4delay_sec();
#endif
  if (rt == 0)
  {         
    while ((rt = d4flush(dbdata[handle])) == r4locked)
#ifdef DBG
      yesnofatal_box("Sono in attesa nella DB_rewrite (d4flush)");
#else  
    u4delay_sec();                                        
#endif      
  }
  if (rt == e4unique)
  {
    DB_get_error();
    fatal_box("Errore in DB_rewrite(): chiav  duplicata nell' indice %ld, file %s",
              dbdata[handle]->rec_num + 1, dbdata[handle]->file.name);
  }
  rt=DB_unlock(handle);
  return (rt);
}


/*-------------------------------------------------------------------------
  appende il record attuale
  --------------------------------------------------------------------------*/
int DB_add(int handle)
{          
  int rt;                           
  int is_locked = 0;
  DATA4 * data = dbdata[handle];
  
  if (data==0) return(-1); 
  is_locked = DB_file_locked(handle);
  if (is_locked == 0)   /* Se non e' stato gia' bloccato in modo esclusivo */
  {
    while (file4lock(&data->file, L4LOCK_POS_OLD, 1L) == r4locked)
#ifdef DBG
      yesnofatal_box("Sono in attesa nella DB_add");
#else  
    u4delay_sec();
#endif                    
  }
  while ((rt = d4append_start(data,0)) == r4locked)
#ifdef DBG
    yesnofatal_box("Sono in attesa nella DB_add (d4append_start)");
#else  
  u4delay_sec();
#endif                    
  if (rt == 0)
  {
    d4recall(data);
    while ((rt = d4append(data)) == r4locked)
#ifdef DBG
      yesnofatal_box("Sono in attesa nella DB_add (d4append)");
#else  
    u4delay_sec();                                        
#endif      
    if (rt == e4unique)
    {
      DB_get_error();
      if (data->rec_num > 0)
        fatal_box("Errore in DB_add(): chiave duplicata nell' indice %ld, file %s",
                  data->rec_num + 1, data->file.name);
      else
        rt = _isreinsert;
    }
    else
      if (rt == 0)
      {         
        while ((rt = d4flush(data)) == r4locked)
#ifdef DBG
          yesnofatal_box("Sono in attesa nella DB_add (d4flush)");
#else  
        u4delay_sec();                                        
#endif      
      }
  }
  DB_unlock(handle);
  if (is_locked == 0)   
  {
    file4unlock(&data->file, L4LOCK_POS_OLD, 1L);
  }
  return(rt);
}

/*-------------------------------------------------------------------------
  Blocca in modo esclusivo il file dati ed indice
  --------------------------------------------------------------------------*/
int DB_lockfile(int handle)
{  
  int rt;

  if(dbdata[handle]==0) return(-1);
  rt = d4lock_file(dbdata[handle]);
  if (rt==0) rt=d4lock_index(dbdata[handle]); 
  return(rt);
}

HIDDEN int  is_inkey(RecDes *r, int numfield)
{                                            
  int found=0,i,j;      
  
  for (i=0; ((i < MaxKeys) && (i < r->NKeys) && !found); i++)
    for (j=0; ((j < r->Ky[i].NkFields) && !found); j++)
      if (numfield == r->Ky[i].FieldSeq[j])
        found=1;
  return(found);                        
}                                         

HIDDEN void do_key(char *fname, RecDes *r, TAG4INFO *tag_info)
{
  int i,j;
  char tiname[9];      /* Tag name, max 8 characters long! */
  
  strcpy(tiname,fname);
  CUpString(tiname); 
  for (i=0; ((i < MaxKeys) && (i < r->NKeys)); i++)    
  {
    tag_info[i].name=(char *)u4alloc(9);    
    tag_info[i].expression=(char *)u4alloc(128);    
    tag_info[i].filter=(char*)u4alloc(20);              
    tag_info[i].descending=0;
    if (r->Ky[i].DupKeys)
      tag_info[i].unique=0;
    else
      tag_info[i].unique=e4unique;    
    strcpy(tag_info[i].filter,".NOT. DELETED()"); /* Not available for DBIII and CLIPPER */
    strcpy(tag_info[i].name,tiname) ;
    if (strlen(tiname) < 8)
      strcat(tag_info[i].name," ");
    tag_info[i].name[strlen(tag_info[i].name)-1] = '0' + i + 1;               
    for (j=0; j < r->Ky[i].NkFields; j++)
    {
      int nf= r->Ky[i].FieldSeq[j]; 
      if (nf > MaxFields)                /* When Upper field is specified */
      {
        nf -= MaxFields;                     
        strcat(tag_info[i].expression,"UPPER(");
      }
      if (r->Ky[i].FromCh[j] != 255)     /* When partial field is specified   */
        strcat(tag_info[i].expression,"SUBSTR(");
      
      switch (r->Fd[nf].TypeF)           /* When numeric field in key is specified */
      {
      case _intfld:
      case _longfld:       
      case _realfld:
      case _wordfld:
      case _intzerofld:
      case _longzerofld:
        strcat(tag_info[i].expression,"STR(");
        break;
      case _datefld:
        strcat(tag_info[i].expression,"DTOS(");
        break;
      case _boolfld:
        strcat(tag_info[i].expression,"IIF(");  /* Logical fields are in key too.. */
        break;
      default:                        /* It's a non sense to keep _realfld in key... */
        break;                        /* however it's possible to have it... */
      }                               /* Le chiavi composte da campi data non necessitano di funzioni di traduzione. */
      
      strcat(tag_info[i].expression,r->Fd[nf].Name); /* Append field name */
      
      if (r->Ky[i].FromCh[j] != 255)                 /* If partial field was specified */
      {                                              /* add parameters to SUBSTR */
        char ts[8];
        
        strcat(tag_info[i].expression,",");
        sprintf(ts,"%d",r->Ky[i].FromCh[j]);
        strcat(tag_info[i].expression,ts);
        strcat(tag_info[i].expression,",");
        sprintf(ts,"%d",r->Ky[i].ToCh[j] - r->Ky[i].FromCh[j] + 1);
        strcat(tag_info[i].expression,ts);
        strcat(tag_info[i].expression,")");
      }           
      
      switch (r->Fd[nf].TypeF)                 /* If numeric field was specified  */
      {                                        /* add parameters to STR */
      case _intfld:
      case _longfld:       
      case _realfld:                           /* Questo tipo di campo(real) non ha senso in un a chiave... */
      case _wordfld:
      case _intzerofld:
      case _longzerofld:  
      {    
        char ts[8];
        strcat(tag_info[i].expression,",");
        sprintf(ts,"%d",r->Fd[nf].Len);
        strcat(tag_info[i].expression,ts);
        strcat(tag_info[i].expression,",0)");
      }
      break;
    case _boolfld:                       
      strcat(tag_info[i].expression,",\"T\",\"F\")");
      break;
    default:
      break;
    }
      
      /* Close parentheses if UPPER or DTOS operators were used: */
      if (r->Ky[i].FieldSeq[j] > MaxFields || (r->Fd[nf].TypeF == _datefld)) 
        strcat(tag_info[i].expression,")");    
      /* If there's another field in key adds "+" operator: */
      if ((j < (r->Ky[i].NkFields-1)) && (strlen(r->Fd[nf].Name) > 0))           
        strcat(tag_info[i].expression,"+");
    }
  }                     
  tag_info[i].name=NULL;
  tag_info[i].expression=NULL;
  tag_info[i].filter=NULL;
  tag_info[i].unique=0;
  tag_info[i].descending=0;
}    

/*-------------------------------------------------------------------------
  Compatta il file dati 
  --------------------------------------------------------------------------*/
int DB_packfile(short vis, const char * filename, long eod)
{                              
  int rt=0,handle;
  
  /*
     if(dbdata[handle]==0) return(-1);
     codebase.auto_open = 0 cosi' non apre gli indici
     */
  code_base.auto_open = 0;
  handle=DB_open(filename,1); /* Exclusive mode open! */
  if (handle > -1)
  { 
    char s[81];
    
    if (vis)
    {
      strcpy(s,"Compattamento dati file : ");
      strcat(s,(char*)filename);
#ifndef FOXPRO
      progind_create(10L,s,1,1,1);
#endif
    }                   
    if (eod < d4reccount(dbdata[handle]))
    {
      rt=d4zap(dbdata[handle],++eod,d4reccount(dbdata[handle]));
    } else
      rt=d4pack(dbdata[handle]);
    if (vis)
    {        
#ifndef FOXPRO
      progind_set_status((long)10);
      progind_destroy();
#endif
    }
    DB_close(handle);             
  }                                      
  else 
    rt=code_base.error_code;
  code_base.auto_open = 1;
  return rt;
}

/*-------------------------------------------------------------------------
  Compatta il file dati 
  --------------------------------------------------------------------------*/
int DB_packmemo(short vis, const char * filename)
{                              
  int rt=0,handle;
  
  code_base.auto_open = 0;
  handle=DB_open(filename,1); /* Exclusive mode open! */
  if (handle > -1)
  { 
    char s[81];
    
    if (vis)
    {
      strcpy(s,"Compattamento memo file : ");
      strcat(s,(char*)filename);
#ifndef FOXPRO
      progind_create(10L,s,1,1,1);
#endif
    }                   
    rt=d4memo_compress(dbdata[handle]);
    if (vis)
    {        
#ifndef FOXPRO
      progind_set_status((long)10);
      progind_destroy();
#endif
    }
    DB_close(handle);             
  }                                      
  else 
    rt=code_base.error_code;
  code_base.auto_open = 1;
  return rt;
}

/*-------------------------------------------------------------------------
  Compatta gli indici
  --------------------------------------------------------------------------*/
int DB_packindex(short vis, const char * filename, RecDes *r, long *peod)
{                       
  int rt=0,handle;          
  TAG4INFO tags[MaxKeys+1];
  char s[82];
  
  strcpy(s,"Ricostruzione indici file : ");
  strcat(s,filename);
  
  /* codebase.auto_open = 0 cosi' non apre gli indici */
  code_base.auto_open=0 ;
  handle=DB_open(filename,1); /* Exclusive mode open */
  if (handle > -1)        
  {       
    int i;                                 
    char *ff = find_slash_backslash((char *)filename);
    if (vis) 
    {
#ifndef FOXPRO
      progind_create((long)r->NKeys,s,1,1,1);
#endif
    }
    if ((ff == NULL) || *ff == '\0')
      ff =  (char *)filename;   
    else
      ff++;     
    do_key(ff,r,tags);
    if (u4switch() &2 || u4switch() & 8) /* Clipper and DBIII */
    {                           
      INDEX4 * w = i4create(dbdata[handle],(char*)filename,tags); 
#ifndef FOXPRO
      progind_set_status((long)r->NKeys);
#endif
      if (w != NULL && code_base.error_code==0)
      {
        FILE *fp;
        char cgp[81];
        
        strcpy(cgp,filename);
        strcat(cgp,".CGP");
        if ((fp=fopen(cgp,"w"))!=NULL)
        { 
          int j;  
          
          for (j=0; j<r->NKeys;j++)
            fprintf(fp,"%s\n",tags[j].name);
          fclose(fp);
        }
      }
    }           
    if (u4switch() & 1 || u4switch() & 4) /* FOXPRO and DBIV */
    {
      INDEX4 * w = i4create(dbdata[handle],NULL,tags);
      if (w == NULL) rt = code_base.error_code; 
#ifndef FOXPRO
      progind_set_status((long)r->NKeys);
#endif
    }
    for (i=0; ((i < MaxKeys) && (i < r->NKeys)); i++)
    {
      u4free(tags[i].name);
      u4free(tags[i].expression);
      u4free(tags[i].filter);
    }       
    if (vis) 
    {
#ifndef FOXPRO
      progind_destroy();
#endif
    }
    *peod=DB_reccount(handle);  
    DB_close(handle);
  }
  code_base.auto_open = 1;
  return(code_base.error_code); 
}


/*-------------------------------------------------------------------------
  costruisce il file filename 
  --------------------------------------------------------------------------*/

int DB_build(const char * filename, RecDes *r) 
{                                                                       

  FIELD4INFO field_info[MaxFields+1]; /* Numero di campi in un record */
  TAG4INFO   tag_info[MaxKeys+1];     /* Numero di chiavi in un file  */
  DATA4      *dbuilded;
  int rt=0,i;
  char *ff = find_slash_backslash((char *)filename);
  
  
  for (i=0; ((i<r->NFields) && (i<MaxFields)); i++) /* Construct field_info */
  {                                                
    field_info[i].name = (char*)u4alloc(12);
    strcpy(field_info[i].name,r->Fd[i].Name);
    field_info[i].len  = r->Fd[i].Len;
    field_info[i].dec  = r->Fd[i].Dec;  
    switch (r->Fd[i].TypeF)
    { 
    case _intfld:
    case _longfld:       
    case _realfld:     /* It's a non sense to keep this in key! */
    case _wordfld:
    case _intzerofld:
    case _longzerofld:
      field_info[i].type=r4num;
      break;
    case _boolfld:
      field_info[i].type=r4log;
      break;
    case _datefld:
      field_info[i].type=r4date;
      break;
    case _memofld:
      field_info[i].type=r4memo;
      field_info[i].len = 10;
      field_info[i].dec = 0;
      break;
    case _charfld:
    default:
      field_info[i].type=r4str;
      break;                                                                                                  
    }
  }                       
  field_info[i].name=NULL;
  field_info[i].type=0;
  field_info[i].len=0;
  field_info[i].dec=0;
  
  if (ff == NULL || *ff == '\0')
    ff = (char *) filename;     
  else
    ff++;     
  do_key(ff,r,tag_info);  
  
  if ((dbuilded=d4create(&code_base, (char *)filename, field_info,  tag_info))==0) /* deve solo creare il file dati vuoto */
    rt=code_base.error_code;                                                             
  else
    rt=d4close(dbuilded);  
  if (u4switch() & 2 || u4switch() & 8) /* Rebuild filename.cgp for CLIPPER AND DBIII only */
  {     
    FILE* fp;   
    char xx[81];
    
    strcpy(xx,(char*)filename);
    
    strcat(xx,".cgp");
    if ((fp=fopen(xx,"w"))!=NULL)
    {         
      int j;     
      for (j=0; tag_info[j].name ; j++)
        fprintf(fp,"%s\n",tag_info[j].name);
      fclose(fp);
    }
  }
  if (rt!=0)   
    rt=code_base.error_code;
  for (i=0; ((i < MaxFields) && (i < r->NFields)); i++)
    u4free(field_info[i].name);
  for (i=0; ((i < MaxKeys) && (i < r->NKeys)); i++)
  {
    u4free(tag_info[i].name);
    u4free(tag_info[i].expression);
    u4free(tag_info[i].filter);
  }       
  return(rt) ;
}

/*-------------------------------------------------------------------------
  ritorna l'ultimo errore
  --------------------------------------------------------------------------*/

int DB_get_error(void)     
{ 
  int rt = code_base.error_code;
  code_base.error_code=0;
  return (rt);
}

/*-------------------------------------------------------------------------
  Azzera la viarabile globale d'errore
  --------------------------------------------------------------------------*/

void DB_zero_error(void)
{
  code_base.error_code=0;
}

/*-------------------------------------------------------------------------
  Si posiziona sull'indice attivo alla chiave indicata, restituisce 
  la prima chiave che trova >= a from. (TCusrsor)
  --------------------------------------------------------------------------*/

int DB_index_seek(int handle, char* from)
{  
  TAG4 *t;

  if(dbdata[handle]==0) return(-1);
  if ((t=d4tag_default(dbdata[handle]))==NULL) return(-1);
  
  if (t4seek(t,from,strlen(from)) < 0) return(DB_get_error());
  return(0); 
  
} 

/*-------------------------------------------------------------------------
  Ritorna il numero di record corrispondente alla chiave corrente
  --------------------------------------------------------------------------*/

long DB_index_recno(int handle)
{
  TAG4 *t;
  
  if(dbdata[handle]==0) return(-1);
  if ((t=d4tag_default(dbdata[handle]))==NULL) return(-1);
  
  return(t4recno(t));
}

/*-------------------------------------------------------------------------
  Si posiziona sulla chiave successiva dell'indice corrente
  --------------------------------------------------------------------------*/
long DB_index_next(int handle)    
{
  TAG4 *t;

  if(dbdata[handle]==0) return(-1);
  if ((t=d4tag_default(dbdata[handle]))==NULL) return(-1);
  
  return(t4skip(t,1L));
}

/*-------------------------------------------------------------------------
  Restituisce la chiave corrente
  --------------------------------------------------------------------------*/

char* DB_index_getkey(int handle)
{
  TAG4 *t;  
  static char key[MAXLEN];       
  int klen;

  if(dbdata[handle]==0) return(NULL);
  if ((t=d4tag_default(dbdata[handle]))==NULL) return(NULL);
  klen=expr4key_len(t->expr);
  if (klen > (MAXLEN-1)) klen=MAXLEN-1;
  memcpy(key,t4key(t),klen); /* t4key non restituisce una null terminated string */
  key[klen]='\0';
  return(key);
}

/*-------------------------------------------------------------------------
  Restituisce vero se l'indice e' alla fine
  --------------------------------------------------------------------------*/

int DB_index_eof(int handle)
{
  TAG4 *t;         

  if(dbdata[handle]==0) return(-1);
  if ((t=d4tag_default(dbdata[handle]))==NULL) return(-1);
  return(t4eof(t));     
}

/*-------------------------------------------------------------------------
  Blocca il record indicato
  --------------------------------------------------------------------------*/

int DB_lock_rec(int handle,long nrec)
{
  if(dbdata[handle]==0) return(-1);
  if(d4lock(dbdata[handle],nrec)==r4locked) return(-1);
  else return(0);
}

/*-------------------------------------------------------------------------
  Ritorna vero(non zero) se il file dati od indice sono stati bloccati 
  in modo esclusivo dalla presente applicazione, non da altri programmi!!!
  --------------------------------------------------------------------------*/


int DB_file_locked(int handle)         
{
  if(dbdata[handle]==0) return(-1);
  return(d4lock_test_file(dbdata[handle]) + d4lock_test_index(dbdata[handle]));
}

/*-------------------------------------------------------------------------
  Ritorna vero se il record nrec e' bloccato dalla presente applicazione,
  non da altri programmi!!!
  --------------------------------------------------------------------------*/

int DB_rec_locked(int handle,long nrec)
{
  if(dbdata[handle]==0) return(-1);
  return(d4lock_test(dbdata[handle],nrec));
}   

/*-------------------------------------------------------------------------
  restituisce la configurazione della libreria
  --------------------------------------------------------------------------*/

long DB_getconf()
{
  return(u4switch());
}

/*-------------------------------------------------------------------------
  Restituisce il numero di versione scritto sull'header dell'indice
  --------------------------------------------------------------------------*/

long DB_changed(int handle)
{                    
  INDEX4 *i;        
  char fn[64];
  
  if(dbdata[handle]==0) return(-1);    
  if (u4switch() & 2 || u4switch()  & 8) /* Clipper & DBIII */
    u4name_piece(fn,64,dbdata[handle]->file.name,0,0); 
  else
    strcpy(fn,dbdata[handle]->file.name);       
  i=d4index(dbdata[handle],fn);
  if (i == NULL) return(-1);
  
  return(i4changed(i));   
}

char* DB_memoptr( const int handle, const char * fieldname )
{
  FIELD4 * f;
  f = d4field( dbdata[ handle ], ( char *)fieldname );
  return f4memo_ptr( f );
}

int DB_memowrite( const int handle, const char * fieldname, const char * data )  
{ 
  int ret;
  
  FIELD4 * f;
  f = d4field( dbdata[ handle ], fieldname );
  ret = f4memo_assign( f, ( char * )data );
  d4flush_data( dbdata[ handle ] );
  return ret;  
}

