Index: /trunk/ppStats/src/ppStatsLoop.c
===================================================================
--- /trunk/ppStats/src/ppStatsLoop.c	(revision 7923)
+++ /trunk/ppStats/src/ppStatsLoop.c	(revision 7924)
@@ -1,3 +1,4 @@
 #include <stdio.h>
+#include <string.h>
 #include <pslib.h>
 #include <psmodules.h>
@@ -22,4 +23,27 @@
 }
 
+static bool doThis(psList *toDoList,    // List of things to do
+                   const char *this     // The name of "this"
+    )
+{
+    if (psListLength(toDoList) == 0) {
+        // No list --- do everything
+        return true;
+    }
+
+    psListIterator *iterator = psListIteratorAlloc(toDoList, PS_LIST_HEAD, false); // Iterator
+    psString test;                      // Test string, from iteration
+    while ((test = psListGetAndIncrement(iterator))) {
+        if (strcmp(this, test) == 0) {
+            // It's in the list --- do it
+            psFree(iterator);
+            return true;
+        }
+    }
+    psFree(iterator);
+    // Couldn't find it --- don't do it
+    return false;
+}
+
 
 void ppStatsLoop(ppStatsData *data,     // The data
@@ -51,4 +75,10 @@
         }
         const char *chipName = psMetadataLookupStr(NULL, chip->concepts, "CHIP.NAME"); // Name of chip
+
+        // Check to see if this is a chip of interest
+        if (!doThis(data->chips, chipName)) {
+            continue;
+        }
+
         psMetadata *chipResults = psMetadataAlloc(); // Metadata to hold the chip-level results
 
@@ -69,4 +99,10 @@
             }
             const char *cellName = psMetadataLookupStr(NULL, cell->concepts, "CELL.NAME"); // Name of cell
+
+            // Check to see if this is a cell of interest
+            if (!doThis(data->cells, cellName)) {
+                continue;
+            }
+
             psMetadata *cellResults = psMetadataAlloc(); // Metadata to hold the cell-level results
 
@@ -84,4 +120,9 @@
             if (!data->doStats) {
                 // Nothing further to do --- don't want to waste our time reading the data
+                continue;
+            }
+
+            pmHDU *hdu = pmHDUFromCell(cell); // HDU for cell
+            if (!hdu || hdu->blankPHU) {
                 continue;
             }
@@ -192,4 +233,9 @@
     psFree(conceptsIter);
 
+    if (psListLength(fpaResults->list) == 0) {
+        psError(PS_ERR_UNKNOWN, true, "No output.\n");
+        return;
+    }
+
     psString output = psMetadataConfigFormat(fpaResults);
     psFree(fpaResults);
Index: /trunk/ppStats/src/ppStatsSetup.c
===================================================================
--- /trunk/ppStats/src/ppStatsSetup.c	(revision 7923)
+++ /trunk/ppStats/src/ppStatsSetup.c	(revision 7924)
@@ -5,4 +5,5 @@
 
 #include "ppStats.h"
+#include "ppStatsData.h"
 
 // Print usage information and die
@@ -10,7 +11,7 @@
     )
 {
-    printf("Return statistics .\n\n"
+    printf("Return headers, concepts and/or image statistics.\n\n"
            "Usage:\n"
-           "\t%s INPUT.fits OUTPUT.fits\n"
+           "\t%s INPUT.fits OUTPUT_NAME\n"
            "\n", config->argv[0]);
     psArgumentHelp(config->arguments);
@@ -21,4 +22,107 @@
     exit(EXIT_FAILURE);
 }
+
+// Generate a list from the arguments
+static void listFromArguments(psMetadata *arguments, // Arguments to parse
+                              const char *name, // Name of the item, for error message
+                              const char *flag, // The flag for the argument of interest
+                              psList *target // The target list
+    )
+{
+    psString regex = NULL;              // Regular expression for the flag
+    psStringAppend(&regex, "^%s$", flag);
+    psMetadataIterator *iterator = psMetadataIteratorAlloc(arguments, PS_LIST_HEAD, regex); // Iterator
+    psFree(regex);
+    psMetadataItem *item;               // Item from iteration
+    while ((item = psMetadataGetAndIncrement(iterator))) {
+        if (item->type != PS_DATA_STRING) {
+            psLogMsg(__func__, PS_LOG_WARN, "%s name is not of type STRING (%x) --- ignored.\n",
+                     name, item->type);
+            continue;
+        }
+        if (item->data.V && strlen(item->data.V) > 0) {
+            psListAdd(target, PS_LIST_TAIL, item->data.V);
+        }
+    }
+    psFree(iterator);
+
+    return;
+}
+
+// Strings in a recipe may be defined multiply (with MULTI) or listed on a single line
+static void listFromRecipe(psMetadata *recipe, // Recipe to search
+                           const char *name, // Name for item within recipe
+                           psList *target // The target list
+    )
+{
+    // If the list already has entries, don't read anything else
+    if (psListLength(target) > 0) {
+        return;
+    }
+
+    // First check that at least one item of interest exists
+    psMetadataItem *checkItem = psMetadataLookup(recipe, name);
+    if (!checkItem) {
+        // Nothing to see here
+        return;
+    }
+
+    psString regex = NULL;              // Regular expression for the flag
+    psStringAppend(&regex, "^%s$", name);
+    psMetadataIterator *iterator = psMetadataIteratorAlloc(recipe, PS_LIST_HEAD, regex);
+    psFree(regex);
+    psMetadataItem *item;
+    int numItem = 0; // Occurrence of the item in the recipe; to help the user in case of trouble
+    while ((item = psMetadataGetAndIncrement(iterator))) {
+        numItem++;
+        if (item->type != PS_DATA_STRING) {
+            psLogMsg(__func__, PS_LOG_WARN, "Occurrence %d of %s in the recipe is "
+                     "not of type STRING (%x) --- ignored.\n", numItem, name, item->type);
+            continue;
+        }
+        // Parse into a list of independent values
+        psList *values = psStringSplit(item->data.V, " ,;", false);
+        // Copy into the target
+        psListIterator *valuesIter = psListIteratorAlloc(values, PS_LIST_HEAD, false);
+        psString valueString;
+        while ((valueString = psListGetAndIncrement(valuesIter))) {
+            psListAdd(target, PS_LIST_TAIL, valueString);
+        }
+        psFree(valuesIter);
+        psFree(values);
+    }
+    psFree(iterator);
+
+    return;
+}
+
+// Set the statistics option; for arguments
+static inline void statsOptionArguments(psMetadata *arguments, // Arguments to parse
+                                        const char *name, // Name for option
+                                        ppStatsData *data, // Configuration data
+                                        psStatsOptions option // Option to check for
+    )
+{
+    if (psMetadataLookupBool(NULL, arguments, name)) {
+        data->stats->options |= option;
+        data->doStats = true;
+    }
+    return;
+}
+
+// Print out what we're going to do, from the list
+static void checkList(psList *list,     // List
+                      const char *name  // Name of list
+    )
+{
+    psString value;
+    psListIterator *iterator = psListIteratorAlloc(list, PS_LIST_HEAD, false);
+    while ((value = psListGetAndIncrement(iterator))) {
+        printf("%s: %s\n", name, value);
+    }
+    psFree(iterator);
+    return;
+}
+
 
 ppStatsData *ppStatsSetup(pmConfig *config // Configuration
@@ -43,5 +147,5 @@
     psMetadataAddBool(arguments, PS_LIST_TAIL, "-clipped-stdev", 0, "Calculate clipped standard deviation", false);
     psMetadataAddS32(arguments, PS_LIST_TAIL, "-iter", 0, "Clipping iterations", 0);
-    psMetadataAddF32(arguments, PS_LIST_TAIL, "-rej", 0, "Clipping level", NAN);
+    psMetadataAddF32(arguments, PS_LIST_TAIL, "-rej", 0, "Clipping level", 0.0);
     psMetadataAddS32(arguments, PS_LIST_TAIL, "-sample", 0, "Sampling fraction", 0.0);
 
@@ -60,42 +164,21 @@
     data->outName = config->argv[2];
 
-    // Get the list of chips, cells and headers
-    #define GET_LIST_FROM_ARGUMENTS(NAME, FLAG, TARGET) { \
-        psMetadataIterator *iterator = psMetadataIteratorAlloc(arguments, PS_LIST_HEAD, "^" FLAG "$"); \
-        psMetadataItem *item; \
-        while ((item = psMetadataGetAndIncrement(iterator))) { \
-            if (item->type != PS_DATA_STRING) { \
-                psLogMsg(__func__, PS_LOG_WARN, NAME " name is not of type STRING (%x) --- ignored.\n", \
-                         item->type); \
-                continue; \
-            } \
-            psListAdd(TARGET, PS_LIST_TAIL, item->data.V); \
-        } \
-        psFree(iterator); \
-    }
-
-    GET_LIST_FROM_ARGUMENTS("Chip", "-chip", data->chips);
-    GET_LIST_FROM_ARGUMENTS("Cell", "-cell", data->cells);
-    GET_LIST_FROM_ARGUMENTS("Header", "-header", data->headers);
-    GET_LIST_FROM_ARGUMENTS("Concept", "-concept", data->concepts);
+    listFromArguments(arguments, "Chip", "-chip", data->chips);
+    listFromArguments(arguments, "Cell", "-cell", data->cells);
+    listFromArguments(arguments, "Header", "-header", data->headers);
+    listFromArguments(arguments, "Concept", "-concept", data->concepts);
 
     // Set the statistics options
-    #define STATS_OPTION(NAME, SYMBOL) \
-    if (psMetadataLookupBool(NULL, arguments, NAME)) { \
-        data->stats->options |= SYMBOL; \
-        data->doStats = true; \
-    } \
-
-    STATS_OPTION("-mean",     PS_STAT_SAMPLE_MEAN);
-    STATS_OPTION("-stdev",    PS_STAT_SAMPLE_STDEV);
-    STATS_OPTION("-median",   PS_STAT_SAMPLE_MEDIAN);
-    STATS_OPTION("-quartile", PS_STAT_SAMPLE_QUARTILE);
-    STATS_OPTION("-robust-median",   PS_STAT_ROBUST_MEDIAN);
-    STATS_OPTION("-robust-stdev",    PS_STAT_ROBUST_STDEV);
-    STATS_OPTION("-robust-quartile", PS_STAT_ROBUST_QUARTILE);
-    STATS_OPTION("-fitted-mean",   PS_STAT_FITTED_MEAN);
-    STATS_OPTION("-fitted-stdev",  PS_STAT_FITTED_STDEV);
-    STATS_OPTION("-clipped-mean",  PS_STAT_CLIPPED_MEAN);
-    STATS_OPTION("-clipped-stdev", PS_STAT_CLIPPED_STDEV);
+    statsOptionArguments(arguments, "-mean", data,     PS_STAT_SAMPLE_MEAN);
+    statsOptionArguments(arguments, "-stdev", data,    PS_STAT_SAMPLE_STDEV);
+    statsOptionArguments(arguments, "-median", data,   PS_STAT_SAMPLE_MEDIAN);
+    statsOptionArguments(arguments, "-quartile", data, PS_STAT_SAMPLE_QUARTILE);
+    statsOptionArguments(arguments, "-robust-median", data,   PS_STAT_ROBUST_MEDIAN);
+    statsOptionArguments(arguments, "-robust-stdev", data,    PS_STAT_ROBUST_STDEV);
+    statsOptionArguments(arguments, "-robust-quartile", data, PS_STAT_ROBUST_QUARTILE);
+    statsOptionArguments(arguments, "-fitted-mean", data,   PS_STAT_FITTED_MEAN);
+    statsOptionArguments(arguments, "-fitted-stdev", data,  PS_STAT_FITTED_STDEV);
+    statsOptionArguments(arguments, "-clipped-mean", data,  PS_STAT_CLIPPED_MEAN);
+    statsOptionArguments(arguments, "-clipped-stdev", data, PS_STAT_CLIPPED_STDEV);
     data->stats->clipSigma = psMetadataLookupF32(NULL, arguments, "-rej");
     data->stats->clipIter = psMetadataLookupS32(NULL, arguments, "-iter");
@@ -150,42 +233,16 @@
     }
 
-    // Strings in a recipe may be defined multiply (with MULTI) or listed on a single line
-    #define GET_LIST_FROM_RECIPE(NAME, TARGET) \
-    if (psListLength(TARGET) < 0) { \
-        psMetadataIterator *iterator = psMetadataIteratorAlloc(recipe, PS_LIST_HEAD, "^" NAME "$"); \
-        psMetadataItem *item; \
-        int numItem = 0; /* Occurrence of the item in the recipe; to help the user in case of trouble */ \
-        while ((item = psMetadataGetAndIncrement(iterator))) { \
-            numItem++; \
-            if (item->type != PS_DATA_STRING) { \
-                psLogMsg(__func__, PS_LOG_WARN, NAME "Occurrence %d of %s in the recipe is " \
-                         "not of type STRING (%x) --- ignored.\n", numItem, NAME, item->type); \
-                continue; \
-            } \
-            /* Parse into a list of independent values */ \
-            psList *values = psStringSplit(item->data.V, " ,;", false); \
-            /* Copy into the target */ \
-            psListIterator *valuesIter = psListIteratorAlloc(values, PS_LIST_HEAD, false); \
-            psString valueString; \
-            while ((valueString = psListGetAndIncrement(valuesIter))) { \
-                psListAdd(TARGET, PS_LIST_TAIL, valueString); \
-            } \
-            psFree(valuesIter); \
-            psFree(values); \
-        } \
-        psFree(iterator);\
-    }
-
-    GET_LIST_FROM_RECIPE("CHIPS", data->chips);
-    GET_LIST_FROM_RECIPE("CELLS", data->cells);
-    GET_LIST_FROM_RECIPE("HEADER", data->headers);
-    GET_LIST_FROM_RECIPE("CONCEPT", data->concepts);
+    listFromRecipe(recipe, "CHIP", data->chips);
+    listFromRecipe(recipe, "CELL", data->cells);
+    listFromRecipe(recipe, "HEADER", data->headers);
+    listFromRecipe(recipe, "CONCEPT", data->concepts);
 
     // Parse the statistics options
     psList *recipeStats = psListAlloc(NULL);
-    GET_LIST_FROM_RECIPE("STATS", recipeStats);
+    listFromRecipe(recipe, "STAT", recipeStats);
     if (psListLength(recipeStats) > 0) {
         psListIterator *iterator = psListIteratorAlloc(recipeStats, PS_LIST_HEAD, false);
         psString stat;
+
 
         #define CHECK_STAT(NAME, SYMBOL) \
@@ -240,5 +297,4 @@
                      "retaining default.\n");
         }
-
     }
 
@@ -260,4 +316,11 @@
     }
 
+    // Print out what we're going to do
+    if (psTraceGetLevel(__func__) > 9) {
+        checkList(data->chips, "CHIP");
+        checkList(data->cells, "CELL");
+        checkList(data->headers, "HEADER");
+        checkList(data->concepts, "CONCEPT");
+    }
 
     return data;
