Index: /trunk/psModules/src/config/Makefile.am
===================================================================
--- /trunk/psModules/src/config/Makefile.am	(revision 10427)
+++ /trunk/psModules/src/config/Makefile.am	(revision 10428)
@@ -5,8 +5,10 @@
 libpsmodulesconfig_la_SOURCES  = \
     pmConfig.c \
+    pmConfigRecipes.c \
     pmVersion.c
 
 pkginclude_HEADERS = \
     pmConfig.h \
+    pmConfigRecipes.h \
     pmVersion.h
 
Index: /trunk/psModules/src/config/pmConfig.c
===================================================================
--- /trunk/psModules/src/config/pmConfig.c	(revision 10427)
+++ /trunk/psModules/src/config/pmConfig.c	(revision 10428)
@@ -4,6 +4,6 @@
  *  @author EAM (IfA)
  *
- *  @version $Revision: 1.56 $ $Name: not supported by cvs2svn $
- *  @date $Date: 2006-12-04 02:14:09 $
+ *  @version $Revision: 1.57 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2006-12-04 20:41:51 $
  *
  *  Copyright 2004 Maui High Performance Computing Center, University of Hawaii
@@ -25,4 +25,5 @@
 #include <pslib.h>
 #include "pmConfig.h"
+#include "pmConfigRecipes.h"
 
 #define PS_SITE "PS_SITE"         // Name of the environment variable containing the site config file
@@ -38,5 +39,5 @@
     psFree(config->cameraName);
     psFree(config->recipes);
-    psFree(config->recipesSource);
+    psFree(config->recipeSymbols);
     psFree(config->arguments);
     psFree(config->database);
@@ -55,5 +56,5 @@
     config->recipes = psMetadataAlloc();
     config->recipesRead = PM_RECIPE_SOURCE_NONE;
-    config->recipesSource = psMetadataAlloc();
+    config->recipeSymbols = psMetadataAlloc();
     config->arguments = psMetadataAlloc();
     config->database = NULL;
@@ -660,181 +661,4 @@
 }
 
-
-// Load the recipes: each time we load a specific recipe, it overrides the metadata
-// entries for an existing recipe metadata
-static bool loadRecipes(pmConfig *config, // The configuration into which to read the recipes
-                        psMetadata *source, // The source configuration, from which to read the filenames
-                        pmRecipeSource sourceType, // The source type
-                        const char *sourceName // The name of the source, for error messages
-                       )
-{
-    assert(config);
-
-    if (!source) {
-        psTrace("psModules.pmConfig", 4, "The %s has not been read --- cannot read recipes from this "
-                "location.\n", sourceName);
-        config->recipesRead &= ~sourceType;
-        return false;
-    }
-
-    bool mdok = true;                   // Status of MD lookup
-    psMetadata *recipes = psMetadataLookupMetadata(&mdok, source, "RECIPES"); // The list of recipes
-    if (!mdok || !recipes) {
-        psLogMsg("psModules.pmConfig", PS_LOG_WARN, "RECIPES in the %s is not of type METADATA --- ignored\n",
-                 sourceName);
-        config->recipesRead &= ~sourceType;
-        return false;
-    }
-
-    // for sourceType == SITE | CAMERA, RECIPES contains a list of files to be read (pmConfigFileRead)
-    // for sourceType == CL, RECIPES contains a list of metadata already loaded
-
-    // Copy the filenames to the target from the "RECIPES" in the source.
-    // We could use psMetadataCopy for this, but it's better to check that everything's of the correct type.
-    // If it's not of the correct type, we can tell the user which file it's in, so they can find it easier.
-    {
-        psMetadataIterator *recipesIter = psMetadataIteratorAlloc(recipes, PS_LIST_HEAD, NULL); // Iterator
-        psMetadataItem *fileItem = NULL;    // MD item containing the filename, from recipe iteration
-        while ((fileItem = psMetadataGetAndIncrement(recipesIter)))
-        {
-            psMetadata *recipe = NULL;
-            psMetadata *current = NULL;
-
-            switch (sourceType) {
-            case PM_RECIPE_SOURCE_SITE:
-            case PM_RECIPE_SOURCE_CAMERA:
-                // type mismatch is a serious error
-                if (fileItem->type != PS_DATA_STRING) {
-                    psAbort ("pmConfig", "%s in %s RECIPES is not of type STR", fileItem->name, sourceName);
-                }
-
-                // Check to see if it's currently defined
-                // XXX EAM : I think this check is now not needed?
-                int check = psMetadataLookupS32(&mdok, config->recipesSource, fileItem->name);
-                if (mdok && check > sourceType) {
-                    // It's already defined with a higher priority
-                    continue;
-                }
-                psString comment = psStringCopy("Recipe added at ");
-                psStringAppend(&comment, "%s from %s", sourceName, (char*)fileItem->data.V);
-                psTrace ("psModules.pmConfig", 3, comment);
-                psMetadataAddS32(config->recipesSource, PS_LIST_TAIL, fileItem->name,
-                                 PS_META_REPLACE, comment, sourceType);
-                psFree(comment);
-
-                // Read the recipe file.
-                if (!pmConfigFileRead(&recipe, fileItem->data.V, "recipe")) {
-                    psError(PS_ERR_IO, false, "Failed to read recipe");
-                    psFree(recipe);  // Drop reference
-                    psFree(recipesIter);
-                    return false;
-                }
-                break;
-
-            case PM_RECIPE_SOURCE_CL:
-                // type mismatch is a serious error
-                if (fileItem->type != PS_DATA_METADATA) {
-                    psAbort ("pmConfig", "%s in %s RECIPES is not of type METADATA", fileItem->name, sourceName);
-                }
-                // increment the ref counter to protect the data
-                recipe = psMemIncrRefCounter (fileItem->data.V);
-                break;
-
-            default:
-                psAbort ("pmConfig", "unknown sourceType");
-            }
-
-            // if this named recipe exists, supplement it
-            current = psMetadataLookupMetadata (NULL, config->recipes, fileItem->name);
-            if (current) {
-                psMemIncrRefCounter (current);
-            }
-            current = psMetadataCopy (current, recipe);
-            psMetadataAdd(config->recipes, PS_LIST_TAIL, fileItem->name, PS_DATA_METADATA | PS_META_REPLACE, fileItem->comment, current);
-            psFree(recipe);  // Drop reference
-            psFree(current);  // Drop reference
-        }
-        psFree(recipesIter);
-    }
-    config->recipesRead |= sourceType;
-
-    {
-        // Having read that, we now need to check to see if any symbolic links need to be resolved
-        psMetadataIterator *recipesIter = psMetadataIteratorAlloc(config->recipesSource, PS_LIST_HEAD, NULL);
-        psMetadataItem *sourceItem = NULL;  // Item containing source, from iteration
-        while ((sourceItem = psMetadataGetAndIncrement(recipesIter))) {
-            assert(sourceItem->type == PS_TYPE_S32); // It should be this type: we put it in ourselves
-            if (sourceItem->data.S32 == PM_RECIPE_SOURCE_SYMBOLIC) {
-                const char *linkName = sourceItem->comment; // The name of the link
-                psMetadata *linkSource = psMetadataLookupMetadata(&mdok, config->recipes, linkName); // The source
-                if (!mdok || !linkSource) {
-                    // Can't yet resolve it
-                    continue;
-                }
-                psString comment = NULL;
-                psStringAppend(&comment, "Symbolic link from %s", linkName);
-                psMetadataAdd(config->recipes, PS_LIST_TAIL, sourceItem->name,
-                              PS_DATA_METADATA | PS_META_REPLACE, comment, linkSource);
-                psFree(comment);
-            }
-        }
-        psFree(recipesIter);
-    }
-    return true;
-}
-
-// this function may be called several times.  it attempts to load the recipe data from one of
-// three locations: config->site, config->camera, and config->argv we cannot read the recipes
-// from config->camera until a camera has been read BUT, the argv recipes must override the
-// camera and site recipes.
-bool pmConfigReadRecipes(pmConfig *config, pmRecipeSource source)
-{
-    PS_ASSERT_PTR_NON_NULL(config, false);
-
-    if (!config->recipes) {
-        config->recipes = psMetadataAlloc();
-    }
-
-    // Read the recipe file names from the site configuration and camera configuration
-    // XXX EAM : I think it should be an error for config->site:recipes not to exist
-    // for now, keep this as a warning
-    if (config->site && (source & PM_RECIPE_SOURCE_SITE)) {
-        if (!loadRecipes(config, config->site, PM_RECIPE_SOURCE_SITE, "site configuration")) {
-            psLogMsg ("psModules.pmConfig", PS_LOG_WARN, "Failed to read recipes from site config");
-        } else {
-            psTrace ("psModules.pmConfig", 3, "read recipes from site config");
-        }
-    }
-
-    // camera-specific recipes are not required : note the absence with a message
-    // camera-specific recipes may be read for a specified camera (in pmConfigRead) or
-    // for an identified camera (in pmConfigCameraFormatFromHeader).  the second
-    // set should not override the first set
-    if (config->camera && (source & PM_RECIPE_SOURCE_CAMERA) && !(config->recipesRead & PM_RECIPE_SOURCE_CAMERA)) {
-        if (!loadRecipes(config, config->camera, PM_RECIPE_SOURCE_CAMERA, "camera configuration")) {
-            psLogMsg ("psModules.pmConfig", PS_LOG_DETAIL, "no recipe supplied by camera config");
-        } else {
-            psTrace ("psModules.pmConfig", 3, "read recipes from camera config");
-        }
-    }
-
-    // apply recipes loaded into config->arguments based on command-line arguments
-    if (config->arguments && (source & PM_RECIPE_SOURCE_CL)) {
-        if (!loadRecipes(config, config->arguments, PM_RECIPE_SOURCE_CL, "command-line arguments")) {
-            psLogMsg ("psModules.pmConfig", PS_LOG_DETAIL, "no recipe supplied on command-line arguments");
-        } else {
-            psTrace ("psModules.pmConfig", 3, "read recipes from command-line arguments");
-        }
-    }
-    /*
-     * We may have seen real errors, but we also get false status returned by valid conditions (e.g. asking a
-     * file for a recipe when none was provided).  For now we'll never signal an error, but this should
-     * be reconsidered ASAP.
-     */
-    psErrorClear();
-    return true;
-}
-
-
 psDB *pmConfigDB(pmConfig *config)
 {
@@ -1057,166 +881,2 @@
     return newName;
 }
-
-// search for options of the form -D KEY VALUE or -D RECIPE:KEY VALUE
-bool pmConfigLoadRecipeOptions (pmConfig *config, char *flag)
-{
-
-    int argNum;
-
-    // save the recipes onto config->arguments:RECIPES
-    psMetadata *recipes = psMetadataLookupMetadata(NULL, config->arguments, "RECIPES");
-    if (!recipes) {
-        recipes = psMetadataAlloc ();
-        psMetadataAddPtr (config->arguments, PS_LIST_TAIL, "RECIPES",  PS_DATA_METADATA, "", recipes);
-    } else {
-        psMemIncrRefCounter (recipes); // so we can free options below if not allocated here
-    }
-
-    // -D key value (all added as string)
-    while ((argNum = psArgumentGet (*config->argc, config->argv, flag))) {
-        psArgumentRemove (argNum, config->argc, config->argv);
-
-        // do we have enough arguments?
-        if (argNum + 1 >= *config->argc) {
-            psError(PS_ERR_IO, true, "insufficient parameters for command-line argument -D");
-            return false;
-        }
-
-        // is a target recipe specified?
-        char *recipeName = NULL;
-        char *key;
-        psArray *words = psStringSplitArray(config->argv[argNum], ":", false);
-        switch (words->n) {
-        case 1:
-            recipeName = config->defaultRecipe;
-            if (!config->defaultRecipe) {
-                psError(PS_ERR_IO, true, "syntax error in parameter: no default recipe available; must specify recipe");
-                return false;
-            }
-            key = words->data[0];
-            break;
-        case 2:
-            recipeName = words->data[0];
-            key = words->data[1];
-            break;
-        default:
-            psError(PS_ERR_IO, true, "syntax error in parameter");
-            return false;
-        }
-
-        // if this recipe is already defined in recipes, supplement
-        psMetadata *recipe = psMetadataLookupMetadata(NULL, recipes, recipeName);
-        if (!recipe) {
-            recipe = psMetadataAlloc ();
-            psMetadataAddPtr (recipes, PS_LIST_TAIL, recipeName,  PS_DATA_METADATA, "", recipe);
-        } else {
-            psMemIncrRefCounter (recipe); // so we can free recipe below if not allocated here
-        }
-
-        bool valid = false;
-        if (!strcmp (flag, "-D")) {
-            psMetadataAddStr (recipe, PS_LIST_TAIL, key, PS_META_REPLACE, "", config->argv[argNum+1]);
-            valid = true;
-        }
-        if (!strcmp (flag, "-Di")) {
-            psMetadataAddS32 (recipe, PS_LIST_TAIL, key, PS_META_REPLACE, "", atoi(config->argv[argNum+1]));
-            valid = true;
-        }
-        if (!strcmp (flag, "-Df")) {
-            psMetadataAddF32 (recipe, PS_LIST_TAIL, key, PS_META_REPLACE, "", atof(config->argv[argNum+1]));
-            valid = true;
-        }
-        if (!strcmp (flag, "-Db")) {
-            if (!strcasecmp (config->argv[argNum+1], "true")) {
-                psMetadataAddBool (recipe, PS_LIST_TAIL, key, PS_META_REPLACE, "", true);
-            } else {
-                psMetadataAddBool (recipe, PS_LIST_TAIL, key, PS_META_REPLACE, "", false);
-            }
-            valid = true;
-        }
-        psFree (words);
-        psFree (recipe);
-        assert (valid);  // flag may be: -D, -Df, -Di, -Db
-
-        psArgumentRemove (argNum, config->argc, config->argv);
-        psArgumentRemove (argNum, config->argc, config->argv);
-    }
-    psFree (recipes);
-    return true;
-}
-
-// examine command-line arguments for -recipe KEY VALUE
-// if VALUE is an existing file, read as metadata and save on config->arguments with name = KEY
-// if VALUE is not an exiting file, it is a symbolic lookup.  ???
-bool pmConfigLoadRecipeArguments (pmConfig *config)
-{
-
-    // Go through the command-line arguments
-    int argNum; // Argument number for "-recipe"
-    while ((argNum = psArgumentGet(*config->argc, config->argv, "-recipe")) > 0) {
-        psArgumentRemove(argNum, config->argc, config->argv);
-        if (argNum + 1 >= *config->argc) {
-            psError(PS_ERR_IO, false, "-recipe command-line switch provided without the required recipe and source\n");
-            return false;
-        }
-
-        const char *recipeName = psStringCopy(config->argv[argNum]); // Name of the recipe
-        psArgumentRemove(argNum, config->argc, config->argv);
-        const char *recipeSource = psStringCopy(config->argv[argNum]); // Source of the recipe
-        psArgumentRemove(argNum, config->argc, config->argv);
-
-        if (access(recipeSource, R_OK) == 0) {
-            // The source is a file: load onto config->arguments
-            // save the recipes onto config->arguments:RECIPES
-            psMetadata *recipes = psMetadataLookupMetadata(NULL, config->arguments, "RECIPES");
-            if (!recipes) {
-                recipes = psMetadataAlloc ();
-                psMetadataAddPtr (config->arguments, PS_LIST_TAIL, "RECIPES",  PS_DATA_METADATA, "", recipes);
-            } else {
-                psMemIncrRefCounter (recipes); // so we can free options below if not allocated here
-            }
-
-            psMetadata *recipe = NULL;      // Recipe from file
-            if (pmConfigFileRead(&recipe, recipeSource, "recipe")) {
-                psString comment = psStringCopy("Recipe added at command line from ");
-                psStringAppend(&comment, "%s", recipeSource);
-                psMetadataAdd(recipes, PS_LIST_TAIL, recipeName, PS_DATA_METADATA | PS_META_REPLACE, comment, recipe);
-                psFree(comment);
-                psFree(recipe);                 // Drop reference
-            } else {
-                psAbort ("pmConfig.c", "error reading config file %s\n", recipeSource);
-            }
-            psFree (recipes);
-        } else {
-            // Assume it's a symbolic reference to something that's not yet read in
-            psMetadataAddS32(config->recipesSource, PS_LIST_TAIL, recipeName, PS_META_REPLACE,
-                             recipeSource, PM_RECIPE_SOURCE_SYMBOLIC);
-        }
-        psTrace ("psModules.pmConfig", 3, "read recipe %s from %s", recipeName, recipeSource);
-        psFree(recipeName);
-        psFree(recipeSource);
-    } // Iterating through the command-line arguments
-
-    return true;
-}
-
-# if (0)
-
-    // Is the source a symbolic reference?
-    // XXX EAM : need to be careful about hierachy and overrides
-    psMetadata *extant = psMetadataLookupMetadata(&mdok, config->recipes, recipeSource); // Does it exist?
-if (mdok && extant)
-{
-    psString comment = psStringCopy("Recipe added from command line as symbolic link to ");
-    psStringAppend(&comment, "%s", recipeSource);
-    psMetadataAdd(config->recipes, PS_LIST_TAIL, recipeName, PS_DATA_METADATA | PS_META_REPLACE,
-                  comment, extant);
-    psFree(comment);
-    // Put the source in the comment, so we can retrieve it again if we need it
-    psMetadataAddS32(config->recipesSource, PS_LIST_TAIL, recipeName, PS_META_REPLACE,
-                     recipeSource, PM_RECIPE_SOURCE_SYMBOLIC);
-
-} else
-
-    # endif
-
Index: /trunk/psModules/src/config/pmConfig.h
===================================================================
--- /trunk/psModules/src/config/pmConfig.h	(revision 10427)
+++ /trunk/psModules/src/config/pmConfig.h	(revision 10428)
@@ -9,6 +9,6 @@
 /// @author Eugene Magnier, IfA
 ///
-/// @version $Revision: 1.20 $ $Name: not supported by cvs2svn $
-/// @date $Date: 2006-12-04 02:14:09 $
+/// @version $Revision: 1.21 $ $Name: not supported by cvs2svn $
+/// @date $Date: 2006-12-04 20:41:51 $
 ///
 /// Copyright 2005-2006 Institute for Astronomy, University of Hawaii
@@ -17,6 +17,4 @@
 #ifndef PM_CONFIG_H
 #define PM_CONFIG_H
-
-#include <pslib.h>
 
 /// Sources for recipes.
@@ -54,6 +52,6 @@
     char *defaultRecipe;  ///< name of top-level recipe for this program
     // Private members
-    pmRecipeSource recipesRead;       ///< Which recipe sources have been read
-    psMetadata *recipesSource;          ///< Where each recipe came from
+    pmRecipeSource recipesRead;  ///< Which recipe sources have been read
+    psMetadata *recipeSymbols;          ///< Where each recipe came from
 }
 pmConfig;
@@ -120,12 +118,4 @@
                                 );
 
-/// Read recipes
-///
-/// Attempt to read recipes from the sources that are available but have not already been read.  Having read a
-/// recipe, attempt to resolve symbolic links that were specified on the command line.
-bool pmConfigReadRecipes(pmConfig *config, ///< Configuration
-                         pmRecipeSource source ///< desired sources for recipes
-                        );
-
 /// Setup the database
 ///
@@ -167,7 +157,3 @@
 psString pmConfigConvertFilename (char *filename, pmConfig *config);
 
-
-bool pmConfigLoadRecipeArguments (pmConfig *config);
-bool pmConfigLoadRecipeOptions (pmConfig *config, char *flag);
-
 #endif
Index: /trunk/psModules/src/config/pmConfigRecipes.c
===================================================================
--- /trunk/psModules/src/config/pmConfigRecipes.c	(revision 10428)
+++ /trunk/psModules/src/config/pmConfigRecipes.c	(revision 10428)
@@ -0,0 +1,435 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <libgen.h>
+#include <assert.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <glob.h>
+#include <pslib.h>
+#include "pmConfig.h"
+#include "pmConfigRecipes.h"
+
+static bool loadRecipeFiles(pmConfig *config, psMetadata *source, pmRecipeSource type, const char *name);
+static bool loadRecipeFromArguments(pmConfig *config);
+static bool loadRecipeSymbols(pmConfig *config);
+static bool loadRecipeOptions(pmConfig *config);
+
+// this function may be called several times.  it attempts to load the recipe data from one of
+// three locations: config->site, config->camera, and config->argv we cannot read the recipes
+// from config->camera until a camera has been read BUT, the argv recipes must override the
+// camera and site recipes.
+bool pmConfigReadRecipes(pmConfig *config, pmRecipeSource source)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    if (!config->recipes) {
+        config->recipes = psMetadataAlloc();
+    }
+
+    // Read the recipe file names from the site configuration and camera configuration
+    // XXX EAM : I think it should be an error for config->site:recipes not to exist
+    // for now, keep this as a warning
+    if (config->site && (source & PM_RECIPE_SOURCE_SITE)) {
+        if (!loadRecipeFiles(config, config->site, PM_RECIPE_SOURCE_SITE, "site configuration")) {
+            psLogMsg ("psModules.pmConfig", PS_LOG_WARN, "Failed to read recipes from site config");
+        } else {
+            psTrace ("psModules.pmConfig", 3, "read recipes from site config");
+        }
+    }
+
+    // camera-specific recipes are not required : note the absence with a message
+    // camera-specific recipes may be read for a specified camera (in pmConfigRead) or
+    // for an identified camera (in pmConfigCameraFormatFromHeader).  the second
+    // set should not override the first set
+    if (config->camera && (source & PM_RECIPE_SOURCE_CAMERA) && !(config->recipesRead & PM_RECIPE_SOURCE_CAMERA)) {
+        if (!loadRecipeFiles(config, config->camera, PM_RECIPE_SOURCE_CAMERA, "camera configuration")) {
+            psLogMsg ("psModules.pmConfig", PS_LOG_DETAIL, "no recipe supplied by camera config");
+        } else {
+            psTrace ("psModules.pmConfig", 3, "read recipes from camera config");
+        }
+    }
+
+    // apply recipes loaded into config->arguments based on command-line arguments
+    if (config->arguments && (source & PM_RECIPE_SOURCE_CL)) {
+        if (!loadRecipeFromArguments(config)) {
+            psLogMsg ("psModules.pmConfig", PS_LOG_DETAIL, "no recipe supplied on command-line arguments");
+        } else {
+            psTrace ("psModules.pmConfig", 3, "read recipes from command-line arguments");
+        }
+        if (!loadRecipeSymbols(config)) {
+            psLogMsg ("psModules.pmConfig", PS_LOG_DETAIL, "no recipe supplied on command-line arguments");
+        } else {
+            psTrace ("psModules.pmConfig", 3, "read recipes from command-line arguments");
+        }
+        if (!loadRecipeOptions(config)) {
+            psLogMsg ("psModules.pmConfig", PS_LOG_DETAIL, "no recipe supplied on command-line arguments");
+        } else {
+            psTrace ("psModules.pmConfig", 3, "read recipes from command-line arguments");
+        }
+    }
+    /*
+     * We may have seen real errors, but we also get false status returned by valid conditions (e.g. asking a
+     * file for a recipe when none was provided).  For now we'll never signal an error, but this should
+     * be reconsidered ASAP.
+     */
+    psErrorClear();
+    return true;
+}
+
+// search for options of the form -D KEY VALUE or -D RECIPE:KEY VALUE
+bool pmConfigLoadRecipeOptions (pmConfig *config, char *flag)
+{
+
+    int argNum;
+
+    // save the recipes onto config->arguments:RECIPES
+    // increment so we can free below (is a NOP is options is NULL)
+    psMetadata *options = psMemIncrRefCounter (psMetadataLookupMetadata(NULL, config->arguments, "OPTIONS"));
+    if (!options) {
+        options = psMetadataAlloc ();
+        psMetadataAddPtr (config->arguments, PS_LIST_TAIL, "OPTIONS",  PS_DATA_METADATA, "", options);
+    }
+
+    // -D key value (all added as string)
+    while ((argNum = psArgumentGet (*config->argc, config->argv, flag))) {
+        psArgumentRemove (argNum, config->argc, config->argv);
+
+        // do we have enough arguments?
+        if (argNum + 1 >= *config->argc) {
+            psError(PS_ERR_IO, true, "insufficient parameters for command-line argument -D");
+            return false;
+        }
+
+        // is a target recipe specified?
+        char *recipeName = NULL;
+        char *key;
+        psArray *words = psStringSplitArray(config->argv[argNum], ":", false);
+        switch (words->n) {
+        case 1:
+            recipeName = config->defaultRecipe;
+            if (!config->defaultRecipe) {
+                psError(PS_ERR_IO, true, "syntax error in parameter: no default recipe available; must specify recipe");
+                return false;
+            }
+            key = words->data[0];
+            break;
+        case 2:
+            recipeName = words->data[0];
+            key = words->data[1];
+            break;
+        default:
+            psError(PS_ERR_IO, true, "syntax error in parameter");
+            return false;
+        }
+
+        // if this recipe is already defined in options, supplement
+        psMetadata *recipe = psMemIncrRefCounter (psMetadataLookupMetadata(NULL, options, recipeName));
+        if (!recipe) {
+            recipe = psMetadataAlloc ();
+            psMetadataAddPtr (options, PS_LIST_TAIL, recipeName,  PS_DATA_METADATA, "", recipe);
+        }
+
+        bool valid = false;
+        if (!strcmp (flag, "-D")) {
+            psMetadataAddStr (recipe, PS_LIST_TAIL, key, PS_META_REPLACE, "", config->argv[argNum+1]);
+            valid = true;
+        }
+        if (!strcmp (flag, "-Di")) {
+            psMetadataAddS32 (recipe, PS_LIST_TAIL, key, PS_META_REPLACE, "", atoi(config->argv[argNum+1]));
+            valid = true;
+        }
+        if (!strcmp (flag, "-Df")) {
+            psMetadataAddF32 (recipe, PS_LIST_TAIL, key, PS_META_REPLACE, "", atof(config->argv[argNum+1]));
+            valid = true;
+        }
+        if (!strcmp (flag, "-Db")) {
+            if (!strcasecmp (config->argv[argNum+1], "true")) {
+                psMetadataAddBool (recipe, PS_LIST_TAIL, key, PS_META_REPLACE, "", true);
+            } else {
+                psMetadataAddBool (recipe, PS_LIST_TAIL, key, PS_META_REPLACE, "", false);
+            }
+            valid = true;
+        }
+        psFree (words);
+        psFree (recipe);
+        assert (valid);  // flag may be: -D, -Df, -Di, -Db
+
+        psArgumentRemove (argNum, config->argc, config->argv);
+        psArgumentRemove (argNum, config->argc, config->argv);
+    }
+    psFree (options);
+    return true;
+}
+
+// examine command-line arguments for -recipe KEY VALUE
+// if VALUE is an existing file, read as metadata and save on config->arguments with name = KEY
+// if VALUE is not an exiting file, it is a symbolic lookup: it is saved on config->recipeSymbols
+// for later interpolation (pmConfigReadRecipes with option CL)
+bool pmConfigLoadRecipeArguments (pmConfig *config)
+{
+
+    // Go through the command-line arguments
+    int argNum; // Argument number for "-recipe"
+    while ((argNum = psArgumentGet(*config->argc, config->argv, "-recipe")) > 0) {
+        psArgumentRemove(argNum, config->argc, config->argv);
+        if (argNum + 1 >= *config->argc) {
+            psError(PS_ERR_IO, false, "-recipe command-line switch provided without the required recipe and source\n");
+            return false;
+        }
+
+        const char *recipeName = psStringCopy(config->argv[argNum]); // Name of the recipe
+        psArgumentRemove(argNum, config->argc, config->argv);
+        const char *recipeSource = psStringCopy(config->argv[argNum]); // Source of the recipe
+        psArgumentRemove(argNum, config->argc, config->argv);
+
+        if (access(recipeSource, R_OK) == 0) {
+            // The source is a file: load onto config->arguments
+            // save the recipes onto config->arguments:RECIPES
+            psMetadata *recipes = psMemIncrRefCounter (psMetadataLookupMetadata(NULL, config->arguments, "RECIPES"));
+            if (!recipes) {
+                recipes = psMetadataAlloc ();
+                psMetadataAddPtr (config->arguments, PS_LIST_TAIL, "RECIPES",  PS_DATA_METADATA, "", recipes);
+            }
+
+            psMetadata *recipe = NULL;      // Recipe from file
+            if (pmConfigFileRead(&recipe, recipeSource, "recipe")) {
+                psString comment = psStringCopy("Recipe added at command line from ");
+                psStringAppend(&comment, "%s", recipeSource);
+                psMetadataAdd(recipes, PS_LIST_TAIL, recipeName, PS_DATA_METADATA | PS_META_REPLACE, comment, recipe);
+                psFree(comment);
+                psFree(recipe);                 // Drop reference
+            } else {
+                psAbort ("pmConfig.c", "error reading config file %s\n", recipeSource);
+            }
+            psFree (recipes);
+        } else {
+            // Assume it's a symbolic reference to something that's not yet read in.
+            // it will be loaded later by pmConfigReadRecipes with option CL
+            psMetadataAddStr(config->recipeSymbols, PS_LIST_TAIL, recipeName, PS_META_REPLACE, "", recipeSource);
+        }
+        psTrace ("psModules.pmConfig", 3, "read recipe %s from %s", recipeName, recipeSource);
+        psFree(recipeName);
+        psFree(recipeSource);
+    } // Iterating through the command-line arguments
+
+    return true;
+}
+
+// Load the recipe files (valid for SITE | CAMERA)
+// each time we load a specific recipe, it overrides the existing metadata for that recipe
+// for sourceType == SITE | CAMERA, RECIPES contains a list of files to be read (pmConfigFileRead)
+static bool loadRecipeFiles(pmConfig *config, // The configuration into which to read the recipes
+                            psMetadata *source, // The source configuration, from which to read the filenames
+                            pmRecipeSource sourceType, // The source type
+                            const char *sourceName // The name of the source, for error messages
+                           )
+{
+    assert(config);
+    if ((sourceType != PM_RECIPE_SOURCE_SITE) && (sourceType != PM_RECIPE_SOURCE_CAMERA)) {
+        psAbort ("pmConfig", "invalid source for loadRecipes");
+    }
+    if (!source) {
+        psTrace("psModules.pmConfig", 4, "The %s has not been read --- cannot read recipes from this "
+                "location.\n", sourceName);
+        config->recipesRead &= ~sourceType;
+        return false;
+    }
+
+    psMetadata *recipes = psMetadataLookupMetadata(NULL, source, "RECIPES"); // The list of recipes
+    if (!recipes) {
+        psLogMsg("psModules.pmConfig", PS_LOG_WARN, "RECIPES not found in the %s\n", sourceName);
+        config->recipesRead &= ~sourceType;
+        return false;
+    }
+
+    // Copy contents of the filenames to config->recipes from the "RECIPES" metadata in the source.
+    // We could use psMetadataCopy for this, but it's better to check that everything's of the correct type.
+    // If it's not of the correct type, we can tell the user which file it's in, so they can find it easier.
+    psMetadataIterator *recipesIter = psMetadataIteratorAlloc(recipes, PS_LIST_HEAD, NULL); // Iterator
+    psMetadataItem *fileItem = NULL;    // MD item containing the filename, from recipe iteration
+    while ((fileItem = psMetadataGetAndIncrement(recipesIter))) {
+        // type mismatch is a serious error
+        if (fileItem->type != PS_DATA_STRING) {
+            psAbort ("pmConfig", "%s in %s RECIPES is not of type STR", fileItem->name, sourceName);
+        }
+
+        psMetadata *recipe = NULL;
+        psMetadata *current = NULL;
+
+        # if (0)
+
+            psString comment = NULL;
+        psStringAppend(&comment, "Recipe added at %s from %s", sourceName, (char*)fileItem->data.V);
+        psTrace ("psModules.pmConfig", 3, comment);
+        psMetadataAddS32(config->recipesSource, PS_LIST_TAIL, fileItem->name, PS_META_REPLACE, comment, sourceType);
+        psFree(comment);
+        # endif
+
+        // Read the recipe file.
+        if (!pmConfigFileRead(&recipe, fileItem->data.V, "recipe")) {
+            psError(PS_ERR_IO, false, "Failed to read recipe");
+            psFree(recipe);  // Drop reference
+            psFree(recipesIter);
+            return false;
+        }
+
+        // if this named recipe exists, supplement it
+        current = psMemIncrRefCounter (psMetadataLookupMetadata (NULL, config->recipes, fileItem->name));
+        current = psMetadataCopy (current, recipe);
+        psMetadataAdd(config->recipes, PS_LIST_TAIL, fileItem->name, PS_DATA_METADATA | PS_META_REPLACE, fileItem->comment, current);
+        psFree(recipe);  // Drop reference
+        psFree(current);  // Drop reference
+    }
+    psFree(recipesIter);
+    config->recipesRead |= sourceType;
+    return true;
+}
+
+// Load the recipes from config->arguments (CL)
+// Load the recipes: each time we load a specific recipe, it overrides the metadata
+// entries for an existing recipe metadata
+static bool loadRecipeFromArguments(pmConfig *config // The configuration into which to read the recipes
+                                   )
+{
+    assert(config);
+    if (!config->arguments) {
+        psTrace("psModules.pmConfig", 4, "no config->arguments metadata, nothing to read here");
+        return false;
+    }
+
+    psMetadata *recipes = psMetadataLookupMetadata(NULL, config->arguments, "RECIPES"); // The list of recipes
+    if (!recipes) {
+        psTrace("psModules.pmConfig", 4, "no RECIPES in config->arguments, nothing to read here");
+        return false;
+    }
+
+    // Copy the recipes from config->arguments:RECIPES to config->recipes.  supplement existing recipes.
+    psMetadataIterator *recipesIter = psMetadataIteratorAlloc(recipes, PS_LIST_HEAD, NULL); // Iterator
+    psMetadataItem *item = NULL;    // MD item containing the filename, from recipe iteration
+    while ((item = psMetadataGetAndIncrement(recipesIter))) {
+        psMetadata *recipe = NULL;
+        psMetadata *current = NULL;
+
+        // type mismatch is a serious error
+        if (item->type != PS_DATA_METADATA) {
+            psAbort ("pmConfig", "%s in config arguments RECIPES is not of type METADATA", item->name);
+        }
+        // increment the ref counter to protect the data
+        recipe = psMemIncrRefCounter (item->data.V);
+
+        // if this named recipe exists, supplement it
+        current = psMemIncrRefCounter (psMetadataLookupMetadata (NULL, config->recipes, item->name));
+        current = psMetadataCopy (current, recipe);
+        psMetadataAdd(config->recipes, PS_LIST_TAIL, item->name, PS_DATA_METADATA | PS_META_REPLACE, "added from command-line recipe", current);
+        psFree(recipe);  // Drop reference
+        psFree(current);  // Drop reference
+    }
+    psFree(recipesIter);
+    return true;
+}
+
+// Load the recipes: each time we load a specific recipe, it overrides the metadata
+// entries for an existing recipe metadata
+static bool loadRecipeSymbols(pmConfig *config // The configuration into which to read the recipes
+                             )
+{
+    assert(config);
+
+    // check to see if any symbolic names need to be resolved
+    // each entry in recipeSymbols are of the form NAME=REF where NAME is an existing
+    // recipe MD and REF is a MD to load over that recipe
+    psMetadataIterator *recipesIter = psMetadataIteratorAlloc(config->recipeSymbols, PS_LIST_HEAD, NULL);
+    psMetadataItem *item = NULL;  // Item containing source, from iteration
+    while ((item = psMetadataGetAndIncrement(recipesIter))) {
+        assert(item->type == PS_DATA_STRING); // It should be this type: we put it in ourselves
+        const char *symbolRef = psStringCopy (item->data.V); // The name of the symbolic reference
+        const char *symbolName = psStringCopy (item->name);
+
+        psMetadata *current = NULL;
+        psMetadata *recipe = NULL;
+
+        // search for linkName in config->recipes
+        recipe = psMetadataLookupMetadata(NULL, config->recipes, symbolRef); // The source
+        if (recipe) {
+            // if this named recipe exists, supplement it
+            current = psMemIncrRefCounter (psMetadataLookupMetadata (NULL, config->recipes, symbolName));
+            current = psMetadataCopy (current, recipe);
+            psMetadataAdd(config->recipes, PS_LIST_TAIL, symbolName, PS_DATA_METADATA | PS_META_REPLACE, symbolRef, current);
+            psFree(current);  // Drop reference
+            psFree (symbolName);
+            psFree (symbolRef);
+            continue;
+        }
+
+        // search for linkName in config->recipes:NAME
+        current = psMetadataLookupMetadata(NULL, config->recipes, symbolName); // The source
+        if (current) {
+            recipe = psMetadataLookupMetadata(NULL, current, symbolRef); // The source
+            if (recipe) {
+                // supplement the existing recipe
+                current = psMetadataCopy (current, recipe);
+                psMetadataAdd(config->recipes, PS_LIST_TAIL, symbolName, PS_DATA_METADATA | PS_META_REPLACE, symbolRef, current);
+                continue;
+            }
+            psFree(recipe);  // Drop reference
+            psFree(current);  // Drop reference
+            psFree (symbolName);
+            psFree (symbolRef);
+        }
+        psFree (symbolName);
+        psFree (symbolRef);
+        // symbol is not found: ERROR
+    }
+    psFree(recipesIter);
+    return true;
+}
+
+// Load the recipe options
+// Load the recipes: each time we load a specific recipe, it overrides the metadata
+// entries for an existing recipe metadata
+static bool loadRecipeOptions(pmConfig *config // The configuration into which to read the recipes
+                             )
+{
+    assert(config);
+    if (!config->arguments) {
+        psTrace("psModules.pmConfig", 4, "no config->arguments metadata, nothing to read here");
+        return false;
+    }
+
+    psMetadata *recipes = psMetadataLookupMetadata(NULL, config->arguments, "OPTIONS"); // The list of recipes
+    if (!recipes) {
+        psTrace("psModules.pmConfig", 4, "no OPTIONS in config->arguments, nothing to read here");
+        return false;
+    }
+
+    // Copy the recipes from config->arguments:OPTIONS to config->recipes.  supplement existing recipes.
+    psMetadataIterator *recipesIter = psMetadataIteratorAlloc(recipes, PS_LIST_HEAD, NULL); // Iterator
+    psMetadataItem *item = NULL;    // MD item containing the filename, from recipe iteration
+    while ((item = psMetadataGetAndIncrement(recipesIter))) {
+        psMetadata *recipe = NULL;
+        psMetadata *current = NULL;
+
+        // type mismatch is a serious error
+        if (item->type != PS_DATA_METADATA) {
+            psAbort ("pmConfig", "%s in config arguments OPTIONS is not of type METADATA", item->name);
+        }
+        // increment the ref counter to protect the data
+        recipe = psMemIncrRefCounter (item->data.V);
+
+        // if this named recipe exists, supplement it
+        current = psMemIncrRefCounter (psMetadataLookupMetadata (NULL, config->recipes, item->name));
+        current = psMetadataCopy (current, recipe);
+        psMetadataAdd(config->recipes, PS_LIST_TAIL, item->name, PS_DATA_METADATA | PS_META_REPLACE, "supplement command-line arguments", current);
+        psFree(recipe);  // Drop reference
+        psFree(current);  // Drop reference
+    }
+    psFree(recipesIter);
+    return true;
+}
+
+
Index: /trunk/psModules/src/config/pmConfigRecipes.h
===================================================================
--- /trunk/psModules/src/config/pmConfigRecipes.h	(revision 10428)
+++ /trunk/psModules/src/config/pmConfigRecipes.h	(revision 10428)
@@ -0,0 +1,32 @@
+/// @file pmConfig.h
+///
+/// @brief Configuration functions
+///
+/// @ingroup Config
+///
+/// @author ?, MHPCC
+/// @author Paul Price, IfA
+/// @author Eugene Magnier, IfA
+///
+/// @version $Revision: 1.1 $ $Name: not supported by cvs2svn $
+/// @date $Date: 2006-12-04 20:41:51 $
+///
+/// Copyright 2005-2006 Institute for Astronomy, University of Hawaii
+///
+
+#ifndef PM_CONFIG_RECIPES_H
+#define PM_CONFIG_RECIPES_H
+
+/// Read recipes
+///
+/// Attempt to read recipes from the sources that are available but have not already been read.  Having read a
+/// recipe, attempt to resolve symbolic links that were specified on the command line.
+bool pmConfigReadRecipes(pmConfig *config, ///< Configuration
+                         pmRecipeSource source ///< desired sources for recipes
+                        );
+
+
+bool pmConfigLoadRecipeArguments (pmConfig *config);
+bool pmConfigLoadRecipeOptions (pmConfig *config, char *flag);
+
+#endif
Index: /trunk/psModules/src/psmodules.h
===================================================================
--- /trunk/psModules/src/psmodules.h	(revision 10427)
+++ /trunk/psModules/src/psmodules.h	(revision 10428)
@@ -6,4 +6,5 @@
 // the following headers are from psModule:config
 #include <pmConfig.h>
+#include <pmConfigRecipes.h>
 #include <pmVersion.h>
 
