Files correlati : ve0.exe Ricompilazione Demo : [ ] Commento : form.cpp Aggiunto supporto per custom/bitamp realtion.cpp Semplici sostituzioni di cose ripetute n volte con una singola variabile scanner.cpp Aggiunti apici in segnalazione di errore strings.cpp Corretta lettura di una stringa da file ad EOF tree.cpp Corretto disegno linee in presenza di header (Elaborazioni differite) git-svn-id: svn://10.65.10.50/trunk@11226 c028cbd2-c16b-5b4b-a496-9718f37d4682
2877 lines
68 KiB
C++
Executable File
2877 lines
68 KiB
C++
Executable File
#include <config.h>
|
|
#include <expr.h>
|
|
#include <extcdecl.h>
|
|
#include <prefix.h>
|
|
#include <relation.h>
|
|
#include <sheet.h>
|
|
#include <sort.h>
|
|
#include <tabutil.h>
|
|
#include <utility.h>
|
|
|
|
#include <codeb.h>
|
|
// *** check if not already defined
|
|
#define NOTFOUND (-1)
|
|
|
|
#define print_name(out, f) out << (f.num() == LF_TABCOM ? "%" : "") << f.name()
|
|
|
|
extern int get_error(int);
|
|
|
|
HIDDEN const char* field_type_str(TFieldtypes f)
|
|
{
|
|
const char* c = "";
|
|
switch(f)
|
|
{
|
|
case _charfld:
|
|
c = "Carattere"; break;
|
|
case _realfld:
|
|
c = "Reale"; break;
|
|
case _datefld:
|
|
c = "Data"; break;
|
|
case _boolfld:
|
|
c = "Logico"; break;
|
|
// case _wordfld:
|
|
// case _intzerofld:
|
|
// case _longzerofld:
|
|
// case _intfld:
|
|
// case _longfld:
|
|
default:
|
|
c = "Intero"; break;
|
|
}
|
|
return c;
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////
|
|
// TRelationdef
|
|
///////////////////////////////////////////////////////////
|
|
|
|
class TRelationdef : public TObject
|
|
{
|
|
friend class TRelation;
|
|
|
|
const TRelation* _rel; // Relazione padre
|
|
int _num; // Posizione file
|
|
int _numto; // Posizione padre
|
|
int _alias; // Alias
|
|
byte _key; // Chiave
|
|
TArray _fields; // Campi di join
|
|
TArray _exprs; // Condizioni di uguaglianza
|
|
TArray _altexprs; // Condizioni di uguaglianza alternative
|
|
TBit_array _forced;
|
|
bool _first_match : 1; // primo match (ed esiste)
|
|
bool _allow_lock : 1; // ??
|
|
bool _write_enable : 1;
|
|
|
|
public: // TObject
|
|
virtual void print_on(ostream& out) const;
|
|
|
|
public:
|
|
int num() const { return _num; }
|
|
int link() const { return _numto; }
|
|
int alias() const { return _alias; }
|
|
bool 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)
|
|
: _num(idx_file), _key(key), _numto(idx_to),
|
|
_rel(rel), _fields(4), _exprs(4), _alias(alias),
|
|
_first_match(FALSE), _allow_lock(allow_lock),
|
|
_write_enable(FALSE)
|
|
{
|
|
TToken_string rels(relexprs);
|
|
int i = 0;
|
|
TString80 r,s;
|
|
|
|
for (const char* g = rels.get(); g; g = rels.get(), i++)
|
|
{
|
|
r = g;
|
|
int eq = r.find('=');
|
|
|
|
CHECKS(eq > 0, "Ahoo! E l'uguale 'ndo sta? ", (const char*)g);
|
|
|
|
s = r.left(eq); // Parte a sinistra dell' =
|
|
|
|
#ifdef DBG
|
|
const char* n = s;
|
|
const int p = s.find('[');
|
|
if (p > 0) n = s.left(p);
|
|
if (rel->file(_num).curr().exist(n) == FALSE)
|
|
{
|
|
yesnofatal_box("Errore di JOIN: '%s' non e' un campo del file %d",
|
|
n, rel->file(_num).num());
|
|
continue;
|
|
}
|
|
#endif
|
|
|
|
if (r[eq+1] == '=')
|
|
{
|
|
_forced.set(i);
|
|
eq++;
|
|
}
|
|
|
|
_fields.add(new TFieldref(s, 0));
|
|
|
|
s = r.mid(eq+1);
|
|
const int par = s.find('(');
|
|
if (par > 0)
|
|
{
|
|
_exprs.add(new TExpression(s.left(par), _strexpr), i);
|
|
_altexprs.add(new TExpression(s.sub(par+1, s.len()-1), _strexpr), i);
|
|
}
|
|
else
|
|
_exprs.add(new TExpression(s, _strexpr), i);
|
|
}
|
|
}
|
|
|
|
|
|
void TRelationdef::print_on(ostream& out) const
|
|
{
|
|
const TLocalisamfile& f = _rel->file(_num);
|
|
|
|
out << "JOIN ";
|
|
print_name(out, f);
|
|
|
|
if (_numto > 0)
|
|
{
|
|
out << " TO ";
|
|
const int alias = _rel->reldef(_numto-1).alias();
|
|
if (alias > 0) out << alias << '@';
|
|
else
|
|
{
|
|
const TLocalisamfile& t = _rel->file(_numto);
|
|
print_name(out, t);
|
|
}
|
|
}
|
|
|
|
if (_key > 1) out << " KEY " << (int)_key;
|
|
|
|
if (_alias > 0) out << " ALIAS " << _alias;
|
|
|
|
for (int i = 0; i < _fields.items(); i++)
|
|
{
|
|
if (i == 0) out << " INTO";
|
|
out << ' ' << _fields[i] << '=';
|
|
if (_forced[i]) out << '=';
|
|
out << _exprs[i];
|
|
if (_altexprs.objptr(i))
|
|
out << '(' << _altexprs[i] << ')';
|
|
}
|
|
}
|
|
|
|
|
|
void TRelationdef::print_on(TToken_string& out) const
|
|
{
|
|
const TLocalisamfile& f = _rel->file(_num);
|
|
|
|
out = "";
|
|
print_name(out, f);
|
|
|
|
// add description
|
|
const char* name = f.name();
|
|
if (f.tab())
|
|
name = TDir::tab_des(name);
|
|
else
|
|
name = prefix().description(name);
|
|
out.add(name);
|
|
|
|
out << '|';
|
|
|
|
if (_numto > 0)
|
|
{
|
|
const int alias = _rel->reldef(_numto-1).alias();
|
|
if (alias > 0)
|
|
out << alias << '@';
|
|
else
|
|
{
|
|
const TLocalisamfile& t = _rel->file(_numto);
|
|
print_name(out, t);
|
|
}
|
|
} else out << ' ';
|
|
|
|
out.add(_key);
|
|
out.add(_alias);
|
|
out << '|';
|
|
for (int i = 0; i < _fields.items(); i++)
|
|
{
|
|
if (i) out << ' ';
|
|
out << _fields[i] << '=';
|
|
if (_forced[i]) out << '=';
|
|
out << _exprs[i];
|
|
if (_altexprs.objptr(i))
|
|
out << '(' << _altexprs[i] << ')';
|
|
}
|
|
}
|
|
|
|
|
|
const char* TRelationdef::evaluate_expr(int j, const TLocalisamfile& to)
|
|
{
|
|
TExpression& expr = (TExpression&)_exprs[j];
|
|
TString16 name;
|
|
for (int k = 0; k < expr.numvar(); k++)
|
|
{
|
|
name = expr.varname(k); name.upper();
|
|
expr.setvar(k, to.get(name));
|
|
}
|
|
|
|
const char* val = (const char* )expr.as_string();
|
|
|
|
if (*val == '\0' && _altexprs.objptr(j))
|
|
{
|
|
TExpression& altexpr = (TExpression&)_altexprs[j];
|
|
for (int k = 0; k < expr.numvar(); k++)
|
|
{
|
|
name = altexpr.varname(k); name.upper();
|
|
altexpr.setvar(k, to.get(name));
|
|
}
|
|
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)
|
|
{
|
|
TTable* 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()
|
|
{
|
|
// _status.restart();
|
|
for (int 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).curr().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);
|
|
for (int i = 0; i < _files.items(); i++)
|
|
{
|
|
const int err = file(i).status();
|
|
const TRecnotype recno = file(i).eof() ? -1l : file(i).recno();
|
|
const int key = file(i).getkey();
|
|
_status.add (err);
|
|
_status.add (recno);
|
|
_status.add (key);
|
|
}
|
|
for (i = 0; i < _reldefs.items(); i++)
|
|
{
|
|
const bool first_match = reldef(i)._first_match;
|
|
_status.add(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];
|
|
}
|
|
|
|
// @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)
|
|
{
|
|
TTable* 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);
|
|
|
|
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);
|
|
|
|
for (int j = 0; j < _reldefs.items(); j++)
|
|
if (reldef(j).num() == i) break;
|
|
|
|
TLocalisamfile& from = file(i);
|
|
TLocalisamfile& to = file(reldef(j).link());
|
|
|
|
reldef(j)._first_match = FALSE;
|
|
|
|
if (from.bad())
|
|
return FALSE; // && vaffanculo();
|
|
|
|
const TRecnotype last = from.recno();
|
|
|
|
bool ok = TRUE; // Corrispondenza trovata ?
|
|
|
|
if (fieldlist == NULL)
|
|
{
|
|
TRectype rec(from.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);
|
|
for (const char* f = fields.get(0); f; f = fields.get())
|
|
old.add(from.curr().get(f));
|
|
|
|
from.next();
|
|
|
|
old.restart();
|
|
|
|
for (f = fields.get(0); f && ok; f = fields.get())
|
|
{
|
|
const TFixed_string v(from.curr().get(f));
|
|
const char* o = old.get();
|
|
if (v != o)
|
|
ok = FALSE;
|
|
}
|
|
}
|
|
|
|
if (!ok)
|
|
{
|
|
from.readat(last);
|
|
return FALSE;
|
|
}
|
|
|
|
// Riposiziona gli eventuali files successivi
|
|
position_rels(_isequal, _nolock, 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;
|
|
TRecnotype recnos[MAXREL];
|
|
|
|
for (int i = 0; i < _files.items() && i < MAXREL; i++)
|
|
{
|
|
// must be file OK, non-empty record
|
|
bad |= file(i).good() || file(i).curr().empty();
|
|
recnos[i] = file(i).recno();
|
|
}
|
|
|
|
// if the above hold, check consistency
|
|
if (bad)
|
|
{
|
|
_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())
|
|
{
|
|
TString16 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 TString& 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=0,ap = 0;
|
|
int pagecnt = 0;
|
|
FILE* indf = NULL;
|
|
|
|
const int l = strlen(to());
|
|
int junk = DB_index_seek(fhnd, (char*)(const char*) from());
|
|
if (junk < 0) junk=get_error(junk);
|
|
if (junk == _iseof) return 0;
|
|
|
|
DB_index_recno(fhnd);
|
|
_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::filtercursor(TRecnotype recno)
|
|
{
|
|
file().readat(recno);
|
|
if (update_relation())
|
|
{
|
|
// memorizzo la chiave prima di eventuali spostamenti
|
|
const TString 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 = TRUE;
|
|
|
|
if (_filterfunction)
|
|
ok = _filterfunction(_if);
|
|
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';
|
|
for (int 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:
|
|
{
|
|
for (const char* 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;
|
|
}
|
|
|
|
if (update_relation())
|
|
{
|
|
const TRecnotype old = file().recno();
|
|
_if->update();
|
|
if (DB_recno(file().handle()) != old)
|
|
file().readat(old);
|
|
}
|
|
|
|
if ((_filterfunction ? _filterfunction(_if) : TRUE ) &&
|
|
(_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.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);
|
|
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('~', ' ');
|
|
}
|
|
|
|
// @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 (file().tab())
|
|
{
|
|
const TTable& f = (const TTable&)file();
|
|
kf = kto = f.name();
|
|
}
|
|
if (from != NULL)
|
|
matildator(*from, _nkey, (tilde & 0x1) != 0, kf);
|
|
if (to != NULL)
|
|
matildator(*to, _nkey, (tilde & 0x2) != 0, kto);
|
|
|
|
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 TString & vn = _fexpr->varname(i);
|
|
if (vn[0] != '#')
|
|
{
|
|
TFieldref f(vn, 0);
|
|
_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)
|
|
{
|
|
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)
|
|
{
|
|
int err = test(op, lockop);
|
|
TLocalisamfile& curfile = file();
|
|
const TRecnotype curpos = curfile.recno();
|
|
if (changed())
|
|
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), _frozen(FALSE), _filterfunction(NULL), _fexpr(NULL),
|
|
_filter_update(FALSE), _filterfunction_update(FALSE), _filter_limit(0)
|
|
{
|
|
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;
|
|
|
|
if (_pos >= items())
|
|
{
|
|
file().setstatus(_iseof);
|
|
curr().zero();
|
|
return nrec;
|
|
}
|
|
file().setstatus(NOERR);
|
|
|
|
if (_pos < _pagefirstpos || _pos > _pagefirstpos + _cmaxelpage)
|
|
read_page(_pos / _cmaxelpage);
|
|
|
|
nrec = _page[_pos - _pagefirstpos];
|
|
|
|
curr().setdirty();
|
|
file().readat(nrec);
|
|
repos();
|
|
return nrec;
|
|
}
|
|
|
|
// @doc EXTERNAL
|
|
|
|
// @mfunc Mette un lock sul record
|
|
//
|
|
// @rdesc Ritorna il numero di errore che si verifica nel porre il lock (NOERR) se
|
|
// non si verificano errori
|
|
int TCursor::lock(
|
|
TReclock l) // @parm Tipo di 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));
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////////
|
|
// 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;
|
|
const bool filtered = has_filter();
|
|
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
|
|
char versus = (s.right(1)=="-") ? 'd' : 'a';
|
|
if (s.right(1) == "-" || s.right(1) == "+")
|
|
s.rtrim(1);
|
|
TFieldref f(s,0);
|
|
// Il controllo del file e' automatico in f.len()
|
|
const int n = f.file();
|
|
const int flen = f.len(relation()->curr(n));
|
|
sort.addsortkey(abspos,flen,versus);
|
|
CHECKS(flen!=0,"Field can not have null length: ",(const char *) s);
|
|
|
|
const TRectype& r = relation()->curr(n);
|
|
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;
|
|
bool to_be_added = TRUE;
|
|
|
|
if (filtered)
|
|
to_be_added = filtercursor(recno);
|
|
|
|
if (to_be_added)
|
|
{
|
|
El_To_Sort Element;
|
|
|
|
fill_sort_key(Element.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(char* k)
|
|
{
|
|
TString16 fmt;
|
|
TString sf;
|
|
*k = '\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());
|
|
const TFieldtypes fld_type = frec.type(f.name());
|
|
if (fld_type == _datefld) // Se il campo e' di tipo data, la converte in ANSI!
|
|
{
|
|
TDate d(sf);
|
|
sf=d.string(ANSI);
|
|
}
|
|
if (is_up) sf.upper();
|
|
if (fld_type == _alfafld || fld_type == _datefld)
|
|
fmt.format("%%-%ds",f.len(frec));
|
|
else
|
|
fmt.format("%%%ds",f.len(frec));
|
|
strcat(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.get_buffer());
|
|
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.get_buffer());
|
|
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)
|
|
{
|
|
if (order_expr && *order_expr && _order_expr != order_expr)
|
|
{
|
|
TString s;
|
|
_order_expr = order_expr;
|
|
_order_expr.restart();
|
|
while ((s=_order_expr.get()).not_empty() && (_is_valid_expr=check_expr(s))) ;
|
|
if (_is_valid_expr)
|
|
_is_changed_expr=TRUE;
|
|
}
|
|
}
|
|
|
|
TSorted_cursor::TSorted_cursor(TRelation *f, const char * order_expr, const char * filter, int key, const TRectype* from, const TRectype* to)
|
|
: TCursor(f,filter,key,from,to)
|
|
{
|
|
change_order(order_expr);
|
|
}
|
|
|
|
TSorted_cursor::~TSorted_cursor()
|
|
{
|
|
}
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////
|
|
// TFieldRef
|
|
///////////////////////////////////////////////////////////
|
|
|
|
// @doc EXTERNAL
|
|
|
|
// @func Converte una stringa in numero logico o numero tabella
|
|
//
|
|
// @rdesc Ritorna il numero logico del file o il numero della tabella
|
|
int name2log(
|
|
const char* name) // @parm Stringa da convertire
|
|
{
|
|
int log = 0;
|
|
|
|
if (name)
|
|
while (*name == ' ') name++;
|
|
|
|
if (name && *name)
|
|
{
|
|
if (isdigit(*name) || *name == '-')
|
|
{
|
|
log = atoi(name);
|
|
if (strchr(name, '@')) log = -log;
|
|
}
|
|
else
|
|
{
|
|
const int len = strlen(name);
|
|
if (len == 3 || len == 4)
|
|
log = TTable::name2log(name);
|
|
}
|
|
|
|
CHECKS(log != 0, "Non e' un file ne' una tabella: ", name);
|
|
}
|
|
|
|
return log;
|
|
}
|
|
|
|
|
|
TFieldref::TFieldref() : _fileid(0), _name(0) {}
|
|
|
|
|
|
TFieldref::TFieldref(const TString& s, short defid)
|
|
{
|
|
operator=(s);
|
|
if (_fileid == 0) _fileid = defid;
|
|
}
|
|
|
|
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 TFildref> deve avere il seguente formato (solamente NAME e il mandante):
|
|
// <nl>FILE->NAME[FROM,TO]
|
|
|
|
{
|
|
int pos = s.find("->");
|
|
if (pos > 0)
|
|
{
|
|
_id = s.left(pos); _id.strip(" ");
|
|
_fileid = name2log(_id);
|
|
pos += 2;
|
|
}
|
|
else
|
|
{
|
|
_id.cut(0);
|
|
_fileid = pos = 0;
|
|
}
|
|
|
|
int par = s.find('[', pos); // 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 : _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 : _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
|
|
{
|
|
buffer = _name; buffer.upper();
|
|
buffer = rec.get(buffer);
|
|
if (_from > 0 || _to > 0)
|
|
{
|
|
const int l = buffer.len();
|
|
if (_to < l && _to > 0) buffer.cut(_to);
|
|
if (_from > 0) buffer.ltrim(_from);
|
|
}
|
|
}
|
|
return buffer;
|
|
}
|
|
|
|
const char* TFieldref::read(const TRelation& r) const
|
|
{
|
|
const TRectype& rec = _id.empty() ? r.curr() : r.lfile(_id).curr();
|
|
const char *s = read(rec);
|
|
return s;
|
|
}
|
|
|
|
void TFieldref::write(const char* val, TRelation& r) const
|
|
{
|
|
TRectype &rec = _id.empty() ? r.curr() : r.lfile(_id).curr();
|
|
write(val, rec);
|
|
}
|
|
|
|
|
|
void TFieldref::write(const char* val, TRectype& rec) const
|
|
{
|
|
if (_fileid >= CNF_GENERAL) return;
|
|
if (_from > 0 || _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
|
|
|
|
int which_file = atoi(tn); // Non spostare questa riga (tn puo' cambiare!)
|
|
if (which_file == 0)
|
|
which_file = TTable::name2log(tn); // Numero logico del file
|
|
|
|
if (tn[0] == '%' || tn[0] == '$')
|
|
descfname << (tn+1);
|
|
else
|
|
descfname << tn;
|
|
descfname << ".des";
|
|
|
|
TConfig conf(descfname, DESCPAR);
|
|
// new record descriptor for _fields array
|
|
TString_array* rdesc = new TString_array;
|
|
|
|
TTrec trec; trec.get(which_file);
|
|
TToken_string ttmp(64);
|
|
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), _menu(FALSE), _cur_file(0), _cur_field(0),
|
|
_cfile(80), _cfield(80)
|
|
{
|
|
read_rel();
|
|
if (_files.items() > 0)
|
|
{
|
|
_cfile = _files.row(0);
|
|
_cfield = "";
|
|
}
|
|
}
|
|
|
|
TRelation_description::~TRelation_description()
|
|
{
|
|
if (_menu) remove_menu();
|
|
}
|
|
|
|
|
|
////////////////////////////////////
|
|
// 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
|
|
|