Files correlati : ve1 Ricompilazione Demo : [ ] Commento : Corretta gestione MESSAGE _TABLEREAD sulle tabelle di modulo git-svn-id: svn://10.65.10.50/trunk@18729 c028cbd2-c16b-5b4b-a496-9718f37d4682
		
			
				
	
	
		
			3063 lines
		
	
	
		
			72 KiB
		
	
	
	
		
			C++
		
	
	
		
			Executable File
		
	
	
	
	
			
		
		
	
	
			3063 lines
		
	
	
		
			72 KiB
		
	
	
	
		
			C++
		
	
	
		
			Executable File
		
	
	
	
	
#include <config.h>
 | 
						||
#include <diction.h>
 | 
						||
#include <expr.h>
 | 
						||
#include <extcdecl.h>
 | 
						||
#include <prefix.h>
 | 
						||
#include <progind.h>
 | 
						||
#include <relation.h>
 | 
						||
#include <sheet.h>
 | 
						||
#include <sort.h>
 | 
						||
#include <tabmod.h>
 | 
						||
#include <tabutil.h>
 | 
						||
#include <utility.h>
 | 
						||
 | 
						||
#include <codeb.h>
 | 
						||
// *** 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++)
 | 
						||
  {
 | 
						||
    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++)
 | 
						||
    {
 | 
						||
      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 <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, "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 <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 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.
 | 
						||
//       <nl>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));
 | 
						||
    
 | 
						||
    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,  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++)
 | 
						||
  {
 | 
						||
    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
 | 
						||
//       <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, 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;
 | 
						||
}
 | 
						||
 | 
						||
///////////////////////////////////////////////////////////
 | 
						||
// 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 = 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 nfields = kd.NkFields;
 | 
						||
		int foc = 0;
 | 
						||
    for (int i = 0; i < nfields; i++)
 | 
						||
    { 
 | 
						||
      const int nf = kd.FieldSeq[i] % MaxFields;
 | 
						||
      const RecFieldDes& rf = recd.Fd[nf];  
 | 
						||
			const int toc = foc + kd.FromCh[i] == 255 ? rf.Len : (kd.ToCh[i] - kd.FromCh[i] + 1);
 | 
						||
 | 
						||
			if (toc >= len)
 | 
						||
      {
 | 
						||
        if (str[foc] > ' ' && rf.TypeF == _alfafld)
 | 
						||
          str.left_just(toc, ' ');
 | 
						||
        break;
 | 
						||
      } 
 | 
						||
			foc = toc + 1;
 | 
						||
    }
 | 
						||
  }
 | 
						||
  str.replace('~', ' '); // non togliere posson arivare record con gia' le tildi
 | 
						||
}
 | 
						||
 | 
						||
// @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 <20> 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 <20> 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)
 | 
						||
{
 | 
						||
  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 <t TReclock>)
 | 
						||
{
 | 
						||
  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!
 | 
						||
//       <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(')');
 | 
						||
    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();
 | 
						||
    const int flen = f.len(r);
 | 
						||
 | 
						||
		if (!id.empty())
 | 
						||
		{
 | 
						||
			_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 = d.string(ANSI);
 | 
						||
      fld_type = _datefld;
 | 
						||
      fld_len = 10;
 | 
						||
    }
 | 
						||
    else
 | 
						||
    {
 | 
						||
      if (is_up) 
 | 
						||
        sf.upper();
 | 
						||
    }
 | 
						||
 | 
						||
    TString8 fmt;
 | 
						||
    if (fld_type == _alfafld || fld_type == _datefld)
 | 
						||
      fmt.format("%%-%ds", fld_len);
 | 
						||
    else  
 | 
						||
      fmt.format("%%%ds", fld_len);
 | 
						||
    k << format(fmt, (const char*)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 <c TFieldref> 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 
 | 
						||
  {
 | 
						||
    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  
 | 
						||
  {
 | 
						||
    rec.put(_name, val);
 | 
						||
  }  
 | 
						||
}
 | 
						||
 | 
						||
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 <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 (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 <p npos> 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 <p npos> 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 <t TReclock>)
 | 
						||
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 <t TReclock>)
 | 
						||
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 <t TReclock>)
 | 
						||
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 <t TReclock>)
 | 
						||
int TSortedfile::prev(word lockop )
 | 
						||
{
 | 
						||
  int err = _curs->pos() > 0 ? NOERR : _isbof;
 | 
						||
  if (err == NOERR)
 | 
						||
    _curs->pred_item();
 | 
						||
  setstatus(err);
 | 
						||
  return err;
 | 
						||
}
 | 
						||
 | 
						||
// @mfunc Salta <p nrec> record dalla posizione corrente (vedi <t TReclock>)
 | 
						||
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 <t TReclock>)
 | 
						||
int TSortedfile::reread(word lockop )
 | 
						||
{
 | 
						||
  return reread(curr(),lockop);
 | 
						||
}
 | 
						||
// @mfunc Rilegge l'ultimo record letto e lo copia in <p rec> (vedi <t TReclock>)
 | 
						||
int TSortedfile::reread(TRectype& rec, word lockop)
 | 
						||
{
 | 
						||
  return 0;
 | 
						||
}
 | 
						||
 | 
						||
 | 
						||
// @mfunc Legge il record (vedi <t TReclock> e <t TIsamop>)
 | 
						||
int TSortedfile::read(word op , word lockop )
 | 
						||
{
 | 
						||
  return read(curr(),op,lockop);
 | 
						||
}
 | 
						||
// @mfunc Legge il record e lo copia in <p rec> (vedi <t TReclock> e <t TIsamop>)
 | 
						||
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 <p nrec> e lo copia in <p rec> (vedi <t TReclock>)
 | 
						||
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 <p nrec> e lo copia in <p rec> (vedi <t TReclock>)
 | 
						||
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 <p rec>
 | 
						||
 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 <p rec>)
 | 
						||
 int TSortedfile::rewrite(const TRectype& rec)
 | 
						||
 {
 | 
						||
  NFCHECK("Operazione 'rewrite' non consentita sul TSortedfile");
 | 
						||
  return 0;
 | 
						||
 }
 | 
						||
 | 
						||
// @mfunc Riscrive un record alla posizione <p nrec> copiando da <p rec>
 | 
						||
int TSortedfile::rewriteat(TRecnotype nrec)
 | 
						||
{
 | 
						||
  return rewriteat(curr(),nrec);
 | 
						||
}
 | 
						||
// @mfunc Riscrive un record alla posizione <p nrec> copiando da <p rec>
 | 
						||
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 <p rec>
 | 
						||
int TSortedfile::remove(const TRectype& rec)
 | 
						||
{
 | 
						||
  NFCHECK("Operazione 'remove' non consentita sul TSortedfile");
 | 
						||
  return 0;
 | 
						||
}
 | 
						||
 | 
						||
// @mfunc Attiva la chiave <p nkey> sul file aperto
 | 
						||
void TSortedfile::setkey(int nkey)
 | 
						||
{
 | 
						||
  _curs->setkey(nkey);
 | 
						||
}
 | 
						||
// @mfunc Resetta la regione del file (chiama <p TCursor::setregion>
 | 
						||
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
 | 
						||
 |