eba1707224
git-svn-id: svn://10.65.10.50/branches/R_10_00@23122 c028cbd2-c16b-5b4b-a496-9718f37d4682
1199 lines
34 KiB
C
Executable File
1199 lines
34 KiB
C
Executable File
/*
|
|
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.
|
|
|
|
*/
|
|
|
|
#define XVT_INCL_NATIVE
|
|
#include <xvt.h>
|
|
#define S4OFF_REPORT
|
|
//#define S4DLL
|
|
//#define S4WIN32
|
|
|
|
#include <d4all.h>
|
|
#include <codeb.h>
|
|
#include <rectypes.h>
|
|
|
|
|
|
/*--------------------------------------------------------------------------
|
|
numero massimo di database aperti contemporaneamente
|
|
--------------------------------------------------------------------------*/
|
|
#define CB4FILES 64
|
|
|
|
#define MAXLEN 137 /* Lunghezza massima chiave */
|
|
|
|
static CODE4 code_base;
|
|
static DATA4* dbdata[CB4FILES];
|
|
static bool dbfast[CB4FILES];
|
|
|
|
#define HANDLE2DATA(handle, data) DATA4* data = dbdata[handle]; if (handle < 0 || handle >= CB4FILES || data == NULL || data->clientId < 0) return -1
|
|
#define HANDLE2DATASTR(handle, data) DATA4* data = dbdata[handle]; if (handle < 0 || handle >= CB4FILES || data == NULL || data->clientId < 0) return NULL
|
|
|
|
static const char* find_slash_backslash(const char* s)
|
|
{
|
|
const char* slash = NULL;
|
|
for ( ; *s; s++)
|
|
{
|
|
if (*s == '\\' || *s == '/')
|
|
slash = s;
|
|
}
|
|
return slash;
|
|
}
|
|
|
|
|
|
/*--------------------------------------------------------------------------
|
|
inizializzazione di CodeBase e delle variabili necessarie
|
|
questa funzione deve essere chiamata SOLO una volta all'interno
|
|
di ciascun eseguibile
|
|
--------------------------------------------------------------------------*/
|
|
void DB_init(void)
|
|
{
|
|
memset(dbdata, 0, sizeof(dbdata));
|
|
|
|
code4init(&code_base);
|
|
// Nella 3.0 il default LOCK4DATA e' stato messo nella DLL per non ridistribuire tutto.
|
|
// Nelle versioni succesive la DLL e' "intonsa" ed ha un default diverso deciso da Codebase.
|
|
// Per cui ora forziano LOCK4DATA (anche se considerato deprecated)
|
|
// in quanto non funziona bene LOCK4RECORD (che sarebbe quello giusto)
|
|
code4unlockAutoSet(&code_base, LOCK4DATA);
|
|
code_base.readLock=0;
|
|
code_base.errDefaultUnique=e4unique;
|
|
code_base.safety=0;
|
|
code_base.lockAttempts=1;
|
|
code4dateFormatSet(&code_base, "CCYYMMDD");
|
|
|
|
code_base.optimize = OPT4EXCLUSIVE; // Is the default?
|
|
code_base.optimizeWrite = OPT4EXCLUSIVE; // Is the default?
|
|
}
|
|
|
|
/*--------------------------------------------------------------------------
|
|
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)
|
|
{
|
|
int i;
|
|
for (i = 0; i < CB4FILES; i++)
|
|
{
|
|
if (dbdata[i] != NULL)
|
|
DB_close(i);
|
|
}
|
|
code4initUndo(&code_base);
|
|
}
|
|
|
|
/*-------------------------------------------------------------------------
|
|
apertura del file 'filename'. Il parametro mode consente se != 0 l'apertura
|
|
esclusiva. Il parametro index consente se == 0 l'apertura senza indici
|
|
--------------------------------------------------------------------------*/
|
|
|
|
int DB_open(const char *filename,int mode,int index)
|
|
{
|
|
int i,found;
|
|
/* cerca il primo posto libero nel vettore dbdata */
|
|
found=-1;
|
|
for(i=0;i<CB4FILES;i++)
|
|
{
|
|
if(dbdata[i]==(DATA4*)NULL)
|
|
{
|
|
found=i;
|
|
break;
|
|
}
|
|
}
|
|
if (found >= 0)
|
|
{
|
|
if (mode)
|
|
code_base.accessMode=1; // Exclusive mode
|
|
|
|
code_base.errorCode=0;
|
|
if (!index)
|
|
{
|
|
code_base.autoOpen = 0; // Se e' stata richiesta l'apertura senza indici, resetta il flag autoOpen
|
|
dbdata[found]=d4open(&code_base, filename);
|
|
code_base.autoOpen = 1;
|
|
}
|
|
else
|
|
dbdata[found]=d4open(&code_base, filename);
|
|
|
|
if (mode)
|
|
code_base.accessMode=0; // Not-Exclusive mode
|
|
|
|
if(dbdata[found]!=NULL)
|
|
{
|
|
HANDLE2DATA(found, data);
|
|
|
|
// Guy: Non capisco bene perche' seleziono comunque un indice
|
|
d4tagSelect(data, d4tagDefault(data));
|
|
if (d4recCount(data) > 0)
|
|
d4top(data);
|
|
|
|
dbfast[found] = mode!=0; // Ottimizabile se esclusivo
|
|
}
|
|
else
|
|
return code_base.errorCode;
|
|
}
|
|
return found;
|
|
}
|
|
|
|
|
|
/*-------------------------------------------------------------------------
|
|
chiusura del database inviduato da handle
|
|
torna -1 se il database non puo essere chiuso
|
|
--------------------------------------------------------------------------*/
|
|
int DB_close(int handle)
|
|
{
|
|
HANDLE2DATA(handle, data);
|
|
|
|
d4close(data);
|
|
dbdata[handle] = NULL;
|
|
dbfast[handle] = FALSE;
|
|
code_base.errorCode=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)
|
|
{
|
|
HANDLE2DATASTR(handle, data);
|
|
return d4record(data);
|
|
}
|
|
|
|
/*-------------------------------------------------------------------------
|
|
torna la lunghezza del record
|
|
--------------------------------------------------------------------------*/
|
|
int DB_reclen(int handle)
|
|
{
|
|
HANDLE2DATA(handle, data);
|
|
return (int)d4recWidth(data);
|
|
}
|
|
|
|
/*-------------------------------------------------------------------------
|
|
torna la lunghezza della chiave corrente
|
|
--------------------------------------------------------------------------*/
|
|
|
|
int DB_keylen(int handle)
|
|
{
|
|
HANDLE2DATA(handle, data);
|
|
return a4tagKeyLen(data);
|
|
}
|
|
|
|
/*-------------------------------------------------------------------------
|
|
torna il numero del record attuale
|
|
--------------------------------------------------------------------------*/
|
|
long int DB_recno(int handle)
|
|
{
|
|
HANDLE2DATA(handle, data);
|
|
return d4recNo(data);
|
|
}
|
|
|
|
/*-------------------------------------------------------------------------
|
|
torna il numero complessivo dei records presenti nell'archivio
|
|
--------------------------------------------------------------------------*/
|
|
long int DB_reccount(int handle)
|
|
{
|
|
HANDLE2DATA(handle, data);
|
|
return d4recCount(data);
|
|
}
|
|
|
|
/*-------------------------------------------------------------------------
|
|
seleziona un indice sul database specificato
|
|
torna -1 se errore, altrimenti 0
|
|
--------------------------------------------------------------------------*/
|
|
int DB_tagselect(int handle,int index_no)
|
|
{
|
|
TAG4 *tt;
|
|
int i;
|
|
|
|
HANDLE2DATA(handle, data);
|
|
/* si posiziona sul primo indice */
|
|
tt=d4tagNext(data, NULL);
|
|
if (tt==NULL)
|
|
return -1;
|
|
for(i=1; i<index_no; i++)
|
|
{
|
|
tt=d4tagNext(data, tt);
|
|
if(tt==NULL)
|
|
return(-1);
|
|
}
|
|
d4tagSelect(data, tt);
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*-------------------------------------------------------------------------
|
|
torna il numero dell'indice selezionato
|
|
torna -1 se errore
|
|
--------------------------------------------------------------------------*/
|
|
int DB_tagget(int handle)
|
|
{
|
|
TAG4 *tt,*tt1;
|
|
int i;
|
|
HANDLE2DATA(handle, data);
|
|
|
|
/* si posiziona sul primo indice */
|
|
tt=d4tagDefault(data);
|
|
if(tt==NULL)
|
|
return(-1);
|
|
|
|
tt1=d4tagNext(data,NULL);
|
|
i=1;
|
|
while(tt!=tt1 && tt1!=NULL)
|
|
{
|
|
tt1=d4tagNext(data,tt1);
|
|
i++;
|
|
}
|
|
return(i);
|
|
}
|
|
|
|
/*-------------------------------------------------------------------------
|
|
si posiziona sul primo record
|
|
torna -1 se errore
|
|
--------------------------------------------------------------------------*/
|
|
int DB_first(int handle)
|
|
{
|
|
HANDLE2DATA(handle, data);
|
|
return d4top(data);
|
|
}
|
|
|
|
|
|
/*-------------------------------------------------------------------------
|
|
si posiziona sull'ultimorecord
|
|
torna -1 se errore
|
|
--------------------------------------------------------------------------*/
|
|
int DB_last(int handle)
|
|
{
|
|
HANDLE2DATA(handle, data);
|
|
return d4bottom(data);
|
|
}
|
|
|
|
|
|
/*-------------------------------------------------------------------------
|
|
skip avanti di un record
|
|
--------------------------------------------------------------------------*/
|
|
int DB_next(int handle)
|
|
{
|
|
return DB_skip(handle, +1);
|
|
}
|
|
|
|
/*-------------------------------------------------------------------------
|
|
skip indietro di un record
|
|
--------------------------------------------------------------------------*/
|
|
int DB_prev(int handle)
|
|
{
|
|
return DB_skip(handle, -1);
|
|
}
|
|
|
|
/*-------------------------------------------------------------------------
|
|
skip di n records
|
|
--------------------------------------------------------------------------*/
|
|
int DB_skip(int handle, long recno)
|
|
{
|
|
HANDLE2DATA(handle, data);
|
|
return d4skip(data, recno);
|
|
}
|
|
|
|
/*-------------------------------------------------------------------------
|
|
locka il record attuale
|
|
--------------------------------------------------------------------------*/
|
|
int DB_lock(int handle)
|
|
{
|
|
HANDLE2DATA(handle, data);
|
|
return d4lock(data, d4recNo(data));
|
|
}
|
|
|
|
/*-------------------------------------------------------------------------
|
|
slocka il record attuale
|
|
--------------------------------------------------------------------------*/
|
|
int DB_unlock(int handle)
|
|
{
|
|
HANDLE2DATA(handle, data);
|
|
return d4unlock(data);
|
|
}
|
|
|
|
/*-------------------------------------------------------------------------
|
|
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;
|
|
const char * k;
|
|
|
|
HANDLE2DATA(handle, data);
|
|
rc = d4seek(data,key);
|
|
if (rc)
|
|
return rc;
|
|
len = a4tagKeyLen(data);
|
|
k = a4tagKey(data);
|
|
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)
|
|
{
|
|
HANDLE2DATA(handle, data);
|
|
return d4eof(data);
|
|
}
|
|
|
|
/*-------------------------------------------------------------------------
|
|
torna 1 se bof, 0 altrimenti
|
|
--------------------------------------------------------------------------*/
|
|
int DB_bof(int handle)
|
|
{
|
|
HANDLE2DATA(handle, data);
|
|
return d4bof(data);
|
|
}
|
|
|
|
/*-------------------------------------------------------------------------
|
|
legge un record per numero record
|
|
--------------------------------------------------------------------------*/
|
|
int DB_go(int handle,long recno)
|
|
{
|
|
HANDLE2DATA(handle, data);
|
|
return d4go(data,recno);
|
|
}
|
|
|
|
/*-------------------------------------------------------------------------
|
|
cancella il record attuale. Non cancella le chiavi.
|
|
--------------------------------------------------------------------------*/
|
|
int DB_delete(int handle)
|
|
{
|
|
HANDLE2DATA(handle, data);
|
|
d4delete(data);
|
|
return 0;
|
|
}
|
|
|
|
/*-------------------------------------------------------------------------
|
|
ripristina il record attuale
|
|
--------------------------------------------------------------------------*/
|
|
int DB_recall(int handle)
|
|
{
|
|
HANDLE2DATA(handle, data);
|
|
d4recall(data);
|
|
return 0;
|
|
}
|
|
|
|
/*-------------------------------------------------------------------------
|
|
|
|
--------------------------------------------------------------------------*/
|
|
int DB_flush(int handle)
|
|
{
|
|
int rt = 0;
|
|
HANDLE2DATA(handle, data);
|
|
|
|
if (dbfast[handle])
|
|
{
|
|
if (data->dataFile->nFieldsMemo > 0)
|
|
rt = d4write(data, -1);
|
|
}
|
|
else
|
|
{
|
|
while ((rt = d4flush(data)) == r4locked)
|
|
u4delaySec();
|
|
}
|
|
return rt;
|
|
}
|
|
|
|
/*-------------------------------------------------------------------------
|
|
riscrive il record attuale
|
|
--------------------------------------------------------------------------*/
|
|
int DB_rewrite(int handle)
|
|
{
|
|
int rt = -1;
|
|
HANDLE2DATA(handle, data);
|
|
while ((rt=d4write(data,d4recNo(data))) == r4locked)
|
|
u4delaySec();
|
|
|
|
if (rt == e4unique)
|
|
{
|
|
char msg[256];
|
|
DB_get_error();
|
|
sprintf(msg, "Errore in DB_rewrite(): chiave duplicata nel record %ld, file %s",
|
|
d4recNo(data) + 1, d4fileName(data));
|
|
xvt_dm_post_fatal_exit(msg);
|
|
}
|
|
|
|
if (rt == 0)
|
|
rt = DB_flush(handle);
|
|
rt = DB_unlock(handle);
|
|
return (rt);
|
|
}
|
|
|
|
|
|
/*-------------------------------------------------------------------------
|
|
appende il record attuale
|
|
--------------------------------------------------------------------------*/
|
|
int DB_add(int handle)
|
|
{
|
|
int rt = -1;
|
|
HANDLE2DATA(handle, data);
|
|
|
|
while ((rt = d4appendStart(data,0)) == r4locked)
|
|
u4delaySec();
|
|
|
|
if (rt == 0)
|
|
{
|
|
d4recall(data);
|
|
while ((rt = d4append(data)) == r4locked)
|
|
u4delaySec();
|
|
if (rt == e4unique)
|
|
{
|
|
const long rec_num = d4recNo(data);
|
|
DB_get_error();
|
|
if (rec_num > 0)
|
|
{
|
|
char msg[256];
|
|
sprintf(msg, "Errore in DB_add(): chiave duplicata nell' indice %ld, file %s",
|
|
rec_num + 1, d4fileName(data));
|
|
xvt_dm_post_fatal_exit(msg);
|
|
}
|
|
else
|
|
rt = _isreinsert;
|
|
}
|
|
else
|
|
{
|
|
if (rt == 0)
|
|
rt = DB_flush(handle);
|
|
}
|
|
}
|
|
|
|
return rt;
|
|
}
|
|
|
|
/*-------------------------------------------------------------------------
|
|
Blocca in modo esclusivo il file dati ed indice
|
|
--------------------------------------------------------------------------*/
|
|
int DB_lockfile(int handle)
|
|
{
|
|
int rt = -1;
|
|
HANDLE2DATA(handle, data);
|
|
rt = d4lockFile(data);
|
|
if (rt==0)
|
|
rt = d4lockIndex(data);
|
|
return rt;
|
|
}
|
|
|
|
HIDDEN void do_key(const char* fname, const RecDes* r, TAG4INFO* tag_info, int ntags)
|
|
{
|
|
const int nkeys = min(r->NKeys, ntags);
|
|
int i,j;
|
|
char tiname[_MAX_FNAME]; /* Tag name */
|
|
xvt_fsys_parse_pathname(fname, NULL, NULL, tiname, NULL, NULL);
|
|
xvt_str_make_upper(tiname);
|
|
|
|
for (i=0; i < nkeys; i++)
|
|
{
|
|
tag_info[i].name=(char *)u4alloc(_MAX_FNAME);
|
|
tag_info[i].expression=(char *)u4alloc(256);
|
|
tag_info[i].filter=(char*)u4alloc(32);
|
|
tag_info[i].descending=0;
|
|
if (r->Ky[i].DupKeys)
|
|
tag_info[i].unique=0;
|
|
else
|
|
tag_info[i].unique= i == 0 ? e4unique : r4unique_continue;
|
|
strcpy((char *)tag_info[i].filter,".NOT. DELETED()"); /* Not available for DBIII and CLIPPER */
|
|
strcpy((char *)tag_info[i].name,tiname) ;
|
|
strcat((char *)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((char *) tag_info[i].expression,"UPPER(");
|
|
}
|
|
if (r->Ky[i].FromCh[j] != 255) /* When partial field is specified */
|
|
strcat((char *)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((char *)tag_info[i].expression,"STR(");
|
|
break;
|
|
case _datefld:
|
|
strcat((char *)tag_info[i].expression,"DTOS(");
|
|
break;
|
|
case _boolfld:
|
|
strcat((char *)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((char *)tag_info[i].expression,r->Fd[nf].Name); /* Append field name */
|
|
|
|
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];
|
|
sprintf(ts,"%d",r->Fd[nf].Len);
|
|
strcat((char *)tag_info[i].expression,",");
|
|
strcat((char *)tag_info[i].expression,ts);
|
|
strcat((char *)tag_info[i].expression,",0)");
|
|
}
|
|
break;
|
|
case _boolfld:
|
|
strcat((char *)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((char *)tag_info[i].expression,")");
|
|
if (r->Ky[i].FromCh[j] != 255) /* If partial field was specified */
|
|
{ /* add parameters to SUBSTR */
|
|
char ts[8];
|
|
|
|
strcat((char *)tag_info[i].expression,",");
|
|
sprintf(ts,"%d",r->Ky[i].FromCh[j] + 1);
|
|
strcat((char *)tag_info[i].expression,ts);
|
|
strcat((char *)tag_info[i].expression,",");
|
|
sprintf(ts,"%d",r->Ky[i].ToCh[j] - r->Ky[i].FromCh[j] + 1);
|
|
strcat((char *)tag_info[i].expression,ts);
|
|
strcat((char *)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((char *)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;
|
|
|
|
code_base.autoOpen = 0;
|
|
handle=DB_open(filename,1,0); /* Exclusive mode open! */
|
|
if (handle >= 0)
|
|
{
|
|
WINDOW pi = NULL_WIN;
|
|
long rc = 0L;
|
|
if (vis)
|
|
{
|
|
char s[_MAX_PATH+32];
|
|
strcpy(s, "Compattamento ");
|
|
strcat(s, filename);
|
|
pi = xvt_dm_progress_create(NULL_WIN, s, 1, FALSE);
|
|
}
|
|
rc = d4recCount(dbdata[handle]);
|
|
if (eod < rc)
|
|
rt=d4zap(dbdata[handle],++eod,rc);
|
|
else
|
|
rt=d4pack(dbdata[handle]);
|
|
if (pi)
|
|
xvt_dm_progress_destroy(pi);
|
|
DB_close(handle);
|
|
}
|
|
else
|
|
rt=code_base.errorCode;
|
|
code_base.autoOpen = 1;
|
|
return rt;
|
|
}
|
|
|
|
/*-------------------------------------------------------------------------
|
|
Compatta il file dati
|
|
--------------------------------------------------------------------------*/
|
|
int DB_packmemo(short vis, const char * filename)
|
|
{
|
|
int rt=0,handle=0;
|
|
|
|
code_base.autoOpen = 0;
|
|
handle=DB_open(filename,1,0); /* Exclusive mode open! */
|
|
if (handle > -1)
|
|
{
|
|
WINDOW pi = NULL_WIN;
|
|
if (vis)
|
|
{
|
|
char s[_MAX_PATH+32];
|
|
strcpy(s, "Compattamento memo : ");
|
|
strcat(s, filename);
|
|
pi = xvt_dm_progress_create(NULL_WIN, s, 1, FALSE);
|
|
}
|
|
rt=d4memoCompress(dbdata[handle]);
|
|
if (pi)
|
|
xvt_dm_progress_destroy(pi);
|
|
DB_close(handle);
|
|
}
|
|
else
|
|
rt=code_base.errorCode;
|
|
code_base.autoOpen = 1;
|
|
return rt;
|
|
}
|
|
|
|
/*-------------------------------------------------------------------------
|
|
Elimina i record duplicati
|
|
--------------------------------------------------------------------------*/
|
|
static int DB_clean_file(int handle, const char* filename,
|
|
const char* tagname, const RecDes* r, short vis)
|
|
{
|
|
TAG4INFO tags[2];
|
|
TAG4 * t;
|
|
char s[256], s0[256];
|
|
int l = 0, lt = 0, rt = 0;
|
|
long cnt = 0;
|
|
INDEX4 *w = NULL;
|
|
long items = DB_reccount(handle);
|
|
WINDOW pi = NULL_WIN;
|
|
|
|
if (items == 0)
|
|
return 0;
|
|
|
|
s[0] = '\0';
|
|
memset(tags, 0, sizeof(tags));
|
|
do_key(tagname, r, tags, 1);
|
|
strcat((char *) tags[0].expression, "+STR(RECNO(),9)");
|
|
w = i4create(dbdata[handle],(char*)filename,tags); // non "production" index ???
|
|
u4free((char *) tags[0].name);
|
|
u4free((char *) tags[0].expression);
|
|
u4free((char *) tags[0].filter);
|
|
if (w == NULL)
|
|
return code_base.errorCode;
|
|
t = d4tagDefault(dbdata[handle]);
|
|
lt = expr4len(t->tagFile->expr);
|
|
l = lt - 9;
|
|
if (vis)
|
|
pi = xvt_dm_progress_create(NULL_WIN,"Ricerca record duplicati",items,FALSE);
|
|
|
|
rt = tfile4bottom(t->tagFile);
|
|
|
|
while (code_base.errorCode == 0)
|
|
{
|
|
strncpy(s0, a4tagKey(dbdata[handle]), lt);
|
|
|
|
if (pi)
|
|
xvt_dm_progress_set_status(pi, ++cnt, items);
|
|
|
|
if (!strncmp(s, s0, l))
|
|
{
|
|
d4go(dbdata[handle],tfile4recNo(t->tagFile));
|
|
d4delete(dbdata[handle]);
|
|
tfile4seek(t->tagFile, s0, lt);
|
|
}
|
|
strncpy(s, s0, lt);
|
|
if (tfile4skip(t->tagFile, -1L) >= 0)
|
|
break;
|
|
|
|
} // while
|
|
rt = code_base.errorCode;
|
|
if (pi)
|
|
xvt_dm_progress_destroy(pi);
|
|
|
|
i4close(w);
|
|
return rt;
|
|
}
|
|
|
|
/*-------------------------------------------------------------------------
|
|
Compatta gli indici
|
|
--------------------------------------------------------------------------*/
|
|
|
|
static int DB_yesnobox(const char* msg)
|
|
{
|
|
return xvt_dm_post_ask("Si", "No", NULL, msg) == RESP_DEFAULT;
|
|
}
|
|
|
|
int DB_packindex(short vis, const char* filename, const RecDes *r, long *peod, bool ask)
|
|
{
|
|
int handle = 0, rt = 0;
|
|
|
|
code_base.autoOpen = 0;
|
|
|
|
handle=DB_open(filename,1,0); /* Exclusive mode open */
|
|
if (handle >= 0)
|
|
{
|
|
TAG4INFO tags[MaxKeys+1];
|
|
INDEX4 * w = NULL;
|
|
WINDOW pi = NULL_WIN;
|
|
int i = 0;
|
|
const char *ff = find_slash_backslash(filename);
|
|
if (ff == NULL || *ff == '\0')
|
|
ff = (const char*)filename;
|
|
else
|
|
ff++;
|
|
memset(tags, 0, sizeof(tags));
|
|
do_key(ff, r, tags, r->NKeys);
|
|
|
|
if (vis)
|
|
{
|
|
char s[_MAX_PATH+32]; sprintf(s,"Creazione di %d indici su %s", (int)r->NKeys, ff);
|
|
pi = xvt_dm_progress_create(NULL_WIN, s, 1, FALSE); // No bar
|
|
}
|
|
|
|
w = i4create(dbdata[handle],NULL,tags); // NULL filename -> "production" index
|
|
if (w == NULL)
|
|
rt = code_base.errorCode;
|
|
|
|
if (pi)
|
|
{
|
|
xvt_dm_progress_destroy(pi);
|
|
pi = NULL_WIN;
|
|
}
|
|
|
|
if (rt == e4unique || rt == r4unique)
|
|
{
|
|
rt = 0;
|
|
if (!ask || DB_yesnobox("Sono stati rilevati alcuni records duplicati:\nsi desidera eliminarli?"))
|
|
{
|
|
if (vis)
|
|
{
|
|
char s[_MAX_PATH+32]; strcpy(s,"Rimozione duplicati "); strcat(s, ff);
|
|
pi = xvt_dm_progress_create(NULL_WIN, s, 1, FALSE); // No bar
|
|
}
|
|
rt = DB_clean_file(handle, filename, ff, r, vis);
|
|
if (pi)
|
|
{
|
|
xvt_dm_progress_destroy(pi);
|
|
pi = NULL_WIN;
|
|
}
|
|
}
|
|
else
|
|
tags[0].unique = r4unique_continue;
|
|
if (rt == 0)
|
|
{
|
|
if (vis)
|
|
{
|
|
char s[_MAX_PATH+32]; sprintf(s,"Creazione di %d indici su %s", (int)r->NKeys, ff);
|
|
pi = xvt_dm_progress_create(NULL_WIN, s, 1, FALSE); // No bar
|
|
}
|
|
// w = i4create(dbdata[handle],filename,tags);
|
|
w = i4create(dbdata[handle], NULL ,tags); // NULL filename -> "production" index 6/8/2014
|
|
if (w == NULL)
|
|
rt = code_base.errorCode;
|
|
if (pi)
|
|
{
|
|
xvt_dm_progress_destroy(pi);
|
|
pi = NULL_WIN;
|
|
}
|
|
}
|
|
}
|
|
for (i=0; i < r->NKeys && tags[i].name; i++)
|
|
{
|
|
u4free((char *) tags[i].name);
|
|
u4free((char *) tags[i].expression);
|
|
u4free((char *) tags[i].filter);
|
|
}
|
|
*peod=DB_reccount(handle);
|
|
DB_close(handle);
|
|
}
|
|
code_base.autoOpen = 1;
|
|
|
|
|
|
#ifdef DBG
|
|
{
|
|
char s[_MAX_PATH+20];
|
|
strcpy(s, filename);
|
|
strcat(s, ".cdx");
|
|
if (xvt_fsys_file_attr(s, XVT_FILE_ATTR_SIZE) == 0)
|
|
{
|
|
strcat(s, " e' stato compromesso!");
|
|
xvt_dm_post_error(s);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
return(rt);
|
|
}
|
|
|
|
|
|
/*-------------------------------------------------------------------------
|
|
costruisce il file filename
|
|
--------------------------------------------------------------------------*/
|
|
|
|
int DB_build(const char* filename, const 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 = NULL;
|
|
int rt=0,i;
|
|
const char *ff = find_slash_backslash(filename);
|
|
|
|
memset(field_info, 0, sizeof(field_info));
|
|
memset(tag_info, 0, sizeof(tag_info));
|
|
|
|
for (i=0; ((i<r->NFields) && (i<MaxFields)); i++) /* Construct field_info */
|
|
{
|
|
field_info[i].name = (char*)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 = filename;
|
|
else
|
|
ff++;
|
|
|
|
do_key(ff,r,tag_info, MaxKeys);
|
|
|
|
if ((dbuilded=d4create(&code_base, filename, field_info, tag_info))==0) /* deve solo creare il file dati vuoto */
|
|
rt=code_base.errorCode;
|
|
else
|
|
rt=d4close(dbuilded);
|
|
|
|
if (rt!=0)
|
|
rt=code_base.errorCode;
|
|
|
|
for (i=0; ((i < MaxKeys) && (i < r->NKeys)); i++)
|
|
{
|
|
u4free((char *) tag_info[i].name);
|
|
u4free((char *) tag_info[i].expression);
|
|
u4free((char *) tag_info[i].filter);
|
|
}
|
|
return(rt) ;
|
|
}
|
|
|
|
/*-------------------------------------------------------------------------
|
|
reperisce il tracciato record e la stringa di definizione delle chiavi
|
|
Stringa di definizione chiavi:
|
|
expression1|unique1$expression2|unique2$expression3|unique3
|
|
--------------------------------------------------------------------------*/
|
|
|
|
int DB_recinfo(const char * filename, FileDes *d, RecDes *r, char* keys)
|
|
{
|
|
/* filename must not have extension since it's used to search index name too*/
|
|
FIELD4INFO *field_info; /* Definizione del tracciato record */
|
|
TAG4INFO *tag_info; /* Definizione delle chiavi */
|
|
INDEX4 *index_file;
|
|
DATA4 *data_file;
|
|
int rt=0,num_fields,i;
|
|
|
|
data_file = d4open(&code_base, (char*)filename);
|
|
if (data_file != NULL)
|
|
{
|
|
field_info = d4fieldInfo(data_file);
|
|
index_file = d4index(data_file,"");
|
|
if (index_file == NULL)
|
|
{
|
|
char msg[256];
|
|
sprintf(msg, "Il file %s e' senza indici.",filename);
|
|
xvt_dm_post_fatal_exit(msg);
|
|
}
|
|
tag_info = i4tagInfo(index_file);
|
|
d->EOD = d->EOX = d4recCount(data_file);
|
|
d->LenR = (word)d4recWidth(data_file);
|
|
d->Flags = 0L;
|
|
strcpy(d->Des,"File esterno");
|
|
strcpy(d->FCalc,"");
|
|
strcpy(d->GenPrompt,"");
|
|
if (field_info != NULL && tag_info != NULL)
|
|
{
|
|
/* Compile field information */
|
|
num_fields = d4numFields(data_file);
|
|
r->NFields = num_fields;
|
|
for (i=0; ((i<num_fields) && (i<MaxFields)); i++)
|
|
{
|
|
strcpy(r->Fd[i].Name,field_info[i].name);
|
|
xvt_str_make_upper(r->Fd[i].Name);
|
|
r->Fd[i].Len = (unsigned char)field_info[i].len;
|
|
r->Fd[i].Dec = (unsigned char)field_info[i].dec;
|
|
switch(field_info[i].type)
|
|
{
|
|
case r4str : r->Fd[i].TypeF = r->Fd[i].Len > 1 ? _alfafld : _charfld; break;
|
|
case r4log : r->Fd[i].TypeF = _boolfld; break;
|
|
case r4date: r->Fd[i].TypeF = _datefld; break;
|
|
case r4memo: r->Fd[i].TypeF = _memofld; break;
|
|
case r4num :
|
|
if (r->Fd[i].Dec > 0)
|
|
r->Fd[i].TypeF = _realfld;
|
|
else
|
|
r->Fd[i].TypeF = r->Fd[i].Len < 6 ? _intfld : _longfld;
|
|
break;
|
|
default: r->Fd[i].TypeF = _alfafld; break; // Considero stringhe tutte le cose sconosciute
|
|
}
|
|
}
|
|
|
|
strcpy(keys,"");
|
|
/* Compile key definition */
|
|
for (i=0; i < MaxKeys; i++) /* Browse all tags */
|
|
{
|
|
if (tag_info[i].name == NULL)
|
|
break;
|
|
strcat(keys,tag_info[i].expression);
|
|
/* Tell me if you're unique my Boy... */
|
|
strcat(keys,"|");
|
|
strcat(keys,tag_info[i].unique == 0 ? "X" : " ");
|
|
strcat(keys,"$");
|
|
}
|
|
r->NKeys = i;
|
|
u4free(field_info);
|
|
u4free(tag_info);
|
|
}
|
|
else
|
|
rt = code_base.errorCode;
|
|
d4close(data_file);
|
|
}
|
|
else
|
|
rt = code_base.errorCode;
|
|
return (rt);
|
|
}
|
|
|
|
/*-------------------------------------------------------------------------
|
|
ritorna l'ultimo errore
|
|
--------------------------------------------------------------------------*/
|
|
|
|
int DB_get_error(void)
|
|
{
|
|
int rt = code_base.errorCode;
|
|
code_base.errorCode=0;
|
|
return (rt);
|
|
}
|
|
|
|
/*-------------------------------------------------------------------------
|
|
Azzera la viarabile globale d'errore
|
|
--------------------------------------------------------------------------*/
|
|
|
|
void DB_zero_error(void)
|
|
{
|
|
code_base.errorCode=0;
|
|
}
|
|
|
|
/*-------------------------------------------------------------------------
|
|
Si posiziona sull'indice attivo alla chiave indicata, restituisce
|
|
la prima chiave che trova >= a from. (TCusrsor)
|
|
--------------------------------------------------------------------------*/
|
|
|
|
int DB_index_seek(int handle, const char* from)
|
|
{
|
|
TAG4* t = NULL;
|
|
HANDLE2DATA(handle, data);
|
|
t = d4tagDefault(data);
|
|
if (t==NULL)
|
|
return -1;
|
|
if (tfile4seek(t->tagFile, 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;
|
|
HANDLE2DATA(handle, data);
|
|
t=d4tagDefault(data);
|
|
if (t==NULL)
|
|
return(-1);
|
|
|
|
return tfile4recNo(t->tagFile);
|
|
}
|
|
|
|
/*-------------------------------------------------------------------------
|
|
Si posiziona sulla chiave successiva dell'indice corrente
|
|
--------------------------------------------------------------------------*/
|
|
long DB_index_next(int handle)
|
|
{
|
|
TAG4 *t;
|
|
HANDLE2DATA(handle, data);
|
|
t = d4tagDefault(data);
|
|
if (t==NULL)
|
|
return -1;
|
|
return tfile4skip(t->tagFile,1L);
|
|
}
|
|
|
|
/*-------------------------------------------------------------------------
|
|
Restituisce la chiave corrente
|
|
--------------------------------------------------------------------------*/
|
|
|
|
char* DB_index_getkey(int handle)
|
|
{
|
|
static char key[MAXLEN];
|
|
TAG4 *t;
|
|
int klen;
|
|
const char* src;
|
|
|
|
HANDLE2DATASTR(handle, data);
|
|
|
|
if ((t=d4tagDefault(data))==NULL)
|
|
return(NULL);
|
|
klen=a4tagKeyLen(data);
|
|
if (klen > (MAXLEN-1)) klen=MAXLEN-1;
|
|
src = a4tagKey(data);
|
|
if (src != NULL)
|
|
{
|
|
memcpy(key,src,klen); /* tfile4key non restituisce una null terminated string */
|
|
key[klen]='\0';
|
|
}
|
|
else
|
|
key[0] = '\0';
|
|
return key;
|
|
}
|
|
|
|
/*-------------------------------------------------------------------------
|
|
Si posiziona sulla chiave ed il record indicati
|
|
--------------------------------------------------------------------------*/
|
|
int DB_index_go(int handle, const char* key, long recno)
|
|
{
|
|
TAG4* t = NULL;
|
|
HANDLE2DATA(handle, data);
|
|
t = d4tagDefault(data);
|
|
if (t==NULL)
|
|
return -1;
|
|
return tfile4go(t->tagFile, key, recno, FALSE);
|
|
}
|
|
|
|
/*-------------------------------------------------------------------------
|
|
Restituisce vero se l'indice e' alla fine
|
|
--------------------------------------------------------------------------*/
|
|
|
|
int DB_index_eof(int handle)
|
|
{
|
|
TAG4* t = NULL;
|
|
HANDLE2DATA(handle, data);
|
|
t = d4tagDefault(data);
|
|
if (t==NULL)
|
|
return -1;
|
|
return tfile4eof(t->tagFile);
|
|
}
|
|
|
|
/*-------------------------------------------------------------------------
|
|
Blocca il record indicato
|
|
--------------------------------------------------------------------------*/
|
|
|
|
int DB_lock_rec(int handle, long nrec)
|
|
{
|
|
HANDLE2DATA(handle, data);
|
|
return d4lock(data, nrec)==r4locked ? -1 : 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)
|
|
{
|
|
HANDLE2DATA(handle, data);
|
|
return a4lockTest(data);
|
|
}
|
|
|
|
/*-------------------------------------------------------------------------
|
|
Ritorna vero se il record nrec e' bloccato dalla presente applicazione,
|
|
non da altri programmi!!!
|
|
--------------------------------------------------------------------------*/
|
|
|
|
int DB_rec_locked(int handle, long nrec)
|
|
{
|
|
HANDLE2DATA(handle, data);
|
|
return d4lockTest(data,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)
|
|
{
|
|
HANDLE2DATA(handle, data);
|
|
return a4indexVersion(data);
|
|
}
|
|
|
|
char* DB_memoptr( const int handle, const char * fieldname )
|
|
{
|
|
FIELD4* f = NULL;
|
|
HANDLE2DATASTR(handle, data);
|
|
f = d4field(data, fieldname);
|
|
return f4memoPtr(f);
|
|
}
|
|
|
|
int DB_memowrite( const int handle, const char* fieldname, const char* memo )
|
|
{
|
|
FIELD4* f = NULL;
|
|
HANDLE2DATA(handle, data);
|
|
f = d4field(data, fieldname);
|
|
return f4memoAssign(f, memo);
|
|
}
|
|
|
|
long DB_version(char* str, int maxstr)
|
|
{
|
|
if (str != NULL && maxstr >= 16)
|
|
sprintf(str, "Codebase %d.%03d", S4VERSION/1000, S4VERSION%1000);
|
|
return S4VERSION;
|
|
}
|