#include #include #include #include #include #define TEXT_TMP_SIZE 1024 static TString _TEXT_TMP(TEXT_TMP_SIZE); static char* TEXT_TMP = (char*)(const char*)_TEXT_TMP; class _HotSpot : public TObject { public: // TArray _spots; // tokenstrings char _bg, _fg; _HotSpot (char fg, char bg) { _fg = fg; _bg = bg; } virtual ~ _HotSpot () { } }; // @doc EXTERNAL // @mfunc Permette di settare gli hot_spot void TTextfile::set_hotspots ( char fg, // @parm Colore di foreground da utilizzare per l'hotspot char bg) // @parm Colore di background da utilizzare per l'hotspot // @comm Aggiunge all'array gli hotspots relativi alla pagina in memoria // (come con xytext) { _HotSpot *hp = new _HotSpot (fg, bg); _hotspots.add (hp); } style TTextfile::_trans_style (char ch) { switch (ch) { case 'r': return normal; case 'i': return italic; case 'b': return bold; case 'u': return underlined; case 't': return tabbed; default: return normal; } } void TTextfile::_save_changes() { TWait_cursor hourglass; // fa i dovuti replace anche sul disco (solo replace di linee esistenti) long line = 0l; fclose(_index); remove(_indname); const TString oldfile(_filename); _filename.temp("txtf"); FILE* newf = fopen(_filename, "a+"); if ((_index = fopen(_indname, "w+b")) == NULL || newf == NULL) { NFCHECK("Impossibile aprire files temporanei"); freeze(); return; } // fseek(_instr, 0l, SEEK_SET); rewind(_instr); while (!feof(_instr)) { const long l = ftell(newf); fwrite (&l, sizeof(long), 1, _index); if (ferror(_index) || ferror(newf)) { error_box ("Errore di scrittura file temporaneo: scrittura interrotta"); freeze (); } if (fgets(TEXT_TMP, TEXT_TMP_SIZE, _instr) == NULL) break; if (line >= _page_start && line <= _page_end) { TString& lin = (TString&)(_page[(int)(line - _page_start)]); if (_dirty_lines[line - _page_start]) { strcpy(TEXT_TMP, lin); strcat(TEXT_TMP, "\n"); } } fprintf(newf, "%s", TEXT_TMP); line++; } fflush(_index); fclose(_instr); fclose(newf); remove(oldfile); rename(_filename, oldfile); _filename = oldfile; _instr = fopen(_filename, "a+"); } void TTextfile::_read_page (long n) { if (_dirty_lines.ones() > 0l) { _save_changes(); _dirty_lines.reset(); } switch (_direction) { case down: _page_start = n; break; case up: _page_start = n + _page_size; break; case updown: _page_start = n - (_page_size / 2l); break; } if (_page_start < 0l) _page_start = 0l; if ((_page_start + _page_size) > _lines) _page_end = _lines - 1; else _page_end = _page_start + _page_size - 1; // zap hotspots _spots.destroy (); long l = 0l; if (_page_start != 0l) { fseek (_index, _page_start * (long) sizeof (long), SEEK_SET); fread (&l, sizeof (long), 1, _index); } fseek (_instr, l, SEEK_SET); for (long i = _page_start; i <= _page_end; i++) { if (feof (_instr)) break; fgets (TEXT_TMP, TEXT_TMP_SIZE, _instr); TEXT_TMP[strlen (TEXT_TMP) - 1] = '\0'; TString & ts = (TString &) _page[(int) (i - _page_start)]; CHECK(ts.size() > 0, "Corrupted string"); ts = TEXT_TMP; if (_interactive) { TString hcol (6); // find hotspots and compile list int len = 0; const char *cp; read_line (i, 0, FALSE); while ((cp = piece ())) { for (int z = 0; z < _hotspots.items (); z++) { _HotSpot & hs = (_HotSpot &) _hotspots[z]; if (hs._fg == get_foreground () && hs._bg == get_background ()) { TToken_string *tts = new TToken_string (50); tts->add (i); // line number tts->add (len); tts->add (len + (int) strlen (cp)); tts->add (cp); tts->add (z); _spots.add (tts); break; } } len += strlen (cp); } } } } // @doc EXTERNAL // @mfunc Legge il testo formattato void TTextfile::read_line ( long n, // @parm Numero della linea da leggere long pos, // @parm Numero della colonna da leggere (mai utilizzata) bool pg) // @parm Fa si' che se la linea non e' nella pagina corrente non si faccia nulla, // diversamente rilegge una nuova pagina dal file. { CHECK (_isopen, "Attempt operation on closed file"); // CHECKD (n >= 0 && n < _lines, "Line not present", n); if (pg && !_in_page (n)) _read_page (n); TString *tp = (TString *) _page.objptr (int (n - _page_start)); if (tp == NULL) return; const char *sp = (const char *) (*tp); _item = 0; _line = ""; int ndx = 0, p = 0; bool first = TRUE; _cur_line = n; char ch; int col = ((int) 'w' << 8) | (int) 'n'; long stl = (long) col << 16; while ((ch = *sp++)) { if (ch == '<' && *(sp) == '@') { // merge field if rel != NULL; // else fill with whitespace const char * save_sp = sp; TToken_string id(80, '@'); sp++; bool terminated = FALSE; while ((ch = *sp++) != '>' && !terminated) if (ch != '\0') id << ch; else terminated = TRUE; if (!terminated) // Prosegue normalmente se non trova la matching > { // id contains tokenstring separated by @ // but with casinations for possible lack of spacing // add spaces if needed for (int i = 0; i < id.len(); i++) { if (id[i] == '@' && id [i+1] == '@') { id.insert(" ", i+1); i+= 2; } } // parse string int len; TString80 file; TString16 field; TString80 format; char just; file = id.get(); format = id.get(); len = (int)id.get_long(); just = id.get_char(); int pos = 0; if ((pos = file.find("->")) == -1) error_box("field specification error"); else { file.cut(pos); field = file.mid(pos+2); } if (len == 0) len = id.len(); TString txt(512); TFieldtypes type; if (_rel != NULL) { // retrieve file and field if (atoi(file) == 0) { // tabella TLocalisamfile& t = _rel->lfile(file); txt = t.get(field); type = t.curr().type(field); } else { TLocalisamfile& f = _rel->lfile(atoi(file)); txt = f.get(field); type = f.curr().type(field); } // apply format to date and number switch (type) { case _longfld: case _realfld: { real r(txt); txt = r.string(format); } break; case _datefld: { TDate dd(txt); TFormatted_date d(dd, format); txt = d.string(); } break; default: break; } // justify as requested if (txt.len() < len) { switch(type) { case _longfld: case _realfld: txt.right_just(len); break; default: txt.left_just(len); break; } } } else { txt.left_just(len); } // ficca il testo cola' dove si puote const int txtlen = txt.len(); for (int k = 0; k < txtlen; k++) TEXT_TMP[ndx++] = txt[k]; } // see (!terminated) above else // Restore sp pointer { sp = save_sp; ch = '<'; } } if (ch == '@' || (ch == '$' && *(sp) == '[')) { if (!first && p >= pos) { CHECKD(ndx < TEXT_TMP_SIZE, "Bad TEXT_TMP index ", ndx); _styles[_item++] = stl; TEXT_TMP[ndx] = '\0'; _line.add (TEXT_TMP); ndx = 0; } while (ch && (ch == '@' || (ch == '$' && *sp == '['))) { if (ch == '@') // font style change ? { const char c = *sp++; if (c == '@') { _styles[_item++] = stl; _line.add("@"); } else { style sss = _trans_style (c); if (sss == normal) stl = (long) col << 16; else stl |= (long) sss; } } else if (ch == '$' && *sp == '[') // color change { ++sp; // eat '[' col = *sp++; ++sp; // eat ',' col |= ((int) (*sp++) << 8); ++sp; // eat ']' stl = (stl & 0x0000ffff) | ((long) col << 16); } ch = *sp++; } // while } if (ch && p >= pos) { CHECKD(ndx < TEXT_TMP_SIZE, "Bad TEXT_TMP index ", ndx); first = FALSE; TEXT_TMP[ndx++] = ch; } p++; } _styles[_item++] = stl; TEXT_TMP[ndx] = '\0'; _line.add(TEXT_TMP); _item = 0; } // @doc EXTERNAL // @mfunc Ritorna la stringa di caratteri senza formattazione // // @rdesc Ritorna la stringa di caratteri ed eventualmente con i campi sostituiti // se la relazione non e' NULL const char* TTextfile::line( long j, // @parm Riga di cui ritornare la stringa long pos, // @parm Colonna di cui ritornare la stringa int howmuch) // @parm Numero di caratteri utili della stringa da ritornare (default -1) // @comm Se

assume valore -1 ritorna tutta la pagina { if (_cur_line != j) read_line (j); *TEXT_TMP = '\0'; FOR_EACH_TOKEN(_line, l) strcat (TEXT_TMP, l); if (howmuch >= 0) { if (((unsigned int)pos+howmuch) < strlen(TEXT_TMP)) TEXT_TMP[pos+howmuch] = '\0'; } return strlen(TEXT_TMP) > (word)pos ? &(TEXT_TMP[pos]) : ""; } // @doc EXTERNAL // @mfunc Cerca una stringa di testo all'interno di una riga // // @rdesc Ritorna la posizione in cui e' stato rintracciato il testo. Ritorna -1 se non e' stato // trovato il testo passato. long TTextfile::search( const char* txt, // @parm Testo da cercare int& ret, // @parm Intero in cui posizionare la posizne del carattere long from, // @parm Posizione all'interno della riga da cui iniziare la ricerca (default 0) bool down, // @parm Indica se la ricerca va effettuata all'indietro: // // @flag TRUE | Ricerca dalla posizione indicata all'inizio della riga (default) // @flag FALSE | Ricerca dalla posizione indicata alla fine della riga bool casesens, // @parm Indica se ricerca il testo con criterio case sensitive (default FALSE) bool regexp) // @parm indica se considerare la stringa un'espressione regolare (def. FALSE) // @comm Cerca in una riga per volta rispettando i formati { TString text(txt); if (!casesens) text.lower(); if (regexp && !text.ends_with("*")) text << '*'; TString lin(512); for (long i = from; down ? (i < lines()) : (i >= 0); down ? i++ : i--) { lin = line(i); if (!casesens) lin.lower(); if (regexp) { for (ret = 0; lin[ret]; ret++) { if (lin.mid(ret).match(text)) return i; } } else { ret = lin.find(text); if (ret >= 0) return i; } } return -1l; } // @doc EXTERNAL // @mfunc Sostituisce un testo all'interno di una riga // // @rdesc Ritorna la posizione in cui e' stato sostituito il testo. Ritorna -1 se non e' stata // fatto nessuna operazione di sostituzione. int TTextfile::replace( long l, // @parm Numero della riga nella quale sostituire il testo const char* txt, // @parm Test da inserire nella riga int pos, // @parm Posizione nella quale inserire il testo (default 0) int len) // @parm Lunghezza del testo da sostituire (default 0) { if (_cur_line != l) read_line(l); TString& line = (TString&)_page[int(l-_page_start)]; char ch; int i = 0, cnt = 0, skip = 0; bool sforating = FALSE; // here's a nice casin while(i < 256) { if (!sforating) { ch = line[i++]; if (ch == '\0') sforating = TRUE; else if (ch == '@' && strchr("ribuokt",line[i]) != NULL) { skip +=2; i++; cnt--; } else if (ch == '$' && line[i] == '[') { skip +=3; i++; cnt--; while(line[i++] != ']') if (line[i] == '\0') return -1; else skip++; } } if (cnt == pos) { line.overwrite(txt, cnt+skip); _dirty_lines.set(l-_page_start); return cnt; } else cnt++; } return -1; } // @doc EXTERNAL // @mfunc Ritorna la stringa di caratteri con la formattazione // // @rdesc La stringa ritornata ha la formattazione (ovvere tutti i @codes, in modo che anche // la printer la possa utilizzare) const char *TTextfile::line_formatted( long j) //parm Numero della riga da ritornare { if (_cur_line != j) read_line (j); TString* tp = (TString*)_page.objptr(int(j-_page_start)); strcpy(TEXT_TMP, (const char*)(*tp)); return TEXT_TMP; } long TTextfile::get_attribute (int pos) { long stl = 0; if (pos == -1) { CHECK (_item > 0, "must execute piece() before style()!"); stl = _styles[_item - 1]; } else { int x = 0, nd = 0; const char *c; _line.restart (); while ((c = _line.get ()) != NULL) { x += strlen (c); stl = _styles[nd++]; if ((x - 1) >= pos) break; } } return stl; } // @doc EXTERNAL // @mfunc Ritorna lo stile del piece // // @rdesc Ritorna un numero (vedi ) indicante lo stile int TTextfile::get_style ( int pos) // @parm Posizione del carattere di cui ritornare lo stile (default -1) // @comm Se viene passato a

valore -1 ritorna lo stile di tutta la piece, altrimente // solamente quello del carattere selezionato. { const long x = get_attribute(pos) & ~tabbed; return (int) (x & 0x0000ffff); } // @doc EXTERNAL // @mfunc Ritorna il colore di background del piece // // @rdesc Ritorna il codice del colore, NULL se il carattere non esiste char TTextfile::get_background ( int pos) // @parm Posizione del carattere di cui conoscere il colore di background (default -1) // @comm Se

e' minore di 0 ritorna il colore dell'intero piece, altrimenti solamente // quello del carattere selezionato. // // @xref { long x = get_attribute (pos); return (char) (x >> 24); } // @doc EXTERNAL // @mfunc Ritorna il colore di foreground del piece // // @rdesc Ritorna il codice del colore, NULL se il carattere non esiste char TTextfile::get_foreground ( int pos) // @parm Posizione del carattere di cui conoscere il colore di background (default -1) // @comm Se

e' minore di 0 ritorna il colore dell'intero piece, altrimenti solamente // quello del carattere selezionato. // // @xref { long x = get_attribute (pos); return (char) ((x >> 16) & 0x000000ff); } const char* TTextfile::piece() { const char* l = _line.get(_item); if (l == NULL) return NULL; _item++; return strcpy(TEXT_TMP, l); } // @doc EXTERNAL // @mfunc Ritorna la parola alla posizione indicata // // @rdesc Stringa indicante la parola cercate const char *TTextfile::word_at ( long x, // @parm Numero della parola da ritornare long y) // @parm Numero della linea su cui cercare la parole { CHECK (_isopen, "Attempt operation on closed file"); TString s (line (y)); int x2 = 0; if (x < s.len ()) { while (isspace (s[(int) x])) { if (x == (s.len () - 1) && y < (_lines - 1l)) { s = line (++y); x = 0l; } else if (x < (s.len () - 1)) x++; else break; } while (isalnum (s[(int) x])) TEXT_TMP[x2++] = s[(int) x++]; } TEXT_TMP[x2] = '\0'; return TEXT_TMP; } // @doc EXTERNAL // @mfunc Aggiunge una riga al text (con i formati del caso) // // @rdesc Ritorna il risultato dell'operazione: // // @flag TRUE | Se la riga e' stat aggiuntac correttamente // @flag FALSE | Se non e' riuscito ad aggiungere la riga bool TTextfile::append ( const char *l) // @parm Riga da aggiungere { CHECK (_isopen, "Attempt operation on closed file"); if (!_accept) return FALSE; fseek (_instr, 0l, SEEK_END); fseek (_index, 0l, SEEK_END); long cpos = ftell (_instr); fprintf (_instr, "%s\n", l); fwrite (&cpos, sizeof (long), 1, _index); if (ferror (_index) || ferror (_instr)) { error_box ("Errore di scrittura file temporaneo: scrittura interrotta"); freeze (); } fflush (_index); fflush (_instr); _lines++; _dirty = TRUE; if ((_lines) < (_page_start + _page_size)) { if (_interactive) { _page.add (new TString(l), int(_lines - _page_start - 1)); _page_end++; int len = 0; const char *cp; read_line (_lines - 1); while ((cp = piece ())) { for (int z = 0; z < _hotspots.items (); z++) { _HotSpot & hs = (_HotSpot &) _hotspots[z]; if (hs._fg == get_foreground () && hs._bg == get_background ()) { TToken_string *tts = new TToken_string (50); tts->add (_lines - 1l); // line number // tts->add (len); tts->add (len + (int) strlen (cp)); tts->add (cp); tts->add (z); _spots.add (tts); break; } } len += strlen (cp); } } return TRUE; } return FALSE; } void TTextfile::close () { if (_isopen) { fclose (_instr); fclose (_index); _instr = _index = NULL; _isopen = FALSE; } } void TTextfile::print () { printer().print_txt(*this); } // @doc EXTERNAL // @mfunc Scrive il testo (non formattato) su file // // @rdesc Ritorna il risultato dell'operazione di scrittura // // @flag TRUE | Se e' riuscito a scrivere il testo // @flag FALSE | Se ha avuto qualche problema nella gestione del file in cui scrivere bool TTextfile::write ( const char *path, // @parm Nome del file in cui scrivere il testo TPoint * from, // @parm Punto da cui iniziare a scrivere il testo TPoint * to, // @parm Punto a cui terminare di scrivere il testo int size) // @parm lunghezza della riga (0 = variabile, > 0 fissa) { FILE* fp = fopen(path, "w"); if (fp != NULL) { TString s(512); long starty = from == NULL ? 0l : from->y; int startx = from == NULL ? 0 : (int) from->x; long endy = to == NULL ? _lines - 1l : to->y; int endx = to == NULL ? -1 : (int) to->x; for (long j = starty; j <= endy; j++) { s = line(j); if (j == endy && endx == -1) endx = s.len(); if (j == starty && j == endy) s = s.sub(startx, endx); else if (j == starty) s = s.mid(startx); else if (j == endy) s = s.left(endx); TString8 frm("%s\n"); if ( size > 0) frm.format("%%%ds\n", -size); fprintf(fp, frm, (const char *) s); } fclose (fp); } else warning_box ("Impossibile scrivere il file %s; scrittura fallita", path); return fp != NULL; } // @doc EXTERNAL // @mfunc Scrive il testo da punto a punto (non formattato) su string_array void TTextfile::write( TString_array& arr, // @parm Array in cui scrivere il testo TPoint * from, // @parm Punto da cui iniziare a scrivere il testo TPoint * to, // @parm Punto a cui terminare di scrivere il testo int size) // @parm lunghezza della riga (0 = variabile, > 0 fissa) { arr.destroy(); TString s(512); long starty = from == NULL ? 0l : from->y; int startx = from == NULL ? 0 : (int) from->x; long endy = to == NULL ? _lines - 1l : to->y; int endx = to == NULL ? -1 : (int) to->x; for (long j = starty; j <= endy; j++) { s = line(j); if (j == endy && endx == -1) endx = s.len(); if (j == starty && j == endy) s = s.sub(startx, endx); else if (j == starty) s = s.mid(startx); else if (j == endy) s = s.left(endx); if (size > 0) s.rpad(size); arr.add(s); } } // Determina il tipo di una stringa: 0=ignoto; 1=stringa; 2=numero static int str_type(TString& str) { str.rtrim(); if (str.blank()) return 0; bool is_string = false; bool is_number = true; for (int i=0; str[i]; i++) { const char c = str[i]; if (c < '\0' || isalnum(c)) is_string = true; if (strchr("0123456789,.", c) == NULL) is_number = false; } if (is_number) { str.strip("."); str.replace(',', '.'); const real r(str); str = r.stringe(); } return is_number ? 2 : (is_string ? 1 : 0); } static bool is_text_line(TString& str) { const int l = str.trim().len(); if (l >= 64) { const char c = str[0]; if (strchr("_-=", c) != NULL && str[l/2] == c && str[l-1] == c) return true; } return false; } bool TTextfile::write_xls(const TFilename& xls) { const TPrinter& pr = printer(); int headerlen = pr.headersize(); const int footerlen = pr.footersize(); const int pagelen = pr.formlen(); TString str; int tabstart = 1; int tabstop = pagelen - footerlen; if (_lines < tabstop) tabstop = _lines; int header_end = 12; if (headerlen > 3) header_end = headerlen; if (header_end > tabstop) header_end = tabstop; for (long j = 1; j < header_end; j++) { read_line(j); str = piece(); if (is_text_line(str)) { if (tabstart <= 1) tabstart = j+1; else { tabstop = j; headerlen = j; } } } if (tabstart <= 1 && headerlen > 0) tabstart = headerlen; if (tabstart < 2) tabstart = 2; TTabulator tab; for (long j = tabstart; j < tabstop; j++) { read_line(j); int x = 0; for (const char* cp = piece(); cp; cp = piece()) { str = cp; const int len = str.len(); if (str_type(str) > 0) tab.add_field(x, str.len()); x += len; } } tab.sort(); TToken_string riga(256, '\t'); TString8 ext = pr.get_form_name().ext(); ext.lower(); const bool is_rep = ext == "frm" || ext == "rep"; ofstream out(xls); for (long j = 0; j < _lines; j++) { const int row = j % pagelen; if (is_rep) { // Esporto la testata solo nella prima pagina e lascio il footer perchè nei form è variabile in lunghezza if (row < headerlen && j >= pagelen) continue; } else { // Esporto la testata solo nella prima pagina ed il footer solo nell'ultima if ((row < headerlen && j >= pagelen) || (row >= pagelen-footerlen && row < _lines-pagelen)) continue; } read_line(j); riga.cut(0); int x = 0; for (const char* cp = piece(); cp; cp = piece()) { str = cp; const int len = str.len(); const int st = str_type(str); if (st > 0) { int idx, pos; if (tab.find_column(x, str.len(), idx, pos)) { const char* old = riga.get(pos); if (old && *old) str.insert(old); riga.add(str, pos); } } x += len; } if (riga.full()) out << riga << endl; } return xls.exist(); } void TTextfile::destroy () { CHECK(_istemp, "destroy() chiamata su testo permanente!"); if (_page.items () > 0) { if (_index) fclose (_index); if (_instr) fclose (_instr); _filename.fremove(); _indname.fremove(); _page_start = _lines = 0l; _page_end = _cur_line = -1l; _accept = TRUE; _instr = fopen (_filename, "a+"); _indname.temp (); _index = fopen (_indname, "w+b"); if (_index == NULL || _instr == NULL) { cantwrite_box (_filename); freeze (); } _isopen = TRUE; // _page.destroy (); _spots.destroy (); } } TTextfile ::TTextfile (const char *file, int pagesize, direction preferred, bool interactive): _page (pagesize), _dirty_lines(pagesize), _page_start (0l), _page_end (-1l), _page_size (pagesize), _lines (0l), _cur_line (-1), _filename (file), _index (NULL), _direction (preferred), _item (0), _line (256), _hotspots (4), _dirty (FALSE), _istemp (FALSE), _accept (TRUE), _interactive(interactive), _rel(NULL) { // open file & build index if (file == NULL || *file <= ' ') { _filename.temp("txtf"); _istemp = TRUE; } for (int i = 0; i < pagesize; i++) _page.add(new TString(132)); _isopen = TRUE; _instr = fopen (_filename, "a+"); _indname.temp("txti"); _index = fopen (_indname, "w+b"); if (_index == NULL || _instr == NULL) { cantwrite_box(_filename); freeze (); } if (!_istemp) while (!feof(_instr)) { const long l = ftell (_instr); fwrite (&l, sizeof (long), 1, _index); if (ferror(_index) || ferror(_instr)) { cantwrite_box(_indname); freeze(); } if (fgets (TEXT_TMP, TEXT_TMP_SIZE, _instr) == NULL) break; _lines++; } } TTextfile::~TTextfile () { if (_index) fclose (_index); if (_instr) fclose (_instr); if (_istemp) remove ((const char *) _filename); remove ((const char *) _indname); } /////////////////////////////////////////////////////////// // TTracciatoInvio /////////////////////////////////////////////////////////// void TTracciatoInvio::add_field(const char* name, InvioFieldType type, int len, int dec) { switch (type) { case AN: break; case CB: if (len == 0) len = 1; type = NU; break; case CF: type = AN; len = 16; break; case PI: case CN: type = NU; len = 11; break; case DA: type = NU; len = 4; break; case D6: type = NU; len = 6; break; case DT: type = NU; len = 8; break; case NP: type = NU; case NU: break; case PN: case PR: type = AN; len = 2; break; case QU: CHECK(len > dec + 1, "numero di decimali incoerente"); break; case FV: len = strlen(name); // CHECK(strlen(name) == len, "lunghezza campo fisso incoerente"); break; default: CHECK(false, "tipo campo sospetto"); break; } TFieldInvio* info = new TFieldInvio; info->_desc = name; info->_type = type; info->_pos = _len + 1; info->_len = len; info->_dec = dec; _len += len; _fields.add(name, info); } const TFieldInvio& TTracciatoInvio::field(const char * name) const { TFieldInvio* info = (TFieldInvio*)_fields.objptr(name); #ifdef DBG if (info == nullptr) fatal_box("Non esiste il campo %s sul tipo record %c", name, _tipo); #endif return *info; } void TTracciatoInvio::zero(TString& buffer) { buffer.fill(' ', TOTAL_SIZE); char* ptr = buffer.get_buffer(); FOR_EACH_ASSOC_OBJECT( _fields, hash, name, obj) { const TFieldInvio& info = (const TFieldInvio&)_fields[name]; if (info._type == NU || info._type == CB || info._type == DA || info._type == DT || info._type == CN) memset(ptr + info._pos - 1, '0', info._len); else if (info._type == FV) buffer.overwrite(info._desc, info._pos -1, info._len); } buffer[0] = _tipo; buffer.overwrite("A\r\n", TOTAL_SIZE - 3); } TTracciatoInvio::TTracciatoInvio(char tipo) : _tipo(tipo), _len(0) { if (strchr("ABCEFHJZ[", tipo) == NULL) NFCHECK("Tipo record non valido: %c", tipo); add_field("Tipo record", AN, 1); // 1 } TTracciatoInvio::~TTracciatoInvio() { } /* /////////////////////////////////////////////////////////// // TTracciatiInvio /////////////////////////////////////////////////////////// TTracciatiInvio _trcInvio; TTracciatiInvio & tracciati() { return _trcInvio; } class TTracciatiInvio : public TObject { TArray _trc; public: const TTracciatoInvio* tracciato(TTracciatoInvio * trc); const TTracciatoInvio& tracciato(TRecordInvio & rec); int tipo2pos(int tipo) const; bool exist(int tipo) const { return _trc.objptr(tipo2pos(tipo)) != nullptr; } void destroy(); TTracciatiInvio(); virtual ~TTracciatiInvio(); }; TTracciatiInvio & tracciati(); const TTracciatoInvio * TTracciatiInvio::tracciato(TTracciatoInvio * trc) { char tipo = trc->tipo(); int pos = tipo2pos(tipo); if (!exist(tipo)) _trc.add(trc, pos); return (const TTracciatoInvio*)_trc.objptr(pos); } const TTracciatoInvio& TTracciatiInvio::tracciato(TRecordInvio & rec) { char tipo = rec.tipo_record(); int pos = tipo2pos(tipo); if (!exist(tipo)) _trc.add(rec.add_tracciato(), pos); return *(const TTracciatoInvio*)_trc.objptr(pos); } int TTracciatiInvio::tipo2pos(int tipo) const { CHECK((tipo >= 'A' && tipo <= 'Z') || tipo == '[', "Tipo record non valido"); return tipo - 'A'; } void TTracciatiInvio::destroy() { _trc.destroy(); } TTracciatiInvio::TTracciatiInvio() { } TTracciatiInvio::~TTracciatiInvio() { destroy(); // Non viene mai chiamato! } */ TTracciatoInvioFileDati::TTracciatoInvioFileDati(const TRectype& rec) { for (int i = 0; i < rec.items(); i++) { const TString16 fieldname(rec.rec_des().Fd[i].Name); TFieldtypes t = rec.type(fieldname); switch (t) { case _nullfld: break; case _alfafld: // @emem Campo di tipo alfanumerico add_field(fieldname, AN, rec.length(fieldname)); break; case _intfld: // @emem Campo di tipo intero case _longfld: // @emem Campo di tipo intero lungo add_field(fieldname, NU, rec.length(fieldname)); break; case _realfld: // @emem Campo di tipo reale (vedi ) add_field(fieldname, QU, rec.length(fieldname), rec.ndec(fieldname)); break; case _datefld: // @emem Campo di tipo data (vedi ) add_field(fieldname, DT, rec.length(fieldname)); break; case _wordfld: // @emem Campo di tipo intero senza segno add_field(fieldname, NU, rec.length(fieldname)); break; case _charfld: // @emem Campo di tipo carattere add_field(fieldname, AN, 1); break; case _boolfld: // @emem Campo di tipo booleano add_field(fieldname, CB, rec.length(fieldname)); break; case _intzerofld: // @emem Campo di tipo intero zero filled case _longzerofld: // @emem Campo di tipo intero lungo zero filled add_field(fieldname, NU, rec.length(fieldname)); break; case _memofld: add_field(fieldname, AN, 512); break; } } } /////////////////////////////////////////////////////////// // TRecordInvio /////////////////////////////////////////////////////////// void TRecordInvio::read_from(istream& ins) { _buffer.fill(' ', TOTAL_SIZE); ins.read(_buffer.get_buffer(), TOTAL_SIZE); } void TRecordInvio::set(const TFieldInvio& fld, const char* val) { TString str(val); // Ci sono campi di 100 caratteri! if (fld._type == AN) str.upper(); if (fld._type == QU) { str.replace(".", ","); int dotpos = str.find("."); if (dotpos < 0) str << "."; dotpos = str.find("."); int dec = fld._dec - (str.len() - dotpos - 1); while (dec > 0) str << '0'; } int lenstr = str.len(); if (lenstr > fld._len) { #ifdef DBG NFCHECK("Campo troppo lungo: '%s' (max. %d)", val, fld._len); #endif if (fld._type == QU) { str.ltrim(lenstr - fld._len); lenstr = fld._len; } else str.cut(lenstr = fld._len); } if (lenstr != fld._len) { str.trim(); if (fld._type == NU) str.right_just(fld._len, '0'); else str.left_just(fld._len); } _buffer.overwrite(str, fld._pos - 1); } void TRecordInvio::set(const char* name, const char* val) { const TFieldInvio& fld = tracciato().field(name); set(fld, val); } void TRecordInvio::set(const char* name, int val) { const TFieldInvio& fld = tracciato().field(name); CHECKS(fld._type == NU, "Invalid numeric field ", name); TString16 str; str.format("%d", val); set(fld, str); } void TRecordInvio::set(const char* name, long val) { const TFieldInvio& fld = tracciato().field(name); CHECKS(fld._type == NU, "Invalid numeric field ", name); TString16 str; str.format("%ld", val); set(fld, str); } void TRecordInvio::set(const char* name, const real& val) { const TFieldInvio& fld = tracciato().field(name); CHECKS(fld._type == NU, "Invalid numeric field ", name); const char* str = val.string(fld._len, 0); set(fld, str); } void TRecordInvio::set(const char* name, const TDate& val) { const TFieldInvio& fld = tracciato().field(name); CHECKS(fld._type == NU && (fld._len == 6 || fld._len == 8), "Invalid date field ", name); const char* str; if (fld._len == 8) str = val.string(full, '\0', full, full, gma_date); else str = val.string(brief, '\0', full, full, gma_date); set(fld, str); } void TRecordInvio::set(const char* name, char val) { const TFieldInvio& fld = get_field(name); CHECKS(fld._type == AN && fld._len == 1, "Invalid char field ", name); const char str[2] = { val, '\0' }; set(fld, str); } void TRecordInvio::set(const char* name, bool val) { const TFieldInvio& fld = get_field(name); CHECKS((fld._type == CB || fld._type == NU) && fld._len == 1, "Invalid boolean field ", name); set(fld, val ? "1" : "0"); } const char* TRecordInvio::get(const char* name, TString& str) const { const TFieldInvio& fld = get_field(name); str = _buffer.mid(fld._pos - 1, fld._len); return str.trim(); } int TRecordInvio::get_int(const char* name) const { TString16 str; get(name, str); return atoi(str); } char TRecordInvio::get_char(const char* name) const { const TFieldInvio& fld = get_field(name); CHECKS(fld._type == AN, "Invalid char field ", name); return _buffer[fld._pos - 1]; } // Calcola i blocchi necessari per contenere la stringa val int TRecordInvio::calculate_blocks(const char* val) const { // Il primo blocco contiene 16 caratteri, gli altri solo 15 perche' c'e' anche il + int blocks = 1; if (val && *val) { const int len = strlen(val); if (len > FIELD_SIZE) blocks += (len - FIELD_SIZE - 1) / (FIELD_SIZE - 1) + 1; } return blocks; } void TRecordInvio::tipo_record(char tipo) { _buffer[0] = tipo; } const TRecordInvio& TRecordInvio::operator = (const TRecordInvio& rec) { _buffer = rec._buffer; if (tipo_record() != rec.tipo_record()) { safe_delete(_tracciato); tracciato(); } return *this; } // Azzera tutti i campi non posizionali void TRecordInvio::azzera_campi_non_posizionali() { CHECK(ha_campi_non_posizionali(), "Impossibile azzerare un record senza campi non posizionali"); char* buf = _buffer.get_buffer() + HEADER_SIZE; memset(buf, ' ', USEABLE_SIZE); } // Aggiunge un campo non posizionale ai record bool TRecordInvio::np_put(const char* code, const char* val) { CHECK(ha_campi_non_posizionali(), "Impossibile aggiungere campi non posizionali"); CHECKS(code && strlen(code) == CODE_SIZE, "Invalid field code ", code); //CHECKS(val && *val, "Can't add empty field ", code); bool ok = true; if (val && *val) { // Cerca il primo posto libero int pos; for (pos = HEADER_SIZE; pos < HEADER_SIZE + USEABLE_SIZE; pos += BLOCK_SIZE) { if (_buffer[pos] == ' ') break; } const int free_blocks = (USEABLE_SIZE - pos) / BLOCK_SIZE; const int needed_blocks = calculate_blocks(val); bool ok = free_blocks >= needed_blocks; TString80 str(val); str.upper(); if (!ok) // Se non ci sono abbastanza blocchi liberi { ok = true; change_record(); pos = HEADER_SIZE; if (str.len() > USEABLE_SIZE) str.cut(USEABLE_SIZE); } int lenstr = str.len(); for (int i = 0; i < lenstr; ) { _buffer.overwrite(code, pos); pos += CODE_SIZE; int maxlen = FIELD_SIZE; if (i > 0) { _buffer.overwrite("+", pos); pos++; maxlen--; } const TString& substr = str.mid(i, maxlen); _buffer.overwrite(substr, pos); pos += maxlen; i += maxlen; } } return ok; } bool TRecordInvio::np_put(const char* code, long val) { TString16 str; str.format("%16ld", val); return np_put(code, str); } bool TRecordInvio::np_put(const char* code, const real& val) { if (!val.is_zero()) { const TString& str = val.stringa(16, 2); return np_put(code, str); } return true; } bool TRecordInvio::np_put(const char* code, char val) { if (val > ' ') { const char str[2] = { val, '\0' }; return np_put(code, str); } return true; } bool TRecordInvio::np_put(const char* code, const TDate& date) { if (date.ok()) { TString16 str; str.format("%8s%02d%02d%04d", "", date.day(), date.month(), date.year()); return np_put(code, str); } return true; } bool TRecordInvio::np_put(const char* code, bool cb) { if (cb) { TString16 str; str.format("%16d", 1); return np_put(code, str); } return true; } bool TRecordInvio::np_get(int pos, TString& key, TString& val) const { CHECK(ha_campi_non_posizionali(), "Impossibile leggere campi non posizionali"); const int n = HEADER_SIZE + pos * BLOCK_SIZE; bool ok = false; if (n < HEADER_SIZE + USEABLE_SIZE) { ok = _buffer[n] > ' '; if (ok) { key = _buffer.mid(n, CODE_SIZE); val = _buffer.mid(n + CODE_SIZE, FIELD_SIZE); #ifdef DBG if (key == "AU001036") int cazzone = atoi(val); #endif } } return ok; } bool TRecordInvio::np_get_real(int pos, TString& key, real& val) const { TString16 str; const bool ok = np_get(pos, key, str); if (ok && str.full()) { str.replace(',', '.'); val = real(str); } else val = ZERO; return ok; } bool TRecordInvio::valid() const { const char tipo = tipo_record(); const bool ok = (tipo > ' ') && (strchr("ABEHJZ", tipo) != NULL); return ok; } TRecordInvio::TRecordInvio(const TRectype& rec) : _buffer(TOTAL_SIZE, ' '), _tracciato(nullptr) { }