Index: /branches/eam_branch_20080719/ippTools/src/dettool_normalizedexp.c
===================================================================
--- /branches/eam_branch_20080719/ippTools/src/dettool_normalizedexp.c	(revision 18628)
+++ /branches/eam_branch_20080719/ippTools/src/dettool_normalizedexp.c	(revision 18628)
@@ -0,0 +1,229 @@
+
+static bool tonormalizedexpMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    PXOPT_LOOKUP_U64(limit, config->args, "-limit", false, false);
+    PXOPT_LOOKUP_BOOL(simple, config->args, "-simple", false);
+
+    psString query = pxDataGet("dettool_tonormalizedexp.sql");
+    if (!query) {
+        psError(PXTOOLS_ERR_DATA, false, "failed to retreive SQL statement");
+        return false;
+    }
+
+    // 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("dettool", PS_LOG_INFO, "no rows found");
+        psFree(output);
+        return true;
+    }
+
+    // negative simple so the default is true
+    if (!ippdbPrintMetadatas(stdout, output, "detPendingNormExp", !simple)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print array");
+        psFree(output);
+        return false;
+    }
+
+    psFree(output);
+
+    return true;
+}
+static bool addnormalizedexpMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+    
+    PXOPT_LOOKUP_STR(det_id, config->args, "-det_id", true, false); // required
+    PXOPT_LOOKUP_S32(iteration, config->args, "-iteration", false, false);
+    PXOPT_LOOKUP_S16(code, config->args, "-code", false, false);
+    PXOPT_LOOKUP_STR(recipe, config->args, "-recip", (code == 0), false); // Required if code == 0
+    PXOPT_LOOKUP_F64(bg, config->args, "-bg", false, false);
+    PXOPT_LOOKUP_F64(bg_stdev, config->args, "-bg_stdev", false, false);
+    PXOPT_LOOKUP_F64(bg_mean_stdev, config->args, "-bg_mean_stdev", false, false);
+    PXOPT_LOOKUP_F64(user_1, config->args, "-user_1", false, false);
+    PXOPT_LOOKUP_F64(user_2, config->args, "-user_2", false, false);
+    PXOPT_LOOKUP_F64(user_3, config->args, "-user_3", false, false);
+    PXOPT_LOOKUP_F64(user_4, config->args, "-user_4", false, false);
+    PXOPT_LOOKUP_F64(user_5, config->args, "-user_5", false, false);
+    PXOPT_LOOKUP_STR(path_base, config->args, "-path_base", false, false);
+
+    psString query = pxDataGet("dettool_tonormalizedexp.sql");
+    if (!query) {
+        psError(PXTOOLS_ERR_DATA, false, "failed to retreive SQL statement");
+        return false;
+    }
+
+    // XXX in some other places, we put the arguments in the .sql and supply them to RunQuery
+    psStringAppend(&query, " WHERE det_id = %s AND iteration = %d", det_id, iteration);
+
+    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("dettool", PS_LOG_INFO, "no rows found");
+        psFree(output);
+        return true;
+    }
+    psFree(output);
+
+    // insert the new row into the detProcessedImfile table
+    if (!detNormalizedExpInsert(config->dbh,
+        (psS64)atoll(det_id),
+        iteration,
+        recipe,
+        bg,
+        bg_stdev,
+        bg_mean_stdev,
+        user_1,
+        user_2,
+        user_3,
+        user_4,
+        user_5,
+        path_base,
+        code
+    )) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+
+    return true;
+}
+
+
+static bool normalizedexpMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    psMetadata *where = psMetadataAlloc();
+    PXOPT_COPY_STR(config->args, where, "-det_id",    "det_id", "==");
+    PXOPT_COPY_S32(config->args, where, "-iteration", "iteration", "==");
+    PXOPT_COPY_STR(config->args, where, "-recip",  "recipe", "==");
+
+    PXOPT_LOOKUP_U64(limit, config->args, "-limit", false, false);
+    PXOPT_LOOKUP_BOOL(simple, config->args, "-simple", false);
+    PXOPT_LOOKUP_BOOL(faulted, config->args, "-faulted", false);
+
+    psString query = pxDataGet("dettool_normalizedexp.sql");
+    if (!query) {
+        psError(PXTOOLS_ERR_DATA, false, "failed to retreive SQL statement");
+        return false;
+    }
+
+    if (psListLength(where->list)) {
+        psString whereClause = psDBGenerateWhereSQL(where, NULL);
+        psStringAppend(&query, " %s", whereClause);
+        psFree(whereClause);
+    }
+    psFree(where);
+
+    if (faulted) {
+        // list only faulted rows
+        psStringAppend(&query, " %s", "AND detNormalizedExp.fault != 0");
+    } else {
+        // don't list faulted rows
+        psStringAppend(&query, " %s", "AND detNormalizedExp.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("dettool", PS_LOG_INFO, "no rows found");
+        psFree(output);
+        return true;
+    }
+
+    // negative simple so the default is true
+    if (!ippdbPrintMetadatas(stdout, output, "detNormalizedExp", !simple)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print array");
+        psFree(output);
+        return false;
+    }
+
+    psFree(output);
+
+    return true;
+}
+
+
+
+static bool revertnormalizedexpMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    psMetadata *where = psMetadataAlloc();
+    PXOPT_COPY_STR(config->args, where, "-det_id",    "det_id", "==");
+    PXOPT_COPY_S32(config->args, where, "-iteration", "iteration", "==");
+    PXOPT_COPY_S16(config->args, where, "-code",      "fault", "==");
+
+    psString query = pxDataGet("dettool_revertnormalizedexp.sql");
+    if (!query) {
+        psError(PXTOOLS_ERR_DATA, false, "failed to retreive SQL statement");
+        return false;
+    }
+
+    if (psListLength(where->list)) {
+        psString whereClause = psDBGenerateWhereConditionSQL(where, "detNormalizedExp");
+        psStringAppend(&query, " AND %s", whereClause);
+        psFree(whereClause);
+    }
+    psFree(where);
+
+    if (!p_psDBRunQuery(config->dbh, query)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(query);
+        return false;
+    }
+    psFree(query);
+
+    if (psDBAffectedRows(config->dbh) < 1) {
+        psError(PS_ERR_UNKNOWN, false, "should have affected atleast 1 row");
+        return false;
+    }
+
+    return true;
+}
Index: /branches/eam_branch_20080719/ippTools/src/dettool_normalizedimfile.c
===================================================================
--- /branches/eam_branch_20080719/ippTools/src/dettool_normalizedimfile.c	(revision 18628)
+++ /branches/eam_branch_20080719/ippTools/src/dettool_normalizedimfile.c	(revision 18628)
@@ -0,0 +1,208 @@
+
+static bool tonormalizeMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    PXOPT_LOOKUP_U64(limit, config->args, "-limit", false, false);
+    PXOPT_LOOKUP_BOOL(simple, config->args, "-simple", false);
+
+    psString query = pxDataGet("dettool_tonormalize.sql");
+    if (!query) {
+        psError(PXTOOLS_ERR_DATA, false, "failed to retreive SQL statement");
+        return false;
+    }
+
+    // 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("dettool", PS_LOG_INFO, "no rows found");
+        psFree(output);
+        return true;
+    }
+
+    // negative simple so the default is true
+    if (!ippdbPrintMetadatas(stdout, output, "detPendingNormImfile", !simple)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print array");
+        psFree(output);
+        return false;
+    }
+
+    psFree(output);
+
+    return true;
+}
+
+static bool addnormalizedimfileMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    // required
+    PXOPT_LOOKUP_STR(det_id, config->args, "-det_id", true, false);
+    PXOPT_LOOKUP_S32(iteration, config->args, "-iteration", false, false);
+    PXOPT_LOOKUP_STR(class_id, config->args, "-class_id", true, false);
+
+    PXOPT_LOOKUP_S16(code, config->args, "-code", false, false);
+
+    // Required if code == 0
+    PXOPT_LOOKUP_STR(uri, config->args, "-uri", (code == 0), false);
+
+    // optional
+    PXOPT_LOOKUP_F64(bg, config->args, "-bg", false, false);
+    PXOPT_LOOKUP_F64(bg_stdev, config->args, "-bg_stdev", false, false);
+    PXOPT_LOOKUP_F64(bg_mean_stdev, config->args, "-bg_mean_stdev", false, false);
+    PXOPT_LOOKUP_F64(user_1, config->args, "-user_1", false, false);
+    PXOPT_LOOKUP_F64(user_2, config->args, "-user_2", false, false);
+    PXOPT_LOOKUP_F64(user_3, config->args, "-user_3", false, false);
+    PXOPT_LOOKUP_F64(user_4, config->args, "-user_4", false, false);
+    PXOPT_LOOKUP_F64(user_5, config->args, "-user_5", false, false);
+    PXOPT_LOOKUP_STR(path_base, config->args, "-path_base", false, false);
+
+    if (!detNormalizedImfileInsert(config->dbh,
+        (psS64)atoll(det_id),
+        iteration,
+        class_id,
+        uri,
+        bg,
+        bg_stdev,
+        bg_mean_stdev,
+        user_1,
+        user_2,
+        user_3,
+        user_4,
+        user_5,
+        path_base,
+        code
+    )) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+
+    return true;
+}
+
+static bool normalizedimfileMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    psMetadata *where = psMetadataAlloc();
+    PXOPT_COPY_STR(config->args, where, "-det_id",    "det_id", "==");
+    PXOPT_COPY_S32(config->args, where, "-iteration", "iteration", "==");
+    PXOPT_COPY_STR(config->args, where, "-class_id",  "class_id", "==");
+    PXOPT_COPY_STR(config->args, where, "-recip",     "recipe", "==");
+
+    PXOPT_LOOKUP_U64(limit, config->args, "-limit", false, false);
+    PXOPT_LOOKUP_BOOL(simple, config->args, "-simple", false);
+    PXOPT_LOOKUP_BOOL(faulted, config->args, "-faulted", false);
+
+    psString query = pxDataGet("dettool_normalizedimfile.sql");
+    if (!query) {
+        psError(PXTOOLS_ERR_DATA, false, "failed to retreive SQL statement");
+        return false;
+    }
+
+    if (psListLength(where->list)) {
+        psString whereClause = psDBGenerateWhereConditionSQL(where, "detNormalizedImfile");
+        psStringAppend(&query, " AND %s", whereClause);
+        psFree(whereClause);
+    }
+    psFree(where);
+
+    if (faulted) {
+        // list only faulted rows
+        psStringAppend(&query, " %s", "AND detNormalizedImfile.fault != 0");
+    } else {
+        // don't list faulted rows
+        psStringAppend(&query, " %s", "AND detNormalizedImfile.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("dettool", PS_LOG_INFO, "no rows found");
+        psFree(output);
+        return true;
+    }
+
+    // negative simple so the default is true
+    if (!ippdbPrintMetadatas(stdout, output, "detNormalizedImfile", !simple)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print array");
+        psFree(output);
+        return false;
+    }
+
+    psFree(output);
+
+    return true;
+}
+
+static bool revertnormalizedimfileMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    psMetadata *where = psMetadataAlloc();
+    PXOPT_COPY_STR(config->args, where, "-det_id",    "det_id", "==");
+    PXOPT_COPY_S32(config->args, where, "-iteration", "iteration", "==");
+    PXOPT_COPY_STR(config->args, where, "-class_id",  "class_id", "==");
+    PXOPT_COPY_S16(config->args, where, "-code",      "fault", "==");
+
+    psString query = pxDataGet("dettool_revertnormalizedimfile.sql");
+    if (!query) {
+        psError(PXTOOLS_ERR_DATA, false, "failed to retreive SQL statement");
+        return false;
+    }
+
+    if (psListLength(where->list)) {
+        psString whereClause = psDBGenerateWhereConditionSQL(where, "detNormalizedImfile");
+        psStringAppend(&query, " AND %s", whereClause);
+        psFree(whereClause);
+    }
+    psFree(where);
+
+    if (!p_psDBRunQuery(config->dbh, query)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(query);
+        return false;
+    }
+    psFree(query);
+
+    if (psDBAffectedRows(config->dbh) < 1) {
+        psError(PS_ERR_UNKNOWN, false, "should have affected atleast 1 row");
+        return false;
+    }
+
+    return true;
+}
Index: /branches/eam_branch_20080719/ippTools/src/dettool_normalizedstat.c
===================================================================
--- /branches/eam_branch_20080719/ippTools/src/dettool_normalizedstat.c	(revision 18628)
+++ /branches/eam_branch_20080719/ippTools/src/dettool_normalizedstat.c	(revision 18628)
@@ -0,0 +1,188 @@
+static bool tonormalizedstatMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    PXOPT_LOOKUP_U64(limit, config->args, "-limit", false, false);
+    PXOPT_LOOKUP_BOOL(simple, config->args, "-simple", false);
+
+    psString query = pxDataGet("dettool_tonormalizedstat.sql");
+    if (!query) {
+        psError(PXTOOLS_ERR_DATA, false, "failed to retreive SQL statement");
+        return false;
+    }
+
+    // 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("dettool", PS_LOG_INFO, "no rows found");
+        psFree(output);
+        return true;
+    }
+
+    // negative simple so the default is true
+    if (!ippdbPrintMetadatas(stdout, output, "detPendingNormStatImfile", !simple)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print array");
+        psFree(output);
+        return false;
+    }
+
+    psFree(output);
+
+    return true;
+}
+
+static bool addnormalizedstatMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, NULL);
+
+    // required
+    PXOPT_LOOKUP_STR(det_id, config->args, "-det_id", true, false);
+    PXOPT_LOOKUP_STR(class_id, config->args, "-class_id", true, false);
+
+    // optional
+    PXOPT_LOOKUP_F32(norm, config->args, "-norm", false, false);
+
+    // default
+    PXOPT_LOOKUP_S32(iteration, config->args, "-iteration", false, false);
+    PXOPT_LOOKUP_S16(code, config->args, "-code", false, false);
+
+    if (!detNormalizedStatImfileInsert(config->dbh,
+        (psS64)atoll(det_id),
+        iteration,
+        class_id,
+        norm,
+        code
+    )) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+
+    return true;
+}
+
+
+static bool normalizedstatMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    psMetadata *where = psMetadataAlloc();
+    PXOPT_COPY_STR(config->args, where, "-det_id",    "det_id", "==");
+    PXOPT_COPY_S32(config->args, where, "-iteration", "iteration", "==");
+    PXOPT_COPY_STR(config->args, where, "-class_id",  "class_id", "==");
+
+    PXOPT_LOOKUP_U64(limit, config->args, "-limit", false, false);
+    PXOPT_LOOKUP_BOOL(faulted, config->args, "-faulted", false);
+    PXOPT_LOOKUP_BOOL(simple, config->args, "-simple", false);
+
+    psString query = pxDataGet("dettool_normalizedstat.sql");
+    if (!query) {
+        psError(PXTOOLS_ERR_DATA, false, "failed to retreive SQL statement");
+        return false;
+    }
+
+    if (faulted) {
+        // list only faulted rows
+        psStringAppend(&query, " %s", "WHERE fault != 0");
+    } else {
+        // don't list faulted rows
+        psStringAppend(&query, " %s", "WHERE fault = 0");
+    }
+
+    if (psListLength(where->list)) {
+        psString whereClause = psDBGenerateWhereConditionSQL(where, NULL);
+        psStringAppend(&query, " AND %s", whereClause);
+        psFree(whereClause);
+    }
+    psFree(where);
+
+    // 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("dettool", PS_LOG_INFO, "no rows found");
+        psFree(output);
+        return true;
+    }
+
+    // negative simple so the default is true
+    if (!ippdbPrintMetadatas(stdout, output, "detNormalizedStatImfile", !simple)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print array");
+        psFree(output);
+        return false;
+    }
+
+    psFree(output);
+
+    return true;
+}
+
+static bool revertnormalizedstatMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    psMetadata *where = psMetadataAlloc();
+    PXOPT_COPY_STR(config->args, where, "-det_id",    "det_id", "==");
+    PXOPT_COPY_S32(config->args, where, "-iteration", "iteration", "==");
+    PXOPT_COPY_STR(config->args, where, "-class_id",  "class_id", "==");
+    PXOPT_COPY_S16(config->args, where, "-code",      "fault", "==");
+
+    psString query = pxDataGet("dettool_revertnormalizedstat.sql");
+    if (!query) {
+        psError(PXTOOLS_ERR_DATA, false, "failed to retreive SQL statement");
+        return false;
+    }
+
+    if (psListLength(where->list)) {
+        psString whereClause = psDBGenerateWhereConditionSQL(where, "detNormalizedStatImfile");
+        psStringAppend(&query, " AND %s", whereClause);
+        psFree(whereClause);
+    }
+    psFree(where);
+
+    if (!p_psDBRunQuery(config->dbh, query)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(query);
+        return false;
+    }
+    psFree(query);
+
+    if (psDBAffectedRows(config->dbh) < 1) {
+        psError(PS_ERR_UNKNOWN, false, "should have affected atleast 1 row");
+        return false;
+    }
+
+    return true;
+}
Index: /branches/eam_branch_20080719/ippTools/src/dettool_processedexp.c
===================================================================
--- /branches/eam_branch_20080719/ippTools/src/dettool_processedexp.c	(revision 18628)
+++ /branches/eam_branch_20080719/ippTools/src/dettool_processedexp.c	(revision 18628)
@@ -0,0 +1,240 @@
+static bool toprocessedexpMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    PXOPT_LOOKUP_U64(limit, config->args, "-limit", false, false);
+    PXOPT_LOOKUP_BOOL(simple, config->args, "-simple", false);
+
+    psString query = pxDataGet("dettool_toprocessedexp.sql");
+    if (!query) {
+        psError(PXTOOLS_ERR_DATA, false, "failed to retreive SQL statement");
+        return false;
+    }
+
+    // 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("dettool", PS_LOG_INFO, "no rows found");
+        psFree(output);
+        return true;
+    }
+
+    // negative simple so the default is true
+    if (!ippdbPrintMetadatas(stdout, output, "detPendingProcessedExp", !simple)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print array");
+        psFree(output);
+        return false;
+    }
+
+    psFree(output);
+
+    return true;
+}
+
+static bool addprocessedexpMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    // det_id, exp_id, recip, -bg, -bg_stdev
+    // are required
+    PXOPT_LOOKUP_STR(det_id, config->args, "-det_id", true, false);
+    PXOPT_LOOKUP_STR(exp_id, config->args, "-exp_id", true, false);
+
+    // default values
+    PXOPT_LOOKUP_S16(code, config->args, "-code", false, false);
+
+    // Required if code == 0
+    PXOPT_LOOKUP_STR(recipe, config->args, "-recip", (code == 0), false);
+
+    // optional
+    PXOPT_LOOKUP_F64(bg, config->args, "-bg", false, false);
+    PXOPT_LOOKUP_F64(bg_stdev, config->args, "-bg_stdev", false, false);
+    PXOPT_LOOKUP_F64(bg_mean_stdev, config->args, "-bg_mean_stdev", false, false);
+    PXOPT_LOOKUP_F64(fringe_0, config->args, "-fringe_0", false, false);
+    PXOPT_LOOKUP_F64(fringe_1, config->args, "-fringe_1", false, false);
+    PXOPT_LOOKUP_F64(fringe_2, config->args, "-fringe_2", false, false);
+    PXOPT_LOOKUP_F64(user_1, config->args, "-user_1", false, false);
+    PXOPT_LOOKUP_F64(user_2, config->args, "-user_2", false, false);
+    PXOPT_LOOKUP_F64(user_3, config->args, "-user_3", false, false);
+    PXOPT_LOOKUP_F64(user_4, config->args, "-user_4", false, false);
+    PXOPT_LOOKUP_F64(user_5, config->args, "-user_5", false, false);
+    PXOPT_LOOKUP_STR(path_base, config->args, "-path_base", true, false);
+
+    psString query = pxDataGet("dettool_addprocessedexp.sql");
+    if (!query) {
+        psError(PXTOOLS_ERR_DATA, false, "failed to retreive SQL statement");
+        return false;
+    }
+
+    if (!p_psDBRunQuery(config->dbh, query, det_id, exp_id)) {
+        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("dettool", PS_LOG_INFO, "no rows found");
+        psFree(output);
+        return true;
+    }
+    psFree(output);
+
+    // create a new detProcessedImfile object
+    detProcessedExpRow *detRow = detProcessedExpRowAlloc(
+        (psS64)atoll(det_id),
+        (psS64)atoll(exp_id),
+        recipe,
+        bg,
+        bg_stdev,
+        bg_mean_stdev,
+        fringe_0,
+        fringe_1,
+        fringe_2,
+        user_1,
+        user_2,
+        user_3,
+        user_4,
+        user_5,
+        path_base,
+        code
+    );
+
+    // insert the new row into the detProcessedImfile table
+    if (!detProcessedExpInsertObject(config->dbh, detRow)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(detRow);
+        return false;
+    }
+
+    psFree(detRow);
+
+    return true;
+}
+
+static bool processedexpMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    psMetadata *where = psMetadataAlloc();
+    PXOPT_COPY_STR(config->args, where, "-det_id", "det_id", "==");
+    PXOPT_COPY_STR(config->args, where, "-exp_id", "exp_id", "==");
+
+    PXOPT_LOOKUP_U64(limit, config->args, "-limit", false, false);
+    PXOPT_LOOKUP_BOOL(faulted, config->args, "-faulted", false);
+    PXOPT_LOOKUP_BOOL(simple, config->args, "-simple", false);
+
+    psString query = psStringCopy("SELECT * FROM detProcessedExp");
+
+    if (psListLength(where->list)) {
+        psString whereClause = psDBGenerateWhereSQL(where, NULL);
+        psStringAppend(&query, " %s", whereClause);
+        psFree(whereClause);
+    }
+    psFree(where);
+
+    if (faulted) {
+        // list only faulted rows
+        psStringAppend(&query, " %s", "AND detProcessedExp.fault != 0");
+    } else {
+        // don't list faulted rows
+        psStringAppend(&query, " %s", "AND detProcessedExp.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("dettool", PS_LOG_INFO, "no rows found");
+        psFree(output);
+        return true;
+    }
+
+    // negative simple so the default is true
+    if (!ippdbPrintMetadatas(stdout, output, "detProcessedExp", !simple)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print array");
+        psFree(output);
+        return false;
+    }
+
+    psFree(output);
+
+    return true;
+}
+
+
+static bool revertprocessedexpMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    psMetadata *where = psMetadataAlloc();
+    PXOPT_COPY_STR(config->args, where, "-det_id", "det_id", "==");
+    PXOPT_COPY_STR(config->args, where, "-exp_id", "exp_id", "==");
+    PXOPT_COPY_S16(config->args, where, "-code",   "fault", "==");
+
+    psString query = pxDataGet("dettool_revertprocessedexp.sql");
+    if (!query) {
+        psError(PXTOOLS_ERR_DATA, false, "failed to retreive SQL statement");
+        return false;
+    }
+
+    if (psListLength(where->list)) {
+        psString whereClause = psDBGenerateWhereConditionSQL(where, "detProcessedExp");
+        psStringAppend(&query, " AND %s", whereClause);
+        psFree(whereClause);
+    }
+    psFree(where);
+
+    if (!p_psDBRunQuery(config->dbh, query)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(query);
+        return false;
+    }
+    psFree(query);
+
+    if (psDBAffectedRows(config->dbh) < 1) {
+        psError(PS_ERR_UNKNOWN, false, "should have affected atleast 1 row");
+        return false;
+    }
+
+    return true;
+}
+
Index: /branches/eam_branch_20080719/ippTools/src/dettool_processedimfile.c
===================================================================
--- /branches/eam_branch_20080719/ippTools/src/dettool_processedimfile.c	(revision 18628)
+++ /branches/eam_branch_20080719/ippTools/src/dettool_processedimfile.c	(revision 18628)
@@ -0,0 +1,274 @@
+
+static bool toprocessedimfileMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    psMetadata *where = psMetadataAlloc();
+    PXOPT_COPY_STR(config->args, where, "-det_id",   "det_id", "==");
+    PXOPT_COPY_STR(config->args, where, "-exp_id",   "exp_id", "==");
+    PXOPT_COPY_STR(config->args, where, "-class_id", "class_id", "==");
+
+    PXOPT_LOOKUP_U64(limit, config->args, "-limit", false, false);
+    PXOPT_LOOKUP_BOOL(simple, config->args, "-simple", false);
+
+    psString query = pxDataGet("dettool_toprocessedimfile.sql");
+    if (!query) {
+        psError(PXTOOLS_ERR_DATA, false, "failed to retreive SQL statement");
+        return false;
+    }
+
+    if (psListLength(where->list)) {
+        psString whereClause = psDBGenerateWhereConditionSQL(where, "rawImfile");
+        psStringAppend(&query, " AND %s", whereClause);
+        psFree(whereClause);
+    }
+    psFree (where);
+
+    // 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("dettool", PS_LOG_INFO, "no rows found");
+        psFree(output);
+        return true;
+    }
+
+    // negative simple so the default is true
+    if (!ippdbPrintMetadatas(stdout, output, "detPendingProcessedImfile", !simple)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print array");
+        psFree(output);
+        return false;
+    }
+
+    psFree(output);
+
+    return true;
+}
+
+static bool addprocessedimfileMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    // det_id, exp_id, class_id, uri, recipe, -bg, -bg_stdev are required
+    PXOPT_LOOKUP_STR(det_id, config->args, "-det_id", true, false);
+    PXOPT_LOOKUP_STR(exp_id, config->args, "-exp_id", true, false);
+    PXOPT_LOOKUP_STR(class_id, config->args, "-class_id", true, false);
+
+    // default values
+    PXOPT_LOOKUP_S16(code, config->args, "-code", false, false);
+
+    // Required if code == 0
+    PXOPT_LOOKUP_STR(uri, config->args, "-uri", (code == 0), false);
+    PXOPT_LOOKUP_STR(recipe, config->args, "-recip", (code == 0), false);
+
+    // optional
+    PXOPT_LOOKUP_F64(bg, config->args, "-bg", false, false);
+    PXOPT_LOOKUP_F64(bg_stdev, config->args, "-bg_stdev", false, false);
+    PXOPT_LOOKUP_F64(bg_mean_stdev, config->args, "-bg_mean_stdev", false, false);
+    PXOPT_LOOKUP_F64(fringe_0, config->args, "-fringe_0", false, false);
+    PXOPT_LOOKUP_F64(fringe_1, config->args, "-fringe_1", false, false);
+    PXOPT_LOOKUP_F64(fringe_2, config->args, "-fringe_2", false, false);
+    PXOPT_LOOKUP_F64(user_1, config->args, "-user_1", false, false);
+    PXOPT_LOOKUP_F64(user_2, config->args, "-user_2", false, false);
+    PXOPT_LOOKUP_F64(user_3, config->args, "-user_3", false, false);
+    PXOPT_LOOKUP_F64(user_4, config->args, "-user_4", false, false);
+    PXOPT_LOOKUP_F64(user_5, config->args, "-user_5", false, false);
+    PXOPT_LOOKUP_STR(path_base, config->args, "-path_base", false, false);
+
+    // find the matching rawImfile by exp_id/class_id
+    psMetadata *where = psMetadataAlloc();
+    PXOPT_COPY_STR(config->args, where, "-exp_id",   "exp_id", "==");
+    PXOPT_COPY_STR(config->args, where, "-class_id", "class_id", "==");
+
+    psArray *rawImfiles = rawImfileSelectRowObjects(config->dbh, where, 0);
+    psFree(where);
+
+    if (!rawImfiles) {
+        psError(PS_ERR_UNKNOWN, false, "no rawImfile rows found ");
+        return false;
+    }
+
+    // create a new detProcessedImfile object
+    detProcessedImfileRow *detRow = detProcessedImfileRowAlloc(
+        (psS64)atoll(det_id),
+        (psS64)atoll(exp_id),
+        class_id,
+        uri,
+        recipe,
+        bg,
+        bg_stdev,
+        bg_mean_stdev,
+        fringe_0,
+        fringe_1,
+        fringe_2,
+        user_1,
+        user_2,
+        user_3,
+        user_4,
+        user_5,
+        path_base,
+        code
+    );
+    psFree(rawImfiles);
+
+    // insert the new row into the detProcessedImfile table
+    if (!detProcessedImfileInsertObject(config->dbh, detRow)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(detRow);
+        return false;
+    }
+
+    psFree(detRow);
+
+    return true;
+}
+
+static bool processedimfileMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    char *value = NULL;
+
+    PXOPT_LOOKUP_BOOL(included, config->args, "-included", false);
+    PXOPT_LOOKUP_BOOL(faulted, config->args, "-faulted", false);
+    PXOPT_LOOKUP_U64(limit, config->args, "-limit", false, false);
+    PXOPT_LOOKUP_BOOL(simple, config->args, "-simple", false);
+
+    psMetadata *where = psMetadataAlloc();
+    PXOPT_COPY_STR(config->args, where, "-det_id", "det_id", "==");
+    PXOPT_COPY_STR(config->args, where, "-exp_id", "exp_id", "==");
+    PXOPT_COPY_STR(config->args, where, "-class_id", "class_id", "==");
+
+    psString query = pxDataGet("dettool_processedimfile.sql");
+    if (!query) {
+        psError(PXTOOLS_ERR_DATA, false, "failed to retreive SQL statement");
+        psFree(where);
+        return false;
+    }
+
+    // add the two required restrictions: detRun.state and detRun.mode
+    // NOTE the above query requires one of the following two WHERE statements
+    {
+        bool status;
+        if ((value = psMetadataLookupStr(&status, config->args, "-select_state"))) {
+            psStringAppend(&query, " WHERE detRun.state = '%s'", value);
+        } else {
+            psStringAppend(&query, " WHERE detRun.state = 'run'");
+        }
+        if ((value = psMetadataLookupStr(&status, config->args, "-select_mode"))) {
+            psStringAppend(&query, " AND detRun.mode = '%s'", value);
+        } else {
+            psStringAppend(&query, " AND detRun.mode = 'master'");
+        }
+    }
+
+    if (psListLength(where->list)) {
+        psString whereClause = psDBGenerateWhereConditionSQL(where, "detProcessedImfile");
+        psStringAppend(&query, " AND %s", whereClause);
+        psFree(whereClause);
+    }
+    psFree (where);
+
+    // restrict search to included imfiles
+    if (included) {
+	psStringAppend(&query, " AND detInputExp.include = 1");
+    }
+
+    if (faulted) {
+        // list only faulted rows
+        psStringAppend(&query, " %s", "AND detProcessedImfile.fault != 0");
+    } else {
+        // don't list faulted rows
+        psStringAppend(&query, " %s", "AND detProcessedImfile.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("dettool", PS_LOG_INFO, "no rows found");
+        psFree(output);
+        return true;
+    }
+
+    // negative simple so the default is true
+    if (!ippdbPrintMetadatas(stdout, output, "rawImfile", !simple)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print array");
+        psFree(output);
+        return false;
+    }
+
+    psFree(output);
+
+    return true;
+}
+
+static bool revertprocessedimfileMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    psMetadata *where = psMetadataAlloc();
+    PXOPT_COPY_STR(config->args, where, "-det_id", "det_id", "==");
+    PXOPT_COPY_STR(config->args, where, "-exp_id", "exp_id", "==");
+    PXOPT_COPY_STR(config->args, where, "-class_id", "class_id", "==");
+    PXOPT_COPY_S16(config->args, where, "-code", "fault", "==");
+
+    psString query = pxDataGet("dettool_revertprocessedimfile.sql");
+    if (!query) {
+        psError(PXTOOLS_ERR_DATA, false, "failed to retreive SQL statement");
+        return false;
+    }
+
+    if (psListLength(where->list)) {
+        psString whereClause = psDBGenerateWhereConditionSQL(where, "detProcessedImfile");
+        psStringAppend(&query, " AND %s", whereClause);
+        psFree(whereClause);
+    }
+    psFree(where);
+
+    if (!p_psDBRunQuery(config->dbh, query)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(query);
+        return false;
+    }
+    psFree(query);
+
+    if (psDBAffectedRows(config->dbh) < 1) {
+        psError(PS_ERR_UNKNOWN, false, "should have affected atleast 1 row");
+        return false;
+    }
+
+    return true;
+}
Index: /branches/eam_branch_20080719/ippTools/src/dettool_residexp.c
===================================================================
--- /branches/eam_branch_20080719/ippTools/src/dettool_residexp.c	(revision 18628)
+++ /branches/eam_branch_20080719/ippTools/src/dettool_residexp.c	(revision 18628)
@@ -0,0 +1,323 @@
+/*
+  The SQL returns a list of exposures for which all component class ids have had residuals
+  created.  This list includes the detrend id, iteration, exposure id, detrend type, and
+  whether the exposure was included in the stack for this iteration.
+*/
+
+static bool toresidexpMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    PXOPT_LOOKUP_U64(limit, config->args, "-limit", false, false);
+    PXOPT_LOOKUP_BOOL(simple, config->args, "-simple", false);
+
+    psString query = pxDataGet("dettool_toresidexp.sql");
+    if (!query) {
+        psError(PXTOOLS_ERR_DATA, false, "failed to retreive SQL statement");
+        return false;
+    }
+
+    // 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("dettool", PS_LOG_INFO, "no rows found");
+        psFree(output);
+        return true;
+    }
+
+    // negative simple so the default is true
+    if (!ippdbPrintMetadatas(stdout, output, "detPendingResidExp", !simple)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print array");
+        psFree(output);
+        return false;
+    }
+
+    psFree(output);
+
+    return true;
+}
+
+/*
+  The SQL returns a list of exposures for which all component class ids have had residuals
+  created.  This list includes the detrend id, iteration, exposure id, detrend type, and
+  whether the exposure was included in the stack for this iteration.
+*/
+
+static bool addresidexpMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    // limit search by det_id, iteration, exp_id
+    psMetadata *where = psMetadataAlloc();
+    PXOPT_COPY_STR(config->args, where, "-det_id", "det_id", "==");
+    PXOPT_COPY_S32(config->args, where, "-iteration", "iteration", "==");
+    PXOPT_COPY_STR(config->args, where, "-exp_id", "exp_id", "==");
+
+    PXOPT_LOOKUP_STR(det_id, config->args, "-det_id", true, false); // required
+    PXOPT_LOOKUP_S32(iteration, config->args, "-iteration", false, false);
+    PXOPT_LOOKUP_STR(exp_id, config->args, "-exp_id", true, false); // required
+    PXOPT_LOOKUP_S16(code, config->args, "-code", false, false);
+    PXOPT_LOOKUP_STR(recipe, config->args, "-recip", (code == 0), false); // Required if code == 0
+    PXOPT_LOOKUP_F64(bg, config->args, "-bg", false, false);
+    PXOPT_LOOKUP_F64(bg_stdev, config->args, "-bg_stdev", false, false);
+    PXOPT_LOOKUP_F64(bg_mean_stdev, config->args, "-bg_mean_stdev", false, false);
+    PXOPT_LOOKUP_F64(bg_skewness, config->args, "-bg_skewness", false, false);
+    PXOPT_LOOKUP_F64(bg_kurtosis, config->args, "-bg_kurtosis", false, false);
+    PXOPT_LOOKUP_F64(bin_stdev, config->args, "-bin_stdev", false, false);
+    PXOPT_LOOKUP_F64(fringe_0, config->args, "-fringe_0", false, false);
+    PXOPT_LOOKUP_F64(fringe_1, config->args, "-fringe_1", false, false);
+    PXOPT_LOOKUP_F64(fringe_2, config->args, "-fringe_2", false, false);
+    PXOPT_LOOKUP_F64(fringe_resid_0, config->args, "-fringe_resid_0", false, false);
+    PXOPT_LOOKUP_F64(fringe_resid_1, config->args, "-fringe_resid_1", false, false);
+    PXOPT_LOOKUP_F64(fringe_resid_2, config->args, "-fringe_resid_2", false, false);
+    PXOPT_LOOKUP_F64(user_1, config->args, "-user_1", false, false);
+    PXOPT_LOOKUP_F64(user_2, config->args, "-user_2", false, false);
+    PXOPT_LOOKUP_F64(user_3, config->args, "-user_3", false, false);
+    PXOPT_LOOKUP_F64(user_4, config->args, "-user_4", false, false);
+    PXOPT_LOOKUP_F64(user_5, config->args, "-user_5", false, false);
+    PXOPT_LOOKUP_STR(path_base, config->args, "-path_base", false, false);
+    PXOPT_LOOKUP_BOOL(reject, config->args, "-reject", false);
+
+    psString query = pxDataGet("dettool_toresidexp.sql");
+    if (!query) {
+        psError(PXTOOLS_ERR_DATA, false, "failed to retreive SQL statement");
+        return false;
+    }
+
+    if (psListLength(where->list)) {
+	psString whereClause = psDBGenerateWhereConditionSQL(where, NULL);
+	psStringAppend(&query, " WHERE %s", whereClause);
+	psFree(whereClause);
+    }
+    psFree(where);
+
+    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 detResidImfile rows found");
+        psFree(output);
+        return false;
+    }
+    psFree(output);
+
+    if (!detResidExpInsert(config->dbh,
+            (psS64)atoll(det_id),
+            iteration,
+            (psS64)atoll(exp_id),
+            recipe,
+            bg,
+            bg_stdev,
+            bg_mean_stdev,
+            bg_skewness,
+            bg_kurtosis,
+            bin_stdev,
+            fringe_0,
+            fringe_1,
+            fringe_2,
+            fringe_resid_0,
+            fringe_resid_1,
+            fringe_resid_2,
+            user_1,
+            user_2,
+            user_3,
+            user_4,
+            user_5,
+            path_base,
+            !reject,
+            code
+        )) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+
+    return true;
+}
+
+static bool residexpMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    psMetadata *where = psMetadataAlloc();
+    PXOPT_COPY_STR(config->args, where, "-det_id",    "det_id", "==");
+    PXOPT_COPY_S32(config->args, where, "-iteration", "iteration", "==");
+    PXOPT_COPY_STR(config->args, where, "-exp_id",    "exp_id", "==");
+    PXOPT_COPY_STR(config->args, where, "-recip",     "recipe", "==");
+
+    PXOPT_LOOKUP_U64(limit,    config->args, "-limit", false, false);
+    PXOPT_LOOKUP_BOOL(simple,  config->args, "-simple", false);
+    PXOPT_LOOKUP_BOOL(faulted, config->args, "-faulted", false);
+    PXOPT_LOOKUP_BOOL(reject,  config->args, "-reject", false);
+
+    psString query = pxDataGet("dettool_residexp.sql");
+    if (!query) {
+        psError(PXTOOLS_ERR_DATA, false, "failed to retreive SQL statement");
+        return false;
+    }
+
+    if (psListLength(where->list)) {
+        psString whereClause = psDBGenerateWhereConditionSQL(where, "detResidExp");
+        psStringAppend(&query, " AND %s", whereClause);
+        psFree(whereClause);
+    }
+    psFree(where);
+
+    if (faulted) {
+        // list only faulted rows
+        psStringAppend(&query, " %s", "AND detResidExp.fault != 0");
+    } else {
+        // don't list faulted rows
+        psStringAppend(&query, " %s", "AND detResidExp.fault = 0");
+    }
+
+    // list only accepted rows
+    // XXX the usage of this flag is unclear : -reject means "accepted"?
+    if (reject) {
+        psStringAppend(&query, " %s", "AND detResidExp.accept != 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("dettool", PS_LOG_INFO, "no rows found");
+        psFree(output);
+        return true;
+    }
+
+    // negative simple so the default is true
+    if (!ippdbPrintMetadatas(stdout, output, "detResidExp", !simple)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print array");
+        psFree(output);
+        return false;
+    }
+
+    psFree(output);
+
+    return true;
+}
+
+
+static bool revertresidexpMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    psMetadata *where = psMetadataAlloc();
+    PXOPT_COPY_STR(config->args, where, "-det_id",    "det_id", "==");
+    PXOPT_COPY_S32(config->args, where, "-iteration", "iteration", "==");
+    PXOPT_COPY_STR(config->args, where, "-exp_id",    "exp_id", "==");
+    PXOPT_COPY_S16(config->args, where, "-code",      "fault", "==");
+
+    psString query = pxDataGet("dettool_revertresidexp.sql");
+    if (!query) {
+        psError(PXTOOLS_ERR_DATA, false, "failed to retreive SQL statement");
+        return false;
+    }
+
+    if (psListLength(where->list)) {
+        psString whereClause = psDBGenerateWhereConditionSQL(where, "detResidExp");
+        psStringAppend(&query, " AND %s", whereClause);
+        psFree(whereClause);
+    }
+    psFree(where);
+
+    if (!p_psDBRunQuery(config->dbh, query)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(query);
+        return false;
+    }
+    psFree(query);
+
+    if (psDBAffectedRows(config->dbh) < 1) {
+        psError(PS_ERR_UNKNOWN, false, "should have affected atleast 1 row");
+        return false;
+    }
+
+    return true;
+}
+
+
+static bool updateresidexpMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    PXOPT_LOOKUP_BOOL(reject, config->args, "-reject", false);
+
+    // build a query to search by det_id, iteration, exp_id
+    psMetadata *where = psMetadataAlloc();
+    PXOPT_COPY_STR(config->args, where, "-det_id", "det_id", "==");
+    PXOPT_COPY_S32(config->args, where, "-iteration", "iteration", "==");
+    PXOPT_COPY_STR(config->args, where, "-exp_id", "exp_id", "==");
+
+    // find the values we're going to set
+    // copy everything but det_id, iteration, & exp_id from the args and
+    // remove the '-' prefix
+    psMetadata *set = psMetadataAlloc();
+    PXOPT_COPY_STR(config->args, set, "-recip", "recipe", "==");
+    PXOPT_COPY_F64(config->args, set, "-bg", "bg", "==");
+    PXOPT_COPY_F64(config->args, set, "-bg_stdev", "bg_stdev", "==");
+    PXOPT_COPY_F64(config->args, set, "-bg_mean_stdev", "bg_mean_stdev", "==");
+    PXOPT_COPY_STR(config->args, set, "-path_base", "path_base", "==");
+
+    // this can't be PXOPT_ macro-ized as reject is !'d
+    if (!psMetadataAddBool(set, PS_LIST_TAIL, "accept", 0, "==", !reject)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item accept");
+        psFree(set);
+        psFree(where);
+        return false;
+    }
+
+    long changed = psDBUpdateRows(config->dbh, "detResidExp", where, set);
+    psFree(set);
+    psFree(where);
+
+    if (changed < 0) {
+        psError(PS_ERR_UNKNOWN, false, "no rows were updated");
+        return false;
+    }
+
+    return true;
+}
Index: /branches/eam_branch_20080719/ippTools/src/dettool_residimfile.c
===================================================================
--- /branches/eam_branch_20080719/ippTools/src/dettool_residimfile.c	(revision 18628)
+++ /branches/eam_branch_20080719/ippTools/src/dettool_residimfile.c	(revision 18628)
@@ -0,0 +1,238 @@
+static bool toresidimfileMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    PXOPT_LOOKUP_U64(limit, config->args, "-limit", false, false);
+    PXOPT_LOOKUP_BOOL(simple, config->args, "-simple", false);
+
+    psString query = pxDataGet("dettool_toresidimfile.sql");
+    if (!query) {
+        psError(PXTOOLS_ERR_DATA, false, "failed to retreive SQL statement");
+        return false;
+    }
+
+    // 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("dettool", PS_LOG_INFO, "no rows found");
+        psFree(output);
+        return true;
+    }
+
+    // negative simple so the default is true
+    if (!ippdbPrintMetadatas(stdout, output, "detPendingResidImfile", !simple)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print array");
+        psFree(output);
+        return false;
+    }
+
+    psFree(output);
+
+    return true;
+}
+
+
+static bool addresidimfileMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    
+    PXOPT_LOOKUP_STR(det_id, config->args, "-det_id", true, false); // required
+    PXOPT_LOOKUP_S32(iteration, config->args, "-iteration", false, false);
+    PXOPT_LOOKUP_STR(exp_id, config->args, "-exp_id", true, false); // required
+    PXOPT_LOOKUP_STR(class_id, config->args, "-class_id", true, false); // required
+    PXOPT_LOOKUP_S16(code, config->args, "-code", false, false);
+    PXOPT_LOOKUP_STR(uri, config->args, "-uri", (code == 0), false); // Required if code == 0
+    PXOPT_LOOKUP_STR(recipe, config->args, "-recip", (code == 0), false); // Required if code == 0
+    PXOPT_LOOKUP_F64(bg, config->args, "-bg", false, false);
+    PXOPT_LOOKUP_F64(bg_stdev, config->args, "-bg_stdev", false, false);
+    PXOPT_LOOKUP_F64(bg_mean_stdev, config->args, "-bg_mean_stdev", false, false);
+    PXOPT_LOOKUP_F64(bg_skewness, config->args, "-bg_skewness", false, false);
+    PXOPT_LOOKUP_F64(bg_kurtosis, config->args, "-bg_kurtosis", false, false);
+    PXOPT_LOOKUP_F64(bin_stdev, config->args, "-bin_stdev", false, false);
+    PXOPT_LOOKUP_F64(fringe_0, config->args, "-fringe_0", false, false);
+    PXOPT_LOOKUP_F64(fringe_1, config->args, "-fringe_1", false, false);
+    PXOPT_LOOKUP_F64(fringe_2, config->args, "-fringe_2", false, false);
+    PXOPT_LOOKUP_F64(fringe_resid_0, config->args, "-fringe_resid_0", false, false);
+    PXOPT_LOOKUP_F64(fringe_resid_1, config->args, "-fringe_resid_1", false, false);
+    PXOPT_LOOKUP_F64(fringe_resid_2, config->args, "-fringe_resid_2", false, false);
+    PXOPT_LOOKUP_F64(user_1, config->args, "-user_1", false, false);
+    PXOPT_LOOKUP_F64(user_2, config->args, "-user_2", false, false);
+    PXOPT_LOOKUP_F64(user_3, config->args, "-user_3", false, false);
+    PXOPT_LOOKUP_F64(user_4, config->args, "-user_4", false, false);
+    PXOPT_LOOKUP_F64(user_5, config->args, "-user_5", false, false);
+    PXOPT_LOOKUP_STR(path_base, config->args, "-path_base", false, false);
+
+    if (!detResidImfileInsert(config->dbh,
+            (psS64)atoll(det_id),
+            iteration,
+            (psS64)atoll(exp_id),
+            class_id,
+            uri,
+            recipe,
+            bg,
+            bg_stdev,
+            bg_mean_stdev,
+            bg_skewness,
+            bg_kurtosis,
+            bin_stdev,
+            fringe_0,
+            fringe_1,
+            fringe_2,
+            fringe_resid_0,
+            fringe_resid_1,
+            fringe_resid_2,
+            user_1,
+            user_2,
+            user_3,
+            user_4,
+            user_5,
+            path_base,
+            code
+    )) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+
+    return true;
+}
+
+
+static bool residimfileMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    psMetadata *where = psMetadataAlloc();
+    PXOPT_COPY_STR(config->args, where, "-det_id",     "det_id", "==");
+    PXOPT_COPY_S32(config->args, where, "-iteration",  "iteration", "==");
+    PXOPT_COPY_STR(config->args, where, "-exp_id",     "exp_id", "==");
+    PXOPT_COPY_STR(config->args, where, "-class_id",   "class_id", "==");
+    PXOPT_COPY_STR(config->args, where, "-recip",      "recipe", "==");
+
+    PXOPT_LOOKUP_U64(limit, config->args, "-limit", false, false);
+    PXOPT_LOOKUP_BOOL(simple, config->args, "-simple", false);
+    PXOPT_LOOKUP_BOOL(faulted, config->args, "-faulted", false);
+
+    psString query = pxDataGet("dettool_residimfile.sql");
+    if (!query) {
+        psError(PXTOOLS_ERR_DATA, false, "failed to retreive SQL statement");
+        return false;
+    }
+
+    // NOTE: the query ends in a WHERE, add the required restriction on detRun.state
+    char *value = NULL;
+    bool status;
+    if ((value = psMetadataLookupStr(&status, config->args, "-select_state"))) {
+        psStringAppend(&query, " detRun.state = '%s'", value);
+    } else {
+        psStringAppend(&query, " detRun.state = 'run'");
+    }
+
+    if (psListLength(where->list)) {
+        psString whereClause = psDBGenerateWhereConditionSQL(where, "detResidImfile");
+        psStringAppend(&query, " AND %s", whereClause);
+        psFree(whereClause);
+    }
+    psFree(where);
+
+    if (faulted) {
+        // list only faulted rows
+        psStringAppend(&query, " %s", "AND detResidImfile.fault != 0");
+    } else {
+        // don't list faulted rows
+        psStringAppend(&query, " %s", "AND detResidImfile.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("dettool", PS_LOG_INFO, "no rows found");
+        psFree(output);
+        return true;
+    }
+
+    // negative simple so the default is true
+    if (!ippdbPrintMetadatas(stdout, output, "rawResidImfile", !simple)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print array");
+        psFree(output);
+        return false;
+    }
+
+    psFree(output);
+
+    return true;
+}
+
+
+static bool revertresidimfileMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    psMetadata *where = psMetadataAlloc();
+    PXOPT_COPY_STR(config->args, where, "-det_id",    "det_id", "==");
+    PXOPT_COPY_S32(config->args, where, "-iteration", "iteration", "==");
+    PXOPT_COPY_STR(config->args, where, "-exp_id",  "exp_id", "==");
+    PXOPT_COPY_STR(config->args, where, "-class_id",  "class_id", "==");
+    PXOPT_COPY_S16(config->args, where, "-code",      "fault", "==");
+
+    psString query = pxDataGet("dettool_revertresidimfile.sql");
+    if (!query) {
+        psError(PXTOOLS_ERR_DATA, false, "failed to retreive SQL statement");
+        return false;
+    }
+
+    if (psListLength(where->list)) {
+        psString whereClause = psDBGenerateWhereConditionSQL(where, "detResidImfile");
+        psStringAppend(&query, " AND %s", whereClause);
+        psFree(whereClause);
+    }
+    psFree(where);
+
+    if (!p_psDBRunQuery(config->dbh, query)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(query);
+        return false;
+    }
+    psFree(query);
+
+    if (psDBAffectedRows(config->dbh) < 1) {
+        psError(PS_ERR_UNKNOWN, false, "should have affected atleast 1 row");
+        return false;
+    }
+
+    return true;
+}
Index: /branches/eam_branch_20080719/ippTools/src/dettool_stack.c
===================================================================
--- /branches/eam_branch_20080719/ippTools/src/dettool_stack.c	(revision 18628)
+++ /branches/eam_branch_20080719/ippTools/src/dettool_stack.c	(revision 18628)
@@ -0,0 +1,292 @@
+static bool tostackedMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    PXOPT_LOOKUP_U64(limit, config->args, "-limit", false, false);
+    PXOPT_LOOKUP_BOOL(simple, config->args, "-simple", false);
+
+    psString query = pxDataGet("dettool_tostacked.sql");
+    if (!query) {
+        psError(PXTOOLS_ERR_DATA, false, "failed to retreive SQL statement");
+        return false;
+    }
+
+    // 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("dettool", PS_LOG_INFO, "no rows found");
+        psFree(output);
+        return true;
+    }
+
+    // negative simple so the default is true
+    if (!ippdbPrintMetadatas(stdout, output, "detPendingStackedImfile", !simple)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print array");
+        psFree(output);
+        return false;
+    }
+
+    psFree(output);
+
+    return true;
+}
+
+// this is NOT a command-line mode : it is used by 'addstackedMode'
+static psArray *searchRawImfiles(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, NULL);
+
+    psMetadata *where = psMetadataAlloc();
+    PXOPT_COPY_STR(config->args, where, "-det_id",    "det_id", "==");
+    PXOPT_COPY_S32(config->args, where, "-iteration", "iteration", "==");
+
+    // select exp_ids from detInputExp matching det_id & iteration
+    // where query should be pre-generated
+    psArray *detInputExp = detInputExpSelectRowObjects(config->dbh, where, 0);
+    psFree (where);
+
+    if (!detInputExp) {
+        psError(PS_ERR_UNKNOWN, false, "no rawExp rows found");
+        return NULL;
+    }
+
+    // generate where query with just the exp_ids
+    psMetadata *where_exp_ids = psMetadataAlloc();
+    for (long i = 0; i < psArrayLength(detInputExp); i++) {
+        detInputExpRow *row = detInputExp->data[i];
+        if (!psMetadataAddS64(where_exp_ids, PS_LIST_TAIL, "exp_id",
+                PS_META_DUPLICATE_OK, "==", row->exp_id)
+        ) {
+            psError(PS_ERR_UNKNOWN, false, "failed to add item exp_id");
+            psFree(detInputExp);
+            psFree(where_exp_ids);
+            return NULL;
+        }
+    }
+    psFree(detInputExp);
+
+    // select rawImfiles with matching exp_ids
+    psArray *rawImfiles =
+        rawImfileSelectRowObjects(config->dbh, where_exp_ids, 0);
+    psFree(where_exp_ids);
+    if (!rawImfiles) {
+        psError(PS_ERR_UNKNOWN, false, "no rawImfile rows found");
+        return NULL;
+    }
+
+    return rawImfiles;
+}
+
+static bool addstackedMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    // det_id, iteration, class_id, uri, & recipe are required
+    PXOPT_LOOKUP_STR(det_id, config->args, "-det_id", true, false);
+    PXOPT_LOOKUP_S32(iteration, config->args, "-iteration", true, false);
+    PXOPT_LOOKUP_STR(class_id, config->args, "-class_id", true, false);
+
+    PXOPT_LOOKUP_S16(code, config->args, "-code", false, false);
+
+    // Required if code == 0
+    PXOPT_LOOKUP_STR(uri, config->args, "-uri", (code == 0), false);
+    PXOPT_LOOKUP_STR(recipe, config->args, "-recip", (code == 0), false);
+
+    // optional
+    PXOPT_LOOKUP_F64(bg, config->args, "-bg", false, false);
+    PXOPT_LOOKUP_F64(bg_stdev, config->args, "-bg_stdev", false, false);
+    PXOPT_LOOKUP_F64(bg_mean_stdev, config->args, "-bg_mean_stdev", false, false);
+    PXOPT_LOOKUP_F64(user_1, config->args, "-user_1", false, false);
+    PXOPT_LOOKUP_F64(user_2, config->args, "-user_2", false, false);
+    PXOPT_LOOKUP_F64(user_3, config->args, "-user_3", false, false);
+    PXOPT_LOOKUP_F64(user_4, config->args, "-user_4", false, false);
+    PXOPT_LOOKUP_F64(user_5, config->args, "-user_5", false, false);
+
+    // correlate the class_id against the input exposure(s)
+    // searchRawImfiles defines a where clause to search by det_id and iteration
+    psArray *rawImfiles = searchRawImfiles(config);
+
+    if (!rawImfiles) {
+        psError(PS_ERR_UNKNOWN, false, "failed to get rawImfiles from db");
+        return false;
+    }
+
+    bool valid_class_id = false;
+    if (rawImfiles) {
+        for (long i = 0; i < psArrayLength(rawImfiles); i++) {
+            if (!rawImfiles->data[i]) {
+                fprintf (stderr, "*");
+                continue;
+            }
+            if (strcmp(class_id, ((rawImfileRow *)rawImfiles->data[i])->class_id) == 0) {
+                valid_class_id = true;
+                break;
+            }
+        }
+        psFree(rawImfiles);
+    }
+
+    if (!valid_class_id) {
+        psError(PS_ERR_UNKNOWN, true,
+            "class_id can not be correlated with the input exposures");
+        return false;
+    }
+
+    // create a new detStackedImfile object
+    detStackedImfileRow *stackedImfile = detStackedImfileRowAlloc(
+            (psS64)atoll(det_id),
+            iteration,
+            class_id,
+            uri,
+            recipe,
+            bg,
+            bg_stdev,
+            bg_mean_stdev,
+            user_1,
+            user_2,
+            user_3,
+            user_4,
+            user_5,
+            code
+        );
+
+    // insert the new row into the detProcessedImfile table
+    if (!detStackedImfileInsertObject(config->dbh, stackedImfile)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(stackedImfile);
+        return false;
+    }
+
+    psFree(stackedImfile);
+
+    return true;
+}
+
+static bool stackedMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    psMetadata *where = psMetadataAlloc();
+    PXOPT_COPY_STR(config->args, where, "-det_id",    "det_id", "==");
+    PXOPT_COPY_S32(config->args, where, "-iteration", "iteration", "==");
+    PXOPT_COPY_STR(config->args, where, "-class_id",  "class_id", "==");
+    PXOPT_COPY_STR(config->args, where, "-recip",     "recipe", "==");
+
+    PXOPT_LOOKUP_U64(limit, config->args, "-limit", false, false);
+    PXOPT_LOOKUP_BOOL(faulted, config->args, "-faulted", false);
+    PXOPT_LOOKUP_BOOL(simple, config->args, "-simple", false);
+
+    psString query = pxDataGet("dettool_stacked.sql");
+    if (!query) {
+        psError(PXTOOLS_ERR_DATA, false, "failed to retreive SQL statement");
+        psFree(where);
+        return false;
+    }
+
+    if (psListLength(where->list)) {
+        psString whereClause = psDBGenerateWhereConditionSQL(where, "detStackedImfile");
+        psStringAppend(&query, " AND %s", whereClause);
+        psFree(whereClause);
+    }
+    psFree(where);
+
+    if (faulted) {
+        // list only faulted rows
+        psStringAppend(&query, " %s", "AND detStackedImfile.fault != 0");
+    } else {
+        // don't list faulted rows
+        psStringAppend(&query, " %s", "AND detStackedImfile.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("dettool", PS_LOG_INFO, "no rows found");
+        psFree(output);
+        return true;
+    }
+
+    // negative simple so the default is true
+    if (!ippdbPrintMetadatas(stdout, output, "rawImfile", !simple)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print array");
+        psFree(output);
+        return false;
+    }
+
+    psFree(output);
+
+    return true;
+}
+
+
+static bool revertstackedMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    psMetadata *where = psMetadataAlloc();
+    PXOPT_COPY_STR(config->args, where, "-det_id",    "det_id", "==");
+    PXOPT_COPY_S32(config->args, where, "-iteration", "iteration", "==");
+    PXOPT_COPY_STR(config->args, where, "-class_id",  "class_id", "==");
+    PXOPT_COPY_S16(config->args, where, "-code",      "fault", "==");
+
+    psString query = pxDataGet("dettool_revertstacked.sql");
+    if (!query) {
+        psError(PXTOOLS_ERR_DATA, false, "failed to retreive SQL statement");
+        return false;
+    }
+
+    if (psListLength(where->list)) {
+        psString whereClause = psDBGenerateWhereConditionSQL(where, "detStackedImfile");
+        psStringAppend(&query, " AND %s", whereClause);
+        psFree(whereClause);
+    }
+    psFree(where);
+
+    if (!p_psDBRunQuery(config->dbh, query)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(query);
+        return false;
+    }
+    psFree(query);
+
+    if (psDBAffectedRows(config->dbh) < 1) {
+        psError(PS_ERR_UNKNOWN, false, "should have affected atleast 1 row");
+        return false;
+    }
+
+    return true;
+}
