Index: trunk/ppStack/src/ppStackCleanup.c
===================================================================
--- trunk/ppStack/src/ppStackCleanup.c	(revision 27427)
+++ trunk/ppStack/src/ppStackCleanup.c	(revision 30620)
@@ -1,16 +1,6 @@
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
+#include "ppStack.h"
 
-#include <stdio.h>
-#include <pslib.h>
-#include <psmodules.h>
-#include <ppStats.h>
-
-#include "ppStack.h"
-#include "ppStackLoop.h"
-
-
-bool ppStackCleanup(ppStackThreadData *stack, ppStackOptions *options, pmConfig *config)
+// ppStackCleanupFiles 
+bool ppStackCleanupFiles(ppStackThreadData *stack, ppStackOptions *options, pmConfig *config, ppStackFileList stackFiles, ppStackFileList photFiles, bool closeJPEGs)
 {
     psAssert(stack, "Require stack");
@@ -18,78 +8,30 @@
     psAssert(config, "Require configuration");
 
-    psMetadata *recipe = psMetadataLookupMetadata(NULL, config->recipes, PPSTACK_RECIPE); // ppStack recipe
-    psAssert(recipe, "We've thrown an error on this before.");
-
-#if 0
-    // Ensure masked regions really look masked
-    {
-        psString maskBadStr = psMetadataLookupStr(NULL, recipe, "MASK.BAD"); // Name of bits for bad
-        psImageMaskType maskBad = pmConfigMaskGet(maskBadStr, config); // Bits to mask for bad pixels
-        if (!pmReadoutMaskApply(options->outRO, maskBad)) {
-            psWarning("Unable to apply mask");
-        }
-    }
-#endif
-
-    // Generate binned JPEGs
-    {
-        int bin1 = psMetadataLookupS32(NULL, recipe, "BIN1"); // First binning level
-        int bin2 = psMetadataLookupS32(NULL, recipe, "BIN2"); // Second binning level
-
-        // Target cells
-        pmFPAview *view = pmFPAviewAlloc(0); // View to cells of interest
-        view->chip = view->cell = 0;
-        pmCell *cell1 = pmFPAfileThisCell(config->files, view, "PPSTACK.OUTPUT.JPEG1");
-        pmCell *cell2 = pmFPAfileThisCell(config->files, view, "PPSTACK.OUTPUT.JPEG2");
-        psImageMaskType maskValue = pmConfigMaskGet("BLANK", config); // Bits to mask
-        psFree(view);
-
-        pmReadout *ro1 = pmReadoutAlloc(cell1), *ro2 = pmReadoutAlloc(cell2); // Binned readouts
-        if (!pmReadoutRebin(ro1, options->outRO, maskValue, bin1, bin1) ||
-            !pmReadoutRebin(ro2, ro1, 0, bin2, bin2)) {
-            psError(PPSTACK_ERR_DATA, false, "Unable to bin output.");
-            psFree(ro1);
-            psFree(ro2);
-            return false;
-        }
-        psFree(ro1);
-        psFree(ro2);
-    }
-
-    // Statistics on output
-    if (options->stats) {
-        psTrace("ppStack", 1, "Gathering statistics on stacked image....\n");
-        psString maskBadStr = psMetadataLookupStr(NULL, recipe, "MASK.BAD"); // Name of bits for bad
-        psImageMaskType maskBad = pmConfigMaskGet(maskBadStr, config); // Bits to mask for bad pixels
-
-        pmFPAview *view = pmFPAviewAlloc(0); // View to readout
-        view->chip = view->cell = view->readout = 0;
-
-        ppStatsFPA(options->stats, options->outRO->parent->parent->parent, view, maskBad, config);
-
-        psFree(view);
-    }
-
     if (!ppStackFilesIterateUp(config)) {
         psError(psErrorCodeLast(), false, "Unable to close files.");
         return false;
     }
-    ppStackFileActivation(config, PPSTACK_FILES_STACK, false);
-    ppStackFileActivation(config, PPSTACK_FILES_PHOT, false);
+    ppStackFileActivation(config, stackFiles, false);
+    ppStackFileActivation(config, photFiles, false);
 
     // Ensure files are freed
-    {
-        options->outRO->data_exists = false;
-        options->outRO->parent->data_exists = false;
-        options->outRO->parent->parent->data_exists = false;
-        psFree(options->outRO);
-        options->outRO = NULL;
+    options->outRO->data_exists = false;
+    options->outRO->parent->data_exists = false;
+    options->outRO->parent->parent->data_exists = false;
+    psFree(options->outRO);
+    options->outRO = NULL;
 
-        options->expRO->data_exists = false;
-        options->expRO->parent->data_exists = false;
-        options->expRO->parent->parent->data_exists = false;
-        psFree(options->expRO);
-        options->expRO = NULL;
+    options->expRO->data_exists = false;
+    options->expRO->parent->data_exists = false;
+    options->expRO->parent->parent->data_exists = false;
+    psFree(options->expRO);
+    options->expRO = NULL;
 
+    for (int i = 0; i < options->num; i++) {
+        pmCellFreeData(options->cells->data[i]);
+    }
+
+    if (closeJPEGs) {
+	// XXX move these close / free operations to the jpeg creation function
         pmFPAview *view = pmFPAviewAlloc(0);// Pointer into FPA hierarchy
         view->chip = view->cell = 0;        // pmFPAviewFreeData doesn't want to deal with readouts
@@ -118,2 +60,70 @@
     return true;
 }
+
+bool ppStackCleanup (pmConfig *config, ppStackOptions *options) {
+
+    psExit exitValue = ppStackExitCode(PS_EXIT_SUCCESS); // Exit code
+
+    // Ensure everything closes
+    if (config) {
+	ppStackFileActivation(config, PPSTACK_FILES_PREPARE, true);
+	ppStackFileActivation(config, PPSTACK_FILES_CONVOLVE, true);
+	ppStackFileActivation(config, PPSTACK_FILES_STACK, true);
+	ppStackFileActivation(config, PPSTACK_FILES_UNCONV, true);
+	ppStackFileActivation(config, PPSTACK_FILES_PHOT, true);
+	if (!ppStackFilesIterateUp(config)) {
+	    psError(psErrorCodeLast(), false, "Unable to close files.");
+	    exitValue = ppStackExitCode(exitValue);
+	    pmFPAfileFreeSetStrict(false);
+	}
+    }
+
+    // Write out summary statistics
+    if (options && options->stats) {
+
+	psMetadataAddS32(options->stats, PS_LIST_TAIL, "QUALITY", PS_META_REPLACE, "Bad data quality flag", options->quality);
+	psMetadataAddF32(options->stats, PS_LIST_TAIL, "TIME_STACK", 0, "Total time", psTimerClear("PPSTACK_TOTAL"));
+
+	const char *statsMDC = psMetadataConfigFormat(options->stats);
+	if (!statsMDC || strlen(statsMDC) == 0) {
+	    psError(PS_ERR_IO, false, "Unable to get statistics MDC file.");
+	    exitValue = ppStackExitCode(exitValue);
+	    exit(exitValue);
+	}
+	if (fprintf(options->statsFile, "%s", statsMDC) != strlen(statsMDC)) {
+	    psError(PS_ERR_IO, false, "Unable to write statistics MDC file.");
+	    exitValue = ppStackExitCode(exitValue);
+	    exit(exitValue);
+	}
+	psFree(statsMDC);
+	if (fclose(options->statsFile) == EOF) {
+	    psError(PS_ERR_IO, false, "Unable to close statistics MDC file.");
+	    exitValue = ppStackExitCode(exitValue);
+	    exit(exitValue);
+	}
+	options->statsFile = NULL;
+	pmConfigRunFilenameAddWrite(config, "STATS", psMetadataLookupStr(NULL, config->arguments, "STATS"));
+    }
+    psFree(options);
+
+    // Dump configuration
+    bool mdok;                                                                    // Status of MD lookup
+    psString dump = psMetadataLookupStr(&mdok, config->arguments, "DUMP_CONFIG"); // File for config
+    if (dump && !pmConfigDump(config, dump)) {
+	psError(psErrorCodeLast(), false, "Unable to dump configuration.");
+	exitValue = ppStackExitCode(exitValue);
+    }
+
+    psTrace("ppStack", 1, "Finished at %f sec\n", psTimerMark("PPSTACK"));
+    psTimerStop();
+
+    psFree(config);
+    pmModelClassCleanup();
+    pmConfigDone();
+    psLibFinalize();
+    pmVisualClose();
+    pmVisualCleanup ();
+
+    exitValue = ppStackExitCode(exitValue);
+    exit(exitValue);
+}
