diff --git a/src/include/relation.cpp b/src/include/relation.cpp index ac42adaa1..3a8f9912b 100755 --- a/src/include/relation.cpp +++ b/src/include/relation.cpp @@ -1,3247 +1,3268 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -// *** check if not already defined -#define NOTFOUND (-1) - -HIDDEN void print_name(ostream& out, const TLocalisamfile& f) -{ - switch (f.num()) - { - case LF_TABCOM: out << '%'; break; - case LF_TABMOD: out << '&'; break; - default: break; - } - out << f.name(); -} - -HIDDEN void print_name(TToken_string& out, const TLocalisamfile& f) -{ - switch (f.num()) - { - case LF_TABCOM: out << '%'; break; - case LF_TABMOD: out << '&'; break; - default: break; - } - out << f.name(); -} - - -extern int get_error(int); - -HIDDEN const char* field_type_str(TFieldtypes f) -{ - const char* c = ""; - switch(f) - { - case _charfld: - c = "Carattere"; break; - case _realfld: - c = "Reale"; break; - case _datefld: - c = "Data"; break; - case _boolfld: - c = "Logico"; break; - // case _wordfld: - // case _intzerofld: - // case _longzerofld: - // case _intfld: - // case _longfld: - default: - c = "Intero"; break; - } - return c; -} - -/////////////////////////////////////////////////////////// -// TRelationdef -/////////////////////////////////////////////////////////// - -class TRelationdef : public TObject -{ - friend class TRelation; - - const TRelation* _rel; // Relazione padre - int _num; // Posizione file - int _numto; // Posizione padre - int _alias; // Alias - byte _key; // Chiave - TArray _fields; // Campi di join - TArray _exprs; // Condizioni di uguaglianza - TArray _altexprs; // Condizioni di uguaglianza alternative - TBit_array _forced; - bool _first_match : 1; // primo match (ed esiste) - bool _allow_lock : 1; // ?? - bool _write_enable : 1; - -public: // TObject - virtual void print_on(ostream& out) const; - -public: - int num() const { return _num; } - int link() const { return _numto; } - int alias() const { return _alias; } - bool allow_lock() const { return _allow_lock; } - bool write_enable() const { return _write_enable; } - void write_enable(bool we) { _write_enable = we; } - TRectype& load_rec(TRectype& r, const TRectype& from) const; - const char* evaluate_expr(int j, const TLocalisamfile& to) const; - - void print_on(TToken_string& out) const; - - TRelationdef(const TRelation* rel, int file, byte key, - int linkto, const char* relexprs, int alias, - bool allow_lock); - virtual ~TRelationdef() {} -}; - - -TRelationdef::TRelationdef(const TRelation* rel, int idx_file, byte key, - int idx_to, const char* relexprs, int alias, - bool allow_lock) -: _rel(rel), _num(idx_file), _numto(idx_to), - _alias(alias), _key(key), _fields(4), _exprs(4), - _first_match(FALSE), _allow_lock(allow_lock), - _write_enable(FALSE) -{ - TToken_string rels(relexprs); - int i = 0; - TString80 r,s; - - for (const char* g = rels.get(); g; g = rels.get(), i++) - { - r = g; - int eq = r.find('='); - - CHECKS(eq > 0, "Ahoo! E l'uguale 'ndo sta? ", (const char*)g); - - s = r.left(eq); // Parte a sinistra dell' = - -#ifdef DBG - const char* n = s; - const int p = s.find('['); - if (p > 0) n = s.left(p); - if (rel->file(_num).curr().exist(n) == FALSE) - { - yesnofatal_box("Errore di JOIN: '%s' non e' un campo del file %d", - n, rel->file(_num).num()); - continue; - } -#endif - - if (r[eq+1] == '=') - { - _forced.set(i); - eq++; - } - - _fields.add(new TFieldref(s, 0)); - - s = r.mid(eq+1); - const int par = s.find('('); - if (par > 0) - { - _exprs.add(new TExpression(s.left(par), _strexpr), i); - _altexprs.add(new TExpression(s.sub(par+1, s.len()-1), _strexpr), i); - } - else - _exprs.add(new TExpression(s, _strexpr), i); - } -} - - -void TRelationdef::print_on(ostream& out) const -{ - const TLocalisamfile& f = _rel->file(_num); - - out << "JOIN "; - print_name(out, f); - - if (_numto > 0) - { - out << " TO "; - const int alias = _rel->reldef(_numto-1).alias(); - if (alias > 0) out << alias << '@'; - else - { - const TLocalisamfile& t = _rel->file(_numto); - print_name(out, t); - } - } - - if (_key > 1) out << " KEY " << (int)_key; - - if (_alias > 0) out << " ALIAS " << _alias; - - for (int i = 0; i < _fields.items(); i++) - { - if (i == 0) out << " INTO"; - out << ' ' << _fields[i] << '='; - if (_forced[i]) out << '='; - out << _exprs[i]; - if (_altexprs.objptr(i)) - out << '(' << _altexprs[i] << ')'; - } -} - - -void TRelationdef::print_on(TToken_string& out) const -{ - const TLocalisamfile& f = _rel->file(_num); - - out.cut(0); - print_name(out, f); - - // add description - const char* name = f.name(); - if (f.tab()) - name = TDir::tab_des(name); - else - name = prefix().description(name); - out.add(name); - - out << '|'; - - if (_numto > 0) - { - const int alias = _rel->reldef(_numto-1).alias(); - if (alias > 0) - out << alias << '@'; - else - { - const TLocalisamfile& t = _rel->file(_numto); - print_name(out, t); - } - } else out << ' '; - - out.add(_key); - out.add(_alias); - out << '|'; - for (int i = 0; i < _fields.items(); i++) - { - if (i) out << ' '; - out << _fields[i] << '='; - if (_forced[i]) out << '='; - out << _exprs[i]; - if (_altexprs.objptr(i)) - out << '(' << _altexprs[i] << ')'; - } -} - - -const char* TRelationdef::evaluate_expr(int j, const TLocalisamfile& to) const -{ - const TRectype& rec = to.curr(); - TExpression& expr = (TExpression&)_exprs[j]; - for (int k = 0; k < expr.numvar(); k++) - { - if (expr.varname(k)[0] != '#') - { - const TFieldref fr(expr.varname(k), rec.num()); - expr.setvar(k, fr.read(rec)); - } - } - - const char* val = (const char*)expr.as_string(); - if (*val == '\0' && _altexprs.objptr(j)) - { - TExpression& altexpr = (TExpression&)_altexprs[j]; - for (int k = 0; k < altexpr.numvar(); k++) - { - if (altexpr.varname(k)[0] != '#') - { - const TFieldref fr(altexpr.varname(k), rec.num()); - altexpr.setvar(k, fr.read(rec)); - } - } - val = (const char*)altexpr.as_string(); - } - - return val; -} - -/////////////////////////////////////////////////////////// -// TRelation -/////////////////////////////////////////////////////////// - -TRelation::TRelation(int logicnum) -: _files(4), _reldefs(4), _errors(NOERR) -{ - TLocalisamfile* f = new TLocalisamfile(logicnum); - _files.add(f); -} - -TRelation::TRelation(const char* tabname) - : _files(4), _reldefs(4), _errors(NOERR) -{ - TLocalisamfile* t = NULL; - if (tabname[0] == '&') - t = new TModule_table(tabname); - else - t = new TTable(tabname); - _files.add(t); -} - -TRelation::TRelation(TLocalisamfile* l) - : _files(4), _reldefs(4), _errors(NOERR) -{ _files.add(l); } - -TRelation::~TRelation() -{} - -void TRelation::print_on(ostream& out) const -{ - const TLocalisamfile& f = file(); - - out << "USE "; print_name(out, f); - - const int k = f.getkey(); - if (k > 1) out << " KEY " << k; - out << endl; - - for (int r = 0; r < _reldefs.items(); r++) - out << _reldefs[r] << endl; -} - -void TRelation::restore_status() -{ - int i; - for (i = _files.last(); i >= 0 ; i--) - { - const int err = _status.get_int(i*3); - const TRecnotype recno = _status.get_long(); - const int key = _status.get_int(); - - file(i).setkey(key); - if (recno >= 0l) file(i).readat(recno); - else file(i).zero(); - file(i).setstatus(err); - } - _status.get(_files.items()*3 - 1); // mi riposiziono prima di tutti i first_match - for (i = 0; i < _reldefs.items(); i++) - { - const bool first_match = _status.get_int() ? TRUE : FALSE; - reldef(i)._first_match = first_match; - } -} - -void TRelation::save_status() -{ - _status.cut(0); - int i; - for (i = 0; i < _files.items(); i++) - { - const int err = file(i).status(); - const TRecnotype recno = file(i).eof() ? -1l : file(i).recno(); - const int key = file(i).getkey(); - _status.add (err); - _status.add (recno); - _status.add (key); - } - for (i = 0; i < _reldefs.items(); i++) - { - const bool first_match = reldef(i)._first_match; - _status.add(first_match); - } -} - -// @doc EXTERNAL - -// @mfunc Ritorna l'indice di

del numero logico passato -// -// @rdesc Ritorna l'indice oppure NOTFOUND nel caso non sia presente -int TRelation::log2ind( - int log) const // @parm Numero logico del file di cui conoscere l'indice - -// @comm Nel caso

sia minore di 0 chiama la -// -// @xref -{ - // returns _files index of logical number or NOTFOUND if not present - - if (log <= 0) - return alias2ind(-log); - - const int nf = _files.items(); - for (int i = 0; i < nf; i++) - if (file(i).num() == log) - return i; - - return NOTFOUND; -} - -// @doc EXTERNAL - -// @mfunc Ritorna l'indice di

del alias del file passato -// -// @rdesc Ritorna l'indice oppure NOTFOUND nel caso non sia presente -int TRelation::alias2ind( - int alias) const // @parm Alias del file di cui conoscere l'indice - - -// @xref -{ - if (alias <= 0) return 0; - - for (int i = 0; i < _reldefs.items(); i++) - { - const TRelationdef& r = (const TRelationdef&)_reldefs[i]; - if (r.alias() == alias) return r.num(); - } - - return NOTFOUND; -} - -// @doc EXTERNAL - -// @mfunc Ritorna l'indice di

del nome del file passato -// -// @rdesc Ritorna l'indice oppure NOTFOUND nel caso non sia presente -int TRelation::name2ind( - const char* name) const // @parm Nome del file di cui conoscere l'indice - -// @xref -{ - const int num = name2log(name); - const int ind = log2ind(num); - return ind; -} - -// @doc EXTERNAL - -// @mfunc Ritorna il descrittore del file -// -// @rdesc Ritorna il reference ad un che indica il descrittore del -// file della relazione -TLocalisamfile& TRelation::lfile( - int logicnum) const // @parm Numero logico del file da ritornare (default 0) - // @parm const char* | name | Nome del file da ritornare -// @syntax TLocalisamfile& lfile(int logicnum) -// @syntax TLocalisamfile& lfile(const char* name) -// -// @comm E' comodo utilizzare anche l'operatore [] che richiama questa funzione con -// la prima sintassi. - -{ - const int idx = log2ind(logicnum); - CHECKD(idx != NOTFOUND, "Relation file not found n. ", logicnum); - return (TLocalisamfile&)_files[idx]; -} - -TLocalisamfile& TRelation::lfile(const char* name) const -{ - const int idx = name2ind(name); - CHECKS(idx != NOTFOUND, "File or Table not found:", name); - return (TLocalisamfile&)_files[idx]; -} - -bool TRelation::has_children(int first) const -{ - for (int i = first; i < _reldefs.items(); i++) - { - const TRelationdef& ref = reldef(i); - if (ref._numto == first) - return true; - } - return false; -} - -// @doc EXTERNAL - -// @mfunc Abilita/disabilita la scrittura sul file -void TRelation::write_enable( - int logicnum, // @parm Numero logico del file da abilitare/disabilitare (default 0) - // @parm cont char* | name | Nome del file da abilitare/disabilitare - const bool on) // @parm Indica l'operazione da effettuare sul file: - // - // @flag TRUE | Abilita la scrittura sul file (default) - // @flag FALSE | Disabilita la scrittura sul file - -// @syntax void write_enable(int logicnum, const bool on) -// @syntax void write_enable(const char* name, const bool on) -// -// @comm Nel caso venga passato un numero logico uguale a 0 vengono abilitati/disabilitati tutti -// i file della relazione - - -{ - if (logicnum == 0) - { - for (int i = 0; i < _reldefs.items(); i++) - reldef(i).write_enable(on); - } - else - { - const int idx = log2ind(logicnum); - CHECKD(idx > 0, "File not found n. ", logicnum); - reldef(idx-1).write_enable(on); - } -} - -void TRelation::write_enable(const char* name, const bool on) - -{ - const int idx = name2ind(name); - CHECKS(idx > 0, "File or Table not found:", name); - reldef(idx-1).write_enable(on); -} - - -// @doc EXTERNAL - -// @mfunc Aggiunge una nuovo file nella relazione -// -// @rdesc Ritorna se e' riuscito ad aggiungere il file alla relazione -bool TRelation::add( - TLocalisamfile* f, // @parm Descrittore del file da aggiungere - const char* relexprs, // @parm Espressione della relazione - int key, // @parm Chiave del file - int linkto, // @parm Posizione alla quale aggiungere il file - int alias, // @parm Alias da dare al file da aggiungere - bool allow_lock) // @parm Indica se fare il lock sul file oppure ignorarli - // @parm int | logicnum | Numero logico del file da aggiungere - // @parm const char* | tabname | Nome della tabella da aggiungere - -// @syntax bool add(TLocalisamfile* f, const char* relexprs, int key, int linkto, int alias, bool allow_lock); -// @syntax bool add(int logicnum, const char* relexprs, int key, int linkto, int alias, bool allow_lock); -// @syntax bool add(const char* tabname, const char* relexprs, int key, int linkto, int alias, bool allow_lock); - -// @comm Il parametro

puo' assumere i valori: -// -// @flag 0 | Per il file principale -// @flag 0 | Indica il numero logico del file -// @flag 0 | Indica l'alias del file - - -{ - const int idxto = log2ind(linkto); - if (idxto == NOTFOUND) - fatal_box("Can't join file %d to %d", f->num(), linkto); - - const int idx = _files.add(f); - - CHECK(relexprs && *relexprs, "Mancano le espressioni di collegamento"); - - TRelationdef* r = new TRelationdef(this, idx, key, idxto, - relexprs, alias, allow_lock); - _reldefs.add(r); - - return TRUE; -} - - -bool TRelation::add(int logicnum, const char* relexprs, int key, - int linkto, int alias, bool allow_lock) -{ - TLocalisamfile* f = new TLocalisamfile(logicnum); - return add(f, relexprs, key, linkto, alias, allow_lock); -} - -bool TRelation::add(const char* tabname, const char* relexprs, int key, - int linkto, int alias, bool allow_lock) -{ - TLocalisamfile* t = NULL; - if (*tabname == '&') - t = new TModule_table(tabname); - else - t = new TTable(tabname); - return add(t, relexprs, key, linkto, alias, allow_lock); -} - -// @doc EXTERNAL - -// @mfunc Sostituisce nella relazione un file -void TRelation::replace( - TLocalisamfile* f, // @parm Descrittore del file sostituto - int index, // @parm Posizione nel quale sostituire il file (default 0) - const char* relexprs, // @parm Nuova Espressione della relazione - int key) // @parm Nuova Chiave del file - -{ - const TLocalisamfile* p = (const TLocalisamfile*)_files.objptr(index); - CHECK(p && p->num() == f->num(), "Can't replace a file with different logic number"); - _files.add(f, index); - if (relexprs && *relexprs) - { - TRelationdef* oldr=(TRelationdef*)_reldefs.objptr(index-1); - TRelationdef* r = new TRelationdef(this, index, key, oldr->link(), - relexprs, oldr->alias(), oldr->allow_lock()); - _reldefs.add(r,index-1); - } -} - -// @mfunc Sostituisce nella relazione un file -void TRelation::replacef( - TLocalisamfile* f, // @parm Descrittore del file sostituto - int lognum, // @parm Numero logico o alias - const char* relexprs, // @parm Nuova Espressione della relazione - int key) // @parm Nuova Chiave del file - -{ - int index=0; - if (lognum < 0) index=alias2ind(lognum); - if (lognum > 0) index=log2ind(lognum); - replace(f,index,relexprs,key); -} - - -TRectype& TRelationdef::load_rec(TRectype& r, const TRectype& from) const -{ - r.zero(); - TString80 val; // Fisso la lunghezza massima ad 80, devono essere campi chiave! - for (int j = 0 ; j < _fields.items(); j++) // for each field - { - const TFieldref& s = (const TFieldref&) _fields[j]; - val = s.read(from); - s.write(val, r); - } - - return r; -} - - -void TRelation::zero() -{ - for (int i = 0; i < _files.items(); i++) - file(i).zero(); -} - -// @doc EXTERNAL - -// @mfunc Permette di posizionare l'albero di relazioni tra i file -// -// @rdesc Ritorna il numero di errore avvenuto nell'opeerazione, NOERR nel caso -// venga eseguito tutto correttamente -int TRelation::position_rels( - TIsamop op, // @parm Operatore che esegue la relazione (default _isequal) - TReclock lockop, // @parm Tipo di lock sul file (default _nolock) - int first) // @parm Numero del file da cui costruire la relazione (default - // 0, cioe' dal file principale) - -// @comm Viene eseguito tutto il lavoro di creazione delle relazione: se non -// trova un record adatto su un file, svuota il record corrente e non -// ritorna errore. -// Le altre funzioni sul record procedono normalmente - -{ - TString expr(80); // Stringa di lavoro per la valutazione delle espressioni - - _errors = NOERR; - - const int primo = first < 0 ? 0 : first; - const int ultimo = first < 0 && (-first) < _reldefs.items() ? -first : _reldefs.items(); - - // workhorse: position files for each active relation - for (int i = primo; i < ultimo; i++) - { - TRelationdef& rd = reldef(i); - - if (primo > 0 && rd.link() < primo) - continue; // Inutile spostare file collegati a record precedenti - - TLocalisamfile& from = file(rd.num()); - TLocalisamfile& to = file(rd.link()); - from.zero(); // Azzera il record corrente (tutti se TSortedfile) - if (to.curr().empty()) - continue; - - from.setkey(rd._key); - - // build record - TRectype& furr = from.curr(); - for (int j = 0; j < rd._fields.items(); j++) // for each field - { - expr = rd.evaluate_expr(j, to); - const TFieldref& s = (const TFieldref&) rd._fields[j]; - s.write(expr, furr); - } // for each field - - const TReclock lck = rd._allow_lock ? lockop : _nolock; - from.read(_isgteq, lck); - - // record read : if not found, zero current record - bool eq = !from.bad(); - for (int kk = 0; eq && kk < rd._fields.items(); kk++) - { - TFieldref& fl = (TFieldref&)rd._fields[kk]; - TString80 f_fr(fl.read(furr)); - TString80 f_ex(rd.evaluate_expr(kk, to)); - if (rd._forced[kk]) - eq = f_fr == f_ex; - else - { - f_fr.rtrim(); f_ex.rtrim(); - eq = f_fr.compare(f_ex, f_ex.len()) == 0; - - // Becca anche 000001=1 - if (!eq && real::is_natural(f_fr) && real::is_natural(f_ex)) - eq = atol(f_fr) == atol(f_ex); - } - } - rd._first_match = eq; - if (eq) - from.setstatus(NOERR); - else - furr.zero(); - } // for each relation - return _errors; -} - - -// @doc EXTERNAL - -// @mfunc Posiziona il numero logico sul record successivo -// -// @rdesc Ritorna se ha trovato la corrispondenza: -// -// @flag TRUE | Corrispondenza trovata -// @flag FALSE | Corrispondenza non trovata -bool TRelation::next_match( - int logicnum, // @parm Numero logico del file da fare avanzare - const char* fieldlist, // @parm Lista dei campi che devono rimanere costanti (default NULL) - int nkey) // @parm Numero della chiave (default 0) - -// @comm Il posizionamento del numero logico non deve essere riferita al file -// principale; in ogni caso viene considerata consistente ad eccezione dei -// casi di inconsistenza nella prima posizione. - -{ - if (logicnum == file().num()) - { - next(); - return file().good(); - } - - const int i = log2ind(logicnum); - CHECKD(i != NOTFOUND,"Nonexistent file referenced in relation ",logicnum); - - int j; - for (j = 0; j < _reldefs.items(); j++) - if (reldef(j).num() == i) break; - - TLocalisamfile& from = file(i); - - reldef(j)._first_match = FALSE; - - if (from.bad()) - return FALSE; // && vaffanculo(); - - const TRecnotype last = from.recno(); - - bool ok = TRUE; // Corrispondenza trovata ? - - if (fieldlist == NULL) - { - TRectype rec(from.num()); - reldef(j).load_rec(rec, from.curr()); - - from.setkey(reldef(j)._key); - from.next(); - ok = (from.good() && from.curr() == rec); - for (int kk = 0; ok && kk < reldef(j)._fields.items(); kk++) - { - if (reldef(j)._forced[kk]) - { - TFieldref& fl = (TFieldref&)reldef(j)._fields[kk]; - const TString f_fr(fl.read(from.curr())); - const TString f_to(fl.read(rec)); - - ok = (f_fr == f_to); - } - } - if (ok) from.setstatus(NOERR); - } - else - { - if (nkey > 0) - from.setkey(nkey); - TToken_string fields(fieldlist); - TToken_string old(80); - const char * f; - for (f = fields.get(0); f; f = fields.get()) - old.add(from.curr().get(f)); - - ok = from.next() == NOERR; - - old.restart(); - - for (f = fields.get(0); f && ok; f = fields.get()) - { - const TFixed_string v(from.curr().get(f)); - const char* o = old.get(); - if (v != o) - ok = false; - } - } - - if (!ok) - { - from.readat(last); - return FALSE; - } - - // Riposiziona gli eventuali files successivi - position_rels(_isequal, _nolock, j+1); - reldef(j)._first_match = FALSE; - - return ok; -} - - -// @doc EXTERNAL - -// @mfunc Controlla se c'e' un record ed e' il primo match (non si e' mai fatta -// ) -// -// @rdesc Ritorna TRUE se c'e' un record ed e' il primo match -bool TRelation::is_first_match( - int logicnum) // @parm Numero logico del file da controllare -{ - const int i = log2ind(logicnum); - CHECKD(i != NOTFOUND,"Nonexistent file referenced in relation ",logicnum); - - for (int j = 0; j < _reldefs.items(); j++) - { - TRelationdef& rdj = reldef(j); - if (rdj.num() == i) - return rdj._first_match; - } - const int err = file().status(); - return err == NOERR; -} - - -#ifdef DBG - -// @doc EXTERNAL - -// @mfunc Controlla se la relazione e' corretta -// -// @rdesc Ritorna i seguent valori -// -// @flag TRUE | La relazione soddisfa i criteri di correttezza -// @flag FALSE | La relazione manca di almeno uno di criteri di correttezza -bool TRelation::isconsistent( - bool reset) // @parm Indica se cercare di riportare la relazione ad uno stato - // consistente (default FALSE) - -// @comm Per essere corretta una relazione necessita che tutti i file siano -// corretti, che il record corrente non sia vuoto e che la relazione sia -// consistente -// Se

e' TRUE si cerca di riportare la relazione in uno stato -// consistente (basato sul record principale). Non viene fatto nessun altro -// controllo -// Questa funzione e' chiamata internamente da -// e . - -{ - const int MAXREL = 24; - int bad = 0, i; - TRecnotype recnos[MAXREL]; - - for (i = 0; i < _files.items() && i < MAXREL; i++) - { - // must be file OK, non-empty record - bad |= file(i).good() || file(i).curr().empty(); - recnos[i] = file(i).recno(); - } - - // if the above hold, check consistency - if (bad) - { - _errors = bad; - return FALSE; - } - - position_rels(_isequal, _nolock); - for (i = 0; i < _files.items(); i++) - if (file(i).recno() != recnos[i]) - { - bad = -1; - break; - } - - if (reset == FALSE) - // leave as before - for(i = 0; i < _files.items(); i++) - file(i).readat(recnos[i]); - - return TRUE; -} - -#endif - - -int TRelation::write(bool force) -{ - _errors = file(0).write(); - if (_errors != NOERR) - return _errors; - - for (int i = 0; i < _reldefs.items(); i++) - { - TRelationdef& rd = reldef(i); - TLocalisamfile& lf = file(rd.num()); - - if (!rd.write_enable() || lf.curr().empty()) - continue; - - int res = lf.write(); - if (force && res == _isreinsert) - res = lf.rewrite(); - if (_errors == NOERR) - _errors = res; - } - - return _errors; -} - -int TRelation::rewrite(bool force) -{ - _errors = file(0).rewrite(); // Riscrive testata - if (force && _errors == _iskeynotfound) // Se non la trova ... - _errors = file(0).write(); // ... forza la scrittura - - for (int i = 0; i < _reldefs.items(); i++) - { - TRelationdef& rd = reldef(i); - TLocalisamfile& lf = file(rd.num()); - - if (!rd.write_enable() || lf.curr().empty()) - continue; - - int res = lf.rewrite(); - if (force && (res == _iskeynotfound || res == _iseof || res == _isemptyfile)) - res = lf.write(); - if (_errors == NOERR) - _errors = res; - } - - return _errors; -} - -int TRelation::remove() -{ - const int res = file(0).remove(); - if (_errors == NOERR && res != _iskeynotfound) _errors = res; - for (int i = 0; i < _reldefs.items(); i++) - { - TRelationdef& rd = reldef(i); - const int log = rd.num(); - - if (!rd.write_enable() || - (file(log).curr()).empty()) continue; - - const int res = file(log).remove(); - if (_errors == NOERR && res != _iskeynotfound) _errors = res; - } - return _errors; -} - -bool TRelation::exist(int logicnum) const -{ - const bool lucky = log2ind(logicnum) >= 0; - return lucky; -} - -void TRelation::mask2rel(const TMask & m) -{ - for (int i = 0; i < _reldefs.items(); i++) - { - TRelationdef& rdi = reldef(i); - - for (int j = 0; j < rdi._exprs.items(); j++) - { - TExpression& expr = (TExpression&)rdi._exprs[j]; - for (int k = 0; k < expr.numvar(); k++) - { - const char * name = expr.varname(k); - if (name[0] == '#') - expr.setvar(k, m.focus_field().evaluate_field(name)); - } - if (rdi._altexprs.objptr(j)) - { - TExpression& altexpr = (TExpression&)rdi._altexprs[j]; - for (int k = 0; k < altexpr.numvar(); k++) - { - const char * name = altexpr.varname(k); - if (name[0] == '#') - altexpr.setvar(k, m.focus_field().evaluate_field(name)); - } - } - } - } -} - -/////////////////////////////////////////////////////////// -// TCursor // -/////////////////////////////////////////////////////////// - -HIDDEN bool __evalcondition(const TRelation& /* r */,TExpression* cond, const TArray& frefs) -{ - for (int i = cond->numvar() - 1; i >= 0; i--) - { - const TRecfield* fr = (const TRecfield*)frefs.objptr(i); - if (fr) - cond->setvar(i, (const char*)*fr); - } - return cond->as_bool(); -} - -// @doc EXTERNAL - -static TFilename _last_name; -static FILE* _last_ndx = NULL; -static bool _last_created = FALSE; - -// @mfunc Apre il file di indice -// -// @rdesc Ritorna l'handle del file aperto -FILE* TCursor::open_index( - bool create) // @parm Indica se creare l'indice nel caso manchi (default FALSE) -{ - _last_created = create; - if (_indexname.empty()) - { - TString8 radix; - radix.format("c%d_", file().num()); - _indexname.temp(radix); - } - - if (_indexname != _last_name || create) - { - if (_last_ndx != NULL) - { - fclose(_last_ndx); - _last_ndx = NULL; - } - fopen_s(&_last_ndx, _indexname, create ? "wb" : "rb"); // Secured _last_ndx = fopen(_indexname, create ? "wb" : "rb"); - if (_last_ndx == NULL) - fatal_box("Can't use cursor index for file %d: '%s'\n", - file().num(), (const char*)_indexname); - _last_name = _indexname; - } - return _last_ndx; -} - -void TCursor::close_index(FILE* f) -{ - CHECK(f == _last_ndx, "Bad open/close index sequence"); - if (_last_created) - { - fclose(_last_ndx); - _last_ndx = NULL; - _last_name.cut(0); - _last_created = FALSE; - } -} - -int TCursor::read_page(long page) -{ - _pagefirstpos = page * _cmaxelpage; - CHECKD(_pagefirstpos < _totrec, "Bad cursor page ", page); - - unsigned long elements = _totrec - _pagefirstpos; - if (elements > _cmaxelpage) - elements = _cmaxelpage; - const unsigned long startpos = sizeof(TRecnotype) * _pagefirstpos; - const unsigned long size = sizeof(TRecnotype) * elements; - - FILE * indf = open_index(); - fseek(indf, startpos, SEEK_SET); - if (fread(_page, size, 1, indf) != 1) - fatal_box("Can't read page number %d in cursor n. %d\n", page, file().num()); - - return (int) elements; -} - -bool TCursor::has_simple_filter() const -{ - bool yes = _filterfunction == NULL; - if (yes && _fexpr != NULL && _fexpr->numvar() > 0) - { - const RecDes& recd = curr().rec_des(); // Descrizione del record della testata - const KeyDes& kd = recd.Ky[_nkey-1]; // Elenco dei campi della chiave del cursore - for (int i = _fexpr->numvar()-1; yes && i >= 0; i--) - { - const char * vn = _fexpr->varname(i); - TFieldref f(vn, 0); - yes = f.file() == 0; - if (yes) - { - for (int k = kd.NkFields-1; k >= 0; k--) - { - const int nf = kd.FieldSeq[k] % MaxFields; - const RecFieldDes& rf = recd.Fd[nf]; - yes = kd.FromCh[k] == 255 && f.name() == rf.Name; - if (yes) break; - } - } - } - } - return yes; -} - -TRecnotype TCursor::buildcursor(TRecnotype rp) -{ - _pagefirstpos = 0L; - memset(_page, 0, sizeof(TRecnotype)*_cmaxelpage); - _fpkey.destroy(); - - const int fhnd = file().handle(); - if (DB_reccount(fhnd) == 0) - return 0; - - TRecnotype oldrecno= RECORD_NON_FISICO,ap = 0; - int pagecnt = 0; - FILE* indf = NULL; - - const int l = strlen(to()); - int junk = DB_index_seek(fhnd, from()); - if (junk < 0) junk=get_error(junk); - if (junk == _iseof) return 0; - - // DB_index_recno(fhnd); // A cosa cavolo serve? - _pos=-1; - - const bool filtered = has_filter(); - const bool simple_filter = filtered && has_simple_filter(); - - // Barra di + - TString msg("Elaborazione "); - msg << relation()->lfile().name(); - TProgress_monitor pi(_if->lfile().items(), msg); - - while (!DB_index_eof(fhnd)) - { - pi.add_status(); - const char *s0 = DB_index_getkey(fhnd); - if (l && (strncmp(to(), s0, l) < 0)) - break; - const TRecnotype recno = DB_index_recno(fhnd); - if (recno == oldrecno) - break; // means that no more keys are available - oldrecno=recno; - - bool to_be_added = true; - - if (filtered) - { - if (simple_filter) - to_be_added = simple_filtercursor(s0); - else - to_be_added = filtercursor(recno); - } - - if (to_be_added) - { - _page[pagecnt++] = recno; - if (_pos < 0 && recno == rp) - _pos = ap; - ap++; - } - - if (pagecnt == _cmaxelpage) - { - _fpkey.add(new TToken_string(s0)); - pagecnt = 0; - if (indf == NULL) - { - indf = open_index(TRUE); - fseek(indf, 0L, SEEK_SET); - } - fwrite(_page,sizeof(TRecnotype)*_cmaxelpage, 1, indf); - } - DB_index_next(fhnd); - } // while - if (indf != NULL) - { - if (pagecnt > 0) - fwrite(_page,sizeof(TRecnotype)*pagecnt, 1, indf); - close_index(indf); - } - - _pagefirstpos = ap > _cmaxelpage ? -(_cmaxelpage + 1) : 0L ; - - return ap; -} - -bool TCursor::call_filterfunction(TRecnotype recno) const -{ - bool ok = true; - if (_filterfunction) - { - const int handle = file().handle(); - // memorizzo la chiave prima di eventuali spostamenti - const TString256 s0 = DB_index_getkey(handle); - ok = _filterfunction(_if); - // ripristino la chiave dopo eventuali spostamenti - if (recno != DB_index_recno(handle)) - DB_index_go(handle, s0, recno); - } - return ok; -} -bool TCursor::filtercursor(TRecnotype recno) -{ - file().readat(recno); - if (update_relation()) - { - // memorizzo la chiave prima di eventuali spostamenti - const TString256 s0 = DB_index_getkey(file().handle()); - - _if->update(-filter_limit()); - - // ripristino la chiave dopo eventuali spostamenti - const int handle = file().handle(); - if (recno != DB_index_recno(handle)) - DB_index_go(handle, s0, recno); - } - - bool ok = call_filterfunction(recno); - - if (ok && _fexpr) - ok = __evalcondition(*_if, _fexpr, _frefs); - return ok; -} - -bool TCursor::simple_filtercursor(const char* key) const -{ - if (_fexpr == NULL) - return true; - - const RecDes& recd = curr().rec_des(); // Descrizione del record della testata - const KeyDes& kd = recd.Ky[_nkey-1]; // Elenco dei campi della chiave del cursore - - TFieldref f; - for (int i = _fexpr->numvar()-1; i >= 0; i--) - { - f = _fexpr->varname(i); - int offset = 0; - for (int k = 0; k < kd.NkFields; k++) - { - const int nf = kd.FieldSeq[k] % MaxFields; - const RecFieldDes& rf = recd.Fd[nf]; - int len = rf.Len; - if (f.name() == rf.Name) - { - offset += f.from(); - if (f.to() > f.from()) - len = f.to() - f.from(); - else - len -= f.from(); - char* val = (char*)(key+offset); - char oldchar = '\0'; - int l; - - for (l = len; l >= 0; l--) // rtrim - { - if (l == 0 || val[l-1] != ' ') - { - oldchar = val[l]; - val[l] = '\0'; - break; - } - } - switch (rf.TypeF) - { - case _boolfld: - _fexpr->setvar(i, strchr("1STXY", *val)!=NULL ? "X" : " "); - break; - case _intfld: - case _longfld: - { - const char* v; - for (v = val; *v == ' ' || *v == '0'; v++); - _fexpr->setvar(i, v); - } - break; - default: - _fexpr->setvar(i, val); - break; - } - if (oldchar) - val[l] = oldchar; - break; - } - offset += len; - } - } - return _fexpr->as_bool(); -} - -bool TCursor::ok() const -{ - if (file().bad()) - return FALSE; - - const TRectype& rec = file().curr(); - - const TFixed_string key(rec.key(_nkey)); - const TString& kf = from(); - const TString& kt = to(); - - if (file().tab()) - { - if (key.compare(kf, 3) < 0 || (kt.not_empty() && kt.compare(key, 3) < 0)) - return FALSE; - } - else - { - if (key < kf || (kt.not_empty() && kt.compare(key, kt.len()) < 0)) - return FALSE; - } - - const TRecnotype old = file().recno(); - - if (update_relation()) - { - _if->update(); - if (DB_recno(file().handle()) != old) - file().readat(old); - } - - if (call_filterfunction(old) && - (_fexpr ? __evalcondition(*_if, _fexpr, _frefs) : true)) - return true; - - return false; -} - -bool TCursor::changed() -{ - if (_frozen && _lastrec > 0L) - return FALSE; - - const TLocalisamfile& f = file(); - - if (prefix().get_dirtype(f.num()) == _nordir && - _index_firm != prefix().get_codditta()) - return TRUE; - - const int handle = f.handle(); - const TRecnotype eod = DB_reccount(handle); - if (_lastrec != eod || - _lastkrec != DB_changed(handle)) - return TRUE; - - if (!f.curr().valid()) - return TRUE; - - return FALSE; -} - -TRecnotype TCursor::update() -{ - TWait_cursor hourglass; - TLocalisamfile& f = file(); - f.setkey(_nkey); - if (f.curr().empty()) - f.zero(); // Usare sempre l'azzeratore esperto del file non direttamente f.curr().zero() - f.read(_isgteq); - - _totrec = buildcursor(f.recno()); - - const int handle = f.handle(); - const TRecnotype eod = DB_reccount(handle); - - _lastrec = eod; - _index_firm = prefix().get_codditta(); - _lastkrec = DB_changed(handle); - - return _totrec; -} - - -HIDDEN void matildator(const TRectype& oldrec, int key, bool tilde, TString& str) -{ -/* - if (tilde) - { - TRectype rec(oldrec); // Utile duplicazione di record - const RecDes* recd = rec.rec_des(); // Descrizione del record della testata - const KeyDes& kd = recd->Ky[key-1]; // Elenco dei campi della chiave - TString80 val; - for (int i = kd.NkFields-1; i >= 0; i--) - { - const int nf = kd.FieldSeq[i] % MaxFields; - const RecFieldDes& rf = recd->Fd[nf]; - val = rec.get(rf.Name); - if (val.not_empty()) - { - if (rf.TypeF == _alfafld && val.len() < rf.Len) - { - val.left_just(rf.Len, '~'); - rec.put(rf.Name, val); - } - break; - } - } - str = rec.key(key); - } - else - str = oldrec.key(key); - str.replace('~', ' '); -*/ - str = oldrec.key(key); - if (tilde) - { - const RecDes& recd = oldrec.rec_des(); // Descrizione del record della testata - const KeyDes& kd = recd.Ky[key-1]; // Elenco dei campi della chiave - const int len = str.len(); - const int kfields = kd.NkFields; - int foc = 0; - for (int k = 0; k < kfields; k++) - { - const int nf = kd.FieldSeq[k] % MaxFields; - const RecFieldDes& rf = recd.Fd[nf]; - int toc = foc; - if (kd.FromCh[k] == 255) // Non e' stato specificato un range - toc += rf.Len; // Considero l'intera lunghezza del campo - else - toc += kd.ToCh[k] - kd.FromCh[k] + 1; - CHECK(toc > foc, "Invalid addition"); - if (toc >= len) - { - if (str[foc] > ' ' && rf.TypeF == _alfafld) - str.left_just(toc, ' '); - break; - } - foc = toc; - } - } - str.replace('~', ' '); -} - -// @doc EXTERNAL - -// @mfunc Setta il filtro sul cursore -void TCursor::filter( - const char* fil, // @parm Espressione del filtro - const TRectype* from, // @parm Primo record del pacchetto da esaminare - const TRectype* to, // @parm Ultimo record del pacchetto da esaminare - int tilde // @parm Flag per riempire le chiavi di ~ from(0x1) o to(0x2) - ) - -// @comm ATTENZIONE: non e' possibile filtrare un cursore congelato - -{ - TString kf(_keyfrom), kto(_keyto); - const bool filterchanged = (fil != NULL) && (_filter != fil); - - if (from != NULL) - matildator(*from, _nkey, (tilde & 0x1) != 0, kf); - if (to != NULL) - matildator(*to, _nkey, (tilde & 0x2) != 0, kto); - - if (kf[0] <= ' ' || kto[0] <= ' ') - { - switch (file().num()) - { - case LF_TABGEN: - case LF_TABCOM: - case LF_TAB: - if (!real::is_natural(file().name())) // E' veramente una tabella o è un file normale? - { - kf.overwrite(file().name(), 0); - kto.overwrite(file().name(), 0); - }; - break; - case LF_TABMOD: - if (!real::is_natural(file().name())) // E' veramente una tabella di modulo o è un file normale? - { - const TModule_table& t = (TModule_table&)file(); - TString16 k; - k.format("%2s%6ld%3s", t.module(), t.customer(), t.name()); - kf.overwrite(k, 0); - kto.overwrite(k, 0); - } - break; - default: - break; - } - } - - if (filterchanged || (_keyfrom != kf) || (_keyto != kto)) - { - CHECK(!frozen(), "Impossibile filtrare un cursore congelato"); - - _pos = 0; - _totrec = 0; - _lastrec = 0; - - if (filterchanged) - { - _filter = fil; - if (_fexpr) delete _fexpr; - _frefs.destroy(); - TTypeexp type = (_filter.find('"') >= 0) ? _strexpr : _numexpr; - if (_filter.not_empty()) - { - _fexpr = new TExpression(_filter, type); - if (_fexpr->type() == _numexpr) - for (int i = 0 ; i < _fexpr->numvar(); i++) - if (file().curr().type(_fexpr->varname(i)) == _alfafld) - { - _fexpr->set_type(_strexpr); - break; - } - } - else _fexpr = NULL; - if (_fexpr) - { - const int items = _fexpr->numvar(); - for (int i = 0 ; i < items; i++) - { - const char * vn = _fexpr->varname(i); //occhio - if (vn[0] != '#') - { - const TFieldref f(vn, 0); - const TString & id = f.id(); - - if (id.full()) - { - _filter_update = true; - const int file_id = _if->name2ind(id); - if (_filter_limit < file_id) - _filter_limit = file_id; - } - _frefs.add(new TRecfield(_if->curr(f.file()), f.name(), f.from(), f.to()), i); - } - else - NFCHECK("Variabile strana %s", (const char*)vn); - } - } - } - _keyfrom = kf; - _keyto = kto; - } -} - -void TCursor::setkey(int nkey) -{ - if (nkey != _nkey) - { - _lastrec = 0L; - _nkey = nkey; - file().setkey(_nkey); - filter(NULL); - } -} - -int TCursor::test(TIsamop op, TReclock lockop) -{ - TLocalisamfile& curfile = file(); - const TRectype& currec = curfile.curr(); - curfile.setkey(_nkey); - - int err = NOERR; - if (op == _isequal) - { - const TString match(currec.key(_nkey)); - bool trovato = FALSE; - - for (err = curfile.read(op, lockop); - err == NOERR && match == currec.key(_nkey); - err = curfile.next(lockop)) - { - if (ok()) - { - trovato = true; - break; - } - else - { - if (lockop != _nolock) - curfile.reread(_unlock); - } - } - if (!trovato && err == NOERR) - { - err = _iskeynotfound; - curfile.setstatus(err); - } - } - else - { - for (err = curfile.read(op, lockop); - err == NOERR; - err = curfile.next(lockop)) - { - if (ok()) - break; - else - { - //if (((TCursor *)this)->items() == 0) - if (_totrec == 0) - { - err = _isemptyfile; - break; - } - - if (lockop != _nolock) - curfile.reread(_unlock); - - const TString& kto = to(); - if (kto.not_empty()) - { - const TFixed_string curkey(currec.key(_nkey)); - if (kto.compare(curkey, kto.len()) < 0) - { - err = _iseof; - break; - } - } - } - } - } - - return err; -} - -TRecnotype TCursor::read(TIsamop op, TReclock lockop) -{ - TLocalisamfile& curfile = file(); - - if (changed()) - { - const TRectype savecurr = curfile.curr(); - update(); - curfile.curr() = savecurr; - } - - int err = test(op, lockop); - const TRecnotype curpos = curfile.recno(); - - TString match; - if (err == NOERR) - match = curfile.curr().key(_nkey); - - if (changed()) - { - NFCHECK("Com'e' possibile che ci sia nuovamente bisogno di update?"); - update(); - } - - if (err != NOERR) - { - _pos = _totrec - 1; - if (_pos < 0) - { - curfile.zero(); - err = _isemptyfile; - } - else - readrec(); - curfile.setstatus(err); - } - else - { - const int pages = (_totrec-1) / _cmaxelpage + 1; - - _pos = -1; - if (pages > 1) - { -// const TString match(curfile.curr().key(_nkey)); - - bool found = FALSE; - FOR_EACH_ARRAY_ROW(_fpkey, p , s) - { - if (match <= *s) - { - const int pagecnt = read_page(p); - found = true; - for (int i = 0; i < pagecnt && _pos < 0L; i++) - if (_page[i] == curpos) - _pos = _pagefirstpos + i; - } - } - if (!found) - { - const int pagecnt = read_page(pages - 1); - for (int i = 0; i < pagecnt && _pos < 0L; i++) - if (_page[i] == curpos) - _pos = _pagefirstpos + i; - } - } - else - { - for (int i = 0; i < _cmaxelpage && _pos < 0L; i++) - if (_page[i] == curpos) - _pos = i; - } - - if (_pos < 0L) - _pos = 0L; - readrec(); - } - - return _pos; -} - -TCursor::TCursor(TRelation* r, const char* fil, int nkey, - const TRectype *from, const TRectype* to, int tilde) - : _if(r), _nkey(nkey), _fexpr(NULL), _frozen(false), _filter_update(false), - _filterfunction_update(false), _filter_limit(0), _filterfunction(NULL) -{ - CHECK(r, "Null cursor relation"); - file().setkey(_nkey); - _pos = 0; - _totrec = 0; - _lastrec = 0; - _lastkrec = 0; - _pagefirstpos = 0L; - - _page = new TRecnotype[_cmaxelpage]; - memset(_page, 0, _cmaxelpage * sizeof(TRecnotype)); - - filter(fil, from, to, tilde); -} - - -TCursor::~TCursor() -{ - if (_indexname.not_empty()) - { - if (_indexname == _last_name && _last_ndx != NULL) - { - _last_created = TRUE; // Force close - close_index(_last_ndx); - } - ::remove(_indexname); - } - delete _page; - if (_fexpr) - delete _fexpr; -} - - -TRecnotype TCursor::readrec() -{ - TRecnotype nrec = 0L; - TLocalisamfile& f = file(); - if (_pos >= items()) - { - f.setstatus(_iseof); - f.zero(); - return nrec; - } - f.setstatus(NOERR); - - if (_pos < _pagefirstpos || _pos >= _pagefirstpos + _cmaxelpage) - read_page(_pos / _cmaxelpage); - nrec = _page[_pos - _pagefirstpos]; - CHECKD(nrec >= 0, "Bad record position ", nrec); - - curr().setdirty(); - curr().readat(f, nrec); - repos(); - return nrec; -} - -// @doc EXTERNAL - -// @mfunc Mette un lock sul record -// -// @rdesc Ritorna il numero di errore che si verifica nel porre il lock (NOERR) se -// non si verificano errori -int TCursor::lock( - TReclock l) // @parm Tipo di lock da porre sul record (vedi ) -{ - int rt=NOERR; - switch(l) - { - case _lock: - rt=DB_lock_rec(file().handle(),_pos); - break; - case _unlock: - rt=DB_unlock(file().handle()); - break; - default: - break; - } - if (rt != NOERR) rt=get_error(rt); - return(rt); -} - -TRecnotype TCursor::operator =(const TRecnotype pos) -{ - if (changed()) - update(); - CHECKD(pos >= 0 && pos <= _totrec, "Bad cursor position : ", pos); - _pos = pos; - readrec(); - - return _pos; -} - -TRecnotype TCursor::operator +=(const TRecnotype npos) -{ - if (changed()) - update(); - - _pos += npos; - if (_pos > _totrec) - _pos = _totrec; - else - if (_pos < 0) - _pos = 0; - readrec(); - return _pos; -} - -TRecnotype TCursor::items() -{ - if (changed()) - update(); - return _totrec; -} - -bool TCursor::next_match(int lognum, const char* fl, int nk) -{ - if (lognum == 0 || lognum == file().num()) - { - ++(*this); - return file().good(); - } - else - return _if->next_match(lognum, fl, nk); -} - -bool TCursor::is_first_match(int ln) -{ - return (ln == 0 || ln == file().num()) ? - (_pos == 0 && file().good()) : (_if->is_first_match(ln)); -} - -bool TCursor::scan(CURSOR_SCAN_FUNC func, void* pJolly, const char* msg) -{ - TRecnotype tot = 0; // Temporarily - { - TWait_cursor hourglass; - tot = items(); - } - bool ok = true; - if (tot > 0) - { - TProgress_monitor* pi = NULL; - if (tot > 1) - { - if (msg == NULL || *msg == '\0') - msg = TR("Elaborazione in corso..."); - pi = new TProgress_monitor(tot, msg, true); - } - - freeze(true); - for (*this = 0; pos() < tot; ++*this) - { - if (!func(*relation(), pJolly)) - { - ok = false; - break; - } - if (pi != NULL && !pi->add_status()) - { - ok = false; - break; - } - } - freeze(false); - if (pi != NULL) - delete pi; - } - return ok; -} - -/////////////////////////////////////////////////////////// -// TSorted_cursor -/////////////////////////////////////////////////////////// - -typedef struct -{ - char f[256]; - TRecnotype p; -} El_To_Sort; - -// @doc EXTERNAL - -// @mfunc Controlla la validita' dell'espressione -// -// @rdesc Ritorna se si tratta di una espressione valida -bool TSorted_cursor::check_expr( - TString& s) // @parm Espressione da controllare - -// @comm Modifica l'espressione in modo da avere una valido parametro per TFieldref! -// Es. UPPER(20->RAGSOC[1,40]+) ==> 20->RAGSOC[1,40]+ -// N.B.: la direzione dell'ordinamento va messa indicata sempre dopo la definizione del campo, mai -// dopo l'operatore UPPER(). E' a cura delle altre funzioni esterne che utilizzano la check_expr() -// discriminare il segno + o - che sia. - -{ - int p=s.find('('); - int p1=s.find(')'); - bool rt=TRUE; - - if (p>-1 && p1>-1) - if (s.find("UPPER") < 0) - { - error_box("Only UPPER() operator is allowed"); - rt=FALSE; - } - else - { - s=s.mid(6,s.len()-7); - p=s.find('('); - p1=s.find(')'); - if (p>-1 || p1 >-1) - { - error_box("Too many parentheses!"); - rt=FALSE; - } - } - else - if ((p>-1 && p1==-1) || (p==-1 && p1>-1)) - { - error_box("Unbalanced parentheses in expression"); - rt=FALSE; - } - return rt; -} - -bool TSorted_cursor::is_upper(TString& s) -// Controlla se nell'espressione esiste l'operatore UPPER() -{ - bool rt=FALSE; - if (s.find("UPPER") >= 0) - rt = check_expr(s); - return rt; -} - -TRecnotype TSorted_cursor::buildcursor(TRecnotype rp) -{ - TRecnotype oldrecno=0,pos,ap = 0; - int abspos=0,junk, l, pagecnt = 0; - TString s; - FILE* _f = NULL; - - setpagefirstpos(0L); - memset(page(), 0, sizeof(TRecnotype) * pagesize()); - fpkey().destroy(); - - if (file().empty()) - return 0; - - TSort sort(sizeof(El_To_Sort)); - - _order_expr.restart(); - - while ((s=_order_expr.get()).not_empty()) - { - check_expr(s); // Toglie l'eventuale operatore UPPER(), in modo che l'ultimo carattere - // indichi eventualmente la direzione dell'ordinamento - const char last = s.right(1)[0]; - const char versus = (last=='-') ? 'd' : 'a'; - if (last == '-' || last == '+') - s.rtrim(1); - TFieldref f(s,0); - // Il controllo del file e' automatico in f.len() - const TString& id = f.id(); - const TRectype& r = relation()->lfile(id).curr(); - int flen = f.len(r); - if (flen == 0) // Campo virtuale - flen = 50; - - if (id.full()) - { - _sort_update = true; - const int file_id = relation()->name2ind(id); - if (filter_limit() < file_id) - set_filter_limit(file_id); - } - - sort.addsortkey(abspos,flen,versus); - CHECKS(flen!=0,"Field can not have null length: ",(const char *) s); - - abspos += flen; - CHECKD(abspos<=256, "Sort key too long: ", abspos); - } - - const int handle = file().handle(); - - sort.init(); - l = strlen(to()); - junk=DB_index_seek(handle, (char*)(const char*) from()); - if (junk < 0) junk=get_error(junk); - if (junk == _iseof) return 0; - - pos = DB_index_recno(handle); - TCursor::pos()=-1; - - // Barra di progressione - TString msg("Elaborazione "); - msg << relation()->lfile().name(); - TProgress_monitor pi(relation()->lfile().items(), msg); - - while (!DB_index_eof(handle)) - { - pi.add_status(); - const char* s0 = DB_index_getkey(handle); - if (l && (strncmp(to(), s0, l) < 0)) - break; - const TRecnotype recno = DB_index_recno(handle); - if (recno == oldrecno) - break; // means that no more keys are available - - oldrecno=recno; - - // Attenzione: la filtercursor non si si limita a filtrare ma avanza anche il cursore!!!! - const bool to_be_added = filtercursor(recno); - if (to_be_added) - { - El_To_Sort Element; - - TFixed_string f(Element.f, sizeof(Element.f)); - fill_sort_key(f); - Element.p=recno; - sort.sort((char *) &Element); - if (TCursor::pos() < 0 && recno == rp) - TCursor::pos() = ap; - ap++; - } - - long rec = DB_index_next(handle); - if (rec < 0) - fatal_box("Can't read index n. %d - file n. %d",file().getkey(),file().num()); - } // while - - sort.endsort(); - - pagecnt = 0; - El_To_Sort* Element = NULL; - while ((Element=(El_To_Sort *)sort.retrieve()) != NULL) - { - page()[pagecnt++]=Element->p; - if (pagecnt == pagesize()) - { - if (_f == NULL) - { - _f = open_index(TRUE); - fseek(_f, 0L, SEEK_SET); - } - fwrite(page(), sizeof(TRecnotype) * pagesize(), 1, _f); - pagecnt=0; - fpkey().add(new TToken_string(Element->f)); - } - } - if (_f != NULL) - { - if (pagecnt > 0) - fwrite(page(),sizeof(TRecnotype) * pagecnt, 1, _f); - close_index(_f); - } - setpagefirstpos(ap > pagesize() ? -(pagesize() + 1) : 0L) ; - - return ap; -} - -const char* TSorted_cursor::fill_sort_key(TString& k) -{ - TString256 sf; - k.cut(0); - for (TString80 s = _order_expr.get(0); s.not_empty(); s = _order_expr.get()) - { - const bool is_up = is_upper(s); - const char last = s.right(1)[0]; - const bool align = last == '*'; - - if (last == '-' || last == '+' || align) - s.rtrim(1); - - const TFieldref f(s,0); - sf = f.read(*relation()); - const TRectype& frec = curr(f.file()); - TFieldtypes fld_type = frec.type(f.name()); - int fld_len = f.len(frec); - // Converte in ANSI i campi data ed i sottocampi con una data! - if (fld_type == _datefld || (f.is_subfield() && TDate::isdate(sf))) - { - if (sf.not_empty()) // Aggiunta del 17-10-2011 per efficienza stampe mg (ma potrebbe non andar bene!) - { - const TDate d(sf); - sf.format("%08ld", d.date2ansi()); - } - else - sf.spaces(8); - fld_type = _datefld; - fld_len = 8; - } - else - { - if (is_up) // Test inutile: tutte le chiavi sono maiuscole 08-02-2016 - sf.upper(); - } - switch (fld_type) - { - case _boolfld: - case _charfld: - case _memofld: - case _alfafld: sf.left_just(fld_len > 0 ? fld_len : 50); break; - case _intfld: - case _longfld: - case _intzerofld: - case _longzerofld: sf.right_just(fld_len > 0 ? fld_len : 50); break; - case _datefld: break; // Gia' lungo 8! - case _realfld: - if (align) - { - real r(sf); - sf = r.string(fld_len > 0 ? fld_len : 50, fld_len > 0 ? (fld_len-4)/2 : 10); - } - else - sf.right_just(fld_len > 0 ? fld_len : 50); - break; - default : - sf.left_just(fld_len > 0 ? fld_len : 50); - break; - } - k << sf; - } - return k; -} - -bool TSorted_cursor::changed() -{ - bool rt = false; - if (_is_valid_expr) - { - rt = TCursor::changed(); - if (!rt) rt=_is_changed_expr; - _is_changed_expr = false; - } - else - NFCHECK("Can't perform changed() while sorted cursor expression is not valid!"); - return rt; -} - -int TSorted_cursor::test(TIsamop op, TReclock lockop) - -{ - int err = NOERR; - - if (items() == 0L) - { - err = _isemptyfile; - file().setstatus(err); - return err; - } - - TString256 searching; fill_sort_key(searching); - searching.rtrim(); - const int cmplen = searching.len(); - - TRecnotype first = 0L; - TRecnotype last = items()-1; - TRecnotype found = -1L; - - FOR_EACH_ARRAY_ROW(fpkey(), i, s) - { - const int cmp = searching.compare(*s, cmplen); - if (cmp <= 0) - last = (i + 1) * pagesize() - 1; - else - first = i * pagesize(); - } - - const bool ghiacciato = !frozen(); - if (ghiacciato) freeze(true); - - TString256 testing; - while (first <= last) - { - const TRecnotype test = (first+last)/2; - TCursor::operator=(test); // verif - fill_sort_key(testing); - const int cmp = searching.compare(testing, cmplen); - if (cmp == 0) - { - if (op != _isgreat) - { - if (found < 0l || test < found) - found = test; - last = test-1; - } - else - first = test+1; - } - else - { - if (cmp < 0) - { - last = test-1; - if (op != _isequal) - { - if (found < 0l || test < found) - found = test; - } - } - else - first = test+1; - } - } - - if (found >= 0L) - { - TCursor::operator=(found); // verif - file().setstatus(NOERR); - if (lockop != _nolock) - lock(lockop); - } - else - { - - found = items()-1; - if (found >= 0) - TCursor::operator=(found); - err = op == _isequal ? _iskeynotfound : _iseof; - file().setstatus(err); - } - - if (ghiacciato) - freeze(false); - - return err; -} - - -TRecnotype TSorted_cursor::read(TIsamop op, TReclock lockop) -{ - TString256 searching; fill_sort_key(searching); - searching.rtrim(); - const int cmplen = searching.len(); - - TRecnotype first = 0L; - TRecnotype last = items()-1; - TRecnotype found = -1L; - - FOR_EACH_ARRAY_ROW(fpkey(), i, s) - { - const int cmp = searching.compare(*s, cmplen); - if (cmp <= 0) - last = (i + 1) * pagesize() - 1; - else - first = i * pagesize(); - } - - const bool ghiacciato = !frozen(); - if (ghiacciato) freeze(true); - - TString256 testing; - while (first <= last) - { - const TRecnotype test = (first+last)/2; - TCursor::operator=(test); - fill_sort_key(testing); - const int cmp = searching.compare(testing, cmplen); - if (cmp == 0) - { - if (op != _isgreat) - { - if (found < 0l || test < found) - found = test; - last = test-1; - } - else - first = test+1; - } - else - { - if (cmp < 0) - { - last = test-1; - if (op != _isequal) - { - if (found < 0l || test < found) - found = test; - } - } - else - first = test+1; - } - } - - if (found >= 0L) - { - TCursor::operator=(found); - file().setstatus(NOERR); - if (lockop != _nolock) - lock(lockop); - } - else - { - found = items()-1; - if (found >= 0) - TCursor::operator=(found); - file().setstatus(op == _isequal ? _iskeynotfound : _iseof); - } - - if (ghiacciato) - freeze(false); - - return found; -} - -void TSorted_cursor::change_order(const char* order_expr) -{ - _sort_update = false; - if (order_expr && *order_expr && _order_expr != order_expr) - { - TString s; - _order_expr = order_expr; - _order_expr.restart(); - while ((s=_order_expr.get()).not_empty() && (_is_valid_expr=check_expr(s))) ; - if (_is_valid_expr) - _is_changed_expr=true; - } -} - -TSorted_cursor::TSorted_cursor(TRelation *f, const char * order_expr, const char * filter, int key, const TRectype* from, const TRectype* to) - : TCursor(f,filter,key,from,to) -{ - change_order(order_expr); -} - -TSorted_cursor::~TSorted_cursor() -{ -} - -/////////////////////////////////////////////////////////// -// TFieldRef -/////////////////////////////////////////////////////////// - -// @doc EXTERNAL - -// @func Converte una stringa in numero logico o numero tabella -// -// @rdesc Ritorna il numero logico del file o il numero della tabella -int name2log( - const char* name) // @parm Stringa da convertire -{ - int log = 0; - - if (name) - { - while (isspace(*name)) - name++; - - if (*name) - { - log = table2logic(TFixed_string(name)); - - CHECKS(log != 0, "Non e' un file ne' una tabella: ", name); - } - } - return log; -} - - -TFieldref::TFieldref() : _fileid(0), _name(0) {} - - -TFieldref::TFieldref(const TString& s, short defid) -{ - operator=(s); - if (_fileid == 0) _fileid = defid; -} - -void TFieldref::copy(const TFieldref& f) -{ - _fileid = f._fileid; - _id = f._id; - _name = f._name; - _from = f._from; - _to = f._to; -} - -TObject* TFieldref::dup() const -{ - TFieldref* f = new TFieldref(*this); - return f; -} - -// @doc EXTERNAL - -// @mfunc Operatore di assegnamento -// -// @rdesc Ritorna l'oggetto assegnato -TFieldref& TFieldref::operator =( - const TString& s) // @parm Stringa da assegnare all'oggetto - -// @comm Un deve avere il seguente formato (solamente NAME e il mandante): -// FILE->NAME[FROM,TO] - -{ - int pos = s.find("->"); - if (pos > 0) - { - _id = s.left(pos); _id.strip(" "); - _fileid = name2log(_id); - pos += 2; - } - else - { - pos = s.find('.'); - if (pos > 0) - { - _id = s.left(pos); _id.strip(" "); - _fileid = name2log(_id); - pos++; - } - else - { - _id.cut(0); - _fileid = pos = 0; - } - } - - int par = s.find('[', pos); // Cerca la fine del nome del campo - _name = s.sub(pos, par); // Estrae il nome del campo - _name.strip(" "); // Elimina eventuali spazi superflui - - if (par > 0) - { - pos = par+1; - set_from(atoi(s.mid(pos))); - par = s.find(',', pos); - if (par > 0) set_to(atoi(s.mid(par+1))); - else set_to(from()+1); - } - else - { - set_from(0); - set_to(-1); - } - - return *this; -} - -void TFieldref::set_file(int f) -{ - _id.cut(0); - if (f != 0) - { - _fileid = f; - _id << abs(f); - if (f < 0) _id << '@'; - } - else _fileid = 0; -} - -void TFieldref::print_on(ostream& out) const -{ - if (_id.not_empty()) out << _id << "->"; - out << _name; - if (_from > 0 || _to > 0) - { - out << '[' << (_from+1); - out << ',' << _to << ']'; - } -} - -static TString buffer; - -const char* TFieldref::read(TConfig& ini, const char* defpar) const -{ - ini.set_paragraph(_id.empty() ? defpar : (const char *) _id); - - if (!ini.exist(_name)) - return ""; - - buffer = ini.get(_name); - // Gestisce valori tra virgolette - const int l = buffer.len(); - if (l > 1) - { - if ((buffer[0] == '"' || buffer[0] == '\'') && buffer[0] == buffer[l-1]) - { - buffer.rtrim(1); - buffer.ltrim(1); - if (strchr(buffer, '\\')) - buffer = esc(buffer); - } - } - if (_from > 0 || _to > 0) - { - if (_to < l && _to > 0) buffer.cut(_to); - if (_from > 0) buffer.ltrim(_from); - } - return buffer; -} - -static int quotes_needed(const char* val) -{ - int yes = 0; - for ( ; *val; val++) if (strchr("\t\n\r\"' ", *val)) - { - yes = 1; - if (*val < ' ') - { - yes = 2; - break; - } - } - return yes; -} - -static void escapes(TString& val) -{ - for (int i = 0; val[i]; i++) - { - if (val[i] == '\n') - { - val.insert(" ", i); - val[i++] = '\\'; - val[i] = 'n'; - } else - if (val[i] == '\r') - { - val.insert(" ", i); - val[i++] = '\\'; - val[i] = 'r'; - } else - if (val[i] == '\t') - { - val.insert(" ", i); - val[i++] = '\\'; - val[i] = 't'; - } - } -} - -void TFieldref::write(TConfig& ini, const char* defpar, const char* val) const -{ - if ((val == NULL || *val == '\0') && !ini.exist(_name)) - return; - - const char* para = _id.empty() ? defpar : (const char*)_id; - if (_from > 0 || _to > 0) - { - buffer = ini.get(_name, para); - if ((buffer[0] == '"' || buffer[0] == '\'') && buffer[0] == buffer[buffer.len()-1]) - { - buffer.rtrim(1); - buffer.ltrim(1); - if (strchr(buffer, '\\')) - buffer = esc(buffer); - } - buffer.overwrite(val, _from); - val = buffer; - } - const int qn = quotes_needed(val); // Controlla se c'e' bisogno di virgolette - if (qn) - { - if (qn == 2) - { - buffer = val; - escapes(buffer); - buffer.insert("\"", 0); - buffer << '"'; - } - else - { - const char* virg = strchr(val, '"') ? "'" : "\""; - buffer = val; - buffer.insert(virg, 0); - buffer << virg; - } - ini.set(_name, buffer, para); - } - else - ini.set(_name, val, para); -} - -const char* TFieldref::read(const TRectype& rec) const -{ - if (_fileid >= CNF_GENERAL) - { - TToken_string s(_name, '.'); - TConfig c(_fileid - CNF_STUDIO, s.get(0)); - buffer = c.get(s.get()); - } - else - { - if (_from > 0 || _to > 0 || _name.find(':') > 0) - { - const TRecfield rf((TRectype&)rec, _name, _from, _to-1); - if (rf.type() == _memofld && _to > 0) - buffer = rec.get(_name).sub(_from, _to); - else - buffer = rf; - } - else - buffer = rec.get(_name); - } - return buffer; -} - -const char* TFieldref::read(const TRelation& r) const -{ - const TRectype& rec = _id.empty() ? r.curr() : r.lfile(_id).curr(); - const char *s = read(rec); - return s; -} - -void TFieldref::write(const char* val, TRelation& r) const -{ - TRectype& rec = _id.empty() ? r.curr() : r.lfile(_id).curr(); - write(val, rec); -} - - -void TFieldref::write(const char* val, TRectype& rec) const -{ - if (_fileid >= CNF_GENERAL) - return; - - if (_name.find(':') > 0) - { - TRecfield rf(rec, _name, _from, _to-1); - rf = val; - } else - if (_from > 0 || _to > 0) - { - buffer = rec.get(_name); - if (_to <= _from) - ((TFieldref*)this)->_to = rec.length(_name); - buffer.overwrite(val, _from, _to - _from); - rec.put(_name, buffer); - } - else - { - if (val && *val) - rec.put(_name, val); - else - rec.zero(_name); - } -} - -int TFieldref::len(const TRectype &rec) const -{ - int len = 0; - if (_to < _from) - { - if (_fileid > 0) - { - const RecDes& recd = prefix().get_recdes(_fileid); - const int p = findfld(&recd, _name); - len = p != FIELDERR ? recd.Fd[p].Len : 0; - } - else - len = rec.length(_name); - } - else - len = _to; - len -= _from; - return len; -} - -/////////////////////////////////////////////////////////// -// TRelation_description -/////////////////////////////////////////////////////////// - - -void TRelation_description::init_files_array() -{ - const TLocalisamfile& f = _rel->file(); - TToken_string s(128); - print_name(s, f); // Logic number - - const char* name = f.name(); - if (f.tab()) - name = TDir::tab_des(name); - else - name = prefix().description(name); - s.add(name); // Description - s.add(" "); // No join - s.add(f.getkey()); // Key - s.add(" | "); // No Alias nor expression - - _files.destroy(); - _files.add(s); // Main file - for (int i = 0; i < _rel->items(); i++) - { - _rel->reldef(i).print_on(s); - _files.add(s); - } -} - - -void TRelation_description::read_rel() -{ - // scan files and build description arrays - init_files_array(); - - _fields.destroy(); - for (int i = 0; i < _files.items(); i++) - { - TToken_string& tt = (TToken_string&)_files[i]; - TFilename descfname; descfname << DESCDIR << "/d"; - - const char* tn = tt.get(0); // Codice del file o tabella - - const int which_file = name2log(tn); // Numero logico del file - - if (tn[0] == '%' || tn[0] == '$') - descfname << (tn+1); - else - descfname << tn; - descfname << ".des"; - - TConfig conf(descfname, DESCPAR); - // new record descriptor for _fields array - TString_array* rdesc = new TString_array; - - TTrec trec; trec.get(which_file); - TToken_string ttmp(64); - TString dfld(256); - - for (int f = 0; f < trec.fields(); f++) - { - ttmp = trec.fielddef(f); - const TString16 name(ttmp.get(0)); - if (!name.blank()) - { - dfld = conf.get(name, NULL, -1, "Missing description"); - if (!dfld.blank() && dfld[0] != '#') - { - ttmp.add(dfld,4); - // contiene: nome campo, tipo, lunghezza, decimali, descrizione - rdesc->add(ttmp); - } - } - } - - _fields.add(rdesc, i); - } -} - -// @doc INTERNAL - -// @mfunc Cambia la relazione descritta -void TRelation_description::change_relation( - TRelation& r, // @parm Nuova relazione da assegnare - TString_array& a) // @parm Array di descrittore dei file -{ - _rel = &r; - read_rel(); - if (a.items() > 0) - _files = a; -} - -void TRelation_description::print_on(ostream& out) const -{ - for (int i = 0; i < _files.items(); i++) - { - TToken_string& r = ((TRelation_description*)this)->_files.row(i); - TString16 cod(r.get(4)); - if (atoi(cod) == 0) - cod = r.get(0); - else - cod << '@'; - out << " " << cod << "->* "; - out << "\"" << r.get(1) << "\"\n"; - } -} - -// @doc INTERNAL - -// @mfunc Seleziona un file -// -// @rdesc Ritorna il risultato dell'operazione: -// -// @flag TRUE | E' stato selzionato il file -// @flag FALSE | Non e' riuscito a selezionare il file -bool TRelation_description::choose_file( - int file) // @parm Numero logico del file da selezionare (default 0) - -// @comm Dopo aver scelto un file (occorre che ritorni TRUE) i metodi della classe -// permettono di conoscere tutti i dati riguardanti i parametri del file selezionato - -{ - TArray_sheet sht(-1,-1,-4,-4,"Selezione archivio", "Codice|Descrizione archivio@70"); - TToken_string tt(80); - - int sel = 0; - - for (int i = 0; i < _files.items(); i++) - { - TToken_string& tf = _files.row(i); - tt = tf.get(4); - - int num = atoi(tt); - if (num == 0) - { - tt = tf.get(0); - num = atoi(tt); - if (sel == 0 && file > 0 && num == file) - sel = i; - } - else - { - if (sel == 0 && file < 0 && num == -file) - sel = i; - tt << '@'; - } - - tt.add(tf.get(1)); - sht.add(tt); - } - - sht.select(sel); - - if (sht.run() == K_ENTER) - { - _cur_file = (int)sht.selected(); - _cfile = _files.row(_cur_file); - return TRUE; - } - return FALSE; -} - -// @doc INTERNAL - -// @mfunc Seleziona un campo

del file selezionato -// -// @rdesc Ritorna il risultato dell'operazione: -// -// @flag TRUE | E' stato selzionato il campo -// @flag FALSE | Non e' riuscito a selezionare il campo -bool TRelation_description::choose_field( - const char* fld) // @parm Nome del campo da selezionare (default "") - -// @comm Occorre che sia selezionato un file, diversamente viene considerato -// selezionato il primo file -{ - TArray_sheet sht(-1,-1,76,20,"Selezione campo", - "Campo@10|Descrizione@50|Tipo@10|Dim."); - TString_array& fd = (TString_array&)_fields[_cur_file]; - TToken_string tt(80); - - int sel = 0; - for (int i = 0; i < fd.items(); i++) - { - TToken_string& tf = fd.row(i); - tt = tf.get(0); - if (sel == 0 && tt == fld) - sel = i; - tt.add(tf.get(4)); - tt.add(field_type_str((TFieldtypes)tf.get_int(1))); - tt.add(tf.get(2)); - sht.add(tt); - } - - sht.select(sel); - if (sht.run() == K_ENTER) - { - _cur_field = (int)sht.selected(); - _cfield = fd.row(_cur_field); - return true; - } - return false; -} - -// @doc INTERNAL - -// @mfunc Costruisce un menu -// -// @rdesc Ritorna solamente FALSE -bool TRelation_description::build_menu( - const char* title) // @parm Titolo del menu' - -// @comm Costruisce un albero di menu' e setta il campo corrente quando viene -// selezionato (deve essere popup) -// -// @devnote Funzione non implementata. -{ - return false; -} - -bool TRelation_description::remove_menu() -{ - return false; -} - -bool TRelation_description::set_cur_file(int id) -{ - const int n = _rel->log2ind(id); - if (n >= 0 && n != _cur_file) - { - _cur_file = n; - _cfile = _files.row(n); - } - return n >= 0; -} - -int TRelation_description::file_num() -{ - int n = -_cfile.get_int(4); - if (n == 0) - n = _cfile.get_int(0); - return n; -} - -const char* TRelation_description::file_desc() -{ - return _cfile.get(1); -} - -void TRelation_description::file_desc(const char* desc) -{ - _cfile.add(desc, 1); - _files.row(_cur_file) = _cfile; -} - - -const char* TRelation_description::field_desc() -{ - return _cfield.get(4); -} - -const char* TRelation_description::field_name() -{ - return _cfield.get(0); -} - -int TRelation_description::field_len() -{ - return _cfield.get_int(2); -} - -TFieldtypes TRelation_description::field_type() -{ - return (TFieldtypes)_cfield.get_int(1); -} - -// @doc INTERNAL - -// @mfunc Setta la descrizione del campo -// -// @rdesc Ritorna se ha trovato il campo da settare -bool TRelation_description::set_field_description( - const char* field, // @parm Campo a cui assegnare la descrizione - const char* des) // @parm Descrizione da assegnare al campo -{ - TString_array& fld = (TString_array&)_fields[_cur_file]; - - for (int i = 0; i < fld.items(); i++) - { - TToken_string& tt = fld.row(i); - if (strcmp(tt.get(0),field) == 0) - { - if (des && *des) - tt.add(des, 4); - else - { - fld.destroy(i); - fld.pack(); - } - return TRUE; - } - } - - if (des && *des) - { - TToken_string tt(80); - tt.add(field); - tt.add(des, 4); - fld.add(tt); - return TRUE; - } - - return FALSE; -} - -const char* TRelation_description::get_field_description(const char* field) -{ - TString_array& fld = (TString_array&)_fields[_cur_file]; - - for (int i = fld.items()-1; i >= 0; i--) - { - TToken_string& tt = fld.row(i); - if (strcmp(tt.get(0),field) == 0) - return tt.get(4); - } - return ""; -} - - -TRelation_description::TRelation_description(TRelation& r) -: _rel(&r), _cur_file(0), _cur_field(0), _menu(FALSE), - _cfile(80), _cfield(80) -{ - read_rel(); - if (_files.items() > 0) - { - _cfile = _files.row(0); - _cfield = ""; - } -} - -TRelation_description::~TRelation_description() -{ - if (_menu) remove_menu(); -} - - -//////////////////////////////////// -// TSortedfile -//////////////////////////////////// -// @mfunc Avanza di

record -int TSortedfile::operator +=(const TRecnotype npos) -{ - int err = NOERR; - TRecnotype p = _curs->pos() + npos; - if (p < 0) - { - p = 0; - err = _isbof; - } - if (p >= _curs->items()) - { - err = _iseof; - p = _curs->items(); - } - *_curs = p; - setstatus(err); - return err; -} - // @mfunc Sposta indietro di

record -int TSortedfile::operator -=(const TRecnotype npos) -{ - return operator+=(-npos); -} - // @mfunc Avanza al record successivo -int TSortedfile::operator ++() -{ - return *this+=1; -} - // @mfunc Indietreggia al record precedente -int TSortedfile::operator --() -{ - return *this-=1; -} - -// @mfunc Si posiziona sul primo record del file (vedi ) -int TSortedfile::first(word lockop) -{ - int err = _curs->items() > 0 ? NOERR : _iseof; - _curs->first_item(); - setstatus(err); - return err; -} -// @mfunc Si posiziona sull'ultimo record del file (vedi ) -int TSortedfile::last(word lockop) -{ - int err = _curs->items() > 0 ? NOERR : _iseof; - _curs->last_item(); - setstatus(err); - return err; -} -// @mfunc Si posiziona sul successivo record del file (vedi ) -int TSortedfile::next(word lockop ) -{ - int err = _curs->pos() < _curs->items() ? NOERR : _iseof; - if (err == NOERR) - _curs->succ_item(); - setstatus(err); - return err; -} -// @mfunc Si posiziona sul precedente record del file (vedi ) -int TSortedfile::prev(word lockop ) -{ - int err = _curs->pos() > 0 ? NOERR : _isbof; - if (err == NOERR) - _curs->pred_item(); - setstatus(err); - return err; -} - -// @mfunc Salta

record dalla posizione corrente (vedi ) -int TSortedfile::skip(TRecnotype nrec, word lockop ) -{ - *_curs += nrec; // manca il lock... - setstatus(_curs->file().status()); - NFCHECK("Operazione 'skip' non consentita sul cursore"); - return 0; -} - -// @mfunc Rilegge l'ultimo record letto (vedi ) -int TSortedfile::reread(word lockop ) -{ - return reread(curr(),lockop); -} -// @mfunc Rilegge l'ultimo record letto e lo copia in

(vedi ) -int TSortedfile::reread(TRectype& rec, word lockop) -{ - return 0; -} - -// @mfunc Legge il record (vedi e ) -int TSortedfile::read(word op , word lockop ) -{ - return read(curr(),op,lockop); -} - -// @mfunc Legge il record e lo copia in

(vedi e ) -int TSortedfile::read(TRectype& rec, word op , word lockop ) -{ - if (&curr() != &rec) - curr() = rec; - _curs->read((TIsamop)op,(TReclock)lockop); - setstatus(_curs->file().status()); - if (&curr() != &rec) - rec = curr(); - return status(); -} - -// @mfunc Legge il record alla posizione

e lo copia in

(vedi ) -int TSortedfile::readat(TRectype& rec, TRecnotype nrec, word lockop ) -{ - // read(rec,_isequal,lockop); - // !!!!!!!!!!!!!! attenzione !!!!!!!!!!!!!! - // non usare un sorted file come file principale di un sorted cursor; - // portare invece le espressioni del sorted file a livello di cursore - // altrimenti questa readat non sempre funziona , perche' viene chiamata con - // parametri presi dalle funzioni a basso livello sugli isam, quindi relativi alle posizioni fisiche - // e non a quanto restituito da recno() (che da' la posizione all'interno del cursore) - *_curs=nrec; - if (&curr() != &rec) - rec=curr(); - setstatus(_curs->file().status()); - return status(); -} -// @mfunc Legge il record alla posizione

e lo copia in

(vedi ) -int TSortedfile::readat(TRecnotype nrec, word lockop ) -{ - return readat(curr(),nrec, lockop ); -} - -// @mfunc Aggiunge un record -int TSortedfile::write() -{ - return write(curr()); -} -// @mfunc Aggiunge un record copiando da

- int TSortedfile::write(const TRectype& rec) - { - NFCHECK("Operazione 'write' non consentita sul TSortedfile"); - return 0; - } - -// @mfunc Riscrive un record - int TSortedfile::rewrite() - { - rewrite(curr()); - return 0; - } -// @mfunc Riscrive un record (il record

) - int TSortedfile::rewrite(const TRectype& rec) - { - NFCHECK("Operazione 'rewrite' non consentita sul TSortedfile"); - return 0; - } - -// @mfunc Riscrive un record alla posizione

copiando da

-int TSortedfile::rewriteat(TRecnotype nrec) -{ - return rewriteat(curr(),nrec); -} -// @mfunc Riscrive un record alla posizione

copiando da

-int TSortedfile::rewriteat(const TRectype& rec, TRecnotype nrec) -{ - NFCHECK("Operazione 'rewriteat' non consentita sul TSortedfile"); - return 0; -} - -// @mfunc Elimina il record -int TSortedfile::remove() -{ - return remove(curr()); -} -// @mfunc Elimina il record copiando da

-int TSortedfile::remove(const TRectype& rec) -{ - NFCHECK("Operazione 'remove' non consentita sul TSortedfile"); - return 0; -} - -// @mfunc Attiva la chiave

sul file aperto -void TSortedfile::setkey(int nkey) -{ - _curs->setkey(nkey); -} -// @mfunc Resetta la regione del file (chiama

-void TSortedfile::setregion(const TRectype &f, const TRectype &t, int tilde) -{ - _curs->setregion(f, t, tilde); -} - - // @mfunc -TRecnotype TSortedfile::eod() const -{ - return _curs->items(); -} - // @mfunc -bool TSortedfile::empty() -{ - return _curs->items()==0; -} - -TRectype& TSortedfile::curr() const -{ - return _curs->curr(); -} - -void TSortedfile::set_curr(TRectype* curr) -{ - _curs->file().set_curr(curr); -} - -// @mfunc Costruttore. -TSortedfile::TSortedfile(int logicnum, TRelation* rel, const char* ordexpr, const char* filter, int nkey) - : TLocalisamfile(logicnum), _rel(NULL) -{ - // costruisce il cursore - if (!rel) - _rel = new TRelation(logicnum); - else - _rel = rel; - _curs = new TSorted_cursor(_rel, ordexpr, "", nkey); - - if (&curr()!=&(_curs->file().curr())) - _curs->file().set_curr(&curr()); - - _curs->setfilter(filter,TRUE); //must pass TRUE because cursors doesn't update rel (BUG). - // DON't move this line BEFORE set_curr : filter's fieldrefs are allocated at cursor setfilter -} - -// @mfunc Distruttore -TSortedfile::~TSortedfile() -{ - delete _curs; - if (_rel) - delete _rel; -} - -// -// *** EOF relation.cpp - +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +// *** check if not already defined +#define NOTFOUND (-1) + +HIDDEN void print_name(ostream& out, const TLocalisamfile& f) +{ + switch (f.num()) + { + case LF_TABCOM: out << '%'; break; + case LF_TABMOD: out << '&'; break; + default: break; + } + out << f.name(); +} + +HIDDEN void print_name(TToken_string& out, const TLocalisamfile& f) +{ + switch (f.num()) + { + case LF_TABCOM: out << '%'; break; + case LF_TABMOD: out << '&'; break; + default: break; + } + out << f.name(); +} + + +extern int get_error(int); + +HIDDEN const char* field_type_str(TFieldtypes f) +{ + const char* c = ""; + switch(f) + { + case _charfld: + c = "Carattere"; break; + case _realfld: + c = "Reale"; break; + case _datefld: + c = "Data"; break; + case _boolfld: + c = "Logico"; break; + // case _wordfld: + // case _intzerofld: + // case _longzerofld: + // case _intfld: + // case _longfld: + default: + c = "Intero"; break; + } + return c; +} + +/////////////////////////////////////////////////////////// +// TRelationdef +/////////////////////////////////////////////////////////// + +class TRelationdef : public TObject +{ + friend class TRelation; + + const TRelation* _rel; // Relazione padre + int _num; // Posizione file + int _numto; // Posizione padre + int _alias; // Alias + byte _key; // Chiave + TArray _fields; // Campi di join + TArray _exprs; // Condizioni di uguaglianza + TArray _altexprs; // Condizioni di uguaglianza alternative + TBit_array _forced; + bool _first_match : 1; // primo match (ed esiste) + bool _allow_lock : 1; // ?? + bool _write_enable : 1; + +public: // TObject + virtual void print_on(ostream& out) const; + +public: + int num() const { return _num; } + int link() const { return _numto; } + int alias() const { return _alias; } + bool allow_lock() const { return _allow_lock; } + bool write_enable() const { return _write_enable; } + void write_enable(bool we) { _write_enable = we; } + TRectype& load_rec(TRectype& r, const TRectype& from) const; + const char* evaluate_expr(int j, const TLocalisamfile& to) const; + + void print_on(TToken_string& out) const; + + TRelationdef(const TRelation* rel, int file, byte key, + int linkto, const char* relexprs, int alias, + bool allow_lock); + virtual ~TRelationdef() {} +}; + + +TRelationdef::TRelationdef(const TRelation* rel, int idx_file, byte key, + int idx_to, const char* relexprs, int alias, + bool allow_lock) +: _rel(rel), _num(idx_file), _numto(idx_to), + _alias(alias), _key(key), _fields(4), _exprs(4), + _first_match(FALSE), _allow_lock(allow_lock), + _write_enable(FALSE) +{ + TToken_string rels(relexprs); + int i = 0; + TString80 r,s; + + for (const char* g = rels.get(); g; g = rels.get(), i++) + { + r = g; + int eq = r.find('='); + + CHECKS(eq > 0, "Ahoo! E l'uguale 'ndo sta? ", (const char*)g); + + s = r.left(eq); // Parte a sinistra dell' = + +#ifdef DBG + const char* n = s; + const int p = s.find('['); + if (p > 0) n = s.left(p); + if (rel->file(_num).curr().exist(n) == FALSE) + { + yesnofatal_box("Errore di JOIN: '%s' non e' un campo del file %d", + n, rel->file(_num).num()); + continue; + } +#endif + + if (r[eq+1] == '=') + { + _forced.set(i); + eq++; + } + + _fields.add(new TFieldref(s, 0)); + + s = r.mid(eq+1); + const int par = s.find('('); + if (par > 0) + { + _exprs.add(new TExpression(s.left(par), _strexpr), i); + _altexprs.add(new TExpression(s.sub(par+1, s.len()-1), _strexpr), i); + } + else + _exprs.add(new TExpression(s, _strexpr), i); + } +} + + +void TRelationdef::print_on(ostream& out) const +{ + const TLocalisamfile& f = _rel->file(_num); + + out << "JOIN "; + print_name(out, f); + + if (_numto > 0) + { + out << " TO "; + const int alias = _rel->reldef(_numto-1).alias(); + if (alias > 0) out << alias << '@'; + else + { + const TLocalisamfile& t = _rel->file(_numto); + print_name(out, t); + } + } + + if (_key > 1) out << " KEY " << (int)_key; + + if (_alias > 0) out << " ALIAS " << _alias; + + for (int i = 0; i < _fields.items(); i++) + { + if (i == 0) out << " INTO"; + out << ' ' << _fields[i] << '='; + if (_forced[i]) out << '='; + out << _exprs[i]; + if (_altexprs.objptr(i)) + out << '(' << _altexprs[i] << ')'; + } +} + + +void TRelationdef::print_on(TToken_string& out) const +{ + const TLocalisamfile& f = _rel->file(_num); + + out.cut(0); + print_name(out, f); + + // add description + const char* name = f.name(); + if (f.tab()) + name = TDir::tab_des(name); + else + name = prefix().description(name); + out.add(name); + + out << '|'; + + if (_numto > 0) + { + const int alias = _rel->reldef(_numto-1).alias(); + if (alias > 0) + out << alias << '@'; + else + { + const TLocalisamfile& t = _rel->file(_numto); + print_name(out, t); + } + } else out << ' '; + + out.add(_key); + out.add(_alias); + out << '|'; + for (int i = 0; i < _fields.items(); i++) + { + if (i) out << ' '; + out << _fields[i] << '='; + if (_forced[i]) out << '='; + out << _exprs[i]; + if (_altexprs.objptr(i)) + out << '(' << _altexprs[i] << ')'; + } +} + + +const char* TRelationdef::evaluate_expr(int j, const TLocalisamfile& to) const +{ + const TRectype& rec = to.curr(); + TExpression& expr = (TExpression&)_exprs[j]; + for (int k = 0; k < expr.numvar(); k++) + { + if (expr.varname(k)[0] != '#') + { + const TFieldref fr(expr.varname(k), rec.num()); + expr.setvar(k, fr.read(rec)); + } + } + + const char* val = (const char*)expr.as_string(); + if (*val == '\0' && _altexprs.objptr(j)) + { + TExpression& altexpr = (TExpression&)_altexprs[j]; + for (int k = 0; k < altexpr.numvar(); k++) + { + if (altexpr.varname(k)[0] != '#') + { + const TFieldref fr(altexpr.varname(k), rec.num()); + altexpr.setvar(k, fr.read(rec)); + } + } + val = (const char*)altexpr.as_string(); + } + + return val; +} + +/////////////////////////////////////////////////////////// +// TRelation +/////////////////////////////////////////////////////////// + +TRelation::TRelation(int logicnum) +: _files(4), _reldefs(4), _errors(NOERR) +{ + TLocalisamfile* f = new TLocalisamfile(logicnum); + _files.add(f); +} + +TRelation::TRelation(const char* tabname) + : _files(4), _reldefs(4), _errors(NOERR) +{ + TLocalisamfile* t = NULL; + if (tabname[0] == '&') + t = new TModule_table(tabname); + else + t = new TTable(tabname); + _files.add(t); +} + +TRelation::TRelation(TLocalisamfile* l) + : _files(4), _reldefs(4), _errors(NOERR) +{ _files.add(l); } + +TRelation::~TRelation() +{} + +void TRelation::print_on(ostream& out) const +{ + const TLocalisamfile& f = file(); + + out << "USE "; print_name(out, f); + + const int k = f.getkey(); + if (k > 1) out << " KEY " << k; + out << endl; + + for (int r = 0; r < _reldefs.items(); r++) + out << _reldefs[r] << endl; +} + +void TRelation::restore_status() +{ + int i; + for (i = _files.last(); i >= 0 ; i--) + { + const int err = _status.get_int(i*3); + const TRecnotype recno = _status.get_long(); + const int key = _status.get_int(); + + file(i).setkey(key); + if (recno >= 0l) file(i).readat(recno); + else file(i).zero(); + file(i).setstatus(err); + } + _status.get(_files.items()*3 - 1); // mi riposiziono prima di tutti i first_match + for (i = 0; i < _reldefs.items(); i++) + { + const bool first_match = _status.get_int() ? TRUE : FALSE; + reldef(i)._first_match = first_match; + } +} + +void TRelation::save_status() +{ + _status.cut(0); + int i; + for (i = 0; i < _files.items(); i++) + { + const int err = file(i).status(); + const TRecnotype recno = file(i).eof() ? -1l : file(i).recno(); + const int key = file(i).getkey(); + _status.add (err); + _status.add (recno); + _status.add (key); + } + for (i = 0; i < _reldefs.items(); i++) + { + const bool first_match = reldef(i)._first_match; + _status.add(first_match); + } +} + +// @doc EXTERNAL + +// @mfunc Ritorna l'indice di

del numero logico passato +// +// @rdesc Ritorna l'indice oppure NOTFOUND nel caso non sia presente +int TRelation::log2ind( + int log) const // @parm Numero logico del file di cui conoscere l'indice + +// @comm Nel caso

sia minore di 0 chiama la +// +// @xref +{ + // returns _files index of logical number or NOTFOUND if not present + + if (log <= 0) + return alias2ind(-log); + + const int nf = _files.items(); + for (int i = 0; i < nf; i++) + if (file(i).num() == log) + return i; + + return NOTFOUND; +} + +// @doc EXTERNAL + +// @mfunc Ritorna l'indice di

del alias del file passato +// +// @rdesc Ritorna l'indice oppure NOTFOUND nel caso non sia presente +int TRelation::alias2ind( + int alias) const // @parm Alias del file di cui conoscere l'indice + + +// @xref +{ + if (alias <= 0) return 0; + + for (int i = 0; i < _reldefs.items(); i++) + { + const TRelationdef& r = (const TRelationdef&)_reldefs[i]; + if (r.alias() == alias) return r.num(); + } + + return NOTFOUND; +} + +// @doc EXTERNAL + +// @mfunc Ritorna l'indice di

del nome del file passato +// +// @rdesc Ritorna l'indice oppure NOTFOUND nel caso non sia presente +int TRelation::name2ind( + const char* name) const // @parm Nome del file di cui conoscere l'indice + +// @xref +{ + const int num = name2log(name); + const int ind = log2ind(num); + return ind; +} + +// @doc EXTERNAL + +// @mfunc Ritorna il descrittore del file +// +// @rdesc Ritorna il reference ad un che indica il descrittore del +// file della relazione +TLocalisamfile& TRelation::lfile( + int logicnum) const // @parm Numero logico del file da ritornare (default 0) + // @parm const char* | name | Nome del file da ritornare +// @syntax TLocalisamfile& lfile(int logicnum) +// @syntax TLocalisamfile& lfile(const char* name) +// +// @comm E' comodo utilizzare anche l'operatore [] che richiama questa funzione con +// la prima sintassi. + +{ + const int idx = log2ind(logicnum); + CHECKD(idx != NOTFOUND, "Relation file not found n. ", logicnum); + return (TLocalisamfile&)_files[idx]; +} + +TLocalisamfile& TRelation::lfile(const char* name) const +{ + const int idx = name2ind(name); + CHECKS(idx != NOTFOUND, "File or Table not found:", name); + return (TLocalisamfile&)_files[idx]; +} + +bool TRelation::has_children(int first) const +{ + for (int i = first; i < _reldefs.items(); i++) + { + const TRelationdef& ref = reldef(i); + if (ref._numto == first) + return true; + } + return false; +} + +// @doc EXTERNAL + +// @mfunc Abilita/disabilita la scrittura sul file +void TRelation::write_enable( + int logicnum, // @parm Numero logico del file da abilitare/disabilitare (default 0) + // @parm cont char* | name | Nome del file da abilitare/disabilitare + const bool on) // @parm Indica l'operazione da effettuare sul file: + // + // @flag TRUE | Abilita la scrittura sul file (default) + // @flag FALSE | Disabilita la scrittura sul file + +// @syntax void write_enable(int logicnum, const bool on) +// @syntax void write_enable(const char* name, const bool on) +// +// @comm Nel caso venga passato un numero logico uguale a 0 vengono abilitati/disabilitati tutti +// i file della relazione + + +{ + if (logicnum == 0) + { + for (int i = 0; i < _reldefs.items(); i++) + reldef(i).write_enable(on); + } + else + { + const int idx = log2ind(logicnum); + CHECKD(idx > 0, "File not found n. ", logicnum); + reldef(idx-1).write_enable(on); + } +} + +void TRelation::write_enable(const char* name, const bool on) + +{ + const int idx = name2ind(name); + CHECKS(idx > 0, "File or Table not found:", name); + reldef(idx-1).write_enable(on); +} + + +// @doc EXTERNAL + +// @mfunc Aggiunge una nuovo file nella relazione +// +// @rdesc Ritorna se e' riuscito ad aggiungere il file alla relazione +bool TRelation::add( + TLocalisamfile* f, // @parm Descrittore del file da aggiungere + const char* relexprs, // @parm Espressione della relazione + int key, // @parm Chiave del file + int linkto, // @parm Posizione alla quale aggiungere il file + int alias, // @parm Alias da dare al file da aggiungere + bool allow_lock) // @parm Indica se fare il lock sul file oppure ignorarli + // @parm int | logicnum | Numero logico del file da aggiungere + // @parm const char* | tabname | Nome della tabella da aggiungere + +// @syntax bool add(TLocalisamfile* f, const char* relexprs, int key, int linkto, int alias, bool allow_lock); +// @syntax bool add(int logicnum, const char* relexprs, int key, int linkto, int alias, bool allow_lock); +// @syntax bool add(const char* tabname, const char* relexprs, int key, int linkto, int alias, bool allow_lock); + +// @comm Il parametro

puo' assumere i valori: +// +// @flag 0 | Per il file principale +// @flag 0 | Indica il numero logico del file +// @flag 0 | Indica l'alias del file + + +{ + const int idxto = log2ind(linkto); + if (idxto == NOTFOUND) + fatal_box("Can't join file %d to %d", f->num(), linkto); + + const int idx = _files.add(f); + + CHECK(relexprs && *relexprs, "Mancano le espressioni di collegamento"); + + TRelationdef* r = new TRelationdef(this, idx, key, idxto, + relexprs, alias, allow_lock); + _reldefs.add(r); + + return TRUE; +} + + +bool TRelation::add(int logicnum, const char* relexprs, int key, + int linkto, int alias, bool allow_lock) +{ + TLocalisamfile* f = new TLocalisamfile(logicnum); + return add(f, relexprs, key, linkto, alias, allow_lock); +} + +bool TRelation::add(const char* tabname, const char* relexprs, int key, + int linkto, int alias, bool allow_lock) +{ + TLocalisamfile* t = NULL; + if (*tabname == '&') + t = new TModule_table(tabname); + else + t = new TTable(tabname); + return add(t, relexprs, key, linkto, alias, allow_lock); +} + +// @doc EXTERNAL + +// @mfunc Sostituisce nella relazione un file +void TRelation::replace( + TLocalisamfile* f, // @parm Descrittore del file sostituto + int index, // @parm Posizione nel quale sostituire il file (default 0) + const char* relexprs, // @parm Nuova Espressione della relazione + int key) // @parm Nuova Chiave del file + +{ + const TLocalisamfile* p = (const TLocalisamfile*)_files.objptr(index); + CHECK(p && p->num() == f->num(), "Can't replace a file with different logic number"); + _files.add(f, index); + if (relexprs && *relexprs) + { + TRelationdef* oldr=(TRelationdef*)_reldefs.objptr(index-1); + TRelationdef* r = new TRelationdef(this, index, key, oldr->link(), + relexprs, oldr->alias(), oldr->allow_lock()); + _reldefs.add(r,index-1); + } +} + +// @mfunc Sostituisce nella relazione un file +void TRelation::replacef( + TLocalisamfile* f, // @parm Descrittore del file sostituto + int lognum, // @parm Numero logico o alias + const char* relexprs, // @parm Nuova Espressione della relazione + int key) // @parm Nuova Chiave del file + +{ + int index=0; + if (lognum < 0) index=alias2ind(lognum); + if (lognum > 0) index=log2ind(lognum); + replace(f,index,relexprs,key); +} + + +TRectype& TRelationdef::load_rec(TRectype& r, const TRectype& from) const +{ + r.zero(); + TString80 val; // Fisso la lunghezza massima ad 80, devono essere campi chiave! + for (int j = 0 ; j < _fields.items(); j++) // for each field + { + const TFieldref& s = (const TFieldref&) _fields[j]; + val = s.read(from); + s.write(val, r); + } + + return r; +} + + +void TRelation::zero() +{ + for (int i = 0; i < _files.items(); i++) + file(i).zero(); +} + +// @doc EXTERNAL + +// @mfunc Permette di posizionare l'albero di relazioni tra i file +// +// @rdesc Ritorna il numero di errore avvenuto nell'opeerazione, NOERR nel caso +// venga eseguito tutto correttamente +int TRelation::position_rels( + TIsamop op, // @parm Operatore che esegue la relazione (default _isequal) + TReclock lockop, // @parm Tipo di lock sul file (default _nolock) + int first) // @parm Numero del file da cui costruire la relazione (default + // 0, cioe' dal file principale) + +// @comm Viene eseguito tutto il lavoro di creazione delle relazione: se non +// trova un record adatto su un file, svuota il record corrente e non +// ritorna errore. +// Le altre funzioni sul record procedono normalmente + +{ + TString expr(80); // Stringa di lavoro per la valutazione delle espressioni + + _errors = NOERR; + + const int primo = first < 0 ? 0 : first; + const int ultimo = first < 0 && (-first) < _reldefs.items() ? -first : _reldefs.items(); + + // workhorse: position files for each active relation + for (int i = primo; i < ultimo; i++) + { + TRelationdef& rd = reldef(i); + + if (primo > 0 && rd.link() < primo) + continue; // Inutile spostare file collegati a record precedenti + + TLocalisamfile& from = file(rd.num()); + TLocalisamfile& to = file(rd.link()); + from.zero(); // Azzera il record corrente (tutti se TSortedfile) + if (to.curr().empty()) + continue; + + from.setkey(rd._key); + + // build record + TRectype& furr = from.curr(); + for (int j = 0; j < rd._fields.items(); j++) // for each field + { + expr = rd.evaluate_expr(j, to); + const TFieldref& s = (const TFieldref&) rd._fields[j]; + s.write(expr, furr); + } // for each field + + const TReclock lck = rd._allow_lock ? lockop : _nolock; + from.read(_isgteq, lck); + + // record read : if not found, zero current record + bool eq = !from.bad(); + for (int kk = 0; eq && kk < rd._fields.items(); kk++) + { + TFieldref& fl = (TFieldref&)rd._fields[kk]; + TString80 f_fr(fl.read(furr)); + TString80 f_ex(rd.evaluate_expr(kk, to)); + if (rd._forced[kk]) + eq = f_fr == f_ex; + else + { + f_fr.rtrim(); f_ex.rtrim(); + eq = f_fr.compare(f_ex, f_ex.len()) == 0; + + // Becca anche 000001=1 + if (!eq && real::is_natural(f_fr) && real::is_natural(f_ex)) + eq = atol(f_fr) == atol(f_ex); + } + } + rd._first_match = eq; + if (eq) + from.setstatus(NOERR); + else + furr.zero(); + } // for each relation + return _errors; +} + + +// @doc EXTERNAL + +// @mfunc Posiziona il numero logico sul record successivo +// +// @rdesc Ritorna se ha trovato la corrispondenza: +// +// @flag TRUE | Corrispondenza trovata +// @flag FALSE | Corrispondenza non trovata +bool TRelation::next_match( + int logicnum, // @parm Numero logico del file da fare avanzare + const char* fieldlist, // @parm Lista dei campi che devono rimanere costanti (default NULL) + int nkey) // @parm Numero della chiave (default 0) + +// @comm Il posizionamento del numero logico non deve essere riferita al file +// principale; in ogni caso viene considerata consistente ad eccezione dei +// casi di inconsistenza nella prima posizione. + +{ + if (logicnum == file().num()) + { + next(); + return file().good(); + } + + const int i = log2ind(logicnum); + CHECKD(i != NOTFOUND,"Nonexistent file referenced in relation ",logicnum); + + int j; + for (j = 0; j < _reldefs.items(); j++) + if (reldef(j).num() == i) break; + + TLocalisamfile& from = file(i); + + reldef(j)._first_match = FALSE; + + if (from.bad()) + return FALSE; // && vaffanculo(); + + const TRecnotype last = from.recno(); + + bool ok = TRUE; // Corrispondenza trovata ? + + if (fieldlist == NULL) + { + TRectype rec(from.num()); + reldef(j).load_rec(rec, from.curr()); + + from.setkey(reldef(j)._key); + from.next(); + ok = (from.good() && from.curr() == rec); + for (int kk = 0; ok && kk < reldef(j)._fields.items(); kk++) + { + if (reldef(j)._forced[kk]) + { + TFieldref& fl = (TFieldref&)reldef(j)._fields[kk]; + const TString f_fr(fl.read(from.curr())); + const TString f_to(fl.read(rec)); + + ok = (f_fr == f_to); + } + } + if (ok) from.setstatus(NOERR); + } + else + { + if (nkey > 0) + from.setkey(nkey); + TToken_string fields(fieldlist); + TToken_string old(80); + const char * f; + for (f = fields.get(0); f; f = fields.get()) + old.add(from.curr().get(f)); + + ok = from.next() == NOERR; + + old.restart(); + + for (f = fields.get(0); f && ok; f = fields.get()) + { + const TFixed_string v(from.curr().get(f)); + const char* o = old.get(); + if (v != o) + ok = false; + } + } + + if (!ok) + { + from.readat(last); + return FALSE; + } + + // Riposiziona gli eventuali files successivi + position_rels(_isequal, _nolock, j+1); + reldef(j)._first_match = FALSE; + + return ok; +} + + +// @doc EXTERNAL + +// @mfunc Controlla se c'e' un record ed e' il primo match (non si e' mai fatta +// ) +// +// @rdesc Ritorna TRUE se c'e' un record ed e' il primo match +bool TRelation::is_first_match( + int logicnum) // @parm Numero logico del file da controllare +{ + const int i = log2ind(logicnum); + CHECKD(i != NOTFOUND,"Nonexistent file referenced in relation ",logicnum); + + for (int j = 0; j < _reldefs.items(); j++) + { + TRelationdef& rdj = reldef(j); + if (rdj.num() == i) + return rdj._first_match; + } + const int err = file().status(); + return err == NOERR; +} + + +#ifdef DBG + +// @doc EXTERNAL + +// @mfunc Controlla se la relazione e' corretta +// +// @rdesc Ritorna i seguent valori +// +// @flag TRUE | La relazione soddisfa i criteri di correttezza +// @flag FALSE | La relazione manca di almeno uno di criteri di correttezza +bool TRelation::isconsistent( + bool reset) // @parm Indica se cercare di riportare la relazione ad uno stato + // consistente (default FALSE) + +// @comm Per essere corretta una relazione necessita che tutti i file siano +// corretti, che il record corrente non sia vuoto e che la relazione sia +// consistente +// Se

e' TRUE si cerca di riportare la relazione in uno stato +// consistente (basato sul record principale). Non viene fatto nessun altro +// controllo +// Questa funzione e' chiamata internamente da +// e . + +{ + const int MAXREL = 24; + int bad = 0, i; + TRecnotype recnos[MAXREL]; + + for (i = 0; i < _files.items() && i < MAXREL; i++) + { + // must be file OK, non-empty record + bad |= file(i).good() || file(i).curr().empty(); + recnos[i] = file(i).recno(); + } + + // if the above hold, check consistency + if (bad) + { + _errors = bad; + return FALSE; + } + + position_rels(_isequal, _nolock); + for (i = 0; i < _files.items(); i++) + if (file(i).recno() != recnos[i]) + { + bad = -1; + break; + } + + if (reset == FALSE) + // leave as before + for(i = 0; i < _files.items(); i++) + file(i).readat(recnos[i]); + + return TRUE; +} + +#endif + + +int TRelation::write(bool force) +{ + _errors = file(0).write(); + if (_errors != NOERR) + return _errors; + + for (int i = 0; i < _reldefs.items(); i++) + { + TRelationdef& rd = reldef(i); + TLocalisamfile& lf = file(rd.num()); + + if (!rd.write_enable() || lf.curr().empty()) + continue; + + int res = lf.write(); + if (force && res == _isreinsert) + res = lf.rewrite(); + if (_errors == NOERR) + _errors = res; + } + + return _errors; +} + +int TRelation::rewrite(bool force) +{ + _errors = file(0).rewrite(); // Riscrive testata + if (force && _errors == _iskeynotfound) // Se non la trova ... + _errors = file(0).write(); // ... forza la scrittura + + for (int i = 0; i < _reldefs.items(); i++) + { + TRelationdef& rd = reldef(i); + TLocalisamfile& lf = file(rd.num()); + + if (!rd.write_enable() || lf.curr().empty()) + continue; + + int res = lf.rewrite(); + if (force && (res == _iskeynotfound || res == _iseof || res == _isemptyfile)) + res = lf.write(); + if (_errors == NOERR) + _errors = res; + } + + return _errors; +} + +int TRelation::remove() +{ + const int res = file(0).remove(); + if (_errors == NOERR && res != _iskeynotfound) _errors = res; + for (int i = 0; i < _reldefs.items(); i++) + { + TRelationdef& rd = reldef(i); + const int log = rd.num(); + + if (!rd.write_enable() || + (file(log).curr()).empty()) continue; + + const int res = file(log).remove(); + if (_errors == NOERR && res != _iskeynotfound) _errors = res; + } + return _errors; +} + +bool TRelation::exist(int logicnum) const +{ + const bool lucky = log2ind(logicnum) >= 0; + return lucky; +} + +void TRelation::mask2rel(const TMask & m) +{ + for (int i = 0; i < _reldefs.items(); i++) + { + TRelationdef& rdi = reldef(i); + + for (int j = 0; j < rdi._exprs.items(); j++) + { + TExpression& expr = (TExpression&)rdi._exprs[j]; + for (int k = 0; k < expr.numvar(); k++) + { + const char * name = expr.varname(k); + if (name[0] == '#') + expr.setvar(k, m.focus_field().evaluate_field(name)); + } + if (rdi._altexprs.objptr(j)) + { + TExpression& altexpr = (TExpression&)rdi._altexprs[j]; + for (int k = 0; k < altexpr.numvar(); k++) + { + const char * name = altexpr.varname(k); + if (name[0] == '#') + altexpr.setvar(k, m.focus_field().evaluate_field(name)); + } + } + } + } +} + +/////////////////////////////////////////////////////////// +// TCursor // +/////////////////////////////////////////////////////////// + +HIDDEN bool __evalcondition(const TRelation& /* r */,TExpression* cond, const TArray& frefs) +{ + for (int i = cond->numvar() - 1; i >= 0; i--) + { + const TRecfield* fr = (const TRecfield*)frefs.objptr(i); + if (fr) + cond->setvar(i, (const char*)*fr); + } + return cond->as_bool(); +} + +// @doc EXTERNAL + +static TFilename _last_name; +static FILE* _last_ndx = NULL; +static bool _last_created = FALSE; + +// @mfunc Apre il file di indice +// +// @rdesc Ritorna l'handle del file aperto +FILE* TCursor::open_index( + bool create) // @parm Indica se creare l'indice nel caso manchi (default FALSE) +{ + _last_created = create; + if (_indexname.empty()) + { + TString8 radix; + radix.format("c%d_", file().num()); + _indexname.temp(radix); + } + + if (_indexname != _last_name || create) + { + if (_last_ndx != NULL) + { + fclose(_last_ndx); + _last_ndx = NULL; + } + fopen_s(&_last_ndx, _indexname, create ? "wb" : "rb"); // Secured _last_ndx = fopen(_indexname, create ? "wb" : "rb"); + if (_last_ndx == NULL) + fatal_box("Can't use cursor index for file %d: '%s'\n", + file().num(), (const char*)_indexname); + _last_name = _indexname; + } + return _last_ndx; +} + +void TCursor::close_index(FILE* f) +{ + CHECK(f == _last_ndx, "Bad open/close index sequence"); + if (_last_created) + { + fclose(_last_ndx); + _last_ndx = NULL; + _last_name.cut(0); + _last_created = FALSE; + } +} + +int TCursor::read_page(long page) +{ + _pagefirstpos = page * _cmaxelpage; + CHECKD(_pagefirstpos < _totrec, "Bad cursor page ", page); + + unsigned long elements = _totrec - _pagefirstpos; + if (elements > _cmaxelpage) + elements = _cmaxelpage; + const unsigned long startpos = sizeof(TRecnotype) * _pagefirstpos; + const unsigned long size = sizeof(TRecnotype) * elements; + + FILE * indf = open_index(); + fseek(indf, startpos, SEEK_SET); + if (fread(_page, size, 1, indf) != 1) + fatal_box("Can't read page number %d in cursor n. %d\n", page, file().num()); + + return (int) elements; +} + +bool TCursor::has_simple_filter() const +{ + bool yes = _filterfunction == NULL; + if (yes && _fexpr != NULL && _fexpr->numvar() > 0) + { + const RecDes& recd = curr().rec_des(); // Descrizione del record della testata + const KeyDes& kd = recd.Ky[_nkey-1]; // Elenco dei campi della chiave del cursore + for (int i = _fexpr->numvar()-1; yes && i >= 0; i--) + { + const char * vn = _fexpr->varname(i); + TFieldref f(vn, 0); + yes = f.file() == 0; + if (yes) + { + for (int k = kd.NkFields-1; k >= 0; k--) + { + const int nf = kd.FieldSeq[k] % MaxFields; + const RecFieldDes& rf = recd.Fd[nf]; + yes = kd.FromCh[k] == 255 && f.name() == rf.Name; + if (yes) break; + } + } + } + } + return yes; +} + +TRecnotype TCursor::buildcursor(TRecnotype rp) +{ + _pagefirstpos = 0L; + memset(_page, 0, sizeof(TRecnotype)*_cmaxelpage); + _fpkey.destroy(); + + const int fhnd = file().handle(); + if (DB_reccount(fhnd) == 0) + return 0; + + TRecnotype oldrecno= RECORD_NON_FISICO,ap = 0; + int pagecnt = 0; + FILE* indf = NULL; + + const int l = strlen(to()); + double perc = 1.0; + + if (l > 0 && DB_index_seek(fhnd, to()) >= 0) + perc = DB_pos_perc(fhnd); + + int junk = DB_index_seek(fhnd, from()); + if (junk < 0) junk=get_error(junk); + if (junk == _iseof) return 0; + + perc -= DB_pos_perc(fhnd); + + // DB_index_recno(fhnd); // A cosa cavolo serve? + _pos=-1; + + const bool filtered = has_filter(); + const bool simple_filter = filtered && has_simple_filter(); + + // Barra di progresso + const long items = (long)((double)_if->lfile().items() * perc); + TString msg("Elaborazione "); + TDir d(_if->lfile().num()); + + msg << d.des(); + + TProgress_monitor pi(items, msg); + + while (!DB_index_eof(fhnd)) + { + pi.add_status(); + const char *s0 = DB_index_getkey(fhnd); + if (l && (strncmp(to(), s0, l) < 0)) + break; + const TRecnotype recno = DB_index_recno(fhnd); + if (recno == oldrecno) + break; // means that no more keys are available + oldrecno=recno; + + bool to_be_added = true; + + if (filtered) + { + if (simple_filter) + to_be_added = simple_filtercursor(s0); + else + to_be_added = filtercursor(recno); + } + + if (to_be_added) + { + _page[pagecnt++] = recno; + if (_pos < 0 && recno == rp) + _pos = ap; + ap++; + } + + if (pagecnt == _cmaxelpage) + { + _fpkey.add(new TToken_string(s0)); + pagecnt = 0; + if (indf == NULL) + { + indf = open_index(TRUE); + fseek(indf, 0L, SEEK_SET); + } + fwrite(_page,sizeof(TRecnotype)*_cmaxelpage, 1, indf); + } + DB_index_next(fhnd); + } // while + if (indf != NULL) + { + if (pagecnt > 0) + fwrite(_page,sizeof(TRecnotype)*pagecnt, 1, indf); + close_index(indf); + } + + _pagefirstpos = ap > _cmaxelpage ? -(_cmaxelpage + 1) : 0L ; + + return ap; +} + +bool TCursor::call_filterfunction(TRecnotype recno) const +{ + bool ok = true; + if (_filterfunction) + { + const int handle = file().handle(); + // memorizzo la chiave prima di eventuali spostamenti + const TString256 s0 = DB_index_getkey(handle); + ok = _filterfunction(_if); + // ripristino la chiave dopo eventuali spostamenti + if (recno != DB_index_recno(handle)) + DB_index_go(handle, s0, recno); + } + return ok; +} +bool TCursor::filtercursor(TRecnotype recno) +{ + file().readat(recno); + if (update_relation()) + { + // memorizzo la chiave prima di eventuali spostamenti + const TString256 s0 = DB_index_getkey(file().handle()); + + _if->update(-filter_limit()); + + // ripristino la chiave dopo eventuali spostamenti + const int handle = file().handle(); + if (recno != DB_index_recno(handle)) + DB_index_go(handle, s0, recno); + } + + bool ok = call_filterfunction(recno); + + if (ok && _fexpr) + ok = __evalcondition(*_if, _fexpr, _frefs); + return ok; +} + +bool TCursor::simple_filtercursor(const char* key) const +{ + if (_fexpr == NULL) + return true; + + const RecDes& recd = curr().rec_des(); // Descrizione del record della testata + const KeyDes& kd = recd.Ky[_nkey-1]; // Elenco dei campi della chiave del cursore + + TFieldref f; + for (int i = _fexpr->numvar()-1; i >= 0; i--) + { + f = _fexpr->varname(i); + int offset = 0; + for (int k = 0; k < kd.NkFields; k++) + { + const int nf = kd.FieldSeq[k] % MaxFields; + const RecFieldDes& rf = recd.Fd[nf]; + int len = rf.Len; + if (f.name() == rf.Name) + { + offset += f.from(); + if (f.to() > f.from()) + len = f.to() - f.from(); + else + len -= f.from(); + char* val = (char*)(key+offset); + char oldchar = '\0'; + int l; + + for (l = len; l >= 0; l--) // rtrim + { + if (l == 0 || val[l-1] != ' ') + { + oldchar = val[l]; + val[l] = '\0'; + break; + } + } + switch (rf.TypeF) + { + case _boolfld: + _fexpr->setvar(i, strchr("1STXY", *val)!=NULL ? "X" : " "); + break; + case _intfld: + case _longfld: + { + const char* v; + for (v = val; *v == ' ' || *v == '0'; v++); + _fexpr->setvar(i, v); + } + break; + default: + _fexpr->setvar(i, val); + break; + } + if (oldchar) + val[l] = oldchar; + break; + } + offset += len; + } + } + return _fexpr->as_bool(); +} + +bool TCursor::ok() const +{ + if (file().bad()) + return FALSE; + + const TRectype& rec = file().curr(); + + const TFixed_string key(rec.key(_nkey)); + const TString& kf = from(); + const TString& kt = to(); + + if (file().tab()) + { + if (key.compare(kf, 3) < 0 || (kt.not_empty() && kt.compare(key, 3) < 0)) + return FALSE; + } + else + { + if (key < kf || (kt.not_empty() && kt.compare(key, kt.len()) < 0)) + return FALSE; + } + + const TRecnotype old = file().recno(); + + if (update_relation()) + { + _if->update(); + if (DB_recno(file().handle()) != old) + file().readat(old); + } + + if (call_filterfunction(old) && + (_fexpr ? __evalcondition(*_if, _fexpr, _frefs) : true)) + return true; + + return false; +} + +bool TCursor::changed() +{ + if (_frozen && _lastrec > 0L) + return FALSE; + + const TLocalisamfile& f = file(); + + if (prefix().get_dirtype(f.num()) == _nordir && + _index_firm != prefix().get_codditta()) + return TRUE; + + const int handle = f.handle(); + const TRecnotype eod = DB_reccount(handle); + if (_lastrec != eod || + _lastkrec != DB_changed(handle)) + return TRUE; + + if (!f.curr().valid()) + return TRUE; + + return FALSE; +} + +TRecnotype TCursor::update() +{ + TWait_cursor hourglass; + TLocalisamfile& f = file(); + f.setkey(_nkey); + if (f.curr().empty()) + f.zero(); // Usare sempre l'azzeratore esperto del file non direttamente f.curr().zero() + f.read(_isgteq); + + _totrec = buildcursor(f.recno()); + + const int handle = f.handle(); + const TRecnotype eod = DB_reccount(handle); + + _lastrec = eod; + _index_firm = prefix().get_codditta(); + _lastkrec = DB_changed(handle); + + return _totrec; +} + + +HIDDEN void matildator(const TRectype& oldrec, int key, bool tilde, TString& str) +{ +/* + if (tilde) + { + TRectype rec(oldrec); // Utile duplicazione di record + const RecDes* recd = rec.rec_des(); // Descrizione del record della testata + const KeyDes& kd = recd->Ky[key-1]; // Elenco dei campi della chiave + TString80 val; + for (int i = kd.NkFields-1; i >= 0; i--) + { + const int nf = kd.FieldSeq[i] % MaxFields; + const RecFieldDes& rf = recd->Fd[nf]; + val = rec.get(rf.Name); + if (val.not_empty()) + { + if (rf.TypeF == _alfafld && val.len() < rf.Len) + { + val.left_just(rf.Len, '~'); + rec.put(rf.Name, val); + } + break; + } + } + str = rec.key(key); + } + else + str = oldrec.key(key); + str.replace('~', ' '); +*/ + str = oldrec.key(key); + if (tilde) + { + const RecDes& recd = oldrec.rec_des(); // Descrizione del record della testata + const KeyDes& kd = recd.Ky[key-1]; // Elenco dei campi della chiave + const int len = str.len(); + const int kfields = kd.NkFields; + int foc = 0; + for (int k = 0; k < kfields; k++) + { + const int nf = kd.FieldSeq[k] % MaxFields; + const RecFieldDes& rf = recd.Fd[nf]; + int toc = foc; + if (kd.FromCh[k] == 255) // Non e' stato specificato un range + toc += rf.Len; // Considero l'intera lunghezza del campo + else + toc += kd.ToCh[k] - kd.FromCh[k] + 1; + CHECK(toc > foc, "Invalid addition"); + if (toc >= len) + { + if (str[foc] > ' ' && rf.TypeF == _alfafld) + str.left_just(toc, ' '); + break; + } + foc = toc; + } + } + str.replace('~', ' '); +} + +// @doc EXTERNAL + +// @mfunc Setta il filtro sul cursore +void TCursor::filter( + const char* fil, // @parm Espressione del filtro + const TRectype* from, // @parm Primo record del pacchetto da esaminare + const TRectype* to, // @parm Ultimo record del pacchetto da esaminare + int tilde // @parm Flag per riempire le chiavi di ~ from(0x1) o to(0x2) + ) + +// @comm ATTENZIONE: non e' possibile filtrare un cursore congelato + +{ + TString kf(_keyfrom), kto(_keyto); + const bool filterchanged = (fil != NULL) && (_filter != fil); + + if (from != NULL) + matildator(*from, _nkey, (tilde & 0x1) != 0, kf); + if (to != NULL) + matildator(*to, _nkey, (tilde & 0x2) != 0, kto); + + if (kf[0] <= ' ' || kto[0] <= ' ') + { + switch (file().num()) + { + case LF_TABGEN: + case LF_TABCOM: + case LF_TAB: + if (!real::is_natural(file().name())) // E' veramente una tabella o è un file normale? + { + kf.overwrite(file().name(), 0); + kto.overwrite(file().name(), 0); + }; + break; + case LF_TABMOD: + if (!real::is_natural(file().name())) // E' veramente una tabella di modulo o è un file normale? + { + const TModule_table& t = (TModule_table&)file(); + TString16 k; + k.format("%2s%6ld%3s", t.module(), t.customer(), t.name()); + kf.overwrite(k, 0); + kto.overwrite(k, 0); + } + break; + default: + break; + } + } + + if (filterchanged || (_keyfrom != kf) || (_keyto != kto)) + { + CHECK(!frozen(), "Impossibile filtrare un cursore congelato"); + + _pos = 0; + _totrec = 0; + _lastrec = 0; + + if (filterchanged) + { + _filter = fil; + if (_fexpr) delete _fexpr; + _frefs.destroy(); + TTypeexp type = (_filter.find('"') >= 0) ? _strexpr : _numexpr; + if (_filter.not_empty()) + { + _fexpr = new TExpression(_filter, type); + if (_fexpr->type() == _numexpr) + for (int i = 0 ; i < _fexpr->numvar(); i++) + if (file().curr().type(_fexpr->varname(i)) == _alfafld) + { + _fexpr->set_type(_strexpr); + break; + } + } + else _fexpr = NULL; + if (_fexpr) + { + const int items = _fexpr->numvar(); + for (int i = 0 ; i < items; i++) + { + const char * vn = _fexpr->varname(i); //occhio + if (vn[0] != '#') + { + const TFieldref f(vn, 0); + const TString & id = f.id(); + + if (id.full()) + { + _filter_update = true; + const int file_id = _if->name2ind(id); + if (_filter_limit < file_id) + _filter_limit = file_id; + } + _frefs.add(new TRecfield(_if->curr(f.file()), f.name(), f.from(), f.to()), i); + } + else + NFCHECK("Variabile strana %s", (const char*)vn); + } + } + } + _keyfrom = kf; + _keyto = kto; + } +} + +void TCursor::setkey(int nkey) +{ + if (nkey != _nkey) + { + _lastrec = 0L; + _nkey = nkey; + file().setkey(_nkey); + filter(NULL); + } +} + +int TCursor::test(TIsamop op, TReclock lockop) +{ + TLocalisamfile& curfile = file(); + const TRectype& currec = curfile.curr(); + curfile.setkey(_nkey); + + int err = NOERR; + if (op == _isequal) + { + const TString match(currec.key(_nkey)); + bool trovato = FALSE; + + for (err = curfile.read(op, lockop); + err == NOERR && match == currec.key(_nkey); + err = curfile.next(lockop)) + { + if (ok()) + { + trovato = true; + break; + } + else + { + if (lockop != _nolock) + curfile.reread(_unlock); + } + } + if (!trovato && err == NOERR) + { + err = _iskeynotfound; + curfile.setstatus(err); + } + } + else + { + for (err = curfile.read(op, lockop); + err == NOERR; + err = curfile.next(lockop)) + { + if (ok()) + break; + else + { + //if (((TCursor *)this)->items() == 0) + if (_totrec == 0) + { + err = _isemptyfile; + break; + } + + if (lockop != _nolock) + curfile.reread(_unlock); + + const TString& kto = to(); + if (kto.not_empty()) + { + const TFixed_string curkey(currec.key(_nkey)); + if (kto.compare(curkey, kto.len()) < 0) + { + err = _iseof; + break; + } + } + } + } + } + + return err; +} + +TRecnotype TCursor::read(TIsamop op, TReclock lockop) +{ + TLocalisamfile& curfile = file(); + + if (changed()) + { + const TRectype savecurr = curfile.curr(); + update(); + curfile.curr() = savecurr; + } + + int err = test(op, lockop); + const TRecnotype curpos = curfile.recno(); + + TString match; + if (err == NOERR) + match = curfile.curr().key(_nkey); + + if (changed()) + { + NFCHECK("Com'e' possibile che ci sia nuovamente bisogno di update?"); + update(); + } + + if (err != NOERR) + { + _pos = _totrec - 1; + if (_pos < 0) + { + curfile.zero(); + err = _isemptyfile; + } + else + readrec(); + curfile.setstatus(err); + } + else + { + const int pages = (_totrec-1) / _cmaxelpage + 1; + + _pos = -1; + if (pages > 1) + { +// const TString match(curfile.curr().key(_nkey)); + + bool found = FALSE; + FOR_EACH_ARRAY_ROW(_fpkey, p , s) + { + if (match <= *s) + { + const int pagecnt = read_page(p); + found = true; + for (int i = 0; i < pagecnt && _pos < 0L; i++) + if (_page[i] == curpos) + _pos = _pagefirstpos + i; + } + } + if (!found) + { + const int pagecnt = read_page(pages - 1); + for (int i = 0; i < pagecnt && _pos < 0L; i++) + if (_page[i] == curpos) + _pos = _pagefirstpos + i; + } + } + else + { + for (int i = 0; i < _cmaxelpage && _pos < 0L; i++) + if (_page[i] == curpos) + _pos = i; + } + + if (_pos < 0L) + _pos = 0L; + readrec(); + } + + return _pos; +} + +TCursor::TCursor(TRelation* r, const char* fil, int nkey, + const TRectype *from, const TRectype* to, int tilde) + : _if(r), _nkey(nkey), _fexpr(NULL), _frozen(false), _filter_update(false), + _filterfunction_update(false), _filter_limit(0), _filterfunction(NULL) +{ + CHECK(r, "Null cursor relation"); + file().setkey(_nkey); + _pos = 0; + _totrec = 0; + _lastrec = 0; + _lastkrec = 0; + _pagefirstpos = 0L; + + _page = new TRecnotype[_cmaxelpage]; + memset(_page, 0, _cmaxelpage * sizeof(TRecnotype)); + + filter(fil, from, to, tilde); +} + + +TCursor::~TCursor() +{ + if (_indexname.not_empty()) + { + if (_indexname == _last_name && _last_ndx != NULL) + { + _last_created = TRUE; // Force close + close_index(_last_ndx); + } + ::remove(_indexname); + } + delete _page; + if (_fexpr) + delete _fexpr; +} + + +TRecnotype TCursor::readrec() +{ + TRecnotype nrec = 0L; + TLocalisamfile& f = file(); + if (_pos >= items()) + { + f.setstatus(_iseof); + f.zero(); + return nrec; + } + f.setstatus(NOERR); + + if (_pos < _pagefirstpos || _pos >= _pagefirstpos + _cmaxelpage) + read_page(_pos / _cmaxelpage); + nrec = _page[_pos - _pagefirstpos]; + CHECKD(nrec >= 0, "Bad record position ", nrec); + + curr().setdirty(); + curr().readat(f, nrec); + repos(); + return nrec; +} + +// @doc EXTERNAL + +// @mfunc Mette un lock sul record +// +// @rdesc Ritorna il numero di errore che si verifica nel porre il lock (NOERR) se +// non si verificano errori +int TCursor::lock( + TReclock l) // @parm Tipo di lock da porre sul record (vedi ) +{ + int rt=NOERR; + switch(l) + { + case _lock: + rt=DB_lock_rec(file().handle(),_pos); + break; + case _unlock: + rt=DB_unlock(file().handle()); + break; + default: + break; + } + if (rt != NOERR) rt=get_error(rt); + return(rt); +} + +TRecnotype TCursor::operator =(const TRecnotype pos) +{ + if (changed()) + update(); + CHECKD(pos >= 0 && pos <= _totrec, "Bad cursor position : ", pos); + _pos = pos; + readrec(); + + return _pos; +} + +TRecnotype TCursor::operator +=(const TRecnotype npos) +{ + if (changed()) + update(); + + _pos += npos; + if (_pos > _totrec) + _pos = _totrec; + else + if (_pos < 0) + _pos = 0; + readrec(); + return _pos; +} + +TRecnotype TCursor::items() +{ + if (changed()) + update(); + return _totrec; +} + +bool TCursor::next_match(int lognum, const char* fl, int nk) +{ + if (lognum == 0 || lognum == file().num()) + { + ++(*this); + return file().good(); + } + else + return _if->next_match(lognum, fl, nk); +} + +bool TCursor::is_first_match(int ln) +{ + return (ln == 0 || ln == file().num()) ? + (_pos == 0 && file().good()) : (_if->is_first_match(ln)); +} + +bool TCursor::scan(CURSOR_SCAN_FUNC func, void* pJolly, const char* msg) +{ + TRecnotype tot = 0; // Temporarily + { + TWait_cursor hourglass; + tot = items(); + } + bool ok = true; + if (tot > 0) + { + TProgress_monitor* pi = NULL; + if (tot > 1) + { + if (msg == NULL || *msg == '\0') + msg = TR("Elaborazione in corso..."); + pi = new TProgress_monitor(tot, msg, true); + } + + freeze(true); + for (*this = 0; pos() < tot; ++*this) + { + if (!func(*relation(), pJolly)) + { + ok = false; + break; + } + if (pi != NULL && !pi->add_status()) + { + ok = false; + break; + } + } + freeze(false); + if (pi != NULL) + delete pi; + } + return ok; +} + +/////////////////////////////////////////////////////////// +// TSorted_cursor +/////////////////////////////////////////////////////////// + +typedef struct +{ + char f[256]; + TRecnotype p; +} El_To_Sort; + +// @doc EXTERNAL + +// @mfunc Controlla la validita' dell'espressione +// +// @rdesc Ritorna se si tratta di una espressione valida +bool TSorted_cursor::check_expr( + TString& s) // @parm Espressione da controllare + +// @comm Modifica l'espressione in modo da avere una valido parametro per TFieldref! +// Es. UPPER(20->RAGSOC[1,40]+) ==> 20->RAGSOC[1,40]+ +// N.B.: la direzione dell'ordinamento va messa indicata sempre dopo la definizione del campo, mai +// dopo l'operatore UPPER(). E' a cura delle altre funzioni esterne che utilizzano la check_expr() +// discriminare il segno + o - che sia. + +{ + int p=s.find('('); + int p1=s.find(')'); + bool rt=TRUE; + + if (p>-1 && p1>-1) + if (s.find("UPPER") < 0) + { + error_box("Only UPPER() operator is allowed"); + rt=FALSE; + } + else + { + s=s.mid(6,s.len()-7); + p=s.find('('); + p1=s.find(')'); + if (p>-1 || p1 >-1) + { + error_box("Too many parentheses!"); + rt=FALSE; + } + } + else + if ((p>-1 && p1==-1) || (p==-1 && p1>-1)) + { + error_box("Unbalanced parentheses in expression"); + rt=FALSE; + } + return rt; +} + +bool TSorted_cursor::is_upper(TString& s) +// Controlla se nell'espressione esiste l'operatore UPPER() +{ + bool rt=FALSE; + if (s.find("UPPER") >= 0) + rt = check_expr(s); + return rt; +} + +TRecnotype TSorted_cursor::buildcursor(TRecnotype rp) +{ + TRecnotype oldrecno=0,pos,ap = 0; + int abspos=0,junk, l, pagecnt = 0; + TString s; + FILE* _f = NULL; + + setpagefirstpos(0L); + memset(page(), 0, sizeof(TRecnotype) * pagesize()); + fpkey().destroy(); + + if (file().empty()) + return 0; + + TSort sort(sizeof(El_To_Sort)); + + _order_expr.restart(); + + while ((s=_order_expr.get()).not_empty()) + { + check_expr(s); // Toglie l'eventuale operatore UPPER(), in modo che l'ultimo carattere + // indichi eventualmente la direzione dell'ordinamento + const char last = s.right(1)[0]; + const char versus = (last=='-') ? 'd' : 'a'; + if (last == '-' || last == '+') + s.rtrim(1); + TFieldref f(s,0); + // Il controllo del file e' automatico in f.len() + const TString& id = f.id(); + const TRectype& r = relation()->lfile(id).curr(); + int flen = f.len(r); + if (flen == 0) // Campo virtuale + flen = 50; + + if (id.full()) + { + _sort_update = true; + const int file_id = relation()->name2ind(id); + if (filter_limit() < file_id) + set_filter_limit(file_id); + } + + sort.addsortkey(abspos,flen,versus); + CHECKS(flen!=0,"Field can not have null length: ",(const char *) s); + + abspos += flen; + CHECKD(abspos<=256, "Sort key too long: ", abspos); + } + + const int handle = file().handle(); + + sort.init(); + l = strlen(to()); + double perc = 1.0; + + if (l > 0 && DB_index_seek(handle, to()) >= 0) + perc = DB_pos_perc(handle); + junk=DB_index_seek(handle, (char*)(const char*) from()); + if (junk < 0) junk=get_error(junk); + if (junk == _iseof) return 0; + + pos = DB_index_recno(handle); + TCursor::pos()=-1; + + perc -= DB_pos_perc(handle); + + // Barra di progressione + const long items = (long)((double)relation()->lfile().items() * perc); + TString msg("Elaborazione "); + TDir d(relation()->lfile().num()); + + msg << d.des(); + + TProgress_monitor pi(items, msg); + + while (!DB_index_eof(handle)) + { + pi.add_status(); + const char* s0 = DB_index_getkey(handle); + if (l && (strncmp(to(), s0, l) < 0)) + break; + const TRecnotype recno = DB_index_recno(handle); + if (recno == oldrecno) + break; // means that no more keys are available + + oldrecno=recno; + + // Attenzione: la filtercursor non si si limita a filtrare ma avanza anche il cursore!!!! + const bool to_be_added = filtercursor(recno); + if (to_be_added) + { + El_To_Sort Element; + + TFixed_string f(Element.f, sizeof(Element.f)); + fill_sort_key(f); + Element.p=recno; + sort.sort((char *) &Element); + if (TCursor::pos() < 0 && recno == rp) + TCursor::pos() = ap; + ap++; + } + + long rec = DB_index_next(handle); + if (rec < 0) + fatal_box("Can't read index n. %d - file n. %d",file().getkey(),file().num()); + } // while + + sort.endsort(); + + pagecnt = 0; + El_To_Sort* Element = NULL; + while ((Element=(El_To_Sort *)sort.retrieve()) != NULL) + { + page()[pagecnt++]=Element->p; + if (pagecnt == pagesize()) + { + if (_f == NULL) + { + _f = open_index(TRUE); + fseek(_f, 0L, SEEK_SET); + } + fwrite(page(), sizeof(TRecnotype) * pagesize(), 1, _f); + pagecnt=0; + fpkey().add(new TToken_string(Element->f)); + } + } + if (_f != NULL) + { + if (pagecnt > 0) + fwrite(page(),sizeof(TRecnotype) * pagecnt, 1, _f); + close_index(_f); + } + setpagefirstpos(ap > pagesize() ? -(pagesize() + 1) : 0L) ; + + return ap; +} + +const char* TSorted_cursor::fill_sort_key(TString& k) +{ + TString256 sf; + k.cut(0); + for (TString80 s = _order_expr.get(0); s.not_empty(); s = _order_expr.get()) + { + const bool is_up = is_upper(s); + const char last = s.right(1)[0]; + const bool align = last == '*'; + + if (last == '-' || last == '+' || align) + s.rtrim(1); + + const TFieldref f(s,0); + sf = f.read(*relation()); + const TRectype& frec = curr(f.file()); + TFieldtypes fld_type = frec.type(f.name()); + int fld_len = f.len(frec); + // Converte in ANSI i campi data ed i sottocampi con una data! + if (fld_type == _datefld || (f.is_subfield() && TDate::isdate(sf))) + { + if (sf.not_empty()) // Aggiunta del 17-10-2011 per efficienza stampe mg (ma potrebbe non andar bene!) + { + const TDate d(sf); + sf.format("%08ld", d.date2ansi()); + } + else + sf.spaces(8); + fld_type = _datefld; + fld_len = 8; + } + else + { + if (is_up) // Test inutile: tutte le chiavi sono maiuscole 08-02-2016 + sf.upper(); + } + switch (fld_type) + { + case _boolfld: + case _charfld: + case _memofld: + case _alfafld: sf.left_just(fld_len > 0 ? fld_len : 50); break; + case _intfld: + case _longfld: + case _intzerofld: + case _longzerofld: sf.right_just(fld_len > 0 ? fld_len : 50); break; + case _datefld: break; // Gia' lungo 8! + case _realfld: + if (align) + { + real r(sf); + sf = r.string(fld_len > 0 ? fld_len : 50, fld_len > 0 ? (fld_len-4)/2 : 10); + } + else + sf.right_just(fld_len > 0 ? fld_len : 50); + break; + default : + sf.left_just(fld_len > 0 ? fld_len : 50); + break; + } + k << sf; + } + return k; +} + +bool TSorted_cursor::changed() +{ + bool rt = false; + if (_is_valid_expr) + { + rt = TCursor::changed(); + if (!rt) rt=_is_changed_expr; + _is_changed_expr = false; + } + else + NFCHECK("Can't perform changed() while sorted cursor expression is not valid!"); + return rt; +} + +int TSorted_cursor::test(TIsamop op, TReclock lockop) + +{ + int err = NOERR; + + if (items() == 0L) + { + err = _isemptyfile; + file().setstatus(err); + return err; + } + + TString256 searching; fill_sort_key(searching); + searching.rtrim(); + const int cmplen = searching.len(); + + TRecnotype first = 0L; + TRecnotype last = items()-1; + TRecnotype found = -1L; + + FOR_EACH_ARRAY_ROW(fpkey(), i, s) + { + const int cmp = searching.compare(*s, cmplen); + if (cmp <= 0) + last = (i + 1) * pagesize() - 1; + else + first = i * pagesize(); + } + + const bool ghiacciato = !frozen(); + if (ghiacciato) freeze(true); + + TString256 testing; + while (first <= last) + { + const TRecnotype test = (first+last)/2; + TCursor::operator=(test); // verif + fill_sort_key(testing); + const int cmp = searching.compare(testing, cmplen); + if (cmp == 0) + { + if (op != _isgreat) + { + if (found < 0l || test < found) + found = test; + last = test-1; + } + else + first = test+1; + } + else + { + if (cmp < 0) + { + last = test-1; + if (op != _isequal) + { + if (found < 0l || test < found) + found = test; + } + } + else + first = test+1; + } + } + + if (found >= 0L) + { + TCursor::operator=(found); // verif + file().setstatus(NOERR); + if (lockop != _nolock) + lock(lockop); + } + else + { + + found = items()-1; + if (found >= 0) + TCursor::operator=(found); + err = op == _isequal ? _iskeynotfound : _iseof; + file().setstatus(err); + } + + if (ghiacciato) + freeze(false); + + return err; +} + + +TRecnotype TSorted_cursor::read(TIsamop op, TReclock lockop) +{ + TString256 searching; fill_sort_key(searching); + searching.rtrim(); + const int cmplen = searching.len(); + + TRecnotype first = 0L; + TRecnotype last = items()-1; + TRecnotype found = -1L; + + FOR_EACH_ARRAY_ROW(fpkey(), i, s) + { + const int cmp = searching.compare(*s, cmplen); + if (cmp <= 0) + last = (i + 1) * pagesize() - 1; + else + first = i * pagesize(); + } + + const bool ghiacciato = !frozen(); + if (ghiacciato) freeze(true); + + TString256 testing; + while (first <= last) + { + const TRecnotype test = (first+last)/2; + TCursor::operator=(test); + fill_sort_key(testing); + const int cmp = searching.compare(testing, cmplen); + if (cmp == 0) + { + if (op != _isgreat) + { + if (found < 0l || test < found) + found = test; + last = test-1; + } + else + first = test+1; + } + else + { + if (cmp < 0) + { + last = test-1; + if (op != _isequal) + { + if (found < 0l || test < found) + found = test; + } + } + else + first = test+1; + } + } + + if (found >= 0L) + { + TCursor::operator=(found); + file().setstatus(NOERR); + if (lockop != _nolock) + lock(lockop); + } + else + { + found = items()-1; + if (found >= 0) + TCursor::operator=(found); + file().setstatus(op == _isequal ? _iskeynotfound : _iseof); + } + + if (ghiacciato) + freeze(false); + + return found; +} + +void TSorted_cursor::change_order(const char* order_expr) +{ + _sort_update = false; + if (order_expr && *order_expr && _order_expr != order_expr) + { + TString s; + _order_expr = order_expr; + _order_expr.restart(); + while ((s=_order_expr.get()).not_empty() && (_is_valid_expr=check_expr(s))) ; + if (_is_valid_expr) + _is_changed_expr=true; + } +} + +TSorted_cursor::TSorted_cursor(TRelation *f, const char * order_expr, const char * filter, int key, const TRectype* from, const TRectype* to) + : TCursor(f,filter,key,from,to) +{ + change_order(order_expr); +} + +TSorted_cursor::~TSorted_cursor() +{ +} + +/////////////////////////////////////////////////////////// +// TFieldRef +/////////////////////////////////////////////////////////// + +// @doc EXTERNAL + +// @func Converte una stringa in numero logico o numero tabella +// +// @rdesc Ritorna il numero logico del file o il numero della tabella +int name2log( + const char* name) // @parm Stringa da convertire +{ + int log = 0; + + if (name) + { + while (isspace(*name)) + name++; + + if (*name) + { + log = table2logic(TFixed_string(name)); + + CHECKS(log != 0, "Non e' un file ne' una tabella: ", name); + } + } + return log; +} + + +TFieldref::TFieldref() : _fileid(0), _name(0) {} + + +TFieldref::TFieldref(const TString& s, short defid) +{ + operator=(s); + if (_fileid == 0) _fileid = defid; +} + +void TFieldref::copy(const TFieldref& f) +{ + _fileid = f._fileid; + _id = f._id; + _name = f._name; + _from = f._from; + _to = f._to; +} + +TObject* TFieldref::dup() const +{ + TFieldref* f = new TFieldref(*this); + return f; +} + +// @doc EXTERNAL + +// @mfunc Operatore di assegnamento +// +// @rdesc Ritorna l'oggetto assegnato +TFieldref& TFieldref::operator =( + const TString& s) // @parm Stringa da assegnare all'oggetto + +// @comm Un deve avere il seguente formato (solamente NAME e il mandante): +// FILE->NAME[FROM,TO] + +{ + int pos = s.find("->"); + if (pos > 0) + { + _id = s.left(pos); _id.strip(" "); + _fileid = name2log(_id); + pos += 2; + } + else + { + pos = s.find('.'); + if (pos > 0) + { + _id = s.left(pos); _id.strip(" "); + _fileid = name2log(_id); + pos++; + } + else + { + _id.cut(0); + _fileid = pos = 0; + } + } + + int par = s.find('[', pos); // Cerca la fine del nome del campo + _name = s.sub(pos, par); // Estrae il nome del campo + _name.strip(" "); // Elimina eventuali spazi superflui + + if (par > 0) + { + pos = par+1; + set_from(atoi(s.mid(pos))); + par = s.find(',', pos); + if (par > 0) set_to(atoi(s.mid(par+1))); + else set_to(from()+1); + } + else + { + set_from(0); + set_to(-1); + } + + return *this; +} + +void TFieldref::set_file(int f) +{ + _id.cut(0); + if (f != 0) + { + _fileid = f; + _id << abs(f); + if (f < 0) _id << '@'; + } + else _fileid = 0; +} + +void TFieldref::print_on(ostream& out) const +{ + if (_id.not_empty()) out << _id << "->"; + out << _name; + if (_from > 0 || _to > 0) + { + out << '[' << (_from+1); + out << ',' << _to << ']'; + } +} + +static TString buffer; + +const char* TFieldref::read(TConfig& ini, const char* defpar) const +{ + ini.set_paragraph(_id.empty() ? defpar : (const char *) _id); + + if (!ini.exist(_name)) + return ""; + + buffer = ini.get(_name); + // Gestisce valori tra virgolette + const int l = buffer.len(); + if (l > 1) + { + if ((buffer[0] == '"' || buffer[0] == '\'') && buffer[0] == buffer[l-1]) + { + buffer.rtrim(1); + buffer.ltrim(1); + if (strchr(buffer, '\\')) + buffer = esc(buffer); + } + } + if (_from > 0 || _to > 0) + { + if (_to < l && _to > 0) buffer.cut(_to); + if (_from > 0) buffer.ltrim(_from); + } + return buffer; +} + +static int quotes_needed(const char* val) +{ + int yes = 0; + for ( ; *val; val++) if (strchr("\t\n\r\"' ", *val)) + { + yes = 1; + if (*val < ' ') + { + yes = 2; + break; + } + } + return yes; +} + +static void escapes(TString& val) +{ + for (int i = 0; val[i]; i++) + { + if (val[i] == '\n') + { + val.insert(" ", i); + val[i++] = '\\'; + val[i] = 'n'; + } else + if (val[i] == '\r') + { + val.insert(" ", i); + val[i++] = '\\'; + val[i] = 'r'; + } else + if (val[i] == '\t') + { + val.insert(" ", i); + val[i++] = '\\'; + val[i] = 't'; + } + } +} + +void TFieldref::write(TConfig& ini, const char* defpar, const char* val) const +{ + if ((val == NULL || *val == '\0') && !ini.exist(_name)) + return; + + const char* para = _id.empty() ? defpar : (const char*)_id; + if (_from > 0 || _to > 0) + { + buffer = ini.get(_name, para); + if ((buffer[0] == '"' || buffer[0] == '\'') && buffer[0] == buffer[buffer.len()-1]) + { + buffer.rtrim(1); + buffer.ltrim(1); + if (strchr(buffer, '\\')) + buffer = esc(buffer); + } + buffer.overwrite(val, _from); + val = buffer; + } + const int qn = quotes_needed(val); // Controlla se c'e' bisogno di virgolette + if (qn) + { + if (qn == 2) + { + buffer = val; + escapes(buffer); + buffer.insert("\"", 0); + buffer << '"'; + } + else + { + const char* virg = strchr(val, '"') ? "'" : "\""; + buffer = val; + buffer.insert(virg, 0); + buffer << virg; + } + ini.set(_name, buffer, para); + } + else + ini.set(_name, val, para); +} + +const char* TFieldref::read(const TRectype& rec) const +{ + if (_fileid >= CNF_GENERAL) + { + TToken_string s(_name, '.'); + TConfig c(_fileid - CNF_STUDIO, s.get(0)); + buffer = c.get(s.get()); + } + else + { + if (_from > 0 || _to > 0 || _name.find(':') > 0) + { + const TRecfield rf((TRectype&)rec, _name, _from, _to-1); + if (rf.type() == _memofld && _to > 0) + buffer = rec.get(_name).sub(_from, _to); + else + buffer = rf; + } + else + buffer = rec.get(_name); + } + return buffer; +} + +const char* TFieldref::read(const TRelation& r) const +{ + const TRectype& rec = _id.empty() ? r.curr() : r.lfile(_id).curr(); + const char *s = read(rec); + return s; +} + +void TFieldref::write(const char* val, TRelation& r) const +{ + TRectype& rec = _id.empty() ? r.curr() : r.lfile(_id).curr(); + write(val, rec); +} + + +void TFieldref::write(const char* val, TRectype& rec) const +{ + if (_fileid >= CNF_GENERAL) + return; + + if (_name.find(':') > 0) + { + TRecfield rf(rec, _name, _from, _to-1); + rf = val; + } else + if (_from > 0 || _to > 0) + { + buffer = rec.get(_name); + if (_to <= _from) + ((TFieldref*)this)->_to = rec.length(_name); + buffer.overwrite(val, _from, _to - _from); + rec.put(_name, buffer); + } + else + { + if (val && *val) + rec.put(_name, val); + else + rec.zero(_name); + } +} + +int TFieldref::len(const TRectype &rec) const +{ + int len = 0; + if (_to < _from) + { + if (_fileid > 0) + { + const RecDes& recd = prefix().get_recdes(_fileid); + const int p = findfld(&recd, _name); + len = p != FIELDERR ? recd.Fd[p].Len : 0; + } + else + len = rec.length(_name); + } + else + len = _to; + len -= _from; + return len; +} + +/////////////////////////////////////////////////////////// +// TRelation_description +/////////////////////////////////////////////////////////// + + +void TRelation_description::init_files_array() +{ + const TLocalisamfile& f = _rel->file(); + TToken_string s(128); + print_name(s, f); // Logic number + + const char* name = f.name(); + if (f.tab()) + name = TDir::tab_des(name); + else + name = prefix().description(name); + s.add(name); // Description + s.add(" "); // No join + s.add(f.getkey()); // Key + s.add(" | "); // No Alias nor expression + + _files.destroy(); + _files.add(s); // Main file + for (int i = 0; i < _rel->items(); i++) + { + _rel->reldef(i).print_on(s); + _files.add(s); + } +} + + +void TRelation_description::read_rel() +{ + // scan files and build description arrays + init_files_array(); + + _fields.destroy(); + for (int i = 0; i < _files.items(); i++) + { + TToken_string& tt = (TToken_string&)_files[i]; + TFilename descfname; descfname << DESCDIR << "/d"; + + const char* tn = tt.get(0); // Codice del file o tabella + + const int which_file = name2log(tn); // Numero logico del file + + if (tn[0] == '%' || tn[0] == '$') + descfname << (tn+1); + else + descfname << tn; + descfname << ".des"; + + TConfig conf(descfname, DESCPAR); + // new record descriptor for _fields array + TString_array* rdesc = new TString_array; + + TTrec trec; trec.get(which_file); + TToken_string ttmp(64); + TString dfld(256); + + for (int f = 0; f < trec.fields(); f++) + { + ttmp = trec.fielddef(f); + const TString16 name(ttmp.get(0)); + if (!name.blank()) + { + dfld = conf.get(name, NULL, -1, "Missing description"); + if (!dfld.blank() && dfld[0] != '#') + { + ttmp.add(dfld,4); + // contiene: nome campo, tipo, lunghezza, decimali, descrizione + rdesc->add(ttmp); + } + } + } + + _fields.add(rdesc, i); + } +} + +// @doc INTERNAL + +// @mfunc Cambia la relazione descritta +void TRelation_description::change_relation( + TRelation& r, // @parm Nuova relazione da assegnare + TString_array& a) // @parm Array di descrittore dei file +{ + _rel = &r; + read_rel(); + if (a.items() > 0) + _files = a; +} + +void TRelation_description::print_on(ostream& out) const +{ + for (int i = 0; i < _files.items(); i++) + { + TToken_string& r = ((TRelation_description*)this)->_files.row(i); + TString16 cod(r.get(4)); + if (atoi(cod) == 0) + cod = r.get(0); + else + cod << '@'; + out << " " << cod << "->* "; + out << "\"" << r.get(1) << "\"\n"; + } +} + +// @doc INTERNAL + +// @mfunc Seleziona un file +// +// @rdesc Ritorna il risultato dell'operazione: +// +// @flag TRUE | E' stato selzionato il file +// @flag FALSE | Non e' riuscito a selezionare il file +bool TRelation_description::choose_file( + int file) // @parm Numero logico del file da selezionare (default 0) + +// @comm Dopo aver scelto un file (occorre che ritorni TRUE) i metodi della classe +// permettono di conoscere tutti i dati riguardanti i parametri del file selezionato + +{ + TArray_sheet sht(-1,-1,-4,-4,"Selezione archivio", "Codice|Descrizione archivio@70"); + TToken_string tt(80); + + int sel = 0; + + for (int i = 0; i < _files.items(); i++) + { + TToken_string& tf = _files.row(i); + tt = tf.get(4); + + int num = atoi(tt); + if (num == 0) + { + tt = tf.get(0); + num = atoi(tt); + if (sel == 0 && file > 0 && num == file) + sel = i; + } + else + { + if (sel == 0 && file < 0 && num == -file) + sel = i; + tt << '@'; + } + + tt.add(tf.get(1)); + sht.add(tt); + } + + sht.select(sel); + + if (sht.run() == K_ENTER) + { + _cur_file = (int)sht.selected(); + _cfile = _files.row(_cur_file); + return TRUE; + } + return FALSE; +} + +// @doc INTERNAL + +// @mfunc Seleziona un campo

del file selezionato +// +// @rdesc Ritorna il risultato dell'operazione: +// +// @flag TRUE | E' stato selzionato il campo +// @flag FALSE | Non e' riuscito a selezionare il campo +bool TRelation_description::choose_field( + const char* fld) // @parm Nome del campo da selezionare (default "") + +// @comm Occorre che sia selezionato un file, diversamente viene considerato +// selezionato il primo file +{ + TArray_sheet sht(-1,-1,76,20,"Selezione campo", + "Campo@10|Descrizione@50|Tipo@10|Dim."); + TString_array& fd = (TString_array&)_fields[_cur_file]; + TToken_string tt(80); + + int sel = 0; + for (int i = 0; i < fd.items(); i++) + { + TToken_string& tf = fd.row(i); + tt = tf.get(0); + if (sel == 0 && tt == fld) + sel = i; + tt.add(tf.get(4)); + tt.add(field_type_str((TFieldtypes)tf.get_int(1))); + tt.add(tf.get(2)); + sht.add(tt); + } + + sht.select(sel); + if (sht.run() == K_ENTER) + { + _cur_field = (int)sht.selected(); + _cfield = fd.row(_cur_field); + return true; + } + return false; +} + +// @doc INTERNAL + +// @mfunc Costruisce un menu +// +// @rdesc Ritorna solamente FALSE +bool TRelation_description::build_menu( + const char* title) // @parm Titolo del menu' + +// @comm Costruisce un albero di menu' e setta il campo corrente quando viene +// selezionato (deve essere popup) +// +// @devnote Funzione non implementata. +{ + return false; +} + +bool TRelation_description::remove_menu() +{ + return false; +} + +bool TRelation_description::set_cur_file(int id) +{ + const int n = _rel->log2ind(id); + if (n >= 0 && n != _cur_file) + { + _cur_file = n; + _cfile = _files.row(n); + } + return n >= 0; +} + +int TRelation_description::file_num() +{ + int n = -_cfile.get_int(4); + if (n == 0) + n = _cfile.get_int(0); + return n; +} + +const char* TRelation_description::file_desc() +{ + return _cfile.get(1); +} + +void TRelation_description::file_desc(const char* desc) +{ + _cfile.add(desc, 1); + _files.row(_cur_file) = _cfile; +} + + +const char* TRelation_description::field_desc() +{ + return _cfield.get(4); +} + +const char* TRelation_description::field_name() +{ + return _cfield.get(0); +} + +int TRelation_description::field_len() +{ + return _cfield.get_int(2); +} + +TFieldtypes TRelation_description::field_type() +{ + return (TFieldtypes)_cfield.get_int(1); +} + +// @doc INTERNAL + +// @mfunc Setta la descrizione del campo +// +// @rdesc Ritorna se ha trovato il campo da settare +bool TRelation_description::set_field_description( + const char* field, // @parm Campo a cui assegnare la descrizione + const char* des) // @parm Descrizione da assegnare al campo +{ + TString_array& fld = (TString_array&)_fields[_cur_file]; + + for (int i = 0; i < fld.items(); i++) + { + TToken_string& tt = fld.row(i); + if (strcmp(tt.get(0),field) == 0) + { + if (des && *des) + tt.add(des, 4); + else + { + fld.destroy(i); + fld.pack(); + } + return TRUE; + } + } + + if (des && *des) + { + TToken_string tt(80); + tt.add(field); + tt.add(des, 4); + fld.add(tt); + return TRUE; + } + + return FALSE; +} + +const char* TRelation_description::get_field_description(const char* field) +{ + TString_array& fld = (TString_array&)_fields[_cur_file]; + + for (int i = fld.items()-1; i >= 0; i--) + { + TToken_string& tt = fld.row(i); + if (strcmp(tt.get(0),field) == 0) + return tt.get(4); + } + return ""; +} + + +TRelation_description::TRelation_description(TRelation& r) +: _rel(&r), _cur_file(0), _cur_field(0), _menu(FALSE), + _cfile(80), _cfield(80) +{ + read_rel(); + if (_files.items() > 0) + { + _cfile = _files.row(0); + _cfield = ""; + } +} + +TRelation_description::~TRelation_description() +{ + if (_menu) remove_menu(); +} + + +//////////////////////////////////// +// TSortedfile +//////////////////////////////////// +// @mfunc Avanza di

record +int TSortedfile::operator +=(const TRecnotype npos) +{ + int err = NOERR; + TRecnotype p = _curs->pos() + npos; + if (p < 0) + { + p = 0; + err = _isbof; + } + if (p >= _curs->items()) + { + err = _iseof; + p = _curs->items(); + } + *_curs = p; + setstatus(err); + return err; +} + // @mfunc Sposta indietro di

record +int TSortedfile::operator -=(const TRecnotype npos) +{ + return operator+=(-npos); +} + // @mfunc Avanza al record successivo +int TSortedfile::operator ++() +{ + return *this+=1; +} + // @mfunc Indietreggia al record precedente +int TSortedfile::operator --() +{ + return *this-=1; +} + +// @mfunc Si posiziona sul primo record del file (vedi ) +int TSortedfile::first(word lockop) +{ + int err = _curs->items() > 0 ? NOERR : _iseof; + _curs->first_item(); + setstatus(err); + return err; +} +// @mfunc Si posiziona sull'ultimo record del file (vedi ) +int TSortedfile::last(word lockop) +{ + int err = _curs->items() > 0 ? NOERR : _iseof; + _curs->last_item(); + setstatus(err); + return err; +} +// @mfunc Si posiziona sul successivo record del file (vedi ) +int TSortedfile::next(word lockop ) +{ + int err = _curs->pos() < _curs->items() ? NOERR : _iseof; + if (err == NOERR) + _curs->succ_item(); + setstatus(err); + return err; +} +// @mfunc Si posiziona sul precedente record del file (vedi ) +int TSortedfile::prev(word lockop ) +{ + int err = _curs->pos() > 0 ? NOERR : _isbof; + if (err == NOERR) + _curs->pred_item(); + setstatus(err); + return err; +} + +// @mfunc Salta

record dalla posizione corrente (vedi ) +int TSortedfile::skip(TRecnotype nrec, word lockop ) +{ + *_curs += nrec; // manca il lock... + setstatus(_curs->file().status()); + NFCHECK("Operazione 'skip' non consentita sul cursore"); + return 0; +} + +// @mfunc Rilegge l'ultimo record letto (vedi ) +int TSortedfile::reread(word lockop ) +{ + return reread(curr(),lockop); +} +// @mfunc Rilegge l'ultimo record letto e lo copia in

(vedi ) +int TSortedfile::reread(TRectype& rec, word lockop) +{ + return 0; +} + +// @mfunc Legge il record (vedi e ) +int TSortedfile::read(word op , word lockop ) +{ + return read(curr(),op,lockop); +} + +// @mfunc Legge il record e lo copia in

(vedi e ) +int TSortedfile::read(TRectype& rec, word op , word lockop ) +{ + if (&curr() != &rec) + curr() = rec; + _curs->read((TIsamop)op,(TReclock)lockop); + setstatus(_curs->file().status()); + if (&curr() != &rec) + rec = curr(); + return status(); +} + +// @mfunc Legge il record alla posizione

e lo copia in

(vedi ) +int TSortedfile::readat(TRectype& rec, TRecnotype nrec, word lockop ) +{ + // read(rec,_isequal,lockop); + // !!!!!!!!!!!!!! attenzione !!!!!!!!!!!!!! + // non usare un sorted file come file principale di un sorted cursor; + // portare invece le espressioni del sorted file a livello di cursore + // altrimenti questa readat non sempre funziona , perche' viene chiamata con + // parametri presi dalle funzioni a basso livello sugli isam, quindi relativi alle posizioni fisiche + // e non a quanto restituito da recno() (che da' la posizione all'interno del cursore) + *_curs=nrec; + if (&curr() != &rec) + rec=curr(); + setstatus(_curs->file().status()); + return status(); +} +// @mfunc Legge il record alla posizione

e lo copia in

(vedi ) +int TSortedfile::readat(TRecnotype nrec, word lockop ) +{ + return readat(curr(),nrec, lockop ); +} + +// @mfunc Aggiunge un record +int TSortedfile::write() +{ + return write(curr()); +} +// @mfunc Aggiunge un record copiando da

+ int TSortedfile::write(const TRectype& rec) + { + NFCHECK("Operazione 'write' non consentita sul TSortedfile"); + return 0; + } + +// @mfunc Riscrive un record + int TSortedfile::rewrite() + { + rewrite(curr()); + return 0; + } +// @mfunc Riscrive un record (il record

) + int TSortedfile::rewrite(const TRectype& rec) + { + NFCHECK("Operazione 'rewrite' non consentita sul TSortedfile"); + return 0; + } + +// @mfunc Riscrive un record alla posizione

copiando da

+int TSortedfile::rewriteat(TRecnotype nrec) +{ + return rewriteat(curr(),nrec); +} +// @mfunc Riscrive un record alla posizione

copiando da

+int TSortedfile::rewriteat(const TRectype& rec, TRecnotype nrec) +{ + NFCHECK("Operazione 'rewriteat' non consentita sul TSortedfile"); + return 0; +} + +// @mfunc Elimina il record +int TSortedfile::remove() +{ + return remove(curr()); +} +// @mfunc Elimina il record copiando da

+int TSortedfile::remove(const TRectype& rec) +{ + NFCHECK("Operazione 'remove' non consentita sul TSortedfile"); + return 0; +} + +// @mfunc Attiva la chiave

sul file aperto +void TSortedfile::setkey(int nkey) +{ + _curs->setkey(nkey); +} +// @mfunc Resetta la regione del file (chiama

+void TSortedfile::setregion(const TRectype &f, const TRectype &t, int tilde) +{ + _curs->setregion(f, t, tilde); +} + + // @mfunc +TRecnotype TSortedfile::eod() const +{ + return _curs->items(); +} + // @mfunc +bool TSortedfile::empty() +{ + return _curs->items()==0; +} + +TRectype& TSortedfile::curr() const +{ + return _curs->curr(); +} + +void TSortedfile::set_curr(TRectype* curr) +{ + _curs->file().set_curr(curr); +} + +// @mfunc Costruttore. +TSortedfile::TSortedfile(int logicnum, TRelation* rel, const char* ordexpr, const char* filter, int nkey) + : TLocalisamfile(logicnum), _rel(NULL) +{ + // costruisce il cursore + if (!rel) + _rel = new TRelation(logicnum); + else + _rel = rel; + _curs = new TSorted_cursor(_rel, ordexpr, "", nkey); + + if (&curr()!=&(_curs->file().curr())) + _curs->file().set_curr(&curr()); + + _curs->setfilter(filter,TRUE); //must pass TRUE because cursors doesn't update rel (BUG). + // DON't move this line BEFORE set_curr : filter's fieldrefs are allocated at cursor setfilter +} + +// @mfunc Distruttore +TSortedfile::~TSortedfile() +{ + delete _curs; + if (_rel) + delete _rel; +} + +// +// *** EOF relation.cpp +