Index: trunk/ippTools/src/stacktool.c
===================================================================
--- trunk/ippTools/src/stacktool.c	(revision 41145)
+++ trunk/ippTools/src/stacktool.c	(revision 41297)
@@ -53,4 +53,6 @@
 static bool exportrunMode(pxConfig *config);
 static bool importrunMode(pxConfig *config);
+static bool importexternalMode(pxConfig *config);
+static bool addexternalMode(pxConfig *config);
 
 static bool setstackRunState(pxConfig *config, psS64 stack_id, const char *state);
@@ -96,4 +98,6 @@
         MODECASE(STACKTOOL_MODE_EXPORTRUN,             exportrunMode);
         MODECASE(STACKTOOL_MODE_IMPORTRUN,             importrunMode);
+        MODECASE(STACKTOOL_MODE_IMPORTEXTERNAL,        importexternalMode);
+        MODECASE(STACKTOOL_MODE_ADDEXTERNAL,           addexternalMode);
         default:
             psAbort("invalid option (this should not happen)");
@@ -2138,2 +2142,130 @@
   return true;
 }
+
+// in this mode, the supplied stacks are inserted into the db and the
+// StackExternalStack table is updated with the IDs of the inserted stack
+bool importexternalMode(pxConfig *config)
+{
+    unsigned int nFail;
+
+    PS_ASSERT_PTR_NON_NULL(config, NULL);
+
+    PXOPT_LOOKUP_STR(infile, config->args, "-infile", true,  false);
+    PXOPT_LOOKUP_S64(ext_camera_id, config->args, "-ext_camera_id", true, false);
+
+    // optionally let the user change the filter:
+    PXOPT_LOOKUP_STR(new_filter, config->args, "-set_filter", false, false);
+
+    psMetadata *input = psMetadataConfigRead (NULL, &nFail, infile, false);
+
+    // XXX for TEST: psMetadataPrint (stderr, input, 1);
+
+    psMetadataItem *itemRun = psMetadataLookup (input, "stackRun");
+    psAssert (itemRun, "entry not in input?");
+    psAssert (itemRun->type == PS_DATA_METADATA_MULTI, "entry not multi?");
+
+    psMetadataItem *entry = psListGet (itemRun->data.list, 0);
+    assert (entry);
+    assert (entry->type == PS_DATA_METADATA);
+
+    // grab the data for the stackRun & format as stackRunRow:
+    stackRunRow *stackRun = stackRunObjectFromMetadata (entry->data.md);
+
+    // save the original stack_id and insert with stack_id = 0 to force a
+    // new iD.  we then need to retrieve that ID and use it below
+    // the new stackRun should 
+
+    psS64 ext_stack_id = stackRun->stack_id;
+    stackRun->stack_id = 0;
+
+    // update filter as well..
+    if (new_filter) {
+      stackRun->filter = psMemIncrRefCounter(new_filter);
+    }
+
+    // start the transaction so we can ensure stackRun, stackSumSkyfile, stackExternalStack table entries are consistent
+    if (!psDBTransaction(config->dbh)) {
+	psError(PS_ERR_UNKNOWN, false, "database error");
+	return false;
+    }
+
+    // now insert the new stackRun object:
+    stackRunInsertObject (config->dbh, stackRun);
+
+    // get the new stack_id
+    psS64 stack_id = psDBLastInsertID(config->dbh);
+
+    // add an entry in the stackExternalStack table:
+    psString query = pxDataGet("stacktool_insertexternal_link.sql");
+    if (!query) {
+	psError(PXTOOLS_ERR_SYS, false, "failed to retreive SQL statement");
+	return false;
+    }
+
+    if (!p_psDBRunQueryF(config->dbh, query, stack_id, ext_stack_id, ext_camera_id)) {
+	psError(PS_ERR_UNKNOWN,false, "database error");
+	return(NULL);
+    }
+
+    // now insert the stackSumSkyfile data
+    psMetadataItem *itemSum = psMetadataLookup (input, "stackSumSkyfile");
+
+    psAssert (itemSum, "entry not in input?");
+    psAssert (itemSum->type == PS_DATA_METADATA_MULTI, "entry not multi?");
+
+    for (int i = 0; i < itemSum->data.list->n; i++) {
+	entry = psListGet (itemSum->data.list, i);
+	assert (entry);
+	assert (entry->type == PS_DATA_METADATA);
+	stackSumSkyfileRow *stackSumSkyfile = stackSumSkyfileObjectFromMetadata (entry->data.md);
+
+	// need to update stack_id
+	stackSumSkyfile->stack_id = stack_id;
+	stackSumSkyfileInsertObject (config->dbh, stackSumSkyfile);
+
+	// fprintf (stdout, "---- row %d ----\n", i);
+	// psMetadataPrint (stderr, entry->data.md, 1);
+    }
+
+    if (!psDBCommit(config->dbh)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+
+    return true;
+}
+
+bool addexternalMode(pxConfig *config) {
+
+    PS_ASSERT_PTR_NON_NULL(config, NULL);
+
+    PXOPT_LOOKUP_STR(ext_camera, config->args, "-ext_camera", true,  false);
+
+    psString query = pxDataGet("stacktool_addexternal.sql");
+    if (!query) {
+	psError(PXTOOLS_ERR_SYS, false, "failed to retreive SQL statement");
+	return false;
+    }
+
+    psStringSubstitute(&query, ext_camera, "@EXT_CAMERA@");
+
+    if (!psDBTransaction(config->dbh)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+
+    if (!p_psDBRunQuery(config->dbh, query)) {
+	psError(PS_ERR_UNKNOWN,false, "database error");
+	return(NULL);
+    }
+
+    int ext_camera_id = psDBLastInsertID(config->dbh);
+    psLogMsg("stacktool", PS_LOG_INFO, "Added new external camera %s (ID = %d)", ext_camera, ext_camera_id);
+
+    if (!psDBCommit(config->dbh)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+
+    return true;
+}
