Index: trunk/psModules/src/config/pmConfigRecipes.c
===================================================================
--- trunk/psModules/src/config/pmConfigRecipes.c	(revision 16611)
+++ trunk/psModules/src/config/pmConfigRecipes.c	(revision 17512)
@@ -13,7 +13,8 @@
 static bool loadRecipeSystem(bool *status, pmConfig *config, psMetadata *source);
 static bool loadRecipeCamera(bool *status, pmConfig *config, psMetadata *source);
+static bool loadRecipeSymbols(bool *status, pmConfig *config, pmRecipeSource source);
 static bool loadRecipeFromArguments(bool *status, pmConfig *config);
-static bool loadRecipeSymbols(bool *status, pmConfig *config);
 static bool loadRecipeOptions(bool *status, pmConfig *config);
+static bool mergeRecipeCamera(bool *status, pmConfig *config);
 
 // use this function to select the options structure for the specified recipe
@@ -45,8 +46,8 @@
 }
 
-// this function may be called several times.  it attempts to load the recipe data from one of
-// three locations: config->complete, config->camera, and argv.  We cannot read the recipes
-// from config->camera until a camera has been read BUT, the argv recipes must override the
-// camera and system recipes.
+// this function may be called several times.  it attempts to load the recipe data from one of three
+// locations: config->complete, config->camera, and argv.  We cannot read the recipes from
+// config->camera until a camera has been read BUT, the argv recipes must override the camera and
+// system recipes.  This command strips the argv elements it uses from the argv list.
 bool pmConfigReadRecipes(pmConfig *config, pmRecipeSource source)
 {
@@ -61,5 +62,6 @@
     // It is an error for config->complete:recipes not to exist.  all programs install their
     // master recipe files in the system:recipe location when they are built.
-    if (config->complete && (source & PM_RECIPE_SOURCE_SYSTEM)) {
+    psAssert (config->complete, "base config data defined");
+    if (source & PM_RECIPE_SOURCE_SYSTEM) {
         if (!loadRecipeSystem(&status, config, config->complete)) {
             psError(PS_ERR_IO, false, "Failed to read recipes from system config");
@@ -85,6 +87,26 @@
     }
 
-    // apply recipes loaded into config->arguments based on command-line arguments
+    // merge camera and sytem recipes, apply recipes loaded into config->arguments based on command-line arguments
     if (config->arguments && (source & PM_RECIPE_SOURCE_CL)) {
+
+	// update the system-level recipes with the symbolically-defined recipes 
+        if (!loadRecipeSymbols(&status, config, PM_RECIPE_SOURCE_SYSTEM)) {
+            psError(PS_ERR_IO, false, "Failed to read recipes from symbolic references");
+            return false;
+        }
+        if (status) {
+            psTrace ("psModules.config", 3, "read recipes from symbolic references");
+        } else {
+            psLogMsg ("psModules.config", PS_LOG_DETAIL, "no recipe supplied by symbolic reference");
+        }
+
+	// merge the SYSTEM and CAMERA recipes
+        if (!mergeRecipeCamera(&status, config)) {
+            psError(PS_ERR_IO, false, "Failed to read recipes from symbolic references");
+            return false;
+        }
+	psLogMsg ("psModules.config", PS_LOG_DETAIL, "merged camera recipes with system recipes");
+
+	// load recipe-files specified on the command line
         if (!loadRecipeFromArguments(&status, config)) {
             psError(PS_ERR_IO, false, "Failed to read recipes from command-line arguments");
@@ -96,5 +118,7 @@
             psLogMsg ("psModules.config", PS_LOG_DETAIL, "no recipe supplied on command-line arguments");
         }
-        if (!loadRecipeSymbols(&status, config)) {
+
+	// update the system-level recipes with the symbolically-defined recipes 
+        if (!loadRecipeSymbols(&status, config, PM_RECIPE_SOURCE_CAMERA)) {
             psError(PS_ERR_IO, false, "Failed to read recipes from symbolic references");
             return false;
@@ -105,4 +129,6 @@
             psLogMsg ("psModules.config", PS_LOG_DETAIL, "no recipe supplied by symbolic reference");
         }
+
+	// override any specific values with values from the command line
         if (!loadRecipeOptions(&status, config)) {
             psError(PS_ERR_IO, false, "Failed to read recipes from symbolic references");
@@ -124,5 +150,5 @@
     int argNum;
 
-    // save the recipes onto config->arguments:RECIPES
+    // save the recipe options onto config->arguments:OPTIONS
     // increment so we can free below (is a NOP if 'options' is NULL)
     psMetadata *options = psMetadataLookupMetadata(&success, config->arguments, "OPTIONS");
@@ -205,8 +231,8 @@
 }
 
-// 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)
+// examine command-line arguments for -recipe RECIPE SYMBOLIC-NAME or -recipe-file RECIPE FILENAME
+// in the first case, the symbolic lookup is saved on config->recipeSymbols
+//   for later interpolation (pmConfigReadRecipes with option CL)
+// in the second case, read as metadata and save on config->arguments with name = KEY
 bool pmConfigLoadRecipeArguments (int *argc, char **argv, pmConfig *config)
 {
@@ -272,6 +298,5 @@
         // 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, NULL,
-                         recipeSource);
+        psMetadataAddStr(config->recipeSymbols, PS_LIST_TAIL, recipeName, PS_META_REPLACE, NULL, recipeSource);
 
         psTrace ("psModules.config", 3, "read recipe %s from %s", recipeName, recipeSource);
@@ -303,63 +328,4 @@
         psError(PS_ERR_IO, false, "RECIPES not found in the system configuration\n");
         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) {
-            psError(PS_ERR_IO, true, "%s in system configuration RECIPES is not of type STR", fileItem->name);
-            return false;
-        }
-
-        // Read the recipe file. if we fail on a file, give a warning, but continue
-        psMetadata *recipe = NULL;
-        if (!pmConfigFileRead(&recipe, fileItem->data.str, "recipe")) {
-            psError(PS_ERR_IO, false, "Failed to read recipe file %s listed in system configuration\n",
-                    fileItem->data.str);
-            return false;
-        }
-
-        // if this named recipe exists, supplement it
-        psMetadataAdd(config->recipes, PS_LIST_TAIL, fileItem->name, PS_DATA_METADATA | PS_META_REPLACE,
-                      fileItem->comment, recipe);
-        psFree(recipe);  // Drop reference
-    }
-    psFree(recipesIter);
-    config->recipesRead |= PM_RECIPE_SOURCE_SYSTEM;
-
-    *status = true;
-    return true;
-}
-
-// Load the recipe files (valid for SYSTEM | CAMERA)
-// each time we load a specific recipe, it overrides the existing metadata for that recipe
-// for sourceType == SYSTEM | CAMERA, RECIPES contains a list of files to be read (pmConfigFileRead)
-static bool loadRecipeCamera(bool *status, // status variable
-                             pmConfig *config, // The configuration into which to read the recipes
-                             psMetadata *source // The source configuration, from which to read the filenames
-    )
-{
-    bool success;
-
-    assert(status);
-    assert(config);
-    *status = false;
-
-    if (!source) {
-        psError(PS_ERR_IO, true, "The camera configuration has not been read --- cannot read recipes from this location.\n");
-        config->recipesRead &= ~PM_RECIPE_SOURCE_CAMERA;
-        return false;
-    }
-
-    // it is not necessary to define any local recipes in the camera config
-    psMetadata *recipes = psMetadataLookupMetadata(&success, source, "RECIPES"); // The list of recipes
-    if (!recipes) {
-        psTrace ("psModules.config", 3, "RECIPES not found in the camera configuration\n");
-        return true;
     }
 
@@ -373,5 +339,63 @@
         char *recipeFile = fileItem->data.str;
 
-        psTrace("psModules.config", 3, "Supplementing %s from %s within camera configuration.\n", recipeName, recipeFile);
+        // type mismatch is a serious error
+        if (fileItem->type != PS_DATA_STRING) {
+            psAbort("%s in system configuration RECIPES is not of type STR", recipeName);
+        }
+
+        // Read the recipe file. if we fail on a file, give a warning, but continue
+        psMetadata *recipe = NULL;
+        if (!pmConfigFileRead(&recipe, recipeFile, "recipe")) {
+            psError(PS_ERR_IO, false, "Failed to read recipe file %s listed in system configuration\n", recipeFile);
+            return false;
+        }
+
+        // and the contents of this recipe file to config->recipes
+        psMetadataAdd(config->recipes, PS_LIST_TAIL, fileItem->name, PS_DATA_METADATA | PS_META_REPLACE, fileItem->comment, recipe);
+        psFree(recipe);  // Drop reference
+    }
+    psFree(recipesIter);
+    config->recipesRead |= PM_RECIPE_SOURCE_SYSTEM;
+
+    *status = true;
+    return true;
+}
+
+// Load the recipe files for a specific CAMERA.  these are saved on recipesCamera
+static bool loadRecipeCamera(bool *status, // status variable
+                             pmConfig *config, // The configuration into which to read the recipes
+                             psMetadata *source // The source configuration, from which to read the filenames
+    )
+{
+    bool success;
+
+    assert(status);
+    assert(config);
+    *status = false;
+
+    if (!source) {
+        psError(PS_ERR_IO, true, "The camera configuration has not been read --- cannot read recipes from this location.\n");
+        config->recipesRead &= ~PM_RECIPE_SOURCE_CAMERA;
+        return false;
+    }
+
+    // it is not necessary to define any local recipes in the camera config; it this entry is missing,
+    // just return true
+    psMetadata *recipes = psMetadataLookupMetadata(&success, source, "RECIPES"); // The list of recipes
+    if (!recipes) {
+        psTrace ("psModules.config", 3, "RECIPES not found in the camera configuration\n");
+        return true;
+    }
+
+    // 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))) {
+        char *recipeName = fileItem->name;
+        char *recipeFile = fileItem->data.str;
+
+        psTrace("psModules.config", 3, "loading %s from %s within camera configuration.\n", recipeName, recipeFile);
 
         // type mismatch is a serious error
@@ -383,10 +407,9 @@
         psMetadata *recipe = NULL;
         if (!pmConfigFileRead(&recipe, recipeFile, "recipe")) {
-            psError(PS_ERR_IO, false, "Failed to read recipe file %s listed in camera configuration\n",
-                    recipeFile);
-            return false;
-        }
-
-        // the named recipe must exist; supplement it
+            psError(PS_ERR_IO, false, "Failed to read recipe file %s listed in camera configuration\n", recipeFile);
+            return false;
+        }
+
+        // the named recipe must exist at the system level
         psMetadata *current = psMetadataLookupMetadata(NULL, config->recipes, recipeName);
         if (!current) {
@@ -396,13 +419,50 @@
         }
 
-        if (!psMetadataUpdate(current, recipe)) {
-            psError(PS_ERR_IO, false, "Failed to update recipe for %s from camera recipe", recipeName);
-            psFree(recipe);  // Drop reference
-            return false;
-        }
+        // add the contents of this recipe file to config->recipesCamera
+        psMetadataAdd(config->recipesCamera, PS_LIST_TAIL, recipeName, PS_DATA_METADATA | PS_META_REPLACE, fileItem->comment, recipe);
         psFree(recipe);  // Drop reference
     }
     psFree(recipesIter);
     config->recipesRead |= PM_RECIPE_SOURCE_CAMERA;
+    *status = true;
+    return true;
+}
+
+// Merge the CAMERA recipes into the SYSTEM recipes
+static bool mergeRecipeCamera(bool *status, // status variable
+                             pmConfig *config // The configuration into which to read the recipes
+    )
+{
+    assert(status);
+    assert(config);
+    *status = false;
+
+    // Copy contents of config->recipesCamera to config->recipes
+    // 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(config->recipesCamera, PS_LIST_HEAD, NULL); // Iterator
+    psMetadataItem *folderItem = NULL;    // MD item containing the filename, from recipe iteration
+    while ((folderItem = psMetadataGetAndIncrement(recipesIter))) {
+        char *recipeName = folderItem->name;
+        psMetadata *recipe = folderItem->data.md;
+
+        psTrace("psModules.config", 3, "merging %s from camera with system recipes.\n", recipeName);
+
+        // type mismatch is a serious error
+        if (folderItem->type != PS_DATA_METADATA) {
+            psAbort("%s not of type METADATA", recipeName);
+        }
+
+        // the select the named recipe from the system level
+        psMetadata *current = psMetadataLookupMetadata(NULL, config->recipes, recipeName);
+        psAssert (current, "Failed to find recipe for %s in system recipe list", recipeName);
+
+        // update the contents of this recipe from the one on config->recipesCamera
+	if (!psMetadataUpdate(current, recipe)) {
+	    psError(PS_ERR_IO, false, "Failed to update recipe for %s from camera recipe", recipeName);
+	    return false;
+	}
+    }
+    psFree(recipesIter);
     *status = true;
     return true;
@@ -464,8 +524,7 @@
 // Load the recipes: each time we load a specific recipe, it overrides the metadata
 // entries for an existing recipe metadata
-static bool loadRecipeSymbols(bool *status,
-                              pmConfig *config // The configuration into which to read the recipes
-    )
-{
+// The configuration into which to read the recipes
+static bool loadRecipeSymbols(bool *status, pmConfig *config, pmRecipeSource source) {
+
     bool found = false;
 
@@ -492,19 +551,24 @@
         }
 
-        // search for sourceName : it may be in config->recipes or target MD
-        psMetadata *sourceMD = NULL;
-        sourceMD = psMetadataLookupMetadata(&found, config->recipes, sourceName);
-        if (!sourceMD) {
-            sourceMD = psMetadataLookupMetadata(&found, targetMD, sourceName);
-            if (!sourceMD) {
-                psError(PS_ERR_IO, false, "Selected symbolic name %s does not exist in recipes", sourceName);
-                return false;
-            }
-        }
-
-        if (!psMetadataUpdate(targetMD, sourceMD)) {
+        // search for sourceName in config->recipes (folder name is targetName)
+	psMetadata *folder = psMetadataLookupMetadata(&found, config->recipes, targetName);
+	psMetadata *sourceMD = psMetadataLookupMetadata(&found, folder, sourceName);
+
+	// if we find the desired symbolic name at this level, set the item comment to say "FOUND"
+        if (sourceMD) {
+	  if (!psMetadataUpdate(targetMD, sourceMD)) {
             psError(PS_ERR_IO, false, "Failed to update recipe for %s from camera recipe", targetName);
             return false;
-        }
+	  }
+	  psStringAppend (&item->comment, "(FOUND)");
+	}	  
+
+	// if we have not found it by the camera level, we have a problem
+	if (source == PM_RECIPE_SOURCE_CAMERA) {
+	  if (strstr (item->comment, "(FOUND)") == NULL) {
+	    psError(PS_ERR_IO, false, "Selected symbolic name %s does not exist in recipes", sourceName);
+	    return false;
+	  }
+	}
     }
     psFree(iter);
