Post-release destabilisation! Completely remove the struct type
authorsimon <simon@cda61777-01e9-0310-a592-d414129be87e>
Thu, 14 Jul 2011 18:52:21 +0000 (18:52 +0000)
committersimon <simon@cda61777-01e9-0310-a592-d414129be87e>
Thu, 14 Jul 2011 18:52:21 +0000 (18:52 +0000)
'Config' in putty.h, which stores all PuTTY's settings and includes an
arbitrary length limit on every single one of those settings which is
stored in string form. In place of it is 'Conf', an opaque data type
everywhere outside the new file conf.c, which stores a list of (key,
value) pairs in which every key contains an integer identifying a
configuration setting, and for some of those integers the key also
contains extra parts (so that, for instance, CONF_environmt is a
string-to-string mapping). Everywhere that a Config was previously
used, a Conf is now; everywhere there was a Config structure copy,
conf_copy() is called; every lookup, adjustment, load and save
operation on a Config has been rewritten; and there's a mechanism for
serialising a Conf into a binary blob and back for use with Duplicate
Session.

User-visible effects of this change _should_ be minimal, though I
don't doubt I've introduced one or two bugs here and there which will
eventually be found. The _intended_ visible effects of this change are
that all arbitrary limits on configuration strings and lists (e.g.
limit on number of port forwardings) should now disappear; that list
boxes in the configuration will now be displayed in a sorted order
rather than the arbitrary order in which they were added to the list
(since the underlying data structure is now a sorted tree234 rather
than an ad-hoc comma-separated string); and one more specific change,
which is that local and dynamic port forwardings on the same port
number are now mutually exclusive in the configuration (putting 'D' in
the key rather than the value was a mistake in the first place).

One other reorganisation as a result of this is that I've moved all
the dialog.c standard handlers (dlg_stdeditbox_handler and friends)
out into config.c, because I can't really justify calling them generic
any more. When they took a pointer to an arbitrary structure type and
the offset of a field within that structure, they were independent of
whether that structure was a Config or something completely different,
but now they really do expect to talk to a Conf, which can _only_ be
used for PuTTY configuration, so I've renamed them all things like
conf_editbox_handler and moved them out of the nominally independent
dialog-box management module into the PuTTY-specific config.c.

git-svn-id: svn://svn.tartarus.org/sgt/putty@9214 cda61777-01e9-0310-a592-d414129be87e

64 files changed:
Recipe
cmdline.c
conf.c [new file with mode: 0644]
config.c
cproxy.c
dialog.c
dialog.h
ldisc.c
ldisc.h
logging.c
macosx/README.OSX
misc.c
network.h
pinger.c
portfwd.c
pproxy.c
proxy.c
proxy.h
pscp.c
psftp.c
putty.h
raw.c
rlogin.c
sercfg.c
settings.c
ssh.c
ssh.h
sshgss.h
sshnogss.c
storage.h
telnet.c
terminal.c
terminal.h
testback.c
unix/gtkcfg.c
unix/gtkdlg.c
unix/gtkwin.c
unix/unix.h
unix/ux_x11.c
unix/uxcfg.c
unix/uxgss.c
unix/uxplink.c
unix/uxproxy.c
unix/uxpterm.c
unix/uxpty.c
unix/uxputty.c
unix/uxser.c
unix/uxsftp.c
unix/uxstore.c
unix/uxucs.c
windows/wincfg.c
windows/winctrls.c
windows/windlg.c
windows/window.c
windows/wingss.c
windows/winplink.c
windows/winproxy.c
windows/winser.c
windows/winsftp.c
windows/winstore.c
windows/winstuff.h
windows/winucs.c
windows/winx11.c
x11fwd.c

diff --git a/Recipe b/Recipe
index 1a34bb8..698d42d 100644 (file)
--- a/Recipe
+++ b/Recipe
@@ -245,7 +245,7 @@ CFLAGS += -DWINVER=0x0500 -D_WIN32_WINDOWS=0x0410 -D_WIN32_WINNT=0x0500
 
 # Terminal emulator and its (platform-independent) dependencies.
 TERMINAL = terminal wcwidth ldiscucs logging tree234 minibidi
-         + config dialog
+         + config dialog conf
 
 # GUI front end and terminal emulator (putty, puttytel).
 GUITERM  = TERMINAL window windlg winctrls sizetip winucs winprint
@@ -272,7 +272,7 @@ SFTP     = sftp int64 logging
 
 # Miscellaneous objects appearing in all the network utilities (not
 # Pageant or PuTTYgen).
-MISC     = timing misc version settings tree234 proxy
+MISC     = timing misc version settings tree234 proxy conf
 WINMISC  = MISC winstore winnet winhandl cmdline windefs winmisc winproxy
          + wintime
 UXMISC   = MISC uxstore uxsel uxnet cmdline uxmisc uxproxy time
@@ -315,12 +315,12 @@ psftp    : [C] psftp winsftp wincons WINSSH BE_SSH SFTP wildcard WINMISC
 
 pageant  : [G] winpgnt sshrsa sshpubk sshdes sshbn sshmd5 version tree234
          + misc sshaes sshsha winpgntc sshdss sshsh256 sshsh512 winutils
-         + winmisc winhelp pageant.res LIBS
+         + winmisc winhelp conf pageant.res LIBS
 
 puttygen : [G] winpgen sshrsag sshdssg sshprime sshdes sshbn sshmd5 version
          + sshrand winnoise sshsha winstore misc winctrls sshrsa sshdss winmisc
          + sshpubk sshaes sshsh256 sshsh512 import winutils puttygen.res
-        + tree234 notiming winhelp winnojmp LIBS wintime
+        + tree234 notiming winhelp winnojmp conf LIBS wintime
 
 pterm    : [X] GTKTERM uxmisc misc ldisc settings uxpty uxsel BE_NONE uxstore
          + uxsignal CHARSET cmdline uxpterm version time xpmpterm xpmptcfg
@@ -338,7 +338,7 @@ plink    : [U] uxplink uxcons NONSSH UXSSH U_BE_ALL logging UXMISC uxsignal
 puttygen : [U] cmdgen sshrsag sshdssg sshprime sshdes sshbn sshmd5 version
          + sshrand uxnoise sshsha misc sshrsa sshdss uxcons uxstore uxmisc
          + sshpubk sshaes sshsh256 sshsh512 import puttygen.res time tree234
-        + uxgen notiming
+        + uxgen notiming conf
 
 pscp     : [U] pscp uxsftp uxcons UXSSH BE_SSH SFTP wildcard UXMISC
 psftp    : [U] psftp uxsftp uxcons UXSSH BE_SSH SFTP wildcard UXMISC
index bfb2b42..acd3c66 100644 (file)
--- a/cmdline.c
+++ b/cmdline.c
@@ -162,7 +162,7 @@ static int cmdline_check_unavailable(int flag, char *p)
     if (need_save < 0) return x; \
 } while (0)
 
-int cmdline_process_param(char *p, char *value, int need_save, Config *cfg)
+int cmdline_process_param(char *p, char *value, int need_save, Conf *conf)
 {
     int ret = 0;
 
@@ -170,7 +170,7 @@ int cmdline_process_param(char *p, char *value, int need_save, Config *cfg)
        RETURN(2);
        /* This parameter must be processed immediately rather than being
         * saved. */
-       do_defaults(value, cfg);
+       do_defaults(value, conf);
        loaded_session = TRUE;
        cmdline_session_name = dupstr(value);
        return 2;
@@ -179,41 +179,49 @@ int cmdline_process_param(char *p, char *value, int need_save, Config *cfg)
        RETURN(1);
        UNAVAILABLE_IN(TOOLTYPE_FILETRANSFER | TOOLTYPE_NONNETWORK);
        SAVEABLE(0);
-       default_protocol = cfg->protocol = PROT_SSH;
-       default_port = cfg->port = 22;
+       default_protocol = PROT_SSH;
+       default_port = 22;
+       conf_set_int(conf, CONF_protocol, default_protocol);
+       conf_set_int(conf, CONF_port, default_port);
        return 1;
     }
     if (!strcmp(p, "-telnet")) {
        RETURN(1);
        UNAVAILABLE_IN(TOOLTYPE_FILETRANSFER | TOOLTYPE_NONNETWORK);
        SAVEABLE(0);
-       default_protocol = cfg->protocol = PROT_TELNET;
-       default_port = cfg->port = 23;
+       default_protocol = PROT_TELNET;
+       default_port = 23;
+       conf_set_int(conf, CONF_protocol, default_protocol);
+       conf_set_int(conf, CONF_port, default_port);
        return 1;
     }
     if (!strcmp(p, "-rlogin")) {
        RETURN(1);
        UNAVAILABLE_IN(TOOLTYPE_FILETRANSFER | TOOLTYPE_NONNETWORK);
        SAVEABLE(0);
-       default_protocol = cfg->protocol = PROT_RLOGIN;
-       default_port = cfg->port = 513;
+       default_protocol = PROT_RLOGIN;
+       default_port = 513;
+       conf_set_int(conf, CONF_protocol, default_protocol);
+       conf_set_int(conf, CONF_port, default_port);
        return 1;
     }
     if (!strcmp(p, "-raw")) {
        RETURN(1);
        UNAVAILABLE_IN(TOOLTYPE_FILETRANSFER | TOOLTYPE_NONNETWORK);
        SAVEABLE(0);
-       default_protocol = cfg->protocol = PROT_RAW;
+       default_protocol = PROT_RAW;
+       conf_set_int(conf, CONF_protocol, default_protocol);
     }
     if (!strcmp(p, "-serial")) {
        RETURN(1);
        /* Serial is not NONNETWORK in an odd sense of the word */
        UNAVAILABLE_IN(TOOLTYPE_FILETRANSFER | TOOLTYPE_NONNETWORK);
        SAVEABLE(0);
-       default_protocol = cfg->protocol = PROT_SERIAL;
-       /* The host parameter will already be loaded into cfg->host, so copy it across */
-       strncpy(cfg->serline, cfg->host, sizeof(cfg->serline) - 1);
-       cfg->serline[sizeof(cfg->serline) - 1] = '\0';
+       default_protocol = PROT_SERIAL;
+       conf_set_int(conf, CONF_protocol, default_protocol);
+       /* The host parameter will already be loaded into CONF_host,
+        * so copy it across */
+       conf_set_str(conf, CONF_serline, conf_get_str(conf, CONF_host));
     }
     if (!strcmp(p, "-v")) {
        RETURN(1);
@@ -223,41 +231,23 @@ int cmdline_process_param(char *p, char *value, int need_save, Config *cfg)
        RETURN(2);
        UNAVAILABLE_IN(TOOLTYPE_NONNETWORK);
        SAVEABLE(0);
-       strncpy(cfg->username, value, sizeof(cfg->username));
-       cfg->username[sizeof(cfg->username) - 1] = '\0';
+       conf_set_str(conf, CONF_username, value);
     }
     if (!strcmp(p, "-loghost")) {
        RETURN(2);
        UNAVAILABLE_IN(TOOLTYPE_NONNETWORK);
        SAVEABLE(0);
-       strncpy(cfg->loghost, value, sizeof(cfg->loghost));
-       cfg->loghost[sizeof(cfg->loghost) - 1] = '\0';
+       conf_set_str(conf, CONF_loghost, value);
     }
     if ((!strcmp(p, "-L") || !strcmp(p, "-R") || !strcmp(p, "-D"))) {
-       char *fwd, *ptr, *q, *qq;
-       int dynamic, i=0;
+       char type, *q, *qq, *key, *val;
        RETURN(2);
        UNAVAILABLE_IN(TOOLTYPE_FILETRANSFER | TOOLTYPE_NONNETWORK);
        SAVEABLE(0);
-       dynamic = !strcmp(p, "-D");
-       fwd = value;
-       ptr = cfg->portfwd;
-       /* if existing forwards, find end of list */
-       while (*ptr) {
-           while (*ptr)
-               ptr++;
-           ptr++;
-       }
-       i = ptr - cfg->portfwd;
-       ptr[0] = p[1];  /* insert a 'L', 'R' or 'D' at the start */
-       ptr++;
-       if (1 + strlen(fwd) + 2 > sizeof(cfg->portfwd) - i) {
-           cmdline_error("out of space for port forwardings");
-           return ret;
-       }
-       strncpy(ptr, fwd, sizeof(cfg->portfwd) - i - 2);
-       if (!dynamic) {
+       if (strcmp(p, "-D")) {
            /*
+             * For -L or -R forwarding types:
+             *
             * We expect _at least_ two colons in this string. The
             * possible formats are `sourceport:desthost:destport',
             * or `sourceip:sourceport:desthost:destport' if you're
@@ -265,19 +255,47 @@ int cmdline_process_param(char *p, char *value, int need_save, Config *cfg)
             * replace the one between source and dest with a \t;
             * this means we must find the second-to-last colon in
             * the string.
+            *
+            * (This looks like a foolish way of doing it given the
+            * existence of strrchr, but it's more efficient than
+            * two strrchrs - not to mention that the second strrchr
+            * would require us to modify the input string!)
             */
-           q = qq = strchr(ptr, ':');
+
+            type = p[1];               /* 'L' or 'R' */
+
+           q = qq = strchr(value, ':');
            while (qq) {
                char *qqq = strchr(qq+1, ':');
                if (qqq)
                    q = qq;
                qq = qqq;
            }
-           if (q) *q = '\t';          /* replace second-last colon with \t */
+
+           if (!q) {
+               cmdline_error("-%c expects at least two colons in its"
+                             " argument", type);
+               return ret;
+           }
+
+           key = dupprintf("%c%.*s", type, q - value, value);
+           val = dupstr(q+1);
+       } else {
+            /*
+             * Dynamic port forwardings are entered under the same key
+             * as if they were local (because they occupy the same
+             * port space - a local and a dynamic forwarding on the
+             * same local port are mutually exclusive), with the
+             * special value "D" (which can be distinguished from
+             * anything in the ordinary -L case by containing no
+             * colon).
+             */
+           key = dupprintf("L%s", value);
+           val = dupstr("D");
        }
-       cfg->portfwd[sizeof(cfg->portfwd) - 1] = '\0';
-       cfg->portfwd[sizeof(cfg->portfwd) - 2] = '\0';
-       ptr[strlen(ptr)+1] = '\000';    /* append 2nd '\000' */
+       conf_set_str_str(conf, CONF_portfwd, key, val);
+       sfree(key);
+       sfree(val);
     }
     if ((!strcmp(p, "-nc"))) {
        char *host, *portp;
@@ -286,20 +304,15 @@ int cmdline_process_param(char *p, char *value, int need_save, Config *cfg)
        UNAVAILABLE_IN(TOOLTYPE_FILETRANSFER | TOOLTYPE_NONNETWORK);
        SAVEABLE(0);
 
-       host = portp = value;
-       while (*portp && *portp != ':')
-           portp++;
-       if (*portp) {
-           unsigned len = portp - host;
-           if (len >= sizeof(cfg->ssh_nc_host))
-               len = sizeof(cfg->ssh_nc_host) - 1;
-           memcpy(cfg->ssh_nc_host, value, len);
-           cfg->ssh_nc_host[len] = '\0';
-           cfg->ssh_nc_port = atoi(portp+1);
-       } else {
+       portp = strchr(value, ':');
+       if (!portp) {
            cmdline_error("-nc expects argument of form 'host:port'");
            return ret;
        }
+
+       host = dupprintf("%.*s", portp - value, value);
+       conf_set_str(conf, CONF_ssh_nc_host, host);
+       conf_set_int(conf, CONF_ssh_nc_port, atoi(portp + 1));
     }
     if (!strcmp(p, "-m")) {
        char *filename, *command;
@@ -317,8 +330,7 @@ int cmdline_process_param(char *p, char *value, int need_save, Config *cfg)
        command = NULL;
        fp = fopen(filename, "r");
        if (!fp) {
-           cmdline_error("unable to open command "
-                         "file \"%s\"", filename);
+           cmdline_error("unable to open command file \"%s\"", filename);
            return ret;
        }
        do {
@@ -332,16 +344,17 @@ int cmdline_process_param(char *p, char *value, int need_save, Config *cfg)
            }
            command[cmdlen++] = d;
        } while (c != EOF);
-       cfg->remote_cmd_ptr = command;
-       cfg->remote_cmd_ptr2 = NULL;
-       cfg->nopty = TRUE;      /* command => no terminal */
        fclose(fp);
+       conf_set_str(conf, CONF_remote_cmd, command);
+       conf_set_str(conf, CONF_remote_cmd2, "");
+       conf_set_int(conf, CONF_nopty, TRUE);   /* command => no terminal */
+       sfree(command);
     }
     if (!strcmp(p, "-P")) {
        RETURN(2);
        UNAVAILABLE_IN(TOOLTYPE_NONNETWORK);
        SAVEABLE(1);                   /* lower priority than -ssh,-telnet */
-       cfg->port = atoi(value);
+       conf_set_int(conf, CONF_port, atoi(value));
     }
     if (!strcmp(p, "-pw")) {
        RETURN(2);
@@ -349,7 +362,7 @@ int cmdline_process_param(char *p, char *value, int need_save, Config *cfg)
        SAVEABLE(1);
        /* We delay evaluating this until after the protocol is decided,
         * so that we can warn if it's of no use with the selected protocol */
-       if (cfg->protocol != PROT_SSH)
+       if (conf_get_int(conf, CONF_protocol) != PROT_SSH)
            cmdline_error("the -pw option can only be used with the "
                          "SSH protocol");
        else {
@@ -366,105 +379,107 @@ int cmdline_process_param(char *p, char *value, int need_save, Config *cfg)
        RETURN(1);
        UNAVAILABLE_IN(TOOLTYPE_NONNETWORK);
        SAVEABLE(0);
-       cfg->tryagent = TRUE;
+       conf_set_int(conf, CONF_tryagent, TRUE);
     }
     if (!strcmp(p, "-noagent") || !strcmp(p, "-nopagent") ||
        !strcmp(p, "-nopageant")) {
        RETURN(1);
        UNAVAILABLE_IN(TOOLTYPE_NONNETWORK);
        SAVEABLE(0);
-       cfg->tryagent = FALSE;
+       conf_set_int(conf, CONF_tryagent, FALSE);
     }
 
     if (!strcmp(p, "-A")) {
        RETURN(1);
        UNAVAILABLE_IN(TOOLTYPE_FILETRANSFER | TOOLTYPE_NONNETWORK);
        SAVEABLE(0);
-       cfg->agentfwd = 1;
+       conf_set_int(conf, CONF_agentfwd, 1);
     }
     if (!strcmp(p, "-a")) {
        RETURN(1);
        UNAVAILABLE_IN(TOOLTYPE_FILETRANSFER | TOOLTYPE_NONNETWORK);
        SAVEABLE(0);
-       cfg->agentfwd = 0;
+       conf_set_int(conf, CONF_agentfwd, 0);
     }
 
     if (!strcmp(p, "-X")) {
        RETURN(1);
        UNAVAILABLE_IN(TOOLTYPE_FILETRANSFER | TOOLTYPE_NONNETWORK);
        SAVEABLE(0);
-       cfg->x11_forward = 1;
+       conf_set_int(conf, CONF_x11_forward, 1);
     }
     if (!strcmp(p, "-x")) {
        RETURN(1);
        UNAVAILABLE_IN(TOOLTYPE_FILETRANSFER | TOOLTYPE_NONNETWORK);
        SAVEABLE(0);
-       cfg->x11_forward = 0;
+       conf_set_int(conf, CONF_x11_forward, 0);
     }
 
     if (!strcmp(p, "-t")) {
        RETURN(1);
        UNAVAILABLE_IN(TOOLTYPE_FILETRANSFER | TOOLTYPE_NONNETWORK);
        SAVEABLE(1);    /* lower priority than -m */
-       cfg->nopty = 0;
+       conf_set_int(conf, CONF_nopty, 0);
     }
     if (!strcmp(p, "-T")) {
        RETURN(1);
        UNAVAILABLE_IN(TOOLTYPE_FILETRANSFER | TOOLTYPE_NONNETWORK);
        SAVEABLE(1);
-       cfg->nopty = 1;
+       conf_set_int(conf, CONF_nopty, 1);
     }
 
     if (!strcmp(p, "-N")) {
        RETURN(1);
        UNAVAILABLE_IN(TOOLTYPE_FILETRANSFER | TOOLTYPE_NONNETWORK);
        SAVEABLE(0);
-       cfg->ssh_no_shell = 1;
+       conf_set_int(conf, CONF_ssh_no_shell, 1);
     }
 
     if (!strcmp(p, "-C")) {
        RETURN(1);
        UNAVAILABLE_IN(TOOLTYPE_NONNETWORK);
        SAVEABLE(0);
-       cfg->compression = 1;
+       conf_set_int(conf, CONF_compression, 1);
     }
 
     if (!strcmp(p, "-1")) {
        RETURN(1);
        UNAVAILABLE_IN(TOOLTYPE_NONNETWORK);
        SAVEABLE(0);
-       cfg->sshprot = 0;              /* ssh protocol 1 only */
+       conf_set_int(conf, CONF_sshprot, 0);   /* ssh protocol 1 only */
     }
     if (!strcmp(p, "-2")) {
        RETURN(1);
        UNAVAILABLE_IN(TOOLTYPE_NONNETWORK);
        SAVEABLE(0);
-       cfg->sshprot = 3;              /* ssh protocol 2 only */
+       conf_set_int(conf, CONF_sshprot, 3);   /* ssh protocol 2 only */
     }
 
     if (!strcmp(p, "-i")) {
+       Filename fn;
        RETURN(2);
        UNAVAILABLE_IN(TOOLTYPE_NONNETWORK);
        SAVEABLE(0);
-       cfg->keyfile = filename_from_str(value);
+       fn = filename_from_str(value);
+       conf_set_filename(conf, CONF_keyfile, &fn);
     }
 
     if (!strcmp(p, "-4") || !strcmp(p, "-ipv4")) {
        RETURN(1);
        SAVEABLE(1);
-       cfg->addressfamily = ADDRTYPE_IPV4;
+       conf_set_int(conf, CONF_addressfamily, ADDRTYPE_IPV4);
     }
     if (!strcmp(p, "-6") || !strcmp(p, "-ipv6")) {
        RETURN(1);
        SAVEABLE(1);
-       cfg->addressfamily = ADDRTYPE_IPV6;
+       conf_set_int(conf, CONF_addressfamily, ADDRTYPE_IPV6);
     }
     if (!strcmp(p, "-sercfg")) {
        char* nextitem;
        RETURN(2);
        UNAVAILABLE_IN(TOOLTYPE_FILETRANSFER | TOOLTYPE_NONNETWORK);
        SAVEABLE(1);
-       if (cfg->protocol != PROT_SERIAL)
+       if (conf_get_int(conf, CONF_protocol) != PROT_SERIAL)
            cmdline_error("the -sercfg option can only be used with the "
                          "serial protocol");
        /* Value[0] contains one or more , separated values, like 19200,8,n,1,X */
@@ -483,55 +498,41 @@ int cmdline_process_param(char *p, char *value, int need_save, Config *cfg)
            if (length == 1) {
                switch (*nextitem) {
                  case '1':
-                   cfg->serstopbits = 2;
-                   break;
                  case '2':
-                   cfg->serstopbits = 4;
+                   conf_set_int(conf, CONF_serstopbits, 2 * (*nextitem-'0'));
                    break;
 
                  case '5':
-                   cfg->serdatabits = 5;
-                   break;
-                 case '6':
-                   cfg->serdatabits = 6;
-                   break;
-                 case '7':
-                   cfg->serdatabits = 7;
-                   break;
-                 case '8':
-                   cfg->serdatabits = 8;
-                   break;
-                 case '9':
-                   cfg->serdatabits = 9;
+                   conf_set_int(conf, CONF_serdatabits, *nextitem-'0');
                    break;
 
                  case 'n':
-                   cfg->serparity = SER_PAR_NONE;
+                   conf_set_int(conf, CONF_serparity, SER_PAR_NONE);
                    break;
                  case 'o':
-                   cfg->serparity = SER_PAR_ODD;
+                   conf_set_int(conf, CONF_serparity, SER_PAR_ODD);
                    break;
                  case 'e':
-                   cfg->serparity = SER_PAR_EVEN;
+                   conf_set_int(conf, CONF_serparity, SER_PAR_EVEN);
                    break;
                  case 'm':
-                   cfg->serparity = SER_PAR_MARK;
+                   conf_set_int(conf, CONF_serparity, SER_PAR_MARK);
                    break;
                  case 's':
-                   cfg->serparity = SER_PAR_SPACE;
+                   conf_set_int(conf, CONF_serparity, SER_PAR_SPACE);
                    break;
 
                  case 'N':
-                   cfg->serflow = SER_FLOW_NONE;
+                   conf_set_int(conf, CONF_serflow, SER_FLOW_NONE);
                    break;
                  case 'X':
-                   cfg->serflow = SER_FLOW_XONXOFF;
+                   conf_set_int(conf, CONF_serflow, SER_FLOW_XONXOFF);
                    break;
                  case 'R':
-                   cfg->serflow = SER_FLOW_RTSCTS;
+                   conf_set_int(conf, CONF_serflow, SER_FLOW_RTSCTS);
                    break;
                  case 'D':
-                   cfg->serflow = SER_FLOW_DSRDTR;
+                   conf_set_int(conf, CONF_serflow, SER_FLOW_DSRDTR);
                    break;
 
                  default:
@@ -540,11 +541,11 @@ int cmdline_process_param(char *p, char *value, int need_save, Config *cfg)
                }
            } else if (length == 3 && !strncmp(nextitem,"1.5",3)) {
                /* Messy special case */
-               cfg->serstopbits = 3;
+               conf_set_int(conf, CONF_serstopbits, 3);
            } else {
                int serspeed = atoi(nextitem);
                if (serspeed != 0) {
-                   cfg->serspeed = serspeed;
+                   conf_set_int(conf, CONF_serspeed, serspeed);
                } else {
                    cmdline_error("Unrecognised suboption \"-sercfg %s\"",
                                  nextitem);
@@ -556,11 +557,11 @@ int cmdline_process_param(char *p, char *value, int need_save, Config *cfg)
     return ret;                               /* unrecognised */
 }
 
-void cmdline_run_saved(Config *cfg)
+void cmdline_run_saved(Conf *conf)
 {
     int pri, i;
     for (pri = 0; pri < NPRIORITIES; pri++)
        for (i = 0; i < saves[pri].nsaved; i++)
            cmdline_process_param(saves[pri].params[i].p,
-                                 saves[pri].params[i].value, 0, cfg);
+                                 saves[pri].params[i].value, 0, conf);
 }
diff --git a/conf.c b/conf.c
new file mode 100644 (file)
index 0000000..89bf5c3
--- /dev/null
+++ b/conf.c
@@ -0,0 +1,610 @@
+/*
+ * conf.c: implementation of the internal storage format used for
+ * the configuration of a PuTTY session.
+ */
+
+#include <stdio.h>
+#include <stddef.h>
+#include <assert.h>
+
+#include "tree234.h"
+#include "putty.h"
+
+/*
+ * Enumeration of types used in keys and values.
+ */
+typedef enum { TYPE_NONE, TYPE_INT, TYPE_STR, TYPE_FILENAME, TYPE_FONT } Type;
+
+/*
+ * Arrays which allow us to look up the subkey and value types for a
+ * given primary key id.
+ */
+#define CONF_SUBKEYTYPE_DEF(valtype, keytype, keyword) TYPE_ ## keytype,
+static int subkeytypes[] = { CONFIG_OPTIONS(CONF_SUBKEYTYPE_DEF) };
+#define CONF_VALUETYPE_DEF(valtype, keytype, keyword) TYPE_ ## valtype,
+static int valuetypes[] = { CONFIG_OPTIONS(CONF_VALUETYPE_DEF) };
+
+/*
+ * Configuration keys are primarily integers (big enum of all the
+ * different configurable options); some keys have string-designated
+ * subkeys, such as the list of environment variables (subkeys
+ * defined by the variable names); some have integer-designated
+ * subkeys (wordness, colours, preference lists).
+ */
+struct key {
+    int primary;
+    union {
+       int i;
+       char *s;
+    } secondary;
+};
+
+struct value {
+    union {
+       int intval;
+       char *stringval;
+       Filename fileval;
+       FontSpec fontval;
+    } u;
+};
+
+struct conf_entry {
+    struct key key;
+    struct value value;
+};
+
+struct conf_tag {
+    tree234 *tree;
+};
+
+/*
+ * Because 'struct key' is the first element in 'struct conf_entry',
+ * it's safe (guaranteed by the C standard) to cast arbitrarily back
+ * and forth between the two types. Therefore, we only need one
+ * comparison function, which can double as a main sort function for
+ * the tree (comparing two conf_entry structures with each other)
+ * and a search function (looking up an externally supplied key).
+ */
+static int conf_cmp(void *av, void *bv)
+{
+    struct key *a = (struct key *)av;
+    struct key *b = (struct key *)bv;
+
+    if (a->primary < b->primary)
+       return -1;
+    else if (a->primary > b->primary)
+       return +1;
+    switch (subkeytypes[a->primary]) {
+      case TYPE_INT:
+       if (a->secondary.i < b->secondary.i)
+           return -1;
+       else if (a->secondary.i > b->secondary.i)
+           return +1;
+       return 0;
+      case TYPE_STR:
+       return strcmp(a->secondary.s, b->secondary.s);
+      default:
+       return 0;
+    }
+}
+
+/*
+ * Free any dynamic data items pointed to by a 'struct key'. We
+ * don't free the structure itself, since it's probably part of a
+ * larger allocated block.
+ */
+static void free_key(struct key *key)
+{
+    if (subkeytypes[key->primary] == TYPE_STR)
+       sfree(key->secondary.s);
+}
+
+/*
+ * Copy a 'struct key' into another one, copying its dynamic data
+ * if necessary.
+ */
+static void copy_key(struct key *to, struct key *from)
+{
+    to->primary = from->primary;
+    switch (subkeytypes[to->primary]) {
+      case TYPE_INT:
+       to->secondary.i = from->secondary.i;
+       break;
+      case TYPE_STR:
+       to->secondary.s = dupstr(from->secondary.s);
+       break;
+    }
+}
+
+/*
+ * Free any dynamic data items pointed to by a 'struct value'. We
+ * don't free the value itself, since it's probably part of a larger
+ * allocated block.
+ */
+static void free_value(struct value *val, int type)
+{
+    if (type == TYPE_STR)
+       sfree(val->u.stringval);
+}
+
+/*
+ * Copy a 'struct value' into another one, copying its dynamic data
+ * if necessary.
+ */
+static void copy_value(struct value *to, struct value *from, int type)
+{
+    switch (type) {
+      case TYPE_INT:
+       to->u.intval = from->u.intval;
+       break;
+      case TYPE_STR:
+       to->u.stringval = dupstr(from->u.stringval);
+       break;
+      case TYPE_FILENAME:
+       to->u.fileval = from->u.fileval;
+       break;
+      case TYPE_FONT:
+       to->u.fontval = from->u.fontval;
+       break;
+    }
+}
+
+/*
+ * Free an entire 'struct conf_entry' and its dynamic data.
+ */
+static void free_entry(struct conf_entry *entry)
+{
+    free_key(&entry->key);
+    free_value(&entry->value, valuetypes[entry->key.primary]);
+    sfree(entry);
+}
+
+Conf *conf_new(void)
+{
+    Conf *conf = snew(struct conf_tag);
+
+    conf->tree = newtree234(conf_cmp);
+
+    return conf;
+}
+
+static void conf_clear(Conf *conf)
+{
+    struct conf_entry *entry;
+
+    while ((entry = delpos234(conf->tree, 0)) != NULL)
+       free_entry(entry);
+}
+
+void conf_free(Conf *conf)
+{
+    conf_clear(conf);
+    freetree234(conf->tree);
+    sfree(conf);
+}
+
+static void conf_insert(Conf *conf, struct conf_entry *entry)
+{
+    struct conf_entry *oldentry = add234(conf->tree, entry);
+    if (oldentry && oldentry != entry) {
+       del234(conf->tree, oldentry);
+       free_entry(oldentry);
+       oldentry = add234(conf->tree, entry);
+       assert(oldentry == entry);
+    }
+}
+
+void conf_copy_into(Conf *newconf, Conf *oldconf)
+{
+    struct conf_entry *entry, *entry2;
+    int i;
+
+    for (i = 0; (entry = index234(oldconf->tree, i)) != NULL; i++) {
+       entry2 = snew(struct conf_entry);
+       copy_key(&entry2->key, &entry->key);
+       copy_value(&entry2->value, &entry->value,
+                  valuetypes[entry->key.primary]);
+       add234(newconf->tree, entry2);
+    }
+}
+
+Conf *conf_copy(Conf *oldconf)
+{
+    Conf *newconf = conf_new();
+
+    conf_copy_into(newconf, oldconf);
+
+    return newconf;
+}
+
+int conf_get_int(Conf *conf, int primary)
+{
+    struct key key;
+    struct conf_entry *entry;
+
+    assert(subkeytypes[primary] == TYPE_NONE);
+    assert(valuetypes[primary] == TYPE_INT);
+    key.primary = primary;
+    entry = find234(conf->tree, &key, NULL);
+    assert(entry);
+    return entry->value.u.intval;
+}
+
+int conf_get_int_int(Conf *conf, int primary, int secondary)
+{
+    struct key key;
+    struct conf_entry *entry;
+
+    assert(subkeytypes[primary] == TYPE_INT);
+    assert(valuetypes[primary] == TYPE_INT);
+    key.primary = primary;
+    key.secondary.i = secondary;
+    entry = find234(conf->tree, &key, NULL);
+    assert(entry);
+    return entry->value.u.intval;
+}
+
+char *conf_get_str(Conf *conf, int primary)
+{
+    struct key key;
+    struct conf_entry *entry;
+
+    assert(subkeytypes[primary] == TYPE_NONE);
+    assert(valuetypes[primary] == TYPE_STR);
+    key.primary = primary;
+    entry = find234(conf->tree, &key, NULL);
+    assert(entry);
+    return entry->value.u.stringval;
+}
+
+char *conf_get_str_str_opt(Conf *conf, int primary, const char *secondary)
+{
+    struct key key;
+    struct conf_entry *entry;
+
+    assert(subkeytypes[primary] == TYPE_STR);
+    assert(valuetypes[primary] == TYPE_STR);
+    key.primary = primary;
+    key.secondary.s = (char *)secondary;
+    entry = find234(conf->tree, &key, NULL);
+    return entry ? entry->value.u.stringval : NULL;
+}
+
+char *conf_get_str_str(Conf *conf, int primary, const char *secondary)
+{
+    char *ret = conf_get_str_str_opt(conf, primary, secondary);
+    assert(ret);
+    return ret;
+}
+
+char *conf_get_str_strs(Conf *conf, int primary,
+                      char *subkeyin, char **subkeyout)
+{
+    struct key key;
+    struct conf_entry *entry;
+
+    assert(subkeytypes[primary] == TYPE_STR);
+    assert(valuetypes[primary] == TYPE_STR);
+    key.primary = primary;
+    if (subkeyin) {
+       key.secondary.s = subkeyin;
+       entry = findrel234(conf->tree, &key, NULL, REL234_GT);
+    } else {
+       key.secondary.s = "";
+       entry = findrel234(conf->tree, &key, NULL, REL234_GE);
+    }
+    if (!entry || entry->key.primary != primary)
+       return NULL;
+    *subkeyout = entry->key.secondary.s;
+    return entry->value.u.stringval;
+}
+
+char *conf_get_str_nthstrkey(Conf *conf, int primary, int n)
+{
+    struct key key;
+    struct conf_entry *entry;
+    int index;
+
+    assert(subkeytypes[primary] == TYPE_STR);
+    assert(valuetypes[primary] == TYPE_STR);
+    key.primary = primary;
+    key.secondary.s = "";
+    entry = findrelpos234(conf->tree, &key, NULL, REL234_GE, &index);
+    if (!entry || entry->key.primary != primary)
+       return NULL;
+    entry = index234(conf->tree, index + n);
+    if (!entry || entry->key.primary != primary)
+       return NULL;
+    return entry->key.secondary.s;
+}
+
+Filename *conf_get_filename(Conf *conf, int primary)
+{
+    struct key key;
+    struct conf_entry *entry;
+
+    assert(subkeytypes[primary] == TYPE_NONE);
+    assert(valuetypes[primary] == TYPE_FILENAME);
+    key.primary = primary;
+    entry = find234(conf->tree, &key, NULL);
+    assert(entry);
+    return &entry->value.u.fileval;
+}
+
+FontSpec *conf_get_fontspec(Conf *conf, int primary)
+{
+    struct key key;
+    struct conf_entry *entry;
+
+    assert(subkeytypes[primary] == TYPE_NONE);
+    assert(valuetypes[primary] == TYPE_FONT);
+    key.primary = primary;
+    entry = find234(conf->tree, &key, NULL);
+    assert(entry);
+    return &entry->value.u.fontval;
+}
+
+void conf_set_int(Conf *conf, int primary, int value)
+{
+    struct conf_entry *entry = snew(struct conf_entry);
+
+    assert(subkeytypes[primary] == TYPE_NONE);
+    assert(valuetypes[primary] == TYPE_INT);
+    entry->key.primary = primary;
+    entry->value.u.intval = value; 
+    conf_insert(conf, entry);
+}
+
+void conf_set_int_int(Conf *conf, int primary, int secondary, int value)
+{
+    struct conf_entry *entry = snew(struct conf_entry);
+
+    assert(subkeytypes[primary] == TYPE_INT);
+    assert(valuetypes[primary] == TYPE_INT);
+    entry->key.primary = primary;
+    entry->key.secondary.i = secondary;
+    entry->value.u.intval = value;
+    conf_insert(conf, entry);
+}
+
+void conf_set_str(Conf *conf, int primary, const char *value)
+{
+    struct conf_entry *entry = snew(struct conf_entry);
+
+    assert(subkeytypes[primary] == TYPE_NONE);
+    assert(valuetypes[primary] == TYPE_STR);
+    entry->key.primary = primary;
+    entry->value.u.stringval = dupstr(value);
+    conf_insert(conf, entry);
+}
+
+void conf_set_str_str(Conf *conf, int primary, const char *secondary,
+                     const char *value)
+{
+    struct conf_entry *entry = snew(struct conf_entry);
+
+    assert(subkeytypes[primary] == TYPE_STR);
+    assert(valuetypes[primary] == TYPE_STR);
+    entry->key.primary = primary;
+    entry->key.secondary.s = dupstr(secondary);
+    entry->value.u.stringval = dupstr(value);
+    conf_insert(conf, entry);
+}
+
+void conf_del_str_str(Conf *conf, int primary, const char *secondary)
+{
+    struct key key;
+    struct conf_entry *entry;
+
+    assert(subkeytypes[primary] == TYPE_STR);
+    assert(valuetypes[primary] == TYPE_STR);
+    key.primary = primary;
+    key.secondary.s = (char *)secondary;
+    entry = find234(conf->tree, &key, NULL);
+    if (entry) {
+       del234(conf->tree, entry);
+       free_entry(entry);
+    }
+ }
+
+void conf_set_filename(Conf *conf, int primary, const Filename *value)
+{
+    struct conf_entry *entry = snew(struct conf_entry);
+
+    assert(subkeytypes[primary] == TYPE_NONE);
+    assert(valuetypes[primary] == TYPE_FILENAME);
+    entry->key.primary = primary;
+    entry->value.u.fileval = *value;   /* structure copy */
+    conf_insert(conf, entry);
+}
+
+void conf_set_fontspec(Conf *conf, int primary, const FontSpec *value)
+{
+    struct conf_entry *entry = snew(struct conf_entry);
+
+    assert(subkeytypes[primary] == TYPE_NONE);
+    assert(valuetypes[primary] == TYPE_FONT);
+    entry->key.primary = primary;
+    entry->value.u.fontval = *value;   /* structure copy */
+    conf_insert(conf, entry);
+}
+
+int conf_serialised_size(Conf *conf)
+{
+    int i;
+    struct conf_entry *entry;
+    int size = 0;
+
+    for (i = 0; (entry = index234(conf->tree, i)) != NULL; i++) {
+       size += 4;   /* primary key */
+       switch (subkeytypes[entry->key.primary]) {
+         case TYPE_INT:
+           size += 4;
+           break;
+         case TYPE_STR:
+           size += 1 + strlen(entry->key.secondary.s);
+           break;
+       }
+       switch (valuetypes[entry->key.primary]) {
+         case TYPE_INT:
+           size += 4;
+           break;
+         case TYPE_STR:
+           size += 1 + strlen(entry->value.u.stringval);
+           break;
+         case TYPE_FILENAME:
+           size += sizeof(entry->value.u.fileval);
+           break;
+         case TYPE_FONT:
+           size += sizeof(entry->value.u.fontval);
+           break;
+       }
+    }
+
+    size += 4;                        /* terminator value */
+
+    return size;
+}
+
+void conf_serialise(Conf *conf, void *vdata)
+{
+    unsigned char *data = (unsigned char *)vdata;
+    int i, len;
+    struct conf_entry *entry;
+
+    for (i = 0; (entry = index234(conf->tree, i)) != NULL; i++) {
+       PUT_32BIT_MSB_FIRST(data, entry->key.primary);
+       data += 4;
+
+       switch (subkeytypes[entry->key.primary]) {
+         case TYPE_INT:
+           PUT_32BIT_MSB_FIRST(data, entry->key.secondary.i);
+           data += 4;
+           break;
+         case TYPE_STR:
+           len = strlen(entry->key.secondary.s);
+           memcpy(data, entry->key.secondary.s, len);
+           data += len;
+           *data++ = 0;
+           break;
+       }
+       switch (valuetypes[entry->key.primary]) {
+         case TYPE_INT:
+           PUT_32BIT_MSB_FIRST(data, entry->value.u.intval);
+           data += 4;
+           break;
+         case TYPE_STR:
+           len = strlen(entry->value.u.stringval);
+           memcpy(data, entry->value.u.stringval, len);
+           data += len;
+           *data++ = 0;
+           break;
+         case TYPE_FILENAME:
+           memcpy(data, &entry->value.u.fileval,
+                  sizeof(entry->value.u.fileval));
+           data += sizeof(entry->value.u.fileval);
+           break;
+         case TYPE_FONT:
+           memcpy(data, &entry->value.u.fontval,
+                  sizeof(entry->value.u.fontval));
+           data += sizeof(entry->value.u.fontval);
+           break;
+       }
+    }
+
+    PUT_32BIT_MSB_FIRST(data, 0xFFFFFFFFU);
+}
+
+int conf_deserialise(Conf *conf, void *vdata, int maxsize)
+{
+    unsigned char *data = (unsigned char *)vdata;
+    unsigned char *start = data;
+    struct conf_entry *entry;
+    int primary;
+    unsigned char *zero;
+
+    while (maxsize >= 4) {
+       primary = GET_32BIT_MSB_FIRST(data);
+       data += 4, maxsize -= 4;
+
+       if ((unsigned)primary >= N_CONFIG_OPTIONS)
+           break;
+
+       entry = snew(struct conf_entry);
+       entry->key.primary = primary;
+
+       switch (subkeytypes[entry->key.primary]) {
+         case TYPE_INT:
+           if (maxsize < 4) {
+               sfree(entry);
+               goto done;
+           }
+           entry->key.secondary.i = GET_32BIT_MSB_FIRST(data);
+           data += 4, maxsize -= 4;
+           break;
+         case TYPE_STR:
+           zero = memchr(data, 0, maxsize);
+           if (!zero) {
+               sfree(entry);
+               goto done;
+           }
+           entry->key.secondary.s = dupstr((char *)data);
+           maxsize -= (zero + 1 - data);
+           data = zero + 1;
+           break;
+       }
+
+       switch (valuetypes[entry->key.primary]) {
+         case TYPE_INT:
+           if (maxsize < 4) {
+               if (subkeytypes[entry->key.primary] == TYPE_STR)
+                   sfree(entry->key.secondary.s);
+               sfree(entry);
+               goto done;
+           }
+           entry->value.u.intval = GET_32BIT_MSB_FIRST(data);
+           data += 4, maxsize -= 4;
+           break;
+         case TYPE_STR:
+           zero = memchr(data, 0, maxsize);
+           if (!zero) {
+               if (subkeytypes[entry->key.primary] == TYPE_STR)
+                   sfree(entry->key.secondary.s);
+               sfree(entry);
+               goto done;
+           }
+           entry->value.u.stringval = dupstr((char *)data);
+           maxsize -= (zero + 1 - data);
+           data = zero + 1;
+           break;
+         case TYPE_FILENAME:
+           if (maxsize < sizeof(entry->value.u.fileval)) {
+               if (subkeytypes[entry->key.primary] == TYPE_STR)
+                   sfree(entry->key.secondary.s);
+               sfree(entry);
+               goto done;
+           }
+           memcpy(&entry->value.u.fileval, data,
+                  sizeof(entry->value.u.fileval));
+           data += sizeof(entry->value.u.fileval);
+           maxsize -= sizeof(entry->value.u.fileval);
+           break;
+         case TYPE_FONT:
+           if (maxsize < sizeof(entry->value.u.fontval)) {
+               if (subkeytypes[entry->key.primary] == TYPE_STR)
+                   sfree(entry->key.secondary.s);
+               sfree(entry);
+               goto done;
+           }
+           memcpy(&entry->value.u.fontval, data,
+                  sizeof(entry->value.u.fontval));
+           data += sizeof(entry->value.u.fontval);
+           maxsize -= sizeof(entry->value.u.fontval);
+           break;
+       }
+       conf_insert(conf, entry);
+    }
+
+    done:
+    return (int)(data - start);
+}
index a1454b4..a03116b 100644 (file)
--- a/config.c
+++ b/config.c
 #define HOST_BOX_TITLE "Host Name (or IP address)"
 #define PORT_BOX_TITLE "Port"
 
+void conf_radiobutton_handler(union control *ctrl, void *dlg,
+                             void *data, int event)
+{
+    int button;
+    Conf *conf = (Conf *)data;
+
+    /*
+     * For a standard radio button set, the context parameter gives
+     * the primary key (CONF_foo), and the extra data per button
+     * gives the value the target field should take if that button
+     * is the one selected.
+     */
+    if (event == EVENT_REFRESH) {
+       int val = conf_get_int(conf, ctrl->radio.context.i);
+       for (button = 0; button < ctrl->radio.nbuttons; button++)
+           if (val == ctrl->radio.buttondata[button].i)
+               break;
+       /* We expected that `break' to happen, in all circumstances. */
+       assert(button < ctrl->radio.nbuttons);
+       dlg_radiobutton_set(ctrl, dlg, button);
+    } else if (event == EVENT_VALCHANGE) {
+       button = dlg_radiobutton_get(ctrl, dlg);
+       assert(button >= 0 && button < ctrl->radio.nbuttons);
+       conf_set_int(conf, ctrl->radio.context.i,
+                    ctrl->radio.buttondata[button].i);
+    }
+}
+
+#define CHECKBOX_INVERT (1<<30)
+void conf_checkbox_handler(union control *ctrl, void *dlg,
+                          void *data, int event)
+{
+    int key, invert;
+    Conf *conf = (Conf *)data;
+
+    /*
+     * For a standard checkbox, the context parameter gives the
+     * primary key (CONF_foo), optionally ORed with CHECKBOX_INVERT.
+     */
+    key = ctrl->checkbox.context.i;
+    if (key & CHECKBOX_INVERT) {
+       key &= ~CHECKBOX_INVERT;
+       invert = 1;
+    } else
+       invert = 0;
+
+    /*
+     * C lacks a logical XOR, so the following code uses the idiom
+     * (!a ^ !b) to obtain the logical XOR of a and b. (That is, 1
+     * iff exactly one of a and b is nonzero, otherwise 0.)
+     */
+
+    if (event == EVENT_REFRESH) {
+       int val = conf_get_int(conf, key);
+       dlg_checkbox_set(ctrl, dlg, (!val ^ !invert));
+    } else if (event == EVENT_VALCHANGE) {
+       conf_set_int(conf, key, !dlg_checkbox_get(ctrl,dlg) ^ !invert);
+    }
+}
+
+void conf_editbox_handler(union control *ctrl, void *dlg,
+                         void *data, int event)
+{
+    /*
+     * The standard edit-box handler expects the main `context'
+     * field to contain the primary key. The secondary `context2'
+     * field indicates the type of this field:
+     *
+     *  - if context2 > 0, the field is a string.
+     *  - if context2 == -1, the field is an int and the edit box
+     *    is numeric.
+     *  - if context2 < -1, the field is an int and the edit box is
+     *    _floating_, and (-context2) gives the scale. (E.g. if
+     *    context2 == -1000, then typing 1.2 into the box will set
+     *    the field to 1200.)
+     */
+    int key = ctrl->editbox.context.i;
+    int length = ctrl->editbox.context2.i;
+    Conf *conf = (Conf *)data;
+
+    if (length > 0) {
+       if (event == EVENT_REFRESH) {
+           char *field = conf_get_str(conf, key);
+           dlg_editbox_set(ctrl, dlg, field);
+       } else if (event == EVENT_VALCHANGE) {
+           char *field = dlg_editbox_get(ctrl, dlg);
+           conf_set_str(conf, key, field);
+           sfree(field);
+       }
+    } else if (length < 0) {
+       if (event == EVENT_REFRESH) {
+           char str[80];
+           int value = conf_get_int(conf, key);
+           if (length == -1)
+               sprintf(str, "%d", value);
+           else
+               sprintf(str, "%g", (double)value / (double)(-length));
+           dlg_editbox_set(ctrl, dlg, str);
+       } else if (event == EVENT_VALCHANGE) {
+           char *str = dlg_editbox_get(ctrl, dlg);
+           if (length == -1)
+               conf_set_int(conf, key, atoi(str));
+           else
+               conf_set_int(conf, key, (int)((-length) * atof(str)));
+           sfree(str);
+       }
+    }
+}
+
+void conf_filesel_handler(union control *ctrl, void *dlg,
+                         void *data, int event)
+{
+    int key = ctrl->fileselect.context.i;
+    Conf *conf = (Conf *)data;
+
+    if (event == EVENT_REFRESH) {
+       dlg_filesel_set(ctrl, dlg, *conf_get_filename(conf, key));
+    } else if (event == EVENT_VALCHANGE) {
+       Filename filename;
+       dlg_filesel_get(ctrl, dlg, &filename);
+       conf_set_filename(conf, key, &filename);
+       /* If Filenames ever become dynamic, free this one. */
+    }
+}
+
+void conf_fontsel_handler(union control *ctrl, void *dlg,
+                         void *data, int event)
+{
+    int key = ctrl->fontselect.context.i;
+    Conf *conf = (Conf *)data;
+
+    if (event == EVENT_REFRESH) {
+       dlg_fontsel_set(ctrl, dlg, *conf_get_fontspec(conf, key));
+    } else if (event == EVENT_VALCHANGE) {
+       FontSpec fontspec;
+       dlg_fontsel_get(ctrl, dlg, &fontspec);
+       conf_set_fontspec(conf, key, &fontspec);
+       /* If FontSpecs ever become dynamic, free this one. */
+    }
+}
+
 static void config_host_handler(union control *ctrl, void *dlg,
                                void *data, int event)
 {
-    Config *cfg = (Config *)data;
+    Conf *conf = (Conf *)data;
 
     /*
      * This function works just like the standard edit box handler,
@@ -26,29 +167,31 @@ static void config_host_handler(union control *ctrl, void *dlg,
      * different places depending on the protocol.
      */
     if (event == EVENT_REFRESH) {
-       if (cfg->protocol == PROT_SERIAL) {
+       if (conf_get_int(conf, CONF_protocol) == PROT_SERIAL) {
            /*
             * This label text is carefully chosen to contain an n,
             * since that's the shortcut for the host name control.
             */
            dlg_label_change(ctrl, dlg, "Serial line");
-           dlg_editbox_set(ctrl, dlg, cfg->serline);
+           dlg_editbox_set(ctrl, dlg, conf_get_str(conf, CONF_serline));
        } else {
            dlg_label_change(ctrl, dlg, HOST_BOX_TITLE);
-           dlg_editbox_set(ctrl, dlg, cfg->host);
+           dlg_editbox_set(ctrl, dlg, conf_get_str(conf, CONF_host));
        }
     } else if (event == EVENT_VALCHANGE) {
-       if (cfg->protocol == PROT_SERIAL)
-           dlg_editbox_get(ctrl, dlg, cfg->serline, lenof(cfg->serline));
+       char *s = dlg_editbox_get(ctrl, dlg);
+       if (conf_get_int(conf, CONF_protocol) == PROT_SERIAL)
+           conf_set_str(conf, CONF_serline, s);
        else
-           dlg_editbox_get(ctrl, dlg, cfg->host, lenof(cfg->host));
+           conf_set_str(conf, CONF_host, s);
+       sfree(s);
     }
 }
 
 static void config_port_handler(union control *ctrl, void *dlg,
                                void *data, int event)
 {
-    Config *cfg = (Config *)data;
+    Conf *conf = (Conf *)data;
     char buf[80];
 
     /*
@@ -57,28 +200,31 @@ static void config_port_handler(union control *ctrl, void *dlg,
      * different places depending on the protocol.
      */
     if (event == EVENT_REFRESH) {
-       if (cfg->protocol == PROT_SERIAL) {
+       if (conf_get_int(conf, CONF_protocol) == PROT_SERIAL) {
            /*
             * This label text is carefully chosen to contain a p,
             * since that's the shortcut for the port control.
             */
            dlg_label_change(ctrl, dlg, "Speed");
-           sprintf(buf, "%d", cfg->serspeed);
+           sprintf(buf, "%d", conf_get_int(conf, CONF_serspeed));
        } else {
            dlg_label_change(ctrl, dlg, PORT_BOX_TITLE);
-           if (cfg->port != 0)
-               sprintf(buf, "%d", cfg->port);
+           if (conf_get_int(conf, CONF_port) != 0)
+               sprintf(buf, "%d", conf_get_int(conf, CONF_port));
            else
                /* Display an (invalid) port of 0 as blank */
                buf[0] = '\0';
        }
        dlg_editbox_set(ctrl, dlg, buf);
     } else if (event == EVENT_VALCHANGE) {
-       dlg_editbox_get(ctrl, dlg, buf, lenof(buf));
-       if (cfg->protocol == PROT_SERIAL)
-           cfg->serspeed = atoi(buf);
+       char *s = dlg_editbox_get(ctrl, dlg);
+       int i = atoi(s);
+       sfree(s);
+
+       if (conf_get_int(conf, CONF_protocol) == PROT_SERIAL)
+           conf_set_int(conf, CONF_serspeed, i);
        else
-           cfg->port = atoi(buf);
+           conf_set_int(conf, CONF_port, i);
     }
 }
 
@@ -95,7 +241,7 @@ void config_protocolbuttons_handler(union control *ctrl, void *dlg,
                                    void *data, int event)
 {
     int button;
-    Config *cfg = (Config *)data;
+    Conf *conf = (Conf *)data;
     struct hostport *hp = (struct hostport *)ctrl->radio.context.p;
 
     /*
@@ -106,20 +252,25 @@ void config_protocolbuttons_handler(union control *ctrl, void *dlg,
      * structure giving the `union control's for both.
      */
     if (event == EVENT_REFRESH) {
+       int protocol = conf_get_int(conf, CONF_protocol);
        for (button = 0; button < ctrl->radio.nbuttons; button++)
-           if (cfg->protocol == ctrl->radio.buttondata[button].i)
+           if (protocol == ctrl->radio.buttondata[button].i)
                break;
        /* We expected that `break' to happen, in all circumstances. */
        assert(button < ctrl->radio.nbuttons);
        dlg_radiobutton_set(ctrl, dlg, button);
     } else if (event == EVENT_VALCHANGE) {
-       int oldproto = cfg->protocol;
+       int oldproto = conf_get_int(conf, CONF_protocol);
+       int newproto, port;
+
        button = dlg_radiobutton_get(ctrl, dlg);
        assert(button >= 0 && button < ctrl->radio.nbuttons);
-       cfg->protocol = ctrl->radio.buttondata[button].i;
-       if (oldproto != cfg->protocol) {
+       newproto = ctrl->radio.buttondata[button].i;
+       conf_set_int(conf, CONF_protocol, newproto);
+
+       if (oldproto != newproto) {
            Backend *ob = backend_from_proto(oldproto);
-           Backend *nb = backend_from_proto(cfg->protocol);
+           Backend *nb = backend_from_proto(newproto);
            assert(ob);
            assert(nb);
            /* Iff the user hasn't changed the port from the old protocol's
@@ -131,8 +282,9 @@ void config_protocolbuttons_handler(union control *ctrl, void *dlg,
             * controls in order and setting a non-default port before
             * getting to the protocol; we want that non-default port
             * to be preserved. */
-           if (cfg->port == ob->default_port)
-               cfg->port = nb->default_port;
+           port = conf_get_int(conf, CONF_port);
+           if (port == ob->default_port)
+               conf_set_int(conf, CONF_port, nb->default_port);
        }
        dlg_refresh(hp->host, dlg);
        dlg_refresh(hp->port, dlg);
@@ -143,26 +295,28 @@ static void loggingbuttons_handler(union control *ctrl, void *dlg,
                                   void *data, int event)
 {
     int button;
-    Config *cfg = (Config *)data;
+    Conf *conf = (Conf *)data;
     /* This function works just like the standard radio-button handler,
      * but it has to fall back to "no logging" in situations where the
      * configured logging type isn't applicable.
      */
     if (event == EVENT_REFRESH) {
+       int logtype = conf_get_int(conf, CONF_logtype);
+
         for (button = 0; button < ctrl->radio.nbuttons; button++)
-            if (cfg->logtype == ctrl->radio.buttondata[button].i)
+            if (logtype == ctrl->radio.buttondata[button].i)
                break;
-    
-    /* We fell off the end, so we lack the configured logging type */
-    if (button == ctrl->radio.nbuttons) {
-        button=0;
-        cfg->logtype=LGTYP_NONE;
-    }
-    dlg_radiobutton_set(ctrl, dlg, button);
+
+       /* We fell off the end, so we lack the configured logging type */
+       if (button == ctrl->radio.nbuttons) {
+           button = 0;
+           conf_set_int(conf, CONF_logtype, LGTYP_NONE);
+       }
+       dlg_radiobutton_set(ctrl, dlg, button);
     } else if (event == EVENT_VALCHANGE) {
         button = dlg_radiobutton_get(ctrl, dlg);
         assert(button >= 0 && button < ctrl->radio.nbuttons);
-        cfg->logtype = ctrl->radio.buttondata[button].i;
+        conf_set_int(conf, CONF_logtype, ctrl->radio.buttondata[button].i);
     }
 }
 
@@ -170,15 +324,15 @@ static void numeric_keypad_handler(union control *ctrl, void *dlg,
                                   void *data, int event)
 {
     int button;
-    Config *cfg = (Config *)data;
+    Conf *conf = (Conf *)data;
     /*
      * This function works much like the standard radio button
-     * handler, but it has to handle two fields in Config.
+     * handler, but it has to handle two fields in Conf.
      */
     if (event == EVENT_REFRESH) {
-       if (cfg->nethack_keypad)
+       if (conf_get_int(conf, CONF_nethack_keypad))
            button = 2;
-       else if (cfg->app_keypad)
+       else if (conf_get_int(conf, CONF_app_keypad))
            button = 1;
        else
            button = 0;
@@ -188,11 +342,11 @@ static void numeric_keypad_handler(union control *ctrl, void *dlg,
        button = dlg_radiobutton_get(ctrl, dlg);
        assert(button >= 0 && button < ctrl->radio.nbuttons);
        if (button == 2) {
-           cfg->app_keypad = FALSE;
-           cfg->nethack_keypad = TRUE;
+           conf_set_int(conf, CONF_app_keypad, FALSE);
+           conf_set_int(conf, CONF_nethack_keypad, TRUE);
        } else {
-           cfg->app_keypad = (button != 0);
-           cfg->nethack_keypad = FALSE;
+           conf_set_int(conf, CONF_app_keypad, (button != 0));
+           conf_set_int(conf, CONF_nethack_keypad, FALSE);
        }
     }
 }
@@ -200,7 +354,7 @@ static void numeric_keypad_handler(union control *ctrl, void *dlg,
 static void cipherlist_handler(union control *ctrl, void *dlg,
                               void *data, int event)
 {
-    Config *cfg = (Config *)data;
+    Conf *conf = (Conf *)data;
     if (event == EVENT_REFRESH) {
        int i;
 
@@ -218,7 +372,7 @@ static void cipherlist_handler(union control *ctrl, void *dlg,
        dlg_update_start(ctrl, dlg);
        dlg_listbox_clear(ctrl, dlg);
        for (i = 0; i < CIPHER_MAX; i++) {
-           int c = cfg->ssh_cipherlist[i];
+           int c = conf_get_int_int(conf, CONF_ssh_cipherlist, i);
            int j;
            char *cstr = NULL;
            for (j = 0; j < (sizeof ciphers) / (sizeof ciphers[0]); j++) {
@@ -236,8 +390,8 @@ static void cipherlist_handler(union control *ctrl, void *dlg,
 
        /* Update array to match the list box. */
        for (i=0; i < CIPHER_MAX; i++)
-           cfg->ssh_cipherlist[i] = dlg_listbox_getid(ctrl, dlg, i);
-
+           conf_set_int_int(conf, CONF_ssh_cipherlist, i,
+                            dlg_listbox_getid(ctrl, dlg, i));
     }
 }
 
@@ -245,14 +399,14 @@ static void cipherlist_handler(union control *ctrl, void *dlg,
 static void gsslist_handler(union control *ctrl, void *dlg,
                            void *data, int event)
 {
-    Config *cfg = (Config *)data;
+    Conf *conf = (Conf *)data;
     if (event == EVENT_REFRESH) {
        int i;
 
        dlg_update_start(ctrl, dlg);
        dlg_listbox_clear(ctrl, dlg);
        for (i = 0; i < ngsslibs; i++) {
-           int id = cfg->ssh_gsslist[i];
+           int id = conf_get_int_int(conf, CONF_ssh_gsslist, i);
            assert(id >= 0 && id < ngsslibs);
            dlg_listbox_addwithid(ctrl, dlg, gsslibnames[id], id);
        }
@@ -263,7 +417,8 @@ static void gsslist_handler(union control *ctrl, void *dlg,
 
        /* Update array to match the list box. */
        for (i=0; i < ngsslibs; i++)
-           cfg->ssh_gsslist[i] = dlg_listbox_getid(ctrl, dlg, i);
+           conf_set_int_int(conf, CONF_ssh_gsslist, i,
+                            dlg_listbox_getid(ctrl, dlg, i));
     }
 }
 #endif
@@ -271,7 +426,7 @@ static void gsslist_handler(union control *ctrl, void *dlg,
 static void kexlist_handler(union control *ctrl, void *dlg,
                            void *data, int event)
 {
-    Config *cfg = (Config *)data;
+    Conf *conf = (Conf *)data;
     if (event == EVENT_REFRESH) {
        int i;
 
@@ -288,7 +443,7 @@ static void kexlist_handler(union control *ctrl, void *dlg,
        dlg_update_start(ctrl, dlg);
        dlg_listbox_clear(ctrl, dlg);
        for (i = 0; i < KEX_MAX; i++) {
-           int k = cfg->ssh_kexlist[i];
+           int k = conf_get_int_int(conf, CONF_ssh_kexlist, i);
            int j;
            char *kstr = NULL;
            for (j = 0; j < (sizeof kexes) / (sizeof kexes[0]); j++) {
@@ -306,18 +461,19 @@ static void kexlist_handler(union control *ctrl, void *dlg,
 
        /* Update array to match the list box. */
        for (i=0; i < KEX_MAX; i++)
-           cfg->ssh_kexlist[i] = dlg_listbox_getid(ctrl, dlg, i);
-
+           conf_set_int_int(conf, CONF_ssh_kexlist, i,
+                            dlg_listbox_getid(ctrl, dlg, i));
     }
 }
 
 static void printerbox_handler(union control *ctrl, void *dlg,
                               void *data, int event)
 {
-    Config *cfg = (Config *)data;
+    Conf *conf = (Conf *)data;
     if (event == EVENT_REFRESH) {
        int nprinters, i;
        printer_enum *pe;
+       char *printer;
 
        dlg_update_start(ctrl, dlg);
        /*
@@ -332,50 +488,55 @@ static void printerbox_handler(union control *ctrl, void *dlg,
                dlg_listbox_add(ctrl, dlg, printer_get_name(pe, i));
            printer_finish_enum(pe);
        }
-       dlg_editbox_set(ctrl, dlg,
-                       (*cfg->printer ? cfg->printer :
-                        PRINTER_DISABLED_STRING));
+       printer = conf_get_str(conf, CONF_printer);
+       if (!printer)
+           printer = PRINTER_DISABLED_STRING;
+       dlg_editbox_set(ctrl, dlg, printer);
        dlg_update_done(ctrl, dlg);
     } else if (event == EVENT_VALCHANGE) {
-       dlg_editbox_get(ctrl, dlg, cfg->printer, sizeof(cfg->printer));
-       if (!strcmp(cfg->printer, PRINTER_DISABLED_STRING))
-           *cfg->printer = '\0';
+       char *printer = dlg_editbox_get(ctrl, dlg);
+       if (!strcmp(printer, PRINTER_DISABLED_STRING))
+           printer[0] = '\0';
+       conf_set_str(conf, CONF_printer, printer);
+       sfree(printer);
     }
 }
 
 static void codepage_handler(union control *ctrl, void *dlg,
                             void *data, int event)
 {
-    Config *cfg = (Config *)data;
+    Conf *conf = (Conf *)data;
     if (event == EVENT_REFRESH) {
        int i;
        const char *cp, *thiscp;
        dlg_update_start(ctrl, dlg);
-       thiscp = cp_name(decode_codepage(cfg->line_codepage));
+       thiscp = cp_name(decode_codepage(conf_get_str(conf,
+                                                     CONF_line_codepage)));
        dlg_listbox_clear(ctrl, dlg);
        for (i = 0; (cp = cp_enumerate(i)) != NULL; i++)
            dlg_listbox_add(ctrl, dlg, cp);
        dlg_editbox_set(ctrl, dlg, thiscp);
-       strcpy(cfg->line_codepage, thiscp);
+       conf_set_str(conf, CONF_line_codepage, thiscp);
        dlg_update_done(ctrl, dlg);
     } else if (event == EVENT_VALCHANGE) {
-       dlg_editbox_get(ctrl, dlg, cfg->line_codepage,
-                       sizeof(cfg->line_codepage));
-       strcpy(cfg->line_codepage,
-              cp_name(decode_codepage(cfg->line_codepage)));
+       char *codepage = dlg_editbox_get(ctrl, dlg);
+       conf_set_str(conf, CONF_line_codepage,
+                    cp_name(decode_codepage(codepage)));
+       sfree(codepage);
     }
 }
 
 static void sshbug_handler(union control *ctrl, void *dlg,
                           void *data, int event)
 {
+    Conf *conf = (Conf *)data;
     if (event == EVENT_REFRESH) {
        dlg_update_start(ctrl, dlg);
        dlg_listbox_clear(ctrl, dlg);
        dlg_listbox_addwithid(ctrl, dlg, "Auto", AUTO);
        dlg_listbox_addwithid(ctrl, dlg, "Off", FORCE_OFF);
        dlg_listbox_addwithid(ctrl, dlg, "On", FORCE_ON);
-       switch (*(int *)ATOFFSET(data, ctrl->listbox.context.i)) {
+       switch (conf_get_int(conf, ctrl->listbox.context.i)) {
          case AUTO:      dlg_listbox_select(ctrl, dlg, 0); break;
          case FORCE_OFF: dlg_listbox_select(ctrl, dlg, 1); break;
          case FORCE_ON:  dlg_listbox_select(ctrl, dlg, 2); break;
@@ -387,7 +548,7 @@ static void sshbug_handler(union control *ctrl, void *dlg,
            i = AUTO;
        else
            i = dlg_listbox_getid(ctrl, dlg, i);
-       *(int *)ATOFFSET(data, ctrl->listbox.context.i) = i;
+       conf_set_int(conf, ctrl->listbox.context.i, i);
     }
 }
 
@@ -407,7 +568,7 @@ struct sessionsaver_data {
  */
 static int load_selected_session(struct sessionsaver_data *ssd,
                                 char *savedsession,
-                                void *dlg, Config *cfg, int *maybe_launch)
+                                void *dlg, Conf *conf, int *maybe_launch)
 {
     int i = dlg_listbox_index(ssd->listbox, dlg);
     int isdef;
@@ -416,7 +577,7 @@ static int load_selected_session(struct sessionsaver_data *ssd,
        return 0;
     }
     isdef = !strcmp(ssd->sesslist.sessions[i], "Default Settings");
-    load_settings(ssd->sesslist.sessions[i], cfg);
+    load_settings(ssd->sesslist.sessions[i], conf);
     if (!isdef) {
        strncpy(savedsession, ssd->sesslist.sessions[i],
                SAVEDSESSION_LEN);
@@ -438,7 +599,7 @@ static int load_selected_session(struct sessionsaver_data *ssd,
 static void sessionsaver_handler(union control *ctrl, void *dlg,
                                 void *data, int event)
 {
-    Config *cfg = (Config *)data;
+    Conf *conf = (Conf *)data;
     struct sessionsaver_data *ssd =
        (struct sessionsaver_data *)ctrl->generic.context.p;
     char *savedsession;
@@ -447,7 +608,13 @@ static void sessionsaver_handler(union control *ctrl, void *dlg,
      * The first time we're called in a new dialog, we must
      * allocate space to store the current contents of the saved
      * session edit box (since it must persist even when we switch
-     * panels, but is not part of the Config).
+     * panels, but is not part of the Conf).
+     *
+     * FIXME: this is disgusting, and we'd do much better to have
+     * the persistent storage be dynamically allocated and get rid
+     * of the arbitrary limit SAVEDSESSION_LEN. To do that would
+     * require a means of making sure the memory gets freed at the
+     * appropriate moment.
      */
     if (!ssd->editbox) {
         savedsession = NULL;
@@ -473,8 +640,9 @@ static void sessionsaver_handler(union control *ctrl, void *dlg,
     } else if (event == EVENT_VALCHANGE) {
         int top, bottom, halfway, i;
        if (ctrl == ssd->editbox) {
-           dlg_editbox_get(ctrl, dlg, savedsession,
-                           SAVEDSESSION_LEN);
+           char *tmp = dlg_editbox_get(ctrl, dlg);
+           strncpy(savedsession, tmp, SAVEDSESSION_LEN);
+           sfree(tmp);
            top = ssd->sesslist.nsessions;
            bottom = -1;
            while (top-bottom > 1) {
@@ -503,8 +671,8 @@ static void sessionsaver_handler(union control *ctrl, void *dlg,
             * double-click on the list box _and_ that session
             * contains a hostname.
             */
-           if (load_selected_session(ssd, savedsession, dlg, cfg, &mbl) &&
-               (mbl && ctrl == ssd->listbox && cfg_launchable(cfg))) {
+           if (load_selected_session(ssd, savedsession, dlg, conf, &mbl) &&
+               (mbl && ctrl == ssd->listbox && conf_launchable(conf))) {
                dlg_end(dlg, 1);       /* it's all over, and succeeded */
            }
        } else if (ctrl == ssd->savebutton) {
@@ -525,7 +693,7 @@ static void sessionsaver_handler(union control *ctrl, void *dlg,
                }
            }
             {
-                char *errmsg = save_settings(savedsession, cfg);
+                char *errmsg = save_settings(savedsession, conf);
                 if (errmsg) {
                     dlg_error_msg(dlg, errmsg);
                     sfree(errmsg);
@@ -560,21 +728,23 @@ static void sessionsaver_handler(union control *ctrl, void *dlg,
             * valid host name in it, then load it and go.
             */
            if (dlg_last_focused(ctrl, dlg) == ssd->listbox &&
-               !cfg_launchable(cfg)) {
-               Config cfg2;
+               !conf_launchable(conf)) {
+               Conf *conf2 = conf_new();
                int mbl = FALSE;
                if (!load_selected_session(ssd, savedsession, dlg,
-                                          &cfg2, &mbl)) {
+                                          conf2, &mbl)) {
                    dlg_beep(dlg);
+                   conf_free(conf2);
                    return;
                }
                /* If at this point we have a valid session, go! */
-               if (mbl && cfg_launchable(&cfg2)) {
-                   *cfg = cfg2;       /* structure copy */
-                   cfg->remote_cmd_ptr = NULL;
+               if (mbl && conf_launchable(conf2)) {
+                   conf_copy_into(conf, conf2);
                    dlg_end(dlg, 1);
                } else
                    dlg_beep(dlg);
+
+               conf_free(conf2);
                 return;
            }
 
@@ -582,7 +752,7 @@ static void sessionsaver_handler(union control *ctrl, void *dlg,
             * Otherwise, do the normal thing: if we have a valid
             * session, get going.
             */
-           if (cfg_launchable(cfg)) {
+           if (conf_launchable(conf)) {
                dlg_end(dlg, 1);
            } else
                dlg_beep(dlg);
@@ -599,7 +769,7 @@ struct charclass_data {
 static void charclass_handler(union control *ctrl, void *dlg,
                              void *data, int event)
 {
-    Config *cfg = (Config *)data;
+    Conf *conf = (Conf *)data;
     struct charclass_data *ccd =
        (struct charclass_data *)ctrl->generic.context.p;
 
@@ -611,20 +781,22 @@ static void charclass_handler(union control *ctrl, void *dlg,
            for (i = 0; i < 128; i++) {
                char str[100];
                sprintf(str, "%d\t(0x%02X)\t%c\t%d", i, i,
-                       (i >= 0x21 && i != 0x7F) ? i : ' ', cfg->wordness[i]);
+                       (i >= 0x21 && i != 0x7F) ? i : ' ',
+                       conf_get_int_int(conf, CONF_wordness, i));
                dlg_listbox_add(ctrl, dlg, str);
            }
            dlg_update_done(ctrl, dlg);
        }
     } else if (event == EVENT_ACTION) {
        if (ctrl == ccd->button) {
-           char str[100];
+           char *str;
            int i, n;
-           dlg_editbox_get(ccd->editbox, dlg, str, sizeof(str));
+           str = dlg_editbox_get(ccd->editbox, dlg);
            n = atoi(str);
+           sfree(str);
            for (i = 0; i < 128; i++) {
                if (dlg_listbox_issel(ccd->listbox, dlg, i))
-                   cfg->wordness[i] = n;
+                   conf_set_int_int(conf, CONF_wordness, i, n);
            }
            dlg_refresh(ccd->listbox, dlg);
        }
@@ -652,7 +824,7 @@ static const char *const colours[] = {
 static void colour_handler(union control *ctrl, void *dlg,
                            void *data, int event)
 {
-    Config *cfg = (Config *)data;
+    Conf *conf = (Conf *)data;
     struct colour_data *cd =
        (struct colour_data *)ctrl->generic.context.p;
     int update = FALSE, clear = FALSE, r, g, b;
@@ -676,31 +848,32 @@ static void colour_handler(union control *ctrl, void *dlg,
                clear = TRUE;
            } else {
                clear = FALSE;
-               r = cfg->colours[i][0];
-               g = cfg->colours[i][1];
-               b = cfg->colours[i][2];
+               r = conf_get_int_int(conf, CONF_colours, i*3+0);
+               g = conf_get_int_int(conf, CONF_colours, i*3+0);
+               b = conf_get_int_int(conf, CONF_colours, i*3+0);
            }
            update = TRUE;
        }
     } else if (event == EVENT_VALCHANGE) {
        if (ctrl == cd->redit || ctrl == cd->gedit || ctrl == cd->bedit) {
            /* The user has changed the colour using the edit boxes. */
-           char buf[80];
+           char *str;
            int i, cval;
 
-           dlg_editbox_get(ctrl, dlg, buf, lenof(buf));
-           cval = atoi(buf);
+           str = dlg_editbox_get(ctrl, dlg);
+           cval = atoi(str);
+           sfree(str);
            if (cval > 255) cval = 255;
            if (cval < 0)   cval = 0;
 
            i = dlg_listbox_index(cd->listbox, dlg);
            if (i >= 0) {
                if (ctrl == cd->redit)
-                   cfg->colours[i][0] = cval;
+                   conf_set_int_int(conf, CONF_colours, i*3+0, cval);
                else if (ctrl == cd->gedit)
-                   cfg->colours[i][1] = cval;
+                   conf_set_int_int(conf, CONF_colours, i*3+1, cval);
                else if (ctrl == cd->bedit)
-                   cfg->colours[i][2] = cval;
+                   conf_set_int_int(conf, CONF_colours, i*3+2, cval);
            }
        }
     } else if (event == EVENT_ACTION) {
@@ -716,9 +889,9 @@ static void colour_handler(union control *ctrl, void *dlg,
             * pick up the results.
             */
            dlg_coloursel_start(ctrl, dlg,
-                               cfg->colours[i][0],
-                               cfg->colours[i][1],
-                               cfg->colours[i][2]);
+                               conf_get_int_int(conf, CONF_colours, i*3+0),
+                               conf_get_int_int(conf, CONF_colours, i*3+1),
+                               conf_get_int_int(conf, CONF_colours, i*3+2));
        }
     } else if (event == EVENT_CALLBACK) {
        if (ctrl == cd->button) {
@@ -729,9 +902,9 @@ static void colour_handler(union control *ctrl, void *dlg,
             * selector did nothing (user hit Cancel, for example).
             */
            if (dlg_coloursel_results(ctrl, dlg, &r, &g, &b)) {
-               cfg->colours[i][0] = r;
-               cfg->colours[i][1] = g;
-               cfg->colours[i][2] = b;
+               conf_set_int_int(conf, CONF_colours, i*3+0, r);
+               conf_set_int_int(conf, CONF_colours, i*3+0, g);
+               conf_set_int_int(conf, CONF_colours, i*3+0, b);
                clear = FALSE;
                update = TRUE;
            }
@@ -760,22 +933,21 @@ struct ttymodes_data {
 static void ttymodes_handler(union control *ctrl, void *dlg,
                             void *data, int event)
 {
-    Config *cfg = (Config *)data;
+    Conf *conf = (Conf *)data;
     struct ttymodes_data *td =
        (struct ttymodes_data *)ctrl->generic.context.p;
 
     if (event == EVENT_REFRESH) {
        if (ctrl == td->listbox) {
-           char *p = cfg->ttymodes;
+           char *key, *val;
            dlg_update_start(ctrl, dlg);
            dlg_listbox_clear(ctrl, dlg);
-           while (*p) {
-               int tabpos = strchr(p, '\t') - p;
-               char *disp = dupprintf("%.*s\t%s", tabpos, p,
-                                      (p[tabpos+1] == 'A') ? "(auto)" :
-                                      p+tabpos+2);
+           for (val = conf_get_str_strs(conf, CONF_ttymodes, NULL, &key);
+                val != NULL;
+                val = conf_get_str_strs(conf, CONF_ttymodes, key, &key)) {
+               char *disp = dupprintf("%s\t%s", key,
+                                      (val[0] == 'A') ? "(auto)" : val+1);
                dlg_listbox_add(ctrl, dlg, disp);
-               p += strlen(p) + 1;
                sfree(disp);
            }
            dlg_update_done(ctrl, dlg);
@@ -795,73 +967,47 @@ static void ttymodes_handler(union control *ctrl, void *dlg,
            int ind = dlg_listbox_index(td->modelist, dlg);
            if (ind >= 0) {
                char type = dlg_radiobutton_get(td->valradio, dlg) ? 'V' : 'A';
-               int slen, left;
-               char *p, str[lenof(cfg->ttymodes)];
+               const char *key;
+               char *str, *val;
                /* Construct new entry */
-               memset(str, 0, lenof(str));
-               strncpy(str, ttymodes[ind], lenof(str)-3);
-               slen = strlen(str);
-               str[slen] = '\t';
-               str[slen+1] = type;
-               slen += 2;
-               if (type == 'V') {
-                   dlg_editbox_get(td->valbox, dlg, str+slen, lenof(str)-slen);
-               }
-               /* Find end of list, deleting any existing instance */
-               p = cfg->ttymodes;
-               left = lenof(cfg->ttymodes);
-               while (*p) {
-                   int t = strchr(p, '\t') - p;
-                   if (t == strlen(ttymodes[ind]) &&
-                       strncmp(p, ttymodes[ind], t) == 0) {
-                       memmove(p, p+strlen(p)+1, left - (strlen(p)+1));
-                       continue;
-                   }
-                   left -= strlen(p) + 1;
-                   p    += strlen(p) + 1;
-               }
-               /* Append new entry */
-               memset(p, 0, left);
-               strncpy(p, str, left - 2);
+               key = ttymodes[ind];
+               str = dlg_editbox_get(td->valbox, dlg);
+               val = dupprintf("%c%s", type, str);
+               sfree(str);
+               conf_set_str_str(conf, CONF_ttymodes, key, val);
+               sfree(val);
                dlg_refresh(td->listbox, dlg);
            } else
                dlg_beep(dlg);
        } else if (ctrl == td->rembutton) {
-           char *p = cfg->ttymodes;
-           int i = 0, len = lenof(cfg->ttymodes);
-           while (*p) {
-               int multisel = dlg_listbox_index(td->listbox, dlg) < 0;
+           int i = 0;
+           char *key, *val;
+           int multisel = dlg_listbox_index(td->listbox, dlg) < 0;
+           for (val = conf_get_str_strs(conf, CONF_ttymodes, NULL, &key);
+                val != NULL;
+                val = conf_get_str_strs(conf, CONF_ttymodes, key, &key)) {
                if (dlg_listbox_issel(td->listbox, dlg, i)) {
                    if (!multisel) {
                        /* Populate controls with entry we're about to
                         * delete, for ease of editing.
                         * (If multiple entries were selected, don't
                         * touch the controls.) */
-                       char *val = strchr(p, '\t');
-                       if (val) {
-                           int ind = 0;
-                           val++;
-                           while (ttymodes[ind]) {
-                               if (strlen(ttymodes[ind]) == val-p-1 &&
-                                   !strncmp(ttymodes[ind], p, val-p-1))
-                                   break;
-                               ind++;
-                           }
-                           dlg_listbox_select(td->modelist, dlg, ind);
-                           dlg_radiobutton_set(td->valradio, dlg,
-                                               (*val == 'V'));
-                           dlg_editbox_set(td->valbox, dlg, val+1);
+                       int ind = 0;
+                       val++;
+                       while (ttymodes[ind]) {
+                           if (!strcmp(ttymodes[ind], key))
+                               break;
+                           ind++;
                        }
+                       dlg_listbox_select(td->modelist, dlg, ind);
+                       dlg_radiobutton_set(td->valradio, dlg,
+                                           (*val == 'V'));
+                       dlg_editbox_set(td->valbox, dlg, val+1);
                    }
-                   memmove(p, p+strlen(p)+1, len - (strlen(p)+1));
-                   i++;
-                   continue;
+                   conf_del_str_str(conf, CONF_ttymodes, key);
                }
-               len -= strlen(p) + 1;
-               p   += strlen(p) + 1;
                i++;
            }
-           memset(p, 0, lenof(cfg->ttymodes) - len);
            dlg_refresh(td->listbox, dlg);
        }
     }
@@ -874,96 +1020,67 @@ struct environ_data {
 static void environ_handler(union control *ctrl, void *dlg,
                            void *data, int event)
 {
-    Config *cfg = (Config *)data;
+    Conf *conf = (Conf *)data;
     struct environ_data *ed =
        (struct environ_data *)ctrl->generic.context.p;
 
     if (event == EVENT_REFRESH) {
        if (ctrl == ed->listbox) {
-           char *p = cfg->environmt;
+           char *key, *val;
            dlg_update_start(ctrl, dlg);
            dlg_listbox_clear(ctrl, dlg);
-           while (*p) {
+           for (val = conf_get_str_strs(conf, CONF_environmt, NULL, &key);
+                val != NULL;
+                val = conf_get_str_strs(conf, CONF_environmt, key, &key)) {
+               char *p = dupprintf("%s\t%s", key, val);
                dlg_listbox_add(ctrl, dlg, p);
-               p += strlen(p) + 1;
+               sfree(p);
            }
            dlg_update_done(ctrl, dlg);
        }
     } else if (event == EVENT_ACTION) {
        if (ctrl == ed->addbutton) {
-           char str[sizeof(cfg->environmt)];
-           char *p;
-           dlg_editbox_get(ed->varbox, dlg, str, sizeof(str)-1);
-           if (!*str) {
+           char *key, *val, *str;
+           key = dlg_editbox_get(ed->varbox, dlg);
+           if (!*key) {
+               sfree(key);
                dlg_beep(dlg);
                return;
            }
-           p = str + strlen(str);
-           *p++ = '\t';
-           dlg_editbox_get(ed->valbox, dlg, p, sizeof(str)-1 - (p - str));
-           if (!*p) {
+           val = dlg_editbox_get(ed->valbox, dlg);
+           if (!*val) {
+               sfree(key);
+               sfree(val);
                dlg_beep(dlg);
                return;
            }
-           p = cfg->environmt;
-           while (*p) {
-               while (*p)
-                   p++;
-               p++;
-           }
-           if ((p - cfg->environmt) + strlen(str) + 2 <
-               sizeof(cfg->environmt)) {
-               strcpy(p, str);
-               p[strlen(str) + 1] = '\0';
-               dlg_listbox_add(ed->listbox, dlg, str);
-               dlg_editbox_set(ed->varbox, dlg, "");
-               dlg_editbox_set(ed->valbox, dlg, "");
-           } else {
-               dlg_error_msg(dlg, "Environment too big");
-           }
+           conf_set_str_str(conf, CONF_environmt, key, val);
+           str = dupcat(key, "\t", val, NULL);
+           dlg_editbox_set(ed->varbox, dlg, "");
+           dlg_editbox_set(ed->valbox, dlg, "");
+           sfree(str);
+           sfree(key);
+           sfree(val);
+           dlg_refresh(ed->listbox, dlg);
        } else if (ctrl == ed->rembutton) {
            int i = dlg_listbox_index(ed->listbox, dlg);
            if (i < 0) {
                dlg_beep(dlg);
            } else {
-               char *p, *q, *str;
-
-               dlg_listbox_del(ed->listbox, dlg, i);
-               p = cfg->environmt;
-               while (i > 0) {
-                   if (!*p)
-                       goto disaster;
-                   while (*p)
-                       p++;
-                   p++;
-                   i--;
-               }
-               q = p;
-               if (!*p)
-                   goto disaster;
-               /* Populate controls with the entry we're about to delete
-                * for ease of editing */
-               str = p;
-               p = strchr(p, '\t');
-               if (!p)
-                   goto disaster;
-               *p = '\0';
-               dlg_editbox_set(ed->varbox, dlg, str);
-               p++;
-               str = p;
-               dlg_editbox_set(ed->valbox, dlg, str);
-               p = strchr(p, '\0');
-               if (!p)
-                   goto disaster;
-               p++;
-               while (*p) {
-                   while (*p)
-                       *q++ = *p++;
-                   *q++ = *p++;
+               char *key, *val;
+
+               key = conf_get_str_nthstrkey(conf, CONF_environmt, i);
+               if (key) {
+                   /* Populate controls with the entry we're about to delete
+                    * for ease of editing */
+                   val = conf_get_str_str(conf, CONF_environmt, key);
+                   dlg_editbox_set(ed->varbox, dlg, key);
+                   dlg_editbox_set(ed->valbox, dlg, val);
+                   /* And delete it */
+                   conf_del_str_str(conf, CONF_environmt, key);
                }
-               *q = '\0';
-               disaster:;
            }
+           dlg_refresh(ed->listbox, dlg);
        }
     }
 }
@@ -979,18 +1096,25 @@ struct portfwd_data {
 static void portfwd_handler(union control *ctrl, void *dlg,
                            void *data, int event)
 {
-    Config *cfg = (Config *)data;
+    Conf *conf = (Conf *)data;
     struct portfwd_data *pfd =
        (struct portfwd_data *)ctrl->generic.context.p;
 
     if (event == EVENT_REFRESH) {
        if (ctrl == pfd->listbox) {
-           char *p = cfg->portfwd;
+           char *key, *val;
            dlg_update_start(ctrl, dlg);
            dlg_listbox_clear(ctrl, dlg);
-           while (*p) {
+           for (val = conf_get_str_strs(conf, CONF_portfwd, NULL, &key);
+                val != NULL;
+                val = conf_get_str_strs(conf, CONF_portfwd, key, &key)) {
+               char *p;
+                if (!strcmp(val, "D"))
+                    p = dupprintf("D%s\t", key+1);
+                else
+                    p = dupprintf("%s\t%s", key, val);
                dlg_listbox_add(ctrl, dlg, p);
-               p += strlen(p) + 1;
+               sfree(p);
            }
            dlg_update_done(ctrl, dlg);
        } else if (ctrl == pfd->direction) {
@@ -1005,137 +1129,111 @@ static void portfwd_handler(union control *ctrl, void *dlg,
        }
     } else if (event == EVENT_ACTION) {
        if (ctrl == pfd->addbutton) {
-           char str[sizeof(cfg->portfwd)];
-           char *p;
-           int i, type;
-           int whichbutton;
+           char *family, *type, *src, *key, *val;
+           int i, whichbutton;
 
            i = 0;
 #ifndef NO_IPV6
            whichbutton = dlg_radiobutton_get(pfd->addressfamily, dlg);
            if (whichbutton == 1)
-               str[i++] = '4';
+               family = "4";
            else if (whichbutton == 2)
-               str[i++] = '6';
+               family = "6";
+           else
+               family = "";
 #endif
 
            whichbutton = dlg_radiobutton_get(pfd->direction, dlg);
            if (whichbutton == 0)
-               type = 'L';
+               type = "L";
            else if (whichbutton == 1)
-               type = 'R';
+               type = "R";
            else
-               type = 'D';
-           str[i++] = type;
+               type = "D";
 
-           dlg_editbox_get(pfd->sourcebox, dlg, str+i, sizeof(str) - i);
-           if (!str[i]) {
+           src = dlg_editbox_get(pfd->sourcebox, dlg);
+           if (!*src) {
                dlg_error_msg(dlg, "You need to specify a source port number");
+               sfree(src);
                return;
            }
-           p = str + strlen(str);
-           if (type != 'D') {
-               *p++ = '\t';
-               dlg_editbox_get(pfd->destbox, dlg, p,
-                               sizeof(str) - (p - str));
-               if (!*p || !strchr(p, ':')) {
+           if (*type != 'D') {
+               val = dlg_editbox_get(pfd->destbox, dlg);
+               if (!*val || !strchr(val, ':')) {
                    dlg_error_msg(dlg,
                                  "You need to specify a destination address\n"
                                  "in the form \"host.name:port\"");
+                   sfree(src);
+                   sfree(val);
                    return;
                }
-           } else
-               *p = '\0';
-           p = cfg->portfwd;
-           while (*p) {
-               if (strcmp(p,str) == 0) {
-                   dlg_error_msg(dlg, "Specified forwarding already exists");
-                   break;
-               }
-               while (*p)
-                   p++;
-               p++;
-           }
-           if (!*p) {
-               if ((p - cfg->portfwd) + strlen(str) + 2 <=
-                   sizeof(cfg->portfwd)) {
-                   strcpy(p, str);
-                   p[strlen(str) + 1] = '\0';
-                   dlg_listbox_add(pfd->listbox, dlg, str);
-                   dlg_editbox_set(pfd->sourcebox, dlg, "");
-                   dlg_editbox_set(pfd->destbox, dlg, "");
-               } else {
-                   dlg_error_msg(dlg, "Too many forwardings");
-               }
+           } else {
+                type = "L";
+               val = dupstr("D");     /* special case */
+            }
+
+           key = dupcat(family, type, src, NULL);
+           sfree(src);
+
+           if (conf_get_str_str_opt(conf, CONF_portfwd, key)) {
+               dlg_error_msg(dlg, "Specified forwarding already exists");
+           } else {
+               conf_set_str_str(conf, CONF_portfwd, key, val);
            }
+
+           sfree(key);
+           sfree(val);
+           dlg_refresh(pfd->listbox, dlg);
        } else if (ctrl == pfd->rembutton) {
            int i = dlg_listbox_index(pfd->listbox, dlg);
-           if (i < 0)
+           if (i < 0) {
                dlg_beep(dlg);
-           else {
-               char *p, *q, *src, *dst;
-               char dir;
-
-               dlg_listbox_del(pfd->listbox, dlg, i);
-               p = cfg->portfwd;
-               while (i > 0) {
-                   if (!*p)
-                       goto disaster2;
-                   while (*p)
-                       p++;
-                   p++;
-                   i--;
-               }
-               q = p;
-               if (!*p)
-                   goto disaster2;
-               /* Populate the controls with the entry we're about to
-                * delete, for ease of editing. */
-               {
+           } else {
+               char *key, *val, *p;
+
+               key = conf_get_str_nthstrkey(conf, CONF_portfwd, i);
+               if (key) {
                    static const char *const afs = "A46";
-                   char *afp = strchr(afs, *p);
+                   static const char *const dirs = "LRD";
+                   char *afp;
+                   int dir;
 #ifndef NO_IPV6
-                   int idx = afp ? afp-afs : 0;
+                   int idx;
+#endif
+
+                   /* Populate controls with the entry we're about to delete
+                    * for ease of editing */
+                   p = key;
+
+                   afp = strchr(afs, *p);
+#ifndef NO_IPV6
+                   idx = afp ? afp-afs : 0;
 #endif
                    if (afp)
                        p++;
 #ifndef NO_IPV6
                    dlg_radiobutton_set(pfd->addressfamily, dlg, idx);
 #endif
-               }
-               {
-                   static const char *const dirs = "LRD";
+
                    dir = *p;
+
+                    val = conf_get_str_str(conf, CONF_portfwd, key);
+                   if (!strcmp(val, "D")) {
+                        dir = 'D';
+                       val = "";
+                   }
+
                    dlg_radiobutton_set(pfd->direction, dlg,
                                        strchr(dirs, dir) - dirs);
-               }
-               p++;
-               if (dir != 'D') {
-                   src = p;
-                   p = strchr(p, '\t');
-                   if (!p)
-                       goto disaster2;
-                   *p = '\0';
                    p++;
-                   dst = p;
-               } else {
-                   src = p;
-                   dst = "";
-               }
-               p = strchr(p, '\0');
-               if (!p)
-                   goto disaster2;
-               dlg_editbox_set(pfd->sourcebox, dlg, src);
-               dlg_editbox_set(pfd->destbox, dlg, dst);
-               p++;
-               while (*p) {
-                   while (*p)
-                       *q++ = *p++;
-                   *q++ = *p++;
+
+                   dlg_editbox_set(pfd->sourcebox, dlg, p);
+                   dlg_editbox_set(pfd->destbox, dlg, val);
+                   /* And delete it */
+                   conf_del_str_str(conf, CONF_portfwd, key);
                }
-               *q = '\0';
-               disaster2:;
            }
+           dlg_refresh(pfd->listbox, dlg);
        }
     }
 }
@@ -1276,8 +1374,8 @@ void setup_config_box(struct controlbox *b, int midsession,
     s = ctrl_getset(b, "Session", "otheropts", NULL);
     c = ctrl_radiobuttons(s, "Close window on exit:", 'x', 4,
                          HELPCTX(session_coe),
-                         dlg_stdradiobutton_handler,
-                         I(offsetof(Config, close_on_exit)),
+                         conf_radiobutton_handler,
+                         I(CONF_close_on_exit),
                          "Always", I(FORCE_ON),
                          "Never", I(FORCE_OFF),
                          "Only on clean exit", I(AUTO), NULL);
@@ -1305,7 +1403,7 @@ void setup_config_box(struct controlbox *b, int midsession,
        ctrl_radiobuttons(s, "Session logging:", NO_SHORTCUT, 2,
                          HELPCTX(logging_main),
                          loggingbuttons_handler,
-                         I(offsetof(Config, logtype)),
+                         I(CONF_logtype),
                          "None", 't', I(LGTYP_NONE),
                          "Printable output", 'p', I(LGTYP_ASCII),
                          "All session output", 'l', I(LGTYP_DEBUG),
@@ -1316,19 +1414,19 @@ void setup_config_box(struct controlbox *b, int midsession,
     ctrl_filesel(s, "Log file name:", 'f',
                 NULL, TRUE, "Select session log file name",
                 HELPCTX(logging_filename),
-                dlg_stdfilesel_handler, I(offsetof(Config, logfilename)));
+                conf_filesel_handler, I(CONF_logfilename));
     ctrl_text(s, "(Log file name can contain &Y, &M, &D for date,"
              " &T for time, and &H for host name)",
              HELPCTX(logging_filename));
     ctrl_radiobuttons(s, "What to do if the log file already exists:", 'e', 1,
                      HELPCTX(logging_exists),
-                     dlg_stdradiobutton_handler, I(offsetof(Config,logxfovr)),
+                     conf_radiobutton_handler, I(CONF_logxfovr),
                      "Always overwrite it", I(LGXF_OVR),
                      "Always append to the end of it", I(LGXF_APN),
                      "Ask the user every time", I(LGXF_ASK), NULL);
     ctrl_checkbox(s, "Flush log file frequently", 'u',
                 HELPCTX(logging_flush),
-                dlg_stdcheckbox_handler, I(offsetof(Config,logflush)));
+                conf_checkbox_handler, I(CONF_logflush));
 
     if ((midsession && protocol == PROT_SSH) ||
        (!midsession && backend_from_proto(PROT_SSH))) {
@@ -1336,10 +1434,10 @@ void setup_config_box(struct controlbox *b, int midsession,
                        "Options specific to SSH packet logging");
        ctrl_checkbox(s, "Omit known password fields", 'k',
                      HELPCTX(logging_ssh_omit_password),
-                     dlg_stdcheckbox_handler, I(offsetof(Config,logomitpass)));
+                     conf_checkbox_handler, I(CONF_logomitpass));
        ctrl_checkbox(s, "Omit session data", 'd',
                      HELPCTX(logging_ssh_omit_data),
-                     dlg_stdcheckbox_handler, I(offsetof(Config,logomitdata)));
+                     conf_checkbox_handler, I(CONF_logomitdata));
     }
 
     /*
@@ -1350,37 +1448,36 @@ void setup_config_box(struct controlbox *b, int midsession,
     s = ctrl_getset(b, "Terminal", "general", "Set various terminal options");
     ctrl_checkbox(s, "Auto wrap mode initially on", 'w',
                  HELPCTX(terminal_autowrap),
-                 dlg_stdcheckbox_handler, I(offsetof(Config,wrap_mode)));
+                 conf_checkbox_handler, I(CONF_wrap_mode));
     ctrl_checkbox(s, "DEC Origin Mode initially on", 'd',
                  HELPCTX(terminal_decom),
-                 dlg_stdcheckbox_handler, I(offsetof(Config,dec_om)));
+                 conf_checkbox_handler, I(CONF_dec_om));
     ctrl_checkbox(s, "Implicit CR in every LF", 'r',
                  HELPCTX(terminal_lfhascr),
-                 dlg_stdcheckbox_handler, I(offsetof(Config,lfhascr)));
+                 conf_checkbox_handler, I(CONF_lfhascr));
     ctrl_checkbox(s, "Implicit LF in every CR", 'f',
                  HELPCTX(terminal_crhaslf),
-                 dlg_stdcheckbox_handler, I(offsetof(Config,crhaslf)));
+                 conf_checkbox_handler, I(CONF_crhaslf));
     ctrl_checkbox(s, "Use background colour to erase screen", 'e',
                  HELPCTX(terminal_bce),
-                 dlg_stdcheckbox_handler, I(offsetof(Config,bce)));
+                 conf_checkbox_handler, I(CONF_bce));
     ctrl_checkbox(s, "Enable blinking text", 'n',
                  HELPCTX(terminal_blink),
-                 dlg_stdcheckbox_handler, I(offsetof(Config,blinktext)));
+                 conf_checkbox_handler, I(CONF_blinktext));
     ctrl_editbox(s, "Answerback to ^E:", 's', 100,
                 HELPCTX(terminal_answerback),
-                dlg_stdeditbox_handler, I(offsetof(Config,answerback)),
-                I(sizeof(((Config *)0)->answerback)));
+                conf_editbox_handler, I(CONF_answerback), I(1));
 
     s = ctrl_getset(b, "Terminal", "ldisc", "Line discipline options");
     ctrl_radiobuttons(s, "Local echo:", 'l', 3,
                      HELPCTX(terminal_localecho),
-                     dlg_stdradiobutton_handler,I(offsetof(Config,localecho)),
+                     conf_radiobutton_handler,I(CONF_localecho),
                      "Auto", I(AUTO),
                      "Force on", I(FORCE_ON),
                      "Force off", I(FORCE_OFF), NULL);
     ctrl_radiobuttons(s, "Local line editing:", 't', 3,
                      HELPCTX(terminal_localedit),
-                     dlg_stdradiobutton_handler,I(offsetof(Config,localedit)),
+                     conf_radiobutton_handler,I(CONF_localedit),
                      "Auto", I(AUTO),
                      "Force on", I(FORCE_ON),
                      "Force off", I(FORCE_OFF), NULL);
@@ -1400,18 +1497,18 @@ void setup_config_box(struct controlbox *b, int midsession,
                    "Change the sequences sent by:");
     ctrl_radiobuttons(s, "The Backspace key", 'b', 2,
                      HELPCTX(keyboard_backspace),
-                     dlg_stdradiobutton_handler,
-                     I(offsetof(Config, bksp_is_delete)),
+                     conf_radiobutton_handler,
+                     I(CONF_bksp_is_delete),
                      "Control-H", I(0), "Control-? (127)", I(1), NULL);
     ctrl_radiobuttons(s, "The Home and End keys", 'e', 2,
                      HELPCTX(keyboard_homeend),
-                     dlg_stdradiobutton_handler,
-                     I(offsetof(Config, rxvt_homeend)),
+                     conf_radiobutton_handler,
+                     I(CONF_rxvt_homeend),
                      "Standard", I(0), "rxvt", I(1), NULL);
     ctrl_radiobuttons(s, "The Function keys and keypad", 'f', 3,
                      HELPCTX(keyboard_funkeys),
-                     dlg_stdradiobutton_handler,
-                     I(offsetof(Config, funky_type)),
+                     conf_radiobutton_handler,
+                     I(CONF_funky_type),
                      "ESC[n~", I(0), "Linux", I(1), "Xterm R6", I(2),
                      "VT400", I(3), "VT100+", I(4), "SCO", I(5), NULL);
 
@@ -1419,8 +1516,8 @@ void setup_config_box(struct controlbox *b, int midsession,
                    "Application keypad settings:");
     ctrl_radiobuttons(s, "Initial state of cursor keys:", 'r', 3,
                      HELPCTX(keyboard_appcursor),
-                     dlg_stdradiobutton_handler,
-                     I(offsetof(Config, app_cursor)),
+                     conf_radiobutton_handler,
+                     I(CONF_app_cursor),
                      "Normal", I(0), "Application", I(1), NULL);
     ctrl_radiobuttons(s, "Initial state of numeric keypad:", 'n', 3,
                      HELPCTX(keyboard_appkeypad),
@@ -1437,7 +1534,7 @@ void setup_config_box(struct controlbox *b, int midsession,
     s = ctrl_getset(b, "Terminal/Bell", "style", "Set the style of bell");
     ctrl_radiobuttons(s, "Action to happen when a bell occurs:", 'b', 1,
                      HELPCTX(bell_style),
-                     dlg_stdradiobutton_handler, I(offsetof(Config, beep)),
+                     conf_radiobutton_handler, I(CONF_beep),
                      "None (bell disabled)", I(BELL_DISABLED),
                      "Make default system alert sound", I(BELL_DEFAULT),
                      "Visual bell (flash window)", I(BELL_VISUAL), NULL);
@@ -1446,19 +1543,19 @@ void setup_config_box(struct controlbox *b, int midsession,
                    "Control the bell overload behaviour");
     ctrl_checkbox(s, "Bell is temporarily disabled when over-used", 'd',
                  HELPCTX(bell_overload),
-                 dlg_stdcheckbox_handler, I(offsetof(Config,bellovl)));
+                 conf_checkbox_handler, I(CONF_bellovl));
     ctrl_editbox(s, "Over-use means this many bells...", 'm', 20,
                 HELPCTX(bell_overload),
-                dlg_stdeditbox_handler, I(offsetof(Config,bellovl_n)), I(-1));
+                conf_editbox_handler, I(CONF_bellovl_n), I(-1));
     ctrl_editbox(s, "... in this many seconds", 't', 20,
                 HELPCTX(bell_overload),
-                dlg_stdeditbox_handler, I(offsetof(Config,bellovl_t)),
+                conf_editbox_handler, I(CONF_bellovl_t),
                 I(-TICKSPERSEC));
     ctrl_text(s, "The bell is re-enabled after a few seconds of silence.",
              HELPCTX(bell_overload));
     ctrl_editbox(s, "Seconds of silence required", 's', 20,
                 HELPCTX(bell_overload),
-                dlg_stdeditbox_handler, I(offsetof(Config,bellovl_s)),
+                conf_editbox_handler, I(CONF_bellovl_s),
                 I(-TICKSPERSEC));
 
     /*
@@ -1470,43 +1567,43 @@ void setup_config_box(struct controlbox *b, int midsession,
     s = ctrl_getset(b, "Terminal/Features", "main", NULL);
     ctrl_checkbox(s, "Disable application cursor keys mode", 'u',
                  HELPCTX(features_application),
-                 dlg_stdcheckbox_handler, I(offsetof(Config,no_applic_c)));
+                 conf_checkbox_handler, I(CONF_no_applic_c));
     ctrl_checkbox(s, "Disable application keypad mode", 'k',
                  HELPCTX(features_application),
-                 dlg_stdcheckbox_handler, I(offsetof(Config,no_applic_k)));
+                 conf_checkbox_handler, I(CONF_no_applic_k));
     ctrl_checkbox(s, "Disable xterm-style mouse reporting", 'x',
                  HELPCTX(features_mouse),
-                 dlg_stdcheckbox_handler, I(offsetof(Config,no_mouse_rep)));
+                 conf_checkbox_handler, I(CONF_no_mouse_rep));
     ctrl_checkbox(s, "Disable remote-controlled terminal resizing", 's',
                  HELPCTX(features_resize),
-                 dlg_stdcheckbox_handler,
-                 I(offsetof(Config,no_remote_resize)));
+                 conf_checkbox_handler,
+                 I(CONF_no_remote_resize));
     ctrl_checkbox(s, "Disable switching to alternate terminal screen", 'w',
                  HELPCTX(features_altscreen),
-                 dlg_stdcheckbox_handler, I(offsetof(Config,no_alt_screen)));
+                 conf_checkbox_handler, I(CONF_no_alt_screen));
     ctrl_checkbox(s, "Disable remote-controlled window title changing", 't',
                  HELPCTX(features_retitle),
-                 dlg_stdcheckbox_handler,
-                 I(offsetof(Config,no_remote_wintitle)));
+                 conf_checkbox_handler,
+                 I(CONF_no_remote_wintitle));
     ctrl_radiobuttons(s, "Response to remote title query (SECURITY):", 'q', 3,
                      HELPCTX(features_qtitle),
-                     dlg_stdradiobutton_handler,
-                     I(offsetof(Config,remote_qtitle_action)),
+                     conf_radiobutton_handler,
+                     I(CONF_remote_qtitle_action),
                      "None", I(TITLE_NONE),
                      "Empty string", I(TITLE_EMPTY),
                      "Window title", I(TITLE_REAL), NULL);
     ctrl_checkbox(s, "Disable destructive backspace on server sending ^?",'b',
                  HELPCTX(features_dbackspace),
-                 dlg_stdcheckbox_handler, I(offsetof(Config,no_dbackspace)));
+                 conf_checkbox_handler, I(CONF_no_dbackspace));
     ctrl_checkbox(s, "Disable remote-controlled character set configuration",
-                 'r', HELPCTX(features_charset), dlg_stdcheckbox_handler,
-                 I(offsetof(Config,no_remote_charset)));
+                 'r', HELPCTX(features_charset), conf_checkbox_handler,
+                 I(CONF_no_remote_charset));
     ctrl_checkbox(s, "Disable Arabic text shaping",
-                 'l', HELPCTX(features_arabicshaping), dlg_stdcheckbox_handler,
-                 I(offsetof(Config, arabicshaping)));
+                 'l', HELPCTX(features_arabicshaping), conf_checkbox_handler,
+                 I(CONF_arabicshaping));
     ctrl_checkbox(s, "Disable bidirectional text display",
-                 'd', HELPCTX(features_bidi), dlg_stdcheckbox_handler,
-                 I(offsetof(Config, bidi)));
+                 'd', HELPCTX(features_bidi), conf_checkbox_handler,
+                 I(CONF_bidi));
 
     /*
      * The Window panel.
@@ -1519,11 +1616,11 @@ void setup_config_box(struct controlbox *b, int midsession,
     ctrl_columns(s, 2, 50, 50);
     c = ctrl_editbox(s, "Columns", 'm', 100,
                     HELPCTX(window_size),
-                    dlg_stdeditbox_handler, I(offsetof(Config,width)), I(-1));
+                    conf_editbox_handler, I(CONF_width), I(-1));
     c->generic.column = 0;
     c = ctrl_editbox(s, "Rows", 'r', 100,
                     HELPCTX(window_size),
-                    dlg_stdeditbox_handler, I(offsetof(Config,height)),I(-1));
+                    conf_editbox_handler, I(CONF_height),I(-1));
     c->generic.column = 1;
     ctrl_columns(s, 1, 100);
 
@@ -1531,20 +1628,20 @@ void setup_config_box(struct controlbox *b, int midsession,
                    "Control the scrollback in the window");
     ctrl_editbox(s, "Lines of scrollback", 's', 50,
                 HELPCTX(window_scrollback),
-                dlg_stdeditbox_handler, I(offsetof(Config,savelines)), I(-1));
+                conf_editbox_handler, I(CONF_savelines), I(-1));
     ctrl_checkbox(s, "Display scrollbar", 'd',
                  HELPCTX(window_scrollback),
-                 dlg_stdcheckbox_handler, I(offsetof(Config,scrollbar)));
+                 conf_checkbox_handler, I(CONF_scrollbar));
     ctrl_checkbox(s, "Reset scrollback on keypress", 'k',
                  HELPCTX(window_scrollback),
-                 dlg_stdcheckbox_handler, I(offsetof(Config,scroll_on_key)));
+                 conf_checkbox_handler, I(CONF_scroll_on_key));
     ctrl_checkbox(s, "Reset scrollback on display activity", 'p',
                  HELPCTX(window_scrollback),
-                 dlg_stdcheckbox_handler, I(offsetof(Config,scroll_on_disp)));
+                 conf_checkbox_handler, I(CONF_scroll_on_disp));
     ctrl_checkbox(s, "Push erased text into scrollback", 'e',
                  HELPCTX(window_erased),
-                 dlg_stdcheckbox_handler,
-                 I(offsetof(Config,erase_to_scrollback)));
+                 conf_checkbox_handler,
+                 I(CONF_erase_to_scrollback));
 
     /*
      * The Window/Appearance panel.
@@ -1557,33 +1654,33 @@ void setup_config_box(struct controlbox *b, int midsession,
                    "Adjust the use of the cursor");
     ctrl_radiobuttons(s, "Cursor appearance:", NO_SHORTCUT, 3,
                      HELPCTX(appearance_cursor),
-                     dlg_stdradiobutton_handler,
-                     I(offsetof(Config, cursor_type)),
+                     conf_radiobutton_handler,
+                     I(CONF_cursor_type),
                      "Block", 'l', I(0),
                      "Underline", 'u', I(1),
                      "Vertical line", 'v', I(2), NULL);
     ctrl_checkbox(s, "Cursor blinks", 'b',
                  HELPCTX(appearance_cursor),
-                 dlg_stdcheckbox_handler, I(offsetof(Config,blink_cur)));
+                 conf_checkbox_handler, I(CONF_blink_cur));
 
     s = ctrl_getset(b, "Window/Appearance", "font",
                    "Font settings");
     ctrl_fontsel(s, "Font used in the terminal window", 'n',
                 HELPCTX(appearance_font),
-                dlg_stdfontsel_handler, I(offsetof(Config, font)));
+                conf_fontsel_handler, I(CONF_font));
 
     s = ctrl_getset(b, "Window/Appearance", "mouse",
                    "Adjust the use of the mouse pointer");
     ctrl_checkbox(s, "Hide mouse pointer when typing in window", 'p',
                  HELPCTX(appearance_hidemouse),
-                 dlg_stdcheckbox_handler, I(offsetof(Config,hide_mouseptr)));
+                 conf_checkbox_handler, I(CONF_hide_mouseptr));
 
     s = ctrl_getset(b, "Window/Appearance", "border",
                    "Adjust the window border");
     ctrl_editbox(s, "Gap between text and window edge:", 'e', 20,
                 HELPCTX(appearance_border),
-                dlg_stdeditbox_handler,
-                I(offsetof(Config,window_border)), I(-1));
+                conf_editbox_handler,
+                I(CONF_window_border), I(-1));
 
     /*
      * The Window/Behaviour panel.
@@ -1596,17 +1693,16 @@ void setup_config_box(struct controlbox *b, int midsession,
                    "Adjust the behaviour of the window title");
     ctrl_editbox(s, "Window title:", 't', 100,
                 HELPCTX(appearance_title),
-                dlg_stdeditbox_handler, I(offsetof(Config,wintitle)),
-                I(sizeof(((Config *)0)->wintitle)));
+                conf_editbox_handler, I(CONF_wintitle), I(1));
     ctrl_checkbox(s, "Separate window and icon titles", 'i',
                  HELPCTX(appearance_title),
-                 dlg_stdcheckbox_handler,
-                 I(CHECKBOX_INVERT | offsetof(Config,win_name_always)));
+                 conf_checkbox_handler,
+                 I(CHECKBOX_INVERT | CONF_win_name_always));
 
     s = ctrl_getset(b, "Window/Behaviour", "main", NULL);
     ctrl_checkbox(s, "Warn before closing window", 'w',
                  HELPCTX(behaviour_closewarn),
-                 dlg_stdcheckbox_handler, I(offsetof(Config,warn_on_close)));
+                 conf_checkbox_handler, I(CONF_warn_on_close));
 
     /*
      * The Window/Translation panel.
@@ -1623,21 +1719,21 @@ void setup_config_box(struct controlbox *b, int midsession,
     s = ctrl_getset(b, "Window/Translation", "tweaks", NULL);
     ctrl_checkbox(s, "Treat CJK ambiguous characters as wide", 'w',
                  HELPCTX(translation_cjk_ambig_wide),
-                 dlg_stdcheckbox_handler, I(offsetof(Config,cjk_ambig_wide)));
+                 conf_checkbox_handler, I(CONF_cjk_ambig_wide));
 
     str = dupprintf("Adjust how %s handles line drawing characters", appname);
     s = ctrl_getset(b, "Window/Translation", "linedraw", str);
     sfree(str);
     ctrl_radiobuttons(s, "Handling of line drawing characters:", NO_SHORTCUT,1,
                      HELPCTX(translation_linedraw),
-                     dlg_stdradiobutton_handler,
-                     I(offsetof(Config, vtmode)),
+                     conf_radiobutton_handler,
+                     I(CONF_vtmode),
                      "Use Unicode line drawing code points",'u',I(VT_UNICODE),
                      "Poor man's line drawing (+, - and |)",'p',I(VT_POORMAN),
                      NULL);
     ctrl_checkbox(s, "Copy and paste line drawing characters as lqqqk",'d',
                  HELPCTX(selection_linedraw),
-                 dlg_stdcheckbox_handler, I(offsetof(Config,rawcnp)));
+                 conf_checkbox_handler, I(CONF_rawcnp));
 
     /*
      * The Window/Selection panel.
@@ -1648,13 +1744,13 @@ void setup_config_box(struct controlbox *b, int midsession,
                    "Control use of mouse");
     ctrl_checkbox(s, "Shift overrides application's use of mouse", 'p',
                  HELPCTX(selection_shiftdrag),
-                 dlg_stdcheckbox_handler, I(offsetof(Config,mouse_override)));
+                 conf_checkbox_handler, I(CONF_mouse_override));
     ctrl_radiobuttons(s,
                      "Default selection mode (Alt+drag does the other one):",
                      NO_SHORTCUT, 2,
                      HELPCTX(selection_rect),
-                     dlg_stdradiobutton_handler,
-                     I(offsetof(Config, rect_select)),
+                     conf_radiobutton_handler,
+                     I(CONF_rect_select),
                      "Normal", 'n', I(0),
                      "Rectangular block", 'r', I(1), NULL);
 
@@ -1692,13 +1788,13 @@ void setup_config_box(struct controlbox *b, int midsession,
                    "General options for colour usage");
     ctrl_checkbox(s, "Allow terminal to specify ANSI colours", 'i',
                  HELPCTX(colours_ansi),
-                 dlg_stdcheckbox_handler, I(offsetof(Config,ansi_colour)));
+                 conf_checkbox_handler, I(CONF_ansi_colour));
     ctrl_checkbox(s, "Allow terminal to use xterm 256-colour mode", '2',
-                 HELPCTX(colours_xterm256), dlg_stdcheckbox_handler,
-                 I(offsetof(Config,xterm_256_colour)));
+                 HELPCTX(colours_xterm256), conf_checkbox_handler,
+                 I(CONF_xterm_256_colour));
     ctrl_checkbox(s, "Bolded text is a different colour", 'b',
                  HELPCTX(colours_bold),
-                 dlg_stdcheckbox_handler, I(offsetof(Config,bold_colour)));
+                 conf_checkbox_handler, I(CONF_bold_colour));
 
     str = dupprintf("Adjust the precise colours %s displays", appname);
     s = ctrl_getset(b, "Window/Colours", "adjust", str);
@@ -1740,7 +1836,7 @@ void setup_config_box(struct controlbox *b, int midsession,
                        "Sending of null packets to keep session active");
        ctrl_editbox(s, "Seconds between keepalives (0 to turn off)", 'k', 20,
                     HELPCTX(connection_keepalive),
-                    dlg_stdeditbox_handler, I(offsetof(Config,ping_interval)),
+                    conf_editbox_handler, I(CONF_ping_interval),
                     I(-1));
 
        if (!midsession) {
@@ -1748,19 +1844,19 @@ void setup_config_box(struct controlbox *b, int midsession,
                            "Low-level TCP connection options");
            ctrl_checkbox(s, "Disable Nagle's algorithm (TCP_NODELAY option)",
                          'n', HELPCTX(connection_nodelay),
-                         dlg_stdcheckbox_handler,
-                         I(offsetof(Config,tcp_nodelay)));
+                         conf_checkbox_handler,
+                         I(CONF_tcp_nodelay));
            ctrl_checkbox(s, "Enable TCP keepalives (SO_KEEPALIVE option)",
                          'p', HELPCTX(connection_tcpkeepalive),
-                         dlg_stdcheckbox_handler,
-                         I(offsetof(Config,tcp_keepalives)));
+                         conf_checkbox_handler,
+                         I(CONF_tcp_keepalives));
 #ifndef NO_IPV6
            s = ctrl_getset(b, "Connection", "ipversion",
                          "Internet protocol version");
            ctrl_radiobuttons(s, NULL, NO_SHORTCUT, 3,
                          HELPCTX(connection_ipversion),
-                         dlg_stdradiobutton_handler,
-                         I(offsetof(Config, addressfamily)),
+                         conf_radiobutton_handler,
+                         I(CONF_addressfamily),
                          "Auto", 'u', I(ADDRTYPE_UNSPEC),
                          "IPv4", '4', I(ADDRTYPE_IPV4),
                          "IPv6", '6', I(ADDRTYPE_IPV6),
@@ -1775,8 +1871,7 @@ void setup_config_box(struct controlbox *b, int midsession,
                                "Logical name of remote host");
                ctrl_editbox(s, label, 'm', 100,
                             HELPCTX(connection_loghost),
-                            dlg_stdeditbox_handler, I(offsetof(Config,loghost)),
-                            I(sizeof(((Config *)0)->loghost)));
+                            conf_editbox_handler, I(CONF_loghost), I(1));
            }
        }
 
@@ -1791,8 +1886,7 @@ void setup_config_box(struct controlbox *b, int midsession,
                            "Login details");
            ctrl_editbox(s, "Auto-login username", 'u', 50,
                         HELPCTX(connection_username),
-                        dlg_stdeditbox_handler, I(offsetof(Config,username)),
-                        I(sizeof(((Config *)0)->username)));
+                        conf_editbox_handler, I(CONF_username), I(1));
            {
                /* We assume the local username is sufficiently stable
                 * to include on the dialog box. */
@@ -1802,8 +1896,8 @@ void setup_config_box(struct controlbox *b, int midsession,
                sfree(user);
                ctrl_radiobuttons(s, "When username is not specified:", 'n', 4,
                                  HELPCTX(connection_username_from_env),
-                                 dlg_stdradiobutton_handler,
-                                 I(offsetof(Config, username_from_env)),
+                                 conf_radiobutton_handler,
+                                 I(CONF_username_from_env),
                                  "Prompt", I(FALSE),
                                  userlabel, I(TRUE),
                                  NULL);
@@ -1814,12 +1908,10 @@ void setup_config_box(struct controlbox *b, int midsession,
                            "Terminal details");
            ctrl_editbox(s, "Terminal-type string", 't', 50,
                         HELPCTX(connection_termtype),
-                        dlg_stdeditbox_handler, I(offsetof(Config,termtype)),
-                        I(sizeof(((Config *)0)->termtype)));
+                        conf_editbox_handler, I(CONF_termtype), I(1));
            ctrl_editbox(s, "Terminal speeds", 's', 50,
                         HELPCTX(connection_termspeed),
-                        dlg_stdeditbox_handler, I(offsetof(Config,termspeed)),
-                        I(sizeof(((Config *)0)->termspeed)));
+                        conf_editbox_handler, I(CONF_termspeed), I(1));
 
            s = ctrl_getset(b, "Connection/Data", "env",
                            "Environment variables");
@@ -1865,8 +1957,8 @@ void setup_config_box(struct controlbox *b, int midsession,
        s = ctrl_getset(b, "Connection/Proxy", "basics", NULL);
        ctrl_radiobuttons(s, "Proxy type:", 't', 3,
                          HELPCTX(proxy_type),
-                         dlg_stdradiobutton_handler,
-                         I(offsetof(Config, proxy_type)),
+                         conf_radiobutton_handler,
+                         I(CONF_proxy_type),
                          "None", I(PROXY_NONE),
                          "SOCKS 4", I(PROXY_SOCKS4),
                          "SOCKS 5", I(PROXY_SOCKS5),
@@ -1876,49 +1968,44 @@ void setup_config_box(struct controlbox *b, int midsession,
        ctrl_columns(s, 2, 80, 20);
        c = ctrl_editbox(s, "Proxy hostname", 'y', 100,
                         HELPCTX(proxy_main),
-                        dlg_stdeditbox_handler,
-                        I(offsetof(Config,proxy_host)),
-                        I(sizeof(((Config *)0)->proxy_host)));
+                        conf_editbox_handler,
+                        I(CONF_proxy_host), I(1));
        c->generic.column = 0;
        c = ctrl_editbox(s, "Port", 'p', 100,
                         HELPCTX(proxy_main),
-                        dlg_stdeditbox_handler,
-                        I(offsetof(Config,proxy_port)),
+                        conf_editbox_handler,
+                        I(CONF_proxy_port),
                         I(-1));
        c->generic.column = 1;
        ctrl_columns(s, 1, 100);
        ctrl_editbox(s, "Exclude Hosts/IPs", 'e', 100,
                     HELPCTX(proxy_exclude),
-                    dlg_stdeditbox_handler,
-                    I(offsetof(Config,proxy_exclude_list)),
-                    I(sizeof(((Config *)0)->proxy_exclude_list)));
+                    conf_editbox_handler,
+                    I(CONF_proxy_exclude_list), I(1));
        ctrl_checkbox(s, "Consider proxying local host connections", 'x',
                      HELPCTX(proxy_exclude),
-                     dlg_stdcheckbox_handler,
-                     I(offsetof(Config,even_proxy_localhost)));
+                     conf_checkbox_handler,
+                     I(CONF_even_proxy_localhost));
        ctrl_radiobuttons(s, "Do DNS name lookup at proxy end:", 'd', 3,
                          HELPCTX(proxy_dns),
-                         dlg_stdradiobutton_handler,
-                         I(offsetof(Config, proxy_dns)),
+                         conf_radiobutton_handler,
+                         I(CONF_proxy_dns),
                          "No", I(FORCE_OFF),
                          "Auto", I(AUTO),
                          "Yes", I(FORCE_ON), NULL);
        ctrl_editbox(s, "Username", 'u', 60,
                     HELPCTX(proxy_auth),
-                    dlg_stdeditbox_handler,
-                    I(offsetof(Config,proxy_username)),
-                    I(sizeof(((Config *)0)->proxy_username)));
+                    conf_editbox_handler,
+                    I(CONF_proxy_username), I(1));
        c = ctrl_editbox(s, "Password", 'w', 60,
                         HELPCTX(proxy_auth),
-                        dlg_stdeditbox_handler,
-                        I(offsetof(Config,proxy_password)),
-                        I(sizeof(((Config *)0)->proxy_password)));
+                        conf_editbox_handler,
+                        I(CONF_proxy_password), I(1));
        c->editbox.password = 1;
        ctrl_editbox(s, "Telnet command", 'm', 100,
                     HELPCTX(proxy_command),
-                    dlg_stdeditbox_handler,
-                    I(offsetof(Config,proxy_telnet_command)),
-                    I(sizeof(((Config *)0)->proxy_telnet_command)));
+                    conf_editbox_handler,
+                    I(CONF_proxy_telnet_command), I(1));
     }
 
     /*
@@ -1939,24 +2026,24 @@ void setup_config_box(struct controlbox *b, int midsession,
            ctrl_radiobuttons(s, "Handling of OLD_ENVIRON ambiguity:",
                              NO_SHORTCUT, 2,
                              HELPCTX(telnet_oldenviron),
-                             dlg_stdradiobutton_handler,
-                             I(offsetof(Config, rfc_environ)),
+                             conf_radiobutton_handler,
+                             I(CONF_rfc_environ),
                              "BSD (commonplace)", 'b', I(0),
                              "RFC 1408 (unusual)", 'f', I(1), NULL);
            ctrl_radiobuttons(s, "Telnet negotiation mode:", 't', 2,
                              HELPCTX(telnet_passive),
-                             dlg_stdradiobutton_handler,
-                             I(offsetof(Config, passive_telnet)),
+                             conf_radiobutton_handler,
+                             I(CONF_passive_telnet),
                              "Passive", I(1), "Active", I(0), NULL);
        }
        ctrl_checkbox(s, "Keyboard sends Telnet special commands", 'k',
                      HELPCTX(telnet_specialkeys),
-                     dlg_stdcheckbox_handler,
-                     I(offsetof(Config,telnet_keyboard)));
+                     conf_checkbox_handler,
+                     I(CONF_telnet_keyboard));
        ctrl_checkbox(s, "Return key sends Telnet New Line instead of ^M",
                      'm', HELPCTX(telnet_newline),
-                     dlg_stdcheckbox_handler,
-                     I(offsetof(Config,telnet_newline)));
+                     conf_checkbox_handler,
+                     I(CONF_telnet_newline));
     }
 
     if (!midsession) {
@@ -1971,8 +2058,7 @@ void setup_config_box(struct controlbox *b, int midsession,
                        "Data to send to the server");
        ctrl_editbox(s, "Local username:", 'l', 50,
                     HELPCTX(rlogin_localuser),
-                    dlg_stdeditbox_handler, I(offsetof(Config,localusername)),
-                    I(sizeof(((Config *)0)->localusername)));
+                    conf_editbox_handler, I(CONF_localusername), I(1));
 
     }
 
@@ -2002,14 +2088,13 @@ void setup_config_box(struct controlbox *b, int midsession,
                            "Data to send to the server");
            ctrl_editbox(s, "Remote command:", 'r', 100,
                         HELPCTX(ssh_command),
-                        dlg_stdeditbox_handler, I(offsetof(Config,remote_cmd)),
-                        I(sizeof(((Config *)0)->remote_cmd)));
+                        conf_editbox_handler, I(CONF_remote_cmd), I(1));
 
            s = ctrl_getset(b, "Connection/SSH", "protocol", "Protocol options");
            ctrl_checkbox(s, "Don't start a shell or command at all", 'n',
                          HELPCTX(ssh_noshell),
-                         dlg_stdcheckbox_handler,
-                         I(offsetof(Config,ssh_no_shell)));
+                         conf_checkbox_handler,
+                         I(CONF_ssh_no_shell));
        }
 
        if (!midsession || protcfginfo != 1) {
@@ -2017,8 +2102,8 @@ void setup_config_box(struct controlbox *b, int midsession,
 
            ctrl_checkbox(s, "Enable compression", 'e',
                          HELPCTX(ssh_compress),
-                         dlg_stdcheckbox_handler,
-                         I(offsetof(Config,compression)));
+                         conf_checkbox_handler,
+                         I(CONF_compression));
        }
 
        if (!midsession) {
@@ -2026,8 +2111,8 @@ void setup_config_box(struct controlbox *b, int midsession,
 
            ctrl_radiobuttons(s, "Preferred SSH protocol version:", NO_SHORTCUT, 4,
                              HELPCTX(ssh_protocol),
-                             dlg_stdradiobutton_handler,
-                             I(offsetof(Config, sshprot)),
+                             conf_radiobutton_handler,
+                             I(CONF_sshprot),
                              "1 only", 'l', I(0),
                              "1", '1', I(1),
                              "2", '2', I(2),
@@ -2043,8 +2128,8 @@ void setup_config_box(struct controlbox *b, int midsession,
 
            ctrl_checkbox(s, "Enable legacy use of single-DES in SSH-2", 'i',
                          HELPCTX(ssh_ciphers),
-                         dlg_stdcheckbox_handler,
-                         I(offsetof(Config,ssh2_des_cbc)));
+                         conf_checkbox_handler,
+                         I(CONF_ssh2_des_cbc));
        }
 
        /*
@@ -2068,13 +2153,13 @@ void setup_config_box(struct controlbox *b, int midsession,
 
            ctrl_editbox(s, "Max minutes before rekey (0 for no limit)", 't', 20,
                         HELPCTX(ssh_kex_repeat),
-                        dlg_stdeditbox_handler,
-                        I(offsetof(Config,ssh_rekey_time)),
+                        conf_editbox_handler,
+                        I(CONF_ssh_rekey_time),
                         I(-1));
            ctrl_editbox(s, "Max data before rekey (0 for no limit)", 'x', 20,
                         HELPCTX(ssh_kex_repeat),
-                        dlg_stdeditbox_handler,
-                        I(offsetof(Config,ssh_rekey_data)),
+                        conf_editbox_handler,
+                        I(CONF_ssh_rekey_data),
                         I(16));
            ctrl_text(s, "(Use 1M for 1 megabyte, 1G for 1 gigabyte etc)",
                      HELPCTX(ssh_kex_repeat));
@@ -2091,41 +2176,41 @@ void setup_config_box(struct controlbox *b, int midsession,
            s = ctrl_getset(b, "Connection/SSH/Auth", "main", NULL);
            ctrl_checkbox(s, "Bypass authentication entirely (SSH-2 only)", 'b',
                          HELPCTX(ssh_auth_bypass),
-                         dlg_stdcheckbox_handler,
-                         I(offsetof(Config,ssh_no_userauth)));
+                         conf_checkbox_handler,
+                         I(CONF_ssh_no_userauth));
            ctrl_checkbox(s, "Display pre-authentication banner (SSH-2 only)",
                          'd', HELPCTX(ssh_auth_banner),
-                         dlg_stdcheckbox_handler,
-                         I(offsetof(Config,ssh_show_banner)));
+                         conf_checkbox_handler,
+                         I(CONF_ssh_show_banner));
 
            s = ctrl_getset(b, "Connection/SSH/Auth", "methods",
                            "Authentication methods");
            ctrl_checkbox(s, "Attempt authentication using Pageant", 'p',
                          HELPCTX(ssh_auth_pageant),
-                         dlg_stdcheckbox_handler,
-                         I(offsetof(Config,tryagent)));
+                         conf_checkbox_handler,
+                         I(CONF_tryagent));
            ctrl_checkbox(s, "Attempt TIS or CryptoCard auth (SSH-1)", 'm',
                          HELPCTX(ssh_auth_tis),
-                         dlg_stdcheckbox_handler,
-                         I(offsetof(Config,try_tis_auth)));
+                         conf_checkbox_handler,
+                         I(CONF_try_tis_auth));
            ctrl_checkbox(s, "Attempt \"keyboard-interactive\" auth (SSH-2)",
                          'i', HELPCTX(ssh_auth_ki),
-                         dlg_stdcheckbox_handler,
-                         I(offsetof(Config,try_ki_auth)));
+                         conf_checkbox_handler,
+                         I(CONF_try_ki_auth));
 
            s = ctrl_getset(b, "Connection/SSH/Auth", "params",
                            "Authentication parameters");
            ctrl_checkbox(s, "Allow agent forwarding", 'f',
                          HELPCTX(ssh_auth_agentfwd),
-                         dlg_stdcheckbox_handler, I(offsetof(Config,agentfwd)));
+                         conf_checkbox_handler, I(CONF_agentfwd));
            ctrl_checkbox(s, "Allow attempted changes of username in SSH-2", NO_SHORTCUT,
                          HELPCTX(ssh_auth_changeuser),
-                         dlg_stdcheckbox_handler,
-                         I(offsetof(Config,change_username)));
+                         conf_checkbox_handler,
+                         I(CONF_change_username));
            ctrl_filesel(s, "Private key file for authentication:", 'k',
                         FILTER_KEY_FILES, FALSE, "Select private key file",
                         HELPCTX(ssh_auth_privkey),
-                        dlg_stdfilesel_handler, I(offsetof(Config, keyfile)));
+                        conf_filesel_handler, I(CONF_keyfile));
 
 #ifndef NO_GSSAPI
            /*
@@ -2138,13 +2223,13 @@ void setup_config_box(struct controlbox *b, int midsession,
 
            ctrl_checkbox(s, "Attempt GSSAPI authentication (SSH-2 only)",
                          't', HELPCTX(ssh_gssapi),
-                         dlg_stdcheckbox_handler,
-                         I(offsetof(Config,try_gssapi_auth)));
+                         conf_checkbox_handler,
+                         I(CONF_try_gssapi_auth));
 
            ctrl_checkbox(s, "Allow GSSAPI credential delegation", 'l',
                          HELPCTX(ssh_gssapi_delegation),
-                         dlg_stdcheckbox_handler,
-                         I(offsetof(Config,gssapifwd)));
+                         conf_checkbox_handler,
+                         I(CONF_gssapifwd));
 
            /*
             * GSSAPI library selection.
@@ -2177,8 +2262,8 @@ void setup_config_box(struct controlbox *b, int midsession,
                ctrl_filesel(s, "User-supplied GSSAPI library path:", 's',
                             FILTER_DYNLIB_FILES, FALSE, "Select library file",
                             HELPCTX(ssh_gssapi_libraries),
-                            dlg_stdfilesel_handler,
-                            I(offsetof(Config, ssh_gss_custom)));
+                            conf_filesel_handler,
+                            I(CONF_ssh_gss_custom));
            }
 #endif
        }
@@ -2192,8 +2277,8 @@ void setup_config_box(struct controlbox *b, int midsession,
            s = ctrl_getset(b, "Connection/SSH/TTY", "sshtty", NULL);
            ctrl_checkbox(s, "Don't allocate a pseudo-terminal", 'p',
                          HELPCTX(ssh_nopty),
-                         dlg_stdcheckbox_handler,
-                         I(offsetof(Config,nopty)));
+                         conf_checkbox_handler,
+                         I(CONF_nopty));
 
            s = ctrl_getset(b, "Connection/SSH/TTY", "ttymodes",
                            "Terminal modes");
@@ -2259,15 +2344,14 @@ void setup_config_box(struct controlbox *b, int midsession,
            s = ctrl_getset(b, "Connection/SSH/X11", "x11", "X11 forwarding");
            ctrl_checkbox(s, "Enable X11 forwarding", 'e',
                          HELPCTX(ssh_tunnels_x11),
-                         dlg_stdcheckbox_handler,I(offsetof(Config,x11_forward)));
+                         conf_checkbox_handler,I(CONF_x11_forward));
            ctrl_editbox(s, "X display location", 'x', 50,
                         HELPCTX(ssh_tunnels_x11),
-                        dlg_stdeditbox_handler, I(offsetof(Config,x11_display)),
-                        I(sizeof(((Config *)0)->x11_display)));
+                        conf_editbox_handler, I(CONF_x11_display), I(1));
            ctrl_radiobuttons(s, "Remote X11 authentication protocol", 'u', 2,
                              HELPCTX(ssh_tunnels_x11auth),
-                             dlg_stdradiobutton_handler,
-                             I(offsetof(Config, x11_auth)),
+                             conf_radiobutton_handler,
+                             I(CONF_x11_auth),
                              "MIT-Magic-Cookie-1", I(X11_MIT),
                              "XDM-Authorization-1", I(X11_XDM), NULL);
        }
@@ -2282,12 +2366,12 @@ void setup_config_box(struct controlbox *b, int midsession,
                        "Port forwarding");
        ctrl_checkbox(s, "Local ports accept connections from other hosts",'t',
                      HELPCTX(ssh_tunnels_portfwd_localhost),
-                     dlg_stdcheckbox_handler,
-                     I(offsetof(Config,lport_acceptall)));
+                     conf_checkbox_handler,
+                     I(CONF_lport_acceptall));
        ctrl_checkbox(s, "Remote ports do the same (SSH-2 only)", 'p',
                      HELPCTX(ssh_tunnels_portfwd_localhost),
-                     dlg_stdcheckbox_handler,
-                     I(offsetof(Config,rport_acceptall)));
+                     conf_checkbox_handler,
+                     I(CONF_rport_acceptall));
 
        ctrl_columns(s, 3, 55, 20, 25);
        c = ctrl_text(s, "Forwarded ports:", HELPCTX(ssh_tunnels_portfwd));
@@ -2355,34 +2439,34 @@ void setup_config_box(struct controlbox *b, int midsession,
                            "Detection of known bugs in SSH servers");
            ctrl_droplist(s, "Chokes on SSH-1 ignore messages", 'i', 20,
                          HELPCTX(ssh_bugs_ignore1),
-                         sshbug_handler, I(offsetof(Config,sshbug_ignore1)));
+                         sshbug_handler, I(CONF_sshbug_ignore1));
            ctrl_droplist(s, "Refuses all SSH-1 password camouflage", 's', 20,
                          HELPCTX(ssh_bugs_plainpw1),
-                         sshbug_handler, I(offsetof(Config,sshbug_plainpw1)));
+                         sshbug_handler, I(CONF_sshbug_plainpw1));
            ctrl_droplist(s, "Chokes on SSH-1 RSA authentication", 'r', 20,
                          HELPCTX(ssh_bugs_rsa1),
-                         sshbug_handler, I(offsetof(Config,sshbug_rsa1)));
+                         sshbug_handler, I(CONF_sshbug_rsa1));
            ctrl_droplist(s, "Chokes on SSH-2 ignore messages", '2', 20,
                          HELPCTX(ssh_bugs_ignore2),
-                         sshbug_handler, I(offsetof(Config,sshbug_ignore2)));
+                         sshbug_handler, I(CONF_sshbug_ignore2));
            ctrl_droplist(s, "Miscomputes SSH-2 HMAC keys", 'm', 20,
                          HELPCTX(ssh_bugs_hmac2),
-                         sshbug_handler, I(offsetof(Config,sshbug_hmac2)));
+                         sshbug_handler, I(CONF_sshbug_hmac2));
            ctrl_droplist(s, "Miscomputes SSH-2 encryption keys", 'e', 20,
                          HELPCTX(ssh_bugs_derivekey2),
-                         sshbug_handler, I(offsetof(Config,sshbug_derivekey2)));
+                         sshbug_handler, I(CONF_sshbug_derivekey2));
            ctrl_droplist(s, "Requires padding on SSH-2 RSA signatures", 'p', 20,
                          HELPCTX(ssh_bugs_rsapad2),
-                         sshbug_handler, I(offsetof(Config,sshbug_rsapad2)));
+                         sshbug_handler, I(CONF_sshbug_rsapad2));
            ctrl_droplist(s, "Misuses the session ID in SSH-2 PK auth", 'n', 20,
                          HELPCTX(ssh_bugs_pksessid2),
-                         sshbug_handler, I(offsetof(Config,sshbug_pksessid2)));
+                         sshbug_handler, I(CONF_sshbug_pksessid2));
            ctrl_droplist(s, "Handles SSH-2 key re-exchange badly", 'k', 20,
                          HELPCTX(ssh_bugs_rekey2),
-                         sshbug_handler, I(offsetof(Config,sshbug_rekey2)));
+                         sshbug_handler, I(CONF_sshbug_rekey2));
            ctrl_droplist(s, "Ignores SSH-2 maximum packet size", 'x', 20,
                          HELPCTX(ssh_bugs_maxpkt2),
-                         sshbug_handler, I(offsetof(Config,sshbug_maxpkt2)));
+                         sshbug_handler, I(CONF_sshbug_maxpkt2));
        }
     }
 }
index bf5eed5..934ce3d 100644 (file)
--- a/cproxy.c
+++ b/cproxy.c
@@ -130,7 +130,8 @@ int proxy_socks5_handlechap (Proxy_Socket p)
                outbuf[2] = 0x04; /* Response */
                outbuf[3] = 0x10; /* Length */
                hmacmd5_chap(data, p->chap_current_datalen,
-                            p->cfg.proxy_password, &outbuf[4]);
+                            conf_get_str(p->conf, CONF_proxy_password),
+                            &outbuf[4]);
                sk_write(p->sub_socket, (char *)outbuf, 20);
              break;
              case 0x11:
@@ -159,7 +160,9 @@ int proxy_socks5_handlechap (Proxy_Socket p)
 
 int proxy_socks5_selectchap(Proxy_Socket p)
 {
-    if (p->cfg.proxy_username[0] || p->cfg.proxy_password[0]) {
+    char *username = conf_get_str(p->conf, CONF_proxy_username);
+    char *password = conf_get_str(p->conf, CONF_proxy_password);
+    if (username[0] || password[0]) {
        char chapbuf[514];
        int ulen;
        chapbuf[0] = '\x01'; /* Version */
@@ -169,11 +172,11 @@ int proxy_socks5_selectchap(Proxy_Socket p)
        chapbuf[4] = '\x85'; /* ...and it's HMAC-MD5, the core one */
        chapbuf[5] = '\x02'; /* Second attribute - username */
 
-       ulen = strlen(p->cfg.proxy_username);
+       ulen = strlen(username);
        if (ulen > 255) ulen = 255; if (ulen < 1) ulen = 1;
 
        chapbuf[6] = ulen;
-       memcpy(chapbuf+7, p->cfg.proxy_username, ulen);
+       memcpy(chapbuf+7, username, ulen);
 
        sk_write(p->sub_socket, chapbuf, ulen + 7);
        p->chap_num_attributes = 0;
index 53b6429..127783a 100644 (file)
--- a/dialog.c
+++ b/dialog.c
@@ -456,140 +456,3 @@ void ctrl_free(union control *ctrl)
     }
     sfree(ctrl);
 }
-
-void dlg_stdradiobutton_handler(union control *ctrl, void *dlg,
-                               void *data, int event)
-{
-    int button;
-    /*
-     * For a standard radio button set, the context parameter gives
-     * offsetof(targetfield, Config), and the extra data per button
-     * gives the value the target field should take if that button
-     * is the one selected.
-     */
-    if (event == EVENT_REFRESH) {
-       for (button = 0; button < ctrl->radio.nbuttons; button++)
-           if (*(int *)ATOFFSET(data, ctrl->radio.context.i) ==
-               ctrl->radio.buttondata[button].i)
-               break;
-       /* We expected that `break' to happen, in all circumstances. */
-       assert(button < ctrl->radio.nbuttons);
-       dlg_radiobutton_set(ctrl, dlg, button);
-    } else if (event == EVENT_VALCHANGE) {
-       button = dlg_radiobutton_get(ctrl, dlg);
-       assert(button >= 0 && button < ctrl->radio.nbuttons);
-       *(int *)ATOFFSET(data, ctrl->radio.context.i) =
-           ctrl->radio.buttondata[button].i;
-    }
-}
-
-void dlg_stdcheckbox_handler(union control *ctrl, void *dlg,
-                               void *data, int event)
-{
-    int offset, invert;
-
-    /*
-     * For a standard checkbox, the context parameter gives
-     * offsetof(targetfield, Config), optionally ORed with
-     * CHECKBOX_INVERT.
-     */
-    offset = ctrl->checkbox.context.i;
-    if (offset & CHECKBOX_INVERT) {
-       offset &= ~CHECKBOX_INVERT;
-       invert = 1;
-    } else
-       invert = 0;
-
-    /*
-     * C lacks a logical XOR, so the following code uses the idiom
-     * (!a ^ !b) to obtain the logical XOR of a and b. (That is, 1
-     * iff exactly one of a and b is nonzero, otherwise 0.)
-     */
-
-    if (event == EVENT_REFRESH) {
-       dlg_checkbox_set(ctrl,dlg, (!*(int *)ATOFFSET(data,offset) ^ !invert));
-    } else if (event == EVENT_VALCHANGE) {
-       *(int *)ATOFFSET(data, offset) = !dlg_checkbox_get(ctrl,dlg) ^ !invert;
-    }
-}
-
-void dlg_stdeditbox_handler(union control *ctrl, void *dlg,
-                           void *data, int event)
-{
-    /*
-     * The standard edit-box handler expects the main `context'
-     * field to contain the `offsetof' a field in the structure
-     * pointed to by `data'. The secondary `context2' field
-     * indicates the type of this field:
-     *
-     *  - if context2 > 0, the field is a char array and context2
-     *    gives its size.
-     *  - if context2 == -1, the field is an int and the edit box
-     *    is numeric.
-     *  - if context2 < -1, the field is an int and the edit box is
-     *    _floating_, and (-context2) gives the scale. (E.g. if
-     *    context2 == -1000, then typing 1.2 into the box will set
-     *    the field to 1200.)
-     */
-    int offset = ctrl->editbox.context.i;
-    int length = ctrl->editbox.context2.i;
-
-    if (length > 0) {
-       char *field = (char *)ATOFFSET(data, offset);
-       if (event == EVENT_REFRESH) {
-           dlg_editbox_set(ctrl, dlg, field);
-       } else if (event == EVENT_VALCHANGE) {
-           dlg_editbox_get(ctrl, dlg, field, length);
-       }
-    } else if (length < 0) {
-       int *field = (int *)ATOFFSET(data, offset);
-       char data[80];
-       if (event == EVENT_REFRESH) {
-           if (length == -1)
-               sprintf(data, "%d", *field);
-           else
-               sprintf(data, "%g", (double)*field / (double)(-length));
-           dlg_editbox_set(ctrl, dlg, data);
-       } else if (event == EVENT_VALCHANGE) {
-           dlg_editbox_get(ctrl, dlg, data, lenof(data));
-           if (length == -1)
-               *field = atoi(data);
-           else
-               *field = (int)((-length) * atof(data));
-       }
-    }
-}
-
-void dlg_stdfilesel_handler(union control *ctrl, void *dlg,
-                           void *data, int event)
-{
-    /*
-     * The standard file-selector handler expects the `context'
-     * field to contain the `offsetof' a Filename field in the
-     * structure pointed to by `data'.
-     */
-    int offset = ctrl->fileselect.context.i;
-
-    if (event == EVENT_REFRESH) {
-       dlg_filesel_set(ctrl, dlg, *(Filename *)ATOFFSET(data, offset));
-    } else if (event == EVENT_VALCHANGE) {
-       dlg_filesel_get(ctrl, dlg, (Filename *)ATOFFSET(data, offset));
-    }
-}
-
-void dlg_stdfontsel_handler(union control *ctrl, void *dlg,
-                           void *data, int event)
-{
-    /*
-     * The standard file-selector handler expects the `context'
-     * field to contain the `offsetof' a FontSpec field in the
-     * structure pointed to by `data'.
-     */
-    int offset = ctrl->fontselect.context.i;
-
-    if (event == EVENT_REFRESH) {
-       dlg_fontsel_set(ctrl, dlg, *(FontSpec *)ATOFFSET(data, offset));
-    } else if (event == EVENT_VALCHANGE) {
-       dlg_fontsel_get(ctrl, dlg, (FontSpec *)ATOFFSET(data, offset));
-    }
-}
index 0cabb3d..fb89b6b 100644 (file)
--- a/dialog.h
+++ b/dialog.h
@@ -162,7 +162,7 @@ union control {
         * 
         * The `data' parameter points to the writable data being
         * modified as a result of the configuration activity; for
-        * example, the PuTTY `Config' structure, although not
+        * example, the PuTTY `Conf' structure, although not
         * necessarily.
         * 
         * The `dlg' parameter is passed back to the platform-
@@ -522,45 +522,6 @@ union control *ctrl_checkbox(struct controlset *, char *label, char shortcut,
 union control *ctrl_tabdelay(struct controlset *, union control *);
 
 /*
- * Standard handler routines to cover most of the common cases in
- * the config box.
- */
-/*
- * The standard radio-button handler expects the main `context'
- * field to contain the `offsetof' of an int field in the structure
- * pointed to by `data', and expects each of the individual button
- * data to give a value for that int field.
- */
-void dlg_stdradiobutton_handler(union control *ctrl, void *dlg,
-                               void *data, int event);
-/*
- * The standard checkbox handler expects the main `context' field
- * to contain the `offsetof' an int field in the structure pointed
- * to by `data', optionally ORed with CHECKBOX_INVERT to indicate
- * that the sense of the datum is opposite to the sense of the
- * checkbox.
- */
-#define CHECKBOX_INVERT (1<<30)
-void dlg_stdcheckbox_handler(union control *ctrl, void *dlg,
-                            void *data, int event);
-/*
- * The standard edit-box handler expects the main `context' field
- * to contain the `offsetof' a field in the structure pointed to by
- * `data'. The secondary `context2' field indicates the type of
- * this field:
- * 
- *  - if context2 > 0, the field is a char array and context2 gives
- *    its size.
- *  - if context2 == -1, the field is an int and the edit box is
- *    numeric.
- *  - if context2 < -1, the field is an int and the edit box is
- *    _floating_, and (-context2) gives the scale. (E.g. if
- *    context2 == -1000, then typing 1.2 into the box will set the
- *    field to 1200.)
- */
-void dlg_stdeditbox_handler(union control *ctrl, void *dlg,
-                           void *data, int event);
-/*
  * The standard file-selector handler expects the main `context'
  * field to contain the `offsetof' a Filename field in the
  * structure pointed to by `data'.
@@ -584,7 +545,7 @@ int dlg_radiobutton_get(union control *ctrl, void *dlg);
 void dlg_checkbox_set(union control *ctrl, void *dlg, int checked);
 int dlg_checkbox_get(union control *ctrl, void *dlg);
 void dlg_editbox_set(union control *ctrl, void *dlg, char const *text);
-void dlg_editbox_get(union control *ctrl, void *dlg, char *buffer, int length);
+char *dlg_editbox_get(union control *ctrl, void *dlg);   /* result must be freed by caller */
 /* The `listbox' functions can also apply to combo boxes. */
 void dlg_listbox_clear(union control *ctrl, void *dlg);
 void dlg_listbox_del(union control *ctrl, void *dlg, int index);
diff --git a/ldisc.c b/ldisc.c
index f2853f4..efbb871 100644 (file)
--- a/ldisc.c
+++ b/ldisc.c
 #include "terminal.h"
 #include "ldisc.h"
 
-#define ECHOING (ldisc->cfg->localecho == FORCE_ON || \
-                 (ldisc->cfg->localecho == AUTO && \
+#define ECHOING (ldisc->localecho == FORCE_ON || \
+                 (ldisc->localecho == AUTO && \
                       (ldisc->back->ldisc(ldisc->backhandle, LD_ECHO) || \
                           term_ldisc(ldisc->term, LD_ECHO))))
-#define EDITING (ldisc->cfg->localedit == FORCE_ON || \
-                 (ldisc->cfg->localedit == AUTO && \
+#define EDITING (ldisc->localedit == FORCE_ON || \
+                 (ldisc->localedit == AUTO && \
                       (ldisc->back->ldisc(ldisc->backhandle, LD_EDIT) || \
                           term_ldisc(ldisc->term, LD_EDIT))))
 
@@ -76,7 +76,7 @@ static void bsb(Ldisc ldisc, int n)
 #define CTRL(x) (x^'@')
 #define KCTRL(x) ((x^'@') | 0x100)
 
-void *ldisc_create(Config *mycfg, Terminal *term,
+void *ldisc_create(Conf *conf, Terminal *term,
                   Backend *back, void *backhandle,
                   void *frontend)
 {
@@ -87,12 +87,13 @@ void *ldisc_create(Config *mycfg, Terminal *term,
     ldisc->bufsiz = 0;
     ldisc->quotenext = 0;
 
-    ldisc->cfg = mycfg;
     ldisc->back = back;
     ldisc->backhandle = backhandle;
     ldisc->term = term;
     ldisc->frontend = frontend;
 
+    ldisc_configure(ldisc, conf);
+
     /* Link ourselves into the backend and the terminal */
     if (term)
        term->ldisc = ldisc;
@@ -102,6 +103,17 @@ void *ldisc_create(Config *mycfg, Terminal *term,
     return ldisc;
 }
 
+void ldisc_configure(void *handle, Conf *conf)
+{
+    Ldisc ldisc = (Ldisc) handle;
+
+    ldisc->telnet_keyboard = conf_get_int(conf, CONF_telnet_keyboard);
+    ldisc->telnet_newline = conf_get_int(conf, CONF_telnet_newline);
+    ldisc->protocol = conf_get_int(conf, CONF_protocol);
+    ldisc->localecho = conf_get_int(conf, CONF_localecho);
+    ldisc->localedit = conf_get_int(conf, CONF_localedit);
+}
+
 void ldisc_free(void *handle)
 {
     Ldisc ldisc = (Ldisc) handle;
@@ -203,7 +215,7 @@ void ldisc_send(void *handle, char *buf, int len, int interactive)
                  * configured telnet specials off! This breaks
                  * talkers otherwise.
                  */
-                if (!ldisc->cfg->telnet_keyboard)
+                if (!ldisc->telnet_keyboard)
                     goto default_case;
                if (c == CTRL('C'))
                    ldisc->back->special(ldisc->backhandle, TS_IP);
@@ -255,7 +267,7 @@ void ldisc_send(void *handle, char *buf, int len, int interactive)
                 *    default clause because of the break.
                 */
              case CTRL('J'):
-               if (ldisc->cfg->protocol == PROT_RAW &&
+               if (ldisc->protocol == PROT_RAW &&
                    ldisc->buflen > 0 && ldisc->buf[ldisc->buflen - 1] == '\r') {
                    if (ECHOING)
                        bsb(ldisc, plen(ldisc, ldisc->buf[ldisc->buflen - 1]));
@@ -264,9 +276,9 @@ void ldisc_send(void *handle, char *buf, int len, int interactive)
              case KCTRL('M'):         /* send with newline */
                    if (ldisc->buflen > 0)
                        ldisc->back->send(ldisc->backhandle, ldisc->buf, ldisc->buflen);
-                   if (ldisc->cfg->protocol == PROT_RAW)
+                   if (ldisc->protocol == PROT_RAW)
                        ldisc->back->send(ldisc->backhandle, "\r\n", 2);
-                   else if (ldisc->cfg->protocol == PROT_TELNET && ldisc->cfg->telnet_newline)
+                   else if (ldisc->protocol == PROT_TELNET && ldisc->telnet_newline)
                        ldisc->back->special(ldisc->backhandle, TS_EOL);
                    else
                        ldisc->back->send(ldisc->backhandle, "\r", 1);
@@ -300,27 +312,27 @@ void ldisc_send(void *handle, char *buf, int len, int interactive)
        if (len > 0) {
            if (ECHOING)
                c_write(ldisc, buf, len);
-           if (keyflag && ldisc->cfg->protocol == PROT_TELNET && len == 1) {
+           if (keyflag && ldisc->protocol == PROT_TELNET && len == 1) {
                switch (buf[0]) {
                  case CTRL('M'):
-                   if (ldisc->cfg->protocol == PROT_TELNET && ldisc->cfg->telnet_newline)
+                   if (ldisc->protocol == PROT_TELNET && ldisc->telnet_newline)
                        ldisc->back->special(ldisc->backhandle, TS_EOL);
                    else
                        ldisc->back->send(ldisc->backhandle, "\r", 1);
                    break;
                  case CTRL('?'):
                  case CTRL('H'):
-                   if (ldisc->cfg->telnet_keyboard) {
+                   if (ldisc->telnet_keyboard) {
                        ldisc->back->special(ldisc->backhandle, TS_EC);
                        break;
                    }
                  case CTRL('C'):
-                   if (ldisc->cfg->telnet_keyboard) {
+                   if (ldisc->telnet_keyboard) {
                        ldisc->back->special(ldisc->backhandle, TS_IP);
                        break;
                    }
                  case CTRL('Z'):
-                   if (ldisc->cfg->telnet_keyboard) {
+                   if (ldisc->telnet_keyboard) {
                        ldisc->back->special(ldisc->backhandle, TS_SUSP);
                        break;
                    }
diff --git a/ldisc.h b/ldisc.h
index 2bca86d..5dbe2a7 100644 (file)
--- a/ldisc.h
+++ b/ldisc.h
 typedef struct ldisc_tag {
     Terminal *term;
     Backend *back;
-    Config *cfg;
     void *backhandle;
     void *frontend;
 
+    /*
+     * Values cached out of conf.
+     */
+    int telnet_keyboard, telnet_newline, protocol, localecho, localedit;
+
     char *buf;
     int buflen, bufsiz, quotenext;
 } *Ldisc;
index 7465480..b2401c3 100644 (file)
--- a/logging.c
+++ b/logging.c
@@ -18,7 +18,8 @@ struct LogContext {
     bufchain queue;
     Filename currlogfilename;
     void *frontend;
-    Config cfg;
+    Conf *conf;
+    int logtype;                      /* cached out of conf */
 };
 
 static void xlatlognam(Filename *d, Filename s, char *hostname, struct tm *tm);
@@ -75,7 +76,7 @@ static void logprintf(struct LogContext *ctx, const char *fmt, ...)
  */
 void logflush(void *handle) {
     struct LogContext *ctx = (struct LogContext *)handle;
-    if (ctx->cfg.logtype > 0)
+    if (ctx->logtype > 0)
        if (ctx->state == L_OPEN)
            fflush(ctx->lgfp);
 }
@@ -110,10 +111,10 @@ static void logfopen_callback(void *handle, int mode)
                      ctx->state == L_ERROR ?
                      (mode == 0 ? "Disabled writing" : "Error writing") :
                      (mode == 1 ? "Appending" : "Writing new"),
-                     (ctx->cfg.logtype == LGTYP_ASCII ? "ASCII" :
-                      ctx->cfg.logtype == LGTYP_DEBUG ? "raw" :
-                      ctx->cfg.logtype == LGTYP_PACKETS ? "SSH packets" :
-                      ctx->cfg.logtype == LGTYP_SSHRAW ? "SSH raw data" :
+                     (ctx->logtype == LGTYP_ASCII ? "ASCII" :
+                      ctx->logtype == LGTYP_DEBUG ? "raw" :
+                      ctx->logtype == LGTYP_PACKETS ? "SSH packets" :
+                      ctx->logtype == LGTYP_SSHRAW ? "SSH raw data" :
                       "unknown"),
                      filename_to_str(&ctx->currlogfilename));
     logevent(ctx->frontend, event);
@@ -148,19 +149,22 @@ void logfopen(void *handle)
     if (ctx->state != L_CLOSED)
        return;
 
-    if (!ctx->cfg.logtype)
+    if (!ctx->logtype)
        return;
 
     tm = ltime();
 
     /* substitute special codes in file name */
-    xlatlognam(&ctx->currlogfilename, ctx->cfg.logfilename,ctx->cfg.host, &tm);
+    xlatlognam(&ctx->currlogfilename,
+              *conf_get_filename(ctx->conf, CONF_logfilename),
+              conf_get_str(ctx->conf, CONF_host), &tm);
 
     ctx->lgfp = f_open(ctx->currlogfilename, "r", FALSE);  /* file already present? */
     if (ctx->lgfp) {
+       int logxfovr = conf_get_int(ctx->conf, CONF_logxfovr);
        fclose(ctx->lgfp);
-       if (ctx->cfg.logxfovr != LGXF_ASK) {
-           mode = ((ctx->cfg.logxfovr == LGXF_OVR) ? 2 : 1);
+       if (logxfovr != LGXF_ASK) {
+           mode = ((logxfovr == LGXF_OVR) ? 2 : 1);
        } else
            mode = askappend(ctx->frontend, ctx->currlogfilename,
                             logfopen_callback, ctx);
@@ -189,8 +193,8 @@ void logfclose(void *handle)
 void logtraffic(void *handle, unsigned char c, int logmode)
 {
     struct LogContext *ctx = (struct LogContext *)handle;
-    if (ctx->cfg.logtype > 0) {
-       if (ctx->cfg.logtype == logmode)
+    if (ctx->logtype > 0) {
+       if (ctx->logtype == logmode)
            logwrite(ctx, &c, 1);
     }
 }
@@ -214,8 +218,8 @@ void log_eventlog(void *handle, const char *event)
     /* If we don't have a context yet (eg winnet.c init) then skip entirely */
     if (!ctx)
        return;
-    if (ctx->cfg.logtype != LGTYP_PACKETS &&
-       ctx->cfg.logtype != LGTYP_SSHRAW)
+    if (ctx->logtype != LGTYP_PACKETS &&
+       ctx->logtype != LGTYP_SSHRAW)
        return;
     logprintf(ctx, "Event Log: %s\r\n", event);
     logflush(ctx);
@@ -236,8 +240,8 @@ void log_packet(void *handle, int direction, int type,
     int p = 0, b = 0, omitted = 0;
     int output_pos = 0; /* NZ if pending output in dumpdata */
 
-    if (!(ctx->cfg.logtype == LGTYP_SSHRAW ||
-          (ctx->cfg.logtype == LGTYP_PACKETS && texttype)))
+    if (!(ctx->logtype == LGTYP_SSHRAW ||
+          (ctx->logtype == LGTYP_PACKETS && texttype)))
        return;
 
     /* Packet header. */
@@ -326,13 +330,14 @@ void log_packet(void *handle, int direction, int type,
     logflush(ctx);
 }
 
-void *log_init(void *frontend, Config *cfg)
+void *log_init(void *frontend, Conf *conf)
 {
     struct LogContext *ctx = snew(struct LogContext);
     ctx->lgfp = NULL;
     ctx->state = L_CLOSED;
     ctx->frontend = frontend;
-    ctx->cfg = *cfg;                  /* STRUCTURE COPY */
+    ctx->conf = conf_copy(conf);
+    ctx->logtype = conf_get_int(ctx->conf, CONF_logtype);
     bufchain_init(&ctx->queue);
     return ctx;
 }
@@ -346,13 +351,15 @@ void log_free(void *handle)
     sfree(ctx);
 }
 
-void log_reconfig(void *handle, Config *cfg)
+void log_reconfig(void *handle, Conf *conf)
 {
     struct LogContext *ctx = (struct LogContext *)handle;
     int reset_logging;
 
-    if (!filename_equal(ctx->cfg.logfilename, cfg->logfilename) ||
-       ctx->cfg.logtype != cfg->logtype)
+    if (!filename_equal(*conf_get_filename(ctx->conf, CONF_logfilename),
+                       *conf_get_filename(conf, CONF_logfilename)) ||
+       conf_get_int(ctx->conf, CONF_logtype) !=
+       conf_get_int(conf, CONF_logtype))
        reset_logging = TRUE;
     else
        reset_logging = FALSE;
@@ -360,7 +367,10 @@ void log_reconfig(void *handle, Config *cfg)
     if (reset_logging)
        logfclose(ctx);
 
-    ctx->cfg = *cfg;                  /* STRUCTURE COPY */
+    conf_free(ctx->conf);
+    ctx->conf = conf_copy(conf);
+
+    ctx->logtype = conf_get_int(ctx->conf, CONF_logtype);
 
     if (reset_logging)
        logfopen(ctx);
index 7076e0e..084671a 100644 (file)
@@ -20,6 +20,15 @@ say you weren't warned!
 
 Other ways in which the port is currently unfinished include:
 
+Bit rot
+-------
+
+ - the conversion of the old fixed-size 'Config' structure to the
+   new dynamic 'Conf' was never applied to this directory
+
+ - probably other things are out of date too; it would need some
+   work to make it compile again
+
 Missing terminal window features
 --------------------------------
 
diff --git a/misc.c b/misc.c
index 8f728ee..cf97c04 100644 (file)
--- a/misc.c
+++ b/misc.c
@@ -635,21 +635,21 @@ void debug_memdump(void *buf, int len, int L)
 #endif                         /* def DEBUG */
 
 /*
- * Determine whether or not a Config structure represents a session
- * which can sensibly be launched right now.
+ * Determine whether or not a Conf represents a session which can
+ * sensibly be launched right now.
  */
-int cfg_launchable(const Config *cfg)
+int conf_launchable(Conf *conf)
 {
-    if (cfg->protocol == PROT_SERIAL)
-       return cfg->serline[0] != 0;
+    if (conf_get_int(conf, CONF_protocol) == PROT_SERIAL)
+       return conf_get_str(conf, CONF_serline)[0] != 0;
     else
-       return cfg->host[0] != 0;
+       return conf_get_str(conf, CONF_host)[0] != 0;
 }
 
-char const *cfg_dest(const Config *cfg)
+char const *conf_dest(Conf *conf)
 {
-    if (cfg->protocol == PROT_SERIAL)
-       return cfg->serline;
+    if (conf_get_int(conf, CONF_protocol) == PROT_SERIAL)
+       return conf_get_str(conf, CONF_serline);
     else
-       return cfg->host;
+       return conf_get_str(conf, CONF_host);
 }
index 6ee7081..d9f4886 100644 (file)
--- a/network.h
+++ b/network.h
@@ -15,7 +15,7 @@
 
 #ifndef DONE_TYPEDEFS
 #define DONE_TYPEDEFS
-typedef struct config_tag Config;
+typedef struct conf_tag Conf;
 typedef struct backend_tag Backend;
 typedef struct terminal_tag Terminal;
 #endif
@@ -94,18 +94,18 @@ struct plug_function_table {
 Socket new_connection(SockAddr addr, char *hostname,
                      int port, int privport,
                      int oobinline, int nodelay, int keepalive,
-                     Plug plug, const Config *cfg);
+                     Plug plug, Conf *conf);
 Socket new_listener(char *srcaddr, int port, Plug plug, int local_host_only,
-                   const Config *cfg, int addressfamily);
+                   Conf *conf, int addressfamily);
 SockAddr name_lookup(char *host, int port, char **canonicalname,
-                    const Config *cfg, int addressfamily);
+                    Conf *conf, int addressfamily);
 
 /* platform-dependent callback from new_connection() */
 /* (same caveat about addr as new_connection()) */
 Socket platform_new_connection(SockAddr addr, char *hostname,
                               int port, int privport,
                               int oobinline, int nodelay, int keepalive,
-                              Plug plug, const Config *cfg);
+                              Plug plug, Conf *conf);
 
 /* socket functions */
 
index c9b7503..3e2626f 100644 (file)
--- a/pinger.c
+++ b/pinger.c
@@ -43,11 +43,11 @@ static void pinger_schedule(Pinger pinger)
     }
 }
 
-Pinger pinger_new(Config *cfg, Backend *back, void *backhandle)
+Pinger pinger_new(Conf *conf, Backend *back, void *backhandle)
 {
     Pinger pinger = snew(struct pinger_tag);
 
-    pinger->interval = cfg->ping_interval;
+    pinger->interval = conf_get_int(conf, CONF_ping_interval);
     pinger->pending = FALSE;
     pinger->back = back;
     pinger->backhandle = backhandle;
@@ -56,10 +56,11 @@ Pinger pinger_new(Config *cfg, Backend *back, void *backhandle)
     return pinger;
 }
 
-void pinger_reconfig(Pinger pinger, Config *oldcfg, Config *newcfg)
+void pinger_reconfig(Pinger pinger, Conf *oldconf, Conf *newconf)
 {
-    if (oldcfg->ping_interval != newcfg->ping_interval) {
-       pinger->interval = newcfg->ping_interval;
+    int newinterval = conf_get_int(newconf, CONF_ping_interval);
+    if (conf_get_int(oldconf, CONF_ping_interval) != newinterval) {
+       pinger->interval = newinterval;
        pinger_schedule(pinger);
     }
 }
index 662d995..e7e9d63 100644 (file)
--- a/portfwd.c
+++ b/portfwd.c
@@ -325,7 +325,7 @@ static void pfd_sent(Plug plug, int bufsize)
  * Called when receiving a PORT OPEN from the server
  */
 const char *pfd_newconnect(Socket *s, char *hostname, int port,
-                          void *c, const Config *cfg, int addressfamily)
+                          void *c, Conf *conf, int addressfamily)
 {
     static const struct plug_function_table fn_table = {
        pfd_log,
@@ -343,7 +343,7 @@ const char *pfd_newconnect(Socket *s, char *hostname, int port,
     /*
      * Try to find host.
      */
-    addr = name_lookup(hostname, port, &dummy_realhost, cfg, addressfamily);
+    addr = name_lookup(hostname, port, &dummy_realhost, conf, addressfamily);
     if ((err = sk_addr_error(addr)) != NULL) {
        sk_addr_free(addr);
        return err;
@@ -362,7 +362,7 @@ const char *pfd_newconnect(Socket *s, char *hostname, int port,
     pr->dynamic = 0;
 
     pr->s = *s = new_connection(addr, dummy_realhost, port,
-                               0, 1, 0, 0, (Plug) pr, cfg);
+                               0, 1, 0, 0, (Plug) pr, conf);
     if ((err = sk_socket_error(*s)) != NULL) {
        sfree(pr);
        return err;
@@ -435,7 +435,7 @@ static int pfd_accepting(Plug p, OSSocket sock)
  sets up a listener on the local machine on (srcaddr:)port
  */
 const char *pfd_addforward(char *desthost, int destport, char *srcaddr,
-                          int port, void *backhandle, const Config *cfg,
+                          int port, void *backhandle, Conf *conf,
                           void **sockdata, int address_family)
 {
     static const struct plug_function_table fn_table = {
@@ -468,7 +468,8 @@ const char *pfd_addforward(char *desthost, int destport, char *srcaddr,
     pr->backhandle = backhandle;
 
     pr->s = s = new_listener(srcaddr, port, (Plug) pr,
-                            !cfg->lport_acceptall, cfg, address_family);
+                            !conf_get_int(conf, CONF_lport_acceptall),
+                            conf, address_family);
     if ((err = sk_socket_error(s)) != NULL) {
        sfree(pr);
        return err;
index 9c4c2d8..1bea6fb 100644 (file)
--- a/pproxy.c
+++ b/pproxy.c
@@ -11,7 +11,7 @@
 Socket platform_new_connection(SockAddr addr, char *hostname,
                               int port, int privport,
                               int oobinline, int nodelay, int keepalive,
-                              Plug plug, const Config *cfg)
+                              Plug plug, Conf *conf)
 {
     return NULL;
 }
diff --git a/proxy.c b/proxy.c
index c6008a5..0b6d9d5 100644 (file)
--- a/proxy.c
+++ b/proxy.c
 #include "network.h"
 #include "proxy.h"
 
-#define do_proxy_dns(cfg) \
-    (cfg->proxy_dns == FORCE_ON || \
-        (cfg->proxy_dns == AUTO && cfg->proxy_type != PROXY_SOCKS4))
+#define do_proxy_dns(conf) \
+    (conf_get_int(conf, CONF_proxy_dns) == FORCE_ON || \
+        (conf_get_int(conf, CONF_proxy_dns) == AUTO && \
+             conf_get_int(conf, CONF_proxy_type) != PROXY_SOCKS4))
 
 /*
  * Call this when proxy negotiation is complete, so that this
@@ -263,7 +264,7 @@ static int plug_proxy_accepting (Plug p, OSSocket sock)
  * it will only check the host name.
  */
 static int proxy_for_destination (SockAddr addr, char *hostname, int port,
-                                 const Config *cfg)
+                                 Conf *conf)
 {
     int s = 0, e = 0;
     char hostip[64];
@@ -274,7 +275,7 @@ static int proxy_for_destination (SockAddr addr, char *hostname, int port,
      * Check the host name and IP against the hard-coded
      * representations of `localhost'.
      */
-    if (!cfg->even_proxy_localhost &&
+    if (!conf_get_int(conf, CONF_even_proxy_localhost) &&
        (sk_hostname_is_local(hostname) ||
         (addr && sk_address_is_local(addr))))
        return 0;                      /* do not proxy */
@@ -288,7 +289,7 @@ static int proxy_for_destination (SockAddr addr, char *hostname, int port,
 
     hostname_len = strlen(hostname);
 
-    exclude_list = cfg->proxy_exclude_list;
+    exclude_list = conf_get_str(conf, CONF_proxy_exclude_list);
 
     /* now parse the exclude list, and see if either our IP
      * or hostname matches anything in it.
@@ -349,11 +350,11 @@ static int proxy_for_destination (SockAddr addr, char *hostname, int port,
 }
 
 SockAddr name_lookup(char *host, int port, char **canonicalname,
-                    const Config *cfg, int addressfamily)
+                    Conf *conf, int addressfamily)
 {
-    if (cfg->proxy_type != PROXY_NONE &&
-       do_proxy_dns(cfg) &&
-       proxy_for_destination(NULL, host, port, cfg)) {
+    if (conf_get_int(conf, CONF_proxy_type) != PROXY_NONE &&
+       do_proxy_dns(conf) &&
+       proxy_for_destination(NULL, host, port, conf)) {
        *canonicalname = dupstr(host);
        return sk_nonamelookup(host);
     }
@@ -364,7 +365,7 @@ SockAddr name_lookup(char *host, int port, char **canonicalname,
 Socket new_connection(SockAddr addr, char *hostname,
                      int port, int privport,
                      int oobinline, int nodelay, int keepalive,
-                     Plug plug, const Config *cfg)
+                     Plug plug, Conf *conf)
 {
     static const struct socket_function_table socket_fn_table = {
        sk_proxy_plug,
@@ -386,24 +387,25 @@ Socket new_connection(SockAddr addr, char *hostname,
        plug_proxy_accepting
     };
 
-    if (cfg->proxy_type != PROXY_NONE &&
-       proxy_for_destination(addr, hostname, port, cfg))
+    if (conf_get_int(conf, CONF_proxy_type) != PROXY_NONE &&
+       proxy_for_destination(addr, hostname, port, conf))
     {
        Proxy_Socket ret;
        Proxy_Plug pplug;
        SockAddr proxy_addr;
        char *proxy_canonical_name;
        Socket sret;
+       int type;
 
        if ((sret = platform_new_connection(addr, hostname, port, privport,
                                            oobinline, nodelay, keepalive,
-                                           plug, cfg)) !=
+                                           plug, conf)) !=
            NULL)
            return sret;
 
        ret = snew(struct Socket_proxy_tag);
        ret->fn = &socket_fn_table;
-       ret->cfg = *cfg;               /* STRUCTURE COPY */
+       ret->conf = conf_copy(conf);
        ret->plug = plug;
        ret->remote_addr = addr;       /* will need to be freed on close */
        ret->remote_port = port;
@@ -419,14 +421,15 @@ Socket new_connection(SockAddr addr, char *hostname,
        ret->sub_socket = NULL;
        ret->state = PROXY_STATE_NEW;
        ret->negotiate = NULL;
-       
-       if (cfg->proxy_type == PROXY_HTTP) {
+
+       type = conf_get_int(conf, CONF_proxy_type);
+       if (type == PROXY_HTTP) {
            ret->negotiate = proxy_http_negotiate;
-       } else if (cfg->proxy_type == PROXY_SOCKS4) {
+       } else if (type == PROXY_SOCKS4) {
             ret->negotiate = proxy_socks4_negotiate;
-       } else if (cfg->proxy_type == PROXY_SOCKS5) {
+       } else if (type == PROXY_SOCKS5) {
             ret->negotiate = proxy_socks5_negotiate;
-       } else if (cfg->proxy_type == PROXY_TELNET) {
+       } else if (type == PROXY_TELNET) {
            ret->negotiate = proxy_telnet_negotiate;
        } else {
            ret->error = "Proxy error: Unknown proxy method";
@@ -440,8 +443,9 @@ Socket new_connection(SockAddr addr, char *hostname,
        pplug->proxy_socket = ret;
 
        /* look-up proxy */
-       proxy_addr = sk_namelookup(cfg->proxy_host,
-                                  &proxy_canonical_name, cfg->addressfamily);
+       proxy_addr = sk_namelookup(conf_get_str(conf, CONF_proxy_host),
+                                  &proxy_canonical_name,
+                                  conf_get_int(conf, CONF_addressfamily));
        if (sk_addr_error(proxy_addr) != NULL) {
            ret->error = "Proxy error: Unable to resolve proxy host name";
            return (Socket)ret;
@@ -451,7 +455,8 @@ Socket new_connection(SockAddr addr, char *hostname,
        /* create the actual socket we will be using,
         * connected to our proxy server and port.
         */
-       ret->sub_socket = sk_new(proxy_addr, cfg->proxy_port,
+       ret->sub_socket = sk_new(proxy_addr,
+                                conf_get_int(conf, CONF_proxy_port),
                                 privport, oobinline,
                                 nodelay, keepalive, (Plug) pplug);
        if (sk_socket_error(ret->sub_socket) != NULL)
@@ -469,7 +474,7 @@ Socket new_connection(SockAddr addr, char *hostname,
 }
 
 Socket new_listener(char *srcaddr, int port, Plug plug, int local_host_only,
-                   const Config *cfg, int addressfamily)
+                   Conf *conf, int addressfamily)
 {
     /* TODO: SOCKS (and potentially others) support inbound
      * TODO: connections via the proxy. support them.
@@ -525,6 +530,7 @@ int proxy_http_negotiate (Proxy_Socket p, int change)
         * request
         */
        char *buf, dest[512];
+       char *username, *password;
 
        sk_getaddr(p->remote_addr, dest, lenof(dest));
 
@@ -533,18 +539,22 @@ int proxy_http_negotiate (Proxy_Socket p, int change)
        sk_write(p->sub_socket, buf, strlen(buf));
        sfree(buf);
 
-       if (p->cfg.proxy_username[0] || p->cfg.proxy_password[0]) {
-           char buf[sizeof(p->cfg.proxy_username)+sizeof(p->cfg.proxy_password)];
-           char buf2[sizeof(buf)*4/3 + 100];
+       username = conf_get_str(p->conf, CONF_proxy_username);
+       password = conf_get_str(p->conf, CONF_proxy_password);
+       if (username[0] || password[0]) {
+           char *buf, *buf2;
            int i, j, len;
-           sprintf(buf, "%s:%s", p->cfg.proxy_username, p->cfg.proxy_password);
+           buf = dupprintf("%s:%s", username, password);
            len = strlen(buf);
+           buf2 = snewn(len * 4 / 3 + 100, char);
            sprintf(buf2, "Proxy-Authorization: Basic ");
            for (i = 0, j = strlen(buf2); i < len; i += 3, j += 4)
                base64_encode_atom((unsigned char *)(buf+i),
                                   (len-i > 3 ? 3 : len-i), buf2+j);
            strcpy(buf2+j, "\r\n");
            sk_write(p->sub_socket, buf2, strlen(buf2));
+           sfree(buf);
+           sfree(buf2);
        }
 
        sk_write(p->sub_socket, "\r\n", 2);
@@ -711,6 +721,7 @@ int proxy_socks4_negotiate (Proxy_Socket p, int change)
 
        int length, type, namelen;
        char *command, addr[4], hostname[512];
+       char *username;
 
        type = sk_addrtype(p->remote_addr);
        if (type == ADDRTYPE_IPV6) {
@@ -728,9 +739,10 @@ int proxy_socks4_negotiate (Proxy_Socket p, int change)
            addr[3] = 1;
        }
 
-       length = strlen(p->cfg.proxy_username) + namelen + 9;
+       username = conf_get_str(p->conf, CONF_proxy_username);
+       length = strlen(username) + namelen + 9;
        command = snewn(length, char);
-       strcpy(command + 8, p->cfg.proxy_username);
+       strcpy(command + 8, username);
 
        command[0] = 4; /* version 4 */
        command[1] = 1; /* CONNECT command */
@@ -743,10 +755,11 @@ int proxy_socks4_negotiate (Proxy_Socket p, int change)
        memcpy(command + 4, addr, 4);
 
        /* hostname */
-       memcpy(command + 8 + strlen(p->cfg.proxy_username) + 1,
+       memcpy(command + 8 + strlen(username) + 1,
               hostname, namelen);
 
        sk_write(p->sub_socket, command, length);
+       sfree(username);
        sfree(command);
 
        p->state = 1;
@@ -868,10 +881,13 @@ int proxy_socks5_negotiate (Proxy_Socket p, int change)
         */
 
        char command[5];
+       char *username, *password;
        int len;
 
        command[0] = 5; /* version 5 */
-       if (p->cfg.proxy_username[0] || p->cfg.proxy_password[0]) {
+       username = conf_get_str(p->conf, CONF_proxy_username);
+       password = conf_get_str(p->conf, CONF_proxy_password);
+       if (username[0] || password[0]) {
            command[2] = 0x00;         /* no authentication */
            len = 3;
            proxy_socks5_offerencryptedauth (command, &len);
@@ -1148,18 +1164,20 @@ int proxy_socks5_negotiate (Proxy_Socket p, int change)
        }
 
        if (p->state == 5) {
-           if (p->cfg.proxy_username[0] || p->cfg.proxy_password[0]) {
-               char userpwbuf[514];
+           char *username = conf_get_str(p->conf, CONF_proxy_username);
+           char *password = conf_get_str(p->conf, CONF_proxy_password);
+           if (username[0] || password[0]) {
+               char userpwbuf[255 + 255 + 3];
                int ulen, plen;
-               ulen = strlen(p->cfg.proxy_username);
+               ulen = strlen(username);
                if (ulen > 255) ulen = 255; if (ulen < 1) ulen = 1;
-               plen = strlen(p->cfg.proxy_password);
+               plen = strlen(password);
                if (plen > 255) plen = 255; if (plen < 1) plen = 1;
                userpwbuf[0] = 1;      /* version number of subnegotiation */
                userpwbuf[1] = ulen;
-               memcpy(userpwbuf+2, p->cfg.proxy_username, ulen);
+               memcpy(userpwbuf+2, username, ulen);
                userpwbuf[ulen+2] = plen;
-               memcpy(userpwbuf+ulen+3, p->cfg.proxy_password, plen);
+               memcpy(userpwbuf+ulen+3, password, plen);
                sk_write(p->sub_socket, userpwbuf, ulen + plen + 3);
                p->state = 7;
            } else 
@@ -1192,8 +1210,9 @@ int proxy_socks5_negotiate (Proxy_Socket p, int change)
  * standardised or at all well-defined.)
  */
 
-char *format_telnet_command(SockAddr addr, int port, const Config *cfg)
+char *format_telnet_command(SockAddr addr, int port, Conf *conf)
 {
+    char *fmt = conf_get_str(conf, CONF_proxy_telnet_command);
     char *ret = NULL;
     int retlen = 0, retsize = 0;
     int so = 0, eo = 0;
@@ -1208,22 +1227,21 @@ char *format_telnet_command(SockAddr addr, int port, const Config *cfg)
      * %%, %host, %port, %user, and %pass
      */
 
-    while (cfg->proxy_telnet_command[eo] != 0) {
+    while (fmt[eo] != 0) {
 
        /* scan forward until we hit end-of-line,
         * or an escape character (\ or %) */
-       while (cfg->proxy_telnet_command[eo] != 0 &&
-              cfg->proxy_telnet_command[eo] != '%' &&
-              cfg->proxy_telnet_command[eo] != '\\') eo++;
+       while (fmt[eo] != 0 && fmt[eo] != '%' && fmt[eo] != '\\')
+           eo++;
 
        /* if we hit eol, break out of our escaping loop */
-       if (cfg->proxy_telnet_command[eo] == 0) break;
+       if (fmt[eo] == 0) break;
 
        /* if there was any unescaped text before the escape
         * character, send that now */
        if (eo != so) {
            ENSURE(eo - so);
-           memcpy(ret + retlen, cfg->proxy_telnet_command + so, eo - so);
+           memcpy(ret + retlen, fmt + so, eo - so);
            retlen += eo - so;
        }
 
@@ -1231,15 +1249,15 @@ char *format_telnet_command(SockAddr addr, int port, const Config *cfg)
 
        /* if the escape character was the last character of
         * the line, we'll just stop and send it. */
-       if (cfg->proxy_telnet_command[eo] == 0) break;
+       if (fmt[eo] == 0) break;
 
-       if (cfg->proxy_telnet_command[so] == '\\') {
+       if (fmt[so] == '\\') {
 
            /* we recognize \\, \%, \r, \n, \t, \x??.
             * anything else, we just send unescaped (including the \).
             */
 
-           switch (cfg->proxy_telnet_command[eo]) {
+           switch (fmt[eo]) {
 
              case '\\':
                ENSURE(1);
@@ -1280,15 +1298,12 @@ char *format_telnet_command(SockAddr addr, int port, const Config *cfg)
 
                    for (;;) {
                        eo++;
-                       if (cfg->proxy_telnet_command[eo] >= '0' &&
-                           cfg->proxy_telnet_command[eo] <= '9')
-                           v += cfg->proxy_telnet_command[eo] - '0';
-                       else if (cfg->proxy_telnet_command[eo] >= 'a' &&
-                                cfg->proxy_telnet_command[eo] <= 'f')
-                           v += cfg->proxy_telnet_command[eo] - 'a' + 10;
-                       else if (cfg->proxy_telnet_command[eo] >= 'A' &&
-                                cfg->proxy_telnet_command[eo] <= 'F')
-                           v += cfg->proxy_telnet_command[eo] - 'A' + 10;
+                       if (fmt[eo] >= '0' && fmt[eo] <= '9')
+                           v += fmt[eo] - '0';
+                       else if (fmt[eo] >= 'a' && fmt[eo] <= 'f')
+                           v += fmt[eo] - 'a' + 10;
+                       else if (fmt[eo] >= 'A' && fmt[eo] <= 'F')
+                           v += fmt[eo] - 'A' + 10;
                        else {
                            /* non hex character, so we abort and just
                             * send the whole thing unescaped (including \x)
@@ -1315,7 +1330,7 @@ char *format_telnet_command(SockAddr addr, int port, const Config *cfg)
 
              default:
                ENSURE(2);
-               memcpy(ret+retlen, cfg->proxy_telnet_command + so, 2);
+               memcpy(ret+retlen, fmt + so, 2);
                retlen += 2;
                eo++;
                break;
@@ -1327,13 +1342,12 @@ char *format_telnet_command(SockAddr addr, int port, const Config *cfg)
             * unescaped (including the %).
             */
 
-           if (cfg->proxy_telnet_command[eo] == '%') {
+           if (fmt[eo] == '%') {
                ENSURE(1);
                ret[retlen++] = '%';
                eo++;
            }
-           else if (strnicmp(cfg->proxy_telnet_command + eo,
-                             "host", 4) == 0) {
+           else if (strnicmp(fmt + eo, "host", 4) == 0) {
                char dest[512];
                int destlen;
                sk_getaddr(addr, dest, lenof(dest));
@@ -1343,8 +1357,7 @@ char *format_telnet_command(SockAddr addr, int port, const Config *cfg)
                retlen += destlen;
                eo += 4;
            }
-           else if (strnicmp(cfg->proxy_telnet_command + eo,
-                             "port", 4) == 0) {
+           else if (strnicmp(fmt + eo, "port", 4) == 0) {
                char portstr[8], portlen;
                portlen = sprintf(portstr, "%i", port);
                ENSURE(portlen);
@@ -1352,35 +1365,35 @@ char *format_telnet_command(SockAddr addr, int port, const Config *cfg)
                retlen += portlen;
                eo += 4;
            }
-           else if (strnicmp(cfg->proxy_telnet_command + eo,
-                             "user", 4) == 0) {
-               int userlen = strlen(cfg->proxy_username);
+           else if (strnicmp(fmt + eo, "user", 4) == 0) {
+               char *username = conf_get_str(conf, CONF_proxy_username);
+               int userlen = strlen(username);
                ENSURE(userlen);
-               memcpy(ret+retlen, cfg->proxy_username, userlen);
+               memcpy(ret+retlen, username, userlen);
                retlen += userlen;
                eo += 4;
            }
-           else if (strnicmp(cfg->proxy_telnet_command + eo,
-                             "pass", 4) == 0) {
-               int passlen = strlen(cfg->proxy_password);
+           else if (strnicmp(fmt + eo, "pass", 4) == 0) {
+               char *password = conf_get_str(conf, CONF_proxy_password);
+               int passlen = strlen(password);
                ENSURE(passlen);
-               memcpy(ret+retlen, cfg->proxy_password, passlen);
+               memcpy(ret+retlen, password, passlen);
                retlen += passlen;
                eo += 4;
            }
-           else if (strnicmp(cfg->proxy_telnet_command + eo,
-                             "proxyhost", 9) == 0) {
-               int phlen = strlen(cfg->proxy_host);
+           else if (strnicmp(fmt + eo, "proxyhost", 9) == 0) {
+               char *host = conf_get_str(conf, CONF_proxy_host);
+               int phlen = strlen(host);
                ENSURE(phlen);
-               memcpy(ret+retlen, cfg->proxy_host, phlen);
+               memcpy(ret+retlen, host, phlen);
                retlen += phlen;
                eo += 9;
            }
-           else if (strnicmp(cfg->proxy_telnet_command + eo,
-                             "proxyport", 9) == 0) {
+           else if (strnicmp(fmt + eo, "proxyport", 9) == 0) {
+               int port = conf_get_int(conf, CONF_proxy_port);
                 char pport[50];
                int pplen;
-                sprintf(pport, "%d", cfg->proxy_port);
+                sprintf(pport, "%d", port);
                 pplen = strlen(pport);
                ENSURE(pplen);
                memcpy(ret+retlen, pport, pplen);
@@ -1404,7 +1417,7 @@ char *format_telnet_command(SockAddr addr, int port, const Config *cfg)
     /* if there is any unescaped text at the end of the line, send it */
     if (eo != so) {
        ENSURE(eo - so);
-       memcpy(ret + retlen, cfg->proxy_telnet_command + so, eo - so);
+       memcpy(ret + retlen, fmt + so, eo - so);
        retlen += eo - so;
     }
 
@@ -1421,7 +1434,7 @@ int proxy_telnet_negotiate (Proxy_Socket p, int change)
        char *formatted_cmd;
 
        formatted_cmd = format_telnet_command(p->remote_addr, p->remote_port,
-                                             &p->cfg);
+                                             p->conf);
 
        sk_write(p->sub_socket, formatted_cmd, strlen(formatted_cmd));
        sfree(formatted_cmd);
diff --git a/proxy.h b/proxy.h
index 35c5798..3df21c7 100644 (file)
--- a/proxy.h
+++ b/proxy.h
@@ -80,7 +80,7 @@ struct Socket_proxy_tag {
     OSSocket accepting_sock;
 
     /* configuration, used to look up proxy settings */
-    Config cfg;
+    Conf *conf;
 
     /* CHAP transient data */
     int chap_num_attributes;
@@ -110,7 +110,7 @@ extern int proxy_socks5_negotiate (Proxy_Socket, int);
  * This may be reused by local-command proxies on individual
  * platforms.
  */
-char *format_telnet_command(SockAddr addr, int port, const Config *cfg);
+char *format_telnet_command(SockAddr addr, int port, Conf *conf);
 
 /*
  * These are implemented in cproxy.c or nocproxy.c, depending on
diff --git a/pscp.c b/pscp.c
index 451bdb0..a0910c7 100644 (file)
--- a/pscp.c
+++ b/pscp.c
@@ -44,7 +44,7 @@ static int using_sftp = 0;
 
 static Backend *back;
 static void *backhandle;
-static Config cfg;
+static Conf *conf;
 
 static void source(char *src);
 static void rsource(char *src);
@@ -333,88 +333,90 @@ static void do_cmd(char *host, char *user, char *cmd)
      */
     if (!loaded_session) {
        /* Try to load settings for `host' into a temporary config */
-       Config cfg2;
-       cfg2.host[0] = '\0';
-       do_defaults(host, &cfg2);
-       if (cfg2.host[0] != '\0') {
+       Conf *conf2 = conf_new();
+       conf_set_str(conf2, CONF_host, "");
+       do_defaults(host, conf2);
+       if (conf_get_str(conf2, CONF_host)[0] != '\0') {
            /* Settings present and include hostname */
            /* Re-load data into the real config. */
-           do_defaults(host, &cfg);
+           do_defaults(host, conf);
        } else {
            /* Session doesn't exist or mention a hostname. */
            /* Use `host' as a bare hostname. */
-           strncpy(cfg.host, host, sizeof(cfg.host) - 1);
-           cfg.host[sizeof(cfg.host) - 1] = '\0';
+           conf_set_str(conf, CONF_host, host);
        }
     } else {
        /* Patch in hostname `host' to session details. */
-       strncpy(cfg.host, host, sizeof(cfg.host) - 1);
-       cfg.host[sizeof(cfg.host) - 1] = '\0';
+       conf_set_str(conf, CONF_host, host);
     }
 
     /*
      * Force use of SSH. (If they got the protocol wrong we assume the
      * port is useless too.)
      */
-    if (cfg.protocol != PROT_SSH) {
-        cfg.protocol = PROT_SSH;
-        cfg.port = 22;
+    if (conf_get_int(conf, CONF_protocol) != PROT_SSH) {
+        conf_set_int(conf, CONF_protocol, PROT_SSH);
+        conf_set_int(conf, CONF_port, 22);
     }
 
     /*
      * Enact command-line overrides.
      */
-    cmdline_run_saved(&cfg);
+    cmdline_run_saved(conf);
 
     /*
-     * Trim leading whitespace off the hostname if it's there.
+     * Muck about with the hostname in various ways.
      */
     {
-       int space = strspn(cfg.host, " \t");
-       memmove(cfg.host, cfg.host+space, 1+strlen(cfg.host)-space);
-    }
+       char *hostbuf = dupstr(conf_get_str(conf, CONF_host));
+       char *host = hostbuf;
+       char *p, *q;
+
+       /*
+        * Trim leading whitespace.
+        */
+       host += strspn(host, " \t");
 
-    /* See if host is of the form user@host */
-    if (cfg.host[0] != '\0') {
-       char *atsign = strrchr(cfg.host, '@');
-       /* Make sure we're not overflowing the user field */
-       if (atsign) {
-           if (atsign - cfg.host < sizeof cfg.username) {
-               strncpy(cfg.username, cfg.host, atsign - cfg.host);
-               cfg.username[atsign - cfg.host] = '\0';
+       /*
+        * See if host is of the form user@host, and separate out
+        * the username if so.
+        */
+       if (host[0] != '\0') {
+           char *atsign = strrchr(host, '@');
+           if (atsign) {
+               *atsign = '\0';
+               conf_set_str(conf, CONF_username, host);
+               host = atsign + 1;
            }
-           memmove(cfg.host, atsign + 1, 1 + strlen(atsign + 1));
        }
-    }
 
-    /*
-     * Remove any remaining whitespace from the hostname.
-     */
-    {
-       int p1 = 0, p2 = 0;
-       while (cfg.host[p2] != '\0') {
-           if (cfg.host[p2] != ' ' && cfg.host[p2] != '\t') {
-               cfg.host[p1] = cfg.host[p2];
-               p1++;
-           }
-           p2++;
+       /*
+        * Remove any remaining whitespace.
+        */
+       p = hostbuf;
+       q = host;
+       while (*q) {
+           if (*q != ' ' && *q != '\t')
+               *p++ = *q;
+           q++;
        }
-       cfg.host[p1] = '\0';
+       *p = '\0';
+
+       conf_set_str(conf, CONF_host, hostbuf);
+       sfree(hostbuf);
     }
 
     /* Set username */
     if (user != NULL && user[0] != '\0') {
-       strncpy(cfg.username, user, sizeof(cfg.username) - 1);
-       cfg.username[sizeof(cfg.username) - 1] = '\0';
-    } else if (cfg.username[0] == '\0') {
+       conf_set_str(conf, CONF_username, user);
+    } else if (conf_get_str(conf, CONF_username)[0] == '\0') {
        user = get_username();
        if (!user)
            bump("Empty user name");
        else {
            if (verbose)
                tell_user(stderr, "Guessing user name: %s", user);
-           strncpy(cfg.username, user, sizeof(cfg.username) - 1);
-           cfg.username[sizeof(cfg.username) - 1] = '\0';
+           conf_set_str(conf, CONF_username, user);
            sfree(user);
        }
     }
@@ -424,10 +426,14 @@ static void do_cmd(char *host, char *user, char *cmd)
      * things like SCP and SFTP: agent forwarding, port forwarding,
      * X forwarding.
      */
-    cfg.x11_forward = 0;
-    cfg.agentfwd = 0;
-    cfg.portfwd[0] = cfg.portfwd[1] = '\0';
-    cfg.ssh_simple = TRUE;
+    conf_set_int(conf, CONF_x11_forward, 0);
+    conf_set_int(conf, CONF_agentfwd, 0);
+    conf_set_int(conf, CONF_ssh_simple, TRUE);
+    {
+       char *key;
+       while ((key = conf_get_str_nthstrkey(conf, CONF_portfwd, 0)) != NULL)
+           conf_del_str_str(conf, CONF_portfwd, key);
+    }
 
     /*
      * Set up main and possibly fallback command depending on
@@ -435,44 +441,49 @@ static void do_cmd(char *host, char *user, char *cmd)
      * Attempt to start the SFTP subsystem as a first choice,
      * falling back to the provided scp command if that fails.
      */
-    cfg.remote_cmd_ptr2 = NULL;
+    conf_set_str(conf, CONF_remote_cmd2, "");
     if (try_sftp) {
        /* First choice is SFTP subsystem. */
        main_cmd_is_sftp = 1;
-       strcpy(cfg.remote_cmd, "sftp");
-       cfg.ssh_subsys = TRUE;
+       conf_set_str(conf, CONF_remote_cmd, "sftp");
+       conf_set_int(conf, CONF_ssh_subsys, TRUE);
        if (try_scp) {
            /* Fallback is to use the provided scp command. */
            fallback_cmd_is_sftp = 0;
-           cfg.remote_cmd_ptr2 = cmd;
-           cfg.ssh_subsys2 = FALSE;
+           conf_set_str(conf, CONF_remote_cmd2, "sftp");
+           conf_set_int(conf, CONF_ssh_subsys2, FALSE);
        } else {
            /* Since we're not going to try SCP, we may as well try
             * harder to find an SFTP server, since in the current
             * implementation we have a spare slot. */
            fallback_cmd_is_sftp = 1;
            /* see psftp.c for full explanation of this kludge */
-           cfg.remote_cmd_ptr2 = 
-               "test -x /usr/lib/sftp-server && exec /usr/lib/sftp-server\n"
-               "test -x /usr/local/lib/sftp-server && exec /usr/local/lib/sftp-server\n"
-               "exec sftp-server";
-           cfg.ssh_subsys2 = FALSE;
+           conf_set_str(conf, CONF_remote_cmd2,
+                        "test -x /usr/lib/sftp-server &&"
+                        " exec /usr/lib/sftp-server\n"
+                        "test -x /usr/local/lib/sftp-server &&"
+                        " exec /usr/local/lib/sftp-server\n"
+                        "exec sftp-server");
+           conf_set_int(conf, CONF_ssh_subsys2, FALSE);
        }
     } else {
        /* Don't try SFTP at all; just try the scp command. */
        main_cmd_is_sftp = 0;
-       cfg.remote_cmd_ptr = cmd;
-       cfg.ssh_subsys = FALSE;
+       conf_set_str(conf, CONF_remote_cmd, cmd);
+       conf_set_int(conf, CONF_ssh_subsys, FALSE);
     }
-    cfg.nopty = TRUE;
+    conf_set_int(conf, CONF_nopty, TRUE);
 
     back = &ssh_backend;
 
-    err = back->init(NULL, &backhandle, &cfg, cfg.host, cfg.port, &realhost, 
-                    0, cfg.tcp_keepalives);
+    err = back->init(NULL, &backhandle, conf,
+                    conf_get_str(conf, CONF_host),
+                    conf_get_int(conf, CONF_port),
+                    &realhost, 0,
+                    conf_get_int(conf, CONF_tcp_keepalives));
     if (err != NULL)
        bump("ssh_init: %s", err);
-    logctx = log_init(NULL, &cfg);
+    logctx = log_init(NULL, conf);
     back->provide_logctx(backhandle, logctx);
     console_provide_logctx(logctx);
     ssh_scp_init();
@@ -2221,14 +2232,15 @@ int psftp_main(int argc, char *argv[])
     sk_init();
 
     /* Load Default Settings before doing anything else. */
-    do_defaults(NULL, &cfg);
+    conf = conf_new();
+    do_defaults(NULL, conf);
     loaded_session = FALSE;
 
     for (i = 1; i < argc; i++) {
        int ret;
        if (argv[i][0] != '-')
            break;
-       ret = cmdline_process_param(argv[i], i+1<argc?argv[i+1]:NULL, 1, &cfg);
+       ret = cmdline_process_param(argv[i], i+1<argc?argv[i+1]:NULL, 1, conf);
        if (ret == -2) {
            cmdline_error("option \"%s\" requires an argument", argv[i]);
        } else if (ret == 2) {
diff --git a/psftp.c b/psftp.c
index 179a1e9..a42b162 100644 (file)
--- a/psftp.c
+++ b/psftp.c
@@ -36,7 +36,7 @@ void do_sftp_cleanup();
 char *pwd, *homedir;
 static Backend *back;
 static void *backhandle;
-static Config cfg;
+static Conf *conf;
 
 /* ----------------------------------------------------------------------
  * Higher-level helper functions used in commands.
@@ -2664,32 +2664,30 @@ static int psftp_connect(char *userhost, char *user, int portnumber)
      */
     if (!loaded_session) {
        /* Try to load settings for `host' into a temporary config */
-       Config cfg2;
-       cfg2.host[0] = '\0';
-       do_defaults(host, &cfg2);
-       if (cfg2.host[0] != '\0') {
+       Conf *conf2 = conf_new();
+       conf_set_str(conf2, CONF_host, "");
+       do_defaults(host, conf2);
+       if (conf_get_str(conf2, CONF_host)[0] != '\0') {
            /* Settings present and include hostname */
            /* Re-load data into the real config. */
-           do_defaults(host, &cfg);
+           do_defaults(host, conf);
        } else {
            /* Session doesn't exist or mention a hostname. */
            /* Use `host' as a bare hostname. */
-           strncpy(cfg.host, host, sizeof(cfg.host) - 1);
-           cfg.host[sizeof(cfg.host) - 1] = '\0';
+           conf_set_str(conf, CONF_host, host);
        }
     } else {
        /* Patch in hostname `host' to session details. */
-       strncpy(cfg.host, host, sizeof(cfg.host) - 1);
-       cfg.host[sizeof(cfg.host) - 1] = '\0';
+       conf_set_str(conf, CONF_host, host);
     }
 
     /*
      * Force use of SSH. (If they got the protocol wrong we assume the
      * port is useless too.)
      */
-    if (cfg.protocol != PROT_SSH) {
-        cfg.protocol = PROT_SSH;
-        cfg.port = 22;
+    if (conf_get_int(conf, CONF_protocol) != PROT_SSH) {
+        conf_set_int(conf, CONF_protocol, PROT_SSH);
+        conf_set_int(conf, CONF_port, 22);
     }
 
     /*
@@ -2698,78 +2696,82 @@ static int psftp_connect(char *userhost, char *user, int portnumber)
      * work for SFTP. (Can be overridden with `-1' option.)
      * But if it says `2 only' or `2', respect which.
      */
-    if (cfg.sshprot != 2 && cfg.sshprot != 3)
-       cfg.sshprot = 2;
+    if ((conf_get_int(conf, CONF_sshprot) & ~1) != 2)   /* is it 2 or 3? */
+       conf_set_int(conf, CONF_sshprot, 2);
 
     /*
      * Enact command-line overrides.
      */
-    cmdline_run_saved(&cfg);
+    cmdline_run_saved(conf);
 
     /*
-     * Trim leading whitespace off the hostname if it's there.
+     * Muck about with the hostname in various ways.
      */
     {
-       int space = strspn(cfg.host, " \t");
-       memmove(cfg.host, cfg.host+space, 1+strlen(cfg.host)-space);
-    }
-
-    /* See if host is of the form user@host */
-    if (cfg.host[0] != '\0') {
-       char *atsign = strrchr(cfg.host, '@');
-       /* Make sure we're not overflowing the user field */
-       if (atsign) {
-           if (atsign - cfg.host < sizeof cfg.username) {
-               strncpy(cfg.username, cfg.host, atsign - cfg.host);
-               cfg.username[atsign - cfg.host] = '\0';
-           }
-           memmove(cfg.host, atsign + 1, 1 + strlen(atsign + 1));
-       }
-    }
+       char *hostbuf = dupstr(conf_get_str(conf, CONF_host));
+       char *host = hostbuf;
+       char *p, *q;
 
-    /*
-     * Trim a colon suffix off the hostname if it's there.
-     */
-    cfg.host[strcspn(cfg.host, ":")] = '\0';
+       /*
+        * Trim leading whitespace.
+        */
+       host += strspn(host, " \t");
 
-    /*
-     * Remove any remaining whitespace from the hostname.
-     */
-    {
-       int p1 = 0, p2 = 0;
-       while (cfg.host[p2] != '\0') {
-           if (cfg.host[p2] != ' ' && cfg.host[p2] != '\t') {
-               cfg.host[p1] = cfg.host[p2];
-               p1++;
+       /*
+        * See if host is of the form user@host, and separate out
+        * the username if so.
+        */
+       if (host[0] != '\0') {
+           char *atsign = strrchr(host, '@');
+           if (atsign) {
+               *atsign = '\0';
+               conf_set_str(conf, CONF_username, host);
+               host = atsign + 1;
            }
-           p2++;
        }
-       cfg.host[p1] = '\0';
+
+       /*
+        * Remove any remaining whitespace.
+        */
+       p = hostbuf;
+       q = host;
+       while (*q) {
+           if (*q != ' ' && *q != '\t')
+               *p++ = *q;
+           q++;
+       }
+       *p = '\0';
+
+       conf_set_str(conf, CONF_host, hostbuf);
+       sfree(hostbuf);
     }
 
     /* Set username */
     if (user != NULL && user[0] != '\0') {
-       strncpy(cfg.username, user, sizeof(cfg.username) - 1);
-       cfg.username[sizeof(cfg.username) - 1] = '\0';
+       conf_set_str(conf, CONF_username, user);
     }
 
     if (portnumber)
-       cfg.port = portnumber;
+       conf_set_int(conf, CONF_port, portnumber);
 
     /*
      * Disable scary things which shouldn't be enabled for simple
      * things like SCP and SFTP: agent forwarding, port forwarding,
      * X forwarding.
      */
-    cfg.x11_forward = 0;
-    cfg.agentfwd = 0;
-    cfg.portfwd[0] = cfg.portfwd[1] = '\0';
-    cfg.ssh_simple = TRUE;
+    conf_set_int(conf, CONF_x11_forward, 0);
+    conf_set_int(conf, CONF_agentfwd, 0);
+    conf_set_int(conf, CONF_ssh_simple, TRUE);
+    {
+       char *key;
+       while ((key = conf_get_str_nthstrkey(conf, CONF_portfwd, 0)) != NULL)
+           conf_del_str_str(conf, CONF_portfwd, key);
+    }
 
     /* Set up subsystem name. */
-    strcpy(cfg.remote_cmd, "sftp");
-    cfg.ssh_subsys = TRUE;
-    cfg.nopty = TRUE;
+    conf_set_str(conf, CONF_remote_cmd, "sftp");
+    conf_set_int(conf, CONF_ssh_subsys, TRUE);
+    conf_set_int(conf, CONF_nopty, TRUE);
 
     /*
      * Set up fallback option, for SSH-1 servers or servers with the
@@ -2788,21 +2790,26 @@ static int psftp_connect(char *userhost, char *user, int portnumber)
      * obvious pathnames and then give up, and when it does give up
      * it will print the preferred pathname in the error messages.
      */
-    cfg.remote_cmd_ptr2 =
-       "test -x /usr/lib/sftp-server && exec /usr/lib/sftp-server\n"
-       "test -x /usr/local/lib/sftp-server && exec /usr/local/lib/sftp-server\n"
-       "exec sftp-server";
-    cfg.ssh_subsys2 = FALSE;
+    conf_set_str(conf, CONF_remote_cmd2,
+                "test -x /usr/lib/sftp-server &&"
+                " exec /usr/lib/sftp-server\n"
+                "test -x /usr/local/lib/sftp-server &&"
+                " exec /usr/local/lib/sftp-server\n"
+                "exec sftp-server");
+    conf_set_int(conf, CONF_ssh_subsys2, FALSE);
 
     back = &ssh_backend;
 
-    err = back->init(NULL, &backhandle, &cfg, cfg.host, cfg.port, &realhost,
-                    0, cfg.tcp_keepalives);
+    err = back->init(NULL, &backhandle, conf,
+                    conf_get_str(conf, CONF_host),
+                    conf_get_int(conf, CONF_port),
+                    &realhost, 0,
+                    conf_get_int(conf, CONF_tcp_keepalives));
     if (err != NULL) {
        fprintf(stderr, "ssh_init: %s\n", err);
        return 1;
     }
-    logctx = log_init(NULL, &cfg);
+    logctx = log_init(NULL, conf);
     back->provide_logctx(backhandle, logctx);
     console_provide_logctx(logctx);
     while (!back->sendok(backhandle)) {
@@ -2854,7 +2861,8 @@ int psftp_main(int argc, char *argv[])
     userhost = user = NULL;
 
     /* Load Default Settings before doing anything else. */
-    do_defaults(NULL, &cfg);
+    conf = conf_new();
+    do_defaults(NULL, conf);
     loaded_session = FALSE;
 
     for (i = 1; i < argc; i++) {
@@ -2866,7 +2874,7 @@ int psftp_main(int argc, char *argv[])
                 userhost = dupstr(argv[i]);
            continue;
        }
-       ret = cmdline_process_param(argv[i], i+1<argc?argv[i+1]:NULL, 1, &cfg);
+       ret = cmdline_process_param(argv[i], i+1<argc?argv[i+1]:NULL, 1, conf);
        if (ret == -2) {
            cmdline_error("option \"%s\" requires an argument", argv[i]);
        } else if (ret == 2) {
@@ -2908,8 +2916,8 @@ int psftp_main(int argc, char *argv[])
      * otherwise been specified, pop it in `userhost' so that
      * `psftp -load sessname' is sufficient to start a session.
      */
-    if (!userhost && cfg.host[0] != '\0') {
-       userhost = dupstr(cfg.host);
+    if (!userhost && conf_get_str(conf, CONF_host)[0] != '\0') {
+       userhost = dupstr(conf_get_str(conf, CONF_host));
     }
 
     /*
diff --git a/putty.h b/putty.h
index 78bc429..fad3486 100644 (file)
--- a/putty.h
+++ b/putty.h
@@ -18,7 +18,7 @@
 
 #ifndef DONE_TYPEDEFS
 #define DONE_TYPEDEFS
-typedef struct config_tag Config;
+typedef struct conf_tag Conf;
 typedef struct backend_tag Backend;
 typedef struct terminal_tag Terminal;
 #endif
@@ -304,7 +304,7 @@ enum {
 };
 
 enum {
-    /* Protocol back ends. (cfg.protocol) */
+    /* Protocol back ends. (CONF_protocol) */
     PROT_RAW, PROT_TELNET, PROT_RLOGIN, PROT_SSH,
     /* PROT_SERIAL is supported on a subset of platforms, but it doesn't
      * hurt to define it globally. */
@@ -312,22 +312,22 @@ enum {
 };
 
 enum {
-    /* Bell settings (cfg.beep) */
+    /* Bell settings (CONF_beep) */
     BELL_DISABLED, BELL_DEFAULT, BELL_VISUAL, BELL_WAVEFILE, BELL_PCSPEAKER
 };
 
 enum {
-    /* Taskbar flashing indication on bell (cfg.beep_ind) */
+    /* Taskbar flashing indication on bell (CONF_beep_ind) */
     B_IND_DISABLED, B_IND_FLASH, B_IND_STEADY
 };
 
 enum {
-    /* Resize actions (cfg.resize_action) */
+    /* Resize actions (CONF_resize_action) */
     RESIZE_TERM, RESIZE_DISABLED, RESIZE_FONT, RESIZE_EITHER
 };
 
 enum {
-    /* Function key types (cfg.funky_type) */
+    /* Function key types (CONF_funky_type) */
     FUNKY_TILDE,
     FUNKY_LINUX,
     FUNKY_XTERM,
@@ -415,12 +415,11 @@ enum {
 
 struct backend_tag {
     const char *(*init) (void *frontend_handle, void **backend_handle,
-                        Config *cfg,
-                        char *host, int port, char **realhost, int nodelay,
-                        int keepalive);
+                        Conf *conf, char *host, int port, char **realhost,
+                        int nodelay, int keepalive);
     void (*free) (void *handle);
     /* back->reconfig() passes in a replacement configuration. */
-    void (*reconfig) (void *handle, Config *cfg);
+    void (*reconfig) (void *handle, Conf *conf);
     /* back->send() returns the current amount of buffered data. */
     int (*send) (void *handle, char *buf, int len);
     /* back->sendbuffer() does the same thing but without attempting a send */
@@ -462,214 +461,6 @@ extern const int be_default_protocol;
 extern const char *const appname;
 
 /*
- * IMPORTANT POLICY POINT: everything in this structure which wants
- * to be treated like an integer must be an actual, honest-to-
- * goodness `int'. No enum-typed variables. This is because parts
- * of the code will want to pass around `int *' pointers to them
- * and we can't run the risk of porting to some system on which the
- * enum comes out as a different size from int.
- */
-struct config_tag {
-    /* Basic options */
-    char host[512];
-    int port;
-    int protocol;
-    int addressfamily;
-    int close_on_exit;
-    int warn_on_close;
-    int ping_interval;                /* in seconds */
-    int tcp_nodelay;
-    int tcp_keepalives;
-    char loghost[512];  /* logical host being contacted, for host key check */
-    /* Proxy options */
-    char proxy_exclude_list[512];
-    int proxy_dns;
-    int even_proxy_localhost;
-    int proxy_type;
-    char proxy_host[512];
-    int proxy_port;
-    char proxy_username[128];
-    char proxy_password[128];
-    char proxy_telnet_command[512];
-    /* SSH options */
-    char remote_cmd[512];
-    char *remote_cmd_ptr;             /* might point to a larger command
-                                       * but never for loading/saving */
-    char *remote_cmd_ptr2;            /* might point to a larger command
-                                       * but never for loading/saving */
-    int nopty;
-    int compression;
-    int ssh_kexlist[KEX_MAX];
-    int ssh_rekey_time;                       /* in minutes */
-    char ssh_rekey_data[16];
-    int tryagent;
-    int agentfwd;
-    int change_username;              /* allow username switching in SSH-2 */
-    int ssh_cipherlist[CIPHER_MAX];
-    Filename keyfile;
-    int sshprot;                      /* use v1 or v2 when both available */
-    int ssh2_des_cbc;                 /* "des-cbc" unrecommended SSH-2 cipher */
-    int ssh_no_userauth;              /* bypass "ssh-userauth" (SSH-2 only) */
-    int ssh_show_banner;              /* show USERAUTH_BANNERs (SSH-2 only) */
-    int try_tis_auth;
-    int try_ki_auth;
-    int try_gssapi_auth;               /* attempt gssapi auth */
-    int gssapifwd;                     /* forward tgt via gss */
-    int ssh_gsslist[4];                       /* preference order for local GSS libs */
-    Filename ssh_gss_custom;
-    int ssh_subsys;                   /* run a subsystem rather than a command */
-    int ssh_subsys2;                  /* fallback to go with remote_cmd_ptr2 */
-    int ssh_no_shell;                 /* avoid running a shell */
-    char ssh_nc_host[512];            /* host to connect to in `nc' mode */
-    int ssh_nc_port;                  /* port to connect to in `nc' mode */
-    /* Telnet options */
-    char termtype[32];
-    char termspeed[32];
-    char ttymodes[768];                       /* MODE\tVvalue\0MODE\tA\0\0 */
-    char environmt[1024];             /* VAR\tvalue\0VAR\tvalue\0\0 */
-    char username[100];
-    int username_from_env;
-    char localusername[100];
-    int rfc_environ;
-    int passive_telnet;
-    /* Serial port options */
-    char serline[256];
-    int serspeed;
-    int serdatabits, serstopbits;
-    int serparity;
-    int serflow;
-    /* Keyboard options */
-    int bksp_is_delete;
-    int rxvt_homeend;
-    int funky_type;
-    int no_applic_c;                  /* totally disable app cursor keys */
-    int no_applic_k;                  /* totally disable app keypad */
-    int no_mouse_rep;                 /* totally disable mouse reporting */
-    int no_remote_resize;             /* disable remote resizing */
-    int no_alt_screen;                /* disable alternate screen */
-    int no_remote_wintitle;           /* disable remote retitling */
-    int no_dbackspace;                /* disable destructive backspace */
-    int no_remote_charset;            /* disable remote charset config */
-    int remote_qtitle_action;         /* remote win title query action */
-    int app_cursor;
-    int app_keypad;
-    int nethack_keypad;
-    int telnet_keyboard;
-    int telnet_newline;
-    int alt_f4;                               /* is it special? */
-    int alt_space;                    /* is it special? */
-    int alt_only;                     /* is it special? */
-    int localecho;
-    int localedit;
-    int alwaysontop;
-    int fullscreenonaltenter;
-    int scroll_on_key;
-    int scroll_on_disp;
-    int erase_to_scrollback;
-    int compose_key;
-    int ctrlaltkeys;
-    char wintitle[256];                       /* initial window title */
-    /* Terminal options */
-    int savelines;
-    int dec_om;
-    int wrap_mode;
-    int lfhascr;
-    int cursor_type;                  /* 0=block 1=underline 2=vertical */
-    int blink_cur;
-    int beep;
-    int beep_ind;
-    int bellovl;                      /* bell overload protection active? */
-    int bellovl_n;                    /* number of bells to cause overload */
-    int bellovl_t;                    /* time interval for overload (seconds) */
-    int bellovl_s;                    /* period of silence to re-enable bell (s) */
-    Filename bell_wavefile;
-    int scrollbar;
-    int scrollbar_in_fullscreen;
-    int resize_action;
-    int bce;
-    int blinktext;
-    int win_name_always;
-    int width, height;
-    FontSpec font;
-    int font_quality;
-    Filename logfilename;
-    int logtype;
-    int logxfovr;
-    int logflush;
-    int logomitpass;
-    int logomitdata;
-    int hide_mouseptr;
-    int sunken_edge;
-    int window_border;
-    char answerback[256];
-    char printer[128];
-    int arabicshaping;
-    int bidi;
-    /* Colour options */
-    int ansi_colour;
-    int xterm_256_colour;
-    int system_colour;
-    int try_palette;
-    int bold_colour;
-    unsigned char colours[22][3];
-    /* Selection options */
-    int mouse_is_xterm;
-    int rect_select;
-    int rawcnp;
-    int rtf_paste;
-    int mouse_override;
-    short wordness[256];
-    /* translations */
-    int vtmode;
-    char line_codepage[128];
-    int cjk_ambig_wide;
-    int utf8_override;
-    int xlat_capslockcyr;
-    /* X11 forwarding */
-    int x11_forward;
-    char x11_display[128];
-    int x11_auth;
-    Filename xauthfile;
-    /* port forwarding */
-    int lport_acceptall; /* accept conns from hosts other than localhost */
-    int rport_acceptall; /* same for remote forwarded ports (SSH-2 only) */
-    /*
-     * The port forwarding string contains a number of
-     * NUL-terminated substrings, terminated in turn by an empty
-     * string (i.e. a second NUL immediately after the previous
-     * one). Each string can be of one of the following forms:
-     * 
-     *   [LR]localport\thost:port
-     *   [LR]localaddr:localport\thost:port
-     *   Dlocalport
-     *   Dlocaladdr:localport
-     */
-    char portfwd[1024];
-    /* SSH bug compatibility modes */
-    int sshbug_ignore1, sshbug_plainpw1, sshbug_rsa1,
-       sshbug_hmac2, sshbug_derivekey2, sshbug_rsapad2,
-       sshbug_pksessid2, sshbug_rekey2, sshbug_maxpkt2,
-       sshbug_ignore2;
-    /*
-     * ssh_simple means that we promise never to open any channel other
-     * than the main one, which means it can safely use a very large
-     * window in SSH-2.
-     */
-    int ssh_simple;
-    /* Options for pterm. Should split out into platform-dependent part. */
-    int stamp_utmp;
-    int login_shell;
-    int scrollbar_on_left;
-    int shadowbold;
-    FontSpec boldfont;
-    FontSpec widefont;
-    FontSpec wideboldfont;
-    int shadowboldoffset;
-    int crhaslf;
-    char winclass[256];
-};
-
-/*
  * Some global flags denoting the type of application.
  * 
  * FLAG_VERBOSE is set when the user requests verbose details.
@@ -839,6 +630,255 @@ void set_busy_status(void *frontend, int status);
 void cleanup_exit(int);
 
 /*
+ * Exports from conf.c, and a big enum (via parametric macro) of
+ * configuration option keys.
+ */
+#define CONFIG_OPTIONS(X) \
+    /* X(value-type, subkey-type, keyword) */ \
+    X(STR, NONE, host) \
+    X(INT, NONE, port) \
+    X(INT, NONE, protocol) \
+    X(INT, NONE, addressfamily) \
+    X(INT, NONE, close_on_exit) \
+    X(INT, NONE, warn_on_close) \
+    X(INT, NONE, ping_interval) /* in seconds */ \
+    X(INT, NONE, tcp_nodelay) \
+    X(INT, NONE, tcp_keepalives) \
+    X(STR, NONE, loghost) /* logical host being contacted, for host key check */ \
+    /* Proxy options */ \
+    X(STR, NONE, proxy_exclude_list) \
+    X(INT, NONE, proxy_dns) \
+    X(INT, NONE, even_proxy_localhost) \
+    X(INT, NONE, proxy_type) \
+    X(STR, NONE, proxy_host) \
+    X(INT, NONE, proxy_port) \
+    X(STR, NONE, proxy_username) \
+    X(STR, NONE, proxy_password) \
+    X(STR, NONE, proxy_telnet_command) \
+    /* SSH options */ \
+    X(STR, NONE, remote_cmd) \
+    X(STR, NONE, remote_cmd2) /* fallback if remote_cmd fails; never loaded or saved */ \
+    X(INT, NONE, nopty) \
+    X(INT, NONE, compression) \
+    X(INT, INT, ssh_kexlist) \
+    X(INT, NONE, ssh_rekey_time) /* in minutes */ \
+    X(STR, NONE, ssh_rekey_data) /* string encoding e.g. "100K", "2M", "1G" */ \
+    X(INT, NONE, tryagent) \
+    X(INT, NONE, agentfwd) \
+    X(INT, NONE, change_username) /* allow username switching in SSH-2 */ \
+    X(INT, INT, ssh_cipherlist) \
+    X(FILENAME, NONE, keyfile) \
+    X(INT, NONE, sshprot) /* use v1 or v2 when both available */ \
+    X(INT, NONE, ssh2_des_cbc) /* "des-cbc" unrecommended SSH-2 cipher */ \
+    X(INT, NONE, ssh_no_userauth) /* bypass "ssh-userauth" (SSH-2 only) */ \
+    X(INT, NONE, ssh_show_banner) /* show USERAUTH_BANNERs (SSH-2 only) */ \
+    X(INT, NONE, try_tis_auth) \
+    X(INT, NONE, try_ki_auth) \
+    X(INT, NONE, try_gssapi_auth) /* attempt gssapi auth */ \
+    X(INT, NONE, gssapifwd) /* forward tgt via gss */ \
+    X(INT, INT, ssh_gsslist) /* preference order for local GSS libs */ \
+    X(FILENAME, NONE, ssh_gss_custom) \
+    X(INT, NONE, ssh_subsys) /* run a subsystem rather than a command */ \
+    X(INT, NONE, ssh_subsys2) /* fallback to go with remote_cmd_ptr2 */ \
+    X(INT, NONE, ssh_no_shell) /* avoid running a shell */ \
+    X(STR, NONE, ssh_nc_host) /* host to connect to in `nc' mode */ \
+    X(INT, NONE, ssh_nc_port) /* port to connect to in `nc' mode */ \
+    /* Telnet options */ \
+    X(STR, NONE, termtype) \
+    X(STR, NONE, termspeed) \
+    X(STR, STR, ttymodes) /* values are "Vvalue" or "A" */ \
+    X(STR, STR, environmt) \
+    X(STR, NONE, username) \
+    X(INT, NONE, username_from_env) \
+    X(STR, NONE, localusername) \
+    X(INT, NONE, rfc_environ) \
+    X(INT, NONE, passive_telnet) \
+    /* Serial port options */ \
+    X(STR, NONE, serline) \
+    X(INT, NONE, serspeed) \
+    X(INT, NONE, serdatabits) \
+    X(INT, NONE, serstopbits) \
+    X(INT, NONE, serparity) \
+    X(INT, NONE, serflow) \
+    /* Keyboard options */ \
+    X(INT, NONE, bksp_is_delete) \
+    X(INT, NONE, rxvt_homeend) \
+    X(INT, NONE, funky_type) \
+    X(INT, NONE, no_applic_c) /* totally disable app cursor keys */ \
+    X(INT, NONE, no_applic_k) /* totally disable app keypad */ \
+    X(INT, NONE, no_mouse_rep) /* totally disable mouse reporting */ \
+    X(INT, NONE, no_remote_resize) /* disable remote resizing */ \
+    X(INT, NONE, no_alt_screen) /* disable alternate screen */ \
+    X(INT, NONE, no_remote_wintitle) /* disable remote retitling */ \
+    X(INT, NONE, no_dbackspace) /* disable destructive backspace */ \
+    X(INT, NONE, no_remote_charset) /* disable remote charset config */ \
+    X(INT, NONE, remote_qtitle_action) /* remote win title query action */ \
+    X(INT, NONE, app_cursor) \
+    X(INT, NONE, app_keypad) \
+    X(INT, NONE, nethack_keypad) \
+    X(INT, NONE, telnet_keyboard) \
+    X(INT, NONE, telnet_newline) \
+    X(INT, NONE, alt_f4) /* is it special? */ \
+    X(INT, NONE, alt_space) /* is it special? */ \
+    X(INT, NONE, alt_only) /* is it special? */ \
+    X(INT, NONE, localecho) \
+    X(INT, NONE, localedit) \
+    X(INT, NONE, alwaysontop) \
+    X(INT, NONE, fullscreenonaltenter) \
+    X(INT, NONE, scroll_on_key) \
+    X(INT, NONE, scroll_on_disp) \
+    X(INT, NONE, erase_to_scrollback) \
+    X(INT, NONE, compose_key) \
+    X(INT, NONE, ctrlaltkeys) \
+    X(STR, NONE, wintitle) /* initial window title */ \
+    /* Terminal options */ \
+    X(INT, NONE, savelines) \
+    X(INT, NONE, dec_om) \
+    X(INT, NONE, wrap_mode) \
+    X(INT, NONE, lfhascr) \
+    X(INT, NONE, cursor_type) /* 0=block 1=underline 2=vertical */ \
+    X(INT, NONE, blink_cur) \
+    X(INT, NONE, beep) \
+    X(INT, NONE, beep_ind) \
+    X(INT, NONE, bellovl) /* bell overload protection active? */ \
+    X(INT, NONE, bellovl_n) /* number of bells to cause overload */ \
+    X(INT, NONE, bellovl_t) /* time interval for overload (seconds) */ \
+    X(INT, NONE, bellovl_s) /* period of silence to re-enable bell (s) */ \
+    X(FILENAME, NONE, bell_wavefile) \
+    X(INT, NONE, scrollbar) \
+    X(INT, NONE, scrollbar_in_fullscreen) \
+    X(INT, NONE, resize_action) \
+    X(INT, NONE, bce) \
+    X(INT, NONE, blinktext) \
+    X(INT, NONE, win_name_always) \
+    X(INT, NONE, width) \
+    X(INT, NONE, height) \
+    X(FONT, NONE, font) \
+    X(INT, NONE, font_quality) \
+    X(FILENAME, NONE, logfilename) \
+    X(INT, NONE, logtype) \
+    X(INT, NONE, logxfovr) \
+    X(INT, NONE, logflush) \
+    X(INT, NONE, logomitpass) \
+    X(INT, NONE, logomitdata) \
+    X(INT, NONE, hide_mouseptr) \
+    X(INT, NONE, sunken_edge) \
+    X(INT, NONE, window_border) \
+    X(STR, NONE, answerback) \
+    X(STR, NONE, printer) \
+    X(INT, NONE, arabicshaping) \
+    X(INT, NONE, bidi) \
+    /* Colour options */ \
+    X(INT, NONE, ansi_colour) \
+    X(INT, NONE, xterm_256_colour) \
+    X(INT, NONE, system_colour) \
+    X(INT, NONE, try_palette) \
+    X(INT, NONE, bold_colour) \
+    X(INT, INT, colours) \
+    /* Selection options */ \
+    X(INT, NONE, mouse_is_xterm) \
+    X(INT, NONE, rect_select) \
+    X(INT, NONE, rawcnp) \
+    X(INT, NONE, rtf_paste) \
+    X(INT, NONE, mouse_override) \
+    X(INT, INT, wordness) \
+    /* translations */ \
+    X(INT, NONE, vtmode) \
+    X(STR, NONE, line_codepage) \
+    X(INT, NONE, cjk_ambig_wide) \
+    X(INT, NONE, utf8_override) \
+    X(INT, NONE, xlat_capslockcyr) \
+    /* X11 forwarding */ \
+    X(INT, NONE, x11_forward) \
+    X(STR, NONE, x11_display) \
+    X(INT, NONE, x11_auth) \
+    X(FILENAME, NONE, xauthfile) \
+    /* port forwarding */ \
+    X(INT, NONE, lport_acceptall) /* accept conns from hosts other than localhost */ \
+    X(INT, NONE, rport_acceptall) /* same for remote forwarded ports (SSH-2 only) */ \
+    /*                                                                \
+     * Subkeys for 'portfwd' can have the following forms:            \
+     *                                                                \
+     *   [LR]localport                                                \
+     *   [LR]localaddr:localport                                      \
+     *                                                                \
+     * Dynamic forwardings are indicated by an 'L' key, and the       \
+     * special value "D". For all other forwardings, the value        \
+     * should be of the form 'host:port'.                             \
+     */ \
+    X(STR, STR, portfwd) \
+    /* SSH bug compatibility modes */ \
+    X(INT, NONE, sshbug_ignore1) \
+    X(INT, NONE, sshbug_plainpw1) \
+    X(INT, NONE, sshbug_rsa1) \
+    X(INT, NONE, sshbug_hmac2) \
+    X(INT, NONE, sshbug_derivekey2) \
+    X(INT, NONE, sshbug_rsapad2) \
+    X(INT, NONE, sshbug_pksessid2) \
+    X(INT, NONE, sshbug_rekey2) \
+    X(INT, NONE, sshbug_maxpkt2) \
+    X(INT, NONE, sshbug_ignore2) \
+    /*                                                                \
+     * ssh_simple means that we promise never to open any channel     \
+     * other than the main one, which means it can safely use a very  \
+     * large window in SSH-2.                                         \
+     */ \
+    X(INT, NONE, ssh_simple) \
+    /* Options for pterm. Should split out into platform-dependent part. */ \
+    X(INT, NONE, stamp_utmp) \
+    X(INT, NONE, login_shell) \
+    X(INT, NONE, scrollbar_on_left) \
+    X(INT, NONE, shadowbold) \
+    X(FONT, NONE, boldfont) \
+    X(FONT, NONE, widefont) \
+    X(FONT, NONE, wideboldfont) \
+    X(INT, NONE, shadowboldoffset) \
+    X(INT, NONE, crhaslf) \
+    X(STR, NONE, winclass) \
+
+/* Now define the actual enum of option keywords using that macro. */
+#define CONF_ENUM_DEF(valtype, keytype, keyword) CONF_ ## keyword,
+enum config_primary_key { CONFIG_OPTIONS(CONF_ENUM_DEF) N_CONFIG_OPTIONS };
+#undef CONF_ENUM_DEF
+
+#define NCFGCOLOURS 22 /* number of colours in CONF_colours above */
+
+/* Functions handling configuration structures. */
+Conf *conf_new(void);                 /* create an empty configuration */
+void conf_free(Conf *conf);
+Conf *conf_copy(Conf *oldconf);
+void conf_copy_into(Conf *dest, Conf *src);
+/* Mandatory accessor functions: enforce by assertion that keys exist. */
+int conf_get_int(Conf *conf, int key);
+int conf_get_int_int(Conf *conf, int key, int subkey);
+char *conf_get_str(Conf *conf, int key);   /* result still owned by conf */
+char *conf_get_str_str(Conf *conf, int key, const char *subkey);
+Filename *conf_get_filename(Conf *conf, int key);
+FontSpec *conf_get_fontspec(Conf *conf, int key);
+/* Optional accessor function: return NULL if key does not exist. */
+char *conf_get_str_str_opt(Conf *conf, int key, const char *subkey);
+/* Accessor function to step through a string-subkeyed list.
+ * Returns the next subkey after the provided one, or the first if NULL.
+ * Returns NULL if there are none left.
+ * Both the return value and *subkeyout are still owned by conf. */
+char *conf_get_str_strs(Conf *conf, int key, char *subkeyin, char **subkeyout);
+/* Return the nth string subkey in a list. Owned by conf. NULL if beyond end */
+char *conf_get_str_nthstrkey(Conf *conf, int key, int n);
+/* Functions to set entries in configuration. Always copy their inputs. */
+void conf_set_int(Conf *conf, int key, int value);
+void conf_set_int_int(Conf *conf, int key, int subkey, int value);
+void conf_set_str(Conf *conf, int key, const char *value);
+void conf_set_str_str(Conf *conf, int key,
+                     const char *subkey, const char *val);
+void conf_del_str_str(Conf *conf, int key, const char *subkey);
+void conf_set_filename(Conf *conf, int key, const Filename *val);
+void conf_set_fontspec(Conf *conf, int key, const FontSpec *val);
+/* Serialisation functions for Duplicate Session */
+int conf_serialised_size(Conf *conf);
+void conf_serialise(Conf *conf, void *data);
+int conf_deserialise(Conf *conf, void *data, int maxsize);/*returns size used*/
+
+/*
  * Exports from noise.c.
  */
 void noise_get_heavy(void (*func) (void *, int));
@@ -853,13 +893,13 @@ void random_destroy_seed(void);
  */
 Backend *backend_from_name(const char *name);
 Backend *backend_from_proto(int proto);
-int get_remote_username(Config *cfg, char *user, size_t len);
-char *save_settings(char *section, Config * cfg);
-void save_open_settings(void *sesskey, Config *cfg);
-void load_settings(char *section, Config * cfg);
-void load_open_settings(void *sesskey, Config *cfg);
+char *get_remote_username(Conf *conf); /* dynamically allocated */
+char *save_settings(char *section, Conf *conf);
+void save_open_settings(void *sesskey, Conf *conf);
+void load_settings(char *section, Conf *conf);
+void load_open_settings(void *sesskey, Conf *conf);
 void get_sesslist(struct sesslist *, int allocate);
-void do_defaults(char *, Config *);
+void do_defaults(char *, Conf *);
 void registry_cleanup(void);
 
 /*
@@ -882,7 +922,7 @@ FontSpec platform_default_fontspec(const char *name);
  * Exports from terminal.c.
  */
 
-Terminal *term_init(Config *, struct unicode_data *, void *);
+Terminal *term_init(Conf *, struct unicode_data *, void *);
 void term_free(Terminal *);
 void term_size(Terminal *, int, int, int);
 void term_paint(Terminal *, Context, int, int, int, int, int);
@@ -904,7 +944,7 @@ void term_paste(Terminal *);
 void term_nopaste(Terminal *);
 int term_ldisc(Terminal *, int option);
 void term_copyall(Terminal *);
-void term_reconfig(Terminal *, Config *);
+void term_reconfig(Terminal *, Conf *);
 void term_seen_key_event(Terminal *); 
 int term_data(Terminal *, int is_stderr, const char *data, int len);
 int term_data_untrusted(Terminal *, const char *data, int len);
@@ -922,9 +962,9 @@ int format_arrow_key(char *buf, Terminal *term, int xkey, int ctrl);
 /*
  * Exports from logging.c.
  */
-void *log_init(void *frontend, Config *cfg);
+void *log_init(void *frontend, Conf *conf);
 void log_free(void *logctx);
-void log_reconfig(void *logctx, Config *cfg);
+void log_reconfig(void *logctx, Conf *conf);
 void logfopen(void *logctx);
 void logfclose(void *logctx);
 void logtraffic(void *logctx, unsigned char c, int logmode);
@@ -975,7 +1015,8 @@ extern Backend ssh_backend;
 /*
  * Exports from ldisc.c.
  */
-void *ldisc_create(Config *, Terminal *, Backend *, void *, void *);
+void *ldisc_create(Conf *, Terminal *, Backend *, void *, void *);
+void ldisc_configure(void *, Conf *);
 void ldisc_free(void *);
 void ldisc_send(void *handle, char *buf, int len, int interactive);
 
@@ -1003,8 +1044,8 @@ void random_unref(void);
  * Exports from pinger.c.
  */
 typedef struct pinger_tag *Pinger;
-Pinger pinger_new(Config *cfg, Backend *back, void *backhandle);
-void pinger_reconfig(Pinger, Config *oldcfg, Config *newcfg);
+Pinger pinger_new(Conf *conf, Backend *back, void *backhandle);
+void pinger_reconfig(Pinger, Conf *oldconf, Conf *newconf);
 void pinger_free(Pinger);
 
 /*
@@ -1012,8 +1053,8 @@ void pinger_free(Pinger);
  */
 
 #include "misc.h"
-int cfg_launchable(const Config *cfg);
-char const *cfg_dest(const Config *cfg);
+int conf_launchable(Conf *conf);
+char const *conf_dest(Conf *conf);
 
 /*
  * Exports from sercfg.c.
@@ -1147,8 +1188,8 @@ void printer_finish_job(printer_job *);
  * defined differently in various places and required _by_
  * cmdline.c).
  */
-int cmdline_process_param(char *, char *, int, Config *);
-void cmdline_run_saved(Config *);
+int cmdline_process_param(char *, char *, int, Conf *);
+void cmdline_run_saved(Conf *);
 void cmdline_cleanup(void);
 int cmdline_get_passwd_input(prompts_t *p, unsigned char *in, int inlen);
 #define TOOLTYPE_FILETRANSFER 1
@@ -1161,6 +1202,18 @@ void cmdline_error(char *, ...);
  * Exports from config.c.
  */
 struct controlbox;
+union control;
+void conf_radiobutton_handler(union control *ctrl, void *dlg,
+                             void *data, int event);
+#define CHECKBOX_INVERT (1<<30)
+void conf_checkbox_handler(union control *ctrl, void *dlg,
+                          void *data, int event);
+void conf_editbox_handler(union control *ctrl, void *dlg,
+                         void *data, int event);
+void conf_filesel_handler(union control *ctrl, void *dlg,
+                         void *data, int event);
+void conf_fontsel_handler(union control *ctrl, void *dlg,
+                         void *data, int event);
 void setup_config_box(struct controlbox *b, int midsession,
                      int protocol, int protcfginfo);
 
diff --git a/raw.c b/raw.c
index b2676a9..6eb605d 100644 (file)
--- a/raw.c
+++ b/raw.c
@@ -89,7 +89,7 @@ static void raw_sent(Plug plug, int bufsize)
  * freed by the caller.
  */
 static const char *raw_init(void *frontend_handle, void **backend_handle,
-                           Config *cfg,
+                           Conf *conf,
                            char *host, int port, char **realhost, int nodelay,
                            int keepalive)
 {
@@ -102,6 +102,8 @@ static const char *raw_init(void *frontend_handle, void **backend_handle,
     SockAddr addr;
     const char *err;
     Raw raw;
+    int addressfamily;
+    char *loghost;
 
     raw = snew(struct raw_backend_data);
     raw->fn = &fn_table;
@@ -110,19 +112,20 @@ static const char *raw_init(void *frontend_handle, void **backend_handle,
 
     raw->frontend = frontend_handle;
 
+    addressfamily = conf_get_int(conf, CONF_addressfamily);
     /*
      * Try to find host.
      */
     {
        char *buf;
        buf = dupprintf("Looking up host \"%s\"%s", host,
-                       (cfg->addressfamily == ADDRTYPE_IPV4 ? " (IPv4)" :
-                        (cfg->addressfamily == ADDRTYPE_IPV6 ? " (IPv6)" :
+                       (addressfamily == ADDRTYPE_IPV4 ? " (IPv4)" :
+                        (addressfamily == ADDRTYPE_IPV6 ? " (IPv6)" :
                          "")));
        logevent(raw->frontend, buf);
        sfree(buf);
     }
-    addr = name_lookup(host, port, realhost, cfg, cfg->addressfamily);
+    addr = name_lookup(host, port, realhost, conf, addressfamily);
     if ((err = sk_addr_error(addr)) != NULL) {
        sk_addr_free(addr);
        return err;
@@ -135,15 +138,16 @@ static const char *raw_init(void *frontend_handle, void **backend_handle,
      * Open socket.
      */
     raw->s = new_connection(addr, *realhost, port, 0, 1, nodelay, keepalive,
-                           (Plug) raw, cfg);
+                           (Plug) raw, conf);
     if ((err = sk_socket_error(raw->s)) != NULL)
        return err;
 
-    if (*cfg->loghost) {
+    loghost = conf_get_str(conf, CONF_loghost);
+    if (*loghost) {
        char *colon;
 
        sfree(*realhost);
-       *realhost = dupstr(cfg->loghost);
+       *realhost = dupstr(loghost);
        colon = strrchr(*realhost, ':');
        if (colon) {
            /*
@@ -170,7 +174,7 @@ static void raw_free(void *handle)
 /*
  * Stub routine (we don't have any need to reconfigure this backend).
  */
-static void raw_reconfig(void *handle, Config *cfg)
+static void raw_reconfig(void *handle, Conf *conf)
 {
 }
 
index 6841fc6..7cfe783 100644 (file)
--- a/rlogin.c
+++ b/rlogin.c
@@ -28,7 +28,7 @@ typedef struct rlogin_tag {
     int term_width, term_height;
     void *frontend;
 
-    Config cfg;
+    Conf *conf;
 
     /* In case we need to read a username from the terminal before starting */
     prompts_t *prompt;
@@ -122,18 +122,18 @@ static void rlogin_startup(Rlogin rlogin, const char *ruser)
 {
     char z = 0;
     char *p;
+
     sk_write(rlogin->s, &z, 1);
-    sk_write(rlogin->s, rlogin->cfg.localusername,
-             strlen(rlogin->cfg.localusername));
+    p = conf_get_str(rlogin->conf, CONF_localusername);
+    sk_write(rlogin->s, p, strlen(p));
     sk_write(rlogin->s, &z, 1);
-    sk_write(rlogin->s, ruser,
-             strlen(ruser));
+    sk_write(rlogin->s, ruser, strlen(ruser));
     sk_write(rlogin->s, &z, 1);
-    sk_write(rlogin->s, rlogin->cfg.termtype,
-             strlen(rlogin->cfg.termtype));
+    p = conf_get_str(rlogin->conf, CONF_termtype);
+    sk_write(rlogin->s, p, strlen(p));
     sk_write(rlogin->s, "/", 1);
-    for (p = rlogin->cfg.termspeed; isdigit((unsigned char)*p); p++) continue;
-    sk_write(rlogin->s, rlogin->cfg.termspeed, p - rlogin->cfg.termspeed);
+    p = conf_get_str(rlogin->conf, CONF_termspeed);
+    sk_write(rlogin->s, p, strspn(p, "0123456789"));
     rlogin->bufsize = sk_write(rlogin->s, &z, 1);
 
     rlogin->prompt = NULL;
@@ -148,7 +148,7 @@ static void rlogin_startup(Rlogin rlogin, const char *ruser)
  * freed by the caller.
  */
 static const char *rlogin_init(void *frontend_handle, void **backend_handle,
-                              Config *cfg,
+                              Conf *conf,
                               char *host, int port, char **realhost,
                               int nodelay, int keepalive)
 {
@@ -161,33 +161,36 @@ static const char *rlogin_init(void *frontend_handle, void **backend_handle,
     SockAddr addr;
     const char *err;
     Rlogin rlogin;
-    char ruser[sizeof(cfg->username)];
+    char *ruser;
+    int addressfamily;
+    char *loghost;
 
     rlogin = snew(struct rlogin_tag);
     rlogin->fn = &fn_table;
     rlogin->s = NULL;
     rlogin->frontend = frontend_handle;
-    rlogin->term_width = cfg->width;
-    rlogin->term_height = cfg->height;
+    rlogin->term_width = conf_get_int(conf, CONF_width);
+    rlogin->term_height = conf_get_int(conf, CONF_height);
     rlogin->firstbyte = 1;
     rlogin->cansize = 0;
     rlogin->prompt = NULL;
-    rlogin->cfg = *cfg;                /* STRUCTURE COPY */
+    rlogin->conf = conf_copy(conf);
     *backend_handle = rlogin;
 
+    addressfamily = conf_get_int(conf, CONF_addressfamily);
     /*
      * Try to find host.
      */
     {
        char *buf;
        buf = dupprintf("Looking up host \"%s\"%s", host,
-                       (cfg->addressfamily == ADDRTYPE_IPV4 ? " (IPv4)" :
-                        (cfg->addressfamily == ADDRTYPE_IPV6 ? " (IPv6)" :
+                       (addressfamily == ADDRTYPE_IPV4 ? " (IPv4)" :
+                        (addressfamily == ADDRTYPE_IPV6 ? " (IPv6)" :
                          "")));
        logevent(rlogin->frontend, buf);
        sfree(buf);
     }
-    addr = name_lookup(host, port, realhost, cfg, cfg->addressfamily);
+    addr = name_lookup(host, port, realhost, conf, addressfamily);
     if ((err = sk_addr_error(addr)) != NULL) {
        sk_addr_free(addr);
        return err;
@@ -200,15 +203,16 @@ static const char *rlogin_init(void *frontend_handle, void **backend_handle,
      * Open socket.
      */
     rlogin->s = new_connection(addr, *realhost, port, 1, 0,
-                              nodelay, keepalive, (Plug) rlogin, cfg);
+                              nodelay, keepalive, (Plug) rlogin, conf);
     if ((err = sk_socket_error(rlogin->s)) != NULL)
        return err;
 
-    if (*cfg->loghost) {
+    loghost = conf_get_str(conf, CONF_loghost);
+    if (*loghost) {
        char *colon;
 
        sfree(*realhost);
-       *realhost = dupstr(cfg->loghost);
+       *realhost = dupstr(loghost);
        colon = strrchr(*realhost, ':');
        if (colon) {
            /*
@@ -226,16 +230,17 @@ static const char *rlogin_init(void *frontend_handle, void **backend_handle,
      * in which case we prompt for it and may end up deferring doing
      * anything else until the local prompt mechanism returns.
      */
-    if (get_remote_username(cfg, ruser, sizeof(ruser))) {
+    if ((ruser = get_remote_username(conf)) == NULL) {
         rlogin_startup(rlogin, ruser);
+        sfree(ruser);
     } else {
         int ret;
 
         rlogin->prompt = new_prompts(rlogin->frontend);
         rlogin->prompt->to_server = TRUE;
         rlogin->prompt->name = dupstr("Rlogin login name");
-        add_prompt(rlogin->prompt, dupstr("rlogin username: "), TRUE,
-                   sizeof(cfg->username)); 
+        /* 512 is an arbitrary limit :-( */
+        add_prompt(rlogin->prompt, dupstr("rlogin username: "), TRUE, 512); 
         ret = get_userpass_input(rlogin->prompt, NULL, 0);
         if (ret >= 0) {
             rlogin_startup(rlogin, rlogin->prompt->prompts[0]->result);
@@ -253,13 +258,14 @@ static void rlogin_free(void *handle)
         free_prompts(rlogin->prompt);
     if (rlogin->s)
        sk_close(rlogin->s);
+    conf_free(rlogin->conf);
     sfree(rlogin);
 }
 
 /*
  * Stub routine (we don't have any need to reconfigure this backend).
  */
-static void rlogin_reconfig(void *handle, Config *cfg)
+static void rlogin_reconfig(void *handle, Conf *conf)
 {
 }
 
index fc26737..fef910f 100644 (file)
--- a/sercfg.c
+++ b/sercfg.c
@@ -31,10 +31,14 @@ static void serial_parity_handler(union control *ctrl, void *dlg,
     };
     int mask = ctrl->listbox.context.i;
     int i, j;
-    Config *cfg = (Config *)data;
+    Conf *conf = (Conf *)data;
 
     if (event == EVENT_REFRESH) {
-       int oldparity = cfg->serparity;/* preserve past reentrant calls */
+       /* Fetching this once at the start of the function ensures we
+        * remember what the right value is supposed to be when
+        * operations below cause reentrant calls to this function. */
+       int oldparity = conf_get_int(conf, CONF_serparity);
+
        dlg_update_start(ctrl, dlg);
        dlg_listbox_clear(ctrl, dlg);
        for (i = 0; i < lenof(parities); i++)  {
@@ -56,14 +60,14 @@ static void serial_parity_handler(union control *ctrl, void *dlg,
            oldparity = SER_PAR_NONE;
        }
        dlg_update_done(ctrl, dlg);
-       cfg->serparity = oldparity;    /* restore */
+       conf_set_int(conf, CONF_serparity, oldparity);    /* restore */
     } else if (event == EVENT_SELCHANGE) {
        int i = dlg_listbox_index(ctrl, dlg);
        if (i < 0)
            i = SER_PAR_NONE;
        else
            i = dlg_listbox_getid(ctrl, dlg, i);
-       cfg->serparity = i;
+       conf_set_int(conf, CONF_serparity, i);
     }
 }
 
@@ -81,10 +85,14 @@ static void serial_flow_handler(union control *ctrl, void *dlg,
     };
     int mask = ctrl->listbox.context.i;
     int i, j;
-    Config *cfg = (Config *)data;
+    Conf *conf = (Conf *)data;
 
     if (event == EVENT_REFRESH) {
-       int oldflow = cfg->serflow;    /* preserve past reentrant calls */
+       /* Fetching this once at the start of the function ensures we
+        * remember what the right value is supposed to be when
+        * operations below cause reentrant calls to this function. */
+       int oldflow = conf_get_int(conf, CONF_serflow);
+
        dlg_update_start(ctrl, dlg);
        dlg_listbox_clear(ctrl, dlg);
        for (i = 0; i < lenof(flows); i++)  {
@@ -105,14 +113,14 @@ static void serial_flow_handler(union control *ctrl, void *dlg,
            oldflow = SER_FLOW_NONE;
        }
        dlg_update_done(ctrl, dlg);
-       cfg->serflow = oldflow;        /* restore */
+       conf_set_int(conf, CONF_serflow, oldflow);/* restore */
     } else if (event == EVENT_SELCHANGE) {
        int i = dlg_listbox_index(ctrl, dlg);
        if (i < 0)
            i = SER_FLOW_NONE;
        else
            i = dlg_listbox_getid(ctrl, dlg, i);
-       cfg->serflow = i;
+       conf_set_int(conf, CONF_serflow, i);
     }
 }
 
@@ -173,23 +181,22 @@ void ser_setup_config_box(struct controlbox *b, int midsession,
                        "Select a serial line");
        ctrl_editbox(s, "Serial line to connect to", 'l', 40,
                     HELPCTX(serial_line),
-                    dlg_stdeditbox_handler, I(offsetof(Config,serline)),
-                    I(sizeof(((Config *)0)->serline)));
+                    conf_editbox_handler, I(CONF_serline), I(1));
     }
 
     s = ctrl_getset(b, "Connection/Serial", "sercfg", "Configure the serial line");
     ctrl_editbox(s, "Speed (baud)", 's', 40,
                 HELPCTX(serial_speed),
-                dlg_stdeditbox_handler, I(offsetof(Config,serspeed)), I(-1));
+                conf_editbox_handler, I(CONF_serspeed), I(-1));
     ctrl_editbox(s, "Data bits", 'b', 40,
                 HELPCTX(serial_databits),
-                dlg_stdeditbox_handler,I(offsetof(Config,serdatabits)),I(-1));
+                conf_editbox_handler, I(CONF_serdatabits), I(-1));
     /*
      * Stop bits come in units of one half.
      */
     ctrl_editbox(s, "Stop bits", 't', 40,
                 HELPCTX(serial_stopbits),
-                dlg_stdeditbox_handler,I(offsetof(Config,serstopbits)),I(-2));
+                conf_editbox_handler, I(CONF_serstopbits), I(-2));
     ctrl_droplist(s, "Parity", 'p', 40,
                  HELPCTX(serial_parity),
                  serial_parity_handler, I(parity_mask));
index 943fbf3..de69d66 100644 (file)
@@ -70,45 +70,35 @@ Backend *backend_from_proto(int proto)
     return NULL;
 }
 
-int get_remote_username(Config *cfg, char *user, size_t len)
+char *get_remote_username(Conf *conf)
 {
-    if (*cfg->username) {
-       strncpy(user, cfg->username, len);
-       user[len-1] = '\0';
+    char *username = conf_get_str(conf, CONF_username);
+    if (*username) {
+       return dupstr(username);
+    } else if (conf_get_int(conf, CONF_username_from_env)) {
+       /* Use local username. */
+       return get_username();     /* might still be NULL */
     } else {
-       if (cfg->username_from_env) {
-           /* Use local username. */
-           char *luser = get_username();
-           if (luser) {
-               strncpy(user, luser, len);
-               user[len-1] = '\0';
-               sfree(luser);
-           } else {
-               *user = '\0';
-           }
-       } else {
-           *user = '\0';
-       }
+       return NULL;
     }
-    return (*user != '\0');
 }
 
-static void gpps(void *handle, const char *name, const char *def,
-                char *val, int len)
+static char *gpps_raw(void *handle, const char *name, const char *def)
 {
-    if (!read_setting_s(handle, name, val, len)) {
-       char *pdef;
-
-       pdef = platform_default_s(name);
-       if (pdef) {
-           strncpy(val, pdef, len);
-           sfree(pdef);
-       } else {
-           strncpy(val, def, len);
-       }
+    char *ret = read_setting_s(handle, name);
+    if (!ret)
+       ret = platform_default_s(name);
+    if (!ret)
+       ret = def ? dupstr(def) : NULL;   /* permit NULL as final fallback */
+    return ret;
+}
 
-       val[len - 1] = '\0';
-    }
+static void gpps(void *handle, const char *name, const char *def,
+                Conf *conf, int primary)
+{
+    char *val = gpps_raw(handle, name, def);
+    conf_set_str(conf, primary, val);
+    sfree(val);
 }
 
 /*
@@ -116,21 +106,30 @@ static void gpps(void *handle, const char *name, const char *def,
  * format of a Filename or Font is platform-dependent. So the
  * platform-dependent functions MUST return some sort of value.
  */
-static void gppfont(void *handle, const char *name, FontSpec *result)
+static void gppfont(void *handle, const char *name, Conf *conf, int primary)
 {
-    if (!read_setting_fontspec(handle, name, result))
-       *result = platform_default_fontspec(name);
+    FontSpec result;
+    if (!read_setting_fontspec(handle, name, &result))
+       result = platform_default_fontspec(name);
+    conf_set_fontspec(conf, primary, &result);
 }
-static void gppfile(void *handle, const char *name, Filename *result)
+static void gppfile(void *handle, const char *name, Conf *conf, int primary)
 {
-    if (!read_setting_filename(handle, name, result))
-       *result = platform_default_filename(name);
+    Filename result;
+    if (!read_setting_filename(handle, name, &result))
+       result = platform_default_filename(name);
+    conf_set_filename(conf, primary, &result);
 }
 
-static void gppi(void *handle, char *name, int def, int *i)
+static int gppi_raw(void *handle, char *name, int def)
 {
     def = platform_default_i(name, def);
-    *i = read_setting_i(handle, name, def);
+    return read_setting_i(handle, name, def);
+}
+
+static void gppi(void *handle, char *name, int def, Conf *conf, int primary)
+{
+    conf_set_int(conf, primary, gppi_raw(handle, name, def));
 }
 
 /*
@@ -139,52 +138,128 @@ static void gppi(void *handle, char *name, int def, int *i)
  *   NAME=VALUE,NAME=VALUE, in storage
  * `def' is in the storage format.
  */
-static void gppmap(void *handle, char *name, char *def, char *val, int len)
+static int gppmap(void *handle, char *name, Conf *conf, int primary)
 {
-    char *buf = snewn(2*len, char), *p, *q;
-    gpps(handle, name, def, buf, 2*len);
+    char *buf, *p, *q, *key, *val;
+
+    /*
+     * Start by clearing any existing subkeys of this key from conf.
+     */
+    for (val = conf_get_str_strs(conf, primary, NULL, &key);
+        val != NULL;
+        val = conf_get_str_strs(conf, primary, key, &key))
+       conf_del_str_str(conf, primary, key);
+
+    /*
+     * Now read a serialised list from the settings and unmarshal it
+     * into its components.
+     */
+    buf = gpps_raw(handle, name, NULL);
+    if (!buf)
+       return FALSE;
+
     p = buf;
-    q = val;
     while (*p) {
+       q = buf;
+       val = NULL;
        while (*p && *p != ',') {
            int c = *p++;
            if (c == '=')
-               c = '\t';
+               c = '\0';
            if (c == '\\')
                c = *p++;
            *q++ = c;
+           if (!c)
+               val = q;
        }
        if (*p == ',')
            p++;
-       *q++ = '\0';
+       if (!val)
+           val = q;
+       *q = '\0';
+
+        if (primary == CONF_portfwd && buf[0] == 'D') {
+            /*
+             * Backwards-compatibility hack: dynamic forwardings are
+             * indexed in the data store as a third type letter in the
+             * key, 'D' alongside 'L' and 'R' - but really, they
+             * should be filed under 'L' with a special _value_,
+             * because local and dynamic forwardings both involve
+             * _listening_ on a local port, and are hence mutually
+             * exclusive on the same port number. So here we translate
+             * the legacy storage format into the sensible internal
+             * form.
+             */
+            char *newkey = dupcat("L", buf+1, NULL);
+            conf_set_str_str(conf, primary, newkey, "D");
+            sfree(newkey);
+        } else {
+            conf_set_str_str(conf, primary, buf, val);
+        }
     }
-    *q = '\0';
     sfree(buf);
+
+    return TRUE;
 }
 
 /*
  * Write a set of name/value pairs in the above format.
  */
-static void wmap(void *handle, char const *key, char const *value, int len)
+static void wmap(void *handle, char const *outkey, Conf *conf, int primary)
 {
-    char *buf = snewn(2*len, char), *p;
-    const char *q;
+    char *buf, *p, *q, *key, *realkey, *val;
+    int len;
+
+    len = 1;                          /* allow for NUL */
+
+    for (val = conf_get_str_strs(conf, primary, NULL, &key);
+        val != NULL;
+        val = conf_get_str_strs(conf, primary, key, &key))
+       len += 2 + 2 * (strlen(key) + strlen(val));   /* allow for escaping */
+
+    buf = snewn(len, char);
     p = buf;
-    q = value;
-    while (*q) {
-       while (*q) {
-           int c = *q++;
-           if (c == '=' || c == ',' || c == '\\')
+
+    for (val = conf_get_str_strs(conf, primary, NULL, &key);
+        val != NULL;
+        val = conf_get_str_strs(conf, primary, key, &key)) {
+
+        if (primary == CONF_portfwd && !strcmp(val, "D")) {
+            /*
+             * Backwards-compatibility hack, as above: translate from
+             * the sensible internal representation of dynamic
+             * forwardings (key "L<port>", value "D") to the
+             * conceptually incoherent legacy storage format (key
+             * "D<port>", value empty).
+             */
+            realkey = key;             /* restore it at end of loop */
+            val = "";
+            key = dupcat("D", key+1, NULL);
+        } else {
+            realkey = NULL;
+        }
+
+       if (p != buf)
+           *p++ = ',';
+       for (q = key; *q; q++) {
+           if (*q == '=' || *q == ',' || *q == '\\')
                *p++ = '\\';
-           if (c == '\t')
-               c = '=';
-           *p++ = c;
+           *p++ = *q;
        }
-       *p++ = ',';
-       q++;
+       *p++ = '=';
+       for (q = val; *q; q++) {
+           if (*q == '=' || *q == ',' || *q == '\\')
+               *p++ = '\\';
+           *p++ = *q;
+       }
+
+        if (realkey) {
+            free(key);
+            key = realkey;
+        }
     }
     *p = '\0';
-    write_setting_s(handle, key, buf);
+    write_setting_s(handle, outkey, buf);
     sfree(buf);
 }
 
@@ -214,9 +289,9 @@ static const char *val2key(const struct keyvalwhere *mapping,
  */
 static void gprefs(void *sesskey, char *name, char *def,
                   const struct keyvalwhere *mapping, int nvals,
-                  int *array)
+                  Conf *conf, int primary)
 {
-    char commalist[256];
+    char *commalist;
     char *p, *q;
     int i, j, n, v, pos;
     unsigned long seen = 0;           /* bitmap for weeding dups etc */
@@ -224,7 +299,7 @@ static void gprefs(void *sesskey, char *name, char *def,
     /*
      * Fetch the string which we'll parse as a comma-separated list.
      */
-    gpps(sesskey, name, def, commalist, sizeof(commalist));
+    commalist = gpps_raw(sesskey, name, def);
 
     /*
      * Go through that list and convert it into values.
@@ -243,10 +318,13 @@ static void gprefs(void *sesskey, char *name, char *def,
         v = key2val(mapping, nvals, q);
         if (v != -1 && !(seen & (1 << v))) {
            seen |= (1 << v);
-           array[n++] = v;
+            conf_set_int_int(conf, primary, n, v);
+            n++;
        }
     }
 
+    sfree(commalist);
+
     /*
      * Now go through 'mapping' and add values that weren't mentioned
      * in the list we fetched. We may have to loop over it multiple
@@ -272,7 +350,8 @@ static void gprefs(void *sesskey, char *name, char *def,
                     pos = (mapping[i].where < 0 ? n : 0);
                 } else {
                     for (j = 0; j < n; j++)
-                        if (array[j] == mapping[i].vrel)
+                        if (conf_get_int_int(conf, primary, j) ==
+                            mapping[i].vrel)
                             break;
                     assert(j < n);     /* implied by (seen & (1<<vrel)) */
                     pos = (mapping[i].where < 0 ? j : j+1);
@@ -282,8 +361,9 @@ static void gprefs(void *sesskey, char *name, char *def,
                  * And add it.
                  */
                 for (j = n-1; j >= pos; j--)
-                    array[j+1] = array[j];
-                array[pos] = mapping[i].v;
+                    conf_set_int_int(conf, primary, j+1,
+                                     conf_get_int_int(conf, primary, j));
+                conf_set_int_int(conf, primary, pos, mapping[i].v);
                 n++;
             }
         }
@@ -295,13 +375,14 @@ static void gprefs(void *sesskey, char *name, char *def,
  */
 static void wprefs(void *sesskey, char *name,
                   const struct keyvalwhere *mapping, int nvals,
-                  int *array)
+                  Conf *conf, int primary)
 {
     char *buf, *p;
     int i, maxlen;
 
     for (maxlen = i = 0; i < nvals; i++) {
-       const char *s = val2key(mapping, nvals, array[i]);
+       const char *s = val2key(mapping, nvals,
+                                conf_get_int_int(conf, primary, i));
        if (s) {
             maxlen += 1 + strlen(s);
         }
@@ -311,7 +392,8 @@ static void wprefs(void *sesskey, char *name,
     p = buf;
 
     for (i = 0; i < nvals; i++) {
-       const char *s = val2key(mapping, nvals, array[i]);
+       const char *s = val2key(mapping, nvals,
+                                conf_get_int_int(conf, primary, i));
        if (s) {
             p += sprintf(p, "%s%s", (p > buf ? "," : ""), s);
        }
@@ -324,7 +406,7 @@ static void wprefs(void *sesskey, char *name,
     sfree(buf);
 }
 
-char *save_settings(char *section, Config * cfg)
+char *save_settings(char *section, Conf *conf)
 {
     void *sesskey;
     char *errmsg;
@@ -332,169 +414,169 @@ char *save_settings(char *section, Config * cfg)
     sesskey = open_settings_w(section, &errmsg);
     if (!sesskey)
        return errmsg;
-    save_open_settings(sesskey, cfg);
+    save_open_settings(sesskey, conf);
     close_settings_w(sesskey);
     return NULL;
 }
 
-void save_open_settings(void *sesskey, Config *cfg)
+void save_open_settings(void *sesskey, Conf *conf)
 {
     int i;
     char *p;
 
     write_setting_i(sesskey, "Present", 1);
-    write_setting_s(sesskey, "HostName", cfg->host);
-    write_setting_filename(sesskey, "LogFileName", cfg->logfilename);
-    write_setting_i(sesskey, "LogType", cfg->logtype);
-    write_setting_i(sesskey, "LogFileClash", cfg->logxfovr);
-    write_setting_i(sesskey, "LogFlush", cfg->logflush);
-    write_setting_i(sesskey, "SSHLogOmitPasswords", cfg->logomitpass);
-    write_setting_i(sesskey, "SSHLogOmitData", cfg->logomitdata);
+    write_setting_s(sesskey, "HostName", conf_get_str(conf, CONF_host));
+    write_setting_filename(sesskey, "LogFileName", *conf_get_filename(conf, CONF_logfilename));
+    write_setting_i(sesskey, "LogType", conf_get_int(conf, CONF_logtype));
+    write_setting_i(sesskey, "LogFileClash", conf_get_int(conf, CONF_logxfovr));
+    write_setting_i(sesskey, "LogFlush", conf_get_int(conf, CONF_logflush));
+    write_setting_i(sesskey, "SSHLogOmitPasswords", conf_get_int(conf, CONF_logomitpass));
+    write_setting_i(sesskey, "SSHLogOmitData", conf_get_int(conf, CONF_logomitdata));
     p = "raw";
     {
-       const Backend *b = backend_from_proto(cfg->protocol);
+       const Backend *b = backend_from_proto(conf_get_int(conf, CONF_protocol));
        if (b)
            p = b->name;
     }
     write_setting_s(sesskey, "Protocol", p);
-    write_setting_i(sesskey, "PortNumber", cfg->port);
+    write_setting_i(sesskey, "PortNumber", conf_get_int(conf, CONF_port));
     /* The CloseOnExit numbers are arranged in a different order from
      * the standard FORCE_ON / FORCE_OFF / AUTO. */
-    write_setting_i(sesskey, "CloseOnExit", (cfg->close_on_exit+2)%3);
-    write_setting_i(sesskey, "WarnOnClose", !!cfg->warn_on_close);
-    write_setting_i(sesskey, "PingInterval", cfg->ping_interval / 60); /* minutes */
-    write_setting_i(sesskey, "PingIntervalSecs", cfg->ping_interval % 60);     /* seconds */
-    write_setting_i(sesskey, "TCPNoDelay", cfg->tcp_nodelay);
-    write_setting_i(sesskey, "TCPKeepalives", cfg->tcp_keepalives);
-    write_setting_s(sesskey, "TerminalType", cfg->termtype);
-    write_setting_s(sesskey, "TerminalSpeed", cfg->termspeed);
-    wmap(sesskey, "TerminalModes", cfg->ttymodes, lenof(cfg->ttymodes));
+    write_setting_i(sesskey, "CloseOnExit", (conf_get_int(conf, CONF_close_on_exit)+2)%3);
+    write_setting_i(sesskey, "WarnOnClose", !!conf_get_int(conf, CONF_warn_on_close));
+    write_setting_i(sesskey, "PingInterval", conf_get_int(conf, CONF_ping_interval) / 60);     /* minutes */
+    write_setting_i(sesskey, "PingIntervalSecs", conf_get_int(conf, CONF_ping_interval) % 60); /* seconds */
+    write_setting_i(sesskey, "TCPNoDelay", conf_get_int(conf, CONF_tcp_nodelay));
+    write_setting_i(sesskey, "TCPKeepalives", conf_get_int(conf, CONF_tcp_keepalives));
+    write_setting_s(sesskey, "TerminalType", conf_get_str(conf, CONF_termtype));
+    write_setting_s(sesskey, "TerminalSpeed", conf_get_str(conf, CONF_termspeed));
+    wmap(sesskey, "TerminalModes", conf, CONF_ttymodes);
 
     /* Address family selection */
-    write_setting_i(sesskey, "AddressFamily", cfg->addressfamily);
+    write_setting_i(sesskey, "AddressFamily", conf_get_int(conf, CONF_addressfamily));
 
     /* proxy settings */
-    write_setting_s(sesskey, "ProxyExcludeList", cfg->proxy_exclude_list);
-    write_setting_i(sesskey, "ProxyDNS", (cfg->proxy_dns+2)%3);
-    write_setting_i(sesskey, "ProxyLocalhost", cfg->even_proxy_localhost);
-    write_setting_i(sesskey, "ProxyMethod", cfg->proxy_type);
-    write_setting_s(sesskey, "ProxyHost", cfg->proxy_host);
-    write_setting_i(sesskey, "ProxyPort", cfg->proxy_port);
-    write_setting_s(sesskey, "ProxyUsername", cfg->proxy_username);
-    write_setting_s(sesskey, "ProxyPassword", cfg->proxy_password);
-    write_setting_s(sesskey, "ProxyTelnetCommand", cfg->proxy_telnet_command);
-    wmap(sesskey, "Environment", cfg->environmt, lenof(cfg->environmt));
-    write_setting_s(sesskey, "UserName", cfg->username);
-    write_setting_i(sesskey, "UserNameFromEnvironment", cfg->username_from_env);
-    write_setting_s(sesskey, "LocalUserName", cfg->localusername);
-    write_setting_i(sesskey, "NoPTY", cfg->nopty);
-    write_setting_i(sesskey, "Compression", cfg->compression);
-    write_setting_i(sesskey, "TryAgent", cfg->tryagent);
-    write_setting_i(sesskey, "AgentFwd", cfg->agentfwd);
-    write_setting_i(sesskey, "GssapiFwd", cfg->gssapifwd);
-    write_setting_i(sesskey, "ChangeUsername", cfg->change_username);
-    wprefs(sesskey, "Cipher", ciphernames, CIPHER_MAX,
-          cfg->ssh_cipherlist);
-    wprefs(sesskey, "KEX", kexnames, KEX_MAX, cfg->ssh_kexlist);
-    write_setting_i(sesskey, "RekeyTime", cfg->ssh_rekey_time);
-    write_setting_s(sesskey, "RekeyBytes", cfg->ssh_rekey_data);
-    write_setting_i(sesskey, "SshNoAuth", cfg->ssh_no_userauth);
-    write_setting_i(sesskey, "SshBanner", cfg->ssh_show_banner);
-    write_setting_i(sesskey, "AuthTIS", cfg->try_tis_auth);
-    write_setting_i(sesskey, "AuthKI", cfg->try_ki_auth);
-    write_setting_i(sesskey, "AuthGSSAPI", cfg->try_gssapi_auth);
+    write_setting_s(sesskey, "ProxyExcludeList", conf_get_str(conf, CONF_proxy_exclude_list));
+    write_setting_i(sesskey, "ProxyDNS", (conf_get_int(conf, CONF_proxy_dns)+2)%3);
+    write_setting_i(sesskey, "ProxyLocalhost", conf_get_int(conf, CONF_even_proxy_localhost));
+    write_setting_i(sesskey, "ProxyMethod", conf_get_int(conf, CONF_proxy_type));
+    write_setting_s(sesskey, "ProxyHost", conf_get_str(conf, CONF_proxy_host));
+    write_setting_i(sesskey, "ProxyPort", conf_get_int(conf, CONF_proxy_port));
+    write_setting_s(sesskey, "ProxyUsername", conf_get_str(conf, CONF_proxy_username));
+    write_setting_s(sesskey, "ProxyPassword", conf_get_str(conf, CONF_proxy_password));
+    write_setting_s(sesskey, "ProxyTelnetCommand", conf_get_str(conf, CONF_proxy_telnet_command));
+    wmap(sesskey, "Environment", conf, CONF_environmt);
+    write_setting_s(sesskey, "UserName", conf_get_str(conf, CONF_username));
+    write_setting_i(sesskey, "UserNameFromEnvironment", conf_get_int(conf, CONF_username_from_env));
+    write_setting_s(sesskey, "LocalUserName", conf_get_str(conf, CONF_localusername));
+    write_setting_i(sesskey, "NoPTY", conf_get_int(conf, CONF_nopty));
+    write_setting_i(sesskey, "Compression", conf_get_int(conf, CONF_compression));
+    write_setting_i(sesskey, "TryAgent", conf_get_int(conf, CONF_tryagent));
+    write_setting_i(sesskey, "AgentFwd", conf_get_int(conf, CONF_agentfwd));
+    write_setting_i(sesskey, "GssapiFwd", conf_get_int(conf, CONF_gssapifwd));
+    write_setting_i(sesskey, "ChangeUsername", conf_get_int(conf, CONF_change_username));
+    wprefs(sesskey, "Cipher", ciphernames, CIPHER_MAX, conf, CONF_ssh_cipherlist);
+    wprefs(sesskey, "KEX", kexnames, KEX_MAX, conf, CONF_ssh_kexlist);
+    write_setting_i(sesskey, "RekeyTime", conf_get_int(conf, CONF_ssh_rekey_time));
+    write_setting_s(sesskey, "RekeyBytes", conf_get_str(conf, CONF_ssh_rekey_data));
+    write_setting_i(sesskey, "SshNoAuth", conf_get_int(conf, CONF_ssh_no_userauth));
+    write_setting_i(sesskey, "SshBanner", conf_get_int(conf, CONF_ssh_show_banner));
+    write_setting_i(sesskey, "AuthTIS", conf_get_int(conf, CONF_try_tis_auth));
+    write_setting_i(sesskey, "AuthKI", conf_get_int(conf, CONF_try_ki_auth));
+    write_setting_i(sesskey, "AuthGSSAPI", conf_get_int(conf, CONF_try_gssapi_auth));
 #ifndef NO_GSSAPI
-    wprefs(sesskey, "GSSLibs", gsslibkeywords, ngsslibs,
-          cfg->ssh_gsslist);
-    write_setting_filename(sesskey, "GSSCustom", cfg->ssh_gss_custom);
+    wprefs(sesskey, "GSSLibs", gsslibkeywords, ngsslibs, conf, CONF_ssh_gsslist);
+    write_setting_filename(sesskey, "GSSCustom", *conf_get_filename(conf, CONF_ssh_gss_custom));
 #endif
-    write_setting_i(sesskey, "SshNoShell", cfg->ssh_no_shell);
-    write_setting_i(sesskey, "SshProt", cfg->sshprot);
-    write_setting_s(sesskey, "LogHost", cfg->loghost);
-    write_setting_i(sesskey, "SSH2DES", cfg->ssh2_des_cbc);
-    write_setting_filename(sesskey, "PublicKeyFile", cfg->keyfile);
-    write_setting_s(sesskey, "RemoteCommand", cfg->remote_cmd);
-    write_setting_i(sesskey, "RFCEnviron", cfg->rfc_environ);
-    write_setting_i(sesskey, "PassiveTelnet", cfg->passive_telnet);
-    write_setting_i(sesskey, "BackspaceIsDelete", cfg->bksp_is_delete);
-    write_setting_i(sesskey, "RXVTHomeEnd", cfg->rxvt_homeend);
-    write_setting_i(sesskey, "LinuxFunctionKeys", cfg->funky_type);
-    write_setting_i(sesskey, "NoApplicationKeys", cfg->no_applic_k);
-    write_setting_i(sesskey, "NoApplicationCursors", cfg->no_applic_c);
-    write_setting_i(sesskey, "NoMouseReporting", cfg->no_mouse_rep);
-    write_setting_i(sesskey, "NoRemoteResize", cfg->no_remote_resize);
-    write_setting_i(sesskey, "NoAltScreen", cfg->no_alt_screen);
-    write_setting_i(sesskey, "NoRemoteWinTitle", cfg->no_remote_wintitle);
-    write_setting_i(sesskey, "RemoteQTitleAction", cfg->remote_qtitle_action);
-    write_setting_i(sesskey, "NoDBackspace", cfg->no_dbackspace);
-    write_setting_i(sesskey, "NoRemoteCharset", cfg->no_remote_charset);
-    write_setting_i(sesskey, "ApplicationCursorKeys", cfg->app_cursor);
-    write_setting_i(sesskey, "ApplicationKeypad", cfg->app_keypad);
-    write_setting_i(sesskey, "NetHackKeypad", cfg->nethack_keypad);
-    write_setting_i(sesskey, "AltF4", cfg->alt_f4);
-    write_setting_i(sesskey, "AltSpace", cfg->alt_space);
-    write_setting_i(sesskey, "AltOnly", cfg->alt_only);
-    write_setting_i(sesskey, "ComposeKey", cfg->compose_key);
-    write_setting_i(sesskey, "CtrlAltKeys", cfg->ctrlaltkeys);
-    write_setting_i(sesskey, "TelnetKey", cfg->telnet_keyboard);
-    write_setting_i(sesskey, "TelnetRet", cfg->telnet_newline);
-    write_setting_i(sesskey, "LocalEcho", cfg->localecho);
-    write_setting_i(sesskey, "LocalEdit", cfg->localedit);
-    write_setting_s(sesskey, "Answerback", cfg->answerback);
-    write_setting_i(sesskey, "AlwaysOnTop", cfg->alwaysontop);
-    write_setting_i(sesskey, "FullScreenOnAltEnter", cfg->fullscreenonaltenter);
-    write_setting_i(sesskey, "HideMousePtr", cfg->hide_mouseptr);
-    write_setting_i(sesskey, "SunkenEdge", cfg->sunken_edge);
-    write_setting_i(sesskey, "WindowBorder", cfg->window_border);
-    write_setting_i(sesskey, "CurType", cfg->cursor_type);
-    write_setting_i(sesskey, "BlinkCur", cfg->blink_cur);
-    write_setting_i(sesskey, "Beep", cfg->beep);
-    write_setting_i(sesskey, "BeepInd", cfg->beep_ind);
-    write_setting_filename(sesskey, "BellWaveFile", cfg->bell_wavefile);
-    write_setting_i(sesskey, "BellOverload", cfg->bellovl);
-    write_setting_i(sesskey, "BellOverloadN", cfg->bellovl_n);
-    write_setting_i(sesskey, "BellOverloadT", cfg->bellovl_t
+    write_setting_i(sesskey, "SshNoShell", conf_get_int(conf, CONF_ssh_no_shell));
+    write_setting_i(sesskey, "SshProt", conf_get_int(conf, CONF_sshprot));
+    write_setting_s(sesskey, "LogHost", conf_get_str(conf, CONF_loghost));
+    write_setting_i(sesskey, "SSH2DES", conf_get_int(conf, CONF_ssh2_des_cbc));
+    write_setting_filename(sesskey, "PublicKeyFile", *conf_get_filename(conf, CONF_keyfile));
+    write_setting_s(sesskey, "RemoteCommand", conf_get_str(conf, CONF_remote_cmd));
+    write_setting_i(sesskey, "RFCEnviron", conf_get_int(conf, CONF_rfc_environ));
+    write_setting_i(sesskey, "PassiveTelnet", conf_get_int(conf, CONF_passive_telnet));
+    write_setting_i(sesskey, "BackspaceIsDelete", conf_get_int(conf, CONF_bksp_is_delete));
+    write_setting_i(sesskey, "RXVTHomeEnd", conf_get_int(conf, CONF_rxvt_homeend));
+    write_setting_i(sesskey, "LinuxFunctionKeys", conf_get_int(conf, CONF_funky_type));
+    write_setting_i(sesskey, "NoApplicationKeys", conf_get_int(conf, CONF_no_applic_k));
+    write_setting_i(sesskey, "NoApplicationCursors", conf_get_int(conf, CONF_no_applic_c));
+    write_setting_i(sesskey, "NoMouseReporting", conf_get_int(conf, CONF_no_mouse_rep));
+    write_setting_i(sesskey, "NoRemoteResize", conf_get_int(conf, CONF_no_remote_resize));
+    write_setting_i(sesskey, "NoAltScreen", conf_get_int(conf, CONF_no_alt_screen));
+    write_setting_i(sesskey, "NoRemoteWinTitle", conf_get_int(conf, CONF_no_remote_wintitle));
+    write_setting_i(sesskey, "RemoteQTitleAction", conf_get_int(conf, CONF_remote_qtitle_action));
+    write_setting_i(sesskey, "NoDBackspace", conf_get_int(conf, CONF_no_dbackspace));
+    write_setting_i(sesskey, "NoRemoteCharset", conf_get_int(conf, CONF_no_remote_charset));
+    write_setting_i(sesskey, "ApplicationCursorKeys", conf_get_int(conf, CONF_app_cursor));
+    write_setting_i(sesskey, "ApplicationKeypad", conf_get_int(conf, CONF_app_keypad));
+    write_setting_i(sesskey, "NetHackKeypad", conf_get_int(conf, CONF_nethack_keypad));
+    write_setting_i(sesskey, "AltF4", conf_get_int(conf, CONF_alt_f4));
+    write_setting_i(sesskey, "AltSpace", conf_get_int(conf, CONF_alt_space));
+    write_setting_i(sesskey, "AltOnly", conf_get_int(conf, CONF_alt_only));
+    write_setting_i(sesskey, "ComposeKey", conf_get_int(conf, CONF_compose_key));
+    write_setting_i(sesskey, "CtrlAltKeys", conf_get_int(conf, CONF_ctrlaltkeys));
+    write_setting_i(sesskey, "TelnetKey", conf_get_int(conf, CONF_telnet_keyboard));
+    write_setting_i(sesskey, "TelnetRet", conf_get_int(conf, CONF_telnet_newline));
+    write_setting_i(sesskey, "LocalEcho", conf_get_int(conf, CONF_localecho));
+    write_setting_i(sesskey, "LocalEdit", conf_get_int(conf, CONF_localedit));
+    write_setting_s(sesskey, "Answerback", conf_get_str(conf, CONF_answerback));
+    write_setting_i(sesskey, "AlwaysOnTop", conf_get_int(conf, CONF_alwaysontop));
+    write_setting_i(sesskey, "FullScreenOnAltEnter", conf_get_int(conf, CONF_fullscreenonaltenter));
+    write_setting_i(sesskey, "HideMousePtr", conf_get_int(conf, CONF_hide_mouseptr));
+    write_setting_i(sesskey, "SunkenEdge", conf_get_int(conf, CONF_sunken_edge));
+    write_setting_i(sesskey, "WindowBorder", conf_get_int(conf, CONF_window_border));
+    write_setting_i(sesskey, "CurType", conf_get_int(conf, CONF_cursor_type));
+    write_setting_i(sesskey, "BlinkCur", conf_get_int(conf, CONF_blink_cur));
+    write_setting_i(sesskey, "Beep", conf_get_int(conf, CONF_beep));
+    write_setting_i(sesskey, "BeepInd", conf_get_int(conf, CONF_beep_ind));
+    write_setting_filename(sesskey, "BellWaveFile", *conf_get_filename(conf, CONF_bell_wavefile));
+    write_setting_i(sesskey, "BellOverload", conf_get_int(conf, CONF_bellovl));
+    write_setting_i(sesskey, "BellOverloadN", conf_get_int(conf, CONF_bellovl_n));
+    write_setting_i(sesskey, "BellOverloadT", conf_get_int(conf, CONF_bellovl_t)
 #ifdef PUTTY_UNIX_H
                    * 1000
 #endif
                    );
-    write_setting_i(sesskey, "BellOverloadS", cfg->bellovl_s
+    write_setting_i(sesskey, "BellOverloadS", conf_get_int(conf, CONF_bellovl_s)
 #ifdef PUTTY_UNIX_H
                    * 1000
 #endif
                    );
-    write_setting_i(sesskey, "ScrollbackLines", cfg->savelines);
-    write_setting_i(sesskey, "DECOriginMode", cfg->dec_om);
-    write_setting_i(sesskey, "AutoWrapMode", cfg->wrap_mode);
-    write_setting_i(sesskey, "LFImpliesCR", cfg->lfhascr);
-    write_setting_i(sesskey, "CRImpliesLF", cfg->crhaslf);
-    write_setting_i(sesskey, "DisableArabicShaping", cfg->arabicshaping);
-    write_setting_i(sesskey, "DisableBidi", cfg->bidi);
-    write_setting_i(sesskey, "WinNameAlways", cfg->win_name_always);
-    write_setting_s(sesskey, "WinTitle", cfg->wintitle);
-    write_setting_i(sesskey, "TermWidth", cfg->width);
-    write_setting_i(sesskey, "TermHeight", cfg->height);
-    write_setting_fontspec(sesskey, "Font", cfg->font);
-    write_setting_i(sesskey, "FontQuality", cfg->font_quality);
-    write_setting_i(sesskey, "FontVTMode", cfg->vtmode);
-    write_setting_i(sesskey, "UseSystemColours", cfg->system_colour);
-    write_setting_i(sesskey, "TryPalette", cfg->try_palette);
-    write_setting_i(sesskey, "ANSIColour", cfg->ansi_colour);
-    write_setting_i(sesskey, "Xterm256Colour", cfg->xterm_256_colour);
-    write_setting_i(sesskey, "BoldAsColour", cfg->bold_colour);
+    write_setting_i(sesskey, "ScrollbackLines", conf_get_int(conf, CONF_savelines));
+    write_setting_i(sesskey, "DECOriginMode", conf_get_int(conf, CONF_dec_om));
+    write_setting_i(sesskey, "AutoWrapMode", conf_get_int(conf, CONF_wrap_mode));
+    write_setting_i(sesskey, "LFImpliesCR", conf_get_int(conf, CONF_lfhascr));
+    write_setting_i(sesskey, "CRImpliesLF", conf_get_int(conf, CONF_crhaslf));
+    write_setting_i(sesskey, "DisableArabicShaping", conf_get_int(conf, CONF_arabicshaping));
+    write_setting_i(sesskey, "DisableBidi", conf_get_int(conf, CONF_bidi));
+    write_setting_i(sesskey, "WinNameAlways", conf_get_int(conf, CONF_win_name_always));
+    write_setting_s(sesskey, "WinTitle", conf_get_str(conf, CONF_wintitle));
+    write_setting_i(sesskey, "TermWidth", conf_get_int(conf, CONF_width));
+    write_setting_i(sesskey, "TermHeight", conf_get_int(conf, CONF_height));
+    write_setting_fontspec(sesskey, "Font", *conf_get_fontspec(conf, CONF_font));
+    write_setting_i(sesskey, "FontQuality", conf_get_int(conf, CONF_font_quality));
+    write_setting_i(sesskey, "FontVTMode", conf_get_int(conf, CONF_vtmode));
+    write_setting_i(sesskey, "UseSystemColours", conf_get_int(conf, CONF_system_colour));
+    write_setting_i(sesskey, "TryPalette", conf_get_int(conf, CONF_try_palette));
+    write_setting_i(sesskey, "ANSIColour", conf_get_int(conf, CONF_ansi_colour));
+    write_setting_i(sesskey, "Xterm256Colour", conf_get_int(conf, CONF_xterm_256_colour));
+    write_setting_i(sesskey, "BoldAsColour", conf_get_int(conf, CONF_bold_colour));
 
     for (i = 0; i < 22; i++) {
        char buf[20], buf2[30];
        sprintf(buf, "Colour%d", i);
-       sprintf(buf2, "%d,%d,%d", cfg->colours[i][0],
-               cfg->colours[i][1], cfg->colours[i][2]);
+       sprintf(buf2, "%d,%d,%d",
+               conf_get_int_int(conf, CONF_colours, i*3+0),
+               conf_get_int_int(conf, CONF_colours, i*3+1),
+               conf_get_int_int(conf, CONF_colours, i*3+2));
        write_setting_s(sesskey, buf, buf2);
     }
-    write_setting_i(sesskey, "RawCNP", cfg->rawcnp);
-    write_setting_i(sesskey, "PasteRTF", cfg->rtf_paste);
-    write_setting_i(sesskey, "MouseIsXterm", cfg->mouse_is_xterm);
-    write_setting_i(sesskey, "RectSelect", cfg->rect_select);
-    write_setting_i(sesskey, "MouseOverride", cfg->mouse_override);
+    write_setting_i(sesskey, "RawCNP", conf_get_int(conf, CONF_rawcnp));
+    write_setting_i(sesskey, "PasteRTF", conf_get_int(conf, CONF_rtf_paste));
+    write_setting_i(sesskey, "MouseIsXterm", conf_get_int(conf, CONF_mouse_is_xterm));
+    write_setting_i(sesskey, "RectSelect", conf_get_int(conf, CONF_rect_select));
+    write_setting_i(sesskey, "MouseOverride", conf_get_int(conf, CONF_mouse_override));
     for (i = 0; i < 256; i += 32) {
        char buf[20], buf2[256];
        int j;
@@ -502,305 +584,287 @@ void save_open_settings(void *sesskey, Config *cfg)
        *buf2 = '\0';
        for (j = i; j < i + 32; j++) {
            sprintf(buf2 + strlen(buf2), "%s%d",
-                   (*buf2 ? "," : ""), cfg->wordness[j]);
+                   (*buf2 ? "," : ""),
+                   conf_get_int_int(conf, CONF_wordness, j));
        }
        write_setting_s(sesskey, buf, buf2);
     }
-    write_setting_s(sesskey, "LineCodePage", cfg->line_codepage);
-    write_setting_i(sesskey, "CJKAmbigWide", cfg->cjk_ambig_wide);
-    write_setting_i(sesskey, "UTF8Override", cfg->utf8_override);
-    write_setting_s(sesskey, "Printer", cfg->printer);
-    write_setting_i(sesskey, "CapsLockCyr", cfg->xlat_capslockcyr);
-    write_setting_i(sesskey, "ScrollBar", cfg->scrollbar);
-    write_setting_i(sesskey, "ScrollBarFullScreen", cfg->scrollbar_in_fullscreen);
-    write_setting_i(sesskey, "ScrollOnKey", cfg->scroll_on_key);
-    write_setting_i(sesskey, "ScrollOnDisp", cfg->scroll_on_disp);
-    write_setting_i(sesskey, "EraseToScrollback", cfg->erase_to_scrollback);
-    write_setting_i(sesskey, "LockSize", cfg->resize_action);
-    write_setting_i(sesskey, "BCE", cfg->bce);
-    write_setting_i(sesskey, "BlinkText", cfg->blinktext);
-    write_setting_i(sesskey, "X11Forward", cfg->x11_forward);
-    write_setting_s(sesskey, "X11Display", cfg->x11_display);
-    write_setting_i(sesskey, "X11AuthType", cfg->x11_auth);
-    write_setting_filename(sesskey, "X11AuthFile", cfg->xauthfile);
-    write_setting_i(sesskey, "LocalPortAcceptAll", cfg->lport_acceptall);
-    write_setting_i(sesskey, "RemotePortAcceptAll", cfg->rport_acceptall);
-    wmap(sesskey, "PortForwardings", cfg->portfwd, lenof(cfg->portfwd));
-    write_setting_i(sesskey, "BugIgnore1", 2-cfg->sshbug_ignore1);
-    write_setting_i(sesskey, "BugPlainPW1", 2-cfg->sshbug_plainpw1);
-    write_setting_i(sesskey, "BugRSA1", 2-cfg->sshbug_rsa1);
-    write_setting_i(sesskey, "BugIgnore2", 2-cfg->sshbug_ignore2);
-    write_setting_i(sesskey, "BugHMAC2", 2-cfg->sshbug_hmac2);
-    write_setting_i(sesskey, "BugDeriveKey2", 2-cfg->sshbug_derivekey2);
-    write_setting_i(sesskey, "BugRSAPad2", 2-cfg->sshbug_rsapad2);
-    write_setting_i(sesskey, "BugPKSessID2", 2-cfg->sshbug_pksessid2);
-    write_setting_i(sesskey, "BugRekey2", 2-cfg->sshbug_rekey2);
-    write_setting_i(sesskey, "BugMaxPkt2", 2-cfg->sshbug_maxpkt2);
-    write_setting_i(sesskey, "StampUtmp", cfg->stamp_utmp);
-    write_setting_i(sesskey, "LoginShell", cfg->login_shell);
-    write_setting_i(sesskey, "ScrollbarOnLeft", cfg->scrollbar_on_left);
-    write_setting_fontspec(sesskey, "BoldFont", cfg->boldfont);
-    write_setting_fontspec(sesskey, "WideFont", cfg->widefont);
-    write_setting_fontspec(sesskey, "WideBoldFont", cfg->wideboldfont);
-    write_setting_i(sesskey, "ShadowBold", cfg->shadowbold);
-    write_setting_i(sesskey, "ShadowBoldOffset", cfg->shadowboldoffset);
-    write_setting_s(sesskey, "SerialLine", cfg->serline);
-    write_setting_i(sesskey, "SerialSpeed", cfg->serspeed);
-    write_setting_i(sesskey, "SerialDataBits", cfg->serdatabits);
-    write_setting_i(sesskey, "SerialStopHalfbits", cfg->serstopbits);
-    write_setting_i(sesskey, "SerialParity", cfg->serparity);
-    write_setting_i(sesskey, "SerialFlowControl", cfg->serflow);
-    write_setting_s(sesskey, "WindowClass", cfg->winclass);
+    write_setting_s(sesskey, "LineCodePage", conf_get_str(conf, CONF_line_codepage));
+    write_setting_i(sesskey, "CJKAmbigWide", conf_get_int(conf, CONF_cjk_ambig_wide));
+    write_setting_i(sesskey, "UTF8Override", conf_get_int(conf, CONF_utf8_override));
+    write_setting_s(sesskey, "Printer", conf_get_str(conf, CONF_printer));
+    write_setting_i(sesskey, "CapsLockCyr", conf_get_int(conf, CONF_xlat_capslockcyr));
+    write_setting_i(sesskey, "ScrollBar", conf_get_int(conf, CONF_scrollbar));
+    write_setting_i(sesskey, "ScrollBarFullScreen", conf_get_int(conf, CONF_scrollbar_in_fullscreen));
+    write_setting_i(sesskey, "ScrollOnKey", conf_get_int(conf, CONF_scroll_on_key));
+    write_setting_i(sesskey, "ScrollOnDisp", conf_get_int(conf, CONF_scroll_on_disp));
+    write_setting_i(sesskey, "EraseToScrollback", conf_get_int(conf, CONF_erase_to_scrollback));
+    write_setting_i(sesskey, "LockSize", conf_get_int(conf, CONF_resize_action));
+    write_setting_i(sesskey, "BCE", conf_get_int(conf, CONF_bce));
+    write_setting_i(sesskey, "BlinkText", conf_get_int(conf, CONF_blinktext));
+    write_setting_i(sesskey, "X11Forward", conf_get_int(conf, CONF_x11_forward));
+    write_setting_s(sesskey, "X11Display", conf_get_str(conf, CONF_x11_display));
+    write_setting_i(sesskey, "X11AuthType", conf_get_int(conf, CONF_x11_auth));
+    write_setting_filename(sesskey, "X11AuthFile", *conf_get_filename(conf, CONF_xauthfile));
+    write_setting_i(sesskey, "LocalPortAcceptAll", conf_get_int(conf, CONF_lport_acceptall));
+    write_setting_i(sesskey, "RemotePortAcceptAll", conf_get_int(conf, CONF_rport_acceptall));
+    wmap(sesskey, "PortForwardings", conf, CONF_portfwd);
+    write_setting_i(sesskey, "BugIgnore1", 2-conf_get_int(conf, CONF_sshbug_ignore1));
+    write_setting_i(sesskey, "BugPlainPW1", 2-conf_get_int(conf, CONF_sshbug_plainpw1));
+    write_setting_i(sesskey, "BugRSA1", 2-conf_get_int(conf, CONF_sshbug_rsa1));
+    write_setting_i(sesskey, "BugIgnore2", 2-conf_get_int(conf, CONF_sshbug_ignore2));
+    write_setting_i(sesskey, "BugHMAC2", 2-conf_get_int(conf, CONF_sshbug_hmac2));
+    write_setting_i(sesskey, "BugDeriveKey2", 2-conf_get_int(conf, CONF_sshbug_derivekey2));
+    write_setting_i(sesskey, "BugRSAPad2", 2-conf_get_int(conf, CONF_sshbug_rsapad2));
+    write_setting_i(sesskey, "BugPKSessID2", 2-conf_get_int(conf, CONF_sshbug_pksessid2));
+    write_setting_i(sesskey, "BugRekey2", 2-conf_get_int(conf, CONF_sshbug_rekey2));
+    write_setting_i(sesskey, "BugMaxPkt2", 2-conf_get_int(conf, CONF_sshbug_maxpkt2));
+    write_setting_i(sesskey, "StampUtmp", conf_get_int(conf, CONF_stamp_utmp));
+    write_setting_i(sesskey, "LoginShell", conf_get_int(conf, CONF_login_shell));
+    write_setting_i(sesskey, "ScrollbarOnLeft", conf_get_int(conf, CONF_scrollbar_on_left));
+    write_setting_fontspec(sesskey, "BoldFont", *conf_get_fontspec(conf, CONF_boldfont));
+    write_setting_fontspec(sesskey, "WideFont", *conf_get_fontspec(conf, CONF_widefont));
+    write_setting_fontspec(sesskey, "WideBoldFont", *conf_get_fontspec(conf, CONF_wideboldfont));
+    write_setting_i(sesskey, "ShadowBold", conf_get_int(conf, CONF_shadowbold));
+    write_setting_i(sesskey, "ShadowBoldOffset", conf_get_int(conf, CONF_shadowboldoffset));
+    write_setting_s(sesskey, "SerialLine", conf_get_str(conf, CONF_serline));
+    write_setting_i(sesskey, "SerialSpeed", conf_get_int(conf, CONF_serspeed));
+    write_setting_i(sesskey, "SerialDataBits", conf_get_int(conf, CONF_serdatabits));
+    write_setting_i(sesskey, "SerialStopHalfbits", conf_get_int(conf, CONF_serstopbits));
+    write_setting_i(sesskey, "SerialParity", conf_get_int(conf, CONF_serparity));
+    write_setting_i(sesskey, "SerialFlowControl", conf_get_int(conf, CONF_serflow));
+    write_setting_s(sesskey, "WindowClass", conf_get_str(conf, CONF_winclass));
 }
 
-void load_settings(char *section, Config * cfg)
+void load_settings(char *section, Conf *conf)
 {
     void *sesskey;
 
     sesskey = open_settings_r(section);
-    load_open_settings(sesskey, cfg);
+    load_open_settings(sesskey, conf);
     close_settings_r(sesskey);
 
-    if (cfg_launchable(cfg))
+    if (conf_launchable(conf))
         add_session_to_jumplist(section);
 }
 
-void load_open_settings(void *sesskey, Config *cfg)
+void load_open_settings(void *sesskey, Conf *conf)
 {
     int i;
-    char prot[10];
-
-    cfg->ssh_subsys = 0;              /* FIXME: load this properly */
-    cfg->remote_cmd_ptr = NULL;
-    cfg->remote_cmd_ptr2 = NULL;
-    cfg->ssh_nc_host[0] = '\0';
-
-    gpps(sesskey, "HostName", "", cfg->host, sizeof(cfg->host));
-    gppfile(sesskey, "LogFileName", &cfg->logfilename);
-    gppi(sesskey, "LogType", 0, &cfg->logtype);
-    gppi(sesskey, "LogFileClash", LGXF_ASK, &cfg->logxfovr);
-    gppi(sesskey, "LogFlush", 1, &cfg->logflush);
-    gppi(sesskey, "SSHLogOmitPasswords", 1, &cfg->logomitpass);
-    gppi(sesskey, "SSHLogOmitData", 0, &cfg->logomitdata);
-
-    gpps(sesskey, "Protocol", "default", prot, 10);
-    cfg->protocol = default_protocol;
-    cfg->port = default_port;
+    char *prot;
+
+    conf_set_int(conf, CONF_ssh_subsys, 0);   /* FIXME: load this properly */
+    conf_set_str(conf, CONF_remote_cmd, "");
+    conf_set_str(conf, CONF_remote_cmd2, "");
+    conf_set_str(conf, CONF_ssh_nc_host, "");
+
+    gpps(sesskey, "HostName", "", conf, CONF_host);
+    gppfile(sesskey, "LogFileName", conf, CONF_logfilename);
+    gppi(sesskey, "LogType", 0, conf, CONF_logtype);
+    gppi(sesskey, "LogFileClash", LGXF_ASK, conf, CONF_logxfovr);
+    gppi(sesskey, "LogFlush", 1, conf, CONF_logflush);
+    gppi(sesskey, "SSHLogOmitPasswords", 1, conf, CONF_logomitpass);
+    gppi(sesskey, "SSHLogOmitData", 0, conf, CONF_logomitdata);
+
+    prot = gpps_raw(sesskey, "Protocol", "default");
+    conf_set_int(conf, CONF_protocol, default_protocol);
+    conf_set_int(conf, CONF_port, default_port);
     {
        const Backend *b = backend_from_name(prot);
        if (b) {
-           cfg->protocol = b->protocol;
-           gppi(sesskey, "PortNumber", default_port, &cfg->port);
+           conf_set_int(conf, CONF_protocol, b->protocol);
+           gppi(sesskey, "PortNumber", default_port, conf, CONF_port);
        }
     }
+    sfree(prot);
 
     /* Address family selection */
-    gppi(sesskey, "AddressFamily", ADDRTYPE_UNSPEC, &cfg->addressfamily);
+    gppi(sesskey, "AddressFamily", ADDRTYPE_UNSPEC, conf, CONF_addressfamily);
 
     /* The CloseOnExit numbers are arranged in a different order from
      * the standard FORCE_ON / FORCE_OFF / AUTO. */
-    gppi(sesskey, "CloseOnExit", 1, &i); cfg->close_on_exit = (i+1)%3;
-    gppi(sesskey, "WarnOnClose", 1, &cfg->warn_on_close);
+    i = gppi_raw(sesskey, "CloseOnExit", 1); conf_set_int(conf, CONF_close_on_exit, (i+1)%3);
+    gppi(sesskey, "WarnOnClose", 1, conf, CONF_warn_on_close);
     {
        /* This is two values for backward compatibility with 0.50/0.51 */
        int pingmin, pingsec;
-       gppi(sesskey, "PingInterval", 0, &pingmin);
-       gppi(sesskey, "PingIntervalSecs", 0, &pingsec);
-       cfg->ping_interval = pingmin * 60 + pingsec;
+       pingmin = gppi_raw(sesskey, "PingInterval", 0);
+       pingsec = gppi_raw(sesskey, "PingIntervalSecs", 0);
+       conf_set_int(conf, CONF_ping_interval, pingmin * 60 + pingsec);
     }
-    gppi(sesskey, "TCPNoDelay", 1, &cfg->tcp_nodelay);
-    gppi(sesskey, "TCPKeepalives", 0, &cfg->tcp_keepalives);
-    gpps(sesskey, "TerminalType", "xterm", cfg->termtype,
-        sizeof(cfg->termtype));
-    gpps(sesskey, "TerminalSpeed", "38400,38400", cfg->termspeed,
-        sizeof(cfg->termspeed));
-    {
+    gppi(sesskey, "TCPNoDelay", 1, conf, CONF_tcp_nodelay);
+    gppi(sesskey, "TCPKeepalives", 0, conf, CONF_tcp_keepalives);
+    gpps(sesskey, "TerminalType", "xterm", conf, CONF_termtype);
+    gpps(sesskey, "TerminalSpeed", "38400,38400", conf, CONF_termspeed);
+    if (!gppmap(sesskey, "TerminalModes", conf, CONF_ttymodes)) {
        /* This hardcodes a big set of defaults in any new saved
         * sessions. Let's hope we don't change our mind. */
-       int i;
-       char *def = dupstr("");
-       /* Default: all set to "auto" */
-       for (i = 0; ttymodes[i]; i++) {
-           char *def2 = dupprintf("%s%s=A,", def, ttymodes[i]);
-           sfree(def);
-           def = def2;
-       }
-       gppmap(sesskey, "TerminalModes", def,
-              cfg->ttymodes, lenof(cfg->ttymodes));
-       sfree(def);
+       for (i = 0; ttymodes[i]; i++)
+           conf_set_str_str(conf, CONF_ttymodes, ttymodes[i], "A");
     }
 
     /* proxy settings */
-    gpps(sesskey, "ProxyExcludeList", "", cfg->proxy_exclude_list,
-        sizeof(cfg->proxy_exclude_list));
-    gppi(sesskey, "ProxyDNS", 1, &i); cfg->proxy_dns = (i+1)%3;
-    gppi(sesskey, "ProxyLocalhost", 0, &cfg->even_proxy_localhost);
-    gppi(sesskey, "ProxyMethod", -1, &cfg->proxy_type);
-    if (cfg->proxy_type == -1) {
+    gpps(sesskey, "ProxyExcludeList", "", conf, CONF_proxy_exclude_list);
+    i = gppi_raw(sesskey, "ProxyDNS", 1); conf_set_int(conf, CONF_proxy_dns, (i+1)%3);
+    gppi(sesskey, "ProxyLocalhost", 0, conf, CONF_even_proxy_localhost);
+    gppi(sesskey, "ProxyMethod", -1, conf, CONF_proxy_type);
+    if (conf_get_int(conf, CONF_proxy_type) == -1) {
         int i;
-        gppi(sesskey, "ProxyType", 0, &i);
+        i = gppi_raw(sesskey, "ProxyType", 0);
         if (i == 0)
-            cfg->proxy_type = PROXY_NONE;
+            conf_set_int(conf, CONF_proxy_type, PROXY_NONE);
         else if (i == 1)
-            cfg->proxy_type = PROXY_HTTP;
+            conf_set_int(conf, CONF_proxy_type, PROXY_HTTP);
         else if (i == 3)
-            cfg->proxy_type = PROXY_TELNET;
+            conf_set_int(conf, CONF_proxy_type, PROXY_TELNET);
         else if (i == 4)
-            cfg->proxy_type = PROXY_CMD;
+            conf_set_int(conf, CONF_proxy_type, PROXY_CMD);
         else {
-            gppi(sesskey, "ProxySOCKSVersion", 5, &i);
+            i = gppi_raw(sesskey, "ProxySOCKSVersion", 5);
             if (i == 5)
-                cfg->proxy_type = PROXY_SOCKS5;
+                conf_set_int(conf, CONF_proxy_type, PROXY_SOCKS5);
             else
-                cfg->proxy_type = PROXY_SOCKS4;
+                conf_set_int(conf, CONF_proxy_type, PROXY_SOCKS4);
         }
     }
-    gpps(sesskey, "ProxyHost", "proxy", cfg->proxy_host,
-        sizeof(cfg->proxy_host));
-    gppi(sesskey, "ProxyPort", 80, &cfg->proxy_port);
-    gpps(sesskey, "ProxyUsername", "", cfg->proxy_username,
-        sizeof(cfg->proxy_username));
-    gpps(sesskey, "ProxyPassword", "", cfg->proxy_password,
-        sizeof(cfg->proxy_password));
+    gpps(sesskey, "ProxyHost", "proxy", conf, CONF_proxy_host);
+    gppi(sesskey, "ProxyPort", 80, conf, CONF_proxy_port);
+    gpps(sesskey, "ProxyUsername", "", conf, CONF_proxy_username);
+    gpps(sesskey, "ProxyPassword", "", conf, CONF_proxy_password);
     gpps(sesskey, "ProxyTelnetCommand", "connect %host %port\\n",
-        cfg->proxy_telnet_command, sizeof(cfg->proxy_telnet_command));
-    gppmap(sesskey, "Environment", "", cfg->environmt, lenof(cfg->environmt));
-    gpps(sesskey, "UserName", "", cfg->username, sizeof(cfg->username));
-    gppi(sesskey, "UserNameFromEnvironment", 0, &cfg->username_from_env);
-    gpps(sesskey, "LocalUserName", "", cfg->localusername,
-        sizeof(cfg->localusername));
-    gppi(sesskey, "NoPTY", 0, &cfg->nopty);
-    gppi(sesskey, "Compression", 0, &cfg->compression);
-    gppi(sesskey, "TryAgent", 1, &cfg->tryagent);
-    gppi(sesskey, "AgentFwd", 0, &cfg->agentfwd);
-    gppi(sesskey, "ChangeUsername", 0, &cfg->change_username);
-    gppi(sesskey, "GssapiFwd", 0, &cfg->gssapifwd);
+        conf, CONF_proxy_telnet_command);
+    gppmap(sesskey, "Environment", conf, CONF_environmt);
+    gpps(sesskey, "UserName", "", conf, CONF_username);
+    gppi(sesskey, "UserNameFromEnvironment", 0, conf, CONF_username_from_env);
+    gpps(sesskey, "LocalUserName", "", conf, CONF_localusername);
+    gppi(sesskey, "NoPTY", 0, conf, CONF_nopty);
+    gppi(sesskey, "Compression", 0, conf, CONF_compression);
+    gppi(sesskey, "TryAgent", 1, conf, CONF_tryagent);
+    gppi(sesskey, "AgentFwd", 0, conf, CONF_agentfwd);
+    gppi(sesskey, "ChangeUsername", 0, conf, CONF_change_username);
+    gppi(sesskey, "GssapiFwd", 0, conf, CONF_gssapifwd);
     gprefs(sesskey, "Cipher", "\0",
-          ciphernames, CIPHER_MAX, cfg->ssh_cipherlist);
+          ciphernames, CIPHER_MAX, conf, CONF_ssh_cipherlist);
     {
        /* Backward-compatibility: we used to have an option to
         * disable gex under the "bugs" panel after one report of
         * a server which offered it then choked, but we never got
         * a server version string or any other reports. */
        char *default_kexes;
-       gppi(sesskey, "BugDHGEx2", 0, &i); i = 2-i;
+       i = 2 - gppi_raw(sesskey, "BugDHGEx2", 0);
        if (i == FORCE_ON)
            default_kexes = "dh-group14-sha1,dh-group1-sha1,rsa,WARN,dh-gex-sha1";
        else
            default_kexes = "dh-gex-sha1,dh-group14-sha1,dh-group1-sha1,rsa,WARN";
        gprefs(sesskey, "KEX", default_kexes,
-              kexnames, KEX_MAX, cfg->ssh_kexlist);
+              kexnames, KEX_MAX, conf, CONF_ssh_kexlist);
     }
-    gppi(sesskey, "RekeyTime", 60, &cfg->ssh_rekey_time);
-    gpps(sesskey, "RekeyBytes", "1G", cfg->ssh_rekey_data,
-        sizeof(cfg->ssh_rekey_data));
-    gppi(sesskey, "SshProt", 2, &cfg->sshprot);
-    gpps(sesskey, "LogHost", "", cfg->loghost, sizeof(cfg->loghost));
-    gppi(sesskey, "SSH2DES", 0, &cfg->ssh2_des_cbc);
-    gppi(sesskey, "SshNoAuth", 0, &cfg->ssh_no_userauth);
-    gppi(sesskey, "SshBanner", 1, &cfg->ssh_show_banner);
-    gppi(sesskey, "AuthTIS", 0, &cfg->try_tis_auth);
-    gppi(sesskey, "AuthKI", 1, &cfg->try_ki_auth);
-    gppi(sesskey, "AuthGSSAPI", 1, &cfg->try_gssapi_auth);
+    gppi(sesskey, "RekeyTime", 60, conf, CONF_ssh_rekey_time);
+    gpps(sesskey, "RekeyBytes", "1G", conf, CONF_ssh_rekey_data);
+    gppi(sesskey, "SshProt", 2, conf, CONF_sshprot);
+    gpps(sesskey, "LogHost", "", conf, CONF_loghost);
+    gppi(sesskey, "SSH2DES", 0, conf, CONF_ssh2_des_cbc);
+    gppi(sesskey, "SshNoAuth", 0, conf, CONF_ssh_no_userauth);
+    gppi(sesskey, "SshBanner", 1, conf, CONF_ssh_show_banner);
+    gppi(sesskey, "AuthTIS", 0, conf, CONF_try_tis_auth);
+    gppi(sesskey, "AuthKI", 1, conf, CONF_try_ki_auth);
+    gppi(sesskey, "AuthGSSAPI", 1, conf, CONF_try_gssapi_auth);
 #ifndef NO_GSSAPI
     gprefs(sesskey, "GSSLibs", "\0",
-          gsslibkeywords, ngsslibs, cfg->ssh_gsslist);
-    gppfile(sesskey, "GSSCustom", &cfg->ssh_gss_custom);
+          gsslibkeywords, ngsslibs, conf, CONF_ssh_gsslist);
+    gppfile(sesskey, "GSSCustom", conf, CONF_ssh_gss_custom);
 #endif
-    gppi(sesskey, "SshNoShell", 0, &cfg->ssh_no_shell);
-    gppfile(sesskey, "PublicKeyFile", &cfg->keyfile);
-    gpps(sesskey, "RemoteCommand", "", cfg->remote_cmd,
-        sizeof(cfg->remote_cmd));
-    gppi(sesskey, "RFCEnviron", 0, &cfg->rfc_environ);
-    gppi(sesskey, "PassiveTelnet", 0, &cfg->passive_telnet);
-    gppi(sesskey, "BackspaceIsDelete", 1, &cfg->bksp_is_delete);
-    gppi(sesskey, "RXVTHomeEnd", 0, &cfg->rxvt_homeend);
-    gppi(sesskey, "LinuxFunctionKeys", 0, &cfg->funky_type);
-    gppi(sesskey, "NoApplicationKeys", 0, &cfg->no_applic_k);
-    gppi(sesskey, "NoApplicationCursors", 0, &cfg->no_applic_c);
-    gppi(sesskey, "NoMouseReporting", 0, &cfg->no_mouse_rep);
-    gppi(sesskey, "NoRemoteResize", 0, &cfg->no_remote_resize);
-    gppi(sesskey, "NoAltScreen", 0, &cfg->no_alt_screen);
-    gppi(sesskey, "NoRemoteWinTitle", 0, &cfg->no_remote_wintitle);
+    gppi(sesskey, "SshNoShell", 0, conf, CONF_ssh_no_shell);
+    gppfile(sesskey, "PublicKeyFile", conf, CONF_keyfile);
+    gpps(sesskey, "RemoteCommand", "", conf, CONF_remote_cmd);
+    gppi(sesskey, "RFCEnviron", 0, conf, CONF_rfc_environ);
+    gppi(sesskey, "PassiveTelnet", 0, conf, CONF_passive_telnet);
+    gppi(sesskey, "BackspaceIsDelete", 1, conf, CONF_bksp_is_delete);
+    gppi(sesskey, "RXVTHomeEnd", 0, conf, CONF_rxvt_homeend);
+    gppi(sesskey, "LinuxFunctionKeys", 0, conf, CONF_funky_type);
+    gppi(sesskey, "NoApplicationKeys", 0, conf, CONF_no_applic_k);
+    gppi(sesskey, "NoApplicationCursors", 0, conf, CONF_no_applic_c);
+    gppi(sesskey, "NoMouseReporting", 0, conf, CONF_no_mouse_rep);
+    gppi(sesskey, "NoRemoteResize", 0, conf, CONF_no_remote_resize);
+    gppi(sesskey, "NoAltScreen", 0, conf, CONF_no_alt_screen);
+    gppi(sesskey, "NoRemoteWinTitle", 0, conf, CONF_no_remote_wintitle);
     {
        /* Backward compatibility */
-       int no_remote_qtitle;
-       gppi(sesskey, "NoRemoteQTitle", 1, &no_remote_qtitle);
+       int no_remote_qtitle = gppi_raw(sesskey, "NoRemoteQTitle", 1);
        /* We deliberately interpret the old setting of "no response" as
         * "empty string". This changes the behaviour, but hopefully for
         * the better; the user can always recover the old behaviour. */
        gppi(sesskey, "RemoteQTitleAction",
             no_remote_qtitle ? TITLE_EMPTY : TITLE_REAL,
-            &cfg->remote_qtitle_action);
+            conf, CONF_remote_qtitle_action);
     }
-    gppi(sesskey, "NoDBackspace", 0, &cfg->no_dbackspace);
-    gppi(sesskey, "NoRemoteCharset", 0, &cfg->no_remote_charset);
-    gppi(sesskey, "ApplicationCursorKeys", 0, &cfg->app_cursor);
-    gppi(sesskey, "ApplicationKeypad", 0, &cfg->app_keypad);
-    gppi(sesskey, "NetHackKeypad", 0, &cfg->nethack_keypad);
-    gppi(sesskey, "AltF4", 1, &cfg->alt_f4);
-    gppi(sesskey, "AltSpace", 0, &cfg->alt_space);
-    gppi(sesskey, "AltOnly", 0, &cfg->alt_only);
-    gppi(sesskey, "ComposeKey", 0, &cfg->compose_key);
-    gppi(sesskey, "CtrlAltKeys", 1, &cfg->ctrlaltkeys);
-    gppi(sesskey, "TelnetKey", 0, &cfg->telnet_keyboard);
-    gppi(sesskey, "TelnetRet", 1, &cfg->telnet_newline);
-    gppi(sesskey, "LocalEcho", AUTO, &cfg->localecho);
-    gppi(sesskey, "LocalEdit", AUTO, &cfg->localedit);
-    gpps(sesskey, "Answerback", "PuTTY", cfg->answerback,
-        sizeof(cfg->answerback));
-    gppi(sesskey, "AlwaysOnTop", 0, &cfg->alwaysontop);
-    gppi(sesskey, "FullScreenOnAltEnter", 0, &cfg->fullscreenonaltenter);
-    gppi(sesskey, "HideMousePtr", 0, &cfg->hide_mouseptr);
-    gppi(sesskey, "SunkenEdge", 0, &cfg->sunken_edge);
-    gppi(sesskey, "WindowBorder", 1, &cfg->window_border);
-    gppi(sesskey, "CurType", 0, &cfg->cursor_type);
-    gppi(sesskey, "BlinkCur", 0, &cfg->blink_cur);
-    /* pedantic compiler tells me I can't use &cfg->beep as an int * :-) */
-    gppi(sesskey, "Beep", 1, &cfg->beep);
-    gppi(sesskey, "BeepInd", 0, &cfg->beep_ind);
-    gppfile(sesskey, "BellWaveFile", &cfg->bell_wavefile);
-    gppi(sesskey, "BellOverload", 1, &cfg->bellovl);
-    gppi(sesskey, "BellOverloadN", 5, &cfg->bellovl_n);
-    gppi(sesskey, "BellOverloadT", 2*TICKSPERSEC
+    gppi(sesskey, "NoDBackspace", 0, conf, CONF_no_dbackspace);
+    gppi(sesskey, "NoRemoteCharset", 0, conf, CONF_no_remote_charset);
+    gppi(sesskey, "ApplicationCursorKeys", 0, conf, CONF_app_cursor);
+    gppi(sesskey, "ApplicationKeypad", 0, conf, CONF_app_keypad);
+    gppi(sesskey, "NetHackKeypad", 0, conf, CONF_nethack_keypad);
+    gppi(sesskey, "AltF4", 1, conf, CONF_alt_f4);
+    gppi(sesskey, "AltSpace", 0, conf, CONF_alt_space);
+    gppi(sesskey, "AltOnly", 0, conf, CONF_alt_only);
+    gppi(sesskey, "ComposeKey", 0, conf, CONF_compose_key);
+    gppi(sesskey, "CtrlAltKeys", 1, conf, CONF_ctrlaltkeys);
+    gppi(sesskey, "TelnetKey", 0, conf, CONF_telnet_keyboard);
+    gppi(sesskey, "TelnetRet", 1, conf, CONF_telnet_newline);
+    gppi(sesskey, "LocalEcho", AUTO, conf, CONF_localecho);
+    gppi(sesskey, "LocalEdit", AUTO, conf, CONF_localedit);
+    gpps(sesskey, "Answerback", "PuTTY", conf, CONF_answerback);
+    gppi(sesskey, "AlwaysOnTop", 0, conf, CONF_alwaysontop);
+    gppi(sesskey, "FullScreenOnAltEnter", 0, conf, CONF_fullscreenonaltenter);
+    gppi(sesskey, "HideMousePtr", 0, conf, CONF_hide_mouseptr);
+    gppi(sesskey, "SunkenEdge", 0, conf, CONF_sunken_edge);
+    gppi(sesskey, "WindowBorder", 1, conf, CONF_window_border);
+    gppi(sesskey, "CurType", 0, conf, CONF_cursor_type);
+    gppi(sesskey, "BlinkCur", 0, conf, CONF_blink_cur);
+    /* pedantic compiler tells me I can't use conf, CONF_beep as an int * :-) */
+    gppi(sesskey, "Beep", 1, conf, CONF_beep);
+    gppi(sesskey, "BeepInd", 0, conf, CONF_beep_ind);
+    gppfile(sesskey, "BellWaveFile", conf, CONF_bell_wavefile);
+    gppi(sesskey, "BellOverload", 1, conf, CONF_bellovl);
+    gppi(sesskey, "BellOverloadN", 5, conf, CONF_bellovl_n);
+    i = gppi_raw(sesskey, "BellOverloadT", 2*TICKSPERSEC
 #ifdef PUTTY_UNIX_H
                                   *1000
 #endif
-                                  , &i);
-    cfg->bellovl_t = i
+                                  );
+    conf_set_int(conf, CONF_bellovl_t, i
 #ifdef PUTTY_UNIX_H
-                   / 1000
+                / 1000
 #endif
-       ;
-    gppi(sesskey, "BellOverloadS", 5*TICKSPERSEC
+                );
+    i = gppi_raw(sesskey, "BellOverloadS", 5*TICKSPERSEC
 #ifdef PUTTY_UNIX_H
                                   *1000
 #endif
-                                  , &i);
-    cfg->bellovl_s = i
+                                  );
+    conf_set_int(conf, CONF_bellovl_s, i
 #ifdef PUTTY_UNIX_H
-                   / 1000
+                / 1000
 #endif
-       ;
-    gppi(sesskey, "ScrollbackLines", 200, &cfg->savelines);
-    gppi(sesskey, "DECOriginMode", 0, &cfg->dec_om);
-    gppi(sesskey, "AutoWrapMode", 1, &cfg->wrap_mode);
-    gppi(sesskey, "LFImpliesCR", 0, &cfg->lfhascr);
-    gppi(sesskey, "CRImpliesLF", 0, &cfg->crhaslf);
-    gppi(sesskey, "DisableArabicShaping", 0, &cfg->arabicshaping);
-    gppi(sesskey, "DisableBidi", 0, &cfg->bidi);
-    gppi(sesskey, "WinNameAlways", 1, &cfg->win_name_always);
-    gpps(sesskey, "WinTitle", "", cfg->wintitle, sizeof(cfg->wintitle));
-    gppi(sesskey, "TermWidth", 80, &cfg->width);
-    gppi(sesskey, "TermHeight", 24, &cfg->height);
-    gppfont(sesskey, "Font", &cfg->font);
-    gppi(sesskey, "FontQuality", FQ_DEFAULT, &cfg->font_quality);
-    gppi(sesskey, "FontVTMode", VT_UNICODE, (int *) &cfg->vtmode);
-    gppi(sesskey, "UseSystemColours", 0, &cfg->system_colour);
-    gppi(sesskey, "TryPalette", 0, &cfg->try_palette);
-    gppi(sesskey, "ANSIColour", 1, &cfg->ansi_colour);
-    gppi(sesskey, "Xterm256Colour", 1, &cfg->xterm_256_colour);
-    gppi(sesskey, "BoldAsColour", 1, &cfg->bold_colour);
+                );
+    gppi(sesskey, "ScrollbackLines", 200, conf, CONF_savelines);
+    gppi(sesskey, "DECOriginMode", 0, conf, CONF_dec_om);
+    gppi(sesskey, "AutoWrapMode", 1, conf, CONF_wrap_mode);
+    gppi(sesskey, "LFImpliesCR", 0, conf, CONF_lfhascr);
+    gppi(sesskey, "CRImpliesLF", 0, conf, CONF_crhaslf);
+    gppi(sesskey, "DisableArabicShaping", 0, conf, CONF_arabicshaping);
+    gppi(sesskey, "DisableBidi", 0, conf, CONF_bidi);
+    gppi(sesskey, "WinNameAlways", 1, conf, CONF_win_name_always);
+    gpps(sesskey, "WinTitle", "", conf, CONF_wintitle);
+    gppi(sesskey, "TermWidth", 80, conf, CONF_width);
+    gppi(sesskey, "TermHeight", 24, conf, CONF_height);
+    gppfont(sesskey, "Font", conf, CONF_font);
+    gppi(sesskey, "FontQuality", FQ_DEFAULT, conf, CONF_font_quality);
+    gppi(sesskey, "FontVTMode", VT_UNICODE, conf, CONF_vtmode);
+    gppi(sesskey, "UseSystemColours", 0, conf, CONF_system_colour);
+    gppi(sesskey, "TryPalette", 0, conf, CONF_try_palette);
+    gppi(sesskey, "ANSIColour", 1, conf, CONF_ansi_colour);
+    gppi(sesskey, "Xterm256Colour", 1, conf, CONF_xterm_256_colour);
+    gppi(sesskey, "BoldAsColour", 1, conf, CONF_bold_colour);
 
     for (i = 0; i < 22; i++) {
        static const char *const defaults[] = {
@@ -810,21 +874,22 @@ void load_open_settings(void *sesskey, Config *cfg)
            "85,85,255", "187,0,187", "255,85,255", "0,187,187",
            "85,255,255", "187,187,187", "255,255,255"
        };
-       char buf[20], buf2[30];
+       char buf[20], *buf2;
        int c0, c1, c2;
        sprintf(buf, "Colour%d", i);
-       gpps(sesskey, buf, defaults[i], buf2, sizeof(buf2));
+       buf2 = gpps_raw(sesskey, buf, defaults[i]);
        if (sscanf(buf2, "%d,%d,%d", &c0, &c1, &c2) == 3) {
-           cfg->colours[i][0] = c0;
-           cfg->colours[i][1] = c1;
-           cfg->colours[i][2] = c2;
+           conf_set_int_int(conf, CONF_colours, i*3+0, c0);
+           conf_set_int_int(conf, CONF_colours, i*3+1, c1);
+           conf_set_int_int(conf, CONF_colours, i*3+2, c2);
        }
+       sfree(buf2);
     }
-    gppi(sesskey, "RawCNP", 0, &cfg->rawcnp);
-    gppi(sesskey, "PasteRTF", 0, &cfg->rtf_paste);
-    gppi(sesskey, "MouseIsXterm", 0, &cfg->mouse_is_xterm);
-    gppi(sesskey, "RectSelect", 0, &cfg->rect_select);
-    gppi(sesskey, "MouseOverride", 1, &cfg->mouse_override);
+    gppi(sesskey, "RawCNP", 0, conf, CONF_rawcnp);
+    gppi(sesskey, "PasteRTF", 0, conf, CONF_rtf_paste);
+    gppi(sesskey, "MouseIsXterm", 0, conf, CONF_mouse_is_xterm);
+    gppi(sesskey, "RectSelect", 0, conf, CONF_rect_select);
+    gppi(sesskey, "MouseOverride", 1, conf, CONF_mouse_override);
     for (i = 0; i < 256; i += 32) {
        static const char *const defaults[] = {
            "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0",
@@ -836,10 +901,10 @@ void load_open_settings(void *sesskey, Config *cfg)
            "2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,1,2,2,2,2,2,2,2,2",
            "2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,1,2,2,2,2,2,2,2,2"
        };
-       char buf[20], buf2[256], *p;
+       char buf[20], *buf2, *p;
        int j;
        sprintf(buf, "Wordness%d", i);
-       gpps(sesskey, buf, defaults[i / 32], buf2, sizeof(buf2));
+       buf2 = gpps_raw(sesskey, buf, defaults[i / 32]);
        p = buf2;
        for (j = i; j < i + 32; j++) {
            char *q = p;
@@ -847,75 +912,74 @@ void load_open_settings(void *sesskey, Config *cfg)
                p++;
            if (*p == ',')
                *p++ = '\0';
-           cfg->wordness[j] = atoi(q);
+           conf_set_int_int(conf, CONF_wordness, j, atoi(q));
        }
+       sfree(buf2);
     }
     /*
      * The empty default for LineCodePage will be converted later
      * into a plausible default for the locale.
      */
-    gpps(sesskey, "LineCodePage", "", cfg->line_codepage,
-        sizeof(cfg->line_codepage));
-    gppi(sesskey, "CJKAmbigWide", 0, &cfg->cjk_ambig_wide);
-    gppi(sesskey, "UTF8Override", 1, &cfg->utf8_override);
-    gpps(sesskey, "Printer", "", cfg->printer, sizeof(cfg->printer));
-    gppi (sesskey, "CapsLockCyr", 0, &cfg->xlat_capslockcyr);
-    gppi(sesskey, "ScrollBar", 1, &cfg->scrollbar);
-    gppi(sesskey, "ScrollBarFullScreen", 0, &cfg->scrollbar_in_fullscreen);
-    gppi(sesskey, "ScrollOnKey", 0, &cfg->scroll_on_key);
-    gppi(sesskey, "ScrollOnDisp", 1, &cfg->scroll_on_disp);
-    gppi(sesskey, "EraseToScrollback", 1, &cfg->erase_to_scrollback);
-    gppi(sesskey, "LockSize", 0, &cfg->resize_action);
-    gppi(sesskey, "BCE", 1, &cfg->bce);
-    gppi(sesskey, "BlinkText", 0, &cfg->blinktext);
-    gppi(sesskey, "X11Forward", 0, &cfg->x11_forward);
-    gpps(sesskey, "X11Display", "", cfg->x11_display,
-        sizeof(cfg->x11_display));
-    gppi(sesskey, "X11AuthType", X11_MIT, &cfg->x11_auth);
-    gppfile(sesskey, "X11AuthFile", &cfg->xauthfile);
-
-    gppi(sesskey, "LocalPortAcceptAll", 0, &cfg->lport_acceptall);
-    gppi(sesskey, "RemotePortAcceptAll", 0, &cfg->rport_acceptall);
-    gppmap(sesskey, "PortForwardings", "", cfg->portfwd, lenof(cfg->portfwd));
-    gppi(sesskey, "BugIgnore1", 0, &i); cfg->sshbug_ignore1 = 2-i;
-    gppi(sesskey, "BugPlainPW1", 0, &i); cfg->sshbug_plainpw1 = 2-i;
-    gppi(sesskey, "BugRSA1", 0, &i); cfg->sshbug_rsa1 = 2-i;
-    gppi(sesskey, "BugIgnore2", 0, &i); cfg->sshbug_ignore2 = 2-i;
+    gpps(sesskey, "LineCodePage", "", conf, CONF_line_codepage);
+    gppi(sesskey, "CJKAmbigWide", 0, conf, CONF_cjk_ambig_wide);
+    gppi(sesskey, "UTF8Override", 1, conf, CONF_utf8_override);
+    gpps(sesskey, "Printer", "", conf, CONF_printer);
+    gppi(sesskey, "CapsLockCyr", 0, conf, CONF_xlat_capslockcyr);
+    gppi(sesskey, "ScrollBar", 1, conf, CONF_scrollbar);
+    gppi(sesskey, "ScrollBarFullScreen", 0, conf, CONF_scrollbar_in_fullscreen);
+    gppi(sesskey, "ScrollOnKey", 0, conf, CONF_scroll_on_key);
+    gppi(sesskey, "ScrollOnDisp", 1, conf, CONF_scroll_on_disp);
+    gppi(sesskey, "EraseToScrollback", 1, conf, CONF_erase_to_scrollback);
+    gppi(sesskey, "LockSize", 0, conf, CONF_resize_action);
+    gppi(sesskey, "BCE", 1, conf, CONF_bce);
+    gppi(sesskey, "BlinkText", 0, conf, CONF_blinktext);
+    gppi(sesskey, "X11Forward", 0, conf, CONF_x11_forward);
+    gpps(sesskey, "X11Display", "", conf, CONF_x11_display);
+    gppi(sesskey, "X11AuthType", X11_MIT, conf, CONF_x11_auth);
+    gppfile(sesskey, "X11AuthFile", conf, CONF_xauthfile);
+
+    gppi(sesskey, "LocalPortAcceptAll", 0, conf, CONF_lport_acceptall);
+    gppi(sesskey, "RemotePortAcceptAll", 0, conf, CONF_rport_acceptall);
+    gppmap(sesskey, "PortForwardings", conf, CONF_portfwd);
+    i = gppi_raw(sesskey, "BugIgnore1", 0); conf_set_int(conf, CONF_sshbug_ignore1, 2-i);
+    i = gppi_raw(sesskey, "BugPlainPW1", 0); conf_set_int(conf, CONF_sshbug_plainpw1, 2-i);
+    i = gppi_raw(sesskey, "BugRSA1", 0); conf_set_int(conf, CONF_sshbug_rsa1, 2-i);
+    i = gppi_raw(sesskey, "BugIgnore2", 0); conf_set_int(conf, CONF_sshbug_ignore2, 2-i);
     {
        int i;
-       gppi(sesskey, "BugHMAC2", 0, &i); cfg->sshbug_hmac2 = 2-i;
-       if (cfg->sshbug_hmac2 == AUTO) {
-           gppi(sesskey, "BuggyMAC", 0, &i);
+       i = gppi_raw(sesskey, "BugHMAC2", 0); conf_set_int(conf, CONF_sshbug_hmac2, 2-i);
+       if (2-i == AUTO) {
+           i = gppi_raw(sesskey, "BuggyMAC", 0);
            if (i == 1)
-               cfg->sshbug_hmac2 = FORCE_ON;
+               conf_set_int(conf, CONF_sshbug_hmac2, FORCE_ON);
        }
     }
-    gppi(sesskey, "BugDeriveKey2", 0, &i); cfg->sshbug_derivekey2 = 2-i;
-    gppi(sesskey, "BugRSAPad2", 0, &i); cfg->sshbug_rsapad2 = 2-i;
-    gppi(sesskey, "BugPKSessID2", 0, &i); cfg->sshbug_pksessid2 = 2-i;
-    gppi(sesskey, "BugRekey2", 0, &i); cfg->sshbug_rekey2 = 2-i;
-    gppi(sesskey, "BugMaxPkt2", 0, &i); cfg->sshbug_maxpkt2 = 2-i;
-    cfg->ssh_simple = FALSE;
-    gppi(sesskey, "StampUtmp", 1, &cfg->stamp_utmp);
-    gppi(sesskey, "LoginShell", 1, &cfg->login_shell);
-    gppi(sesskey, "ScrollbarOnLeft", 0, &cfg->scrollbar_on_left);
-    gppi(sesskey, "ShadowBold", 0, &cfg->shadowbold);
-    gppfont(sesskey, "BoldFont", &cfg->boldfont);
-    gppfont(sesskey, "WideFont", &cfg->widefont);
-    gppfont(sesskey, "WideBoldFont", &cfg->wideboldfont);
-    gppi(sesskey, "ShadowBoldOffset", 1, &cfg->shadowboldoffset);
-    gpps(sesskey, "SerialLine", "", cfg->serline, sizeof(cfg->serline));
-    gppi(sesskey, "SerialSpeed", 9600, &cfg->serspeed);
-    gppi(sesskey, "SerialDataBits", 8, &cfg->serdatabits);
-    gppi(sesskey, "SerialStopHalfbits", 2, &cfg->serstopbits);
-    gppi(sesskey, "SerialParity", SER_PAR_NONE, &cfg->serparity);
-    gppi(sesskey, "SerialFlowControl", SER_FLOW_XONXOFF, &cfg->serflow);
-    gpps(sesskey, "WindowClass", "", cfg->winclass, sizeof(cfg->winclass));
+    i = gppi_raw(sesskey, "BugDeriveKey2", 0); conf_set_int(conf, CONF_sshbug_derivekey2, 2-i);
+    i = gppi_raw(sesskey, "BugRSAPad2", 0); conf_set_int(conf, CONF_sshbug_rsapad2, 2-i);
+    i = gppi_raw(sesskey, "BugPKSessID2", 0); conf_set_int(conf, CONF_sshbug_pksessid2, 2-i);
+    i = gppi_raw(sesskey, "BugRekey2", 0); conf_set_int(conf, CONF_sshbug_rekey2, 2-i);
+    i = gppi_raw(sesskey, "BugMaxPkt2", 0); conf_set_int(conf, CONF_sshbug_maxpkt2, 2-i);
+    conf_set_int(conf, CONF_ssh_simple, FALSE);
+    gppi(sesskey, "StampUtmp", 1, conf, CONF_stamp_utmp);
+    gppi(sesskey, "LoginShell", 1, conf, CONF_login_shell);
+    gppi(sesskey, "ScrollbarOnLeft", 0, conf, CONF_scrollbar_on_left);
+    gppi(sesskey, "ShadowBold", 0, conf, CONF_shadowbold);
+    gppfont(sesskey, "BoldFont", conf, CONF_boldfont);
+    gppfont(sesskey, "WideFont", conf, CONF_widefont);
+    gppfont(sesskey, "WideBoldFont", conf, CONF_wideboldfont);
+    gppi(sesskey, "ShadowBoldOffset", 1, conf, CONF_shadowboldoffset);
+    gpps(sesskey, "SerialLine", "", conf, CONF_serline);
+    gppi(sesskey, "SerialSpeed", 9600, conf, CONF_serspeed);
+    gppi(sesskey, "SerialDataBits", 8, conf, CONF_serdatabits);
+    gppi(sesskey, "SerialStopHalfbits", 2, conf, CONF_serstopbits);
+    gppi(sesskey, "SerialParity", SER_PAR_NONE, conf, CONF_serparity);
+    gppi(sesskey, "SerialFlowControl", SER_FLOW_XONXOFF, conf, CONF_serflow);
+    gpps(sesskey, "WindowClass", "", conf, CONF_winclass);
 }
 
-void do_defaults(char *session, Config * cfg)
+void do_defaults(char *session, Conf *conf)
 {
-    load_settings(session, cfg);
+    load_settings(session, conf);
 }
 
 static int sessioncmp(const void *av, const void *bv)
diff --git a/ssh.c b/ssh.c
index 017b48b..e3b8a33 100644 (file)
--- a/ssh.c
+++ b/ssh.c
@@ -884,12 +884,26 @@ struct ssh_tag {
     struct Packet *(*s_rdpkt) (Ssh ssh, unsigned char **data, int *datalen);
 
     /*
-     * We maintain a full _copy_ of a Config structure here, not
-     * merely a pointer to it. That way, when we're passed a new
-     * one for reconfiguration, we can check the differences and
-     * potentially reconfigure port forwardings etc in mid-session.
+     * We maintain our own copy of a Conf structure here. That way,
+     * when we're passed a new one for reconfiguration, we can check
+     * the differences and potentially reconfigure port forwardings
+     * etc in mid-session.
      */
-    Config cfg;
+    Conf *conf;
+
+    /*
+     * Values cached out of conf so as to avoid the tree234 lookup
+     * cost every time they're used.
+     */
+    int logomitdata;
+
+    /*
+     * Dynamically allocated username string created during SSH
+     * login. Stored in here rather than in the coroutine state so
+     * that it'll be reliably freed if we shut down the SSH session
+     * at some unexpected moment.
+     */
+    char *username;
 
     /*
      * Used to transfer data back from async callbacks.
@@ -978,13 +992,13 @@ static void logeventf(Ssh ssh, const char *fmt, ...)
 
 static void dont_log_password(Ssh ssh, struct Packet *pkt, int blanktype)
 {
-    if (ssh->cfg.logomitpass)
+    if (conf_get_int(ssh->conf, CONF_logomitpass))
        pkt->logmode = blanktype;
 }
 
 static void dont_log_data(Ssh ssh, struct Packet *pkt, int blanktype)
 {
-    if (ssh->cfg.logomitdata)
+    if (ssh->logomitdata)
        pkt->logmode = blanktype;
 }
 
@@ -993,26 +1007,27 @@ static void end_log_omission(Ssh ssh, struct Packet *pkt)
     pkt->logmode = PKTLOG_EMIT;
 }
 
-/* Helper function for common bits of parsing cfg.ttymodes. */
-static void parse_ttymodes(Ssh ssh, char *modes,
+/* Helper function for common bits of parsing ttymodes. */
+static void parse_ttymodes(Ssh ssh,
                           void (*do_mode)(void *data, char *mode, char *val),
                           void *data)
 {
-    while (*modes) {
-       char *t = strchr(modes, '\t');
-       char *m = snewn(t-modes+1, char);
-       char *val;
-       strncpy(m, modes, t-modes);
-       m[t-modes] = '\0';
-       if (*(t+1) == 'A')
-           val = get_ttymode(ssh->frontend, m);
+    char *key, *val;
+
+    for (val = conf_get_str_strs(ssh->conf, CONF_ttymodes, NULL, &key);
+        val != NULL;
+        val = conf_get_str_strs(ssh->conf, CONF_ttymodes, key, &key)) {
+       /*
+        * val[0] is either 'V', indicating that an explicit value
+        * follows it, or 'A' indicating that we should pass the
+        * value through from the local environment via get_ttymode.
+        */
+       if (val[0] == 'A')
+           val = get_ttymode(ssh->frontend, key);
        else
-           val = dupstr(t+2);
+           val++;                     /* skip the 'V' */
        if (val)
-           do_mode(data, m, val);
-       sfree(m);
-       sfree(val);
-       modes += strlen(modes) + 1;
+           do_mode(data, key, val);
     }
 }
 
@@ -1300,7 +1315,7 @@ static struct Packet *ssh1_rdpkt(Ssh ssh, unsigned char **data, int *datalen)
     if (ssh->logctx) {
        int nblanks = 0;
        struct logblank_t blank;
-       if (ssh->cfg.logomitdata) {
+       if (ssh->logomitdata) {
            int do_blank = FALSE, blank_prefix = 0;
            /* "Session data" packets - omit the data field */
            if ((st->pktin->type == SSH1_SMSG_STDOUT_DATA) ||
@@ -1533,7 +1548,7 @@ static struct Packet *ssh2_rdpkt(Ssh ssh, unsigned char **data, int *datalen)
     if (ssh->logctx) {
        int nblanks = 0;
        struct logblank_t blank;
-       if (ssh->cfg.logomitdata) {
+       if (ssh->logomitdata) {
            int do_blank = FALSE, blank_prefix = 0;
            /* "Session data" packets - omit the data field */
            if (st->pktin->type == SSH2_MSG_CHANNEL_DATA) {
@@ -2417,8 +2432,8 @@ static void ssh_detect_bugs(Ssh ssh, char *vstring)
      *    with SSH1_MSG_IGNOREs -- but this string never seems to change,
      *    so we can't distinguish them.
      */
-    if (ssh->cfg.sshbug_ignore1 == FORCE_ON ||
-       (ssh->cfg.sshbug_ignore1 == AUTO &&
+    if (conf_get_int(ssh->conf, CONF_sshbug_ignore1) == FORCE_ON ||
+       (conf_get_int(ssh->conf, CONF_sshbug_ignore1) == AUTO &&
         (!strcmp(imp, "1.2.18") || !strcmp(imp, "1.2.19") ||
          !strcmp(imp, "1.2.20") || !strcmp(imp, "1.2.21") ||
          !strcmp(imp, "1.2.22") || !strcmp(imp, "Cisco-1.25") ||
@@ -2432,8 +2447,8 @@ static void ssh_detect_bugs(Ssh ssh, char *vstring)
        logevent("We believe remote version has SSH-1 ignore bug");
     }
 
-    if (ssh->cfg.sshbug_plainpw1 == FORCE_ON ||
-       (ssh->cfg.sshbug_plainpw1 == AUTO &&
+    if (conf_get_int(ssh->conf, CONF_sshbug_plainpw1) == FORCE_ON ||
+       (conf_get_int(ssh->conf, CONF_sshbug_plainpw1) == AUTO &&
         (!strcmp(imp, "Cisco-1.25") || !strcmp(imp, "OSU_1.4alpha3")))) {
        /*
         * These versions need a plain password sent; they can't
@@ -2444,8 +2459,8 @@ static void ssh_detect_bugs(Ssh ssh, char *vstring)
        logevent("We believe remote version needs a plain SSH-1 password");
     }
 
-    if (ssh->cfg.sshbug_rsa1 == FORCE_ON ||
-       (ssh->cfg.sshbug_rsa1 == AUTO &&
+    if (conf_get_int(ssh->conf, CONF_sshbug_rsa1) == FORCE_ON ||
+       (conf_get_int(ssh->conf, CONF_sshbug_rsa1) == AUTO &&
         (!strcmp(imp, "Cisco-1.25")))) {
        /*
         * These versions apparently have no clue whatever about
@@ -2456,8 +2471,8 @@ static void ssh_detect_bugs(Ssh ssh, char *vstring)
        logevent("We believe remote version can't handle SSH-1 RSA authentication");
     }
 
-    if (ssh->cfg.sshbug_hmac2 == FORCE_ON ||
-       (ssh->cfg.sshbug_hmac2 == AUTO &&
+    if (conf_get_int(ssh->conf, CONF_sshbug_hmac2) == FORCE_ON ||
+       (conf_get_int(ssh->conf, CONF_sshbug_hmac2) == AUTO &&
         !wc_match("* VShell", imp) &&
         (wc_match("2.1.0*", imp) || wc_match("2.0.*", imp) ||
          wc_match("2.2.0*", imp) || wc_match("2.3.0*", imp) ||
@@ -2469,8 +2484,8 @@ static void ssh_detect_bugs(Ssh ssh, char *vstring)
        logevent("We believe remote version has SSH-2 HMAC bug");
     }
 
-    if (ssh->cfg.sshbug_derivekey2 == FORCE_ON ||
-       (ssh->cfg.sshbug_derivekey2 == AUTO &&
+    if (conf_get_int(ssh->conf, CONF_sshbug_derivekey2) == FORCE_ON ||
+       (conf_get_int(ssh->conf, CONF_sshbug_derivekey2) == AUTO &&
         !wc_match("* VShell", imp) &&
         (wc_match("2.0.0*", imp) || wc_match("2.0.10*", imp) ))) {
        /*
@@ -2482,8 +2497,8 @@ static void ssh_detect_bugs(Ssh ssh, char *vstring)
        logevent("We believe remote version has SSH-2 key-derivation bug");
     }
 
-    if (ssh->cfg.sshbug_rsapad2 == FORCE_ON ||
-       (ssh->cfg.sshbug_rsapad2 == AUTO &&
+    if (conf_get_int(ssh->conf, CONF_sshbug_rsapad2) == FORCE_ON ||
+       (conf_get_int(ssh->conf, CONF_sshbug_rsapad2) == AUTO &&
         (wc_match("OpenSSH_2.[5-9]*", imp) ||
          wc_match("OpenSSH_3.[0-2]*", imp)))) {
        /*
@@ -2493,8 +2508,8 @@ static void ssh_detect_bugs(Ssh ssh, char *vstring)
        logevent("We believe remote version has SSH-2 RSA padding bug");
     }
 
-    if (ssh->cfg.sshbug_pksessid2 == FORCE_ON ||
-       (ssh->cfg.sshbug_pksessid2 == AUTO &&
+    if (conf_get_int(ssh->conf, CONF_sshbug_pksessid2) == FORCE_ON ||
+       (conf_get_int(ssh->conf, CONF_sshbug_pksessid2) == AUTO &&
         wc_match("OpenSSH_2.[0-2]*", imp))) {
        /*
         * These versions have the SSH-2 session-ID bug in
@@ -2504,8 +2519,8 @@ static void ssh_detect_bugs(Ssh ssh, char *vstring)
        logevent("We believe remote version has SSH-2 public-key-session-ID bug");
     }
 
-    if (ssh->cfg.sshbug_rekey2 == FORCE_ON ||
-       (ssh->cfg.sshbug_rekey2 == AUTO &&
+    if (conf_get_int(ssh->conf, CONF_sshbug_rekey2) == FORCE_ON ||
+       (conf_get_int(ssh->conf, CONF_sshbug_rekey2) == AUTO &&
         (wc_match("DigiSSH_2.0", imp) ||
          wc_match("OpenSSH_2.[0-4]*", imp) ||
          wc_match("OpenSSH_2.5.[0-3]*", imp) ||
@@ -2520,8 +2535,8 @@ static void ssh_detect_bugs(Ssh ssh, char *vstring)
        logevent("We believe remote version has SSH-2 rekey bug");
     }
 
-    if (ssh->cfg.sshbug_maxpkt2 == FORCE_ON ||
-       (ssh->cfg.sshbug_maxpkt2 == AUTO &&
+    if (conf_get_int(ssh->conf, CONF_sshbug_maxpkt2) == FORCE_ON ||
+       (conf_get_int(ssh->conf, CONF_sshbug_maxpkt2) == AUTO &&
         (wc_match("1.36_sshlib GlobalSCAPE", imp) ||
           wc_match("1.36 sshlib: GlobalScape", imp)))) {
        /*
@@ -2531,7 +2546,7 @@ static void ssh_detect_bugs(Ssh ssh, char *vstring)
        logevent("We believe remote version ignores SSH-2 maximum packet size");
     }
 
-    if (ssh->cfg.sshbug_ignore2 == FORCE_ON) {
+    if (conf_get_int(ssh->conf, CONF_sshbug_ignore2) == FORCE_ON) {
        /*
         * Servers that don't support SSH2_MSG_IGNORE. Currently,
         * none detected automatically.
@@ -2674,16 +2689,16 @@ static int do_ssh_init(Ssh ssh, unsigned char c)
     /* Anything greater or equal to "1.99" means protocol 2 is supported. */
     s->proto2 = ssh_versioncmp(s->version, "1.99") >= 0;
 
-    if (ssh->cfg.sshprot == 0 && !s->proto1) {
+    if (conf_get_int(ssh->conf, CONF_sshprot) == 0 && !s->proto1) {
        bombout(("SSH protocol version 1 required by user but not provided by server"));
        crStop(0);
     }
-    if (ssh->cfg.sshprot == 3 && !s->proto2) {
+    if (conf_get_int(ssh->conf, CONF_sshprot) == 3 && !s->proto2) {
        bombout(("SSH protocol version 2 required by user but not provided by server"));
        crStop(0);
     }
 
-    if (s->proto2 && (ssh->cfg.sshprot >= 2 || !s->proto1))
+    if (s->proto2 && (conf_get_int(ssh->conf, CONF_sshprot) >= 2 || !s->proto1))
        ssh->version = 2;
     else
        ssh->version = 1;
@@ -2691,7 +2706,7 @@ static int do_ssh_init(Ssh ssh, unsigned char c)
     logeventf(ssh, "Using SSH protocol version %d", ssh->version);
 
     /* Send the version string, if we haven't already */
-    if (ssh->cfg.sshprot != 3)
+    if (conf_get_int(ssh->conf, CONF_sshprot) != 3)
        ssh_send_verstring(ssh, s->version);
 
     if (ssh->version == 2) {
@@ -2723,7 +2738,7 @@ static int do_ssh_init(Ssh ssh, unsigned char c)
 
     update_specials_menu(ssh->frontend);
     ssh->state = SSH_STATE_BEFORE_SIZE;
-    ssh->pinger = pinger_new(&ssh->cfg, &ssh_backend, ssh);
+    ssh->pinger = pinger_new(ssh->conf, &ssh_backend, ssh);
 
     sfree(s->vstring);
 
@@ -2976,11 +2991,14 @@ static const char *connect_to_host(Ssh ssh, char *host, int port,
 
     SockAddr addr;
     const char *err;
-
-    if (*ssh->cfg.loghost) {
+    char *loghost;
+    int addressfamily, sshprot;
+    
+    loghost = conf_get_str(ssh->conf, CONF_loghost);
+    if (*loghost) {
        char *colon;
 
-       ssh->savedhost = dupstr(ssh->cfg.loghost);
+       ssh->savedhost = dupstr(loghost);
        ssh->savedport = 22;           /* default ssh port */
 
        /*
@@ -3005,11 +3023,11 @@ static const char *connect_to_host(Ssh ssh, char *host, int port,
     /*
      * Try to find host.
      */
+    addressfamily = conf_get_int(ssh->conf, CONF_addressfamily);
     logeventf(ssh, "Looking up host \"%s\"%s", host,
-             (ssh->cfg.addressfamily == ADDRTYPE_IPV4 ? " (IPv4)" :
-              (ssh->cfg.addressfamily == ADDRTYPE_IPV6 ? " (IPv6)" : "")));
-    addr = name_lookup(host, port, realhost, &ssh->cfg,
-                      ssh->cfg.addressfamily);
+             (addressfamily == ADDRTYPE_IPV4 ? " (IPv4)" :
+              (addressfamily == ADDRTYPE_IPV6 ? " (IPv6)" : "")));
+    addr = name_lookup(host, port, realhost, ssh->conf, addressfamily);
     if ((err = sk_addr_error(addr)) != NULL) {
        sk_addr_free(addr);
        return err;
@@ -3021,7 +3039,7 @@ static const char *connect_to_host(Ssh ssh, char *host, int port,
      */
     ssh->fn = &fn_table;
     ssh->s = new_connection(addr, *realhost, port,
-                           0, 1, nodelay, keepalive, (Plug) ssh, &ssh->cfg);
+                           0, 1, nodelay, keepalive, (Plug) ssh, ssh->conf);
     if ((err = sk_socket_error(ssh->s)) != NULL) {
        ssh->s = NULL;
        notify_remote_exit(ssh->frontend);
@@ -3032,9 +3050,10 @@ static const char *connect_to_host(Ssh ssh, char *host, int port,
      * If the SSH version number's fixed, set it now, and if it's SSH-2,
      * send the version string too.
      */
-    if (ssh->cfg.sshprot == 0)
+    sshprot = conf_get_int(ssh->conf, CONF_sshprot);
+    if (sshprot == 0)
        ssh->version = 1;
-    if (ssh->cfg.sshprot == 3) {
+    if (sshprot == 3) {
        ssh->version = 2;
        ssh_send_verstring(ssh, NULL);
     }
@@ -3042,9 +3061,9 @@ static const char *connect_to_host(Ssh ssh, char *host, int port,
     /*
      * loghost, if configured, overrides realhost.
      */
-    if (*ssh->cfg.loghost) {
+    if (*loghost) {
        sfree(*realhost);
-       *realhost = dupstr(ssh->cfg.loghost);
+       *realhost = dupstr(loghost);
     }
 
     return NULL;
@@ -3209,7 +3228,6 @@ static int do_ssh1_login(Ssh ssh, unsigned char *in, int inlen,
        int tis_auth_refused, ccard_auth_refused;
        unsigned char session_id[16];
        int cipher_type;
-       char username[100];
        void *publickey_blob;
        int publickey_bloblen;
        char *publickey_comment;
@@ -3226,6 +3244,7 @@ static int do_ssh1_login(Ssh ssh, unsigned char *in, int inlen,
        char *commentp;
        int commentlen;
         int dlgret;
+       Filename *keyfile;
     };
     crState(do_ssh1_login_state);
 
@@ -3365,7 +3384,8 @@ static int do_ssh1_login(Ssh ssh, unsigned char *in, int inlen,
        char *cipher_string = NULL;
        int i;
        for (i = 0; !cipher_chosen && i < CIPHER_MAX; i++) {
-           int next_cipher = ssh->cfg.ssh_cipherlist[i];
+           int next_cipher = conf_get_int_int(ssh->conf,
+                                              CONF_ssh_cipherlist, i);
            if (next_cipher == CIPHER_WARN) {
                /* If/when we choose a cipher, warn about it */
                warn = 1;
@@ -3480,14 +3500,13 @@ static int do_ssh1_login(Ssh ssh, unsigned char *in, int inlen,
 
     fflush(stdout); /* FIXME eh? */
     {
-       if (!get_remote_username(&ssh->cfg, s->username,
-                                sizeof(s->username))) {
+       if ((ssh->username = get_remote_username(ssh->conf)) == NULL) {
            int ret; /* need not be kept over crReturn */
            s->cur_prompt = new_prompts(ssh->frontend);
            s->cur_prompt->to_server = TRUE;
            s->cur_prompt->name = dupstr("SSH login name");
-           add_prompt(s->cur_prompt, dupstr("login as: "), TRUE,
-                      lenof(s->username)); 
+           /* 512 is an arbitrary upper limit on username size */
+           add_prompt(s->cur_prompt, dupstr("login as: "), TRUE, 512);
            ret = get_userpass_input(s->cur_prompt, NULL, 0);
            while (ret < 0) {
                ssh->send_ok = 1;
@@ -3503,14 +3522,13 @@ static int do_ssh1_login(Ssh ssh, unsigned char *in, int inlen,
                ssh_disconnect(ssh, "No username provided", NULL, 0, TRUE);
                crStop(0);
            }
-           memcpy(s->username, s->cur_prompt->prompts[0]->result,
-                  lenof(s->username));
+           ssh->username = dupstr(s->cur_prompt->prompts[0]->result);
            free_prompts(s->cur_prompt);
        }
 
-       send_packet(ssh, SSH1_CMSG_USER, PKT_STR, s->username, PKT_END);
+       send_packet(ssh, SSH1_CMSG_USER, PKT_STR, ssh->username, PKT_END);
        {
-           char *userlog = dupprintf("Sent username \"%s\"", s->username);
+           char *userlog = dupprintf("Sent username \"%s\"", ssh->username);
            logevent(userlog);
            if (flags & FLAG_INTERACTIVE &&
                (!((flags & FLAG_STDERR) && (flags & FLAG_VERBOSE)))) {
@@ -3533,24 +3551,25 @@ static int do_ssh1_login(Ssh ssh, unsigned char *in, int inlen,
     /*
      * Load the public half of any configured keyfile for later use.
      */
-    if (!filename_is_null(ssh->cfg.keyfile)) {
+    s->keyfile = conf_get_filename(ssh->conf, CONF_keyfile);
+    if (!filename_is_null(*s->keyfile)) {
        int keytype;
        logeventf(ssh, "Reading private key file \"%.150s\"",
-                 filename_to_str(&ssh->cfg.keyfile));
-       keytype = key_type(&ssh->cfg.keyfile);
+                 filename_to_str(s->keyfile));
+       keytype = key_type(s->keyfile);
        if (keytype == SSH_KEYTYPE_SSH1) {
            const char *error;
-           if (rsakey_pubblob(&ssh->cfg.keyfile,
+           if (rsakey_pubblob(s->keyfile,
                               &s->publickey_blob, &s->publickey_bloblen,
                               &s->publickey_comment, &error)) {
-               s->publickey_encrypted = rsakey_encrypted(&ssh->cfg.keyfile,
+               s->publickey_encrypted = rsakey_encrypted(s->keyfile,
                                                          NULL);
            } else {
                char *msgbuf;
                logeventf(ssh, "Unable to load private key (%s)", error);
                msgbuf = dupprintf("Unable to load private key file "
                                   "\"%.150s\" (%s)\r\n",
-                                  filename_to_str(&ssh->cfg.keyfile),
+                                  filename_to_str(s->keyfile),
                                   error);
                c_write_str(ssh, msgbuf);
                sfree(msgbuf);
@@ -3562,7 +3581,7 @@ static int do_ssh1_login(Ssh ssh, unsigned char *in, int inlen,
                      key_type_to_str(keytype));
            msgbuf = dupprintf("Unable to use key file \"%.150s\""
                               " (%s)\r\n",
-                              filename_to_str(&ssh->cfg.keyfile),
+                              filename_to_str(s->keyfile),
                               key_type_to_str(keytype));
            c_write_str(ssh, msgbuf);
            sfree(msgbuf);
@@ -3574,7 +3593,7 @@ static int do_ssh1_login(Ssh ssh, unsigned char *in, int inlen,
     while (pktin->type == SSH1_SMSG_FAILURE) {
        s->pwpkt_type = SSH1_CMSG_AUTH_PASSWORD;
 
-       if (ssh->cfg.tryagent && agent_exists() && !s->tried_agent) {
+       if (conf_get_int(ssh->conf, CONF_tryagent) && agent_exists() && !s->tried_agent) {
            /*
             * Attempt RSA authentication using Pageant.
             */
@@ -3758,8 +3777,9 @@ static int do_ssh1_login(Ssh ssh, unsigned char *in, int inlen,
            int got_passphrase; /* need not be kept over crReturn */
            if (flags & FLAG_VERBOSE)
                c_write_str(ssh, "Trying public key authentication.\r\n");
+           s->keyfile = conf_get_filename(ssh->conf, CONF_keyfile);
            logeventf(ssh, "Trying public key \"%s\"",
-                     filename_to_str(&ssh->cfg.keyfile));
+                     filename_to_str(s->keyfile));
            s->tried_publickey = 1;
            got_passphrase = FALSE;
            while (!got_passphrase) {
@@ -3801,7 +3821,8 @@ static int do_ssh1_login(Ssh ssh, unsigned char *in, int inlen,
                /*
                 * Try decrypting key with passphrase.
                 */
-               ret = loadrsakey(&ssh->cfg.keyfile, &s->key, passphrase,
+               s->keyfile = conf_get_filename(ssh->conf, CONF_keyfile);
+               ret = loadrsakey(s->keyfile, &s->key, passphrase,
                                 &error);
                if (passphrase) {
                    memset(passphrase, 0, strlen(passphrase));
@@ -3812,7 +3833,7 @@ static int do_ssh1_login(Ssh ssh, unsigned char *in, int inlen,
                    got_passphrase = TRUE;
                } else if (ret == 0) {
                    c_write_str(ssh, "Couldn't load private key from ");
-                   c_write_str(ssh, filename_to_str(&ssh->cfg.keyfile));
+                   c_write_str(ssh, filename_to_str(s->keyfile));
                    c_write_str(ssh, " (");
                    c_write_str(ssh, error);
                    c_write_str(ssh, ").\r\n");
@@ -3895,7 +3916,7 @@ static int do_ssh1_login(Ssh ssh, unsigned char *in, int inlen,
         */
        s->cur_prompt = new_prompts(ssh->frontend);
 
-       if (ssh->cfg.try_tis_auth &&
+       if (conf_get_int(ssh->conf, CONF_try_tis_auth) &&
            (s->supported_auths_mask & (1 << SSH1_AUTH_TIS)) &&
            !s->tis_auth_refused) {
            s->pwpkt_type = SSH1_CMSG_AUTH_TIS_RESPONSE;
@@ -3938,7 +3959,7 @@ static int do_ssh1_login(Ssh ssh, unsigned char *in, int inlen,
                sfree(instr_suf);
            }
        }
-       if (ssh->cfg.try_tis_auth &&
+       if (conf_get_int(ssh->conf, CONF_try_tis_auth) &&
            (s->supported_auths_mask & (1 << SSH1_AUTH_CCARD)) &&
            !s->ccard_auth_refused) {
            s->pwpkt_type = SSH1_CMSG_AUTH_CCARD_RESPONSE;
@@ -3988,8 +4009,8 @@ static int do_ssh1_login(Ssh ssh, unsigned char *in, int inlen,
            }
            s->cur_prompt->to_server = TRUE;
            s->cur_prompt->name = dupstr("SSH password");
-           add_prompt(s->cur_prompt, dupprintf("%.90s@%.90s's password: ",
-                                               s->username, ssh->savedhost),
+           add_prompt(s->cur_prompt, dupprintf("%s@%s's password: ",
+                                               ssh->username, ssh->savedhost),
                       FALSE, SSH_MAX_PASSWORD_LEN);
        }
 
@@ -4365,11 +4386,11 @@ static void ssh_rportfwd_succfail(Ssh ssh, struct Packet *pktin, void *ctx)
     }
 }
 
-static void ssh_setup_portfwd(Ssh ssh, const Config *cfg)
+static void ssh_setup_portfwd(Ssh ssh, Conf *conf)
 {
-    const char *portfwd_strptr = cfg->portfwd;
     struct ssh_portfwd *epf;
     int i;
+    char *key, *val;
 
     if (!ssh->portfwds) {
        ssh->portfwds = newtree234(ssh_portcmp);
@@ -4387,80 +4408,34 @@ static void ssh_setup_portfwd(Ssh ssh, const Config *cfg)
            epf->status = DESTROY;
     }
 
-    while (*portfwd_strptr) {
+    for (val = conf_get_str_strs(conf, CONF_portfwd, NULL, &key);
+        val != NULL;
+        val = conf_get_str_strs(conf, CONF_portfwd, key, &key)) {
+       char *kp, *kp2, *vp, *vp2;
        char address_family, type;
        int sport,dport,sserv,dserv;
-       char sports[256], dports[256], saddr[256], host[256];
-       int n;
+       char *sports, *dports, *saddr, *host;
+
+       kp = key;
 
        address_family = 'A';
        type = 'L';
-       if (*portfwd_strptr == 'A' ||
-           *portfwd_strptr == '4' ||
-           *portfwd_strptr == '6')
-           address_family = *portfwd_strptr++;
-       if (*portfwd_strptr == 'L' ||
-           *portfwd_strptr == 'R' ||
-           *portfwd_strptr == 'D')
-           type = *portfwd_strptr++;
-
-       saddr[0] = '\0';
-
-       n = 0;
-       while (*portfwd_strptr && *portfwd_strptr != '\t') {
-           if (*portfwd_strptr == ':') {
-               /*
-                * We've seen a colon in the middle of the
-                * source port number. This means that
-                * everything we've seen until now is the
-                * source _address_, so we'll move it into
-                * saddr and start sports from the beginning
-                * again.
-                */
-               portfwd_strptr++;
-               sports[n] = '\0';
-               if (ssh->version == 1 && type == 'R') {
-                   logeventf(ssh, "SSH-1 cannot handle remote source address "
-                             "spec \"%s\"; ignoring", sports);
-               } else
-                   strcpy(saddr, sports);
-               n = 0;
-           }
-           if (n < lenof(sports)-1) sports[n++] = *portfwd_strptr++;
-       }
-       sports[n] = 0;
-       if (type != 'D') {
-           if (*portfwd_strptr == '\t')
-               portfwd_strptr++;
-           n = 0;
-           while (*portfwd_strptr && *portfwd_strptr != ':') {
-               if (n < lenof(host)-1) host[n++] = *portfwd_strptr++;
-           }
-           host[n] = 0;
-           if (*portfwd_strptr == ':')
-               portfwd_strptr++;
-           n = 0;
-           while (*portfwd_strptr) {
-               if (n < lenof(dports)-1) dports[n++] = *portfwd_strptr++;
-           }
-           dports[n] = 0;
-           portfwd_strptr++;
-           dport = atoi(dports);
-           dserv = 0;
-           if (dport == 0) {
-               dserv = 1;
-               dport = net_service_lookup(dports);
-               if (!dport) {
-                   logeventf(ssh, "Service lookup failed for destination"
-                             " port \"%s\"", dports);
-               }
-           }
+       if (*kp == 'A' || *kp == '4' || *kp == '6')
+           address_family = *kp++;
+       if (*kp == 'L' || *kp == 'R')
+           type = *kp++;
+
+       if ((kp2 = strchr(kp, ':')) != NULL) {
+           /*
+            * There's a colon in the middle of the source port
+            * string, which means that the part before it is
+            * actually a source address.
+            */
+           saddr = dupprintf("%.*s", (int)(kp2 - kp), kp);
+           sports = kp2+1;
        } else {
-           while (*portfwd_strptr) portfwd_strptr++;
-           host[0] = 0;
-           dports[0] = 0;
-           dport = dserv = -1;
-           portfwd_strptr++;          /* eat the NUL and move to next one */
+           saddr = NULL;
+           sports = kp;
        }
        sport = atoi(sports);
        sserv = 0;
@@ -4472,16 +4447,44 @@ static void ssh_setup_portfwd(Ssh ssh, const Config *cfg)
                          " port \"%s\"", sports);
            }
        }
+
+       if (type == 'L' && !strcmp(val, "D")) {
+            /* dynamic forwarding */
+           host = NULL;
+           dports = NULL;
+           dport = -1;
+           dserv = 0;
+            type = 'D';
+        } else {
+            /* ordinary forwarding */
+           vp = val;
+           vp2 = vp + strcspn(vp, ":");
+           host = dupprintf("%.*s", (int)(vp2 - vp), vp);
+           if (vp2)
+               vp2++;
+           dports = vp2;
+           dport = atoi(dports);
+           dserv = 0;
+           if (dport == 0) {
+               dserv = 1;
+               dport = net_service_lookup(dports);
+               if (!dport) {
+                   logeventf(ssh, "Service lookup failed for destination"
+                             " port \"%s\"", dports);
+               }
+           }
+       }
+
        if (sport && dport) {
            /* Set up a description of the source port. */
            struct ssh_portfwd *pfrec, *epfrec;
 
            pfrec = snew(struct ssh_portfwd);
            pfrec->type = type;
-           pfrec->saddr = *saddr ? dupstr(saddr) : NULL;
+           pfrec->saddr = saddr;
            pfrec->sserv = sserv ? dupstr(sports) : NULL;
            pfrec->sport = sport;
-           pfrec->daddr = *host ? dupstr(host) : NULL;
+           pfrec->daddr = host;
            pfrec->dserv = dserv ? dupstr(dports) : NULL;
            pfrec->dport = dport;
            pfrec->local = NULL;
@@ -4509,6 +4512,9 @@ static void ssh_setup_portfwd(Ssh ssh, const Config *cfg)
            } else {
                pfrec->status = CREATE;
            }
+       } else {
+           sfree(saddr);
+           sfree(host);
        }
     }
 
@@ -4562,8 +4568,8 @@ static void ssh_setup_portfwd(Ssh ssh, const Config *cfg)
                    ssh2_pkt_addbool(pktout, 0);/* _don't_ want reply */
                    if (epf->saddr) {
                        ssh2_pkt_addstring(pktout, epf->saddr);
-                   } else if (ssh->cfg.rport_acceptall) {
-                       /* XXX: ssh->cfg.rport_acceptall may not represent
+                   } else if (conf_get_int(conf, CONF_rport_acceptall)) {
+                       /* XXX: rport_acceptall may not represent
                         * what was used to open the original connection,
                         * since it's reconfigurable. */
                        ssh2_pkt_addstring(pktout, "0.0.0.0");
@@ -4612,7 +4618,7 @@ static void ssh_setup_portfwd(Ssh ssh, const Config *cfg)
            if (epf->type == 'L') {
                const char *err = pfd_addforward(epf->daddr, epf->dport,
                                                 epf->saddr, epf->sport,
-                                                ssh, cfg,
+                                                ssh, conf,
                                                 &epf->local,
                                                 epf->addressfamily);
 
@@ -4624,7 +4630,7 @@ static void ssh_setup_portfwd(Ssh ssh, const Config *cfg)
            } else if (epf->type == 'D') {
                const char *err = pfd_addforward(NULL, -1,
                                                 epf->saddr, epf->sport,
-                                                ssh, cfg,
+                                                ssh, conf,
                                                 &epf->local,
                                                 epf->addressfamily);
 
@@ -4680,7 +4686,7 @@ static void ssh_setup_portfwd(Ssh ssh, const Config *cfg)
                        ssh2_pkt_addbool(pktout, 1);/* want reply */
                        if (epf->saddr) {
                            ssh2_pkt_addstring(pktout, epf->saddr);
-                       } else if (cfg->rport_acceptall) {
+                       } else if (conf_get_int(conf, CONF_rport_acceptall)) {
                            ssh2_pkt_addstring(pktout, "0.0.0.0");
                        } else {
                            ssh2_pkt_addstring(pktout, "127.0.0.1");
@@ -4736,7 +4742,7 @@ static void ssh1_smsg_x11_open(Ssh ssh, struct Packet *pktin)
        c->ssh = ssh;
 
        if (x11_init(&c->u.x11.s, ssh->x11disp, c,
-                    NULL, -1, &ssh->cfg) != NULL) {
+                    NULL, -1, ssh->conf) != NULL) {
            logevent("Opening X11 forward connection failed");
            sfree(c);
            send_packet(ssh, SSH1_MSG_CHANNEL_OPEN_FAILURE,
@@ -4822,7 +4828,7 @@ static void ssh1_msg_port_open(Ssh ssh, struct Packet *pktin)
        logeventf(ssh, "Received remote port open request for %s:%d",
                  pf.dhost, port);
        e = pfd_newconnect(&c->u.pfd.s, pf.dhost, port,
-                          c, &ssh->cfg, pfp->pfrec->addressfamily);
+                          c, ssh->conf, pfp->pfrec->addressfamily);
        if (e != NULL) {
            logeventf(ssh, "Port open failed: %s", e);
            sfree(c);
@@ -5054,7 +5060,7 @@ static void do_ssh1_connection(Ssh ssh, unsigned char *in, int inlen,
     ssh->packet_dispatch[SSH1_MSG_CHANNEL_DATA] = ssh1_msg_channel_data;
     ssh->packet_dispatch[SSH1_SMSG_EXIT_STATUS] = ssh1_smsg_exit_status;
 
-    if (ssh->cfg.agentfwd && agent_exists()) {
+    if (conf_get_int(ssh->conf, CONF_agentfwd) && agent_exists()) {
        logevent("Requesting agent forwarding");
        send_packet(ssh, SSH1_CMSG_AGENT_REQUEST_FORWARDING, PKT_END);
        do {
@@ -5073,9 +5079,9 @@ static void do_ssh1_connection(Ssh ssh, unsigned char *in, int inlen,
        }
     }
 
-    if (ssh->cfg.x11_forward &&
-       (ssh->x11disp = x11_setup_display(ssh->cfg.x11_display,
-                                         ssh->cfg.x11_auth, &ssh->cfg))) {
+    if (conf_get_int(ssh->conf, CONF_x11_forward) &&
+       (ssh->x11disp = x11_setup_display(conf_get_str(ssh->conf, CONF_x11_display),
+                                         conf_get_int(ssh->conf, CONF_x11_auth), ssh->conf))) {
        logevent("Requesting X11 forwarding");
        /*
         * Note that while we blank the X authentication data here, we don't
@@ -5116,24 +5122,23 @@ static void do_ssh1_connection(Ssh ssh, unsigned char *in, int inlen,
        }
     }
 
-    ssh_setup_portfwd(ssh, &ssh->cfg);
+    ssh_setup_portfwd(ssh, ssh->conf);
     ssh->packet_dispatch[SSH1_MSG_PORT_OPEN] = ssh1_msg_port_open;
 
-    if (!ssh->cfg.nopty) {
+    if (!conf_get_int(ssh->conf, CONF_nopty)) {
        struct Packet *pkt;
        /* Unpick the terminal-speed string. */
        /* XXX perhaps we should allow no speeds to be sent. */
        ssh->ospeed = 38400; ssh->ispeed = 38400; /* last-resort defaults */
-       sscanf(ssh->cfg.termspeed, "%d,%d", &ssh->ospeed, &ssh->ispeed);
+       sscanf(conf_get_str(ssh->conf, CONF_termspeed), "%d,%d", &ssh->ospeed, &ssh->ispeed);
        /* Send the pty request. */
        pkt = ssh1_pkt_init(SSH1_CMSG_REQUEST_PTY);
-       ssh_pkt_addstring(pkt, ssh->cfg.termtype);
+       ssh_pkt_addstring(pkt, conf_get_str(ssh->conf, CONF_termtype));
        ssh_pkt_adduint32(pkt, ssh->term_height);
        ssh_pkt_adduint32(pkt, ssh->term_width);
        ssh_pkt_adduint32(pkt, 0); /* width in pixels */
        ssh_pkt_adduint32(pkt, 0); /* height in pixels */
-       parse_ttymodes(ssh, ssh->cfg.ttymodes,
-                      ssh1_send_ttymode, (void *)pkt);
+       parse_ttymodes(ssh, ssh1_send_ttymode, (void *)pkt);
        ssh_pkt_addbyte(pkt, SSH1_TTY_OP_ISPEED);
        ssh_pkt_adduint32(pkt, ssh->ispeed);
        ssh_pkt_addbyte(pkt, SSH1_TTY_OP_OSPEED);
@@ -5158,7 +5163,7 @@ static void do_ssh1_connection(Ssh ssh, unsigned char *in, int inlen,
        ssh->editing = ssh->echoing = 1;
     }
 
-    if (ssh->cfg.compression) {
+    if (conf_get_int(ssh->conf, CONF_compression)) {
        send_packet(ssh, SSH1_CMSG_REQUEST_COMPRESSION, PKT_INT, 6, PKT_END);
        do {
            crReturnV;
@@ -5186,12 +5191,11 @@ static void do_ssh1_connection(Ssh ssh, unsigned char *in, int inlen,
      * exists, we fall straight back to that.
      */
     {
-       char *cmd = ssh->cfg.remote_cmd_ptr;
-
-       if (!cmd) cmd = ssh->cfg.remote_cmd;
+       char *cmd = conf_get_str(ssh->conf, CONF_remote_cmd);
        
-       if (ssh->cfg.ssh_subsys && ssh->cfg.remote_cmd_ptr2) {
-           cmd = ssh->cfg.remote_cmd_ptr2;
+       if (conf_get_int(ssh->conf, CONF_ssh_subsys) &&
+           conf_get_str(ssh->conf, CONF_remote_cmd2)) {
+           cmd = conf_get_str(ssh->conf, CONF_remote_cmd2);
            ssh->fallback_cmd = TRUE;
        }
        if (*cmd)
@@ -5462,7 +5466,7 @@ static int do_ssh2_transport(Ssh ssh, void *vin, int inlen,
         */
        s->n_preferred_kex = 0;
        for (i = 0; i < KEX_MAX; i++) {
-           switch (ssh->cfg.ssh_kexlist[i]) {
+           switch (conf_get_int_int(ssh->conf, CONF_ssh_kexlist, i)) {
              case KEX_DHGEX:
                s->preferred_kex[s->n_preferred_kex++] =
                    &ssh_diffiehellman_gex;
@@ -5494,12 +5498,12 @@ static int do_ssh2_transport(Ssh ssh, void *vin, int inlen,
         */
        s->n_preferred_ciphers = 0;
        for (i = 0; i < CIPHER_MAX; i++) {
-           switch (ssh->cfg.ssh_cipherlist[i]) {
+           switch (conf_get_int_int(ssh->conf, CONF_ssh_cipherlist, i)) {
              case CIPHER_BLOWFISH:
                s->preferred_ciphers[s->n_preferred_ciphers++] = &ssh2_blowfish;
                break;
              case CIPHER_DES:
-               if (ssh->cfg.ssh2_des_cbc) {
+               if (conf_get_int(ssh->conf, CONF_ssh2_des_cbc)) {
                    s->preferred_ciphers[s->n_preferred_ciphers++] = &ssh2_des;
                }
                break;
@@ -5525,7 +5529,7 @@ static int do_ssh2_transport(Ssh ssh, void *vin, int inlen,
        /*
         * Set up preferred compression.
         */
-       if (ssh->cfg.compression)
+       if (conf_get_int(ssh->conf, CONF_compression))
            s->preferred_comp = &ssh_zlib;
        else
            s->preferred_comp = &ssh_comp_none;
@@ -6321,8 +6325,8 @@ static int do_ssh2_transport(Ssh ssh, void *vin, int inlen,
      */
     ssh->kex_in_progress = FALSE;
     ssh->last_rekey = GETTICKCOUNT();
-    if (ssh->cfg.ssh_rekey_time != 0)
-       ssh->next_rekey = schedule_timer(ssh->cfg.ssh_rekey_time*60*TICKSPERSEC,
+    if (conf_get_int(ssh->conf, CONF_ssh_rekey_time) != 0)
+       ssh->next_rekey = schedule_timer(conf_get_int(ssh->conf, CONF_ssh_rekey_time)*60*TICKSPERSEC,
                                         ssh2_timer, ssh);
 
     /*
@@ -6403,9 +6407,9 @@ static int do_ssh2_transport(Ssh ssh, void *vin, int inlen,
              * hit the event log _too_ often. */
             ssh->outgoing_data_size = 0;
             ssh->incoming_data_size = 0;
-            if (ssh->cfg.ssh_rekey_time != 0) {
+            if (conf_get_int(ssh->conf, CONF_ssh_rekey_time) != 0) {
                 ssh->next_rekey =
-                    schedule_timer(ssh->cfg.ssh_rekey_time*60*TICKSPERSEC,
+                    schedule_timer(conf_get_int(ssh->conf, CONF_ssh_rekey_time)*60*TICKSPERSEC,
                                    ssh2_timer, ssh);
             }
             goto wait_for_rekey;       /* this is still utterly horrid */
@@ -6511,7 +6515,7 @@ static void ssh2_channel_init(struct ssh_channel *c)
     c->pending_close = FALSE;
     c->throttling_conn = FALSE;
     c->v.v2.locwindow = c->v.v2.locmaxwin = c->v.v2.remlocwin =
-       ssh->cfg.ssh_simple ? OUR_V2_BIGWIN : OUR_V2_WINSIZE;
+       conf_get_int(ssh->conf, CONF_ssh_simple) ? OUR_V2_BIGWIN : OUR_V2_WINSIZE;
     c->v.v2.winadj_head = c->v.v2.winadj_tail = NULL;
     c->v.v2.throttle_state = UNTHROTTLED;
     bufchain_init(&c->v.v2.outbuffer);
@@ -6796,7 +6800,7 @@ static void ssh2_msg_channel_data(Ssh ssh, struct Packet *pktin)
         * throttle the whole channel.
         */
        if ((bufsize > c->v.v2.locmaxwin ||
-            (ssh->cfg.ssh_simple && bufsize > 0)) &&
+            (conf_get_int(ssh->conf, CONF_ssh_simple) && bufsize > 0)) &&
            !c->throttling_conn) {
            c->throttling_conn = 1;
            ssh_throttle_conn(ssh, +1);
@@ -6871,7 +6875,7 @@ static void ssh2_msg_channel_close(Ssh ssh, struct Packet *pktin)
      * (This is only our termination condition if we're
      * not running in -N mode.)
      */
-    if (!ssh->cfg.ssh_no_shell && count234(ssh->channels) == 0) {
+    if (!conf_get_int(ssh->conf, CONF_ssh_no_shell) && count234(ssh->channels) == 0) {
        /*
         * We used to send SSH_MSG_DISCONNECT here,
         * because I'd believed that _every_ conforming
@@ -7186,7 +7190,7 @@ static void ssh2_msg_channel_open(Ssh ssh, struct Packet *pktin)
        if (!ssh->X11_fwd_enabled)
            error = "X11 forwarding is not enabled";
        else if ((x11err = x11_init(&c->u.x11.s, ssh->x11disp, c,
-                                   addrstr, peerport, &ssh->cfg)) != NULL) {
+                                   addrstr, peerport, ssh->conf)) != NULL) {
            logeventf(ssh, "Local X11 connection failed: %s", x11err);
            error = "Unable to open an X11 connection";
        } else {
@@ -7213,7 +7217,7 @@ static void ssh2_msg_channel_open(Ssh ssh, struct Packet *pktin)
            const char *e = pfd_newconnect(&c->u.pfd.s,
                                           realpf->dhost,
                                           realpf->dport, c,
-                                          &ssh->cfg,
+                                          ssh->conf,
                                           realpf->pfrec->addressfamily);
            logeventf(ssh, "Attempting to forward remote port to "
                      "%s:%d", realpf->dhost, realpf->dport);
@@ -7269,7 +7273,7 @@ static void ssh2_msg_channel_open(Ssh ssh, struct Packet *pktin)
 static void ssh2_msg_userauth_banner(Ssh ssh, struct Packet *pktin)
 {
     /* Arbitrary limit to prevent unbounded inflation of buffer */
-    if (ssh->cfg.ssh_show_banner &&
+    if (conf_get_int(ssh->conf, CONF_ssh_show_banner) &&
        bufchain_size(&ssh->banner) <= 131072) {
        char *banner = NULL;
        int size = 0;
@@ -7327,7 +7331,7 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen,
        int we_are_in, userauth_success;
        prompts_t *cur_prompt;
        int num_prompts;
-       char username[100];
+       char *username;
        char *password;
        int got_username;
        void *publickey_blob;
@@ -7346,6 +7350,7 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen,
        int try_send;
        int num_env, env_left, env_ok;
        struct Packet *pktout;
+       Filename *keyfile;
 #ifndef NO_GSSAPI
        struct ssh_gss_library *gsslib;
        Ssh_gss_ctx gss_ctx;
@@ -7365,7 +7370,7 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen,
     s->tried_gssapi = FALSE;
 #endif
 
-    if (!ssh->cfg.ssh_no_userauth) {
+    if (!conf_get_int(ssh->conf, CONF_ssh_no_userauth)) {
        /*
         * Request userauth protocol, and await a response to it.
         */
@@ -7408,28 +7413,29 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen,
         * Load the public half of any configured public key file
         * for later use.
         */
-       if (!filename_is_null(ssh->cfg.keyfile)) {
+       s->keyfile = conf_get_filename(ssh->conf, CONF_keyfile);
+       if (!filename_is_null(*s->keyfile)) {
            int keytype;
            logeventf(ssh, "Reading private key file \"%.150s\"",
-                     filename_to_str(&ssh->cfg.keyfile));
-           keytype = key_type(&ssh->cfg.keyfile);
+                     filename_to_str(s->keyfile));
+           keytype = key_type(s->keyfile);
            if (keytype == SSH_KEYTYPE_SSH2) {
                const char *error;
                s->publickey_blob =
-                   ssh2_userkey_loadpub(&ssh->cfg.keyfile,
+                   ssh2_userkey_loadpub(s->keyfile,
                                         &s->publickey_algorithm,
                                         &s->publickey_bloblen, 
                                         &s->publickey_comment, &error);
                if (s->publickey_blob) {
                    s->publickey_encrypted =
-                       ssh2_userkey_encrypted(&ssh->cfg.keyfile, NULL);
+                       ssh2_userkey_encrypted(s->keyfile, NULL);
                } else {
                    char *msgbuf;
                    logeventf(ssh, "Unable to load private key (%s)", 
                              error);
                    msgbuf = dupprintf("Unable to load private key file "
                                       "\"%.150s\" (%s)\r\n",
-                                      filename_to_str(&ssh->cfg.keyfile),
+                                      filename_to_str(s->keyfile),
                                       error);
                    c_write_str(ssh, msgbuf);
                    sfree(msgbuf);
@@ -7440,7 +7446,7 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen,
                          key_type_to_str(keytype));
                msgbuf = dupprintf("Unable to use key file \"%.150s\""
                                   " (%s)\r\n",
-                                  filename_to_str(&ssh->cfg.keyfile),
+                                  filename_to_str(s->keyfile),
                                   key_type_to_str(keytype));
                c_write_str(ssh, msgbuf);
                sfree(msgbuf);
@@ -7455,7 +7461,7 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen,
        s->nkeys = 0;
        s->agent_response = NULL;
        s->pkblob_in_agent = NULL;
-       if (ssh->cfg.tryagent && agent_exists()) {
+       if (conf_get_int(ssh->conf, CONF_tryagent) && agent_exists()) {
 
            void *r;
 
@@ -7538,26 +7544,24 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen,
      *    the username they will want to be able to get back and
      *    retype it!
      */
-    s->username[0] = '\0';
     s->got_username = FALSE;
     while (!s->we_are_in) {
        /*
         * Get a username.
         */
-       if (s->got_username && !ssh->cfg.change_username) {
+       if (s->got_username && !conf_get_int(ssh->conf, CONF_change_username)) {
            /*
             * We got a username last time round this loop, and
             * with change_username turned off we don't try to get
             * it again.
             */
-       } else if (!get_remote_username(&ssh->cfg, s->username,
-                                       sizeof(s->username))) {
+       } else if ((ssh->username = get_remote_username(ssh->conf)) == NULL) {
            int ret; /* need not be kept over crReturn */
            s->cur_prompt = new_prompts(ssh->frontend);
            s->cur_prompt->to_server = TRUE;
            s->cur_prompt->name = dupstr("SSH login name");
-           add_prompt(s->cur_prompt, dupstr("login as: "), TRUE,
-                      lenof(s->username)); 
+           /* 512 is an arbitrary limit :-( */
+           add_prompt(s->cur_prompt, dupstr("login as: "), TRUE, 512); 
            ret = get_userpass_input(s->cur_prompt, NULL, 0);
            while (ret < 0) {
                ssh->send_ok = 1;
@@ -7574,13 +7578,12 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen,
                ssh_disconnect(ssh, "No username provided", NULL, 0, TRUE);
                crStopV;
            }
-           memcpy(s->username, s->cur_prompt->prompts[0]->result,
-                  lenof(s->username));
+           ssh->username = dupstr(s->cur_prompt->prompts[0]->result);
            free_prompts(s->cur_prompt);
        } else {
            char *stuff;
            if ((flags & FLAG_VERBOSE) || (flags & FLAG_INTERACTIVE)) {
-               stuff = dupprintf("Using username \"%s\".\r\n", s->username);
+               stuff = dupprintf("Using username \"%s\".\r\n", ssh->username);
                c_write_str(ssh, stuff);
                sfree(stuff);
            }
@@ -7595,7 +7598,7 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen,
        ssh->pkt_actx = SSH2_PKTCTX_NOAUTH;
 
        s->pktout = ssh2_pkt_init(SSH2_MSG_USERAUTH_REQUEST);
-       ssh2_pkt_addstring(s->pktout, s->username);
+       ssh2_pkt_addstring(s->pktout, ssh->username);
        ssh2_pkt_addstring(s->pktout, "ssh-connection");/* service requested */
        ssh2_pkt_addstring(s->pktout, "none");    /* method */
        ssh2_pkt_send(ssh, s->pktout);
@@ -7707,7 +7710,7 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen,
                        c_write_str(ssh, "Access denied\r\n");
                        logevent("Access denied");
                        if (s->type == AUTH_TYPE_PASSWORD &&
-                           ssh->cfg.change_username) {
+                           conf_get_int(ssh->conf, CONF_change_username)) {
                            /* XXX perhaps we should allow
                             * keyboard-interactive to do this too? */
                            s->we_are_in = FALSE;
@@ -7723,12 +7726,12 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen,
                    in_commasep_string("publickey", methods, methlen);
                s->can_passwd =
                    in_commasep_string("password", methods, methlen);
-               s->can_keyb_inter = ssh->cfg.try_ki_auth &&
+               s->can_keyb_inter = conf_get_int(ssh->conf, CONF_try_ki_auth) &&
                    in_commasep_string("keyboard-interactive", methods, methlen);
 #ifndef NO_GSSAPI
                if (!ssh->gsslibs)
-                   ssh->gsslibs = ssh_gss_setup(&ssh->cfg);
-               s->can_gssapi = ssh->cfg.try_gssapi_auth &&
+                   ssh->gsslibs = ssh_gss_setup(ssh->conf);
+               s->can_gssapi = conf_get_int(ssh->conf, CONF_try_gssapi_auth) &&
                    in_commasep_string("gssapi-with-mic", methods, methlen) &&
                    ssh->gsslibs->nlibraries > 0;
 #endif
@@ -7761,7 +7764,7 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen,
 
                /* See if server will accept it */
                s->pktout = ssh2_pkt_init(SSH2_MSG_USERAUTH_REQUEST);
-               ssh2_pkt_addstring(s->pktout, s->username);
+               ssh2_pkt_addstring(s->pktout, ssh->username);
                ssh2_pkt_addstring(s->pktout, "ssh-connection");
                                                    /* service requested */
                ssh2_pkt_addstring(s->pktout, "publickey");
@@ -7796,7 +7799,7 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen,
                     * Construct a SIGN_REQUEST.
                     */
                    s->pktout = ssh2_pkt_init(SSH2_MSG_USERAUTH_REQUEST);
-                   ssh2_pkt_addstring(s->pktout, s->username);
+                   ssh2_pkt_addstring(s->pktout, ssh->username);
                    ssh2_pkt_addstring(s->pktout, "ssh-connection");
                                                        /* service requested */
                    ssh2_pkt_addstring(s->pktout, "publickey");
@@ -7900,7 +7903,7 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen,
                 * willing to accept it.
                 */
                s->pktout = ssh2_pkt_init(SSH2_MSG_USERAUTH_REQUEST);
-               ssh2_pkt_addstring(s->pktout, s->username);
+               ssh2_pkt_addstring(s->pktout, ssh->username);
                ssh2_pkt_addstring(s->pktout, "ssh-connection");
                                                /* service requested */
                ssh2_pkt_addstring(s->pktout, "publickey");     /* method */
@@ -7974,8 +7977,8 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen,
                    /*
                     * Try decrypting the key.
                     */
-                   key = ssh2_load_userkey(&ssh->cfg.keyfile, passphrase,
-                                           &error);
+                   s->keyfile = conf_get_filename(ssh->conf, CONF_keyfile);
+                   key = ssh2_load_userkey(s->keyfile, passphrase, &error);
                    if (passphrase) {
                        /* burn the evidence */
                        memset(passphrase, 0, strlen(passphrase));
@@ -8008,7 +8011,7 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen,
                     * Hallelujah. Generate a signature and send it.
                     */
                    s->pktout = ssh2_pkt_init(SSH2_MSG_USERAUTH_REQUEST);
-                   ssh2_pkt_addstring(s->pktout, s->username);
+                   ssh2_pkt_addstring(s->pktout, ssh->username);
                    ssh2_pkt_addstring(s->pktout, "ssh-connection");
                                                    /* service requested */
                    ssh2_pkt_addstring(s->pktout, "publickey");
@@ -8081,7 +8084,8 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen,
                    int i, j;
                    s->gsslib = NULL;
                    for (i = 0; i < ngsslibs; i++) {
-                       int want_id = ssh->cfg.ssh_gsslist[i];
+                       int want_id = conf_get_int_int(ssh->conf,
+                                                      CONF_ssh_gsslist, i);
                        for (j = 0; j < ssh->gsslibs->nlibraries; j++)
                            if (ssh->gsslibs->libraries[j].id == want_id) {
                                s->gsslib = &ssh->gsslibs->libraries[j];
@@ -8104,7 +8108,7 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen,
 
                /* Sending USERAUTH_REQUEST with "gssapi-with-mic" method */
                s->pktout = ssh2_pkt_init(SSH2_MSG_USERAUTH_REQUEST);
-               ssh2_pkt_addstring(s->pktout, s->username);
+               ssh2_pkt_addstring(s->pktout, ssh->username);
                ssh2_pkt_addstring(s->pktout, "ssh-connection");
                ssh2_pkt_addstring(s->pktout, "gssapi-with-mic");
 
@@ -8175,7 +8179,7 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen,
                        (s->gsslib,
                         &s->gss_ctx,
                         s->gss_srv_name,
-                        ssh->cfg.gssapifwd,
+                        conf_get_int(ssh->conf, CONF_gssapifwd),
                         &s->gss_rcvtok,
                         &s->gss_sndtok);
 
@@ -8231,7 +8235,7 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen,
                ssh_pkt_addstring_start(s->pktout);
                ssh_pkt_addstring_data(s->pktout, (char *)ssh->v2_session_id, ssh->v2_session_id_len);
                ssh_pkt_addbyte(s->pktout, SSH2_MSG_USERAUTH_REQUEST);
-               ssh_pkt_addstring(s->pktout, s->username);
+               ssh_pkt_addstring(s->pktout, ssh->username);
                ssh_pkt_addstring(s->pktout, "ssh-connection");
                ssh_pkt_addstring(s->pktout, "gssapi-with-mic");
 
@@ -8262,7 +8266,7 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen,
                ssh->pkt_actx = SSH2_PKTCTX_KBDINTER;
 
                s->pktout = ssh2_pkt_init(SSH2_MSG_USERAUTH_REQUEST);
-               ssh2_pkt_addstring(s->pktout, s->username);
+               ssh2_pkt_addstring(s->pktout, ssh->username);
                ssh2_pkt_addstring(s->pktout, "ssh-connection");
                                                        /* service requested */
                ssh2_pkt_addstring(s->pktout, "keyboard-interactive");
@@ -8417,8 +8421,8 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen,
                s->cur_prompt = new_prompts(ssh->frontend);
                s->cur_prompt->to_server = TRUE;
                s->cur_prompt->name = dupstr("SSH password");
-               add_prompt(s->cur_prompt, dupprintf("%.90s@%.90s's password: ",
-                                                   s->username,
+               add_prompt(s->cur_prompt, dupprintf("%s@%s's password: ",
+                                                   ssh->username,
                                                    ssh->savedhost),
                           FALSE, SSH_MAX_PASSWORD_LEN);
 
@@ -8458,7 +8462,7 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen,
                 * people who find out how long their password is!
                 */
                s->pktout = ssh2_pkt_init(SSH2_MSG_USERAUTH_REQUEST);
-               ssh2_pkt_addstring(s->pktout, s->username);
+               ssh2_pkt_addstring(s->pktout, ssh->username);
                ssh2_pkt_addstring(s->pktout, "ssh-connection");
                                                        /* service requested */
                ssh2_pkt_addstring(s->pktout, "password");
@@ -8587,7 +8591,7 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen,
                     * (see above for padding rationale)
                     */
                    s->pktout = ssh2_pkt_init(SSH2_MSG_USERAUTH_REQUEST);
-                   ssh2_pkt_addstring(s->pktout, s->username);
+                   ssh2_pkt_addstring(s->pktout, ssh->username);
                    ssh2_pkt_addstring(s->pktout, "ssh-connection");
                                                        /* service requested */
                    ssh2_pkt_addstring(s->pktout, "password");
@@ -8692,9 +8696,9 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen,
     /*
      * Create the main session channel.
      */
-    if (ssh->cfg.ssh_no_shell) {
+    if (conf_get_int(ssh->conf, CONF_ssh_no_shell)) {
        ssh->mainchan = NULL;
-    } else if (*ssh->cfg.ssh_nc_host) {
+    } else if (*conf_get_str(ssh->conf, CONF_ssh_nc_host)) {
        /*
         * Just start a direct-tcpip channel and use it as the main
         * channel.
@@ -8704,14 +8708,15 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen,
        ssh2_channel_init(ssh->mainchan);
        logeventf(ssh,
                  "Opening direct-tcpip channel to %s:%d in place of session",
-                 ssh->cfg.ssh_nc_host, ssh->cfg.ssh_nc_port);
+                 conf_get_str(ssh->conf, CONF_ssh_nc_host),
+                 conf_get_int(ssh->conf, CONF_ssh_nc_port));
        s->pktout = ssh2_pkt_init(SSH2_MSG_CHANNEL_OPEN);
        ssh2_pkt_addstring(s->pktout, "direct-tcpip");
        ssh2_pkt_adduint32(s->pktout, ssh->mainchan->localid);
        ssh2_pkt_adduint32(s->pktout, ssh->mainchan->v.v2.locwindow);/* our window size */
        ssh2_pkt_adduint32(s->pktout, OUR_V2_MAXPKT);      /* our max pkt size */
-       ssh2_pkt_addstring(s->pktout, ssh->cfg.ssh_nc_host);
-       ssh2_pkt_adduint32(s->pktout, ssh->cfg.ssh_nc_port);
+       ssh2_pkt_addstring(s->pktout, conf_get_str(ssh->conf, CONF_ssh_nc_host));
+       ssh2_pkt_adduint32(s->pktout, conf_get_int(ssh->conf, CONF_ssh_nc_port));
        /*
         * There's nothing meaningful to put in the originator
         * fields, but some servers insist on syntactically correct
@@ -8789,7 +8794,7 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen,
     ssh->packet_dispatch[SSH2_MSG_CHANNEL_OPEN] =
        ssh2_msg_channel_open;
 
-    if (ssh->mainchan && ssh->cfg.ssh_simple) {
+    if (ssh->mainchan && conf_get_int(ssh->conf, CONF_ssh_simple)) {
        /*
         * This message indicates to the server that we promise
         * not to try to run any other channel in parallel with
@@ -8806,9 +8811,9 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen,
     /*
      * Potentially enable X11 forwarding.
      */
-    if (ssh->mainchan && !ssh->ncmode && ssh->cfg.x11_forward &&
-       (ssh->x11disp = x11_setup_display(ssh->cfg.x11_display,
-                                         ssh->cfg.x11_auth, &ssh->cfg))) {
+    if (ssh->mainchan && !ssh->ncmode && conf_get_int(ssh->conf, CONF_x11_forward) &&
+       (ssh->x11disp = x11_setup_display(conf_get_str(ssh->conf, CONF_x11_display),
+                                         conf_get_int(ssh->conf, CONF_x11_auth), ssh->conf))) {
        logevent("Requesting X11 forwarding");
        s->pktout = ssh2_pkt_init(SSH2_MSG_CHANNEL_REQUEST);
        ssh2_pkt_adduint32(s->pktout, ssh->mainchan->remoteid);
@@ -8847,12 +8852,12 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen,
     /*
      * Enable port forwardings.
      */
-    ssh_setup_portfwd(ssh, &ssh->cfg);
+    ssh_setup_portfwd(ssh, ssh->conf);
 
     /*
      * Potentially enable agent forwarding.
      */
-    if (ssh->mainchan && !ssh->ncmode && ssh->cfg.agentfwd && agent_exists()) {
+    if (ssh->mainchan && !ssh->ncmode && conf_get_int(ssh->conf, CONF_agentfwd) && agent_exists()) {
        logevent("Requesting OpenSSH-style agent forwarding");
        s->pktout = ssh2_pkt_init(SSH2_MSG_CHANNEL_REQUEST);
        ssh2_pkt_adduint32(s->pktout, ssh->mainchan->remoteid);
@@ -8878,24 +8883,23 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen,
     /*
      * Now allocate a pty for the session.
      */
-    if (ssh->mainchan && !ssh->ncmode && !ssh->cfg.nopty) {
+    if (ssh->mainchan && !ssh->ncmode && !conf_get_int(ssh->conf, CONF_nopty)) {
        /* Unpick the terminal-speed string. */
        /* XXX perhaps we should allow no speeds to be sent. */
         ssh->ospeed = 38400; ssh->ispeed = 38400; /* last-resort defaults */
-       sscanf(ssh->cfg.termspeed, "%d,%d", &ssh->ospeed, &ssh->ispeed);
+       sscanf(conf_get_str(ssh->conf, CONF_termspeed), "%d,%d", &ssh->ospeed, &ssh->ispeed);
        /* Build the pty request. */
        s->pktout = ssh2_pkt_init(SSH2_MSG_CHANNEL_REQUEST);
        ssh2_pkt_adduint32(s->pktout, ssh->mainchan->remoteid); /* recipient channel */
        ssh2_pkt_addstring(s->pktout, "pty-req");
        ssh2_pkt_addbool(s->pktout, 1);        /* want reply */
-       ssh2_pkt_addstring(s->pktout, ssh->cfg.termtype);
+       ssh2_pkt_addstring(s->pktout, conf_get_str(ssh->conf, CONF_termtype));
        ssh2_pkt_adduint32(s->pktout, ssh->term_width);
        ssh2_pkt_adduint32(s->pktout, ssh->term_height);
        ssh2_pkt_adduint32(s->pktout, 0);              /* pixel width */
        ssh2_pkt_adduint32(s->pktout, 0);              /* pixel height */
        ssh2_pkt_addstring_start(s->pktout);
-       parse_ttymodes(ssh, ssh->cfg.ttymodes,
-                      ssh2_send_ttymode, (void *)s->pktout);
+       parse_ttymodes(ssh, ssh2_send_ttymode, (void *)s->pktout);
        ssh2_pkt_addbyte(s->pktout, SSH2_TTY_OP_ISPEED);
        ssh2_pkt_adduint32(s->pktout, ssh->ispeed);
        ssh2_pkt_addbyte(s->pktout, SSH2_TTY_OP_OSPEED);
@@ -8928,63 +8932,57 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen,
      * Simplest thing here is to send all the requests at once, and
      * then wait for a whole bunch of successes or failures.
      */
-    if (ssh->mainchan && !ssh->ncmode && *ssh->cfg.environmt) {
-       char *e = ssh->cfg.environmt;
-       char *var, *varend, *val;
+    if (ssh->mainchan && !ssh->ncmode) {
+       char *key, *val;
 
        s->num_env = 0;
 
-       while (*e) {
-           var = e;
-           while (*e && *e != '\t') e++;
-           varend = e;
-           if (*e == '\t') e++;
-           val = e;
-           while (*e) e++;
-           e++;
-
+       for (val = conf_get_str_strs(ssh->conf, CONF_environmt, NULL, &key);
+            val != NULL;
+            val = conf_get_str_strs(ssh->conf, CONF_environmt, key, &key)) {
            s->pktout = ssh2_pkt_init(SSH2_MSG_CHANNEL_REQUEST);
            ssh2_pkt_adduint32(s->pktout, ssh->mainchan->remoteid);
            ssh2_pkt_addstring(s->pktout, "env");
            ssh2_pkt_addbool(s->pktout, 1);            /* want reply */
-           ssh2_pkt_addstring_start(s->pktout);
-           ssh2_pkt_addstring_data(s->pktout, var, varend-var);
+           ssh2_pkt_addstring(s->pktout, key);
            ssh2_pkt_addstring(s->pktout, val);
            ssh2_pkt_send(ssh, s->pktout);
 
            s->num_env++;
        }
 
-       logeventf(ssh, "Sent %d environment variables", s->num_env);
+       if (s->num_env) {
+           logeventf(ssh, "Sent %d environment variables", s->num_env);
 
-       s->env_ok = 0;
-       s->env_left = s->num_env;
+           s->env_ok = 0;
+           s->env_left = s->num_env;
 
-       while (s->env_left > 0) {
-           crWaitUntilV(pktin);
+           while (s->env_left > 0) {
+               crWaitUntilV(pktin);
 
-           if (pktin->type != SSH2_MSG_CHANNEL_SUCCESS) {
-               if (pktin->type != SSH2_MSG_CHANNEL_FAILURE) {
-                   bombout(("Unexpected response to environment request:"
-                            " packet type %d", pktin->type));
-                   crStopV;
+               if (pktin->type != SSH2_MSG_CHANNEL_SUCCESS) {
+                   if (pktin->type != SSH2_MSG_CHANNEL_FAILURE) {
+                       bombout(("Unexpected response to environment request:"
+                                " packet type %d", pktin->type));
+                       crStopV;
+                   }
+               } else {
+                   s->env_ok++;
                }
-           } else {
-               s->env_ok++;
-           }
 
-           s->env_left--;
-       }
+               s->env_left--;
+           }
 
-       if (s->env_ok == s->num_env) {
-           logevent("All environment variables successfully set");
-       } else if (s->env_ok == 0) {
-           logevent("All environment variables refused");
-           c_write_str(ssh, "Server refused to set environment variables\r\n");
-       } else {
-           logeventf(ssh, "%d environment variables refused",
-                     s->num_env - s->env_ok);
-           c_write_str(ssh, "Server refused to set all environment variables\r\n");
+           if (s->env_ok == s->num_env) {
+               logevent("All environment variables successfully set");
+           } else if (s->env_ok == 0) {
+               logevent("All environment variables refused");
+               c_write_str(ssh, "Server refused to set environment variables\r\n");
+           } else {
+               logeventf(ssh, "%d environment variables refused",
+                         s->num_env - s->env_ok);
+               c_write_str(ssh, "Server refused to set all environment variables\r\n");
+           }
        }
     }
 
@@ -8998,12 +8996,11 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen,
        char *cmd;
 
        if (ssh->fallback_cmd) {
-           subsys = ssh->cfg.ssh_subsys2;
-           cmd = ssh->cfg.remote_cmd_ptr2;
+           subsys = conf_get_int(ssh->conf, CONF_ssh_subsys2);
+           cmd = conf_get_str(ssh->conf, CONF_remote_cmd2);
        } else {
-           subsys = ssh->cfg.ssh_subsys;
-           cmd = ssh->cfg.remote_cmd_ptr;
-           if (!cmd) cmd = ssh->cfg.remote_cmd;
+           subsys = conf_get_int(ssh->conf, CONF_ssh_subsys);
+           cmd = conf_get_str(ssh->conf, CONF_remote_cmd);
        }
 
        s->pktout = ssh2_pkt_init(SSH2_MSG_CHANNEL_REQUEST);
@@ -9036,7 +9033,8 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen,
             * not, and if the fallback command exists, try falling
             * back to it before complaining.
             */
-           if (!ssh->fallback_cmd && ssh->cfg.remote_cmd_ptr2 != NULL) {
+           if (!ssh->fallback_cmd &&
+               *conf_get_str(ssh->conf, CONF_remote_cmd2)) {
                logevent("Primary command failed; attempting fallback");
                ssh->fallback_cmd = TRUE;
                continue;
@@ -9226,7 +9224,7 @@ static void ssh2_timer(void *ctx, long now)
     if (ssh->state == SSH_STATE_CLOSED)
        return;
 
-    if (!ssh->kex_in_progress && ssh->cfg.ssh_rekey_time != 0 &&
+    if (!ssh->kex_in_progress && conf_get_int(ssh->conf, CONF_ssh_rekey_time) != 0 &&
        now - ssh->next_rekey >= 0) {
        do_ssh2_transport(ssh, "timeout", -1, NULL);
     }
@@ -9267,21 +9265,26 @@ static void ssh2_protocol(Ssh ssh, void *vin, int inlen,
     }
 }
 
+static void ssh_cache_conf_values(Ssh ssh)
+{
+    ssh->logomitdata = conf_get_int(ssh->conf, CONF_logomitdata);
+}
+
 /*
  * Called to set up the connection.
  *
  * Returns an error message, or NULL on success.
  */
 static const char *ssh_init(void *frontend_handle, void **backend_handle,
-                           Config *cfg,
-                           char *host, int port, char **realhost, int nodelay,
-                           int keepalive)
+                           Conf *conf, char *host, int port, char **realhost,
+                           int nodelay, int keepalive)
 {
     const char *p;
     Ssh ssh;
 
     ssh = snew(struct ssh_tag);
-    ssh->cfg = *cfg;                  /* STRUCTURE COPY */
+    ssh->conf = conf_copy(conf);
+    ssh_cache_conf_values(ssh);
     ssh->version = 0;                 /* when not ready yet */
     ssh->s = NULL;
     ssh->cipher = NULL;
@@ -9343,6 +9346,7 @@ static const char *ssh_init(void *frontend_handle, void **backend_handle,
     ssh->deferred_rekey_reason = NULL;
     bufchain_init(&ssh->queued_incoming_data);
     ssh->frozen = FALSE;
+    ssh->username = NULL;
 
     *backend_handle = ssh;
 
@@ -9352,8 +9356,8 @@ static const char *ssh_init(void *frontend_handle, void **backend_handle,
 #endif
 
     ssh->frontend = frontend_handle;
-    ssh->term_width = ssh->cfg.width;
-    ssh->term_height = ssh->cfg.height;
+    ssh->term_width = conf_get_int(ssh->conf, CONF_width);
+    ssh->term_height = conf_get_int(ssh->conf, CONF_height);
 
     ssh->channels = NULL;
     ssh->rportfwds = NULL;
@@ -9374,7 +9378,8 @@ static const char *ssh_init(void *frontend_handle, void **backend_handle,
 
     ssh->incoming_data_size = ssh->outgoing_data_size =
        ssh->deferred_data_size = 0L;
-    ssh->max_data_size = parse_blocksize(ssh->cfg.ssh_rekey_data);
+    ssh->max_data_size = parse_blocksize(conf_get_str(ssh->conf,
+                                                     CONF_ssh_rekey_data));
     ssh->kex_in_progress = FALSE;
 
 #ifndef NO_GSSAPI
@@ -9478,6 +9483,8 @@ static void ssh_free(void *handle)
     if (ssh->pinger)
        pinger_free(ssh->pinger);
     bufchain_clear(&ssh->queued_incoming_data);
+    sfree(ssh->username);
+    conf_free(ssh->conf);
 #ifndef NO_GSSAPI
     if (ssh->gsslibs)
        ssh_gss_cleanup(ssh->gsslibs);
@@ -9490,19 +9497,21 @@ static void ssh_free(void *handle)
 /*
  * Reconfigure the SSH backend.
  */
-static void ssh_reconfig(void *handle, Config *cfg)
+static void ssh_reconfig(void *handle, Conf *conf)
 {
     Ssh ssh = (Ssh) handle;
     char *rekeying = NULL, rekey_mandatory = FALSE;
     unsigned long old_max_data_size;
+    int i, rekey_time;
 
-    pinger_reconfig(ssh->pinger, &ssh->cfg, cfg);
+    pinger_reconfig(ssh->pinger, ssh->conf, conf);
     if (ssh->portfwds)
-       ssh_setup_portfwd(ssh, cfg);
+       ssh_setup_portfwd(ssh, conf);
 
-    if (ssh->cfg.ssh_rekey_time != cfg->ssh_rekey_time &&
-       cfg->ssh_rekey_time != 0) {
-       long new_next = ssh->last_rekey + cfg->ssh_rekey_time*60*TICKSPERSEC;
+    rekey_time = conf_get_int(conf, CONF_ssh_rekey_time);
+    if (conf_get_int(ssh->conf, CONF_ssh_rekey_time) != rekey_time &&
+       rekey_time != 0) {
+       long new_next = ssh->last_rekey + rekey_time*60*TICKSPERSEC;
        long now = GETTICKCOUNT();
 
        if (new_next - now < 0) {
@@ -9513,7 +9522,8 @@ static void ssh_reconfig(void *handle, Config *cfg)
     }
 
     old_max_data_size = ssh->max_data_size;
-    ssh->max_data_size = parse_blocksize(cfg->ssh_rekey_data);
+    ssh->max_data_size = parse_blocksize(conf_get_str(ssh->conf,
+                                                     CONF_ssh_rekey_data));
     if (old_max_data_size != ssh->max_data_size &&
        ssh->max_data_size != 0) {
        if (ssh->outgoing_data_size > ssh->max_data_size ||
@@ -9521,19 +9531,27 @@ static void ssh_reconfig(void *handle, Config *cfg)
            rekeying = "data limit lowered";
     }
 
-    if (ssh->cfg.compression != cfg->compression) {
+    if (conf_get_int(ssh->conf, CONF_compression) !=
+       conf_get_int(conf, CONF_compression)) {
        rekeying = "compression setting changed";
        rekey_mandatory = TRUE;
     }
 
-    if (ssh->cfg.ssh2_des_cbc != cfg->ssh2_des_cbc ||
-       memcmp(ssh->cfg.ssh_cipherlist, cfg->ssh_cipherlist,
-              sizeof(ssh->cfg.ssh_cipherlist))) {
+    for (i = 0; i < CIPHER_MAX; i++)
+       if (conf_get_int_int(ssh->conf, CONF_ssh_cipherlist, i) !=
+           conf_get_int_int(conf, CONF_ssh_cipherlist, i)) {
+       rekeying = "cipher settings changed";
+       rekey_mandatory = TRUE;
+    }
+    if (conf_get_int(ssh->conf, CONF_ssh2_des_cbc) !=
+       conf_get_int(conf, CONF_ssh2_des_cbc)) {
        rekeying = "cipher settings changed";
        rekey_mandatory = TRUE;
     }
 
-    ssh->cfg = *cfg;                  /* STRUCTURE COPY */
+    conf_free(ssh->conf);
+    ssh->conf = conf_copy(conf);
+    ssh_cache_conf_values(ssh);
 
     if (rekeying) {
        if (!ssh->kex_in_progress) {
@@ -9611,7 +9629,7 @@ static void ssh_size(void *handle, int width, int height)
        ssh->size_needed = TRUE;       /* buffer for later */
        break;
       case SSH_STATE_SESSION:
-       if (!ssh->cfg.nopty) {
+       if (!conf_get_int(ssh->conf, CONF_nopty)) {
            if (ssh->version == 1) {
                send_packet(ssh, SSH1_CMSG_WINDOW_SIZE,
                            PKT_INT, ssh->term_height,
@@ -9835,7 +9853,7 @@ static void ssh_unthrottle(void *handle, int bufsize)
            ssh2_set_window(ssh->mainchan,
                            bufsize < ssh->mainchan->v.v2.locmaxwin ?
                            ssh->mainchan->v.v2.locmaxwin - bufsize : 0);
-           if (ssh->cfg.ssh_simple)
+           if (conf_get_int(ssh->conf, CONF_ssh_simple))
                buflimit = 0;
            else
                buflimit = ssh->mainchan->v.v2.locmaxwin;
diff --git a/ssh.h b/ssh.h
index 90dafed..8d28847 100644 (file)
--- a/ssh.h
+++ b/ssh.h
@@ -334,13 +334,11 @@ void ssh_send_port_open(void *channel, char *hostname, int port, char *org);
 
 /* Exports from portfwd.c */
 extern const char *pfd_newconnect(Socket * s, char *hostname, int port,
-                                 void *c, const Config *cfg,
-                                 int addressfamily);
+                                 void *c, Conf *conf, int addressfamily);
 /* desthost == NULL indicates dynamic (SOCKS) port forwarding */
 extern const char *pfd_addforward(char *desthost, int destport, char *srcaddr,
-                                 int port, void *backhandle,
-                                 const Config *cfg, void **sockdata,
-                                 int address_family);
+                                 int port, void *backhandle, Conf *conf,
+                                 void **sockdata, int address_family);
 extern void pfd_close(Socket s);
 extern void pfd_terminate(void *sockdata);
 extern int pfd_send(Socket s, char *data, int len);
@@ -393,18 +391,17 @@ struct X11Display {
  * details are looked up by calling platform_get_x11_auth.
  */
 extern struct X11Display *x11_setup_display(char *display, int authtype,
-                                           const Config *);
+                                           Conf *);
 void x11_free_display(struct X11Display *disp);
 extern const char *x11_init(Socket *, struct X11Display *, void *,
-                           const char *, int, const Config *);
+                           const char *, int, Conf *);
 extern void x11_close(Socket);
 extern int x11_send(Socket, char *, int);
 extern void x11_unthrottle(Socket s);
 extern void x11_override_throttle(Socket s, int enable);
 char *x11_display(const char *display);
 /* Platform-dependent X11 functions */
-extern void platform_get_x11_auth(struct X11Display *display,
-                                 const Config *);
+extern void platform_get_x11_auth(struct X11Display *display, Conf *);
     /* examine a mostly-filled-in X11Display and fill in localauth* */
 extern const int platform_uses_x11_unix_by_default;
     /* choose default X transport in the absence of a specified one */
index ffce099..32ccb4d 100644 (file)
--- a/sshgss.h
+++ b/sshgss.h
@@ -47,7 +47,7 @@ struct ssh_gss_liblist {
     struct ssh_gss_library *libraries;
     int nlibraries;
 };
-struct ssh_gss_liblist *ssh_gss_setup(const Config *cfg);
+struct ssh_gss_liblist *ssh_gss_setup(Conf *conf);
 void ssh_gss_cleanup(struct ssh_gss_liblist *list);
 
 /*
index fd8915b..fa4ac65 100644 (file)
@@ -3,7 +3,7 @@
 
 /* For platforms not supporting GSSAPI */
 
-struct ssh_gss_liblist *ssh_gss_setup(const Config *cfg)
+struct ssh_gss_liblist *ssh_gss_setup(Conf *conf)
 {
     struct ssh_gss_liblist *list = snew(struct ssh_gss_liblist *);
     list->libraries = NULL;
index 704f231..01cfb03 100644 (file)
--- a/storage.h
+++ b/storage.h
@@ -9,9 +9,9 @@
 /* ----------------------------------------------------------------------
  * Functions to save and restore PuTTY sessions. Note that this is
  * only the low-level code to do the reading and writing. The
- * higher-level code that translates a Config structure into a set
- * of (key,value) pairs is elsewhere, since it doesn't (mostly)
- * change between platforms.
+ * higher-level code that translates an internal Conf structure into
+ * a set of (key,value) pairs in their external storage format is
+ * elsewhere, since it doesn't (mostly) change between platforms.
  */
 
 /*
@@ -41,8 +41,8 @@ void close_settings_w(void *handle);
  * number of calls to read_setting_s() and read_setting_i(), and
  * then close it using close_settings_r().
  * 
- * read_setting_s() writes into the provided buffer and returns a
- * pointer to the same buffer.
+ * read_setting_s() returns a dynamically allocated string which the
+ * caller must free.
  * 
  * If a particular string setting is not present in the session,
  * read_setting_s() can return NULL, in which case the caller
@@ -53,7 +53,7 @@ void close_settings_w(void *handle);
  * the provided buffer, and return zero if they failed to.
  */
 void *open_settings_r(const char *sessionname);
-char *read_setting_s(void *handle, const char *key, char *buffer, int buflen);
+char *read_setting_s(void *handle, const char *key);
 int read_setting_i(void *handle, const char *key, int defvalue);
 int read_setting_filename(void *handle, const char *key, Filename *value);
 int read_setting_fontspec(void *handle, const char *key, FontSpec *font);
index 20ccc83..05c9bf6 100644 (file)
--- a/telnet.c
+++ b/telnet.c
@@ -201,7 +201,7 @@ typedef struct telnet_tag {
            SEENSB, SUBNEGOT, SUBNEG_IAC, SEENCR
     } state;
 
-    Config cfg;
+    Conf *conf;
 
     Pinger pinger;
 } *Telnet;
@@ -363,42 +363,46 @@ static void proc_rec_opt(Telnet telnet, int cmd, int option)
 
 static void process_subneg(Telnet telnet)
 {
-    unsigned char b[2048], *p, *q;
-    int var, value, n;
-    char *e;
+    unsigned char *b, *p, *q;
+    int var, value, n, bsize;
+    char *e, *eval, *ekey, *user;
 
     switch (telnet->sb_opt) {
       case TELOPT_TSPEED:
        if (telnet->sb_len == 1 && telnet->sb_buf[0] == TELQUAL_SEND) {
            char *logbuf;
+           char *termspeed = conf_get_str(telnet->conf, CONF_termspeed);
+           b = snewn(20 + strlen(termspeed), unsigned char);
            b[0] = IAC;
            b[1] = SB;
            b[2] = TELOPT_TSPEED;
            b[3] = TELQUAL_IS;
-           strcpy((char *)(b + 4), telnet->cfg.termspeed);
-           n = 4 + strlen(telnet->cfg.termspeed);
+           strcpy((char *)(b + 4), termspeed);
+           n = 4 + strlen(termspeed);
            b[n] = IAC;
            b[n + 1] = SE;
            telnet->bufsize = sk_write(telnet->s, (char *)b, n + 2);
            logevent(telnet->frontend, "server:\tSB TSPEED SEND");
-           logbuf = dupprintf("client:\tSB TSPEED IS %s", telnet->cfg.termspeed);
+           logbuf = dupprintf("client:\tSB TSPEED IS %s", termspeed);
            logevent(telnet->frontend, logbuf);
            sfree(logbuf);
+           sfree(b);
        } else
            logevent(telnet->frontend, "server:\tSB TSPEED <something weird>");
        break;
       case TELOPT_TTYPE:
        if (telnet->sb_len == 1 && telnet->sb_buf[0] == TELQUAL_SEND) {
            char *logbuf;
+           char *termtype = conf_get_str(telnet->conf, CONF_termtype);
+           b = snewn(20 + strlen(termtype), unsigned char);
            b[0] = IAC;
            b[1] = SB;
            b[2] = TELOPT_TTYPE;
            b[3] = TELQUAL_IS;
-           for (n = 0; telnet->cfg.termtype[n]; n++)
-               b[n + 4] = (telnet->cfg.termtype[n] >= 'a'
-                           && telnet->cfg.termtype[n] <=
-                           'z' ? telnet->cfg.termtype[n] + 'A' -
-                           'a' : telnet->cfg.termtype[n]);
+           for (n = 0; termtype[n]; n++)
+               b[n + 4] = (termtype[n] >= 'a' && termtype[n] <= 'z' ?
+                           termtype[n] + 'A' - 'a' :
+                           termtype[n]);
            b[n + 4] = IAC;
            b[n + 5] = SE;
            telnet->bufsize = sk_write(telnet->s, (char *)b, n + 6);
@@ -407,6 +411,7 @@ static void process_subneg(Telnet telnet)
            logbuf = dupprintf("client:\tSB TTYPE IS %s", b + 4);
            logevent(telnet->frontend, logbuf);
            sfree(logbuf);
+           sfree(b);
        } else
            logevent(telnet->frontend, "server:\tSB TTYPE <something weird>\r\n");
        break;
@@ -421,7 +426,7 @@ static void process_subneg(Telnet telnet)
            logevent(telnet->frontend, logbuf);
            sfree(logbuf);
            if (telnet->sb_opt == TELOPT_OLD_ENVIRON) {
-               if (telnet->cfg.rfc_environ) {
+               if (conf_get_str(telnet->conf, CONF_rfc_environ)) {
                    value = RFC_VALUE;
                    var = RFC_VAR;
                } else {
@@ -449,50 +454,75 @@ static void process_subneg(Telnet telnet)
                value = RFC_VALUE;
                var = RFC_VAR;
            }
+           bsize = 20;
+           for (eval = conf_get_str_strs(telnet->conf, CONF_environmt,
+                                        NULL, &ekey);
+                eval != NULL;
+                eval = conf_get_str_strs(telnet->conf, CONF_environmt,
+                                        ekey, &ekey))
+                bsize += strlen(ekey) + strlen(eval) + 2;
+           user = get_remote_username(telnet->conf);
+           if (user)
+               bsize += 6 + strlen(user);
+
+           b = snewn(bsize, unsigned char);
            b[0] = IAC;
            b[1] = SB;
            b[2] = telnet->sb_opt;
            b[3] = TELQUAL_IS;
            n = 4;
-           e = telnet->cfg.environmt;
-           while (*e) {
+           for (eval = conf_get_str_strs(telnet->conf, CONF_environmt,
+                                        NULL, &ekey);
+                eval != NULL;
+                eval = conf_get_str_strs(telnet->conf, CONF_environmt,
+                                        ekey, &ekey)) {
                b[n++] = var;
-               while (*e && *e != '\t')
-                   b[n++] = *e++;
-               if (*e == '\t')
-                   e++;
+               for (e = ekey; *e; e++)
+                   b[n++] = *e;
                b[n++] = value;
-               while (*e)
-                   b[n++] = *e++;
-               e++;
+               for (e = eval; *e; e++)
+                   b[n++] = *e;
            }
-           {
-               char user[sizeof(telnet->cfg.username)];
-               (void) get_remote_username(&telnet->cfg, user, sizeof(user));
-               if (*user) {
-                   b[n++] = var;
-                   b[n++] = 'U';
-                   b[n++] = 'S';
-                   b[n++] = 'E';
-                   b[n++] = 'R';
-                   b[n++] = value;
-                   e = user;
-                   while (*e)
-                       b[n++] = *e++;
-               }
-               b[n++] = IAC;
-               b[n++] = SE;
-               telnet->bufsize = sk_write(telnet->s, (char *)b, n);
-               logbuf = dupprintf("client:\tSB %s IS %s%s%s%s",
-                                  telopt(telnet->sb_opt),
-                                  *user ? "USER=" : "",
-                                  user,
-                                  *user ? " " : "",
-                                  n == 6 ? "<nothing>" :
-                                  (*telnet->cfg.environmt ? "<stuff>" : ""));
+           if (user) {
+               b[n++] = var;
+               b[n++] = 'U';
+               b[n++] = 'S';
+               b[n++] = 'E';
+               b[n++] = 'R';
+               b[n++] = value;
+               for (e = user; *e; e++)
+                   b[n++] = *e;
+           }
+           b[n++] = IAC;
+           b[n++] = SE;
+           telnet->bufsize = sk_write(telnet->s, (char *)b, n);
+           if (n == 6) {
+               logbuf = dupprintf("client:\tSB %s IS <nothing>",
+                                  telopt(telnet->sb_opt));
                logevent(telnet->frontend, logbuf);
                sfree(logbuf);
+           } else {
+               logbuf = dupprintf("client:\tSB %s IS:",
+                                  telopt(telnet->sb_opt));
+               logevent(telnet->frontend, logbuf);
+               sfree(logbuf);
+               for (eval = conf_get_str_strs(telnet->conf, CONF_environmt,
+                                            NULL, &ekey);
+                    eval != NULL;
+                    eval = conf_get_str_strs(telnet->conf, CONF_environmt,
+                                            ekey, &ekey)) {
+                   logbuf = dupprintf("\t%s=%s", ekey, eval);
+                   logevent(telnet->frontend, logbuf);
+                   sfree(logbuf);
+               }
+               if (user) {
+                   logbuf = dupprintf("\tUSER=%s", user);
+                   logevent(telnet->frontend, logbuf);
+                   sfree(logbuf);
+               }
            }
+           sfree(b);
+           sfree(user);
        }
        break;
     }
@@ -674,9 +704,8 @@ static void telnet_sent(Plug plug, int bufsize)
  * freed by the caller.
  */
 static const char *telnet_init(void *frontend_handle, void **backend_handle,
-                              Config *cfg,
-                              char *host, int port, char **realhost,
-                              int nodelay, int keepalive)
+                              Conf *conf, char *host, int port,
+                              char **realhost, int nodelay, int keepalive)
 {
     static const struct plug_function_table fn_table = {
        telnet_log,
@@ -687,10 +716,12 @@ static const char *telnet_init(void *frontend_handle, void **backend_handle,
     SockAddr addr;
     const char *err;
     Telnet telnet;
+    char *loghost;
+    int addressfamily;
 
     telnet = snew(struct telnet_tag);
     telnet->fn = &fn_table;
-    telnet->cfg = *cfg;                       /* STRUCTURE COPY */
+    telnet->conf = conf_copy(conf);
     telnet->s = NULL;
     telnet->echoing = TRUE;
     telnet->editing = TRUE;
@@ -698,8 +729,8 @@ static const char *telnet_init(void *frontend_handle, void **backend_handle,
     telnet->sb_buf = NULL;
     telnet->sb_size = 0;
     telnet->frontend = frontend_handle;
-    telnet->term_width = telnet->cfg.width;
-    telnet->term_height = telnet->cfg.height;
+    telnet->term_width = conf_get_int(telnet->conf, CONF_width);
+    telnet->term_height = conf_get_int(telnet->conf, CONF_height);
     telnet->state = TOP_LEVEL;
     telnet->ldisc = NULL;
     telnet->pinger = NULL;
@@ -710,14 +741,15 @@ static const char *telnet_init(void *frontend_handle, void **backend_handle,
      */
     {
        char *buf;
+       addressfamily = conf_get_int(telnet->conf, CONF_addressfamily);
        buf = dupprintf("Looking up host \"%s\"%s", host,
-                       (cfg->addressfamily == ADDRTYPE_IPV4 ? " (IPv4)" :
-                        (cfg->addressfamily == ADDRTYPE_IPV6 ? " (IPv6)" :
+                       (addressfamily == ADDRTYPE_IPV4 ? " (IPv4)" :
+                        (addressfamily == ADDRTYPE_IPV6 ? " (IPv6)" :
                          "")));
        logevent(telnet->frontend, buf);
        sfree(buf);
     }
-    addr = name_lookup(host, port, realhost, &telnet->cfg, cfg->addressfamily);
+    addr = name_lookup(host, port, realhost, telnet->conf, addressfamily);
     if ((err = sk_addr_error(addr)) != NULL) {
        sk_addr_free(addr);
        return err;
@@ -730,16 +762,16 @@ static const char *telnet_init(void *frontend_handle, void **backend_handle,
      * Open socket.
      */
     telnet->s = new_connection(addr, *realhost, port, 0, 1,
-                              nodelay, keepalive, (Plug) telnet, &telnet->cfg);
+                              nodelay, keepalive, (Plug) telnet, telnet->conf);
     if ((err = sk_socket_error(telnet->s)) != NULL)
        return err;
 
-    telnet->pinger = pinger_new(&telnet->cfg, &telnet_backend, telnet);
+    telnet->pinger = pinger_new(telnet->conf, &telnet_backend, telnet);
 
     /*
      * Initialise option states.
      */
-    if (telnet->cfg.passive_telnet) {
+    if (conf_get_int(telnet->conf, CONF_passive_telnet)) {
        const struct Opt *const *o;
 
        for (o = opts; *o; o++)
@@ -768,11 +800,12 @@ static const char *telnet_init(void *frontend_handle, void **backend_handle,
     /*
      * loghost overrides realhost, if specified.
      */
-    if (*telnet->cfg.loghost) {
+    loghost = conf_get_str(telnet->conf, CONF_loghost);
+    if (*loghost) {
        char *colon;
 
        sfree(*realhost);
-       *realhost = dupstr(telnet->cfg.loghost);
+       *realhost = dupstr(loghost);
        colon = strrchr(*realhost, ':');
        if (colon) {
            /*
@@ -796,6 +829,7 @@ static void telnet_free(void *handle)
        sk_close(telnet->s);
     if (telnet->pinger)
        pinger_free(telnet->pinger);
+    conf_free(telnet->conf);
     sfree(telnet);
 }
 /*
@@ -803,11 +837,12 @@ static void telnet_free(void *handle)
  * necessary, in this backend: we just save the fresh config for
  * any subsequent negotiations.
  */
-static void telnet_reconfig(void *handle, Config *cfg)
+static void telnet_reconfig(void *handle, Conf *conf)
 {
     Telnet telnet = (Telnet) handle;
-    pinger_reconfig(telnet->pinger, &telnet->cfg, cfg);
-    telnet->cfg = *cfg;                       /* STRUCTURE COPY */
+    pinger_reconfig(telnet->pinger, telnet->conf, conf);
+    conf_free(telnet->conf);
+    telnet->conf = conf_copy(conf);
 }
 
 /*
index 73ab393..9865355 100644 (file)
@@ -989,7 +989,7 @@ static void resizeline(Terminal *term, termline *line, int cols)
 static int sblines(Terminal *term)
 {
     int sblines = count234(term->scrollback);
-    if (term->cfg.erase_to_scrollback &&
+    if (term->erase_to_scrollback &&
        term->alt_which && term->alt_screen) {
            sblines += term->alt_sblines;
     }
@@ -1015,7 +1015,7 @@ static termline *lineptr(Terminal *term, int y, int lineno, int screen)
 
        assert(!screen);
 
-       if (term->cfg.erase_to_scrollback &&
+       if (term->erase_to_scrollback &&
            term->alt_which && term->alt_screen) {
            altlines = term->alt_sblines;
        }
@@ -1133,7 +1133,7 @@ static void term_schedule_tblink(Terminal *term)
  */
 static void term_schedule_cblink(Terminal *term)
 {
-    if (term->cfg.blink_cur && term->has_focus) {
+    if (term->blink_cur && term->has_focus) {
        if (!term->cblink_pending)
            term->next_cblink = schedule_timer(CBLINK_DELAY, term_timer, term);
        term->cblink_pending = TRUE;
@@ -1197,11 +1197,11 @@ static void power_on(Terminal *term, int clear)
        for (i = 0; i < term->cols; i++)
            term->tabs[i] = (i % 8 == 0 ? TRUE : FALSE);
     }
-    term->alt_om = term->dec_om = term->cfg.dec_om;
+    term->alt_om = term->dec_om = conf_get_int(term->conf, CONF_dec_om);
     term->alt_ins = term->insert = FALSE;
     term->alt_wnext = term->wrapnext =
         term->save_wnext = term->alt_save_wnext = FALSE;
-    term->alt_wrap = term->wrap = term->cfg.wrap_mode;
+    term->alt_wrap = term->wrap = conf_get_int(term->conf, CONF_wrap_mode);
     term->alt_cset = term->cset = term->save_cset = term->alt_save_cset = 0;
     term->alt_utf = term->utf = term->save_utf = term->alt_save_utf = 0;
     term->utf_state = 0;
@@ -1216,10 +1216,10 @@ static void power_on(Terminal *term, int clear)
     term->default_attr = term->save_attr =
        term->alt_save_attr = term->curr_attr = ATTR_DEFAULT;
     term->term_editing = term->term_echoing = FALSE;
-    term->app_cursor_keys = term->cfg.app_cursor;
-    term->app_keypad_keys = term->cfg.app_keypad;
-    term->use_bce = term->cfg.bce;
-    term->blink_is_real = term->cfg.blinktext;
+    term->app_cursor_keys = conf_get_int(term->conf, CONF_app_cursor);
+    term->app_keypad_keys = conf_get_int(term->conf, CONF_app_keypad);
+    term->use_bce = conf_get_int(term->conf, CONF_bce);
+    term->blink_is_real = conf_get_int(term->conf, CONF_blinktext);
     term->erase_char = term->basic_erase_char;
     term->alt_which = 0;
     term_print_finish(term);
@@ -1228,7 +1228,7 @@ static void power_on(Terminal *term, int clear)
     {
        int i;
        for (i = 0; i < 256; i++)
-           term->wordness[i] = term->cfg.wordness[i];
+           term->wordness[i] = conf_get_int_int(term->conf, CONF_wordness, i);
     }
     if (term->screen) {
        swap_screen(term, 1, FALSE, FALSE);
@@ -1261,7 +1261,7 @@ void term_update(Terminal *term)
     ctx = get_ctx(term->frontend);
     if (ctx) {
        int need_sbar_update = term->seen_disp_event;
-       if (term->seen_disp_event && term->cfg.scroll_on_disp) {
+       if (term->seen_disp_event && term->scroll_on_disp) {
            term->disptop = 0;         /* return to main screen */
            term->seen_disp_event = 0;
            need_sbar_update = TRUE;
@@ -1300,7 +1300,7 @@ void term_seen_key_event(Terminal *term)
     /*
      * Reset the scrollback on keypress, if we're doing that.
      */
-    if (term->cfg.scroll_on_key) {
+    if (term->scroll_on_key) {
        term->disptop = 0;             /* return to main screen */
        seen_disp_event(term);
     }
@@ -1328,12 +1328,82 @@ static void set_erase_char(Terminal *term)
 }
 
 /*
+ * We copy a bunch of stuff out of the Conf structure into local
+ * fields in the Terminal structure, to avoid the repeated tree234
+ * lookups which would be involved in fetching them from the former
+ * every time.
+ */
+void term_copy_stuff_from_conf(Terminal *term)
+{
+    term->ansi_colour = conf_get_int(term->conf, CONF_ansi_colour);
+    term->arabicshaping = conf_get_int(term->conf, CONF_arabicshaping);
+    term->beep = conf_get_int(term->conf, CONF_beep);
+    term->bellovl = conf_get_int(term->conf, CONF_bellovl);
+    term->bellovl_n = conf_get_int(term->conf, CONF_bellovl_n);
+    term->bellovl_s = conf_get_int(term->conf, CONF_bellovl_s);
+    term->bellovl_t = conf_get_int(term->conf, CONF_bellovl_t);
+    term->bidi = conf_get_int(term->conf, CONF_bidi);
+    term->bksp_is_delete = conf_get_int(term->conf, CONF_bksp_is_delete);
+    term->blink_cur = conf_get_int(term->conf, CONF_blink_cur);
+    term->blinktext = conf_get_int(term->conf, CONF_blinktext);
+    term->cjk_ambig_wide = conf_get_int(term->conf, CONF_cjk_ambig_wide);
+    term->conf_height = conf_get_int(term->conf, CONF_height);
+    term->conf_width = conf_get_int(term->conf, CONF_width);
+    term->crhaslf = conf_get_int(term->conf, CONF_crhaslf);
+    term->erase_to_scrollback = conf_get_int(term->conf, CONF_erase_to_scrollback);
+    term->funky_type = conf_get_int(term->conf, CONF_funky_type);
+    term->lfhascr = conf_get_int(term->conf, CONF_lfhascr);
+    term->logflush = conf_get_int(term->conf, CONF_logflush);
+    term->logtype = conf_get_int(term->conf, CONF_logtype);
+    term->mouse_override = conf_get_int(term->conf, CONF_mouse_override);
+    term->nethack_keypad = conf_get_int(term->conf, CONF_nethack_keypad);
+    term->no_alt_screen = conf_get_int(term->conf, CONF_no_alt_screen);
+    term->no_applic_c = conf_get_int(term->conf, CONF_no_applic_c);
+    term->no_applic_k = conf_get_int(term->conf, CONF_no_applic_k);
+    term->no_dbackspace = conf_get_int(term->conf, CONF_no_dbackspace);
+    term->no_mouse_rep = conf_get_int(term->conf, CONF_no_mouse_rep);
+    term->no_remote_charset = conf_get_int(term->conf, CONF_no_remote_charset);
+    term->no_remote_resize = conf_get_int(term->conf, CONF_no_remote_resize);
+    term->no_remote_wintitle = conf_get_int(term->conf, CONF_no_remote_wintitle);
+    term->rawcnp = conf_get_int(term->conf, CONF_rawcnp);
+    term->rect_select = conf_get_int(term->conf, CONF_rect_select);
+    term->remote_qtitle_action = conf_get_int(term->conf, CONF_remote_qtitle_action);
+    term->rxvt_homeend = conf_get_int(term->conf, CONF_rxvt_homeend);
+    term->scroll_on_disp = conf_get_int(term->conf, CONF_scroll_on_disp);
+    term->scroll_on_key = conf_get_int(term->conf, CONF_scroll_on_key);
+    term->xterm_256_colour = conf_get_int(term->conf, CONF_xterm_256_colour);
+
+    /*
+     * Parse the control-character escapes in the configured
+     * answerback string.
+     */
+    {
+       char *answerback = conf_get_str(term->conf, CONF_answerback);
+       int maxlen = strlen(answerback);
+
+       term->answerback = snewn(maxlen, char);
+       term->answerbacklen = 0;
+
+       while (*answerback) {
+           char *n;
+           char c = ctrlparse(answerback, &n);
+           if (n) {
+               term->answerback[term->answerbacklen++] = c;
+               answerback = n;
+           } else {
+               term->answerback[term->answerbacklen++] = *answerback++;
+           }
+       }
+    }
+}
+
+/*
  * When the user reconfigures us, we need to check the forbidden-
  * alternate-screen config option, disable raw mouse mode if the
  * user has disabled mouse reporting, and abandon a print job if
  * the user has disabled printing.
  */
-void term_reconfig(Terminal *term, Config *cfg)
+void term_reconfig(Terminal *term, Conf *conf)
 {
     /*
      * Before adopting the new config, check all those terminal
@@ -1345,21 +1415,28 @@ void term_reconfig(Terminal *term, Config *cfg)
     int reset_wrap, reset_decom, reset_bce, reset_tblink, reset_charclass;
     int i;
 
-    reset_wrap = (term->cfg.wrap_mode != cfg->wrap_mode);
-    reset_decom = (term->cfg.dec_om != cfg->dec_om);
-    reset_bce = (term->cfg.bce != cfg->bce);
-    reset_tblink = (term->cfg.blinktext != cfg->blinktext);
+    reset_wrap = (conf_get_int(term->conf, CONF_wrap_mode) !=
+                 conf_get_int(conf, CONF_wrap_mode));
+    reset_decom = (conf_get_int(term->conf, CONF_dec_om) !=
+                  conf_get_int(conf, CONF_dec_om));
+    reset_bce = (conf_get_int(term->conf, CONF_bce) !=
+                conf_get_int(conf, CONF_bce));
+    reset_tblink = (conf_get_int(term->conf, CONF_blinktext) !=
+                   conf_get_int(conf, CONF_blinktext));
     reset_charclass = 0;
-    for (i = 0; i < lenof(term->cfg.wordness); i++)
-       if (term->cfg.wordness[i] != cfg->wordness[i])
+    for (i = 0; i < 256; i++)
+       if (conf_get_int_int(term->conf, CONF_wordness, i) !=
+           conf_get_int_int(conf, CONF_wordness, i))
            reset_charclass = 1;
 
     /*
      * If the bidi or shaping settings have changed, flush the bidi
      * cache completely.
      */
-    if (term->cfg.arabicshaping != cfg->arabicshaping ||
-       term->cfg.bidi != cfg->bidi) {
+    if (conf_get_int(term->conf, CONF_arabicshaping) !=
+       conf_get_int(conf, CONF_arabicshaping) ||
+       conf_get_int(term->conf, CONF_bidi) !=
+       conf_get_int(conf, CONF_bidi)) {
        for (i = 0; i < term->bidi_cache_size; i++) {
            sfree(term->pre_bidi_cache[i].chars);
            sfree(term->post_bidi_cache[i].chars);
@@ -1370,39 +1447,41 @@ void term_reconfig(Terminal *term, Config *cfg)
        }
     }
 
-    term->cfg = *cfg;                 /* STRUCTURE COPY */
+    conf_free(term->conf);
+    term->conf = conf_copy(conf);
 
     if (reset_wrap)
-       term->alt_wrap = term->wrap = term->cfg.wrap_mode;
+       term->alt_wrap = term->wrap = conf_get_int(term->conf, CONF_wrap_mode);
     if (reset_decom)
-       term->alt_om = term->dec_om = term->cfg.dec_om;
+       term->alt_om = term->dec_om = conf_get_int(term->conf, CONF_dec_om);
     if (reset_bce) {
-       term->use_bce = term->cfg.bce;
+       term->use_bce = conf_get_int(term->conf, CONF_bce);
        set_erase_char(term);
     }
     if (reset_tblink) {
-       term->blink_is_real = term->cfg.blinktext;
+       term->blink_is_real = conf_get_int(term->conf, CONF_blinktext);
     }
     if (reset_charclass)
        for (i = 0; i < 256; i++)
-           term->wordness[i] = term->cfg.wordness[i];
+           term->wordness[i] = conf_get_int_int(term->conf, CONF_wordness, i);
 
-    if (term->cfg.no_alt_screen)
+    if (conf_get_int(term->conf, CONF_no_alt_screen))
        swap_screen(term, 0, FALSE, FALSE);
-    if (term->cfg.no_mouse_rep) {
+    if (conf_get_int(term->conf, CONF_no_mouse_rep)) {
        term->xterm_mouse = 0;
        set_raw_mouse_mode(term->frontend, 0);
     }
-    if (term->cfg.no_remote_charset) {
+    if (conf_get_int(term->conf, CONF_no_remote_charset)) {
        term->cset_attr[0] = term->cset_attr[1] = CSET_ASCII;
        term->sco_acs = term->alt_sco_acs = 0;
        term->utf = 0;
     }
-    if (!*term->cfg.printer) {
+    if (!conf_get_str(term->conf, CONF_printer)) {
        term_print_finish(term);
     }
     term_schedule_tblink(term);
     term_schedule_cblink(term);
+    term_copy_stuff_from_conf(term);
 }
 
 /*
@@ -1423,7 +1502,7 @@ void term_clrsb(Terminal *term)
 /*
  * Initialise the terminal.
  */
-Terminal *term_init(Config *mycfg, struct unicode_data *ucsdata,
+Terminal *term_init(Conf *myconf, struct unicode_data *ucsdata,
                    void *frontend)
 {
     Terminal *term;
@@ -1435,7 +1514,7 @@ Terminal *term_init(Config *mycfg, struct unicode_data *ucsdata,
     term = snew(Terminal);
     term->frontend = frontend;
     term->ucsdata = ucsdata;
-    term->cfg = *mycfg;                       /* STRUCTURE COPY */
+    term->conf = conf_copy(myconf);
     term->logctx = NULL;
     term->compatibility_level = TM_PUTTY;
     strcpy(term->id_string, "\033[?6c");
@@ -1497,6 +1576,8 @@ Terminal *term_init(Config *mycfg, struct unicode_data *ucsdata,
     term->basic_erase_char.cc_next = 0;
     term->erase_char = term->basic_erase_char;
 
+    term_copy_stuff_from_conf(term);
+
     return term;
 }
 
@@ -1543,6 +1624,8 @@ void term_free(Terminal *term)
 
     expire_timer_context(term);
 
+    conf_free(term->conf);
+
     sfree(term);
 }
 
@@ -2243,7 +2326,7 @@ static void erase_lots(Terminal *term,
     if (start.y == 0 && start.x == 0 && end.x == 0 && erase_lattr)
        erasing_lines_from_top = 1;
 
-    if (term->cfg.erase_to_scrollback && erasing_lines_from_top) {
+    if (term->erase_to_scrollback && erasing_lines_from_top) {
        /* If it's a whole number of lines, starting at the top, and
         * we're fully erasing them, erase by scrolling and keep the
         * lines in the scrollback. */
@@ -2334,13 +2417,13 @@ static void toggle_mode(Terminal *term, int mode, int query, int state)
                term->blink_is_real = FALSE;
                term->vt52_bold = FALSE;
            } else {
-               term->blink_is_real = term->cfg.blinktext;
+               term->blink_is_real = term->blinktext;
            }
            term_schedule_tblink(term);
            break;
          case 3:                      /* DECCOLM: 80/132 columns */
            deselect(term);
-           if (!term->cfg.no_remote_resize)
+           if (!term->no_remote_resize)
                request_resize(term->frontend, state ? 132 : 80, term->rows);
            term->reset_132 = state;
            term->alt_t = term->marg_t = 0;
@@ -2387,7 +2470,7 @@ static void toggle_mode(Terminal *term, int mode, int query, int state)
          case 47:                     /* alternate screen */
            compatibility(OTHER);
            deselect(term);
-           swap_screen(term, term->cfg.no_alt_screen ? 0 : state, FALSE, FALSE);
+           swap_screen(term, term->no_alt_screen ? 0 : state, FALSE, FALSE);
            term->disptop = 0;
            break;
          case 1000:                   /* xterm mouse 1 (normal) */
@@ -2401,22 +2484,22 @@ static void toggle_mode(Terminal *term, int mode, int query, int state)
          case 1047:                   /* alternate screen */
            compatibility(OTHER);
            deselect(term);
-           swap_screen(term, term->cfg.no_alt_screen ? 0 : state, TRUE, TRUE);
+           swap_screen(term, term->no_alt_screen ? 0 : state, TRUE, TRUE);
            term->disptop = 0;
            break;
          case 1048:                   /* save/restore cursor */
-           if (!term->cfg.no_alt_screen)
+           if (!term->no_alt_screen)
                 save_cursor(term, state);
            if (!state) seen_disp_event(term);
            break;
          case 1049:                   /* cursor & alternate screen */
-           if (state && !term->cfg.no_alt_screen)
+           if (state && !term->no_alt_screen)
                save_cursor(term, state);
            if (!state) seen_disp_event(term);
            compatibility(OTHER);
            deselect(term);
-           swap_screen(term, term->cfg.no_alt_screen ? 0 : state, TRUE, FALSE);
-           if (!state && !term->cfg.no_alt_screen)
+           swap_screen(term, term->no_alt_screen ? 0 : state, TRUE, FALSE);
+           if (!state && !term->no_alt_screen)
                save_cursor(term, state);
            term->disptop = 0;
            break;
@@ -2454,14 +2537,14 @@ static void do_osc(Terminal *term)
        switch (term->esc_args[0]) {
          case 0:
          case 1:
-           if (!term->cfg.no_remote_wintitle)
+           if (!term->no_remote_wintitle)
                set_icon(term->frontend, term->osc_string);
            if (term->esc_args[0] == 1)
                break;
            /* fall through: parameter 0 means set both */
          case 2:
          case 21:
-           if (!term->cfg.no_remote_wintitle)
+           if (!term->no_remote_wintitle)
                set_title(term->frontend, term->osc_string);
            break;
        }
@@ -2471,10 +2554,10 @@ static void do_osc(Terminal *term)
 /*
  * ANSI printing routines.
  */
-static void term_print_setup(Terminal *term)
+static void term_print_setup(Terminal *term, char *printer)
 {
     bufchain_clear(&term->printer_buf);
-    term->print_job = printer_start_job(term->cfg.printer);
+    term->print_job = printer_start_job(printer);
 }
 static void term_print_flush(Terminal *term)
 {
@@ -2549,7 +2632,7 @@ static void term_out(Terminal *term)
             * Optionally log the session traffic to a file. Useful for
             * debugging and possibly also useful for actual logging.
             */
-           if (term->cfg.logtype == LGTYP_DEBUG && term->logctx)
+           if (term->logtype == LGTYP_DEBUG && term->logctx)
                logtraffic(term->logctx, (unsigned char) c, LGTYP_DEBUG);
        } else {
            c = unget;
@@ -2741,7 +2824,7 @@ static void term_out(Terminal *term)
                term->curs.x--;
            term->wrapnext = FALSE;
            /* destructive backspace might be disabled */
-           if (!term->cfg.no_dbackspace) {
+           if (!term->no_dbackspace) {
                check_boundary(term, term->curs.x, term->curs.y);
                check_boundary(term, term->curs.x+1, term->curs.y);
                copy_termchar(scrlineptr(term->curs.y),
@@ -2761,19 +2844,8 @@ static void term_out(Terminal *term)
                 */
                compatibility(ANSIMIN);
                if (term->ldisc) {
-                   char abuf[lenof(term->cfg.answerback)], *s, *d;
-                   for (s = term->cfg.answerback, d = abuf; *s;) {
-                       char *n;
-                       char c = ctrlparse(s, &n);
-                       if (n) {
-                           *d++ = c;
-                           s = n;
-                       } else {
-                           *d++ = *s++;
-                       }
-                   }
                    lpage_send(term->ldisc, DEFAULT_CODEPAGE,
-                              abuf, d - abuf, 0);
+                              term->answerback, term->answerbacklen, 0);
                }
                break;
              case '\007':            /* BEL: Bell */
@@ -2800,7 +2872,7 @@ static void term_out(Terminal *term)
                     * t seconds ago.
                     */
                    while (term->beephead &&
-                          term->beephead->ticks < ticks - term->cfg.bellovl_t) {
+                          term->beephead->ticks < ticks - term->bellovl_t) {
                        struct beeptime *tmp = term->beephead;
                        term->beephead = tmp->next;
                        sfree(tmp);
@@ -2809,16 +2881,16 @@ static void term_out(Terminal *term)
                        term->nbeeps--;
                    }
 
-                   if (term->cfg.bellovl && term->beep_overloaded &&
-                       ticks - term->lastbeep >= (unsigned)term->cfg.bellovl_s) {
+                   if (term->bellovl && term->beep_overloaded &&
+                       ticks - term->lastbeep >= (unsigned)term->bellovl_s) {
                        /*
                         * If we're currently overloaded and the
                         * last beep was more than s seconds ago,
                         * leave overload mode.
                         */
                        term->beep_overloaded = FALSE;
-                   } else if (term->cfg.bellovl && !term->beep_overloaded &&
-                              term->nbeeps >= term->cfg.bellovl_n) {
+                   } else if (term->bellovl && !term->beep_overloaded &&
+                              term->nbeeps >= term->bellovl_n) {
                        /*
                         * Now, if we have n or more beeps
                         * remaining in the queue, go into overload
@@ -2831,10 +2903,10 @@ static void term_out(Terminal *term)
                    /*
                     * Perform an actual beep if we're not overloaded.
                     */
-                   if (!term->cfg.bellovl || !term->beep_overloaded) {
-                       do_beep(term->frontend, term->cfg.beep);
+                   if (!term->bellovl || !term->beep_overloaded) {
+                       do_beep(term->frontend, term->beep);
 
-                       if (term->cfg.beep == BELL_VISUAL) {
+                       if (term->beep == BELL_VISUAL) {
                            term_schedule_vbell(term, FALSE, 0);
                        }
                    }
@@ -2876,12 +2948,12 @@ static void term_out(Terminal *term)
                seen_disp_event(term);
                term->paste_hold = 0;
 
-        if (term->cfg.crhaslf) {  
-                 if (term->curs.y == term->marg_b)
-                   scroll(term, term->marg_t, term->marg_b, 1, TRUE);
-                 else if (term->curs.y < term->rows - 1)
-                   term->curs.y++;
-        }
+               if (term->crhaslf) {
+                   if (term->curs.y == term->marg_b)
+                       scroll(term, term->marg_t, term->marg_b, 1, TRUE);
+                   else if (term->curs.y < term->rows - 1)
+                       term->curs.y++;
+               }
                if (term->logctx)
                    logtraffic(term->logctx, (unsigned char) c, LGTYP_ASCII);
                break;
@@ -2901,7 +2973,7 @@ static void term_out(Terminal *term)
                    scroll(term, term->marg_t, term->marg_b, 1, TRUE);
                else if (term->curs.y < term->rows - 1)
                    term->curs.y++;
-               if (term->cfg.lfhascr)
+               if (term->lfhascr)
                    term->curs.x = 0;
                term->wrapnext = FALSE;
                seen_disp_event(term);
@@ -2943,7 +3015,7 @@ static void term_out(Terminal *term)
                    if (DIRECT_CHAR(c))
                        width = 1;
                    if (!width)
-                       width = (term->cfg.cjk_ambig_wide ?
+                       width = (term->cjk_ambig_wide ?
                                 mk_wcwidth_cjk((wchar_t) c) :
                                 mk_wcwidth((wchar_t) c));
 
@@ -3166,7 +3238,7 @@ static void term_out(Terminal *term)
                    if (term->ldisc)   /* cause ldisc to notice changes */
                        ldisc_send(term->ldisc, NULL, 0, 0);
                    if (term->reset_132) {
-                       if (!term->cfg.no_remote_resize)
+                       if (!term->no_remote_resize)
                            request_resize(term->frontend, 80, term->rows);
                        term->reset_132 = 0;
                    }
@@ -3231,55 +3303,55 @@ static void term_out(Terminal *term)
                  /* GZD4: G0 designate 94-set */
                  case ANSI('A', '('):
                    compatibility(VT100);
-                   if (!term->cfg.no_remote_charset)
+                   if (!term->no_remote_charset)
                        term->cset_attr[0] = CSET_GBCHR;
                    break;
                  case ANSI('B', '('):
                    compatibility(VT100);
-                   if (!term->cfg.no_remote_charset)
+                   if (!term->no_remote_charset)
                        term->cset_attr[0] = CSET_ASCII;
                    break;
                  case ANSI('0', '('):
                    compatibility(VT100);
-                   if (!term->cfg.no_remote_charset)
+                   if (!term->no_remote_charset)
                        term->cset_attr[0] = CSET_LINEDRW;
                    break;
                  case ANSI('U', '('): 
                    compatibility(OTHER);
-                   if (!term->cfg.no_remote_charset)
+                   if (!term->no_remote_charset)
                        term->cset_attr[0] = CSET_SCOACS; 
                    break;
                  /* G1D4: G1-designate 94-set */
                  case ANSI('A', ')'):
                    compatibility(VT100);
-                   if (!term->cfg.no_remote_charset)
+                   if (!term->no_remote_charset)
                        term->cset_attr[1] = CSET_GBCHR;
                    break;
                  case ANSI('B', ')'):
                    compatibility(VT100);
-                   if (!term->cfg.no_remote_charset)
+                   if (!term->no_remote_charset)
                        term->cset_attr[1] = CSET_ASCII;
                    break;
                  case ANSI('0', ')'):
                    compatibility(VT100);
-                   if (!term->cfg.no_remote_charset)
+                   if (!term->no_remote_charset)
                        term->cset_attr[1] = CSET_LINEDRW;
                    break;
                  case ANSI('U', ')'): 
                    compatibility(OTHER);
-                   if (!term->cfg.no_remote_charset)
+                   if (!term->no_remote_charset)
                        term->cset_attr[1] = CSET_SCOACS; 
                    break;
                  /* DOCS: Designate other coding system */
                  case ANSI('8', '%'):  /* Old Linux code */
                  case ANSI('G', '%'):
                    compatibility(OTHER);
-                   if (!term->cfg.no_remote_charset)
+                   if (!term->no_remote_charset)
                        term->utf = 1;
                    break;
                  case ANSI('@', '%'):
                    compatibility(OTHER);
-                   if (!term->cfg.no_remote_charset)
+                   if (!term->no_remote_charset)
                        term->utf = 0;
                    break;
                }
@@ -3463,12 +3535,15 @@ static void term_out(Terminal *term)
                      case ANSI_QUE('i'):
                        compatibility(VT100);
                        {
+                           char *printer;
                            if (term->esc_nargs != 1) break;
-                           if (term->esc_args[0] == 5 && *term->cfg.printer) {
+                           if (term->esc_args[0] == 5 && 
+                               (printer = conf_get_str(term->conf,
+                                                       CONF_printer))[0]) {
                                term->printing = TRUE;
                                term->only_printing = !term->esc_query;
                                term->print_state = 0;
-                               term_print_setup(term);
+                               term_print_setup(term, printer);
                            } else if (term->esc_args[0] == 4 &&
                                       term->printing) {
                                term_print_finish(term);
@@ -3591,15 +3666,15 @@ static void term_out(Terminal *term)
                                    break;
                                  case 10:      /* SCO acs off */
                                    compatibility(SCOANSI);
-                                   if (term->cfg.no_remote_charset) break;
+                                   if (term->no_remote_charset) break;
                                    term->sco_acs = 0; break;
                                  case 11:      /* SCO acs on */
                                    compatibility(SCOANSI);
-                                   if (term->cfg.no_remote_charset) break;
+                                   if (term->no_remote_charset) break;
                                    term->sco_acs = 1; break;
                                  case 12:      /* SCO acs on, |0x80 */
                                    compatibility(SCOANSI);
-                                   if (term->cfg.no_remote_charset) break;
+                                   if (term->no_remote_charset) break;
                                    term->sco_acs = 2; break;
                                  case 22:      /* disable bold */
                                    compatibility2(OTHER, VT220);
@@ -3722,7 +3797,7 @@ static void term_out(Terminal *term)
                            && (term->esc_args[0] < 1 ||
                                term->esc_args[0] >= 24)) {
                            compatibility(VT340TEXT);
-                           if (!term->cfg.no_remote_resize)
+                           if (!term->no_remote_resize)
                                request_resize(term->frontend, term->cols,
                                               def(term->esc_args[0], 24));
                            deselect(term);
@@ -3742,7 +3817,7 @@ static void term_out(Terminal *term)
                                break;
                              case 3:
                                if (term->esc_nargs >= 3) {
-                                   if (!term->cfg.no_remote_resize)
+                                   if (!term->no_remote_resize)
                                        move_window(term->frontend,
                                                    def(term->esc_args[1], 0),
                                                    def(term->esc_args[2], 0));
@@ -3767,10 +3842,10 @@ static void term_out(Terminal *term)
                                break;
                              case 8:
                                if (term->esc_nargs >= 3) {
-                                   if (!term->cfg.no_remote_resize)
+                                   if (!term->no_remote_resize)
                                        request_resize(term->frontend,
-                                                      def(term->esc_args[2], term->cfg.width),
-                                                      def(term->esc_args[1], term->cfg.height));
+                                                      def(term->esc_args[2], term->conf_width),
+                                                      def(term->esc_args[1], term->conf_height));
                                }
                                break;
                              case 9:
@@ -3825,8 +3900,8 @@ static void term_out(Terminal *term)
                                break;
                              case 20:
                                if (term->ldisc &&
-                                   term->cfg.remote_qtitle_action != TITLE_NONE) {
-                                   if(term->cfg.remote_qtitle_action == TITLE_REAL)
+                                   term->remote_qtitle_action != TITLE_NONE) {
+                                   if(term->remote_qtitle_action == TITLE_REAL)
                                        p = get_window_title(term->frontend, TRUE);
                                    else
                                        p = EMPTY_WINDOW_TITLE;
@@ -3838,8 +3913,8 @@ static void term_out(Terminal *term)
                                break;
                              case 21:
                                if (term->ldisc &&
-                                   term->cfg.remote_qtitle_action != TITLE_NONE) {
-                                   if(term->cfg.remote_qtitle_action == TITLE_REAL)
+                                   term->remote_qtitle_action != TITLE_NONE) {
+                                   if(term->remote_qtitle_action == TITLE_REAL)
                                        p = get_window_title(term->frontend, FALSE);
                                    else
                                        p = EMPTY_WINDOW_TITLE;
@@ -3875,10 +3950,10 @@ static void term_out(Terminal *term)
                         */
                        compatibility(VT420);
                        if (term->esc_nargs == 1 && term->esc_args[0] > 0) {
-                           if (!term->cfg.no_remote_resize)
+                           if (!term->no_remote_resize)
                                request_resize(term->frontend, term->cols,
                                               def(term->esc_args[0],
-                                                  term->cfg.height));
+                                                  term->conf_height));
                            deselect(term);
                        }
                        break;
@@ -3890,10 +3965,11 @@ static void term_out(Terminal *term)
                         */
                        compatibility(VT340TEXT);
                        if (term->esc_nargs <= 1) {
-                           if (!term->cfg.no_remote_resize)
+                           if (!term->no_remote_resize)
                                request_resize(term->frontend,
                                               def(term->esc_args[0],
-                                                  term->cfg.width), term->rows);
+                                                  term->conf_width),
+                                              term->rows);
                            deselect(term);
                        }
                        break;
@@ -4095,7 +4171,7 @@ static void term_out(Terminal *term)
                         * Well we should do a soft reset at this point ...
                         */
                        if (!has_compat(VT420) && has_compat(VT100)) {
-                           if (!term->cfg.no_remote_resize) {
+                           if (!term->no_remote_resize) {
                                if (term->reset_132)
                                    request_resize(132, 24);
                                else
@@ -4330,7 +4406,7 @@ static void term_out(Terminal *term)
                     *     emulation.
                     */
                    term->vt52_mode = FALSE;
-                   term->blink_is_real = term->cfg.blinktext;
+                   term->blink_is_real = term->blinktext;
                    term_schedule_tblink(term);
                    break;
 #if 0
@@ -4474,7 +4550,7 @@ static void term_out(Terminal *term)
     }
 
     term_print_flush(term);
-    if (term->cfg.logflush)
+    if (term->logflush)
        logflush(term->logctx);
 }
 
@@ -4577,7 +4653,7 @@ static termchar *term_bidi_line(Terminal *term, struct termline *ldata,
     int it;
 
     /* Do Arabic shaping and bidi. */
-    if(!term->cfg.bidi || !term->cfg.arabicshaping) {
+    if(!term->bidi || !term->arabicshaping) {
 
        if (!term_bidi_cache_hit(term, scr_y, ldata->chars, term->cols)) {
 
@@ -4595,7 +4671,7 @@ static termchar *term_bidi_line(Terminal *term, struct termline *ldata,
 
                switch (uc & CSET_MASK) {
                  case CSET_LINEDRW:
-                   if (!term->cfg.rawcnp) {
+                   if (!term->rawcnp) {
                        uc = term->ucsdata->unitab_xterm[uc & 0xFF];
                        break;
                    }
@@ -4620,15 +4696,15 @@ static termchar *term_bidi_line(Terminal *term, struct termline *ldata,
                term->wcFrom[it].index = it;
            }
 
-           if(!term->cfg.bidi)
+           if(!term->bidi)
                do_bidi(term->wcFrom, term->cols);
 
            /* this is saved iff done from inside the shaping */
-           if(!term->cfg.bidi && term->cfg.arabicshaping)
+           if(!term->bidi && term->arabicshaping)
                for(it=0; it<term->cols; it++)
                    term->wcTo[it] = term->wcFrom[it];
 
-           if(!term->cfg.arabicshaping)
+           if(!term->arabicshaping)
                do_shape(term->wcFrom, term->wcTo, term->cols);
 
            if (term->ltemp_size < ldata->size) {
@@ -4690,14 +4766,14 @@ static void do_paint(Terminal *term, Context ctx, int may_optimise)
     /* Depends on:
      * screen array, disptop, scrtop,
      * selection, rv, 
-     * cfg.blinkpc, blink_is_real, tblinker, 
-     * curs.y, curs.x, cblinker, cfg.blink_cur, cursor_on, has_focus, wrapnext
+     * blinkpc, blink_is_real, tblinker, 
+     * curs.y, curs.x, cblinker, blink_cur, cursor_on, has_focus, wrapnext
      */
 
     /* Has the cursor position or type changed ? */
     if (term->cursor_on) {
        if (term->has_focus) {
-           if (term->cblinker || !term->cfg.blink_cur)
+           if (term->cblinker || !term->blink_cur)
                cursor = TATTR_ACTCURS;
            else
                cursor = 0;
@@ -4805,11 +4881,11 @@ static void do_paint(Terminal *term, Context ctx, int may_optimise)
            tchar = d->chr;
            tattr = d->attr;
 
-            if (!term->cfg.ansi_colour)
+            if (!term->ansi_colour)
                 tattr = (tattr & ~(ATTR_FGMASK | ATTR_BGMASK)) | 
                 ATTR_DEFFG | ATTR_DEFBG;
 
-           if (!term->cfg.xterm_256_colour) {
+           if (!term->xterm_256_colour) {
                int colour;
                colour = (tattr & ATTR_FGMASK) >> ATTR_FGSHIFT;
                if (colour >= 16 && colour < 256)
@@ -5265,7 +5341,7 @@ static void clipme(Terminal *term, pos top, pos bottom, int rect, int desel)
 
                switch (uc & CSET_MASK) {
                  case CSET_LINEDRW:
-                   if (!term->cfg.rawcnp) {
+                   if (!term->rawcnp) {
                        uc = term->ucsdata->unitab_xterm[uc & 0xFF];
                        break;
                    }
@@ -5647,8 +5723,8 @@ void term_mouse(Terminal *term, Mouse_Button braw, Mouse_Button bcooked,
     pos selpoint;
     termline *ldata;
     int raw_mouse = (term->xterm_mouse &&
-                    !term->cfg.no_mouse_rep &&
-                    !(term->cfg.mouse_override && shift));
+                    !term->no_mouse_rep &&
+                    !(term->mouse_override && shift));
     int default_seltype;
 
     if (y < 0) {
@@ -5755,7 +5831,7 @@ void term_mouse(Terminal *term, Mouse_Button braw, Mouse_Button bcooked,
      * Set the selection type (rectangular or normal) at the start
      * of a selection attempt, from the state of Alt.
      */
-    if (!alt ^ !term->cfg.rect_select)
+    if (!alt ^ !term->rect_select)
        default_seltype = RECTANGULAR;
     else
        default_seltype = LEXICOGRAPHIC;
@@ -5875,7 +5951,7 @@ int format_arrow_key(char *buf, Terminal *term, int xkey, int ctrl)
     if (term->vt52_mode)
        p += sprintf((char *) p, "\x1B%c", xkey);
     else {
-       int app_flg = (term->app_cursor_keys && !term->cfg.no_applic_c);
+       int app_flg = (term->app_cursor_keys && !term->no_applic_c);
 #if 0
        /*
         * RDB: VT100 & VT102 manuals both state the app cursor
@@ -5992,7 +6068,7 @@ void term_key(Terminal *term, Key_Sym keysym, wchar_t *text, size_t tlen,
     }
 
     /* Nethack keypad */
-    if (term->cfg.nethack_keypad) {
+    if (term->nethack_keypad) {
        char c = 0;
        switch (keysym) {
          case PK_KP1: c = 'b'; break;
@@ -6026,10 +6102,10 @@ void term_key(Terminal *term, Key_Sym keysym, wchar_t *text, size_t tlen,
         * In VT400 mode, PFn always emits an escape sequence.  In
         * Linux and tilde modes, this only happens in app keypad mode.
         */
-       if (term->cfg.funky_type == FUNKY_VT400 ||
-           ((term->cfg.funky_type == FUNKY_LINUX ||
-             term->cfg.funky_type == FUNKY_TILDE) &&
-            term->app_keypad_keys && !term->cfg.no_applic_k)) {
+       if (term->funky_type == FUNKY_VT400 ||
+           ((term->funky_type == FUNKY_LINUX ||
+             term->funky_type == FUNKY_TILDE) &&
+            term->app_keypad_keys && !term->no_applic_k)) {
            switch (keysym) {
              case PK_PF1: xkey = 'P'; break;
              case PK_PF2: xkey = 'Q'; break;
@@ -6038,7 +6114,7 @@ void term_key(Terminal *term, Key_Sym keysym, wchar_t *text, size_t tlen,
              default: break; /* else gcc warns `enum value not used' */
            }
        }
-       if (term->app_keypad_keys && !term->cfg.no_applic_k) {
+       if (term->app_keypad_keys && !term->no_applic_k) {
            switch (keysym) {
              case PK_KP0: xkey = 'p'; break;
              case PK_KP1: xkey = 'q'; break;
@@ -6054,7 +6130,7 @@ void term_key(Terminal *term, Key_Sym keysym, wchar_t *text, size_t tlen,
              case PK_KPENTER: xkey = 'M'; break;
              default: break; /* else gcc warns `enum value not used' */
            }
-           if (term->cfg.funky_type == FUNKY_XTERM && tlen > 0) {
+           if (term->funky_type == FUNKY_XTERM && tlen > 0) {
                /*
                 * xterm can't see the layout of the keypad, so it has
                 * to rely on the X keysyms returned by the keys.
@@ -6126,10 +6202,10 @@ void term_key(Terminal *term, Key_Sym keysym, wchar_t *text, size_t tlen,
        goto done;
       case PK_BACKSPACE:
            if (modifiers == 0)
-               *p++ = (term->cfg.bksp_is_delete ? 0x7F : 0x08);
+               *p++ = (term->bksp_is_delete ? 0x7F : 0x08);
            else if (modifiers == PKM_SHIFT)
                /* We do the opposite of what is configured */
-               *p++ = (term->cfg.bksp_is_delete ? 0x08 : 0x7F);
+               *p++ = (term->bksp_is_delete ? 0x08 : 0x7F);
            else break;
            goto done;
       case PK_TAB:
@@ -6157,7 +6233,7 @@ void term_key(Terminal *term, Key_Sym keysym, wchar_t *text, size_t tlen,
     }
 
     /* SCO function keys and editing keys */
-    if (term->cfg.funky_type == FUNKY_SCO) {
+    if (term->funky_type == FUNKY_SCO) {
        if (PK_ISFKEY(keysym) && keysym <= PK_F12) {
            static char const codes[] =
                "MNOPQRSTUVWX" "YZabcdefghij" "klmnopqrstuv" "wxyz@[\\]^_`{";
@@ -6187,7 +6263,7 @@ void term_key(Terminal *term, Key_Sym keysym, wchar_t *text, size_t tlen,
     if (PK_ISEDITING(keysym) && (modifiers & PKM_SHIFT) == 0) {
        int code;
 
-       if (term->cfg.funky_type == FUNKY_XTERM) {
+       if (term->funky_type == FUNKY_XTERM) {
            /* Xterm shuffles these keys, apparently. */
            switch (keysym) {
              case PK_HOME:     keysym = PK_INSERT;   break;
@@ -6201,7 +6277,7 @@ void term_key(Terminal *term, Key_Sym keysym, wchar_t *text, size_t tlen,
        }
 
        /* RXVT Home/End */
-       if (term->cfg.rxvt_homeend &&
+       if (term->rxvt_homeend &&
            (keysym == PK_HOME || keysym == PK_END)) {
            p += sprintf((char *) p, keysym == PK_HOME ? "\x1B[H" : "\x1BOw");
            goto done;
@@ -6244,7 +6320,7 @@ void term_key(Terminal *term, Key_Sym keysym, wchar_t *text, size_t tlen,
        /* Map Shift+F1-F10 to F11-F20 */
        if (keysym >= PK_F1 && keysym <= PK_F10 && (modifiers & PKM_SHIFT))
            keysym += 10;
-       if ((term->vt52_mode || term->cfg.funky_type == FUNKY_VT100P) &&
+       if ((term->vt52_mode || term->funky_type == FUNKY_VT100P) &&
            keysym <= PK_F14) {
            /* XXX This overrides the XTERM/VT52 mode below */
            int offt = 0;
@@ -6254,11 +6330,11 @@ void term_key(Terminal *term, Key_Sym keysym, wchar_t *text, size_t tlen,
                         'P' + keysym - PK_F1 - offt);
            goto done;
        }
-       if (term->cfg.funky_type == FUNKY_LINUX && keysym <= PK_F5) {
+       if (term->funky_type == FUNKY_LINUX && keysym <= PK_F5) {
            p += sprintf((char *) p, "\x1B[[%c", 'A' + keysym - PK_F1);
            goto done;
        }
-       if (term->cfg.funky_type == FUNKY_XTERM && keysym <= PK_F4) {
+       if (term->funky_type == FUNKY_XTERM && keysym <= PK_F4) {
            if (term->vt52_mode)
                p += sprintf((char *) p, "\x1B%c", 'P' + keysym - PK_F1);
            else
@@ -6478,9 +6554,9 @@ char *term_get_ttymode(Terminal *term, const char *mode)
 {
     char *val = NULL;
     if (strcmp(mode, "ERASE") == 0) {
-       val = term->cfg.bksp_is_delete ? "^?" : "^H";
+       val = term->bksp_is_delete ? "^?" : "^H";
     }
-    /* FIXME: perhaps we should set ONLCR based on cfg.lfhascr as well? */
+    /* FIXME: perhaps we should set ONLCR based on lfhascr as well? */
     /* FIXME: or ECHO and friends based on local echo state? */
     return dupstr(val);
 }
index aef47a5..42a67ed 100644 (file)
@@ -233,13 +233,13 @@ struct terminal_tag {
     struct unicode_data *ucsdata;
 
     /*
-     * We maintain a full _copy_ of a Config structure here, not
-     * merely a pointer to it. That way, when we're passed a new
-     * one for reconfiguration, we can check the differences and
-     * adjust the _current_ setting of (e.g.) auto wrap mode rather
-     * than only the default.
+     * We maintain a full copy of a Conf here, not merely a pointer
+     * to it. That way, when we're passed a new one for
+     * reconfiguration, we can check the differences and adjust the
+     * _current_ setting of (e.g.) auto wrap mode rather than only
+     * the default.
      */
-    Config cfg;
+    Conf *conf;
 
     /*
      * from_backend calls term_out, but it can also be called from
@@ -273,6 +273,52 @@ struct terminal_tag {
     int wcFromTo_size;
     struct bidi_cache_entry *pre_bidi_cache, *post_bidi_cache;
     int bidi_cache_size;
+
+    /*
+     * We copy a bunch of stuff out of the Conf structure into local
+     * fields in the Terminal structure, to avoid the repeated
+     * tree234 lookups which would be involved in fetching them from
+     * the former every time.
+     */
+    int ansi_colour;
+    char *answerback;
+    int answerbacklen;
+    int arabicshaping;
+    int beep;
+    int bellovl;
+    int bellovl_n;
+    int bellovl_s;
+    int bellovl_t;
+    int bidi;
+    int bksp_is_delete;
+    int blink_cur;
+    int blinktext;
+    int cjk_ambig_wide;
+    int conf_height;
+    int conf_width;
+    int crhaslf;
+    int erase_to_scrollback;
+    int funky_type;
+    int lfhascr;
+    int logflush;
+    int logtype;
+    int mouse_override;
+    int nethack_keypad;
+    int no_alt_screen;
+    int no_applic_c;
+    int no_applic_k;
+    int no_dbackspace;
+    int no_mouse_rep;
+    int no_remote_charset;
+    int no_remote_resize;
+    int no_remote_wintitle;
+    int rawcnp;
+    int rect_select;
+    int remote_qtitle_action;
+    int rxvt_homeend;
+    int scroll_on_disp;
+    int scroll_on_key;
+    int xterm_256_colour;
 };
 
 #define in_utf(term) ((term)->utf || (term)->ucsdata->line_codepage==CP_UTF8)
index 0175e8e..be1faf4 100644 (file)
 
 #include "putty.h"
 
-static const char *null_init(void *, void **, Config *, char *, int, char **,
+static const char *null_init(void *, void **, Conf *, char *, int, char **,
                             int, int);
-static const char *loop_init(void *, void **, Config *, char *, int, char **,
+static const char *loop_init(void *, void **, Conf *, char *, int, char **,
                             int, int);
 static void null_free(void *);
 static void loop_free(void *);
-static void null_reconfig(void *, Config *);
+static void null_reconfig(void *, Conf *);
 static int null_send(void *, char *, int);
 static int loop_send(void *, char *, int);
 static int null_sendbuffer(void *);
@@ -74,14 +74,14 @@ struct loop_state {
 };
 
 static const char *null_init(void *frontend_handle, void **backend_handle,
-                            Config *cfg, char *host, int port,
+                            Conf *conf, char *host, int port,
                             char **realhost, int nodelay, int keepalive) {
 
     return NULL;
 }
 
 static const char *loop_init(void *frontend_handle, void **backend_handle,
-                            Config *cfg, char *host, int port,
+                            Conf *conf, char *host, int port,
                             char **realhost, int nodelay, int keepalive) {
     struct loop_state *st = snew(struct loop_state);
 
@@ -101,7 +101,7 @@ static void loop_free(void *handle)
     sfree(handle);
 }
 
-static void null_reconfig(void *handle, Config *cfg) {
+static void null_reconfig(void *handle, Conf *conf) {
 
 }
 
index 8cbdd51..ab8ef97 100644 (file)
@@ -42,8 +42,8 @@ void gtk_setup_config_box(struct controlbox *b, int midsession, void *win)
                    "Control the scrollback in the window");
     ctrl_checkbox(s, "Scrollbar on left", 'l',
                  HELPCTX(no_help),
-                 dlg_stdcheckbox_handler,
-                  I(offsetof(Config,scrollbar_on_left)));
+                 conf_checkbox_handler,
+                  I(CONF_scrollbar_on_left));
     /*
      * Really this wants to go just after `Display scrollbar'. See
      * if we can find that control, and do some shuffling.
@@ -51,7 +51,7 @@ void gtk_setup_config_box(struct controlbox *b, int midsession, void *win)
     for (i = 0; i < s->ncontrols; i++) {
         c = s->ctrls[i];
         if (c->generic.type == CTRL_CHECKBOX &&
-            c->generic.context.i == offsetof(Config,scrollbar)) {
+            c->generic.context.i == CONF_scrollbar) {
             /*
              * Control i is the scrollbar checkbox.
              * Control s->ncontrols-1 is the scrollbar-on-left one.
@@ -89,29 +89,29 @@ void gtk_setup_config_box(struct controlbox *b, int midsession, void *win)
                     "Fonts for displaying non-bold text");
     ctrl_fontsel(s, "Font used for ordinary text", 'f',
                 HELPCTX(no_help),
-                dlg_stdfontsel_handler, I(offsetof(Config,font)));
+                conf_fontsel_handler, I(CONF_font));
     ctrl_fontsel(s, "Font used for wide (CJK) text", 'w',
                 HELPCTX(no_help),
-                dlg_stdfontsel_handler, I(offsetof(Config,widefont)));
+                conf_fontsel_handler, I(CONF_widefont));
     s = ctrl_getset(b, "Window/Fonts", "fontbold",
                     "Fonts for displaying bolded text");
     ctrl_fontsel(s, "Font used for bolded text", 'b',
                 HELPCTX(no_help),
-                dlg_stdfontsel_handler, I(offsetof(Config,boldfont)));
+                conf_fontsel_handler, I(CONF_boldfont));
     ctrl_fontsel(s, "Font used for bold wide text", 'i',
                 HELPCTX(no_help),
-                dlg_stdfontsel_handler, I(offsetof(Config,wideboldfont)));
+                conf_fontsel_handler, I(CONF_wideboldfont));
     ctrl_checkbox(s, "Use shadow bold instead of bold fonts", 'u',
                  HELPCTX(no_help),
-                 dlg_stdcheckbox_handler,
-                 I(offsetof(Config,shadowbold)));
+                 conf_checkbox_handler,
+                 I(CONF_shadowbold));
     ctrl_text(s, "(Note that bold fonts or shadow bolding are only"
              " used if you have not requested bolding to be done by"
              " changing the text colour.)",
               HELPCTX(no_help));
     ctrl_editbox(s, "Horizontal offset for shadow bold:", 'z', 20,
-                HELPCTX(no_help), dlg_stdeditbox_handler,
-                 I(offsetof(Config,shadowboldoffset)), I(-1));
+                HELPCTX(no_help), conf_editbox_handler,
+                 I(CONF_shadowboldoffset), I(-1));
 
     /*
      * Markus Kuhn feels, not totally unreasonably, that it's good
@@ -125,8 +125,8 @@ void gtk_setup_config_box(struct controlbox *b, int midsession, void *win)
                    "Character set translation on received data");
     ctrl_checkbox(s, "Override with UTF-8 if locale says so", 'l',
                  HELPCTX(translation_utf8_override),
-                 dlg_stdcheckbox_handler,
-                 I(offsetof(Config,utf8_override)));
+                 conf_checkbox_handler,
+                 I(CONF_utf8_override));
 
     if (!midsession) {
         /*
@@ -137,8 +137,7 @@ void gtk_setup_config_box(struct controlbox *b, int midsession, void *win)
         s = ctrl_getset(b, "Window/Behaviour", "x11",
                         "X Window System settings");
         ctrl_editbox(s, "Window class name:", 'z', 50,
-                     HELPCTX(no_help), dlg_stdeditbox_handler,
-                     I(offsetof(Config,winclass)),
-                     I(sizeof(((Config *)0)->winclass)));
+                     HELPCTX(no_help), conf_editbox_handler,
+                     I(CONF_winclass), I(1));
     }
 }
index 5472ca6..ec7ae43 100644 (file)
@@ -330,11 +330,10 @@ void dlg_editbox_set(union control *ctrl, void *dlg, char const *text)
      * The first call to "changed", if allowed to proceed normally,
      * will cause an EVENT_VALCHANGE event on the edit box, causing
      * a call to dlg_editbox_get() which will read the empty string
-     * out of the GtkEntry - and promptly write it straight into
-     * the Config structure, which is precisely where our `text'
-     * pointer is probably pointing, so the second editing
-     * operation will insert that instead of the string we
-     * originally asked for.
+     * out of the GtkEntry - and promptly write it straight into the
+     * Conf structure, which is precisely where our `text' pointer
+     * is probably pointing, so the second editing operation will
+     * insert that instead of the string we originally asked for.
      *
      * Hence, we must take our own copy of the text before we do
      * this.
@@ -344,7 +343,7 @@ void dlg_editbox_set(union control *ctrl, void *dlg, char const *text)
     sfree(tmpstring);
 }
 
-void dlg_editbox_get(union control *ctrl, void *dlg, char *buffer, int length)
+char *dlg_editbox_get(union control *ctrl, void *dlg)
 {
     struct dlgparam *dp = (struct dlgparam *)dlg;
     struct uctrl *uc = dlg_find_byctrl(dp, ctrl);
@@ -353,25 +352,16 @@ void dlg_editbox_get(union control *ctrl, void *dlg, char *buffer, int length)
 #if GTK_CHECK_VERSION(2,4,0)
     if (uc->combo) {
 #if GTK_CHECK_VERSION(2,6,0)
-       strncpy(buffer,
-               gtk_combo_box_get_active_text(GTK_COMBO_BOX(uc->combo)),
-               length);
+       return dupstr(gtk_combo_box_get_active_text(GTK_COMBO_BOX(uc->combo)));
 #else
-       strncpy(buffer,
-               gtk_entry_get_text
-               (GTK_ENTRY(gtk_bin_get_child(GTK_BIN(uc->combo)))),
-               length);
+       return dupstr(gtk_entry_get_text
+                     (GTK_ENTRY(gtk_bin_get_child(GTK_BIN(uc->combo)))));
 #endif
-       buffer[length-1] = '\0';
-       return;
     }
 #endif
 
     if (uc->entry) {
-       strncpy(buffer, gtk_entry_get_text(GTK_ENTRY(uc->entry)),
-               length);
-       buffer[length-1] = '\0';
-       return;
+       return dupstr(gtk_entry_get_text(GTK_ENTRY(uc->entry)));
     }
 
     assert(!"We shouldn't get here");
@@ -2826,12 +2816,12 @@ void set_dialog_action_area(GtkDialog *dlg, GtkWidget *w)
 #endif
 }
 
-int do_config_box(const char *title, Config *cfg, int midsession,
+int do_config_box(const char *title, Conf *conf, int midsession,
                  int protcfginfo)
 {
     GtkWidget *window, *hbox, *vbox, *cols, *label,
        *tree, *treescroll, *panels, *panelvbox;
-    int index, level;
+    int index, level, protocol;
     struct controlbox *ctrlbox;
     char *path;
 #if GTK_CHECK_VERSION(2,0,0)
@@ -2859,8 +2849,9 @@ int do_config_box(const char *title, Config *cfg, int midsession,
     window = gtk_dialog_new();
 
     ctrlbox = ctrl_new_box();
-    setup_config_box(ctrlbox, midsession, cfg->protocol, protcfginfo);
-    unix_setup_config_box(ctrlbox, midsession, cfg->protocol);
+    protocol = conf_get_int(conf, CONF_protocol);
+    setup_config_box(ctrlbox, midsession, protocol, protcfginfo);
+    unix_setup_config_box(ctrlbox, midsession, protocol);
     gtk_setup_config_box(ctrlbox, midsession, window);
 
     gtk_window_set_title(GTK_WINDOW(window), title);
@@ -3095,7 +3086,7 @@ int do_config_box(const char *title, Config *cfg, int midsession,
     }
 #endif
 
-    dp.data = cfg;
+    dp.data = conf;
     dlg_refresh(NULL, &dp);
 
     dp.shortcuts = &selparams[0].shortcuts;
index be8b2c3..c43c4e3 100644 (file)
@@ -45,7 +45,6 @@ ASSERT(sizeof(long) <= sizeof(gpointer));
 #endif
 
 /* Colours come in two flavours: configurable, and xterm-extended. */
-#define NCFGCOLOURS (lenof(((Config *)0)->colours))
 #define NEXTCOLOURS 240 /* 216 colour-cube plus 24 shades of grey */
 #define NALLCOLOURS (NCFGCOLOURS + NEXTCOLOURS)
 
@@ -88,8 +87,8 @@ struct gui_data {
     guint term_exit_idle_id;
     int alt_keycode;
     int alt_digits;
-    char wintitle[sizeof(((Config *)0)->wintitle)];
-    char icontitle[sizeof(((Config *)0)->wintitle)];
+    char *wintitle;
+    char *icontitle;
     int master_fd, master_func_id;
     void *ldisc;
     Backend *back;
@@ -98,14 +97,25 @@ struct gui_data {
     void *logctx;
     int exited;
     struct unicode_data ucsdata;
-    Config cfg;
+    Conf *conf;
     void *eventlogstuff;
     char *progname, **gtkargvstart;
     int ngtkargs;
     guint32 input_event_time; /* Timestamp of the most recent input event. */
     int reconfiguring;
+    /* Cached things out of conf that we refer to a lot */
+    int bold_colour;
+    int window_border;
+    int cursor_type;
 };
 
+static void cache_conf_values(struct gui_data *inst)
+{
+    inst->bold_colour = conf_get_int(inst->conf, CONF_bold_colour);
+    inst->window_border = conf_get_int(inst->conf, CONF_window_border);
+    inst->cursor_type = conf_get_int(inst->conf, CONF_cursor_type);
+}
+
 struct draw_ctx {
     GdkGC *gc;
     struct gui_data *inst;
@@ -134,7 +144,7 @@ void connection_fatal(void *frontend, char *p, ...)
     inst->exited = TRUE;
     fatal_message_box(inst->window, msg);
     sfree(msg);
-    if (inst->cfg.close_on_exit == FORCE_ON)
+    if (conf_get_int(inst->conf, CONF_close_on_exit) == FORCE_ON)
         cleanup_exit(1);
 }
 
@@ -394,7 +404,7 @@ char *get_window_title(void *frontend, int icon)
 gint delete_window(GtkWidget *widget, GdkEvent *event, gpointer data)
 {
     struct gui_data *inst = (struct gui_data *)data;
-    if (!inst->exited && inst->cfg.warn_on_close) {
+    if (!inst->exited && conf_get_int(inst->conf, CONF_warn_on_close)) {
        if (!reallyclose(inst))
            return TRUE;
     }
@@ -425,7 +435,7 @@ static void update_mouseptr(struct gui_data *inst)
 
 static void show_mouseptr(struct gui_data *inst, int show)
 {
-    if (!inst->cfg.hide_mouseptr)
+    if (!conf_get_int(inst->conf, CONF_hide_mouseptr))
        show = 1;
     inst->mouseptr_visible = show;
     update_mouseptr(inst);
@@ -436,8 +446,8 @@ void draw_backing_rect(struct gui_data *inst)
     GdkGC *gc = gdk_gc_new(inst->area->window);
     gdk_gc_set_foreground(gc, &inst->cols[258]);    /* default background */
     gdk_draw_rectangle(inst->pixmap, gc, 1, 0, 0,
-                      inst->cfg.width * inst->font_width + 2*inst->cfg.window_border,
-                      inst->cfg.height * inst->font_height + 2*inst->cfg.window_border);
+                      inst->width * inst->font_width + 2*inst->window_border,
+                      inst->height * inst->font_height + 2*inst->window_border);
     gdk_gc_unref(gc);
 }
 
@@ -450,11 +460,13 @@ gint configure_area(GtkWidget *widget, GdkEventConfigure *event, gpointer data)
      * See if the terminal size has changed, in which case we must
      * let the terminal know.
      */
-    w = (event->width - 2*inst->cfg.window_border) / inst->font_width;
-    h = (event->height - 2*inst->cfg.window_border) / inst->font_height;
+    w = (event->width - 2*inst->window_border) / inst->font_width;
+    h = (event->height - 2*inst->window_border) / inst->font_height;
     if (w != inst->width || h != inst->height) {
-       inst->cfg.width = inst->width = w;
-       inst->cfg.height = inst->height = h;
+       inst->width = w;
+       inst->height = h;
+       conf_set_int(inst->conf, CONF_width, inst->width);
+       conf_set_int(inst->conf, CONF_height, inst->height);
        need_size = 1;
     }
 
@@ -464,15 +476,13 @@ gint configure_area(GtkWidget *widget, GdkEventConfigure *event, gpointer data)
     }
 
     inst->pixmap = gdk_pixmap_new(widget->window,
-                                 (inst->cfg.width * inst->font_width +
-                                  2*inst->cfg.window_border),
-                                 (inst->cfg.height * inst->font_height +
-                                  2*inst->cfg.window_border), -1);
+                                 (w * inst->font_width + 2*inst->window_border),
+                                 (h * inst->font_height + 2*inst->window_border), -1);
 
     draw_backing_rect(inst);
 
     if (need_size && inst->term) {
-       term_size(inst->term, h, w, inst->cfg.savelines);
+       term_size(inst->term, h, w, conf_get_int(inst->conf, CONF_savelines));
     }
 
     if (inst->term)
@@ -618,7 +628,7 @@ gint key_event(GtkWidget *widget, GdkEventKey *event, gpointer data)
         * at all.
         */
        if (event->keyval == GDK_Page_Up && (event->state & GDK_SHIFT_MASK)) {
-           term_scroll(inst->term, 0, -inst->cfg.height/2);
+           term_scroll(inst->term, 0, -inst->height/2);
            return TRUE;
        }
        if (event->keyval == GDK_Page_Up && (event->state & GDK_CONTROL_MASK)) {
@@ -626,7 +636,7 @@ gint key_event(GtkWidget *widget, GdkEventKey *event, gpointer data)
            return TRUE;
        }
        if (event->keyval == GDK_Page_Down && (event->state & GDK_SHIFT_MASK)) {
-           term_scroll(inst->term, 0, +inst->cfg.height/2);
+           term_scroll(inst->term, 0, +inst->height/2);
            return TRUE;
        }
        if (event->keyval == GDK_Page_Down && (event->state & GDK_CONTROL_MASK)) {
@@ -755,7 +765,8 @@ gint key_event(GtkWidget *widget, GdkEventKey *event, gpointer data)
        /* We don't let GTK tell us what Backspace is! We know better. */
        if (event->keyval == GDK_BackSpace &&
            !(event->state & GDK_SHIFT_MASK)) {
-           output[1] = inst->cfg.bksp_is_delete ? '\x7F' : '\x08';
+           output[1] = conf_get_int(inst->conf, CONF_bksp_is_delete) ?
+               '\x7F' : '\x08';
            use_ucsoutput = FALSE;
            end = 2;
            special = TRUE;
@@ -763,7 +774,8 @@ gint key_event(GtkWidget *widget, GdkEventKey *event, gpointer data)
        /* For Shift Backspace, do opposite of what is configured. */
        if (event->keyval == GDK_BackSpace &&
            (event->state & GDK_SHIFT_MASK)) {
-           output[1] = inst->cfg.bksp_is_delete ? '\x08' : '\x7F';
+           output[1] = conf_get_int(inst->conf, CONF_bksp_is_delete) ?
+               '\x08' : '\x7F';
            use_ucsoutput = FALSE;
            end = 2;
            special = TRUE;
@@ -786,7 +798,7 @@ gint key_event(GtkWidget *widget, GdkEventKey *event, gpointer data)
        /*
         * NetHack keypad mode.
         */
-       if (inst->cfg.nethack_keypad) {
+       if (conf_get_int(inst->conf, CONF_nethack_keypad)) {
            char *keys = NULL;
            switch (event->keyval) {
              case GDK_KP_1: case GDK_KP_End: keys = "bB\002"; break;
@@ -815,7 +827,8 @@ gint key_event(GtkWidget *widget, GdkEventKey *event, gpointer data)
        /*
         * Application keypad mode.
         */
-       if (inst->term->app_keypad_keys && !inst->cfg.no_applic_k) {
+       if (inst->term->app_keypad_keys &&
+           !conf_get_int(inst->conf, CONF_no_applic_k)) {
            int xkey = 0;
            switch (event->keyval) {
              case GDK_Num_Lock: xkey = 'P'; break;
@@ -829,7 +842,7 @@ gint key_event(GtkWidget *widget, GdkEventKey *event, gpointer data)
                 * in xterm function key mode we change which two...
                 */
              case GDK_KP_Add:
-               if (inst->cfg.funky_type == FUNKY_XTERM) {
+               if (conf_get_int(inst->conf, CONF_funky_type) == FUNKY_XTERM) {
                    if (event->state & GDK_SHIFT_MASK)
                        xkey = 'l';
                    else
@@ -876,6 +889,7 @@ gint key_event(GtkWidget *widget, GdkEventKey *event, gpointer data)
         */
        {
            int code = 0;
+           int funky_type = conf_get_int(inst->conf, CONF_funky_type);
            switch (event->keyval) {
              case GDK_F1:
                code = (event->state & GDK_SHIFT_MASK ? 23 : 11);
@@ -959,7 +973,7 @@ gint key_event(GtkWidget *widget, GdkEventKey *event, gpointer data)
                break;
            }
            /* Reorder edit keys to physical order */
-           if (inst->cfg.funky_type == FUNKY_VT400 && code <= 6)
+           if (funky_type == FUNKY_VT400 && code <= 6)
                code = "\0\2\1\4\5\3\6"[code];
 
            if (inst->term->vt52_mode && code > 0 && code <= 6) {
@@ -968,7 +982,7 @@ gint key_event(GtkWidget *widget, GdkEventKey *event, gpointer data)
                goto done;
            }
 
-           if (inst->cfg.funky_type == FUNKY_SCO &&     /* SCO function keys */
+           if (funky_type == FUNKY_SCO &&     /* SCO function keys */
                code >= 11 && code <= 34) {
                char codes[] = "MNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz@[\\]^_`{";
                int index = 0;
@@ -992,7 +1006,7 @@ gint key_event(GtkWidget *widget, GdkEventKey *event, gpointer data)
                use_ucsoutput = FALSE;
                goto done;
            }
-           if (inst->cfg.funky_type == FUNKY_SCO &&     /* SCO small keypad */
+           if (funky_type == FUNKY_SCO &&     /* SCO small keypad */
                code >= 1 && code <= 6) {
                char codes[] = "HL.FIG";
                if (code == 3) {
@@ -1004,7 +1018,7 @@ gint key_event(GtkWidget *widget, GdkEventKey *event, gpointer data)
                use_ucsoutput = FALSE;
                goto done;
            }
-           if ((inst->term->vt52_mode || inst->cfg.funky_type == FUNKY_VT100P) &&
+           if ((inst->term->vt52_mode || funky_type == FUNKY_VT100P) &&
                code >= 11 && code <= 24) {
                int offt = 0;
                if (code > 15)
@@ -1020,12 +1034,12 @@ gint key_event(GtkWidget *widget, GdkEventKey *event, gpointer data)
                use_ucsoutput = FALSE;
                goto done;
            }
-           if (inst->cfg.funky_type == FUNKY_LINUX && code >= 11 && code <= 15) {
+           if (funky_type == FUNKY_LINUX && code >= 11 && code <= 15) {
                end = 1 + sprintf(output+1, "\x1B[[%c", code + 'A' - 11);
                use_ucsoutput = FALSE;
                goto done;
            }
-           if (inst->cfg.funky_type == FUNKY_XTERM && code >= 11 && code <= 14) {
+           if (funky_type == FUNKY_XTERM && code >= 11 && code <= 14) {
                if (inst->term->vt52_mode)
                    end = 1 + sprintf(output+1, "\x1B%c", code + 'P' - 11);
                else
@@ -1033,7 +1047,8 @@ gint key_event(GtkWidget *widget, GdkEventKey *event, gpointer data)
                use_ucsoutput = FALSE;
                goto done;
            }
-           if (inst->cfg.rxvt_homeend && (code == 1 || code == 4)) {
+           if ((code == 1 || code == 4) &&
+               conf_get_int(inst->conf, CONF_rxvt_homeend)) {
                end = 1 + sprintf(output+1, code == 1 ? "\x1B[H" : "\x1BOw");
                use_ucsoutput = FALSE;
                goto done;
@@ -1166,12 +1181,13 @@ gboolean button_internal(struct gui_data *inst, guint32 timestamp,
       default: return FALSE;          /* don't know this event type */
     }
 
-    if (send_raw_mouse && !(inst->cfg.mouse_override && shift) &&
+    if (send_raw_mouse && !(shift && conf_get_int(inst->conf,
+                                                 CONF_mouse_override)) &&
        act != MA_CLICK && act != MA_RELEASE)
        return TRUE;                   /* we ignore these in raw mouse mode */
 
-    x = (ex - inst->cfg.window_border) / inst->font_width;
-    y = (ey - inst->cfg.window_border) / inst->font_height;
+    x = (ex - inst->window_border) / inst->font_width;
+    y = (ey - inst->window_border) / inst->font_height;
 
     term_mouse(inst->term, button, translate_button(button), act,
               x, y, shift, ctrl, alt);
@@ -1231,8 +1247,8 @@ gint motion_event(GtkWidget *widget, GdkEventMotion *event, gpointer data)
     else
        return FALSE;                  /* don't even know what button! */
 
-    x = (event->x - inst->cfg.window_border) / inst->font_width;
-    y = (event->y - inst->cfg.window_border) / inst->font_height;
+    x = (event->x - inst->window_border) / inst->font_width;
+    y = (event->y - inst->window_border) / inst->font_height;
 
     term_mouse(inst->term, button, translate_button(button), MA_DRAG,
               x, y, shift, ctrl, alt);
@@ -1255,13 +1271,14 @@ void frontend_keypress(void *handle)
 static gint idle_exit_func(gpointer data)
 {
     struct gui_data *inst = (struct gui_data *)data;
-    int exitcode;
+    int exitcode, close_on_exit;
 
     if (!inst->exited &&
         (exitcode = inst->back->exitcode(inst->backhandle)) >= 0) {
        inst->exited = TRUE;
-       if (inst->cfg.close_on_exit == FORCE_ON ||
-           (inst->cfg.close_on_exit == AUTO && exitcode == 0))
+       close_on_exit = conf_get_int(inst->conf, CONF_close_on_exit);
+       if (close_on_exit == FORCE_ON ||
+           (close_on_exit == AUTO && exitcode == 0))
            gtk_main_quit();           /* just go */
        if (inst->ldisc) {
            ldisc_free(inst->ldisc);
@@ -1364,7 +1381,7 @@ void set_busy_status(void *frontend, int status)
 void set_raw_mouse_mode(void *frontend, int activate)
 {
     struct gui_data *inst = (struct gui_data *)frontend;
-    activate = activate && !inst->cfg.no_mouse_rep;
+    activate = activate && !conf_get_int(inst->conf, CONF_no_mouse_rep);
     send_raw_mouse = activate;
     update_mouseptr(inst);
 }
@@ -1412,8 +1429,8 @@ void request_resize(void *frontend, int w, int h)
     offset_x = outer.width - inner.width;
     offset_y = outer.height - inner.height;
 
-    area_x = inst->font_width * w + 2*inst->cfg.window_border;
-    area_y = inst->font_height * h + 2*inst->cfg.window_border;
+    area_x = inst->font_width * w + 2*inst->window_border;
+    area_y = inst->font_height * h + 2*inst->window_border;
 
     /*
      * Now we must set the size request on the drawing area back to
@@ -1495,7 +1512,7 @@ void palette_set(void *frontend, int n, int r, int g, int b)
 void palette_reset(void *frontend)
 {
     struct gui_data *inst = (struct gui_data *)frontend;
-    /* This maps colour indices in inst->cfg to those used in inst->cols. */
+    /* This maps colour indices in inst->conf to those used in inst->cols. */
     static const int ww[] = {
        256, 257, 258, 259, 260, 261,
        0, 8, 1, 9, 2, 10, 3, 11,
@@ -1513,9 +1530,12 @@ void palette_reset(void *frontend)
     }
 
     for (i = 0; i < NCFGCOLOURS; i++) {
-       inst->cols[ww[i]].red = inst->cfg.colours[i][0] * 0x0101;
-       inst->cols[ww[i]].green = inst->cfg.colours[i][1] * 0x0101;
-       inst->cols[ww[i]].blue = inst->cfg.colours[i][2] * 0x0101;
+       inst->cols[ww[i]].red =
+           conf_get_int_int(inst->conf, CONF_colours, i*3+0) * 0x0101;
+       inst->cols[ww[i]].green =
+           conf_get_int_int(inst->conf, CONF_colours, i*3+1) * 0x0101;
+       inst->cols[ww[i]].blue = 
+           conf_get_int_int(inst->conf, CONF_colours, i*3+2) * 0x0101;
     }
 
     for (i = 0; i < NEXTCOLOURS; i++) {
@@ -1537,8 +1557,10 @@ void palette_reset(void *frontend)
     for (i = 0; i < NALLCOLOURS; i++) {
        if (!success[i])
            g_error("%s: couldn't allocate colour %d (#%02x%02x%02x)\n",
-                    appname, i, inst->cfg.colours[i][0],
-                    inst->cfg.colours[i][1], inst->cfg.colours[i][2]);
+                    appname, i,
+                   conf_get_int_int(inst->conf, CONF_colours, i*3+0),
+                   conf_get_int_int(inst->conf, CONF_colours, i*3+1),
+                   conf_get_int_int(inst->conf, CONF_colours, i*3+2));
     }
 
     /* Since Default Background may have changed, ensure that space
@@ -1898,30 +1920,40 @@ static void set_window_titles(struct gui_data *inst)
      * is life.
      */
     gtk_window_set_title(GTK_WINDOW(inst->window), inst->wintitle);
-    if (!inst->cfg.win_name_always)
+    if (!conf_get_int(inst->conf, CONF_win_name_always))
        gdk_window_set_icon_name(inst->window->window, inst->icontitle);
 }
 
 void set_title(void *frontend, char *title)
 {
     struct gui_data *inst = (struct gui_data *)frontend;
-    strncpy(inst->wintitle, title, lenof(inst->wintitle));
-    inst->wintitle[lenof(inst->wintitle)-1] = '\0';
+    sfree(inst->wintitle);
+    inst->wintitle = dupstr(title);
     set_window_titles(inst);
 }
 
 void set_icon(void *frontend, char *title)
 {
     struct gui_data *inst = (struct gui_data *)frontend;
-    strncpy(inst->icontitle, title, lenof(inst->icontitle));
-    inst->icontitle[lenof(inst->icontitle)-1] = '\0';
+    sfree(inst->icontitle);
+    inst->icontitle = dupstr(title);
+    set_window_titles(inst);
+}
+
+void set_title_and_icon(void *frontend, char *title, char *icon)
+{
+    struct gui_data *inst = (struct gui_data *)frontend;
+    sfree(inst->wintitle);
+    inst->wintitle = dupstr(title);
+    sfree(inst->icontitle);
+    inst->icontitle = dupstr(icon);
     set_window_titles(inst);
 }
 
 void set_sbar(void *frontend, int total, int start, int page)
 {
     struct gui_data *inst = (struct gui_data *)frontend;
-    if (!inst->cfg.scrollbar)
+    if (!conf_get_int(inst->conf, CONF_scrollbar))
        return;
     inst->sbar_adjust->lower = 0;
     inst->sbar_adjust->upper = total;
@@ -1938,7 +1970,7 @@ void scrollbar_moved(GtkAdjustment *adj, gpointer data)
 {
     struct gui_data *inst = (struct gui_data *)data;
 
-    if (!inst->cfg.scrollbar)
+    if (!conf_get_int(inst->conf, CONF_scrollbar))
        return;
     if (!inst->ignore_sbar)
        term_scroll(inst->term, 1, (int)adj->value);
@@ -2027,11 +2059,11 @@ void do_text_internal(Context ctx, int x, int y, wchar_t *text, int len,
        nfg = nbg;
        nbg = t;
     }
-    if (inst->cfg.bold_colour && (attr & ATTR_BOLD)) {
+    if (inst->bold_colour && (attr & ATTR_BOLD)) {
        if (nfg < 16) nfg |= 8;
        else if (nfg >= 256) nfg |= 1;
     }
-    if (inst->cfg.bold_colour && (attr & ATTR_BLINK)) {
+    if (inst->bold_colour && (attr & ATTR_BLINK)) {
        if (nbg < 16) nbg |= 8;
        else if (nbg >= 256) nbg |= 1;
     }
@@ -2049,7 +2081,7 @@ void do_text_internal(Context ctx, int x, int y, wchar_t *text, int len,
        widefactor = 1;
     }
 
-    if ((attr & ATTR_BOLD) && !inst->cfg.bold_colour) {
+    if ((attr & ATTR_BOLD) && !inst->bold_colour) {
        bold = 1;
        fontid |= 1;
     } else {
@@ -2086,8 +2118,8 @@ void do_text_internal(Context ctx, int x, int y, wchar_t *text, int len,
     {
        GdkRectangle r;
 
-       r.x = x*inst->font_width+inst->cfg.window_border;
-       r.y = y*inst->font_height+inst->cfg.window_border;
+       r.x = x*inst->font_width+inst->window_border;
+       r.y = y*inst->font_height+inst->window_border;
        r.width = rlen*widefactor*inst->font_width;
        r.height = inst->font_height;
        gdk_gc_set_clip_rectangle(gc, &r);
@@ -2095,8 +2127,8 @@ void do_text_internal(Context ctx, int x, int y, wchar_t *text, int len,
 
     gdk_gc_set_foreground(gc, &inst->cols[nbg]);
     gdk_draw_rectangle(inst->pixmap, gc, 1,
-                      x*inst->font_width+inst->cfg.window_border,
-                      y*inst->font_height+inst->cfg.window_border,
+                      x*inst->font_width+inst->window_border,
+                      y*inst->font_height+inst->window_border,
                       rlen*widefactor*inst->font_width, inst->font_height);
 
     gdk_gc_set_foreground(gc, &inst->cols[nfg]);
@@ -2116,8 +2148,8 @@ void do_text_internal(Context ctx, int x, int y, wchar_t *text, int len,
                                 text + combining, len, gcs, len*10+1, ".",
                                 NULL, NULL);
            unifont_draw_text(inst->pixmap, gc, inst->fonts[fontid],
-                             x*inst->font_width+inst->cfg.window_border,
-                             y*inst->font_height+inst->cfg.window_border+inst->fonts[0]->ascent,
+                             x*inst->font_width+inst->window_border,
+                             y*inst->font_height+inst->window_border+inst->fonts[0]->ascent,
                              gcs, mblen, widefactor > 1, bold, inst->font_width);
        }
 
@@ -2128,10 +2160,10 @@ void do_text_internal(Context ctx, int x, int y, wchar_t *text, int len,
        int uheight = inst->fonts[0]->ascent + 1;
        if (uheight >= inst->font_height)
            uheight = inst->font_height - 1;
-       gdk_draw_line(inst->pixmap, gc, x*inst->font_width+inst->cfg.window_border,
-                     y*inst->font_height + uheight + inst->cfg.window_border,
-                     (x+len)*widefactor*inst->font_width-1+inst->cfg.window_border,
-                     y*inst->font_height + uheight + inst->cfg.window_border);
+       gdk_draw_line(inst->pixmap, gc, x*inst->font_width+inst->window_border,
+                     y*inst->font_height + uheight + inst->window_border,
+                     (x+len)*widefactor*inst->font_width-1+inst->window_border,
+                     y*inst->font_height + uheight + inst->window_border);
     }
 
     if ((lattr & LATTR_MODE) != LATTR_NORM) {
@@ -2146,10 +2178,10 @@ void do_text_internal(Context ctx, int x, int y, wchar_t *text, int len,
        int i;
        for (i = 0; i < len * widefactor * inst->font_width; i++) {
            gdk_draw_pixmap(inst->pixmap, gc, inst->pixmap,
-                           x*inst->font_width+inst->cfg.window_border + 2*i,
-                           y*inst->font_height+inst->cfg.window_border,
-                           x*inst->font_width+inst->cfg.window_border + 2*i+1,
-                           y*inst->font_height+inst->cfg.window_border,
+                           x*inst->font_width+inst->window_border + 2*i,
+                           y*inst->font_height+inst->window_border,
+                           x*inst->font_width+inst->window_border + 2*i+1,
+                           y*inst->font_height+inst->window_border,
                            len * widefactor * inst->font_width - i, inst->font_height);
        }
        len *= 2;
@@ -2162,10 +2194,10 @@ void do_text_internal(Context ctx, int x, int y, wchar_t *text, int len,
                dt = 1, db = 0;
            for (i = 0; i < inst->font_height; i+=2) {
                gdk_draw_pixmap(inst->pixmap, gc, inst->pixmap,
-                               x*inst->font_width+inst->cfg.window_border,
-                               y*inst->font_height+inst->cfg.window_border+dt*i+db,
-                               x*inst->font_width+inst->cfg.window_border,
-                               y*inst->font_height+inst->cfg.window_border+dt*(i+1),
+                               x*inst->font_width+inst->window_border,
+                               y*inst->font_height+inst->window_border+dt*i+db,
+                               x*inst->font_width+inst->window_border,
+                               y*inst->font_height+inst->window_border+dt*(i+1),
                                len * widefactor * inst->font_width, inst->font_height-i-1);
            }
        }
@@ -2198,10 +2230,10 @@ void do_text(Context ctx, int x, int y, wchar_t *text, int len,
     }
 
     gdk_draw_pixmap(inst->area->window, gc, inst->pixmap,
-                   x*inst->font_width+inst->cfg.window_border,
-                   y*inst->font_height+inst->cfg.window_border,
-                   x*inst->font_width+inst->cfg.window_border,
-                   y*inst->font_height+inst->cfg.window_border,
+                   x*inst->font_width+inst->window_border,
+                   y*inst->font_height+inst->window_border,
+                   x*inst->font_width+inst->window_border,
+                   y*inst->font_height+inst->window_border,
                    len*widefactor*inst->font_width, inst->font_height);
 }
 
@@ -2219,7 +2251,7 @@ void do_cursor(Context ctx, int x, int y, wchar_t *text, int len,
        passive = 1;
     } else
        passive = 0;
-    if ((attr & TATTR_ACTCURS) && inst->cfg.cursor_type != 0) {
+    if ((attr & TATTR_ACTCURS) && inst->cursor_type != 0) {
        attr &= ~TATTR_ACTCURS;
         active = 1;
     } else
@@ -2244,7 +2276,7 @@ void do_cursor(Context ctx, int x, int y, wchar_t *text, int len,
        len *= 2;
     }
 
-    if (inst->cfg.cursor_type == 0) {
+    if (inst->cursor_type == 0) {
        /*
         * An active block cursor will already have been done by
         * the above do_text call, so we only need to do anything
@@ -2253,8 +2285,8 @@ void do_cursor(Context ctx, int x, int y, wchar_t *text, int len,
        if (passive) {
            gdk_gc_set_foreground(gc, &inst->cols[261]);
            gdk_draw_rectangle(inst->pixmap, gc, 0,
-                              x*inst->font_width+inst->cfg.window_border,
-                              y*inst->font_height+inst->cfg.window_border,
+                              x*inst->font_width+inst->window_border,
+                              y*inst->font_height+inst->window_border,
                               len*widefactor*inst->font_width-1, inst->font_height-1);
        }
     } else {
@@ -2268,13 +2300,13 @@ void do_cursor(Context ctx, int x, int y, wchar_t *text, int len,
        else
            char_width = inst->font_width;
 
-       if (inst->cfg.cursor_type == 1) {
+       if (inst->cursor_type == 1) {
            uheight = inst->fonts[0]->ascent + 1;
            if (uheight >= inst->font_height)
                uheight = inst->font_height - 1;
 
-           startx = x * inst->font_width + inst->cfg.window_border;
-           starty = y * inst->font_height + inst->cfg.window_border + uheight;
+           startx = x * inst->font_width + inst->window_border;
+           starty = y * inst->font_height + inst->window_border + uheight;
            dx = 1;
            dy = 0;
            length = len * widefactor * char_width;
@@ -2282,8 +2314,8 @@ void do_cursor(Context ctx, int x, int y, wchar_t *text, int len,
            int xadjust = 0;
            if (attr & TATTR_RIGHTCURS)
                xadjust = char_width - 1;
-           startx = x * inst->font_width + inst->cfg.window_border + xadjust;
-           starty = y * inst->font_height + inst->cfg.window_border;
+           startx = x * inst->font_width + inst->window_border + xadjust;
+           starty = y * inst->font_height + inst->window_border;
            dx = 0;
            dy = 1;
            length = inst->font_height;
@@ -2305,10 +2337,10 @@ void do_cursor(Context ctx, int x, int y, wchar_t *text, int len,
     }
 
     gdk_draw_pixmap(inst->area->window, gc, inst->pixmap,
-                   x*inst->font_width+inst->cfg.window_border,
-                   y*inst->font_height+inst->cfg.window_border,
-                   x*inst->font_width+inst->cfg.window_border,
-                   y*inst->font_height+inst->cfg.window_border,
+                   x*inst->font_width+inst->window_border,
+                   y*inst->font_height+inst->window_border,
+                   x*inst->font_width+inst->window_border,
+                   y*inst->font_height+inst->window_border,
                    len*widefactor*inst->font_width, inst->font_height);
 }
 
@@ -2468,7 +2500,7 @@ static void help(FILE *fp) {
 }
 
 int do_cmdline(int argc, char **argv, int do_everything, int *allow_launch,
-               struct gui_data *inst, Config *cfg)
+               struct gui_data *inst, Conf *conf)
 {
     int err = 0;
     char *val;
@@ -2507,7 +2539,7 @@ int do_cmdline(int argc, char **argv, int do_everything, int *allow_launch,
            p = "-title";
 
         ret = cmdline_process_param(p, (argc > 1 ? argv[1] : NULL),
-                                    do_everything ? 1 : -1, cfg);
+                                    do_everything ? 1 : -1, conf);
 
        if (ret == -2) {
            cmdline_error("option \"%s\" requires an argument", p);
@@ -2519,34 +2551,41 @@ int do_cmdline(int argc, char **argv, int do_everything, int *allow_launch,
         }
 
        if (!strcmp(p, "-fn") || !strcmp(p, "-font")) {
+           FontSpec fs;
            EXPECTS_ARG;
            SECOND_PASS_ONLY;
-           strncpy(cfg->font.name, val, sizeof(cfg->font.name));
-           cfg->font.name[sizeof(cfg->font.name)-1] = '\0';
+           strncpy(fs.name, val, sizeof(fs.name));
+           fs.name[sizeof(fs.name)-1] = '\0';
+           conf_set_fontspec(conf, CONF_font, &fs);
 
        } else if (!strcmp(p, "-fb")) {
+           FontSpec fs;
            EXPECTS_ARG;
            SECOND_PASS_ONLY;
-           strncpy(cfg->boldfont.name, val, sizeof(cfg->boldfont.name));
-           cfg->boldfont.name[sizeof(cfg->boldfont.name)-1] = '\0';
+           strncpy(fs.name, val, sizeof(fs.name));
+           fs.name[sizeof(fs.name)-1] = '\0';
+           conf_set_fontspec(conf, CONF_boldfont, &fs);
 
        } else if (!strcmp(p, "-fw")) {
+           FontSpec fs;
            EXPECTS_ARG;
            SECOND_PASS_ONLY;
-           strncpy(cfg->widefont.name, val, sizeof(cfg->widefont.name));
-           cfg->widefont.name[sizeof(cfg->widefont.name)-1] = '\0';
+           strncpy(fs.name, val, sizeof(fs.name));
+           fs.name[sizeof(fs.name)-1] = '\0';
+           conf_set_fontspec(conf, CONF_widefont, &fs);
 
        } else if (!strcmp(p, "-fwb")) {
+           FontSpec fs;
            EXPECTS_ARG;
            SECOND_PASS_ONLY;
-           strncpy(cfg->wideboldfont.name, val, sizeof(cfg->wideboldfont.name));
-           cfg->wideboldfont.name[sizeof(cfg->wideboldfont.name)-1] = '\0';
+           strncpy(fs.name, val, sizeof(fs.name));
+           fs.name[sizeof(fs.name)-1] = '\0';
+           conf_set_fontspec(conf, CONF_wideboldfont, &fs);
 
        } else if (!strcmp(p, "-cs")) {
            EXPECTS_ARG;
            SECOND_PASS_ONLY;
-           strncpy(cfg->line_codepage, val, sizeof(cfg->line_codepage));
-           cfg->line_codepage[sizeof(cfg->line_codepage)-1] = '\0';
+           conf_set_str(conf, CONF_line_codepage, val);
 
        } else if (!strcmp(p, "-geometry")) {
            int flags, x, y;
@@ -2556,9 +2595,9 @@ int do_cmdline(int argc, char **argv, int do_everything, int *allow_launch,
 
            flags = XParseGeometry(val, &x, &y, &w, &h);
            if (flags & WidthValue)
-               cfg->width = (int)w;
+               conf_set_int(conf, CONF_width, w);
            if (flags & HeightValue)
-               cfg->height = (int)h;
+               conf_set_int(conf, CONF_height, h);
 
             if (flags & (XValue | YValue)) {
                 inst->xpos = x;
@@ -2571,7 +2610,7 @@ int do_cmdline(int argc, char **argv, int do_everything, int *allow_launch,
        } else if (!strcmp(p, "-sl")) {
            EXPECTS_ARG;
            SECOND_PASS_ONLY;
-           cfg->savelines = atoi(val);
+           conf_set_int(conf, CONF_savelines, atoi(val));
 
        } else if (!strcmp(p, "-fg") || !strcmp(p, "-bg") ||
                   !strcmp(p, "-bfg") || !strcmp(p, "-bbg") ||
@@ -2593,9 +2632,9 @@ int do_cmdline(int argc, char **argv, int do_everything, int *allow_launch,
                         !strcmp(p, "-cfg") ? 4 :
                         !strcmp(p, "-cbg") ? 5 : -1);
                assert(index != -1);
-               cfg->colours[index][0] = col.red / 256;
-               cfg->colours[index][1] = col.green / 256;
-               cfg->colours[index][2] = col.blue / 256;
+               conf_set_int_int(conf, CONF_colours, index*3+0, col.red / 256);
+               conf_set_int_int(conf, CONF_colours, index*3+1,col.green/ 256);
+               conf_set_int_int(conf, CONF_colours, index*3+2, col.blue/ 256);
            }
 
        } else if (use_pty_argv && !strcmp(p, "-e")) {
@@ -2618,43 +2657,44 @@ int do_cmdline(int argc, char **argv, int do_everything, int *allow_launch,
        } else if (!strcmp(p, "-title")) {
            EXPECTS_ARG;
            SECOND_PASS_ONLY;
-           strncpy(cfg->wintitle, val, sizeof(cfg->wintitle));
-           cfg->wintitle[sizeof(cfg->wintitle)-1] = '\0';
+           conf_set_str(conf, CONF_wintitle, val);
 
        } else if (!strcmp(p, "-log")) {
+           Filename fn;
            EXPECTS_ARG;
            SECOND_PASS_ONLY;
-           strncpy(cfg->logfilename.path, val, sizeof(cfg->logfilename.path));
-           cfg->logfilename.path[sizeof(cfg->logfilename.path)-1] = '\0';
-           cfg->logtype = LGTYP_DEBUG;
+           strncpy(fn.path, val, sizeof(fn.path));
+           fn.path[sizeof(fn.path)-1] = '\0';
+           conf_set_filename(conf, CONF_logfilename, &fn);
+           conf_set_int(conf, CONF_logtype, LGTYP_DEBUG);
 
        } else if (!strcmp(p, "-ut-") || !strcmp(p, "+ut")) {
            SECOND_PASS_ONLY;
-           cfg->stamp_utmp = 0;
+           conf_set_int(conf, CONF_stamp_utmp, 0);
 
        } else if (!strcmp(p, "-ut")) {
            SECOND_PASS_ONLY;
-           cfg->stamp_utmp = 1;
+           conf_set_int(conf, CONF_stamp_utmp, 1);
 
        } else if (!strcmp(p, "-ls-") || !strcmp(p, "+ls")) {
            SECOND_PASS_ONLY;
-           cfg->login_shell = 0;
+           conf_set_int(conf, CONF_login_shell, 0);
 
        } else if (!strcmp(p, "-ls")) {
            SECOND_PASS_ONLY;
-           cfg->login_shell = 1;
+           conf_set_int(conf, CONF_login_shell, 1);
 
        } else if (!strcmp(p, "-nethack")) {
            SECOND_PASS_ONLY;
-           cfg->nethack_keypad = 1;
+           conf_set_int(conf, CONF_nethack_keypad, 1);
 
        } else if (!strcmp(p, "-sb-") || !strcmp(p, "+sb")) {
            SECOND_PASS_ONLY;
-           cfg->scrollbar = 0;
+           conf_set_int(conf, CONF_scrollbar, 0);
 
        } else if (!strcmp(p, "-sb")) {
            SECOND_PASS_ONLY;
-           cfg->scrollbar = 0;
+           conf_set_int(conf, CONF_scrollbar, 1);
 
        } else if (!strcmp(p, "-name")) {
            EXPECTS_ARG;
@@ -2673,7 +2713,7 @@ int do_cmdline(int argc, char **argv, int do_everything, int *allow_launch,
             exit(1);
 
        } else if(p[0] != '-' && (!do_everything ||
-                                  process_nonoption_arg(p, cfg,
+                                  process_nonoption_arg(p, conf,
                                                        allow_launch))) {
             /* do nothing */
 
@@ -2701,6 +2741,10 @@ void uxsel_input_remove(int id) {
 
 void setup_fonts_ucs(struct gui_data *inst)
 {
+    int shadowbold = conf_get_int(inst->conf, CONF_shadowbold);
+    int shadowboldoffset = conf_get_int(inst->conf, CONF_shadowboldoffset);
+    FontSpec *fs;
+
     if (inst->fonts[0])
         unifont_destroy(inst->fonts[0]);
     if (inst->fonts[1])
@@ -2710,54 +2754,50 @@ void setup_fonts_ucs(struct gui_data *inst)
     if (inst->fonts[3])
         unifont_destroy(inst->fonts[3]);
 
-    inst->fonts[0] = unifont_create(inst->area, inst->cfg.font.name,
-                                   FALSE, FALSE,
-                                   inst->cfg.shadowboldoffset,
-                                   inst->cfg.shadowbold);
+    fs = conf_get_fontspec(inst->conf, CONF_font);
+    inst->fonts[0] = unifont_create(inst->area, fs->name, FALSE, FALSE,
+                                   shadowboldoffset, shadowbold);
     if (!inst->fonts[0]) {
        fprintf(stderr, "%s: unable to load font \"%s\"\n", appname,
-               inst->cfg.font.name);
+               fs->name);
        exit(1);
     }
 
-    if (inst->cfg.shadowbold || !inst->cfg.boldfont.name[0]) {
+    fs = conf_get_fontspec(inst->conf, CONF_boldfont);
+    if (shadowbold || !fs->name[0]) {
        inst->fonts[1] = NULL;
     } else {
-       inst->fonts[1] = unifont_create(inst->area, inst->cfg.boldfont.name,
-                                       FALSE, TRUE,
-                                       inst->cfg.shadowboldoffset,
-                                       inst->cfg.shadowbold);
+       inst->fonts[1] = unifont_create(inst->area, fs->name, FALSE, TRUE,
+                                       shadowboldoffset, shadowbold);
        if (!inst->fonts[1]) {
            fprintf(stderr, "%s: unable to load bold font \"%s\"\n", appname,
-                   inst->cfg.boldfont.name);
+                   fs->name);
            exit(1);
        }
     }
 
-    if (inst->cfg.widefont.name[0]) {
-       inst->fonts[2] = unifont_create(inst->area, inst->cfg.widefont.name,
-                                       TRUE, FALSE,
-                                       inst->cfg.shadowboldoffset,
-                                       inst->cfg.shadowbold);
+    fs = conf_get_fontspec(inst->conf, CONF_widefont);
+    if (fs->name[0]) {
+       inst->fonts[2] = unifont_create(inst->area, fs->name, TRUE, FALSE,
+                                       shadowboldoffset, shadowbold);
        if (!inst->fonts[2]) {
            fprintf(stderr, "%s: unable to load wide font \"%s\"\n", appname,
-                   inst->cfg.widefont.name);
+                   fs->name);
            exit(1);
        }
     } else {
        inst->fonts[2] = NULL;
     }
 
-    if (inst->cfg.shadowbold || !inst->cfg.wideboldfont.name[0]) {
+    fs = conf_get_fontspec(inst->conf, CONF_wideboldfont);
+    if (shadowbold || !fs->name[0]) {
        inst->fonts[3] = NULL;
     } else {
-       inst->fonts[3] = unifont_create(inst->area,
-                                       inst->cfg.wideboldfont.name, TRUE,
-                                       TRUE, inst->cfg.shadowboldoffset,
-                                       inst->cfg.shadowbold);
+       inst->fonts[3] = unifont_create(inst->area, fs->name, TRUE, TRUE,
+                                       shadowboldoffset, shadowbold);
        if (!inst->fonts[3]) {
            fprintf(stderr, "%s: unable to load wide bold font \"%s\"\n", appname,
-                   inst->cfg.boldfont.name);
+                   fs->name);
            exit(1);
        }
     }
@@ -2765,20 +2805,21 @@ void setup_fonts_ucs(struct gui_data *inst)
     inst->font_width = inst->fonts[0]->width;
     inst->font_height = inst->fonts[0]->height;
 
-    inst->direct_to_font = init_ucs(&inst->ucsdata, inst->cfg.line_codepage,
-                                   inst->cfg.utf8_override,
+    inst->direct_to_font = init_ucs(&inst->ucsdata,
+                                   conf_get_str(inst->conf, CONF_line_codepage),
+                                   conf_get_int(inst->conf, CONF_utf8_override),
                                    inst->fonts[0]->public_charset,
-                                   inst->cfg.vtmode);
+                                   conf_get_int(inst->conf, CONF_vtmode));
 }
 
 void set_geom_hints(struct gui_data *inst)
 {
     GdkGeometry geom;
-    geom.min_width = inst->font_width + 2*inst->cfg.window_border;
-    geom.min_height = inst->font_height + 2*inst->cfg.window_border;
+    geom.min_width = inst->font_width + 2*inst->window_border;
+    geom.min_height = inst->font_height + 2*inst->window_border;
     geom.max_width = geom.max_height = -1;
-    geom.base_width = 2*inst->cfg.window_border;
-    geom.base_height = 2*inst->cfg.window_border;
+    geom.base_width = 2*inst->window_border;
+    geom.base_height = 2*inst->window_border;
     geom.width_inc = inst->font_width;
     geom.height_inc = inst->font_height;
     geom.min_aspect = geom.max_aspect = 0;
@@ -2831,7 +2872,7 @@ void event_log_menuitem(GtkMenuItem *item, gpointer data)
 
 void change_settings_menuitem(GtkMenuItem *item, gpointer data)
 {
-    /* This maps colour indices in inst->cfg to those used in inst->cols. */
+    /* This maps colour indices in inst->conf to those used in inst->cols. */
     static const int ww[] = {
        256, 257, 258, 259, 260, 261,
        0, 8, 1, 9, 2, 10, 3, 11,
@@ -2839,8 +2880,8 @@ void change_settings_menuitem(GtkMenuItem *item, gpointer data)
     };
     struct gui_data *inst = (struct gui_data *)data;
     char *title = dupcat(appname, " Reconfiguration", NULL);
-    Config cfg2, oldcfg;
-    int i, need_size;
+    Conf *oldconf, *newconf;
+    int i, j, need_size;
 
     assert(lenof(ww) == NCFGCOLOURS);
 
@@ -2849,43 +2890,48 @@ void change_settings_menuitem(GtkMenuItem *item, gpointer data)
     else
       inst->reconfiguring = TRUE;
 
-    cfg2 = inst->cfg;                  /* structure copy */
+    oldconf = inst->conf;
+    newconf = conf_copy(inst->conf);
 
-    if (do_config_box(title, &cfg2, 1,
+    if (do_config_box(title, newconf, 1,
                      inst->back?inst->back->cfg_info(inst->backhandle):0)) {
-
-        oldcfg = inst->cfg;            /* structure copy */
-        inst->cfg = cfg2;              /* structure copy */
+        inst->conf = newconf;
 
         /* Pass new config data to the logging module */
-        log_reconfig(inst->logctx, &cfg2);
+        log_reconfig(inst->logctx, inst->conf);
         /*
          * Flush the line discipline's edit buffer in the case
          * where local editing has just been disabled.
          */
+       ldisc_configure(inst->ldisc, inst->conf);
         if (inst->ldisc)
            ldisc_send(inst->ldisc, NULL, 0, 0);
         /* Pass new config data to the terminal */
-        term_reconfig(inst->term, &cfg2);
+        term_reconfig(inst->term, inst->conf);
         /* Pass new config data to the back end */
         if (inst->back)
-           inst->back->reconfig(inst->backhandle, &cfg2);
+           inst->back->reconfig(inst->backhandle, inst->conf);
+
+       cache_conf_values(inst);
 
         /*
-         * Just setting inst->cfg is sufficient to cause colour
+         * Just setting inst->conf is sufficient to cause colour
          * setting changes to appear on the next ESC]R palette
          * reset. But we should also check whether any colour
-         * settings have been changed, and revert the ones that
-         * have to the new default, on the assumption that the user
-         * is most likely to want an immediate update.
+         * settings have been changed, and revert the ones that have
+         * to the new default, on the assumption that the user is
+         * most likely to want an immediate update.
          */
         for (i = 0; i < NCFGCOLOURS; i++) {
-            if (oldcfg.colours[i][0] != cfg2.colours[i][0] ||
-                oldcfg.colours[i][1] != cfg2.colours[i][1] ||
-                oldcfg.colours[i][2] != cfg2.colours[i][2]) {
-                real_palette_set(inst, ww[i], cfg2.colours[i][0],
-                                 cfg2.colours[i][1],
-                                 cfg2.colours[i][2]);
+           for (j = 0; j < 3; j++)
+               if (conf_get_int_int(oldconf, CONF_colours, i*3+j) !=
+                   conf_get_int_int(newconf, CONF_colours, i*3+j))
+                   break;
+           if (j < 3) {
+                real_palette_set(inst, ww[i],
+                                conf_get_int_int(newconf,CONF_colours,i*3+0),
+                                conf_get_int_int(newconf,CONF_colours,i*3+1),
+                                conf_get_int_int(newconf,CONF_colours,i*3+2));
 
                /*
                 * If the default background has changed, we must
@@ -2903,35 +2949,48 @@ void change_settings_menuitem(GtkMenuItem *item, gpointer data)
          * If the scrollbar needs to be shown, hidden, or moved
          * from one end to the other of the window, do so now.
          */
-        if (oldcfg.scrollbar != cfg2.scrollbar) {
-            if (cfg2.scrollbar)
+        if (conf_get_int(oldconf, CONF_scrollbar) !=
+           conf_get_int(newconf, CONF_scrollbar)) {
+            if (conf_get_int(newconf, CONF_scrollbar))
                 gtk_widget_show(inst->sbar);
             else
                 gtk_widget_hide(inst->sbar);
         }
-        if (oldcfg.scrollbar_on_left != cfg2.scrollbar_on_left) {
+        if (conf_get_int(oldconf, CONF_scrollbar_on_left) !=
+           conf_get_int(newconf, CONF_scrollbar_on_left)) {
             gtk_box_reorder_child(inst->hbox, inst->sbar,
-                                  cfg2.scrollbar_on_left ? 0 : 1);
+                                  conf_get_int(newconf, CONF_scrollbar_on_left)
+                                 ? 0 : 1);
         }
 
         /*
          * Change the window title, if required.
          */
-        if (strcmp(oldcfg.wintitle, cfg2.wintitle))
-            set_title(inst, cfg2.wintitle);
+        if (strcmp(conf_get_str(oldconf, CONF_wintitle),
+                  conf_get_str(newconf, CONF_wintitle)))
+            set_title(inst, conf_get_str(oldconf, CONF_wintitle));
        set_window_titles(inst);
 
         /*
          * Redo the whole tangled fonts and Unicode mess if
          * necessary.
          */
-        if (strcmp(oldcfg.font.name, cfg2.font.name) ||
-            strcmp(oldcfg.boldfont.name, cfg2.boldfont.name) ||
-            strcmp(oldcfg.widefont.name, cfg2.widefont.name) ||
-            strcmp(oldcfg.wideboldfont.name, cfg2.wideboldfont.name) ||
-            strcmp(oldcfg.line_codepage, cfg2.line_codepage) ||
-           oldcfg.vtmode != cfg2.vtmode ||
-           oldcfg.shadowbold != cfg2.shadowbold) {
+        if (strcmp(conf_get_fontspec(oldconf, CONF_font)->name,
+                  conf_get_fontspec(newconf, CONF_font)->name) ||
+           strcmp(conf_get_fontspec(oldconf, CONF_boldfont)->name,
+                  conf_get_fontspec(newconf, CONF_boldfont)->name) ||
+           strcmp(conf_get_fontspec(oldconf, CONF_widefont)->name,
+                  conf_get_fontspec(newconf, CONF_widefont)->name) ||
+           strcmp(conf_get_fontspec(oldconf, CONF_wideboldfont)->name,
+                  conf_get_fontspec(newconf, CONF_wideboldfont)->name) ||
+           strcmp(conf_get_str(oldconf, CONF_line_codepage),
+                  conf_get_str(newconf, CONF_line_codepage)) ||
+           conf_get_int(oldconf, CONF_vtmode) !=
+           conf_get_int(newconf, CONF_vtmode) ||
+           conf_get_int(oldconf, CONF_shadowbold) !=
+           conf_get_int(newconf, CONF_shadowbold) ||
+           conf_get_int(oldconf, CONF_shadowboldoffset) !=
+           conf_get_int(newconf, CONF_shadowboldoffset)) {
             setup_fonts_ucs(inst);
             need_size = 1;
         } else
@@ -2940,10 +2999,16 @@ void change_settings_menuitem(GtkMenuItem *item, gpointer data)
         /*
          * Resize the window.
          */
-        if (oldcfg.width != cfg2.width || oldcfg.height != cfg2.height ||
-            oldcfg.window_border != cfg2.window_border || need_size) {
+        if (conf_get_int(oldconf, CONF_width) !=
+           conf_get_int(newconf, CONF_width) ||
+           conf_get_int(oldconf, CONF_height) !=
+           conf_get_int(newconf, CONF_height) ||
+           conf_get_int(oldconf, CONF_window_border) !=
+           conf_get_int(newconf, CONF_window_border) ||
+           need_size) {
             set_geom_hints(inst);
-            request_resize(inst, cfg2.width, cfg2.height);
+            request_resize(inst, conf_get_int(newconf, CONF_width),
+                          conf_get_int(newconf, CONF_height));
         } else {
            /*
             * The above will have caused a call to term_size() for
@@ -2952,9 +3017,10 @@ void change_settings_menuitem(GtkMenuItem *item, gpointer data)
             * happened and we will need an explicit term_size()
             * here.
             */
-           if (oldcfg.savelines != cfg2.savelines)
+           if (conf_get_int(oldconf, CONF_savelines) !=
+               conf_get_int(newconf, CONF_savelines))
                term_size(inst->term, inst->term->rows, inst->term->cols,
-                         cfg2.savelines);
+                         conf_get_int(newconf, CONF_savelines));
        }
 
         term_invalidate(inst->term);
@@ -2964,6 +3030,10 @@ void change_settings_menuitem(GtkMenuItem *item, gpointer data)
         * border has been redrawn as well as the text area.
         */
        gtk_widget_queue_draw(inst->area);
+
+       conf_free(oldconf);
+    } else {
+       conf_free(newconf);
     }
     sfree(title);
     inst->reconfiguring = FALSE;
@@ -3056,11 +3126,11 @@ void dup_session_menuitem(GtkMenuItem *item, gpointer gdata)
 {
     struct gui_data *inst = (struct gui_data *)gdata;
     /*
-     * For this feature we must marshal cfg and (possibly) pty_argv
+     * For this feature we must marshal conf and (possibly) pty_argv
      * into a byte stream, create a pipe, and send this byte stream
      * to the child through the pipe.
      */
-    int i, ret, size;
+    int i, ret, sersize, size;
     char *data;
     char option[80];
     int pipefd[2];
@@ -3070,16 +3140,16 @@ void dup_session_menuitem(GtkMenuItem *item, gpointer gdata)
        return;
     }
 
-    size = sizeof(inst->cfg);
+    size = sersize = conf_serialised_size(inst->conf);
     if (use_pty_argv && pty_argv) {
        for (i = 0; pty_argv[i]; i++)
            size += strlen(pty_argv[i]) + 1;
     }
 
     data = snewn(size, char);
-    memcpy(data, &inst->cfg, sizeof(inst->cfg));
+    conf_serialise(inst->conf, data);
     if (use_pty_argv && pty_argv) {
-       int p = sizeof(inst->cfg);
+       int p = sersize;
        for (i = 0; pty_argv[i]; i++) {
            strcpy(data + p, pty_argv[i]);
            p += strlen(pty_argv[i]) + 1;
@@ -3101,9 +3171,9 @@ void dup_session_menuitem(GtkMenuItem *item, gpointer gdata)
     sfree(data);
 }
 
-int read_dupsession_data(struct gui_data *inst, Config *cfg, char *arg)
+int read_dupsession_data(struct gui_data *inst, Conf *conf, char *arg)
 {
-    int fd, i, ret, size;
+    int fd, i, ret, size, size_used;
     char *data;
 
     if (sscanf(arg, "---[%d,%d]", &fd, &size) != 2) {
@@ -3124,10 +3194,10 @@ int read_dupsession_data(struct gui_data *inst, Config *cfg, char *arg)
        exit(1);
     }
 
-    memcpy(cfg, data, sizeof(Config));
-    if (use_pty_argv && size > sizeof(Config)) {
+    size_used = conf_deserialise(conf, data, size);
+    if (use_pty_argv && size > size_used) {
        int n = 0;
-       i = sizeof(Config);
+       i = size_used;
        while (i < size) {
            while (i < size && data[i]) i++;
            if (i >= size) {
@@ -3141,7 +3211,7 @@ int read_dupsession_data(struct gui_data *inst, Config *cfg, char *arg)
        pty_argv = snewn(n+1, char *);
        pty_argv[n] = NULL;
        n = 0;
-       i = sizeof(Config);
+       i = size_used;
        while (i < size) {
            char *p = data + i;
            while (i < size && data[i]) i++;
@@ -3320,33 +3390,36 @@ void update_specials_menu(void *frontend)
 
 static void start_backend(struct gui_data *inst)
 {
-    extern Backend *select_backend(Config *cfg);
+    extern Backend *select_backend(Conf *conf);
     char *realhost;
     const char *error;
+    char *s;
 
-    inst->back = select_backend(&inst->cfg);
+    inst->back = select_backend(inst->conf);
 
     error = inst->back->init((void *)inst, &inst->backhandle,
-                            &inst->cfg, inst->cfg.host, inst->cfg.port,
-                            &realhost, inst->cfg.tcp_nodelay,
-                            inst->cfg.tcp_keepalives);
+                            inst->conf,
+                            conf_get_str(inst->conf, CONF_host),
+                            conf_get_int(inst->conf, CONF_port),
+                            &realhost,
+                            conf_get_int(inst->conf, CONF_tcp_nodelay),
+                            conf_get_int(inst->conf, CONF_tcp_keepalives));
 
     if (error) {
        char *msg = dupprintf("Unable to open connection to %s:\n%s",
-                             inst->cfg.host, error);
+                             conf_get_str(inst->conf, CONF_host), error);
        inst->exited = TRUE;
        fatal_message_box(inst->window, msg);
        sfree(msg);
        exit(0);
     }
 
-    if (inst->cfg.wintitle[0]) {
-       set_title(inst, inst->cfg.wintitle);
-       set_icon(inst, inst->cfg.wintitle);
+    s = conf_get_str(inst->conf, CONF_wintitle);
+    if (s[0]) {
+       set_title_and_icon(inst, s, s);
     } else {
        char *title = make_default_wintitle(realhost);
-       set_title(inst, title);
-       set_icon(inst, title);
+       set_title_and_icon(inst, title, title);
        sfree(title);
     }
     sfree(realhost);
@@ -3356,7 +3429,7 @@ static void start_backend(struct gui_data *inst)
     term_provide_resize_fn(inst->term, inst->back->size, inst->backhandle);
 
     inst->ldisc =
-       ldisc_create(&inst->cfg, inst->term, inst->back, inst->backhandle,
+       ldisc_create(inst->conf, inst->term, inst->back, inst->backhandle,
                     inst);
 
     gtk_widget_set_sensitive(inst->restartitem, FALSE);
@@ -3364,7 +3437,7 @@ static void start_backend(struct gui_data *inst)
 
 int pt_main(int argc, char **argv)
 {
-    extern int cfgbox(Config *cfg);
+    extern int cfgbox(Conf *conf);
     struct gui_data *inst;
 
     /*
@@ -3374,6 +3447,8 @@ int pt_main(int argc, char **argv)
     memset(inst, 0, sizeof(*inst));
     inst->alt_keycode = -1;            /* this one needs _not_ to be zero */
     inst->busy_status = BUSY_NOT;
+    inst->conf = conf_new();
+    inst->wintitle = inst->icontitle = NULL;
 
     /* defer any child exit handling until we're ready to deal with
      * it */
@@ -3395,7 +3470,7 @@ int pt_main(int argc, char **argv)
     }
 
     if (argc > 1 && !strncmp(argv[1], "---", 3)) {
-       read_dupsession_data(inst, &inst->cfg, argv[1]);
+       read_dupsession_data(inst, inst->conf, argv[1]);
        /* Splatter this argument so it doesn't clutter a ps listing */
        memset(argv[1], 0, strlen(argv[1]));
     } else {
@@ -3403,19 +3478,19 @@ int pt_main(int argc, char **argv)
         * a session. This gets set to TRUE if something happens to change
         * that (e.g., a hostname is specified on the command-line). */
        int allow_launch = FALSE;
-       if (do_cmdline(argc, argv, 0, &allow_launch, inst, &inst->cfg))
+       if (do_cmdline(argc, argv, 0, &allow_launch, inst, inst->conf))
            exit(1);                   /* pre-defaults pass to get -class */
-       do_defaults(NULL, &inst->cfg);
-       if (do_cmdline(argc, argv, 1, &allow_launch, inst, &inst->cfg))
+       do_defaults(NULL, inst->conf);
+       if (do_cmdline(argc, argv, 1, &allow_launch, inst, inst->conf))
            exit(1);                   /* post-defaults, do everything */
 
-       cmdline_run_saved(&inst->cfg);
+       cmdline_run_saved(inst->conf);
 
        if (loaded_session)
            allow_launch = TRUE;
 
-       if ((!allow_launch || !cfg_launchable(&inst->cfg)) &&
-           !cfgbox(&inst->cfg))
+       if ((!allow_launch || !conf_launchable(inst->conf)) &&
+           !cfgbox(inst->conf))
            exit(0);                   /* config box hit Cancel */
     }
 
@@ -3430,21 +3505,25 @@ int pt_main(int argc, char **argv)
     init_cutbuffers();
 
     inst->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
-    if (inst->cfg.winclass[0])
-        gtk_window_set_wmclass(GTK_WINDOW(inst->window),
-                               inst->cfg.winclass, inst->cfg.winclass);
+    {
+        const char *winclass = conf_get_str(inst->conf, CONF_winclass);
+        if (*winclass)
+            gtk_window_set_wmclass(GTK_WINDOW(inst->window),
+                                   winclass, winclass);
+    }
 
     /*
      * Set up the colour map.
      */
     palette_reset(inst);
 
-    inst->width = inst->cfg.width;
-    inst->height = inst->cfg.height;
+    inst->width = conf_get_int(inst->conf, CONF_width);
+    inst->height = conf_get_int(inst->conf, CONF_height);
+    cache_conf_values(inst);
 
     gtk_drawing_area_size(GTK_DRAWING_AREA(inst->area),
-                         inst->font_width * inst->cfg.width + 2*inst->cfg.window_border,
-                         inst->font_height * inst->cfg.height + 2*inst->cfg.window_border);
+                         inst->font_width * inst->width + 2*inst->window_border,
+                         inst->font_height * inst->height + 2*inst->window_border);
     inst->sbar_adjust = GTK_ADJUSTMENT(gtk_adjustment_new(0,0,0,0,0,0));
     inst->sbar = gtk_vscrollbar_new(inst->sbar_adjust);
     inst->hbox = GTK_BOX(gtk_hbox_new(FALSE, 0));
@@ -3453,10 +3532,10 @@ int pt_main(int argc, char **argv)
      * unwanted, so we can pop it up quickly if it suddenly becomes
      * desirable.
      */
-    if (inst->cfg.scrollbar_on_left)
+    if (conf_get_int(inst->conf, CONF_scrollbar_on_left))
         gtk_box_pack_start(inst->hbox, inst->sbar, FALSE, FALSE, 0);
     gtk_box_pack_start(inst->hbox, inst->area, TRUE, TRUE, 0);
-    if (!inst->cfg.scrollbar_on_left)
+    if (!conf_get_int(inst->conf, CONF_scrollbar_on_left))
         gtk_box_pack_start(inst->hbox, inst->sbar, FALSE, FALSE, 0);
 
     gtk_container_add(GTK_CONTAINER(inst->window), GTK_WIDGET(inst->hbox));
@@ -3464,7 +3543,7 @@ int pt_main(int argc, char **argv)
     set_geom_hints(inst);
 
     gtk_widget_show(inst->area);
-    if (inst->cfg.scrollbar)
+    if (conf_get_int(inst->conf, CONF_scrollbar))
        gtk_widget_show(inst->sbar);
     else
        gtk_widget_hide(inst->sbar);
@@ -3512,7 +3591,7 @@ int pt_main(int argc, char **argv)
                       GTK_SIGNAL_FUNC(selection_get), inst);
     gtk_signal_connect(GTK_OBJECT(inst->area), "selection_clear_event",
                       GTK_SIGNAL_FUNC(selection_clear), inst);
-    if (inst->cfg.scrollbar)
+    if (conf_get_int(inst->conf, CONF_scrollbar))
        gtk_signal_connect(GTK_OBJECT(inst->sbar_adjust), "value_changed",
                           GTK_SIGNAL_FUNC(scrollbar_moved), inst);
     gtk_widget_add_events(GTK_WIDGET(inst->area),
@@ -3612,13 +3691,14 @@ int pt_main(int argc, char **argv)
 
     inst->eventlogstuff = eventlogstuff_new();
 
-    inst->term = term_init(&inst->cfg, &inst->ucsdata, inst);
-    inst->logctx = log_init(inst, &inst->cfg);
+    inst->term = term_init(inst->conf, &inst->ucsdata, inst);
+    inst->logctx = log_init(inst, inst->conf);
     term_provide_logctx(inst->term, inst->logctx);
 
     uxsel_init();
 
-    term_size(inst->term, inst->cfg.height, inst->cfg.width, inst->cfg.savelines);
+    term_size(inst->term, inst->height, inst->width,
+             conf_get_int(inst->conf, CONF_savelines));
 
     start_backend(inst);
 
index 74f4174..faf5600 100644 (file)
@@ -89,7 +89,7 @@ long get_windowid(void *frontend);
 void *get_window(void *frontend);      /* void * to avoid depending on gtk.h */
 
 /* Things pterm.c needs from gtkdlg.c */
-int do_config_box(const char *title, Config *cfg,
+int do_config_box(const char *title, Conf *conf,
                  int midsession, int protcfginfo);
 void fatal_message_box(void *window, char *msg);
 void about_box(void *window);
@@ -100,7 +100,7 @@ int reallyclose(void *frontend);
 
 /* Things pterm.c needs from {ptermm,uxputty}.c */
 char *make_default_wintitle(char *hostname);
-int process_nonoption_arg(char *arg, Config *cfg, int *allow_launch);
+int process_nonoption_arg(char *arg, Conf *conf, int *allow_launch);
 
 /* pterm.c needs this special function in xkeysym.c */
 int keysym_to_unicode(int keysym);
index 78a5615..63a92b5 100644 (file)
@@ -12,7 +12,7 @@
 #include "ssh.h"
 #include "network.h"
 
-void platform_get_x11_auth(struct X11Display *disp, const Config *cfg)
+void platform_get_x11_auth(struct X11Display *disp, Conf *conf)
 {
     char *xauthfile;
     int needs_free;
index c7d8d5f..e48a9b6 100644 (file)
@@ -16,11 +16,11 @@ void unix_setup_config_box(struct controlbox *b, int midsession, int protocol)
     union control *c;
 
     /*
-     * The Config structure contains two Unix-specific elements
-     * which are not configured in here: stamp_utmp and
-     * login_shell. This is because pterm does not put up a
-     * configuration box right at the start, which is the only time
-     * when these elements would be useful to configure.
+     * The Conf structure contains two Unix-specific elements which
+     * are not configured in here: stamp_utmp and login_shell. This
+     * is because pterm does not put up a configuration box right at
+     * the start, which is the only time when these elements would
+     * be useful to configure.
      */
 
     /*
@@ -41,8 +41,8 @@ void unix_setup_config_box(struct controlbox *b, int midsession, int protocol)
        for (i = 0; i < s->ncontrols; i++) {
            c = s->ctrls[i];
            if (c->generic.type == CTRL_RADIO &&
-               c->generic.context.i == offsetof(Config, proxy_type)) {
-               assert(c->generic.handler == dlg_stdradiobutton_handler);
+               c->generic.context.i == CONF_proxy_type) {
+               assert(c->generic.handler == conf_radiobutton_handler);
                c->radio.nbuttons++;
                c->radio.buttons =
                    sresize(c->radio.buttons, c->radio.nbuttons, char *);
@@ -58,9 +58,8 @@ void unix_setup_config_box(struct controlbox *b, int midsession, int protocol)
        for (i = 0; i < s->ncontrols; i++) {
            c = s->ctrls[i];
            if (c->generic.type == CTRL_EDITBOX &&
-               c->generic.context.i ==
-               offsetof(Config, proxy_telnet_command)) {
-               assert(c->generic.handler == dlg_stdeditbox_handler);
+               c->generic.context.i == CONF_proxy_telnet_command) {
+               assert(c->generic.handler == conf_editbox_handler);
                sfree(c->generic.label);
                c->generic.label = dupstr("Telnet command, or local"
                                          " proxy command");
index 278fb86..2a9e129 100644 (file)
@@ -53,9 +53,10 @@ static void gss_init(struct ssh_gss_library *lib, void *dlhandle,
 }
 
 /* Dynamically load gssapi libs. */
-struct ssh_gss_liblist *ssh_gss_setup(const Config *cfg)
+struct ssh_gss_liblist *ssh_gss_setup(Conf *conf)
 {
     void *gsslib;
+    char *gsspath;
     struct ssh_gss_liblist *list = snew(struct ssh_gss_liblist);
 
     list->libraries = snewn(4, struct ssh_gss_library);
@@ -77,11 +78,11 @@ struct ssh_gss_liblist *ssh_gss_setup(const Config *cfg)
                 2, "Using GSSAPI from libgss.so.1");
 
     /* User-specified GSSAPI library */
-    if (cfg->ssh_gss_custom.path[0] &&
-       (gsslib = dlopen(cfg->ssh_gss_custom.path, RTLD_LAZY)) != NULL)
+    gsspath = conf_get_filename(conf, CONF_ssh_gss_custom)->path;
+    if (*gsspath && (gsslib = dlopen(gsspath, RTLD_LAZY)) != NULL)
        gss_init(&list->libraries[list->nlibraries++], gsslib,
                 3, dupprintf("Using GSSAPI from user-specified"
-                             " library '%s'", cfg->ssh_gss_custom.path));
+                             " library '%s'", gsspath));
 
     return list;
 }
@@ -129,7 +130,7 @@ const struct keyvalwhere gsslibkeywords[] = {
 #include <gssapi/gssapi.h>
 
 /* Dynamically load gssapi libs. */
-struct ssh_gss_liblist *ssh_gss_setup(const Config *cfg)
+struct ssh_gss_liblist *ssh_gss_setup(Conf *conf)
 {
     struct ssh_gss_liblist *list = snew(struct ssh_gss_liblist);
 
index d76bbca..4d36e50 100644 (file)
@@ -98,7 +98,7 @@ static int local_tty = FALSE; /* do we have a local tty? */
 
 static Backend *back;
 static void *backhandle;
-static Config cfg;
+static Conf *conf;
 
 /*
  * Default settings that are specific to pterm.
@@ -605,10 +605,11 @@ int main(int argc, char **argv)
     /*
      * Process the command line.
      */
-    do_defaults(NULL, &cfg);
+    conf = conf_new();
+    do_defaults(NULL, conf);
     loaded_session = FALSE;
-    default_protocol = cfg.protocol;
-    default_port = cfg.port;
+    default_protocol = conf_get_int(conf, CONF_protocol);
+    default_port = conf_get_int(conf, CONF_port);
     errors = 0;
     {
        /*
@@ -618,8 +619,10 @@ int main(int argc, char **argv)
        if (p) {
            const Backend *b = backend_from_name(p);
            if (b) {
-               default_protocol = cfg.protocol = b->protocol;
-               default_port = cfg.port = b->default_port;
+               default_protocol = b->protocol;
+               default_port = b->default_port;
+               conf_set_int(conf, CONF_protocol, default_protocol);
+               conf_set_int(conf, CONF_port, default_port);
            }
        }
     }
@@ -627,7 +630,7 @@ int main(int argc, char **argv)
        char *p = *++argv;
        if (*p == '-') {
            int ret = cmdline_process_param(p, (argc > 1 ? argv[1] : NULL),
-                                           1, &cfg);
+                                           1, conf);
            if (ret == -2) {
                fprintf(stderr,
                        "plink: option \"%s\" requires an argument\n", p);
@@ -639,7 +642,7 @@ int main(int argc, char **argv)
            } else if (!strcmp(p, "-batch")) {
                console_batch_mode = 1;
            } else if (!strcmp(p, "-s")) {
-                /* Save status to write to cfg later. */
+                /* Save status to write to conf later. */
                use_subsystem = 1;
            } else if (!strcmp(p, "-V")) {
                 version();
@@ -660,7 +663,7 @@ int main(int argc, char **argv)
                errors = 1;
            }
        } else if (*p) {
-           if (!cfg_launchable(&cfg) || !(got_host || loaded_session)) {
+           if (!conf_launchable(conf) || !(got_host || loaded_session)) {
                char *q = p;
 
                /*
@@ -674,7 +677,7 @@ int main(int argc, char **argv)
                    q += 7;
                    if (q[0] == '/' && q[1] == '/')
                        q += 2;
-                   cfg.protocol = PROT_TELNET;
+                   conf_set_int(conf, CONF_protocol, PROT_TELNET);
                    p = q;
                    while (*p && *p != ':' && *p != '/')
                        p++;
@@ -682,11 +685,10 @@ int main(int argc, char **argv)
                    if (*p)
                        *p++ = '\0';
                    if (c == ':')
-                       cfg.port = atoi(p);
+                       conf_set_int(conf, CONF_port, atoi(p));
                    else
-                       cfg.port = -1;
-                   strncpy(cfg.host, q, sizeof(cfg.host) - 1);
-                   cfg.host[sizeof(cfg.host) - 1] = '\0';
+                       conf_set_int(conf, CONF_port, -1);
+                   conf_set_str(conf, CONF_host, q);
                    got_host = TRUE;
                } else {
                    char *r, *user, *host;
@@ -701,7 +703,9 @@ int main(int argc, char **argv)
                        *r = '\0';
                        b = backend_from_name(p);
                        if (b) {
-                           default_protocol = cfg.protocol = b->protocol;
+                           default_protocol = b->protocol;
+                           conf_set_int(conf, CONF_protocol,
+                                        default_protocol);
                            portnumber = b->default_port;
                        }
                        p = r + 1;
@@ -728,26 +732,24 @@ int main(int argc, char **argv)
                     * same name as the hostname.
                     */
                    {
-                       Config cfg2;
-                       do_defaults(host, &cfg2);
-                       if (loaded_session || !cfg_launchable(&cfg2)) {
+                       Conf *conf2 = conf_new();
+                       do_defaults(host, conf2);
+                       if (loaded_session || !conf_launchable(conf2)) {
                            /* No settings for this host; use defaults */
                            /* (or session was already loaded with -load) */
-                           strncpy(cfg.host, host, sizeof(cfg.host) - 1);
-                           cfg.host[sizeof(cfg.host) - 1] = '\0';
-                           cfg.port = default_port;
+                           conf_set_str(conf, CONF_host, host);
+                           conf_set_int(conf, CONF_port, default_port);
                            got_host = TRUE;
                        } else {
-                           cfg = cfg2;
+                           conf_copy_into(conf, conf2);
                            loaded_session = TRUE;
                        }
+                       conf_free(conf2);
                    }
 
                    if (user) {
                        /* Patch in specified username. */
-                       strncpy(cfg.username, user,
-                               sizeof(cfg.username) - 1);
-                       cfg.username[sizeof(cfg.username) - 1] = '\0';
+                       conf_set_str(conf, CONF_username, user);
                    }
 
                }
@@ -774,9 +776,9 @@ int main(int argc, char **argv)
                }
                if (cmdlen) command[--cmdlen]='\0';
                                       /* change trailing blank to NUL */
-               cfg.remote_cmd_ptr = command;
-               cfg.remote_cmd_ptr2 = NULL;
-               cfg.nopty = TRUE;      /* command => no terminal */
+               conf_set_str(conf, CONF_remote_cmd, command);
+               conf_set_str(conf, CONF_remote_cmd2, "");
+               conf_set_int(conf, CONF_nopty, TRUE);  /* command => no tty */
 
                break;                 /* done with cmdline */
            }
@@ -786,70 +788,78 @@ int main(int argc, char **argv)
     if (errors)
        return 1;
 
-    if (!cfg_launchable(&cfg) || !(got_host || loaded_session)) {
+    if (!conf_launchable(conf) || !(got_host || loaded_session)) {
        usage();
     }
 
     /*
-     * Trim leading whitespace off the hostname if it's there.
+     * Muck about with the hostname in various ways.
      */
     {
-       int space = strspn(cfg.host, " \t");
-       memmove(cfg.host, cfg.host+space, 1+strlen(cfg.host)-space);
-    }
+       char *hostbuf = dupstr(conf_get_str(conf, CONF_host));
+       char *host = hostbuf;
+       char *p, *q;
+
+       /*
+        * Trim leading whitespace.
+        */
+       host += strspn(host, " \t");
 
-    /* See if host is of the form user@host */
-    if (cfg.host[0] != '\0') {
-       char *atsign = strrchr(cfg.host, '@');
-       /* Make sure we're not overflowing the user field */
-       if (atsign) {
-           if (atsign - cfg.host < sizeof cfg.username) {
-               strncpy(cfg.username, cfg.host, atsign - cfg.host);
-               cfg.username[atsign - cfg.host] = '\0';
+       /*
+        * See if host is of the form user@host, and separate out
+        * the username if so.
+        */
+       if (host[0] != '\0') {
+           char *atsign = strrchr(host, '@');
+           if (atsign) {
+               *atsign = '\0';
+               conf_set_str(conf, CONF_username, host);
+               host = atsign + 1;
            }
-           memmove(cfg.host, atsign + 1, 1 + strlen(atsign + 1));
        }
+
+       /*
+        * Trim off a colon suffix if it's there.
+        */
+       host[strcspn(host, ":")] = '\0';
+
+       /*
+        * Remove any remaining whitespace.
+        */
+       p = hostbuf;
+       q = host;
+       while (*q) {
+           if (*q != ' ' && *q != '\t')
+               *p++ = *q;
+           q++;
+       }
+       *p = '\0';
+
+       conf_set_str(conf, CONF_host, hostbuf);
+       sfree(hostbuf);
     }
 
     /*
      * Perform command-line overrides on session configuration.
      */
-    cmdline_run_saved(&cfg);
+    cmdline_run_saved(conf);
 
     /*
      * Apply subsystem status.
      */
     if (use_subsystem)
-        cfg.ssh_subsys = TRUE;
-
-    /*
-     * Trim a colon suffix off the hostname if it's there.
-     */
-    cfg.host[strcspn(cfg.host, ":")] = '\0';
-
-    /*
-     * Remove any remaining whitespace from the hostname.
-     */
-    {
-       int p1 = 0, p2 = 0;
-       while (cfg.host[p2] != '\0') {
-           if (cfg.host[p2] != ' ' && cfg.host[p2] != '\t') {
-               cfg.host[p1] = cfg.host[p2];
-               p1++;
-           }
-           p2++;
-       }
-       cfg.host[p1] = '\0';
-    }
+        conf_set_int(conf, CONF_ssh_subsys, TRUE);
 
-    if (!cfg.remote_cmd_ptr && !*cfg.remote_cmd && !*cfg.ssh_nc_host)
+    if (!*conf_get_str(conf, CONF_remote_cmd) &&
+       !*conf_get_str(conf, CONF_remote_cmd2) &&
+       !*conf_get_str(conf, CONF_ssh_nc_host))
        flags |= FLAG_INTERACTIVE;
 
     /*
      * Select protocol. This is farmed out into a table in a
      * separate file to enable an ssh-free variant.
      */
-    back = backend_from_proto(cfg.protocol);
+    back = backend_from_proto(conf_get_int(conf, CONF_protocol));
     if (back == NULL) {
        fprintf(stderr,
                "Internal fault: Unsupported protocol found\n");
@@ -860,7 +870,7 @@ int main(int argc, char **argv)
      * Select port.
      */
     if (portnumber != -1)
-       cfg.port = portnumber;
+       conf_set_int(conf, CONF_port, portnumber);
 
     /*
      * Set up the pipe we'll use to tell us about SIGWINCH.
@@ -879,28 +889,34 @@ int main(int argc, char **argv)
      * connection is set up, so if there are none now, we can safely set
      * the "simple" flag.
      */
-    if (cfg.protocol == PROT_SSH && !cfg.x11_forward &&        !cfg.agentfwd &&
-       cfg.portfwd[0] == '\0' && cfg.portfwd[1] == '\0')
-       cfg.ssh_simple = TRUE;
+    if (conf_get_int(conf, CONF_protocol) == PROT_SSH &&
+       !conf_get_int(conf, CONF_x11_forward) &&
+       !conf_get_int(conf, CONF_agentfwd) &&
+       !conf_get_str_nthstrkey(conf, CONF_portfwd, 0))
+       conf_set_int(conf, CONF_ssh_simple, TRUE);
+
     /*
      * Start up the connection.
      */
-    logctx = log_init(NULL, &cfg);
+    logctx = log_init(NULL, conf);
     console_provide_logctx(logctx);
     {
        const char *error;
        char *realhost;
        /* nodelay is only useful if stdin is a terminal device */
-       int nodelay = cfg.tcp_nodelay && isatty(0);
+       int nodelay = conf_get_int(conf, CONF_tcp_nodelay) && isatty(0);
 
-       error = back->init(NULL, &backhandle, &cfg, cfg.host, cfg.port,
-                          &realhost, nodelay, cfg.tcp_keepalives);
+       error = back->init(NULL, &backhandle, conf,
+                          conf_get_str(conf, CONF_host),
+                          conf_get_int(conf, CONF_port),
+                          &realhost, nodelay,
+                          conf_get_int(conf, CONF_tcp_keepalives));
        if (error) {
            fprintf(stderr, "Unable to open connection:\n%s\n", error);
            return 1;
        }
        back->provide_logctx(backhandle, logctx);
-       ldisc_create(&cfg, NULL, back, backhandle, NULL);
+       ldisc_create(conf, NULL, back, backhandle, NULL);
        sfree(realhost);
     }
     connopen = 1;
index 792bbdc..b441b80 100644 (file)
@@ -224,7 +224,7 @@ static int localproxy_select_result(int fd, int event)
 Socket platform_new_connection(SockAddr addr, char *hostname,
                               int port, int privport,
                               int oobinline, int nodelay, int keepalive,
-                              Plug plug, const Config *cfg)
+                              Plug plug, Conf *conf)
 {
     char *cmd;
 
@@ -243,10 +243,10 @@ Socket platform_new_connection(SockAddr addr, char *hostname,
     Local_Proxy_Socket ret;
     int to_cmd_pipe[2], from_cmd_pipe[2], pid;
 
-    if (cfg->proxy_type != PROXY_CMD)
+    if (conf_get_int(conf, CONF_proxy_type) != PROXY_CMD)
        return NULL;
 
-    cmd = format_telnet_command(addr, port, cfg);
+    cmd = format_telnet_command(addr, port, conf);
 
     ret = snew(struct Socket_localproxy_tag);
     ret->fn = &socket_fn_table;
index c20c14a..370527a 100644 (file)
@@ -12,19 +12,19 @@ const int use_event_log = 0;               /* pterm doesn't need it */
 const int new_session = 0, saved_sessions = 0;   /* or these */
 const int use_pty_argv = TRUE;
 
-Backend *select_backend(Config *cfg)
+Backend *select_backend(Conf *conf)
 {
     return &pty_backend;
 }
 
-int cfgbox(Config *cfg)
+int cfgbox(Conf *conf)
 {
     /*
      * This is a no-op in pterm, except that we'll ensure the
      * protocol is set to -1 to inhibit the useless Connection
      * panel in the config box.
      */
-    cfg->protocol = -1;
+    conf_set_int(conf, CONF_protocol, -1);
     return 1;
 }
 
@@ -33,7 +33,7 @@ void cleanup_exit(int code)
     exit(code);
 }
 
-int process_nonoption_arg(char *arg, Config *cfg, int *allow_launch)
+int process_nonoption_arg(char *arg, Conf *conf, int *allow_launch)
 {
     return 0;                          /* pterm doesn't have any. */
 }
index bdfa5ab..a9c0676 100644 (file)
@@ -76,7 +76,7 @@ typedef struct pty_tag *Pty;
 static int pty_signal_pipe[2] = { -1, -1 };   /* obviously bogus initial val */
 
 struct pty_tag {
-    Config cfg;
+    Conf *conf;
     int master_fd, slave_fd;
     void *frontend;
     char name[FILENAME_MAX];
@@ -588,6 +588,8 @@ int pty_real_select_result(Pty pty, int event, int status)
     }
 
     if (finished && !pty->finished) {
+       int close_on_exit;
+
        uxsel_del(pty->master_fd);
        pty_close(pty);
        pty->master_fd = -1;
@@ -600,8 +602,9 @@ int pty_real_select_result(Pty pty, int event, int status)
         * Only On Clean and it wasn't a clean exit) do we output a
         * `terminated' message.
         */
-       if (pty->cfg.close_on_exit == FORCE_OFF ||
-           (pty->cfg.close_on_exit == AUTO && pty->exit_code != 0)) {
+       close_on_exit = conf_get_int(pty->conf, CONF_close_on_exit);
+       if (close_on_exit == FORCE_OFF ||
+           (close_on_exit == AUTO && pty->exit_code != 0)) {
            char message[512];
            if (WIFEXITED(pty->exit_code))
                sprintf(message, "\r\n[pterm: process terminated with exit"
@@ -681,7 +684,7 @@ static void pty_uxsel_setup(Pty pty)
  * Also places the canonical host name into `realhost'. It must be
  * freed by the caller.
  */
-static const char *pty_init(void *frontend, void **backend_handle, Config *cfg,
+static const char *pty_init(void *frontend, void **backend_handle, Conf *conf,
                            char *host, int port, char **realhost, int nodelay,
                            int keepalive)
 {
@@ -705,9 +708,9 @@ static const char *pty_init(void *frontend, void **backend_handle, Config *cfg,
     pty->frontend = frontend;
     *backend_handle = NULL;           /* we can't sensibly use this, sadly */
 
-    pty->cfg = *cfg;                  /* structure copy */
-    pty->term_width = cfg->width;
-    pty->term_height = cfg->height;
+    pty->conf = conf_copy(conf);
+    pty->term_width = conf_get_int(conf, CONF_width);
+    pty->term_height = conf_get_int(conf, CONF_height);
 
     if (pty->master_fd < 0)
        pty_open_master(pty);
@@ -719,7 +722,8 @@ static const char *pty_init(void *frontend, void **backend_handle, Config *cfg,
     {
        struct termios attrs;
        tcgetattr(pty->master_fd, &attrs);
-       attrs.c_cc[VERASE] = cfg->bksp_is_delete ? '\177' : '\010';
+       attrs.c_cc[VERASE] = conf_get_int(conf, CONF_bksp_is_delete)
+           ? '\177' : '\010';
        tcsetattr(pty->master_fd, TCSANOW, &attrs);
     }
 
@@ -728,7 +732,7 @@ static const char *pty_init(void *frontend, void **backend_handle, Config *cfg,
      * Stamp utmp (that is, tell the utmp helper process to do so),
      * or not.
      */
-    if (!cfg->stamp_utmp) {
+    if (!conf_get_int(conf, CONF_stamp_utmp)) {
        close(pty_utmp_helper_pipe);   /* just let the child process die */
        pty_utmp_helper_pipe = -1;
     } else {
@@ -787,7 +791,8 @@ static const char *pty_init(void *frontend, void **backend_handle, Config *cfg,
        close(open(pty->name, O_WRONLY, 0));
        setpgid(pgrp, pgrp);
        {
-           char *term_env_var = dupprintf("TERM=%s", cfg->termtype);
+           char *term_env_var = dupprintf("TERM=%s",
+                                          conf_get_str(conf, CONF_termtype));
            putenv(term_env_var);
            /* We mustn't free term_env_var, as putenv links it into the
             * environment in place.
@@ -803,18 +808,12 @@ static const char *pty_init(void *frontend, void **backend_handle, Config *cfg,
        }
 #endif
        {
-           char *e = cfg->environmt;
-           char *var, *varend, *val, *varval;
-           while (*e) {
-               var = e;
-               while (*e && *e != '\t') e++;
-               varend = e;
-               if (*e == '\t') e++;
-               val = e;
-               while (*e) e++;
-               e++;
-
-               varval = dupprintf("%.*s=%s", varend-var, var, val);
+           char *key, *val;
+
+           for (val = conf_get_str_strs(conf, CONF_environmt, NULL, &key);
+                val != NULL;
+                val = conf_get_str_strs(conf, CONF_environmt, key, &key)) {
+               char *varval = dupcat(key, "=", val, NULL);
                putenv(varval);
                /*
                 * We must not free varval, since putenv links it
@@ -841,7 +840,7 @@ static const char *pty_init(void *frontend, void **backend_handle, Config *cfg,
        else {
            char *shell = getenv("SHELL");
            char *shellname;
-           if (cfg->login_shell) {
+           if (conf_get_int(conf, CONF_login_shell)) {
                char *p = strrchr(shell, '/');
                shellname = snewn(2+strlen(shell), char);
                p = p ? p+1 : shell;
@@ -884,7 +883,7 @@ static const char *pty_init(void *frontend, void **backend_handle, Config *cfg,
     return NULL;
 }
 
-static void pty_reconfig(void *handle, Config *cfg)
+static void pty_reconfig(void *handle, Conf *conf)
 {
     Pty pty = (Pty)handle;
     /*
@@ -892,7 +891,7 @@ static void pty_reconfig(void *handle, Config *cfg)
      * unfortunately we do need to pick up the setting of Close On
      * Exit so we know whether to give a `terminated' message.
      */
-    pty->cfg = *cfg;                  /* structure copy */
+    conf_copy_into(pty->conf, conf);
 }
 
 /*
index 73e1197..fc718a0 100644 (file)
@@ -31,17 +31,17 @@ void cleanup_exit(int code)
     exit(code);
 }
 
-Backend *select_backend(Config *cfg)
+Backend *select_backend(Conf *conf)
 {
-    Backend *back = backend_from_proto(cfg->protocol);
+    Backend *back = backend_from_proto(conf_get_int(conf, CONF_protocol));
     assert(back != NULL);
     return back;
 }
 
-int cfgbox(Config *cfg)
+int cfgbox(Conf *conf)
 {
     char *title = dupcat(appname, " Configuration", NULL);
-    int ret = do_config_box(title, cfg, 0, 0);
+    int ret = do_config_box(title, conf, 0, 0);
     sfree(title);
     return ret;
 }
@@ -50,7 +50,7 @@ static int got_host = 0;
 
 const int use_event_log = 1, new_session = 1, saved_sessions = 1;
 
-int process_nonoption_arg(char *arg, Config *cfg, int *allow_launch)
+int process_nonoption_arg(char *arg, Conf *conf, int *allow_launch)
 {
     char *p, *q = arg;
 
@@ -61,7 +61,7 @@ int process_nonoption_arg(char *arg, Config *cfg, int *allow_launch)
          * argument, so that it will be deferred until it's a good
          * moment to run it.
          */
-        int ret = cmdline_process_param("-P", arg, 1, cfg);
+        int ret = cmdline_process_param("-P", arg, 1, conf);
         assert(ret == 2);
     } else if (!strncmp(q, "telnet:", 7)) {
         /*
@@ -74,7 +74,7 @@ int process_nonoption_arg(char *arg, Config *cfg, int *allow_launch)
         q += 7;
         if (q[0] == '/' && q[1] == '/')
             q += 2;
-        cfg->protocol = PROT_TELNET;
+        conf_set_int(conf, CONF_protocol, PROT_TELNET);
         p = q;
         while (*p && *p != ':' && *p != '/')
             p++;
@@ -82,11 +82,10 @@ int process_nonoption_arg(char *arg, Config *cfg, int *allow_launch)
         if (*p)
             *p++ = '\0';
         if (c == ':')
-            cfg->port = atoi(p);
+            conf_set_int(conf, CONF_port, atoi(p));
         else
-            cfg->port = -1;
-        strncpy(cfg->host, q, sizeof(cfg->host) - 1);
-        cfg->host[sizeof(cfg->host) - 1] = '\0';
+            conf_set_int(conf, CONF_port, -1);
+       conf_set_str(conf, CONF_host, q);
         got_host = 1;
     } else {
         /*
@@ -97,8 +96,7 @@ int process_nonoption_arg(char *arg, Config *cfg, int *allow_launch)
             p++;
         if (*p)
             *p++ = '\0';
-        strncpy(cfg->host, q, sizeof(cfg->host) - 1);
-        cfg->host[sizeof(cfg->host) - 1] = '\0';
+        conf_set_str(conf, CONF_host, q);
         got_host = 1;
     }
     if (got_host)
index 2155b92..8f4955c 100644 (file)
@@ -60,10 +60,10 @@ static int serial_select_result(int fd, int event);
 static void serial_uxsel_setup(Serial serial);
 static void serial_try_write(Serial serial);
 
-static const char *serial_configure(Serial serial, Config *cfg)
+static const char *serial_configure(Serial serial, Conf *conf)
 {
     struct termios options;
-    int bflag, bval;
+    int bflag, bval, speed, flow, parity;
     const char *str;
     char *msg;
 
@@ -75,8 +75,9 @@ static const char *serial_configure(Serial serial, Config *cfg)
     /*
      * Find the appropriate baud rate flag.
      */
+    speed = conf_get_int(conf, CONF_serspeed);
 #define SETBAUD(x) (bflag = B ## x, bval = x)
-#define CHECKBAUD(x) do { if (cfg->serspeed >= x) SETBAUD(x); } while (0)
+#define CHECKBAUD(x) do { if (speed >= x) SETBAUD(x); } while (0)
     SETBAUD(50);
 #ifdef B75
     CHECKBAUD(75);
@@ -183,18 +184,19 @@ static const char *serial_configure(Serial serial, Config *cfg)
     sfree(msg);
 
     options.c_cflag &= ~CSIZE;
-    switch (cfg->serdatabits) {
+    switch (conf_get_int(conf, CONF_serdatabits)) {
       case 5: options.c_cflag |= CS5; break;
       case 6: options.c_cflag |= CS6; break;
       case 7: options.c_cflag |= CS7; break;
       case 8: options.c_cflag |= CS8; break;
       default: return "Invalid number of data bits (need 5, 6, 7 or 8)";
     }
-    msg = dupprintf("Configuring %d data bits", cfg->serdatabits);
+    msg = dupprintf("Configuring %d data bits",
+                   conf_get_int(conf, CONF_serdatabits));
     logevent(serial->frontend, msg);
     sfree(msg);
 
-    if (cfg->serstopbits >= 4) {
+    if (conf_get_int(conf, CONF_serstopbits) >= 4) {
        options.c_cflag |= CSTOPB;
     } else {
        options.c_cflag &= ~CSTOPB;
@@ -211,10 +213,11 @@ static const char *serial_configure(Serial serial, Config *cfg)
 #ifdef CNEW_RTSCTS
     options.c_cflag &= ~CNEW_RTSCTS;
 #endif
-    if (cfg->serflow == SER_FLOW_XONXOFF) {
+    flow = conf_get_int(conf, CONF_serflow);
+    if (flow == SER_FLOW_XONXOFF) {
        options.c_iflag |= IXON | IXOFF;
        str = "XON/XOFF";
-    } else if (cfg->serflow == SER_FLOW_RTSCTS) {
+    } else if (flow == SER_FLOW_RTSCTS) {
 #ifdef CRTSCTS
        options.c_cflag |= CRTSCTS;
 #endif
@@ -229,11 +232,12 @@ static const char *serial_configure(Serial serial, Config *cfg)
     sfree(msg);
 
     /* Parity */
-    if (cfg->serparity == SER_PAR_ODD) {
+    parity = conf_get_int(conf, CONF_serparity);
+    if (parity == SER_PAR_ODD) {
        options.c_cflag |= PARENB;
        options.c_cflag |= PARODD;
        str = "odd";
-    } else if (cfg->serparity == SER_PAR_EVEN) {
+    } else if (parity == SER_PAR_EVEN) {
        options.c_cflag |= PARENB;
        options.c_cflag &= ~PARODD;
        str = "even";
@@ -284,12 +288,13 @@ static const char *serial_configure(Serial serial, Config *cfg)
  * freed by the caller.
  */
 static const char *serial_init(void *frontend_handle, void **backend_handle,
-                              Config *cfg,
+                              Conf *conf,
                               char *host, int port, char **realhost, int nodelay,
                               int keepalive)
 {
     Serial serial;
     const char *err;
+    char *line;
 
     serial = snew(struct serial_backend_data);
     *backend_handle = serial;
@@ -299,22 +304,23 @@ static const char *serial_init(void *frontend_handle, void **backend_handle,
     serial->inbufsize = 0;
     bufchain_init(&serial->output_data);
 
+    line = conf_get_str(conf, CONF_serline);
     {
-       char *msg = dupprintf("Opening serial device %s", cfg->serline);
+       char *msg = dupprintf("Opening serial device %s", line);
        logevent(serial->frontend, msg);
     }
 
-    serial->fd = open(cfg->serline, O_RDWR | O_NOCTTY | O_NDELAY | O_NONBLOCK);
+    serial->fd = open(line, O_RDWR | O_NOCTTY | O_NDELAY | O_NONBLOCK);
     if (serial->fd < 0)
        return "Unable to open serial port";
 
     cloexec(serial->fd);
 
-    err = serial_configure(serial, cfg);
+    err = serial_configure(serial, conf);
     if (err)
        return err;
 
-    *realhost = dupstr(cfg->serline);
+    *realhost = dupstr(line);
 
     if (!serial_by_fd)
        serial_by_fd = newtree234(serial_compare_by_fd);
@@ -349,14 +355,14 @@ static void serial_free(void *handle)
     sfree(serial);
 }
 
-static void serial_reconfig(void *handle, Config *cfg)
+static void serial_reconfig(void *handle, Conf *conf)
 {
     Serial serial = (Serial) handle;
 
     /*
      * FIXME: what should we do if this returns an error?
      */
-    serial_configure(serial, cfg);
+    serial_configure(serial, conf);
 }
 
 static int serial_select_result(int fd, int event)
index b26c4b5..3170ecf 100644 (file)
@@ -34,7 +34,7 @@ char *x_get_default(const char *key)
     return NULL;                      /* this is a stub */
 }
 
-void platform_get_x11_auth(struct X11Display *display, const Config *cfg)
+void platform_get_x11_auth(struct X11Display *display, Conf *conf)
 {
     /* Do nothing, therefore no auth. */
 }
index e7b9c15..8d49e48 100644 (file)
@@ -317,7 +317,7 @@ void *open_settings_r(const char *sessionname)
     return ret;
 }
 
-char *read_setting_s(void *handle, const char *key, char *buffer, int buflen)
+char *read_setting_s(void *handle, const char *key)
 {
     tree234 *tree = (tree234 *)handle;
     const char *val;
@@ -333,11 +333,8 @@ char *read_setting_s(void *handle, const char *key, char *buffer, int buflen)
 
     if (!val)
        return NULL;
-    else {
-       strncpy(buffer, val, buflen);
-       buffer[buflen-1] = '\0';
-       return buffer;
-    }
+    else
+       return dupstr(val);
 }
 
 int read_setting_i(void *handle, const char *key, int defvalue)
@@ -375,26 +372,40 @@ int read_setting_fontspec(void *handle, const char *name, FontSpec *result)
      * ("FontName").
      */
     char *suffname = dupcat(name, "Name", NULL);
-    if (read_setting_s(handle, suffname, result->name, sizeof(result->name))) {
+    char *tmp;
+
+    if ((tmp = read_setting_s(handle, suffname)) != NULL) {
+       strncpy(result->name, tmp, sizeof(result->name)-1);
+       result->name[sizeof(result->name)-1] = '\0';
        sfree(suffname);
+       sfree(tmp);
        return TRUE;                   /* got new-style name */
     }
     sfree(suffname);
 
     /* Fall back to old-style name. */
-    memcpy(result->name, "server:", 7);
-    if (!read_setting_s(handle, name,
-                       result->name + 7, sizeof(result->name) - 7) ||
-       !result->name[7]) {
-       result->name[0] = '\0';
-       return FALSE;
-    } else {
+    tmp = read_setting_s(handle, name);
+    if (tmp && *tmp) {
+       strcpy(result->name, "server:");
+       strncpy(result->name + 7, tmp, sizeof(result->name) - 8);
+       result->name[sizeof(result->name)-1] = '\0';
+       sfree(tmp);
        return TRUE;
+    } else {
+       sfree(tmp);
+       return FALSE;
     }
 }
 int read_setting_filename(void *handle, const char *name, Filename *result)
 {
-    return !!read_setting_s(handle, name, result->path, sizeof(result->path));
+    char *tmp = read_setting_s(handle, name);
+    if (tmp) {
+       strncpy(result->path, tmp, sizeof(result->path)-1);
+       result->path[sizeof(result->path)-1] = '\0';
+       sfree(tmp);
+       return TRUE;
+    } else
+       return FALSE;
 }
 
 void write_setting_fontspec(void *handle, const char *name, FontSpec result)
index ec881c5..071ecfc 100644 (file)
@@ -139,7 +139,7 @@ int init_ucs(struct unicode_data *ucsdata, char *linecharset,
 
     /*
      * Failing that, line_codepage should be decoded from the
-     * specification in cfg.
+     * specification in conf.
      */
     if (ucsdata->line_codepage == CS_NONE)
        ucsdata->line_codepage = decode_codepage(linecharset);
index 1cf56c8..9d3673a 100644 (file)
@@ -70,8 +70,8 @@ void win_setup_config_box(struct controlbox *b, HWND *hwndp, int has_help,
                    "Control the scrollback in the window");
     ctrl_checkbox(s, "Display scrollbar in full screen mode", 'i',
                  HELPCTX(window_scrollback),
-                 dlg_stdcheckbox_handler,
-                 I(offsetof(Config,scrollbar_in_fullscreen)));
+                 conf_checkbox_handler,
+                 I(CONF_scrollbar_in_fullscreen));
     /*
      * Really this wants to go just after `Display scrollbar'. See
      * if we can find that control, and do some shuffling.
@@ -81,7 +81,7 @@ void win_setup_config_box(struct controlbox *b, HWND *hwndp, int has_help,
         for (i = 0; i < s->ncontrols; i++) {
             c = s->ctrls[i];
             if (c->generic.type == CTRL_CHECKBOX &&
-                c->generic.context.i == offsetof(Config,scrollbar)) {
+                c->generic.context.i == CONF_scrollbar) {
                 /*
                  * Control i is the scrollbar checkbox.
                  * Control s->ncontrols-1 is the scrollbar-in-FS one.
@@ -105,10 +105,10 @@ void win_setup_config_box(struct controlbox *b, HWND *hwndp, int has_help,
                    "Enable extra keyboard features:");
     ctrl_checkbox(s, "AltGr acts as Compose key", 't',
                  HELPCTX(keyboard_compose),
-                 dlg_stdcheckbox_handler, I(offsetof(Config,compose_key)));
+                 conf_checkbox_handler, I(CONF_compose_key));
     ctrl_checkbox(s, "Control-Alt is different from AltGr", 'd',
                  HELPCTX(keyboard_ctrlalt),
-                 dlg_stdcheckbox_handler, I(offsetof(Config,ctrlaltkeys)));
+                 conf_checkbox_handler, I(CONF_ctrlaltkeys));
 
     /*
      * Windows allows an arbitrary .WAV to be played as a bell, and
@@ -133,8 +133,8 @@ void win_setup_config_box(struct controlbox *b, HWND *hwndp, int has_help,
        for (i = 0; i < s->ncontrols; i++) {
            c = s->ctrls[i];
            if (c->generic.type == CTRL_RADIO &&
-               c->generic.context.i == offsetof(Config, beep)) {
-               assert(c->generic.handler == dlg_stdradiobutton_handler);
+               c->generic.context.i == CONF_beep) {
+               assert(c->generic.handler == conf_radiobutton_handler);
                c->radio.nbuttons += 2;
                c->radio.buttons =
                    sresize(c->radio.buttons, c->radio.nbuttons, char *);
@@ -159,7 +159,7 @@ void win_setup_config_box(struct controlbox *b, HWND *hwndp, int has_help,
     ctrl_filesel(s, "Custom sound file to play as a bell:", NO_SHORTCUT,
                 FILTER_WAVE_FILES, FALSE, "Select bell sound file",
                 HELPCTX(bell_style),
-                dlg_stdfilesel_handler, I(offsetof(Config, bell_wavefile)));
+                conf_filesel_handler, I(CONF_bell_wavefile));
 
     /*
      * While we've got this box open, taskbar flashing on a bell is
@@ -167,8 +167,8 @@ void win_setup_config_box(struct controlbox *b, HWND *hwndp, int has_help,
      */
     ctrl_radiobuttons(s, "Taskbar/caption indication on bell:", 'i', 3,
                      HELPCTX(bell_taskbar),
-                     dlg_stdradiobutton_handler,
-                     I(offsetof(Config, beep_ind)),
+                     conf_radiobutton_handler,
+                     I(CONF_beep_ind),
                      "Disabled", I(B_IND_DISABLED),
                      "Flashing", I(B_IND_FLASH),
                      "Steady", I(B_IND_STEADY), NULL);
@@ -180,7 +180,7 @@ void win_setup_config_box(struct controlbox *b, HWND *hwndp, int has_help,
                    "Adjust the window border");
     ctrl_checkbox(s, "Sunken-edge border (slightly thicker)", 's',
                  HELPCTX(appearance_border),
-                 dlg_stdcheckbox_handler, I(offsetof(Config,sunken_edge)));
+                 conf_checkbox_handler, I(CONF_sunken_edge));
 
     /*
      * Configurable font quality settings for Windows.
@@ -191,8 +191,8 @@ void win_setup_config_box(struct controlbox *b, HWND *hwndp, int has_help,
                   HELPCTX(appearance_font), variable_pitch_handler, I(0));
     ctrl_radiobuttons(s, "Font quality:", 'q', 2,
                      HELPCTX(appearance_font),
-                     dlg_stdradiobutton_handler,
-                     I(offsetof(Config, font_quality)),
+                     conf_radiobutton_handler,
+                     I(CONF_font_quality),
                      "Antialiased", I(FQ_ANTIALIASED),
                      "Non-Antialiased", I(FQ_NONANTIALIASED),
                      "ClearType", I(FQ_CLEARTYPE),
@@ -206,8 +206,8 @@ void win_setup_config_box(struct controlbox *b, HWND *hwndp, int has_help,
     s = ctrl_getset(b, "Window/Translation", "tweaks", NULL);
     ctrl_checkbox(s, "Caps Lock acts as Cyrillic switch", 's',
                  HELPCTX(translation_cyrillic),
-                 dlg_stdcheckbox_handler,
-                 I(offsetof(Config,xlat_capslockcyr)));
+                 conf_checkbox_handler,
+                 I(CONF_xlat_capslockcyr));
 
     /*
      * On Windows we can use but not enumerate translation tables
@@ -232,8 +232,8 @@ void win_setup_config_box(struct controlbox *b, HWND *hwndp, int has_help,
        for (i = 0; i < s->ncontrols; i++) {
            c = s->ctrls[i];
            if (c->generic.type == CTRL_RADIO &&
-               c->generic.context.i == offsetof(Config, vtmode)) {
-               assert(c->generic.handler == dlg_stdradiobutton_handler);
+               c->generic.context.i == CONF_vtmode) {
+               assert(c->generic.handler == conf_radiobutton_handler);
                c->radio.nbuttons += 3;
                c->radio.buttons =
                    sresize(c->radio.buttons, c->radio.nbuttons, char *);
@@ -272,7 +272,7 @@ void win_setup_config_box(struct controlbox *b, HWND *hwndp, int has_help,
                    "Formatting of pasted characters");
     ctrl_checkbox(s, "Paste to clipboard in RTF as well as plain text", 'f',
                  HELPCTX(selection_rtf),
-                 dlg_stdcheckbox_handler, I(offsetof(Config,rtf_paste)));
+                 conf_checkbox_handler, I(CONF_rtf_paste));
 
     /*
      * Windows often has no middle button, so we supply a selection
@@ -283,8 +283,8 @@ void win_setup_config_box(struct controlbox *b, HWND *hwndp, int has_help,
                    "Control use of mouse");
     ctrl_radiobuttons(s, "Action of mouse buttons:", 'm', 1,
                      HELPCTX(selection_buttons),
-                     dlg_stdradiobutton_handler,
-                     I(offsetof(Config, mouse_is_xterm)),
+                     conf_radiobutton_handler,
+                     I(CONF_mouse_is_xterm),
                      "Windows (Middle extends, Right brings up menu)", I(2),
                      "Compromise (Middle extends, Right pastes)", I(0),
                      "xterm (Right extends, Middle pastes)", I(1), NULL);
@@ -304,10 +304,10 @@ void win_setup_config_box(struct controlbox *b, HWND *hwndp, int has_help,
                    "General options for colour usage");
     ctrl_checkbox(s, "Attempt to use logical palettes", 'l',
                  HELPCTX(colours_logpal),
-                 dlg_stdcheckbox_handler, I(offsetof(Config,try_palette)));
+                 conf_checkbox_handler, I(CONF_try_palette));
     ctrl_checkbox(s, "Use system colours", 's',
                   HELPCTX(colours_system),
-                  dlg_stdcheckbox_handler, I(offsetof(Config,system_colour)));
+                  conf_checkbox_handler, I(CONF_system_colour));
 
 
     /*
@@ -316,8 +316,8 @@ void win_setup_config_box(struct controlbox *b, HWND *hwndp, int has_help,
     s = ctrl_getset(b, "Window", "size", "Set the size of the window");
     ctrl_radiobuttons(s, "When window is resized:", 'z', 1,
                      HELPCTX(window_resize),
-                     dlg_stdradiobutton_handler,
-                     I(offsetof(Config, resize_action)),
+                     conf_radiobutton_handler,
+                     I(CONF_resize_action),
                      "Change the number of rows and columns", I(RESIZE_TERM),
                      "Change the size of the font", I(RESIZE_FONT),
                      "Change font size only when maximised", I(RESIZE_EITHER),
@@ -331,20 +331,20 @@ void win_setup_config_box(struct controlbox *b, HWND *hwndp, int has_help,
     s = ctrl_getset(b, "Window/Behaviour", "main", NULL);
     ctrl_checkbox(s, "Window closes on ALT-F4", '4',
                  HELPCTX(behaviour_altf4),
-                 dlg_stdcheckbox_handler, I(offsetof(Config,alt_f4)));
+                 conf_checkbox_handler, I(CONF_alt_f4));
     ctrl_checkbox(s, "System menu appears on ALT-Space", 'y',
                  HELPCTX(behaviour_altspace),
-                 dlg_stdcheckbox_handler, I(offsetof(Config,alt_space)));
+                 conf_checkbox_handler, I(CONF_alt_space));
     ctrl_checkbox(s, "System menu appears on ALT alone", 'l',
                  HELPCTX(behaviour_altonly),
-                 dlg_stdcheckbox_handler, I(offsetof(Config,alt_only)));
+                 conf_checkbox_handler, I(CONF_alt_only));
     ctrl_checkbox(s, "Ensure window is always on top", 'e',
                  HELPCTX(behaviour_alwaysontop),
-                 dlg_stdcheckbox_handler, I(offsetof(Config,alwaysontop)));
+                 conf_checkbox_handler, I(CONF_alwaysontop));
     ctrl_checkbox(s, "Full screen on Alt-Enter", 'f',
                  HELPCTX(behaviour_altenter),
-                 dlg_stdcheckbox_handler,
-                 I(offsetof(Config,fullscreenonaltenter)));
+                 conf_checkbox_handler,
+                 I(CONF_fullscreenonaltenter));
 
     /*
      * Windows supports a local-command proxy. This also means we
@@ -356,8 +356,8 @@ void win_setup_config_box(struct controlbox *b, HWND *hwndp, int has_help,
        for (i = 0; i < s->ncontrols; i++) {
            c = s->ctrls[i];
            if (c->generic.type == CTRL_RADIO &&
-               c->generic.context.i == offsetof(Config, proxy_type)) {
-               assert(c->generic.handler == dlg_stdradiobutton_handler);
+               c->generic.context.i == CONF_proxy_type) {
+               assert(c->generic.handler == conf_radiobutton_handler);
                c->radio.nbuttons++;
                c->radio.buttons =
                    sresize(c->radio.buttons, c->radio.nbuttons, char *);
@@ -373,9 +373,8 @@ void win_setup_config_box(struct controlbox *b, HWND *hwndp, int has_help,
        for (i = 0; i < s->ncontrols; i++) {
            c = s->ctrls[i];
            if (c->generic.type == CTRL_EDITBOX &&
-               c->generic.context.i ==
-               offsetof(Config, proxy_telnet_command)) {
-               assert(c->generic.handler == dlg_stdeditbox_handler);
+               c->generic.context.i == CONF_proxy_telnet_command) {
+               assert(c->generic.handler == conf_editbox_handler);
                sfree(c->generic.label);
                c->generic.label = dupstr("Telnet command, or local"
                                          " proxy command");
@@ -399,6 +398,6 @@ void win_setup_config_box(struct controlbox *b, HWND *hwndp, int has_help,
        ctrl_filesel(s, "X authority file for local display", 't',
                     NULL, FALSE, "Select X authority file",
                     HELPCTX(ssh_tunnels_xauthority),
-                    dlg_stdfilesel_handler, I(offsetof(Config, xauthfile)));
+                    conf_filesel_handler, I(CONF_xauthfile));
     }
 }
index 7c6bf8d..8453e2e 100644 (file)
@@ -2100,13 +2100,23 @@ void dlg_editbox_set(union control *ctrl, void *dlg, char const *text)
     SetDlgItemText(dp->hwnd, c->base_id+1, text);
 }
 
-void dlg_editbox_get(union control *ctrl, void *dlg, char *buffer, int length)
+char *dlg_editbox_get(union control *ctrl, void *dlg)
 {
     struct dlgparam *dp = (struct dlgparam *)dlg;
     struct winctrl *c = dlg_findbyctrl(dp, ctrl);
+    char *ret;
+    int size;
     assert(c && c->ctrl->generic.type == CTRL_EDITBOX);
-    GetDlgItemText(dp->hwnd, c->base_id+1, buffer, length);
-    buffer[length-1] = '\0';
+
+    size = 0;
+    ret = NULL;
+    do {
+       size = size * 4 / 3 + 512;
+       ret = sresize(ret, size, char);
+       GetDlgItemText(dp->hwnd, c->base_id+1, ret, size);
+    } while (!memchr(ret, '\0', size-1));
+
+    return ret;
 }
 
 /* The `listbox' functions can also apply to combo boxes. */
@@ -2471,8 +2481,10 @@ int dlg_coloursel_results(union control *ctrl, void *dlg,
 void dlg_auto_set_fixed_pitch_flag(void *dlg)
 {
     struct dlgparam *dp = (struct dlgparam *)dlg;
-    Config *cfg = (Config *)dp->data;
-    HFONT font;
+    Conf *conf = (Conf *)dp->data;
+    FontSpec *font;
+    int quality;
+    HFONT hfont;
     HDC hdc;
     TEXTMETRIC tm;
     int is_var;
@@ -2483,16 +2495,19 @@ void dlg_auto_set_fixed_pitch_flag(void *dlg)
      * dialog box as false.
      *
      * We assume here that any client of the dlg_* mechanism which is
-     * using font selectors at all is also using a normal 'Config *'
+     * using font selectors at all is also using a normal 'Conf *'
      * as dp->data.
      */
 
-    font = CreateFont(0, 0, 0, 0, FW_DONTCARE, FALSE, FALSE, FALSE,
-                      DEFAULT_CHARSET, OUT_DEFAULT_PRECIS,
-                      CLIP_DEFAULT_PRECIS, FONT_QUALITY(cfg->font_quality),
-                      FIXED_PITCH | FF_DONTCARE, cfg->font.name);
+    quality = conf_get_int(conf, CONF_font_quality);
+    font = conf_get_fontspec(conf, CONF_font);
+
+    hfont = CreateFont(0, 0, 0, 0, FW_DONTCARE, FALSE, FALSE, FALSE,
+                       DEFAULT_CHARSET, OUT_DEFAULT_PRECIS,
+                       CLIP_DEFAULT_PRECIS, FONT_QUALITY(quality),
+                       FIXED_PITCH | FF_DONTCARE, font->name);
     hdc = GetDC(NULL);
-    if (font && hdc && SelectObject(hdc, font) && GetTextMetrics(hdc, &tm)) {
+    if (font && hdc && SelectObject(hdc, hfont) && GetTextMetrics(hdc, &tm)) {
         /* Note that the TMPF_FIXED_PITCH bit is defined upside down :-( */
         is_var = (tm.tmPitchAndFamily & TMPF_FIXED_PITCH);
     } else {
@@ -2500,8 +2515,8 @@ void dlg_auto_set_fixed_pitch_flag(void *dlg)
     }
     if (hdc)
         ReleaseDC(NULL, hdc);
-    if (font)
-        DeleteObject(font);
+    if (hfont)
+        DeleteObject(hfont);
 
     if (is_var)
         dp->fixed_pitch_fonts = FALSE;
index 7377cd3..c24ec58 100644 (file)
@@ -44,7 +44,7 @@ static struct dlgparam dp;
 static char **events = NULL;
 static int nevents = 0, negsize = 0;
 
-extern Config cfg;                    /* defined in window.c */
+extern Conf *conf;                    /* defined in window.c */
 
 #define PRINTER_DISABLED_STRING "None (printing disabled)"
 
@@ -648,7 +648,7 @@ int do_config(void)
     dp_add_tree(&dp, &ctrls_panel);
     dp.wintitle = dupprintf("%s Configuration", appname);
     dp.errtitle = dupprintf("%s Error", appname);
-    dp.data = &cfg;
+    dp.data = conf;
     dlg_auto_set_fixed_pitch_flag(&dp);
     dp.shortcuts['g'] = TRUE;         /* the treeview: `Cate&gory' */
 
@@ -666,15 +666,15 @@ int do_config(void)
 
 int do_reconfig(HWND hwnd, int protcfginfo)
 {
-    Config backup_cfg;
-    int ret;
+    Conf *backup_conf;
+    int ret, protocol;
 
-    backup_cfg = cfg;                 /* structure copy */
+    backup_conf = conf_copy(conf);
 
     ctrlbox = ctrl_new_box();
-    setup_config_box(ctrlbox, TRUE, cfg.protocol, protcfginfo);
-    win_setup_config_box(ctrlbox, &dp.hwnd, has_help(), TRUE,
-                         cfg.protocol);
+    protocol = conf_get_int(conf, CONF_protocol);
+    setup_config_box(ctrlbox, TRUE, protocol, protcfginfo);
+    win_setup_config_box(ctrlbox, &dp.hwnd, has_help(), TRUE, protocol);
     dp_init(&dp);
     winctrl_init(&ctrls_base);
     winctrl_init(&ctrls_panel);
@@ -682,7 +682,7 @@ int do_reconfig(HWND hwnd, int protcfginfo)
     dp_add_tree(&dp, &ctrls_panel);
     dp.wintitle = dupprintf("%s Reconfiguration", appname);
     dp.errtitle = dupprintf("%s Error", appname);
-    dp.data = &cfg;
+    dp.data = conf;
     dlg_auto_set_fixed_pitch_flag(&dp);
     dp.shortcuts['g'] = TRUE;         /* the treeview: `Cate&gory' */
 
@@ -695,7 +695,9 @@ int do_reconfig(HWND hwnd, int protcfginfo)
     dp_cleanup(&dp);
 
     if (!ret)
-       cfg = backup_cfg;              /* structure copy */
+       conf_copy_into(conf, backup_conf);
+
+    conf_free(backup_conf);
 
     return ret;
 }
index c8c2b56..908c1f6 100644 (file)
@@ -80,7 +80,7 @@ static Mouse_Button translate_button(Mouse_Button button);
 static LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
 static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam,
                        unsigned char *output);
-static void cfgtopalette(void);
+static void conftopalette(void);
 static void systopalette(void);
 static void init_palette(void);
 static void init_fonts(int, int);
@@ -140,7 +140,11 @@ static struct {
 enum { SYSMENU, CTXMENU };
 static HMENU savedsess_menu;
 
-Config cfg;                           /* exported to windlg.c */
+Conf *conf;                           /* exported to windlg.c */
+
+static void conf_cache_data(void);
+int cursor_type;
+int vtmode;
 
 static struct sesslist sesslist;       /* for saved-session menu */
 
@@ -223,7 +227,7 @@ static void start_backend(void)
      * Select protocol. This is farmed out into a table in a
      * separate file to enable an ssh-free variant.
      */
-    back = backend_from_proto(cfg.protocol);
+    back = backend_from_proto(conf_get_int(conf, CONF_protocol));
     if (back == NULL) {
        char *str = dupprintf("%s Internal Error", appname);
        MessageBox(NULL, "Unsupported protocol number found",
@@ -232,22 +236,24 @@ static void start_backend(void)
        cleanup_exit(1);
     }
 
-    error = back->init(NULL, &backhandle, &cfg,
-                      cfg.host, cfg.port, &realhost, cfg.tcp_nodelay,
-                      cfg.tcp_keepalives);
+    error = back->init(NULL, &backhandle, conf,
+                      conf_get_str(conf, CONF_host),
+                      conf_get_int(conf, CONF_port),
+                      &realhost,
+                      conf_get_int(conf, CONF_tcp_nodelay),
+                      conf_get_int(conf, CONF_tcp_keepalives));
     back->provide_logctx(backhandle, logctx);
     if (error) {
        char *str = dupprintf("%s Error", appname);
        sprintf(msg, "Unable to open connection to\n"
-               "%.800s\n" "%s", cfg_dest(&cfg), error);
+               "%.800s\n" "%s", conf_dest(conf), error);
        MessageBox(NULL, msg, str, MB_ICONERROR | MB_OK);
        sfree(str);
        exit(0);
     }
     window_name = icon_name = NULL;
-    if (*cfg.wintitle) {
-       title = cfg.wintitle;
-    } else {
+    title = conf_get_str(conf, CONF_wintitle);
+    if (!*title) {
        sprintf(msg, "%s - %s", realhost, appname);
        title = msg;
     }
@@ -263,7 +269,7 @@ static void start_backend(void)
     /*
      * Set up a line discipline.
      */
-    ldisc = ldisc_create(&cfg, term, back, backhandle, NULL);
+    ldisc = ldisc_create(conf, term, back, backhandle, NULL);
 
     /*
      * Destroy the Restart Session menu item. (This will return
@@ -364,6 +370,8 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show)
 
     init_flashwindow();
 
+    conf = conf_new();
+
     /*
      * Initialize COM.
      */
@@ -395,9 +403,9 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show)
            if (b)
                default_port = b->default_port;
        }
-       cfg.logtype = LGTYP_NONE;
+       conf_set_int(conf, CONF_logtype, LGTYP_NONE);
 
-       do_defaults(NULL, &cfg);
+       do_defaults(NULL, conf);
 
        p = cmdline;
 
@@ -422,8 +430,8 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show)
            while (i > 1 && isspace(p[i - 1]))
                i--;
            p[i] = '\0';
-           do_defaults(p + 1, &cfg);
-           if (!cfg_launchable(&cfg) && !do_config()) {
+           do_defaults(p + 1, conf);
+           if (!conf_launchable(conf) && !do_config()) {
                cleanup_exit(0);
            }
            allow_launch = TRUE;    /* allow it to be launched directly */
@@ -431,15 +439,16 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show)
            /*
             * An initial & means we've been given a command line
             * containing the hex value of a HANDLE for a file
-            * mapping object, which we must then extract as a
-            * config.
+            * mapping object, which we must then interpret as a
+            * serialised Conf.
             */
            HANDLE filemap;
-           Config *cp;
-           if (sscanf(p + 1, "%p", &filemap) == 1 &&
+           void *cp;
+           unsigned cpsize;
+           if (sscanf(p + 1, "%p:%u", &filemap, &cpsize) == 1 &&
                (cp = MapViewOfFile(filemap, FILE_MAP_READ,
-                                   0, 0, sizeof(Config))) != NULL) {
-               cfg = *cp;
+                                   0, 0, cpsize)) != NULL) {
+               conf_deserialise(conf, cp, cpsize);
                UnmapViewOfFile(cp);
                CloseHandle(filemap);
            } else if (!do_config()) {
@@ -461,7 +470,7 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show)
                int ret;
 
                ret = cmdline_process_param(p, i+1<argc?argv[i+1]:NULL,
-                                           1, &cfg);
+                                           1, conf);
                if (ret == -2) {
                    cmdline_error("option \"%s\" requires an argument", p);
                } else if (ret == 2) {
@@ -522,7 +531,7 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show)
                         * argument, so that it will be deferred
                         * until it's a good moment to run it.
                         */
-                       int ret = cmdline_process_param("-P", p, 1, &cfg);
+                       int ret = cmdline_process_param("-P", p, 1, conf);
                        assert(ret == 2);
                    } else if (!strncmp(q, "telnet:", 7)) {
                        /*
@@ -535,7 +544,7 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show)
                        q += 7;
                        if (q[0] == '/' && q[1] == '/')
                            q += 2;
-                       cfg.protocol = PROT_TELNET;
+                       conf_set_int(conf, CONF_protocol, PROT_TELNET);
                        p = q;
                        while (*p && *p != ':' && *p != '/')
                            p++;
@@ -543,11 +552,10 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show)
                        if (*p)
                            *p++ = '\0';
                        if (c == ':')
-                           cfg.port = atoi(p);
+                           conf_set_int(conf, CONF_port, atoi(p));
                        else
-                           cfg.port = -1;
-                       strncpy(cfg.host, q, sizeof(cfg.host) - 1);
-                       cfg.host[sizeof(cfg.host) - 1] = '\0';
+                           conf_set_int(conf, CONF_port, -1);
+                       conf_set_str(conf, CONF_host, q);
                        got_host = 1;
                    } else {
                        /*
@@ -558,8 +566,7 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show)
                            p++;
                        if (*p)
                            *p++ = '\0';
-                       strncpy(cfg.host, q, sizeof(cfg.host) - 1);
-                       cfg.host[sizeof(cfg.host) - 1] = '\0';
+                       conf_set_str(conf, CONF_host, q);
                        got_host = 1;
                    }
                } else {
@@ -568,65 +575,60 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show)
            }
        }
 
-       cmdline_run_saved(&cfg);
+       cmdline_run_saved(conf);
 
        if (loaded_session || got_host)
            allow_launch = TRUE;
 
-       if ((!allow_launch || !cfg_launchable(&cfg)) && !do_config()) {
+       if ((!allow_launch || !conf_launchable(conf)) && !do_config()) {
            cleanup_exit(0);
        }
 
        /*
-        * Trim leading whitespace off the hostname if it's there.
+        * Muck about with the hostname in various ways.
         */
        {
-           int space = strspn(cfg.host, " \t");
-           memmove(cfg.host, cfg.host+space, 1+strlen(cfg.host)-space);
-       }
+           char *hostbuf = dupstr(conf_get_str(conf, CONF_host));
+           char *host = hostbuf;
+           char *p, *q;
 
-       /* See if host is of the form user@host */
-       if (cfg.host[0] != '\0') {
-           char *atsign = strrchr(cfg.host, '@');
-           /* Make sure we're not overflowing the user field */
-           if (atsign) {
-               if (atsign - cfg.host < sizeof cfg.username) {
-                   strncpy(cfg.username, cfg.host, atsign - cfg.host);
-                   cfg.username[atsign - cfg.host] = '\0';
+           /*
+            * Trim leading whitespace.
+            */
+           host += strspn(host, " \t");
+
+           /*
+            * See if host is of the form user@host, and separate
+            * out the username if so.
+            */
+           if (host[0] != '\0') {
+               char *atsign = strrchr(host, '@');
+               if (atsign) {
+                   *atsign = '\0';
+                   conf_set_str(conf, CONF_username, host);
+                   host = atsign + 1;
                }
-               memmove(cfg.host, atsign + 1, 1 + strlen(atsign + 1));
            }
-       }
 
-       /*
-        * Trim a colon suffix off the hostname if it's there. In
-        * order to protect IPv6 address literals against this
-        * treatment, we do not do this if there's _more_ than one
-        * colon.
-        */
-       {
-           char *c = strchr(cfg.host, ':');
+           /*
+            * Trim off a colon suffix if it's there.
+            */
+           host[strcspn(host, ":")] = '\0';
 
-           if (c) {
-               char *d = strchr(c+1, ':');
-               if (!d)
-                   *c = '\0';
+           /*
+            * Remove any remaining whitespace.
+            */
+           p = hostbuf;
+           q = host;
+           while (*q) {
+               if (*q != ' ' && *q != '\t')
+                   *p++ = *q;
+               q++;
            }
-       }
+           *p = '\0';
 
-       /*
-        * Remove any remaining whitespace from the hostname.
-        */
-       {
-           int p1 = 0, p2 = 0;
-           while (cfg.host[p2] != '\0') {
-               if (cfg.host[p2] != ' ' && cfg.host[p2] != '\t') {
-                   cfg.host[p1] = cfg.host[p2];
-                   p1++;
-               }
-               p2++;
-           }
-           cfg.host[p1] = '\0';
+           conf_set_str(conf, CONF_host, hostbuf);
+           sfree(hostbuf);
        }
     }
 
@@ -647,7 +649,9 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show)
 
     memset(&ucsdata, 0, sizeof(ucsdata));
 
-    cfgtopalette();
+    conf_cache_data();
+
+    conftopalette();
 
     /*
      * Guess some defaults for the window size. This all gets
@@ -660,8 +664,8 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show)
     font_height = 20;
     extra_width = 25;
     extra_height = 28;
-    guess_width = extra_width + font_width * cfg.width;
-    guess_height = extra_height + font_height * cfg.height;
+    guess_width = extra_width + font_width * conf_get_int(conf, CONF_width);
+    guess_height = extra_height + font_height*conf_get_int(conf, CONF_height);
     {
        RECT r;
        get_fullscreen_rect(&r);
@@ -674,13 +678,13 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show)
     {
        int winmode = WS_OVERLAPPEDWINDOW | WS_VSCROLL;
        int exwinmode = 0;
-       if (!cfg.scrollbar)
+       if (!conf_get_int(conf, CONF_scrollbar))
            winmode &= ~(WS_VSCROLL);
-       if (cfg.resize_action == RESIZE_DISABLED)
+       if (conf_get_int(conf, CONF_resize_action) == RESIZE_DISABLED)
            winmode &= ~(WS_THICKFRAME | WS_MAXIMIZEBOX);
-       if (cfg.alwaysontop)
+       if (conf_get_int(conf, CONF_alwaysontop))
            exwinmode |= WS_EX_TOPMOST;
-       if (cfg.sunken_edge)
+       if (conf_get_int(conf, CONF_sunken_edge))
            exwinmode |= WS_EX_CLIENTEDGE;
        hwnd = CreateWindowEx(exwinmode, appname, appname,
                              winmode, CW_USEDEFAULT, CW_USEDEFAULT,
@@ -694,10 +698,12 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show)
      * which will call schedule_timer(), which will in turn call
      * timer_change_notify() which will expect hwnd to exist.)
      */
-    term = term_init(&cfg, &ucsdata, NULL);
-    logctx = log_init(NULL, &cfg);
+    term = term_init(conf, &ucsdata, NULL);
+    logctx = log_init(NULL, conf);
     term_provide_logctx(term, logctx);
-    term_size(term, cfg.height, cfg.width, cfg.savelines);
+    term_size(term, conf_get_int(conf, CONF_height),
+             conf_get_int(conf, CONF_width),
+             conf_get_int(conf, CONF_savelines));
 
     /*
      * Initialise the fonts, simultaneously correcting the guesses
@@ -712,7 +718,7 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show)
        RECT cr, wr;
        GetWindowRect(hwnd, &wr);
        GetClientRect(hwnd, &cr);
-       offset_width = offset_height = cfg.window_border;
+       offset_width = offset_height = conf_get_int(conf, CONF_window_border);
        extra_width = wr.right - wr.left - cr.right + cr.left + offset_width*2;
        extra_height = wr.bottom - wr.top - cr.bottom + cr.top +offset_height*2;
     }
@@ -793,8 +799,9 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show)
            AppendMenu(m, MF_ENABLED, IDM_CLRSB, "C&lear Scrollback");
            AppendMenu(m, MF_ENABLED, IDM_RESET, "Rese&t Terminal");
            AppendMenu(m, MF_SEPARATOR, 0, 0);
-           AppendMenu(m, (cfg.resize_action == RESIZE_DISABLED) ?
-                      MF_GRAYED : MF_ENABLED, IDM_FULLSCREEN, "&Full Screen");
+           AppendMenu(m, (conf_get_int(conf, CONF_resize_action)
+                          == RESIZE_DISABLED) ? MF_GRAYED : MF_ENABLED,
+                      IDM_FULLSCREEN, "&Full Screen");
            AppendMenu(m, MF_SEPARATOR, 0, 0);
            if (has_help())
                AppendMenu(m, MF_ENABLED, IDM_HELP, "&Help");
@@ -888,7 +895,7 @@ void cleanup_exit(int code)
        DeleteObject(pal);
     sk_cleanup();
 
-    if (cfg.protocol == PROT_SSH) {
+    if (conf_get_int(conf, CONF_protocol) == PROT_SSH) {
        random_save_seed();
 #ifdef MSCRYPTOAPI
        crypto_wrapup();
@@ -1066,7 +1073,7 @@ void set_busy_status(void *frontend, int status)
  */
 void set_raw_mouse_mode(void *frontend, int activate)
 {
-    activate = activate && !cfg.no_mouse_rep;
+    activate = activate && !conf_get_int(conf, CONF_no_mouse_rep);
     send_raw_mouse = activate;
     update_mouse_pointer();
 }
@@ -1086,7 +1093,7 @@ void connection_fatal(void *frontend, char *fmt, ...)
     MessageBox(hwnd, stuff, morestuff, MB_ICONERROR | MB_OK);
     sfree(stuff);
 
-    if (cfg.close_on_exit == FORCE_ON)
+    if (conf_get_int(conf, CONF_close_on_exit) == FORCE_ON)
        PostQuitMessage(1);
     else {
        must_close_session = TRUE;
@@ -1132,7 +1139,7 @@ static void enact_pending_netevent(void)
  * Copy the colour palette from the configuration data into defpal.
  * This is non-trivial because the colour indices are different.
  */
-static void cfgtopalette(void)
+static void conftopalette(void)
 {
     int i;
     static const int ww[] = {
@@ -1143,9 +1150,9 @@ static void cfgtopalette(void)
 
     for (i = 0; i < 22; i++) {
        int w = ww[i];
-       defpal[w].rgbtRed = cfg.colours[i][0];
-       defpal[w].rgbtGreen = cfg.colours[i][1];
-       defpal[w].rgbtBlue = cfg.colours[i][2];
+       defpal[w].rgbtRed = conf_get_int_int(conf, CONF_colours, i*3+0);
+       defpal[w].rgbtGreen = conf_get_int_int(conf, CONF_colours, i*3+1);
+       defpal[w].rgbtBlue = conf_get_int_int(conf, CONF_colours, i*3+2);
     }
     for (i = 0; i < NEXTCOLOURS; i++) {
        if (i < 216) {
@@ -1162,7 +1169,7 @@ static void cfgtopalette(void)
     }
 
     /* Override with system colours if appropriate */
-    if (cfg.system_colour)
+    if (conf_get_int(conf, CONF_system_colour))
         systopalette();
 }
 
@@ -1202,7 +1209,8 @@ static void init_palette(void)
     int i;
     HDC hdc = GetDC(hwnd);
     if (hdc) {
-       if (cfg.try_palette && GetDeviceCaps(hdc, RASTERCAPS) & RC_PALETTE) {
+       if (conf_get_int(conf, CONF_try_palette) &&
+           GetDeviceCaps(hdc, RASTERCAPS) & RC_PALETTE) {
            /*
             * This is a genuine case where we must use smalloc
             * because the snew macros can't cope.
@@ -1385,18 +1393,22 @@ static void init_fonts(int pick_width, int pick_height)
 {
     TEXTMETRIC tm;
     CPINFO cpinfo;
+    FontSpec *font;
     int fontsize[3];
     int i;
+    int quality;
     HDC hdc;
     int fw_dontcare, fw_bold;
 
     for (i = 0; i < FONT_MAXNO; i++)
        fonts[i] = NULL;
 
-    bold_mode = cfg.bold_colour ? BOLD_COLOURS : BOLD_FONT;
+    bold_mode = conf_get_int(conf, CONF_bold_colour) ?
+       BOLD_COLOURS : BOLD_FONT;
     und_mode = UND_FONT;
 
-    if (cfg.font.isbold) {
+    font = conf_get_fontspec(conf, CONF_font);
+    if (font->isbold) {
        fw_dontcare = FW_BOLD;
        fw_bold = FW_HEAVY;
     } else {
@@ -1409,7 +1421,7 @@ static void init_fonts(int pick_width, int pick_height)
     if (pick_height)
        font_height = pick_height;
     else {
-       font_height = cfg.font.height;
+       font_height = font->height;
        if (font_height > 0) {
            font_height =
                -MulDiv(font_height, GetDeviceCaps(hdc, LOGPIXELSY), 72);
@@ -1417,13 +1429,14 @@ static void init_fonts(int pick_width, int pick_height)
     }
     font_width = pick_width;
 
+    quality = conf_get_int(conf, CONF_font_quality);
 #define f(i,c,w,u) \
     fonts[i] = CreateFont (font_height, font_width, 0, 0, w, FALSE, u, FALSE, \
                           c, OUT_DEFAULT_PRECIS, \
-                          CLIP_DEFAULT_PRECIS, FONT_QUALITY(cfg.font_quality), \
-                          FIXED_PITCH | FF_DONTCARE, cfg.font.name)
+                          CLIP_DEFAULT_PRECIS, quality, \
+                          FIXED_PITCH | FF_DONTCARE, font->name)
 
-    f(FONT_NORMAL, cfg.font.charset, fw_dontcare, FALSE);
+    f(FONT_NORMAL, font->charset, fw_dontcare, FALSE);
 
     SelectObject(hdc, fonts[FONT_NORMAL]);
     GetTextMetrics(hdc, &tm);
@@ -1466,7 +1479,7 @@ static void init_fonts(int pick_width, int pick_height)
        ucsdata.dbcs_screenfont = (cpinfo.MaxCharSize > 1);
     }
 
-    f(FONT_UNDERLINE, cfg.font.charset, fw_dontcare, TRUE);
+    f(FONT_UNDERLINE, font->charset, fw_dontcare, TRUE);
 
     /*
      * Some fonts, e.g. 9-pt Courier, draw their underlines
@@ -1517,7 +1530,7 @@ static void init_fonts(int pick_width, int pick_height)
     }
 
     if (bold_mode == BOLD_FONT) {
-       f(FONT_BOLD, cfg.font.charset, fw_bold, FALSE);
+       f(FONT_BOLD, font->charset, fw_bold, FALSE);
     }
 #undef f
 
@@ -1551,15 +1564,16 @@ static void init_fonts(int pick_width, int pick_height)
     }
     fontflag[0] = fontflag[1] = fontflag[2] = 1;
 
-    init_ucs(&cfg, &ucsdata);
+    init_ucs(conf, &ucsdata);
 }
 
 static void another_font(int fontno)
 {
     int basefont;
-    int fw_dontcare, fw_bold;
+    int fw_dontcare, fw_bold, quality;
     int c, u, w, x;
     char *s;
+    FontSpec *font;
 
     if (fontno < 0 || fontno >= FONT_MAXNO || fontflag[fontno])
        return;
@@ -1568,7 +1582,9 @@ static void another_font(int fontno)
     if (basefont != fontno && !fontflag[basefont])
        another_font(basefont);
 
-    if (cfg.font.isbold) {
+    font = conf_get_fontspec(conf, CONF_font);
+
+    if (font->isbold) {
        fw_dontcare = FW_BOLD;
        fw_bold = FW_HEAVY;
     } else {
@@ -1576,10 +1592,10 @@ static void another_font(int fontno)
        fw_bold = FW_BOLD;
     }
 
-    c = cfg.font.charset;
+    c = font->charset;
     w = fw_dontcare;
     u = FALSE;
-    s = cfg.font.name;
+    s = font->name;
     x = font_width;
 
     if (fontno & FONT_WIDE)
@@ -1593,10 +1609,12 @@ static void another_font(int fontno)
     if (fontno & FONT_UNDERLINE)
        u = TRUE;
 
+    quality = conf_get_int(conf, CONF_font_quality);
+
     fonts[fontno] =
        CreateFont(font_height * (1 + !!(fontno & FONT_HIGH)), x, 0, 0, w,
                   FALSE, u, FALSE, c, OUT_DEFAULT_PRECIS,
-                  CLIP_DEFAULT_PRECIS, FONT_QUALITY(cfg.font_quality),
+                  CLIP_DEFAULT_PRECIS, FONT_QUALITY(quality),
                   DEFAULT_PITCH | FF_DONTCARE, s);
 
     fontflag[fontno] = 1;
@@ -1619,11 +1637,11 @@ void request_resize(void *frontend, int w, int h)
 
     /* If the window is maximized supress resizing attempts */
     if (IsZoomed(hwnd)) {
-       if (cfg.resize_action == RESIZE_TERM)
+       if (conf_get_int(conf, CONF_resize_action) == RESIZE_TERM)
            return;
     }
 
-    if (cfg.resize_action == RESIZE_DISABLED) return;
+    if (conf_get_int(conf, CONF_resize_action) == RESIZE_DISABLED) return;
     if (h == term->rows && w == term->cols) return;
 
     /* Sanity checks ... */
@@ -1654,9 +1672,10 @@ void request_resize(void *frontend, int w, int h)
        }
     }
 
-    term_size(term, h, w, cfg.savelines);
+    term_size(term, h, w, conf_get_int(conf, CONF_savelines));
 
-    if (cfg.resize_action != RESIZE_FONT && !IsZoomed(hwnd)) {
+    if (conf_get_int(conf, CONF_resize_action) != RESIZE_FONT &&
+       !IsZoomed(hwnd)) {
        width = extra_width + font_width * w;
        height = extra_height + font_height * h;
 
@@ -1677,7 +1696,7 @@ static void reset_window(int reinit) {
      * This function doesn't like to change the terminal size but if the
      * font size is locked that may be it's only soluion.
      */
-    int win_width, win_height;
+    int win_width, win_height, resize_action, window_border;
     RECT cr, wr;
 
 #ifdef RDB_DEBUG_PATCH
@@ -1691,7 +1710,11 @@ static void reset_window(int reinit) {
     win_width  = cr.right - cr.left;
     win_height = cr.bottom - cr.top;
 
-    if (cfg.resize_action == RESIZE_DISABLED) reinit = 2;
+    resize_action = conf_get_int(conf, CONF_resize_action);
+    window_border = conf_get_int(conf, CONF_window_border);
+
+    if (resize_action == RESIZE_DISABLED)
+       reinit = 2;
 
     /* Are we being forced to reload the fonts ? */
     if (reinit>1) {
@@ -1726,9 +1749,9 @@ static void reset_window(int reinit) {
        extra_width = wr.right - wr.left - cr.right + cr.left;
        extra_height = wr.bottom - wr.top - cr.bottom + cr.top;
 
-       if (cfg.resize_action != RESIZE_TERM) {
-           if (  font_width != win_width/term->cols || 
-                 font_height != win_height/term->rows) {
+       if (resize_action != RESIZE_TERM) {
+           if (font_width != win_width/term->cols || 
+               font_height != win_height/term->rows) {
                deinit_fonts();
                init_fonts(win_width/term->cols, win_height/term->rows);
                offset_width = (win_width-font_width*term->cols)/2;
@@ -1740,13 +1763,13 @@ static void reset_window(int reinit) {
 #endif
            }
        } else {
-           if (  font_width * term->cols != win_width || 
-                 font_height * term->rows != win_height) {
+           if (font_width * term->cols != win_width || 
+               font_height * term->rows != win_height) {
                /* Our only choice at this point is to change the 
                 * size of the terminal; Oh well.
                 */
                term_size(term, win_height/font_height, win_width/font_width,
-                         cfg.savelines);
+                         conf_get_int(conf, CONF_savelines));
                offset_width = (win_width-font_width*term->cols)/2;
                offset_height = (win_height-font_height*term->rows)/2;
                InvalidateRect(hwnd, NULL, TRUE);
@@ -1766,7 +1789,7 @@ static void reset_window(int reinit) {
        debug((27, "reset_window() -> Forced re-init"));
 #endif
 
-       offset_width = offset_height = cfg.window_border;
+       offset_width = offset_height = window_border;
        extra_width = wr.right - wr.left - cr.right + cr.left + offset_width*2;
        extra_height = wr.bottom - wr.top - cr.bottom + cr.top +offset_height*2;
 
@@ -1791,10 +1814,10 @@ static void reset_window(int reinit) {
      * window. But that may be too big for the screen which forces us
      * to change the terminal.
      */
-    if ((cfg.resize_action == RESIZE_TERM && reinit<=0) ||
-        (cfg.resize_action == RESIZE_EITHER && reinit<0) ||
+    if ((resize_action == RESIZE_TERM && reinit<=0) ||
+        (resize_action == RESIZE_EITHER && reinit<0) ||
            reinit>0) {
-       offset_width = offset_height = cfg.window_border;
+       offset_width = offset_height = window_border;
        extra_width = wr.right - wr.left - cr.right + cr.left + offset_width*2;
        extra_height = wr.bottom - wr.top - cr.bottom + cr.top +offset_height*2;
 
@@ -1811,7 +1834,7 @@ static void reset_window(int reinit) {
 
            /* Grrr too big */
            if ( term->rows > height || term->cols > width ) {
-               if (cfg.resize_action == RESIZE_EITHER) {
+               if (resize_action == RESIZE_EITHER) {
                    /* Make the font the biggest we can */
                    if (term->cols > width)
                        font_width = (ss.right - ss.left - extra_width)
@@ -1828,7 +1851,8 @@ static void reset_window(int reinit) {
                } else {
                    if ( height > term->rows ) height = term->rows;
                    if ( width > term->cols )  width = term->cols;
-                   term_size(term, height, width, cfg.savelines);
+                   term_size(term, height, width,
+                             conf_get_int(conf, CONF_savelines));
 #ifdef RDB_DEBUG_PATCH
                    debug((27, "reset_window() -> term resize to (%d,%d)",
                               height, width));
@@ -1853,12 +1877,12 @@ static void reset_window(int reinit) {
 
     /* We're allowed to or must change the font but do we want to ?  */
 
-    if (font_width != (win_width-cfg.window_border*2)/term->cols || 
-       font_height != (win_height-cfg.window_border*2)/term->rows) {
+    if (font_width != (win_width-window_border*2)/term->cols || 
+       font_height != (win_height-window_border*2)/term->rows) {
 
        deinit_fonts();
-       init_fonts((win_width-cfg.window_border*2)/term->cols, 
-                  (win_height-cfg.window_border*2)/term->rows);
+       init_fonts((win_width-window_border*2)/term->cols, 
+                  (win_height-window_border*2)/term->rows);
        offset_width = (win_width-font_width*term->cols)/2;
        offset_height = (win_height-font_height*term->rows)/2;
 
@@ -1887,7 +1911,8 @@ static void click(Mouse_Button b, int x, int y, int shift, int ctrl, int alt)
 {
     int thistime = GetMessageTime();
 
-    if (send_raw_mouse && !(cfg.mouse_override && shift)) {
+    if (send_raw_mouse &&
+       !(shift && conf_get_int(conf, CONF_mouse_override))) {
        lastbtn = MBT_NOTHING;
        term_mouse(term, b, translate_button(b), MA_CLICK,
                   x, y, shift, ctrl, alt);
@@ -1917,9 +1942,11 @@ static Mouse_Button translate_button(Mouse_Button button)
     if (button == MBT_LEFT)
        return MBT_SELECT;
     if (button == MBT_MIDDLE)
-       return cfg.mouse_is_xterm == 1 ? MBT_PASTE : MBT_EXTEND;
+       return conf_get_int(conf, CONF_mouse_is_xterm) == 1 ?
+       MBT_PASTE : MBT_EXTEND;
     if (button == MBT_RIGHT)
-       return cfg.mouse_is_xterm == 1 ? MBT_EXTEND : MBT_PASTE;
+       return conf_get_int(conf, CONF_mouse_is_xterm) == 1 ?
+       MBT_EXTEND : MBT_PASTE;
     return 0;                         /* shouldn't happen */
 }
 
@@ -1928,8 +1955,8 @@ static void show_mouseptr(int show)
     /* NB that the counter in ShowCursor() is also frobbed by
      * update_mouse_pointer() */
     static int cursor_visible = 1;
-    if (!cfg.hide_mouseptr)           /* override if this feature disabled */
-       show = 1;
+    if (!conf_get_int(conf, CONF_hide_mouseptr))
+       show = 1;                      /* override if this feature disabled */
     if (cursor_visible && !show)
        ShowCursor(FALSE);
     else if (!cursor_visible && show)
@@ -1954,14 +1981,15 @@ static int resizing;
 
 void notify_remote_exit(void *fe)
 {
-    int exitcode;
+    int exitcode, close_on_exit;
 
     if (!session_closed &&
         (exitcode = back->exitcode(backhandle)) >= 0) {
+       close_on_exit = conf_get_int(conf, CONF_close_on_exit);
        /* Abnormal exits will already have set session_closed and taken
         * appropriate action. */
-       if (cfg.close_on_exit == FORCE_ON ||
-           (cfg.close_on_exit == AUTO && exitcode != INT_MAX)) {
+       if (close_on_exit == FORCE_ON ||
+           (close_on_exit == AUTO && exitcode != INT_MAX)) {
            PostQuitMessage(0);
        } else {
            must_close_session = TRUE;
@@ -1985,6 +2013,13 @@ void timer_change_notify(long next)
     timing_next_time = next;
 }
 
+static void conf_cache_data(void)
+{
+    /* Cache some items from conf to speed lookups in very hot code */
+    cursor_type = conf_get_int(conf, CONF_cursor_type);
+    vtmode = conf_get_int(conf, CONF_vtmode);
+}
+
 static LRESULT CALLBACK WndProc(HWND hwnd, UINT message,
                                WPARAM wParam, LPARAM lParam)
 {
@@ -1994,6 +2029,7 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message,
     static int fullscr_on_max = FALSE;
     static int processed_resize = FALSE;
     static UINT last_mousemove = 0;
+    int resize_action;
 
     switch (message) {
       case WM_TIMER:
@@ -2014,7 +2050,7 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message,
            char *str;
            show_mouseptr(1);
            str = dupprintf("%s Exit Confirmation", appname);
-           if (!cfg.warn_on_close || session_closed ||
+           if (session_closed || !conf_get_int(conf, CONF_warn_on_close) ||
                MessageBox(hwnd,
                           "Are you sure you want to close this session?",
                           str, MB_ICONWARNING | MB_OKCANCEL | MB_DEFBUTTON1)
@@ -2061,7 +2097,10 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message,
                     * config structure.
                     */
                    SECURITY_ATTRIBUTES sa;
-                   Config *p;
+                   void *p;
+                   int size;
+
+                   size = conf_serialised_size(conf);
 
                    sa.nLength = sizeof(sa);
                    sa.lpSecurityDescriptor = NULL;
@@ -2069,18 +2108,16 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message,
                    filemap = CreateFileMapping(INVALID_HANDLE_VALUE,
                                                &sa,
                                                PAGE_READWRITE,
-                                               0, sizeof(Config), NULL);
+                                               0, size, NULL);
                    if (filemap && filemap != INVALID_HANDLE_VALUE) {
-                       p = (Config *) MapViewOfFile(filemap,
-                                                    FILE_MAP_WRITE,
-                                                    0, 0, sizeof(Config));
+                       p = MapViewOfFile(filemap, FILE_MAP_WRITE, 0, 0, size);
                        if (p) {
-                           *p = cfg;  /* structure copy */
+                           conf_serialise(conf, p);
                            UnmapViewOfFile(p);
                        }
                    }
                    inherit_handles = TRUE;
-                   sprintf(c, "putty &%p", filemap);
+                   sprintf(c, "putty &%p:%u", filemap, (unsigned)size);
                    cl = c;
                } else if (wParam == IDM_SAVEDSESS) {
                    unsigned int sessno = ((lParam - IDM_SAVED_MIN)
@@ -2124,7 +2161,7 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message,
            break;
          case IDM_RECONF:
            {
-               Config prev_cfg;
+               Conf *prev_conf;
                int init_lvl = 1;
                int reconfig_result;
 
@@ -2133,8 +2170,15 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message,
                else
                    reconfiguring = TRUE;
 
-               GetWindowText(hwnd, cfg.wintitle, sizeof(cfg.wintitle));
-               prev_cfg = cfg;
+               /*
+                * Copy the current window title into the stored
+                * previous configuration, so that doing nothing to
+                * the window title field in the config box doesn't
+                * reset the title to its startup state.
+                */
+               conf_set_str(conf, CONF_wintitle, window_name);
+
+               prev_conf = conf_copy(conf);
 
                reconfig_result =
                    do_reconfig(hwnd, back ? back->cfg_info(backhandle) : 0);
@@ -2142,53 +2186,60 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message,
                if (!reconfig_result)
                    break;
 
+               conf_cache_data();
+
+               resize_action = conf_get_int(conf, CONF_resize_action);
                {
                    /* Disable full-screen if resizing forbidden */
                    int i;
                    for (i = 0; i < lenof(popup_menus); i++)
                        EnableMenuItem(popup_menus[i].menu, IDM_FULLSCREEN,
                                       MF_BYCOMMAND | 
-                                      (cfg.resize_action == RESIZE_DISABLED)
+                                      (resize_action == RESIZE_DISABLED)
                                       ? MF_GRAYED : MF_ENABLED);
                    /* Gracefully unzoom if necessary */
-                   if (IsZoomed(hwnd) &&
-                       (cfg.resize_action == RESIZE_DISABLED)) {
+                   if (IsZoomed(hwnd) && (resize_action == RESIZE_DISABLED))
                        ShowWindow(hwnd, SW_RESTORE);
-                   }
                }
 
                /* Pass new config data to the logging module */
-               log_reconfig(logctx, &cfg);
+               log_reconfig(logctx, conf);
 
                sfree(logpal);
                /*
                 * Flush the line discipline's edit buffer in the
                 * case where local editing has just been disabled.
                 */
+               ldisc_configure(ldisc, conf);
                if (ldisc)
                    ldisc_send(ldisc, NULL, 0, 0);
                if (pal)
                    DeleteObject(pal);
                logpal = NULL;
                pal = NULL;
-               cfgtopalette();
+               conftopalette();
                init_palette();
 
                /* Pass new config data to the terminal */
-               term_reconfig(term, &cfg);
+               term_reconfig(term, conf);
 
                /* Pass new config data to the back end */
                if (back)
-                   back->reconfig(backhandle, &cfg);
+                   back->reconfig(backhandle, conf);
 
                /* Screen size changed ? */
-               if (cfg.height != prev_cfg.height ||
-                   cfg.width != prev_cfg.width ||
-                   cfg.savelines != prev_cfg.savelines ||
-                   cfg.resize_action == RESIZE_FONT ||
-                   (cfg.resize_action == RESIZE_EITHER && IsZoomed(hwnd)) ||
-                   cfg.resize_action == RESIZE_DISABLED)
-                   term_size(term, cfg.height, cfg.width, cfg.savelines);
+               if (conf_get_int(conf, CONF_height) !=
+                   conf_get_int(prev_conf, CONF_height) ||
+                   conf_get_int(conf, CONF_width) !=
+                   conf_get_int(prev_conf, CONF_width) ||
+                   conf_get_int(conf, CONF_savelines) !=
+                   conf_get_int(prev_conf, CONF_savelines) ||
+                   resize_action == RESIZE_FONT ||
+                   (resize_action == RESIZE_EITHER && IsZoomed(hwnd)) ||
+                   resize_action == RESIZE_DISABLED)
+                   term_size(term, conf_get_int(conf, CONF_height),
+                             conf_get_int(conf, CONF_width),
+                             conf_get_int(conf, CONF_savelines));
 
                /* Enable or disable the scroll bar, etc */
                {
@@ -2197,8 +2248,9 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message,
                        GetWindowLongPtr(hwnd, GWL_EXSTYLE);
 
                    nexflag = exflag;
-                   if (cfg.alwaysontop != prev_cfg.alwaysontop) {
-                       if (cfg.alwaysontop) {
+                   if (conf_get_int(conf, CONF_alwaysontop) !=
+                       conf_get_int(prev_conf, CONF_alwaysontop)) {
+                       if (conf_get_int(conf, CONF_alwaysontop)) {
                            nexflag |= WS_EX_TOPMOST;
                            SetWindowPos(hwnd, HWND_TOPMOST, 0, 0, 0, 0,
                                         SWP_NOMOVE | SWP_NOSIZE);
@@ -2208,25 +2260,26 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message,
                                         SWP_NOMOVE | SWP_NOSIZE);
                        }
                    }
-                   if (cfg.sunken_edge)
+                   if (conf_get_int(conf, CONF_sunken_edge))
                        nexflag |= WS_EX_CLIENTEDGE;
                    else
                        nexflag &= ~(WS_EX_CLIENTEDGE);
 
                    nflg = flag;
-                   if (is_full_screen() ?
-                       cfg.scrollbar_in_fullscreen : cfg.scrollbar)
+                   if (conf_get_int(conf, is_full_screen() ?
+                                    CONF_scrollbar_in_fullscreen :
+                                    CONF_scrollbar))
                        nflg |= WS_VSCROLL;
                    else
                        nflg &= ~WS_VSCROLL;
 
-                   if (cfg.resize_action == RESIZE_DISABLED ||
+                   if (resize_action == RESIZE_DISABLED ||
                         is_full_screen())
                        nflg &= ~WS_THICKFRAME;
                    else
                        nflg |= WS_THICKFRAME;
 
-                   if (cfg.resize_action == RESIZE_DISABLED)
+                   if (resize_action == RESIZE_DISABLED)
                        nflg &= ~WS_MAXIMIZEBOX;
                    else
                        nflg |= WS_MAXIMIZEBOX;
@@ -2247,34 +2300,47 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message,
                }
 
                /* Oops */
-               if (cfg.resize_action == RESIZE_DISABLED && IsZoomed(hwnd)) {
+               if (resize_action == RESIZE_DISABLED && IsZoomed(hwnd)) {
                    force_normal(hwnd);
                    init_lvl = 2;
                }
 
-               set_title(NULL, cfg.wintitle);
+               set_title(NULL, conf_get_str(conf, CONF_wintitle));
                if (IsIconic(hwnd)) {
                    SetWindowText(hwnd,
-                                 cfg.win_name_always ? window_name :
-                                 icon_name);
+                                 conf_get_int(conf, CONF_win_name_always) ?
+                                 window_name : icon_name);
                }
 
-               if (strcmp(cfg.font.name, prev_cfg.font.name) != 0 ||
-                   strcmp(cfg.line_codepage, prev_cfg.line_codepage) != 0 ||
-                   cfg.font.isbold != prev_cfg.font.isbold ||
-                   cfg.font.height != prev_cfg.font.height ||
-                   cfg.font.charset != prev_cfg.font.charset ||
-                   cfg.font_quality != prev_cfg.font_quality ||
-                   cfg.vtmode != prev_cfg.vtmode ||
-                   cfg.bold_colour != prev_cfg.bold_colour ||
-                   cfg.resize_action == RESIZE_DISABLED ||
-                   cfg.resize_action == RESIZE_EITHER ||
-                   (cfg.resize_action != prev_cfg.resize_action))
-                   init_lvl = 2;
+               {
+                   FontSpec *font = conf_get_fontspec(conf, CONF_font);
+                   FontSpec *prev_font = conf_get_fontspec(prev_conf,
+                                                           CONF_font);
+
+                   if (!strcmp(font->name, prev_font->name) ||
+                       !strcmp(conf_get_str(conf, CONF_line_codepage),
+                               conf_get_str(prev_conf, CONF_line_codepage)) ||
+                       font->isbold != prev_font->isbold ||
+                       font->height != prev_font->height ||
+                       font->charset != prev_font->charset ||
+                       conf_get_int(conf, CONF_font_quality) !=
+                       conf_get_int(prev_conf, CONF_font_quality) ||
+                       conf_get_int(conf, CONF_vtmode) !=
+                       conf_get_int(prev_conf, CONF_vtmode) ||
+                       conf_get_int(conf, CONF_bold_colour) !=
+                       conf_get_int(prev_conf, CONF_bold_colour) ||
+                       resize_action == RESIZE_DISABLED ||
+                       resize_action == RESIZE_EITHER ||
+                       resize_action != conf_get_int(prev_conf,
+                                                     CONF_resize_action))
+                       init_lvl = 2;
+               }
 
                InvalidateRect(hwnd, NULL, TRUE);
                reset_window(init_lvl);
                net_pending_errors();
+
+               conf_free(prev_conf);
            }
            break;
          case IDM_COPYALL:
@@ -2352,7 +2418,8 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message,
       case WM_MBUTTONUP:
       case WM_RBUTTONUP:
        if (message == WM_RBUTTONDOWN &&
-           ((wParam & MK_CONTROL) || (cfg.mouse_is_xterm == 2))) {
+           ((wParam & MK_CONTROL) ||
+            (conf_get_int(conf, CONF_mouse_is_xterm) == 2))) {
            POINT cursorpos;
 
            show_mouseptr(1);          /* make sure pointer is visible */
@@ -2659,7 +2726,9 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message,
        debug((27, "WM_EXITSIZEMOVE"));
 #endif
        if (need_backend_resize) {
-           term_size(term, cfg.height, cfg.width, cfg.savelines);
+           term_size(term, conf_get_int(conf, CONF_height),
+                     conf_get_int(conf, CONF_width),
+                     conf_get_int(conf, CONF_savelines));
            InvalidateRect(hwnd, NULL, TRUE);
        }
        break;
@@ -2669,13 +2738,15 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message,
         * 1) Keep the sizetip uptodate
         * 2) Make sure the window size is _stepped_ in units of the font size.
         */
-        if (cfg.resize_action == RESIZE_TERM ||
-            (cfg.resize_action == RESIZE_EITHER && !is_alt_pressed())) {
+       resize_action = conf_get_int(conf, CONF_resize_action);
+       if (resize_action == RESIZE_TERM ||
+            (resize_action == RESIZE_EITHER && !is_alt_pressed())) {
            int width, height, w, h, ew, eh;
            LPRECT r = (LPRECT) lParam;
 
-           if ( !need_backend_resize && cfg.resize_action == RESIZE_EITHER &&
-                   (cfg.height != term->rows || cfg.width != term->cols )) {
+           if (!need_backend_resize && resize_action == RESIZE_EITHER &&
+               (conf_get_int(conf, CONF_height) != term->rows ||
+                conf_get_int(conf, CONF_width) != term->cols)) {
                /* 
                 * Great! It seems that both the terminal size and the
                 * font size have been changed and the user is now dragging.
@@ -2684,11 +2755,9 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message,
                 * font size!
                 *
                 * This would be easier but it seems to be too confusing.
-
-               term_size(term, cfg.height, cfg.width, cfg.savelines);
-               reset_window(2);
                 */
-               cfg.height=term->rows; cfg.width=term->cols;
+               conf_set_int(conf, CONF_height, term->rows);
+               conf_set_int(conf, CONF_width, term->cols);
 
                InvalidateRect(hwnd, NULL, TRUE);
                need_backend_resize = TRUE;
@@ -2725,8 +2794,9 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message,
                return 0;
        } else {
            int width, height, w, h, rv = 0;
-           int ex_width = extra_width + (cfg.window_border - offset_width) * 2;
-           int ex_height = extra_height + (cfg.window_border - offset_height) * 2;
+           int window_border = conf_get_int(conf, CONF_window_border);
+           int ex_width = extra_width + (window_border - offset_width) * 2;
+           int ex_height = extra_height + (window_border - offset_height) * 2;
            LPRECT r = (LPRECT) lParam;
 
            width = r->right - r->left - ex_width;
@@ -2762,6 +2832,7 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message,
        sys_cursor_update();
        break;
       case WM_SIZE:
+       resize_action = conf_get_int(conf, CONF_resize_action);
 #ifdef RDB_DEBUG_PATCH
        debug((27, "WM_SIZE %s (%d,%d)",
                (wParam == SIZE_MINIMIZED) ? "SIZE_MINIMIZED":
@@ -2773,7 +2844,8 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message,
 #endif
        if (wParam == SIZE_MINIMIZED)
            SetWindowText(hwnd,
-                         cfg.win_name_always ? window_name : icon_name);
+                         conf_get_int(conf, CONF_win_name_always) ?
+                         window_name : icon_name);
        if (wParam == SIZE_RESTORED || wParam == SIZE_MAXIMIZED)
            SetWindowText(hwnd, window_name);
         if (wParam == SIZE_RESTORED) {
@@ -2806,12 +2878,13 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message,
 
         processed_resize = TRUE;
 
-       if (cfg.resize_action == RESIZE_DISABLED) {
+       if (resize_action == RESIZE_DISABLED) {
            /* A resize, well it better be a minimize. */
            reset_window(-1);
        } else {
 
            int width, height, w, h;
+            int window_border = conf_get_int(conf, CONF_window_border);
 
            width = LOWORD(lParam);
            height = HIWORD(lParam);
@@ -2820,36 +2893,36 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message,
                 was_zoomed = 1;
                 prev_rows = term->rows;
                 prev_cols = term->cols;
-                if (cfg.resize_action == RESIZE_TERM) {
+                if (resize_action == RESIZE_TERM) {
                     w = width / font_width;
                     if (w < 1) w = 1;
                     h = height / font_height;
                     if (h < 1) h = 1;
 
-                    term_size(term, h, w, cfg.savelines);
+                    term_size(term, h, w, conf_get_int(conf, CONF_savelines));
                 }
                 reset_window(0);
             } else if (wParam == SIZE_RESTORED && was_zoomed) {
                 was_zoomed = 0;
-                if (cfg.resize_action == RESIZE_TERM) {
-                    w = (width-cfg.window_border*2) / font_width;
+                if (resize_action == RESIZE_TERM) {
+                    w = (width-window_border*2) / font_width;
                     if (w < 1) w = 1;
-                    h = (height-cfg.window_border*2) / font_height;
+                    h = (height-window_border*2) / font_height;
                     if (h < 1) h = 1;
-                    term_size(term, h, w, cfg.savelines);
+                    term_size(term, h, w, conf_get_int(conf, CONF_savelines));
                     reset_window(2);
-                } else if (cfg.resize_action != RESIZE_FONT)
+                } else if (resize_action != RESIZE_FONT)
                     reset_window(2);
                 else
                     reset_window(0);
             } else if (wParam == SIZE_MINIMIZED) {
                 /* do nothing */
-           } else if (cfg.resize_action == RESIZE_TERM ||
-                       (cfg.resize_action == RESIZE_EITHER &&
+           } else if (resize_action == RESIZE_TERM ||
+                       (resize_action == RESIZE_EITHER &&
                         !is_alt_pressed())) {
-                w = (width-cfg.window_border*2) / font_width;
+                w = (width-window_border*2) / font_width;
                 if (w < 1) w = 1;
-                h = (height-cfg.window_border*2) / font_height;
+                h = (height-window_border*2) / font_height;
                 if (h < 1) h = 1;
 
                 if (resizing) {
@@ -2860,10 +2933,10 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message,
                      * opaque drag.)
                      */
                    need_backend_resize = TRUE;
-                   cfg.height = h;
-                   cfg.width = w;
+                   conf_set_int(conf, CONF_height, h);
+                   conf_set_int(conf, CONF_height, w);
                 } else {
-                    term_size(term, h, w, cfg.savelines);
+                    term_size(term, h, w, conf_get_int(conf, CONF_savelines));
                 }
             } else {
                 reset_window(0);
@@ -3060,7 +3133,7 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message,
        }
        return 0;
       case WM_SYSCOLORCHANGE:
-       if (cfg.system_colour) {
+       if (conf_get_int(conf, CONF_system_colour)) {
            /* Refresh palette from system colours. */
            /* XXX actually this zaps the entire palette. */
            systopalette();
@@ -3111,8 +3184,8 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message,
                } else
                    break;
 
-               if (send_raw_mouse &&
-                   !(cfg.mouse_override && shift_pressed)) {
+               if (send_raw_mouse && shift_pressed &&
+                   !(conf_get_int(conf, CONF_mouse_override))) {
                    /* Mouse wheel position is in screen coordinates for
                     * some reason */
                    POINT p;
@@ -3239,7 +3312,7 @@ void do_text_internal(Context ctx, int x, int y, wchar_t *text, int len,
     x += offset_width;
     y += offset_height;
 
-    if ((attr & TATTR_ACTCURS) && (cfg.cursor_type == 0 || term->big_cursor)) {
+    if ((attr & TATTR_ACTCURS) && (cursor_type == 0 || term->big_cursor)) {
        attr &= ~(ATTR_REVERSE|ATTR_BLINK|ATTR_COLOURS);
        if (bold_mode == BOLD_COLOURS)
            attr &= ~ATTR_BOLD;
@@ -3249,7 +3322,7 @@ void do_text_internal(Context ctx, int x, int y, wchar_t *text, int len,
     }
 
     nfont = 0;
-    if (cfg.vtmode == VT_POORMAN && lattr != LATTR_NORM) {
+    if (vtmode == VT_POORMAN && lattr != LATTR_NORM) {
        /* Assume a poorman font is borken in other ways too. */
        lattr = LATTR_WIDE;
     } else
@@ -3553,7 +3626,7 @@ void do_cursor(Context ctx, int x, int y, wchar_t *text, int len,
     int fnt_width;
     int char_width;
     HDC hdc = ctx;
-    int ctype = cfg.cursor_type;
+    int ctype = cursor_type;
 
     lattr &= LATTR_MODE;
 
@@ -3698,13 +3771,17 @@ static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam,
     int r, i, code;
     unsigned char *p = output;
     static int alt_sum = 0;
+    int funky_type = conf_get_int(conf, CONF_funky_type);
+    int no_applic_k = conf_get_int(conf, CONF_no_applic_k);
+    int ctrlaltkeys = conf_get_int(conf, CONF_ctrlaltkeys);
+    int nethack_keypad = conf_get_int(conf, CONF_nethack_keypad);
 
     HKL kbd_layout = GetKeyboardLayout(0);
 
     /* keys is for ToAsciiEx. There's some ick here, see below. */
     static WORD keys[3];
     static int compose_char = 0;
-    static WPARAM compose_key = 0;
+    static WPARAM compose_keycode = 0;
 
     r = GetKeyboardState(keystate);
     if (!r)
@@ -3791,9 +3868,9 @@ static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam,
 
 
        /* Nastyness with NUMLock - Shift-NUMLock is left alone though */
-       if ((cfg.funky_type == FUNKY_VT400 ||
-            (cfg.funky_type <= FUNKY_LINUX && term->app_keypad_keys &&
-             !cfg.no_applic_k))
+       if ((funky_type == FUNKY_VT400 ||
+            (funky_type <= FUNKY_LINUX && term->app_keypad_keys &&
+             !no_applic_k))
            && wParam == VK_NUMLOCK && !(keystate[VK_SHIFT] & 0x80)) {
 
            wParam = VK_EXECUTE;
@@ -3819,7 +3896,7 @@ static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam,
 
     /* Make sure Ctrl-ALT is not the same as AltGr for ToAscii unless told. */
     if (left_alt && (keystate[VK_CONTROL] & 0x80)) {
-       if (cfg.ctrlaltkeys)
+       if (ctrlaltkeys)
            keystate[VK_MENU] = 0;
        else {
            keystate[VK_RMENU] = 0x80;
@@ -3833,16 +3910,16 @@ static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam,
 
     /* Note if AltGr was pressed and if it was used as a compose key */
     if (!compose_state) {
-       compose_key = 0x100;
-       if (cfg.compose_key) {
+       compose_keycode = 0x100;
+       if (conf_get_int(conf, CONF_compose_key)) {
            if (wParam == VK_MENU && (HIWORD(lParam) & KF_EXTENDED))
-               compose_key = wParam;
+               compose_keycode = wParam;
        }
        if (wParam == VK_APPS)
-           compose_key = wParam;
+           compose_keycode = wParam;
     }
 
-    if (wParam == compose_key) {
+    if (wParam == compose_keycode) {
        if (compose_state == 0
            && (HIWORD(lParam) & (KF_UP | KF_REPEAT)) == 0) compose_state =
                1;
@@ -3857,9 +3934,9 @@ static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam,
        compose_state = 0;
 
     /* Sanitize the number pad if not using a PC NumPad */
-    if (left_alt || (term->app_keypad_keys && !cfg.no_applic_k
-                    && cfg.funky_type != FUNKY_XTERM)
-       || cfg.funky_type == FUNKY_VT400 || cfg.nethack_keypad || compose_state) {
+    if (left_alt || (term->app_keypad_keys && !no_applic_k
+                    && funky_type != FUNKY_XTERM)
+       || funky_type == FUNKY_VT400 || nethack_keypad || compose_state) {
        if ((HIWORD(lParam) & KF_EXTENDED) == 0) {
            int nParam = 0;
            switch (wParam) {
@@ -3936,15 +4013,17 @@ static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam,
            request_paste(NULL);
            return 0;
        }
-       if (left_alt && wParam == VK_F4 && cfg.alt_f4) {
+       if (left_alt && wParam == VK_F4 && conf_get_int(conf, CONF_alt_f4)) {
            return -1;
        }
-       if (left_alt && wParam == VK_SPACE && cfg.alt_space) {
+       if (left_alt && wParam == VK_SPACE && conf_get_int(conf,
+                                                          CONF_alt_space)) {
            SendMessage(hwnd, WM_SYSCOMMAND, SC_KEYMENU, 0);
            return -1;
        }
-       if (left_alt && wParam == VK_RETURN && cfg.fullscreenonaltenter &&
-           (cfg.resize_action != RESIZE_DISABLED)) {
+       if (left_alt && wParam == VK_RETURN &&
+           conf_get_int(conf, CONF_fullscreenonaltenter) &&
+           (conf_get_int(conf, CONF_resize_action) != RESIZE_DISABLED)) {
            if ((HIWORD(lParam) & (KF_UP | KF_REPEAT)) != KF_REPEAT)
                flip_full_screen();
            return -1;
@@ -3956,7 +4035,7 @@ static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam,
        }
 
        /* Nethack keypad */
-       if (cfg.nethack_keypad && !left_alt) {
+       if (nethack_keypad && !left_alt) {
            switch (wParam) {
              case VK_NUMPAD1:
                *p++ = "bB\002\002"[shift_state & 3];
@@ -3992,9 +4071,9 @@ static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam,
        if (!left_alt) {
            int xkey = 0;
 
-           if (cfg.funky_type == FUNKY_VT400 ||
-               (cfg.funky_type <= FUNKY_LINUX &&
-                term->app_keypad_keys && !cfg.no_applic_k)) switch (wParam) {
+           if (funky_type == FUNKY_VT400 ||
+               (funky_type <= FUNKY_LINUX &&
+                term->app_keypad_keys && !no_applic_k)) switch (wParam) {
                  case VK_EXECUTE:
                    xkey = 'P';
                    break;
@@ -4008,7 +4087,7 @@ static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam,
                    xkey = 'S';
                    break;
                }
-           if (term->app_keypad_keys && !cfg.no_applic_k)
+           if (term->app_keypad_keys && !no_applic_k)
                switch (wParam) {
                  case VK_NUMPAD0:
                    xkey = 'p';
@@ -4045,7 +4124,7 @@ static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam,
                    xkey = 'n';
                    break;
                  case VK_ADD:
-                   if (cfg.funky_type == FUNKY_XTERM) {
+                   if (funky_type == FUNKY_XTERM) {
                        if (shift_state)
                            xkey = 'l';
                        else
@@ -4057,15 +4136,15 @@ static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam,
                    break;
 
                  case VK_DIVIDE:
-                   if (cfg.funky_type == FUNKY_XTERM)
+                   if (funky_type == FUNKY_XTERM)
                        xkey = 'o';
                    break;
                  case VK_MULTIPLY:
-                   if (cfg.funky_type == FUNKY_XTERM)
+                   if (funky_type == FUNKY_XTERM)
                        xkey = 'j';
                    break;
                  case VK_SUBTRACT:
-                   if (cfg.funky_type == FUNKY_XTERM)
+                   if (funky_type == FUNKY_XTERM)
                        xkey = 'm';
                    break;
 
@@ -4087,13 +4166,13 @@ static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam,
        }
 
        if (wParam == VK_BACK && shift_state == 0) {    /* Backspace */
-           *p++ = (cfg.bksp_is_delete ? 0x7F : 0x08);
+           *p++ = (conf_get_int(conf, CONF_bksp_is_delete) ? 0x7F : 0x08);
            *p++ = 0;
            return -2;
        }
        if (wParam == VK_BACK && shift_state == 1) {    /* Shift Backspace */
            /* We do the opposite of what is configured */
-           *p++ = (cfg.bksp_is_delete ? 0x08 : 0x7F);
+           *p++ = (conf_get_int(conf, CONF_bksp_is_delete) ? 0x08 : 0x7F);
            *p++ = 0;
            return -2;
        }
@@ -4237,7 +4316,7 @@ static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam,
            break;
        }
        /* Reorder edit keys to physical order */
-       if (cfg.funky_type == FUNKY_VT400 && code <= 6)
+       if (funky_type == FUNKY_VT400 && code <= 6)
            code = "\0\2\1\4\5\3\6"[code];
 
        if (term->vt52_mode && code > 0 && code <= 6) {
@@ -4245,8 +4324,8 @@ static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam,
            return p - output;
        }
 
-       if (cfg.funky_type == FUNKY_SCO &&     /* SCO function keys */
-           code >= 11 && code <= 34) {
+       if (funky_type == FUNKY_SCO && code >= 11 && code <= 34) {
+           /* SCO function keys */
            char codes[] = "MNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz@[\\]^_`{";
            int index = 0;
            switch (wParam) {
@@ -4268,7 +4347,7 @@ static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam,
            p += sprintf((char *) p, "\x1B[%c", codes[index]);
            return p - output;
        }
-       if (cfg.funky_type == FUNKY_SCO &&     /* SCO small keypad */
+       if (funky_type == FUNKY_SCO &&     /* SCO small keypad */
            code >= 1 && code <= 6) {
            char codes[] = "HL.FIG";
            if (code == 3) {
@@ -4278,7 +4357,7 @@ static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam,
            }
            return p - output;
        }
-       if ((term->vt52_mode || cfg.funky_type == FUNKY_VT100P) && code >= 11 && code <= 24) {
+       if ((term->vt52_mode || funky_type == FUNKY_VT100P) && code >= 11 && code <= 24) {
            int offt = 0;
            if (code > 15)
                offt++;
@@ -4291,18 +4370,19 @@ static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam,
                    sprintf((char *) p, "\x1BO%c", code + 'P' - 11 - offt);
            return p - output;
        }
-       if (cfg.funky_type == FUNKY_LINUX && code >= 11 && code <= 15) {
+       if (funky_type == FUNKY_LINUX && code >= 11 && code <= 15) {
            p += sprintf((char *) p, "\x1B[[%c", code + 'A' - 11);
            return p - output;
        }
-       if (cfg.funky_type == FUNKY_XTERM && code >= 11 && code <= 14) {
+       if (funky_type == FUNKY_XTERM && code >= 11 && code <= 14) {
            if (term->vt52_mode)
                p += sprintf((char *) p, "\x1B%c", code + 'P' - 11);
            else
                p += sprintf((char *) p, "\x1BO%c", code + 'P' - 11);
            return p - output;
        }
-       if (cfg.rxvt_homeend && (code == 1 || code == 4)) {
+       if ((code == 1 || code == 4) &&
+           conf_get_int(conf, CONF_rxvt_homeend)) {
            p += sprintf((char *) p, code == 1 ? "\x1B[H" : "\x1BOw");
            return p - output;
        }
@@ -4362,7 +4442,8 @@ static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam,
        BOOL capsOn=0;
 
        /* helg: clear CAPS LOCK state if caps lock switches to cyrillic */
-       if(cfg.xlat_capslockcyr && keystate[VK_CAPITAL] != 0) {
+       if(keystate[VK_CAPITAL] != 0 &&
+          conf_get_int(conf, CONF_xlat_capslockcyr)) {
            capsOn= !left_alt;
            keystate[VK_CAPITAL] = 0;
        }
@@ -4512,7 +4593,7 @@ static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam,
      * we return -1, which means Windows will give the keystroke
      * its default handling (i.e. bring up the System menu).
      */
-    if (wParam == VK_MENU && !cfg.alt_only)
+    if (wParam == VK_MENU && !conf_get_int(conf, CONF_alt_only))
        return 0;
 
     return -1;
@@ -4523,7 +4604,7 @@ void set_title(void *frontend, char *title)
     sfree(window_name);
     window_name = snewn(1 + strlen(title), char);
     strcpy(window_name, title);
-    if (cfg.win_name_always || !IsIconic(hwnd))
+    if (conf_get_int(conf, CONF_win_name_always) || !IsIconic(hwnd))
        SetWindowText(hwnd, title);
 }
 
@@ -4532,7 +4613,7 @@ void set_icon(void *frontend, char *title)
     sfree(icon_name);
     icon_name = snewn(1 + strlen(title), char);
     strcpy(icon_name, title);
-    if (!cfg.win_name_always && IsIconic(hwnd))
+    if (!conf_get_int(conf, CONF_win_name_always) && IsIconic(hwnd))
        SetWindowText(hwnd, title);
 }
 
@@ -4540,7 +4621,8 @@ void set_sbar(void *frontend, int total, int start, int page)
 {
     SCROLLINFO si;
 
-    if (is_full_screen() ? !cfg.scrollbar_in_fullscreen : !cfg.scrollbar)
+    if (!conf_get_int(conf, is_full_screen() ?
+                     CONF_scrollbar_in_fullscreen : CONF_scrollbar))
        return;
 
     si.cbSize = sizeof(si);
@@ -4696,7 +4778,7 @@ void write_clip(void *frontend, wchar_t * data, int *attr, int len, int must_des
     memcpy(lock, data, len * sizeof(wchar_t));
     WideCharToMultiByte(CP_ACP, 0, data, len, lock2, len2, NULL, NULL);
 
-    if (cfg.rtf_paste) {
+    if (conf_get_int(conf, CONF_rtf_paste)) {
        wchar_t unitab[256];
        char *rtf = NULL;
        unsigned char *tdata = (unsigned char *)lock2;
@@ -4711,13 +4793,14 @@ void write_clip(void *frontend, wchar_t * data, int *attr, int len, int must_des
        int attrUnder, lastAttrUnder = 0;
        int palette[NALLCOLOURS];
        int numcolours;
+       FontSpec *font = conf_get_fontspec(conf, CONF_font);
 
        get_unitab(CP_ACP, unitab, 0);
 
-       rtfsize = 100 + strlen(cfg.font.name);
+       rtfsize = 100 + strlen(font->name);
        rtf = snewn(rtfsize, char);
        rtflen = sprintf(rtf, "{\\rtf1\\ansi\\deff0{\\fonttbl\\f0\\fmodern %s;}\\f0\\fs%d",
-                        cfg.font.name, cfg.font.height*2);
+                        font->name, font->height*2);
 
        /*
         * Add colour palette
@@ -5176,7 +5259,8 @@ static void flash_window_timer(void *ctx, long now)
  */
 static void flash_window(int mode)
 {
-    if ((mode == 0) || (cfg.beep_ind == B_IND_DISABLED)) {
+    int beep_ind = conf_get_int(conf, CONF_beep_ind);
+    if ((mode == 0) || (beep_ind == B_IND_DISABLED)) {
        /* stop */
        if (flashing) {
            flashing = 0;
@@ -5198,7 +5282,7 @@ static void flash_window(int mode)
                 * "flashing" mode, although I haven't seen this
                 * documented. */
                flash_window_ex(FLASHW_ALL | FLASHW_TIMER,
-                               (cfg.beep_ind == B_IND_FLASH ? 0 : 2),
+                               (beep_ind == B_IND_FLASH ? 0 : 2),
                                0 /* system cursor blink rate */);
                /* No need to schedule timer */
            } else {
@@ -5207,7 +5291,7 @@ static void flash_window(int mode)
            }
        }
 
-    } else if ((mode == 1) && (cfg.beep_ind == B_IND_FLASH)) {
+    } else if ((mode == 1) && (beep_ind == B_IND_FLASH)) {
        /* maintain */
        if (flashing && !p_FlashWindowEx) {
            FlashWindow(hwnd, TRUE);    /* toggle */
@@ -5241,16 +5325,17 @@ void do_beep(void *frontend, int mode)
         */
        lastbeep = GetTickCount();
     } else if (mode == BELL_WAVEFILE) {
-       if (!PlaySound(cfg.bell_wavefile.path, NULL,
+       Filename *bell_wavefile = conf_get_filename(conf, CONF_bell_wavefile);
+       if (!PlaySound(bell_wavefile->path, NULL,
                       SND_ASYNC | SND_FILENAME)) {
-           char buf[sizeof(cfg.bell_wavefile.path) + 80];
+           char buf[sizeof(bell_wavefile->path) + 80];
            char otherbuf[100];
            sprintf(buf, "Unable to play sound file\n%s\n"
-                   "Using default sound instead", cfg.bell_wavefile.path);
+                   "Using default sound instead", bell_wavefile->path);
            sprintf(otherbuf, "%.70s Sound Error", appname);
            MessageBox(hwnd, buf, otherbuf,
                       MB_OK | MB_ICONEXCLAMATION);
-           cfg.beep = BELL_DEFAULT;
+           conf_set_int(conf, CONF_beep, BELL_DEFAULT);
        }
     } else if (mode == BELL_PCSPEAKER) {
        static long lastbeep = 0;
@@ -5296,8 +5381,9 @@ void set_iconic(void *frontend, int iconic)
  */
 void move_window(void *frontend, int x, int y)
 {
-    if (cfg.resize_action == RESIZE_DISABLED || 
-        cfg.resize_action == RESIZE_FONT ||
+    int resize_action = conf_get_int(conf, CONF_resize_action);
+    if (resize_action == RESIZE_DISABLED || 
+       resize_action == RESIZE_FONT ||
        IsZoomed(hwnd))
        return;
 
@@ -5310,7 +5396,7 @@ void move_window(void *frontend, int x, int y)
  */
 void set_zorder(void *frontend, int top)
 {
-    if (cfg.alwaysontop)
+    if (conf_get_int(conf, CONF_alwaysontop))
        return;                        /* ignore */
     SetWindowPos(hwnd, top ? HWND_TOP : HWND_BOTTOM, 0, 0, 0, 0,
                 SWP_NOMOVE | SWP_NOSIZE);
@@ -5432,7 +5518,7 @@ static void make_full_screen()
     /* Remove the window furniture. */
     style = GetWindowLongPtr(hwnd, GWL_STYLE);
     style &= ~(WS_CAPTION | WS_BORDER | WS_THICKFRAME);
-    if (cfg.scrollbar_in_fullscreen)
+    if (conf_get_int(conf, CONF_scrollbar_in_fullscreen))
        style |= WS_VSCROLL;
     else
        style &= ~WS_VSCROLL;
@@ -5467,11 +5553,11 @@ static void clear_full_screen()
     /* Reinstate the window furniture. */
     style = oldstyle = GetWindowLongPtr(hwnd, GWL_STYLE);
     style |= WS_CAPTION | WS_BORDER;
-    if (cfg.resize_action == RESIZE_DISABLED)
+    if (conf_get_int(conf, CONF_resize_action) == RESIZE_DISABLED)
         style &= ~WS_THICKFRAME;
     else
         style |= WS_THICKFRAME;
-    if (cfg.scrollbar)
+    if (conf_get_int(conf, CONF_scrollbar))
        style |= WS_VSCROLL;
     else
        style &= ~WS_VSCROLL;
index a161ad6..237b389 100644 (file)
@@ -65,11 +65,12 @@ const char *gsslogmsg = NULL;
 
 static void ssh_sspi_bind_fns(struct ssh_gss_library *lib);
 
-struct ssh_gss_liblist *ssh_gss_setup(const Config *cfg)
+struct ssh_gss_liblist *ssh_gss_setup(Conf *conf)
 {
     HMODULE module;
     HKEY regkey;
     struct ssh_gss_liblist *list = snew(struct ssh_gss_liblist);
+    char *path;
 
     list->libraries = snewn(3, struct ssh_gss_library);
     list->nlibraries = 0;
@@ -148,8 +149,9 @@ struct ssh_gss_liblist *ssh_gss_setup(const Config *cfg)
      * Custom GSSAPI DLL.
      */
     module = NULL;
-    if (cfg->ssh_gss_custom.path[0]) {
-       module = LoadLibrary(cfg->ssh_gss_custom.path);
+    path = conf_get_filename(conf, CONF_ssh_gss_custom)->path;
+    if (*path) {
+       module = LoadLibrary(path);
     }
     if (module) {
        struct ssh_gss_library *lib =
@@ -157,7 +159,7 @@ struct ssh_gss_liblist *ssh_gss_setup(const Config *cfg)
 
        lib->id = 2;
        lib->gsslogmsg = dupprintf("Using GSSAPI from user-specified"
-                                  " library '%s'", cfg->ssh_gss_custom.path);
+                                  " library '%s'", path);
        lib->handle = (void *)module;
 
 #define BIND_GSS_FN(name) \
index 7eb3aec..3fe03af 100644 (file)
@@ -83,7 +83,7 @@ WSAEVENT netevent;
 
 static Backend *back;
 static void *backhandle;
-static Config cfg;
+static Conf *conf;
 
 int term_ldisc(Terminal *term, int mode)
 {
@@ -298,10 +298,11 @@ int main(int argc, char **argv)
     /*
      * Process the command line.
      */
-    do_defaults(NULL, &cfg);
+    conf = conf_new();
+    do_defaults(NULL, conf);
     loaded_session = FALSE;
-    default_protocol = cfg.protocol;
-    default_port = cfg.port;
+    default_protocol = conf_get_int(conf, CONF_protocol);
+    default_port = conf_get_int(conf, CONF_port);
     errors = 0;
     {
        /*
@@ -311,8 +312,10 @@ int main(int argc, char **argv)
        if (p) {
            const Backend *b = backend_from_name(p);
            if (b) {
-               default_protocol = cfg.protocol = b->protocol;
-               default_port = cfg.port = b->default_port;
+               default_protocol = b->protocol;
+               default_port = b->default_port;
+               conf_set_int(conf, CONF_protocol, default_protocol);
+               conf_set_int(conf, CONF_port, default_port);
            }
        }
     }
@@ -320,7 +323,7 @@ int main(int argc, char **argv)
        char *p = *++argv;
        if (*p == '-') {
            int ret = cmdline_process_param(p, (argc > 1 ? argv[1] : NULL),
-                                           1, &cfg);
+                                           1, conf);
            if (ret == -2) {
                fprintf(stderr,
                        "plink: option \"%s\" requires an argument\n", p);
@@ -332,7 +335,7 @@ int main(int argc, char **argv)
            } else if (!strcmp(p, "-batch")) {
                console_batch_mode = 1;
            } else if (!strcmp(p, "-s")) {
-               /* Save status to write to cfg later. */
+               /* Save status to write to conf later. */
                use_subsystem = 1;
            } else if (!strcmp(p, "-V")) {
                 version();
@@ -344,7 +347,7 @@ int main(int argc, char **argv)
                errors = 1;
            }
        } else if (*p) {
-           if (!cfg_launchable(&cfg) || !(got_host || loaded_session)) {
+           if (!conf_launchable(conf) || !(got_host || loaded_session)) {
                char *q = p;
                /*
                 * If the hostname starts with "telnet:", set the
@@ -357,7 +360,7 @@ int main(int argc, char **argv)
                    q += 7;
                    if (q[0] == '/' && q[1] == '/')
                        q += 2;
-                   cfg.protocol = PROT_TELNET;
+                   conf_set_int(conf, CONF_protocol, PROT_TELNET);
                    p = q;
                    while (*p && *p != ':' && *p != '/')
                        p++;
@@ -365,11 +368,10 @@ int main(int argc, char **argv)
                    if (*p)
                        *p++ = '\0';
                    if (c == ':')
-                       cfg.port = atoi(p);
+                       conf_set_int(conf, CONF_port, atoi(p));
                    else
-                       cfg.port = -1;
-                   strncpy(cfg.host, q, sizeof(cfg.host) - 1);
-                   cfg.host[sizeof(cfg.host) - 1] = '\0';
+                       conf_set_int(conf, CONF_port, -1);
+                   conf_set_str(conf, CONF_host, q);
                    got_host = TRUE;
                } else {
                    char *r, *user, *host;
@@ -384,7 +386,9 @@ int main(int argc, char **argv)
                        *r = '\0';
                        b = backend_from_name(p);
                        if (b) {
-                           default_protocol = cfg.protocol = b->protocol;
+                           default_protocol = b->protocol;
+                           conf_set_int(conf, CONF_protocol,
+                                        default_protocol);
                            portnumber = b->default_port;
                        }
                        p = r + 1;
@@ -411,26 +415,24 @@ int main(int argc, char **argv)
                     * same name as the hostname.
                     */
                    {
-                       Config cfg2;
-                       do_defaults(host, &cfg2);
-                       if (loaded_session || !cfg_launchable(&cfg2)) {
+                       Conf *conf2 = conf_new();
+                       do_defaults(host, conf2);
+                       if (loaded_session || !conf_launchable(conf2)) {
                            /* No settings for this host; use defaults */
                            /* (or session was already loaded with -load) */
-                           strncpy(cfg.host, host, sizeof(cfg.host) - 1);
-                           cfg.host[sizeof(cfg.host) - 1] = '\0';
-                           cfg.port = default_port;
+                           conf_set_str(conf, CONF_host, host);
+                           conf_set_int(conf, CONF_port, default_port);
                            got_host = TRUE;
                        } else {
-                           cfg = cfg2;
+                           conf_copy_into(conf, conf2);
                            loaded_session = TRUE;
                        }
+                       conf_free(conf2);
                    }
 
                    if (user) {
                        /* Patch in specified username. */
-                       strncpy(cfg.username, user,
-                               sizeof(cfg.username) - 1);
-                       cfg.username[sizeof(cfg.username) - 1] = '\0';
+                       conf_set_str(conf, CONF_username, user);
                    }
 
                }
@@ -457,9 +459,9 @@ int main(int argc, char **argv)
                }
                if (cmdlen) command[--cmdlen]='\0';
                                       /* change trailing blank to NUL */
-               cfg.remote_cmd_ptr = command;
-               cfg.remote_cmd_ptr2 = NULL;
-               cfg.nopty = TRUE;      /* command => no terminal */
+               conf_set_str(conf, CONF_remote_cmd, command);
+               conf_set_str(conf, CONF_remote_cmd2, "");
+               conf_set_int(conf, CONF_nopty, TRUE);  /* command => no tty */
 
                break;                 /* done with cmdline */
            }
@@ -469,70 +471,78 @@ int main(int argc, char **argv)
     if (errors)
        return 1;
 
-    if (!cfg_launchable(&cfg) || !(got_host || loaded_session)) {
+    if (!conf_launchable(conf) || !(got_host || loaded_session)) {
        usage();
     }
 
     /*
-     * Trim leading whitespace off the hostname if it's there.
+     * Muck about with the hostname in various ways.
      */
     {
-       int space = strspn(cfg.host, " \t");
-       memmove(cfg.host, cfg.host+space, 1+strlen(cfg.host)-space);
-    }
+       char *hostbuf = dupstr(conf_get_str(conf, CONF_host));
+       char *host = hostbuf;
+       char *p, *q;
+
+       /*
+        * Trim leading whitespace.
+        */
+       host += strspn(host, " \t");
 
-    /* See if host is of the form user@host */
-    if (cfg_launchable(&cfg)) {
-       char *atsign = strrchr(cfg.host, '@');
-       /* Make sure we're not overflowing the user field */
-       if (atsign) {
-           if (atsign - cfg.host < sizeof cfg.username) {
-               strncpy(cfg.username, cfg.host, atsign - cfg.host);
-               cfg.username[atsign - cfg.host] = '\0';
+       /*
+        * See if host is of the form user@host, and separate out
+        * the username if so.
+        */
+       if (host[0] != '\0') {
+           char *atsign = strrchr(host, '@');
+           if (atsign) {
+               *atsign = '\0';
+               conf_set_str(conf, CONF_username, host);
+               host = atsign + 1;
            }
-           memmove(cfg.host, atsign + 1, 1 + strlen(atsign + 1));
        }
+
+       /*
+        * Trim off a colon suffix if it's there.
+        */
+       host[strcspn(host, ":")] = '\0';
+
+       /*
+        * Remove any remaining whitespace.
+        */
+       p = hostbuf;
+       q = host;
+       while (*q) {
+           if (*q != ' ' && *q != '\t')
+               *p++ = *q;
+           q++;
+       }
+       *p = '\0';
+
+       conf_set_str(conf, CONF_host, hostbuf);
+       sfree(hostbuf);
     }
 
     /*
      * Perform command-line overrides on session configuration.
      */
-    cmdline_run_saved(&cfg);
+    cmdline_run_saved(conf);
 
     /*
      * Apply subsystem status.
      */
     if (use_subsystem)
-       cfg.ssh_subsys = TRUE;
-
-    /*
-     * Trim a colon suffix off the hostname if it's there.
-     */
-    cfg.host[strcspn(cfg.host, ":")] = '\0';
-
-    /*
-     * Remove any remaining whitespace from the hostname.
-     */
-    {
-       int p1 = 0, p2 = 0;
-       while (cfg.host[p2] != '\0') {
-           if (cfg.host[p2] != ' ' && cfg.host[p2] != '\t') {
-               cfg.host[p1] = cfg.host[p2];
-               p1++;
-           }
-           p2++;
-       }
-       cfg.host[p1] = '\0';
-    }
+        conf_set_int(conf, CONF_ssh_subsys, TRUE);
 
-    if (!cfg.remote_cmd_ptr && !*cfg.remote_cmd && !*cfg.ssh_nc_host)
+    if (!*conf_get_str(conf, CONF_remote_cmd) &&
+       !*conf_get_str(conf, CONF_remote_cmd2) &&
+       !*conf_get_str(conf, CONF_ssh_nc_host))
        flags |= FLAG_INTERACTIVE;
 
     /*
      * Select protocol. This is farmed out into a table in a
      * separate file to enable an ssh-free variant.
      */
-    back = backend_from_proto(cfg.protocol);
+    back = backend_from_proto(conf_get_int(conf, CONF_protocol));
     if (back == NULL) {
        fprintf(stderr,
                "Internal fault: Unsupported protocol found\n");
@@ -543,7 +553,7 @@ int main(int argc, char **argv)
      * Select port.
      */
     if (portnumber != -1)
-       cfg.port = portnumber;
+       conf_set_int(conf, CONF_port, portnumber);
 
     sk_init();
     if (p_WSAEventSelect == NULL) {
@@ -551,7 +561,7 @@ int main(int argc, char **argv)
        return 1;
     }
 
-    logctx = log_init(NULL, &cfg);
+    logctx = log_init(NULL, conf);
     console_provide_logctx(logctx);
 
     /*
@@ -562,11 +572,14 @@ int main(int argc, char **argv)
        const char *error;
        char *realhost;
        /* nodelay is only useful if stdin is a character device (console) */
-       int nodelay = cfg.tcp_nodelay &&
+       int nodelay = conf_get_int(conf, CONF_tcp_nodelay) &&
            (GetFileType(GetStdHandle(STD_INPUT_HANDLE)) == FILE_TYPE_CHAR);
 
-       error = back->init(NULL, &backhandle, &cfg, cfg.host, cfg.port,
-                          &realhost, nodelay, cfg.tcp_keepalives);
+       error = back->init(NULL, &backhandle, conf,
+                          conf_get_str(conf, CONF_host),
+                          conf_get_int(conf, CONF_port),
+                          &realhost, nodelay,
+                          conf_get_int(conf, CONF_tcp_keepalives));
        if (error) {
            fprintf(stderr, "Unable to open connection:\n%s", error);
            return 1;
index 877dc5e..b1d3f6e 100644 (file)
@@ -123,7 +123,7 @@ static const char *sk_localproxy_socket_error(Socket s)
 Socket platform_new_connection(SockAddr addr, char *hostname,
                               int port, int privport,
                               int oobinline, int nodelay, int keepalive,
-                              Plug plug, const Config *cfg)
+                              Plug plug, Conf *conf)
 {
     char *cmd;
 
@@ -145,10 +145,10 @@ Socket platform_new_connection(SockAddr addr, char *hostname,
     STARTUPINFO si;
     PROCESS_INFORMATION pi;
 
-    if (cfg->proxy_type != PROXY_CMD)
+    if (conf_get_int(conf, CONF_proxy_type) != PROXY_CMD)
        return NULL;
 
-    cmd = format_telnet_command(addr, port, cfg);
+    cmd = format_telnet_command(addr, port, conf);
 
     {
        char *msg = dupprintf("Starting local proxy command: %s", cmd);
index ab88406..e0fc20e 100644 (file)
@@ -87,7 +87,7 @@ static void serial_sentdata(struct handle *h, int new_backlog)
     }
 }
 
-static const char *serial_configure(Serial serial, HANDLE serport, Config *cfg)
+static const char *serial_configure(Serial serial, HANDLE serport, Conf *conf)
 {
     DCB dcb;
     COMMTIMEOUTS timeouts;
@@ -121,17 +121,17 @@ static const char *serial_configure(Serial serial, HANDLE serport, Config *cfg)
        /*
         * Configurable parameters.
         */
-       dcb.BaudRate = cfg->serspeed;
-       msg = dupprintf("Configuring baud rate %d", cfg->serspeed);
+       dcb.BaudRate = conf_get_int(conf, CONF_serspeed);
+       msg = dupprintf("Configuring baud rate %d", dcb.BaudRate);
        logevent(serial->frontend, msg);
        sfree(msg);
 
-       dcb.ByteSize = cfg->serdatabits;
-       msg = dupprintf("Configuring %d data bits", cfg->serdatabits);
+       dcb.ByteSize = conf_get_int(conf, CONF_serdatabits);
+       msg = dupprintf("Configuring %d data bits", dcb.ByteSize);
        logevent(serial->frontend, msg);
        sfree(msg);
 
-       switch (cfg->serstopbits) {
+       switch (conf_get_int(conf, CONF_serstopbits)) {
          case 2: dcb.StopBits = ONESTOPBIT; str = "1"; break;
          case 3: dcb.StopBits = ONE5STOPBITS; str = "1.5"; break;
          case 4: dcb.StopBits = TWOSTOPBITS; str = "2"; break;
@@ -141,7 +141,7 @@ static const char *serial_configure(Serial serial, HANDLE serport, Config *cfg)
        logevent(serial->frontend, msg);
        sfree(msg);
 
-       switch (cfg->serparity) {
+       switch (conf_get_int(conf, CONF_serparity)) {
          case SER_PAR_NONE: dcb.Parity = NOPARITY; str = "no"; break;
          case SER_PAR_ODD: dcb.Parity = ODDPARITY; str = "odd"; break;
          case SER_PAR_EVEN: dcb.Parity = EVENPARITY; str = "even"; break;
@@ -152,7 +152,7 @@ static const char *serial_configure(Serial serial, HANDLE serport, Config *cfg)
        logevent(serial->frontend, msg);
        sfree(msg);
 
-       switch (cfg->serflow) {
+       switch (conf_get_int(conf, CONF_serflow)) {
          case SER_FLOW_NONE:
            str = "no";
            break;
@@ -199,13 +199,13 @@ static const char *serial_configure(Serial serial, HANDLE serport, Config *cfg)
  * freed by the caller.
  */
 static const char *serial_init(void *frontend_handle, void **backend_handle,
-                              Config *cfg,
-                              char *host, int port, char **realhost, int nodelay,
-                              int keepalive)
+                              Conf *conf, char *host, int port,
+                              char **realhost, int nodelay, int keepalive)
 {
     Serial serial;
     HANDLE serport;
     const char *err;
+    char *serline;
 
     serial = snew(struct serial_backend_data);
     serial->port = INVALID_HANDLE_VALUE;
@@ -216,8 +216,9 @@ static const char *serial_init(void *frontend_handle, void **backend_handle,
 
     serial->frontend = frontend_handle;
 
+    serline = conf_get_str(conf, CONF_serline);
     {
-       char *msg = dupprintf("Opening serial device %s", cfg->serline);
+       char *msg = dupprintf("Opening serial device %s", serline);
        logevent(serial->frontend, msg);
     }
 
@@ -246,9 +247,7 @@ static const char *serial_init(void *frontend_handle, void **backend_handle,
         * existing configurations using \\.\ continue working.)
         */
        char *serfilename =
-           dupprintf("%s%s",
-                     strchr(cfg->serline, '\\') ? "" : "\\\\.\\",
-                     cfg->serline);
+           dupprintf("%s%s", strchr(serline, '\\') ? "" : "\\\\.\\", serline);
        serport = CreateFile(serfilename, GENERIC_READ | GENERIC_WRITE, 0, NULL,
                             OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
        sfree(serfilename);
@@ -257,7 +256,7 @@ static const char *serial_init(void *frontend_handle, void **backend_handle,
     if (serport == INVALID_HANDLE_VALUE)
        return "Unable to open serial port";
 
-    err = serial_configure(serial, serport, cfg);
+    err = serial_configure(serial, serport, conf);
     if (err)
        return err;
 
@@ -269,7 +268,7 @@ static const char *serial_init(void *frontend_handle, void **backend_handle,
                                  HANDLE_FLAG_IGNOREEOF |
                                  HANDLE_FLAG_UNITBUFFER);
 
-    *realhost = dupstr(cfg->serline);
+    *realhost = dupstr(serline);
 
     /*
      * Specials are always available.
@@ -288,12 +287,12 @@ static void serial_free(void *handle)
     sfree(serial);
 }
 
-static void serial_reconfig(void *handle, Config *cfg)
+static void serial_reconfig(void *handle, Conf *conf)
 {
     Serial serial = (Serial) handle;
     const char *err;
 
-    err = serial_configure(serial, serial->port, cfg);
+    err = serial_configure(serial, serial->port, conf);
 
     /*
      * FIXME: what should we do if err returns something?
index c5555d5..de84997 100644 (file)
@@ -20,7 +20,7 @@ int get_userpass_input(prompts_t *p, unsigned char *in, int inlen)
     return ret;
 }
 
-void platform_get_x11_auth(struct X11Display *display, const Config *cfg)
+void platform_get_x11_auth(struct X11Display *display, Conf *conf)
 {
     /* Do nothing, therefore no auth. */
 }
index aae5a4c..12d6cdd 100644 (file)
@@ -150,17 +150,26 @@ void *open_settings_r(const char *sessionname)
     return (void *) sesskey;
 }
 
-char *read_setting_s(void *handle, const char *key, char *buffer, int buflen)
+char *read_setting_s(void *handle, const char *key)
 {
     DWORD type, size;
-    size = buflen;
+    char *ret;
 
-    if (!handle ||
-       RegQueryValueEx((HKEY) handle, key, 0,
-                       &type, buffer, &size) != ERROR_SUCCESS ||
+    if (!handle)
+       return NULL;
+
+    /* Find out the type and size of the data. */
+    if (RegQueryValueEx((HKEY) handle, key, 0,
+                       &type, NULL, &size) != ERROR_SUCCESS ||
+       type != REG_SZ)
+       return NULL;
+
+    ret = snewn(size+1, char);
+    if (RegQueryValueEx((HKEY) handle, key, 0,
+                       &type, ret, &size) != ERROR_SUCCESS ||
        type != REG_SZ) return NULL;
-    else
-       return buffer;
+
+    return ret;
 }
 
 int read_setting_i(void *handle, const char *key, int defvalue)
@@ -181,17 +190,25 @@ int read_setting_fontspec(void *handle, const char *name, FontSpec *result)
 {
     char *settingname;
     FontSpec ret;
+    char *fontname;
 
-    if (!read_setting_s(handle, name, ret.name, sizeof(ret.name)))
+    fontname = read_setting_s(handle, name);
+    if (!fontname)
        return 0;
+    strncpy(ret.name, fontname, sizeof(ret.name)-1);
+    ret.name[sizeof(ret.name)-1] = '\0';
+    sfree(fontname);
+
     settingname = dupcat(name, "IsBold", NULL);
     ret.isbold = read_setting_i(handle, settingname, -1);
     sfree(settingname);
     if (ret.isbold == -1) return 0;
+
     settingname = dupcat(name, "CharSet", NULL);
     ret.charset = read_setting_i(handle, settingname, -1);
     sfree(settingname);
     if (ret.charset == -1) return 0;
+
     settingname = dupcat(name, "Height", NULL);
     ret.height = read_setting_i(handle, settingname, INT_MIN);
     sfree(settingname);
@@ -218,7 +235,14 @@ void write_setting_fontspec(void *handle, const char *name, FontSpec font)
 
 int read_setting_filename(void *handle, const char *name, Filename *result)
 {
-    return !!read_setting_s(handle, name, result->path, sizeof(result->path));
+    char *tmp = read_setting_s(handle, name);
+    if (tmp) {
+       strncpy(result->path, tmp, sizeof(result->path)-1);
+       result->path[sizeof(result->path)-1] = '\0';
+       sfree(tmp);
+       return TRUE;
+    } else
+       return FALSE;
 }
 
 void write_setting_filename(void *handle, const char *name, Filename result)
index feaf9e9..2b70ddc 100644 (file)
@@ -115,7 +115,7 @@ struct FontSpec {
 
 #ifndef DONE_TYPEDEFS
 #define DONE_TYPEDEFS
-typedef struct config_tag Config;
+typedef struct conf_tag Conf;
 typedef struct backend_tag Backend;
 typedef struct terminal_tag Terminal;
 #endif
@@ -473,7 +473,7 @@ void EnableSizeTip(int bEnable);
  * Exports from unicode.c.
  */
 struct unicode_data;
-void init_ucs(Config *, struct unicode_data *);
+void init_ucs(Conf *, struct unicode_data *);
 
 /*
  * Exports from winhandl.c.
index 757a275..9abd83c 100644 (file)
@@ -436,24 +436,27 @@ static const struct cp_list_item cp_list[] = {
 
 static void link_font(WCHAR * line_tbl, WCHAR * font_tbl, WCHAR attr);
 
-void init_ucs(Config *cfg, struct unicode_data *ucsdata)
+void init_ucs(Conf *conf, struct unicode_data *ucsdata)
 {
     int i, j;
     int used_dtf = 0;
     char tbuf[256];
+    int vtmode;
 
     for (i = 0; i < 256; i++)
        tbuf[i] = i;
 
     /* Decide on the Line and Font codepages */
-    ucsdata->line_codepage = decode_codepage(cfg->line_codepage);
+    ucsdata->line_codepage = decode_codepage(conf_get_str(conf,
+                                                         CONF_line_codepage));
 
     if (ucsdata->font_codepage <= 0) { 
        ucsdata->font_codepage=0; 
        ucsdata->dbcs_screenfont=0; 
     }
 
-    if (cfg->vtmode == VT_OEMONLY) {
+    vtmode = conf_get_int(conf, CONF_vtmode);
+    if (vtmode == VT_OEMONLY) {
        ucsdata->font_codepage = 437;
        ucsdata->dbcs_screenfont = 0;
        if (ucsdata->line_codepage <= 0)
@@ -473,7 +476,7 @@ void init_ucs(Config *cfg, struct unicode_data *ucsdata)
        if (ucsdata->font_codepage == 437)
            ucsdata->unitab_font[0] = ucsdata->unitab_font[255] = 0xFFFF;
     }
-    if (cfg->vtmode == VT_XWINDOWS)
+    if (vtmode == VT_XWINDOWS)
        memcpy(ucsdata->unitab_font + 1, unitab_xterm_std,
               sizeof(unitab_xterm_std));
 
@@ -481,7 +484,7 @@ void init_ucs(Config *cfg, struct unicode_data *ucsdata)
     get_unitab(CP_OEMCP, ucsdata->unitab_oemcp, 1);
 
     /* Collect CP437 ucs table for SCO acs */
-    if (cfg->vtmode == VT_OEMANSI || cfg->vtmode == VT_XWINDOWS)
+    if (vtmode == VT_OEMANSI || vtmode == VT_XWINDOWS)
        memcpy(ucsdata->unitab_scoacs, ucsdata->unitab_oemcp,
               sizeof(ucsdata->unitab_scoacs));
     else
@@ -490,7 +493,7 @@ void init_ucs(Config *cfg, struct unicode_data *ucsdata)
     /* Collect line set ucs table */
     if (ucsdata->line_codepage == ucsdata->font_codepage &&
        (ucsdata->dbcs_screenfont ||
-        cfg->vtmode == VT_POORMAN || ucsdata->font_codepage==0)) {
+        vtmode == VT_POORMAN || ucsdata->font_codepage==0)) {
 
        /* For DBCS and POOR fonts force direct to font */
        used_dtf = 1;
@@ -560,14 +563,14 @@ void init_ucs(Config *cfg, struct unicode_data *ucsdata)
            ucsdata->unitab_ctrl[i] = 0xFF;
 
     /* Generate line->screen direct conversion links. */
-    if (cfg->vtmode == VT_OEMANSI || cfg->vtmode == VT_XWINDOWS)
+    if (vtmode == VT_OEMANSI || vtmode == VT_XWINDOWS)
        link_font(ucsdata->unitab_scoacs, ucsdata->unitab_oemcp, CSET_OEMCP);
 
     link_font(ucsdata->unitab_line, ucsdata->unitab_font, CSET_ACP);
     link_font(ucsdata->unitab_scoacs, ucsdata->unitab_font, CSET_ACP);
     link_font(ucsdata->unitab_xterm, ucsdata->unitab_font, CSET_ACP);
 
-    if (cfg->vtmode == VT_OEMANSI || cfg->vtmode == VT_XWINDOWS) {
+    if (vtmode == VT_OEMANSI || vtmode == VT_XWINDOWS) {
        link_font(ucsdata->unitab_line, ucsdata->unitab_oemcp, CSET_OEMCP);
        link_font(ucsdata->unitab_xterm, ucsdata->unitab_oemcp, CSET_OEMCP);
     }
@@ -581,7 +584,7 @@ void init_ucs(Config *cfg, struct unicode_data *ucsdata)
     }
 
     /* Last chance, if !unicode then try poorman links. */
-    if (cfg->vtmode != VT_UNICODE) {
+    if (vtmode != VT_UNICODE) {
        static const char poorman_scoacs[] = 
            "CueaaaaceeeiiiAAE**ooouuyOUc$YPsaiounNao?++**!<>###||||++||++++++--|-+||++--|-+----++++++++##||#aBTPEsyt******EN=+><++-=... n2* ";
        static const char poorman_latin1[] =
index c1204ae..b8c7fa7 100644 (file)
@@ -9,10 +9,11 @@
 #include "putty.h"
 #include "ssh.h"
 
-void platform_get_x11_auth(struct X11Display *disp, const Config *cfg)
+void platform_get_x11_auth(struct X11Display *disp, Conf *conf)
 {
-    if (cfg->xauthfile.path[0])
-       x11_get_auth_from_authfile(disp, cfg->xauthfile.path);
+    char *xauthpath = conf_get_filename(conf, CONF_xauthfile)->path;
+    if (xauthpath[0])
+       x11_get_auth_from_authfile(disp, xauthpath);
 }
 
 const int platform_uses_x11_unix_by_default = FALSE;
index d674120..9082471 100644 (file)
--- a/x11fwd.c
+++ b/x11fwd.c
@@ -68,8 +68,7 @@ static const struct plug_function_table dummy_plug = {
     dummy_plug_sent, dummy_plug_accepting
 };
 
-struct X11Display *x11_setup_display(char *display, int authtype,
-                                    const Config *cfg)
+struct X11Display *x11_setup_display(char *display, int authtype, Conf *conf)
 {
     struct X11Display *disp = snew(struct X11Display);
     char *localcopy;
@@ -166,7 +165,7 @@ struct X11Display *x11_setup_display(char *display, int authtype,
 
        disp->port = 6000 + disp->displaynum;
        disp->addr = name_lookup(disp->hostname, disp->port,
-                                &disp->realhost, cfg, ADDRTYPE_UNSPEC);
+                                &disp->realhost, conf, ADDRTYPE_UNSPEC);
     
        if ((err = sk_addr_error(disp->addr)) != NULL) {
            sk_addr_free(disp->addr);
@@ -249,7 +248,7 @@ struct X11Display *x11_setup_display(char *display, int authtype,
     disp->localauthproto = X11_NO_AUTH;
     disp->localauthdata = NULL;
     disp->localauthdatalen = 0;
-    platform_get_x11_auth(disp, cfg);
+    platform_get_x11_auth(disp, conf);
 
     return disp;
 }
@@ -558,8 +557,7 @@ int x11_get_screen_number(char *display)
  * also, fills the SocketsStructure
  */
 extern const char *x11_init(Socket *s, struct X11Display *disp, void *c,
-                           const char *peeraddr, int peerport,
-                           const Config *cfg)
+                           const char *peeraddr, int peerport, Conf *conf)
 {
     static const struct plug_function_table fn_table = {
        x11_log,
@@ -586,7 +584,7 @@ extern const char *x11_init(Socket *s, struct X11Display *disp, void *c,
 
     pr->s = *s = new_connection(sk_addr_dup(disp->addr),
                                disp->realhost, disp->port,
-                               0, 1, 0, 0, (Plug) pr, cfg);
+                               0, 1, 0, 0, (Plug) pr, conf);
     if ((err = sk_socket_error(*s)) != NULL) {
        sfree(pr);
        return err;