maskfld.cpp relation.cpp Corretta gestione cursori vuoti git-svn-id: svn://10.65.10.50/trunk@2936 c028cbd2-c16b-5b4b-a496-9718f37d4682
		
			
				
	
	
		
			2493 lines
		
	
	
		
			62 KiB
		
	
	
	
		
			C++
		
	
	
		
			Executable File
		
	
	
	
	
			
		
		
	
	
			2493 lines
		
	
	
		
			62 KiB
		
	
	
	
		
			C++
		
	
	
		
			Executable File
		
	
	
	
	
| #include <ctype.h>
 | |
| #include <stdlib.h>
 | |
| 
 | |
| #include <applicat.h>
 | |
| #include <config.h>
 | |
| #include <expr.h>
 | |
| #include <extcdecl.h>
 | |
| #include <prefix.h>
 | |
| #include <relation.h>
 | |
| #include <sheet.h>
 | |
| #include <sort.h>
 | |
| #include <tabutil.h>
 | |
| #include <utility.h>
 | |
| #include <xvtility.h>
 | |
| 
 | |
| #include <codeb.h>
 | |
| 
 | |
| // *** check if not already defined
 | |
| #define NOTFOUND (-1)
 | |
| 
 | |
| // *** maximum number of elements in a cursor working page
 | |
| #define CMAXELPAGE 8000
 | |
| 
 | |
| #define print_name(out, f) out << (f.num() == LF_TABCOM ? "%" : "") << 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 write_enable() const { return _write_enable; }
 | |
|   void write_enable(bool we) { _write_enable = we; }
 | |
|   TRectype& load_rec(TRectype& r, const TBaseisamfile& 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)
 | |
| : _num(idx_file), _key(key), _numto(idx_to),
 | |
|   _rel(rel), _fields(4), _exprs(4), _alias(alias),
 | |
|   _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 = "";
 | |
|   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)
 | |
| {             
 | |
|   TExpression& expr = (TExpression&)_exprs[j];
 | |
|   for (int k = 0; k < expr.numvar(); k++)
 | |
|     expr.setvar(k, to.get(expr.varname(k)));
 | |
|   
 | |
|   const char* val = expr;
 | |
|   
 | |
|   if (*val == '\0' && _altexprs.objptr(j))
 | |
|   {
 | |
|     TExpression& altexpr = (TExpression&)_altexprs[j];
 | |
|     for (int k = 0; k < expr.numvar(); k++)
 | |
|       altexpr.setvar(k, to.get(altexpr.varname(k)));
 | |
|     val = altexpr;
 | |
|   }
 | |
| 
 | |
|   return val;
 | |
| }  
 | |
| 
 | |
| ///////////////////////////////////////////////////////////
 | |
| // TRelation
 | |
| ///////////////////////////////////////////////////////////
 | |
| 
 | |
| TRelation::TRelation(int logicnum)
 | |
| : _files(4), _reldefs(4), _errors(NOERR)
 | |
| {
 | |
|   TLocalisamfile* f = new TLocalisamfile(logicnum, FALSE);
 | |
|   _files.add(f);
 | |
| }
 | |
| 
 | |
| TRelation::TRelation(const char* tabname)
 | |
| : _files(4), _reldefs(4), _errors(NOERR)
 | |
| {
 | |
|   TTable* t = new TTable(tabname, FALSE);
 | |
|   _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()
 | |
| {
 | |
|   _status.restart();
 | |
|   for (int i = 0; i < _files.items(); i++)
 | |
|   {
 | |
|     const int err   = _status.get_int();
 | |
|     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).curr().zero();
 | |
|     file(i).setstatus(err);
 | |
|   }
 | |
|   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);
 | |
|   for (int 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(format("%d",first_match));
 | |
|   }
 | |
| }
 | |
| 
 | |
| // @doc EXTERNAL
 | |
| 
 | |
| // @mfunc Ritorna l'indice di <p _files> 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 <p log> sia minore di 0 chiama la <mf TRelation::alias2ind>
 | |
| //
 | |
| // @xref <mf TRelation::alias2ind> <mf TRelation::name2ind>
 | |
| {
 | |
|   // 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 <p _files> 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 <mf TRelation::log2ind> <mf TRelation::name2ind>
 | |
| {
 | |
|   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 <p _files> 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 <mf TRelation::alias2ind> <mf TRelation::alias2ind>
 | |
| {
 | |
|   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 <c TLocalisamfile> 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, "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];
 | |
| }
 | |
| 
 | |
| // @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 dell 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 principale
 | |
|      int linkto,            // @parm Posizione a cui 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 <p linkto> puo' assumere i valori:
 | |
| //
 | |
| // @flag 0 | Per il file principale
 | |
| // @flag <gt>0 | Indica il numero logico del file
 | |
| // @flag <lt>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 _errors;
 | |
| }
 | |
| 
 | |
| 
 | |
| bool TRelation::add(int logicnum, const char* relexprs, int key,
 | |
|                     int linkto, int alias, bool allow_lock)
 | |
| {
 | |
|   TLocalisamfile* f = new TLocalisamfile(logicnum, FALSE);
 | |
|   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)
 | |
| {
 | |
|   TTable* t = new TTable(tabname, FALSE);
 | |
|   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 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);
 | |
| } 
 | |
| 
 | |
| TRectype& TRelationdef::load_rec(TRectype& r, const TBaseisamfile& from) const
 | |
| {
 | |
|   r.zero();
 | |
| 
 | |
|   for (int j = 0 ; j < _fields.items(); j++) // for each field
 | |
|   {
 | |
|     TFieldref& s = (TFieldref&) _fields[j];
 | |
|     s.write(s.read(from.curr()),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)
 | |
|     TDate& atdate,   // @parm UNUSED
 | |
|     int first)       // @parm Numero del file da cui construtire al 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.
 | |
| //       <nl>Le altre funzioni sul record procedono normalmente
 | |
| 
 | |
| {
 | |
|   _errors = NOERR;
 | |
| 
 | |
|   // workhorse: position files for each active relation
 | |
|   for (int i = first; i < _reldefs.items(); i++)
 | |
|   {
 | |
|     TRelationdef& rd     = reldef(i);
 | |
|     TLocalisamfile& from = file(rd.num());
 | |
|     TLocalisamfile& to   = file(rd.link());
 | |
|     TReclock lck = rd._allow_lock ? lockop : _nolock;
 | |
| 
 | |
|     if (to.curr().empty())
 | |
|     { from.zero(); continue; }
 | |
| 
 | |
|     from.setkey(rd._key);
 | |
|     from.curr().zero();
 | |
| 
 | |
|     // build record
 | |
|     if (rd._fields.items() && rd._exprs.items())
 | |
|     {
 | |
|       for (int j = 0 ; j < rd._fields.items(); j++) // for each field
 | |
|       {
 | |
|         /*      
 | |
|            TExpression& expr = (TExpression&)rd._exprs[j];
 | |
|            for (int k = 0; k < expr.numvar(); k++)
 | |
|            expr.setvar(k, to.get(expr.varname(k)));
 | |
|            */     
 | |
|         const char* expr = rd.evaluate_expr(j, to);
 | |
|         TFieldref& s = (TFieldref&) rd._fields[j];
 | |
|         s.write(expr, from.curr());
 | |
|       } // for each field
 | |
|     }
 | |
| 
 | |
|     // read record: if not found, zero current record
 | |
|     const TRectype rec(from.curr());
 | |
|     from.read(op, lck, atdate);
 | |
|     if (from.bad())
 | |
|     {
 | |
|       rd._first_match = (from.curr() == rec);
 | |
|       if (rd._first_match)
 | |
|       {
 | |
|         bool eq = TRUE;
 | |
| 
 | |
|         for (int kk = 0; eq && kk < rd._fields.items(); kk++)
 | |
|         {
 | |
|           if (rd._forced[kk])
 | |
|           {
 | |
|             TFieldref& fl = (TFieldref&)rd._fields[kk];
 | |
|             const TString f_fr(fl.read(from.curr()));
 | |
|             eq = (f_fr == rd.evaluate_expr(kk, to));
 | |
|           }
 | |
|         }
 | |
|         if (eq) from.setstatus(NOERR);
 | |
|         else    { rd._first_match = FALSE; from.curr().zero(); }
 | |
|       }
 | |
|       else from.curr().zero();
 | |
|     }
 | |
|     else rd._first_match = TRUE;
 | |
|   } // 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);
 | |
|   
 | |
|   for (int j = 0; j < _reldefs.items(); j++)
 | |
|     if (reldef(j).num() == i) break;
 | |
|   
 | |
|   TLocalisamfile& from = file(i);
 | |
|   TLocalisamfile& to = file(reldef(j).link());
 | |
| 
 | |
|   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.curr());
 | |
|     reldef(j).load_rec(rec, from);
 | |
|     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(to.curr()));
 | |
|         
 | |
|         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);
 | |
|     for (const char* f = fields.get(0); f; f = fields.get())
 | |
|       old.add(from.curr().get(f));
 | |
|     
 | |
|     from.next();
 | |
|     
 | |
|     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, (TDate&)botime, 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
 | |
| //       <mf TRelation::position_rels>)
 | |
| //
 | |
| // @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++)
 | |
|     if (reldef(j).num() == i) return reldef(j)._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
 | |
| //       <nl>Se <p reset> e' TRUE si cerca di riportare la relazione in uno stato
 | |
| //       consistente (basato sul record principale). Non viene fatto nessun altro
 | |
| //       controllo
 | |
| //       <nl>Questa funzione e' chiamata internamente da <mf TRelation::update>
 | |
| //       e <mf TRelation::remove>.
 | |
| 
 | |
| {
 | |
|   const int MAXREL = 24;
 | |
|   int bad = 0;
 | |
|   TRecnotype recnos[MAXREL];
 | |
|   
 | |
|   for (int 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)
 | |
|     return _errors = bad;
 | |
|   
 | |
|   position_rels(_isequal, _nolock, (TDate&)botime);
 | |
|   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 _errors = bad;
 | |
| }
 | |
| 
 | |
| #endif
 | |
| 
 | |
| 
 | |
| int TRelation::write(bool force, TDate& atdate)
 | |
| {
 | |
|   _errors = file(0).write(atdate);
 | |
|   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(atdate);
 | |
|     if (force && res == _isreinsert)
 | |
|       res = lf.rewrite(atdate);
 | |
|     if (_errors == NOERR) 
 | |
|       _errors = res;
 | |
|   }
 | |
| 
 | |
|   return _errors;
 | |
| }
 | |
| 
 | |
| int TRelation::rewrite(bool force, TDate& atdate)
 | |
| {
 | |
|   _errors = file(0).rewrite(atdate);                // Riscrive testata
 | |
|   if (force && _errors == _iskeynotfound)           // Se non la trova ...
 | |
|     _errors = file(0).write(atdate);                // ... 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(atdate);
 | |
|     if (force && res == _iskeynotfound)
 | |
|       res = lf.write(atdate);
 | |
|     if (_errors == NOERR) _errors = res;
 | |
|   }
 | |
| 
 | |
|   return _errors;
 | |
| }
 | |
| 
 | |
| int TRelation::remove(TDate& atdate)
 | |
| {
 | |
|   const int res = file(0).remove(atdate);
 | |
|   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(atdate);
 | |
|     if (_errors == NOERR && res != _iskeynotfound) _errors = res;
 | |
|   }
 | |
|   return _errors;
 | |
| }
 | |
| 
 | |
| 
 | |
| ///////////////////////////////////////////////////////////
 | |
| // TCursor
 | |
| ///////////////////////////////////////////////////////////
 | |
| 
 | |
| 
 | |
| HIDDEN bool __evalcondition(const TRelation& r,TExpression* cond)
 | |
| {
 | |
|   for (int i = cond->numvar() - 1; i >= 0;  i--)
 | |
|   {
 | |
|     const char* s = cond->varname(i);
 | |
|     TFieldref f(s,0);
 | |
|     cond->setvar(i, f.read(r));
 | |
|   }
 | |
|   return (bool) *cond;
 | |
| }
 | |
| 
 | |
| // @doc EXTERNAL
 | |
| 
 | |
| // @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)
 | |
| {
 | |
| #if XVT_OS == XVT_OS_SCOUNIX
 | |
|   const char* const r = "r";
 | |
|   const char* const w = "w";
 | |
| #else
 | |
|   const char* const r = "rb";
 | |
|   const char* const w = "wb";
 | |
| #endif
 | |
| 
 | |
|   if (create && _indexname.empty()) _indexname.temp("ci$$");
 | |
|   FILE* f = fopen(_indexname, create ? w : r);
 | |
|   if (f == NULL)
 | |
|     fatal_box("Can't use cursor index for file %d: '%s'\n", 
 | |
|               file().filehnd()->ln, (const char*)_indexname);
 | |
| 
 | |
|   return f;
 | |
| }
 | |
| 
 | |
| TRecnotype TCursor::buildcursor(TRecnotype rp)
 | |
| {
 | |
|   
 | |
|   TRecnotype oldrecno=0,pos,ap = 0;
 | |
|   int   junk, l, pagecnt = 0;
 | |
|   const bool filtered = has_filter();
 | |
| 
 | |
|   FILE* _f = open_index(TRUE);
 | |
|   
 | |
|   if (DB_reccount(file().filehnd()->fhnd) == 0)
 | |
|   {
 | |
|     fclose(_f);
 | |
|     return 0;
 | |
|   }
 | |
|   
 | |
|   fseek(_f, 0L, SEEK_SET);
 | |
| 
 | |
|   l = strlen(to());
 | |
|   junk=DB_index_seek(file().filehnd()->fhnd, (char*)(const char*) from());
 | |
|   if (junk < 0) junk=get_error(junk);
 | |
|   if (junk == _iseof) return 0;
 | |
| 
 | |
|   TRecnotype* page = new TRecnotype [CMAXELPAGE];   
 | |
|   pos = DB_index_recno(file().filehnd()->fhnd);
 | |
|   _pos=-1;           
 | |
|   
 | |
|   while (TRUE)
 | |
|   {              
 | |
|     if (DB_index_eof(file().filehnd()->fhnd)) break;    
 | |
|     const char* s0 = DB_index_getkey(file().filehnd()->fhnd);
 | |
|     const TRecnotype recno = DB_index_recno(file().filehnd()->fhnd);
 | |
|     if (l && (strncmp(to(), s0, l) < 0)) break;
 | |
|     if (recno == oldrecno) break; // means that no more keys are available  
 | |
|     oldrecno=recno;
 | |
|     if (pagecnt == CMAXELPAGE)
 | |
|     {     
 | |
|       if (filtered) pagecnt = filtercursor(pagecnt,page);
 | |
|       fwrite(page,sizeof(TRecnotype),pagecnt,_f);
 | |
|       for (int i= 0; i< pagecnt; i++)
 | |
|         if (page[i] == rp)
 | |
|         {
 | |
|           _pos = ap + i;
 | |
|           break;                                        
 | |
|         }
 | |
|       ap += pagecnt;
 | |
|       pagecnt = 0;
 | |
|     }
 | |
|     page[pagecnt++] = recno;
 | |
|     DB_index_next(file().filehnd()->fhnd);
 | |
|     int rt=get_error(-1);
 | |
|     if (rt != NOERR)
 | |
|       fatal_box("Can't read index n. %d - file n. %d",DB_tagget(file().filehnd()->fhnd),file().filehnd()->ln);
 | |
|   } // while
 | |
|   if (pagecnt)
 | |
|   {
 | |
|     if (filtered) pagecnt = filtercursor(pagecnt, page);
 | |
|     fwrite(page, sizeof(TRecnotype), pagecnt, _f);
 | |
|     for (int i = 0; i < pagecnt; i++)
 | |
|       if (page[i] == rp)
 | |
|       {
 | |
|         _pos = ap + i;
 | |
|         break;
 | |
|       }
 | |
|     ap += pagecnt;
 | |
|   }
 | |
|   if (_pos == -1) pos=0;
 | |
|   delete page;
 | |
|   fclose (_f);
 | |
|   return ap;
 | |
| }
 | |
| 
 | |
| 
 | |
| int TCursor::filtercursor(int pagecnt, TRecnotype* page)
 | |
| 
 | |
| {
 | |
|   int np=0, handle=file().filehnd()->fhnd;
 | |
|   TRectype& rec=file().curr();
 | |
|   for (int i=0; i< pagecnt; i++)
 | |
|   {
 | |
|     file().readat(rec,page[i]);
 | |
|     if (update_relation()) _if->update();
 | |
|     if ((_filterfunction ? _filterfunction(_if) : TRUE ) &&
 | |
|         (_fexpr ? __evalcondition(*_if, _fexpr) : TRUE))
 | |
|     {
 | |
|       if (np < i) page[np] = page[i];
 | |
|       np++;
 | |
|     }
 | |
|   }                                               
 | |
|   return np;
 | |
| }
 | |
| 
 | |
| 
 | |
| bool TCursor::ok() const
 | |
| 
 | |
| {
 | |
|   if (file().bad()) return FALSE;
 | |
| 
 | |
|   const TRectype& rec = file().curr();
 | |
|   TString       key(rec.key(_nkey)), kf(from()), kt(to());
 | |
|   if (file().tab())
 | |
|   {
 | |
|     key.ltrim(3);
 | |
|     kf.ltrim(3);
 | |
|     kt.ltrim(3);
 | |
|   }
 | |
|   
 | |
|   if (key < (const char *) kf || (kt.not_empty() && kt < (const char *) key.left(kt.len())))
 | |
|     return FALSE;                  
 | |
|   if (_filter_update || _filterfunction_update) _if->update();
 | |
|   if ((_filterfunction ? _filterfunction(_if) : TRUE ) &&
 | |
|       (_fexpr ? __evalcondition(*_if, _fexpr) : TRUE))
 | |
|     return TRUE;
 | |
|   return FALSE;
 | |
| }            
 | |
| 
 | |
| bool TCursor::changed()
 | |
| 
 | |
| {
 | |
|   isdef* fh = file().filehnd();
 | |
| 
 | |
|   if (_frozen && _lastrec > 0L) return _filename != fh->d->SysName;
 | |
| 
 | |
|   const TRecnotype eod = DB_reccount(fh->fhnd);
 | |
| 
 | |
|   if (_lastrec != eod ||
 | |
|       (_lastkrec != DB_changed(fh->fhnd)) ||
 | |
|       (_filename != fh->d->SysName))
 | |
|   {
 | |
|     //    _lastrec = eod;
 | |
|     //    _filename = fh->d->SysName;
 | |
|     //    _lastkrec = DB_changed(fh->fhnd);
 | |
|     return TRUE;
 | |
|   }
 | |
|   else return FALSE;
 | |
| }
 | |
| 
 | |
| TRecnotype TCursor::update()
 | |
| 
 | |
| {
 | |
|   main_app().begin_wait();
 | |
| 
 | |
|   file().setkey(_nkey);   
 | |
|   if (file().curr().empty()) file().curr().zero();
 | |
|   file().read(_isgteq);
 | |
|   const TRecnotype totrec = buildcursor(file().recno());
 | |
| 
 | |
|   main_app().end_wait();
 | |
| 
 | |
|   isdef* fh = file().filehnd();
 | |
|   const TRecnotype eod = DB_reccount(fh->fhnd);
 | |
| 
 | |
|   _lastrec = eod;
 | |
|   _filename = fh->d->SysName;
 | |
|   _lastkrec = DB_changed(fh->fhnd);
 | |
| 
 | |
|   return totrec;
 | |
| }
 | |
| 
 | |
| // @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
 | |
|      
 | |
| // @comm ATTENZIONE: non e' possibile filtrare un cursore congelato     
 | |
| 
 | |
| {
 | |
|   CHECK(!_frozen, "Impossibile filtrare un cursore congelato");
 | |
| 
 | |
|   TString kf(_keyfrom), kto(_keyto);
 | |
| 
 | |
|   const bool filterchanged = (fil != NULL) && (_filter != fil);
 | |
| 
 | |
|   if (file().tab())
 | |
|   {
 | |
|     TTable& f = (TTable&) file();
 | |
|     kf = kto = f.name();
 | |
|   }
 | |
|   if (from != NULL)
 | |
|   {
 | |
|     kf = from->key(_nkey);
 | |
|     int p;
 | |
|     while ((p = kf.find('~')) != -1) kf[p] = ' ';
 | |
|   }
 | |
|   if (to != NULL)
 | |
|   {
 | |
|     kto = to->key(_nkey);
 | |
|     int p;
 | |
|     while ((p = kto.find('~')) != -1) kto[p] = ' ';
 | |
|   }
 | |
|   
 | |
|   if (filterchanged || (_keyfrom != kf) || (_keyto != kto))
 | |
|   {
 | |
|     _pos = 0;
 | |
|     _totrec = 0;
 | |
|     _lastrec = 0;
 | |
|     if (filterchanged)
 | |
|     {
 | |
|       _filter = fil;
 | |
|       if (_fexpr) delete _fexpr;
 | |
|       TTypeexp type = (_filter.find('"') != -1) ? _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;
 | |
|     }
 | |
|     _keyfrom = kf;
 | |
|     _keyto = kto;
 | |
|   }
 | |
| }
 | |
| 
 | |
| void TCursor::setkey(int nkey)
 | |
| 
 | |
| {
 | |
|   if (nkey != _nkey)
 | |
|   {
 | |
|     _lastrec = 0L;
 | |
|     _nkey = nkey;
 | |
|     file().setkey(_nkey);
 | |
|     filter(NULL);
 | |
|   }
 | |
| }
 | |
| 
 | |
| 
 | |
| TRecnotype TCursor::read(TIsamop op, TReclock lockop, TDate& atdate)
 | |
| {
 | |
|   TRecnotype *page;
 | |
|   int pagecnt;
 | |
| 
 | |
|   file().setkey(_nkey);
 | |
|   const bool approx = (op == _isgteq);
 | |
| 
 | |
|   _if->file().read(op, lockop, atdate);
 | |
|   if (approx)
 | |
|   {
 | |
|     while (_if->file().good() && !ok())
 | |
|       _if->file().next();
 | |
|   }
 | |
|   const TRecnotype curpos = file().recno();
 | |
| 
 | |
|   if (changed())
 | |
|     _totrec = update();
 | |
|   if (approx && _if->file().status() == _iseof) 
 | |
|   { 
 | |
|     _pos = _totrec - 1;
 | |
|     if (_pos < 0)
 | |
|     { 
 | |
|       file().zero();
 | |
|       file().setstatus(_isemptyfile);
 | |
|     }
 | |
|     else
 | |
|     {
 | |
|       readrec();
 | |
|       file().setstatus(_iseof);
 | |
|     }  
 | |
|     return _pos;
 | |
|   }
 | |
| 
 | |
|   FILE* _f = open_index();
 | |
| 
 | |
|   if (fseek(_f, 0L, SEEK_SET) != 0)
 | |
|     fatal_box("Can't repos cursor : File %d\n", file().filehnd()->ln);
 | |
| 
 | |
|   page = new TRecnotype [CMAXELPAGE];
 | |
|   _pos = -1;
 | |
| 
 | |
|   for (TRecnotype max = _totrec; _pos == -1 && max > 0; max -= pagecnt)
 | |
|   {
 | |
|     pagecnt = (max < CMAXELPAGE) ? (int)max : CMAXELPAGE;
 | |
|     fread(page, sizeof(TRecnotype), pagecnt, _f);
 | |
|     for (int i = 0; i < pagecnt; i++)
 | |
|       if (page[i] == curpos)
 | |
|       {
 | |
|         _pos = _totrec - max + i;
 | |
|         break;
 | |
|       }
 | |
|   }
 | |
|   if (_pos == -1) _pos = 0;
 | |
|   delete page;
 | |
|   fclose(_f);
 | |
| 
 | |
|   readrec();
 | |
|   return _pos;
 | |
| }
 | |
| 
 | |
| 
 | |
| TCursor::TCursor(TRelation* r, const char* fil, int nkey, 
 | |
|                  const TRectype *from, const TRectype* to)
 | |
| : _if(r), _nkey(nkey), _frozen(FALSE), _filterfunction(NULL), _fexpr(NULL), _filter_update(FALSE), _filterfunction_update(FALSE)
 | |
| {
 | |
|   file().setkey(_nkey);
 | |
|   _pos = 0;
 | |
|   _totrec = 0;
 | |
|   _lastrec = 0;
 | |
|   _lastkrec = 0;
 | |
|   filter(fil, from, to);
 | |
| }
 | |
| 
 | |
| 
 | |
| TCursor::~TCursor()
 | |
| 
 | |
| {
 | |
|   if (_indexname.not_empty())
 | |
|     ::remove(_indexname);
 | |
|   if (_fexpr) 
 | |
|     delete _fexpr;
 | |
| }
 | |
| 
 | |
| 
 | |
| TRecnotype TCursor::readrec()
 | |
| 
 | |
| {
 | |
|   TRecnotype& nrec = file().filehnd()->RecNo;
 | |
| 
 | |
|   if (_pos >= items())
 | |
|   {
 | |
|     file().setstatus(_iseof);
 | |
|     curr().zero();
 | |
|     return nrec = 0L;
 | |
|   }
 | |
|   file().setstatus(NOERR);
 | |
| 
 | |
|   FILE* _f = open_index();
 | |
| 
 | |
|   if (fseek(_f, _pos * sizeof(TRecnotype), SEEK_SET) != 0)
 | |
|     fatal_box("Can't seek position %ld in cursor n. %d\n", _pos, file().filehnd()->ln);
 | |
|   
 | |
|   if (fread(&nrec, sizeof(TRecnotype), 1, _f) != 1)
 | |
|     fatal_box("Can't read position %ld in cursor n. %d\n", _pos, file().filehnd()->ln);
 | |
| 
 | |
|   fclose(_f);
 | |
| 
 | |
|   curr().setdirty();
 | |
|   file().readat(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 locke da porre sul record (vedi <t TReclock>)
 | |
| {
 | |
|   int rt=NOERR; 
 | |
|   switch(l)
 | |
|   {
 | |
|   case _lock:
 | |
|     rt=DB_lock_rec(file().filehnd()->fhnd,_pos);
 | |
|     break;
 | |
|   case _unlock:
 | |
|     rt=DB_unlock(file().filehnd()->fhnd);
 | |
|     break;                             
 | |
|   default:
 | |
|     break;                      
 | |
|   }                   
 | |
|   if (rt != NOERR) rt=get_error(rt);
 | |
|   return(rt);
 | |
| }
 | |
| 
 | |
| TRecnotype TCursor::operator =(const TRecnotype pos)
 | |
| {
 | |
|   if (changed())
 | |
|     _totrec = update();
 | |
|   CHECKD(pos >= 0 && pos <= _totrec, "Bad cursor position : ", pos);
 | |
|   _pos = pos;
 | |
|   readrec();
 | |
|   return _pos;
 | |
| }
 | |
| 
 | |
| TRecnotype TCursor::operator +=(const TRecnotype npos)
 | |
| {
 | |
|   if (changed())
 | |
|     _totrec = update();
 | |
| 
 | |
|   _pos += npos;
 | |
|   if (_pos > _totrec) _pos = _totrec; 
 | |
|   else
 | |
|     if (_pos < 0) _pos = 0;
 | |
|   readrec();
 | |
|   
 | |
|   return _pos;
 | |
| }
 | |
| 
 | |
| TRecnotype TCursor::items()
 | |
| {
 | |
|   if (changed())
 | |
|     _totrec = 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));
 | |
| }
 | |
| 
 | |
| 
 | |
| ///////////////////////////////////////////////////////////
 | |
| // TSorted_cursor
 | |
| ///////////////////////////////////////////////////////////
 | |
| 
 | |
| typedef struct {
 | |
|   char       f[512];
 | |
|   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!
 | |
| //       <nl>Es. UPPER(20->RAGSOC[1,40]+) ==> 20->RAGSOC[1,40]+
 | |
| //       <nl>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(')');
 | |
|     int rt=TRUE;
 | |
|     
 | |
|     if (p>-1 && p1>-1)
 | |
|       if (s.find("UPPER") == -1) 
 | |
|       {
 | |
|         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=TRUE;
 | |
|   if (s.find("UPPER")>-1)
 | |
|     rt=check_expr(s);
 | |
|   else
 | |
|     rt=FALSE;
 | |
|   return rt;
 | |
| }
 | |
| 
 | |
| TRecnotype  TSorted_cursor::buildcursor(TRecnotype rp)
 | |
| {  
 | |
|   TRecnotype oldrecno=0,pos,ap = 0;
 | |
|   int   abspos=0,junk, l, pagecnt = 0;
 | |
|   const bool filtered = has_filter();
 | |
|   TString s;
 | |
|   El_To_Sort * Element;
 | |
|   
 | |
|   _sort = new TSort(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
 | |
|     char versus = (s.right(1)=="-") ? 'd' : 'a';  
 | |
|     if (s.right(1) == "-" || s.right(1) == "+")
 | |
|       s.cut(s.len()-1);
 | |
|     TFieldref  f(s,0);
 | |
|     // Il controllo del file e'  automatico in f.len()
 | |
|     int n = f.file();
 | |
|     int flen = f.len(n<0 ? relation()->lfile(n).curr() : relation()->curr());
 | |
|     _sort->addsortkey(abspos+f.from(),flen,versus); 
 | |
|     CHECKS(flen!=0,"Field can not have null length: ",(const char *) s);
 | |
|     int lf = (relation()->lfile(n).num());
 | |
|     TRectype r(lf);
 | |
|     abspos+=r.length(f.name());
 | |
|     CHECKD(abspos<=512,"Cannot exceed 512 bytes-key %d",abspos);
 | |
|   } 
 | |
|   
 | |
|   _sort->init();      
 | |
|   
 | |
|   FILE* _f = open_index(TRUE); 
 | |
|   
 | |
|   if (DB_reccount(file().filehnd()->fhnd) == 0)
 | |
|   {
 | |
|     fclose(_f);
 | |
|     return 0;
 | |
|   }
 | |
|   
 | |
|   fseek(_f, 0L, SEEK_SET);
 | |
| 
 | |
|   l = strlen(to());
 | |
|   junk=DB_index_seek(file().filehnd()->fhnd, (char*)(const char*) from());
 | |
|   if (junk < 0) junk=get_error(junk);
 | |
|   if (junk == _iseof) return 0;
 | |
| 
 | |
|   TRecnotype* page = new TRecnotype [CMAXELPAGE];   
 | |
|   pos = DB_index_recno(file().filehnd()->fhnd);
 | |
|   TCursor::pos()=-1;           
 | |
|   
 | |
|   while (TRUE)
 | |
|   {              
 | |
|     if (DB_index_eof(file().filehnd()->fhnd)) break;    
 | |
|     const char* s0 = DB_index_getkey(file().filehnd()->fhnd);
 | |
|     const TRecnotype recno = DB_index_recno(file().filehnd()->fhnd);
 | |
|     if (l && (strncmp(to(), s0, l) < 0)) break;
 | |
|     if (recno == oldrecno) break; // means that no more keys are available  
 | |
|     oldrecno=recno;
 | |
|     if (pagecnt == CMAXELPAGE)
 | |
|     {     
 | |
|       pagecnt = filtercursor(pagecnt,page);
 | |
|       for (int i= 0; i< pagecnt; i++)
 | |
|         if (page[i] == rp)
 | |
|         {
 | |
|           TCursor::pos() = ap + i;
 | |
|           break;                                        
 | |
|         }
 | |
|       ap += pagecnt;
 | |
|       pagecnt = 0;
 | |
|     }
 | |
|     page[pagecnt++] = recno;  // lasciare cosi' altrimenti la readat legge due volte lo stesso record
 | |
|     DB_index_next(file().filehnd()->fhnd);
 | |
|     int rt=get_error(-1);
 | |
|     if (rt != NOERR)
 | |
|       fatal_box("Can't read index n. %d - file n. %d",DB_tagget(file().filehnd()->fhnd),file().filehnd()->ln);
 | |
|   } // while
 | |
|   if (pagecnt)
 | |
|   {
 | |
|     pagecnt = filtercursor(pagecnt, page);
 | |
|     for (int i = 0; i < pagecnt; i++)
 | |
|       if (page[i] == rp)
 | |
|       {
 | |
|         TCursor::pos() = ap + i;
 | |
|         break;
 | |
|       }
 | |
|     ap += pagecnt;
 | |
|   }          
 | |
|   _sort->endsort();
 | |
|   pagecnt=0;
 | |
|   while ((Element=(El_To_Sort *)_sort->retrieve()) != NULL)
 | |
|   {     
 | |
|       page[pagecnt++]=Element->p;
 | |
|       if (pagecnt==CMAXELPAGE)   
 | |
|       {
 | |
|       fwrite(page,sizeof(TRecnotype),pagecnt,_f);  
 | |
|       pagecnt=0;
 | |
|     }
 | |
|   }
 | |
|   if (pagecnt)
 | |
|     fwrite(page,sizeof(TRecnotype),pagecnt,_f);  
 | |
|   if (TCursor::pos() == -1) pos=0;
 | |
|   delete page; 
 | |
|   if (_sort) delete _sort;
 | |
|   fclose (_f);
 | |
|   return ap;
 | |
| }
 | |
| 
 | |
| int TSorted_cursor::filtercursor(int pagecnt, TRecnotype* page)
 | |
| {     
 | |
|   
 | |
|   int np=0, handle=file().filehnd()->fhnd;
 | |
|   TRectype& rec=file().curr();
 | |
|   TString s;
 | |
|   El_To_Sort Element;
 | |
|   
 | |
|   for (int i=0; i< pagecnt; i++)
 | |
|   {
 | |
|     file().readat(rec,page[i]);
 | |
|     if (update_relation()) relation()->update();
 | |
|     if ((filterfunction() ? filterfunction()(relation()) : TRUE ) &&
 | |
|         (expression() ? __evalcondition(*relation(), expression()) : TRUE))
 | |
|     {
 | |
|       if (np < i) page[np] = page[i];
 | |
|       np++;       
 | |
|       
 | |
|       _order_expr.restart();
 | |
|       strcpy(Element.f,"");
 | |
|       while ((s=_order_expr.get()).not_empty())
 | |
|       {  
 | |
|         bool is_up=is_upper(s);     
 | |
|         int p=s.find('[');// Qui estrae il nome del campo:
 | |
|         if (p>-1)         // rimane solo l'indicazione del file (eventuale) 
 | |
|           s.cut(p);       // ed il nome del campo: 
 | |
|         else              // UPPER(20->RAGSOC[1,30]+) ===>> 20->RAGSOC
 | |
|         if (s.right(1) == "-" || s.right(1) == "+")
 | |
|           s.cut(s.len()-1);
 | |
|         TFieldref f(s,0);  
 | |
|         TString sf=f.read(*relation());
 | |
|         TFieldtypes fld_type =  file(f.file()).curr().type(f.name());
 | |
|         if (fld_type == _datefld) // Se il campo e' di tipo data, la converte in ANSI!
 | |
|         {
 | |
|           TDate d(sf);
 | |
|           sf=d.string(ANSI);
 | |
|         }
 | |
|         if (is_up) sf.upper();
 | |
|         TString fmt;  
 | |
|         if (fld_type == _alfafld || fld_type == _datefld)
 | |
|           fmt.format("%%-%ds",f.len(rec));
 | |
|         else  
 | |
|           fmt.format("%%%ds",f.len(rec));
 | |
|         strcat(Element.f,(const char*)sf.format((const char *)fmt,(const char *)sf));
 | |
|       }
 | |
|       Element.p=page[i];
 | |
|       _sort->sort((char *) &Element);
 | |
|     }
 | |
|   }                                               
 | |
|   
 | |
|   return np;
 | |
| }
 | |
| 
 | |
| bool TSorted_cursor::changed()
 | |
| {  
 | |
|   bool rt;
 | |
|   
 | |
|   if (_is_valid_expr)
 | |
|   {
 | |
|     rt = TCursor::changed();
 | |
|     if (!rt) rt=_is_changed_expr;
 | |
|     _is_changed_expr = FALSE;
 | |
|   } else         
 | |
|     {
 | |
|       error_box("Can't perform changed() while expression ain't valid!");
 | |
|       rt=FALSE;
 | |
|     }
 | |
|   return rt;
 | |
| }
 | |
| 
 | |
| 
 | |
| void TSorted_cursor::change_order(const char* order_expr)
 | |
| {          
 | |
|   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 (*name == ' ') name++;
 | |
|   
 | |
|   if (name && *name)
 | |
|   {
 | |
|     if (isdigit(*name) || *name == '-')
 | |
|     {
 | |
|       log = atoi(name);
 | |
|       if (strchr(name, '@')) log = -log;
 | |
|     }
 | |
|     else
 | |
|     {         
 | |
|       const int len = strlen(name);
 | |
|       if (len == 3 || len == 4)
 | |
|         log = TTable::name2log(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;
 | |
| }
 | |
| 
 | |
| TObject* TFieldref::dup() const
 | |
| {
 | |
|   TFieldref* f = new TFieldref();
 | |
|   f->_fileid = _fileid;
 | |
|   f->_id = _id;
 | |
|   f->_name = _name;
 | |
|   f->_from = _from;
 | |
|   f->_to = _to;
 | |
|   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 <c TFildref> deve avere il seguente formato (solamente NAME e il mandante):
 | |
| //   <nl>FILE->NAME[FROM,TO]
 | |
| 
 | |
| {
 | |
|   int pos = s.find("->");
 | |
|   if (pos > 0)
 | |
|   {
 | |
|     _id = s.left(pos); _id.strip(" ");
 | |
|     _fileid = name2log(_id);
 | |
|     pos += 2;
 | |
|   } 
 | |
|   else 
 | |
|   {
 | |
|     _id.cut(0);
 | |
|     _fileid = pos = 0;
 | |
|   }  
 | |
|   
 | |
|   int par = s.find('[', pos);
 | |
|   _name = s.sub(pos, par); _name.strip(" ");
 | |
|   
 | |
|   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 << ']';
 | |
|   }
 | |
| }
 | |
| 
 | |
| const char* TFieldref::read(const TRectype& rec) const
 | |
| {
 | |
|   static TString256 buffer;
 | |
| 
 | |
|   if (_fileid >= CNF_GENERAL)
 | |
|   {
 | |
|     TToken_string s(_name, '.');
 | |
|     TConfig c(_fileid - CNF_STUDIO, s.get());
 | |
|     buffer = c.get(s.get());
 | |
|   }
 | |
|   else
 | |
|   {
 | |
|     buffer = rec.get(_name);
 | |
|     if (_from > 0 || _to > 0)
 | |
|     {
 | |
|       const int l = buffer.len();
 | |
|       if (_to < l && _to > 0) buffer.cut(_to);
 | |
|       if (_from > 0) buffer.ltrim(_from);
 | |
|     } 
 | |
|   }
 | |
|   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)
 | |
|   {
 | |
|     TString campo(rec.get(_name));
 | |
|     campo.overwrite(val, _from);
 | |
|     rec.put(_name, campo);
 | |
|   }
 | |
|   else  
 | |
|     rec.put(_name, val);
 | |
| }
 | |
| 
 | |
| int TFieldref::len(TRectype &rec) const
 | |
| {
 | |
|   if (_to >= 0) 
 | |
|     return _to - _from;
 | |
|   if (_fileid <= 0) 
 | |
|     return rec.length(_name) - _from;
 | |
|   
 | |
|   const TRectype r(_fileid);
 | |
|   const int len = r.length(_name);
 | |
|   return  len - _from;
 | |
| }
 | |
| 
 | |
| ///////////////////////////////////////////////////////////
 | |
| // TRecord_Array
 | |
| ///////////////////////////////////////////////////////////
 | |
| 
 | |
| TRecord_array::TRecord_array(const TRectype& r, const char* numfield, int first)
 | |
| : _file(r.num()), _num(numfield), _offset(first - 1)
 | |
| { 
 | |
|   read(r); 
 | |
| } 
 | |
| 
 | |
| TRecord_array::TRecord_array(int logicnum, const char* numfield, int first)
 | |
|              : _file(logicnum), _num(numfield), _offset(first - 1)
 | |
| {
 | |
|   set_key(new TRectype(logicnum));
 | |
| } 
 | |
| 
 | |
| TRecord_array::TRecord_array(const TRecord_array& a) 
 | |
|              : TArray(a), _file(a._file), _offset(a._offset), _num(a._num) 
 | |
| {}
 | |
| 
 | |
| void TRecord_array::set_key(TRectype* r)
 | |
| {                                                                     
 | |
|   CHECK(r != NULL, "TRecord_array can't have a null key");
 | |
|   CHECK(r->num() == _file, "Bad key record");
 | |
| 
 | |
|   add(r, 0);                              // Setta il nuovo campo chiave
 | |
|   if (rows() > 0)
 | |
|   {
 | |
|     const RecDes* recd = r->rec_des();    // Descrizione del record della testata
 | |
|     const KeyDes& kd = recd->Ky[0];       // Elenco dei campi della chiave 1
 | |
|     // Copia tutti i campi chiave, tranne l'ultimo, in tutti i records
 | |
|     for (int i = kd.NkFields-2; i >= 0; i--)
 | |
|     {                        
 | |
|       const int nf = kd.FieldSeq[i] % MaxFields;
 | |
|       const RecFieldDes& rf = recd->Fd[nf];  
 | |
|       const TString& val = r->get(rf.Name);
 | |
|       renum_key(rf.Name, val);
 | |
|     }
 | |
|   }
 | |
| }
 | |
| 
 | |
| const TRectype& TRecord_array::key() const 
 | |
| {
 | |
|   TRectype* r = (TRectype*)objptr(0);
 | |
|   CHECK(r, "TRecord_array lost its key");
 | |
|   return *r; 
 | |
| }
 | |
| 
 | |
| bool TRecord_array::exist(int n) const
 | |
| { 
 | |
|   const int i = n > 0 ? n - _offset : -1;
 | |
|   TRectype* r = (TRectype*)objptr(i);
 | |
|   return r != NULL;
 | |
| }  
 | |
| 
 | |
| // @doc EXTERNAL
 | |
| 
 | |
| // @mfunc Ritorna la riga <p r>
 | |
| //
 | |
| // @rdesc Ritorna un TRectype alla riga cercata o eventualemete il reference della nuova creata,
 | |
| //    NULL nes caso non esista e non venga creata.
 | |
| TRectype& TRecord_array::row(
 | |
|           int n,  // @parm Numero della riga da creare
 | |
|           bool create)  // @parm Indica se creare una nuova riga se non esiste:
 | |
|       //
 | |
|       // @flag TRUE | Se la riga <p n> non esiste viene creata
 | |
|       // @flag FALSE | Se la riga <p n> non esiste non viene creata
 | |
| 
 | |
| // @comm Nel caso <p create> si TRUE e venga richiesta una riga non esistente si crea un nuovo
 | |
| //   record copiando la chiave.
 | |
| {                    
 | |
|   const int i = n >= 0 ? n - _offset : -1;
 | |
|   TRectype* r = (TRectype*)objptr(i);
 | |
|   if (r == NULL && create)
 | |
|   {
 | |
|     r = (TRectype*)key().dup();                 // Crea nuovo record copiando la chiave
 | |
|     n = add(r, i) + _offset;                    // Riassegna n se era negativo!
 | |
|     char str[16]; sprintf(str, "%d", n);
 | |
|     r->renum_key(_num, str);                    // Aggiorna campo numero riga
 | |
|   }
 | |
|   CHECKD(r && n > 0, "Bad record number ", n);  
 | |
|   return *r;
 | |
| }
 | |
| 
 | |
| // @doc EXTERNAL
 | |
| 
 | |
| // @mfunc Rinumera il campo chiave in seguito a reinsert
 | |
| //
 | |
| // @rdesc Ritorna se e' stato rinumerato il campo chiave
 | |
| bool TRecord_array::renum_key(
 | |
|      const char* field,   // @parm Campo della chiave a cui assegnare un nuovo valore
 | |
|      const TString& num)  // @parm Nuovo valore da assegnare al campo
 | |
|         // @parm long | num | Nuovo valore da assegnare al campo
 | |
| 
 | |
| // @syntax bool renum_key(const char* field, const TString& num);
 | |
| // @syntax bool renum_key(const char* field, long num);
 | |
| 
 | |
| {      
 | |
|   CHECKS(_num != field, "You can't renumber field ", field);
 | |
|   const TString& curr = key().get(field);
 | |
|   if (curr == num)
 | |
|     return FALSE;
 | |
|   
 | |
|   for (int i = last(); i >= 0; i--)
 | |
|   {
 | |
|     TRectype* r = (TRectype*)objptr(i);
 | |
|     if (r != NULL)
 | |
|       r->renum_key(field, num);
 | |
|   }  
 | |
|   
 | |
|   return TRUE; 
 | |
| }
 | |
| 
 | |
| bool TRecord_array::renum_key(const char* field, long num)
 | |
| {   
 | |
|   CHECKS(num > 0, "Null key value for field: ", field);
 | |
|   TString16 n; n << num;
 | |
|   return renum_key(field, n);
 | |
| }
 | |
| 
 | |
| 
 | |
| int TRecord_array::rec2row(const TRectype& r) const
 | |
| {
 | |
|   CHECK(r.num() == _file, "Incompatible record");
 | |
|   const int n = atoi(r.get(_num)) - _offset;    // Non e' detto che sia un int!
 | |
|   CHECKD(n >= 0 && n < 30000, "Bad line number in record ", n + _offset);
 | |
|   return n;
 | |
| }
 | |
| 
 | |
| int TRecord_array::insert_row(TRectype* r)
 | |
| {      
 | |
|   const int nr = rec2row(*r);
 | |
|   CHECK(nr > 0, "You cannot insert a new key");
 | |
|   const bool shift = exist(nr);
 | |
|   insert(r, nr);
 | |
|   if (shift)                     
 | |
|   {                             
 | |
|     for (int f = last(); f > nr; f = pred(f))         
 | |
|     {    
 | |
|       char n[16]; 
 | |
|       TRectype & rec = row(f, FALSE);
 | |
|       
 | |
|       sprintf(n, "%ld", (long)_offset + f);
 | |
|       renum_key(_num, n);
 | |
|     }
 | |
|   }
 | |
|   return nr;
 | |
| }
 | |
| 
 | |
| int TRecord_array::add_row(TRectype* r)
 | |
| {               
 | |
|   const int nr = rec2row(*r);
 | |
|   add(r, nr);
 | |
|   if (nr == 0 && rows() > 0)                        // Se ho cambiato il record campione
 | |
|   {                                                 // e ci sono altre righe ...
 | |
|     for (int f = r->items()-1; f >= 0; f--)         
 | |
|     {                                               
 | |
|       const char* fn = r->fieldname(f);
 | |
|       const TString& v = r->get(fn);
 | |
|       if (v.not_empty()) renum_key(fn, v);          // ... aggiorna tutte le righe in base
 | |
|     }                                               // ai campi non vuoti del campione
 | |
|   }
 | |
|   return nr;
 | |
| }
 | |
| 
 | |
| // @doc EXTERNAL
 | |
| 
 | |
| // @mfunc Cancella una riga
 | |
| //
 | |
| // @rdesc Ritorna se e' riuscito ad eliminare la riga
 | |
| bool TRecord_array::destroy_row(
 | |
|      int r,   // @parm Numero della riga da eliminare
 | |
|      bool pack) // @parm Indica se compattare gli elementi dell'array (default FALSE)
 | |
|     // @parm const TRectype& | r | Record da eliminare
 | |
| 
 | |
| // @syntax bool destroy_row(int n, bool pack);
 | |
| // @syntax bool destroy_row(const TRectype& r, bool pack);
 | |
| 
 | |
| {                       
 | |
|   CHECKD(r > _offset, "Can't destroy row ", r);
 | |
|   const int index = r - _offset;
 | |
|   const bool ok = destroy(index, pack);
 | |
|   
 | |
|   if (ok && pack)
 | |
|   {
 | |
|     for (int i = size()-1; i > index; i--)
 | |
|     {
 | |
|       TRectype* r = (TRectype*)objptr(i);
 | |
|       if (r != NULL)
 | |
|       {
 | |
|         TString16 n; n << i+_offset;
 | |
|         r->renum_key(_num, n);
 | |
|       }  
 | |
|     }  
 | |
|   }
 | |
|   return ok;
 | |
| } 
 | |
| 
 | |
| void TRecord_array::destroy_rows()
 | |
| {                      
 | |
|   for (int i = last(); i > 0; i = pred(i))
 | |
|     destroy(i);
 | |
| } 
 | |
| 
 | |
| // @doc EXTERNAL
 | |
| 
 | |
| // @mfunc Confronta i campi della chiave 1 scartando l'ultimo
 | |
| //
 | |
| // @rdesc Ritorna il risulato del confronto:
 | |
| //
 | |
| // @flag TRUE | Se i campi sono uguali
 | |
| // @flag FALSE | Se i campi sono diversi
 | |
| bool TRecord_array::good(
 | |
|      const TRectype& rec) const  // @parm Record di cui confrontare i campi
 | |
| {                                
 | |
|   const bool yes = key().same_key(rec, 1, 1);
 | |
|   return yes;
 | |
| }
 | |
| 
 | |
| int TRecord_array::read(TRectype* filter)
 | |
| {            
 | |
|   CHECKD(filter->num() == _file, "Bad key record ", filter->num());
 | |
|   CHECKS(filter->get(_num).empty(), "You can't specify in the filter the field ", (const char*)_num);
 | |
| 
 | |
|   destroy();
 | |
|   int err = NOERR;
 | |
|   if (filter != NULL && !filter->empty())
 | |
|   {
 | |
|     TLocalisamfile f(_file);
 | |
| 
 | |
|     set_key(filter);    
 | |
|     TRectype* rec = (TRectype*)filter->dup();
 | |
|     err = rec->read(f, _isgteq);
 | |
|     for (int e = err; e == NOERR && good(*rec); e = rec->next(f))
 | |
|     {
 | |
|       add_row(rec);
 | |
|       rec = (TRectype*)(key().dup());
 | |
|     }  
 | |
|     delete rec;
 | |
|   }
 | |
|   return err;  
 | |
| }
 | |
| 
 | |
| int TRecord_array::read(const TRectype& filter)
 | |
| { 
 | |
|   TRectype* f = (TRectype*)filter.dup();
 | |
|   return read(f);  
 | |
| }
 | |
| 
 | |
| int TRecord_array::remove_from(int pos) const
 | |
| {                 
 | |
|   int err = NOERR;
 | |
| 
 | |
|   TRectype* rec = (TRectype*)key().dup();
 | |
|   CHECK(!rec->empty(), "Can't use empty key");
 | |
| 
 | |
|   TLocalisamfile f(_file);
 | |
|   rec->put(_num, pos);
 | |
|   for (int e = rec->read(f, _isgteq); e == NOERR && good(*rec); e = rec->next(f))
 | |
|   {                     
 | |
|     const int found = rec->get_int(_num);
 | |
|     if (found >= pos)
 | |
|     {
 | |
|       err = rec->remove(f);             
 | |
|       if (err != NOERR)
 | |
|         break;
 | |
|       pos = found+1;  
 | |
|     }
 | |
|     else
 | |
|       break;    
 | |
|   } 
 | |
|   delete rec;
 | |
| 
 | |
|   return err;
 | |
| }
 | |
| 
 | |
| 
 | |
| int TRecord_array::write(bool re) const
 | |
| {          
 | |
|   const int EOR = 32000;              // End of records on file
 | |
|   int last_on_file = 0;               // Last record found on file
 | |
|   int err = NOERR;
 | |
|     
 | |
|   TLocalisamfile f(_file);
 | |
|   const int u = last();
 | |
|   for (int i = 1; i <= u; i++)
 | |
|   {
 | |
|     const TRectype* r = (TRectype*)objptr(i);
 | |
| 
 | |
|     if (r != NULL) 
 | |
|     {               
 | |
|       if (re)
 | |
|       {
 | |
|         err = r->rewrite(f);     
 | |
|         if (err == _iskeynotfound || err == _iseof || err == _isemptyfile)
 | |
|           err = r->write(f);           
 | |
|         if (err != NOERR) 
 | |
|           break;
 | |
|       }
 | |
|       else
 | |
|       {
 | |
|         err = r->write(f);     
 | |
|         if (err == _isreinsert)
 | |
|         {
 | |
|           err = r->rewrite(f);           
 | |
|           re = TRUE;
 | |
|         }  
 | |
|         if (err != NOERR) 
 | |
|           break;
 | |
|       }    
 | |
|     }
 | |
|     else
 | |
|     {                    
 | |
|       const int pos = i+_offset;                                       
 | |
|       
 | |
|       // Se pos < last_on_file sicuramente la read fallira' per cui la riga
 | |
|       // non puo' esistere su file: non esistendo nemmeno in memoria non devo
 | |
|       // fare assolutamente nulla!
 | |
|       if (pos >= last_on_file)                // Puo' esistere su file?
 | |
|       {
 | |
|         TRectype* rec = (TRectype*)key().dup();
 | |
|         CHECK(!rec->empty(), "TRecord_array has an empty key");
 | |
|         rec->put(_num, pos);
 | |
|         err = rec->read(f, _isgteq);
 | |
|         if (err == NOERR && good(*rec))       // Cerca una riga >= pos sul file
 | |
|         {               
 | |
|           last_on_file = atoi(rec->get(_num));
 | |
|           if (last_on_file == pos)            // La riga c'era ma ora non piu'
 | |
|           {
 | |
|             err = (rec->remove(f));           // Cancello il record indesiderato
 | |
|             if (err != NOERR) 
 | |
|             {         
 | |
|               delete rec;                     // Orrore! Un errore di cancellazione!
 | |
|               break;
 | |
|             }  
 | |
|           }
 | |
|         } 
 | |
|         else
 | |
|           last_on_file = EOR;                 // Sul file non ci sono piu' righe da cancellare
 | |
|         delete rec;   
 | |
|       }  
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   // Cancella eventuali residui successivi
 | |
|   if (err == NOERR && last_on_file != EOR)
 | |
|     remove_from(i + _offset);
 | |
|   
 | |
|   return err;
 | |
| }
 | |
| 
 | |
| int TRecord_array::remove() const
 | |
| {
 | |
|   return remove_from(1 + _offset);
 | |
| }
 | |
| 
 | |
| ///////////////////////////////////////////////////////////
 | |
| // 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
 | |
| 
 | |
|     int which_file = atoi(tn);                    // Non spostare questa riga (tn puo' cambiare!)
 | |
|     if (which_file == 0)
 | |
|       which_file = TTable::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);
 | |
|     
 | |
|     for (int f = 0; f < trec.fields(); f++)
 | |
|     {  
 | |
|       ttmp = trec.fielddef(f);  
 | |
|       const TString16 name(ttmp.get(0));   
 | |
|       if (!name.blank())
 | |
|       { 
 | |
|         TString80 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,0,0,"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 <p fld> 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 (defualt "")
 | |
| 
 | |
| // @comm Occorre sche sia selezioanto un file, diversamente viene considerato
 | |
| //   selezionato il promo file
 | |
| {              
 | |
|   TArray_sheet sht(-1,-1,0,0,"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 assgnare 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 = 0; i < fld.items(); 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), _menu(FALSE), _cur_file(0), _cur_field(0),
 | |
|   _cfile(80), _cfield(80)
 | |
| { 
 | |
|   read_rel();
 | |
|   if (_files.items() > 0) 
 | |
|   {
 | |
|     _cfile  = _files.row(0);
 | |
|     _cfield = "";
 | |
|   }
 | |
| }
 | |
| 
 | |
| TRelation_description::~TRelation_description()
 | |
| { 
 | |
|   if (_menu) remove_menu(); 
 | |
| }  
 | |
| 
 | |
| 
 | |
| // *** EOF relation.cpp
 | |
| 
 |