Index: trunk/psModules/src/config/Makefile.am
===================================================================
--- trunk/psModules/src/config/Makefile.am	(revision 5309)
+++ trunk/psModules/src/config/Makefile.am	(revision 5355)
@@ -3,7 +3,8 @@
 libpsmoduleconfig_la_CPPFLAGS = $(SRCINC) $(PSMODULE_CFLAGS)
 libpsmoduleconfig_la_LDFLAGS  = -release $(PACKAGE_VERSION)
-libpsmoduleconfig_la_SOURCES  =
+libpsmoduleconfig_la_SOURCES  = \
+    pmConfig.c
 
 psmoduleincludedir = $(includedir)
-psmoduleinclude_HEADERS =
-
+psmoduleinclude_HEADERS = \
+    pmConfig.h
Index: trunk/psModules/src/config/pmConfig.c
===================================================================
--- trunk/psModules/src/config/pmConfig.c	(revision 5355)
+++ trunk/psModules/src/config/pmConfig.c	(revision 5355)
@@ -0,0 +1,362 @@
+/** @file  pmConfig.h
+ *
+ *  @author PAP, IfA
+ *
+ *  @version $Revision: 1.1 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2005-10-17 21:34:12 $
+ *
+ *  Copyright 2004 Maui High Performance Computing Center, University of Hawaii
+ *
+ */
+#include <stdio.h>
+#include <string.h>
+#include <assert.h>
+#include "pslib.h"
+#include "psAdditionals.h"
+#include "pmConfig.h"
+
+// XXX: These comments should have PS-specific prefixes.
+#define PS_SITE "PS_SITE"  // Name of the environment variable containing the site config file
+#define DEFAULT_SITE "ipprc.config" // Default site config file
+
+
+
+/** readConfig
+ *
+ * This function attempts to open the file specified in the parameter list
+ * parse it into metadata, then return it as a psMetaData table.
+ *
+ */
+static bool readConfig(
+    psMetadata **config,                // Config to output
+    const char *name,                   // Name of file
+    const char *description)            // Description of file
+{
+    int numBadLines = 0;  // Number of bad lines in config file
+    psLogMsg(__func__, PS_LOG_INFO, "Loading %s configuration from file %s\n",
+             description, name);
+    *config = psMetadataConfigParse(NULL, &numBadLines, name, true);
+    if (numBadLines > 0) {
+        psLogMsg(__func__, PS_LOG_WARN, "%d bad lines in %s configuration file (%s)\n",
+                 description, name);
+    }
+    if (! *config) {
+        psError(PS_ERR_IO, false, "Unable to read %s configuration from %s\n",
+                description, name);
+        return false;
+    }
+    return true;
+}
+
+
+
+bool pmConfigRead(
+    psMetadata **site, psMetadata **camera,
+    psMetadata **recipe,
+    int *argc,
+    char **argv,
+    const char *recipeName)
+{
+    PS_ASSERT_PTR_NON_NULL(site, false);
+    PS_ASSERT_PTR_NON_NULL(*site, false);
+    PS_ASSERT_PTR_NON_NULL(camera, false);
+    PS_ASSERT_PTR_NON_NULL(*camera, false);
+    PS_ASSERT_PTR_NON_NULL(recipe, false);
+    PS_ASSERT_PTR_NON_NULL(*recipe, false);
+    PS_ASSERT_INT_POSITIVE(*argc, false);
+    PS_ASSERT_PTR_NON_NULL(argv, false);
+
+    //
+    // The following section of code attempts to determine which file is
+    // the configuration file.  At the end of this code block, the siteName
+    // variable will contain the name of the configuration file.
+    //
+    char *siteName = NULL;
+    //
+    // First, try command line
+    //
+    int argNum = 0;
+    if ((argNum = psArgumentGet(*argc, argv, "-site"))) {
+        (void)psArgumentRemove(argNum, argc, argv);
+        if (argNum >= *argc) {
+            psLogMsg(__func__, PS_LOG_WARN,
+                     "-site command-line switch provided without the required filename --- ignored.\n");
+        } else {
+            siteName = argv[argNum];
+            (void)psArgumentRemove(argNum, argc, argv);
+        }
+    }
+
+    //
+    // Next, try environment variable
+    //
+    if (! siteName) {
+        siteName = getenv(PS_SITE);
+    }
+
+    //
+    // Last chance is ~/.ipprc
+    //
+    bool cleanupSiteName = false; // Do I have to psFree siteName?
+    if (! siteName) {
+        siteName = psStringCopy(DEFAULT_SITE);
+        cleanupSiteName = true;
+    }
+
+    if (! readConfig(site, siteName, "site")) {
+        if (cleanupSiteName) {
+            psFree(siteName);
+        }
+        return false;
+    }
+
+
+    //
+    // Next is the camera configuration
+    //
+    if ((argNum = psArgumentGet(*argc, argv, "-camera"))) {
+        (void)psArgumentRemove(argNum, argc, argv);
+        if (argNum >= *argc) {
+            psLogMsg(__func__, PS_LOG_WARN,
+                     "-camera command-line switch provided without the required filename --- ignored.\n");
+        } else {
+            (void)psArgumentRemove(argNum, argc, argv);
+            (void)readConfig(camera, argv[argNum], "camera");
+        }
+    }
+
+    //
+    // And then the recipe configuration
+    //
+    if ((argNum = psArgumentGet(*argc, argv, "-recipe"))) {
+        (void)psArgumentRemove(argNum, argc, argv);
+        if (argNum >= *argc) {
+            psLogMsg(__func__, PS_LOG_WARN,
+                     "-recipe command-line switch provided without the required filename --- ignored.\n");
+        } else {
+            (void)psArgumentRemove(argNum, argc, argv);
+            (void)readConfig(recipe, argv[argNum], "recipe");
+        }
+    }
+    // Or, load the recipe from the camera file, if appropriate
+    if (! *recipe && *camera && recipeName) {
+        *recipe = pmConfigRecipeFromCamera(*camera, recipeName);
+    }
+
+
+    //
+    // Now we can look into the site configuration and do the required stuff
+    //
+    bool mdok = true;   // Status of MD lookup result
+    psString timeName = psMetadataLookupString(&mdok, *site, "TIME"); // Name of time file
+    if (mdok && timeName) {
+        psTrace(__func__, 7, "Initialising psTime with file %s\n", timeName);
+        #ifdef PRODUCTION
+
+        psTimeInitialize(timeName);
+        #else
+
+        psLibInit(timeName);
+        #endif
+
+    }
+
+    int logLevel = psMetadataLookupS32(&mdok, *site, "LOGLEVEL"); // Logging level
+    if (mdok && logLevel >= 0) {
+        psTrace(__func__, 7, "Setting log level to %d\n", logLevel);
+        psLogSetLevel(logLevel);
+    }
+
+    psString logFormat = psMetadataLookupString(&mdok, *site, "LOGLEVEL"); // Log format
+    if (mdok && logFormat) {
+        psTrace(__func__, 7, "Setting log format to %s\n", logFormat);
+        psLogSetFormat(logFormat);
+    }
+
+    psString logDest = psMetadataLookupString(&mdok, *site, "LOGDEST"); // Log destination
+    if (mdok && logDest) {
+        // XXX: Only stdout is provided for now; this section should be expanded in the future to do files,
+        // and perhaps even sockets.
+        if (strcasecmp(logDest, "STDOUT") != 0) {
+            psLogMsg(__func__, PS_LOG_WARN, "Only STDOUT is currently supported as a log destination.\n");
+        }
+        psTrace(__func__, 7, "Setting log destination to STDOUT.\n");
+        // XXX: Use something other than "1"
+        psLogSetDestination(1);
+    }
+
+    psMetadata *trace = psMetadataLookupMD(&mdok, *site, "TRACE"); // Trace levels
+    if (mdok && trace) {
+        psMetadataIterator *traceIter = psMetadataIteratorAlloc(trace, PS_LIST_HEAD, NULL); // Iterator
+        psMetadataItem *traceItem = NULL; // Item from MD iteration
+        while ((traceItem = psMetadataGetAndIncrement(traceIter))) {
+            if (traceItem->type != PS_DATA_S32) {
+                psLogMsg(__func__, PS_LOG_WARN, "The level for trace component %s is not of type S32 (%x)\n",
+                         traceItem->name, traceItem->type);
+                continue;
+            }
+            psTrace(__func__, 7, "Setting trace level for %s to %d\n", traceItem->name, traceItem->data.S32);
+            (void)psTraceSetLevel(traceItem->name, traceItem->data.S32);
+        }
+        psFree(traceIter);
+    }
+
+    if (cleanupSiteName) {
+        psFree(siteName);
+    }
+    return true;
+}
+
+bool pmConfigValidateCamera(
+    const psMetadata *camera,
+    const psMetadata *header)
+{
+    // Read the rule for that camera
+    bool mdStatus = true;  // Status of MD lookup
+    psMetadata *rule = psMetadataLookupMD(&mdStatus, camera, "RULE");
+    if (! mdStatus || ! rule) {
+        psLogMsg(__func__, PS_LOG_WARN, "Unable to read rule for camera.\n");
+        return false;
+    }
+
+    // Apply the rules
+    psMetadataIterator *ruleIter = psMetadataIteratorAlloc(rule, PS_LIST_HEAD, NULL); // Rule iterator
+    psMetadataItem *ruleItem = NULL; // Item from the metadata
+    bool match = true;   // Does it match?
+    while ((ruleItem = psMetadataGetAndIncrement(ruleIter)) && match) {
+        // Check for the existence of the rule
+        psMetadataItem *headerItem = psMetadataLookup((psMetadata*)header, ruleItem->name);
+        if (! headerItem || headerItem->type != ruleItem->type) {
+            match = false;
+        }
+
+        // Check to see if the rule works
+        switch (ruleItem->type) {
+        case PS_DATA_STRING:
+            psTrace(__func__, 8, "Matching %s: '%s' vs '%s'\n", ruleItem->name,
+                    ruleItem->data.V, headerItem->data.V);
+            if (strncmp(ruleItem->data.V, headerItem->data.V,
+                        strlen(ruleItem->data.V)) != 0) {
+                match = false;
+            }
+            break;
+        case PS_DATA_S32:
+        case PS_DATA_BOOL:
+            psTrace(__func__, 8, "Matching %s: %d vs %d\n", ruleItem->name,
+                    ruleItem->data.S32, headerItem->data.S32);
+            if (ruleItem->data.S32 != headerItem->data.S32) {
+                match = false;
+            }
+            break;
+        case PS_DATA_F32:
+            psTrace(__func__, 8, "Matching %s: %f vs %f\n", ruleItem->name,
+                    ruleItem->data.F32, headerItem->data.F32);
+            if (ruleItem->data.F32 != headerItem->data.F32) {
+                match = false;
+            }
+            break;
+        case PS_DATA_F64:
+            psTrace(__func__, 8, "Matching %s: %g vs %g\n", ruleItem->name,
+                    ruleItem->data.F64, headerItem->data.F64);
+            if (ruleItem->data.F64 != headerItem->data.F64) {
+                match = false;
+            }
+            break;
+        default:
+            psLogMsg(__func__, PS_LOG_WARN, "Ignoring invalid type in metadata: %x\n",
+                     ruleItem->type);
+        }
+    } // Iterating through the RULEs
+
+    psFree(ruleIter);
+
+    return match;
+}
+
+
+
+// Work out what camera we have, based on the FITS header and a set of rules specified in the IPP
+// configuration; return the camera configuration
+
+psMetadata *pmConfigCameraFromHeader(
+    const psMetadata *ipprc,            // The IPP configuration
+    const psMetadata *header)           // The FITS header
+{
+    bool mdStatus = false;  // Metadata lookup status
+    psMetadata *cameras = psMetadataLookupMD(&mdStatus, ipprc, "CAMERAS");
+    if (! mdStatus) {
+        psError(PS_ERR_IO, false, "Unable to find CAMERAS in the configuration.\n");
+        return NULL;
+    }
+
+    psMetadata *winner = NULL;       // The camera configuration whose rule first matches the supplied header
+
+    // Iterate over the cameras
+    psMetadataIterator *iterator = psMetadataIteratorAlloc(cameras, PS_LIST_HEAD, NULL); // MD Iterator
+    psMetadataItem *cameraItem = NULL; // Item from the metadata
+    while ((cameraItem = psMetadataGetAndIncrement(iterator))) {
+        // Open the camera information
+        psTrace(__func__, 3, "Inspecting camera %s (%s)\n", cameraItem->name,
+                cameraItem->comment);
+        psMetadata *camera = NULL; // The camera metadata
+        if (cameraItem->type == PS_DATA_METADATA) {
+            camera = psMemIncrRefCounter(cameraItem->data.md);
+        } else if (cameraItem->type == PS_DATA_STRING) {
+            psTrace(__func__, 5, "Reading camera configuration for %s...\n", cameraItem->name);
+            int badLines = 0;  // Number of bad lines in reading camera configuration
+            camera = psMetadataConfigParse(NULL, &badLines, cameraItem->data.V, true);
+            if (badLines > 0) {
+                psLogMsg(__func__, PS_LOG_WARN, "%d bad lines encountered while reading camera"
+                         "configuration %s\n", badLines, cameraItem->name);
+            }
+        }
+
+        if (! camera) {
+            psLogMsg(__func__, PS_LOG_WARN, "Unable to interpret camera configuration for %s (%s)\n",
+                     cameraItem->name, cameraItem->comment);
+            continue;
+        }
+
+        if (pmConfigValidateCamera(camera, header)) {
+            if (! winner) {
+                // This is the first match
+                winner = psMemIncrRefCounter(camera);
+                psLogMsg(__func__, PS_LOG_INFO, "FITS header matches camera %s\n",
+                         cameraItem->name);
+            } else {
+                // We have a duplicate match
+                psLogMsg(__func__, PS_LOG_WARN, "Additional camera found that matches the rules: %s\n",
+                         cameraItem->name);
+            }
+        } // Done inspecting the camera
+
+        psFree(camera);
+
+    } // Done looking at all cameras
+    if (! winner) {
+        psError(PS_ERR_IO, true, "Unable to find an camera that matches input FITS header!\n");
+    }
+
+    psFree(iterator);
+    return winner;
+}
+
+psMetadata *pmConfigRecipeFromCamera(
+    const psMetadata *camera,
+    const char *recipeName)
+{
+    assert(camera);
+    assert(recipeName);
+
+    psMetadata *recipe = NULL; // Recipe to read
+    bool mdok = true;   // Status of MD lookup
+    psMetadata *recipes = psMetadataLookupMD(&mdok, camera, "RECIPES"); // The list of recipes
+    if (! mdok || ! recipes) {
+        psLogMsg(__func__, PS_LOG_WARN, "RECIPES in the camera configuration file is not of type METADATA\n");
+    } else {
+        psString recipeFileName = psMetadataLookupString(&mdok, recipes, recipeName);
+        (void)readConfig(&recipe, recipeFileName, "recipe");
+    }
+
+    return recipe;
+}
Index: trunk/psModules/src/config/pmConfig.h
===================================================================
--- trunk/psModules/src/config/pmConfig.h	(revision 5355)
+++ trunk/psModules/src/config/pmConfig.h	(revision 5355)
@@ -0,0 +1,86 @@
+/** @file  pmConfig.h
+ *
+ *  @author PAP, IfA
+ *
+ *  @version $Revision: 1.1 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2005-10-17 21:34:12 $
+ *
+ *  Copyright 2004 Maui High Performance Computing Center, University of Hawaii
+ *
+ */
+#ifndef PM_CONFIG_H
+#define PM_CONFIG_H
+
+#include "pslib.h"
+
+
+
+/** pmConfigRead
+ * 
+ * pmConfigRead shall load the site configuration (according to the above rule
+ * for determining the source). The camera configuration shall also be loaded if
+ * it is specified on the command line (argc, argv); otherwise it shall be set to
+ * NULL. The recipe shall also be loaded from the command line (if specified) or,
+ * if the camera configuration has been loaded, from the camera configuration and
+ * recipe specification therein (see below). In dealing with the command line
+ * parameters, the functions shall use the appropriate functions in psLib to
+ * retrieve and remove the relevant options from the argument list; this
+ * simplifies assignment of the mandatory arguments, since all the optional
+ * command line arguments are removed leaving only the mandatory arguments. The
+ * following psLib setups shall also be performed if they are specified in the
+ * site configuration:
+ *
+ */
+bool pmConfigRead(
+    psMetadata **site,
+    psMetadata **camera,
+    psMetadata **recipe,
+    int *argc,
+    char **argv,
+    const char *recipeName
+);
+
+
+
+/** pmConfigValidateCamera
+ * 
+ * This function, used by pmConfigCameraFromHeader, shall return true if the
+ * FITS header matches the rule contained in the camera configuration (see
+ * x2.2.2.3); otherwise it shall return false.
+ * 
+ */
+bool pmConfigValidateCamera(
+    const psMetadata *camera,
+    const psMetadata *header
+);
+
+
+
+/** pmConfigCameraFromHeader
+ * 
+ * pmConfigCameraFromHeader shall load the camera configuration based on the
+ * contents of the FITS header, using the list of known cameras contained in the
+ * site configuration. If more than one camera matches the FITS header, a warning
+ * shall be generated and the first matching camera returned.
+ * 
+ */
+psMetadata *pmConfigCameraFromHeader(
+    const psMetadata *site,
+    const psMetadata *header
+);
+
+
+
+/** pmConfigRecipeFromCamera
+ * 
+ * pmConfigRecipeFromCamera shall load the recipe configuration based on the
+ * recipeName and the list of known recipes contained in the camera 
+ * configuration.
+ * 
+ */
+psMetadata *pmConfigRecipeFromCamera(
+    const psMetadata *camera,
+    const char *recipeName
+);
+
+#endif
Index: trunk/psModules/src/config/psAdditionals.c
===================================================================
--- trunk/psModules/src/config/psAdditionals.c	(revision 5355)
+++ trunk/psModules/src/config/psAdditionals.c	(revision 5355)
@@ -0,0 +1,486 @@
+#include <stdio.h>
+#include <string.h>
+#include "pslib.h"
+#include "psAdditionals.h"
+
+psMetadata *psMetadataLookupMD(bool *status, const psMetadata *md, const char *key)
+{
+    psMetadataItem *item = psMetadataLookup((psMetadata*)md, key); // The metadata with instruments
+    psMetadata *value = NULL;  // The value to return
+    if (!item) {
+        // The given key isn't in the metadata
+        if (status) {
+            *status = false;
+        } else {
+            psError(PS_ERR_IO, true, "Couldn't find %s in the metadata.\n");
+        }
+    } else if (item->type != PS_META_META) {
+        // The value at the key isn't metadata
+        if (status) {
+            *status = false;
+        } else {
+            psLogMsg(__func__, PS_LOG_WARN, "%s isn't of type PS_META_META, as expected.\n");
+        }
+        value = NULL;
+    } else {
+        // We have the requested metadata
+        if (status) {
+            *status = true;
+        }
+        value = item->data.md; // The requested metadata
+    }
+    return value;
+}
+
+
+char *psMetadataLookupString(bool *status, const psMetadata *md, const char *key)
+{
+    psMetadataItem *item = psMetadataLookup((psMetadata*)md, key); // The metadata with instruments
+    char *value = NULL;   // The value to return
+    if (!item) {
+        // The given key isn't in the metadata
+        if (status) {
+            *status = false;
+        } else {
+            psError(PS_ERR_IO, true, "Couldn't find %s in the metadata.\n");
+        }
+    } else if (item->type != PS_META_STR) {
+        // The value at the key isn't of the desired type
+        if (status) {
+            *status = false;
+        } else {
+            psLogMsg(__func__, PS_LOG_WARN, "%s isn't of type PS_META_STR, as expected.\n");
+        }
+        value = NULL;
+    } else {
+        // We have the requested metadata
+        if (status) {
+            *status = true;
+        }
+        value = item->data.V; // The requested metadata
+    }
+    return value;
+}
+
+
+void psMetadataPrint(psMetadata *md, int level)
+{
+    psMetadataIterator *iter = psMetadataIteratorAlloc(md, PS_LIST_HEAD, NULL); // Iterator
+    psMetadataItem *item = NULL; // Item from metadata
+    while (item = psMetadataGetAndIncrement(iter)) {
+        // Indent...
+        for (int i = 0; i < level; i++) {
+            printf(" ");
+        }
+        printf("%s", item->name);
+        if (item->comment && strlen(item->comment) > 0) {
+            printf(" (%s)", item->comment);
+        }
+        printf(": ");
+        switch (item->type) {
+        case PS_META_STR:
+            printf("%s", item->data.V);
+            break;
+        case PS_META_BOOL:
+            if (item->data.B) {
+                printf("True");
+            } else {
+                printf("False");
+            }
+            break;
+        case PS_META_S32:
+            printf("%d", item->data.S32);
+            break;
+        case PS_META_F32:
+            printf("%f", item->data.F32);
+            break;
+        case PS_META_F64:
+            printf("%f", item->data.F64);
+            break;
+        case PS_META_META:
+            printf("\n");
+            psMetadataPrint(item->data.V, level + 1);
+            break;
+        default:
+            printf("\n");
+            psError(PS_ERR_IO, false, "Non-printable metadata type: %x\n", item->type);
+        }
+        printf("\n");
+    }
+    psFree(iter);
+
+    return;
+}
+
+
+// Set verbosity level
+int psArgumentVerbosity(int *argc, char **argv)
+{
+    int logLevel = 2;   // Default log level
+    int argnum = 0;   // Argument number
+
+    // set in order, so that -vvv overrides -vv overrides -v
+    if (argnum = psArgumentGet(*argc, argv, "-v")) {
+        psArgumentRemove(argnum, argc, argv);
+        logLevel = 3;
+    }
+    if (argnum = psArgumentGet(*argc, argv, "-vv")) {
+        psArgumentRemove(argnum, argc, argv);
+        logLevel = 4;
+    }
+    if (argnum = psArgumentGet(*argc, argv, "-vvv")) {
+        psArgumentRemove(argnum, argc, argv);
+        logLevel = 5;
+    }
+    psLogSetLevel (logLevel);  // XXX: This function should return an error if the log level is invalid
+
+    if (argnum = psArgumentGet(*argc, argv, "-logfmt")) {
+        if (*argc < argnum + 2) {
+            psError(PS_ERR_IO, true, "-logfmt switch specified without a format.");
+        } else {
+            psArgumentRemove(argnum, argc, argv);
+            psLogSetFormat(argv[argnum]); // XXX EAM : this function should return an error if the log format is invalid
+            psArgumentRemove(argnum, argc, argv);
+        }
+    }
+
+    // Now the trace stuff
+    // argument format is: -trace (facil) (level)
+    while (argnum = psArgumentGet(*argc, argv, "-trace")) {
+        if (*argc < argnum + 3) {
+            psError(PS_ERR_IO, true, "-trace switch specified without facility and level.");
+        }
+        psArgumentRemove(argnum, argc, argv);
+        psTraceSetLevel(argv[argnum], atoi(argv[argnum+1])); // XXX: This function should return an error if the trace level is invalid
+        psArgumentRemove(argnum, argc, argv);
+        psArgumentRemove(argnum, argc, argv);
+    }
+    if ((argnum = psArgumentGet(*argc, argv, "-trace-levels"))) {
+        psTracePrintLevels();
+        exit(2);
+    }
+
+    return logLevel;
+}
+
+// Find the location of the specified argument
+int psArgumentGet(int argc, char **argv, const char *arg)
+{
+    for (int i = 1; i < argc; i++) {
+        if (!strcmp(argv[i], arg))
+            return i;
+    }
+
+    return 0;
+}
+
+// Remove the specified argument (by location)
+bool psArgumentRemove(int argnum, int *argc, char **argv)
+{
+    if (argnum > 0) {
+        (*argc)--;
+        for (int i = argnum; i < *argc; i++) {
+            argv[i] = argv[i+1];
+        }
+    } else {
+        return false;
+    }
+
+    return true;
+}
+
+
+static psMetadataItem *argumentRead(psMetadataItem *item, // Item to read into
+                                    int argnum, // Argument number
+                                    int *argc, // Number of arguments in total
+                                    char **argv // The arguments
+                                   )
+{
+    psMetadataItem *newItem = NULL;
+    switch(item->type) {
+        // Only doing a representative set of types
+    case PS_META_S32:
+        newItem = psMetadataItemAlloc(item->name, item->type, item->comment, atoi(argv[argnum]));
+        psArgumentRemove(argnum, argc, argv);
+        break;
+    case PS_META_F32:
+        newItem = psMetadataItemAlloc(item->name, item->type, item->comment, atof(argv[argnum]));
+        psArgumentRemove(argnum, argc, argv);
+        break;
+    case PS_META_BOOL:
+        // Turn option on; no optional argument to remove
+        newItem = psMetadataItemAlloc(item->name, item->type, item->comment, true);
+        break;
+        // XXX: Include the other numerical types
+    case PS_META_STR: {
+            //psString string = psStringCopy(argv[argnum]); // Get the argument into PS memory management
+            //psFree(string);
+            newItem = psMetadataItemAlloc(item->name, item->type, item->comment, argv[argnum]);
+            psArgumentRemove(argnum, argc, argv);
+        }
+        break;
+    default:
+        psError(PS_ERR_IO, true, "Argument type (%x) is not supported --- argument %s ignored\n",
+                item->type, item->name);
+        psFree(newItem);
+        return NULL;
+    }
+
+    return newItem;
+}
+
+
+// XXX: There is a memory leak in the MULTI section.  I think it might have something to do with reference
+// counting between lists and MD, in the second section of the code (copy newArgs into arguments), but I'm not
+// entirely sure.
+bool psArgumentParse(psMetadata *arguments, int *argc, char **argv)
+{
+    // We need to do a bit of mucking around in order to preserve the arguments metadata until the last
+    // minute --- if there is a bad argument, we need to return the old "arguments", since they contain
+    // the default values, which we probably want to output in a "help" message (we don't want to print
+    // the changed values and have the user think that they are default values).
+
+    psMetadata *newArgs = psMetadataAlloc(); // Place to read arguments into
+    psList *changed = psListAlloc(NULL);// List of keys that have changed
+
+    for (int i = 1; i < *argc; i++) {
+        psTrace(__func__, 7, "Looking at %s\n", argv[i]);
+        psMetadataItem *argItem = psMetadataLookup(arguments, argv[i]);
+        if (argItem) {
+            psArgumentRemove(i, argc, argv); // Remove the switch
+            if (argItem->type != PS_META_MULTI) {
+                if (argItem->type != PS_META_BOOL && *argc < i + 1) {
+                    psError(PS_ERR_IO, true, "Required argument for %s is missing.\n", argItem->name);
+                    // XXX: Cleanup before returning
+                    psFree(newArgs);
+                    return false;
+                }
+                psMetadataItem *newItem = argumentRead(argItem, i, argc, argv);
+                psMetadataAddItem(newArgs, newItem, PS_LIST_TAIL, PS_META_REPLACE);
+                psFree(newItem);
+            } else {
+                // Go through the MULTI
+                psList *multi = argItem->data.V; // The list of MULTI psMetadataItems
+                if (*argc < i + multi->size) {
+                    psError(PS_ERR_IO, true, "Not enough arguments for %s.\n", argItem->name);
+                    // Remove the arguments --- they will be ignored
+                    for (int j = i; i < *argc; i++) {
+                        psArgumentRemove(i, argc, argv);
+                    }
+                    // XXX: Cleanup before returning
+                    psFree(newArgs);
+                    return false;
+                }
+
+                // Remove any prior existence in the newArgs --- this is important because we specify
+                // adding the new items as DUPLICATE_OK, so if some idiot specifies it twice, we'd end
+                // up with two copies of everything.
+                psMetadataItem *checkItem = psMetadataLookup(newArgs, argItem->name);
+                if (checkItem) {
+                    (void)psMetadataRemove(newArgs, 0, argItem->name);
+                    (void)psListRemoveData(changed, argItem->name);
+                }
+
+                psListIterator *multiIter = psListIteratorAlloc(multi, PS_LIST_HEAD, true);
+                psMetadataItem *nextItem = NULL; // Item from list
+                while (nextItem = psListGetAndIncrement(multiIter)) {
+                    psMetadataItem *newItem = argumentRead(nextItem, i, argc, argv);
+                    psMetadataAddItem(newArgs, newItem, PS_LIST_TAIL, PS_META_DUPLICATE_OK);
+                    //psFree(newItem);
+                }
+                psFree(multiIter);
+            }
+
+            // Some book-keeping
+            //     psString name = psStringCopy(argItem->name);
+            psListAdd(changed, PS_LIST_TAIL, argItem->name);
+            i--;
+
+        } else if (strncmp(argv[i], "-", 1) == 0 || strncmp(argv[i], "+", 1) == 0) {
+            // Someone's specified a bad option
+            psError(PS_ERR_IO, true, "Unknown option: %s\n", argv[i]);
+            psFree(newArgs);
+            return false;
+        }
+    }
+
+    // All the arguments are good, so now we can copy the newArgs over
+    psListIterator *changedIter = psListIteratorAlloc(changed, PS_LIST_HEAD, false); // Iterator
+    psString name = NULL;  // Item from iteration
+    while (name = psListGetAndIncrement(changedIter)) {
+        printf("Updating %s\n", name);
+        psMetadataItem *oldItem = psMetadataLookup(arguments, name);
+        psMetadataItem *newItem = psMetadataLookup(newArgs, name);
+        if (oldItem->type != newItem->type) {
+            psAbort(__func__, "Shouldn't reach here!\n");
+        }
+        switch (oldItem->type) {
+            // Only doing a representative set of types
+        case PS_META_S32:
+            oldItem->data.S32 = newItem->data.S32;
+            break;
+        case PS_META_F32:
+            oldItem->data.F32 = newItem->data.F32;
+            break;
+        case PS_META_BOOL:
+            oldItem->data.B = newItem->data.B;
+            break;
+            // XXX: Include the other numerical types
+        case PS_META_STR:
+            psFree(oldItem->data.V);
+            oldItem->data.V = psMemIncrRefCounter(newItem->data.V);
+            break;
+        case PS_META_MULTI: {
+                psList *newMulti = psMemIncrRefCounter(newItem->data.V); // The new list of MULTI
+                psList *oldMulti = oldItem->data.V; // The old list of MULTI
+                psListIterator *newMultiIter = psListIteratorAlloc(newMulti, PS_LIST_HEAD, false);
+                psListIterator *oldMultiIter = psListIteratorAlloc(oldMulti, PS_LIST_HEAD, true);
+                psMetadataItem *newMultiItem = NULL; // Item from iterator
+                while (newMultiItem = psListGetAndIncrement(newMultiIter)) {
+                    psMetadataItem *oldMultiItem = psListGetAndIncrement(oldMultiIter);
+                    if (! oldMultiItem) {
+                        psAbort(__func__,
+                                "Something went very wrong here!  The lists SHOULD be of the same length!\n");
+                    }
+                    switch (oldMultiItem->type) {
+                        // Only doing a representative set of types
+                    case PS_META_S32:
+                        oldItem->data.S32 = newItem->data.S32;
+                        break;
+                    case PS_META_F32:
+                        oldItem->data.F32 = newItem->data.F32;
+                        break;
+                        // XXX: Include the other numerical types
+                    case PS_META_STR:
+                        psFree(oldItem->data.V);
+                        oldItem->data.V = psMemIncrRefCounter(newItem->data.V);
+                        break;
+                    default:
+                        psAbort(__func__, "Should never ever get here, ever.\n");
+                    }
+                    psFree(oldMultiItem);
+                    psFree(newMultiItem);
+                }
+                psFree(newMultiIter);
+                psFree(oldMultiIter);
+            }
+            break;
+        default:
+            psAbort(__func__, "Should never ever ever get here.\n");
+        }
+    }
+    psFree(changedIter);
+    psFree(changed);
+
+    // Now, blow away the newArgs and we're done.
+    psFree(newArgs);
+    return true;
+}
+
+
+static int argLength(psMetadataItem *arg)
+{
+    switch (arg->type) {
+        // Only doing a representative set of types
+    case PS_META_S32:
+        return arg->data.S32 >= 0 ? (int)log10f((float)arg->data.S32) + 1 :
+               (int)log10f(-(float)arg->data.S32) + 2;
+        // XXX: Other numerical types
+    case PS_META_F32:
+        return arg->data.F32 >= 0 ? 12 : 13; // -d.dddddde?dd
+    case PS_META_F64:
+        return arg->data.F64 >= 0 ? 12 : 13; // -d.dddddde?dd
+    case PS_META_BOOL:
+        return arg->data.B ? 4 : 5;
+    case PS_META_STR:
+        return strlen(arg->data.V);
+    default:
+        psAbort(__func__, "Argument type (%x) is not supported.\n", arg->type);
+    }
+
+    return 0;
+}
+
+#define NUM_SPACES 4   // Number of spaces between
+
+void psArgumentHelp(psMetadata *arguments)
+{
+    printf("Optional arguments, with default values:\n");
+    psMetadataIterator *argIter = psMetadataIteratorAlloc(arguments, PS_LIST_HEAD, NULL);
+    psMetadataItem *argItem = NULL; // Item from iterator
+    int maxName = 4;   // Maximum length of a name
+    int maxValue = 4;   // Maximum length of a value
+
+    // First pass to get the sizes
+    while (argItem = psMetadataGetAndIncrement(argIter)) {
+        if (strlen(argItem->name) > maxName) {
+            maxName = strlen(argItem->name);
+        }
+        int valLength = argLength(argItem);
+        if (valLength > maxValue) {
+            maxValue = valLength;
+        }
+    }
+
+    // Second pass to print
+    psMetadataIteratorSet(argIter, PS_LIST_HEAD);
+    psString lastName = NULL;  // Last name we printed
+    while (argItem = psMetadataGetAndIncrement(argIter)) {
+        // Initial indent
+        for (int i = 0; i < NUM_SPACES; i++) {
+            printf(" ");
+        }
+
+        // Print the name if required
+        int position = 0; // Number of spaces in
+        if (! lastName || strcmp(lastName, argItem->name) != 0) {
+            // A new name
+            printf("%s", argItem->name);
+            position += strlen(argItem->name);
+            lastName = argItem->name;
+        }
+        for (int i = position; i < maxName + NUM_SPACES; i++) {
+            printf(" ");
+        }
+
+        // Print the value
+        printf("(");
+        switch (argItem->type) {
+            // Only doing a representative set of types
+        case PS_META_S32:
+            printf("%d", argItem->data.S32);
+            break;
+            // XXX: Other numerical types
+        case PS_META_F32:
+            printf("%.6e", argItem->data.F32);
+            break;
+        case PS_META_F64:
+            printf("%.6e", argItem->data.F64);
+            break;
+        case PS_META_BOOL:
+            if (argItem->data.B) {
+                printf("TRUE");
+            } else {
+                printf("FALSE");
+            }
+            break;
+        case PS_META_STR:
+            printf("%s", argItem->data.V);
+            break;
+        default:
+            psAbort(__func__, "Argument type (%x) is not supported.\n", argItem->type);
+        }
+        printf(")");
+        for (int i = argLength(argItem); i < maxValue + NUM_SPACES; i++) {
+            printf(" ");
+        }
+
+        // Print the comment
+        if (argItem->comment) {
+            printf("%s", argItem->comment);
+        }
+        printf("\n");
+    }
+
+    psFree(argIter);
+}
Index: trunk/psModules/src/config/psAdditionals.h
===================================================================
--- trunk/psModules/src/config/psAdditionals.h	(revision 5355)
+++ trunk/psModules/src/config/psAdditionals.h	(revision 5355)
@@ -0,0 +1,27 @@
+#ifndef PS_ADDITIONALS_H
+#define PS_ADDITIONALS_H
+
+#include "pslib.h"
+
+// Get a value from the metadata that we believe should be metadata.
+psMetadata *psMetadataLookupMD(bool *status, const psMetadata *md, const char *key);
+
+// Get a value from the metadata that we believe should be a string
+char *psMetadataLookupString(bool *status, const psMetadata *md, const char *key);
+
+#if 0
+pmChip *psMetadataLookupChip(bool *status, const psMetadata *md, const char *key);
+pmCell *psMetadataLookupCell(bool *status, const psMetadata *md, const char *key);
+#endif
+
+// Print out the metadata
+void psMetadataPrint(psMetadata *md, int level);
+
+// Argument handling
+int psArgumentVerbosity(int *argc, char **argv);
+int psArgumentGet(int argc, char **argv, const char *arg);
+bool psArgumentRemove(int argnum, int *argc, char **argv);
+bool psArgumentParse(psMetadata *arguments, int *argc, char **argv);
+void psArgumentHelp(psMetadata *arguments);
+
+#endif
