Index: /trunk/dettools/doc/dettools.txt
===================================================================
--- /trunk/dettools/doc/dettools.txt	(revision 5881)
+++ /trunk/dettools/doc/dettools.txt	(revision 5881)
@@ -0,0 +1,113 @@
+mkdetrend tools
+
+dettool -define [options]
+ * define a new detRun, specifying the constraints, and adding it to
+ the run table.  if the detRun ID already exists, creates a new
+ version.  the initial state is set to START.  the iteration is set to
+ 0. also creates a new master detrend frame entry
+ (detID.version.iteration define this frame uniquely).
+ 
+options : 
+  -ID ID : a free-form string; if not specified, a unique string is constructed
+  -type type : bias dark (mask?) flat fringe (other?)
+  -camera camera 
+  -filter filter
+  -time start stop
+  -exptime min max
+  -airmass min max
+  -expgroup groupID
+
+  (-type and -camera are mandatory)
+  (-filter is mandatory for 'light' types)
+  (-exptime is mandatory for dark)
+
+dettool -getinput
+ * select the input matching a given detRun.  selects the input
+ exposures and the input files.
+ - this function could be merged with -define
+ - an alternate implementation would define a detRun ID for a given
+ stack of input exposures
+
+dettool -select raw [-state state] [-outmode mode]
+ * select the unprocessed input infiles
+ - output of this program is used by pantasks to schedule the ppImage
+ run on each image
+
+dettool -select stack [-state state] [-outmode mode]
+ * select the files to be stacked for a given detRun, if exposures are ready
+ - output of this program is used by ppMerge to build a master stack
+
+dettool -select rawresid [-state state]
+ * select the residual imfiles to be generated
+ - output of this program is used for another ppImage run 
+
+dettool -update raw
+ * select the raw input exposures, count processed imfiles, update if done
+
+dettool -update resid
+ * select the resid exposures, count processed resid imfiles, update if done
+ - for a given resid exposure, compile the results from the stacks for
+ each chip and renormalize the output files as needed.
+
+dettool -select norm
+ * select the master detrend frames & imfiles to be normalized
+
+dettool -resid
+ * construct the resid exposures and imfiles given 
+
+dettool -assess
+ * for a given master detrend frame, compile the results from the
+ residual exposures and assess the validity of the input exposures and of
+ the complete stack.  if too many input images are rejected, the
+ affect the master detrend frame state.  if input images are rejected
+ and a new set of master stacks should be made, create a new master
+ detrend image, incrementing the iteration by one.
+
+** NOTE the sequence is: 
+
+   process, stack, merge, residual, assess
+              ^--------------------------<
+
+   IF we have no relevant detrend image for comparison.  otherwise,
+   the sequence should skip from process to residual on the first
+   pass, with a lower rejection threshold for the first assess pass.
+   the tools above allow either option; it is the choice of state
+   after process that determines which happens next.
+
+
+
+States:
+ input detrend exposures:
+  RAW
+  PROCESSED
+
+ input detrend imfiles:
+  RAW
+  PROCESSED
+  
+ master detrend frames
+  RAW
+  NORMALIZED
+  MKRESID
+  SUCCESS
+  FAILURE
+  RETRY
+  (also has NEW/PRIOR flag)
+
+ master detrend imfiles
+  NEW (not yet created)
+  RAW (created, but not normalized)
+  NORMALIZED
+
+ resid exposure
+  RAW
+  PROCESSED
+  ASSESSED
+
+ resid imfiles:
+  RAW
+  PROCESSED
+
+ master detrend run:
+  NEW
+  DONE
Index: /trunk/dettools/src/detSelectInputImfiles.c
===================================================================
--- /trunk/dettools/src/detSelectInputImfiles.c	(revision 5881)
+++ /trunk/dettools/src/detSelectInputImfiles.c	(revision 5881)
@@ -0,0 +1,159 @@
+# include "dettools.h"
+
+// select raw frames (exposure+images) which match the given config options
+psArray *detSelectInputImfiles (detConfig *config, int state) {
+
+    // build 'where' structure
+    psMetadata *where = psMetadataAlloc ();
+
+    psMetadataAddStr (where, PS_LIST_TAIL, "STATE", 0, "==", state);
+
+    // XXX EAM : do we really want/need to allow these restrictions?
+    if (config->type != DET_TYPE_UNDEF) {
+	psMetadataAddU8  (where, PS_LIST_TAIL, "TYPE",  0, "==", config->type);
+    }
+    if (config->camera != NULL) {
+	psMetadataAddStr (where, PS_LIST_TAIL, "CAMERA", 0, "==", config->camera);
+    }
+    if (config->filter != NULL) {
+	psMetadataAddStr (where, PS_LIST_TAIL, "FILTER", 0, "==", config->filter);
+    }
+    if (config->timeSelect) {
+	psMetadataAddTime (where, PS_LIST_TAIL, "TIME_START", 0, "<", config->timeStop);
+	psMetadataAddTime (where, PS_LIST_TAIL, "TIME_STOP", 0, ">", config->timeStart);
+    }
+    if (config->exptimeSelect) {
+	psMetadataAddTime (where, PS_LIST_TAIL, "EXPTIME", 0, "<", config->exptimeMin);
+	psMetadataAddTime (where, PS_LIST_TAIL, "EXPTIME", 0, ">", config->exptimeMax);
+    }
+    if (config->airmassSelect) {
+	psMetadataAddTime (where, PS_LIST_TAIL, "AIRMASS", 0, "<", config->airmassMin);
+	psMetadataAddTime (where, PS_LIST_TAIL, "AIRMASS", 0, ">", config->airmassMax);
+    }
+
+    psArray *imfiles = detInputImfileSelectRows (config->database, where, MAX_ROWS);
+    psFree (where);
+
+    return imfiles;
+} 
+
+// select raw frames (exposure+images) which match the given config options
+psArray *detSelectRawUpdateExposures (detConfig *config) {
+
+    // build 'where' structure
+    psMetadata *where = psMetadataAlloc ();
+
+    psMetadataAddStr (where, PS_LIST_TAIL, "STATE", 0, "==", DET_STATE_RAW);
+
+    // XXX EAM : do we really want/need to allow these restrictions?
+    if (config->type != DET_TYPE_UNDEF) {
+	psMetadataAddU8  (where, PS_LIST_TAIL, "TYPE",  0, "==", config->type);
+    }
+    if (config->camera != NULL) {
+	psMetadataAddStr (where, PS_LIST_TAIL, "CAMERA", 0, "==", config->camera);
+    }
+    if (config->filter != NULL) {
+	psMetadataAddStr (where, PS_LIST_TAIL, "FILTER", 0, "==", config->filter);
+    }
+    if (config->timeSelect) {
+	psMetadataAddTime (where, PS_LIST_TAIL, "TIME_START", 0, "<", config->timeStop);
+	psMetadataAddTime (where, PS_LIST_TAIL, "TIME_STOP", 0, ">", config->timeStart);
+    }
+    if (config->exptimeSelect) {
+	psMetadataAddTime (where, PS_LIST_TAIL, "EXPTIME", 0, "<", config->exptimeMin);
+	psMetadataAddTime (where, PS_LIST_TAIL, "EXPTIME", 0, ">", config->exptimeMax);
+    }
+    if (config->airmassSelect) {
+	psMetadataAddTime (where, PS_LIST_TAIL, "AIRMASS", 0, "<", config->airmassMin);
+	psMetadataAddTime (where, PS_LIST_TAIL, "AIRMASS", 0, ">", config->airmassMax);
+    }
+
+    psArray *exposures = detInputExposureSelectRows (config->database, where, MAX_ROWS);
+    psFree (where);
+
+    // output array of detMasterExposures
+    psArray *ready = psArrayAlloc (exposures->n);
+    ready->n = 0;
+
+    // 'where' to select each exposure
+    psMetadata *where = psMetadataAlloc ();
+    for (int i = 0; i < exposures->n; i++) {
+	detInputExposure *exposure = exposures->data[i];
+
+	// are all chips finished processing?
+	psMetadataAddStr (where, PS_LIST_TAIL, "STATE", PS_META_REPLACE, "==", DET_STATE_PROCESSED);
+	psMetadataAddStr (where, PS_LIST_TAIL, "DET_ID", PS_META_REPLACE, "==", exposure->detID);
+	psMetadataAddStr (where, PS_LIST_TAIL, "VERSION", PS_META_REPLACE, "==", exposure->version);
+
+	psArray *imfiles = detInputImfileSelectRows (config->database, where, MAX_ROWS);
+	if (imfiles->n != exposure->Nclass) {
+	    psFree (imfiles);
+	    continue;
+	}
+
+	psFree (imfiles);
+	psArrayAdd (ready, 100, exposure);
+    }
+    return ready;
+} 
+
+// select raw frames (exposure+images) which match the given config options
+psArray *detSelectResidUpdateExposures (detConfig *config) {
+
+    // build 'where' structure
+    psMetadata *where = psMetadataAlloc ();
+
+    psMetadataAddStr (where, PS_LIST_TAIL, "STATE", 0, "==", DET_STATE_RAW);
+
+    // XXX EAM : do we really want/need to allow these restrictions?
+    if (config->type != DET_TYPE_UNDEF) {
+	psMetadataAddU8  (where, PS_LIST_TAIL, "TYPE",  0, "==", config->type);
+    }
+    if (config->camera != NULL) {
+	psMetadataAddStr (where, PS_LIST_TAIL, "CAMERA", 0, "==", config->camera);
+    }
+    if (config->filter != NULL) {
+	psMetadataAddStr (where, PS_LIST_TAIL, "FILTER", 0, "==", config->filter);
+    }
+    if (config->timeSelect) {
+	psMetadataAddTime (where, PS_LIST_TAIL, "TIME_START", 0, "<", config->timeStop);
+	psMetadataAddTime (where, PS_LIST_TAIL, "TIME_STOP", 0, ">", config->timeStart);
+    }
+    if (config->exptimeSelect) {
+	psMetadataAddTime (where, PS_LIST_TAIL, "EXPTIME", 0, "<", config->exptimeMin);
+	psMetadataAddTime (where, PS_LIST_TAIL, "EXPTIME", 0, ">", config->exptimeMax);
+    }
+    if (config->airmassSelect) {
+	psMetadataAddTime (where, PS_LIST_TAIL, "AIRMASS", 0, "<", config->airmassMin);
+	psMetadataAddTime (where, PS_LIST_TAIL, "AIRMASS", 0, ">", config->airmassMax);
+    }
+
+    psArray *exposures = detResidExposureSelectRows (config->database, where, MAX_ROWS);
+    psFree (where);
+
+    // output array of detMasterExposures
+    psArray *ready = psArrayAlloc (exposures->n);
+    ready->n = 0;
+
+    // 'where' to select each exposure
+    psMetadata *where = psMetadataAlloc ();
+    for (int i = 0; i < exposures->n; i++) {
+	detResidExposure *exposure = exposures->data[i];
+
+	// are all chips finished processing?
+	psMetadataAddStr (where, PS_LIST_TAIL, "STATE", PS_META_REPLACE, "==", DET_STATE_PROCESSED);
+	psMetadataAddStr (where, PS_LIST_TAIL, "DET_ID", PS_META_REPLACE, "==", exposure->detID);
+	psMetadataAddStr (where, PS_LIST_TAIL, "VERSION", PS_META_REPLACE, "==", exposure->version);
+	psMetadataAddStr (where, PS_LIST_TAIL, "ITERATION", PS_META_REPLACE, "==", exposure->iteration);
+
+	psArray *imfiles = detResidImfileSelectRows (config->database, where, MAX_ROWS);
+	if (imfiles->n != exposure->Nclass) {
+	    psFree (imfiles);
+	    continue;
+	}
+
+	psFree (imfiles);
+	psArrayAdd (ready, 100, exposure);
+    }
+    return ready;
+} 
Index: /trunk/dettools/src/detSelectMasterExposures.c
===================================================================
--- /trunk/dettools/src/detSelectMasterExposures.c	(revision 5881)
+++ /trunk/dettools/src/detSelectMasterExposures.c	(revision 5881)
@@ -0,0 +1,138 @@
+# include "dettools.h"
+
+// select raw frames (exposure+images) which match the given config options
+psArray *detSelectMasterExposuresForStack (detConfig *config) {
+
+    // build 'where' structure
+    psMetadata *where = psMetadataAlloc ();
+
+    psMetadataAddStr (where, PS_LIST_TAIL, "STATE", 0, "==", DET_STATE_RAW);
+
+    // XXX EAM : do we really want/need to allow these restrictions?
+    if (config->type != DET_TYPE_UNDEF) {
+	psMetadataAddU8  (where, PS_LIST_TAIL, "TYPE",  0, "==", config->type);
+    }
+    if (config->camera != NULL) {
+	psMetadataAddStr (where, PS_LIST_TAIL, "CAMERA", 0, "==", config->camera);
+    }
+    if (config->filter != NULL) {
+	psMetadataAddStr (where, PS_LIST_TAIL, "FILTER", 0, "==", config->filter);
+    }
+
+    psArray *exposures = detMasterExposureSelectRows (config->database, where, MAX_ROWS);
+    psFree (where);
+
+    // output array of detMasterExposures
+    psArray *ready = psArrayAlloc (exposures->n);
+    ready->n = 0;
+
+    // 'where' to select each exposure
+    psMetadata *where = psMetadataAlloc ();
+    for (int i = 0; i < exposures->n; i++) {
+	detMasterExposure *exposure = exposures->data[i];
+	
+	// is the input data for this master exposure ready for stacking?
+	psMetadataAddStr (where, PS_LIST_TAIL, "STATE", PS_META_REPLACE, "==", DET_STATE_PROCESSED);
+	psMetadataAddStr (where, PS_LIST_TAIL, "DET_ID", PS_META_REPLACE, "==", exposure->detID);
+	psMetadataAddStr (where, PS_LIST_TAIL, "VERSION", PS_META_REPLACE, "==", exposure->version);
+
+	psArray *inputs = detInputExposureSelectRows (config->database, where, MAX_ROWS);
+	if (inputs->n != exposure->nInput) {
+	    psFree (inputs);
+	    continue;
+	}
+
+	psFree (inputs);
+	psArrayAdd (ready, 100, exposure);
+    }
+    return ready;
+} 
+
+// select stack frames (detMasterImfile + detInputImfiles) which match the given master detrend exposures
+// XXX an alternative to this would construct the imfiles here based on the camera metadata
+psArray *detSelectMasterImfilesForStack (detConfig *config, psArray *exposures) {
+
+    psArray *frames = psArrayAlloc (100);
+
+    // 'where' to select each exposure
+    psMetadata *where = psMetadataAlloc ();
+    for (int i = 0; i < exposures->n; i++) {
+	detMasterExposure *exposure = exposures->data[i];
+	
+	// select the detrend master imfiles for this master exposure
+	psMetadataAddStr (where, PS_LIST_TAIL, "DET_ID", PS_META_REPLACE, "==", exposure->detID);
+	psMetadataAddStr (where, PS_LIST_TAIL, "VERSION", PS_META_REPLACE, "==", exposure->version);
+	psMetadataAddStr (where, PS_LIST_TAIL, "ITERATION", PS_META_REPLACE, "==", exposure->iteration);
+
+	psArray *detImfiles = detMasterImfileSelectRows (config->database, where, MAX_ROWS);
+
+	for (int j = 0; j < detImfiles->n; j++) {
+	    
+	    detMasterImfile *detImfile = detImfiles->data[j];
+
+	    // XXX : confirm that this detImfile has state DET_STATE_NEW ??
+
+	    // select the input imfiles for this master imfile
+	    psMetadataAddStr (where, PS_LIST_TAIL, "CLASS_ID", PS_META_REPLACE, "==", detImfile->classID);
+
+	    psArray *inputImfiles = detInputImfileSelectRows (config->database, where, MAX_ROWS);
+
+	    detStackFrame *stackFrame = detStackFrameAlloc (detImfile, inputImfiles);
+
+	    psArrayAdd (frames, 100, stackFrame);
+	}
+    }
+    return frames;
+} 
+
+
+// ************ normalization (put in new file) *******
+
+// select raw frames (exposure+images) which match the given config options
+psArray *detSelectMasterExposuresForNormalization (detConfig *config) {
+
+    // build 'where' structure
+    psMetadata *where = psMetadataAlloc ();
+
+    psMetadataAddStr (where, PS_LIST_TAIL, "STATE", 0, "==", DET_STATE_RAW);
+
+    // XXX EAM : do we really want/need to allow these restrictions?
+    if (config->type != DET_TYPE_UNDEF) {
+	psMetadataAddU8  (where, PS_LIST_TAIL, "TYPE",  0, "==", config->type);
+    }
+    if (config->camera != NULL) {
+	psMetadataAddStr (where, PS_LIST_TAIL, "CAMERA", 0, "==", config->camera);
+    }
+    if (config->filter != NULL) {
+	psMetadataAddStr (where, PS_LIST_TAIL, "FILTER", 0, "==", config->filter);
+    }
+
+    psArray *exposures = detMasterExposureSelectRows (config->database, where, MAX_ROWS);
+    psFree (where);
+
+    // output array of detMasterExposures
+    psArray *frames = psArrayAlloc (exposures->n);
+    frames->n = 0;
+
+    // 'where' to select each exposure
+    psMetadata *where = psMetadataAlloc ();
+    for (int i = 0; i < exposures->n; i++) {
+	detMasterExposure *exposure = exposures->data[i];
+	
+	// is the input data for this master exposure ready for stacking?
+	psMetadataAddStr (where, PS_LIST_TAIL, "STATE", PS_META_REPLACE, "==", DET_STATE_RAW);
+	psMetadataAddStr (where, PS_LIST_TAIL, "DET_ID", PS_META_REPLACE, "==", exposure->detID);
+	psMetadataAddStr (where, PS_LIST_TAIL, "VERSION", PS_META_REPLACE, "==", exposure->version);
+	psMetadataAddStr (where, PS_LIST_TAIL, "ITERATION", PS_META_REPLACE, "==", exposure->iteration);
+
+	psArray *imfiles = detMasterImfileSelectRows (config->database, where, MAX_ROWS);
+	if (imfiles->n != exposure->nImfiles) {
+	    psFree (imfiles);
+	    continue;
+	}
+
+	detNormFrame *frame = detNormFrameAlloc (exposure, imfiles);
+	psArrayAdd (frames, 100, frame);
+    }
+    return frames;
+} 
Index: /trunk/dettools/src/dettools.c
===================================================================
--- /trunk/dettools/src/dettools.c	(revision 5881)
+++ /trunk/dettools/src/dettools.c	(revision 5881)
@@ -0,0 +1,78 @@
+# include "p2search.h"
+
+int main (int argc, char **argv) {
+
+    psArray *imfiles;
+    psArray *frames;
+    psArray *pendingFrames;
+
+    dettoolsConfig (&config, argc, argv);
+
+    // -select options
+    if (config->mode == DET_MODE_SELECT_RAW) {
+	imfiles = detSelectInputImfiles (config, DET_STATE_RAW);
+	detWriteInputImfiles (config, imfiles);
+    }	
+
+    if (config->mode == DET_MODE_SELECT_RESID) {
+	imfiles = detSelectResidImfiles (config, DET_STATE_RAW);
+	detWriteResidImfiles (config, imfiles);
+    }	
+
+    if (config->mode == DET_MODE_SELECT_STACK) {
+	exposures = detSelectMasterExposuresForStack (config);
+	frames = detSelectMasterImfilesForStack (config, exposures);
+	detWriteStackFrames (config, frames);
+    }	
+
+    if (config->mode == DET_MODE_SELECT_NORM) {
+	frames  = detSelectMasterExposuresForNorm (config);
+	detWriteNormFrames (config, frames);
+    }	
+
+    // -update options
+    if (config->mode == DET_MODE_UPDATE_RAW) {
+	exposures = detSelectRawUpdateExposures (config);
+	detUpdateRawExposures (config, exposures);
+    }
+
+    if (config->mode == DET_MODE_UPDATE_RESID) {
+	exposures = detSelectResidUpdateExposures (config);
+	detUpdateResdiExposures (config, exposures);
+    }
+
+    if (config->mode == DET_MODE_UPDATE_RUN) {
+	// select master detrend exposures where (state == MKRESID)
+	// foreach master exposure:
+	//   select matching resid exposures where (state == PROCESSED)
+	//   if (residExposures->n != nInput) continue;
+	//   calculate summary stats
+	//   set state of masterExposure
+	//   create new masterExposure iteration (if needed)
+	//   set state of masterRun (if done)
+    }
+
+    // -define options
+    if (config->mode == DET_MODE_DEFINE_RUN) {
+	// select matching group, if desired
+	// select matching raw exposures, imfiles
+	// create masterRun based on options
+	// create new inputExposures
+	// create new inputImfiles
+	// create new detrendImfiles
+    }
+
+    if (config->mode == DET_MODE_DEFINE_RESID) {
+	// select master detrend exposures where (state == NORMALIZED)
+	// foreach master exposure:
+	//   select matching input exposures where (state == PROCESSED)
+	//   foreach input exposure
+	//     create matching resid exposure
+	//     select matching input imfiles
+	//     foreach input imfile
+	//       create matching resid imfile
+	//   set master exposure state to MKRESID
+    }
+
+    exit (0);
+}
Index: /trunk/dettools/src/dettools.h
===================================================================
--- /trunk/dettools/src/dettools.h	(revision 5881)
+++ /trunk/dettools/src/dettools.h	(revision 5881)
@@ -0,0 +1,153 @@
+# include <stdio.h>
+# include <strings.h>  // for strcasecmp
+# include <unistd.h>   // for unlink
+# include <pslib.h>
+# include <pmObjects.h>
+# include <pmPSF.h>
+# include <pmPSFtry.h>
+# include <pmModelGroup.h>
+
+typedef enum {
+    DET_MODE_NONE,		      	// 
+    DET_MODE_QUICK,		      	// 
+    DET_MODE_SELECT_RAW,		// 
+    DET_MODE_SELECT_RESID,		// 
+    DET_MODE_SELECT_STACK,		// 
+    DET_MODE_SELECT_NORM,		// 
+
+    DET_MODE_UPDATE_RAW,		// 
+    DET_MODE_UPDATE_RESID,		// 
+    DET_MODE_UPDATE_RUN,		// 
+
+    DET_MODE_DEFINE_RUN,		// 
+    DET_MODE_DEFINE_RESID,		// 
+} detMode;
+
+typedef unsigned char detType;
+typedef unsigned char detState;
+
+typedef struct {
+    p2mode mode;
+
+    detType type;
+    char *camera;
+    char *filter;
+
+    bool groupSelect;
+    char *groupID;
+
+    bool timeSelect;
+    psTime start;
+    psTime stop;
+
+    bool exptimeSelect;
+    float exptimeMin;
+    float exptimeMax;
+
+    bool airmassSelect;
+    float airmassMin;
+    float airmassMax;
+
+    psDB *database;
+} detConfig;
+
+// these structures should be defined using the mddb api tools
+// check definition in p2search.h
+typedef struct ppRawExposure;
+typedef struct ppRawImfile;
+
+// this structure should be defined using the mddb api tools
+typedef struct {
+    char expID[32];			// free-form exposure ID string
+    char class;				// class of file: fpa (0), chip (1), cell (2), readout (3)?
+    int Nclass;				// number of files of this class
+    int Ndone;				// number of files of this class
+    char P1version;
+    char P2version;
+    char state;
+} detInputExposure;
+
+// this structure should be defined using the mddb api tools
+typedef struct {
+    char expID[32];			// free-form exposure ID string
+    char P2version;
+    char class;				// class of file: fpa (0), chip (1), cell (2), readout (3)?
+    char classID[32];			// identifier for class (chip00, cell00, etc)
+    char urlroot[128];			// locator for file (neb / file)
+    char input[16];			// extension for input image
+    char output[16];			// extension for output image
+    char log[16];			// extension for log file
+    char smf[16];			// extension for object table
+    char state;				// current P2 state
+} detInputImfile;
+
+// this structure should be defined using the mddb api tools
+typedef struct {
+    a detID;
+    a version;
+    a iteration;
+    a type;
+    a camera;
+    a filter;
+    a class;
+    int nInput;
+    int nImfiles;
+    detState state;
+    float stats;
+} detMasterExposure;
+
+typedef struct {
+    a detID;
+    a version;
+    a iteration;
+    a classID;
+    a stats;
+    detState state;
+} detMasterImfile;
+
+typedef struct {
+    a detID;
+    a version;
+    a state;
+
+    int nIterations;			// number of iterations STARTED
+
+    detType type;
+    char *camera;
+    char *filter;
+
+    bool groupSelect;
+    char *groupID;
+
+    bool timeSelect;
+    psTime start;
+    psTime stop;
+
+    bool exptimeSelect;
+    float exptimeMin;
+    float exptimeMax;
+
+    bool airmassSelect;
+    float airmassMin;
+    float airmassMax;
+} detMasterRun;
+
+typedef struct {
+    ppRawExposure *exposure;
+    psArray *images;
+} ppRawFrame;
+
+typedef struct {
+    detMasterImfile *imfile;
+    psArray *inputImfiles;
+} detStackFrame;
+
+typedef struct {
+    detMasterExposure *exposure;
+    psArray *imfiles;
+} detNormFrame;
+
+typedef struct {
+    ppDoneExposure *exposure;
+    psArray *images;
+} p2DoneFrame;
Index: /trunk/dettools/src/dettoolsConfig.c
===================================================================
--- /trunk/dettools/src/dettoolsConfig.c	(revision 5881)
+++ /trunk/dettools/src/dettoolsConfig.c	(revision 5881)
@@ -0,0 +1,68 @@
+# include "p2search.h"
+
+bool p2searchConfig (psConfig *config, int argc, char **argv) {
+
+    // Parse the configurations (re-org a la ppImage)
+    config->site = NULL;            // Site configuration
+    config->camera = NULL;          // Camera configuration
+    config->recipe = NULL;          // Recipe configuration
+    if (! pmConfigRead(&config->site, &config->camera, &config->recipe, &argc, argv, RECIPE)) {
+        psErrorStackPrint(stderr, "Can't find site configuration!\n");
+        exit(EXIT_FAILURE);
+    }
+
+    // Parse other command-line arguments
+    config->arguments = psMetadataAlloc(); // The arguments, with default values
+
+    config->mode = P2_MODE_NONE;
+    if ((N = psArgumentGet (*argc, argv, "-quick"))) {
+	psArgumentRemove (N, argc, argv);
+	if (config->mode) psAbort ("p2search", "only one mode selection is allowed");
+	config->mode = P2_MODE_QUICK;
+    }
+    if ((N = psArgumentGet (*argc, argv, "-define"))) {
+	psArgumentRemove (N, argc, argv);
+	if (config->mode) psAbort ("p2search", "only one mode selection is allowed");
+	config->mode = P2_MODE_DEFINE;
+    }
+    if ((N = psArgumentGet (*argc, argv, "-pending"))) {
+	psArgumentRemove (N, argc, argv);
+	if (config->mode) psAbort ("p2search", "only one mode selection is allowed");
+	config->mode = P2_MODE_PENDING;
+    }
+    if ((N = psArgumentGet (*argc, argv, "-update"))) {
+	psArgumentRemove (N, argc, argv);
+	if (config->mode) psAbort ("p2search", "only one mode selection is allowed");
+	config->mode = P2_MODE_UPDATE;
+    }
+    if ((N = psArgumentGet (*argc, argv, "-done"))) {
+	psArgumentRemove (N, argc, argv);
+	if (config->mode) psAbort ("p2search", "only one mode selection is allowed");
+	config->mode = P2_MODE_UPDATE;
+    }
+
+    // paul's argument parsing convention requires: -key value 
+    psMetadataAddStr(config->arguments, PS_LIST_TAIL, "-quick",   0, "examine raw image table, write ppImage output", "");
+    psMetadataAddStr(config->arguments, PS_LIST_TAIL, "-define",  0, "examine raw image table, add to pending image table", "");
+    psMetadataAddStr(config->arguments, PS_LIST_TAIL, "-pending", 0, "examine pending image table, write ppImage output", "");
+    psMetadataAddStr(config->arguments, PS_LIST_TAIL, "-update",  0, "update pending image table", "");
+    psMetadataAddS32(config->arguments, PS_LIST_TAIL, "-time",    0, "define time range of interest", "");
+    psMetadataAddS32(config->arguments, PS_LIST_TAIL, "-camera",  0, "define camera of interest", "");
+    psMetadataAddS32(config->arguments, PS_LIST_TAIL, "-filter",  0, "define filter of interest", "");
+
+    if (! psArgumentParse(config->arguments, &argc, argv) || argc != 2) {
+        printf("\nPan-STARRS Phase 2 Search Tool\n\n");
+        printf("Usage: %s [mode] [options]\n\n", argv[0]);
+        printf(" [mode] : -quick | -define | -pending | -update\n\n");
+        psArgumentHelp(config->arguments);
+        psFree(config->arguments);
+        exit(EXIT_FAILURE);
+    }
+
+    // add the input and output images to the arguments list
+    psMetadataAddStr (config->arguments, PS_LIST_TAIL, "-output", 0, "Name of the output image", argv[1]);
+
+    // define Database handle, if used
+    config->database = pmConfigDB(config->site);
+    return true;
+} 
Index: /trunk/ippMonitor/Makefile
===================================================================
--- /trunk/ippMonitor/Makefile	(revision 5881)
+++ /trunk/ippMonitor/Makefile	(revision 5881)
@@ -0,0 +1,52 @@
+default: php
+help:
+	@echo "USAGE: make php"
+
+SRC     =       src
+DESTBIN =       /var/www/kiawe/phpipp
+
+INSTALL = \
+$(DESTBIN)/menu.php \
+$(DESTBIN)/menu.dat \
+$(DESTBIN)/ipp.css \
+$(DESTBIN)/Login.php \
+$(DESTBIN)/SelectProject.php \
+$(DESTBIN)/ScienceStats.php \
+$(DESTBIN)/DetrendStats.php \
+$(DESTBIN)/phptest.php 
+
+PICTURES = \
+$(DESTBIN)/PScolorlogo2.jpg
+
+php: $(INSTALL) $(PICTURES)
+
+# dependancy rules for binary code #########################
+# .PRECIOUS: %.$(ARCH).o
+# .PRECIOUS: $(BIN)/%.$(ARCH)
+
+$(DESTBIN)/%.php: $(SRC)/%.php
+	@if [ ! -d $(DESTBIN) ]; then mkdir -p $(DESTBIN) || exit; fi
+	rm -f $(DESTBIN)/$*.php || exit
+	cp $(SRC)/$*.php $(DESTBIN)/$*.php || exit
+
+$(DESTBIN)/%.css: $(SRC)/%.css
+	@if [ ! -d $(DESTBIN) ]; then mkdir -p $(DESTBIN) || exit; fi
+	rm -f $(DESTBIN)/$*.css || exit
+	cp $(SRC)/$*.css $(DESTBIN)/$*.css || exit
+
+$(DESTBIN)/%.dat: $(SRC)/%.dat
+	@if [ ! -d $(DESTBIN) ]; then mkdir -p $(DESTBIN) || exit; fi
+	rm -f $(DESTBIN)/$*.dat || exit
+	cp $(SRC)/$*.dat $(DESTBIN)/$*.dat || exit
+
+$(DESTBIN)/%.jpg: $(SRC)/%.jpg
+	@if [ ! -d $(DESTBIN) ]; then mkdir -p $(DESTBIN) || exit; fi
+	rm -f $(DESTBIN)/$*.jpg || exit
+	cp $(SRC)/$*.jpg $(DESTBIN)/$*.jpg || exit
+
+# utilities #################################################
+
+clean:	
+	rm -f `find . -name "*.o"`
+	rm -f `find . -name "*~"`
+	rm -f `find . -name "#*"`
Index: /trunk/ippMonitor/src/DetrendStats.php
===================================================================
--- /trunk/ippMonitor/src/DetrendStats.php	(revision 5881)
+++ /trunk/ippMonitor/src/DetrendStats.php	(revision 5881)
@@ -0,0 +1,26 @@
+
+<? function maintext () { ?>
+<p>
+Examine the Detrend creation statistics
+</p>
+<? } ?>
+
+<?php 
+
+include 'menu.php';
+
+if ($_SERVER[REQUEST_METHOD] != 'GET') { 
+  menu('Login', 'ipp.css');
+  echo "Invalid Client Request<br>\n";
+  menu_end ();
+  exit ();
+}
+
+checkID ();
+
+menu('Login', 'ipp.css');
+maintext ();
+menu_end();
+
+
+?>
Index: /trunk/ippMonitor/src/Login.php
===================================================================
--- /trunk/ippMonitor/src/Login.php	(revision 5881)
+++ /trunk/ippMonitor/src/Login.php	(revision 5881)
@@ -0,0 +1,77 @@
+
+<? function logoutform ($myID) { ?>
+<p>
+You are currently logged in.  Click here to log out.<br>
+<form action="Login.php" method="POST">
+<input type="hidden" name="myID" value="<? $myID ?>"><br>
+<input type="submit" name="logout">
+</form>
+</p>
+<? } ?>
+
+<?php 
+
+include 'menu.php';
+
+if ($_SERVER[REQUEST_METHOD] == 'GET') { 
+  $myID = $_GET[myID];
+  if ($myID == "") {
+    menu ('Login', 'ipp.css');
+    logintext ();
+    loginform ();
+    menu_end ();
+    exit ();
+ } else {
+    menu ('Login', 'ipp.css');
+    logoutform ($myID);
+    menu_end ();
+    exit ();
+ }
+} 
+
+if ($_SERVER[REQUEST_METHOD] != 'POST') { 
+  menu ('Login', 'ipp.css');
+  echo "Invalid Client Request<br>\n";
+  menu_end ();
+  exit ();
+}
+
+if (key_exists (login, $_POST)) {
+  $username = $_POST[username];
+  $password = $_POST[password];
+
+  $success = (($username == "eugene") && ($password == "test"));
+
+  if (!$success) {  
+    menu ('Login', 'ipp.css');
+    echo "Login Failed, please try again<br>\n";
+    loginform ();
+    menu_end ();
+    exit ();
+  }
+
+  $_GET[myID] = "foobar";
+  menu ('Login', 'ipp.css');
+  echo "Login Accepted\n";
+  menu_end();
+  exit ();
+}
+
+if (key_exists (logout, $_POST)) {
+  $oldID = $_POST[myID];
+  menu ('Login', 'ipp.css');
+  echo "You are now logged out<br>\n";
+  logintext ();
+  loginform ();
+  menu_end ();
+  exit ();
+}
+
+menu ('Login', 'ipp.css');
+echo "Invalid Client Post Request<br>\n";
+foreach ($_POST as $key => $value) {
+  echo "$key : $value<br>\n";
+}
+menu_end ();
+
+?>
Index: /trunk/ippMonitor/src/ScienceStats.php
===================================================================
--- /trunk/ippMonitor/src/ScienceStats.php	(revision 5881)
+++ /trunk/ippMonitor/src/ScienceStats.php	(revision 5881)
@@ -0,0 +1,26 @@
+
+<? function maintext () { ?>
+<p>
+Explore the Science results
+</p>
+<? } ?>
+
+<?php 
+
+include 'menu.php';
+
+if ($_SERVER[REQUEST_METHOD] != 'GET') { 
+  menu('Login', 'ipp.css');
+  echo "Invalid Client Request<br>\n";
+  menu_end ();
+  exit ();
+}
+
+checkID ();
+
+menu('Login', 'ipp.css');
+maintext ();
+menu_end();
+
+
+?>
Index: /trunk/ippMonitor/src/SelectProject.php
===================================================================
--- /trunk/ippMonitor/src/SelectProject.php	(revision 5881)
+++ /trunk/ippMonitor/src/SelectProject.php	(revision 5881)
@@ -0,0 +1,46 @@
+
+<? function projectform () { ?>
+  <p>
+  Select the project of interest
+  </p>
+  <form action="<? $myPage ?>" method="POST">
+  Project: <input type="text" name="proj"><br>
+  <input type="submit" name="project">
+  </form>
+<? } ?>
+
+<?php 
+
+include 'menu.php';
+
+checkID ();
+
+if ($_SERVER[REQUEST_METHOD] == 'GET') { 
+    menu ('Project', 'ipp.css');
+    projectform ();
+    menu_end ();
+}
+
+if ($_SERVER[REQUEST_METHOD] != 'POST') { 
+  menu ('Project', 'ipp.css');
+  echo "Invalid Client Request<br>\n";
+  menu_end ();
+}
+
+if (key_exists (project, $_POST)) {
+  $myProj = $_POST[proj];
+  # validate the existence of the project
+  $_GET[proj] = $myProj;
+  menu ('Project', 'ipp.css');
+  echo "New project is : $myProj\n";
+  menu_end();
+}
+  
+menu ('Project', 'ipp.css');
+echo "Invalid Client Post Request<br>\n";
+foreach ($_POST as $key => $value) {
+  echo "$key : $value<br>\n";
+}
+menu_end ();
+
+?>
Index: /trunk/ippMonitor/src/ipp.css
===================================================================
--- /trunk/ippMonitor/src/ipp.css	(revision 5881)
+++ /trunk/ippMonitor/src/ipp.css	(revision 5881)
@@ -0,0 +1,47 @@
+
+/* internal link line: c0d0d0 : d8d0f0
+   external link line: 303070 
+   border:             0080c0
+ */
+
+body  { bgcolor: #a0d0e0; background-color: #a0d0e0; padding: 5px; margin: 5px }
+
+a:link, a:visited, a:active { text-decoration: underline; font-weight: bold; color: #ff0000 }
+
+a.menutop     { text-decoration: none; color: #ffffff; font-weight: bold }
+a.menuext     { text-decoration: none; color: #ffffff; font-weight: bold }
+a.menulink    { text-decoration: none; font-weight: normal; color: #000000}
+a.menuselect  { text-decoration: none; font-weight: normal; color: #0000ff}
+a.piclink     { text-decoration: none; font-weight: normal; color: #0080c0}
+
+td.menutop    { text-align: left; background: #0080c0; font-size: small; color: #ffffff; padding: 2px; }
+td.menuext    { text-align: left; background: #303070; font-size: small; color: #ffffff; padding: 2px; }
+td.menulink   { text-align: left; background: #d0d0ff; font-size: small; color: #000000; padding: 2px; font-weight: normal; text-indent: 0px; }
+td.menuselect { text-align: left; background: #d0d0ff; font-size: small; color: #ff0000; padding: 2px; font-weight: normal; text-indent: 0px; }
+td.menuline   { text-align: left; background: #d0d0ff; font-size: small; color: #000000; padding: 2px; font-weight: normal; text-indent: 0px; }
+
+td.picture    { text-align: left; background: #00ffff; font-size: small; color: #ff0000; padding: 0px; text-indent: 2px; font-weight: normal; }
+td.piclink    { text-align: left; background: #0080c0; font-size: small; color: #ff0000; padding: 0px; text-indent: 0px; font-weight: normal; }
+
+table.page { border: 0px solid #0080c0; background: #a0d0e0; padding: 0px}
+
+td.title { background-color: #ff0000; padding: 5px; font-size: x-large; text-align: left; vertical-align: top}
+
+td.body  { 
+           text-align: left; 
+           font-size: normal;  
+           font-weight: normal;  
+           vertical-align: top;
+	   background: #d0d0ff; 
+	   background-color: #d0d0ff; 
+	   border: 3px solid #0080c0; 
+	   padding: 5px; 
+}
+
+table.menu { text-align: left; 
+             font-size: small; 
+	     font-weight: normal; 
+	     background: #ff0000; 
+	     border: 3px solid #0080c0; 
+	     padding: 0px; 
+}
Index: /trunk/ippMonitor/src/menu.dat
===================================================================
--- /trunk/ippMonitor/src/menu.dat	(revision 5881)
+++ /trunk/ippMonitor/src/menu.dat	(revision 5881)
@@ -0,0 +1,20 @@
+# style   | select-style | type    | menu line   		  | link page       
+
+# we have four valid menu types: picture, piclink, link, plain
+# picture   | picture 	 | picture | PScolorlogo2.jpg     	  | none
+# piclink   | piclink 	 | piclink | PScolorlogo2.jpg     	  | http://panstarrs.ifa.hawaii.edu
+# menulink  | menuselect | link    | other page   		  | notest.php     
+# menutop   | menutop	 | plain   | foo bar     		  | none            
+
+piclink   | piclink 	 | piclink | PScolorlogo2.jpg     	  | http://panstarrs.ifa.hawaii.edu
+menuext   | menuext	 | link    | Pan-STARRS public		  | http://panstarrs.ifa.hawaii.edu
+menuext   | menuext	 | link    | Pan-STARRS project		  | http://panstarrs.ifa.hawaii.edu/project
+menuext   | menuext	 | link    | Pan-STARRS IPP 		  | http://panstarrs.ifa.hawaii.edu/project/IPP
+menutop   | menutop      | plain   | &nbsp;                       | 
+menulink  | menuselect	 | link    | Login       		  | Login.php     
+menulink  | menuselect	 | link    | Select Project   		  | SelectProject.php     
+menulink  | menuselect	 | link    | Science Stats   		  | ScienceStats.php     
+menulink  | menuselect	 | link    | Detrend Stats   		  | DetrendStats.php     
+
+menutop   | menutop	 | plain   | foo bar     		  | none            
+menutop   | menutop	 | link    | test page   		  | phptest.php     
Index: /trunk/ippMonitor/src/menu.php
===================================================================
--- /trunk/ippMonitor/src/menu.php	(revision 5881)
+++ /trunk/ippMonitor/src/menu.php	(revision 5881)
@@ -0,0 +1,113 @@
+
+<? function loginform () { ?>
+  <form action="Login.php" method="POST">
+  Username: <input type="text" name="username"><br>
+  Password: <input type="text" name="password"><br>
+  <input type="submit" name="login">
+  </form>
+<? } ?>
+
+<? function logintext () { ?>
+<p>
+Welcome to the IPP status pages.  From these pages, you may examine
+summary information about the IPP processing results.  In order to
+continue, it is necessary to login as an authorized IPP user.
+</p>
+<? } ?>
+
+<?php
+
+function checkID () {
+
+  $myID = $_GET[myID];
+
+  if ($myID == "") {
+    menu('Login', 'ipp.css');
+    logintext ();    
+    loginform ();
+    menu_end ();
+  }
+
+  if ($myID != "foobar") {
+    menu('Login', 'ipp.css');
+    echo "unknown user, please login again<br>\n";
+    loginform ();
+    menu_end ();
+  }
+  $myPage = $_SERVER[SCRIPT_NAME] . "?myID=$myID";
+}
+
+function menu ($title, $sheet) {
+
+  echo "<html><head><title>\n";
+  echo "$title\n";
+  echo "</title></head>\n\n";
+
+  echo "<link rel=\"STYLESHEET\" HREF=\"$sheet\">\n";
+  echo "<body>\n";
+
+  $root = "/phpipp";
+
+  $file = fopen ("menu.dat", "r");
+  $myID = $_GET[myID];
+  $myProj = $_GET[proj];
+
+  echo "<table class=page cellspacing=10px><tr><td valign=top>\n";
+  echo "<table class=menu cellspacing=0px>\n";
+
+  while ($line = fgetcsv ($file, 1024, "|")) {
+    if (count($line) != 5) continue;
+    if (ereg ('^[:blank:]*#', $line[0])) continue;
+
+    $type = trim($line[2]);
+    $name = trim($line[3]);
+    $base = trim($line[4]);
+
+    $link = $base;
+    if ($myID || $myProj) {
+       $link = $link . "?";        
+    }
+    if ($myID) {
+        $link = $link . "myID=$myID";
+    } 
+    if ($myProj && !$myID) {
+        $link = $link . "proj=$myProj";
+    }
+    if ($myProj && $myID) {
+        $link = $link . "&proj=$myProj";
+    }
+	
+    $this = $_SERVER[SCRIPT_NAME]; 
+    $page = "$root/" . $base;
+    if ($page == $this) {
+	$style = $line[1];
+    } else { 
+	$style = $line[0];
+    }       
+
+    switch ($type) { 
+      case 'plain':
+        echo "<tr><td class=$style> $name </td></tr>\n";  
+        break;
+      case 'picture':
+        echo "<tr><td class=$style> <img src=\"$name\"> </td></tr>\n";     
+        break;
+      case 'piclink':
+        echo "<tr><td class=$style> <a class=$style href=\"$link\"> <img class=$style src=\"$name\"> </a></td></tr>\n";     
+        break;
+      default:
+        echo "<tr><td class=$style> <a class=$style href=\"$link\"> $name </a></td></tr>\n";     
+        break;
+    } 
+  }
+  fclose ($file);
+  echo "</table></td><td class=body valign=top>\n";
+}
+
+function menu_end () { 
+  echo "</td></tr></table>\n";
+  echo "</body></html>\n";
+  exit ();
+}
+
+?>
Index: /trunk/ippMonitor/src/phptest.php
===================================================================
--- /trunk/ippMonitor/src/phptest.php	(revision 5881)
+++ /trunk/ippMonitor/src/phptest.php	(revision 5881)
@@ -0,0 +1,26 @@
+<?php include 'menu.php'; ?>
+
+<?php menu('test.page', 'ipp.css'); ?>
+
+<?
+
+$varlist = array ('SERVER_NAME', 'GATEWAY_INTERFACE', 'SERVER_PROTOCOL',
+'SERVER_PORT', 'REQUEST_METHOD', 'PATH_INFO', 'PATH_TRANSLATED', 'SCRIPT_NAME',
+'QUERY_STRING', 'REMOTE_HOST', 'REMOTE_ADDR', 'AUTH_TYPE', 'REMOTE_USER',
+'REMOTE_IDENT', 'CONTENT_TYPE', 'CONTENT_LENGTH');
+
+foreach ($varlist as $name) {
+  echo "$name : $_SERVER[$name]<br>\n";
+}
+
+echo "get list<br>\n";
+foreach ($_GET as $key => $value) {
+  echo "$key : $value<br>\n";
+}
+
+?>
+
+<?php menu_dataend(); ?>
+
+</body>
+</html>
Index: /trunk/ippTools/doc/p2tools.txt
===================================================================
--- /trunk/ippTools/doc/p2tools.txt	(revision 5881)
+++ /trunk/ippTools/doc/p2tools.txt	(revision 5881)
@@ -0,0 +1,24 @@
+
+Phase 2 pipeline tools:
+
+p2define -time (start) (stop) -camera (camera) -region (ra,dec) (ra,dec)
+  * input: searches mddb:raw_exposures,raw_images
+  * output: updates mddb:P2_exposures_pending,P2_images_pending
+  * alternative output: identical to p2pending
+ 
+p2pending 
+  * input: searches mddb:P2_exposures_pending,P2_images_pending
+  * output: Nlines consisting of:
+    (expID) (class) (URL) 
+  * options: ?
+
+(place both of these in a single user tool, p2search)
+
+p2search -quick [options]
+p2search -define [options]
+p2search -pending [options]
+p2search -update [options]
+
+ppImage file://path/filename file://path/outroot -recipe (recipe) 
+ppImage neb://nebname neb://outroot -recipe (recipe)
+
Index: /trunk/ippTools/src/chiptool.c
===================================================================
--- /trunk/ippTools/src/chiptool.c	(revision 5881)
+++ /trunk/ippTools/src/chiptool.c	(revision 5881)
@@ -0,0 +1,38 @@
+# include "p2search.h"
+
+int main (int argc, char **argv) {
+
+    psArray *rawFrames;
+    psArray *doneFrames;
+    psArray *pendingFrames;
+
+    p2searchConfig (&config, argc, argv);
+
+    if (config->mode == P2_MODE_QUICK) {
+	rawFrames = p2searchRawFrames (config);
+	pendingFrames = p2rawToPending (config, rawFrames);
+	p2writePendingFrames (config, pendingFrames);
+    }	
+
+    if (config->mode == P2_MODE_DEFINE) {
+	rawFrames = p2searchRawFrames (config);
+	p2insertPendingFrames (config, rawFrames);
+    }
+
+    if (config->mode == P2_MODE_PENDING) {
+	pendingFrames = p2searchPendingFrames (config, FALSE);
+	p2writePendingImages (config, pendingFrames);
+    }
+
+    if (config->mode == P2_MODE_UPDATE) {
+	p2updatePendingFrames (config, pendingFrames);
+    }
+
+    if (config->mode == P2_MODE_DONE) {
+	pendingFrames = p2searchPendingFrames (config, TRUE);
+	doneFrames = p2pendingToDone (config);
+	p2insertDoneFrames (config, doneFrames);
+    }
+
+    exit (0);
+}
Index: /trunk/ippTools/src/chiptool.h
===================================================================
--- /trunk/ippTools/src/chiptool.h	(revision 5881)
+++ /trunk/ippTools/src/chiptool.h	(revision 5881)
@@ -0,0 +1,125 @@
+# include <stdio.h>
+# include <strings.h>  // for strcasecmp
+# include <unistd.h>   // for unlink
+# include <pslib.h>
+# include <pmObjects.h>
+# include <pmPSF.h>
+# include <pmPSFtry.h>
+# include <pmModelGroup.h>
+
+// load these values from the db in the init stage
+# define P2_TYPE_OBJECT 1
+# define P2_STATE_READY 2
+
+typedef enum {
+    P2_MODE_NONE,		      	// grab from raw, output for ppImage
+    P2_MODE_QUICK,		      	// grab from raw, output for ppImage
+    P2_MODE_DEFINE,			// grab from raw, create pending
+    P2_MODE_PENDING,			// grab from pending, output for ppImage
+    P2_MODE_UPDATE,			// set the current state
+    P2_MODE_DONE,			// set the current state
+    P2_MODE_CREATE,			// set the current state
+    P2_MODE_DELETE,			// set the current state
+} p2mode;
+
+typedef struct {
+    p2mode mode;
+    psTime start;
+    psTime stop;
+    psSphere r1;
+    psSphere r2;
+    char *camera;
+    char *filter;
+    psDB *database;
+} p2config;
+
+// this structure should be defined using the mddb api tools
+typedef struct {
+    char expID[32];			// free-form exposure ID string
+    char class;				// class of file: fpa (0), chip (1), cell (2), readout (3)?
+    int Nclass;				// number of files of this class
+    char camera[32];			// free-form camera ID string
+    char filter[32];			// free-form filter ID string
+    psTime time_start;			// exposure start time
+    psTime time_stop;			// exposure stop time
+    psTime exptime;			// exposure duration (seconds)
+    char exptype;			// type of exposure: object (0), bias (1), dark (2), flat (3), etc
+    float sky;				// measure sky brightness
+    float seeing;			// measured image quality 
+    float dettemp;			// temperature of detector
+} ppRawExposure;
+
+// this structure should be defined using the mddb api tools
+typedef struct {
+    char expID[32];			// free-form exposure ID string
+    char class;				// class of file: fpa (0), chip (1), cell (2), readout (3)?
+    char classID[32];			// identifier for class (chip00, cell00, etc)
+    char url[128];			// locator for file (neb / file)
+    char state;				// current load state
+} ppRawImfile;
+
+// this structure should be defined using the mddb api tools
+typedef struct {
+    char expID[32];			// free-form exposure ID string
+    char class;				// class of file: fpa (0), chip (1), cell (2), readout (3)?
+    int Nclass;				// number of files of this class
+    int Ndone;				// number of files of this class
+    char P1version;
+    char P2version;
+    char state;
+} p2PendingExposure;
+
+// this structure should be defined using the mddb api tools
+typedef struct {
+    char expID[32];			// free-form exposure ID string
+    char P2version;
+    char class;				// class of file: fpa (0), chip (1), cell (2), readout (3)?
+    char classID[32];			// identifier for class (chip00, cell00, etc)
+    char urlroot[128];			// locator for file (neb / file)
+    char input[16];			// extension for input image
+    char output[16];			// extension for output image
+    char log[16];			// extension for log file
+    char smf[16];			// extension for object table
+    char state;				// current P2 state
+} p2PendingImfile;
+
+// this structure should be defined using the mddb api tools
+typedef struct {
+    char expID[32];			// free-form exposure ID string
+    char class;				// class of file: fpa (0), chip (1), cell (2), readout (3)?
+    int Nclass;				// number of files of this class
+    int Ndone;				// number of files of this class
+    char P1version;
+    char P2version;
+    char state;
+} p2DoneExposure;
+
+// this structure should be defined using the mddb api tools
+typedef struct {
+    char expID[32];			// free-form exposure ID string
+    char P2version;
+    char class;				// class of file: fpa (0), chip (1), cell (2), readout (3)?
+    char classID[32];			// identifier for class (chip00, cell00, etc)
+    char urlroot[128];			// locator for file (neb / file)
+    char input[16];			// extension for input image
+    char output[16];			// extension for output image
+    char log[16];			// extension for log file
+    char smf[16];			// extension for object table
+    char state;				// current P2 state
+} p2DoneImfile;
+
+typedef struct {
+    ppRawExposure *exposure;
+    psArray *images;
+} ppRawFrame;
+
+typedef struct {
+    ppPendingExposure *exposure;
+    psArray *images;
+} p2PendingFrame;
+
+typedef struct {
+    ppDoneExposure *exposure;
+    psArray *images;
+} p2DoneFrame;
+
Index: /trunk/ippTools/src/chiptoolConfig.c
===================================================================
--- /trunk/ippTools/src/chiptoolConfig.c	(revision 5881)
+++ /trunk/ippTools/src/chiptoolConfig.c	(revision 5881)
@@ -0,0 +1,68 @@
+# include "p2search.h"
+
+bool p2searchConfig (psConfig *config, int argc, char **argv) {
+
+    // Parse the configurations (re-org a la ppImage)
+    config->site = NULL;            // Site configuration
+    config->camera = NULL;          // Camera configuration
+    config->recipe = NULL;          // Recipe configuration
+    if (! pmConfigRead(&config->site, &config->camera, &config->recipe, &argc, argv, RECIPE)) {
+        psErrorStackPrint(stderr, "Can't find site configuration!\n");
+        exit(EXIT_FAILURE);
+    }
+
+    // Parse other command-line arguments
+    config->arguments = psMetadataAlloc(); // The arguments, with default values
+
+    config->mode = P2_MODE_NONE;
+    if ((N = psArgumentGet (*argc, argv, "-quick"))) {
+	psArgumentRemove (N, argc, argv);
+	if (config->mode) psAbort ("p2search", "only one mode selection is allowed");
+	config->mode = P2_MODE_QUICK;
+    }
+    if ((N = psArgumentGet (*argc, argv, "-define"))) {
+	psArgumentRemove (N, argc, argv);
+	if (config->mode) psAbort ("p2search", "only one mode selection is allowed");
+	config->mode = P2_MODE_DEFINE;
+    }
+    if ((N = psArgumentGet (*argc, argv, "-pending"))) {
+	psArgumentRemove (N, argc, argv);
+	if (config->mode) psAbort ("p2search", "only one mode selection is allowed");
+	config->mode = P2_MODE_PENDING;
+    }
+    if ((N = psArgumentGet (*argc, argv, "-update"))) {
+	psArgumentRemove (N, argc, argv);
+	if (config->mode) psAbort ("p2search", "only one mode selection is allowed");
+	config->mode = P2_MODE_UPDATE;
+    }
+    if ((N = psArgumentGet (*argc, argv, "-done"))) {
+	psArgumentRemove (N, argc, argv);
+	if (config->mode) psAbort ("p2search", "only one mode selection is allowed");
+	config->mode = P2_MODE_UPDATE;
+    }
+
+    // paul's argument parsing convention requires: -key value 
+    psMetadataAddStr(config->arguments, PS_LIST_TAIL, "-quick",   0, "examine raw image table, write ppImage output", "");
+    psMetadataAddStr(config->arguments, PS_LIST_TAIL, "-define",  0, "examine raw image table, add to pending image table", "");
+    psMetadataAddStr(config->arguments, PS_LIST_TAIL, "-pending", 0, "examine pending image table, write ppImage output", "");
+    psMetadataAddStr(config->arguments, PS_LIST_TAIL, "-update",  0, "update pending image table", "");
+    psMetadataAddS32(config->arguments, PS_LIST_TAIL, "-time",    0, "define time range of interest", "");
+    psMetadataAddS32(config->arguments, PS_LIST_TAIL, "-camera",  0, "define camera of interest", "");
+    psMetadataAddS32(config->arguments, PS_LIST_TAIL, "-filter",  0, "define filter of interest", "");
+
+    if (! psArgumentParse(config->arguments, &argc, argv) || argc != 2) {
+        printf("\nPan-STARRS Phase 2 Search Tool\n\n");
+        printf("Usage: %s [mode] [options]\n\n", argv[0]);
+        printf(" [mode] : -quick | -define | -pending | -update\n\n");
+        psArgumentHelp(config->arguments);
+        psFree(config->arguments);
+        exit(EXIT_FAILURE);
+    }
+
+    // add the input and output images to the arguments list
+    psMetadataAddStr (config->arguments, PS_LIST_TAIL, "-output", 0, "Name of the output image", argv[1]);
+
+    // define Database handle, if used
+    config->database = pmConfigDB(config->site);
+    return true;
+} 
Index: /trunk/ippTools/src/p2insertDoneFrames.c
===================================================================
--- /trunk/ippTools/src/p2insertDoneFrames.c	(revision 5881)
+++ /trunk/ippTools/src/p2insertDoneFrames.c	(revision 5881)
@@ -0,0 +1,19 @@
+# include "p2search.h"
+
+// select pending frames (exposure+images) which are done/not done
+bool p2insertDoneFrames (p2SearchConfig *config, psArray *doneFrames) {
+
+    for (int i = 0; i < doneFrames->n; i++) {
+	p2DoneFrame *doneFrame = doneFrames->data[i];
+	p2DoneExposure *doneExposure = doneFrame->exposure;
+	
+	psArray *doneImages = doneFrame->images;
+	
+	p2DoneExposureInsertOneRow (config->database, doneExposure);
+	p2DoneImageInsertRows (config->database, doneImages);
+    }
+    return true;
+} 
+
+// XXX : must remove the done frames from the pending table, or we'll get duplicates
+// XXX : add P3pending insert function? selected on recipe type? 
Index: /trunk/ippTools/src/p2insertPendingFrames.c
===================================================================
--- /trunk/ippTools/src/p2insertPendingFrames.c	(revision 5881)
+++ /trunk/ippTools/src/p2insertPendingFrames.c	(revision 5881)
@@ -0,0 +1,62 @@
+# include "p2search.h"
+
+// select pending frames (exposure+images) which are done/not done
+// NOTE this function converts the rawFrames to pendingFrames AND inserts them
+//      this is useful since the table must be locked the whole time, to get the versions
+bool p2insertPendingFrames (p2SearchConfig *config, psArray *rawFrames) {
+
+    psMetadata *where = psMetadataAlloc ();
+
+    for (int i = 0; i < rawFrames->n; i++) {
+	ppRawFrame *rawFrame = rawFrames->data[i];
+
+	// find the next available version numbers
+	psMetadataAddStr (where, PS_LIST_TAIL, "EXP_ID", PS_META_REPLACE, "==", rawFrame->exposure->expID);
+	psArray *exposures = p2PendingExposureSelectRows (config->database, where, MAX_ROWS);
+	int version = -1;
+	for (int j = 0; j < exposures->n; j++) {
+	    p2PendingExposure *exposure = exposures->data[j];
+	    version = PS_MAX (version, exposure->P2version);
+	}
+	version ++;
+
+	p2PendingExposure *pendingExposure = p2PendingExposureAlloc ();
+	strcpy (pendingExposure->expID, rawFrame->exposure->expID);
+	pendingExposure->class = rawFrame->exposure->class;
+	pendingExposure->Nclass = rawFrame->exposure->Nclass;
+	pendingExposure->Ndone = 0;
+	pendingExposure->P1version = 0xff;
+	pendingExposure->P2version = version;
+	pendingExposure->state = P2_STATE_PENDING;
+
+	psArray *pendingImages = psArrayAlloc (rawFrame->images->n);
+	pendingImages->n = 0;
+	for (int j = 0; j < rawFrame->images->n; j++) {
+	    ppRawImage *rawImage = rawFrame->images->data[j];
+	    p2PendingImage *pendingImage = p2PendingImageAlloc ();
+
+	    strcpy (pendingImage->expID, rawImage->expID);
+	    pendingImage->P2version = version;
+	    pendingImage->class = rawImage->class;
+	    strcpy (pendingImage->classID, rawImage->classID);
+
+	    // XXX cleanup the url somehow
+	    strcpy (pendingImage->urlroot = rawImage->url);
+	    strcpy (pendingImage->input, "raw.fits");
+	    strcpy (pendingImage->output, "flt.fits");
+	    strcpy (pendingImage->log, "log");
+	    strcpy (pendingImage->smf, "smf");
+	    pendingImage->state = P2_STATE_PENDING;
+
+	    psArrayAdd (pendingImages, 100, pendingImage);
+	}
+	
+	p2PendingExposureInsertOneRow (config->database, pendingExposure);
+	p2PendingImageInsertRows (config->database, pendingImages);
+    }
+    return true;
+} 
+
+// XXX the filename layout is defined by this code
+// XXX have the SQL command update the version number?
+// XXX EAM : need to lock the table : inner or outer loop?
Index: /trunk/ippTools/src/p2pendingToDone.c
===================================================================
--- /trunk/ippTools/src/p2pendingToDone.c	(revision 5881)
+++ /trunk/ippTools/src/p2pendingToDone.c	(revision 5881)
@@ -0,0 +1,51 @@
+# include "p2search.h"
+
+// select pending frames (exposure+images) which are done/not done
+psArray *p2pendingToDone (p2SearchConfig *config, psArray *pendingFrames) {
+
+    psArray *doneFrames = psArrayAlloc (frames->n);
+    doneFrames->n = 0;
+
+    for (int i = 0; i < pendingFrames->n; i++) {
+	ppPendingFrame *pendingFrame = pendingFrames->data[i];
+	if (pendingFrame->exposure->state != P2_STATE_DONE) continue;
+
+	p2DoneExposure *doneExposure = p2DoneExposureAlloc ();
+	strcpy (doneExposure->expID, pendingFrame->exposure->expID);
+	doneExposure->class = pendingFrame->exposure->class;
+	doneExposure->Nclass = pendingFrame->exposure->Nclass;
+	doneExposure->Ndone = pendingFrame->exposure->Nclass;
+	doneExposure->P1version = pendingFrame->exposure->P1version;
+	doneExposure->P2version = pendingFrame->exposure->P2version;
+	doneExposure->state = P2_STATE_DONE;
+
+	psArray *doneImages = psArrayAlloc (pendingFrame->images->n);
+	doneImages->n = 0;
+	for (int j = 0; j < pendingFrame->images->n; j++) {
+	    ppPendingImage *pendingImage = pendingFrame->images->data[j];
+	    if (pendingImage->state != P2_STATE_DONE) {
+		psAbort ("p2search", "programming error!");
+	    }
+
+	    p2DoneImage *doneImage = p2DoneImageAlloc ();
+
+	    strcpy (doneImage->expID, pendingImage->expID);
+	    doneImage->P2version = pendingImage->P2version;
+	    doneImage->class = pendingImage->class;
+	    strcpy (doneImage->classID, pendingImage->classID);
+	    strcpy (doneImage->urlroot, pendingImage->urlroot);
+	    strcpy (doneImage->input,   pendingImage->input);
+	    strcpy (doneImage->output,  pendingImage->output);
+	    strcpy (doneImage->log,     pendingImage->log);
+	    strcpy (doneImage->smf,     pendingImage->smf);
+	    doneImage->state = P2_STATE_DONE;
+	    psArrayAdd (doneImages, 100, doneImage);
+	}
+	
+	p2DoneFrame *doneFrame = p2DoneFrameAlloc (doneExposure, doneImages);
+	psArrayAdd (doneFrames, 100, doneFrame);
+    }
+    return doneFrames;
+} 
+
+// XXX the filename layout is defined by this code
Index: /trunk/ippTools/src/p2rawToPending.c
===================================================================
--- /trunk/ippTools/src/p2rawToPending.c	(revision 5881)
+++ /trunk/ippTools/src/p2rawToPending.c	(revision 5881)
@@ -0,0 +1,48 @@
+# include "p2search.h"
+
+// select pending frames (exposure+images) which are done/not done
+psArray *p2rawToPending (p2SearchConfig *config, psArray *rawFrames) {
+
+    psArray *pendingFrames = psArrayAlloc (frames->n);
+    pendingFrames->n = 0;
+
+    for (int i = 0; i < rawFrames->n; i++) {
+	ppRawFrame *rawFrame = rawFrames->data[i];
+
+	p2PendingExposure *pendingExposure = p2PendingExposureAlloc ();
+	strcpy (pendingExposure->expID, rawFrame->exposure->expID);
+	pendingExposure->class = rawFrame->exposure->class;
+	pendingExposure->Nclass = rawFrame->exposure->Nclass;
+	pendingExposure->Ndone = 0;
+	pendingExposure->P1version = 0xff;
+	pendingExposure->P2version = 0xff;
+	pendingExposure->state = P2_STATE_PENDING;
+
+	psArray *pendingImages = psArrayAlloc (rawFrame->images->n);
+	pendingImages->n = 0;
+	for (int j = 0; j < rawFrame->images->n; j++) {
+	    ppRawImage *rawImage = rawFrame->images->data[j];
+	    p2PendingImage *pendingImage = p2PendingImageAlloc ();
+
+	    strcpy (pendingImage->expID, rawImage->expID);
+	    pendingImage->P2version = 0xff;
+	    pendingImage->class = rawImage->;
+	    strcpy (pendingImage->classID, rawImage->classID);
+
+	    pendingImage->urlroot = rawImage->;
+	    strcpy (pendingImage->input, "raw.fits");
+	    strcpy (pendingImage->output, "flt.fits");
+	    strcpy (pendingImage->log, "log");
+	    strcpy (pendingImage->smf, "smf");
+	    pendingImage->state = P2_STATE_PENDING;
+
+	    psArrayAdd (pendingImages, 100, pendingImage);
+	}
+	
+	p2PendingFrame *pendingFrame = p2PendingFrameAlloc (pendingExposure, pendingImages);
+	psArrayAdd (pendingFrames, 100, pendingFrame);
+    }
+    return pendingFrames;
+} 
+
+// XXX the filename layout is defined by this code
Index: /trunk/ippTools/src/p2searchPendingFrames.c
===================================================================
--- /trunk/ippTools/src/p2searchPendingFrames.c	(revision 5881)
+++ /trunk/ippTools/src/p2searchPendingFrames.c	(revision 5881)
@@ -0,0 +1,52 @@
+# include "p2search.h"
+
+// select pending frames (exposure+images) which are done/not done
+psArray *p2searchPendingFrames (p2SearchConfig *config, bool isDone) {
+
+    // build 'where' structure
+    psMetadata *where = psMetadataAlloc ();
+
+    if (isDone) {
+	psMetadataAddStr (where, PS_LIST_TAIL, "STATE", 0, "==", P2_STATE_DONE);
+    } else {
+	psMetadataAddStr (where, PS_LIST_TAIL, "STATE", 0, "==", P2_STATE_PENDING);
+    }
+
+    // in order to include these restrictions, we need to define these table columns
+    if (config->camera != NULL) {
+	psMetadataAddStr (where, PS_LIST_TAIL, "CAMERA", 0, "==", config->camera);
+    }
+    if (config->filter != NULL) {
+	psMetadataAddStr (where, PS_LIST_TAIL, "FILTER", 0, "==", config->filter);
+    }
+    if (!psTimeIsZero(config->start) && !psTimeIsZero(config->stop)) {
+	psMetadataAddTime (where, PS_LIST_TAIL, "TIME_START", 0, "<", config->time_stop);
+	psMetadataAddTime (where, PS_LIST_TAIL, "TIME_STOP", 0, ">", config->time_start);
+    }
+
+    psArray *exposures = p2PendingExposureSelectRows (config->database, where, MAX_ROWS);
+    psFree (where);
+
+    // output array of rawSet
+    psArray *frames = psArrayAlloc (exposures->n);
+
+    // 'where' to select each exposure
+    psMetadata *where = psMetadataAlloc ();
+    for (int i = 0; i < exposures->n; i++) {
+	p2PendingExposure *exposure = exposures->data[i];
+	
+	psMetadataAddStr (where, PS_LIST_TAIL, "EXP_ID", PS_META_REPLACE, "==", exposure->expID);
+	psMetadataAddStr (where, PS_LIST_TAIL, "P2_VERSION", PS_META_REPLACE, "==", exposure->P2version);
+
+	psArray *images = ppPendingImageSelectRows (config->database, where, MAX_ROWS);
+
+	ppPendingFrame *frame = ppPendingFrameAlloc (exposure, images);
+
+	psArrayAdd (frames, 100, frame);
+    }
+    return frames;
+} 
+
+// XXX EAM : this may be more efficient if we just select all p2PendingImages which
+// have the appropriate state.  that would limit the ability to filter the selection.
+// test to see how well/poorly this performs
Index: /trunk/ippTools/src/p2searchRawFrames.c
===================================================================
--- /trunk/ippTools/src/p2searchRawFrames.c	(revision 5881)
+++ /trunk/ippTools/src/p2searchRawFrames.c	(revision 5881)
@@ -0,0 +1,42 @@
+# include "p2search.h"
+
+// select raw frames (exposure+images) which match the given config options
+psArray p2searchRawImages (p2SearchConfig *config) {
+
+    // build 'where' structure
+    psMetadata *where = psMetadataAlloc ();
+
+    psMetadataAddU8  (where, PS_LIST_TAIL, "TYPE",  0, "==", P2_TYPE_OBJECT);
+    psMetadataAddStr (where, PS_LIST_TAIL, "STATE", 0, "==", P2_START_READY);
+
+    if (config->camera != NULL) {
+	psMetadataAddStr (where, PS_LIST_TAIL, "CAMERA", 0, "==", config->camera);
+    }
+    if (config->filter != NULL) {
+	psMetadataAddStr (where, PS_LIST_TAIL, "FILTER", 0, "==", config->filter);
+    }
+    if (!psTimeIsZero(config->start) && !psTimeIsZero(config->stop)) {
+	psMetadataAddTime (where, PS_LIST_TAIL, "TIME_START", 0, "<", config->time_stop);
+	psMetadataAddTime (where, PS_LIST_TAIL, "TIME_STOP", 0, ">", config->time_start);
+    }
+
+    psArray *exposures = ppRawExposureSelectRows (config->database, where, MAX_ROWS);
+    psFree (where);
+
+    // output array of ppRawFrame
+    psArray *frames = psArrayAlloc (exposures->n);
+
+    // 'where' to select each exposure
+    psMetadata *where = psMetadataAlloc ();
+    for (int i = 0; i < exposures->n; i++) {
+	ppRawExposure *exposure = exposures->data[i];
+	
+	psMetadataAddStr (where, PS_LIST_TAIL, "EXP_ID", PS_META_REPLACE, "==", exposure->expID);
+
+	psArray *images = ppRawImageSelectRows (config->database, where, MAX_ROWS);
+
+	ppRawFrame *frame = ppRawFrameAlloc (exposure, images);
+	psArrayAdd (frames, 100, frame);
+    }
+    return frames;
+} 
Index: /trunk/ippTools/src/p2updatePending.c
===================================================================
--- /trunk/ippTools/src/p2updatePending.c	(revision 5881)
+++ /trunk/ippTools/src/p2updatePending.c	(revision 5881)
@@ -0,0 +1,31 @@
+# include "p2search.h"
+
+// select pending frames (exposure+images) which are not done, select their done images, count, update
+bool p2updatePendingFrames (p2SearchConfig *config, psArray *pendingFrames) {
+
+    psMetadata *where = psMetadataAlloc ();
+
+    // select all of the exposures which are still pending
+    psMetadataAddStr (where, PS_LIST_TAIL, "STATE", 0, "==", P2_STATE_PENDING);
+    psArray *exposures = p2PendingExposureSelectRows (config->database, where, MAX_ROWS);
+
+    // we will now select all of the matching images which are done
+    psMetadataAddStr (where, PS_LIST_TAIL, "STATE", PS_META_REPLACE, "==", P2_STATE_DONE);
+    for (int i = 0; i < exposures->n; i++) {
+	p2PendingExposure *exposure = exposures->data[i];
+
+	// find the next available version numbers
+	psMetadataAddStr (where, PS_LIST_TAIL, "EXP_ID",     PS_META_REPLACE, "==", exposure->expID);
+	psMetadataAddStr (where, PS_LIST_TAIL, "P2_VERSION", PS_META_REPLACE, "==", exposure->P2version);
+	psArray *images = p2PendingImageSelectRows (config->database, where, MAX_ROWS);
+	if (images->n != exposure->Nclass) {
+	    // free things
+	    continue;
+	}
+	// frame is complete, set exposure state to done
+	// XXX add P2 stats here?
+	exposure->state = P2_STATE_DONE;
+	p2PendingExposureUpdateRows (config->database, exposure);
+    }
+    return true;
+} 
Index: /trunk/ippTools/src/p2writePendingFrames.c
===================================================================
--- /trunk/ippTools/src/p2writePendingFrames.c	(revision 5881)
+++ /trunk/ippTools/src/p2writePendingFrames.c	(revision 5881)
@@ -0,0 +1,25 @@
+# include "p2search.h"
+
+// select pending frames (exposure+images) which are done/not done
+bool p2writePendingImages (p2SearchConfig *config, psArray *frames) {
+
+    for (int i = 0; i < frames->n; i++) {
+	p2PendingFrame *frame = frames->data[i];
+	
+	for (int j = 0; j < frame->images->n; j++) {
+	    p2PendingImage *image = frame->images->data[j];
+	    fprintf (stdout, "%s %c %c %s %s %s %s %s %s %c\n",
+		     image->expID,
+		     image->P2version,
+		     image->class,
+		     image->classID,
+		     image->urlroot,
+		     image->input,
+		     image->output,
+		     image->log,
+		     image->smf,
+		     image->state);
+	}
+    }
+    return true;
+} 
Index: /trunk/ippTools/src/pxadmin.c
===================================================================
--- /trunk/ippTools/src/pxadmin.c	(revision 5881)
+++ /trunk/ippTools/src/pxadmin.c	(revision 5881)
@@ -0,0 +1,16 @@
+# include "p2search.h"
+
+int main (int argc, char **argv) {
+
+    p2adminConfig (&config, argc, argv);
+
+    if (config->mode == P2_MODE_CREATE) {
+	p2deleteTables (config);
+    }
+
+    if (config->mode == P2_MODE_DELETE) {
+	p2createTables (config);
+    }
+
+    exit (0);
+}
Index: /trunk/ippTools/src/pxadminConfig.c
===================================================================
--- /trunk/ippTools/src/pxadminConfig.c	(revision 5881)
+++ /trunk/ippTools/src/pxadminConfig.c	(revision 5881)
@@ -0,0 +1,64 @@
+# include "p2search.h"
+
+bool p2adminConfig (psConfig *config, int argc, char **argv) {
+
+    // Parse the configurations (re-org a la ppImage)
+    config->site = NULL;            // Site configuration
+    config->camera = NULL;          // Camera configuration
+    config->recipe = NULL;          // Recipe configuration
+    if (! pmConfigRead(&config->site, &config->camera, &config->recipe, &argc, argv, RECIPE)) {
+        psErrorStackPrint(stderr, "Can't find site configuration!\n");
+        exit(EXIT_FAILURE);
+    }
+
+    // Parse other command-line arguments
+    config->arguments = psMetadataAlloc(); // The arguments, with default values
+
+    config->mode = P2_MODE_NONE;
+    if ((N = psArgumentGet (*argc, argv, "-create"))) {
+	psArgumentRemove (N, argc, argv);
+	if (config->mode) psAbort ("p2search", "only one mode selection is allowed");
+	config->mode = P2_MODE_CREATE;
+    }
+    if ((N = psArgumentGet (*argc, argv, "-delete"))) {
+	psArgumentRemove (N, argc, argv);
+	if (config->mode) psAbort ("p2search", "only one mode selection is allowed");
+	config->mode = P2_MODE_DELETE;
+    }
+
+    if (config->mode == P2_MODE_NONE) {
+	fprintf (stderr, "admin mode not specified\n");
+        psArgumentHelp(config->arguments);
+        psFree(config->arguments);
+        exit(EXIT_FAILURE);
+    }
+
+    // paul's argument parsing convention requires: -key value 
+    psMetadataAddStr(config->arguments, PS_LIST_TAIL, "-create", 0, "create the P2 tables", "");
+    psMetadataAddStr(config->arguments, PS_LIST_TAIL, "-delete", 0, "delete the P2 tables", "");
+
+    if ((N = psArgumentGet (*argc, argv, "-help"))) {
+        psArgumentHelp(config->arguments);
+        psFree(config->arguments);
+        exit(EXIT_FAILURE);
+    }
+
+    if (! psArgumentParse(config->arguments, &argc, argv) || argc != 1) {
+        printf("\nPan-STARRS Phase 2 Admin Tool\n\n");
+        printf("Usage: %s [mode]\n", argv[0]);
+        printf(" [mode] : -create | -delete\n\n");
+        psArgumentHelp(config->arguments);
+        psFree(config->arguments);
+        exit(EXIT_FAILURE);
+    }
+
+    // define Database handle, if used
+    config->database = pmConfigDB(config->site);
+    return true;
+} 
+
+/*
+ * USAGE:
+ * p2admin -create : create the P2 tables
+ * p2admin -delete : delete the P2 tables (asks for confirmation)
+ */
