Index: trunk/psModules/src/config/pmConfigMask.c
===================================================================
--- trunk/psModules/src/config/pmConfigMask.c	(revision 16815)
+++ trunk/psModules/src/config/pmConfigMask.c	(revision 18554)
@@ -9,5 +9,5 @@
 #include "pmConfigMask.h"
 
-psMaskType pmConfigMask(const char *masks, const pmConfig *config)
+psMaskType pmConfigMaskGet(const char *masks, const pmConfig *config)
 {
     assert (config);
@@ -39,2 +39,202 @@
     return mask;
 }
+
+bool pmConfigMaskSet(const pmConfig *config, const char *maskName, psMaskType maskValue)
+{
+    assert (config);
+    PS_ASSERT_STRING_NON_EMPTY(maskName, false);
+
+    psMetadata *recipe = psMetadataLookupMetadata(NULL, config->recipes, "MASKS"); // The recipe
+    if (!recipe) {
+        psError(PS_ERR_UNEXPECTED_NULL, false, "Unable to find MASKS recipe.");
+        return false;
+    }
+
+    psMetadataAddU8 (recipe, PS_LIST_TAIL, maskName, PS_META_REPLACE, "user-defined mask", maskValue);
+
+    return true;
+}
+
+// replace the named masks in the recipe with values in the header:
+// replace only the names in the header in the recipe
+bool pmConfigMaskReadHeader (pmConfig *config, psMetadata *header) {
+
+    bool status = false;
+    char namekey[80];
+    char valuekey[80];
+
+    psMetadata *recipe = psMetadataLookupMetadata(NULL, config->recipes, "MASKS"); // The recipe
+    if (!recipe) {
+        psError(PS_ERR_UNEXPECTED_NULL, false, "Unable to find MASKS recipe.");
+        return false;
+    }
+
+    int nMask = psMetadataLookupS32 (&status, header, "MSKNUM");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "Unable to find MSKNUM in header.");
+        return false;
+    }
+
+    for (int i = 0; i < nMask; i++) {
+
+	snprintf (namekey,  64, "MSKNAM%02d", i);
+	snprintf (valuekey, 64, "MSKVAL%02d", i);
+
+	char *name = psMetadataLookupStr (&status, header, namekey);
+	psU8 bit = psMetadataLookupU8 (&status, header, valuekey);
+
+	// XXX validate that bit is a 2^n value?
+
+	psMetadataItem *item = psMetadataLookup (header, name);
+	if (!item) {
+	    psWarning("mask recipe entry %s not in recipe\n", name);
+	    psMetadataAddU8 (recipe, PS_LIST_TAIL, name, 0, "Bitmask bit value", bit);
+	} else {
+	    item->data.U8 = bit;
+	}
+    }
+    
+    return true;
+}
+
+// write the named mask bits to the header
+bool pmConfigMaskWriteHeader (pmConfig *config, psMetadata *header) {
+
+    char namekey[80];
+    char valuekey[80];
+
+    psMetadata *recipe = psMetadataLookupMetadata(NULL, config->recipes, "MASKS"); // The recipe
+    if (!recipe) {
+        psError(PS_ERR_UNEXPECTED_NULL, false, "Unable to find MASKS recipe.");
+        return false;
+    }
+
+    int nMask = 0;
+
+    psMetadataIterator *iter = psMetadataIteratorAlloc(recipe, PS_LIST_HEAD, NULL); // Iterator
+
+    psMetadataItem *item;               // Item from iteration
+    while ((item = psMetadataGetAndIncrement(iter))) {
+
+	if (item->type != PS_DATA_U8) {
+	    psWarning("mask recipe entry %s is not a bit value\n", item->name);
+	    continue;
+	}
+
+	snprintf (namekey,  64, "MSKNAM%02d", nMask);
+	snprintf (valuekey, 64, "MSKVAL%02d", nMask);
+
+	psMetadataAddStr (header, PS_LIST_TAIL, namekey, 0, "Bitmask bit name", item->name);
+	psMetadataAddU8 (header, PS_LIST_TAIL, valuekey, 0, "Bitmask bit value", item->data.U8);
+	nMask ++;
+    }
+    
+    psMetadataAddS32 (header, PS_LIST_TAIL, "MSKNUM", 0, "Bitmask bit count", nMask);
+    return true;
+}
+
+// examine named mask values in mask recipe and set the bits for maskValue and markValue
+// this function sets an appropriate value for the following required named mask concepts: 
+// FLAT (used to mark out-of-range corrections in the flat-fielding)
+// BLANK (used to mark non-existent pixels)
+// SAT (used to mark pixels with values out-of-range on the high end)
+// BAD (used to mark pixels with values out-of-range on the low end)
+// If there is no explicit value for the above, the 'DETECTOR' and 'RANGE' bits are used
+// If these latter do not exist, the value 0x01 is used.
+// The values actually used for these names are written back to the config file
+bool pmConfigMaskSetBits (psMaskType *outMaskValue, psMaskType *outMarkValue, pmConfig *config) {
+
+    psMaskType maskValue = 0;
+
+    // mask for generic detector defect
+    psMaskType detectorMask = pmConfigMaskGet("DETECTOR", config); 
+    maskValue |= detectorMask;
+
+    // mask for dark structures
+    psMaskType darkMask = pmConfigMaskGet("DARK", config);
+    maskValue |= darkMask;
+
+    // mask for non-linear flat regions (default to DETECTOR if not defined)
+    psMaskType flatMask = pmConfigMaskGet("FLAT", config); 
+    if (!flatMask) {
+	flatMask = detectorMask;
+	pmConfigMaskSet (config, "FLAT", flatMask);
+    }
+    if (!flatMask) {
+	flatMask = 0x01;
+	pmConfigMaskSet (config, "FLAT", flatMask);
+    }
+    maskValue |= flatMask;
+
+    // mask for non-existent data  (default to DETECTOR if not defined)
+    psMaskType blankMask = pmConfigMaskGet("BLANK", config); 
+    if (!blankMask) {
+	blankMask = detectorMask;
+	pmConfigMaskSet (config, "BLANK", blankMask);
+    }
+    if (!blankMask) {
+	blankMask = 0x01;
+	pmConfigMaskSet (config, "BLANK", blankMask);
+    }
+    maskValue |= blankMask;
+
+    // mask for generic data range errors
+    psMaskType rangeMask = pmConfigMaskGet("RANGE", config);
+    maskValue |= rangeMask;
+
+    // mask for saturated data  (default to RANGE if not defined)
+    psMaskType satMask = pmConfigMaskGet("SAT", config);
+    if (!satMask) {
+	satMask = rangeMask;
+	pmConfigMaskSet (config, "SAT", satMask);
+    }
+    if (!satMask) {
+	satMask = 0x01;
+	pmConfigMaskSet (config, "SAT", satMask);
+    }
+    maskValue |= satMask;
+    
+    // mask for below-range data  (default to RANGE if not defined)
+    psMaskType badMask = pmConfigMaskGet("BAD", config);
+    if (!badMask) {
+	badMask = rangeMask;
+	pmConfigMaskSet (config, "BAD", badMask);
+    }
+    if (!badMask) {
+	badMask = 0x01;
+	pmConfigMaskSet (config, "BAD", badMask);
+    }
+    maskValue |= badMask;
+
+    // XXX not sure what to do with these
+    psMaskType crMask = pmConfigMaskGet("CR", config);
+    maskValue |= crMask;
+
+    psMaskType ghostMask = pmConfigMaskGet("GHOST", config);
+    maskValue |= ghostMask;
+
+    // search for an unset bit to use for MARK:
+    psMaskType markValue = 0x80;
+
+    int nBits = sizeof(psMaskType) * 8;
+    for (int i = 0; !markValue && (i < nBits); i++) {
+	if (maskValue & markValue) {
+	    markValue >>= 1;
+	} else {
+	    markValue = markValue;
+	}
+    }
+    if (!markValue) {
+	psError (PS_ERR_UNKNOWN, true, "Unable to define the MARK bit mask: all bits taken!");
+	return false;
+    }
+
+    // update the config table
+    pmConfigMaskSet (config, "MASK.VALUE", maskValue);
+    pmConfigMaskSet (config, "MARK.VALUE", markValue);
+
+    *outMaskValue = maskValue;
+    *outMarkValue = markValue;
+
+    return true;
+}
