/************************************************************
**
** Application: SyncDiscs
**
** Title:       c.dbox
**
*************************************************************/

/*
*
* Copyright (c) 2017, David Pilling and Chris Johnson
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*   * Redistributions of source code must retain the above copyright
*     notice, this list of conditions and the following disclaimer.
*   * Redistributions in binary form must reproduce the above
*     copyright notice, this list of conditions and the following
*     disclaimer in the documentation and/or other materials provided
*     with the distribution.
*   * Neither the name of the copyright holder nor the names of their
*     contributors may be used to endorse or promote products derived
*     from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* HOLDER BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/

/* Include files */
/* from standard clib */
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <stdio.h>
#include <locale.h>
#include <time.h>
#include <ctype.h>


/* From TaskLib */
#include "Tasklib:h.os"
#include "Tasklib:h.wimp"
#include "Tasklib:h.wimpt"
#include "Tasklib:h.werr"
#include "Tasklib:h.flex"
#include "Tasklib:h.sprite"
#include "Tasklib:h.transform"
#include "Tasklib:h.bbc"
#include "Tasklib:h.akbd"
#include "Tasklib:h.wos"
#include "Tasklib:h.err"
#include "Tasklib:h.task"
#include "Tasklib:h.poll"
#include "Tasklib:h.temp"
#include "Tasklib:h.scrap"
#include "Tasklib:h.fsx"
#include "Tasklib:h.etc"
#include "Tasklib:h.xx2con"
#include "Tasklib:h.swis"
#include "Tasklib:h.save"
#include "Tasklib:h.bf"
#include "Tasklib:h.key"
#include "Tasklib:h.crc"
#include "Tasklib:h.pane"
#include "Tasklib:h.bits"
#include "Tasklib:h.timex"
#include "Tasklib:h.mlo"
#include "Tasklib:h.mym"
#include "Tasklib:h.log"



/* from application */
////#include "h.compswitch"
#include "h.debugDefs"

#include "h.arcs"
#include "h.constants"
#include "h.reslink"
#include "h.syncdiscs"
#include "h.dbox"
#include "h.zone"
#include "h.progress"
#include "h.main"
#include "dirlist.h"



#define MAX_FILER_ACT 10

#define APPLICATION   0x2000


/* Icon IDs in main window */
#define MP_PRIMARY_PATH    2
#define MP_SECONDARY_PATH  4
#define MP_SCRAP_PATH      9
#define MP_PRIMARY_POPUP   27
#define MP_SECONDARY_POPUP 28
#define MP_SCRAP_POPUP     29
#define MP_NEWER           6
#define MP_EXTRA           7
#define MP_OVERWRITE       22
#define MP_LOGFILE_PATH    11
#define MP_LOGFILE_MENU    13
#define MP_LOG_ENABLE      12
#define MP_OPEN_LOG_FILE   25
#define MP_SETTINGS        24
#define MP_COMPARE         23
#define MP_SAVE            10
#define MP_SAVE_MENU       26
#define MP_CANCEL          1
#define MP_START           0
#define MP_ZONEFILE_PATH   31
#define MP_ZONEFILE_POPUP  32
#define MP_ZONEFILE_DIROPEN 33
#define MP_ZONEFILE_ENABLE  34


/* Icon IDs in 'settings' window */
#define SETTINGS_COPYDIR           11
#define SETTINGS_NEWLOG            22
#define SETTINGS_LOG_KEEP          4
#define SETTINGS_LOG_KEEP_UP       6
#define SETTINGS_LOG_KEEP_DOWN     5
#define SETTINGS_SAVE              8
#define SETTINGS_AUTOOPENLOG       1
#define SETTINGS_CLOSE             10
#define SETTINGS_MULTITASK         12
#define SETTINGS_TSTAMP_MAKE_EQUAL 13
#define SETTINGS_TSTAMP_TIME       14
#define SETTINGS_TSTAMP_DOWN       15
#define SETTINGS_TSTAMP_UP         16
#define SETTINGS_TSTAMP_UNITS      17
#define SETTINGS_MULTIPLE_FA       18
#define SETTINGS_MULTIPLE_FA_NUM   19
#define SETTINGS_MULTIPLE_FA_DOWN  20
#define SETTINGS_MULTIPLE_FA_UP    21
#define SETTINGS_IGNORE_FILETYPE   24
#define SETTINGS_SAVE_LOG_IN_PRI   2
#define SETTINGS_VERBOSE_FA        25
#define SETTINGS_VERBOSE_LOG       26
#define SETTINGS_NO_DELETE_IGNORED  28

#ifdef KILL_TRAPDELETE
#define SETTINGS_DISABLE_TRAPDELETE  27
#endif

#ifdef KILL_SPARKFS
#define SETTINGS_DISABLE_SPARKFS   29
#endif


#define SETTINGS_LABEL_KEEP        3
#define SETTINGS_LABEL_COPIES      7


#define OSFILE_ARCHIVE 0xDDC
#define OSFILE_ZIP     0xA91



/* for zone file list menu */
#define ZONE_FILE_MAX 20
#define ZONE_FILE_LEAF_MAX 64


/* for os_gbpb reason 12 */

/**************************************************
 * Object type                                    *
 * 0 - Not found                                  *
 * 1 - File found                                 *
 * 2 - Directory found                            *
 * 3 - Image file found (both file and directory) *
 *                                                *
 **************************************************/

typedef struct gbpb12data
{
  int   load_add;
  int   exec_add;
  int   length;
  int   attributes;
  int   obj_type;
  int   obj_filetype;
  char  name[128];
} gbpb12data;





/****************************************************************************/


static int  mphandle;           /* main window handle */
static int  mpicon;


static win_position sd_pos;
static int  settings_handle;    /* settings window handle */
static win_position settings_pos;

static syncsettings sync;       /* all additional settings */
static syncsettings syncjob;

joblist jobs;

static char jobsave_filename [FILENAMELENGTH];

/* For reading/writing config etc in Choices */
static char * cwritepath = "<Choices$Write>.CJohnson.SyncDiscs.";
static char * creadpath = "Choices:CJohnson.SyncDiscs.";
static char * apppath = "<SyncDiscs$Dir>.Resources.";
static char *write_chsysvar = "Choices$Write";
static char *read_chsysvar = "Choices$Path";

/* for zone file list menu */
static int zone_files;
static char zone_filenames [ZONE_FILE_MAX][ZONE_FILE_LEAF_MAX];
static int *menudata;


/****************************************************************************/

/* function prototypes */

static os_error *configsave (void);
static os_error *settings_close (int w, int userhandle);
static os_error *openjobsave (void);
static os_error *savejobdetails2 (buffer *pbf, char *save_id,  synctag *tag);
static char * getjobtok(char * string, int * len);
static os_error * getjobtag(synctag * tag, char * p);
static synctag * findjobtag(synctag *tags, char * name);



/****************************************************************************/

/****************************************************************************/

/********************************************************************
 *                                                                  *
 * Read the next object in the given directory                      *
 * Returns object info in supplied buffer                           *
 * object offset for next read (-1 if finished)                     *
 * number of objects read (1 for object read, 0 if nothing read)    *
 *                                                                  *
 ********************************************************************/

static os_error *get_next_object (char *parentdir, int *obj_offset,
                                  char *buff, int size, int *read)
{
  os_error   *err;
  os_regset   rx;

  /* Use OS_GBPB 12 to read file info from a directory */

  rx.r[0] = 12;
  rx.r[1] = (int) parentdir;
  rx.r[2] = (int) buff;
  rx.r[3] = 1;                        /* ask for one object */
  rx.r[4] = *obj_offset;
  rx.r[5] = size;
  rx.r[6] = 0;                        /* match all files */

  err = os_swix (OS_GBPB, &rx);

  /* save back the object offset and number of objects read */
  *obj_offset = rx.r[4];
  *read = rx.r[3];
  return (err);
}



/****************************************************************************/

static os_error * zonefilelistmenu_decode (int * menu)

{
  os_error   *err = NULL;
  int choice;
  char *path;

  choice = menu[0];
  if (choice >= 0)
  {
    /* insert the path in to the writable */
    err = flex_alloc ((flex_ptr) &path, ZONE_FILE_LEAF_MAX + 64);
    if (!err)
    {
      sprintf (path, "%s.%s", ZONE_FILE_DIR, zone_filenames[choice]);
      writeicon (mphandle, MP_ZONEFILE_PATH, path);

      err = flex_free ((flex_ptr) &path);
    }

  }
  return (err);
}


#define WARN_TEXT_LENGTH 128

/****************************************************************************/

static void zonefilelist_openmenu (void)
{
  os_error   *err;

  int   obj_offset;        /* for os_gbpb */
  int   read;              /* for os_gbpb */
  char  zonefiledir[64];   /* for path of directory holding zone files */
  char  buff[160];         /* to hold file info data from gbpb call */
  char *warn;              /* buffer for warning messages */
  int  i;
  gbpb12data *dat;
  int len;

  zone_files = 0;
  obj_offset = 0;
  strcpy (zonefiledir, ZONE_FILE_DIR);

  err = flex_alloc ((flex_ptr) &warn, WARN_TEXT_LENGTH);
  if (err)
  {
    errorbox (err->errmess);
    return;
  }

  /* Get the names of the files in the zone file dir and store them */
  while (obj_offset != -1)
  {
    /* get the next object in the directory */
    err = get_next_object (zonefiledir, &obj_offset, buff, sizeof (buff), &read);
    if (err)
    {
      errorbox (err->errmess);
      return;
    }
    if (read)
    {
      dat = (gbpb12data *)buff;
      if ((dat->obj_type == 1) && (dat->obj_filetype == 0xfff))
      {
        /* It is a text file */
        /* Have we reached the max number of files? */
        if (zone_files < ZONE_FILE_MAX)
        {
          /* check the name is not longer than max leaf length */
          len = strlen (dat->name);
          if (len < ZONE_FILE_LEAF_MAX)
          {
            strcpy (zone_filenames[zone_files], dat->name);
            zone_files++;
          }
          else
          {
            sprintf (warn, "A zone file name was longer than the %d chars allowed", ZONE_FILE_LEAF_MAX);
            messagebox (warn);
          }
        }
        else
        {
          sprintf (warn, "The maximum number of zone files allowed (%d) has been exceeded",
                                                                                 ZONE_FILE_MAX);
          messagebox (warn);
          /* do not load any more */
          obj_offset = -1;
        }
      }
    }
  }

  flex_free ((flex_ptr) &warn);

  /* Now create the menu - but only if we have found some files */
  if (zone_files > 0)
  {
    err = createusermenu (zonefilelistmenu_decode, NULL);
    if (!err)
    {
      for (i = 0; i < zone_files; i++)
      {
        err = addusermenu (zone_filenames[i], 0);
      }
      menudata = completeusermenu ("Zone files");
    }
    openupmenu (usermenuindex);

  }
  return;
}


/****************************************************************************/

static os_error *pathsave (char *name, int type)
{
  char       *savepath;

  iconaddr (mphandle, MP_LOGFILE_PATH, &savepath);

  strcpy (savepath, name);

  if (mphandle)
    wimp_set_icon_state (mphandle, MP_LOGFILE_PATH, (wimp_iconflags) 0, (wimp_iconflags) 0);

  return (NULL);
  USE (type);
}


/*
 *
 * This is the original function - now replaced
 *
 * static os_error * opensavepath(void) { os_error * err; char *
 * savepath;
 *
 * iconaddr(mphandle,11,&savepath);
 *
 * err=opensave(savepath,TEXT,pathsave);
 *
 * return(err); }
*/


/************************************************************/

static os_error *menuwindow_atpointer (int handle)
{
  os_error   *err;
  mousestr    mouse;

  err = getpointer (&mouse);
  if (!err)
    err = wimp_create_menu ((wimp_menustr *) handle, mouse.x + 16, mouse.y + 16);

  return (err);
}




/*************************************************************************
 *                                                                       *
 * cj replacement for commented out function above, to allow save        *
 * dialogue to open at pointer (i.e. at the menu button) rather than     *
 * always at centre of screen                                            *
 *                                                                       *
 *************************************************************************/

static os_error *opensavepath (void)
{
  os_error   *err;
  char       *savepath;
  int         handle;

  iconaddr (mphandle, MP_LOGFILE_PATH, &savepath);
  err = setsave (savepath, TEXT, pathsave, &handle);
  if (!err)
    err = menuwindow_atpointer (handle);

  return (err);
}





/************************************************************/

static os_error *syncdiscsloadtype (int type, mousestr * m, int userhandle,
                                    int *method)
{
  mpicon = m->icon;

  if (type == SYNCDISCS_FILETYPE)
  {
    if (*method == 0) *method = 1;
  }
  else
  {
    if ((mpicon == MP_LOGFILE_PATH) || (mpicon == MP_PRIMARY_PATH)
                                    || (mpicon == MP_SECONDARY_PATH)
                                    || (mpicon == MP_SCRAP_PATH)
                                    || (mpicon == MP_ZONEFILE_PATH))
    {
      if (*method == 0) *method = 1;
    }
  }

  return (NULL);


  USE (userhandle);
  USE (type);

}

/************************************************************/

void dbox_insert_path (char *path, int list, BOOL shift)

{
  os_error   *err;
  int         field_id;
  char        filepath[FILENAMELENGTH];
  char *ptr, *ptr2;
  int len1, len2;

  /* Set the object id of the path writable field */
  switch (list)
  {
    case PRIMARY_LIST:
      field_id = MP_PRIMARY_PATH;
      break;
    case SECONDARY_LIST:
      field_id = MP_SECONDARY_PATH;
      break;
    case SCRAP_LIST:
      field_id = MP_SCRAP_PATH;
      break;
  }
  /* Now insert the new path in to the writable */
  if (shift)
  {
    /* We need to add the path to what is already there */
    /* Read the existing contents of the field */
    iconaddr (mphandle, field_id, &ptr);
    /* get the length of the current contents */
    len1 = strlen (ptr);
    /* Get length of path to be added */
    len2 = strlen (path);
    if ( (len1 + len2 + 2) > FILEPATH_WRITABLE_LENGTH )
    {
      /* Exceeds the icon maximum text length */
      /* Raise error */
      return;
    }
    ptr2 = filepath;
    /* Is there something there already? */
    if (len1 > 0)
    {
      ptr2 += sprintf (ptr2, "%s,", ptr);
    }
    ptr2 += sprintf (ptr2, path);
    /* Now write the new combined path in to the field */
    err = writeicon (mphandle, field_id, filepath);
  }
  else
  {
    /* Just write the new path in to the field, replacing previous content */
    err = writeicon (mphandle, field_id, path);
  }
  return;
}




/************************************************************/

static os_error *syncdiscsload (char *name, int type,
                                int userhandle, int xvolatile)
{
  os_error   *err;
  char        path[NAMELEN];
  char        path2[NAMELEN];
  char       *p, *p1, *p2;

  err = NULL;

  if (type == SYNCDISCS_FILETYPE)
  {
    return (jobload (name, type, userhandle, xvolatile));
  }

  switch (mpicon)
  {
    case MP_LOGFILE_PATH:
      /* Do not put path in to writable - do nothing */
      return (NULL);
      break;

    case MP_ZONEFILE_PATH:
      /* The zone file should be a text file. If so, just enter the path of
      ** the file in to the writable */
      if (type == TEXT)
      {
////        strcpy (path, name);
        err = writeicon (mphandle, mpicon, name);
      }
      break;

    case MP_PRIMARY_PATH:
    case MP_SECONDARY_PATH:
    case MP_SCRAP_PATH:
      if ((type == DIRECTORY) || (type == APPLICATION) || (type == OSFILE_ARCHIVE)
                        || (type == OSFILE_ZIP))
      {
        strcpy (path, name);

        if (isctrl && isshift)
        {
          /* The control + shift combination is used only in one situation
          ** - namely when adding the secondary dir leaf automatically */

          if (mpicon == MP_SECONDARY_PATH)
          {
            /*******************************************************************
             *                                                                 *
             * If a directory is dropped onto the secondary path with both     *
             * SHIFT and CONTROL keys pressed, then attempt to automatically   *
             * add the correct leafname to the secondary path. This is useful  *
             * when the secondary directory does not yet exist, and saves      *
             * having to type the leafname in by hand. Only attempt this if    *
             * the option has been enabled in the settings dialogue.           *
             *                                                                 *
             *******************************************************************/

              p = fs_leaf (path);
              if (p != path)
              {
                iconaddr (mphandle, MP_PRIMARY_PATH, &p1);
                p2 = fs_leaf (p1);
                if (p2 != p1)
                {
                  if (strcmp (p, p2))
                  {
                    /* leafs of Primary and Secondary are different - try to add
                    ** the primary leaf to the secondary, including the . path
                    ** separator */

                    p2--;
                    strcat (path, p2);
                  }
                }
              }

          }
          /* Either write the new path with added leaf, or just treat the drag
          ** as if neither shift nor ctrl were pressed */

          err = writeicon (mphandle, mpicon, path);
        }
        else
        {
          /* If control pressed, the name will have one element removed - useful
          ** if you wish to enter the root of a drive */
          if (isctrl)
          {
            p = fs_leaf (path);
            if (p != path)
            *(p - 1) = 0;
          }

          /* If shift is pressed, add a comma to end of existing field contents
          ** and then append the new leaf name */
          if (isshift)
          {
            iconaddr (mphandle, mpicon, &p);
            strcpy (path2, p);
            if (path2[0] != '\0')
            {
              strcat (path2, ",");
            }
            strcat (path2, path);
            err = writeicon (mphandle, mpicon, path2);
          }
          else
          /* Just replace current contents of field with the new leaf name */
          {
            err = writeicon (mphandle, mpicon, path);
          }
        }
      }
      break;

  }


  return (err);

}

/******************************************************************************
 *                                                                            *
 * Show the log file, as currently set in the main window, in a text editor.  *
 * Also can be called from the syncdiscs() function to auto show the log      *
 * file at the end of the sync process.                                       *
 *                                                                            *
 ******************************************************************************/

os_error *show_log_file (char *logfile)
{
  os_error   *err;
  char        buf[300];
  int         type;
  int        open_for_write = 0;

  /* Does the file exist? */
  err = fs_exists (logfile, &type);
  if (type)
  {
    /* Check it is not open for writing */
    err = fs_file_is_writable (logfile, &open_for_write);
    if (!open_for_write)
    {
      /* make up the command line */
      sprintf (buf, "Filer_Run %s", logfile);
      os_cli (buf);
    }
  }
  else
  {
    messagebox ("The log file could not be found");
  }

  return (err);
}






/************************************************************/

/* Changed to use the modified syncsettings structure, with char arrays rather
   than char pointers */

static os_error *syncdiscsicon (int handle, int userhandle, wimp_mousestr * m)
{
  os_error   *err;
  char *ptr;
  char *cmd;

  err = NULL;

  switch (m->i)
  {
    case MP_START:
    case MP_COMPARE:
      /* Get the paths from the writable icons */
      iconaddr (handle, MP_PRIMARY_PATH, &ptr);
      strcpy (sync.primary, ptr);
      iconaddr (handle, MP_SECONDARY_PATH, &ptr);
      strcpy (sync.secondary, ptr);
      iconaddr (handle, MP_SCRAP_PATH, &ptr);
      strcpy (sync.scrap, ptr);
      iconaddr (handle, MP_LOGFILE_PATH, &ptr);
      strcpy (sync.logfile, ptr);
      iconaddr (handle, MP_ZONEFILE_PATH, &ptr);
      strcpy (sync.zonefile, ptr);

      sync.compare = (m->i == MP_COMPARE);

      /*************************************************************************
       *                                                                       *
       * Now we need to claim memory for the settings and copy all the         *
       *  sync settings values into the sync job queue. First check that       *
       *  the queue is not already full (there shouldn't be any possibility    *
       *  at this point since we couldn't open the sync control window while   *
       *  the syncdiscs function is running with jobs in the queue).           *
       *                                                                       *
       *************************************************************************/

      if (jobs.number < JOBLIST_MAX )
      {
        err = flex_alloc ((flex_ptr) &jobs.settings[jobs.number], sizeof (syncsettings));
        if (!err)
        {
          /* copy the settings into the jobs queue */
          *jobs.settings[jobs.number] = sync;
          jobs.number++;
          /* Start the syncing (or comparing) process only if not currently running */
          if (!sync_running) err = syncdiscs ();
        }
      }
      else
      {
        messagebox ("The job queue is full, please wait until the queue has decreased");
      }
      break;

    case MP_CANCEL:
      err = syncdiscsclose (handle, userhandle);
      break;

    case MP_NEWER:
      err = selectst (handle, MP_NEWER, sync.newer ^= 1);
      shadeiconst (mphandle, MP_OVERWRITE, sync.newer);
      break;

    case MP_EXTRA:
      err = selectst (handle, MP_EXTRA, sync.extra ^= 1);
      break;

    case MP_LOG_ENABLE:
      err = selectst (handle, MP_LOG_ENABLE, sync.log ^= 1);
      break;

    case MP_ZONEFILE_ENABLE:
      err = selectst (handle, MP_ZONEFILE_ENABLE, sync.use_zone_file ^= 1);
      break;

    case MP_SAVE:
      err = configsave ();
      break;

    case MP_LOGFILE_MENU:
      err = opensavepath ();
      break;

    case MP_OVERWRITE:
      err = selectst (handle, MP_OVERWRITE, sync.overwrite ^= 1);
      shadeiconst (mphandle, MP_NEWER, sync.overwrite);
      break;

    case MP_SETTINGS:
      err = open_settings ();
      break;

    case MP_OPEN_LOG_FILE:
      /* Get the current log file path */
      iconaddr (handle, MP_LOGFILE_PATH, &ptr);
      err = show_log_file (ptr);
      break;

    case MP_ZONEFILE_DIROPEN:
      {
        /* Get a buffer */
        err = flex_alloc ((flex_ptr) &cmd, 128);
        if (!err)
        {
          /* make command and run it */
          sprintf (cmd, "filer_opendir %s", ZONE_FILE_DIR);
          err = os_cli (cmd);
          flex_free ((flex_ptr) &cmd);
        }
      }
      break;

    case MP_SAVE_MENU:
      err = openjobsave ();
      break;

    case MP_PRIMARY_POPUP:
      dirlist_openmenu (PRIMARY_LIST);
      break;

    case MP_SECONDARY_POPUP:
      dirlist_openmenu (SECONDARY_LIST);
      break;

    case MP_SCRAP_POPUP:
      dirlist_openmenu (SCRAP_LIST);
      break;

    case MP_ZONEFILE_POPUP:
      zonefilelist_openmenu ();
      break;

  }

  return (err);
}



/************************************************************/

static os_error *syncdiscskey (int handle, int userhandle, int icon, int *key)
{
  os_error   *err;
  int         ch;
  wimp_mousestr m;

  err = NULL;

  ch = *key;

  if (ch == RETURN)
  {
    *key = -1;
    m.i = MP_START;
    err = syncdiscsicon (handle, userhandle, &m);
  }
  else if (ch == ESCAPE)
  {
    *key = -1;
    m.i = MP_CANCEL;
    err = syncdiscsicon (handle, userhandle, &m);
  }

  return (err);

  USE (icon);
}






/************************************************************/

os_error   *syncdiscsclose (int w, int userhandle)
{
  os_error   *err;

  wos_savewindposition (mphandle, &sd_pos);

  remkeyevent (syncdiscskey, mphandle, 0);
  remclickevent (syncdiscsicon, mphandle, 0);
  remcloseevent (syncdiscsclose, mphandle, 0);
  remdataload (mphandle, 0, syncdiscsload);
  remdataloadtype (mphandle, 0, syncdiscsloadtype);
  remhelpevent (NULL, mphandle, TSYNCDISCS);

  err = closedown (&mphandle);

  if (settings_handle)
    settings_close (0, 0);

  return (err);

  USE (userhandle);
  USE (w);
}



/* Following instructions pulled out of opensyncdiscs() to allow
 * other functions to call the setup code.
 */

/******************************************************************/

void dbox_shade_menubuttons ( BOOL bPri, BOOL bSec, BOOL bScrap)

{
  shadeiconst (mphandle, MP_PRIMARY_POPUP, bPri);
  shadeiconst (mphandle, MP_SECONDARY_POPUP, bSec);
  shadeiconst (mphandle, MP_SCRAP_POPUP, bScrap);

  return;
}



/******************************************************************/

static void setup_syncdiscs_buttons (void)
{
  BOOL prim;
  BOOL sec;
  BOOL scrap;

  selectst (mphandle, MP_NEWER, sync.newer);
  selectst (mphandle, MP_EXTRA, sync.extra);
  selectst (mphandle, MP_LOG_ENABLE, sync.log);
  selectst (mphandle, MP_OVERWRITE, sync.overwrite);
  selectst (mphandle, MP_ZONEFILE_ENABLE, sync.use_zone_file);
  shadeiconst (mphandle, MP_LOGFILE_PATH, sync.save_log_in_primary);
  shadeiconst (mphandle, MP_LOGFILE_MENU, sync.save_log_in_primary);
  /* shade/unshade contradictory settings */
  if (sync.newer)
  {
    sync.overwrite = FALSE;
    selectst (mphandle, MP_OVERWRITE, FALSE);
    shadeiconst (mphandle, MP_OVERWRITE, TRUE);
  }
  else
  {
    if (sync.overwrite)
    {
      shadeiconst (mphandle, MP_NEWER, TRUE);
    }
  }
  /* Check status of dir lists - shade/unshade menu buttons */
  dirlist_status (&prim, &sec, &scrap);
  dbox_shade_menubuttons (prim, sec, scrap);
  return;
}




/******************************************************************/

os_error   *opensyncdiscs (void)
{
  os_error   *err;

  if (mphandle)
    err = forward (mphandle, 0, NULL);
  else
  {
    err = createwindow (TSYNCDISCS, &mphandle);
    if (!err)
    {
      addkeyevent (syncdiscskey, mphandle, 0);
      addclickevent (syncdiscsicon, mphandle, 0);
      addcloseevent (syncdiscsclose, mphandle, 0);

      adddataload (mphandle, 0, syncdiscsload);
      adddataloadtype (mphandle, 0, syncdiscsloadtype);
      addhelpevent (NULL, mphandle, TSYNCDISCS);
      setup_syncdiscs_buttons ();

      /* Following changed to remember last position of syncdiscs
       * window when it is reopened. Only applies to current session.
       */

      if (sd_pos.first_time)
      {
        popup (mphandle, 0);
        sd_pos.first_time = 0;
      }
      else
      {
        open (mphandle, sd_pos.x0, sd_pos.y0, sd_pos.x1, sd_pos.y1, 0, 0, -1);
      }
    }
  }
  return (err);
}



/* LOAD/SAVE CONFIGURATION FILES */

/************************************************************/

void make_choices_path (BOOL save, char *path, char *filename)

{
////  char *chsysvar;
////  char *chpath;
  os_error * err;
  int type;

  if (save)
  {
    if (os_read_var_size (write_chsysvar) != 0)
    {
      /* use the appropriate choices path to save config file */
      sprintf (path, "%s%s", cwritepath, filename);
      /* ensure all directories exist */
      fs_create_dir_chain (path);
    }
    else
    {
      /* Look inside app directory */
      sprintf (path, "%s%s", apppath, filename);
    }
  }
  else
  {
    if (os_read_var_size (read_chsysvar) != 0)
    {
      /* use the appropriate choices path to save config file */
      sprintf (path, "%s%s", creadpath, filename);
      /* does the file in choices exist? */
      err = fs_exists (path, &type);
      if (!type || err)
      {
        /* drop back to use the app path */
        sprintf (path, "%s%s", apppath, filename);
      }
    }
    else
    {
      /* Look inside app directory */
      sprintf (path, "%s%s", apppath, filename);
    }
  }


  return;
}


/***************************************************************
 *                                                             *
 * Settings config2 *                                          *
 *                                                             *
 * Loading and saving config2 settings now modified to use     *
 * the syncjob load and save routines rather than the stdio    *
 * file read and write functions                               *
 *                                                             *
 ***************************************************************/



static synctag config2table[] =
{
  "MultiTask",      CONINT, &sync.multitask,       0,
  "NewLog",         CONINT, &sync.new_log,         0,
  "NumberOfLogs",   CONINT, &sync.num_old_logs,    0,
  "AutoOpenLog",    CONINT, &sync.auto_open_log,   0,
  "FastCopy",       CONINT, &sync.copy_dir,        0,
  "TimeEqual",      CONINT, &sync.tstamp_eq,       0,
  "TimeRes",        CONINT, &sync.timeresolution,  0,
  "MultipleFA",     CONINT, &sync.multiple_fa,     0,
  "MultipleFANum",  CONINT, &sync.num_fa,          0,
  "VerboseFA",      CONINT, &sync.verbose_fileractions, 0,
  "IgnoreFiletype", CONINT, &sync.ignore_filetype, 0,
  "LogInPrimary",   CONINT, &sync.save_log_in_primary, 0,
  "VerboseLog",     CONINT, &sync.verbose_log,     0,
  "NoDeleteIgnored", CONINT, &sync.no_delete_ignored, 0,

#ifdef KILL_TRAPDELETE
  "DisableTrapDelete", CONINT, &sync.disable_trapdelete, 0,
#endif
#ifdef KILL_SPARKFS
  "DisableSparkFS", CONINT, &sync.kill_sparkfs,    0,
#endif

  NULL, 0, NULL, 0
};



/************************************************************/

static os_error *config2save (void)
{
  os_error  *err;
  char   *spath;
  buffer     bf;
  char *leaf = "Config2";


  err = flex_alloc ((flex_ptr)&spath, FSMAXWIMPMSGPATH);
  if (!err)
  {
    make_choices_path (CHSAVE, spath, leaf);
    err = bf_open (spath, 'w', DEFBUFFSIZE, &bf);
    if (!err) err = savejobdetails2 (&bf, leaf, config2table);
    err = bf_closec(&bf, err, spath, TEXT);
  }
  flex_free ((flex_ptr)&spath);
  return (err);
}



/************************************************************/

os_error   *config2init (void)
{
  os_error  *err;
  buffer     bf;
  int        eof;
  char       string[256];
  int        len;
  char    *  p;
  synctag * tag;
  int        type;
  char   *spath;


  err = flex_alloc ((flex_ptr)&spath, FSMAXWIMPMSGPATH);
  if (!err)
  {
    make_choices_path (CHLOAD, spath, "Config2");
    /* first check that config file exists. If not just return */
    err = fs_exists (spath, &type);
    if (type && !err)
    {
      err = bf_open(spath, 'r', DEFBUFFSIZE, &bf);
      if (!err)
      {
        eof = 0;

        while (!eof)
        {
          err = bf_getstring (&bf, string, sizeof(string), &eof);
          if (err) break;

          p = getjobtok (string, &len);
          if (len)
          {
            /* The additional test (strcmp (p, "#")) has been added for
             * backwards compatibility because earlier routines for config2
             * load/save used # as comment marker
            */
            if ((strncmp (p, "//", 2)) && (*p != '#'))
            {
              tag = findjobtag (config2table, p);
              if (tag != NULL) getjobtag(tag, p+len);
            }
          }
        }
        err = bf_close (&bf, err);
      }
      /* now set up the value for filer_action resume, depending on
      ** the configured value of num_fa
      */
      if (sync.num_fa < 6)
        sync.num_fa_resume = sync.num_fa - 1;
      else
        sync.num_fa_resume = sync.num_fa - 2;
    }
  }
  flex_free ((flex_ptr)&spath);
  return (err);
}




/************************************************************/

/* Main config */

static contag contable[] =
{
#ifdef NEVER
  "Primary",   CONSTR, NULL, (contokstr *) 0, NULL,
  "Secondary", CONSTR, NULL, (contokstr *) 0, NULL,
  "Scrap",     CONSTR, NULL, (contokstr *) 0, NULL,
  "LogFile",   CONSTR, NULL, (contokstr *) 0, NULL,
#endif
  "Primary",   CONSTRING, NULL, NULL, NULL,
  "Secondary", CONSTRING, NULL, NULL, NULL,
  "Scrap",     CONSTRING, NULL, NULL, NULL,
  "LogFile",   CONSTRING, NULL, NULL, NULL,
  "ZoneFile",  CONSTRING, NULL, NULL, NULL,
  "Newer",     CONINT, &sync.newer,     NULL, NULL,
  "Extra",     CONINT, &sync.extra,     NULL, NULL,
  "Log",       CONINT, &sync.log,       NULL, NULL,
  "Overwrite", CONINT, &sync.overwrite, NULL, NULL,
  "UseZoneFile", CONINT, &sync.use_zone_file, NULL, NULL,

  NULL, 0, NULL, NULL, NULL
};


static conlink configlink =
{
  "SyncDiscs",
  NULL,
  NULL
};





/************************************************************/

static os_error *configsave (void)
{
  os_error  *err;
  char   *spath;


  err = flex_alloc ((flex_ptr)&spath, FSMAXWIMPMSGPATH);
  if (!err)
  {
    make_choices_path (CHSAVE, spath, "Config");
    err = saveconfig (spath);
  }
  flex_free ((flex_ptr)&spath);
  return (err);
}




/************************************************************/

os_error   *configinit (void)
{
  os_error   *err;
  wimp_wind  *ww;
  wimp_icon  *wi;
  int         type;
  char       *p;
  char       *spath;


  err = flex_alloc ((flex_ptr)&spath, FSMAXWIMPMSGPATH);
  if (!err)
  {
    err = findtemp (TSYNCDISCS, &ww);
    if (!err)
    {
      wi = (wimp_icon *) (((char *) ww) + sizeof (wimp_wind));

      contable[0].address = wi[MP_PRIMARY_PATH].data.indirecttext.buffer;
      contable[0].data.len = wi[MP_PRIMARY_PATH].data.indirecttext.bufflen;

      contable[1].address = wi[MP_SECONDARY_PATH].data.indirecttext.buffer;
      contable[1].data.len = wi[MP_SECONDARY_PATH].data.indirecttext.bufflen;

      contable[2].address = wi[MP_SCRAP_PATH].data.indirecttext.buffer;
      contable[2].data.len = wi[MP_SCRAP_PATH].data.indirecttext.bufflen;

      contable[3].address = wi[MP_LOGFILE_PATH].data.indirecttext.buffer;
      contable[3].data.len = wi[MP_LOGFILE_PATH].data.indirecttext.bufflen;

      contable[4].address = wi[MP_ZONEFILE_PATH].data.indirecttext.buffer;
      contable[4].data.len = wi[MP_ZONEFILE_PATH].data.indirecttext.bufflen;

      err = addcontable (contable, &configlink);
      if (!err)
      {
        make_choices_path (CHLOAD, spath, "Config");
        fs_exists (spath, &type);
        if (type) err = loadconfig (spath);

        /* This is probably an evil hack - used to insert a default *
         * logfile path if config file has a blank entry */
        p = contable[3].address;
        if (p[0] == '\0')
        {
          strcpy (p, "<SyncDiscs$Dir>.^.Log");
        }
      }
    }
  }
  flex_free ((flex_ptr)&spath);
  return (err);
}



/* End of configuration functions */




/************************************************************/

/* For command line use the following parameters can be set from the
 * command line arguments, or will take the default values
 *
 * Primary path
 * Secondary path
 * Scrap dir path
 * Log file path + use log flag
 * Newer
 * Extra
 * Overwrite
 *
 * The following will take default values
 *
 * Make timestamps equal
 * Time resolution
 * Copy whole directories
 * Make new log
 * Number of old logs*
 *
 * The following will take specific values
 *
 * Multitask = 0 - Always single task
 */

/* 0011 Changed to use a local copy of the settings structure */

os_error   *commandline (int argc, char *argv[], int *quit)
{
  os_error   *err;
  int         i;
  syncsettings cmdl_sync;


  err = NULL;
  *quit = 0;

  /* make a local copy of the settings struct */
  cmdl_sync = sync;
  /* make sure we single task whatever the state of the settings */
  cmdl_sync.multitask = 0;
  /* Now parse the argument list */
  for (i = 1; i < argc; i++)
  {
    if (!cstrcmp (argv[i], "-quit"))
      *quit = 1;
    else if (!cstrcmp (argv[i], "-primary"))
    {
      i++;
      if (i < argc)
        strcpy (contable[0].address, argv[i]);
      else
        err = geterror (EMISS);
    }
    else if (!cstrcmp (argv[i], "-secondary"))
    {
      i++;
      if (i < argc)
        strcpy (contable[1].address, argv[i]);
      else
        err = geterror (EMISS);
    }
    else if (!cstrcmp (argv[i], "-scrap"))
    {
      i++;
      if (i < argc)
        strcpy (contable[2].address, argv[i]);
      else
        err = geterror (EMISS);
    }
    else if (!cstrcmp (argv[i], "-log"))
    {
      i++;
      if (i < argc)
        strcpy (contable[3].address, argv[i]);
      else
        err = geterror (EMISS);
      cmdl_sync.log = 1;
    }
    else if (!cstrcmp (argv[i], "-zone"))
    {
      i++;
      if (i < argc)
      {
        strcpy (contable[4].address, argv[i]);
        cmdl_sync.use_zone_file = 1;
      }
      else
        err = geterror (EMISS);
    }
    else if (!cstrcmp (argv[i], "~log"))
    {
      cmdl_sync.log = 0;
    }
    else if (!cstrcmp (argv[i], "-newer"))
    {
      cmdl_sync.newer = 1;
    }
    else if (!cstrcmp (argv[i], "~newer"))
    {
      cmdl_sync.newer = 0;
    }
    else if (!cstrcmp (argv[i], "-extra"))
    {
      cmdl_sync.extra = 1;
    }
    else if (!cstrcmp (argv[i], "~extra"))
    {
      cmdl_sync.extra = 0;
    }
    else if (!cstrcmp (argv[i], "-over"))
    {
      cmdl_sync.overwrite = 1;
    }
    else if (!cstrcmp (argv[i], "~over"))
    {
      cmdl_sync.overwrite = 0;
    }
    else if (!cstrcmp (argv[i], "~zone"))
    {
      cmdl_sync.use_zone_file = 0;
    }
    else if (!cstrcmp (argv[i], "-multitask"))
    {
      cmdl_sync.multitask = 1;
    }
    else
      err = geterror (ECOMM);
  }


  if (!err && *quit)
  {
    strcpy (cmdl_sync.primary, contable[0].address);
    strcpy (cmdl_sync.secondary, contable[1].address);
    strcpy (cmdl_sync.scrap, contable[2].address);
    strcpy (cmdl_sync.logfile, contable[3].address);
    if (jobs.number < JOBLIST_MAX )
    {
      err = flex_alloc ((flex_ptr) &jobs.settings[jobs.number], sizeof (syncsettings));
      if (!err)
      {
        /* copy the settings into the jobs queue */
        *jobs.settings[jobs.number] = cmdl_sync;
        jobs.number++;
        /* and start syncing (or comparing) if not already syncing */
        if (!sync_running) err = syncdiscs ();
      }
    }
  }

  return (err);
}


/************************************************************/

static os_error *write_ind_icon_string (int win, int ic, char *string)
{
  os_error   *err;
  wimp_icon   iconinfo;

  err = wimp_get_icon_info (win, ic, &iconinfo);

  if (!err)
  {
    /* Is icon indirected? */
    if (iconinfo.flags & wimp_INDIRECT)
    {
      /* Will string fit in the buffer? */
      if (strlen(string) < iconinfo.data.indirecttext.bufflen)
      {
        strcpy (iconinfo.data.indirecttext.buffer, string);
        err = wimp_set_icon_state (win, ic, (wimp_iconflags) 0, (wimp_iconflags) 0);
      }
      else
      {

      }
    }
  }

  return (err);
}


/************************************************************/

static os_error *write_ind_icon_int_value (int win, int ic, int val)
{
  char        buffer[16];

  sprintf (buffer, "%d", val);

  return (write_ind_icon_string (win, ic, buffer));
}



/************************************************************************
 *                                                                      *
 * Initialise the settings variables to default values in case the      *
 * settings config file is not found or corrupted. Many of these        *
 * variables are static and hence should be zeroed anyway, but not all. *
 * Better safe than sorry                                               *
 *                                                                      *
 ************************************************************************/

void init_default_settings (void)
{
  sync.newer = 0;
  sync.extra = 0;
  sync.overwrite = 0;
  sync.log = 0;
  sync.compare = 0;
  sync.timeresolution = 300;
  sync.tstamp_eq = 0;
  sync.multitask = 0;
  sync.copy_dir = 1;
  sync.new_log = 0;
  sync.num_old_logs = 5;
  sync.auto_open_log = 0;
  sync.num_fa = 10;
  sync.num_fa_resume = 8;
  sync.multiple_fa = 0;
  sync.ignore_filetype = 0;
  sync.save_log_in_primary = 0;
  sync.verbose_log = 0;
  sync.no_delete_ignored = 0;
  sync.use_zone_file = 0;
#ifdef KILL_TRAPDELETE
  sync.disable_trapdelete = 0;
#endif

#ifdef KILL_SPARKFS
  sync.kill_sparkfs = 0;
#endif

#ifdef KILL_TASKS
  sync.killtasks = 0;
  strcpy (sync.killtasks_filename, "<SyncDiscs$Dir>.Resources.Quit" );
#endif
  sd_pos.first_time = 1;
  settings_pos.first_time = 1;
////  shiftcontroldrag = 0;
  sync.verbose_fileractions = 0;
  return;
}




/************************************************************/

static char * time_units[] = { "centiseconds", "seconds", "minutes", "hours" };

static os_error *write_time_resolution (void)
{
  os_error * err;
  int        val;
  int        time_units_index;


  if (sync.timeresolution < 100)
  {
    time_units_index = 0;
    val = sync.timeresolution;
  }
  else
  {
    if (sync.timeresolution < 6000)
    {
      time_units_index = 1;
      val = (int)(sync.timeresolution / 100);
    }
    else
    {
      if (sync.timeresolution < 720000)
      {
        time_units_index = 2;
        val = (int)(sync.timeresolution / 6000);
      }
      else
      {
        time_units_index = 3;
        val = (int)(sync.timeresolution / 360000);
      }
    }
  }

  err = write_ind_icon_int_value ( settings_handle,
                                   SETTINGS_TSTAMP_TIME, val);
  if (!err)
    err = write_ind_icon_string ( settings_handle,
                                  SETTINGS_TSTAMP_UNITS,
                                  time_units[time_units_index]);

  return (err);
}




/************************************************************/

static os_error *set_time_resolution_value (int button, int icon)
{
  int         inc = 100;        /* cj: inc size in cs for
                                 * timeresolution */

  if ((button == 1 && icon == SETTINGS_TSTAMP_UP)
      || (button == 4 && icon == SETTINGS_TSTAMP_DOWN))
  {
    /* decreasing value */
    if (sync.timeresolution > 720000)
      inc = -360000;
    else if (sync.timeresolution > 360000)
      inc = -60000;
    else if (sync.timeresolution > 60000)
      inc = -30000;
    else if (sync.timeresolution > 6000)
      inc = -6000;
    else if (sync.timeresolution > 1000)
      inc = -500;
    else if (sync.timeresolution > 100)
      inc = -100;
    else
      inc = -10;
  }

  if ((button == 1 && icon == SETTINGS_TSTAMP_DOWN)
      || (button == 4 && icon == SETTINGS_TSTAMP_UP))
  {
    /* increasing value */
    if (sync.timeresolution < 100)            /* < 1sec, inc by 10cs */
      inc = 10;
    else if (sync.timeresolution < 1000)      /* < 10 sec, inc by 1 sec */
      inc = 100;
    else if (sync.timeresolution < 6000)      /* < 1 min, inc by 5 sec */
      inc = 500;
    else if (sync.timeresolution < 60000)     /* < 10 min, inc by 1 min */
      inc = 6000;
    else if (sync.timeresolution < 360000)    /* < 1 hr, inc by 5 min */
      inc = 30000;
    else if (sync.timeresolution < 720000)    /* < 2 hr, inc by 10 min */
      inc = 60000;
    else
      inc = 360000;                           /* inc by 1 hr */
  }

  sync.timeresolution += inc;
  /* Limit range to 10 cs to 12 hr */
  sync.timeresolution = CUT (sync.timeresolution, 10, 4320000);

  return (write_time_resolution ());
}




/************************************************************/

static os_error *settings_icon (int handle, int userhandle, wimp_mousestr * m)
{
  os_error   *err;
  int         inc = 1;

  err = NULL;

  if (m->bbits == 1)
    inc = -inc;           /* adjust button */

  switch (m->i)
  {
  case SETTINGS_MULTITASK:
    err = selectst (handle, SETTINGS_MULTITASK, sync.multitask ^= 1);
    break;

  case SETTINGS_COPYDIR:
    err = selectst (handle, SETTINGS_COPYDIR, sync.copy_dir ^= 1);
    break;

  case SETTINGS_NEWLOG:
    err = selectst (handle, SETTINGS_NEWLOG, sync.new_log ^= 1);
    shadeiconst (settings_handle, SETTINGS_LABEL_KEEP, !sync.new_log);
    shadeiconst (settings_handle, SETTINGS_LOG_KEEP, !sync.new_log);
    shadeiconst (settings_handle, SETTINGS_LOG_KEEP_UP, !sync.new_log);
    shadeiconst (settings_handle, SETTINGS_LOG_KEEP_DOWN, !sync.new_log);
    shadeiconst (settings_handle, SETTINGS_LABEL_COPIES, !sync.new_log);
    break;

  case SETTINGS_AUTOOPENLOG:
    err = selectst (handle, SETTINGS_AUTOOPENLOG, sync.auto_open_log ^= 1);
    break;

  case SETTINGS_LOG_KEEP_DOWN:
    /* simply invert inc and use the same code as for up bump */
    inc = -inc;
  case SETTINGS_LOG_KEEP_UP:
    sync.num_old_logs += inc;
    sync.num_old_logs = CUT (sync.num_old_logs, 0, 9);
    write_ind_icon_int_value (handle, SETTINGS_LOG_KEEP, sync.num_old_logs);
    break;

  case SETTINGS_TSTAMP_MAKE_EQUAL:
    err = selectst (handle, SETTINGS_TSTAMP_MAKE_EQUAL, sync.tstamp_eq ^= 1);
    shadeiconst (settings_handle, SETTINGS_TSTAMP_TIME, !sync.tstamp_eq);
    shadeiconst (settings_handle, SETTINGS_TSTAMP_DOWN, !sync.tstamp_eq);
    shadeiconst (settings_handle, SETTINGS_TSTAMP_UP, !sync.tstamp_eq);
    shadeiconst (settings_handle, SETTINGS_TSTAMP_UNITS, !sync.tstamp_eq);
    break;

  case SETTINGS_TSTAMP_DOWN:
    err = set_time_resolution_value (m->bbits, SETTINGS_TSTAMP_DOWN);
    break;

  case SETTINGS_TSTAMP_UP:
    err = set_time_resolution_value (m->bbits, SETTINGS_TSTAMP_UP);
    break;

  case SETTINGS_SAVE:
    err = config2save ();
    /* if Select click - close the window */
    if (m->bbits == 4) settings_close (0, 0);
    break;

  case SETTINGS_CLOSE:
    settings_close (0, 0);
    break;

  case SETTINGS_MULTIPLE_FA:
    err = selectst (handle, SETTINGS_MULTIPLE_FA, sync.multiple_fa ^= 1);
    shadeiconst (settings_handle, SETTINGS_MULTIPLE_FA_NUM, !sync.multiple_fa);
    shadeiconst (settings_handle, SETTINGS_MULTIPLE_FA_DOWN, !sync.multiple_fa);
    shadeiconst (settings_handle, SETTINGS_MULTIPLE_FA_UP, !sync.multiple_fa);
    break;

  case SETTINGS_SAVE_LOG_IN_PRI:
    err = selectst (handle, SETTINGS_SAVE_LOG_IN_PRI, sync.save_log_in_primary ^= 1);
    shadeiconst (mphandle, MP_LOGFILE_PATH, sync.save_log_in_primary);
    shadeiconst (mphandle, MP_LOGFILE_MENU, sync.save_log_in_primary);
    break;

  case SETTINGS_MULTIPLE_FA_DOWN:
    /* simply invert inc and use the same code as for up bump */
    inc = -inc;
  case SETTINGS_MULTIPLE_FA_UP:
    sync.num_fa += inc;
    sync.num_fa = CUT (sync.num_fa, 2, MAX_FILER_ACT);
    write_ind_icon_int_value (handle, SETTINGS_MULTIPLE_FA_NUM, sync.num_fa);
    if (sync.num_fa < 6)
      sync.num_fa_resume = sync.num_fa - 1;
    else
      sync.num_fa_resume = sync.num_fa - 2;
    break;

  case SETTINGS_IGNORE_FILETYPE:
    err = selectst (handle, SETTINGS_IGNORE_FILETYPE, sync.ignore_filetype ^= 1);
    break;

  case SETTINGS_VERBOSE_LOG:
    err = selectst (handle, SETTINGS_VERBOSE_LOG, sync.verbose_log ^= 1);
    break;

  case SETTINGS_VERBOSE_FA:
    err = selectst (handle, SETTINGS_VERBOSE_FA, sync.verbose_fileractions ^= 1);
    break;

#ifdef KILL_TRAPDELETE_ALLOW_SETTING
  case SETTINGS_DISABLE_TRAPDELETE:
    err = selectst (handle, SETTINGS_DISABLE_TRAPDELETE, sync.disable_trapdelete ^= 1);
    break;
#endif

#ifdef KILL_SPARKFS_ALLOW_SETTING
  case SETTINGS_DISABLE_SPARKFS:
    err = selectst (handle, SETTINGS_DISABLE_SPARKFS, sync.kill_sparkfs ^= 1);
    break;
#endif

  case SETTINGS_NO_DELETE_IGNORED:
    err = selectst (handle, SETTINGS_NO_DELETE_IGNORED, sync.no_delete_ignored ^= 1);
    break;

#if 0
  case SETTINGS_SHIFT_CTRL_DRAG:
    err = selectst (handle, SETTINGS_SHIFT_CTRL_DRAG, shiftcontroldrag ^= 1);
    break;
#endif

  }

  return (err);

  USE (userhandle);
}



/************************************************************/

static os_error *settings_close (int w, int userhandle)
{
  os_error   *err;

  wos_savewindposition (settings_handle, &settings_pos);

  remclickevent (settings_icon, settings_handle, 0);
  remcloseevent (settings_close, settings_handle, 0);
  remhelpevent (NULL, settings_handle, TSETTINGS);
  err = closedown (&settings_handle);

  return (err);

  USE (userhandle);
  USE (w);
}





/************************************************************/

/* Following instructions pulled out of open_settings() to allow
 * other functions to call the setup code.
*/

static void setup_settings (void)
{
  /* Make the settings window reflect the values of the main
   * syncsettings values.
   * We discard any returned errors.
  */
  selectst (settings_handle, SETTINGS_COPYDIR, sync.copy_dir);

  selectst (settings_handle, SETTINGS_NEWLOG, sync.new_log);
  write_ind_icon_int_value (settings_handle, SETTINGS_LOG_KEEP, sync.num_old_logs);
  shadeiconst (settings_handle, SETTINGS_LABEL_KEEP, !sync.new_log);
  shadeiconst (settings_handle, SETTINGS_LOG_KEEP, !sync.new_log);
  shadeiconst (settings_handle, SETTINGS_LOG_KEEP_UP, !sync.new_log);
  shadeiconst (settings_handle, SETTINGS_LOG_KEEP_DOWN, !sync.new_log);
  shadeiconst (settings_handle, SETTINGS_LABEL_COPIES, !sync.new_log);

  selectst (settings_handle, SETTINGS_AUTOOPENLOG, sync.auto_open_log);

  selectst (settings_handle, SETTINGS_TSTAMP_MAKE_EQUAL, sync.tstamp_eq);
  write_time_resolution ();
  shadeiconst (settings_handle, SETTINGS_TSTAMP_TIME, !sync.tstamp_eq);
  shadeiconst (settings_handle, SETTINGS_TSTAMP_DOWN, !sync.tstamp_eq);
  shadeiconst (settings_handle, SETTINGS_TSTAMP_UP, !sync.tstamp_eq);
  shadeiconst (settings_handle, SETTINGS_TSTAMP_UNITS, !sync.tstamp_eq);

  selectst (settings_handle, SETTINGS_MULTITASK, sync.multitask);
  selectst (settings_handle, SETTINGS_IGNORE_FILETYPE, sync.ignore_filetype);

  selectst (settings_handle, SETTINGS_MULTIPLE_FA, sync.multiple_fa);
  write_ind_icon_int_value (settings_handle, SETTINGS_MULTIPLE_FA_NUM, sync.num_fa);
  shadeiconst (settings_handle, SETTINGS_MULTIPLE_FA_NUM, !sync.multiple_fa);
  shadeiconst (settings_handle, SETTINGS_MULTIPLE_FA_DOWN, !sync.multiple_fa);
  shadeiconst (settings_handle, SETTINGS_MULTIPLE_FA_UP, !sync.multiple_fa);

  selectst (settings_handle, SETTINGS_SAVE_LOG_IN_PRI, sync.save_log_in_primary);
  selectst (settings_handle, SETTINGS_VERBOSE_FA, sync.verbose_fileractions);
  selectst (settings_handle, SETTINGS_VERBOSE_LOG, sync.verbose_log);
  selectst (settings_handle, SETTINGS_NO_DELETE_IGNORED, sync.no_delete_ignored);
#ifdef KILL_TRAPDELETE_ALLOW_SETTING
  selectst (settings_handle, SETTINGS_DISABLE_TRAPDELETE, sync.disable_trapdelete);
#endif

#ifdef KILL_SPARKFS_ALLOW_SETTING
  selectst (settings_handle, SETTINGS_DISABLE_SPARKFS, sync.kill_sparkfs);
#endif

#if 0
  selectst (settings_handle, SETTINGS_SHIFT_CTRL_DRAG, shiftcontroldrag);
#endif
  return;
}




/************************************************************/

os_error *open_settings (void)
{
  os_error   *err;

  if (settings_handle)
    err = forward (settings_handle, 0, NULL);
  else
  {
    err = createwindow (TSETTINGS, &settings_handle);
    if (!err)
    {
      addclickevent (settings_icon, settings_handle, 0);
      addcloseevent (settings_close, settings_handle, 0);
      addhelpevent (NULL, settings_handle, TSETTINGS);
      setup_settings ();

      /* Remember last position of settings
       * window when it is reopened. Only applies to current session.
       * If first open, then centre it
       */

      if (settings_pos.first_time)
      {
        popup (settings_handle, 0);
        settings_pos.first_time = 0;
      }
      else
      {
        open (settings_handle, settings_pos.x0, settings_pos.y0,
                               settings_pos.x1, settings_pos.y1, 0, 0, -1);
      }

    }
  }
  return (err);
}




/*********************************************************************
 *                                                                   *
 * loading and saving job files                                      *
 *                                                                   *
 * The following null event handler is to allow us to start off      *
 * a synchronisation process after loading or opening a job          *
 * file if a sync process is not already running. It is done this    *
 * way so the load can be acknowledged immediately before any null   *
 * polls are passed around. If it is not acknowledged immediately    *
 * the filer will 'run' the file and we will end up with two copies  *
 *                                                                   *
 *********************************************************************/

static void syncstart (int handle)
{
  remzeroevent (syncstart, 0);
  if (!sync_running) syncdiscs ();
  return;

  USE (handle);
}




/*************************************************************************
 *                                                                       *
 * The routines to load and save the job details are based unashamedly   *
 * on the TASKLIB configure code (xx2con.c), albeit much simplified      *
 * because we only allow int and string objects and only one table of    *
 * tags is used, so we do not need the linked list functionality.        *
 *                                                                       *
 ************************************************************************/


static synctag synctable[] =
{
  "Primary",        CONSTR, &syncjob.primary,         FILENAMELENGTH,
  "Secondary",      CONSTR, &syncjob.secondary,       FILENAMELENGTH,
  "Scrap",          CONSTR, &syncjob.scrap,           FILENAMELENGTH,
  "LogFile",        CONSTR, &syncjob.logfile,         FILENAMELENGTH,
  "ZoneFile",       CONSTR, &syncjob.zonefile,        FILENAMELENGTH,
  "Newer",          CONINT, &syncjob.newer,           0,
  "Extra",          CONINT, &syncjob.extra,           0,
  "Log",            CONINT, &syncjob.log,             0,
  "Overwrite",      CONINT, &syncjob.overwrite,       0,
  "MultiTask",      CONINT, &syncjob.multitask,       0,
  "NewLog",         CONINT, &syncjob.new_log,         0,
  "NumberOfLogs",   CONINT, &syncjob.num_old_logs,    0,
  "AutoOpenLog",    CONINT, &syncjob.auto_open_log,   0,
  "FastCopy",       CONINT, &syncjob.copy_dir,        0,
  "TimeEqual",      CONINT, &syncjob.tstamp_eq,       0,
  "TimeRes",        CONINT, &syncjob.timeresolution,  0,
  "MultipleFA",     CONINT, &syncjob.multiple_fa,     0,
  "MultipleFANum",  CONINT, &syncjob.num_fa,          0,
  "IgnoreFiletype", CONINT, &syncjob.ignore_filetype, 0,
  "Compare",        CONINT, &syncjob.compare,         0,
  "LogInPrimary",   CONINT, &syncjob.save_log_in_primary,   0,
  "VerboseLog",     CONINT, &syncjob.verbose_log,     0,
  "VerboseFA",      CONINT, &syncjob.verbose_fileractions, 0,
  "NoDeleteIgnored", CONINT, &syncjob.no_delete_ignored, 0,
  "UseZoneFile",    CONINT, &syncjob.use_zone_file, 0,
#ifdef KILL_TRAPDELETE
  "DisableTrapDelete",  CONINT, &syncjob.disable_trapdelete, 0,
#endif

#ifdef KILL_SPARKFS
  "DisableSparkFS",  CONINT, &syncjob.kill_sparkfs, 0,
#endif


  NULL, 0, NULL, 0
};


/************************************************************/

/* The following function is now 'generic' and is used to save
 * the config2 settings as well as saving a syncjob file
*/

static os_error *savejobdetails2 (buffer *pbf, char *save_id,  synctag *tag)
{
  os_error  *err;
  char     * p;

    err = stdfileheader(pbf, save_id);
    if (!err)
    {
      while (tag->name)
      {
        switch (tag->type)
        {
          case CONINT:
            err = bf_printf (pbf, "%s %d\n", tag->name, *((int*)tag->address));
            break;

          case CONSTR:
            p = (char*)tag->address;
            err = bf_printf (pbf, "%s %s\n", tag->name, p);
            break;

         }
         tag++;
         if(err) break;
      }

      if (!err) err=bf_printf(pbf, "\n");

    }

  return (err);
}



/************************************************************/

static os_error *savejobdetails (char *name, int type, synctag *tag)
{
  os_error  *err;
  buffer     bf;

  err = bf_open (name, 'w', DEFBUFFSIZE, &bf);
  if (!err) err = savejobdetails2 (&bf, "Syncjob", tag);
  err = bf_closec(&bf, err, name, type);
  /* remember the saved filename */
  strcpy (jobsave_filename, name);

  return (err);
}



/************************************************************/

/*
   jobsavedetails () will save a full set of settings as a syncdisc file in the
   directory to which the saveas icon is dragged or the path entered by hand.
   The job can then be run by double clicking or dragging to SyncDiscs
*/


static os_error *jobsavedetails (char *name, int type)
{
  os_error   *err;
  char       *ptr;

  /* take a copy of the current settings */
  syncjob = sync;
  /* get the current contents of the sync dbox writable icons */
  iconaddr (mphandle, MP_PRIMARY_PATH, &ptr);
  strcpy (syncjob.primary, ptr);
  iconaddr (mphandle, MP_SECONDARY_PATH, &ptr);
  strcpy (syncjob.secondary, ptr);
  iconaddr (mphandle, MP_SCRAP_PATH, &ptr);
  strcpy (syncjob.scrap, ptr);
  iconaddr (mphandle, MP_LOGFILE_PATH, &ptr);
  strcpy (syncjob.logfile, ptr);
  iconaddr (mphandle, MP_ZONEFILE_PATH, &ptr);
  strcpy (syncjob.zonefile, ptr);

  err = savejobdetails (name, type, synctable);

  return (err);
}





/************************************************************/

static os_error *openjobsave (void)
{
  os_error   *err;
  int         handle;

  err = NULL;

  err = setsave (jobsave_filename, SYNCDISCS_FILETYPE, jobsavedetails, &handle);
  if (!err) err = menuwindow_atpointer (handle);
  return (err);
}




/************************************************************/

/* a token is a non space, followed by a space or zero */

static char * getjobtok (char * string, int * len)
{
 int    tlen;
 char * p;
 char * q;

 p = string;
 while (*p == ' ') p++;
 q = p;

 /* the test *q!=':' added to make routine usable for load/save
  * config2 and make it backwardly compatible with the use of :
  * as end of token marker
 */

 while ((*q > ' ') && (*q != ':')) q++;

 tlen = q - p;
 if (*q)
 {
  *q = 0;
  tlen++;
 }

 *len = tlen;

 return (p);
}




/************************************************************/

static os_error * getjobtag (synctag * tag, char * p)
{
 os_error * err;

 err = NULL;

 switch (tag->type)
 {
  case CONINT:
    sscanf (p, "%d", (int*)tag->address);
    break;

  case CONSTR:
    if (strlen (p) < tag->len) strcpy ((char*)tag->address, p);
    break;
 }

 return (err);
}



/************************************************************/

static synctag * findjobtag (synctag *tags, char * name)
{
 synctag * tag;
 tag = tags;
 while (tag->name)
 {
  if (!strcmp (tag->name, name)) return (tag);
  tag++;
 }
 return(NULL);
}



/************************************************************/

os_error *jobload2 (char *name)
{
  os_error  *err;
  buffer     bf;
  char       string[256];
  int        len;
  char    *  p;
  synctag * tag;
  int       eof;


  /* pick up all the default settings */
  syncjob = sync;

  /* get the current contents of the writable icons */
  strcpy (syncjob.primary, contable[0].address);
  strcpy (syncjob.secondary, contable[1].address);
  strcpy (syncjob.scrap, contable[2].address);
  strcpy (syncjob.logfile, contable[3].address);
  strcpy (syncjob.zonefile, contable[4].address);
  /* ensure the 'compare' flag is unset */
  syncjob.compare = 0;

  /* Now get the settings from the syncjob file and overwrite the
   * default settings (this means the syncjob file needs only a
   * subset of all the settings, useful if produced manually)
  */
  err = bf_open(name, 'r', DEFBUFFSIZE, &bf);
  if (!err)
  {
    eof = 0;

    while (!eof)
    {
      err = bf_getstring (&bf, string, sizeof(string), &eof);
      if (err) break;

      p = getjobtok (string, &len);
      if (len)
      {
        if (strcmp (p, "//"))
        {
          tag = findjobtag (synctable, p);
          if (tag != NULL) getjobtag(tag, p+len);
        }
      }
    }
    err = bf_close (&bf, err);
    /* we need to recalculate the filer action resume value in case the num_fa
       is different to the default */
    if (syncjob.num_fa < 6)
      syncjob.num_fa_resume = syncjob.num_fa - 1;
    else
      syncjob.num_fa_resume = syncjob.num_fa - 2;

    if (!err)
    {
      if (isctrl)
      {
        /********************************************************************
         *                                                                  *
         * If the control key is held down when dragging/double clicking    *
         * then we just load the new values into the syncdiscs control      *
         * window or settings structure and do not add the job to the       *
         * queue. The settings can then be edited as required, and the      *
         * sync process started manually.                                   *
         *                                                                  *
         * Transfer all the settings to the main syncsettings structure.    *
         * If the syncdisc control window is open, we need to change all    *
         * settings to reflect the new values. Same applies for the         *
         * settings window if open. If it is not, then the new settings     *
         * will be used when it is next opened.                             *
         *                                                                  *
         ********************************************************************/

        sync = syncjob;
        if (settings_handle) setup_settings ();
        if (mphandle)
        {
          setup_syncdiscs_buttons ();

          /* now deal with the writable icons */
          err = writeicon (mphandle, MP_PRIMARY_PATH, sync.primary);
          if (!err) err = writeicon (mphandle, MP_SECONDARY_PATH, sync.secondary);
          if (!err) err = writeicon (mphandle, MP_SCRAP_PATH, sync.scrap);
          if (!err) err = writeicon (mphandle, MP_LOGFILE_PATH, sync.logfile);
          if (!err) err = writeicon (mphandle, MP_ZONEFILE_PATH, sync.zonefile);
        }
        else
        {
          /* just write the new paths back into the icon blocks */
          strcpy (contable[0].address, syncjob.primary);
          strcpy (contable[1].address, syncjob.secondary);
          strcpy (contable[2].address, syncjob.scrap);
          strcpy (contable[3].address, syncjob.logfile);
          strcpy (contable[4].address, syncjob.zonefile);

          /* If we are not syncing, and the control window is not open,
           * then we open it ready for the user to edit/launch/whatever
          */

          if (!sync_running)
          {
            err = opensyncdiscs ();
          }
        }
        /* Remember the filepath so the user can easily save the edited
         * file back to where it came from.
        */
        strcpy (jobsave_filename, name);
      }
      else
      {
        /* Control was not pressed, so we add the settings to the queue */
        /* First check that max number of queued jobs will not be exceded */

        if (jobs.number < JOBLIST_MAX)
        {
          /* claim some storage memory */

          err = flex_alloc ((flex_ptr) &jobs.settings[jobs.number],sizeof(syncsettings));
          if (err) return (NULL);

          /* now add the job to the queue */

          *jobs.settings[jobs.number] = syncjob;

          jobs.number++;
          progress_update_job_count_field ();
          /********************************************************************
           *                                                                  *
           * If a sync process is currently running then the new job          *
           * will be run as the queue is processed. If we are not doing       *
           * a sync at the moment, we need to start the process off.          *
           * We cannot just start it here because we must return to the       *
           * calling function in the poll library to acknowledge the file     *
           * open message as soon as possible. (If the data open message is   *
           * not acknowledged in good time, the wimp will then filer_run      *
           * the file, and we end up with the same job in the queue twice!).  *
           * Therefore we register a null event handler to set off the sync   *
           * process once the message interchange has completed.              *
           *                                                                  *
           ********************************************************************/

          if (!sync_running)
          {
            addzeroevent (syncstart, 0);
          }

          /* Is the file being loaded a transfer file created by a second
           * instance of SyncDiscs? If so (since other syncjob files are
           * unlikely to be located within the app), just delete it again.
          */
          if (!cstrcmp (name, JOB_TRANSFER_FILENAME)) fs_wipe (name);

        }
        else
        {
          make_errlog_entry (200, "Syncjob (%s) ignored - queue is full\n", name);
        }
      }
    }
  }
  return (err);
}



/************************************************************/

os_error *jobloadtype (int type, mousestr * m, int userhandle, int *method)
{
  if (*method == 0 && (type == SYNCDISCS_FILETYPE)) *method = 1;
  return (NULL);

  USE (m);
  USE (userhandle);

}



/************************************************************/

os_error *jobload (char *name, int type, int userhandle, int xvolatile)
{
  return (jobload2 (name));

  USE (type);
  USE (userhandle);
  USE (xvolatile);
}


/************************************************************/

static os_error *jobopen (char *name, int type,
                          int userhandle, int xvolatile)
{
  return (jobload2 (name));

  USE (type);
  USE (userhandle);
  USE (xvolatile);
}


/************************************************************/

/* Initialisation of functions to load syncdisc job files.
 * A drag to the syncdisc control window is covered by changes
 * to syncdiscsload() & syncdiscsloadtype()
 * Drags to the progress window are registered and deregistered
 * when the window is created / destroyed
 */

void jobloadinit (void)
{
  /* A drag to the iconbar icon */
  adddataload (-2, iconbaricon, jobload);
  adddataloadtype (-2, iconbaricon, jobloadtype);
  /* a double click in a filer window */
  adddataopen (SYNCDISCS_FILETYPE, jobopen);
  jobs.number = 0;
  jobs.settings[0] = NULL;
  strcpy (jobsave_filename, "Syncjob");
  return;
}





/*************  End of c.dbox  ***********************/

