#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); 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 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); TFieldref& s = (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(); while (!DB_index_eof(fhnd)) { 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) const { 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) { TProgind* pi = NULL; if (tot > 1) { if (msg == NULL || *msg == '\0') msg = TR("Elaborazione in corso..."); pi = new TProgind(tot, msg, true, true); } freeze(true); for (*this = 0; pos() < tot; ++*this) { if (!func(*relation(), pJolly)) { ok = false; break; } if (pi != NULL && !pi->addstatus(1)) { 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; while (!DB_index_eof(handle)) { 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) { TString 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]; if (last == '-' || last == '+') 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))) { const TDate d(sf); sf.format("%08ld", d.date2ansi()); fld_type = _datefld; fld_len = 8; } else { if (is_up) sf.upper(); } switch (fld_type) { case _boolfld: case _charfld: case _alfafld: sf.left_just(fld_len); break; case _datefld: break; // Gia' lungo 8! default : sf.right_just(fld_len); 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; } 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); 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 (_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