RCS_ID_C="$Id: napsaprefs.c,v 3.9 1994/05/18 15:09:54 ppessi Exp $";
/*
 * napsaprefs.c --- preferences for napsaterm
 *
 * Author: ppessi <Pekka.Pessi@hut.fi>
 *
 * Copyright (c) 1993 Pekka Pessi
 *
 * Created      : Sun Nov  7 08:27:25 1993 ppessi
 * Last modified: Wed May 18 18:08:55 1994 ppessi
 *
 * $Log: napsaprefs.c,v $
 * Revision 3.9  1994/05/18  15:09:54  ppessi
 * Changed default ctrl8bit tool type to ctrl8bit=no
 *
 * Revision 3.8  1994/05/14  12:49:00  ppessi
 * Multicharacter options are now preceded with double hyphen.
 * Added "Control Codes" (7/8 bit) entry into "Setup" menu.
 *
 * Revision 3.7  1994/05/11  12:53:42  ppessi
 * Merged changes from Napsaterm 3.5 by R. Knop
 *
 * Revision 3.6  1994/02/25  16:08:03  ppessi
 * Fixed a bug in the national setting (only Danish and US could be selected)
 *
 * Revision 3.5  1994/02/25  02:33:38  ppessi
 * Added RCS ID
 *
 * Revision 3.4  1994/02/25  02:00:48  ppessi
 * Reverted back to S:Napsaprefs preference file
 *
 * Revision 3.3  1994/01/16  18:21:15  ppessi
 * No perrorparse() printouts
 *
 * Revision 3.2  94/01/08  08:54:52  ppessi
 * Enabled switch tool types; added support to different program names
 * 
 * Revision 3.1  94/01/07  22:51:25  ppessi
 * Version 3 beta
 * 
 * Revision 2.1  93/11/18  18:34:12  ppessi
 * Changed menu format.
 * 
 * Revision 2.0  93/11/15  03:38:53  ppessi
 * Version 2 initial revision
 */


#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include "nifty.h"
#include "amiga.h"
#include "napsaprefs.h"
#include "display.h"
#include "dispmacros.h"
#include "wimp.h"

#include <workbench/workbench.h>
#include <workbench/startup.h>

#if USE_PRAGMAS
#include <proto/icon.h>
#endif
#if USE_INLINE
#include <inline/icon.h>
#endif
#if USE_CLIB
#include <clib/icon_protos.h>
#endif

#define PREFSFILE \
"AmiTCP:db/NapsaPrefs" NUL "s:NapsaPrefs" NUL "s:NiftyPrefs" NUL

/*
 * Hosts to connect
 */
#define MAXHOSTS 63		/* you can specify only 63 hosts */
int  hostc;
char *hostarray[MAXHOSTS + 1];

static void addhost(char *newhost, void *dummy);

struct napsaprefs np;  

struct parsing {
  char     p_type;
  char     p_slen;		/* significant length */
  ushort   p_offset;
  union {
    const char *pu_option;
    const char *pu_values;
    int       (*pu_func)(char *, void *);
  } p;
  char    *p_resource;
  long     p_default;
};

#define p_option p.pu_option
#define p_values p.pu_values
#define p_func   p.pu_func

#define T_END     0
#define T_STRING  1
#define T_FLAG    2		/* true/false */
#define T_TOGGLE  3		/* option will toggle the default */
#define T_NUMBER  4
#define T_SPECIAL 5
#define T_ENUM    6

#define OFFSET_OF(x) ((ushort)&((struct napsaprefs *)NULL)->x)

#define LPARSE(type, what, optnam, resource, default) \
{type,2,OFFSET_OF(what),optnam,resource,(long)default}

#define PARSE(type, what, optnam, resource, default) \
{type,sizeof(optnam)-1,OFFSET_OF(what),optnam,resource,(long)default}

#define PENUM(what, resource, values, default, len) \
{T_ENUM,len,OFFSET_OF(what),values,resource,(long)default}

#define PRESET(type, what, resource, default) \
{type,0,OFFSET_OF(what),NULL,resource,(long)default}

#define PSPECIAL(what, resource, function) \
{T_SPECIAL, 0, OFFSET_OF(what), (void *)function, resource, 0}

#define NP_ADDR(offset) (void*)((char*)&np + offset)

extern char nation_names[];

static char national_names[] = 
  "None" NUL "Multinational" NUL "National" NUL;

static char bell_names[] = 
  "None" NUL "Visual" NUL "Audio" NUL "Both" NUL "Display" NUL;

static char cursor_names[] =
  "Underlined" NUL "Block" NUL "Invisible" NUL;

static char sizing_names[] = 
  "None" NUL "Column" NUL "Row" NUL;

static char emulation_names[] =
  "vt102" NUL "vt52" NUL "h19" NUL;

static char mouse_names[] =
  "Off" NUL "Down" NUL "Up" NUL "Both" NUL;

/* Added RKNOP 940328 */
static char keypad_names[] =
  "Numeric" NUL "Application" NUL;
/* End added RKNOP */

static const struct parsing presets[] =
{
  PARSE(T_STRING, logname,  "l",   NULL, NULL),
  PARSE(T_FLAG,   show_version, "V", NULL, 1),
  PARSE(T_TOGGLE, wait_to_end,  "w", "WAITTOEND", 0),
  
  /* emulation mode */
  PENUM(emulation, "EMULATION", emulation_names, EMU_VT102, 3), 
  PARSE(T_FLAG, emulation, "-vt102", NULL, EMU_VT102), 
  PARSE(T_FLAG, emulation, "-vt52",  NULL, EMU_VT52), 
  PARSE(T_FLAG, emulation, "-h19",   NULL, EMU_H19),

  PENUM(national,  "NATIONAL", national_names, 0, 2),
  /* Default is US (plain ASCII) */
  PENUM(nation, "NATION", nation_names, 0, 2),
  PSPECIAL(keymap, "KEYMAP", parsekeymap),
  PSPECIAL(keymap, "KEYBOARD", parsekeymap),
#if 0
  PRESET(T_STRING, nation, "NATION", NULL),
  PRESET(T_STRING, keymap, "KEYMAP", NULL),
  PRESET(T_STRING, keymap, "KEYBOARD", NULL),
#endif

  LPARSE(T_TOGGLE, slow,   "-slow",   NULL, 0),

  /*Added RKNOP 940328*/
  /* Keypad mode */
  PENUM(applkeypad, "KEYPAD", keypad_names, KEYP_NUMERIC, 2),
  PARSE(T_FLAG, applkeypad, "-numeric", NULL, KEYP_NUMERIC),
  PARSE(T_FLAG, applkeypad, "-application", NULL, KEYP_APPL),
  /*end added RKNOP 940328*/

  /* Cursor style, see "dispmacros.h" */
  PENUM(visual, "CURSOR", cursor_names, CUR_BLOCK, 1),
  PARSE(T_FLAG, visual, "-ic",   NULL, 2),

  /* stripping */
  PARSE(T_TOGGLE, pass8,   "7",  "PASS8",        1),
  PRESET(T_FLAG,   altismeta, "ALTISMETA", 0),
  PARSE(T_TOGGLE, invert,   "v",  "INVERSE", 0),

  PENUM(mouse, "MOUSE", mouse_names, 0, 0),
  PRESET(T_FLAG,  backspace, "BACKSPACE2DELETE", 0),
  PRESET(T_FLAG,  delete,    "DELETE2BACKSPACE", 0),
  PRESET(T_FLAG,  ctrl8bit,  "CTRL8BIT", 0),
  PRESET(T_FLAG,  blink,     "CURSORBLINK", 0),

  PARSE(T_STRING, device,   "d",  "DEVICE", "serial.device"),
  PARSE(T_NUMBER, unit,     "u",  "UNIT", 0),
  PARSE(T_NUMBER, dnetwork, "N",   NULL, 0),
  PARSE(T_NUMBER, bps,      "B",  "LINESPEED", 38400),
  LPARSE(T_FLAG,  shared, "-shared", NULL, 1),
  LPARSE(T_FLAG,  stdio,  "-stdio",  NULL, 1),
  /* The telnet server port */
  PARSE(T_STRING, service,  "s",  "SERVICE", "telnet"),
  /* LPARSE(T_FLAG,  telnet,  "telnet",  NULL, 1), */

  /* This won't change the prefs structure */
  PSPECIAL(emulation, "HOST", addhost),
  PARSE(T_STRING, remotename, "r", "REMOTENAME", NULL),
  PRESET(T_STRING, remoteterm, "REMOTETYPE", NULL),

  /* bell type */
  PENUM(bell_type, "BELL", bell_names, BELL_VISUAL, 1),

  PARSE(T_TOGGLE, col80, "-80", "FIXEDCOLUMNS", 0),

  /* sizing gadget */
  PENUM(size_gadget, "SIZEGADGET", sizing_names, SIZE_COLUMN, 1),
  PARSE(T_STRING, title,    "p",  "TITLE", NULL),
  PARSE(T_STRING, geometry, "g",  "GEOMETRY", NULL),
  PARSE(T_STRING, pubscname,"S",  "PUBSCREENNAME", NULL),
  PARSE(T_STRING, basefont, "f",  "BASEFONT", DEFAULTFONTDEF),

  {T_END}
};

/*
 * show options 
 */
static void usage(void)
{
/*  puts(version); */
/*  puts(copyright); */
  PutStr("Usage: " PROGNAME " [-option ...]\n"
	 "Available " PROGNAME " options are:\n"
	 "  -V                 show version number & copyright notice\n"
	 "  --h19              emulate an h19 instead of a vt102 terminal\n"
	 "  --vt52             emulate a vt52 on startup\n"
	 "  --vt102            emulate a vt102 on startup (default emulation)\n"
	 "  -l <file>          write all output to a log file\n"
	 "  --slow             start up in slow mode\n"
	 "  --ic               start up with invisible cursor\n"
	 "  --numeric          use numeric keypad\n"
	 "  --application      use application keypad\n"
	 "  -w                 wait after program ends before closing window\n"
	 "  -d <devicename>    specify device to use (e.g. serial.device)\n"
	 "  -u <unit #>        specify unit number to use (see -d)\n"
	 "  -B <linespeed>     specify line speed to use\n"
	 "  --shared           open device in shared mode\n"
	 "  --stdio            take input from stardard input.\n"
	 "  -N <net>           specify DNet network number\n"
	 "  -s <service>       specify TCP service name\n"
	 "  -r <username>      specify remote user name to use\n"
	 "  -7                 strip 8th bit of character codes\n"
	 "  -S <screen>        specify public screen name\n"
	 "  --80                set to fixed 80 columns\n"
	 "  -f <font>/<size>   set the name and size of the default font\n"
	 "  -p <name>          set the program name\n"
	 "  -g <l>/<t>/<w>/<h> set up window geometry.");
  exit(1);
}

/*
 * Set default values 
 */
static void preset(void)
{
  const struct parsing *p;

  hostc = 0;

  for (p = presets; p->p_type != T_END; p++) {
    if (p->p_resource) {
      void *what = NP_ADDR(p->p_offset);
      switch (p->p_type) {
      case T_STRING:
      case T_NUMBER:
	*(long*)what = p->p_default;
	break;
      case T_FLAG:
      case T_TOGGLE:
      case T_ENUM:
	*(char*)what = p->p_default;
	break;
      }
    }
  }
}

/*
 * `secure' strdup that removes the LF
 */
static char *pstrdup(char *s)
{
  char *val;
  int size = 0;

  while (s[size] && s[size] != '\n')
    size++;

  val = malloc(size + 1);
  if (!val) {
    fatalError(MEMORY_ERROR_MSG);
  }
  memcpy(val, s, size);
  val[size] = '\0';

  return val;
}

/*
 * Add a new host to hosts structure 
 */
static void addhost(char *newhost, void *dummy) 
{
  if (hostc < MAXHOSTS) {
    hostarray[hostc++] = newhost;
    while (*newhost) {
      if (*newhost++ == '|') {
	newhost[-1] = '\0';
	if (hostc < MAXHOSTS) {
	  hostarray[hostc++] = newhost;
	} else {
	  break; 
	}
      }
    }
  } else {
    free(newhost);
  }
}

/* 
 * Parse a preset entry from icon or from preset file
 */
static void parse_entry(const char *preset, char *value)
{
  const struct parsing *p;

  for (p = presets; p->p_type != T_END; p++) {
    if (p->p_resource == NULL || strcmp(p->p_resource, preset))
      continue;
    switch (p->p_type) {

    case T_STRING:
      *(char**)NP_ADDR(p->p_offset) = pstrdup(value);
      break;

    case T_NUMBER:
      if (StrToLong(value, (long*)NP_ADDR(p->p_offset)) <= 0) {
	perrorparse("Expected number for option %s", p->p_resource);
      }
      break;

    case T_FLAG:
    case T_TOGGLE:
      if (*value == '0' || 
	  *value == 'n' || 
	  (*value == 'o' && value[1] == 'f') || 
	  *value == 'f')
	*(char*)NP_ADDR(p->p_offset) = 0;
      else 
	if (*value == '\0' ||
	    *value == '1' || 
	    *value == 'y' ||
	    (*value == 'o' && value[1] == 'n') || 
	    *value == 't')
	  *(char*)NP_ADDR(p->p_offset) = 1;
      break;

    case T_SPECIAL:
      /* Call the special function */
      p->p_func(pstrdup(value), NP_ADDR(p->p_offset));
      break;

    case T_ENUM:
      {
	/*
	 * Find which token is given 
	 */
	int e;
	const char *t = p->p_values;
	char *n = value;

	/* NUL-terminate */
	while (*n && *n != '\n')
	  n++;
	*n = '\0';

	for (e = 0; *t; e++) {
	  if (p->p_slen ? 
	      !Strnicmp(t, value, p->p_slen) : 
	      !Stricmp(t, value)) {
	    /* Found */
	    *(char*)NP_ADDR(p->p_offset) = e;
	    break;
	  }
	  while (*t++)
	    ;
	}
	if (!*t)
	  perrorparse("Illegal value %s for option %s", value, p->p_resource);
      }
      break;
    }
    break; /* for () */
  }
  if (p->p_type == T_END) {
    perrorparse("Unknown option %s", preset);
  }
}

/*
 * Parse preset file
 */
static void parsepreset(char *myname, char *filename)
{
  char *s, *preset, buf[256];
  BPTR fh;

  while (*filename) {
    if (fh = Open(filename, MODE_OLDFILE))
      break;
    filename += strlen(filename) + 1;
  }
  if (!*filename) 
    return;

  buf[255] = '\0';

  while (FGets(fh, buf, sizeof(buf)-1)) {
    /* Comment? */
    if (!isalnum(buf[0])) 
      continue;
    for (preset = s = buf; *s; s++) {
      if (*s == ':')
	break;
      else if (islower(*s)) 
	*s += 'A' - 'a';
      else if (*s == '.') {
	preset = s + 1;
	/* Is the program name correct? */
	if (strncmp(myname, buf, s - buf))
	  goto not_for_me;
      }
    }
    if (*s) {
      *s++ = '\0';
      while (isspace(*s)) 
	s++;
      parse_entry(preset, s);
    }
  not_for_me:
    ;
  }

  Close(fh);
  return;
}

char **parseargs(char **argv)
{
  char *cont;
  const struct parsing *p;
  char progname[128];

  strncpy(progname, FilePart(argv[0]), sizeof(progname));
  for (cont = progname; *cont; cont++) {
    if (islower(*cont))
      *cont += 'A' - 'a';
  }

  preset();
  parsepreset(progname, PREFSFILE); 

  for (argv = argv + 1; *argv != NULL; argv++) {
    if (**argv == '-') {
      /* '-' ends options */
      if ((*argv)[1] == '\0')
	return argv + 1;
      cont = *argv + 1;
      for (p = presets; p->p_type != T_END; p++) {
	if (p->p_type < T_SPECIAL && 
	    p->p_option && 
	    !strncmp(p->p_option, cont, p->p_slen)) {
	  void *what = NP_ADDR(p->p_offset);
	  switch (p->p_type) {
	  case T_STRING:
	  case T_NUMBER:
	    cont = *++cont ? cont : *(++argv);
	    if (!cont) usage();
	    if (p->p_type == T_NUMBER) 
	      StrToLong(cont, (long *)what);
	    else
	      *(char **)what = cont;
	    break;
	  case T_FLAG:
	    *(char *)what = p->p_default;
	    break;
	  case T_TOGGLE:
	    *(char *)what = !p->p_default;
	    break;
	  }
	  break;
	}
      }
      /* Didn't found the option? */
      if (p->p_type == T_END) usage();
    } else {
      break;
    }
  }

  return argv;
}

/*
 * Workbench setup
 */
static void parseicon(UBYTE *name)
{
  struct DiskObject *dobj;
  char **toolarray;
  char *s, buf[256];

  if (!name) 
    return;

  if (dobj = GetDiskObjectNew(name)) {
    for (toolarray = (char **)dobj->do_ToolTypes;
	 toolarray && *toolarray;
	 toolarray++) {
      s = strncpy(buf, *toolarray, sizeof(buf));
      buf[sizeof(buf) - 1] = '\0';
      while (*s && *s != '=')
	s++;
      if (*s)
	*s++ = '\0';
      parse_entry(buf, s);
    }

    /* Free the diskobject we got */
    FreeDiskObject(dobj);
  } else {
    addhost(pstrdup(name), NULL);
  }
}

char **parsewbargs(struct WBStartup *argmsg)
{
  LONG i;
  struct WBArg *wb_arg_list = argmsg->sm_ArgList;

  preset();

  for (i = 0; i < argmsg->sm_NumArgs; i++, wb_arg_list++) {
    if (wb_arg_list->wa_Lock) {
      /* change to the proper directory */
      BPTR olddir = CurrentDir(wb_arg_list->wa_Lock) ;

      /* process the file */
      parseicon(wb_arg_list->wa_Name);
      /* change back to the original directory when done */
      CurrentDir(olddir) ;
    } else {
      /* something that does not support locks */
      parseicon(wb_arg_list->wa_Name);
    }
  }

  hostarray[hostc] = NULL;

  return hostarray;
}

#include <stdarg.h>

#if 0
/*
 * Print parse error
 */
void perrorparse(char *fmt, ...)
{
  va_list va;

  va_start(va, fmt);
  vfprintf(stderr,fmt, va);
  va_end(va);
  fputc('\n', stderr);
}
#else
void perrorparse(char *fmt, ...)
{
}
#endif

/*
 * Setup menu items show their current value
 */
#define SETUP_ITEMS 15

struct setup_menu_item setup_items[SETUP_ITEMS + 1] = 
{
  {
    OFFSET_OF(national), 3,
    "National Mode",
    national_names,
    change_national
  },
  {
    OFFSET_OF(nation), 8,
    "Nation",
    nation_names,
    change_nation
  },
  {
    OFFSET_OF(emulation), 3,
    "Terminal Type",
    emulation_names,
    NULL
  },    
  {
    OFFSET_OF(ctrl8bit), 2, 
    "Control Codes",
    "7 bit" NUL "8 bit" NUL,
    NULL
  },
  {
    OFFSET_OF(backspace), 2, 
    "Backspace is sent as",
    "Backspace" NUL "Delete" NUL,
    NULL
  },
  {
    OFFSET_OF(delete), 2, 
    "Del is sent as",
    "Delete" NUL "Backspace" NUL,
    NULL
  },
  {
    OFFSET_OF(altismeta), 2, 
    "Left Alt key is ",
    "Alt" NUL "Meta" NUL,
    NULL
  },
  {
    /* Added RKNOP 940328 */
    OFFSET_OF(applkeypad), 2,
    "Keypad",
    "Numeric" NUL "Application" NUL,
    NULL
  },
  {
    OFFSET_OF(mouse), 4,
    "Mouse Events",
    mouse_names,
    NULL
  },
  {
    OFFSET_OF(visual), 3,
    "Cursor",
    cursor_names,
    change_cursor
  },
  {
    OFFSET_OF(col80), 2,
    "Display Width",
    "Variable" NUL "80 Columns"  NUL,
    change_mode80
  },
  {
    OFFSET_OF(slow), 2, 
    "Display Speed",
    "Fast" NUL "Slow" NUL,
    NULL
  },
  {
    OFFSET_OF(bell_type), 5, 
    "Bell Type",
    bell_names,
    NULL
  },
  {
    OFFSET_OF(invert), 2,
    "Reverse",
    NULL,
    change_invert
  },
/* added RKNOP 940328 */
  {
    OFFSET_OF(ansi_LNM), 2,
    "ansi_LNM",
    NULL,
    change_ansi_LNM
  },
/* end added RKNOP 940328 */
  { 0 }
};

int getsetup(unsigned int n)
{
  return ((UBYTE*)&np)[setup_items[n].offset];
}

/*
 * Change setup menu item n, return new value 
 *
 * If new value is illegal, won't call toggle function
 */
int changesetup(unsigned int n, int val)
{
  UBYTE *vp = ((UBYTE*)&np) + setup_items[n].offset;

  if (n >= SETUP_ITEMS)
    return -1;

  if (val >= 0 && val < setup_items[n].choices && *vp != val) {
    *vp = val;
    if (setup_items[n].change_f)
      setup_items[n].change_f(val);
  }

  return *vp;
}

#if 0
int
main(int argc, char **argv)
{
  char **argv2 = parseargs(argv + 1);

  printpresets();

  if (*argv2) {
    puts(*argv2);
  }

  return 0;
}
#endif
