Index: trunk/ippTools/src/chiptool.c
===================================================================
--- trunk/ippTools/src/chiptool.c	(revision 27737)
+++ trunk/ippTools/src/chiptool.c	(revision 27738)
@@ -60,4 +60,5 @@
 static bool runstateMode(pxConfig *config);
 static bool setimfiletoupdateMode(pxConfig *config);
+static bool listrunMode(pxConfig *config);
 
 # define MODECASE(caseName, func) \
@@ -103,4 +104,5 @@
         MODECASE(CHIPTOOL_MODE_RUNSTATE,                runstateMode);
         MODECASE(CHIPTOOL_MODE_SETIMFILETOUPDATE,       setimfiletoupdateMode);
+        MODECASE(CHIPTOOL_MODE_LISTRUN,                 listrunMode);
         default:
             psAbort("invalid option (this should not happen)");
@@ -699,4 +701,8 @@
     PXOPT_LOOKUP_BOOL(simple, config->args, "-simple", false);
     PXOPT_LOOKUP_BOOL(faulted, config->args, "-faulted", false);
+    PXOPT_LOOKUP_BOOL(allfiles, config->args, "-allfiles", false);
+    if (allfiles) {
+        faulted = false;
+    }
 
     psMetadata *where = psMetadataAlloc();
@@ -740,5 +746,5 @@
         // list only faulted rows
         psStringAppend(&query, " %s", "AND chipProcessedImfile.fault != 0");
-    } else {
+    } else if (!allfiles) {
         // don't list faulted rows
         psStringAppend(&query, " %s", "AND chipProcessedImfile.fault = 0");
@@ -1414,6 +1420,6 @@
     }
 
-    char *set_magicked_imfile = "";
-    char *set_magicked_run = "";
+    psString set_magicked_imfile = psStringCopy("");
+    psString set_magicked_run = psStringCopy("");
     if (!strcmp(data_state, "full")) {
         // if (chipProcessedImfile.magicked < 0 and rawImfile.magicked = 0) leave magicked unchanged. This will
@@ -1421,10 +1427,14 @@
         // otherwise copy magicked from the rawImfile
         // Same thing for chipRun/rawExp
-        set_magicked_imfile = "\n , chipProcessedImfile.magicked = IF((chipProcessedImfile.magicked < 0 AND rawImfile.magicked = 0), chipProcessedImfile.magicked, rawImfile.magicked)";
-        set_magicked_run = "\n , chipRun.magicked = IF((chipRun.magicked < 0 AND rawExp.magicked = 0), chipRun.magicked, rawExp.magicked)";
+        psStringAppend(&set_magicked_imfile, "\n , chipProcessedImfile.magicked = IF((chipProcessedImfile.magicked < 0"
+                                      " AND rawImfile.magicked = 0), chipProcessedImfile.magicked, rawImfile.magicked)");
+        psStringAppend(&set_magicked_run, "\n , chipRun.magicked = IF((chipRun.magicked < 0 AND rawExp.magicked = 0), "
+                                      " chipRun.magicked, rawExp.magicked)");
+
     } else if (!strcmp(data_state, "cleaned") || !strcmp(data_state, "purged")) {
         // if magicked is non-zero set it to -1
-        set_magicked_imfile = "\n, chipProcessedImfile.magicked = IF(chipProcessedImfile.magicked = 0, 0, -1)";
-        set_magicked_run = "\n, chipRun.magicked = IF(chipRun.magicked = 0, 0, -1)";
+        // Once one imfile has been cleaned, the chipRun is no longer 'magicked'
+        psStringAppend(&set_magicked_imfile, "\n, chipProcessedImfile.magicked = IF(chipProcessedImfile.magicked = 0, 0, -1),"
+                                             " chipRun.magicked = IF(chipRun.magicked = 0, 0, -1)");
     }
 
@@ -1440,4 +1450,6 @@
     }
     psFree(query);
+    psFree(set_magicked_imfile);
+    psFree(set_magicked_run);
     if (psDBAffectedRows(config->dbh) < 1) {
         psError(PS_ERR_UNKNOWN, false, "should have affected atleast 1 row");
@@ -1757,2 +1769,90 @@
     return true;
 }
+
+static bool listrunMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, NULL);
+
+    PXOPT_LOOKUP_U64(limit, config->args, "-limit", false, false);
+    PXOPT_LOOKUP_BOOL(simple, config->args, "-simple", false);
+    PXOPT_LOOKUP_BOOL(pstamp_order, config->args, "-pstamp_order", false);
+
+    psMetadata *where = psMetadataAlloc();
+    pxchipGetSearchArgs (config, where); // chipRun, chipProcessedImfile, rawExp
+    PXOPT_COPY_S64(config->args, where, "-chip_id", "chipRun.chip_id", "==");
+    PXOPT_COPY_STR(config->args, where, "-reduction", "chipRun.reduction", "==");
+    PXOPT_COPY_STR(config->args, where, "-state", "chipRun.state", "==");
+    pxAddLabelSearchArgs (config, where, "-label", "chipRun.label", "LIKE");
+    pxAddLabelSearchArgs (config, where, "-data_group", "chipRun.data_group", "LIKE");
+    pxAddLabelSearchArgs (config, where, "-dist_group", "chipRun.dist_group", "LIKE");
+    PXOPT_COPY_S64(config->args, where, "-magicked", "chipRun.magicked", "==");
+
+    psString where2 = NULL;
+    pxmagicAddWhere(config, &where2, "chipRun");
+    // add cuts on ra and decl if supplied
+    if (!pxspaceAddWhere(config, &where2, "rawExp")) {
+        psError(psErrorCodeLast(), false, "pxSpaceAddWhere failed");
+        return false;
+    }
+
+    psString query = pxDataGet("chiptool_listrun.sql");
+    if (!query) {
+        psError(PXTOOLS_ERR_SYS, false, "failed to retreive SQL statement");
+        return false;
+    }
+
+    if (psListLength(where->list)) {
+        psString whereClause = psDBGenerateWhereConditionSQL(where, NULL);
+        psStringAppend(&query, " WHERE %s %s", whereClause, where2 ? where2 : "");
+        psFree(whereClause);
+    } else if (psMetadataLookupBool(NULL, config->args, "-all") || where2) {
+        psStringAppend(&query, " WHERE chipRun.chip_id IS NOT NULL %s", where2 ? where2 : "");
+    } else {
+        psFree(where);
+        psError(PXTOOLS_ERR_CONFIG, false, "search parameters (or -all) are required");
+        return false;
+    }
+    psFree(where);
+
+    if (pstamp_order) {
+        // put runs in order of exposure id with newest chip Runs first
+        // The postage stamp parser depends on this behavior
+        psStringAppend(&query, "\nORDER by exp_id, chip_id DESC");
+    }
+
+    // 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;
+    }
+
+    // negative simple so the default is true
+    if (!ippdbPrintMetadatas(stdout, output, "chipRun", !simple)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print array");
+        psFree(output);
+        return false;
+    }
+
+    psFree(output);
+
+    return true;
+}
Index: trunk/ippTools/src/chiptoolConfig.c
===================================================================
--- trunk/ippTools/src/chiptoolConfig.c	(revision 27737)
+++ trunk/ippTools/src/chiptoolConfig.c	(revision 27738)
@@ -196,4 +196,5 @@
     psMetadataAddBool(processedimfileArgs, PS_LIST_TAIL, "-all",  0,            "list everything without search terms", false);
     psMetadataAddBool(processedimfileArgs, PS_LIST_TAIL, "-faulted",  0,        "only return imfiles with a fault status set", false);
+    psMetadataAddBool(processedimfileArgs, PS_LIST_TAIL, "-allfiles",  0,       "only all matching imfiles regardless of fault status", false);
     psMetadataAddBool(processedimfileArgs, PS_LIST_TAIL, "-simple",  0,         "use the simple output format", false);
 
@@ -216,4 +217,22 @@
     psMetadataAddS16(updateprocessedimfileArgs, PS_LIST_TAIL, "-fault",  0,            "set fault code (required)", 0);
     psMetadataAddStr(updateprocessedimfileArgs, PS_LIST_TAIL, "-set_state", 0,         "set state", NULL);
+
+    // -processedExp
+    psMetadata *listrunArgs = psMetadataAlloc();
+    pxchipSetSearchArgs(listrunArgs);
+    psMetadataAddS64(listrunArgs, PS_LIST_TAIL, "-chip_id",  0,         "search by  chip ID", 0);
+    psMetadataAddStr(listrunArgs,  PS_LIST_TAIL, "-reduction",          0, "search by reduction class", NULL);
+    psMetadataAddStr(listrunArgs,  PS_LIST_TAIL, "-label",  PS_META_DUPLICATE_OK, "search by chipRun label (LIKE comparison)", NULL);
+    psMetadataAddStr(listrunArgs,  PS_LIST_TAIL, "-state",              0, "search by chipRun state", NULL);
+    psMetadataAddStr(listrunArgs,  PS_LIST_TAIL, "-data_group",  PS_META_DUPLICATE_OK, "search by chipRun data_group (LIKE comparison)", NULL);
+    psMetadataAddStr(listrunArgs,  PS_LIST_TAIL, "-dist_group",  PS_META_DUPLICATE_OK, "search by chipRun dist_group (LIKE comparison)", NULL);
+    pxmagicAddArguments(listrunArgs);
+
+    psMetadataAddBool(listrunArgs, PS_LIST_TAIL, "-pstamp_order",  0,    "order results for postage stamp server", false);
+    psMetadataAddU64(listrunArgs,  PS_LIST_TAIL, "-limit",  0,           "limit result set to N items", 0);
+    psMetadataAddBool(listrunArgs, PS_LIST_TAIL, "-all",  0,            "list everything without search terms", false);
+    psMetadataAddBool(listrunArgs, PS_LIST_TAIL, "-simple",  0,         "use the simple output format", false);
+    pxspaceAddArguments(listrunArgs);
+
     // -block
     psMetadata *blockArgs = psMetadataAlloc();
@@ -328,4 +347,5 @@
     PXOPT_ADD_MODE("-revertprocessedimfile","undo a processed imfile",              CHIPTOOL_MODE_REVERTPROCESSEDIMFILE,revertprocessedimfileArgs);
     PXOPT_ADD_MODE("-runstate",             "list the states of chip run",          CHIPTOOL_MODE_RUNSTATE,             runstateArgs);
+    PXOPT_ADD_MODE("-listrun",              "list chipRuns",                        CHIPTOOL_MODE_LISTRUN,              listrunArgs);
     PXOPT_ADD_MODE("-advanceexp",           "advance completed exposures",          CHIPTOOL_MODE_ADVANCEEXP,           advanceexpArgs);
     PXOPT_ADD_MODE("-block",                "set a label block",                    CHIPTOOL_MODE_BLOCK,                blockArgs);
