campo-sirio/include/isam.cpp
guy 6bac6177c4 Patch level : 2.0 nopatch
Files correlati     :
Ricompilazione Demo : [ ]
Commento            :

Aggiunte utili per gestione dizionario
assoc.cpp    Aggiunta possibilita' di selezionare un elemento causale di un assoc
controls.cpp Corretta gestione scrollbar dei listbox
diction.cpp  Migliorata selezione caratteri da trimmare dalle traduzioni
isam.cpp     Corretto azzeramento dei memo (non azzerava il TString_array corrispondente)
msksheet.cpp Resa personalizzabile la larghezza della colonna col numero di riga
netsock.cpp  Migliorata gestione "a capo" in protocollo soap
progind.cpp  Corretto posizionamento progind sovrapposte
relapp.cpp   Cambiato un messaggio di richiesta annullamento


git-svn-id: svn://10.65.10.50/trunk@11651 c028cbd2-c16b-5b4b-a496-9718f37d4682
2003-12-03 09:41:16 +00:00

3759 lines
89 KiB
C++
Executable File

#include <fcntl.h>
#include <io.h>
#include <diction.h>
#include <share.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <codeb.h>
#define __ISAM_CPP
#include <config.h>
#include <execp.h>
#include <expr.h>
#include <extcdecl.h>
#include <mailbox.h>
#include <postman.h>
#include <prefix.h>
#include <progind.h>
#include <recarray.h>
#include <relation.h>
#include <scanner.h>
#include <utility.h>
#include <tabutil.h>
#include <varrec.h>
#ifdef JOURNAL
#include <journal.h>
static FILE* _journal = (FILE*)0xFFFF;
FILE* get_journal()
{
if (_journal == (FILE*)0xFFFF)
{
TConfig ini(CONFIG_INSTALL, "Main");
const TString& name = ini.get("Journal");
if (name.not_empty())
_journal = fopen(name, "ab");
else
_journal = NULL;
}
return _journal;
}
TJournalHeader& get_journal_header(int num, const char* op, const char* type)
{
static TJournalHeader jh;
const int size = sizeof(TJournalHeader);
CHECKD(size == 64, "Invalid journal header: ", size);
memset(&jh, 0, size);
strcpy(jh._signature, "J00");
jh._header_length = size;
time(&jh._time);
jh._firm = prefix().get_codditta();
jh._logic_num = num;
strcpy(jh._operation, op);
strcpy(jh._type, type);
jh._data_size = 0;
return jh;
}
void write_journal(TJournalHeader& jh, void* data, int len)
{
FILE* j = get_journal();
jh._data_size = len;
fwrite(&jh, sizeof(TJournalHeader), 1, j);
fwrite(data, len, 1, j);
fflush(j);
}
#endif
#define RECLOCKTYPES 0xFF00
#define READTYPES 0x00FF
#define INVFLD 255
Str80 cprefix;
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 (ODD(l))
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);
}
void __getfieldbuff(byte l, byte t, const char * recin, char *s)
{
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)
strncpy(s, recin, l);
}
s[l] = '\0';
if (l)
{
if (t == _datefld)
{
TDate dt(s);
#ifdef _DEMO_
int y = dt.year();
if (y & 0x0001) y--;
y >>= 3;
y++;
y /= 10;
if (y >= 25)
{
int m = dt.month();
if (m > 3)
dt.set_month(rand() % 3 + 1);
}
#endif
strcpy(s, dt.string(full));
}
else
if (t == _boolfld)
{
const char ok = toupper(*s);
if (ok == 'T' || ok == 'Y' || ok == 'S' || ok == 'X')
strcpy(s,"X");
else
strcpy(s," ");
}
}
}
void __putfieldbuff(byte l, byte d, byte t, const char* s, char* recout)
{
int len, i;
CHECK(recout, "Can't write null record" );
char s2[40];
if (t == _datefld)
{
if (*s)
{
const TDate dt(s);
strcpy(s2, dt.string(ANSI));
s = s2;
}
}
else
if (t == _boolfld)
{
strcpy(s2, (*s && strchr("1STXY", toupper(*s)) != NULL) ? "T" : "F");
s = s2;
}
else
if (t == _realfld)
{
real r(s);
strcpy(s2, r.string(l, d));
s = s2;
}
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)
{
strcpy(s2, "0");
s = s2;
len = 1;
}
__field_changed = FALSE;
const char c = (t == _intzerofld || t == _longzerofld) ? '0' : ' ';
for (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 (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("install.ini");
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 (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("install.ini");
TCallbackTableinfo fi;
TTable t(tabname);
fi._var1.format("Edit_%s", t.name());
fi._var2.format("Edit_%d", t.num());
fi._var2.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.compare("ba", 2, TRUE) != 0)
{
TConfig c(CONFIG_STUDIO, fi._module);
if (c.exist("TabPrg"))
app = c.get("TabPrg");
}
}
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
void get_idx_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
// @comm Ritorna il nome con il prefisso corrente
{
long c = DB_getconf();
TDir d;
TTrec r;
d.get(logicnum);
r.get(logicnum);
TFilename f(d.name());
if (c & 1) f.ext("cdx");
if (c & 4) f.ext("mdx");
i_names.cut(0);
i_names.add(f);
f.ext("");
f.rtrim(1);
if ((c & 2) || (c & 8)) // DBIII or CLIPPER format, returns f_name + .cgp, f_nameX + .n{d|t}x
for (int j=1; j<=r.keys();j++)
{
TString xx=f.name();
if (xx.len()<8)
f << ('0' + j);
else
f[8] = ('0' + j);
if (c & 2) // CLIPPER
f.ext("ndx");
else // DBIII
f.ext("ntx");
i_names.add(f);
}
i_names.restart();
}
// 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)
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)
{
CHECKD(rd->NFields <= MaxFields, "Tracciato con troppi campi ", rd->NFields);
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] = ' ';
}
HIDDEN const char * translate_key(const char* key) // Traduce l'espressione chiave di CodeBase
{
// 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,to;
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, 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) > 80)
{
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))
{
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))
{
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)
{
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.not_empty(),"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);
}
long 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) 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());
TString key(128);
__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)
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;
#ifdef _DEMO_
const int logicnum = num();
if ((logicnum > LF_COMUNI && logicnum < LF_ANALISI) || logicnum > LF_RELANA)
{
if (items() > 979L)
return _isfilefull;
}
#endif
// Forza l'uso della chiave principale (per chiavi duplicate?)
const int fhnd = handle(1);
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);
if (_lasterr == NOERR)
{
#ifdef JOURNAL
if (get_journal())
{
TJournalHeader& jh = get_journal_header(num(), "ADD", "REC");
write_journal(jh, rec.string(), dst_len);
}
#endif
}
else
_lasterr = get_error(_lasterr);
_recno = DB_recno(fhnd);
if (_lasterr == NOERR && rec.has_memo())
((TRectype &)rec).write_memo(_isam_handle, _recno );
return _lasterr;
}
int TBaseisamfile::write(const TRectype& rec)
{
return rec.write(*this);
}
int TBaseisamfile::write()
{
return TBaseisamfile::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)
{
#ifdef JOURNAL
if (get_journal())
{
TJournalHeader& jh = get_journal_header(num(), "MOD", "REC");
write_journal(jh, rec.string(), len);
}
#endif
}
else
_lasterr = get_error(_lasterr);
}
else
DB_unlock(fhnd);
_recno = DB_recno(fhnd);
prefix().unlock_record(_isam_handle, _recno);
if(_lasterr == NOERR)
{
if (curr().has_memo( ))
((TRectype &)rec).write_memo(_isam_handle, _recno );
rec_cache(_logicnum).notify_change();
}
}
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");
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)
{
_lasterr = DB_delete(fhnd); // Put only deletion flag on record, must remove keys too!
if (_lasterr == NOERR)
{
DB_flush(fhnd);
#ifdef JOURNAL
if (get_journal())
{
const int len = DB_reclen(fhnd);
TJournalHeader& jh = get_journal_header(num(), "DEL", "REC");
write_journal(jh, rec.string(), len);
}
#endif
}
else
{
_lasterr = get_error(_lasterr);
DB_recall(fhnd);
}
}
if(_lasterr == NOERR)
{
if (curr().has_memo( ))
curr().init_memo();
rec_cache(_logicnum).notify_change();
}
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) : -60;
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;
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);
}
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();
}
/*
//A cosa cavolo serve?
// @doc EXTERNAL
// @mfunc Aggiorna i flags associati al file
//
// @rdesc Ritorna NOERR se e' riuscita ad eseguire l'operazione, altrimenti ritorna il numero
// di errore generato (vedi <t TIsamerr>).
int TIsamfile::flags(
bool updateeod) // @parm Indica se aggiornare anche l'EOD del file
{
if (num() <= 0)
return NOERR ;
TDir d;
const TDirtype dirtype = prefix().get_dirtype(_filename);
d.get(num(), _lock, dirtype);
int err = d.status(dirtype);
if (err == NOERR)
{
const FileDes& fd = prefix().get_filedes(_filename);
d.flags() = fd.Flags;
if (updateeod)
d.eod() = fd.EOD;
d.put(num(), dirtype);
}
setstatus(err);
return err;
}
*/
///////////////////////////////////////////////////////////
// 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 << SLASH << radix;
}
n.ext("dbf");
}
else
n.temp(NULL, "dbf");
if (!create)
{
TDir dir; dir.get(logicnum);
const word& len = dir.len();
FILE* f = fopen(n, "rb");
if (f == NULL)
fatal_box("Impossibile aprire il file temporaneo %s: %s",(const char*) n,(const char*)_strerror(NULL));
fseek(f, 0, SEEK_END);
eod = eox = ftell(f) / len;
fclose(f);
}
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 = new RecDes;
memcpy(rd, curr().rec_des(), sizeof(RecDes));
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);
}
delete rd;
return err;
}
const char* TExternisamfile::name() const
{
return filename();
}
///////////////////////////////////////////////////////////
// TSystemisamfile
///////////////////////////////////////////////////////////
int TSystemisamfile::build(TRecnotype eox, const TTrec& r)
{
int err=NOERR;
TDir d;
d.get(num());
CHECK(r.len() != 0, "Can't create a file with empty field info");
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);
if (r.len() != 0)
{
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);
}
}
return err;
}
int TSystemisamfile::build(TRecnotype eox)
{
TTrec r;
r.get(num());
return build(eox,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 vis) // @parm Indica se visualizzare lo stato dell'operazione
{
CHECK(newrec.len() != 0, "Can't update file with empty field info");
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;
oldrec.get(num());
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
setstatus(err);
return err;
}
}
else
{
if (dir.eox() > 0L)
{
dir.eod() = 0L;
dir.eox() = 0L;
}
}
const int oldfields = oldrec.fields();
int wfields = wrec.fields();
int newfields = wfields;
TToken_string def;
for (int j = 0; j < oldfields; j++)
{
def = oldrec.fielddef(j);
if (def.get(0)[0] == '_')
{
if (newfields < MaxFields)
wrec.update_fielddef(++newfields, def);
else
if(!yesnofatal_box("Il campo %s non verra' preservato, devo continuare",
(const char *) def.get(0)))
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((const char*) tmpfname, wrec.rec());
if (err != NOERR)
{
err=get_error(err);
return (err);
}
if (dir.eod() > 0 && oldrec.len() > 0)
{
err = _open_ex(_excllock, FALSE);
if (err != NOERR)
return err;
// 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 = prefix().get_handle(ishandle);
if (fhnd < 0 )
{
err=get_error(fhnd);
return err;
}
TString s(256);
s.format("Aggiornamento archivio %s", (const char*) fname);
const TRecnotype nitems = items();
TProgind p(nitems > 0 ? nitems : 1, s, FALSE, TRUE, 70);
TExtrectype nrec(wrec);
const int nflds = curr().items();
TArray infld, outfld;
for (int 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 clock_t start_time = clock();
for (first(); good(); next())
{
const bool tick = p.addstatus(1);
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 && nrec.has_memo())
nrec.write_memo(ishandle, DB_recno(fhnd));
if (err != NOERR)
err=get_error(err);
setstatus(err);
}
close();
prefix().close_isamfile(ishandle);
if (err!=NOERR) err=get_error(err);
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.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>
{
TDir d;
d.get(num(),_nolock, _nordir,_sysdirop); // Chi commenta muore!
d.get(num(),_nolock, (d.is_com()) ? _comdir : _nordir);
// CHECKS(filehnd() == NULL, "Can't pack open file", (const char*)filename());
int err = DB_packfile(vis, d.name(), zap ? 0L : d.eod());
if (zap && err == NOERR)
err = packindex(vis, FALSE);
if (err == NOERR && curr().has_memo())
err = DB_packmemo(vis,d.name());
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>
{
int err=NOERR;
TRecnotype peod;
TTrec r;
TDir d;
r.get(num());
d.get(num(),_nolock, _nordir,_sysdirop);
bool is_com = d.is_com();
d.get(num(),_nolock, is_com ? _comdir : _nordir);
err=DB_packindex(vis,d.name(),r.rec(),&peod,ask);
if (err != NOERR)
err = get_error(err);
if (err != NOERR)
error_box("Errore in compattamento indici.\nFile %d : %d", num(),err);
else
if (peod >= 0 && peod != d.eod())
{
d.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);
}
}
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)
// @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");
if (extended)
{
TDate d(TODAY);
TLocalisamfile ditte(LF_NDITTE);
firm.format("%05ld", prefix().get_codditta());
year.format("%04d", d.year());
ditte.zero();
ditte.put("CODDITTA", firm);
if (ditte.read() == NOERR)
attprev = ditte.get("CODATTPREV");
}
fseek(fl, 0L, SEEK_END);
nitems = ftell(fl);
fclose(fl);
err = _open_ex();
if (err != NOERR)
{
error_box("Impossibile aprire il file %d", _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);
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();
nflds++;
}
}
}
}
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 = (const TString & ) fld[j];
len[j] = (curr().type(wfld) == _datefld) ? 10 : curr().length(wfld);
}
}
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("Imp. archivio %d\n%6ld records %6ld errori - %3d", _logicnum, r, e, last);
TProgind p(nitems, s1, TRUE, TRUE, 70);
long pos = 16*nflds;
for (s = f.line(); s.not_empty() && !p.iscancelled(); s = f.line())
{
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) % 50 == 0)
{
s1.format("Imp. archivio %d\n%6ld records %6ld errori - %3d", _logicnum, r, e, last);
p.set_text(s1);
}
pos += s.len()+2;
p.setstatus(pos);
zero();
if (fixedlen)
{
int pos = 0;
for (int j = 0; j < nflds; j++)
{
s1 = s.mid(pos,len[j]);
s1.rtrim();
put((const TString&) fld[j], s1);
pos += len[j];
}
}
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
{
// error_box("Errore di scrittura alla riga %ld", r+e+1);
e++;
last = status();
// break;
}
}
s1.format("Imp. archivio %d\n%6ld records %6ld errori - %3d", _logicnum, 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
// @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();
TArray fld(nflds);
TBit_array rjust(nflds);
int len[MaxFields];
for (int j = 0; j < nflds; j++)
{
fld.add(TString(curr().fieldname(j)), j);
const TString & wfld = (const TString&) fld[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)
return error_box("Non e' possibile scaricare a lunghezza fissa un file con campi memo");
}
TRecnotype i = 0;
const TRecnotype nitems = items();
s.format("Esportazione archivio %s", filename());
TProgind p(nitems, s, TRUE, TRUE, 70);
TString s1, sfld;
fprintf(f, "[Header]\nVersion=%ld\nFile=%d",
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;
}
if (skip)
continue;
}
s = "";
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);
}
}
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;
}
#endif // FOXPRO
void TBaseisamfile::recover()
{
}
////////////////////////////////////////////////////////////
// Memo data
////////////////////////////////////////////////////////////
void TMemo_data::init(TRecnotype recno, TIsam_handle file)
{
CHECK(file != NULL || recno < 0, "Valid memo recno with NULL memo file");
_recno = recno;
_isamfile = file;
}
void TMemo_data::destroy()
{
TString_array::destroy();
_dirty.reset();
_recno = RECORD_NON_FISICO;
_isamfile = NULL;
}
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);
const bool has_memo_fld = _length > 0 && lf_has_memo(_logicnum);
if(has_memo_fld)
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);
TCodeb_handle cb_handle = prefix().get_handle(file);
DB_go( cb_handle, recno);
for( int i = _memo_data->last( ); i > 0; i = _memo_data->pred( i ) )
{
if (_memo_data->is_dirty(i))
DB_memowrite( cb_handle, r->Fd[ i ].Name, ( char * )( const char * )_memo_data->row( i ) );
}
*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(128);
__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>
{
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);
return res;
}
HIDDEN bool fld_empty(const char* s, int len, bool number)
{
if (*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)
{
for (int 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)
///////////////////////////////////////////////////////////
RecDes* TRectype::rec_des() const
{
return (RecDes*)&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();
int p = findfld(recd, fieldname);
if (p != FIELDERR)
return (TFieldtypes) recd->Fd[p].TypeF;
else
return _nullfld;
}
int TRectype::length(const char* fieldname) const
{
const RecDes * recd = rec_des();
int p = findfld(recd, fieldname);
if (p != FIELDERR)
return(recd->Fd[p].Len);
else
return(0);
}
int TRectype::ndec(const char* fieldname) const
{
const RecDes * recd = rec_des();
int p = findfld(recd, fieldname);
if (p != FIELDERR)
return(recd->Fd[p].Dec);
else
return(0);
}
bool TRectype::exist(const char* fieldname) const
{
const bool ok = findfld(rec_des(), fieldname) != FIELDERR;
return ok;
}
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);
const RecFieldDes& fd = recd->Fd[nf];
if (nf != FIELDERR)
{
TString& tmp = get_tmp_string(fd.Len + (fd.TypeF == _datefld ? 2 : 0));
__getfieldbuff(fd.Len, fd.TypeF, _rec + fd.RecOff, tmp.get_buffer());
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 int orig = _memo_data->file();
if (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));
}
bool TRectype::get_bool(const char* fieldname) const
{
return *(get_str(fieldname)) == 'X';
}
TDate TRectype::get_date(const char* fieldname) const
{
TDate d(get_str(fieldname));
return d;
}
void TRectype::put(const char* fieldname, int val)
{
TString16 tmp;
tmp.format("%d", val);
put_str(fieldname, tmp);
}
void TRectype::put(const char* fieldname, long val)
{
TString16 tmp;
tmp.format("%ld", val);
put_str(fieldname, tmp);
}
void TRectype::put(const char* fieldname, word val)
{
TString16 tmp; tmp.format("%u", val);
put_str( fieldname, tmp);
}
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_str( fieldname, val.string(full));
}
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)
{
const char s[2] = { val ? 'X' : ' ', '\0' };
put_str( fieldname, s);
}
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);
}
setempty(FALSE);
}
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);
break;
case _memofld:
__putfieldbuff(len, dec, _memofld, "", p);
_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);
}
// 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);
if (num() == LF_TABCOM)
cod.insert("%");
else
if (num() == LF_TABGEN)
cod.insert("#");
return ::get_tabapp(cod, app);
}
else
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.empty())
r.get_relapp(app);
if (app.not_empty())
{
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();
{
TConfig ini(ininame, "Transaction");
ini.set("Action", "LINK");
TString16 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_removefile(ininame);
if (ok)
rec_cache(logicnum).notify_change();
}
return ok;
}
///////////////////////////////////////////////////////////
// TRecfield (campo/sottocampo di un record)
///////////////////////////////////////////////////////////
void TRecfield::set(int from, int to)
{
const RecDes* rd = _rec->rec_des();
const int nf = findfld(rd, _name);
if (nf == FIELDERR)
{
NFCHECK("File n. %d unknown field %s", _rec->num(), _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 + from;
_dec = rd->Fd[nf].Dec;
_type = (TFieldtypes)rd->Fd[nf].TypeF;
if (to >= 0)
{
CHECK(from <= to && to <= rd->Fd[nf].Len, "Invalid Range");
_len = to - from + 1;
}
else
_len = rd->Fd[nf].Len - from;
}
}
TRecfield::TRecfield(TRectype& rec, const char* name, int from, int to)
{
strcpy(_name, name);
_rec = &rec;
set(from, to);
}
int TRecfield::operator =(int i)
{
char buff[32];
sprintf(buff, "%d", i);
__putfieldbuff( _len, _dec, _type, buff, _p);
_rec->setempty(FALSE);
return i;
}
long TRecfield::operator =(long l)
{
char buff[32];
sprintf(buff, "%ld", l);
__putfieldbuff( _len, _dec, _type, buff, _p);
_rec->setempty(FALSE);
return l;
}
const real& TRecfield::operator =(const real& r)
{
char buff[80];
strcpy(buff, r.string());
__putfieldbuff( _len, _dec, _type, buff, _p);
_rec->setempty(FALSE);
return r;
}
const TDate& TRecfield::operator =(const TDate& d)
{
char buff[16];
strcpy(buff, (const char*)d);
__putfieldbuff( _len, _dec, _type, buff, _p);
_rec->setempty(FALSE);
return d;
}
const char* TRecfield::operator =(const char* s)
{
if (_type == _memofld)
_rec->put(_name, s);
else
__putfieldbuff( _len, _dec, _type, s, _p);
_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;
}
TRecfield::operator int() const
{
char tmp[32];
if (_type == _intfld || _type == _intzerofld || _type == _longfld || _type == _longzerofld)
{
strncpy(tmp, _p, _len);
tmp[_len] = '\0';
}
else
__getfieldbuff( _len, _type, _p, tmp);
return atoi(tmp);
}
TRecfield::operator long() const
{
char tmp[32];
if (_type == _longfld || _type == _longzerofld || _type == _intfld || _type == _intzerofld)
{
strncpy(tmp, _p, _len);
tmp[_len] = '\0';
}
else
__getfieldbuff( _len, _type, _p, tmp);
return atol(tmp);
}
TRecfield::operator const real() const
{
char tmp[32];
if (_type == _realfld)
{
strncpy(tmp, _p, _len);
tmp[_len] = '\0';
}
else
__getfieldbuff( _len, _type, _p, tmp);
real r(tmp);
return r;
}
TRecfield::operator TDate() const
{
char tmp[16];
if (_type == _datefld)
{
strncpy(tmp, _p, 8);
tmp[8] = '\0';
return TDate(atol(tmp));
}
__getfieldbuff(_len, _type, _p, tmp);
return TDate(tmp);
}
TRecfield::operator const char*() const
{
if (_type == _memofld)
return _rec->get(_name);
TString& tmp = get_tmp_string(_len);
__getfieldbuff(_len, _type, _p, tmp.get_buffer());
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;
}