#include <sys/stat.h>

#include <agasys.h>
#include <applicat.h>
#include <defmask.h>
#include <dongle.h>
#include <execp.h>
#include <files.h>
#include <golem.h>
#include <lffiles.h>
#include <msksheet.h>
#include <prefix.h>
#include <progind.h>
#include <text.h>
#include <sheet.h>
#include <utility.h>
#include <text.h>

#include "ba1.h"
#include "ba1600.h"
#include "ba1600a.h"


int find(const TString& name, TString_array & rows) ;

///////////////////////////////////////////////////////////
// Configurazione per installazione
///////////////////////////////////////////////////////////
         
class TFascicolator_mask;         
         
class TCreazione_dischi : public TSkeleton_application
{ 
protected:
  TFascicolator_mask* _mask;

  virtual bool use_files() const { return FALSE; }
  virtual void main_loop();
public:
  virtual bool modify_mode() { return FALSE; }
  TFascicolator_mask& mask() const { return *_mask; }
};

class TFascicolator : public TCreazione_dischi
{ 
protected:
//  virtual bool use_files() const { return TRUE; }
  virtual void main_loop();
public:
  virtual bool modify_mode() { return TRUE;}
};

inline TCreazione_dischi& app() { return (TCreazione_dischi&)main_app(); }

class TFconv_ini : public TConfig
{
public:         
  void export_module(const char* module, const char* summary);
  
  TFconv_ini(const char* path = "fconv.ini") : TConfig(path) { }
  virtual ~TFconv_ini() { }
};


// costruisce la lista del modulo e dei suoi sottomoduli interni
// vengono creati solo i sottomoduli che contengono almeno 
// una variabile di tipo File
int TInstall_ini::build_list(const TString& module, TString_array& a, 
                             const char* sommario, bool agg)
{            
  CHECKS(module.len() >= 2 || module[0]=='_', "Bad module ", (const char*)module);
  
  TConfig* sum = NULL;
  if (sommario && *sommario)
    sum = new TConfig(sommario, module);
  
  TAssoc_array vars;  
  TAuto_token_string tmp;
  TString paragraph;
  for (int sub = 0; sub <= 9; sub++)
  {
    paragraph = module;
    if (module[2] == '\0')     // Ho specificato un modulo principale
      paragraph << sub;
      
    bool reset_par=TRUE;
    vars.destroy();

    TAssoc_array& varlist = list_variables(paragraph);
    FOR_EACH_ASSOC_STRING(varlist, obj, key, str)
    {            
      const bool is_file = strncmp(key, "File", 4) == 0;
      tmp = str;     // Nome e aggiornamento
      // Quando creo il disco di aggiornamento salto tutti i file
      // che non hanno il flag di aggiornamento settato
      if (agg && is_file && tmp.get_char(1) != 'X')
        continue;
      if (sum) 
      {
        if (!is_file && reset_par)
          vars.add(key,tmp);
        else
        {
          if (reset_par)
          {
            reset_par=FALSE;
            sum->set_paragraph(paragraph);
            sum->remove_all();
            FOR_EACH_ASSOC_STRING(vars, obj, key, str)
              sum->set(key, str);
          }
          sum->set(key, tmp);
        }
      }

      if (is_file)
      {
        tmp.add(paragraph, 2);              // Sottomodulo
        tmp.lower();  
        a.add(tmp);
      }  
    }  

    if (module[2] != '\0')
      break;
  }
  
  if (sum)
    delete sum;
  
  return a.items();
}

int TInstall_ini::build_complete_list(const TString& module, TString_array& a, 
                                      const char* sommario, bool agg)
{     
  if (sommario && *sommario)
  {
    TConfig sum(sommario, "Main");
    sum.set("Demo", demo() ? "X" : "");
  }
  // Lista dei file appartenenti ai sottomoduli del modulo principale (0-9)
  build_list(module, a, sommario, agg); 
  
  TString_array b;    //array locale temporaneo contenente la lista dei files da uccidere
  build_kill_list(module, b, sommario, agg);
  TAuto_token_string altri(get("Moduli", module));
  FOR_EACH_TOKEN(altri, mod)
  {
    const TString16 submodule = mod;
    // Lista dei files appartenenti ai sottomoduli esterni (moduli esclusi!)
    if (submodule.len() > 2)
      build_list(submodule, a, sommario, agg);
  }
  return a.items();
}

// costruisce la lista di programmi di gestione del modulo
int TInstall_ini::build_app_list(const TString& module, TString_array& a)
{
  TString paragraph;
  TToken_string row;
  for (int sub = 0; sub <= 9; sub++)
  {
    paragraph = module;
    if (module[2] == '\0')     // Ho specificato un modulo principale
      paragraph << sub;

    TAssoc_array& varlist = list_variables(paragraph);
    FOR_EACH_ASSOC_STRING(varlist, obj, key, str)
    {                                 
      int num;
      if (sscanf(key, "Edit_%d", &num) == 1)
      {
        row = "Edit";
        row.add(num);
        row.add(str);
        a.add(row);
      }
    }
    if (module[2] != '\0')
      break;
  }
  return a.items();
}

//crea x ogni modulo il sottomodulo 10 con i files da accoppare
int TInstall_ini::build_kill_list(const TString& module, TString_array& a, 
                             const char* sommario, bool agg)
{            
  CHECKS(module.len() >= 2, "Bad module ", (const char*)module);
  
  TString paragraph;
  paragraph << module << 99; 
  
  TConfig* sum = NULL;
  if (sommario && *sommario)
    sum = new TConfig(sommario, paragraph); //va nei sottomoduli 99
  
  TAuto_token_string tmp;     

  TAssoc_array& varlist = list_variables(paragraph);
  FOR_EACH_ASSOC_STRING(varlist, obj, key, str)
  {            
    const bool is_kill = strncmp(key, "Kill", 4) == 0;
    // Quando creo il disco di aggiornamento prendo solo i file che devono essere uccisi
    if (is_kill)
    {
      tmp = str;     // Nome e aggiornamento  

      if (agg && tmp.get_char(1) <= ' ')  //non e' selezionato
        continue;
      if (sum)
        sum->set(key, tmp);
      
      tmp.lower();
      a.add(tmp);
    }  
  }
  
  if (sum)
    delete sum;
  
  return a.items();
}

void TInstall_ini::export_paragraph(const char* module, const char* summary,const bool remove_old)
{                             
  CHECK(module && *module > ' ', "Can't export NULL module");
  CHECK(summary && *summary > ' ', "Can't export to NULL .ini");
  TInstall_ini sum(summary, module);     
  TString_array old_list;
  int last_file_num=0;
  const bool is_submodule=(module[2]!='\0');
  const int submodule=(module[2]-'0');
  char main_module[3]={0,0,0};
  strncpy(main_module, module,2);

  if (remove_old || !is_submodule )
  {
    // substitute...
    sum.remove_all();
  } 
  else 
  {
    // merge...
    sum.build_list(main_module, old_list);
    sum.set_paragraph(module);
    last_file_num=old_list.items();
  }

  TAssoc_array& ass = list_variables(module);
  TString newkey,tmps;
  TToken_string item_value;
  TString ;
  FOR_EACH_ASSOC_STRING(ass, obj, key, str)
  {
    if (!remove_old && is_submodule && strncmp(key, "File", 4)==0 )
    {
      // merging "File(X)" items...
      item_value=str;
      tmps=item_value.get(0);
      int item_number=find(tmps, old_list);
      if (item_number>=0)
      {
        // file sostituito
        TString old_smodule(old_list.row(item_number).get(2));
        TAssoc_array& oldvars = sum.list_variables(old_smodule);
        THash_object* obj;
        TToken_string oldvalue;
        oldvars.restart();
        do 
        {
          obj=oldvars.get_hashobj();
          oldvalue=((TString &)obj->obj());
          if (tmps == oldvalue.get(0))
            break;
        } 
        while (obj);
        newkey=obj->key();
        if (old_smodule!=module)
        {
          // devo cancellare il file dal vecchio sottomodulo
          sum.set_paragraph(old_smodule);
          sum.remove(newkey);
          sum.set_paragraph(module);
        } 
      } 
      else 
      {
        // nuovo file
        newkey = "File";
        newkey << '(' << last_file_num++ << ')';
      }
      sum.set(newkey, str);
    } 
    else 
    {
      sum.set(key, str);
    }
  }   
}   

void TInstall_ini::export_module_paragraphs(const char* module, const char* summary, const bool remove_old)
{           
  // esporta le info di composizione del modulo
  TString mod;
  for (int sub = -1; sub <= 9; sub++)
  {            
    mod = module;  
    if (sub >= 0) mod << sub;
    if (set_paragraph(mod))
      export_paragraph(mod, summary,remove_old);
  }
  // esporta la lista di eventuali files da eliminare (sono i killed)
  mod = module;  
  mod << 99;
  if (set_paragraph(mod))
    export_paragraph(mod, summary,remove_old);
  
  if (remove_old)
  {
    // esporta le info di conversione 
    TFilename path;
    TInstall_ini inst(summary);
    if (inst.name()==inst.default_name())
    {
      path = inst.name().path();
      path.add(module); path << "fconv.ini";
      TFconv_ini fconv(path); 
      fconv.export_module(module, "fconv.ini");
    }  
    else
    { 
      TFconv_ini fconv; // 
      path = inst.name().path();
      path.add(module); path << "fconv.ini";
      fconv.export_module(module, path);
    }
  }
}

const TString& TInstall_ini::version(const char* module)
{ 
  CHECK(module && *module > ' ', "Can't get version of NULL module");
  const TString& ver = get("Versione", module);
  if (ver.empty() && strlen(module) > 2)
  {      
    TString16 str;
    str.strncpy(module, 2);
    return get("Versione", str);
  }
  return ver;
}

int TInstall_ini::patch(const char* module)
{ 
  CHECK(module && *module > ' ', "Can't get version of NULL module");
  int patch = get_int("Patch", module);
  if (patch == 0 && strlen(module) > 2)
  {      
    TString16 str;
    str.strncpy(module, 2);
    patch = get_int("Patch", str);
  }
  return patch;
}

void TInstall_ini::version_info(const char* module,int& year, int& release,int& tag, int& patchlevel)
{
  TString ver = version(module);
  if (ver[0] == '9')
    ver.insert("19", 0);
  year = atoi(ver.left(4));   
  if (year == 0) 
    app().get_version_info(year, release, tag, patchlevel);
  else
  {  
    release = atoi(ver.mid(4,2));  
    if (release == 0) 
      release++;
    tag = atoi(ver.mid(6,2));  
    patchlevel = patch(module);
  }  
}

bool TInstall_ini::update_prices(const char* from)
{   
  CHECK(fexist(from), "Can't find listino prezzi");
  TConfig from_ini(from);
  from_ini.write_protect();
  const TDate curr_date(get("Listino","Main"));
  const TDate from_date(from_ini.get("Listino","Main"));
  if (from_date < curr_date)
    return FALSE;  
  set("Listino", from_date);
  
  TString_array modules; 
  from_ini.list_paragraphs(modules);
  FOR_EACH_ARRAY_ROW_BACK(modules, r, row) if (row->len() == 2)
  {
     TAssoc_array& prices = from_ini.list_variables(*row);
     set_paragraph(*row);
     FOR_EACH_ASSOC_STRING(prices, obj, key, str)
     {
        const TFixed_string price(key);
        if (price.compare("Prezzo", 6, TRUE) == 0)
          set(key, str);
     }
  } 
  return TRUE;
}

void TInstall_ini::prices(const char* module, word users, real& full, real& assist, bool correct_ass)
{
  real last_pac, last_ass;
  full = assist = 0.0;
  for (word u = 1; u <= users; u++)
  {                
    TAuto_token_string prezzi = get("Prezzo", module, int(u));
    if (prezzi.not_empty())
    {
      last_pac = prezzi.get(0); 
      if (last_pac < 50000.0) 
        last_pac *= 1000.0;
      last_ass = prezzi.get(); 
      if (last_ass < 50000.0) 
        last_ass *= 1000.0;
      const int mese = TDate(TODAY).month();
      last_ass = last_ass * (correct_ass ? (12-mese) / 12 : 1);
      last_ass.round(-3);
    }
    full += last_pac;
    assist += last_ass;
  }
}

///////////////////////////////////////////////////////////
// TFconv_ini
///////////////////////////////////////////////////////////

void TFconv_ini::export_module(const char* module, const char* summary)
{
  TScanner scanner(AUT_FILE);
  int module_code;
  for (module_code = 0; scanner.line().not_empty(); module_code++)
  {
    if (scanner.token().compare(module, 2, TRUE) == 0)
      break;
  }
  scanner.close();

  TConfig sommario(summary);
            
  TString_array paragraphs;
  list_paragraphs(paragraphs);
  paragraphs.sort();
  
  FOR_EACH_ARRAY_ROW(paragraphs, p, para)
  {
    TAssoc_array& variables = list_variables(*para);
    FOR_EACH_ASSOC_STRING(variables, obj, key, str)
    {
      const char* parenthesis = strchr(key, '(');
      if (parenthesis)
      {
        const int logic_num = atoi(parenthesis+1);
        if (logic_num > 0)
        {
          TDir dirinfo; dirinfo.get(logic_num);
          const long flags = dirinfo.flags() % 10000;
          if (flags == module_code)
            sommario.set(key, str, module);
        }    
      }
    }
  }
}

///////////////////////////////////////////////////////////
// Maschera composizione del modulo
///////////////////////////////////////////////////////////

class TMod_composition_msk : public TMask
{ 
protected:
  static bool sheet_notify(TSheet_field& sf, int row, KEY key);
  static bool missing_notify(TSheet_field& sf, int row, KEY key);
  bool kill_missing(const char* name, bool update);
  static bool link_handler(TMask_field& f, KEY k);  

  static bool kill_notify(TSheet_field& sf, int row, KEY key); //metodo per la gestione della lista dei files da eliminare effettivamente
  static bool obsolete_notify(TSheet_field& sf, int row, KEY key);   //metodo x riempire la lista dei files che si consiglia di eliminare
  bool kill_obsolete(const char* name, bool update);  //metodi x eliminare i files dalla lista dei consigliati quando si aggiungono
  static bool obs_handler(TMask_field& f, KEY k);     //alla lista definitiva di eliminazione
                                                        
  static bool file_handler(TMask_field& f, KEY k);
  static bool edit_handler(TMask_field& f, KEY k);
  static bool deselect_handler(TMask_field& f, KEY k);
  static bool isam_handler(TMask_field& f, KEY k);
  static bool kill_handler(TMask_field& f, KEY k);
   
public:
  void load(const TString& module);
  void save();

  TMod_composition_msk(const bool modify_mode=FALSE);
  virtual ~TMod_composition_msk() { }
};

// Toglie il file dallo sheet dei mancanti
bool TMod_composition_msk::kill_missing(const char* name, bool update)
{
  TSheet_field& miss = sfield(F_MISSING);
  FOR_EACH_SHEET_ROW_BACK(miss, r, row)
  {
    if (row->compare(name, -1, TRUE) == 0)
    {
      miss.destroy(r, update);
      break;
    }
  }
  return r >= 0;
}

// Toglie il file dallo sheet degli ELIMINABILI
bool TMod_composition_msk::kill_obsolete(const char* name, bool update)
{
  TSheet_field& obs = sfield(F_OBSOLETE);
  FOR_EACH_SHEET_ROW_BACK(obs, r, row)
  {
    if (row->compare(name, -1, TRUE) == 0)
    {
      obs.destroy(r, update);
      break;
    }
  }
  return r >= 0;
}

bool TMod_composition_msk::sheet_notify(TSheet_field& sf, int r, KEY key)
{           
  bool ok = TRUE;
  switch(key)
  {
  case K_TAB:
    // Posso cancellare solo le righe abilitate
    sf.sheet_mask().enable(DLG_DELREC, !sf.cell_disabled(r, 1));
    break;
  case K_ENTER:
    {
      TFilename mask = sf.row(r).get(0);
      if (mask.find('*') >= 0 || mask.find('?') >= 0)
      {
        TString_array arr; list_files(mask, arr);
        const int items = arr.items();
        
        if (items > 0)
        {
          TMod_composition_msk& msk = (TMod_composition_msk&)sf.mask();
          TString_array & rows=msk.sfield(F_SHEET).rows_array();

          TString16 module = sf.row(r).get(2);
          if (module.len() < 3)
            module << msk.get(F_MODULE) << '1';
        
          TFilename start;
          DIRECTORY dir; xvt_fsys_get_dir(&dir);
          xvt_fsys_convert_dir_to_str(&dir, start.get_buffer(), start.size());
          const int maxlen = start.len();
          
          bool found = FALSE;
          for (int i = 0; i < items; i++)
          {               
            TString& file = arr.row(i);
            if (file.compare(start, maxlen, TRUE) == 0)
              file.ltrim(maxlen+1);
            file.lower();
            
            msk.kill_missing(file, FALSE);
            
            if (::find(file,rows)>=0)
            {
              TToken_string& row = sf.row(found ? -1 : r);
              row = file;
              row.add(" ");
              row.add(module);
              found = TRUE;
            }  
          }
          // Se ne ho trovato almeno uno valido allora updato
          if (found)
          {
            sf.force_update();
            TSheet_field& miss = msk.sfield(F_MISSING);
            miss.force_update();
          }  
        }
        else
          ok = sf.error_box(FR("Nessun file corrisponde a %s"), mask.get_buffer());
      }
    }  
    break;
  case K_DEL:
    ok = !sf.cell_disabled(r, 1);
    if (ok)
    {
      // Sposto la riga cancellata nello sheet a fianco
      TSheet_field& miss = sf.mask().sfield(F_MISSING);
      miss.row(-1) = sf.row(r).get(0);
      miss.force_update();
    }
    break;
  case K_CTRL+K_INS:  
    { 
      // Propongo il sottomodulo automaticamente in inserimento
      TString16 module;
      module << sf.mask().get(F_MODULE) << '1';
      
      TToken_string& row = sf.row(r);
      row.add(module, 2);
    }
    break;
  default: 
    break;   
  }
  return ok;
}

//metodo per la gestione dello sheet con la lista dei files da ELIMINARE effettivamente
bool TMod_composition_msk::kill_notify(TSheet_field& sf, int r, KEY key)
{
  bool ok = TRUE;
  switch(key)
  {
  case K_TAB:
    // Posso cancellare solo le righe abilitate
    sf.sheet_mask().enable(DLG_DELREC, !sf.cell_disabled(r, 1));
    break;
  case K_ENTER:
    {
      TFilename mask = sf.row(r).get(0);
      if (mask.find('*') >= 0 || mask.find('?') >= 0)
      {
        TString_array arr; list_files(mask, arr);
        const int items = arr.items();
        
        if (items > 0)
        {
          TMod_composition_msk& msk = (TMod_composition_msk&)sf.mask();
          TString_array & rows=msk.sfield(F_KILL).rows_array();
        
          TFilename start;
          DIRECTORY dir; xvt_fsys_get_dir(&dir);
          xvt_fsys_convert_dir_to_str(&dir, start.get_buffer(), start.size());
          const int maxlen = start.len();
          
          bool found = FALSE;
          for (int i = 0; i < items; i++)
          {               
            TString& file = arr.row(i);
            if (file.compare(start, maxlen, TRUE) == 0)
              file.ltrim(maxlen+1);
            file.lower();
            
            msk.kill_obsolete(file, FALSE);
            
            if (::find(file,rows)>=0)
            {
              TToken_string& row = sf.row(found ? -1 : r);
              row = file;
              row.add(" ");
              found = TRUE;
            }  
          }
          // Se ne ho trovato almeno uno valido allora updato
          if (found)
          {
            sf.force_update();
            TSheet_field& miss = msk.sfield(F_OBSOLETE);
            miss.force_update();
          }  
        }
        else
          ok = sf.error_box("Nessun file corrisponde a %s", mask.get_buffer());
      }
    }  
    break;
  case K_DEL:
    ok = !sf.cell_disabled(r, 1);
    if (ok)
    {
      // Sposto la riga cancellata nello sheet a fianco
      TSheet_field& miss = sf.mask().sfield(F_OBSOLETE);
      miss.row(-1) = sf.row(r).get(0);
      miss.force_update();
    }
    break;
  default: 
    break;   
  }
  return ok;
}

bool TMod_composition_msk::missing_notify(TSheet_field& sf, int r, KEY key)
{           
  bool ok = TRUE;
  if (key == K_INS)
  {   
    // Sposto tutte le righe nello spreadsheet a fianco
    TMask& mainmask = sf.mask();
    TSheet_field& sheet = mainmask.sfield(F_SHEET);

    FOR_EACH_SHEET_ROW(sf, idx, riga)
    {
      TToken_string& newrow = sheet.row(-1);
      newrow = *riga;
      TString16 submod = newrow.left(2);
      submod << '1';
      newrow.add(submod, 2);
    } 

    sf.destroy();
    sf.force_update();
    sheet.force_update();
    ok = FALSE;
  }
  return ok;
}

bool TMod_composition_msk::file_handler(TMask_field& f, KEY k)
{
  if (k == K_F9)
  {                        
    TFilename start;
    DIRECTORY dir; xvt_fsys_get_dir(&dir);
    xvt_fsys_convert_dir_to_str(&dir, start.get_buffer(), start.size());

    FILE_SPEC fs;
    xvt_fsys_get_dir(&fs.dir);
    strcpy(fs.type, "");
    strcpy(fs.name, "*.*");
    strcpy(fs.creator, "SETUP");
    
    FL_STATUS ok = xvt_dm_post_file_open(&fs, TR("Selezionare il file ..."));
    xvt_fsys_set_dir(&dir);

    if (ok == FL_OK)
    {
      TFilename file;
      xvt_fsys_convert_dir_to_str(&fs.dir, file.get_buffer(), file.size());
      
      const int maxlen = start.len();
      if (file.compare(start, maxlen, TRUE) == 0)
      {
        file.ltrim(maxlen+1);
        file.add(fs.name); 
        file.ext(fs.type);
        f.set(file);    
        k = K_TAB;
      }
      else
      {
        return f.error_box(FR("Il file deve trovarsi nel percorso %s"), 
                           start.get_buffer());
      }  
    }
  }
    
  if (k = K_TAB && f.focusdirty())
  {
    TMod_composition_msk& msk = (TMod_composition_msk&)f.mask().get_sheet()->mask();
    msk.kill_missing(f.get(), TRUE);
  }
  
  return TRUE;
}

bool TMod_composition_msk::kill_handler(TMask_field& f, KEY k)
{
  if (k = K_TAB && f.focusdirty())
  {
    TMod_composition_msk& msk = (TMod_composition_msk&)f.mask().get_sheet()->mask();
    msk.kill_obsolete(f.get(), TRUE);
  }
  
  return TRUE;
}

bool TMod_composition_msk::edit_handler(TMask_field& f, KEY k)
{
  if (k == K_SPACE)
  {
    TFilename file = f.mask().get(101);
    if (stricmp(file.ext(), "exe") == 0)
    {                         
      file << " -0";
      TExternal_app app(file);
      app.run();
    }
    else
      ::edit_url(file);
  }  
  return TRUE;
}

bool TMod_composition_msk::link_handler(TMask_field& f, KEY k)
{
  if (k == K_SPACE)
  {
    TMask& modmask = f.mask();
    TSheet_field* sf = modmask.get_sheet();
    TMask& mainmask = sf->mask();
    TSheet_field& sheet = mainmask.sfield(F_SHEET);
    TToken_string& newrow = sheet.row(-1);
    newrow = modmask.get(101);          // Nome del file
    newrow.add(" ");                    // Non e' nell'aggiornamento
    newrow.add(mainmask.get(F_MODULE)); // Modulo attuale
    newrow << '1';                      // Sottomodulo standard
    if (modmask.is_running())
    {
      modmask.stop_run(K_ESC);
      do_events();
    }  
    sf->destroy(sf->selected());
    sheet.force_update();  
    sf->force_update();
  }
  return TRUE;
}  

static int obsolete_found(TConfig& cfg, void* jolly)
{
  TAssoc_array& list = cfg.list_variables(); 
  TString_array& files = *(TString_array*)jolly; //castato jolly a TString_array; sono i files su disco
  FOR_EACH_ASSOC_STRING(list, hash, key, string)
  {
    if (strncmp(key, "File(", 5) == 0)
    { 
      int pos = files.find(string);
      if (pos >=0)
        files.destroy(pos, TRUE); //se trova il file sia su disco che nell'ini -> lo toglie dall'elenco su disco
    }
  }
  return 0;
}

// metodo per riempire lo sheet dei file che si CONSIGLIA di eliminare
bool TMod_composition_msk::obsolete_notify(TSheet_field& sf, int r, KEY key)
{ 
  if (key == K_INS) //la compilazione dello sheet avviene solo quando si preme il pulsante +
  { 
    TWait_cursor hourglass;
    TString_array& elenco_dir = sf.rows_array();  //string_array che conterra' i files della directory
    elenco_dir.destroy();                         //resetta l'elenco dei files della directory
    list_files("*.*", elenco_dir);                //legge tutti i files della dir corrente e li mette in elenco_dir
    FOR_EACH_ARRAY_ROW(elenco_dir,i,row)          //mette in minuscolo tutti i nomi di files su disco
      row->lower();
                                                  //e' il file install.ini
    TInstall_ini ini;
    ini.for_each_paragraph(obsolete_found, &elenco_dir);  //per ogni paragrafo dell'install.ini chiama la obsolete_found
    sf.force_update();                            //aggiornamento dello sheet (di sinistra) sulla maschera
  }
  return TRUE;
}

//spostamento oggetti da sheet obsoleti a sheet con files da eliminare (sulla mask e' da destra a sinistra)
bool TMod_composition_msk::obs_handler(TMask_field& f, KEY k)
{
  if (k == K_SPACE)
  {
    TMask& modmask = f.mask();              //maschera di riga dello sheet
    TSheet_field* sf = modmask.get_sheet();    
    TMask& mainmask = sf->mask();
    TSheet_field& sheet = mainmask.sfield(F_KILL);  //sheet sinistro della maschera di Eliminazione
    TToken_string& newrow = sheet.row(-1);          //aggiunge una riga allo sheet...
    newrow = modmask.get(101);          //..e ci mette il nome del file

    if (modmask.is_running())
    {
      modmask.stop_run(K_ESC);
      do_events();
    }  
    sf->destroy(sf->selected());
    sheet.force_update();     //update dello sheet sinistro della maschera di eliminazione (files condannati)
    sf->force_update();
  }
  return TRUE;
}


bool TMod_composition_msk::deselect_handler(TMask_field& f, KEY k)
{                          
  if (k == K_SPACE)
  {           
    TSheet_field& sheet = f.mask().sfield(F_SHEET);
    FOR_EACH_SHEET_ROW_BACK(sheet, r, row)
      row->add(" ", 1);
    sheet.force_update();  
  }
  return TRUE;
}

bool TMod_composition_msk::isam_handler(TMask_field& f, KEY k)
{                          
  if (k == K_F9)
  {
    TMask& m = f.mask();
    TArray_sheet sht(-1,-1,-4,-4,TR("Selezione archivio"), HR("Codice@6R|Descrizione archivio@70"));
    const TPrefix& pref = prefix();
    const int total = pref.items();
    if (total > 0)
    {
      TWait_cursor hourglass;
      for (int i = LF_USER; i < total; i++)
      {
        TToken_string* row = new TToken_string;
        *row << i;
        row->add(pref.description(*row));
        sht.rows_array().add(row);
      }
      sht.select(m.get_int(f.dlg()) - LF_USER);
    }
    if (sht.run() == K_ENTER)
      m.set(f.dlg(), sht.selected() + LF_USER);
  }
  return TRUE;
}

static int file_compare(const TObject** o1, const TObject** o2)
{
  TToken_string* r1 = (TToken_string*)*o1;
  TToken_string* r2 = (TToken_string*)*o2;
  
  int cmp = stricmp(r1->get(-2), r2->get(-2));
  if (cmp == 0)
    cmp = stricmp(*r1, *r2);
  return cmp;  
}

void TMod_composition_msk::load(const TString& module)
{                 
  TWait_cursor hourglass;
  set(F_MODULE, module);

  TInstall_ini ini;                             //install.ini
  
  TSheet_field& s = sfield(F_SHEET);            //legge da install.ini la lista dei files del modulo
  ini.build_list(module, s.rows_array());
  s.rows_array().TArray::sort(file_compare);
  
  TSheet_field& p = sfield(F_PROGRAMS);         //la lista dei programmi tipo quelli x l'editing
  ini.build_app_list(module, p.rows_array());

  TFilename mask; 
  mask << module << "*.*";                    
  
  TSheet_field& miss = sfield(F_MISSING);
  TString_array& arr = miss.rows_array();
  list_files(mask, arr);
  
  TSheet_field& kill = sfield(F_KILL);         //legge da install.ini la lista dei files da uccidere
  ini.build_kill_list(module, kill.rows_array());

  const char* bad_ext[] = { "bsc", "mak", "obj", "pdb", "rc", 
                            "res", "sbr", "vcw", "wsp", NULL };
  
  FOR_EACH_ARRAY_ROW_BACK(arr, index, row)
  { 
    mask = *row;   
    mask.lower();
    const TString16 ext = mask.ext();

    bool ok = TRUE;
    for (int e = 0; bad_ext[e]; e++)
    {
      if (ext == bad_ext[e])
      {
        ok = FALSE;
        break;
      }  
    }
    
    if (ok)
    {
      FOR_EACH_SHEET_ROW_BACK(s, i, row)
      {
        if (mask.compare(row->get(0), -1, TRUE) == 0)
          break;
      }   
      ok = i < 0;
    }  
    
    if (ok)
      arr.row(index) = mask;
    else
      arr.destroy(index);
  }       
  arr.sort();
}

void TMod_composition_msk::save()
{                      
  TWait_cursor hourglass;
  TSheet_field& sheet = sfield(F_SHEET);

  TInstall_ini ini;
  int index;       
  
  const TString module = get(F_MODULE);
  const TString version = ini.version(module);
  const long patch = ini.patch(module);
  
  for (index = 0; index <= 9; index++)
  {
    TString16 sub; sub << module << index;
    if (ini.set_paragraph(sub))
      ini.remove_all();
  }

  TToken_string tmp;    
  index = 0;
  
  FOR_EACH_SHEET_ROW(sheet, r, row)
  {
    TString16 sub = row->get(2);
    if (sub.blank())
      sub << module << '1';
    else
    {  
      if (isdigit(sub[0]) && sub[1]=='\0')
      {
        sub.insert(module, 0);
        sub.cut(3);
      }  
    }   
    if (sub.left(2) == module)
    {
      tmp = row->get(0);       // Nome del file
      const bool agg = row->get_char() > ' ';
      if (agg) tmp.add("X");   // Flag aggiornamento
      ini.set("File", tmp, sub, TRUE, index++);
      ini.set("Versione", version); // Aggiorna versione del sottomodulo
      ini.set("Patch", patch);
    }
  }
  
  TSheet_field& sp = sfield(F_PROGRAMS);
  FOR_EACH_SHEET_ROW(sp, pr, prow)
  {                 
    TString16 var = prow->get(0);
    var <<  '_' << prow->get(1);
    
    TFilename n(prow->get(2));
    const int spc = n.find(' ');
    if (spc >= 0) n.cut(spc);
    n.ext("exe");
    TString16 sub = module;                
    FOR_EACH_SHEET_ROW(sheet, sr, srow)
    {
      if (n == srow->get(0))
      {
        sub = srow->get(2);
        break;
      }
    }
    n = prow->get(2);
    ini.set(var, n, sub);
  }
  
  TSheet_field& kp = sfield(F_KILL);
  TString16 sub = module;
  sub << 99;  
  FOR_EACH_SHEET_ROW(kp, kr, krow)
  {    
    ini.set("Kill", *krow, sub, TRUE, kr);
  }
  
}

TMod_composition_msk::TMod_composition_msk(const bool modify_mode)
                    : TMask("ba1600b")
{                
  TSheet_field& s = sfield(F_SHEET);
  TSheet_field& miss = sfield(F_MISSING);
  TSheet_field& prog = sfield(F_PROGRAMS);
  TSheet_field& kill = sfield(F_KILL);  
  TSheet_field& obs = sfield(F_OBSOLETE);
  set_handler(F_DESELECT, deselect_handler);
  if (modify_mode)
  {
    s.set_notify(sheet_notify);
    s.sheet_mask().set_handler(S_FILE, file_handler);
    s.sheet_mask().set_handler(DLG_EDIT, edit_handler);
    miss.disable();   // Read-only sheet
    miss.set_notify(missing_notify);
    miss.sheet_mask().set_handler(100, link_handler);
    prog.sheet_mask().set_handler(102, isam_handler);
    
    obs.set_notify(obsolete_notify);
    obs.sheet_mask().set_handler(100, obs_handler);
    
    kill.set_notify(kill_notify);
    kill.sheet_mask().set_handler(101, kill_handler);
  } 
  else 
  {
    s.disable();   // Read-only sheet

    miss.hide();   
    disable_page(1);
  }
}

///////////////////////////////////////////////////////////
// Maschera del modulo
///////////////////////////////////////////////////////////

class TModule_mask : public TMask
{ 
  bool _dirty_composition;
  bool module_dependent(int row, TString_array &p_submodules, TString &sub_mod, TString &ver, int & patch) const ;

public:
  bool list_is_dirty() const { return _dirty_composition;}
  void dirty_composition(bool val = TRUE) { _dirty_composition = val; }
  bool check_patchlevels(TMod_composition_msk &mm);
  virtual ~TModule_mask() {}
};

///////////////////////////////////////////////////////////
// Maschera principale
///////////////////////////////////////////////////////////

class TCreadischi_mask : public TMask
{ 
protected:    
  static bool list_handler(TMask_field& f, KEY k);
  static bool confirm_handler(TMask_field& f, KEY k);
  static bool creazip_handler(TMask_field& f, KEY k);
  static bool testpatch_handler(TMask_field& f, KEY k);
  static bool why_handler(TMask_field& f, KEY k);
  static bool modules_notify(TSheet_field& f, int row, KEY k);
  static bool import_export_handler(TMask_field& f, KEY k);
  static bool patchl_handler(TMask_field& f, KEY k);


  virtual const TFilename& build_export_path(TFilename& path) const;

  virtual bool zip_file(const char* archive, const char* file) const;
  virtual int  split_file(const TFilename& file, size_t size) const;
  virtual bool move_file(const TFilename& file, const char* dir) const;
  virtual bool zip_module(const TString& module, bool agg, int patch_level) const;

  virtual bool set_version_info(const TFilename& filename, 
                        TInstall_ini& ini, const char* module) const;
  
  virtual bool show_all_modules() {return FALSE;}
public:
  virtual void save();
  virtual void load();

  TCreadischi_mask();
  virtual ~TCreadischi_mask() { }
};

class TFascicolator_mask : public TCreadischi_mask
{ 
  long find_signature(const TFilename& filename, const char* signature) const;

protected:    
  static bool list_handler(TMask_field& f, KEY k);
  static bool confirm_handler(TMask_field& f, KEY k);
  static bool creazip_handler(TMask_field& f, KEY k);

  static bool patchl_handler(TMask_field& f, KEY k);
  
  // fuinzioni per la "firma"  del file con il numero di release
  virtual bool set_version_info(const TFilename& filename, 
                        TInstall_ini& ini, const char* module) const;
       
  virtual bool show_all_modules() {return TRUE;}
public:
  virtual void save();

  TFascicolator_mask();
  virtual ~TFascicolator_mask() { }
};
                                     

bool TCreadischi_mask::modules_notify(TSheet_field& f, int row, KEY k)
{          
  bool ok = TRUE;
  if (k == K_INS || k == K_DEL)
    ok = FALSE;
  if (k == K_TAB)
  {
    TModule_mask &mm =(TModule_mask &)f.sheet_mask();
    mm.dirty_composition(FALSE);
  }
  return ok;
}

// controlla se il modulo in curr_row dipende da uno dei sottomoduli nell'array p_submodules
// e ne restituisce :
//TString16 &sub_mod, : il codice del (primo) sottomodulo da cui dipende
//TString16 &ver,     : la propria versione 
//int & patch         : la propria patchlevel
bool TModule_mask::module_dependent(int rownum, TString_array &p_submodules, TString &sub_mod, TString &ver, int & patch) const 
{
  TSheet_field& modsheet = get_sheet()->mask().sfield(F_SHEET);
  TToken_string& curr_row=modsheet.row(rownum);
  TAuto_token_string ext_mod=curr_row.get(modsheet.cid2index(S_EXTERN));
  const int smods = ext_mod.blank() ? 0 : ext_mod.items() ;
  for (int c=0; c < smods; c++)
  {
    // is an external SUB module ?
    sub_mod = ext_mod.get(c);
    if (sub_mod[2]!='\0' )
    {
      // check patched submodules
      const int pmods = p_submodules.items();
      for (int p=0; p < pmods; p++)
      {
        if (p_submodules.row(p) == sub_mod)
        {
          ver=curr_row.get(modsheet.cid2index(S_VERSION));
          patch=atoi(curr_row.get(modsheet.cid2index(S_PATCHLEVEL)));
          return TRUE;
        }
      } // loop over patched modules
    }
  } // loop over external modules
  return FALSE;
}

bool TModule_mask::check_patchlevels( TMod_composition_msk &mm)
{
  const TString& module = get(S_MODULE);
  const TString& version =get(S_VERSION);
  const int patchlev = get_int(S_PATCHLEVEL);

  // crea la lista dei sottomoduli 
  TSheet_field& sf = mm.sfield(F_SHEET);
  TString_array patched_submodules,submodules;
  FOR_EACH_SHEET_ROW(sf, idx, riga)
  {
    TToken_string& curr_row=sf.row(idx);
    TString16 submod = curr_row.get(2);
    if (*curr_row.get(1)>' ')
    {
      if (patched_submodules.find(submod)<0)
        patched_submodules.add(submod);
    } else {
      if (submodules.find(submod)<0)
        submodules.add(submod);
    }
  } 

  // cerca i moduli che includono i sottomoduli patchati
  bool need_update(FALSE);
  TString16 sub_mod, sub_ver;
  int sub_patch;
  TSheet_field& modsheet = get_sheet()->mask().sfield(F_SHEET);
  FOR_EACH_SHEET_ROW(modsheet, midx, mriga)
  {
    TToken_string& curr_row=modsheet.row(midx);
    TString16 mod_code(curr_row.get(modsheet.cid2index(S_MODULE)));
    if (module_dependent(midx,patched_submodules, sub_mod, sub_ver, sub_patch))
    {
      if (version == sub_ver)
      {
        if ( patchlev > sub_patch 
          && yesno_box(FR("Il modulo '%s' dipende dal sottomodulo '%s'.\n Aggiorno il suo numero di patch a %d ?"),(const char *)mod_code,(const char *)sub_mod,patchlev))
        {
          curr_row.add(patchlev , modsheet.cid2index(S_PATCHLEVEL));
          curr_row.add(get(S_DATE) , modsheet.cid2index(S_DATE));
          need_update=TRUE;
        }
      } else 
        if (!sub_ver.blank())
          warning_box(FR("Il modulo '%s', dipendente dal sottomodulo '%s' \nha codice di release %s"),
            (const char *)mod_code, (const char *)sub_mod,(const char *)sub_ver);
    } 
    else if (module_dependent(midx,submodules, sub_mod, sub_ver, sub_patch))
    {
      if (version == sub_ver)
      {
        if ( patchlev > sub_patch 
          && noyes_box(FR("Il modulo '%s' dipende da sottomoduli del modulo '%s'.\n Aggiorno il suo numero di patch a %d ?"),(const char *)mod_code,(const char *)module,patchlev))
        {
          curr_row.add(patchlev , modsheet.cid2index(S_PATCHLEVEL));
          curr_row.add(get(S_DATE) , modsheet.cid2index(S_DATE));
          need_update=TRUE;
        }
      } else 
        if (!sub_ver.blank())
          warning_box(FR("Il modulo '%s', dipendente da sottomoduli del modulo '%s'\nha codice di release %s"),
            (const char *)mod_code, (const char *)sub_mod,(const char *)sub_ver);
    }
  }
  if (need_update)
    modsheet.force_update();
  return TRUE;
}

bool TFascicolator_mask::patchl_handler(TMask_field& f, KEY k)
{
  if (k == K_TAB && f.focusdirty())
  {                       
//    TModule_mask& m = (TModule_mask&)f.mask();
//    m.dirty_composition();
  }  
  return TRUE;
}  



bool TCreadischi_mask::list_handler(TMask_field& f, KEY k)
{
  if (k == K_SPACE)
  {                
    TModule_mask& m = (TModule_mask&)f.mask();
    const TString& module = m.get(S_MODULE);

    if (module.not_empty())
    {
      TMod_composition_msk mm; 
      mm.load(module);
      if (mm.run() == K_ENTER)
        mm.save();
    }
  }
  return TRUE;
}

bool TFascicolator_mask::list_handler(TMask_field& f, KEY k)
{
  if (k == K_SPACE)
  {                
    TModule_mask& m = (TModule_mask&)f.mask();
    const TString& module = m.get(S_MODULE);

    if (module.not_empty())
    {
      TMod_composition_msk mm(TRUE); 
      mm.load(module);
      if (mm.run() == K_ENTER)
      {
        // Salvo nel .ini
        mm.save();
        m.dirty_composition();
      }
    }
  }
  return TRUE;
}

bool TCreadischi_mask::confirm_handler(TMask_field& f, KEY k)
{
  return TRUE;
}

bool TFascicolator_mask::confirm_handler(TMask_field& f, KEY k)
{
  if (k == K_SPACE)
  {                       
    TModule_mask& m = (TModule_mask& )f.mask();
    const TString& module = m.get(S_MODULE);
    const patchlevel=m.get_int(S_PATCHLEVEL);
    const bool dirty_module= m.field(S_DATE).dirty() || 
      m.field(S_PREPROCESS).dirty() || 
      m.field(S_POSTPROCESS).dirty() || 
      m.field(S_EXTERN).dirty();
    const bool dirty_version= m.field(S_VERSION).dirty() || 
      m.field(S_PATCHLEVEL).dirty();
    if (dirty_version || dirty_module)
    {
      TIndwin infobar(60,TR("Salvataggio composizione modulo"),FALSE,FALSE);
      TInstall_ini ini;                
      ini.set_paragraph(module);
      ini.set("Versione", m.get(S_VERSION));
      ini.set("Patch", patchlevel);
      ini.set("Data", m.get(S_DATE));
      ini.set("Moduli", m.get(S_EXTERN));
      ini.set("PreProcess", m.get(S_PREPROCESS));
      ini.set("PostProcess", m.get(S_POSTPROCESS));
      // sottomoduli
      if (dirty_version)
      {
        TString16 submodule=module;
        submodule<<'0';
        for (int i=0; i <=9; i++)
        {
          submodule[2]='0'+i;
          if (ini.set_paragraph(submodule))
          {
            ini.set("Versione", m.get(S_VERSION));
            ini.set("Patch", m.get(S_PATCHLEVEL));
          }
        }
      }
    }
    if (patchlevel>0 && (m.list_is_dirty() || dirty_version))
    {
      // controlla le consistenze tra patch di moduli diversi intrinsecamente correlati
      TIndwin infobar(60,TR("Controllo dipendenze tra sottomoduli"),FALSE,FALSE);
      TMod_composition_msk mc; 
      mc.load(module);
      m.check_patchlevels(mc);
    }
  }  
  return TRUE;
}  

bool TFascicolator_mask::creazip_handler(TMask_field& f, KEY k)
{
  if (k == K_SPACE)
  {                       
    TMask& m = f.mask();
    TMask_field& fconfirm= m.field(DLG_OK);
    confirm_handler(fconfirm, K_SPACE);
    TCreadischi_mask::creazip_handler(f,k);
  }  
  return TRUE;
}  

bool TCreadischi_mask::creazip_handler(TMask_field& f, KEY k)
{
  if (k == K_SPACE)
  {                       
    TMask& m = f.mask();
    const TString& module = m.get(S_MODULE);
    const bool agg = f.dlg() == S_CREATEPATCH;
    TCreadischi_mask& fm = (TCreadischi_mask&)m.get_sheet()->mask();

    if (fm.zip_module(module, agg, m.get_int(S_PATCHLEVEL)))
    {
      // creazione XXfconv.ini (esporta le info di conversione )
      TFilename fconv_path;
      TFconv_ini fconv; // 
      fconv_path = fm.get(F_DISKPATH);
      fconv_path.add(module); fconv_path << "fconv.ini";
      fconv.export_module(module, fconv_path);
    }  
  }  
  return TRUE;
}  

bool TCreadischi_mask::why_handler(TMask_field& f, KEY k)
{
  if (k != K_SPACE)
    return TRUE;

  TArray_sheet& main_sheet = (TArray_sheet&)f.mask();
  const TFilename stopfile = main_sheet.row(main_sheet.selected()).get(1);
  TString16 module = stopfile.name(); module.cut(2);

  TFilename path = app().mask().get(F_DISKPATH);
  path.add(module); path << "????a.ini";
  
  TString_array inifiles;
  list_files(path, inifiles);
  inifiles.sort();
  TProgind pi(inifiles.items(), TR("Scansione archivi successivi..."), FALSE, TRUE);
  
  TString caption; caption << TR("File eliminabili da ") << stopfile.name();
  TArray_sheet sheet(3, 3, -3, -3, caption, HR("Modulo|File@20|Ultima Patch@50"));

  TAssoc_array files;
  TString_array para;
  TToken_string tok;
  FOR_EACH_ARRAY_ROW_BACK(inifiles, numf, filename)
  { 
    pi.addstatus(1);
    const bool is_last = stopfile == *filename;
    
    TConfig ini(*filename);
    ini.list_paragraphs(para);
    FOR_EACH_ARRAY_ROW_BACK(para, nump, paraname) if (paraname->len() == 3)
    {          
      ini.set_paragraph(*paraname);
      TAssoc_array& vars = ini.list_variables();  
      FOR_EACH_ASSOC_STRING(vars, obj, key, str)
      {             
        if (strncmp(key, "File(", 5) == 0)
        {
          tok = str;
          const int pipe = tok.find('|'); 
          if (pipe > 0) tok.cut(pipe);  
          tok.lower();
          
          if (is_last)
          {
            const TString* nextpatch = (const TString*)files.objptr(tok);
            TToken_string row;
            row = *paraname;
            row.add(tok);
            if (nextpatch)
              row.add(*nextpatch);
            else  
              row.add(TR("*** Nessuna ***"));  // Should never happen!
            sheet.add(row);
          }
          else
          {
            if (!files.is_key(tok))
              files.add(tok, *filename);
          }    
        }  
      }
    }
    if (is_last) break;
  }
  sheet.run();
  return FALSE;
}

bool TCreadischi_mask::testpatch_handler(TMask_field& f, KEY k)
{
  if (k != K_SPACE)
    return TRUE;
  
  TMask& m = f.mask();
  const TString& module = m.get(S_MODULE);
  TFilename path = app().mask().get(F_DISKPATH);
  path.add(module); path << "????a.ini";
  
  TArray_sheet sheet(3, 3, -3, -3, TR("File eliminabili"), HR("@1|Percorso assoluto@70"));
  sheet.add_button(DLG_USER+1, "Dettagli", 'D');
  sheet.set_handler(DLG_USER+1, why_handler);
  
  TString_array& inifiles = sheet.rows_array();
  list_files(path, inifiles);
  inifiles.sort();
  
  if (inifiles.items() > 0)
  {
    TProgind pi(inifiles.items(), TR("Scansione archivi..."), FALSE, TRUE);
    TAssoc_array files;
    TString_array para;
    TToken_string tok;
    FOR_EACH_ARRAY_ROW_BACK(inifiles, numf, filename)
    { 
      pi.addstatus(1);
      bool can_be_deleted = TRUE;
      TConfig ini(*filename, module);
      ini.list_paragraphs(para);
      FOR_EACH_ARRAY_ROW_BACK(para, nump, paraname) if (paraname->len() == 3)
      {          
        ini.set_paragraph(*paraname);
        TAssoc_array& vars = ini.list_variables();  
        FOR_EACH_ASSOC_STRING(vars, obj, key, str)
        {             
          if (strncmp(key, "File(", 5) == 0)
          {
            tok = str;
            const int pipe = tok.find('|'); 
            if (pipe > 0) tok.cut(pipe);  
            tok.lower();
            if (!files.is_key(tok))
            {
              files.add(tok);
              can_be_deleted = FALSE;
            }
          }  
        }
      }
      if (can_be_deleted)
        filename->insert(" |", 0);
      else
        inifiles.destroy(numf, TRUE);
    }
  }
  
  if (inifiles.items() == 0)
    return message_box(TR("Non e' stato rilevato nessun file eliminabile"));

  if (sheet.run() == K_ENTER) 
  {
    const long tot = sheet.checked();
    if (tot > 0 && yesno_box(FR("Confermare la cancellazione di %ld file"), tot)) 
    {                      
      TWait_cursor hourglass;
      for (long i = sheet.items()-1; i >= 0; i--) if (sheet.checked(i))
      {
        for (int d = 1; d <= 9; d++)
        {
          TFilename name = sheet.row(i).get(1);
          name.ext(""); name << d << ".zip";
          if (::remove(name) != 0)
          { 
            if (d == 1)
              error_box(FR("Errore di cancellazione del file %s"), (const char*)name);
            break;
          }
        }  
      }  
    }
  }
  
  return TRUE;  
}

bool TCreadischi_mask::import_export_handler(TMask_field& f, KEY k)
{
  if (k == K_SPACE)
  {                 
    const bool is_export = f.dlg() == S_EXPORT;
    const TMask& m = f.mask();
    const TString& module = m.get(S_MODULE);
                              
    const TCreadischi_mask& fm = (const TCreadischi_mask&)m.get_sheet()->mask();                          
    TFilename path = module;
    fm.build_export_path(path);

    FILE_SPEC fs;
    xvt_fsys_convert_str_to_dir(path.get_buffer(), &fs.dir);
    path.add(module); path << "inst.ini"; path.lower();
    strcpy(fs.type, "ini");
    strcpy(fs.name, path.name());
    strcpy(fs.creator, "INST");
    
    bool ok;
    
    DIRECTORY dir; 
    xvt_fsys_get_dir(&dir); // Salva directory corrente (Non usare la bacata xvt_fsys_save_dir)
    if (is_export)
      ok = xvt_dm_post_file_save(&fs, TR("Esporta il modulo in:")) == FL_OK;
    else
      ok = xvt_dm_post_file_open(&fs, TR("Importa il modulo da:")) == FL_OK;
    xvt_fsys_set_dir(&dir); // Ripristina directory corrente
    
    if (ok)
    {
      path = fs.dir.path;
      path.add(fs.name);
      if (is_export)
      {
        TInstall_ini inst;
        inst.export_module_paragraphs(module, path, TRUE);
      }  
      else
      { 
        TInstall_ini ini(path);
        ini.export_module_paragraphs(module, ini.default_name(), TRUE);
      }
    }
  }
  return TRUE;
}

const TFilename& TCreadischi_mask::build_export_path(TFilename& path) const
{    
  CHECK(path.not_empty(), "Please, specify the module");
  const TString module(path);
  
  path.cut(0);
  path << SLASH << "src" << SLASH << module;
  if (!path.exist()) 
  {
    path.cut(0);
    path << SLASH << 'u' << SLASH << user() << SLASH << "src" << SLASH << module;
    if (!path.exist()) 
    {
      path.cut(0);  
      path << SLASH << 'u' << SLASH << user() << SLASH << "p.due" << SLASH << module;
      if (!path.exist())     
        path.tempdir();
    }  
  }    
  path.lower();  
  return path;
}

void TCreadischi_mask::load()
{ 
  TWait_cursor hourglass;
  TSheet_field& s = sfield(F_SHEET);
  
  TString tmp;
  TString_array modules;

  TInstall_ini ini;
  ini.list_paragraphs(modules);
  
  set(F_DISKSIZE, ini.get("DiskSize"));
  set(F_DISKPATH, ini.get("DiskPath"));
  
  FOR_EACH_ARRAY_ROW(modules, m, riga)
  {
    const TString& module = *riga;
    ini.set_paragraph(module);
    tmp = ini.get("Versione");
    if (module[0] == '_' ||  // linea di descrizione area
      (module.len() == 2 &&  // linea di modulo principale
        (!tmp.blank()  || show_all_modules())))
    {
      TToken_string& row = s.row(-1);
      row = ini.get("Descrizione");
      
      if (module[0] == '_')
      {
        s.disable_cell(s.items()-1, -1);
      }
      else
      {
        row.add(module);
      
        tmp = ini.get("Versione");
        row.add(tmp); // versione
            
        tmp = ini.get("Patch");
        row.add(tmp);
  
        tmp = ini.get("Data");
        row.add(tmp);
    
        tmp = ini.get("Moduli");
        row.add(tmp);
          
        tmp = ini.get("PreProcess");
        row.add(tmp);
      
        tmp = ini.get("PostProcess");
        row.add(tmp);
      } 
    } 
  }
}

void TCreadischi_mask::save()
{
  TInstall_ini ini;
  ini.set("DiskSize", get(F_DISKSIZE));
  ini.set("DiskPath", get(F_DISKPATH));
}


void TFascicolator_mask::save()
{
  TSheet_field& s = sfield(F_SHEET);
  TCreadischi_mask::save();

  TProgind pi(s.items(), TR("Salvataggio in corso..."), FALSE, TRUE);
  TInstall_ini ini;
  
  TString tmp;
  FOR_EACH_SHEET_ROW_BACK(s, r, row)
  {          
    pi.addstatus(1);
  
    tmp = row->get(1);
    if (tmp.not_empty() && tmp != "xx")
    {
      ini.set_paragraph(tmp);
      
      tmp = row->get(0);
      ini.set("Descrizione", tmp);
      
      tmp = row->get(2);
      ini.set("Versione", tmp);

      tmp = row->get();
      ini.set("Patch", tmp);

      tmp = row->get();
      ini.set("Data", tmp);
      
      tmp = row->get();
      ini.set("Moduli", tmp);

      tmp = row->get();
      ini.set("PreProcess", tmp);

      tmp = row->get();
      ini.set("PostProcess", tmp);
    }  
  }  
}

bool TCreadischi_mask::zip_file(const char* archive, const char* listfile) const
{  
  TString msg;
  msg.format(TR("Creazione del file compresso %s..."), (const char*)archive);

  TIndwin waitw(100,msg,FALSE,FALSE); 
  TWait_cursor hourglass;

	return aga_zip_filelist(listfile, archive);
}

bool TCreadischi_mask::move_file(const TFilename& file, const char* dir) const
{
  TFilename dest(dir);
  dest.add(file.name());
  
  const long filesize = fsize(file);
  
  bool space_ok = xvt_fsys_is_network_drive(dest) != 0; // Pezza per far funzionare //192.168.4.3/...
  while (!space_ok)
  {
    space_ok = xvt_fsys_test_disk_free_space(dest, filesize) != 0;
    if (!space_ok)
    {
      TString msg(128);
      msg << TR("Lo spazio sull'unita' e' insufficiente");
      if (xvt_fsys_is_removable_drive(dest))
      {
        msg << TR(":\nInserire un nuovo disco e ritentare?");
        if (!yesno_box(msg))
          return FALSE;
      }
      else
        return error_box(msg);  
    }  
  }  

  bool write_ok = TRUE;
  bool user_abort = FALSE;
  do
  {
    write_ok = ::fcopy(file, dest);
    if (write_ok) 
      ::remove(file);  
    else
    {
      if (!yesno_box(FR("Errore di copia del file %s.\nSi desidera riprovare?"),
                     (const char*)file))
        user_abort = TRUE;
    }    
  } while (!write_ok && !user_abort);
    
  return write_ok;
}

// Dato il file Pippo.zip lo splitta in Pippo1.zip, Pippo2.zip, Pippo?.zip
int TCreadischi_mask::split_file(const TFilename& archive, size_t chunk_size) const
{
  int disks = 1;
	const size_t tot_size = ::fsize(archive);
  if (chunk_size > 0 && chunk_size < tot_size)
  {
    TFilename sommario(archive); 
    sommario.ext("ini");
    chunk_size -= ::fsize(sommario); 
    const long minsize = 720*1024L;
    if (chunk_size < minsize)
      chunk_size = minsize;

		FILE* inf = fopen(archive, "rb");
		if (inf == NULL)
			return 0;

	  TString msg; msg << TR("Separazione del file ") << archive << "...";
    TProgind pi(tot_size, msg, FALSE, TRUE); 
		
		byte* buff = new byte[chunk_size];
		for (int d = 1; ; d++)
		{
			const size_t r = fread(buff, 1, chunk_size, inf);
			pi.addstatus(r);
			if (r > 0)
			{
        TFilename chunk(archive);
	  		chunk.ext(""); chunk << d; chunk.ext("zip");
  			FILE* ouf = fopen(chunk, "wb");
			  fwrite(buff, r, 1, ouf);
				fclose(ouf);
				disks = d;
			}
			else
				break;
		}
		delete buff;
		fclose(inf);
  
    ::remove(archive);
  }
  else
  {
    TFilename archive1(archive);
    archive1.ext("");
    archive1 << '1';
    archive1.ext("zip");
    ::rename(archive,archive1);
  }
  
  return disks;  
}

long TFascicolator_mask::find_signature(const TFilename& filename, const char* signature) const
{       
  bool found = FALSE;
  long position = -1;
  int compare = 0;
  ifstream infile(filename, ios::nocreate | ios::in | ios::binary);
  for (int car = infile.get(); car != EOF; car = infile.get())
  {                              
    if (car == signature[compare])
    {            
      if (compare == 0)
        position = infile.tellg()-1;
      compare++;
      if (signature[compare] == '\0')
      {
        found = TRUE;
        break;
      }  
    }
    else
    {
      if (compare > 0)
        infile.seekg(position+1, ios::beg);
      compare = 0;
    }
  }  
  return found ? position : -1;
}

bool TCreadischi_mask::set_version_info(const TFilename& filename, 
                                          TInstall_ini& ini, const char* module) const
{           
  return TRUE;
}

bool TFascicolator_mask::set_version_info(const TFilename& filename, 
                                          TInstall_ini& ini, const char* module) const
{           
  bool ok = FALSE;
  TString80 str = "Don't cry for me "; str << "Argentina.";
  long position = find_signature(filename, str);
  if (position > 0)
  {
    fstream outfile(filename, ios::in | ios::out | ios::nocreate | ios::binary);
    position += str.len();
    outfile.seekp(position);
    if (outfile.good())
    {                    
      int year, release, tag, patch, checksum;
      ini.version_info(module, year, release, tag, patch);
      checksum = year + release + tag + patch;
      str.format("%04d.%02d.%02d.%03d.%04d", year, release, tag, patch, checksum);

      TString oldfirm("XXXX.XX.XX.XXX.XXXX");
      outfile.read(oldfirm.get_buffer(),19);
      if (oldfirm!=str)
      {
        outfile.seekp(position);
        outfile.write(str, str.len());  
      }
      ok = outfile.good() != 0;      
      if (!ok)
        error_box("Error writing signature in %s error n. %d", (const char *) filename, errno);
    }  
  }
  return ok;
}

bool TCreadischi_mask::zip_module(const TString& main_module, bool agg, int patch_level) const
{  
  TString_array arr;
  TInstall_ini ini;

  TFilename sommario; sommario.tempdir();
  sommario.add(main_module);
  if (agg)
  {
    if (patch_level <= 0)
      return error_box(TR("Il numero di patch deve essere superiore a zero"));
    TString16 name; 
    name.format("%04da.ini", patch_level);
    sommario << name;                  // Nome del file sommario aggiornamento
  }  
  else
    sommario << "inst.ini";            // Nome del file sommario completo
  
  ini.build_complete_list(main_module, arr, sommario, agg);
  if (arr.items() == 0)
  {
    ::remove(sommario);
    return error_box(TR("Nessun file da compattare"));
  }  
    
  const TFilename path = get(F_DISKPATH);
  // *****************
  // creazione ZIP
  TFilename archivio(sommario);
  archivio.ext("zip");            // Nome del file archivio completo
  
  bool aborted = FALSE;                                                              
  // ******************
  // compilazione lista e relativa firma dei files
  TString msg;
  if (path.blank())
    msg << TR("Controllo dei file per ") <<  archivio << " ...";
  else
    msg << TR("Preparazione dei file per ") <<  archivio << " ...";

  TFilename filelist;
  filelist.temp("", main_module);
  struct _stat info;
  long lasttime=0;
  
  // blocco della prima Progind
  { 
    ofstream fileh(filelist);
    TProgind pi(arr.items(), msg, TRUE, TRUE); 
    TFilename cmd;
    
    FOR_EACH_ARRAY_ROW_BACK(arr, i, row)
    {                    
      pi.addstatus(1);
      if (pi.iscancelled())
      {
        aborted = TRUE;
        break;
      }
      cmd = row->get(0);
      if (cmd.exist())
      {                   
        // Aggiungo il nome corrente alla lista dei files da compattare
        fileh << cmd << '\n';      
        if (stricmp(cmd.ext(), "exe") == 0)
        {
          TString16 submod = row->get(2);
          submod.cut(2);
          set_version_info(cmd, ini, submod);  
        }  
        _stat((const char *)cmd,&info);
        lasttime = max(lasttime,info.st_mtime);
      }
      else
      {
        // Se non trovo anche uno solo dei files nella lista, � un casino
        TString msg(128);
        msg << TR("Impossibile aprire il file ") << cmd << TR(". Interrompere?");
        if (yesno_box(msg))
        {
          aborted = TRUE;
          break;
        } 
      } 
    }
    fileh.close();
  }

  // Se non specifico un path ho gia' finito
  if (path.blank())
  {
    message_box(TR("Nessun percorso specificato. Il pacchetto non verra' creato"));
    return FALSE;
  }
  TFilename zipfile = path;
  zipfile.add(archivio.name());
  zipfile.ext("");
  zipfile << '1';
  zipfile.ext("zip");
  if (zipfile.exist())
  {
    _stat((const char *)zipfile,&info);
    if (lasttime   <= info.st_mtime)
    {
      aborted = !yesno_box(FR("Il file %s risulta gi� aggiornato.\nSi desidera rigenerarlo comunque?"),(const char *)zipfile);
    }
    if (!aborted && !agg)
    {
      // main zip updated; are there some patches?
      TFilename patchfile = zipfile.path();
      TString16 modpatch;
      modpatch.format("%s%04da.ini",(const char *)main_module,patch_level);
      patchfile.add(modpatch);
      if (patchfile.exist())
      {
        _stat((const char *)patchfile,&info);
        if (lasttime   <= info.st_mtime)
          aborted = !yesno_box(FR("Il file di patch %s \nrisulta gi� aggiornato.\nSi desidera procedere comunque alla generazione di %s ?"),(const char *)patchfile,(const char *)zipfile);
      }
    } 
  } 
  if (aborted)
  {
    ::remove(sommario);
    ::remove(filelist); // elimina il file lista-file
    return TRUE;
  }

  zip_file(archivio, filelist);    // Compatto gli eventuali ultimi rimasti
  ::remove(filelist); // elimina il file lista-file    
    
  const size_t size = get_long(F_DISKSIZE) * 1024;  
  const int disks = split_file(archivio, size);
    
  // Memorizza il numero dei dischetti nel sommario
  ini.set("Dischi", disks, main_module);     // Aggiorna install.ini
  ini.export_paragraph(main_module, sommario,TRUE); // Aggiorna sommario
    
  const char drive = toupper(path[0]);  
  const bool floppy = xvt_fsys_is_removable_drive(path) != 0;
    
	msg.format(TR("Creazione del file %s"), (const char*)archivio);
  TProgind pi(disks, msg, FALSE, TRUE);
  for (int d = 1; d <= disks && !aborted; d++)
  {            
    if (floppy)
      message_box(TR("Inserire il disco %d di %d nell'unita`:"), d, disks);
  
    // Costruisco il nome del file da copiare su dischetto  
    TFilename src(archivio);
    src.ext("");
    src << d;
    src.ext("zip");
  
    msg.format(TR("Generazione del disco %d/%d del modulo %s..."), d, disks, (const char*)main_module);
    pi.set_text(msg);
		pi.addstatus(1);
    do_events();
  
    bool ok = TRUE;
    if (d == 1)
      ok = move_file(sommario, path);
    if (ok)
      ok = move_file(src, path);
    aborted = !ok || pi.iscancelled();
  }
  // scrive il sommario completo 
  if (!agg && size==0)
  {
    archivio = path;
    archivio.add("install.ini");
    fcopy("install.ini",(const char *)archivio);
  }
    
  return TRUE;
}

TCreadischi_mask::TCreadischi_mask()
                  : TMask("ba1600a")
{ 
  TSheet_field& s = sfield(F_SHEET);
  s.set_notify(modules_notify);
  
  TMask& m = s.sheet_mask();

  m.set_handler(DLG_OK, confirm_handler);
  m.set_handler(S_LIST, list_handler);
  m.set_handler(S_CREATEZIP, creazip_handler);
  m.set_handler(S_CREATEPATCH, creazip_handler);
  m.set_handler(S_TESTPATCH, testpatch_handler);
  m.hide(S_IMPORT);
  m.set_handler(S_EXPORT, import_export_handler);
  s.disable();
}

TFascicolator_mask::TFascicolator_mask()
                  : TCreadischi_mask()
{ 
  TSheet_field& s = sfield(F_SHEET);
  s.set_notify(TCreadischi_mask::modules_notify);
  
  TMask& m = s.sheet_mask();

  m.set_handler(DLG_OK, confirm_handler);
  m.set_handler(S_LIST, list_handler);
  m.set_handler(S_CREATEZIP, creazip_handler);
  m.set_handler(S_CREATEPATCH, creazip_handler);
  m.set_handler(S_PATCHLEVEL, patchl_handler);
  m.show(S_IMPORT);
  m.set_handler(S_IMPORT, import_export_handler);
  s.enable(TRUE);
  s.enable_column(S_MODULE,TRUE);
  s.enable_column(S_VERSION,TRUE);
  s.enable_column(S_PATCHLEVEL,TRUE);
  s.enable_column(S_EXTERN,TRUE);
  s.enable_column(S_PREPROCESS,TRUE);
  s.enable_column(S_POSTPROCESS,TRUE);
}

///////////////////////////////////////////////////////////
// Programma principale
///////////////////////////////////////////////////////////
void TFascicolator::main_loop()
{
  _mask = new TFascicolator_mask;
  _mask->load();
  int key;
  do 
  {
    key = _mask->run();
    if (key == K_ENTER)
      _mask->save();
  }
  while (key != K_ENTER && key != K_QUIT);
  delete _mask; _mask = NULL;
}



void TCreazione_dischi::main_loop()
{
  TCreadischi_mask m;
  m.load();
  int key;
  do 
  {
    key=m.run();
    if (key == K_ENTER)
      m.save();
  }
  while (key != K_ENTER && key != K_QUIT);
}

// Cerca un file nell'array di colonne e ne restituisce il numero del sottomodulo
// -1 se 
int find(const TString& name, TString_array & rows) 
{
  for(int r=rows.items()-1; r >=0 ; r-- )
  {
    if (name.compare(rows.row(r).get(0), -1, TRUE) == 0)
      break;
  }
  return r;
}

int ba1600(int argc, char* argv[])
{ 
  if (user() == ::dongle().administrator())
  {
    if (argc>2 && strcmp(argv[1],"GODMODE"))
    {
      TFascicolator app;
      app.run(argc, argv, "Megascicolator");
    } 
    else 
    {
      TCreazione_dischi app;
      app.run(argc, argv, TR("Creazione dischetti"));
    }
  } 
	else 
    error_box(FR("L'utente %s non e' abilitato all'esecuzione di questo programma"), (const char*)user());
  return 0;
}