chiark / gitweb /
REORG Delete everything that's not innduct or build system or changed for innduct
[innduct.git] / lib / innconf.c
diff --git a/lib/innconf.c b/lib/innconf.c
deleted file mode 100644 (file)
index f04510e..0000000
+++ /dev/null
@@ -1,785 +0,0 @@
-/*  $Id: innconf.c 7751 2008-04-06 14:35:40Z iulius $
-**
-**  Manage the global innconf struct.
-**
-**  The functions in this file collapse the parse tree for inn.conf into the
-**  innconf struct that's used throughout INN.  The code to collapse a
-**  configuration parse tree into a struct is fairly generic and should
-**  probably be moved into a separate library.
-**
-**  When adding new inn.conf parameters, make sure to add them in all of the
-**  following places:
-**
-**   * The table in this file.
-**   * include/inn/innconf.h
-**   * doc/pod/inn.conf.pod (and regenerate doc/man/inn.conf.5)
-**   * Add the default value to samples/inn.conf.in
-**
-**  Please maintain the current organization of parameters.  There are two
-**  different orders, one of which is a logical order used by the
-**  documentation, the include file, and the sample file, and the other of
-**  which is used in this file.  The order in this file is documentation of
-**  where each parameter is used, for later work at breaking up this mess
-**  of parameters into separate configuration groups for each INN subsystem.
-*/
-
-#include "config.h"
-#include "clibrary.h"
-#include <ctype.h>
-
-#include "inn/confparse.h"
-#include "inn/innconf.h"
-#include "inn/messages.h"
-#include "inn/vector.h"
-#include "libinn.h"
-#include "paths.h"
-
-/* Instantiation of the global innconf variable. */
-struct innconf *innconf = NULL;
-
-/* Data types used to express the mappings from the configuration parse into
-   the innconf struct. */
-
-enum type {
-    TYPE_BOOLEAN,
-    TYPE_NUMBER,
-    TYPE_STRING
-};
-
-struct config {
-    const char *name;
-    size_t location;
-    enum type type;
-    struct {
-        bool boolean;
-        long integer;
-        const char *string;
-    } defaults;
-};
-
-/* The following macros are helpers to make it easier to define the table that
-   specifies how to convert the configuration file into a struct. */
-
-#define K(name)         (#name), offsetof(struct innconf, name)
-
-#define BOOL(def)       TYPE_BOOLEAN, { (def),     0,  NULL }
-#define NUMBER(def)     TYPE_NUMBER,  {     0, (def),  NULL }
-#define STRING(def)     TYPE_STRING,  {     0,     0, (def) }
-
-/* Accessor macros to get a pointer to a value inside a struct. */
-#define CONF_BOOL(conf, offset)   (bool *) (void *)((char *) (conf) + (offset))
-#define CONF_LONG(conf, offset)   (long *) (void *)((char *) (conf) + (offset))
-#define CONF_STRING(conf, offset) (char **)(void *)((char *) (conf) + (offset))
-
-/* Special notes:
-
-   checkincludedtext and localmaxartisize are used by both nnrpd and inews,
-   but inews should probably just let nnrpd do that checking.
-
-   organization is used by both nnrpd and inews.  Perhaps inews should just
-   let nnrpd set it.
-
-   mergetogroups is currently used by nnrpd for permission checking on
-   posting.  I think the check should always be performed based on the
-   newsgroup to which the user is actually posting, and nnrpd should let
-   innd do the merging.
-
-   useoverchan is only used in innd and overchan.  It should probably be
-   something the storage system knows.  Ideally, the storage system would
-   handle overchan itself, but that would require a lot of infrastructure;
-   in the interim, it could be something that programs could ask the
-   overview subsystem about.
-
-   doinnwatch and docnfsstat are used by rc.news currently, but really
-   should be grouped with the appropriate subsystem.
-
-   newsrequeue also uses nntplinklog, but the parameter should go away
-   completely anyway.
-
-   timer is currently used in various places, but it may be best to replace
-   it with individual settings for innd and innfeed, and any other code that
-   wants to use it.
-
-   maxforks is used by rnews and nnrpd.  I'm not sure this is that useful of
-   a setting to have.
-*/
-
-const struct config config_table[] = {
-    { K(domain),                STRING  (NULL) },
-    { K(enableoverview),        BOOL    (true) },
-    { K(fromhost),              STRING  (NULL) },
-    { K(groupbaseexpiry),       BOOL    (true) },
-    { K(mailcmd),               STRING  (NULL) },
-    { K(maxforks),              NUMBER  (10) },
-    { K(mta),                   STRING  (NULL) },
-    { K(nicekids),              NUMBER  (4) },
-    { K(ovmethod),              STRING  (NULL) },
-    { K(pathhost),              STRING  (NULL) },
-    { K(rlimitnofile),          NUMBER  (-1) },
-    { K(server),                STRING  (NULL) },
-    { K(sourceaddress),         STRING  (NULL) },
-    { K(sourceaddress6),        STRING  (NULL) },
-    { K(timer),                 NUMBER  (0) },
-
-    { K(patharchive),           STRING  (NULL) },
-    { K(patharticles),          STRING  (NULL) },
-    { K(pathbin),               STRING  (NULL) },
-    { K(pathcontrol),           STRING  (NULL) },
-    { K(pathdb),                STRING  (NULL) },
-    { K(pathetc),               STRING  (NULL) },
-    { K(pathfilter),            STRING  (NULL) },
-    { K(pathhttp),              STRING  (NULL) },
-    { K(pathincoming),          STRING  (NULL) },
-    { K(pathlog),               STRING  (NULL) },
-    { K(pathnews),              STRING  (NULL) },
-    { K(pathoutgoing),          STRING  (NULL) },
-    { K(pathoverview),          STRING  (NULL) },
-    { K(pathrun),               STRING  (NULL) },
-    { K(pathspool),             STRING  (NULL) },
-    { K(pathtmp),               STRING  (NULL) },
-
-    /* The following settings are specific to innd. */
-    { K(artcutoff),             NUMBER  (10) },
-    { K(badiocount),            NUMBER  (5) },
-    { K(bindaddress),           STRING  (NULL) },
-    { K(bindaddress6),          STRING  (NULL) },
-    { K(blockbackoff),          NUMBER  (120) },
-    { K(chaninacttime),         NUMBER  (600) },
-    { K(chanretrytime),         NUMBER  (300) },
-    { K(datamovethreshold),     NUMBER  (8192) },
-    { K(dontrejectfiltered),    BOOL    (false) },
-    { K(hiscachesize),          NUMBER  (0) },
-    { K(icdsynccount),          NUMBER  (10) },
-    { K(ignorenewsgroups),      BOOL    (false) },
-    { K(linecountfuzz),         NUMBER  (0) },
-    { K(logartsize),            BOOL    (true) },
-    { K(logcancelcomm),         BOOL    (false) },
-    { K(logipaddr),             BOOL    (true) },
-    { K(logsitename),           BOOL    (true) },
-    { K(maxartsize),            NUMBER  (1000000) },
-    { K(maxconnections),        NUMBER  (50) },
-    { K(mergetogroups),         BOOL    (false) },
-    { K(nntpactsync),           NUMBER  (200) },
-    { K(nntplinklog),           BOOL    (false) },
-    { K(noreader),              BOOL    (false) },
-    { K(pathalias),             STRING  (NULL) },
-    { K(pathcluster),           STRING  (NULL) },
-    { K(pauseretrytime),        NUMBER  (300) },
-    { K(peertimeout),           NUMBER  (3600) },
-    { K(port),                  NUMBER  (119) },
-    { K(readerswhenstopped),    BOOL    (false) },
-    { K(refusecybercancels),    BOOL    (false) },
-    { K(remembertrash),         BOOL    (true) },
-    { K(stathist),              STRING  (NULL) },
-    { K(status),                NUMBER  (0) },
-    { K(verifycancels),         BOOL    (false) },
-    { K(wanttrash),             BOOL    (false) },
-    { K(wipcheck),              NUMBER  (5) },
-    { K(wipexpire),             NUMBER  (10) },
-    { K(xrefslave),             BOOL    (false) },
-
-    /* The following settings are specific to nnrpd. */
-    { K(addnntppostingdate),    BOOL    (true) },
-    { K(addnntppostinghost),    BOOL    (true) },
-    { K(allownewnews),          BOOL    (true) },
-    { K(backoffauth),           BOOL    (false) },
-    { K(backoffdb),             STRING  (NULL) },
-    { K(backoffk),              NUMBER  (1) },
-    { K(backoffpostfast),       NUMBER  (0) },
-    { K(backoffpostslow),       NUMBER  (1) },
-    { K(backofftrigger),        NUMBER  (10000) },
-    { K(checkincludedtext),     BOOL    (false) },
-    { K(clienttimeout),         NUMBER  (600) },
-    { K(complaints),            STRING  (NULL) },
-    { K(initialtimeout),        NUMBER  (10) },
-    { K(keyartlimit),           NUMBER  (100000) },
-    { K(keylimit),              NUMBER  (512) },
-    { K(keymaxwords),           NUMBER  (250) },
-    { K(keywords),              BOOL    (false) },
-    { K(localmaxartsize),       NUMBER  (1000000) },
-    { K(maxcmdreadsize),        NUMBER  (BUFSIZ) },
-    { K(msgidcachesize),        NUMBER  (10000) },
-    { K(moderatormailer),       STRING  (NULL) },
-    { K(nfsreader),             BOOL    (false) },
-    { K(nfsreaderdelay),        NUMBER  (60) },
-    { K(nicenewnews),           NUMBER  (0) },
-    { K(nicennrpd),             NUMBER  (0) },
-    { K(nnrpdflags),            STRING  ("") },
-    { K(nnrpdauthsender),       BOOL    (false) },
-    { K(nnrpdloadlimit),        NUMBER  (16) },
-    { K(nnrpdoverstats),        BOOL    (false) },
-    { K(organization),          STRING  (NULL) },
-    { K(readertrack),           BOOL    (false) },
-    { K(spoolfirst),            BOOL    (false) },
-    { K(strippostcc),           BOOL    (false) },
-
-    /* The following settings are used by nnrpd and rnews. */
-    { K(nnrpdposthost),         STRING  (NULL) },
-    { K(nnrpdpostport),         NUMBER  (119) },
-
-    /* The following settings are specific to the storage subsystem. */
-    { K(articlemmap),           BOOL    (false) },
-    { K(cnfscheckfudgesize),    NUMBER  (0) },
-    { K(immediatecancel),       BOOL    (false) },
-    { K(keepmmappedthreshold),  NUMBER  (1024) },
-    { K(nfswriter),             BOOL    (false) },
-    { K(nnrpdcheckart),         BOOL    (true) },
-    { K(overcachesize),         NUMBER  (15) },
-    { K(ovgrouppat),            STRING  (NULL) },
-    { K(storeonxref),           BOOL    (true) },
-    { K(tradindexedmmap),       BOOL    (true) },
-    { K(useoverchan),           BOOL    (false) },
-    { K(wireformat),            BOOL    (false) },
-
-    /* The following settings are specific to the history subsystem. */
-    { K(hismethod),             STRING  (NULL) },
-
-    /* The following settings are specific to rc.news. */
-    { K(docnfsstat),            BOOL    (false) },
-    { K(innflags),              STRING  (NULL) },
-    { K(pgpverify),             BOOL    (false) },
-
-    /* The following settings are specific to innwatch. */
-    { K(doinnwatch),            BOOL    (true) },
-    { K(innwatchbatchspace),    NUMBER  (800) },
-    { K(innwatchlibspace),      NUMBER  (25000) },
-    { K(innwatchloload),        NUMBER  (1000) },
-    { K(innwatchhiload),        NUMBER  (2000) },
-    { K(innwatchpauseload),     NUMBER  (1500) },
-    { K(innwatchsleeptime),     NUMBER  (600) },
-    { K(innwatchspoolnodes),    NUMBER  (200) },
-    { K(innwatchspoolspace),    NUMBER  (8000) },
-
-    /* The following settings are specific to scanlogs. */
-    { K(logcycles),             NUMBER  (3) },
-};
-
-
-/*
-**  Set some defaults that cannot be included in the table because they depend
-**  on other elements or require function calls to set.  Called after the
-**  configuration file is read, so any that have to override what's read have
-**  to free whatever values are set by the file.
-*/
-static void
-innconf_set_defaults(void)
-{
-    char *value;
-
-    /* Some environment variables override settings in inn.conf. */
-    value = getenv("FROMHOST");
-    if (value != NULL) {
-        if (innconf->fromhost != NULL)
-            free(innconf->fromhost);
-        innconf->fromhost = xstrdup(value);
-    }
-    value = getenv("NNTPSERVER");
-    if (value != NULL) {
-        if (innconf->server != NULL)
-            free(innconf->server);
-        innconf->server = xstrdup(value);
-    }
-    value = getenv("ORGANIZATION");
-    if (value != NULL) {
-        if (innconf->organization != NULL)
-            free(innconf->organization);
-        innconf->organization = xstrdup(value);
-    }
-    value = getenv("INND_BIND_ADDRESS");
-    if (value != NULL) {
-        if (innconf->bindaddress != NULL)
-            free(innconf->bindaddress);
-        innconf->bindaddress = xstrdup(value);
-    }
-    value = getenv("INND_BIND_ADDRESS6");
-    if (value != NULL) {
-        if (innconf->bindaddress6 != NULL)
-            free(innconf->bindaddress6);
-        innconf->bindaddress6 = xstrdup(value);
-    }
-
-    /* Some parameters have defaults that depend on other parameters. */
-    if (innconf->fromhost == NULL)
-        innconf->fromhost = xstrdup(GetFQDN(innconf->domain));
-    if (innconf->pathhost == NULL)
-        innconf->pathhost = xstrdup(GetFQDN(innconf->domain));
-    if (innconf->pathtmp == NULL)
-        innconf->pathtmp = xstrdup(_PATH_TMP);
-
-    /* All of the paths are relative to other paths if not set except for
-       pathnews, which is required to be set by innconf_validate. */
-    if (innconf->pathbin == NULL)
-        innconf->pathbin = concatpath(innconf->pathnews, "bin");
-    if (innconf->pathfilter == NULL)
-        innconf->pathfilter = concatpath(innconf->pathbin, "filter");
-    if (innconf->pathdb == NULL)
-        innconf->pathdb = concatpath(innconf->pathnews, "db");
-    if (innconf->pathetc == NULL)
-        innconf->pathetc = concatpath(innconf->pathnews, "etc");
-    if (innconf->pathrun == NULL)
-        innconf->pathrun = concatpath(innconf->pathnews, "run");
-    if (innconf->pathlog == NULL)
-        innconf->pathlog = concatpath(innconf->pathnews, "log");
-    if (innconf->pathhttp == NULL)
-        innconf->pathhttp = xstrdup(innconf->pathlog);
-    if (innconf->pathspool == NULL)
-        innconf->pathspool = concatpath(innconf->pathnews, "spool");
-    if (innconf->patharticles == NULL)
-        innconf->patharticles = concatpath(innconf->pathspool, "articles");
-    if (innconf->pathoverview == NULL)
-        innconf->pathoverview = concatpath(innconf->pathspool, "overview");
-    if (innconf->pathoutgoing == NULL)
-        innconf->pathoutgoing = concatpath(innconf->pathspool, "outgoing");
-    if (innconf->pathincoming == NULL)
-        innconf->pathincoming = concatpath(innconf->pathspool, "incoming");
-    if (innconf->patharchive == NULL)
-        innconf->patharchive = concatpath(innconf->pathspool, "archive");
-
-    /* One other parameter depends on pathbin. */
-    if (innconf->mailcmd == NULL)
-        innconf->mailcmd = concatpath(innconf->pathbin, "innmail");
-}
-
-
-/*
-**  Given a config_group struct representing the inn.conf file, parse that
-**  into an innconf struct and return the newly allocated struct.  This
-**  routine should be pulled out into a library for smashing configuration
-**  file parse results into structs.
-*/
-static struct innconf *
-innconf_parse(struct config_group *group)
-{
-    unsigned int i;
-    bool *bool_ptr;
-    long *long_ptr;
-    const char *char_ptr;
-    char **string;
-    struct innconf *config;
-
-    config = xmalloc(sizeof(struct innconf));
-    for (i = 0; i < ARRAY_SIZE(config_table); i++)
-        switch (config_table[i].type) {
-        case TYPE_BOOLEAN:
-            bool_ptr = CONF_BOOL(config, config_table[i].location);
-            if (!config_param_boolean(group, config_table[i].name, bool_ptr))
-                *bool_ptr = config_table[i].defaults.boolean;
-            break;
-        case TYPE_NUMBER:
-            long_ptr = CONF_LONG(config, config_table[i].location);
-            if (!config_param_integer(group, config_table[i].name, long_ptr))
-                *long_ptr = config_table[i].defaults.integer;
-            break;
-        case TYPE_STRING:
-            if (!config_param_string(group, config_table[i].name, &char_ptr))
-                char_ptr = config_table[i].defaults.string;
-            string = CONF_STRING(config, config_table[i].location);
-            *string = (char_ptr == NULL) ? NULL : xstrdup(char_ptr);
-            break;
-        default:
-            die("internal error: invalid type in row %u of config table", i);
-            break;
-        }
-    return config;
-}
-
-
-/*
-**  Check the configuration file for consistency and ensure that mandatory
-**  settings are present.  Returns true if the file is okay and false
-**  otherwise.
-*/
-static bool
-innconf_validate(struct config_group *group)
-{
-    bool okay = true;
-    long threshold;
-
-    if (GetFQDN(innconf->domain) == NULL) {
-        warn("hostname does not resolve or domain not set in inn.conf");
-        okay = false;
-    }
-    if (innconf->mta == NULL) {
-        warn("must set mta in inn.conf");
-        okay = false;
-    }
-    if (innconf->pathnews == NULL) {
-        warn("must set pathnews in inn.conf");
-        okay = false;
-    }
-    if (innconf->hismethod == NULL) {
-        warn("must set hismethod in inn.conf");
-        okay = false;
-    }
-    if (innconf->enableoverview && innconf->ovmethod == NULL) {
-        warn("ovmethod must be set in inn.conf if enableoverview is true");
-        okay = false;
-    }
-    threshold = innconf->datamovethreshold;
-    if (threshold <= 0 || threshold > 1024 * 1024) {
-        config_error_param(group, "datamovethreshold",
-                           "maximum value for datamovethreshold is 1MB");
-        innconf->datamovethreshold = 1024 * 1024;
-    }
-    return okay;
-}
-
-
-/*
-**  Read in inn.conf.  Takes a single argument, which is either NULL to read
-**  the default configuration file or a path to an alternate configuration
-**  file to read.  Returns true if the file was read successfully and false
-**  otherwise.
-*/
-bool
-innconf_read(const char *path)
-{
-    struct config_group *group;
-    char *tmpdir;
-
-    if (innconf != NULL)
-        innconf_free(innconf);
-    if (path == NULL)
-        path = getenv("INNCONF");
-    group = config_parse_file(path == NULL ? _PATH_CONFIG : path);
-    if (group == NULL)
-        return false;
-
-    innconf = innconf_parse(group);
-    if (!innconf_validate(group))
-        return false;
-    config_free(group);
-    innconf_set_defaults();
-
-    /* It's not clear that this belongs here, but it was done by the old
-       configuration parser, so this is a convenient place to do it. */
-    tmpdir = getenv("TMPDIR");
-    if (tmpdir == NULL || strcmp(tmpdir, innconf->pathtmp) != 0)
-        if (setenv("TMPDIR", innconf->pathtmp, true) != 0) {
-            warn("cannot set TMPDIR in the environment");
-            return false;
-        }
-
-    return true;
-}
-
-
-/*
-**  Check an inn.conf file.  This involves reading it in and then additionally
-**  making sure that there are no keys defined in the inn.conf file that
-**  aren't recognized.  This doesn't have to be very fast (and isn't).
-**  Returns true if everything checks out successfully, and false otherwise.
-**
-**  A lot of code is duplicated with innconf_read here and should be
-**  refactored.
-*/
-bool
-innconf_check(const char *path)
-{
-    struct config_group *group;
-    struct vector *params;
-    size_t set, known;
-    bool found;
-    bool okay = true;
-
-    if (innconf != NULL)
-        innconf_free(innconf);
-    if (path == NULL)
-        path = getenv("INNCONF");
-    group = config_parse_file(path == NULL ? _PATH_CONFIG : path);
-    if (group == NULL)
-        return false;
-
-    innconf = innconf_parse(group);
-    if (!innconf_validate(group))
-        return false;
-
-    /* Now, do the work that innconf_read doesn't do.  Get a list of
-       parameters defined in innconf and then walk our list of valid
-       parameters and see if there are any set that we don't recognize. */
-    params = config_params(group);
-    for (set = 0; set < params->count; set++) {
-        found = false;
-        for (known = 0; known < ARRAY_SIZE(config_table); known++)
-            if (strcmp(params->strings[set], config_table[known].name) == 0)
-                found = true;
-        if (!found) {
-            config_error_param(group, params->strings[set],
-                               "unknown parameter %s", params->strings[set]);
-            okay = false;
-        }
-    }
-
-    /* Check and warn about a few other parameters. */
-    if (innconf->peertimeout < 3 * 60)
-        config_error_param(group, "peertimeout",
-                           "warning: NNTP draft (15) states inactivity"
-                           " timeouts MUST be at least three minutes");
-    if (innconf->clienttimeout < 3 * 60)
-        config_error_param(group, "clienttimeout",
-                           "warning: NNTP draft (15) states inactivity"
-                           " timeouts MUST be at least three minutes");
-
-    /* All done.  Free the parse tree and return. */
-    config_free(group);
-    return okay;
-}
-
-
-/*
-**  Free innconf, requiring some complexity since all strings stored in the
-**  innconf struct are allocated memory.  This routine is mostly generic to
-**  any struct smashed down from a configuration file parse.
-*/
-void
-innconf_free(struct innconf *config)
-{
-    unsigned int i;
-    char *p;
-
-    for (i = 0; i < ARRAY_SIZE(config_table); i++)
-        if (config_table[i].type == TYPE_STRING) {
-            p = *CONF_STRING(config, config_table[i].location);
-            if (p != NULL)
-                free(p);
-        }
-    free(config);
-}
-
-
-/*
-**  Print a single boolean value with appropriate quoting.
-*/
-static void
-print_boolean(FILE *file, const char *key, bool value,
-              enum innconf_quoting quoting)
-{
-    char *upper, *p;
-
-    switch (quoting) {
-    case INNCONF_QUOTE_NONE:
-        fprintf(file, "%s\n", value ? "true" : "false");
-        break;
-    case INNCONF_QUOTE_SHELL:
-        upper = xstrdup(key);
-        for (p = upper; *p != '\0'; p++)
-            *p = toupper(*p);
-        fprintf(file, "%s=%s; export %s;\n", upper, value ? "true" : "false",
-                upper);
-        free(upper);
-        break;
-    case INNCONF_QUOTE_PERL:
-        fprintf(file, "$%s = '%s';\n", key, value ? "true" : "false");
-        break;
-    case INNCONF_QUOTE_TCL:
-        fprintf(file, "set inn_%s \"%s\"\n", key, value ? "true" : "false");
-        break;
-    }
-}
-
-
-/*
-**  Print a single integer value with appropriate quoting.
-*/
-static void
-print_number(FILE *file, const char *key, long value,
-             enum innconf_quoting quoting)
-{
-    char *upper, *p;
-
-    switch (quoting) {
-    case INNCONF_QUOTE_NONE:
-        fprintf(file, "%ld\n", value);
-        break;
-    case INNCONF_QUOTE_SHELL:
-        upper = xstrdup(key);
-        for (p = upper; *p != '\0'; p++)
-            *p = toupper(*p);
-        fprintf(file, "%s=%ld; export %s;\n", upper, value, upper);
-        free(upper);
-        break;
-    case INNCONF_QUOTE_PERL:
-        fprintf(file, "$%s = %ld;\n", key, value);
-        break;
-    case INNCONF_QUOTE_TCL:
-        fprintf(file, "set inn_%s %ld\n", key, value);
-        break;
-    }
-}
-
-
-/*
-**  Print a single string value with appropriate quoting.
-*/
-static void
-print_string(FILE *file, const char *key, const char *value,
-             enum innconf_quoting quoting)
-{
-    char *upper, *p;
-    const char *letter;
-    static const char tcl_unsafe[] = "$[]{}\"\\";
-
-    switch (quoting) {
-    case INNCONF_QUOTE_NONE:
-        fprintf(file, "%s\n", value != NULL ? value : "");
-        break;
-    case INNCONF_QUOTE_SHELL:
-        upper = xstrdup(key);
-        for (p = upper; *p != '\0'; p++)
-            *p = toupper(*p);
-        fprintf(file, "%s='", upper);
-        for (letter = value; letter != NULL && *letter != '\0'; letter++) {
-            if (*letter == '\'')
-                fputs("'\\''", file);
-            else if (*letter == '\\')
-                fputs("\\\\", file);
-            else
-                fputc(*letter, file);
-        }
-        fprintf(file, "'; export %s;\n", upper);
-        free(upper);
-        break;
-    case INNCONF_QUOTE_PERL:
-        fprintf(file, "$%s = '", key);
-        for (letter = value; letter != NULL && *letter != '\0'; letter++) {
-            if (*letter == '\'' || *letter == '\\')
-                fputc('\\', file);
-            fputc(*letter, file);
-        }
-        fputs("';\n", file);
-        break;
-    case INNCONF_QUOTE_TCL:
-        fprintf(file, "set inn_%s \"", key);
-        for (letter = value; letter != NULL && *letter != '\0'; letter++) {
-            if (strchr(tcl_unsafe, *letter) != NULL)
-                fputc('\\', file);
-            fputc(*letter, file);
-        }
-        fputs("\"\n", file);
-        break;
-    }
-}
-
-
-/*
-**  Print a single paramter to the given file.  Takes an index into the table
-**  specifying the attribute to print and the quoting.
-*/
-static void
-print_parameter(FILE *file, size_t i, enum innconf_quoting quoting)
-{
-    bool bool_val;
-    long long_val;
-    const char *string_val;
-
-    switch (config_table[i].type) {
-    case TYPE_BOOLEAN:
-        bool_val = *CONF_BOOL(innconf, config_table[i].location);
-        print_boolean(file, config_table[i].name, bool_val, quoting);
-        break;
-    case TYPE_NUMBER:
-        long_val = *CONF_LONG(innconf, config_table[i].location);
-        print_number(file, config_table[i].name, long_val, quoting);
-        break;
-    case TYPE_STRING:
-        string_val = *CONF_STRING(innconf, config_table[i].location);
-        print_string(file, config_table[i].name, string_val, quoting);
-        break;
-    default:
-        die("internal error: invalid type in row %d of config table", i);
-        break;
-    }
-}
-
-
-/*
-**  Given a single parameter, find it in the table and print out its value.
-*/
-bool
-innconf_print_value(FILE *file, const char *key, enum innconf_quoting quoting)
-{
-    size_t i;
-
-    for (i = 0; i < ARRAY_SIZE(config_table); i++)
-        if (strcmp(key, config_table[i].name) == 0) {
-            print_parameter(file, i, quoting);
-            return true;
-        }
-    return false;
-}
-
-
-/*
-**  Dump the entire inn.conf configuration with appropriate quoting.
-*/
-void
-innconf_dump(FILE *file, enum innconf_quoting quoting)
-{
-    size_t i;
-
-    for (i = 0; i < ARRAY_SIZE(config_table); i++)
-        print_parameter(file, i, quoting);
-}
-
-
-/*
-**  Compare two innconf structs to see if they represent identical
-**  configurations.  This routine is mostly used for testing.  Prints warnings
-**  about where the two configurations differ and return false if they differ,
-**  true if they match.  This too should be moved into a config smashing
-**  library.
-*/
-bool
-innconf_compare(struct innconf *conf1, struct innconf *conf2)
-{
-    unsigned int i;
-    bool bool1, bool2;
-    long long1, long2;
-    const char *string1, *string2;
-    bool okay = true;
-
-    for (i = 0; i < ARRAY_SIZE(config_table); i++)
-        switch (config_table[i].type) {
-        case TYPE_BOOLEAN:
-            bool1 = *CONF_BOOL(conf1, config_table[i].location);
-            bool2 = *CONF_BOOL(conf2, config_table[i].location);
-            if (bool1 != bool2) {
-                warn("boolean variable %s differs: %d != %d",
-                     config_table[i].name, bool1, bool2);
-                okay = false;
-            }
-            break;
-        case TYPE_NUMBER:
-            long1 = *CONF_LONG(conf1, config_table[i].location);
-            long2 = *CONF_LONG(conf2, config_table[i].location);
-            if (long1 != long2) {
-                warn("integer variable %s differs: %ld != %ld",
-                     config_table[i].name, long1, long2);
-                okay = false;
-            }
-            break;
-        case TYPE_STRING:
-            string1 = *CONF_STRING(conf1, config_table[i].location);
-            string2 = *CONF_STRING(conf2, config_table[i].location);
-            if (string1 == NULL && string2 != NULL) {
-                warn("string variable %s differs: NULL != %s",
-                     config_table[i].name, string2);
-                okay = false;
-            } else if (string1 != NULL && string2 == NULL) {
-                warn("string variable %s differs: %s != NULL",
-                     config_table[i].name, string1);
-                okay = false;
-            } else if (string1 != NULL && string2 != NULL) {
-                if (strcmp(string1, string2) != 0) {
-                    warn("string variable %s differs: %s != %s",
-                         config_table[i].name, string1, string2);
-                    okay = false;
-                }
-            }
-            break;
-        default:
-            die("internal error: invalid type in row %d of config table", i);
-            break;
-        }
-    return okay;
-}