Index: trunk/ippTools/src/chiptool.c
===================================================================
--- trunk/ippTools/src/chiptool.c	(revision 13937)
+++ trunk/ippTools/src/chiptool.c	(revision 14018)
@@ -30,4 +30,5 @@
 #include "pxtools.h"
 #include "pxdata.h"
+
 #include "chiptool.h"
 #include "camtool.h"
@@ -42,6 +43,4 @@
 static bool unblockMode(pxConfig *config);
 
-static chipProcessedImfileRow *chipPendingToProcessedImfile(pxConfig *config, chipPendingImfileRow *imfile);
-static chipProcessedExpRow *chipPendingToProcessedExp(pxConfig *config, chipPendingExpRow *pendingExp);
 static bool chipProcessedCompleteExp(pxConfig *config);
 
@@ -243,5 +242,5 @@
 
         // queue the exp
-        if (!chipQueueExpTag(config, exp_tag, workdir, label, reduction, expgroup, dvodb)) {
+        if (!pxchipQueueByExpTag(config, exp_tag, workdir, label, reduction, expgroup, dvodb)) {
             if (!psDBRollback(config->dbh)) {
                 psError(PS_ERR_UNKNOWN, false, "database error");
@@ -283,5 +282,5 @@
 
     if (config->where) {
-        psString whereClause = psDBGenerateWhereConditionSQL(config->where, "chipPendingImfile");
+        psString whereClause = psDBGenerateWhereConditionSQL(config->where, "chipInputImfile");
         psStringAppend(&query, " AND %s", whereClause);
         psFree(whereClause);
@@ -330,5 +329,5 @@
 
     // negative simple so the default is true
-    if (!ippdbPrintMetadatas(stdout, output, "chipPendingImfile", !simple)) {
+    if (!ippdbPrintMetadatas(stdout, output, "chipInputImfile", !simple)) {
         psError(PS_ERR_UNKNOWN, false, "failed to print array");
         psFree(output);
@@ -346,491 +345,36 @@
     PS_ASSERT_PTR_NON_NULL(config, false);
 
-    // select * from chipProcessedImfiles
-    // where
-    // exp_tag & class_id are not in chipPendingImfile
-
-    // add the completed imfile to
-    // the chipProcessedeImfile tables
-    // remove corresponding entries from the
-    // chipPendingImfile table
-    // check to see if any chipPendingExps have no
-    // associated chipPendingImfiles
-    // if so move the chipPendingExp(s) to chipProcessedExp
-
-    psString query = pxDataGet("chiptool_find_unprocessed_imfile.pl");
-    if (!query) {
-        psError(PXTOOLS_ERR_DATA, false, "failed to retreive SQL statement");
-        return false;
-    }
-
-    {
-        psMetadata *where = psMetadataAlloc();
-        bool status = false;
-        psString chip_id = psMetadataLookupStr(&status, config->args, "-chip_id");        if (!status) {
-            psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -chip_id");
-            psFree(query);
-            return false;
-        }
-        if (chip_id) {
-            if (!psMetadataAddStr(where, PS_LIST_TAIL, "chip_id", 0, "==", chip_id)) {
-                psError(PS_ERR_UNKNOWN, false, "failed to add item chip_id");
-                psFree(where);
-                psFree(query);
-                return false;
-            }
-        }
-
-        psString class_id = psMetadataLookupStr(&status, config->args, "-class_id");
-        if (!status) {
-            psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -class_id");
-            psFree(query);
-            return false;
-        }
-        if (class_id) {
-            if (!psMetadataAddStr(where, PS_LIST_TAIL, "class_id", 0, "==", class_id)) {
-                psError(PS_ERR_UNKNOWN, false, "failed to add item class_id");
-                psFree(where);
-                psFree(query);
-                return false;
-            }
-        }
-
-        psString whereClause = psDBGenerateWhereConditionSQL(config->where, "chipPendingImfile");
-        psFree(where);
-        if (whereClause) {
-            psStringAppend(&query, " AND %s", whereClause);
-            psFree(whereClause);
-        }
-    }
-
-    if (!p_psDBRunQuery(config->dbh, query)) {
-        psError(PS_ERR_UNKNOWN, false, "database error");
-        psFree(query);
-        return false;
-    }
-    psFree(query);
-
-    psArray *output = p_psDBFetchResult(config->dbh);
-    if (!output) {
-        psError(PS_ERR_UNKNOWN, false, "database error");
-        return false;
-    }
-    if (!psArrayLength(output)) {
-        // XXX check psError here
-        psError(PS_ERR_UNKNOWN, false, "no chipPendingImfile rows found");
-        psFree(output);
-        return false;
-    }
-
-    // start a transaction so we don't end up with an incremented iteration
-    // count but no detInputExps
-    if (!psDBTransaction(config->dbh)) {
-        psError(PS_ERR_UNKNOWN, false, "database error");
-        psFree(output);
-        return false;
-    }
-
-    // insert into chipProcessedImfile
-    // remove chipPendingImfile entry
-    for (long i = 0; i < psArrayLength(output); i++) {
-        psMetadata *row = output->data[i];
-        // convert metadata into a chipPendingImfile object
-        chipPendingImfileRow *object = chipPendingImfileObjectFromMetadata(row);
-        // convert chipPendingImfile object into a chipProcessedImfile object
-        chipProcessedImfileRow *imfile = chipPendingToProcessedImfile(config, object);
-        if (!imfile) {
-            // rollback
-            if (!psDBRollback(config->dbh)) {
-                psError(PS_ERR_UNKNOWN, false, "database error");
-            }
-            psError(PS_ERR_UNKNOWN, false, "failed to convert chipPendingImfile to chipProcessedImfile");
-            psFree(object);
-            psFree(output);
-            return false;
-        }
-        // insert chipProccessedImfile object into the database
-        if (!chipProcessedImfileInsertObject(config->dbh, imfile)) {
-            // rollback
-            if (!psDBRollback(config->dbh)) {
-                psError(PS_ERR_UNKNOWN, false, "database error");
-            }
-            psError(PS_ERR_UNKNOWN, false, "database error");
-            psFree(imfile);
-            psFree(object);
-            psFree(output);
-            return false;
-        }
-        psFree(imfile);
-        // delete the chipPendingImfile object from the database
-        if (!chipPendingImfileDeleteObject(config->dbh, object)) {
-            // there must be atleast 1 Imfile to get this far
-            // rollback
-            if (!psDBRollback(config->dbh)) {
-                psError(PS_ERR_UNKNOWN, false, "database error");
-            }
-            psError(PS_ERR_UNKNOWN, false, "database error");
-            psFree(object);
-            psFree(output);
-            return false;
-        }
-        psFree(object);
-    }
-
-    psFree(output);
-
-    // XXX I've decided to make the transaction cover the Exp migration as
-    // well.  Otherwise, if the last imfile in an exp is moved and the exp
-    // migration fails then the data base is left in a satuation where the exp
-    // migration can't happen.
-
-    if (!chipProcessedCompleteExp(config)) {
-        // rollback
-        if (!psDBRollback(config->dbh)) {
-            psError(PS_ERR_UNKNOWN, false, "database error");
-        }
-        psError(PS_ERR_UNKNOWN, false, "database error");
-        return false;
-    }
-
-    // point of no return for chipPendingImfile -> chipProcessedImfile
-    // point of no return for chipPendingExp -> chipProcessedExp
-    if (!psDBCommit(config->dbh)) {
-        psError(PS_ERR_UNKNOWN, false, "database error");
-        return false;
-    }
-
-    return true;
-}
-
-
-static bool processedimfileMode(pxConfig *config)
-{
-    PS_ASSERT_PTR_NON_NULL(config, NULL);
-
     bool status = false;
-    psU64 limit = psMetadataLookupU64(&status, config->args, "-limit");
-    if (!status) {
-        psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -limit");
-        return false;
-    }
-
-    bool faulted = psMetadataLookupU64(&status, config->args, "-faulted");
-    if (!status) {
-        psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -faulted");
-        return false;
-    }
-
-    // XXX does this need to be constrained so that it won't return any results
-    // if a match chipPendingExp hasn't been registered?
-    psString query = pxDataGet("chiptool_processedimfile.sql");
-    if (!query) {
-        psError(PXTOOLS_ERR_DATA, false, "failed to retreive SQL statement");
-        return false;
-    }
-
-    if (config->where) {
-        psString whereClause = psDBGenerateWhereConditionSQL(config->where, "chipProcessedImfile");
-        psStringAppend(&query, " AND %s", whereClause);
-        psFree(whereClause);
-    }
-
-    if (faulted) {
-        // list only faulted rows
-        psStringAppend(&query, " %s", "AND chipProcessedImfile.fault != 0");
-    } else {
-        // don't list faulted rows
-        psStringAppend(&query, " %s", "AND chipProcessedImfile.fault = 0");
-    }
-
-    // treat limit == 0 as "no limit"
-    if (limit) {
-        psString limitString = psDBGenerateLimitSQL(limit);
-        psStringAppend(&query, " %s", limitString);
-        psFree(limitString);
-    }
-
-    if (!p_psDBRunQuery(config->dbh, query)) {
-        psError(PS_ERR_UNKNOWN, false, "database error");
-        psFree(query);
-        return false;
-    }
-    psFree(query);
-
-    psArray *output = p_psDBFetchResult(config->dbh);
-    if (!output) {
-        psError(PS_ERR_UNKNOWN, false, "database error");
-        return false;
-    }
-    if (!psArrayLength(output)) {
-        psTrace("chiptool", PS_LOG_INFO, "no rows found");
-        psFree(output);
-        return true;
-    }
-
-    bool simple = false;
-    {
-        bool status = false;
-        simple = psMetadataLookupBool(&status, config->args, "-simple");
-        if (!status) {
-            psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -simple");
-            return false;
-        }
-    }
-
-    if (!convertIdToStr(output)) {
-        psError(PS_ERR_UNKNOWN, false, "failed to convert id fields into a strings");
-        psFree(output);
-        return false;
-    }
-
-    // negative simple so the default is true
-    if (!ippdbPrintMetadatas(stdout, output, "chipProcessedImfile", !simple)) {
-        psError(PS_ERR_UNKNOWN, false, "failed to print array");
-        psFree(output);
-        return false;
-    }
-
-    psFree(output);
-
-    return true;
-}
-
-
-static bool updateprocessedimfileMode(pxConfig *config)
-{
-    PS_ASSERT_PTR_NON_NULL(config, false);
-
-    bool status = false;
-    psS16 code = psMetadataLookupS16(&status, config->args, "-code");
-    if (!status) {
-        psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -code");
-        return false;
-    }
-    if (code == INT16_MAX) {
-        psError(PS_ERR_UNKNOWN, true, "-code is required");
-        return false;
-    }
-
-    if (!pxSetFaultCode(config->dbh, "chipProcessedImfile", config->where, code)) {
-        psError(PS_ERR_UNKNOWN, false, "failed to set set fault flag");
-        return false;
-    }
-
-    return true;
-}
-
-
-static bool blockMode(pxConfig *config)
-{
-    PS_ASSERT_PTR_NON_NULL(config, false);
-
-    bool status = false;
-    psString label = psMetadataLookupStr(&status, config->args, "-label");
-    if (!status) {
-        psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -label");
-        return false;
-    }
-    if (!label) {
-        psError(PS_ERR_UNKNOWN, true, "-label is required");
-        return false;
-    }
-
-    if (!chipMaskInsert(config->dbh, label)) {
-        psError(PS_ERR_UNKNOWN, false, "database error");
-        return false;
-    }
-
-    return true;
-}
-
-
-static bool maskedMode(pxConfig *config)
-{
-    PS_ASSERT_PTR_NON_NULL(config, false);
-
-    psString query = psStringCopy("SELECT * FROM chipMask");
-
-    if (!p_psDBRunQuery(config->dbh, query)) {
-        psError(PS_ERR_UNKNOWN, false, "database error");
-        psFree(query);
-        return false;
-    }
-    psFree(query);
-
-    psArray *output = p_psDBFetchResult(config->dbh);
-    if (!output) {
-        psError(PS_ERR_UNKNOWN, false, "database error");
-        return false;
-    }
-    if (!psArrayLength(output)) {
-        psTrace("chiptool", PS_LOG_INFO, "no rows found");
-        psFree(output);
-        return true;
-    }
-
-    bool simple = false;
-    {
-        bool status = false;
-        simple = psMetadataLookupBool(&status, config->args, "-simple");
-        if (!status) {
-            psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -simple");
-            return false;
-        }
-    }
-
-    if (!convertIdToStr(output)) {
-        psError(PS_ERR_UNKNOWN, false, "failed to convert id fields into a strings");
-        psFree(output);
-        return false;
-    }
-
-    // negative simple so the default is true
-    if (!ippdbPrintMetadatas(stdout, output, "chipMask", !simple)) {
-        psError(PS_ERR_UNKNOWN, false, "failed to print array");
-        psFree(output);
-        return false;
-    }
-
-    psFree(output);
-
-    return true;
-}
-
-
-static bool unblockMode(pxConfig *config)
-{
-    PS_ASSERT_PTR_NON_NULL(config, false);
-
-    bool status = false;
-    psString label = psMetadataLookupStr(&status, config->args, "-label");
-    if (!status) {
-        psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -label");
-        return false;
-    }
-    if (!label) {
-        psError(PS_ERR_UNKNOWN, true, "-label is required");
-        return false;
-    }
-
-    char *query = "DELETE FROM chipMask WHERE label = '%s'";
-
-    if (!p_psDBRunQuery(config->dbh, query, label)) {
-        psError(PS_ERR_UNKNOWN, false, "database error");
-        psFree(query);
-        return false;
-    }
-
-    return true;
-}
-
-
-static bool chipProcessedCompleteExp(pxConfig *config)
-{
-    PS_ASSERT_PTR_NON_NULL(config, false);
-
-    // look for completed chipPendingExp
-    // migrate them to chipProccessedExp & camPendingExp
-    psString query = pxDataGet("chiptool_completely_processed_exp.sql");
-    if (!query) {
-        psError(PXTOOLS_ERR_DATA, false, "failed to retreive SQL statement");
-        return false;
-    }
-
-    if (!p_psDBRunQuery(config->dbh, query)) {
-        psError(PS_ERR_UNKNOWN, false, "database error");
-        psFree(query);
-        return false;
-    }
-    psFree(query);
-
-    psArray *output = p_psDBFetchResult(config->dbh);
-    if (!output) {
-        psError(PS_ERR_UNKNOWN, false, "database error");
-        return false;
-    }
-    if (!psArrayLength(output)) {
-        psTrace("chiptool", PS_LOG_INFO, "no rows found");
-        psFree(output);
-        return true;
-    }
-
-    // insert into chipProcessedExp
-    // insert into camPendingExp
-    // remove chipPendingExp entry
-    for (long i = 0; i < psArrayLength(output); i++) {
-        psMetadata *row = output->data[i];
-
-        // convert metadata into a chipPendingExp object
-        chipPendingExpRow *pendingExp = chipPendingExpObjectFromMetadata(row);
-        if (!pendingExp) {
-            psError(PS_ERR_UNKNOWN, false, "failed to convert metadata into chipPendingExp");
-            psFree(output);
-            return false;
-        }
-
-        // convert chipPendingExp object into a chipProcesseExp object
-        chipProcessedExpRow *processedExp = chipPendingToProcessedExp(config, pendingExp);
-        if (!processedExp) {
-            psError(PS_ERR_UNKNOWN, false, "failed to convert chipPendingExp to chipProcessedExp");
-            psFree(pendingExp);
-            psFree(output);
-            return false;
-        }
-
-        // delete the chipPendingExp object from the database
-        if (!chipPendingExpDeleteObject(config->dbh, pendingExp)) {
-            // there must be atleast 1 Imfile to get this far
-            psError(PS_ERR_UNKNOWN, false, "database error");
-            psFree(processedExp);
-            psFree(pendingExp);
-            psFree(output);
-            return false;
-        }
-        psFree(pendingExp);
-
-        // insert chipProccessedExp object into the database
-        if (!chipProcessedExpInsertObject(config->dbh, processedExp)) {
-            psError(PS_ERR_UNKNOWN, false, "database error");
-            psFree(processedExp);
-            psFree(output);
-            return false;
-        }
-
-        // camQueueChipID() can only be run after the chipProcessedExp entry
-        // has been inserted.
-        // queue the chip_id in the camPendingExp table
-        if (!camQueueChipID(config,
-                    processedExp->chip_id,
-                    processedExp->workdir,
-                    processedExp->label,
-                    processedExp->reduction,
-                    processedExp->expgroup,
-                    processedExp->dvodb
-        )) {
-            // rollback
-            if (!psDBRollback(config->dbh)) {
-                psError(PS_ERR_UNKNOWN, false, "database error");
-            }
-            psError(PS_ERR_UNKNOWN, false, "failed to queue camPendingExp");
-            psFree(processedExp);
-            psFree(output);
-            return false;
-        }
-
-        psFree(processedExp);
-    }
-
-    psFree(output);
-
-    return true;
-}
-
-
-static chipProcessedImfileRow *chipPendingToProcessedImfile(pxConfig *config, chipPendingImfileRow *imfile)
-{
-    PS_ASSERT_PTR_NON_NULL(config, NULL);
-    PS_ASSERT_PTR_NON_NULL(imfile, NULL);
-
-    bool status = false;
+    // chip_id, ext_tag, class_id are required
+    psString chip_id = psMetadataLookupStr(&status, config->args, "-chip_id");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -chip_id");
+        return false;
+    }
+    if (!chip_id) {
+        psError(PS_ERR_UNKNOWN, true, "-chip_id is required");
+        return false;
+    }
+
+    psString exp_tag = psMetadataLookupStr(&status, config->args, "-exp_tag");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -exp_tag");
+        return false;
+    }
+    if (!exp_tag) {
+        psError(PS_ERR_UNKNOWN, true, "-exp_tag is required");
+        return false;
+    }
+
+    psString class_id = psMetadataLookupStr(&status, config->args, "-class_id");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -class_id");
+        return false;
+    }
+    if (!class_id) {
+        psError(PS_ERR_UNKNOWN, true, "-class_id is required");
+        return false;
+    }
+
     psString uri = psMetadataLookupStr(&status, config->args, "-uri");
     if (!status) {
@@ -961,7 +505,13 @@
     }
 
-    return chipProcessedImfileRowAlloc(
-        imfile->chip_id,
-        imfile->class_id,
+    if (!psDBTransaction(config->dbh)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+
+    chipProcessedImfileRow *imfile = chipProcessedImfileRowAlloc(
+        (psU64)atoll(chip_id),
+        exp_tag,
+        class_id,
         uri,
         bg,
@@ -985,21 +535,335 @@
         path_base,
         code
-        );
-}
-
-
-static chipProcessedExpRow *chipPendingToProcessedExp(pxConfig *config, chipPendingExpRow *pendingExp)
+    );
+    if (!imfile) {
+        // rollback
+        if (!psDBRollback(config->dbh)) {
+            psError(PS_ERR_UNKNOWN, false, "database error");
+        }
+        psError(PS_ERR_UNKNOWN, false, "failed to alloc chipProcessedImfileRow object");
+        return false;
+    }
+    if (!chipProcessedImfileInsertObject(config->dbh, imfile)) {
+        // rollback
+        if (!psDBRollback(config->dbh)) {
+            psError(PS_ERR_UNKNOWN, false, "database error");
+        }
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(imfile);
+        return false;
+    }
+
+
+
+    // XXX I've decided to make the transaction cover the Exp migration as
+    // well.  Otherwise, if the last imfile in an exp is moved and the exp
+    // migration fails then the data base is left in a satuation where the exp
+    // migration can't happen.
+
+    if (!chipProcessedCompleteExp(config)) {
+        // rollback
+        if (!psDBRollback(config->dbh)) {
+            psError(PS_ERR_UNKNOWN, false, "database error");
+        }
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+
+    if (!psDBCommit(config->dbh)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+
+    return true;
+}
+
+
+static bool processedimfileMode(pxConfig *config)
 {
-    PS_ASSERT_PTR_NON_NULL(pendingExp, NULL);
-
-    return chipProcessedExpRowAlloc(
-        pendingExp->chip_id,
-        pendingExp->exp_tag,
-        pendingExp->guide_id,
-        pendingExp->workdir,
-        pendingExp->label,
-        pendingExp->reduction,
-        pendingExp->expgroup,
-        pendingExp->dvodb
-    );
-}
+    PS_ASSERT_PTR_NON_NULL(config, NULL);
+
+    bool status = false;
+    psU64 limit = psMetadataLookupU64(&status, config->args, "-limit");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -limit");
+        return false;
+    }
+
+    bool faulted = psMetadataLookupU64(&status, config->args, "-faulted");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -faulted");
+        return false;
+    }
+
+    // XXX does this need to be constrained so that it won't return any results
+    // if a match chipPendingExp hasn't been registered?
+    psString query = pxDataGet("chiptool_processedimfile.sql");
+    if (!query) {
+        psError(PXTOOLS_ERR_DATA, false, "failed to retreive SQL statement");
+        return false;
+    }
+
+    if (config->where) {
+        psString whereClause = psDBGenerateWhereConditionSQL(config->where, "chipProcessedImfile");
+        psStringAppend(&query, " AND %s", whereClause);
+        psFree(whereClause);
+    }
+
+    if (faulted) {
+        // list only faulted rows
+        psStringAppend(&query, " %s", "AND chipProcessedImfile.fault != 0");
+    } else {
+        // don't list faulted rows
+        psStringAppend(&query, " %s", "AND chipProcessedImfile.fault = 0");
+    }
+
+    // treat limit == 0 as "no limit"
+    if (limit) {
+        psString limitString = psDBGenerateLimitSQL(limit);
+        psStringAppend(&query, " %s", limitString);
+        psFree(limitString);
+    }
+
+    if (!p_psDBRunQuery(config->dbh, query)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(query);
+        return false;
+    }
+    psFree(query);
+
+    psArray *output = p_psDBFetchResult(config->dbh);
+    if (!output) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+    if (!psArrayLength(output)) {
+        psTrace("chiptool", PS_LOG_INFO, "no rows found");
+        psFree(output);
+        return true;
+    }
+
+    bool simple = false;
+    {
+        bool status = false;
+        simple = psMetadataLookupBool(&status, config->args, "-simple");
+        if (!status) {
+            psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -simple");
+            return false;
+        }
+    }
+
+    if (!convertIdToStr(output)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to convert id fields into a strings");
+        psFree(output);
+        return false;
+    }
+
+    // negative simple so the default is true
+    if (!ippdbPrintMetadatas(stdout, output, "chipProcessedImfile", !simple)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print array");
+        psFree(output);
+        return false;
+    }
+
+    psFree(output);
+
+    return true;
+}
+
+
+static bool updateprocessedimfileMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    bool status = false;
+    psS16 code = psMetadataLookupS16(&status, config->args, "-code");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -code");
+        return false;
+    }
+    if (code == INT16_MAX) {
+        psError(PS_ERR_UNKNOWN, true, "-code is required");
+        return false;
+    }
+
+    if (!pxSetFaultCode(config->dbh, "chipProcessedImfile", config->where, code)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to set set fault flag");
+        return false;
+    }
+
+    return true;
+}
+
+
+static bool blockMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    bool status = false;
+    psString label = psMetadataLookupStr(&status, config->args, "-label");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -label");
+        return false;
+    }
+    if (!label) {
+        psError(PS_ERR_UNKNOWN, true, "-label is required");
+        return false;
+    }
+
+    if (!chipMaskInsert(config->dbh, label)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+
+    return true;
+}
+
+
+static bool maskedMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    psString query = psStringCopy("SELECT * FROM chipMask");
+
+    if (!p_psDBRunQuery(config->dbh, query)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(query);
+        return false;
+    }
+    psFree(query);
+
+    psArray *output = p_psDBFetchResult(config->dbh);
+    if (!output) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+    if (!psArrayLength(output)) {
+        psTrace("chiptool", PS_LOG_INFO, "no rows found");
+        psFree(output);
+        return true;
+    }
+
+    bool simple = false;
+    {
+        bool status = false;
+        simple = psMetadataLookupBool(&status, config->args, "-simple");
+        if (!status) {
+            psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -simple");
+            return false;
+        }
+    }
+
+    if (!convertIdToStr(output)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to convert id fields into a strings");
+        psFree(output);
+        return false;
+    }
+
+    // negative simple so the default is true
+    if (!ippdbPrintMetadatas(stdout, output, "chipMask", !simple)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print array");
+        psFree(output);
+        return false;
+    }
+
+    psFree(output);
+
+    return true;
+}
+
+
+static bool unblockMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    bool status = false;
+    psString label = psMetadataLookupStr(&status, config->args, "-label");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -label");
+        return false;
+    }
+    if (!label) {
+        psError(PS_ERR_UNKNOWN, true, "-label is required");
+        return false;
+    }
+
+    char *query = "DELETE FROM chipMask WHERE label = '%s'";
+
+    if (!p_psDBRunQuery(config->dbh, query, label)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(query);
+        return false;
+    }
+
+    return true;
+}
+
+
+static bool chipProcessedCompleteExp(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    // look for completed chipPendingExp
+    // migrate them to chipProccessedExp & camPendingExp
+    psString query = pxDataGet("chiptool_completely_processed_exp.sql");
+    if (!query) {
+        psError(PXTOOLS_ERR_DATA, false, "failed to retreive SQL statement");
+        return false;
+    }
+
+    if (!p_psDBRunQuery(config->dbh, query)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(query);
+        return false;
+    }
+    psFree(query);
+
+    psArray *output = p_psDBFetchResult(config->dbh);
+    if (!output) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+    if (!psArrayLength(output)) {
+        psTrace("chiptool", PS_LOG_INFO, "no rows found");
+        psFree(output);
+        return true;
+    }
+
+    for (long i = 0; i < psArrayLength(output); i++) {
+        psMetadata *row = output->data[i];
+
+        chipRunRow *chipRun = chipRunObjectFromMetadata(row);
+        // set chipRun.state to 'stop'
+        if (!pxchipRunSetState(config, chipRun->chip_id, "stop")) {
+            psError(PS_ERR_UNKNOWN, false, "failed to change chipRun.state for chip_id: %" PRId64, chipRun->chip_id);
+            psFree(chipRun);
+            psFree(output);
+            return false;
+        }
+
+        // camQueueChipID() can only be run after chipRun.state has been set to
+        // stop
+        if (!pxcamQueueByChipID(config,
+                    chipRun->chip_id,
+                    chipRun->workdir,
+                    chipRun->label,
+                    chipRun->reduction,
+                    chipRun->expgroup,
+                    chipRun->dvodb
+        )) {
+            // rollback
+            if (!psDBRollback(config->dbh)) {
+                psError(PS_ERR_UNKNOWN, false, "database error");
+            }
+            psError(PS_ERR_UNKNOWN, false, "failed to queue camPendingExp");
+            psFree(chipRun);
+            psFree(output);
+            return false;
+        }
+        psFree(chipRun);
+    }
+
+    psFree(output);
+
+    return true;
+}
