Files correlati : cg4.exe Commento : Nella stampa registri la variabile agenzie di viagio non ere inizializzata per cui poteva considerare agenzie di viaggio per l'IVA per cassa anche ditte che non lo erano. Nella liquidazione IVA la restore status non funzionava più. Questo aveva efftto per i trimestrali che cacolano 3 mesi e stampano sul terzo che non veniva mai esaminato.
3359 lines
82 KiB
C++
Executable File
3359 lines
82 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) const;
|
||
|
||
void print_on(TToken_string& out) const;
|
||
|
||
// Funzioni per costruttore di copia
|
||
|
||
virtual TRelationdef & copy(const TRelationdef & reldef);
|
||
virtual TRelationdef* dup() const { return new TRelationdef(*this); }
|
||
virtual TRelationdef& operator =(const TRelationdef & reldef) { return copy(reldef); }
|
||
|
||
TRelationdef(const TRelation* rel, int file, byte key,
|
||
int linkto, const char* relexprs, int alias,
|
||
bool allow_lock);
|
||
// Costruttore di copia
|
||
TRelationdef(const TRelationdef& reldef);
|
||
|
||
virtual ~TRelationdef() {}
|
||
};
|
||
|
||
TRelationdef & TRelationdef::copy(const TRelationdef & reldef)
|
||
{
|
||
_rel = reldef._rel;
|
||
_num = reldef._num;
|
||
_numto = reldef._numto;
|
||
_alias = reldef._alias;
|
||
_key = reldef._key;
|
||
_fields = reldef._fields;
|
||
_exprs = reldef._exprs;
|
||
_first_match = reldef._first_match;
|
||
_allow_lock = reldef._allow_lock;
|
||
_write_enable = reldef._write_enable;
|
||
return *this;
|
||
}
|
||
|
||
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);
|
||
}
|
||
}
|
||
|
||
TRelationdef::TRelationdef(const TRelationdef & reldef)
|
||
{
|
||
copy(reldef);
|
||
}
|
||
|
||
|
||
void TRelationdef::print_on(ostream& out) const
|
||
{
|
||
const TLocalisamfile& f = _rel->file(_num);
|
||
|
||
out << "JOIN ";
|
||
print_name(out, f);
|
||
|
||
if (_numto > 0)
|
||
{
|
||
out << " TO ";
|
||
const int alias = _rel->reldef(_numto-1).alias();
|
||
if (alias > 0) out << alias << '@';
|
||
else
|
||
{
|
||
const TLocalisamfile& t = _rel->file(_numto);
|
||
print_name(out, t);
|
||
}
|
||
}
|
||
|
||
if (_key > 1) out << " KEY " << (int)_key;
|
||
|
||
if (_alias > 0) out << " ALIAS " << _alias;
|
||
|
||
for (int i = 0; i < _fields.items(); i++)
|
||
{
|
||
if (i == 0) out << " INTO";
|
||
out << ' ' << _fields[i] << '=';
|
||
if (_forced[i]) out << '=';
|
||
out << _exprs[i];
|
||
if (_altexprs.objptr(i))
|
||
out << '(' << _altexprs[i] << ')';
|
||
}
|
||
}
|
||
|
||
|
||
void TRelationdef::print_on(TToken_string& out) const
|
||
{
|
||
const TLocalisamfile& f = _rel->file(_num);
|
||
|
||
out.cut(0);
|
||
print_name(out, f);
|
||
|
||
// add description
|
||
const char* name = f.name();
|
||
if (f.tab())
|
||
name = TDir::tab_des(name);
|
||
else
|
||
name = prefix().description(name);
|
||
out.add(name);
|
||
|
||
out << '|';
|
||
|
||
if (_numto > 0)
|
||
{
|
||
const int alias = _rel->reldef(_numto-1).alias();
|
||
if (alias > 0)
|
||
out << alias << '@';
|
||
else
|
||
{
|
||
const TLocalisamfile& t = _rel->file(_numto);
|
||
print_name(out, t);
|
||
}
|
||
} else out << ' ';
|
||
|
||
out.add(_key);
|
||
out.add(_alias);
|
||
out << '|';
|
||
for (int i = 0; i < _fields.items(); i++)
|
||
{
|
||
if (i) out << ' ';
|
||
out << _fields[i] << '=';
|
||
if (_forced[i]) out << '=';
|
||
out << _exprs[i];
|
||
if (_altexprs.objptr(i))
|
||
out << '(' << _altexprs[i] << ')';
|
||
}
|
||
}
|
||
|
||
|
||
const char* TRelationdef::evaluate_expr(int j, const TLocalisamfile& to) const
|
||
{
|
||
const TRectype& rec = to.curr();
|
||
TExpression& expr = (TExpression&)_exprs[j];
|
||
for (int k = 0; k < expr.numvar(); k++)
|
||
{
|
||
if (expr.varname(k)[0] != '#')
|
||
{
|
||
const TFieldref fr(expr.varname(k), rec.num());
|
||
expr.setvar(k, fr.read(rec));
|
||
}
|
||
}
|
||
|
||
const char* val = (const char*)expr.as_string();
|
||
if (*val == '\0' && _altexprs.objptr(j))
|
||
{
|
||
TExpression& altexpr = (TExpression&)_altexprs[j];
|
||
for (int k = 0; k < altexpr.numvar(); k++)
|
||
{
|
||
if (altexpr.varname(k)[0] != '#')
|
||
{
|
||
const TFieldref fr(altexpr.varname(k), rec.num());
|
||
altexpr.setvar(k, fr.read(rec));
|
||
}
|
||
}
|
||
val = (const char*)altexpr.as_string();
|
||
}
|
||
|
||
return val;
|
||
}
|
||
|
||
///////////////////////////////////////////////////////////
|
||
// TRelation
|
||
///////////////////////////////////////////////////////////
|
||
|
||
TRelation & TRelation::copy(const TRelation & rel)
|
||
{
|
||
_errors = rel._errors;
|
||
_status = rel._status;
|
||
FOR_EACH_ARRAY_ITEM(rel._files, i, file)
|
||
{
|
||
TLocalisamfile * f = (TLocalisamfile *)file;
|
||
int logic = f->num();
|
||
|
||
_files.add(new TLocalisamfile(logic));
|
||
lfile(logic).curr() = f->curr();
|
||
lfile(logic).setkey(f->getkey());
|
||
lfile(logic).read();
|
||
}
|
||
_reldefs = rel._reldefs;
|
||
return *this;
|
||
}
|
||
|
||
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(const TRelation & rel)
|
||
{
|
||
copy(rel);
|
||
}
|
||
|
||
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()[0] == 'X';
|
||
|
||
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);
|
||
const TFieldref& s = (const TFieldref&) rd._fields[j];
|
||
s.write(expr, furr);
|
||
} // for each field
|
||
|
||
const TReclock lck = rd._allow_lock ? lockop : _nolock;
|
||
from.read(_isgteq, lck);
|
||
|
||
// record read : if not found, zero current record
|
||
bool eq = !from.bad();
|
||
for (int kk = 0; eq && kk < rd._fields.items(); kk++)
|
||
{
|
||
TFieldref& fl = (TFieldref&)rd._fields[kk];
|
||
TString80 f_fr(fl.read(furr));
|
||
TString80 f_ex(rd.evaluate_expr(kk, to));
|
||
if (rd._forced[kk])
|
||
eq = f_fr == f_ex;
|
||
else
|
||
{
|
||
f_fr.rtrim(); f_ex.rtrim();
|
||
eq = f_fr.compare(f_ex, f_ex.len()) == 0;
|
||
|
||
// Becca anche 000001=1
|
||
if (!eq && real::is_natural(f_fr) && real::is_natural(f_ex))
|
||
eq = atol(f_fr) == atol(f_ex);
|
||
}
|
||
}
|
||
rd._first_match = eq;
|
||
if (eq)
|
||
from.setstatus(NOERR);
|
||
else
|
||
furr.zero();
|
||
} // for each relation
|
||
return _errors;
|
||
}
|
||
|
||
|
||
// @doc EXTERNAL
|
||
|
||
// @mfunc Posiziona il numero logico sul record successivo
|
||
//
|
||
// @rdesc Ritorna se ha trovato la corrispondenza:
|
||
//
|
||
// @flag TRUE | Corrispondenza trovata
|
||
// @flag FALSE | Corrispondenza non trovata
|
||
bool TRelation::next_match(
|
||
int logicnum, // @parm Numero logico del file da fare avanzare
|
||
const char* fieldlist, // @parm Lista dei campi che devono rimanere costanti (default NULL)
|
||
int nkey) // @parm Numero della chiave (default 0)
|
||
|
||
// @comm Il posizionamento del numero logico non deve essere riferita al file
|
||
// principale; in ogni caso viene considerata consistente ad eccezione dei
|
||
// casi di inconsistenza nella prima posizione.
|
||
|
||
{
|
||
if (logicnum == file().num())
|
||
{
|
||
next();
|
||
return file().good();
|
||
}
|
||
|
||
const int i = log2ind(logicnum);
|
||
CHECKD(i != NOTFOUND,"Nonexistent file referenced in relation ",logicnum);
|
||
|
||
int j;
|
||
for (j = 0; j < _reldefs.items(); j++)
|
||
if (reldef(j).num() == i) break;
|
||
|
||
TLocalisamfile& from = file(i);
|
||
|
||
reldef(j)._first_match = FALSE;
|
||
|
||
if (from.bad())
|
||
return FALSE; // && vaffanculo();
|
||
|
||
const TRecnotype last = from.recno();
|
||
|
||
bool ok = TRUE; // Corrispondenza trovata ?
|
||
|
||
if (fieldlist == NULL)
|
||
{
|
||
TRectype rec(from.num());
|
||
reldef(j).load_rec(rec, from.curr());
|
||
|
||
from.setkey(reldef(j)._key);
|
||
from.next();
|
||
ok = (from.good() && from.curr() == rec);
|
||
for (int kk = 0; ok && kk < reldef(j)._fields.items(); kk++)
|
||
{
|
||
if (reldef(j)._forced[kk])
|
||
{
|
||
TFieldref& fl = (TFieldref&)reldef(j)._fields[kk];
|
||
const TString f_fr(fl.read(from.curr()));
|
||
const TString f_to(fl.read(rec));
|
||
|
||
ok = (f_fr == f_to);
|
||
}
|
||
}
|
||
if (ok) from.setstatus(NOERR);
|
||
}
|
||
else
|
||
{
|
||
if (nkey > 0)
|
||
from.setkey(nkey);
|
||
TToken_string fields(fieldlist);
|
||
TToken_string old(80);
|
||
const char * f;
|
||
for (f = fields.get(0); f; f = fields.get())
|
||
old.add(from.curr().get(f));
|
||
|
||
ok = from.next() == NOERR;
|
||
|
||
old.restart();
|
||
|
||
for (f = fields.get(0); f && ok; f = fields.get())
|
||
{
|
||
const TFixed_string v(from.curr().get(f));
|
||
const char* o = old.get();
|
||
if (v != o)
|
||
ok = false;
|
||
}
|
||
}
|
||
|
||
if (!ok)
|
||
{
|
||
from.readat(last);
|
||
return FALSE;
|
||
}
|
||
|
||
// Riposiziona gli eventuali files successivi
|
||
position_rels(_isequal, _nolock, j+1);
|
||
reldef(j)._first_match = FALSE;
|
||
|
||
return ok;
|
||
}
|
||
|
||
|
||
// @doc EXTERNAL
|
||
|
||
// @mfunc Controlla se c'e' un record ed e' il primo match (non si e' mai fatta
|
||
// <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;
|
||
}
|
||
|
||
void TRelation::mask2rel(const TMask & m)
|
||
{
|
||
for (int i = 0; i < _reldefs.items(); i++)
|
||
{
|
||
TRelationdef& rdi = reldef(i);
|
||
|
||
for (int j = 0; j < rdi._exprs.items(); j++)
|
||
{
|
||
TExpression& expr = (TExpression&)rdi._exprs[j];
|
||
for (int k = 0; k < expr.numvar(); k++)
|
||
{
|
||
const char * name = expr.varname(k);
|
||
if (name[0] == '#')
|
||
expr.setvar(k, m.focus_field().evaluate_field(name));
|
||
}
|
||
if (rdi._altexprs.objptr(j))
|
||
{
|
||
TExpression& altexpr = (TExpression&)rdi._altexprs[j];
|
||
for (int k = 0; k < altexpr.numvar(); k++)
|
||
{
|
||
const char * name = altexpr.varname(k);
|
||
if (name[0] == '#')
|
||
altexpr.setvar(k, m.focus_field().evaluate_field(name));
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
///////////////////////////////////////////////////////////
|
||
// TCursor //
|
||
///////////////////////////////////////////////////////////
|
||
|
||
HIDDEN bool __evalcondition(const TRelation& /* r */,TExpression* cond, const TArray& frefs)
|
||
{
|
||
for (int i = cond->numvar() - 1; i >= 0; i--)
|
||
{
|
||
const TRecfield* fr = (const TRecfield*)frefs.objptr(i);
|
||
if (fr)
|
||
cond->setvar(i, (const char*)*fr);
|
||
}
|
||
return cond->as_bool();
|
||
}
|
||
|
||
// @doc EXTERNAL
|
||
|
||
static TFilename _last_name;
|
||
static FILE* _last_ndx = NULL;
|
||
static bool _last_created = FALSE;
|
||
|
||
// @mfunc Apre il file di indice
|
||
//
|
||
// @rdesc Ritorna l'handle del file aperto
|
||
FILE* TCursor::open_index(
|
||
bool create) // @parm Indica se creare l'indice nel caso manchi (default FALSE)
|
||
{
|
||
_last_created = create;
|
||
if (_indexname.empty())
|
||
{
|
||
TString8 radix;
|
||
radix.format("c%d_", file().num());
|
||
_indexname.temp(radix);
|
||
}
|
||
|
||
if (_indexname != _last_name || create)
|
||
{
|
||
if (_last_ndx != NULL)
|
||
{
|
||
fclose(_last_ndx);
|
||
_last_ndx = NULL;
|
||
}
|
||
fopen_s(&_last_ndx, _indexname, create ? "wb" : "rb"); // Secured _last_ndx = fopen(_indexname, create ? "wb" : "rb");
|
||
if (_last_ndx == NULL)
|
||
fatal_box("Can't use cursor index for file %d: '%s'\n",
|
||
file().num(), (const char*)_indexname);
|
||
_last_name = _indexname;
|
||
}
|
||
return _last_ndx;
|
||
}
|
||
|
||
void TCursor::close_index(FILE* f)
|
||
{
|
||
CHECK(f == _last_ndx, "Bad open/close index sequence");
|
||
if (_last_created)
|
||
{
|
||
fclose(_last_ndx);
|
||
_last_ndx = NULL;
|
||
_last_name.cut(0);
|
||
_last_created = FALSE;
|
||
}
|
||
}
|
||
|
||
int TCursor::read_page(long page)
|
||
{
|
||
_pagefirstpos = page * _cmaxelpage;
|
||
CHECKD(_pagefirstpos < _totrec, "Bad cursor page ", page);
|
||
|
||
unsigned long elements = _totrec - _pagefirstpos;
|
||
if (elements > _cmaxelpage)
|
||
elements = _cmaxelpage;
|
||
const unsigned long startpos = sizeof(TRecnotype) * _pagefirstpos;
|
||
const unsigned long size = sizeof(TRecnotype) * elements;
|
||
|
||
FILE * indf = open_index();
|
||
fseek(indf, startpos, SEEK_SET);
|
||
if (fread(_page, size, 1, indf) != 1)
|
||
fatal_box("Can't read page number %d in cursor n. %d\n", page, file().num());
|
||
|
||
return (int) elements;
|
||
}
|
||
|
||
bool TCursor::has_simple_filter() const
|
||
{
|
||
bool yes = _filterfunction == NULL;
|
||
if (yes && _fexpr != NULL && _fexpr->numvar() > 0)
|
||
{
|
||
const RecDes& recd = curr().rec_des(); // Descrizione del record della testata
|
||
const KeyDes& kd = recd.Ky[_nkey-1]; // Elenco dei campi della chiave del cursore
|
||
for (int i = _fexpr->numvar()-1; yes && i >= 0; i--)
|
||
{
|
||
const char * vn = _fexpr->varname(i);
|
||
TFieldref f(vn, 0);
|
||
yes = f.file() == 0;
|
||
if (yes)
|
||
{
|
||
for (int k = kd.NkFields-1; k >= 0; k--)
|
||
{
|
||
const int nf = kd.FieldSeq[k] % MaxFields;
|
||
const RecFieldDes& rf = recd.Fd[nf];
|
||
yes = kd.FromCh[k] == 255 && f.name() == rf.Name;
|
||
if (yes) break;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
return yes;
|
||
}
|
||
|
||
TRecnotype TCursor::buildcursor(TRecnotype rp)
|
||
{
|
||
_pagefirstpos = 0L;
|
||
memset(_page, 0, sizeof(TRecnotype)*_cmaxelpage);
|
||
_fpkey.destroy();
|
||
|
||
const int fhnd = file().handle();
|
||
if (DB_reccount(fhnd) == 0)
|
||
return 0;
|
||
|
||
TRecnotype oldrecno= RECORD_NON_FISICO,ap = 0;
|
||
int pagecnt = 0;
|
||
FILE* indf = NULL;
|
||
|
||
const int l = strlen(to());
|
||
double perc = 1.0;
|
||
|
||
if (l > 0 && DB_index_seek(fhnd, to()) >= 0)
|
||
perc = DB_pos_perc(fhnd);
|
||
|
||
int junk = DB_index_seek(fhnd, from());
|
||
if (junk < 0) junk=get_error(junk);
|
||
if (junk == _iseof) return 0;
|
||
|
||
perc -= DB_pos_perc(fhnd);
|
||
|
||
// DB_index_recno(fhnd); // A cosa cavolo serve?
|
||
_pos=-1;
|
||
|
||
const bool filtered = has_filter();
|
||
const bool simple_filter = filtered && has_simple_filter();
|
||
|
||
// Barra di progresso
|
||
const long items = (long)((double)_if->lfile().items() * perc);
|
||
TString msg("Elaborazione ");
|
||
TDir d(_if->lfile().num());
|
||
|
||
msg << d.des();
|
||
|
||
TProgress_monitor pi(items, msg);
|
||
|
||
while (!DB_index_eof(fhnd))
|
||
{
|
||
pi.add_status();
|
||
const char *s0 = DB_index_getkey(fhnd);
|
||
if (l && (strncmp(to(), s0, l) < 0))
|
||
break;
|
||
const TRecnotype recno = DB_index_recno(fhnd);
|
||
if (recno == oldrecno)
|
||
break; // means that no more keys are available
|
||
oldrecno=recno;
|
||
|
||
bool to_be_added = true;
|
||
|
||
if (filtered)
|
||
{
|
||
if (simple_filter)
|
||
to_be_added = simple_filtercursor(s0);
|
||
else
|
||
to_be_added = filtercursor(recno);
|
||
}
|
||
|
||
if (to_be_added)
|
||
{
|
||
_page[pagecnt++] = recno;
|
||
if (_pos < 0 && recno == rp)
|
||
_pos = ap;
|
||
ap++;
|
||
}
|
||
|
||
if (pagecnt == _cmaxelpage)
|
||
{
|
||
_fpkey.add(new TToken_string(s0));
|
||
pagecnt = 0;
|
||
if (indf == NULL)
|
||
{
|
||
indf = open_index(TRUE);
|
||
fseek(indf, 0L, SEEK_SET);
|
||
}
|
||
fwrite(_page,sizeof(TRecnotype)*_cmaxelpage, 1, indf);
|
||
}
|
||
DB_index_next(fhnd);
|
||
} // while
|
||
if (indf != NULL)
|
||
{
|
||
if (pagecnt > 0)
|
||
fwrite(_page,sizeof(TRecnotype)*pagecnt, 1, indf);
|
||
close_index(indf);
|
||
}
|
||
|
||
_pagefirstpos = ap > _cmaxelpage ? -(_cmaxelpage + 1) : 0L ;
|
||
if (_pos < 0L)
|
||
_pos = 0L;
|
||
|
||
return ap;
|
||
}
|
||
|
||
bool TCursor::call_filterfunction(TRecnotype recno) const
|
||
{
|
||
bool ok = true;
|
||
if (_filterfunction)
|
||
{
|
||
const int handle = file().handle();
|
||
// memorizzo la chiave prima di eventuali spostamenti
|
||
const TString256 s0 = DB_index_getkey(handle);
|
||
ok = _filterfunction(_if);
|
||
// ripristino la chiave dopo eventuali spostamenti
|
||
if (recno != DB_index_recno(handle))
|
||
DB_index_go(handle, s0, recno);
|
||
}
|
||
return ok;
|
||
}
|
||
bool TCursor::filtercursor(TRecnotype recno)
|
||
{
|
||
file().readat(recno);
|
||
if (update_relation())
|
||
{
|
||
// memorizzo la chiave prima di eventuali spostamenti
|
||
const TString256 s0 = DB_index_getkey(file().handle());
|
||
|
||
_if->update(-filter_limit());
|
||
|
||
// ripristino la chiave dopo eventuali spostamenti
|
||
const int handle = file().handle();
|
||
if (recno != DB_index_recno(handle))
|
||
DB_index_go(handle, s0, recno);
|
||
}
|
||
|
||
bool ok = call_filterfunction(recno);
|
||
|
||
if (ok && _fexpr)
|
||
ok = __evalcondition(*_if, _fexpr, _frefs);
|
||
return ok;
|
||
}
|
||
|
||
bool TCursor::simple_filtercursor(const char* key) const
|
||
{
|
||
if (_fexpr == NULL)
|
||
return true;
|
||
|
||
const RecDes& recd = curr().rec_des(); // Descrizione del record della testata
|
||
const KeyDes& kd = recd.Ky[_nkey-1]; // Elenco dei campi della chiave del cursore
|
||
|
||
TFieldref f;
|
||
for (int i = _fexpr->numvar()-1; i >= 0; i--)
|
||
{
|
||
f = _fexpr->varname(i);
|
||
int offset = 0;
|
||
for (int k = 0; k < kd.NkFields; k++)
|
||
{
|
||
const int nf = kd.FieldSeq[k] % MaxFields;
|
||
const RecFieldDes& rf = recd.Fd[nf];
|
||
int len = rf.Len;
|
||
if (f.name() == rf.Name)
|
||
{
|
||
offset += f.from();
|
||
if (f.to() > f.from())
|
||
len = f.to() - f.from();
|
||
else
|
||
len -= f.from();
|
||
char* val = (char*)(key+offset);
|
||
char oldchar = '\0';
|
||
int l;
|
||
|
||
for (l = len; l >= 0; l--) // rtrim
|
||
{
|
||
if (l == 0 || val[l-1] != ' ')
|
||
{
|
||
oldchar = val[l];
|
||
val[l] = '\0';
|
||
break;
|
||
}
|
||
}
|
||
switch (rf.TypeF)
|
||
{
|
||
case _boolfld:
|
||
_fexpr->setvar(i, strchr("1STXY", *val)!=NULL ? "X" : " ");
|
||
break;
|
||
case _intfld:
|
||
case _longfld:
|
||
{
|
||
const char* v;
|
||
for (v = val; *v == ' ' || *v == '0'; v++);
|
||
_fexpr->setvar(i, v);
|
||
}
|
||
break;
|
||
default:
|
||
_fexpr->setvar(i, val);
|
||
break;
|
||
}
|
||
if (oldchar)
|
||
val[l] = oldchar;
|
||
break;
|
||
}
|
||
offset += len;
|
||
}
|
||
}
|
||
return _fexpr->as_bool();
|
||
}
|
||
|
||
bool TCursor::ok() const
|
||
{
|
||
if (file().bad())
|
||
return FALSE;
|
||
|
||
const TRectype& rec = file().curr();
|
||
|
||
const TFixed_string key(rec.key(_nkey));
|
||
const TString& kf = from();
|
||
const TString& kt = to();
|
||
|
||
if (file().tab())
|
||
{
|
||
if (key.compare(kf, 3) < 0 || (kt.not_empty() && kt.compare(key, 3) < 0))
|
||
return FALSE;
|
||
}
|
||
else
|
||
{
|
||
if (key < kf || (kt.not_empty() && kt.compare(key, kt.len()) < 0))
|
||
return FALSE;
|
||
}
|
||
|
||
const TRecnotype old = file().recno();
|
||
|
||
if (update_relation())
|
||
{
|
||
_if->update();
|
||
if (DB_recno(file().handle()) != old)
|
||
file().readat(old);
|
||
}
|
||
|
||
if (call_filterfunction(old) &&
|
||
(_fexpr ? __evalcondition(*_if, _fexpr, _frefs) : true))
|
||
return true;
|
||
|
||
return false;
|
||
}
|
||
|
||
bool TCursor::changed()
|
||
{
|
||
if (_frozen && _lastrec > 0L)
|
||
return FALSE;
|
||
|
||
const TLocalisamfile& f = file();
|
||
|
||
if (prefix().get_dirtype(f.num()) == _nordir &&
|
||
_index_firm != prefix().get_codditta())
|
||
return TRUE;
|
||
|
||
const int handle = f.handle();
|
||
const TRecnotype eod = DB_reccount(handle);
|
||
if (_lastrec != eod ||
|
||
_lastkrec != DB_changed(handle))
|
||
return TRUE;
|
||
|
||
if (!f.curr().valid())
|
||
return TRUE;
|
||
|
||
return FALSE;
|
||
}
|
||
|
||
TRecnotype TCursor::update()
|
||
{
|
||
TWait_cursor hourglass;
|
||
TLocalisamfile& f = file();
|
||
f.setkey(_nkey);
|
||
if (f.curr().empty())
|
||
f.zero(); // Usare sempre l'azzeratore esperto del file non direttamente f.curr().zero()
|
||
f.read(_isgteq);
|
||
|
||
_totrec = buildcursor(f.recno());
|
||
|
||
const int handle = f.handle();
|
||
const TRecnotype eod = DB_reccount(handle);
|
||
|
||
_lastrec = eod;
|
||
_index_firm = prefix().get_codditta();
|
||
_lastkrec = DB_changed(handle);
|
||
|
||
return _totrec;
|
||
}
|
||
|
||
|
||
HIDDEN void matildator(const TRectype& oldrec, int key, bool tilde, TString& str)
|
||
{
|
||
/*
|
||
if (tilde)
|
||
{
|
||
TRectype rec(oldrec); // Utile duplicazione di record
|
||
const RecDes* recd = rec.rec_des(); // Descrizione del record della testata
|
||
const KeyDes& kd = recd->Ky[key-1]; // Elenco dei campi della chiave
|
||
TString80 val;
|
||
for (int i = kd.NkFields-1; i >= 0; i--)
|
||
{
|
||
const int nf = kd.FieldSeq[i] % MaxFields;
|
||
const RecFieldDes& rf = recd->Fd[nf];
|
||
val = rec.get(rf.Name);
|
||
if (val.not_empty())
|
||
{
|
||
if (rf.TypeF == _alfafld && val.len() < rf.Len)
|
||
{
|
||
val.left_just(rf.Len, '~');
|
||
rec.put(rf.Name, val);
|
||
}
|
||
break;
|
||
}
|
||
}
|
||
str = rec.key(key);
|
||
}
|
||
else
|
||
str = oldrec.key(key);
|
||
str.replace('~', ' ');
|
||
*/
|
||
str = oldrec.key(key);
|
||
if (tilde)
|
||
{
|
||
const RecDes& recd = oldrec.rec_des(); // Descrizione del record della testata
|
||
const KeyDes& kd = recd.Ky[key-1]; // Elenco dei campi della chiave
|
||
const int len = str.len();
|
||
const int kfields = kd.NkFields;
|
||
int foc = 0;
|
||
for (int k = 0; k < kfields; k++)
|
||
{
|
||
const int nf = kd.FieldSeq[k] % MaxFields;
|
||
const RecFieldDes& rf = recd.Fd[nf];
|
||
int toc = foc;
|
||
if (kd.FromCh[k] == 255) // Non e' stato specificato un range
|
||
toc += rf.Len; // Considero l'intera lunghezza del campo
|
||
else
|
||
toc += kd.ToCh[k] - kd.FromCh[k] + 1;
|
||
CHECK(toc > foc, "Invalid addition");
|
||
if (toc >= len)
|
||
{
|
||
if (str[foc] > ' ' && rf.TypeF == _alfafld)
|
||
str.left_just(toc, ' ');
|
||
break;
|
||
}
|
||
foc = toc;
|
||
}
|
||
}
|
||
str.replace('~', ' ');
|
||
}
|
||
|
||
// @doc EXTERNAL
|
||
|
||
// @mfunc Setta il filtro sul cursore
|
||
void TCursor::filter(
|
||
const char* fil, // @parm Espressione del filtro
|
||
const TRectype* from, // @parm Primo record del pacchetto da esaminare
|
||
const TRectype* to, // @parm Ultimo record del pacchetto da esaminare
|
||
int tilde // @parm Flag per riempire le chiavi di ~ from(0x1) o to(0x2)
|
||
)
|
||
|
||
// @comm ATTENZIONE: non e' possibile filtrare un cursore congelato
|
||
|
||
{
|
||
TString kf(_keyfrom), kto(_keyto);
|
||
const bool filterchanged = (fil != NULL) && (_filter != fil);
|
||
|
||
if (from != NULL)
|
||
matildator(*from, _nkey, (tilde & 0x1) != 0, kf);
|
||
if (to != NULL)
|
||
matildator(*to, _nkey, (tilde & 0x2) != 0, kto);
|
||
|
||
if (kf[0] <= ' ' || kto[0] <= ' ')
|
||
{
|
||
switch (file().num())
|
||
{
|
||
case LF_TABGEN:
|
||
case LF_TABCOM:
|
||
case LF_TAB:
|
||
if (!real::is_natural(file().name())) // E' veramente una tabella o <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;
|
||
safe_delete(_fexpr);
|
||
_frefs.destroy();
|
||
TTypeexp type = (_filter.find('"') >= 0) ? _strexpr : _numexpr;
|
||
if (_filter.not_empty())
|
||
{
|
||
_fexpr = new TExpression(_filter, type);
|
||
if (_fexpr->type() == _numexpr)
|
||
for (int i = 0 ; i < _fexpr->numvar(); i++)
|
||
if (file().curr().type(_fexpr->varname(i)) == _alfafld)
|
||
{
|
||
_fexpr->set_type(_strexpr);
|
||
break;
|
||
}
|
||
}
|
||
else _fexpr = NULL;
|
||
if (_fexpr)
|
||
{
|
||
const int items = _fexpr->numvar();
|
||
for (int i = 0 ; i < items; i++)
|
||
{
|
||
const char * vn = _fexpr->varname(i); //occhio
|
||
if (vn[0] != '#')
|
||
{
|
||
const TFieldref f(vn, 0);
|
||
const TString & id = f.id();
|
||
|
||
if (id.full())
|
||
{
|
||
_filter_update = true;
|
||
const int file_id = _if->name2ind(id);
|
||
if (_filter_limit < file_id)
|
||
_filter_limit = file_id;
|
||
}
|
||
_frefs.add(new TRecfield(_if->curr(f.file()), f.name(), f.from(), f.to()), i);
|
||
}
|
||
else
|
||
NFCHECK("Variabile strana %s", (const char*)vn);
|
||
}
|
||
}
|
||
}
|
||
_keyfrom = kf;
|
||
_keyto = kto;
|
||
}
|
||
}
|
||
|
||
void TCursor::setkey(int nkey)
|
||
{
|
||
if (nkey != _nkey)
|
||
{
|
||
_lastrec = 0L;
|
||
_nkey = nkey;
|
||
file().setkey(_nkey);
|
||
filter(NULL);
|
||
}
|
||
}
|
||
|
||
int TCursor::test(TIsamop op, TReclock lockop)
|
||
{
|
||
TLocalisamfile& curfile = file();
|
||
const TRectype& currec = curfile.curr();
|
||
curfile.setkey(_nkey);
|
||
|
||
int err = NOERR;
|
||
if (op == _isequal)
|
||
{
|
||
const TString match(currec.key(_nkey));
|
||
bool trovato = FALSE;
|
||
|
||
for (err = curfile.read(op, lockop);
|
||
err == NOERR && match == currec.key(_nkey);
|
||
err = curfile.next(lockop))
|
||
{
|
||
if (ok())
|
||
{
|
||
trovato = true;
|
||
break;
|
||
}
|
||
else
|
||
{
|
||
if (lockop != _nolock)
|
||
curfile.reread(_unlock);
|
||
}
|
||
}
|
||
if (!trovato && err == NOERR)
|
||
{
|
||
err = _iskeynotfound;
|
||
curfile.setstatus(err);
|
||
}
|
||
}
|
||
else
|
||
{
|
||
for (err = curfile.read(op, lockop);
|
||
err == NOERR;
|
||
err = curfile.next(lockop))
|
||
{
|
||
if (ok())
|
||
break;
|
||
else
|
||
{
|
||
//if (((TCursor *)this)->items() == 0)
|
||
if (_totrec == 0)
|
||
{
|
||
err = _isemptyfile;
|
||
break;
|
||
}
|
||
|
||
if (lockop != _nolock)
|
||
curfile.reread(_unlock);
|
||
|
||
const TString& kto = to();
|
||
if (kto.not_empty())
|
||
{
|
||
const TFixed_string curkey(currec.key(_nkey));
|
||
if (kto.compare(curkey, kto.len()) < 0)
|
||
{
|
||
err = _iseof;
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
return err;
|
||
}
|
||
|
||
TRecnotype TCursor::read(TIsamop op, TReclock lockop)
|
||
{
|
||
TLocalisamfile& curfile = file();
|
||
|
||
if (changed())
|
||
{
|
||
const TRectype savecurr = curfile.curr();
|
||
update();
|
||
curfile.curr() = savecurr;
|
||
}
|
||
|
||
int err = test(op, lockop);
|
||
const TRecnotype curpos = curfile.recno();
|
||
|
||
TString match;
|
||
if (err == NOERR)
|
||
match = curfile.curr().key(_nkey);
|
||
|
||
if (changed())
|
||
{
|
||
NFCHECK("Com'e' possibile che ci sia nuovamente bisogno di update?");
|
||
update();
|
||
}
|
||
|
||
if (err != NOERR)
|
||
{
|
||
_pos = _totrec - 1;
|
||
if (_pos < 0)
|
||
{
|
||
curfile.zero();
|
||
err = _isemptyfile;
|
||
}
|
||
else
|
||
readrec();
|
||
curfile.setstatus(err);
|
||
}
|
||
else
|
||
{
|
||
const int pages = (_totrec-1) / _cmaxelpage + 1;
|
||
|
||
_pos = -1;
|
||
if (pages > 1)
|
||
{
|
||
// const TString match(curfile.curr().key(_nkey));
|
||
|
||
bool found = FALSE;
|
||
FOR_EACH_ARRAY_ROW(_fpkey, p , s)
|
||
{
|
||
if (match <= *s)
|
||
{
|
||
const int pagecnt = read_page(p);
|
||
found = true;
|
||
for (int i = 0; i < pagecnt && _pos < 0L; i++)
|
||
if (_page[i] == curpos)
|
||
_pos = _pagefirstpos + i;
|
||
}
|
||
}
|
||
if (!found)
|
||
{
|
||
const int pagecnt = read_page(pages - 1);
|
||
for (int i = 0; i < pagecnt && _pos < 0L; i++)
|
||
if (_page[i] == curpos)
|
||
_pos = _pagefirstpos + i;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
for (int i = 0; i < _cmaxelpage && _pos < 0L; i++)
|
||
if (_page[i] == curpos)
|
||
_pos = i;
|
||
}
|
||
|
||
if (_pos < 0L)
|
||
_pos = 0L;
|
||
readrec();
|
||
}
|
||
|
||
return _pos;
|
||
}
|
||
|
||
|
||
TCursor & TCursor::copy(const TCursor & cursor)
|
||
{
|
||
safe_delete(_if);
|
||
_if = new TRelation(*cursor._if);
|
||
_nkey = cursor._nkey;
|
||
file().setkey(_nkey);
|
||
_fexpr = cursor._fexpr == nullptr ? nullptr : new TExpression(*cursor._fexpr);
|
||
_filename = cursor._filename;
|
||
_filter = cursor._filter;
|
||
_keyfrom = cursor._keyfrom;
|
||
_keyto = cursor._keyto;
|
||
FOR_EACH_ARRAY_ITEM(cursor._frefs, i, obj)
|
||
{
|
||
TRecfield * fref = (TRecfield *)obj;
|
||
|
||
_frefs.add(new TRecfield(_if->curr(fref->record().num()), fref->name(), fref->from(), fref->to()));
|
||
}
|
||
_frozen = cursor._frozen;
|
||
_filter_update = cursor._filter_update;
|
||
_filterfunction_update = cursor._filterfunction_update;
|
||
_filter_limit = cursor._filter_limit;
|
||
_filterfunction = cursor._filterfunction;
|
||
_pos = 0;
|
||
_totrec = 0;
|
||
_lastrec = 0;
|
||
_lastkrec = 0;
|
||
_pagefirstpos = 0L;
|
||
return *this;
|
||
}
|
||
|
||
TCursor::TCursor(TRelation* r, const char* fil, int nkey,
|
||
const TRectype *from, const TRectype* to, int tilde)
|
||
: _if(r), _nkey(nkey), _fexpr(NULL), _frozen(false), _filter_update(false),
|
||
_filterfunction_update(false), _filter_limit(0), _filterfunction(NULL)
|
||
{
|
||
CHECK(r, "Null cursor relation");
|
||
file().setkey(_nkey);
|
||
_pos = 0;
|
||
_totrec = 0;
|
||
_lastrec = 0;
|
||
_lastkrec = 0;
|
||
_pagefirstpos = 0L;
|
||
|
||
_page = new TRecnotype[_cmaxelpage];
|
||
memset(_page, 0, _cmaxelpage * sizeof(TRecnotype));
|
||
|
||
filter(fil, from, to, tilde);
|
||
}
|
||
|
||
TCursor::TCursor(const TCursor & cursor) : _if(nullptr)
|
||
{
|
||
copy(cursor);
|
||
_page = new TRecnotype[_cmaxelpage];
|
||
memset(_page, 0, _cmaxelpage * sizeof(TRecnotype));
|
||
}
|
||
|
||
TCursor::~TCursor()
|
||
{
|
||
if (_indexname.not_empty())
|
||
{
|
||
if (_indexname == _last_name && _last_ndx != NULL)
|
||
{
|
||
_last_created = TRUE; // Force close
|
||
close_index(_last_ndx);
|
||
}
|
||
::remove(_indexname);
|
||
}
|
||
SAFE_DELETE(_page);
|
||
SAFE_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 < 0)
|
||
_pos = 0;
|
||
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)
|
||
{
|
||
TProgress_monitor* pi = NULL;
|
||
if (tot > 1)
|
||
{
|
||
if (msg == NULL || *msg == '\0')
|
||
msg = TR("Elaborazione in corso...");
|
||
pi = new TProgress_monitor(tot, msg, true);
|
||
}
|
||
|
||
freeze(true);
|
||
for (*this = 0; pos() < tot; ++*this)
|
||
{
|
||
if (!func(*relation(), pJolly))
|
||
{
|
||
ok = false;
|
||
break;
|
||
}
|
||
if (pi != NULL && !pi->add_status())
|
||
{
|
||
ok = false;
|
||
break;
|
||
}
|
||
}
|
||
freeze(false);
|
||
SAFE_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();
|
||
int flen = f.len(r);
|
||
if (flen == 0) // Campo virtuale
|
||
flen = 50;
|
||
|
||
if (id.full())
|
||
{
|
||
_sort_update = true;
|
||
const int file_id = relation()->name2ind(id);
|
||
if (filter_limit() < file_id)
|
||
set_filter_limit(file_id);
|
||
}
|
||
|
||
sort.addsortkey(abspos,flen,versus);
|
||
CHECKS(flen!=0,"Field can not have null length: ",(const char *) s);
|
||
|
||
abspos += flen;
|
||
CHECKD(abspos<=256, "Sort key too long: ", abspos);
|
||
}
|
||
|
||
const int handle = file().handle();
|
||
|
||
sort.init();
|
||
l = strlen(to());
|
||
double perc = 1.0;
|
||
|
||
if (l > 0 && DB_index_seek(handle, to()) >= 0)
|
||
perc = DB_pos_perc(handle);
|
||
junk=DB_index_seek(handle, (char*)(const char*) from());
|
||
if (junk < 0) junk=get_error(junk);
|
||
if (junk == _iseof) return 0;
|
||
|
||
pos = DB_index_recno(handle);
|
||
TCursor::pos()=-1;
|
||
|
||
perc -= DB_pos_perc(handle);
|
||
|
||
// Barra di progressione
|
||
const long items = (long)((double)relation()->lfile().items() * perc);
|
||
TString msg("Elaborazione ");
|
||
TDir d(relation()->lfile().num());
|
||
|
||
msg << d.des();
|
||
|
||
TProgress_monitor pi(items, msg);
|
||
|
||
while (!DB_index_eof(handle))
|
||
{
|
||
pi.add_status();
|
||
const char* s0 = DB_index_getkey(handle);
|
||
if (l && (strncmp(to(), s0, l) < 0))
|
||
break;
|
||
const TRecnotype recno = DB_index_recno(handle);
|
||
if (recno == oldrecno)
|
||
break; // means that no more keys are available
|
||
|
||
oldrecno=recno;
|
||
|
||
// Attenzione: la filtercursor non si si limita a filtrare ma avanza anche il cursore!!!!
|
||
const bool to_be_added = filtercursor(recno);
|
||
if (to_be_added)
|
||
{
|
||
El_To_Sort Element;
|
||
|
||
TFixed_string f(Element.f, sizeof(Element.f));
|
||
fill_sort_key(f);
|
||
Element.p=recno;
|
||
sort.sort((char *) &Element);
|
||
if (TCursor::pos() < 0 && recno == rp)
|
||
TCursor::pos() = ap;
|
||
ap++;
|
||
}
|
||
|
||
long rec = DB_index_next(handle);
|
||
if (rec < 0)
|
||
fatal_box("Can't read index n. %d - file n. %d",file().getkey(),file().num());
|
||
} // while
|
||
|
||
sort.endsort();
|
||
|
||
pagecnt = 0;
|
||
El_To_Sort* Element = NULL;
|
||
while ((Element=(El_To_Sort *)sort.retrieve()) != NULL)
|
||
{
|
||
page()[pagecnt++]=Element->p;
|
||
if (pagecnt == pagesize())
|
||
{
|
||
if (_f == NULL)
|
||
{
|
||
_f = open_index(TRUE);
|
||
fseek(_f, 0L, SEEK_SET);
|
||
}
|
||
fwrite(page(), sizeof(TRecnotype) * pagesize(), 1, _f);
|
||
pagecnt=0;
|
||
fpkey().add(new TToken_string(Element->f));
|
||
}
|
||
}
|
||
if (_f != NULL)
|
||
{
|
||
if (pagecnt > 0)
|
||
fwrite(page(),sizeof(TRecnotype) * pagecnt, 1, _f);
|
||
close_index(_f);
|
||
}
|
||
setpagefirstpos(ap > pagesize() ? -(pagesize() + 1) : 0L) ;
|
||
|
||
return ap;
|
||
}
|
||
|
||
const char* TSorted_cursor::fill_sort_key(TString& k)
|
||
{
|
||
TString256 sf;
|
||
k.cut(0);
|
||
for (TString80 s = _order_expr.get(0); s.not_empty(); s = _order_expr.get())
|
||
{
|
||
const bool is_up = is_upper(s);
|
||
const char last = s.right(1)[0];
|
||
const bool align = last == '*';
|
||
|
||
if (last == '-' || last == '+' || align)
|
||
s.rtrim(1);
|
||
|
||
const TFieldref f(s,0);
|
||
sf = f.read(*relation());
|
||
const TRectype& frec = curr(f.file());
|
||
TFieldtypes fld_type = frec.type(f.name());
|
||
int fld_len = f.len(frec);
|
||
// Converte in ANSI i campi data ed i sottocampi con una data!
|
||
if (fld_type == _datefld || (f.is_subfield() && TDate::isdate(sf)))
|
||
{
|
||
if (sf.not_empty()) // Aggiunta del 17-10-2011 per efficienza stampe mg (ma potrebbe non andar bene!)
|
||
{
|
||
const TDate d(sf);
|
||
sf.format("%08ld", d.date2ansi());
|
||
}
|
||
else
|
||
sf.spaces(8);
|
||
fld_type = _datefld;
|
||
fld_len = 8;
|
||
}
|
||
else
|
||
{
|
||
if (is_up) // Test inutile: tutte le chiavi sono maiuscole 08-02-2016
|
||
sf.upper();
|
||
}
|
||
switch (fld_type)
|
||
{
|
||
case _boolfld:
|
||
case _charfld:
|
||
case _memofld:
|
||
case _alfafld: sf.left_just(fld_len > 0 ? fld_len : 50); break;
|
||
case _intfld:
|
||
case _longfld:
|
||
case _intzerofld:
|
||
case _longzerofld: sf.right_just(fld_len > 0 ? fld_len : 50); break;
|
||
case _datefld: break; // Gia' lungo 8!
|
||
case _realfld:
|
||
if (align)
|
||
{
|
||
real r(sf);
|
||
sf = r.string(fld_len > 0 ? fld_len : 50, fld_len > 0 ? (fld_len-4)/2 : 10);
|
||
}
|
||
else
|
||
sf.right_just(fld_len > 0 ? fld_len : 50);
|
||
break;
|
||
default :
|
||
sf.left_just(fld_len > 0 ? fld_len : 50);
|
||
break;
|
||
}
|
||
k << sf;
|
||
}
|
||
return k;
|
||
}
|
||
|
||
bool TSorted_cursor::changed()
|
||
{
|
||
bool rt = false;
|
||
if (_is_valid_expr)
|
||
{
|
||
rt = TCursor::changed();
|
||
if (!rt) rt=_is_changed_expr;
|
||
_is_changed_expr = false;
|
||
}
|
||
else
|
||
NFCHECK("Can't perform changed() while sorted cursor expression is not valid!");
|
||
return rt;
|
||
}
|
||
|
||
int TSorted_cursor::test(TIsamop op, TReclock lockop)
|
||
|
||
{
|
||
int err = NOERR;
|
||
|
||
if (items() == 0L)
|
||
{
|
||
err = _isemptyfile;
|
||
file().setstatus(err);
|
||
return err;
|
||
}
|
||
|
||
TString256 searching; fill_sort_key(searching);
|
||
searching.rtrim();
|
||
const int cmplen = searching.len();
|
||
|
||
TRecnotype first = 0L;
|
||
TRecnotype last = items()-1;
|
||
TRecnotype found = -1L;
|
||
|
||
FOR_EACH_ARRAY_ROW(fpkey(), i, s)
|
||
{
|
||
const int cmp = searching.compare(*s, cmplen);
|
||
if (cmp <= 0)
|
||
last = (i + 1) * pagesize() - 1;
|
||
else
|
||
first = i * pagesize();
|
||
}
|
||
|
||
const bool ghiacciato = !frozen();
|
||
if (ghiacciato) freeze(true);
|
||
|
||
TString256 testing;
|
||
while (first <= last)
|
||
{
|
||
const TRecnotype test = (first+last)/2;
|
||
TCursor::operator=(test); // verif
|
||
fill_sort_key(testing);
|
||
const int cmp = searching.compare(testing, cmplen);
|
||
if (cmp == 0)
|
||
{
|
||
if (op != _isgreat)
|
||
{
|
||
if (found < 0l || test < found)
|
||
found = test;
|
||
last = test-1;
|
||
}
|
||
else
|
||
first = test+1;
|
||
}
|
||
else
|
||
{
|
||
if (cmp < 0)
|
||
{
|
||
last = test-1;
|
||
if (op != _isequal)
|
||
{
|
||
if (found < 0l || test < found)
|
||
found = test;
|
||
}
|
||
}
|
||
else
|
||
first = test+1;
|
||
}
|
||
}
|
||
|
||
if (found >= 0L)
|
||
{
|
||
TCursor::operator=(found); // verif
|
||
file().setstatus(NOERR);
|
||
if (lockop != _nolock)
|
||
lock(lockop);
|
||
}
|
||
else
|
||
{
|
||
|
||
found = items()-1;
|
||
if (found >= 0)
|
||
TCursor::operator=(found);
|
||
err = op == _isequal ? _iskeynotfound : _iseof;
|
||
file().setstatus(err);
|
||
}
|
||
|
||
if (ghiacciato)
|
||
freeze(false);
|
||
|
||
return err;
|
||
}
|
||
|
||
|
||
TRecnotype TSorted_cursor::read(TIsamop op, TReclock lockop)
|
||
{
|
||
TString256 searching; fill_sort_key(searching);
|
||
searching.rtrim();
|
||
const int cmplen = searching.len();
|
||
|
||
TRecnotype first = 0L;
|
||
TRecnotype last = items()-1;
|
||
TRecnotype found = -1L;
|
||
|
||
FOR_EACH_ARRAY_ROW(fpkey(), i, s)
|
||
{
|
||
const int cmp = searching.compare(*s, cmplen);
|
||
if (cmp <= 0)
|
||
last = (i + 1) * pagesize() - 1;
|
||
else
|
||
first = i * pagesize();
|
||
}
|
||
|
||
const bool ghiacciato = !frozen();
|
||
if (ghiacciato) freeze(true);
|
||
|
||
TString256 testing;
|
||
while (first <= last)
|
||
{
|
||
const TRecnotype test = (first+last)/2;
|
||
TCursor::operator=(test);
|
||
fill_sort_key(testing);
|
||
const int cmp = searching.compare(testing, cmplen);
|
||
if (cmp == 0)
|
||
{
|
||
if (op != _isgreat)
|
||
{
|
||
if (found < 0l || test < found)
|
||
found = test;
|
||
last = test-1;
|
||
}
|
||
else
|
||
first = test+1;
|
||
}
|
||
else
|
||
{
|
||
if (cmp < 0)
|
||
{
|
||
last = test-1;
|
||
if (op != _isequal)
|
||
{
|
||
if (found < 0l || test < found)
|
||
found = test;
|
||
}
|
||
}
|
||
else
|
||
first = test+1;
|
||
}
|
||
}
|
||
|
||
if (found >= 0L)
|
||
{
|
||
TCursor::operator=(found);
|
||
file().setstatus(NOERR);
|
||
if (lockop != _nolock)
|
||
lock(lockop);
|
||
}
|
||
else
|
||
{
|
||
found = items()-1;
|
||
if (found >= 0)
|
||
TCursor::operator=(found);
|
||
file().setstatus(op == _isequal ? _iskeynotfound : _iseof);
|
||
}
|
||
|
||
if (ghiacciato)
|
||
freeze(false);
|
||
|
||
return found;
|
||
}
|
||
|
||
void TSorted_cursor::change_order(const char* order_expr)
|
||
{
|
||
_sort_update = false;
|
||
if (order_expr && *order_expr && _order_expr != order_expr)
|
||
{
|
||
TString s;
|
||
_order_expr = order_expr;
|
||
_order_expr.restart();
|
||
while ((s=_order_expr.get()).not_empty() && (_is_valid_expr=check_expr(s))) ;
|
||
if (_is_valid_expr)
|
||
_is_changed_expr=true;
|
||
}
|
||
}
|
||
|
||
TSorted_cursor::TSorted_cursor(TRelation *f, const char * order_expr, const char * filter, int key, const TRectype* from, const TRectype* to)
|
||
: TCursor(f,filter,key,from,to)
|
||
{
|
||
change_order(order_expr);
|
||
}
|
||
|
||
TSorted_cursor::~TSorted_cursor()
|
||
{
|
||
}
|
||
|
||
///////////////////////////////////////////////////////////
|
||
// TFieldRef
|
||
///////////////////////////////////////////////////////////
|
||
|
||
// @doc EXTERNAL
|
||
|
||
// @func Converte una stringa in numero logico o numero tabella
|
||
//
|
||
// @rdesc Ritorna il numero logico del file o il numero della tabella
|
||
int name2log(
|
||
const char* name) // @parm Stringa da convertire
|
||
{
|
||
int log = 0;
|
||
|
||
if (name)
|
||
{
|
||
while (isspace(*name))
|
||
name++;
|
||
|
||
if (*name)
|
||
{
|
||
log = table2logic(TFixed_string(name));
|
||
|
||
CHECKS(log != 0, "Non e' un file ne' una tabella: ", name);
|
||
}
|
||
}
|
||
return log;
|
||
}
|
||
|
||
|
||
TFieldref::TFieldref() : _fileid(0), _name(0) {}
|
||
|
||
|
||
TFieldref::TFieldref(const TString& s, short defid)
|
||
{
|
||
operator=(s);
|
||
if (_fileid == 0) _fileid = defid;
|
||
}
|
||
|
||
void TFieldref::copy(const TFieldref& f)
|
||
{
|
||
_fileid = f._fileid;
|
||
_id = f._id;
|
||
_name = f._name;
|
||
_from = f._from;
|
||
_to = f._to;
|
||
}
|
||
|
||
TObject* TFieldref::dup() const
|
||
{
|
||
TFieldref* f = new TFieldref(*this);
|
||
return f;
|
||
}
|
||
|
||
// @doc EXTERNAL
|
||
|
||
// @mfunc Operatore di assegnamento
|
||
//
|
||
// @rdesc Ritorna l'oggetto assegnato
|
||
TFieldref& TFieldref::operator =(
|
||
const TString& s) // @parm Stringa da assegnare all'oggetto
|
||
|
||
// @comm Un <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);
|
||
if (rf.type() == _memofld && _to > 0)
|
||
buffer = rec.get(_name).sub(_from, _to);
|
||
else
|
||
buffer = rf;
|
||
}
|
||
else
|
||
buffer = rec.get(_name);
|
||
}
|
||
return buffer;
|
||
}
|
||
|
||
const char* TFieldref::read(const TRelation& r) const
|
||
{
|
||
const TRectype& rec = _id.empty() ? r.curr() : r.lfile(_id).curr();
|
||
const char *s = read(rec);
|
||
return s;
|
||
}
|
||
|
||
void TFieldref::write(const char* val, TRelation& r) const
|
||
{
|
||
TRectype& rec = _id.empty() ? r.curr() : r.lfile(_id).curr();
|
||
write(val, rec);
|
||
}
|
||
|
||
|
||
void TFieldref::write(const char* val, TRectype& rec) const
|
||
{
|
||
if (_fileid >= CNF_GENERAL)
|
||
return;
|
||
|
||
if (_name.find(':') > 0)
|
||
{
|
||
TRecfield rf(rec, _name, _from, _to-1);
|
||
rf = val;
|
||
} else
|
||
if (_from > 0 || _to > 0)
|
||
{
|
||
buffer = rec.get(_name);
|
||
if (_to <= _from)
|
||
((TFieldref*)this)->_to = rec.length(_name);
|
||
buffer.overwrite(val, _from, _to - _from);
|
||
rec.put(_name, buffer);
|
||
}
|
||
else
|
||
{
|
||
if (val && *val)
|
||
rec.put(_name, val);
|
||
else
|
||
rec.zero(_name);
|
||
}
|
||
}
|
||
|
||
int TFieldref::len(const TRectype &rec) const
|
||
{
|
||
int len = 0;
|
||
if (_to < _from)
|
||
{
|
||
if (_fileid > 0)
|
||
{
|
||
const RecDes& recd = prefix().get_recdes(_fileid);
|
||
const int p = findfld(&recd, _name);
|
||
len = p != FIELDERR ? recd.Fd[p].Len : 0;
|
||
}
|
||
else
|
||
len = rec.length(_name);
|
||
}
|
||
else
|
||
len = _to;
|
||
len -= _from;
|
||
return len;
|
||
}
|
||
|
||
///////////////////////////////////////////////////////////
|
||
// TRelation_description
|
||
///////////////////////////////////////////////////////////
|
||
|
||
|
||
void TRelation_description::init_files_array()
|
||
{
|
||
const TLocalisamfile& f = _rel->file();
|
||
TToken_string s(128);
|
||
print_name(s, f); // Logic number
|
||
|
||
const char* name = f.name();
|
||
if (f.tab())
|
||
name = TDir::tab_des(name);
|
||
else
|
||
name = prefix().description(name);
|
||
s.add(name); // Description
|
||
s.add(" "); // No join
|
||
s.add(f.getkey()); // Key
|
||
s.add(" | "); // No Alias nor expression
|
||
|
||
_files.destroy();
|
||
_files.add(s); // Main file
|
||
for (int i = 0; i < _rel->items(); i++)
|
||
{
|
||
_rel->reldef(i).print_on(s);
|
||
_files.add(s);
|
||
}
|
||
}
|
||
|
||
|
||
void TRelation_description::read_rel()
|
||
{
|
||
// scan files and build description arrays
|
||
init_files_array();
|
||
|
||
_fields.destroy();
|
||
for (int i = 0; i < _files.items(); i++)
|
||
{
|
||
TToken_string& tt = (TToken_string&)_files[i];
|
||
TFilename descfname; descfname << DESCDIR << "/d";
|
||
|
||
const char* tn = tt.get(0); // Codice del file o tabella
|
||
|
||
const int which_file = name2log(tn); // Numero logico del file
|
||
|
||
if (tn[0] == '%' || tn[0] == '$')
|
||
descfname << (tn+1);
|
||
else
|
||
descfname << tn;
|
||
descfname << ".des";
|
||
|
||
TConfig conf(descfname, DESCPAR);
|
||
// new record descriptor for _fields array
|
||
TString_array* rdesc = new TString_array;
|
||
|
||
TTrec trec; trec.get(which_file);
|
||
TToken_string ttmp(64);
|
||
TString dfld(256);
|
||
|
||
for (int f = 0; f < trec.fields(); f++)
|
||
{
|
||
ttmp = trec.fielddef(f);
|
||
const TString16 name(ttmp.get(0));
|
||
if (!name.blank())
|
||
{
|
||
dfld = conf.get(name, NULL, -1, "Missing description");
|
||
if (!dfld.blank() && dfld[0] != '#')
|
||
{
|
||
ttmp.add(dfld,4);
|
||
// contiene: nome campo, tipo, lunghezza, decimali, descrizione
|
||
rdesc->add(ttmp);
|
||
}
|
||
}
|
||
}
|
||
|
||
_fields.add(rdesc, i);
|
||
}
|
||
}
|
||
|
||
// @doc INTERNAL
|
||
|
||
// @mfunc Cambia la relazione descritta
|
||
void TRelation_description::change_relation(
|
||
TRelation& r, // @parm Nuova relazione da assegnare
|
||
TString_array& a) // @parm Array di descrittore dei file
|
||
{
|
||
_rel = &r;
|
||
read_rel();
|
||
if (a.items() > 0)
|
||
_files = a;
|
||
}
|
||
|
||
void TRelation_description::print_on(ostream& out) const
|
||
{
|
||
for (int i = 0; i < _files.items(); i++)
|
||
{
|
||
TToken_string& r = ((TRelation_description*)this)->_files.row(i);
|
||
TString16 cod(r.get(4));
|
||
if (atoi(cod) == 0)
|
||
cod = r.get(0);
|
||
else
|
||
cod << '@';
|
||
out << " " << cod << "->* ";
|
||
out << "\"" << r.get(1) << "\"\n";
|
||
}
|
||
}
|
||
|
||
// @doc INTERNAL
|
||
|
||
// @mfunc Seleziona un file
|
||
//
|
||
// @rdesc Ritorna il risultato dell'operazione:
|
||
//
|
||
// @flag TRUE | E' stato selzionato il file
|
||
// @flag FALSE | Non e' riuscito a selezionare il file
|
||
bool TRelation_description::choose_file(
|
||
int file) // @parm Numero logico del file da selezionare (default 0)
|
||
|
||
// @comm Dopo aver scelto un file (occorre che ritorni TRUE) i metodi della classe
|
||
// permettono di conoscere tutti i dati riguardanti i parametri del file selezionato
|
||
|
||
{
|
||
TArray_sheet sht(-1,-1,-4,-4,"Selezione archivio", "Codice|Descrizione archivio@70");
|
||
TToken_string tt(80);
|
||
|
||
int sel = 0;
|
||
|
||
for (int i = 0; i < _files.items(); i++)
|
||
{
|
||
TToken_string& tf = _files.row(i);
|
||
tt = tf.get(4);
|
||
|
||
int num = atoi(tt);
|
||
if (num == 0)
|
||
{
|
||
tt = tf.get(0);
|
||
num = atoi(tt);
|
||
if (sel == 0 && file > 0 && num == file)
|
||
sel = i;
|
||
}
|
||
else
|
||
{
|
||
if (sel == 0 && file < 0 && num == -file)
|
||
sel = i;
|
||
tt << '@';
|
||
}
|
||
|
||
tt.add(tf.get(1));
|
||
sht.add(tt);
|
||
}
|
||
|
||
sht.select(sel);
|
||
|
||
if (sht.run() == K_ENTER)
|
||
{
|
||
_cur_file = (int)sht.selected();
|
||
_cfile = _files.row(_cur_file);
|
||
return TRUE;
|
||
}
|
||
return FALSE;
|
||
}
|
||
|
||
// @doc INTERNAL
|
||
|
||
// @mfunc Seleziona un campo <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()
|
||
{
|
||
SAFE_DELETE(_curs);
|
||
SAFE_DELETE(_rel);
|
||
}
|
||
|
||
//
|
||
// *** EOF relation.cpp
|
||
|