Index: /trunk/archive/scripts/src/phase2/Makefile
===================================================================
--- /trunk/archive/scripts/src/phase2/Makefile	(revision 4793)
+++ /trunk/archive/scripts/src/phase2/Makefile	(revision 4793)
@@ -0,0 +1,26 @@
+SHELL = /bin/sh
+CC = gcc
+CFLAGS += -O0 -g -std=c99 -Werror -I/home/mithrandir/price/pan-starrs/jhroot/i686-pc-linux-gnu/include/ -D_GNU_SOURCE
+PSLIB += -L/home/mithrandir/price/pan-starrs/jhroot/i686-pc-linux-gnu/lib/ -lpslib -lgsl -lgslcblas -lfftw3f -lsla -lcfitsio -lm -lxml2 -lmysqlclient
+LDFLAGS += $(PSLIB)
+
+OBJS = papPhase2.o psAdditionals.o
+TARGET = papPhase2
+
+.PHONY: tags clean empty
+
+.c.o:
+		$(CC) -c $(CFLAGS) $(OPTFLAGS) $<
+
+$(TARGET):	$(OBJS)
+		$(CC) $(CFLAGS) -o $@ $(OBJS) $(LDFLAGS) $(OPTFLAGS)
+
+clean:
+		-$(RM) *.o gmon.* profile.txt
+
+empty:		clean
+		-$(RM) $(TARGET) TAGS
+
+# Tags for emacs
+tags:
+		etags `find . -name \*.[ch] -print`
Index: /trunk/archive/scripts/src/phase2/papPhase2.c
===================================================================
--- /trunk/archive/scripts/src/phase2/papPhase2.c	(revision 4793)
+++ /trunk/archive/scripts/src/phase2/papPhase2.c	(revision 4793)
@@ -0,0 +1,680 @@
+#include <stdio.h>
+#include "pslib.h"
+#include "psAdditionals.h"
+
+// Phase 2 needs to:
+// * Read configurations
+// * Read in the images
+// * Flag bad pixels
+// * Non-linearity correction
+// * Bias/dark subtraction (MHPCC)
+// * Flat-field (MHPCC)
+// * Fringe subtraction (Not for now)
+// * Source identification and photometry (IPP prototype)
+// * Astrometry (IPP prototype soon)
+// * Write calibrated image
+// * Write source catalogue, astrometry, etc.
+ 
+// Currently neglecting different input types (F32, S32, U16, etc)
+
+// Would like to fold in Nebulous at some stage as well.
+
+// Currently neglect to convolve the reference frames with the orthogonal transfer kernel
+
+// phase2 INPUT.fits OUTPUT.fits -bias BIAS.fits -dark DARK.fits -flat FLAT.fits -chip CHIP
+// 
+// Most are self-explanatory.  "-chip" says "only work on this particular chip".
+
+static psMemoryId memPrintAlloc(const psMemBlock *mb)
+{
+    printf("Allocated memory block %lld (%lld):\n"
+	   "\tFile %s, line %d, size %d\n"
+	   "\tPosts: %x %x %x\n",
+	   mb->id, mb->refCounter, mb->file, mb->lineno, mb->userMemorySize, mb->startblock, mb->endblock,
+	   *(void**)((int8_t *)(mb + 1) + mb->userMemorySize));
+    return 0;
+}
+static psMemoryId memPrintFree(const psMemBlock *mb)
+{
+    printf("Freed memory block %lld (%lld):\n"
+	   "\tFile %s, line %d, size %d\n"
+	   "\tPosts: %x %x %x\n",
+	   mb->id, mb->refCounter, mb->file, mb->lineno, mb->userMemorySize, mb->startblock, mb->endblock,
+	   *(void**)((int8_t *)(mb + 1) + mb->userMemorySize));
+    return 0;
+}
+
+int main(int argc, char *argv[])
+{
+    psMemAllocateCallbackSetID(71);
+    psMemFreeCallbackSetID(71);
+    psMemAllocateCallbackSet(memPrintAlloc);
+    psMemFreeCallbackSet(memPrintFree);
+
+    psTraceSetLevel("psArgumentParse", 10);
+
+    // Parse the configurations
+    psMetadata *site = NULL;            // Site configuration
+    psMetadata *camera = NULL;          // Camera configuration
+    psMetadata *recipe = NULL;          // Recipe configuration
+
+#if 0
+    if (! pmConfigRead(&site, &camera, &recipe, &argc, argv, "moduleName")) {
+        psErrorStackPrint("Can't find site configuration!\n");
+        exit(EXIT_FAILURE);
+    }
+#endif
+
+    // Parse other command-line arguments
+    psMetadata *arguments = psMetadataAlloc(); // The arguments, with default values
+    psMetadataAddStr(arguments, PS_LIST_TAIL, "-bias", "Name of the bias image", "");
+    psMetadataAddStr(arguments, PS_LIST_TAIL, "-dark", "Name of the dark image", "");
+    psMetadataAddStr(arguments, PS_LIST_TAIL, "-flat", "Name of the flat-field image", "");
+    psMetadataAddStr(arguments, PS_LIST_TAIL, "-mask", "Name of the mask image", "");
+    psMetadataAddS32(arguments, PS_LIST_TAIL, "-chip", "Chip number to process (if positive)", -1);
+
+    psMetadataAdd(arguments, PS_LIST_TAIL, "-string", PS_META_STR, "Test string", "SomeString");
+    psMetadataAdd(arguments, PS_LIST_TAIL, "-bool", PS_META_BOOL, "Test bool", false);
+#if 1
+    psMetadataAdd(arguments, PS_LIST_TAIL, "-int", PS_META_S32 | PS_META_DUPLICATE_OK, "Test integer 1", 1);
+    psMetadataAdd(arguments, PS_LIST_TAIL, "-int", PS_META_S32 | PS_META_DUPLICATE_OK, "Test integer 2", 2);
+    psMetadataAdd(arguments, PS_LIST_TAIL, "-int", PS_META_S32 | PS_META_DUPLICATE_OK, "Test integer 3", 3);
+#endif
+    psMetadataAdd(arguments, PS_LIST_TAIL, "-float", PS_META_F32, "Test float", 1.234567);
+
+    if (! psArgumentParse(arguments, &argc, argv) || argc != 3) {
+	printf("\nPan-STARRS Phase 2 processing\n\n");
+	printf("Usage: %s INPUT.fits OUTPUT.fits\n\n", argv[0]);
+	psArgumentHelp(arguments);
+	psFree(arguments);
+	exit(EXIT_FAILURE);
+    }
+    const char *inputName = argv[1];	// Name of input image
+    const char *outputName = argv[2];	// Name of output image
+
+    printf("Success: %s %s\n", inputName, outputName);
+
+    const char *biasName = psMetadataLookupString(NULL, arguments, "-bias"); // Name of bias image
+    const char *darkName = psMetadataLookupString(NULL, arguments, "-dark"); // Name of dark image
+    const char *flatName = psMetadataLookupString(NULL, arguments, "-flat"); // Name of flat-field image
+    const char *maskName = psMetadataLookupString(NULL, arguments, "-mask"); // Name of mask image
+    const int chipNum = psMetadataLookupS32(NULL, arguments, "-chip"); // Chip number to work on
+    printf("Bias: %s\n", biasName);
+    printf("Dark: %s\n", darkName);
+    printf("Flat: %s\n", flatName);
+    printf("Mask: %s\n", maskName);
+    printf("Chip: %d\n", chipNum);
+
+    // For testing
+    psString string = psMetadataLookupString(NULL, arguments, "-string");
+    float floating = psMetadataLookupF32(NULL, arguments, "-float");
+    bool mdok = false;
+    bool boolean = psMetadataLookupBool(&mdok, arguments, "-bool");
+    if (!mdok) printf("Urgh!\n");
+    printf("String: %s\n", string);
+    printf("Float: %f\n", floating);
+    printf("Boolean: %d\n", boolean);
+
+#if 0
+    psMetadataItem *intItem = psMetadataLookup(arguments, "-int");
+    if (intItem->type == PS_META_MULTI) {
+	psList *intMulti = intItem->data.V;
+	psListIterator *intIter = psListIteratorAlloc(intMulti, PS_LIST_HEAD, false);
+	psMetadataItem *intMultiItem = NULL;
+	while (intMultiItem = psListGetAndIncrement(intIter)) {
+	    printf("Integer: %d\n", intMultiItem->data.S32);
+	}
+	psFree(intIter);
+    }
+#endif
+
+
+
+
+
+#if 0
+    // Open the input
+    psFits *inputFile = psFitsOpen(inputName, "r"); // File handle for FITS file
+    if (! inputFile) {
+	psErrorStackPrint("Can't open input image: %s\n", inputName);
+        exit(EXIT_FAILURE);
+    }
+    psMetadata *header = psFitsReadHeader(NULL, inputFile); // FITS header
+    psDB *database = pmConfigDB(site);	// Database handle
+
+    // Open the output and output mask
+    // We do it here so that we don't process the whole lot and then find out we can't open the output file
+    psFits *outputFile = psFitsOpen(outputName, "w");
+    if (! outputFile) {
+	psErrorStackPrint("Can't open output image: %s\n", outputName);
+	exit(EXIT_FAILURE);
+    }
+    psString outputMaskName = psStringCopy(outputName);
+    (void)psStringAppend(outputMaskName, ".mask");
+    psFits *outputMaskFile = psFits(outputMaskName, "w");
+    if (! outputMaskFile) {
+	psErrorStackPrint("Can't open output mask image: %s\n", outputMaskName);
+	exit(EXIT_FAILURE);
+    }
+    psFree(outputMaskName);
+	
+    // Get camera configuration from header if not already defined
+    if (! camera) {
+	camera = pmConfigCameraFromHeader(site, header);
+	if (! camera) {
+	    psErrorStackPrint("Can't find camera configuration!\n");
+	    exit(EXIT_FAILURE);
+	}
+    } else if (! pmConfigValidateCamera(camera, inputHeader)) {
+	psError("phase2", true, "%s does not seem to be from the camera.\n", inputName);
+	exit(EXIT_FAILURE);
+    }
+    if (! recipe && !(recipe = pmConfigRecipeFromCamera(camera, "moduleName"))) {
+        psErrorStackPrint("Can't find recipe configuration!\n");
+        exit(EXIT_FAILURE);
+    }
+
+    // Construct camera in preparation for reading
+    pmFPA *input = pmFPAConstruct(camera, db);
+    pmFPA *mask = pmFPAConstruct(camera, db);
+    pmFPA *bias = pmFPAConstruct(camera, db);
+    pmFPA *dark = pmFPAConstruct(camera, db);
+    pmFPA *flat = pmFPAConstruct(camera, db);
+
+    // Set various tasks
+    bool doMask = false;		// Mask bad pixles
+    bool doNonLin = false;		// Non-linearity correction
+    bool doBias = false;		// Bias subtraction
+    bool doDark = false;		// Dark subtraction
+    bool doOverscan = false;		// Overscan subtraction
+    bool doFlat = false;		// Flat-field normalisation
+    bool doFringe = false;		// Fringe subtraction
+    bool doSource = false;		// Source identification and photometry
+    bool doAstrom = false;		// Astrometry
+    pmOverscanAxis overscanMode = PM_OVERSCAN_NONE; // Axis for overscan
+    pmFit overscanFit = PM_FIT_NONE;	// Fit type for overscan
+    int overscanBins = 1;		// Number of pixels per bin for overscan
+    psStats *overscanStats = NULL;	// Statistics for overscan
+
+    if (psMetadataLookupBool(NULL, recipe, "MASK")) {
+	if (maskName) {
+	    doMask = true;
+	} else {
+	    psLogMsg("phase2", PS_LOG_WARN, "Masking is desired, but no mask was supplied --- no masking "
+		     "will be performed.\n");
+	}
+    }
+    if (psMetadataLookupBool(NULL, recipe, "NONLIN")) {
+	doNonLin = true;
+    }
+    if (psMetadataLookupBool(NULL, recipe, "BIAS")) {
+	if (biasName) {
+	    doBias = true;
+	} else {
+	    psLogMsg("phase2", PS_LOG_WARN, "Bias subtraction is desired, but no bias was supplied --- "
+		     "no bias subtraction will be performed.\n");
+	}
+    }
+    if (psMetadataLookupBool(NULL, recipe, "DARK")) {
+	if (darkName) {
+	    doDark = true;
+	} else {
+	    psLogMsg("phase2", PS_LOG_WARN, "Dark subtraction is desired, but no dark was supplied --- "
+		     "no dark subtraction will be performed.\n");
+	}
+    }
+    if (psMetadataLookupBool(NULL, recipe, "OVERSCAN")) {
+	doOverscan = true;
+	psString mode = psMetadataLookupString(NULL, recipe, "OVERSCAN.MODE");
+	if (strcasecmp(mode, "INDIVIDUAL") == 0) {
+	    overscanMode = PM_OVERSCAN_ROWS;
+	    // By "ROWS", I mean either rows or columns --- will check the READDIR for each chip
+	} else if (strcasecmp(mode, "ALL") == 0) {
+	    overscanMode = PM_OVERSCAN_ALL;
+	} else if (strcasecmp(mode, "NONE") != 0) {
+	    psLogMsg("phase2", PS_LOG_WARN, "OVERSCAN.MODE (%s) is not one of NONE, INDIVIDUAL, or ALL:"
+		     " assuming NONE.\n", mode);
+	}
+	psFree(mode);
+	psString fit = psMetadataLookupString(NULL, recipe, "OVERSCAN.FIT");
+	if (strcasecmp(fit, "POLYNOMIAL") == 0) {
+	    overscanFit = PM_FIT_POLYNOMIAL;
+	} else if (strcasecmp(fit, "SPLINE") == 0) {
+	    overscanFit = PM_FIT_SPLINE;
+	} else if (strcasecmp(fit, "NONE") != 0) {
+	    psLogMsg("phase2", PS_LOG_WARN, "OVERSCAN.FIT (%s) is not one of NONE, POLYNOMIAL, or SPLINE:"
+		     " assuming NONE.\n", fit);
+	}
+	psFree(fit);
+	overscanBins = psMetadataLookupS32(NULL, recipe, "OVERSCAN.BIN");
+	if (overscanBins <= 0) {
+	    psErrorStackPrint("OVERSCAN.BIN (%d) is non-positive --- assuming 1.\n", overscanBins);
+	    overscanBins = 1;
+	}
+	psString stat = psMetadataLookupString(NULL, recipe, "OVERSCAN.STAT");
+	if (strcasecmp(stat, "MEAN")) {
+	    overscanStats = psStatsAlloc(PS_STAT_SAMPLE_MEAN);
+	} else if (strcasecmp(stat, "MEDIAN")) {
+	    overscanStats = psStatsAlloc(PS_STAT_SAMPLE_MEDIAN);
+	} else {
+	    psErrorStackPrint("OVERSCAN.STAT (%s) is not one of MEAN, MEDIAN: assuming MEAN\n", stat);
+	    overscanStats = psStatsAlloc(PS_STAT_SAMPLE_MEAN);	    
+	}
+    }
+    if (psMetadataLookupBool(NULL, recipe, "FLAT")) {
+	if (flatName) {
+	    doFlat = true;
+	} else {
+	    psLogMsg("phase2", PS_LOG_WARN, "Flat-fielding is desired, but no flat was supplied --- "
+		     "no flat-fielding will be performed.\n");
+	}
+    }
+	 
+    // Chip selection
+    if (chipNum >= 0) {
+	if (! pmFPASelectChip(input, chipNum) || ! pmFPASelectChip(bias, chipNum) ||
+	    ! pmFPASelectChip(dark, chipNum) || ! pmFPASelectChip(flat, chipNum) ||
+	    ! pmFPASelectChip(mask, chipNum)) {
+	    psErrorStackPrint("Chip number %d doesn't exist in camera.\n", chipNum);
+	    exit(EXIT_FAILURE);
+	}
+	psLogMsg("phase2", PS_LOG_INFO, "Operating only on chip %d\n", chipNum);
+    }
+
+    // Read in the input pixels
+    if (! pmFPARead(input, inputFile)) {
+	psErrorStackPrint("Unable to populate camera from FITS file: %s\n", inputName);
+	exit(EXIT_FAILURE);
+    }
+    psFitsClose(inputFile);
+
+    // Load the calibration frames, if required
+    if (biasName) {
+	psFits *biasFile = psFitsOpen(biasName, "r"); // File handle for bias
+	psMetadata *biasHeader = psFitsReadHeader(NULL, biasFile); // FITS header for bias
+	if (! pmConfigValidateCamera(camera, biasHeader)) {
+	    psError("phase2", true, "Bias (%s) does not seem to be from the same camera.\n", biasName);
+	    exit(EXIT_FAILURE);
+	}
+	psFree(biasHeader);
+	if (! pmFPARead(bias, biasFile)) {
+	    psErrorStackPrint("Unable to populate bias camera from fits FITS: %s\n", biasName);
+	    exit(EXIT_FAILURE);
+	}
+	psFitsClose(biasFile);
+    }
+
+    if (darkName) {
+	psFits *darkFile = psFitsOpen(darkName, "r"); // File handle for dark
+	psMetadata *darkHeader = psFitsReadHeader(NULL, darkFile); // FITS header for dark
+	if (! pmConfigValidateCamera(camera, darkHeader)) {
+	    psError("phase2", true, "Dark (%s) does not seem to be from the same camera.\n", darkName);
+	    exit(EXIT_FAILURE);
+	}
+	psFree(darkHeader);
+	if (! pmFPARead(dark, darkFile)) {
+	    psErrorStackPrint("Unable to populate dark camera from fits FITS: %s\n", darkName);
+	    exit(EXIT_FAILURE);
+	}
+	psFitsClose(darkFile);
+    }
+
+    if (maskName) {
+	psFits *maskFile = psFitsOpen(maskName, "r"); // File handle for mask
+	psMetadata *maskHeader = psFitsReadHeader(NULL, maskFile); // FITS header for mask
+	if (! pmConfigValidateCamera(camera, maskHeader)) {
+	    psError("phase2", true, "Mask (%s) does not seem to be from the same camera.\n", maskName);
+	    exit(EXIT_FAILURE);
+	}
+	psFree(maskHeader);
+	if (! pmFPARead(mask, maskFile)) {
+	    psErrorStackPrint("Unable to populate mask camera from fits FITS: %s\n", maskName);
+	    exit(EXIT_FAILURE);
+	}
+	psFitsClose(maskFile);
+    }
+    
+    if (flatName) {
+	psFits *flatFile = psFitsOpen(flatName, "r"); // File handle for flat
+	psMetadata *flatHeader = psFitsReadHeader(NULL, flatFile); // FITS header for flat
+	if (! pmConfigValidateCamera(camera, flatHeader)) {
+	    psError("phase2", true, "Flat (%s) does not seem to be from the same camera.\n", flatName);
+	    exit(EXIT_FAILURE);
+	}
+	psFree(flatHeader);
+	if (! pmFPARead(flat, flatFile)) {
+	    psErrorStackPrint("Unable to populate flat camera from fits FITS: %s\n", flatName);
+	    exit(EXIT_FAILURE);
+	}
+	psFitsClose(flatFile);
+    }
+    
+    psArray *inputChips = inputs->chips; // Array of chips in input image
+    psArray *biasChips = bias->chips;	// Array of chips in bias image
+    psArray *darkChips = dark->chips;	// Array of chips in dark image
+    psArray *maskChips = mask->chips;	// Array of chips in mask image
+    psArray *flatChips = flat->chips;	// Array of chips in flat image
+    for (int i = 0; i < inputChips->n; i++) {
+	pmChip *inputChip = inputChips->data[i]; // Chip of interest in input image
+	pmChip *biasChip = biasChips->data[i]; // Chip of interest in bias image
+	pmChip *darkChip = darkChips->data[i]; // Chip of interest in dark image
+	pmChip *maskChip = maskChips->data[i]; // Chip of interest in mask image
+	pmChip *flatChip = flatChips->data[i]; // Chip of interest in flat image
+
+	if (! inputChip->valid) {
+	    continue;
+	}
+
+	psArray *inputCells = inputChip->cells; // Array of cells in input image
+	psArray *biasCells = biasChip->cells; // Array of cells in bias image
+	psArray *darkCells = darkChip->cells; // Array of cells in dark image
+	psArray *maskCells = maskChip->cells; // Array of cells in mask image
+	psArray *flatCells = flatChip->cells; // Array of cells in flat image
+
+	for (int j = 0; j < inputCells->n; j++) {
+	    pmCell *inputCell = inputCells->data[i]; // Cell of interest in input image
+	    pmCell *biasCell = biasCells->data[i]; // Cell of interest in bias image
+	    pmCell *darkCell = darkCells->data[i]; // Cell of interest in dark imag
+	    pmCell *maskCell = maskCells->data[i]; // Cell of interest in mask image
+	    pmCell *flatCell = flatCells->data[i]; // Cell of interest in flat image
+
+	    if (! inputCell->valid) {
+		continue;
+	    }
+
+	    psArray *inputReadouts = inputCell->readouts; // Array of readouts in input image
+	    if (biasCell->readouts->n != 1) {
+		psLogMsg("phase2", PS_LOG_WARN, "Bias contains multiple readouts: only the first will be"
+			 " used.");
+	    }
+	    if (darkCell->readouts->n != 1) {
+		psLogMsg("phase2", PS_LOG_WARN, "Dark contains multiple readouts: only the first will be"
+			 " used.");
+	    }
+	    if (maskCell->readouts->n != 1) {
+		psLogMsg("phase2", PS_LOG_WARN, "Mask contains multiple readouts: only the first will be"
+			 " used.");
+	    }
+	    if (flatCell->readouts->n != 1) {
+		psLogMsg("phase2", PS_LOG_WARN, "Flat contains multiple readouts: only the first will be"
+			 " used.");
+	    }
+
+	    psImage *biasImage = NULL;	// Bias pixels
+	    psImage *darkImage = NULL;	// Dark pixels
+	    float darkTime = 1.0;	// Dark time for dark image
+
+	    if (biasName) {
+		pmReadout *biasReadout = biasCell->readouts->data[0]; // Readout of interest in bias image
+		biasImage = biasReadout->image;
+	    }
+	    if (darkName) {
+		pmReadout *darkReadout = darkCell->readouts->data[0]; // Readout of interest in dark image
+		darkImage = darkReadout->image;
+		darkTime = pmReadoutGetDarkTime(darkReadout);
+		if (darkTime <= 0.0) {
+		    psErrorStackPrint("DARKTIME for dark image (%f) is non-positive.\n", darkTime);
+		    exit(EXIT_FAILURE);
+		}
+	    }
+	    if (maskName) {
+		pmReadout *maskReadout = maskCell->readouts->data[0]; // Readout of interest in mask image
+	    }
+	    if (flatName) {
+		pmReadout *flatReadout = flatCell->readouts->data[0]; // Readout of interest in mask image
+	    }
+
+	    for (int k = 0; k < inputReadouts->n; k++) {
+		pmReadout *inputReadout = inputReadouts->data[k]; // Readout of interest in input image
+		psImage *inputImage = inputReadout->image; // The actual image
+		psList *inputBias = inputReadout->bias; // List of overscan bias regions
+
+		// Mask bad pixels
+		if (doMask) {
+		    // Need to change this later to grow the mask by the size of the convolution kernel
+		    (void)pmMaskBadPixels(inputReadout, maskReadout,
+					  PS_MASK_TRAP | PS_MASK_BADCOL | PS_MASK_SAT, saturation,
+					  PS_MASK_TRAP, 0);
+		}
+
+		// Non-linearity correction
+		if (doNonLin) {
+		    psMetadataItem *dataItem = psMetadataLookup(recipe, "NONLIN.DATA");
+		    if (! dataItem) {
+			psLogMsg("phase2", PS_LOG_WARN, "Non-linearity correction desired, but unable to "
+				 "find NONLIN.DATA in recipe --- ignored.\n");
+		    } else {
+			switch (dataItem->type) {
+			  case PS_META_VECTOR:
+			    // These are the polynomial coefficients
+			    psVector *coeff = dataItem->data.V; // The coefficient vector
+			    if (coeff->type.type != PS_TYPE_F64) {
+				psVector *temp = psVectorCopy(NULL, coeff, PS_TYPE_F64); // F64 version
+				psFree(coeff);
+				coeff = temp;
+			    }
+			    psPolynomial1D *correction = psPolynomial1DAlloc(PS_POLYNOMIAL_ORD,
+									     coeff->n + 1);
+			    for (int i = 0; i < coeff->n; i++) {
+				correction->coeff[i] = coeff->data.F64[i];
+			    }
+			    (void)pmNonLinearityPolynomial(inputReadout, correction);
+			    psFree(coeffCopy);
+			    psFree(coeff);
+			    psFree(correction);
+			    break;
+			  case PS_META_STR:
+			    // This is a filename
+			    psString name = dataItem->data.V;	// Filename
+			    psLookupTable *table = psLookupTableAlloc(name, "%f %f", 0);
+			    if (psLookupTableRead(table) <= 0) {
+				psErrorStackPrint("Unable to read non-linearity correction file %s --- "
+						  "ignored\n", tableName);
+			    } else {
+				(void)pmNonLinearityLookup(inputReadout, table);
+			    }
+			    psFree(table);
+			    break;
+			  case PS_META_META:
+			    // This is a menu
+			    psMetadata *options = dataItem->data.V; // Options with concept values as keys
+			    bool mdok = false; // Success of MD lookup
+			    psString concept = psMetadataLookupStr(&mdok, recipe, "NONLIN.SOURCE");
+			    if (! mdok || ! concept) {
+				psLogMsg("phase2", PS_LOG_WARN, "Non-linearity correction desired, but "
+					 "unable to find NONLIN.SOURCE in recipe --- ignored.\n");
+			    } else {
+				psMetadataItem *conceptValueItem = pmCellGetConcept(inputCell, concept);
+				if (! conceptValueItem) {
+				    psLogMsg("phase2", PS_LOG_WARN, "Unable to find value of concept %s "
+					     "for non-linearity correction --- ignored.\n", concept);
+				} else if (conceptValueItem->type != PS_META_STR) {
+				    psLogMsg("phase2", PS_LOG_WARN, "Type for concept %s isn't STRING, as"
+					     " expected for non-linearity correction --- ignored.\n",
+					     concept);
+				} else {
+				    psString conceptValue = conceptValueItem->data.V;
+				    // Get the value of the concept
+				    psMetadataItem *optionItem = psMetadataLookup(options, conceptValue);
+				    if (!optionItem) {
+					psLogMsg("phase2", PS_LOG_WARN, "Unable to find %s in NONLIN.DATA"
+						 " --- ignored.\n", conceptValue);
+				    } else if (optionItem->type == PS_META_VECTOR) {
+					// These are the polynomial coefficients
+					psVector *coeff = optionItem->data.V; // The coefficient vector
+					if (coeff->type.type != PS_TYPE_F64) {
+					    psVector *temp = psVectorCopy(NULL, coeff, PS_TYPE_F64);
+					    psFree(coeff);
+					    coeff = temp;
+					}
+					// Polynomial correction
+					psPolynomial1D *correction = psPolynomial1DAlloc(PS_POLYNOMIAL_ORD,
+											 coeff->n + 1);
+					for (int i = 0; i < coeff->n; i++) {
+					    correction->coeff[i] = coeff->data.F64[i];
+					}
+					(void)pmNonLinearityPolynomial(inputReadout, correction);
+					psFree(coeffCopy);
+					psFree(coeff);
+					psFree(correction);
+				    } else if (optionItem->type == PS_META_STR) {
+					// This is a filename
+					psString tableName = optionItem->data.V; // The filename
+					psLookupTable *table = psLookupTableAlloc(tableName, "%f %f", 0);
+					if ((int numLines = psLookupTableRead(table)) <= 0) {
+					    psErrorStackPrint("Unable to read non-linearity correction "
+							      "file %s --- ignored\n", tableName);
+					} else {
+					    (void)pmNonLinearityLookup(inputReadout, table);
+					}
+					psFree(table);
+				    } else {
+					psLogMsg("phase2", PS_LOG_WARN, "Non-linearity correction "
+						 "desired but unable to interpret NONLIN.DATA for %s"
+						 " --- ignored\n", conceptValue);
+				    }
+				}
+			    }
+			    break;
+			  default:
+			    psLogMsg("phase2", PS_LOG_WARN, "Non-linearity correction desired, but "
+				     "NONLIN.DATA is of invalid type --- ignored.\n");
+			}
+		    }
+		} // Non-linearity correction
+
+		// Bias, dark and overscan subtraction are all merged.
+
+		// Changed the definition of pmSubtractBias to do the dark subtraction as well, but
+		// it's not in there yet.  Once it is, we can get rid of "pedestal".
+
+		psImage *pedestal = NULL;	// Pedestal image (bias + scaled dark)
+		if (doDark) {
+		    float inputTime = pmReadoutGetDarkTime(inputReadout); // Dark time for input image
+		    if (inputTime <= 0) {
+			psErrorStackPrint("DARKTIME for input image (%f) is non-positive.\n", inputTime);
+			exit(EXIT_FAILURE);
+		    }
+
+		    pedestal = psBinaryOp(NULL, darkImage, "*",
+					  psScalarAlloc(inputTime * darkTime, PS_TYPE_F32));
+		}
+		if (doBias) {
+		    if (pedestal) {
+			if (biasImage->numRows != darkImage->numRows ||
+			    biasImage->numCols != darkImage->numCols) {
+			    psError("phase2", true, "Bias and dark images have different dimensions: "
+				    "%dx%d vs %dx%d\n", biasImage->numCols, biasImage->numRows,
+				    darkImage->numCols, darkImage->numRows);
+			    exit(EXIT_FAILURE);
+			}
+			(void)psBinaryOp(pedestal, pedestal, "+", biasImage);
+		    } else {
+			pedestal = psMemIncrRefCounter(biasImage);
+		    }
+		}
+
+		if (overscanMode == PM_OVERSCAN_ROWS || overscanMode == PM_OVERSCAN_COLUMNS) {
+		    // Need to get the read direction
+		    int readdir = pmCellGetReaddir(inputCell); // Read direction
+		    if (readdir == 1) {
+			overscanMode = PM_OVERSCAN_ROWS;
+		    } else if (readdir == 2) {
+			overscanMode = PM_OVERSCAN_COLUMNS;
+		    } else {
+			psErrorStackPrint("CELL.READDIR (%d) is not 1 or 2 --- assuming 1.\n", readdir);
+			overscanMode = PM_OVERSCAN_ROWS;
+		    }
+		}
+
+		void *overscanResult = NULL; // Result of overscan fit
+		(void)pmSubtractBias(inputImage, overscanResult, inputBias, overscanMode, overscanStat,
+				     overscanBin, overscanFit, pedestal);
+
+		// Output overscan fit results, if required
+		if (doOverscan) {
+		    switch (overscanFit) {
+		      case PM_FIT_POLYNOMIAL:
+			psPolynomial1D *poly = (psPolynomial1D*)overscanResult;	// The polynomial
+			psString coeffs = NULL;	// String containing the coefficients
+			for (int i = 0; i < poly->n; i++) {
+			    psStringAppend(&coeffs, "%.2f ", poly->coeffs[i]);
+			}
+			psLogMsg("phase2", PS_LOG_INFO, "Overscan polynomial coefficients:\n%s\n",
+				 coeffs);
+			psFree(coeffs);
+			break;
+		      case PM_FIT_SPLINE:
+			psSpline1D *spline = (psSpline1D*)overscanResult; // The spline
+			psString coeffs = NULL;	// String containing the coefficients
+			for (int i = 0; i < spline->n; i++) {
+			    psPolynomial1D *poly = spline->spline[i]; // i-th polynomial
+			    psStringAppend(&coeffs, "%d: ", i);
+			    for (int j = 0; j < poly->n; j++) {
+				psStringAppend(&coeffs, "%.2f ", poly->coeffs[i]);
+			    }
+			    psStringAppend(&coeffs, "\n");
+			}
+			psLogMsg("phase2", PS_LOG_INFO, "Overscan spline coefficients:\n%s\n", coeffs);
+			psFree(coeffs);
+			break;
+		      case PM_FIT_NONE:
+			break;
+		      default:
+			psAbort("Should never get here!!!\n");
+		    }
+		}
+
+		// Flat-field correction
+		if (doFlat) {
+		    (void)pmFlatField(inputReadout, maskReadout, flatReadout);
+		}
+
+		// Fringe subtraction
+		if (doFringe) {
+#if 0
+		    // Placeholder
+		    pmReadout *pmSubtractSky(pmReadout *in, psPolynomial2D *poly, psImage *mask,
+					     psU8 maskVal, int binFactor, psStats *stats, float clipSD);
+#endif
+		}
+
+		// Source identification and photometry
+		if (doSource) {
+		}
+
+		// Astrometry
+		if (doAstrom) {
+		}
+
+	    } // Iterating over readouts
+	} // Iterating over cells
+    } // Iterating over chips
+
+    // Write the output
+    pmFPAWrite(outputFile, input);
+    pmFPAWriteMask(outputMaskFile, input);
+    psFitsClose(outputFile);
+    psFitsClose(outputMaskFile);
+
+    psFree(input);
+    psFree(mask);
+    psFree(bias);
+    psFree(dark);
+    psFree(flat);
+
+#endif
+
+    
+    psMemCheckCorruption(true);
+    psFree(arguments);
+    psMemBlock **leaks = NULL;          // List of leaks
+    int nLeaks = psMemCheckLeaks(0, &leaks, NULL, false); // Number of leaks
+    printf("%d leaks found.\n", nLeaks);
+    for (int i = 0; i < nLeaks; i++) {
+        printf("Memory leak detection: memBlock %lld\n"
+	       "\tFile %s, line %d, size %d\n",
+	       leaks[i]->id, leaks[i]->file, leaks[i]->lineno, leaks[i]->userMemorySize);
+    }
+
+}
Index: /trunk/archive/scripts/src/phase2/psAdditionals.c
===================================================================
--- /trunk/archive/scripts/src/phase2/psAdditionals.c	(revision 4793)
+++ /trunk/archive/scripts/src/phase2/psAdditionals.c	(revision 4793)
@@ -0,0 +1,487 @@
+#include <stdio.h>
+#include <string.h>
+#include "pslib.h"
+#include "psAdditionals.h"
+
+psMetadata *psMetadataLookupMD(bool *status, const psMetadata *md, const char *key)
+{
+    psMetadataItem *item = psMetadataLookup((psMetadata*)md, key); // The metadata with instruments
+    psMetadata *value = NULL;		// The value to return
+    if (!item) {
+	// The given key isn't in the metadata
+	if (status) {
+	    *status = false;
+	} else {
+	    psError(PS_ERR_IO, true, "Couldn't find %s in the metadata.\n");
+	}
+    } else if (item->type != PS_META_META) {
+	// The value at the key isn't metadata
+	if (status) {
+	    *status = false;
+	} else {
+	    psLogMsg(__func__, PS_LOG_WARN, "%s isn't of type PS_META_META, as expected.\n");
+	}
+	value = NULL;
+    } else {
+	// We have the requested metadata
+	if (status) {
+	    *status = true;
+	}
+	value = item->data.md; // The requested metadata
+    }
+    return value;
+}
+
+
+char *psMetadataLookupString(bool *status, const psMetadata *md, const char *key)
+{
+    psMetadataItem *item = psMetadataLookup((psMetadata*)md, key); // The metadata with instruments
+    char *value = NULL;			// The value to return
+    if (!item) {
+	// The given key isn't in the metadata
+	if (status) {
+	    *status = false;
+	} else {
+	    psError(PS_ERR_IO, true, "Couldn't find %s in the metadata.\n");
+	}
+    } else if (item->type != PS_META_STR) {
+	// The value at the key isn't of the desired type
+	if (status) {
+	    *status = false;
+	} else {
+	    psLogMsg(__func__, PS_LOG_WARN, "%s isn't of type PS_META_STR, as expected.\n");
+	}
+	value = NULL;
+    } else {
+	// We have the requested metadata
+	if (status) {
+	    *status = true;
+	}
+	value = item->data.V; // The requested metadata
+    }
+    return value;
+}
+
+
+void psMetadataPrint(psMetadata *md, int level)
+{
+    psMetadataIterator *iter = psMetadataIteratorAlloc(md, PS_LIST_HEAD, NULL);	// Iterator
+    psMetadataItem *item = NULL;	// Item from metadata
+    while (item = psMetadataGetAndIncrement(iter)) {
+	// Indent...
+	for (int i = 0; i < level; i++) {
+	    printf(" ");
+	}
+	printf("%s", item->name);
+	if (item->comment && strlen(item->comment) > 0) {
+	    printf(" (%s)", item->comment);
+	}
+	printf(": ");
+	switch (item->type) {
+	  case PS_META_STR:
+	    printf("%s", item->data.V);
+	    break;
+	  case PS_META_BOOL:
+	    if (item->data.B) {
+		printf("True");
+	    } else {
+		printf("False");
+	    }
+	    break;
+	  case PS_META_S32:
+	    printf("%d", item->data.S32);
+	    break;
+	  case PS_META_F32:
+	    printf("%f", item->data.F32);
+	    break;
+	  case PS_META_F64:
+	    printf("%f", item->data.F64);
+	    break;
+	  case PS_META_META:
+	    printf("\n");
+	    psMetadataPrint(item->data.V, level + 1);
+	    break;
+	  default:
+	    psError(PS_ERR_IO, false, "Non-printable metadata type: %x\n", item->type);
+	}
+	printf("\n");
+    }
+    psFree(iter);
+
+    return;
+}
+
+
+// Set verbosity level
+int psArgumentVerbosity(int *argc, char **argv)
+{
+    int logLevel = 2;			// Default log level
+    int argnum = 0;			// Argument number
+
+    // set in order, so that -vvv overrides -vv overrides -v
+    if (argnum = psArgumentGet(*argc, argv, "-v")) {
+        psArgumentRemove(argnum, argc, argv);
+        logLevel = 3;
+    }
+    if (argnum = psArgumentGet(*argc, argv, "-vv")) {
+        psArgumentRemove(argnum, argc, argv);
+        logLevel = 4;
+    }
+    if (argnum = psArgumentGet(*argc, argv, "-vvv")) {
+        psArgumentRemove(argnum, argc, argv);
+        logLevel = 5;
+    }
+    psLogSetLevel (logLevel);		// XXX: This function should return an error if the log level is invalid
+
+    if (argnum = psArgumentGet(*argc, argv, "-logfmt")) {
+        if (*argc < argnum + 2) {
+            psError(PS_ERR_IO, true, "-logfmt switch specified without a format.");
+        } else {
+	    psArgumentRemove(argnum, argc, argv);
+	    psLogSetFormat(argv[argnum]); // XXX EAM : this function should return an error if the log format is invalid
+	    psArgumentRemove(argnum, argc, argv);
+	}
+    }
+
+    // Now the trace stuff
+    // argument format is: -trace (facil) (level)
+    while (argnum = psArgumentGet(*argc, argv, "-trace")) {
+        if (*argc < argnum + 3) {
+            psError(PS_ERR_IO, true, "-trace switch specified without facility and level.");
+        }
+        psArgumentRemove(argnum, argc, argv);
+        psTraceSetLevel(argv[argnum], atoi(argv[argnum+1])); // XXX: This function should return an error if the trace level is invalid
+        psArgumentRemove(argnum, argc, argv);
+        psArgumentRemove(argnum, argc, argv);
+    }
+    if ((argnum = psArgumentGet(*argc, argv, "-trace-levels"))) {
+        psTracePrintLevels();
+        exit(2);
+    }
+
+    return logLevel;
+}
+ 
+// Find the location of the specified argument
+int psArgumentGet(int argc, char **argv, const char *arg)
+{
+    for (int i = 1; i < argc; i++) {
+        if (!strcmp(argv[i], arg))
+            return i;
+    }
+   
+    return 0;
+}
+
+// Remove the specified argument (by location)
+bool psArgumentRemove(int argnum, int *argc, char **argv)
+{
+    if (argnum > 0) {
+        (*argc)--;
+        for (int i = argnum; i < *argc; i++) {
+            argv[i] = argv[i+1];
+        }
+    } else {
+	return false;
+    }
+    
+    return true;
+}
+
+
+static psMetadataItem *argumentRead(psMetadataItem *item, // Item to read into
+				    int argnum,	// Argument number
+				    int *argc,	// Number of arguments in total
+				    char **argv	// The arguments
+				    )
+{
+    psMetadataItem *newItem = NULL;
+    switch(item->type) {
+	// Only doing a representative set of types
+      case PS_META_S32:
+	newItem = psMetadataItemAlloc(item->name, item->type, item->comment, atoi(argv[argnum]));
+	psArgumentRemove(argnum, argc, argv);
+	break;
+      case PS_META_F32:
+	newItem = psMetadataItemAlloc(item->name, item->type, item->comment, atof(argv[argnum]));
+	psArgumentRemove(argnum, argc, argv);
+	break;
+      case PS_META_BOOL:
+	// Turn option on; no optional argument to remove
+	newItem = psMetadataItemAlloc(item->name, item->type, item->comment, true);
+	break;
+	// XXX: Include the other numerical types
+      case PS_META_STR:
+	{
+	    //psString string = psStringCopy(argv[argnum]);	// Get the argument into PS memory management
+	    //psFree(string);
+	    newItem = psMetadataItemAlloc(item->name, item->type, item->comment, argv[argnum]);
+	    psArgumentRemove(argnum, argc, argv);
+	}
+	break;
+      default:
+	psError(PS_ERR_IO, true, "Argument type (%x) is not supported --- argument %s ignored\n",
+		item->type, item->name);
+	psFree(newItem);
+	return NULL;
+    }
+
+    return newItem;
+}
+
+
+// XXX: There is a memory leak in the MULTI section.  I think it might have something to do with reference
+// counting between lists and MD, in the second section of the code (copy newArgs into arguments), but I'm not
+// entirely sure.
+bool psArgumentParse(psMetadata *arguments, int *argc, char **argv)
+{
+    // We need to do a bit of mucking around in order to preserve the arguments metadata until the last
+    // minute --- if there is a bad argument, we need to return the old "arguments", since they contain
+    // the default values, which we probably want to output in a "help" message (we don't want to print
+    // the changed values and have the user think that they are default values).
+
+    psMetadata *newArgs = psMetadataAlloc(); // Place to read arguments into
+    psList *changed = psListAlloc(NULL);// List of keys that have changed
+
+    for (int i = 1; i < *argc; i++) {
+	psTrace(__func__, 7, "Looking at %s\n", argv[i]);
+	psMetadataItem *argItem = psMetadataLookup(arguments, argv[i]);
+	if (argItem) {
+	    psArgumentRemove(i, argc, argv); // Remove the switch
+	    if (argItem->type != PS_META_MULTI) {
+		if (argItem->type != PS_META_BOOL && *argc < i + 1) {
+		    psError(PS_ERR_IO, true, "Required argument for %s is missing.\n", argItem->name);
+		    // XXX: Cleanup before returning
+		    psFree(newArgs);
+		    return false;
+		}
+		psMetadataItem *newItem = argumentRead(argItem, i, argc, argv);
+		psMetadataAddItem(newArgs, newItem, PS_LIST_TAIL, PS_META_REPLACE);
+		psFree(newItem);
+	    } else {
+		// Go through the MULTI
+		psList *multi = argItem->data.V; // The list of MULTI psMetadataItems
+		if (*argc < i + multi->size) {
+		    psError(PS_ERR_IO, true, "Not enough arguments for %s.\n", argItem->name);
+		    // Remove the arguments --- they will be ignored
+		    for (int j = i; i < *argc; i++) {
+			psArgumentRemove(i, argc, argv);
+		    }
+		    // XXX: Cleanup before returning
+		    psFree(newArgs);
+		    return false;
+		}
+
+		// Remove any prior existence in the newArgs --- this is important because we specify
+		// adding the new items as DUPLICATE_OK, so if some idiot specifies it twice, we'd end
+		// up with two copies of everything.
+		psMetadataItem *checkItem = psMetadataLookup(newArgs, argItem->name);
+		if (checkItem) {
+		    (void)psMetadataRemove(newArgs, 0, argItem->name);
+		    (void)psListRemoveData(changed, argItem->name);
+		}
+
+		psListIterator *multiIter = psListIteratorAlloc(multi, PS_LIST_HEAD, true);
+		psMetadataItem *nextItem = NULL; // Item from list
+		while (nextItem = psListGetAndIncrement(multiIter)) {
+		    psMetadataItem *newItem = argumentRead(nextItem, i, argc, argv);
+		    psMetadataAddItem(newArgs, newItem, PS_LIST_TAIL, PS_META_DUPLICATE_OK);
+		    //psFree(newItem);
+		}
+		psFree(multiIter);
+	    }
+
+	    // Some book-keeping
+	    //	    psString name = psStringCopy(argItem->name);
+	    psListAdd(changed, PS_LIST_TAIL, argItem->name);
+	    i--;
+
+	} else if (strncmp(argv[i], "-", 1) == 0 || strncmp(argv[i], "+", 1) == 0) {
+	    // Someone's specified a bad option
+	    psError(PS_ERR_IO, true, "Unknown option: %s\n", argv[i]);
+	    psFree(newArgs);
+	    return false;
+	}
+    }
+
+    // All the arguments are good, so now we can copy the newArgs over
+    psListIterator *changedIter = psListIteratorAlloc(changed, PS_LIST_HEAD, false); // Iterator
+    psString name = NULL;		// Item from iteration
+    while (name = psListGetAndIncrement(changedIter)) {
+	printf("Updating %s\n", name);
+	psMetadataItem *oldItem = psMetadataLookup(arguments, name);
+	psMetadataItem *newItem = psMetadataLookup(newArgs, name);
+	if (oldItem->type != newItem->type) {
+	    psAbort(__func__, "Shouldn't reach here!\n");
+	}
+	switch (oldItem->type) {
+	    // Only doing a representative set of types
+	  case PS_META_S32:
+	    oldItem->data.S32 = newItem->data.S32;
+	    break;
+	  case PS_META_F32:
+	    oldItem->data.F32 = newItem->data.F32;
+	    break;
+	  case PS_META_BOOL:
+	    oldItem->data.B = newItem->data.B;
+	    break;
+	    // XXX: Include the other numerical types
+	  case PS_META_STR:
+	    psFree(oldItem->data.V);
+	    oldItem->data.V = psMemIncrRefCounter(newItem->data.V);
+	    break;
+	  case PS_META_MULTI:
+	    {
+		psList *newMulti = psMemIncrRefCounter(newItem->data.V);	// The new list of MULTI
+		psList *oldMulti = oldItem->data.V;	// The old list of MULTI
+		psListIterator *newMultiIter = psListIteratorAlloc(newMulti, PS_LIST_HEAD, false);
+		psListIterator *oldMultiIter = psListIteratorAlloc(oldMulti, PS_LIST_HEAD, true);
+		psMetadataItem *newMultiItem = NULL; // Item from iterator
+		while (newMultiItem = psListGetAndIncrement(newMultiIter)) {
+		    psMetadataItem *oldMultiItem = psListGetAndIncrement(oldMultiIter);
+		    if (! oldMultiItem) {
+			psAbort(__func__, 
+				"Something went very wrong here!  The lists SHOULD be of the same length!\n");
+		    }
+		    switch (oldMultiItem->type) {
+			// Only doing a representative set of types
+		      case PS_META_S32:
+			oldItem->data.S32 = newItem->data.S32;
+			break;
+		      case PS_META_F32:
+			oldItem->data.F32 = newItem->data.F32;
+			break;
+			// XXX: Include the other numerical types
+		      case PS_META_STR:
+			psFree(oldItem->data.V);
+			oldItem->data.V = psMemIncrRefCounter(newItem->data.V);
+			break;
+		      default:
+			psAbort(__func__, "Should never ever get here, ever.\n");
+		    }
+		    psFree(oldMultiItem);
+		    psFree(newMultiItem);
+		}
+		psFree(newMultiIter);
+		psFree(oldMultiIter);
+	    }
+	    break;
+	  default:
+	    psAbort(__func__, "Should never ever ever get here.\n");
+	}
+    }
+    psFree(changedIter);
+    psFree(changed);
+
+    // Now, blow away the newArgs and we're done.
+    psFree(newArgs);
+    return true;
+}
+
+
+static int argLength(psMetadataItem *arg)
+{
+    switch (arg->type) {
+	// Only doing a representative set of types
+      case PS_META_S32:
+	return arg->data.S32 >= 0 ? (int)log10f((float)arg->data.S32) + 1 :
+	    (int)log10f(-(float)arg->data.S32) + 2;
+	// XXX: Other numerical types
+      case PS_META_F32:
+	return arg->data.F32 >= 0 ? 12 : 13; // -d.dddddde?dd
+      case PS_META_F64:
+	return arg->data.F64 >= 0 ? 12 : 13; // -d.dddddde?dd
+      case PS_META_BOOL:
+	return arg->data.B ? 4 : 5;
+      case PS_META_STR:
+	return strlen(arg->data.V);
+      default:
+	psAbort(__func__, "Argument type (%x) is not supported.\n", arg->type);
+    }
+ 
+    return 0;
+}
+
+#define NUM_SPACES 4			// Number of spaces between
+
+void psArgumentHelp(psMetadata *arguments)
+{
+    printf("Optional arguments, with default values:\n");
+    psMetadataIterator *argIter = psMetadataIteratorAlloc(arguments, PS_LIST_HEAD, NULL);
+    psMetadataItem *argItem = NULL;	// Item from iterator
+    int maxName = 4;			// Maximum length of a name
+    int maxValue = 4;			// Maximum length of a value
+
+    // First pass to get the sizes
+    while (argItem = psMetadataGetAndIncrement(argIter)) {
+	if (strlen(argItem->name) > maxName) {
+	    maxName = strlen(argItem->name);
+	}
+	int valLength = argLength(argItem);
+	if (valLength > maxValue) {
+	    maxValue = valLength;
+	}
+    }
+
+    // Second pass to print
+    psMetadataIteratorSet(argIter, PS_LIST_HEAD);
+    psString lastName = NULL;		// Last name we printed
+    while (argItem = psMetadataGetAndIncrement(argIter)) {
+	// Initial indent
+	for (int i = 0; i < NUM_SPACES; i++) {
+	    printf(" ");
+	}
+
+	// Print the name if required
+	int position = 0;	// Number of spaces in
+	if (! lastName || strcmp(lastName, argItem->name) != 0) {
+	    // A new name
+	    printf("%s", argItem->name);
+	    position += strlen(argItem->name);
+	    lastName = argItem->name;
+	}
+	for (int i = position; i < maxName + NUM_SPACES; i++) {
+	    printf(" ");
+	}
+
+	// Print the value
+	printf("(");
+	switch (argItem->type) {
+	    // Only doing a representative set of types
+	  case PS_META_S32:
+	    printf("%d", argItem->data.S32);
+	    break;
+	    // XXX: Other numerical types
+	  case PS_META_F32:
+	    printf("%.6e", argItem->data.F32);
+	    break;
+	  case PS_META_F64:
+	    printf("%.6e", argItem->data.F64);
+	    break;
+	  case PS_META_BOOL:
+	    if (argItem->data.B) {
+		printf("TRUE");
+	    } else {
+		printf("FALSE");
+	    }
+	    break;
+	  case PS_META_STR:
+	    printf("%s", argItem->data.V);
+	    break;
+	  default:
+	    psAbort(__func__, "Argument type (%x) is not supported.\n", argItem->type);
+	}
+	printf(")");
+	for (int i = argLength(argItem); i < maxValue + NUM_SPACES; i++) {
+	    printf(" ");
+	}
+
+	// Print the comment
+	if (argItem->comment) {
+	    printf("%s", argItem->comment);
+	}
+	printf("\n");
+    }
+
+    psFree(argIter);
+}
Index: /trunk/archive/scripts/src/phase2/psAdditionals.h
===================================================================
--- /trunk/archive/scripts/src/phase2/psAdditionals.h	(revision 4793)
+++ /trunk/archive/scripts/src/phase2/psAdditionals.h	(revision 4793)
@@ -0,0 +1,29 @@
+#ifndef PS_ADDITIONALS_H
+#define PS_ADDITIONALS_H
+
+#include "pslib.h"
+
+typedef char* psString;
+
+// Get a value from the metadata that we believe should be metadata.
+psMetadata *psMetadataLookupMD(bool *status, const psMetadata *md, const char *key);
+
+// Get a value from the metadata that we believe should be a string
+char *psMetadataLookupString(bool *status, const psMetadata *md, const char *key);
+
+#if 0
+pmChip *psMetadataLookupChip(bool *status, const psMetadata *md, const char *key);
+pmCell *psMetadataLookupCell(bool *status, const psMetadata *md, const char *key);
+#endif
+
+// Print out the metadata
+void psMetadataPrint(psMetadata *md, int level);
+
+// Argument handling
+int psArgumentVerbosity(int *argc, char **argv);
+int psArgumentGet(int argc, char **argv, const char *arg);
+bool psArgumentRemove(int argnum, int *argc, char **argv);
+bool psArgumentParse(psMetadata *arguments, int *argc, char **argv);
+void psArgumentHelp(psMetadata *arguments);
+
+#endif
