#include "../xvaga/incstr.h" #include #include #include #include #include #include #include #include #include #include #include #include #include /////////////////////////////////////////////////////////// // Utility /////////////////////////////////////////////////////////// static bool is_numeric(const char* str) { if (str == NULL || *str == '\0' || (*str == '0' && isdigit(str[1]))) return false; // Se comincia per zero va preservato! if (*str == '-') { str++; if (*str <= ' ') return false; } while (*str) { if (strchr("0123456789.,", *str) == NULL) return false; str++; } return true; } static void num_reformat(TString& val) { xvt_str_number_format(val.get_buffer(), val.size()); const int comma = val.find(','); const int point = val.find('.'); if (comma >= 0 && comma < point) val.strip(","); else if (point >= 0 && point < comma) val.strip("."); } /////////////////////////////////////////////////////////// // TRecordset /////////////////////////////////////////////////////////// const TString& TRecordset::query_text() const { return EMPTY_STRING; } const TToken_string& TRecordset::sheet_head() const { TToken_string head; TToken_string tablefield(32, '.'); const unsigned int cols = columns(); for (unsigned int c = 0; c < cols; c++) { const TRecordset_column_info& ci = column_info(c); tablefield = ci._name; int maxlen = 0; FOR_EACH_TOKEN(tablefield, tok) { if (maxlen == 0) head.add(tok); else head << '\n' << tok; const int len = strlen(tok); if (len > maxlen) maxlen = len; } head << '@' << max(ci._width, maxlen); switch (ci._type) { case _boolfld: head << 'C'; break; case _wordfld: case _intfld: case _longfld: head << 'R'; break; case _datefld: head << 'D'; break; case _realfld: head << (ci._width > 7 ? 'V' : 'R'); break; default: break; } } // Creo la stringa temporanea solo ora, altrimenti puo' essere sovrascritta! TToken_string& h = get_tmp_string(); h = head; return h; } bool TRecordset::save_as_html(const char* path) { ofstream out(path); if (out.fail()) return false; TProgind pi(items(), TR("Esportazione in corso..."), true, true); out << "" << endl; save_html_head(out, main_app().title()); out << "" << endl; /* More annoyng than useful TString qry; parsed_text(qry); if (qry.full()) { for (int i = qry.find('\n'); i > 0; i = qry.find('\n', i+1)) qry.insert("
", i+1); out << "

" << qry << "

" << endl; }*/ out << ""; out << " " << endl; const unsigned int cols = columns(); TAttributes attr; if (cols > 0) { out << " " << endl; for (unsigned int c = 0; c < cols; c++) { const TRecordset_column_info& ci = column_info(c); out << " " << endl; } TXmlItem tr; tr.SetTag("tr"); tr.SetColorAttr("bgcolor", BTN_BACK_COLOR); tr.Write(out, 2); out << endl; for (unsigned int c = 0; c < cols; c++) { const TRecordset_column_info& ci = column_info(c); TToken_string header(ci._name, '\n'); TString str; FOR_EACH_TOKEN(header, tok) { if (str.not_empty()) str << "
"; str << tok; } if (get_attr(c, attr, true)) out << " " << endl; } out << " " << endl; out << " " << endl; } out << " " << endl; TString val; for (bool ok = move_first(); ok; ok = move_next()) { if (!pi.addstatus(1)) break; out << " " << endl; for (unsigned int c = 0; c < cols; c++) { const TRecordset_column_info& ci = column_info(c); if (get_attr(c, attr)) out << " " << endl; } out << " " << endl; } out << " " << endl; out << "
" << main_app().title() << "
" ; else out << " "; out << str << "
" ; else out << " "; switch (ci._type) { case _intfld: case _longfld: { const TVariant& var = get(c); const long r = var.as_int(); val.cut(0); if (r != 0) val << r; else var.as_string(val); } break; case _realfld: { get(c).as_string(val); if (val.full() && real::is_real(val)) { const real r(val); if (r.is_zero()) //elimina gli '0' dalle celle vuote in excel, rendendo leggibile il file excel val.cut(0); else val = r.stringe(); } } break; default: get(c).as_string(val); break; } if (val.full()) { val.rtrim(); out << val; } out << "
" << endl; out << "" << endl; out << "" << endl; return !pi.iscancelled(); } bool TRecordset::save_as_text(const char* path) { ofstream out(path); if (out.fail()) return false; TProgind pi(items(), TR("Esportazione in corso..."), true, true); TString val; const char sep = text_separator(); const bool as400 = (sep == ' '); const unsigned int cols = columns(); for (bool ok = move_first(); ok; ok = move_next()) { for (unsigned int c = 0; ; c++) { const TVariant& var = get(c); if (var.is_null() && c >= cols) break; if (c > 0 && !as400) out << sep; if (as400) { var.as_string(val); if (var.type() == _realfld || (cols == 0 && is_numeric(val))) { num_reformat(val); val.lpad(column_info(c)._width); } else val.rpad(column_info(c)._width); out << val; } else { if (!var.is_empty()) { var.as_string(val); val.rtrim(); if (val.find('\n') >= 0 || val.find('\t') >= 0) out << unesc(val); // Evitiamo doppi separatori di campo e record else { if (var.type() == _realfld || (cols == 0 && is_numeric(val))) num_reformat(val); out << val; } } } } out << endl; if (!pi.addstatus(1)) break; } return !pi.iscancelled(); } bool TRecordset::save_as_campo(const char* path) { ofstream out(path); if (out.fail()) return false; TProgind pi(items(), TR("Esportazione in corso..."), true, true); out << "[Head]" << endl; out << "Version=0"; const unsigned int cols = columns(); for (unsigned int c = 0; c < cols; c++) { const TRecordset_column_info& ci = column_info(c); if ((c % 8) == 0) out << endl << "Fields="; else out << '|'; out << ci._name; } out << endl << endl << "[Data]" << endl; TString val; for (bool ok = move_first(); ok; ok = move_next()) { for (unsigned int c = 0; ; c++) { const TVariant& var = get(c); if (var.is_null() && c >= cols) break; if (c > 0) out << '|'; if (!var.is_empty()) { var.as_string(val); if (var.type() == _alfafld) { val.rtrim(); val.replace('|', SAFE_PIPE_CHR); // Evitiamo doppi separatori di campo if (val.find('\n') >= 0) // Evitiamo doppi separatori di record out << unesc(val); else out << val; } else out << val; } } out << endl; if (!pi.addstatus(1)) break; } return !pi.iscancelled(); } bool TRecordset::save_as_dbf(const char* table, int mode) { char volume[_MAX_DRIVE]; char dirname[_MAX_PATH]; char name[_MAX_FNAME]; char ext[_MAX_EXT]; xvt_fsys_parse_pathname(table, volume, dirname, name, ext, NULL); const int logicnum = table2logic(name); if (mode == 0x0) mode = 0x1; if (mode & 0x4) { if (logicnum >= LF_USER && *dirname == '\0') { TSystemisamfile isam(logicnum); if (isam.zap() != NOERR) return error_box(TR("Impossibile cancellare il file '%s'"), table); } else { TFilename n; n = volume; n.add(dirname); n.add(name); n.ext("*"); TString_array files; list_files(n, files); FOR_EACH_ARRAY_ROW(files, f, row) xvt_fsys_remove_file(*row); } mode = 0x1; } TBaseisamfile* pisam = NULL; if (logicnum >= LF_USER) { if (*dirname) pisam = new TIsamtempfile(logicnum, table); else pisam = new TFast_isamfile(logicnum); } if (pisam == NULL) return error_box("Impossibile creare il file %s", table); TBaseisamfile& isam = *pisam; TRectype& rec = isam.curr(); TString_array names; int valid = 0; for (unsigned int j = 0; j < columns(); j++) { const TVariant& var = get(j); const TString& name = column_info(j)._name; if (rec.exist(name)) { names.add(name); valid++; } else names.add(EMPTY_STRING); } TProgress_monitor pi(items(), TR("Esportazione in corso..."), true); bool ok = true; for (bool go = move_first(); go; go = move_next()) { if (!pi.add_status()) break; rec.zero(); FOR_EACH_ARRAY_ROW(names, j, name) if (name->not_empty()) rec.put(*name, get(j).as_string()); int err = NOERR; bool to_be_written = true; // Devo solo aggiornare parte dei campi? if ((mode & 0x2) && valid < rec.items()) { err = isam.read(_isequal, _lock); if (err != NOERR) rec.zero(); FOR_EACH_ARRAY_ROW(names, j, name) if (name->not_empty()) rec.put(*name, get(j).as_string()); if (err == NOERR) to_be_written = isam.rewrite() != NOERR; } if (to_be_written) { err = (mode & 0x1) ? isam.write() : isam.rewrite(); if (err != NOERR && (mode & 0x3) != 0) err = (mode & 0x1) ? isam.rewrite() : isam.write(); } if (err != NOERR) { ok = error_box(FR("Errore %d durante la scrittura del record %s"), err, rec.build_key()); break; } } return ok; } bool TRecordset::save_as(const char* path, TRecordsetExportFormat fmt, int mode) { if (fmt == fmt_unknown) { TString ext; xvt_fsys_parse_pathname(path, NULL, NULL, NULL, ext.get_buffer(), NULL); ext.lower(); if (ext == "htm" || ext == "html" || ext == "xml" || ext == "xls") fmt = fmt_html; else if (ext == "dbf") fmt = fmt_dbf; else if (ext == "csv") fmt = fmt_csv; } bool ok = false; switch (fmt) { case fmt_html : ok = save_as_html(path); break; case fmt_campo : ok = save_as_campo(path); break; case fmt_dbf : ok = save_as_dbf(path, mode); break; case fmt_as400 : ok = save_as_as400(path); break; case fmt_csv : ok = save_as_csv(path); break; default : ok = save_as_text(path); break; } return ok; } int TRecordset::find_column(const char* column_name) const { int i; for (i = columns()-1; i >= 0; i--) { const TRecordset_column_info& info = column_info(i); if (info._name == column_name) break; } return i; } TVariant& TRecordset::get_tmp_var() const { static TArray _page; // Variants to be returned by get static int _next_var = 0; // Index of next variant to be returned if (_next_var >= 32) _next_var = 0; TVariant* var = (TVariant*)_page.objptr(_next_var); if (var == NULL) { var = new TVariant; _page.add(var, _next_var); } _next_var++; return *var; } const TVariant& TRecordset::get(const char* column_name) const { if (*column_name == '#') return get_var(column_name); char* colon = (char*)strchr(column_name, ':'); //antica porcata if (colon != NULL) { *colon = '\0'; const int i = find_column(column_name); *colon = ':'; if (i >= 0) { const TString& str = get(i).as_string(); TString subfield; subfield << (colon+1) << '='; int s = str.find(subfield); if (s == 0 || (s > 0 && str[s-1] < ' ')) { s += subfield.len(); const int e = str.find('\n', s); TVariant& var = get_tmp_var(); var.set(str.sub(s, e)); return var; } } } else { const int i = find_column(column_name); if (i >= 0) return get(i); } return NULL_VARIANT; } const TVariant& TRecordset::get_var(const char* name) const { const TVariant* var = (const TVariant*)_var.objptr(name); // Non so che variabile sia: provo con quelle di sistema if (var == NULL && *name == '#') { const TFixed_string n(name); // Se mi accorgo che posso e voglio accedere ad un campo del recordset padre if (_parentset != NULL && n.starts_with("#PARENT.")) var = &_parentset->get(name+8); // Attenzione! E' giusto usare get() e non get_var() else { if (n.starts_with("#RECORD.")) { const TFixed_string fn(name + 8); TVariant& v = get_tmp_var(); if (fn == "NUMBER") v.set(current_row()+1); else if (fn == "LAST") v.set(items()); var = &v; } } } return var != NULL ? *var : NULL_VARIANT; } bool TRecordset::set_var(const char* name, const TVariant& var, bool create) { bool ok = false; TVariant* old = (TVariant*)_var.objptr(name); if (old != NULL) { *old = var; ok = true; } else { if (create) { const TFixed_string n(name); if (n.starts_with("#PARENT.") || n.starts_with("#RECORD.")) { // Aggiungo solo il nome alla lista: niente valore! _varnames.add(name); } else { _var.add(name, var); _varnames.add(name); } ok = true; } } if (ok) requery(); return ok; } bool is_var_separator(char c) { if (isspace(c)) return true; return strchr("<=>+-*/(,.", c) != NULL; } // Cerca le variabili nel testo SQL: // Una variabile comincia per # ed e' composta da soli caratteri alfanumerici. // Prima del simbolo # e dopo il nome della variabile deve esserci un separatore o blank void TRecordset::find_and_reset_vars() { _var.destroy(); _varnames.destroy(); const TString& sql = query_text(); int diesis = sql.find('#'); // cerco il primo # for ( ; diesis > 0; diesis = sql.find('#', diesis+1)) // Cerco tutti i # { if (is_var_separator(sql[diesis-1])) // Controllo che ci sia un separatore prima del # { int i = diesis+1; for ( ; sql[i] && (isalnum(sql[i]) || strchr("@_.#", sql[i]) != NULL); i++); if (i > diesis+1) { const TString& name = sql.sub(diesis, i); set_var(name, NULL_VARIANT, true); diesis = i; //ricomincia a cercare variabili dopo la fine della variabile corrente;senza questa istruzione non possono funzionare cose tip #PARENT.#PARENT.33.CAMPO } } } } void TRecordset::parsed_text(TString& sql) const { sql = query_text(); const bool is_isam = sql.starts_with("US"); const bool is_sql = !is_isam; const char* apici = is_isam ? "\"" : "'"; const bool vars = ((TRecordset*)this)->ask_variables(false); if (vars) // Se ci sono variabili faccio le sostituzioni { const TString_array& names = variables(); TString s; FOR_EACH_ARRAY_ROW(names, i, name) // Scandisco tutte le variabili { TVariant var = get_var(*name); int pos = sql.find(*name); for ( ; pos > 0; pos = sql.find(*name, pos+1)) { const TString& after = sql.mid(pos+name->len()); sql.cut(pos); if (var.type() == _datefld) { if (var.is_empty()) s.cut(0) << *apici << *apici; else s.format("%ld", var.as_date().date2ansi()); } else { s = var.as_string(); if (is_sql) // Raddoppia gli apici in SQL { for (int i = 0; s[i]; i++) { if (s[i] == '\'') s.insert("'", i++); } } } if ((var.is_string() && s[0] != *apici && sql.right(1) != apici) || var.is_null()) { s.insert(apici); s << apici; } sql << s << after; } } } } bool ask_variable(const char* name, TVariant& var) { TMask m(TR("Richiesta variabile"), 1, 52, 5); m.add_static(-1, 0, name, 1, 0); m.add_string(101, 0, "", 1, 1, 80, "", 50); m.add_button(DLG_OK, 0, "", -12, -1, 10, 2); m.add_button(DLG_CANCEL, 0, "", -22, -1, 10, 2); m.set(101, var.as_string()); const bool ok = m.run() == K_ENTER; if (ok) { const TString& str = m.get(101); if (str.match("??-??-2???")) var = TDate(str); else if (is_numeric(str)) var = real(str); else var = str; } return ok; } bool TRecordset::ask_variables(bool all) { const bool ok = variables().items() > 0; if (ok) // Se ci sono variabili faccio le sostituzioni { FOR_EACH_ARRAY_ROW(_varnames, i, name) { TVariant var = get_var(*name); if (var.is_null() || all) { ask_variable(*name, var); if (var.is_null()) var.set(""); // Mi serve assolutamente un valore! set_var(*name, var); } } } return ok; } const TString& TRecordset::driver_version() const { return EMPTY_STRING; } TRecordset::TRecordset() : _parentset(NULL), _text_separator('\t') { } /////////////////////////////////////////////////////////// // Utility /////////////////////////////////////////////////////////// static void sort_files(TString_array& files) { TFilename path; // Trasforma i path completi in nomi senza estensione FOR_EACH_ARRAY_ROW(files, i, row) { path = *row; path = path.name(); path.ext(""); path.lower(); *row = path; } files.sort(); // Ordina alfabeticamente // Rimuove i files doppi proveninenti da Campo e Custom for (int j = files.last(); j > 0; j--) { if (files.row(j) == files.row(j-1)) files.destroy(j); } files.pack(); } static bool get_xml_attr(const TString& line, const char* attr, TString& value) { TString80 str; str << ' ' << attr << "=\""; const int pos = line.find(str); if (pos >= 0) { const int apicia = line.find('"', pos)+1; const int apicic = line.find('"', apicia); if (apicic > apicia) { value = line.sub(apicia, apicic); return true; } } value.cut(0); return false; } static bool get_xml_child(const TString& line, const char* tag, TString& value) { TString80 str; str << '<' << tag << '>'; const int pos = line.find(str); if (pos >= 0) { const int apicia = line.find('>', pos)+1; const int apicic = line.find('<', apicia); if (apicic > apicia) { value = line.sub(apicia, apicic); return true; } } value.cut(0); return false; } bool list_custom_files(const char* ext, const char* classe, TString_array& files) { TString_array lista; TFilename path; TWait_cursor hourglass; TFilename name = path.name(); const bool wild = (name.find('*') >= 0) || (name.find('?') >= 0); if (!wild) name = "*"; // Leggo i files in custom if (main_app().has_module(RSAUT)) { TFilename custom = firm2dir(-1); custom.add("custom"); if (!custom.exist()) xvt_fsys_mkdir(custom); path = custom; path.add(name); path.ext(ext); } list_files(path, lista); path = name; path.ext(ext); // Leggo i files in campo list_files(path, lista); sort_files(lista); // Ordino i files e rimuovo i doppioni TString8 acqua; TString stringona, desc; FOR_EACH_ARRAY_ROW(lista, i, row) { path = *row; path.ext(ext); bool ok = path.custom_path(); if (ok) { TScanner scan(path); stringona.cut(0); for (int i = 0; i < 4 && scan.good(); i++) // Leggo solo le prime righe stringona << scan.line(); get_xml_attr(stringona, "class", acqua); if (classe && *classe) ok = acqua == classe; if (ok) { get_xml_child(stringona, "description", desc); TToken_string* riga = new TToken_string; riga->add(*row); riga->add(acqua); riga->add(desc); riga->add(path.find("custom") > 0 ? "X" : ""); files.add(riga); } } } return !files.empty(); } bool select_custom_file(TFilename& path, const char* ext, const char* classe) { TArray_sheet sheet(-1, -1, 78, 20, TR("Selezione"), HR("Nome@16|Classe@8|Descrizione@50|Custom")); TString_array& files = sheet.rows_array(); TFilename first = path.name(); first.ext(""); // Riga su cui posizionarsi bool ok = list_custom_files(ext, classe, files); if (ok) { if (first.full()) // Cerco la prima riga da selezionare se possibile { FOR_EACH_ARRAY_ROW(files, i, row) { if (first.compare(row->get(0), -1, true) == 0) // Ho trovato la selezione corrente { sheet.select(i); break; } } } ok = sheet.run() == K_ENTER; if (ok) { path = sheet.row(-1).get(0); path.ext(ext); ok = path.custom_path(); } } return ok; } /////////////////////////////////////////////////////////// // TCursor_parser /////////////////////////////////////////////////////////// class TCursor_parser { istream& _instr; TArray& _column; TString _pushed; TString _token; TRelation* _relation; TCursor* _cursor; protected: const TString& pop(); const TString& line(); void push(); void add_column_info(const char* table, const TRectype& rec); void parse_sortexpr(TToken_string& se); void parse_filter(TToken_string& filter); void parse_select(TToken_string& filter); void parse_region(TRectype& rec, bool final); void parse_join_param(TRelation* rel, const TString& j, int to); void parse_join(); void parse_sortedjoin(); public: TRelation* get_relation() { return _relation; } TCursor* get_cursor() { return _cursor; } TCursor_parser(istream& instr, TArray& column); }; const TString& TCursor_parser::pop() { if (_pushed.not_empty()) { _token = _pushed; _pushed.cut(0); } else { _token.cut(0); eatwhite(_instr); char instring = '\0'; while (_instr) { char c; _instr.get(c); if (c == EOF) break; if (instring > ' ') // Sono dentro ad una stringa { if (c == '\n') break; if (c == instring) instring = '\0'; _token << c; } else { if (c == '"' || c == '\'') { instring = c; _token << c; } else { if (isspace(c)) break; _token << c; } } } } return _token; } const TString& TCursor_parser::line() { if (_pushed.not_empty()) return pop(); char* buff = _token.get_buffer(512); _instr.getline(buff, _token.size()); return _token; } void TCursor_parser::push() { CHECK(_pushed.empty(), "Repushing?"); _pushed = _token; } void TCursor_parser::add_column_info(const char* table, const TRectype& rec) { for (int i = 0; i < rec.items(); i++) { TRecordset_column_info* info = new TRecordset_column_info; const char* name = rec.fieldname(i); info->_name << table << '.' << name; info->_type = rec.type(name); switch (info->_type) { case _datefld: info->_width = 10; break; case _memofld: info->_width = 50; break; default : info->_width = rec.length(name); break; } _column.add(info); } } void TCursor_parser::parse_sortexpr(TToken_string& se) { const char sep = se.separator(); se.separator(' '); _instr.getline(se.get_buffer(), se.size()); se.strip_double_spaces(); se.replace(' ', sep); se.separator(sep); // Trasforma i nomi dei files in numeri se necessario if (se.find('.') > 0) { TToken_string fld(16, '.'); TString16 name; for (int i = 0; se.get(i, fld); i++) { if (!isdigit(fld[0]) && fld.find('.') > 0) { fld.get(0, name); const int num = ::table2logic(name); if (num != 0) { fld.add(num, 0); se.add(fld, i); } } } } } void TCursor_parser::parse_filter(TToken_string& filter) { const TString& str = pop(); while (str.find('=') > 0) { filter.add(str); pop(); } push(); } void TCursor_parser::parse_select(TToken_string& filter) { filter = line(); filter.trim(); } void TCursor_parser::parse_region(TRectype& rec, bool final) { TString16 field; TString256 value; while (true) { const TString& ass = pop(); const int equal = ass.find('='); if (equal > 0) { field = ass.left(equal); value = ass.mid(equal+1); value.trim(); const bool tilde = value[0] == '='; if (tilde) value.ltrim(1); if (value[0] == '"' || value[0] == '\'') { int fr = -1, to = -1; if (value.ends_with("]")) { const int pa = value.rfind('['); if (pa > 0 && value[pa-1] == value[0]) { TToken_string range(value.mid(pa+1), ','); fr = range.get_int()-1; to = range.get_int(); value.cut(pa); } } value.rtrim(1); value.ltrim(1); if (fr >= 0) value = value.sub(fr, to); } else if (value.find('(') > 0) { TExpression expr(value, _strexpr, true); const TString& str = expr.as_string(); if (expr.error() == 0) { value = str; value.trim(); } } if (tilde && final) value << '~'; rec.put(field, value); } else break; } push(); } void TCursor_parser::parse_join_param(TRelation* rel, const TString& j, int to) { int key = 1; const TString& tok = pop(); if (tok.starts_with("KE")) { pop(); key = atoi(tok); } else push(); int alias = 0; pop(); if (tok.starts_with("AL")) { pop(); alias = atoi(tok); } else push(); TToken_string exp(80); pop(); if (tok == "INTO") { parse_filter(exp); } if (exp.empty()) yesnofatal_box("JOIN senza espressioni INTO"); const int logicnum = table2logic(j); switch (logicnum) { case LF_TABGEN: case LF_TABCOM: case LF_TAB: case LF_TABMOD: rel->add(j, exp, key, to, alias); // join table break; default: rel->add(logicnum, exp, key, to, alias); // join file break; } TString16 tabname; if (alias > 0) tabname << alias << '@'; else tabname = j; const TRectype& rec = rel->curr(logicnum); add_column_info(tabname, rec); } void TCursor_parser::parse_join() { const TString j = pop(); // File or table int to = 0; const TString& tok = pop(); if (tok == "TO") // TO keyword { pop(); to = table2logic(tok); } else push(); parse_join_param(_relation, j, to); } void TCursor_parser::parse_sortedjoin() { TToken_string filter,sortexp; const TString j = pop(); // File or table const TString& tok = pop(); if (tok == "BY" ) parse_sortexpr(sortexp); else push(); pop(); if (tok.starts_with("FI") || tok.starts_with("SE")) { parse_select(filter); } else push(); TRelation* sortrel = new TRelation(table2logic(j)); while (true) { pop(); if (tok.empty() || tok.starts_with("JO")) { break; } if (tok.starts_with("US")) // USING keyword { const TString subj = pop(); // File or table parse_join_param(sortrel, subj, table2logic(j)); } } int to = 0; pop(); if (tok == "TO") // TO keyword { pop(); to = table2logic(tok); } else push(); int key = 1; pop(); if (tok.starts_with("KE")) { pop(); key = atoi(tok); } else push(); int alias = 0; pop(); if (tok.starts_with("AL")) { pop(); alias = atoi(tok); } else push(); TToken_string exp(80); if (pop() == "INTO") { pop(); while (tok.find('=') > 0) { exp.add(tok); pop(); } } push(); TSortedfile *sf= new TSortedfile(atoi(j),sortrel,sortexp,filter,key); _relation->add((TLocalisamfile *)sf, exp, key, to, alias, false); // join table TString16 tabname = j; if (alias > 0) tabname.cut(0) << alias << '@'; add_column_info(tabname, sf->curr()); } TCursor_parser::TCursor_parser(istream& instr, TArray& col) : _instr(instr), _column(col), _relation(NULL), _cursor(NULL) { _column.destroy(); const TString& tok = pop(); if (!tok.starts_with("US")) push(); pop(); if (tok.blank()) return; TString table(tok); table.upper(); if (table.ends_with(".DBF")) { TFilename dbf = table; if (table[0] == '$' || table[0] == '%') { if (table[0] == '%') dbf = firm2dir(0); else dbf = firm2dir(prefix().get_codditta()); dbf.add(table.mid(1)); } else { if (dbf.is_relative_path()) { if (!dbf.custom_path()) { dbf.tempdir(); dbf.add(table); } } } dbf.lower(); TExternisamfile* eif = new TExternisamfile(dbf, true); _relation = new TRelation(eif); } else { int logicnum = table2logic(tok); if (logicnum == LF_MAG && tok == "MAG") // Faccio prevalere la tabella MAG sul file MAG logicnum = LF_TAB; if (logicnum == LF_TAB || logicnum == LF_TABCOM || logicnum == LF_TABMOD) _relation = new TRelation(tok); else _relation = new TRelation(logicnum); } pop(); add_column_info(table, _relation->curr()); int key = 1; // key number if (tok.starts_with("KE")) { pop(); key = atoi(tok); } else push(); pop(); TToken_string filter; if (tok.starts_with("FI") || tok.starts_with("SE")) parse_select(filter); else push(); pop(); if (tok.starts_with("BY")) // "sort BY": user-defined sort { TToken_string ordexpr(256); parse_sortexpr(ordexpr); _cursor = new TSorted_cursor(_relation, ordexpr, "", key); } else push(); if (_cursor == NULL) _cursor = new TCursor(_relation, "", key); _relation->lfile().zero(); // Azzera correttamente tabelle normali e di modulo! TRectype rec_start(_relation->curr()); TRectype rec_stop(rec_start); TString_array fields; pop(); while (tok.starts_with("DI")) { pop(); pop(); TString field; if (tok.find(".") < 0) field << table << "."; field << tok; fields.add(field); pop(); } push(); while (true) { pop(); if (tok.starts_with("FR")) parse_region(rec_start, false); else if (tok.starts_with("TO")) parse_region(rec_stop, true); else if (tok.starts_with("JO")) parse_join(); else if (tok.starts_with("SO")) parse_sortedjoin(); else break; } push(); if (!rec_start.empty() || !rec_stop.empty()) _cursor->setregion(rec_start, rec_stop); if (!filter.empty()) _cursor->setfilter(filter); if (fields.items() > 0) { FOR_EACH_ARRAY_ITEM_BACK(_column, i, obj) { TRecordset_column_info* info = (TRecordset_column_info*)obj; if (fields.find(info->_name) == -1) _column.destroy(i, true); } } if (_relation->items() == 0) // Non ci sono anche tabelle collegate { FOR_EACH_ARRAY_ITEM(_column, i, obj) { TRecordset_column_info* info = (TRecordset_column_info*)obj; const int arrow = info->_name.find('.'); if (arrow > 0) info->_name = info->_name.mid(arrow+1); } } } /////////////////////////////////////////////////////////// // TISAM_recordset /////////////////////////////////////////////////////////// const TVariant& TISAM_recordset::get_field(int logic, const char* fldname) const { const int idx = relation()->log2ind(logic); if (idx < 0) return NULL_VARIANT; TString80 name = fldname; TString16 subfield; int from = 1, to = 0; const int open_bracket = name.find('['); if (open_bracket > 0) { TToken_string range(name.mid(open_bracket+1), ','); from = range.get_int(); to = range.get_int(); name.cut(open_bracket); } const int colon = name.find(':'); if (colon > 0) { subfield = name.mid(colon+1); name.cut(colon); } const TRectype& rec = relation()->file(idx).curr(); const TFieldtypes ft = rec.type(name); if (ft == _nullfld) { switch (logic) // Proviamo la magia dei campi virtuali { case LF_DOC : subfield = name; name = "G1"; break; case LF_RIGHEDOC: subfield = name; name = "RG1"; break; default: return NULL_VARIANT; } } TVariant& var = get_tmp_var(); switch (ft) { case _boolfld: var.set(rec.get_bool(name)); break; case _datefld: var.set(rec.get_date(name)); break; case _realfld: var.set(rec.get_real(name)); break; case _intfld : case _longfld: case _wordfld: var.set(rec.get_long(name)); break; case _intzerofld : // Non usare il convertitore degll interi case _longzerofld: // in modo da preservare gli zeri iniziali default : var.set(rec.get(name)); break; } if (subfield.not_empty()) { subfield << '='; const TString& str = var.as_string(); int s = str.find(subfield); if (s == 0 || (s > 0 && str[s-1] < ' ')) { s += subfield.len(); const int e = str.find('\n', s); var.set(str.sub(s, e)); } else var.set_null(); } if (to >= from && !var.is_empty()) var.set(var.as_string().sub(from-1, to)); return var; } const TVariant& TISAM_recordset::get(size_t c) const { const TRecordset_column_info* info = (const TRecordset_column_info*)_column.objptr(c); if (info != NULL) { int logic = 0; const char* field = info->_name; const int dot = info->_name.find('.'); if (dot > 0) { logic = table2logic(info->_name.left(dot)); field += dot+1; } return get_field(logic, field); } return NULL_VARIANT; } const TVariant& TISAM_recordset::get(const char* name) const { if (*name == '#') return get_var(name); const TFixed_string fldname(name); int table_end = fldname.find('.'); int field_start = table_end+1; if (table_end < 0) { table_end = fldname.find('-'); if (table_end > 0) field_start = table_end+2; } int logic = 0; const char* field = name; if (table_end > 0) { const TString& table = fldname.left(table_end); logic = table2logic(table); field += field_start; } return get_field(logic, field); } const TRecordset_column_info& TISAM_recordset::column_info(size_t i) const { return (const TRecordset_column_info&)_column[i]; } TCursor* TISAM_recordset::cursor() const { if (_cursor == NULL && _use.full()) { TString use; parsed_text(use); TParagraph_string msg(use, 64); TPerformance_profiler prof(msg.get(0)); TISAM_recordset* my = (TISAM_recordset*)this; istrstream instr(use.get_buffer(), use.len()+1); //"barata" x aggiungere il carattere finale TCursor_parser parser(instr, my->_column); my->_relation = parser.get_relation(); my->_cursor = parser.get_cursor(); if (_cursor != NULL) { set_custom_filter(*_cursor); const TRecnotype items = _cursor->items(); _cursor->freeze(); if (items > 0) *_cursor = 0L; } } return _cursor; } TRelation* TISAM_recordset::relation() const { cursor(); return _relation; } TRecnotype TISAM_recordset::current_row() const { TCursor* c = cursor(); return c != NULL ? c->pos() : -1; } TRecnotype TISAM_recordset::items() const { TCursor* c = cursor(); return c != NULL ? c->items() : 0; } unsigned int TISAM_recordset::columns() const { cursor(); return _column.items(); } bool TISAM_recordset::move_to(TRecnotype pos) { TCursor* c = cursor(); bool ok = c != NULL && pos >= 0; if (ok) { *c = pos; ok = pos >= 0 && pos < c->items(); } return ok; } void TISAM_recordset::reset() { _column.destroy(); requery(); } void TISAM_recordset::requery() { if (_cursor != NULL) { delete _cursor; _cursor = NULL; } if (_relation != NULL) { delete _relation; _relation = NULL; } } void TISAM_recordset::set(const char* use) { reset(); _use = use; find_and_reset_vars(); } const TString& TISAM_recordset::driver_version() const { TString& tmp = get_tmp_string(20); DB_version(tmp.get_buffer(), tmp.size()); return tmp; } TISAM_recordset::TISAM_recordset(const char* use) : _relation(NULL), _cursor(NULL) { set(use); } TISAM_recordset::~TISAM_recordset() { requery(); } /////////////////////////////////////////////////////////// // TRecordset_sheet /////////////////////////////////////////////////////////// void TRecordset_sheet::get_row(long r, TToken_string& row) { row.separator('\t'); row.cut(0); if (_query.move_to(r)) { TString str; unsigned int cols = _query.sheet_head().items(); if (cols == 0) cols = _query.columns(); for (unsigned int c = 0; c < cols; c++) { _query.get(c).as_string(str); row.add(str); } } } long TRecordset_sheet::get_items() const { return _query.items(); } TRecordset_sheet::TRecordset_sheet(TRecordset& query, const char* title, byte buttons) : TSheet(-1, -1, -2, -5, title, query.sheet_head(), buttons, 1), _query(query) { }