#include #include #include #include #include #include #include #include #include "ba1.h" #include "ba1500.h" #include "ba1600.h" #include "ba1700a.h" // definizioni delle colonne dello sheet #define C_MODULE 1 #define C_CODE 2 #define C_RELEASE 3 #define C_PATCH 4 #define C_DATAREL 5 #define C_CURRRELEASE 6 #define C_CURRPATCH 7 #define C_CURRDATAREL 8 #define C_ISPATCH 9 HIDDEN int compare_version(const char* v1, int p1, const char* v2, int p2) { TString16 ver1(v1), ver2(v2); ver1.trim(); if (ver1.len() == 4) ver1.insert((v1[0] == '9') ? "19" : "20", 0); ver2.trim(); if (ver2.len() == 4) ver2.insert((v2[0] == '9') ? "19" : "20", 0); int res = ver1.compare(ver2, -1, TRUE); if (res == 0) res = p1 - p2; return res; } HIDDEN word version2year(const char* v) { TString16 ver(v); if (ver.len() == 4) ver.insert((v[0] == '9') ? "19" : "20", 0); ver.cut(4); return atoi(ver); } /////////////////////////////////////////////////////////// // Maschera principale /////////////////////////////////////////////////////////// class TInstaller_mask : public TArray_sheet { static TInstaller_mask* _curr_mask; word _year_assist; bool _installed; // Flag per verificare se almeno un modulo e' stato installato protected: // TSheet virtual bool on_key(KEY key); static bool tutti_handler(TMask_field& f, KEY k); protected: static bool path_handler(TMask_field& fld, KEY key); static bool sheet_notify(TSheet_field& s, int r, KEY k); static bool install_handler(TMask_field& fld, KEY key); bool add_module(TConfig& ini, const TString& module, bool patch, int pos=-1); bool add_header(TConfig& ini, const TString& module, bool patch); int sort_modules(); void update_version(); bool move_file(const TFilename& fromdir, const TFilename& file, const char* dir) const; bool move_module(const TString& module, TInstall_ini& ini, bool update) const; bool can_install(const char* module, TInstall_ini& ini); void install_selection(); bool install_patches(const TString& module, const TString& lastrelease, int lastpatch); bool do_process(TToken_string& commands) const; bool pre_process(TInstall_ini& ini, const char* module) const; bool post_process(TInstall_ini& ini, const char* module) const; public: bool installed() { return _installed;} bool autoload(); bool install(const TString& module, int patch); TInstaller_mask(); virtual ~TInstaller_mask(); }; TInstaller_mask* TInstaller_mask::_curr_mask = NULL; // Copia nello sheet i dati di un modulo prendendoli da un .ini bool TInstaller_mask::add_module(TConfig& ini, const TString& module, bool patch, int pos) { ini.write_protect(); bool ok = ini.set_paragraph(module); if (ok) { TToken_string row; row = " "; // Not selected row.add(ini.get("Descrizione")); row.add(module); row.add(ini.get("Versione")); row.add(ini.get("Patch")); row.add(ini.get("Data")); row.add(patch ? "X" : " ", 9); if (pos==-1) add(row); else insert(row,pos); } return ok; } // Copia nello sheet i dati di un modulo prendendoli da un .ini bool TInstaller_mask::add_header(TConfig& ini, const TString& module, bool patch) { ini.write_protect(); bool ok = ini.set_paragraph(module); if (ok) { TToken_string row; row = " "; // Not selected row.add(ini.get("Descrizione")); row.add(ini.get(" ")); enable_row(add(row),FALSE); } return ok; } static int compare_modules(const TObject** o1, const TObject** o2) { TToken_string& ts1 = *(TToken_string*)(*o1); TToken_string& ts2 = *(TToken_string*)(*o2); int res = 0; for (int i = 2; i < 5 && res == 0; i++) { TString16 s1 = ts1.get(i); const char* s2 = ts2.get(i); res = s1.compare(s2); } return res; } int TInstaller_mask::sort_modules() { // sort const int tot = int(items()); return tot; // vecchio codice per bubblare for (int r = 0; r < tot; r++) { const bool patch = row(r).get_char(C_ISPATCH) > ' '; const char* mod = row(r).get(C_CODE); if (patch) { TString16 dis=mod; const int patchlevel = row(r).get_int(C_PATCH); // pop the patch line up to the module one int br=r-1; while (br && !(dis==row(br).get(C_CODE) && row(br).get_int(C_PATCH)get(C_CODE) != ' ') { const TString16 module = row->get(C_CODE); ini.set_paragraph(module); const TString16 newver = row->get(C_RELEASE); const TString16 oldver = ini.get("Versione"); row->add(oldver, C_CURRRELEASE); row->add(ini.get("Patch"), C_CURRPATCH); row->add(ini.get("Data"), C_CURRDATAREL); } } force_update(); } // Cerca nel percorso specificato sulla maschera tutti i possibili files .ini // utilizzabili per un'installazione e li inserisce nello spreadsheet bool TInstaller_mask::autoload() { TString_array& mask_rows = rows_array(); const TString& path = get(F_PATH); if (!fexist(path)) return error_box("Specificare un percorso valido"); TWait_cursor hourglass; destroy(); TFilename ininame; ininame = path; ininame.add(TInstall_ini::default_name()); TString_array modules; if (fexist(ininame)) { TInstall_ini ini(ininame); ini.list_paragraphs(modules); FOR_EACH_ARRAY_ROW(modules, i, row) { const TString& module = *row; if (module[0] == '_' || module.len() == 2) { if (module[0] == '_') add_header(ini, module, FALSE); else add_module(ini, module, FALSE); } } } else { ininame = path; ininame.add("??inst.ini"); list_files(ininame, modules); FOR_EACH_ARRAY_ROW(modules, m, row) { TString& ininame = *row; ininame.lower(); const int pos = ininame.find("inst.ini"); CHECKS(pos >= 2, "Invalid installation configuration: ", (const char*)ininame); const TString16 module = ininame.mid(pos-2, 2); TConfig ini(ininame, module); add_module(ini, module, FALSE); } } // add patches ininame = path; ininame.add("??0???a.ini"); modules.destroy(); list_files(ininame, modules); modules.sort(); // sort to have patches in patchlevel order FOR_EACH_ARRAY_ROW(modules, am, arow) { TString& ininame = *arow; ininame.lower(); const int pos = ininame.find("a.ini"); CHECKS(pos >= 6, "Invalid installation configuration: ", (const char*)ininame); const TString16 module = ininame.mid(pos-6, 2); TConfig ini(ininame, module); const int patchlevel = ini.get_int("Patch"); const int maxrows=int(items()); bool found=FALSE; for (int r = maxrows-1; r >=0 && !found; r--) found=(module == row(r).get(C_CODE) ); if (found) { TString16 patchversion = ini.get("Versione"); if (patchversion == mask_rows.row(r+1).get(C_RELEASE) // se le versioni corrispondono ... && patchlevel > atoi(mask_rows.row(r+1).get(C_PATCH)) ) // ..e il patchlevel è superiore { mask_rows.row(r+1).add(patchlevel, C_PATCH); // aggiorna il patchlevel mostrato per il modulo force_update(r+1); } } else add_module(ini, module, TRUE); } const bool ok = sort_modules() > 0; if (ok) update_version(); else error_box("Non e' stato trovato nessun modulo da installare\n" "in %s", (const char*)path); return ok; } bool TInstaller_mask::do_process(TToken_string& commands) const { bool ok = TRUE; TFilename cmd; for (const char* c = commands.get(0); c && ok; c = commands.get()) { cmd = c; if (!cmd.blank()) { TWait_cursor hourglass; TExternal_app app(cmd); ok = app.run() == 0; } } return ok; } bool TInstaller_mask::pre_process(TInstall_ini& ini, const char* module) const { TAuto_token_string commands(ini.get("PreProcess", module)); bool ok = do_process(commands); return ok; } bool TInstaller_mask::post_process(TInstall_ini& ini, const char* module) const { TAuto_token_string commands(ini.get("PostProcess", module)); return do_process(commands); } bool TInstaller_mask::can_install(const char* module, TInstall_ini& ini) { TInstall_ini curini; if (curini.demo() != ini.demo()) { TString msg; msg << "Attenzione: Non e' possibile installare la versione "; msg << (ini.demo() ? "dimostrativa" : "normale"); msg << " nella cartella della versione "; msg << (curini.demo() ? "dimostrativa" : "normale"); return error_box(msg); } const TString& version = ini.version(module); const word year = version2year(version); if (year < 1997) return error_box("Il modulo '%s' non ha una versione valida.", module); #ifndef _DEMO_ if (year > _year_assist) return error_box("Per installare la versione %s del modulo '%s'\n" "occorre il contratto di assistenza per l'anno %d.", (const char*)version, module, year); #endif TAuto_token_string altri(ini.get("Moduli", module)); if (stricmp(module, "ba") != 0 && altri.get_pos("ba") < 0) altri.add("ba"); // La base e' obbligatoria per tutti bool ok = TRUE; TString submodule; for (const char* mod = altri.get(0); mod && ok; mod = altri.get()) { submodule = mod; if (submodule.len() == 2) { if (curini.get("Versione", submodule).empty()) { TString msg; msg << "L'installazione del modulo '" << module << "'\nrichiede la presenza del modulo '" << submodule << "':\nSi desidera procedere alla sua installazione?"; ok = yesno_box(msg); if (ok) ok = install(submodule, 0); } } } return ok; } // sposta il file dal direttorio temporaneo a quello passato come destinazione // from: direttorio di partenza // file: nome del file con path completo da spostare (può includere sottodirettori) // todir: direttorio destinazione (si assume che esista già) bool TInstaller_mask::move_file(const TFilename& from, const TFilename& file, const char* todir) const { TFilename dest(todir); dest.add(file.mid(from.len()+1)); if (!fexist(dest.path())) { // file contains non existent subdir specification TToken_string dirs((const char * )(dest.path()),'\\'); TFilename subdir; for (int c=0; c < dirs.items()-1; c++) { subdir.add(dirs.get(c)); if (!fexist(subdir) ) // build destination directory make_dir(subdir); } } const long filesize = fsize(file); bool space_ok = FALSE; while (!space_ok) { int disk = 0; if (dest[1] == ':') { const char letter = toupper(dest[0]); disk = 'A' - letter + 1; } space_ok = ::os_test_disk_free_space(dest, filesize); if (!space_ok) { TString msg; msg << "Lo spazio sull'unita' e' insufficiente"; if (::os_is_removable_drive(dest)) { msg << ":\nInserire un nuovo disco e ritentare?"; if (!yesno_box(msg)) return FALSE; } else return error_box(msg); } } bool write_ok = TRUE; bool user_retry = FALSE; do { write_ok = ::fcopy(file, dest); if (write_ok) ::remove(file); else user_retry = yesno_box("Errore di copia del file %s.\nSi desidera ritentare?", (const char*)file); } while (!write_ok && user_retry); return write_ok; } bool TInstaller_mask::move_module(const TString& module, TInstall_ini& ini, bool update) const { bool ok = TRUE; TFilename src; src.tempdir(); const TFilename tempdir(src); TString_array list; ini.build_list(module, list); FOR_EACH_ARRAY_ROW(list, f, file) { src = tempdir; src.add(file->get(0)); if (update) { const bool move_ok = move_file(tempdir, src, "."); if (!move_ok) ok = update = FALSE; } if (!update) ::remove(src); } if (update) ini.export_paragraph(module, ini.default_name()); return ok; } bool TInstaller_mask::install(const TString& module, int patchlevel) { bool ok = FALSE; TString16 lastrelease; // release che sto installando int lastpatch=patchlevel; // patchlevel che sto installando const TString& path = get(F_PATH); TFilename ininame = path; ininame.add(module); if (patchlevel > 0) { TString16 name; name.format("%04da.ini", patchlevel); ininame << name; } else ininame << "inst.ini"; if (fexist(ininame)) { TInstall_ini ini(ininame); ini.write_protect(); lastpatch=ini.get_int("Patch",module); lastrelease=ini.get("Versione",module); if (!can_install(module, ini)) return FALSE; const int dischi = ini.get_int("Dischi", module); ok = dischi > 0; if (!ok) { return error_box("Impossibile determinare il numero dei dischetti in %s",ininame.name()); } else { ok = pre_process(ini, module); if (!ok) return FALSE; } TString msg("Decompressione"); if (patchlevel > 0 ) msg << " della patch " << patchlevel ; msg << " del modulo '" << module << "' in corso..."; TProgind pi(dischi, msg, FALSE, TRUE); TFilename tempdir; tempdir.tempdir(); TFilename cmdline; for (int d = 1; d <= dischi && ok; d++) { cmdline = path; cmdline.add(module); if (patchlevel > 0) { TString16 name; name.format("%04da", patchlevel); cmdline << name; } else cmdline << "inst"; cmdline << d << ".zip"; ok = cmdline.exist(); while (!ok) { message_box("Inserire il disco %d di %d del modulo\n'%s'", d, dischi, (const char*)ini.get("Descrizione")); ok = fexist(cmdline); if (!ok) { if (!yesno_box("Impossibile trovare %s\nSi desidera riprovare?", (const char*)cmdline)) break; } } if (ok) { const long required = fsize(cmdline) * (dischi-d+1) * 4; if (::os_test_disk_free_space("", required)) { ok = yesno_box("Lo spazio su disco potrebbe essere insufficiente:\n" "Si desidera continuare ugualmente?"); } } if (ok) { TWait_cursor hourglass; cmdline.insert("unzip.pif -o ", 0); cmdline << " -d " << tempdir; TExternal_app app(cmdline); ok = app.run(FALSE, FALSE, FALSE, FALSE) == 0; pi.addstatus(1); } } if (ok) { msg.cut(0); msg << "Aggiornamento del modulo '" << module << "' in corso..."; pi.set_text(msg); ok = move_module(module, ini, TRUE); TAuto_token_string altri(ini.get("Moduli", module)); FOR_EACH_TOKEN(altri, mod) { const TString16 submod = mod; if (submod.len() > 2) // sposta sottomoduli esterni { bool upd = ok; if (ok) { TInstall_ini curini; const TString16 curver = curini.version(submod); const int curpatch = curini.patch(submod); const TString16 reqver = ini.version(submod); const int reqpatch = ini.patch(submod); int distance = compare_version(reqver, reqpatch, curver, curpatch); upd = distance > 0; } ok &= move_module(submod, ini, upd); } } } if (ok) ok = post_process(ini, module); } else { // non c'e' il .ini del modulo ma un unico "install.ini" ininame = path; ininame.add(TInstall_ini::default_name()); ok = fexist(ininame); if (ok) { TInstall_ini ini(ininame); ini.write_protect(); lastpatch=ini.get_int("Patch",module); lastrelease=ini.get("Versione",module); if (!can_install(module, ini)) return FALSE; TString_array list; const int files = ini.build_complete_list(module, list); if (files > 0) { ok = pre_process(ini, module); if (ok) { TProgind pi(files, "Copia in corso...", FALSE, TRUE); TFilename src, dst; for (int f = 0; f < files && ok; f++) { pi.addstatus(1); dst = list.row(f).get(0); src = path; src.add(dst); ok = fcopy(src, dst); } } if (ok) { ini.export_module_paragraphs(module, ini.default_name()); ok = post_process(ini, module); } } } } if (ok) { { TInstall_ini ini; ini.set("DiskPath", path); ini.set("Data", TDate(TODAY), module); ini.update_prices(ininame); } // Non togliere le parentesi graffe soprastanti per permettere l'aggiornamento fisico del .ini (CON LA CHIAMATA DEL DISTRUTTORE) update_version(); } if (patchlevel == 0) // Se installo un modulo pricipale ... { // ... installo DOPO tutte le patches successive install_patches(module, lastrelease, lastpatch); } return ok; } bool TInstaller_mask::install_patches(const TString& module, const TString& lastrelease, int lastpatch) { bool ok = FALSE; TString_array modules; TFilename ininame = get(F_PATH); ininame.add(module); ininame << "0???a.ini"; modules.destroy(); list_files(ininame, modules); modules.sort(); // sort by patch number FOR_EACH_ARRAY_ROW(modules, am, arow) { TString& ininame = *arow; ininame.lower(); const int pos = ininame.find("a.ini"); CHECKS(pos >= 6, "Invalid installation configuration: ", (const char*)ininame); const TString16 patchmodule = ininame.mid(pos-6, 2); TConfig ini(ininame, patchmodule); const int patchlevel = ini.get_int("Patch"); const char * patchversion = ini.get("Versione"); if (lastrelease == patchversion // installa solo le patch della stessa ver.. && lastpatch < patchlevel) // ... e patch superiore ok = install(module, patchlevel); } return ok; } bool TInstaller_mask::path_handler(TMask_field& fld, KEY key) { bool ok = TRUE; if (key == K_TAB && fld.focusdirty()) { TFilename path = fld.get(); if (path.len() == 2 && isalpha(path[0]) && path[1] == ':') { path << SLASH; fld.set(path); } if (path.exist()) _curr_mask->autoload(); else ok = fld.error_box("Specificare un percorso valido"); } return ok; } void TInstaller_mask::install_selection() { TString_array& arr = rows_array(); FOR_EACH_ARRAY_ROW(arr, r, row) if (checked(r)) { const TString newver = row->get(C_RELEASE); if (newver.blank()) { check(r, FALSE); continue; } const TString modulo = row->get(C_CODE); const int newpatch = row->get_int(C_PATCH); const TString oldver = row->get(C_CURRRELEASE); const int oldpatch = row->get_int(C_CURRPATCH); if (version2year(newver) < 1997) { error_box("Il modulo '%s' non ha una versione valida.", (const char*)modulo); continue; } bool ok = TRUE; const int cmp = compare_version(oldver, oldpatch, newver, newpatch); if (cmp == 0) ok = yesno_box("Si desidera reinstallare la versione %s.%d del modulo '%s' ?", (const char*)newver, newpatch, (const char*)modulo); if (cmp > 0) { TString256 msg; msg.format("Si desidera ritornare alla versione %s.%d del modulo '%s' ?\n" "Attenzione: non e' garantito il corretto\n" "funzionamento di tutti i programmi!", (const char*)newver, newpatch, (const char*)modulo); ok = noyes_box(msg); } const bool is_patch = row->get_char(C_ISPATCH) > ' '; if (is_patch) { // installo le patch solo se esiste già un modulo installato della stessa versione if (!oldver.blank() ) if (oldver != newver) ok =error_box("Impossibile installare le patch della versione %s \nperché il modulo '%s' installato ha versione %s ",(const char *)newver,(const char *)modulo,(const char *)oldver); } if (ok) { if (is_patch) { ok = install_patches(modulo, oldver, oldpatch); // installa tutte le patch if (!ok) ok = install(modulo, newpatch); // re-installa l'ultima per sicurezza () } else ok = install(modulo, 0); // installa il modulo if (ok) _installed = TRUE; // Setta il flag di almeno un modulo installato if (ok) check(r, FALSE); } check(r,FALSE); // uncheck } } bool TInstaller_mask::install_handler(TMask_field& fld, KEY key) { if (key == K_SPACE) { if (_curr_mask->items() == 1) _curr_mask->check(0); if (_curr_mask->one_checked()) _curr_mask->install_selection(); else error_box("Selezionare uno piu' moduli da installare."); } return TRUE; } bool TInstaller_mask::tutti_handler(TMask_field& f, KEY k) { if (k == K_SPACE) { TSheet& s = (TSheet&)f.mask(); if (s.check_enabled()) { if (s.one_checked()) s.uncheck(-1); else { for (long i = s.items()-1; i >=0; i--) { TToken_string r = s.row(i); const TString16 newver = r.get(C_RELEASE); const int newpatch = r.get_int(C_PATCH); const TString16 curver = r.get(C_CURRRELEASE); const int curpatch = r.get_int(C_CURRPATCH); s.check(i, compare_version(newver, newpatch, curver, curpatch) > 0); } } } } return TRUE; } bool TInstaller_mask::on_key(KEY key) { bool ok = TRUE; if (key == K_CTRL+'N') autoload(); else if (key == K_F8) { for (long i = items()-1; i >= 0; i--) { TToken_string r = row(i); const TString16 newver = r.get(C_RELEASE); const int newpatch = r.get_int(C_PATCH); const TString16 curver = r.get(C_CURRRELEASE); const int curpatch = r.get_int(C_CURRPATCH); check(i, compare_version(newver, newpatch, curver, curpatch) > 0); } } else ok = TArray_sheet::on_key(key); return ok; } TInstaller_mask::TInstaller_mask() : TArray_sheet(0, 0, 0, 0, "Installazione", "@1|Modulo@30|Cod.|Versione da\ninstallare@11|Livello\nPatch@8|Data\nRilascio@10|Versione\nInstallata@10|Livello\nPatch@8|Data\nInstallazione@13|Aggiornamento", 0x18, 3) { _curr_mask = this; _installed = FALSE; add_string(F_PATH, 0, "Percorso da cui installare ", 1, 1, 50); add_string(F_CURPATH, 0, "Percorso in cui installare ", 1, 2, 50, "D"); add_button(F_INSTALL, "Installa", '\0'); // NON mettere 'I' set_handler(F_PATH, path_handler); set_handler(F_INSTALL, install_handler); set_handler(DLG_USER, tutti_handler); TDongle dongle; dongle.login(); dongle.logout(); _year_assist = dongle.year_assist(); TInstall_ini ini; TFilename path = ini.get("DiskPath"); set(F_PATH, path); const char lettera = toupper(path[0]); const bool floppy = ::os_is_removable_drive(path); if (path.not_empty() && !floppy) autoload(); DIRECTORY dir; xvt_fsys_get_dir(&dir); xvt_fsys_convert_dir_to_str(&dir, path.get_buffer(), path.size()); set(F_CURPATH, path); } TInstaller_mask::~TInstaller_mask() { _curr_mask = NULL; } /////////////////////////////////////////////////////////// // Programma principale /////////////////////////////////////////////////////////// class TInstaller : public TSkeleton_application { protected: virtual bool use_files() const { return FALSE; } virtual void main_loop(); }; void TInstaller::main_loop() { TInstaller_mask m; m.load(); m.run(); if (m.installed()) // Almeno 1 modulo installato ? { // Lancia conversione: ba1 -0 -C -uPRASSI TExternal_app conversion("ba1 -0 -C"); conversion.run(); } } int ba1700(int argc, char* argv[]) { TInstaller app; app.run(argc, argv, "Installer"); return 0; }