Index: trunk/psModules/src/config/pmConfig.c
===================================================================
--- trunk/psModules/src/config/pmConfig.c	(revision 16415)
+++ trunk/psModules/src/config/pmConfig.c	(revision 16611)
@@ -34,6 +34,6 @@
 #endif // ifdef HAVE_NEBCLIENT
 
-#define PS_SITE "PS_SITE"         // Name of the environment variable containing the site config file
-#define PS_DEFAULT_SITE ".ipprc"  // Default site config file
+#define IPPRC_ENV "IPPRC"        // Name of the environment variable containing the top-level config file
+#define IPPRC_FILE ".ipprc"      // Default top-level config file
 
 #define DEFAULT_LOG STDERR_FILENO       // Default file descriptor for log messages
@@ -52,5 +52,8 @@
 static void configFree(pmConfig *config)
 {
+    psFree(config->user);
     psFree(config->site);
+    psFree(config->system);
+    psFree(config->complete);
     psFree(config->files);
     psFree(config->camera);
@@ -80,5 +83,8 @@
 
     // Initialise
+    config->user = NULL;
     config->site = NULL;
+    config->system = NULL;
+    config->complete = NULL;
     config->camera = NULL;
     config->cameraName = NULL;
@@ -145,5 +151,7 @@
     PS_ASSERT_STRING_NON_EMPTY(path,);
 
-    pmConfigDone();
+    assert (configPath == NULL);
+    // XXX why was this being called?  pmConfigSet should only be called once...
+    // pmConfigDone();
 
     psList *list = psStringSplit(path, ":", false);
@@ -159,5 +167,7 @@
 void pmConfigDone(void)
 {
-    psFree(configPath);
+    if (configPath) { 
+	psFree(configPath);
+    }
     configPath = NULL;
 
@@ -380,25 +390,20 @@
     config->defaultRecipe = defaultRecipe;
 
-    //
-    // The following section of code attempts to determine which file is
-    // the configuration file.  At the end of this code block, the siteName
+    // The following section of code attempts to determine which file to use as the
+    // top-level the configuration file.  At the end of this code block, the configFile
     // variable will contain the name of the configuration file.
-    //
-    char *siteName = NULL;
+
+    char *configFile = NULL;
     //
     // First, try command line
     //
-    psS32 argNum = psArgumentGet(*argc, argv, "-site");
+    psS32 argNum = psArgumentGet(*argc, argv, "-ipprc");
     if (argNum != 0) {
-        //
-        // We remove the "-site" argument from argv.  Then
-        // we look for the next argument, which should be the filename, and
-        // remove it as well.
-        //
+        // remove the "-ipprc" argument from argv, check and remove filename
         psArgumentRemove(argNum, argc, argv);
         if (argNum >= *argc) {
-            psWarning("-site command-line switch provided without the required filename --- ignored.\n");
+            psWarning("-ipprc command-line switch provided without the required filename --- ignored.\n");
         } else {
-            siteName = psStringCopy(argv[argNum]);
+            configFile = psStringCopy(argv[argNum]);
             psArgumentRemove(argNum, argc, argv);
         }
@@ -407,8 +412,8 @@
     // Next, try environment variable
     //
-    if (!siteName) {
-        siteName = getenv(PS_SITE);
-        if (siteName) {
-            siteName = psStringCopy (siteName);
+    if (!configFile) {
+        configFile = getenv(IPPRC_ENV);
+        if (configFile) {
+            configFile = psStringCopy (configFile);
         }
     }
@@ -417,35 +422,53 @@
     // Last chance is ~/.ipprc
     //
-    if (!siteName) {
+    if (!configFile) {
         char *home = getenv("HOME");
-        siteName = psStringCopy(home);
-        psStringAppend(&siteName, "/%s", PS_DEFAULT_SITE);
-    }
-
+        configFile = psStringCopy(home);
+        psStringAppend(&configFile, "/%s", IPPRC_FILE);
+    }
 
     // We have the configuration filename; now we read and parse the config
-    // file and store in psMetadata struct site.
-    //
-
-    if (!pmConfigFileRead(&config->site, siteName, "site")) {
+    // file and store in psMetadata struct user.
+    // XXX move this section to pmConfigReadUser.c ?
+
+    if (!pmConfigFileRead(&config->user, configFile, "user")) {
         psFree(config);
         return NULL;
     }
 
-    // Set options based on the site configuration.
+    // XXX why was this being called here?  Is someone calling pmConfigRead multiple times?
+    // pmConfigDone();
+    assert (configPath == NULL);  
+
+    // define the config-file search path (configPath).  
+    psString path = psMetadataLookupStr(NULL, config->user, "PATH");
+    pmConfigSet (path);
+
+    // read the SITE file
+    psString siteFile = psMetadataLookupStr(NULL, config->user, "SITE");
+    if (!pmConfigFileRead(&config->site, siteFile, "site")) {
+        psFree(config);
+        return NULL;
+    }
+
+    // load the SYSTEM file
+    psString systemFile = psMetadataLookupStr(NULL, config->user, "SYSTEM");
+    if (!pmConfigFileRead(&config->system, systemFile, "system")) {
+        psFree(config);
+        return NULL;
+    }
+
+    // interpolate USER, SITE and SYSTEM into the config->complete metadata
+    config->complete = psMetadataCopy (NULL,             config->user);
+    config->complete = psMetadataCopy (config->complete, config->site);
+    config->complete = psMetadataCopy (config->complete, config->system);
+
+    // Set LOG and TRACE options based on the user configuration.  These must be set AFTER
+    // the SITE and SYSTEM config files are read so path:// entries here can be resolved.
     {
         bool mdok = true;   // Status of MD lookup result
 
-        // Initialise the psLib time handling
-        psString timeName = psMetadataLookupStr(&mdok, config->site, "TIME");
-        if (mdok && timeName)
-        {
-            psTrace("psModules.config", 7, "Initialising psTime with file %s\n", timeName);
-            psTimeInit(timeName);
-        }
-
-
         // Set logging level
-        int logLevel = psMetadataLookupS32(&mdok, config->site, "LOGLEVEL");
+        int logLevel = psMetadataLookupS32(&mdok, config->user, "LOGLEVEL");
         if (mdok && logLevel >= 0)
         {
@@ -456,5 +479,5 @@
 
         // Set logging format
-        psString logFormat = psMetadataLookupStr(&mdok, config->site, "LOGFORMAT");
+        psString logFormat = psMetadataLookupStr(&mdok, config->user, "LOGFORMAT");
         if (mdok && logFormat)
         {
@@ -463,5 +486,5 @@
         }
 
-        // Set logging destination first from command line, second from site configuration
+        // Set logging destination first from command line, second from user configuration
         psString logDest = NULL;        // Logging destination
         argNum = psArgumentGet(*argc, argv, "-log");
@@ -477,5 +500,5 @@
         }
         if (!logDest) {
-            logDest = psMemIncrRefCounter(psMetadataLookupStr(&mdok, config->site, "LOGDEST"));
+            logDest = psMemIncrRefCounter(psMetadataLookupStr(&mdok, config->user, "LOGDEST"));
         }
         if (logDest) {
@@ -497,5 +520,5 @@
 
         // Set trace levels
-        psMetadata *trace = psMetadataLookupMetadata(&mdok, config->site, "TRACE");
+        psMetadata *trace = psMetadataLookupMetadata(&mdok, config->user, "TRACE");
         if (mdok && trace) {
             psMetadataIterator *traceIter = psMetadataIteratorAlloc(trace, PS_LIST_HEAD, NULL); // Iterator
@@ -515,5 +538,5 @@
 
         // Set trace formats
-        psString traceFormat = psMetadataLookupStr(&mdok, config->site, "TRACEFORMAT");
+        psString traceFormat = psMetadataLookupStr(&mdok, config->user, "TRACEFORMAT");
         if (mdok && traceFormat) {
             psTrace("psModules.config", 7, "Setting trace format to %s\n", traceFormat);
@@ -535,5 +558,5 @@
         }
         if (!traceDest) {
-            traceDest = psMemIncrRefCounter(psMetadataLookupStr(&mdok, config->site, "TRACEDEST"));
+            traceDest = psMemIncrRefCounter(psMetadataLookupStr(&mdok, config->user, "TRACEDEST"));
         }
         if (traceDest) {
@@ -563,31 +586,20 @@
     }
 
-    // define the config-file search path (configPath).  Ensure that
-    // it contains the directory where we found the config file in
-    // the first place
-    if (configPath) {
-        pmConfigDone();
-    }
-
-    psString siteNameDir = psStringCopy(dirname(siteName));
-    psFree(siteName);
-
-    psString path = psMetadataLookupStr(NULL, config->site, "PATH");
-    psString newPath = NULL;            // New path
-    // The following gymnastics with 'newPath' are required to avoid changing the pointer out from under the
-    // psMetadataItem on which 'path' sits (leading to memory corruption because it no longer points to valid
-    // memory).
-    if (path) {
-        psStringAppend(&newPath, "%s:%s", path, siteNameDir);
-    } else {
-        newPath = psMemIncrRefCounter(siteNameDir);
-    }
-    psFree(siteNameDir);
-    pmConfigSet(newPath);
-    psFree(newPath);
+    // XXX read TIME from SITE (or system?)
+    { 
+	bool mdok = true;
+
+        // Initialise the psLib time handling
+	// XXX is this still needed / desired?
+        psString timeName = psMetadataLookupStr(&mdok, config->complete, "TIME");
+        if (mdok && timeName)
+        {
+            psTrace("psModules.config", 7, "Initialising psTime with file %s\n", timeName);
+            psTimeInit(timeName);
+        }
+    }
 
     // Next, we do a similar thing for the camera configuration file.  The
     // file is read and parsed into psMetadata struct "camera".
-    //
     argNum = psArgumentGet(*argc, argv, "-camera");
     if (argNum > 0) {
@@ -600,6 +612,6 @@
             char *cameraName = argv[argNum]; // symbolic name of the camera
 
-            // look for the CAMERAS list in config->site
-            psMetadata *cameras = psMetadataLookupMetadata(&mdok, config->site, "CAMERAS");
+            // look for the CAMERAS list in config->complete
+            psMetadata *cameras = psMetadataLookupMetadata(&mdok, config->complete, "CAMERAS");
             if (!cameras) {
                 psError(PS_ERR_IO, false, "Unable to find CAMERAS in site configuration.\n");
@@ -647,5 +659,5 @@
                                   "Camera specified on command line", config->camera);
 
-            if (!pmConfigCameraSkycellVersion(config->site, cameraName)) {
+            if (!pmConfigCameraSkycellVersion(config->complete, cameraName)) {
                 psError(PS_ERR_UNKNOWN, false,
                         "Unable to generate skycell versions of specified camera %s.\n",
@@ -655,5 +667,5 @@
             }
 
-            if (!pmConfigCameraMosaickedVersions(config->site, cameraName)) {
+            if (!pmConfigCameraMosaickedVersions(config->complete, cameraName)) {
                 psError(PS_ERR_UNKNOWN, false,
                         "Unable to generate mosaicked versions of specified camera %s.\n",
@@ -668,12 +680,12 @@
     if (!config->camera && readCameraConfig) {
         bool mdok;                      // Status of MD lookup
-        psMetadata *cameras = psMetadataLookupMetadata(&mdok, config->site, "CAMERAS"); // List of cameras
+        psMetadata *cameras = psMetadataLookupMetadata(&mdok, config->complete, "CAMERAS"); // List of cameras
         if (!mdok || !cameras) {
-            psError(PS_ERR_UNEXPECTED_NULL, true, "Unable to find CAMERAS in the site configuration.\n");
+            psError(PS_ERR_UNEXPECTED_NULL, true, "Unable to find CAMERAS in the system configuration.\n");
             return false;
         }
 
         if (!metadataReadFiles(cameras, "camera configuration")) {
-            psError(PS_ERR_UNKNOWN, false, "Unable to read cameras within site configuration.\n");
+            psError(PS_ERR_UNKNOWN, false, "Unable to read cameras within system configuration.\n");
             psFree(config);
             return NULL;
@@ -702,10 +714,10 @@
         psFree(iter);
 
-        if (!pmConfigCameraSkycellVersionsAll(config->site)) {
+        if (!pmConfigCameraSkycellVersionsAll(config->complete)) {
             psError(PS_ERR_UNKNOWN, false, "Unable to generate skycell versions of cameras.\n");
             psFree(config);
             return NULL;
         }
-        if (!pmConfigCameraMosaickedVersionsAll(config->site)) {
+        if (!pmConfigCameraMosaickedVersionsAll(config->complete)) {
             psError(PS_ERR_UNKNOWN, false, "Unable to generate mosaicked versions of cameras.\n");
             psFree(config);
@@ -715,5 +727,5 @@
 
     // Load the recipes from the camera file, if appropriate
-    if(!pmConfigReadRecipes(config, PM_RECIPE_SOURCE_SITE | PM_RECIPE_SOURCE_CAMERA)) {
+    if(!pmConfigReadRecipes(config, PM_RECIPE_SOURCE_SYSTEM | PM_RECIPE_SOURCE_CAMERA)) {
         psError(PS_ERR_IO, false, "Failed to read recipes from camera file");
         psFree(config);
@@ -746,5 +758,5 @@
         psArgumentRemove(argNum, argc, argv);
 
-        psMetadata *cameras = psMetadataLookupMetadata(NULL, config->site, "CAMERAS"); // List of cameras
+        psMetadata *cameras = psMetadataLookupMetadata(NULL, config->complete, "CAMERAS"); // List of cameras
         if (!cameras) {
             psError(PS_ERR_UNEXPECTED_NULL, false, "Unable to find CAMERAS in the site configuration.\n");
@@ -794,5 +806,5 @@
         } else {
             char *dbserver = argv[argNum]; // The camera configuration file to read
-            if (!psMetadataAddStr(config->site, PS_LIST_TAIL, "DBSERVER", PS_META_REPLACE, NULL, dbserver)) {
+            if (!psMetadataAddStr(config->complete, PS_LIST_TAIL, "DBSERVER", PS_META_REPLACE, NULL, dbserver)) {
                 psWarning("Failed to overwrite .ipprc DBSERVER value");
             }
@@ -809,5 +821,5 @@
         } else {
             char *dbname = argv[argNum]; // The camera configuration file to read
-            if (!psMetadataAddStr(config->site, PS_LIST_TAIL, "DBNAME", PS_META_REPLACE, NULL, dbname)) {
+            if (!psMetadataAddStr(config->complete, PS_LIST_TAIL, "DBNAME", PS_META_REPLACE, NULL, dbname)) {
                 psWarning("Failed to overwrite .ipprc DBNAME value");
             }
@@ -824,5 +836,5 @@
         } else {
             char *dbuser = argv[argNum]; // The camera configuration file to read
-            if (!psMetadataAddStr(config->site, PS_LIST_TAIL, "DBUSER", PS_META_REPLACE, NULL, dbuser)) {
+            if (!psMetadataAddStr(config->complete, PS_LIST_TAIL, "DBUSER", PS_META_REPLACE, NULL, dbuser)) {
                 psWarning("Failed to overwrite .ipprc DBUSER value");
             }
@@ -839,5 +851,5 @@
         } else {
             char *dbpassword = argv[argNum]; // The camera configuration file to read
-            if (!psMetadataAddStr(config->site, PS_LIST_TAIL, "DBPASSWORD", PS_META_REPLACE,
+            if (!psMetadataAddStr(config->complete, PS_LIST_TAIL, "DBPASSWORD", PS_META_REPLACE,
                                   NULL, dbpassword)) {
                 psWarning("Failed to overwrite .ipprc DBPASSWORD value");
@@ -855,5 +867,5 @@
         } else {
             char *dbport = argv[argNum]; // The camera configuration file to read
-            if (!psMetadataAddS32(config->site, PS_LIST_TAIL, "DBPORT", PS_META_REPLACE, NULL,
+            if (!psMetadataAddS32(config->complete, PS_LIST_TAIL, "DBPORT", PS_META_REPLACE, NULL,
                                   (psS32)atoi(dbport))) {
                 psWarning("Failed to overwrite .ipprc DBPORT value");
@@ -1027,5 +1039,5 @@
     if (! config->camera) {
         bool mdok;                      // Metadata lookup status
-        psMetadata *cameras = psMetadataLookupMetadata(&mdok, config->site, "CAMERAS");
+        psMetadata *cameras = psMetadataLookupMetadata(&mdok, config->complete, "CAMERAS");
         if (! mdok || !cameras) {
             psError(PS_ERR_IO, true, "Unable to find CAMERAS in the configuration.");
@@ -1114,5 +1126,5 @@
     PS_ASSERT_STRING_NON_EMPTY(cameraName, NULL);
 
-    psMetadata *cameras = psMetadataLookupMetadata(NULL, config->site, "CAMERAS");
+    psMetadata *cameras = psMetadataLookupMetadata(NULL, config->complete, "CAMERAS");
     if (!cameras) {
         psError(PS_ERR_IO, true, "Unable to find CAMERAS in the configuration.");
@@ -1140,5 +1152,5 @@
 {
     PS_ASSERT_PTR_NON_NULL(config, NULL);
-    PS_ASSERT_PTR_NON_NULL(config->site, NULL);
+    PS_ASSERT_PTR_NON_NULL(config->complete, NULL);
 
     #ifndef HAVE_PSDB
@@ -1157,9 +1169,9 @@
 
     // XXX leaky strings
-    psString dbServer = psMetadataLookupStr(&mdStatus01, config->site, "DBSERVER");
-    psString dbUsername = psMetadataLookupStr(&mdStatus02, config->site, "DBUSER");
-    psString dbPassword = psMetadataLookupStr(&mdStatus03, config->site, "DBPASSWORD");
-    psString dbName = psMetadataLookupStr(&mdStatus04, config->site, "DBNAME");
-    psS32 dbPort = psMetadataLookupS32(&mdStatus05, config->site, "DBPORT");
+    psString dbServer = psMetadataLookupStr(&mdStatus01, config->complete, "DBSERVER");
+    psString dbUsername = psMetadataLookupStr(&mdStatus02, config->complete, "DBUSER");
+    psString dbPassword = psMetadataLookupStr(&mdStatus03, config->complete, "DBPASSWORD");
+    psString dbName = psMetadataLookupStr(&mdStatus04, config->complete, "DBNAME");
+    psS32 dbPort = psMetadataLookupS32(&mdStatus05, config->complete, "DBPORT");
     if (!mdStatus05) {
         dbPort = 0;
@@ -1352,11 +1364,11 @@
     // replace path://PATH with matched datapath
     if (!strncasecmp(filename, "path://", strlen("path://"))) {
-        PS_ASSERT_METADATA_NON_NULL(config->site, NULL);
+        PS_ASSERT_METADATA_NON_NULL(config->complete, NULL);
 
         psString newName = psStringCopy(filename);
 
         // filename should be of the form: path://PATH/rest/of/file
-        // replace PATH with matching name from config->site:DATAPATH
-        psMetadata *datapath = psMetadataLookupPtr (NULL, config->site, "DATAPATH");
+        // replace PATH with matching name from config->complete:DATAPATH
+        psMetadata *datapath = psMetadataLookupPtr (NULL, config->complete, "DATAPATH");
         if (datapath == NULL) {
             psError(PS_ERR_UNKNOWN, true, "DATAPATH is not defined in config.site");
@@ -1410,5 +1422,5 @@
         // if env isn't set, check the config system
         if (!neb_server) {
-            neb_server = psMetadataLookupStr(&status, config->site, "NEB_SERVER");
+            neb_server = psMetadataLookupStr(&status, config->complete, "NEB_SERVER");
             if (!status) {
                 psError(PM_ERR_CONFIG, true, "failed to lookup config value for NEB_SERVER.");
