#include <dos.h>

#define XVT_INCL_NATIVE
#include <applicat.h>
#include <execp.h>
#include <msksheet.h>
#include <prefix.h>
#include <progind.h>
#include <urldefid.h>
#include <utility.h>

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

///////////////////////////////////////////////////////////
// Configurazione per installazione
///////////////////////////////////////////////////////////

int TInstall_ini::build_list(const TString& module, TString_array& a, 
                             const char* sommario, bool agg)
{            
  CHECKS(module.len() >= 2, "Bad module ", (const char*)module);

  TConfig* sum = NULL;
  if (sommario)
    sum = new TConfig(sommario, module);
  
  TAuto_token_string tmp;
  TString paragraph;
  for (int sub = 0; ; sub++)
  {
    paragraph = module;
    if (module[2] == '\0')     // Ho specificato un modulo principale
      paragraph << sub;
    
    if (set_paragraph(paragraph))
    {
      for (int index = 0; exist("File", index); index++)
      { 
        tmp = get("File", NULL, index);     // Nome e  aggiornamento
        
        // Quando creo il disco di aggiornamento salto tutti i file
        // che non hanno il flag di aggiornamento settato
        const bool inagg = tmp.get_char(2) > ' ';
        if (agg && !inagg)
          continue;
  
        if (sum) 
          sum->set("File", tmp, paragraph, TRUE, index);
        tmp.add(paragraph, 2);              // Sottomodulo
        tmp.lower();  
        a.add(tmp);
      }
    }  
    else
      break;

   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)
{
  build_list(module, a, sommario, agg);
  TAuto_token_string altri(get("Moduli", module));
  FOR_EACH_TOKEN(altri, mod)
  {
    const TString submodule = mod;
    export_paragraph(submodule, sommario);
  }
  return a.items();
}

void TInstall_ini::export_paragraph(const char* module, const char* summary)
{                             
  TConfig sum(summary, module);
  TAssoc_array& ass = (TAssoc_array&)list_variables(module);
  FOR_EACH_ASSOC_STRING(ass, obj, key, str)
    sum.set(key, str);
}   

void TInstall_ini::export_module_paragraphs(const char* module, const char* summary)
{
  TString mod;
  for (int sub = -1; ; sub++)
  {            
    mod = module;  
    if (sub >= 0) mod << sub;
    if (set_paragraph(mod))
      export_paragraph(mod, summary);
    else
      break;
  }
}

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

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() { }
};

void TFconv_ini::export_module(const char* module, const char* summary)
{
  TScanner scanner("prassi.aut");
  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)
  {
    set_paragraph(*para);
    TAssoc_array& variables = (TAssoc_array&)list_variables();
    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);
        }    
      }
    }
  }
}

///////////////////////////////////////////////////////////
// TAuto_token_string
///////////////////////////////////////////////////////////

TAuto_token_string& TAuto_token_string::create(const char* ts)
{           
  // Copia la stringa
  set(ts);  

  // Calcola il separatore
  for (const char* s = get_buffer(); *s; s++)
  {
    if (strchr("|;,!^&+\t\n", *s) != NULL)
    {                  
      separator(*s);
      break;
    }
  }  
  
  return *this;
}

///////////////////////////////////////////////////////////
// Maschera modulo
///////////////////////////////////////////////////////////

class TModule_mask : 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);
  static bool file_handler(TMask_field& f, KEY k);
  static bool link_handler(TMask_field& f, KEY k);
  static bool deselect_handler(TMask_field& f, KEY k);
  
  bool find(const TString& name) const;
  bool kill_missing(const char* name, bool update);
   
public:
  void load(const char* module);
  void save();

  TModule_mask();
  virtual ~TModule_mask() { }
};

// Cerca un file nello sheet principale
bool TModule_mask::find(const TString& name) const
{
  TSheet_field& sf = sfield(F_SHEET);
  FOR_EACH_SHEET_ROW_BACK(sf, r, row)
  {
    if (name.compare(row->get(0), -1, TRUE) == 0)
      break;
  }
  return r >= 0;
}

// Toglie il file dallo sheet dei mancanti
bool TModule_mask::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;
}

bool TModule_mask::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();
        
        TString16 module;
        module << sf.mask().get(F_MODULE) << '0';
        
        if (items > 0)
        {
          TModule_mask& msk = (TModule_mask&)sf.mask();
        
          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 (!msk.find(file))
            {
              TToken_string& row = sf.row(found ? -1 : r);
              row = file;
              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("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) << '0';
      
      TToken_string& row = sf.row(r);
      row.add(module, 1);
    }
    break;
  default: 
    break;   
  }
  return ok;
}

bool TModule_mask::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 << '0';
      newrow.add(submod, 2);
    } 

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

bool TModule_mask::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, "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("Il file deve trovarsi nel perrcorso %s", 
                           start.get_buffer());
      }  
    }
  }
    
  if (k = K_TAB && f.focusdirty())
  {
    TModule_mask& msk = (TModule_mask&)f.mask().get_sheet()->mask();
    msk.kill_missing(f.get(), TRUE);
  }
  
  return TRUE;
}

bool TModule_mask::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 fa' parte dell'aggiornamento
    TString16 submod = newrow.left(2);
    submod << '0';                      // Sottomodulo standard
    newrow.add(submod);
    if (modmask.is_running())
    {
      modmask.stop_run(K_ESC);
      do_events();
    }  
    sf->destroy(sf->selected());
    sheet.force_update();  
    sf->force_update();
  }
  return TRUE;
}  

bool TModule_mask::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;
}

void TModule_mask::load(const char* module)
{                 
  TWait_cursor hourglass;
  set(F_MODULE, module);

  TInstall_ini ini;
  TSheet_field& s = sfield(F_SHEET);
  ini.build_list(module, s.rows_array());
  s.rows_array().sort();

  TFilename mask; 
  mask << module << "*.*";                    
  
  TSheet_field& miss = sfield(F_MISSING);
  TString_array& arr = miss.rows_array();
  list_files(mask, arr);
  
  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 TModule_mask::save()
{                      
  TWait_cursor hourglass;
  TSheet_field& sheet = sfield(F_SHEET);

  TInstall_ini ini;
  int index;       
  
  const TString module = get(F_MODULE);
  for (index = 0; ; index++)
  {
    TString16 sub; sub << module << index;
    if (ini.set_paragraph(sub))
    {
      TAssoc_array& list = (TAssoc_array&)ini.list_variables();
      list.destroy();
    }
    else
      break;
  }

  TToken_string tmp;    
  index = 0;
  
  FOR_EACH_SHEET_ROW_BACK(sheet, r, row)
  {
    TString16 sub = row->get(2);
    if (isdigit(sub[0]) || sub.left(2) == module)
    {
      tmp = row->get(0);       // Nome del file
      const bool agg = row->get_char(1) > ' ';
      if (agg) tmp.add("X");   // Flag aggiornamento
      ini.set("File", tmp, sub, TRUE, index++);
    }
  }
}


TModule_mask::TModule_mask()
            : TMask("ba1600b")
{                
  set_handler(F_DESELECT, deselect_handler);

  TSheet_field& s = sfield(F_SHEET);
  s.set_notify(sheet_notify);
  s.sheet_mask().set_handler(S_FILE, file_handler);
  
  TSheet_field& miss = sfield(F_MISSING);
  miss.set_notify(missing_notify);
  miss.disable();   // Read-only sheet
  miss.sheet_mask().set_handler(100, link_handler);
}

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

class TFascicolator_mask : public TMask
{ 
protected:    
  static bool sheet_notify(TSheet_field& f, int row, KEY k);
  static bool list_handler(TMask_field& f, KEY k);
  static bool save_handler(TMask_field& f, KEY k);
  static bool import_export_handler(TMask_field& f, KEY k);
  
  bool zip_file(const char* archive, const char* file) const;
  int  split_file(const TFilename& file, long size) const;
  bool move_file(const TFilename& file, const char* dir) const;
  bool zip_module(const TString& module, bool agg) const;
  
  const TFilename& build_export_path(TFilename& path) const;
       
public:
  void load();
  void save();

  TFascicolator_mask();
  virtual ~TFascicolator_mask() { }
};
                                     
bool TFascicolator_mask::sheet_notify(TSheet_field& f, int row, KEY k)
{          
  bool ok = TRUE;
  if (k == K_INS || k == K_DEL)
    ok = FALSE;
  return ok;
}
                                     
bool TFascicolator_mask::list_handler(TMask_field& f, KEY k)
{
  if (k == K_SPACE)
  {                
    const TMask& m = f.mask();
    const TString& module = m.get(S_MODULE);
    TModule_mask mm;
    mm.load(module);
    if (mm.run() == K_ENTER)
      mm.save();
  }     
  return TRUE;
}

bool TFascicolator_mask::save_handler(TMask_field& f, KEY k)
{
  if (k == K_SPACE)
  {                       
    TMask& m = f.mask();
    const TString& module = m.get(S_MODULE);
    {
      TInstall_ini ini;                
      ini.set_paragraph(module);
      ini.set("Versione", m.get(S_VERSION));
      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));
    }
    const bool agg = f.dlg() == S_SAVEAGG;
    TFascicolator_mask& fm = (TFascicolator_mask&)m.get_sheet()->mask();
    fm.zip_module(module, agg);
  }  
  return TRUE;
}  

bool TFascicolator_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 TFascicolator_mask& fm = (const TFascicolator_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, "Esporta il modulo in:") == FL_OK;
    else
      ok = xvt_dm_post_file_open(&fs, "Importa il modulo da:") == FL_OK;
    xvt_fsys_set_dir(&dir); // Ripristina directory corrente
    
    if (ok)
    {
      if (is_export)
      {
        TInstall_ini inst;
        inst.export_module_paragraphs(module, path);
        
        TFconv_ini fconv; 
        path = path.path();
        path.add(module); path << "fconv.ini";
        fconv.export_module(module, path);
      }  
      else
      { 
        TInstall_ini ini(path);
        ini.export_module_paragraphs(module, ini.default_name());

        path = path.path();
        path.add(module); path << "fconv.ini";
        TFconv_ini fconv(path); 
        fconv.export_module(module, "fconv.ini");
      }
    }
  }
  return TRUE;
}

const TFilename& TFascicolator_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 (!fexist(path)) 
  {
    path.cut(0);
    path << SLASH << 'u' << SLASH << user() << SLASH << "src" << SLASH << module;
    if (!fexist(path)) 
    {
      path.cut(0);  
      path << SLASH << 'u' << SLASH << user() << SLASH << "p.due" << SLASH << module;
      if (!fexist(path))     
        path.tempdir();
    }  
  }    
  path.lower();  
  return path;
}

void TFascicolator_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_BACK(modules, m, riga)
  {
    const TString& module = *riga;
    if (module.len() == 2)
    {
      TToken_string& row = s.row(-1);
      ini.set_paragraph(module);
      row = ini.get("Descrizione");
      row.add(module);
  
      tmp = ini.get("Versione");
      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);
    } 
  }
  s.rows_array().sort();
}

void TFascicolator_mask::save()
{
  TSheet_field& s = sfield(F_SHEET);
  TProgind pi(s.items(), "Salvataggio in corso...", FALSE, TRUE);

  TInstall_ini ini;
  
  ini.set("DiskSize", get(F_DISKSIZE));
  ini.set("DiskPath", get(F_DISKPATH));
  
  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("Data", tmp);
      
      tmp = row->get();
      ini.set("Moduli", tmp);

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

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

bool TFascicolator_mask::zip_file(const char* archive, const char* file) const
{  
  TWait_cursor hourglass;
  TFilename cmd;
  cmd << "zip.pif " << archive << ' ' << file;
  TExternal_app app(cmd);
  int err = app.run(FALSE, FALSE, FALSE, FALSE);
  return err == 0;
}

bool TFascicolator_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 = FALSE;
  while (!space_ok)
  {
    int disk = 0;
    if (dest[1] == ':')
    {
      const char letter = toupper(dest[0]);
      disk = 'A' - letter + 1;
    }  
    struct _diskfree_t drive;
    _dos_getdiskfree(disk, &drive);
    
    const unsigned requested_clusters = unsigned(filesize / drive.sectors_per_cluster / drive.bytes_per_sector) + 1;
    space_ok = requested_clusters <= drive.avail_clusters;
    if (!space_ok)
    {
      TString msg(128);
      msg << "Lo spazio sull'unita' e' insufficiente";
      if (GetDriveType(disk-1) == DRIVE_REMOVABLE)
      {
        msg << ":\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("Errore di scrittura del file %s.\nSi desidera riprovare?",
                     (const char*)file));
        user_abort = TRUE;
    }    
  } while (!write_ok && !user_abort);
    
  return write_ok;
}

int TFascicolator_mask::split_file(const TFilename& archive, long size) const
{
  TWait_cursor hourglass;

  int disks = 1;
  if (size > 0L)
  {
    TFilename sommario(archive); sommario.ext("ini");
    size -= ::fsize(sommario); 
    const long minsize = 360*1024L;
    if (size < minsize)
      size = minsize;
  
    DIRECTORY curdir, tmpdir;
    xvt_fsys_get_dir(&curdir);  // Legge directory corrente
    
    // Costruisce percorso assoluto dello splitter
    TFilename cmd;     
    xvt_fsys_convert_dir_to_str(&curdir, cmd.get_buffer(), cmd.size());
    cmd.add("zipsplit.pif");
    cmd << " -n " << size << " " << archive;
    
    // Salta alla directory temporanea
    xvt_fsys_convert_str_to_dir((char*)archive.path(), &tmpdir);
    xvt_fsys_set_dir(&tmpdir);
    // Esegue lo splitter nella directory temporanea
    TExternal_app app(cmd);
    int err = app.run(FALSE, FALSE, FALSE, FALSE);
    // Torna nella directory principale
    xvt_fsys_set_dir(&curdir);

    if (err != NOERR)
      return error_box("Errore %d durante lo splitting del file %s",
                       err, (const char*)archive);
    
    // Conta il numero di dischetti generati
    TFilename src;
    for (int d = 2; ; d++)
    {            
      src = archive;
      src.ext("");
      src << d;
      src.ext("zip");
      if (::fexist(src))
        disks = d;
      else
        break;
    }  
    ::remove(archive);
  }
  
  return disks;  
}

bool TFascicolator_mask::zip_module(const TString& main_module, bool agg) const
{  
  TString_array arr;
  TInstall_ini ini;
  
  TFilename sommario; sommario.tempdir();
  sommario.add(main_module);
  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("Nessun file da compattare");
  }  
    
  TFilename archivio(sommario);
  archivio.ext("zip");            // Nome del file archivio completo
  
  bool aborted = FALSE;                                                              
  TString msg;
  msg << "Creazione del file " << archivio << " ...";

  TProgind pi(arr.items(), msg, TRUE, TRUE); 
  TString cmd(120);
  
  FOR_EACH_ARRAY_ROW_BACK(arr, i, row)
  {                    
    pi.addstatus(1);
    if (pi.iscancelled())
    {
      aborted = TRUE;
      break;
    }
    
    // Aggiungo il nome corrente alla lista dei files da compattare
    cmd << ' ' << row->get(0);
    
    if (cmd.len() > 80)         // Se la riga ha raggiunto la dimensione giusta...
    {                              
      zip_file(archivio, cmd);  // ... allora compatto
      cmd.cut(0);               // e svuoto la riga di comando
    }
  }
  if (cmd.len() > 0 && !aborted)
    zip_file(archivio, cmd);    // Compatto gli eventuali ultimi rimasti
 
  msg.cut(0);
  msg << "Separazione del file " << archivio << " ...";
  pi.set_text(msg);

  const long 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); // Aggiorna sommario
  
  // Se non specifico un path ho gia' finito
  const TFilename path = get(F_DISKPATH);
  if (path.blank())
    return TRUE;
  
  const char drive = toupper(path[0]);  
  const bool floppy = (GetDriveType(drive - 'A') == DRIVE_REMOVABLE) && (path[1] == ':');
  
  for (int d = 1; d <= disks && !aborted; d++)
  {            
    if (floppy)
      message_box("Inserire il disco %d di %d nell'unita' %c:", d, disks, drive);

    // Costruisco il nome del file da copiare su dischetto  
    TFilename src(archivio);
    src.ext("");
    src << d;
    src.ext("zip");

    msg.cut(0);
    msg << "Generazione del disco " << d << " di " << disks
        << " del modulo " << main_module << " ...";
    pi.set_text(msg);
    do_events();

    bool ok = TRUE;
    if (d == 1)
      ok = move_file(sommario, path);
    if (ok)
      ok = move_file(src, path);
    aborted = !ok || pi.iscancelled();
  }

  return TRUE;
}

TFascicolator_mask::TFascicolator_mask()
                  : TMask("ba1600a")
{ 
  TSheet_field& s = sfield(F_SHEET);
  s.set_notify(sheet_notify);
  
  TMask& m = s.sheet_mask();
  m.set_handler(S_LIST, list_handler);
  m.set_handler(S_SAVE, save_handler);
  m.set_handler(S_SAVEAGG, save_handler);
  m.set_handler(S_IMPORT, import_export_handler);
  m.set_handler(S_EXPORT, import_export_handler);
}

///////////////////////////////////////////////////////////
// Programma principale
///////////////////////////////////////////////////////////

class TFascicolator : public TSkeleton_application
{ 
protected:
  virtual bool use_files() const { return FALSE; }
  virtual void main_loop();
};

void TFascicolator::main_loop()
{
  TFascicolator_mask m;
  m.load();
  if (m.run() == K_ENTER)
    m.save();
}

int ba1600(int argc, char* argv[])
{ 
  TFascicolator app;
  app.run(argc, argv, "Megascicolator");
  return 0;
}