Index: /branches/haf_branches/videophot2010/ippTools/src/Makefile.am
===================================================================
--- /branches/haf_branches/videophot2010/ippTools/src/Makefile.am	(revision 29944)
+++ /branches/haf_branches/videophot2010/ippTools/src/Makefile.am	(revision 29945)
@@ -28,5 +28,6 @@
 	pubtool \
 	diffphottool \
-	minidvodbtool
+	minidvodbtool \
+	videophottool
 
 pkginclude_HEADERS = \
@@ -47,4 +48,5 @@
 	pxtoolsErrorCodes.h \
 	pxtree.h \
+	pxvidephot.h \
 	pxwarp.h
 
@@ -76,5 +78,6 @@
 	pubtool.h \
 	diffphottool.h \
-	minidvodbtool.h
+	minidvodbtool.h \
+	videophottool.h
 
 lib_LTLIBRARIES = libpxtools.la
@@ -100,4 +103,5 @@
 	pxtoolsErrorCodes.c \
 	pxtree.c \
+	pxvideophot.c \
 	pxwarp.c
 
@@ -284,4 +288,10 @@
     diffphottoolConfig.c
 
+videophottool_CFLAGS = $(PSLIB_CFLAGS) $(PSMODULES_CFLAGS) $(IPPDB_CFLAGS)
+videophottool_LDADD = $(PSLIB_LIBS) $(PSMODULES_LIBS) $(IPPDB_LIBS) libpxtools.la
+videophottool_SOURCES = \
+    videophottool.c \
+    videophottoolConfig.c
+
 minidvodbtool_CFLAGS = $(PSLIB_CFLAGS) $(PSMODULES_CFLAGS) $(IPPDB_CFLAGS)
 minidvodbtool_LDADD = $(PSLIB_LIBS) $(PSMODULES_LIBS) $(IPPDB_LIBS) libpxtools.la
Index: /branches/haf_branches/videophot2010/ippTools/src/pxtools.h
===================================================================
--- /branches/haf_branches/videophot2010/ippTools/src/pxtools.h	(revision 29944)
+++ /branches/haf_branches/videophot2010/ippTools/src/pxtools.h	(revision 29945)
@@ -48,4 +48,5 @@
 #include "pxmagic.h"
 #include "pxspace.h"
+#include "pxvideophot.h"
 
 # define MAX_ROWS 10e9
Index: /branches/haf_branches/videophot2010/ippTools/src/pxvideophot.c
===================================================================
--- /branches/haf_branches/videophot2010/ippTools/src/pxvideophot.c	(revision 29945)
+++ /branches/haf_branches/videophot2010/ippTools/src/pxvideophot.c	(revision 29945)
@@ -0,0 +1,318 @@
+/*
+ * pxvideophot.c
+ *
+ * Copyright (C) 2007-2008  Joshua Hoblitt
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * program; see the file COPYING. If not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <ippdb.h>
+#include <string.h>
+
+#include "pxtools.h"
+#include "pxvideophot.h"
+
+bool pxvideophotSetSearchArgs (psMetadata *md) {
+
+    // psMetadataAddStr(md,  PS_LIST_TAIL, "-class_id",           0, "search by class ID", NULL);
+    // psMetadataAddStr(md,  PS_LIST_TAIL, "-reduction",          0, "search by reduction class", NULL);
+
+    // XXX need to allow multiple exp_ids
+    psMetadataAddS64(md,  PS_LIST_TAIL, "-exp_id",             0, "search by exp_id", 0);
+    psMetadataAddStr(md,  PS_LIST_TAIL, "-exp_name",           0, "search by exp_name", NULL);
+    psMetadataAddStr(md,  PS_LIST_TAIL, "-inst",               0, "search for camera", NULL);
+    psMetadataAddStr(md,  PS_LIST_TAIL, "-telescope",          0, "search for telescope", NULL);
+    psMetadataAddTime(md, PS_LIST_TAIL, "-dateobs_begin",      0, "search for exposures by time (>=)", NULL);
+    psMetadataAddTime(md, PS_LIST_TAIL, "-dateobs_end",        0, "search for exposures by time (<=)", NULL);
+    psMetadataAddStr(md,  PS_LIST_TAIL, "-exp_tag",            0, "search by exp_tag", NULL);
+    psMetadataAddStr(md,  PS_LIST_TAIL, "-exp_type",           0, "search by exp_type", NULL);
+    psMetadataAddStr(md,  PS_LIST_TAIL, "-filelevel",          0, "search by filelevel", NULL);
+    psMetadataAddStr(md,  PS_LIST_TAIL, "-filter",             0, "search for filter", NULL);
+    psMetadataAddF32(md,  PS_LIST_TAIL, "-airmass_min",        0, "search by min airmass", NAN);
+    psMetadataAddF32(md,  PS_LIST_TAIL, "-airmass_max",        0, "search by max airmass", NAN);
+    psMetadataAddF64(md,  PS_LIST_TAIL, "-ra_min",             0, "search by min RA (degrees) ", NAN);
+    psMetadataAddF64(md,  PS_LIST_TAIL, "-ra_max",             0, "search by max RA (degrees) ", NAN);
+    psMetadataAddF64(md,  PS_LIST_TAIL, "-decl_min",           0, "search by min DEC (degrees)", NAN);
+    psMetadataAddF64(md,  PS_LIST_TAIL, "-decl_max",           0, "search by max DEC (degrees)", NAN);
+    psMetadataAddF32(md,  PS_LIST_TAIL, "-exp_time_min",       0, "search by min exposure time", NAN);
+    psMetadataAddF32(md,  PS_LIST_TAIL, "-exp_time_max",       0, "search by max exposure time", NAN);
+    psMetadataAddF32(md,  PS_LIST_TAIL, "-sat_pixel_frac_min", 0, "search by min fraction of saturated pixels", NAN);
+    psMetadataAddF32(md,  PS_LIST_TAIL, "-sat_pixel_frac_max", 0, "search by max fraction of saturated pixels", NAN);
+    psMetadataAddF64(md,  PS_LIST_TAIL, "-bg_min",             0, "search by min background", NAN);
+    psMetadataAddF64(md,  PS_LIST_TAIL, "-bg_max",             0, "search by max background", NAN);
+    psMetadataAddF64(md,  PS_LIST_TAIL, "-bg_stdev_min",       0, "search by min background standard deviation", NAN);
+    psMetadataAddF64(md,  PS_LIST_TAIL, "-bg_stdev_max",       0, "search by max background standard deviation", NAN);
+    psMetadataAddF64(md,  PS_LIST_TAIL, "-bg_mean_stdev_min",  0, "search by min background mean standard deviation (across imfiles)", NAN);
+    psMetadataAddF64(md,  PS_LIST_TAIL, "-bg_mean_stdev_max",  0, "search by max background mean standard deviation (across imfiles)", NAN);
+    psMetadataAddF64(md,  PS_LIST_TAIL, "-alt_min",            0, "search by min altitude", NAN);
+    psMetadataAddF64(md,  PS_LIST_TAIL, "-alt_max",            0, "search by max altitude", NAN);
+    psMetadataAddF64(md,  PS_LIST_TAIL, "-az_min",             0, "search by min azimuth ", NAN);
+    psMetadataAddF64(md,  PS_LIST_TAIL, "-az_max",             0, "search by max azimuth ", NAN);
+    psMetadataAddF64(md,  PS_LIST_TAIL, "-ccd_temp_min",       0, "search by min ccd tempature", NAN);
+    psMetadataAddF64(md,  PS_LIST_TAIL, "-ccd_temp_max",       0, "search by max ccd tempature", NAN);
+    psMetadataAddF64(md,  PS_LIST_TAIL, "-posang_min",         0, "search by min rotator position angle", NAN);
+    psMetadataAddF64(md,  PS_LIST_TAIL, "-posang_max",         0, "search by max rotator position angle", NAN);
+    psMetadataAddF32(md,  PS_LIST_TAIL, "-sun_angle_min",      0, "search by min solar angle", NAN);
+    psMetadataAddF32(md,  PS_LIST_TAIL, "-sun_angle_max",      0, "search by max solar angle", NAN);
+    psMetadataAddStr(md,  PS_LIST_TAIL, "-object",             0, "search by exposure object", NULL);
+    psMetadataAddStr(md,  PS_LIST_TAIL, "-comment",            0, "search by comment field (LIKE comparison)", NULL);
+    psMetadataAddStr(md,  PS_LIST_TAIL, "-obs_mode",           0, "search by observation mode", NULL);
+    return true;
+}
+
+// XXX explicit refs to rawExp and videophotRun?
+// XXX careful with entries that could match rawExp rawImfile, videophotRun, or videophotProcessedImfile (eg, bg)
+bool pxvideophotGetSearchArgs (pxConfig *config, psMetadata *where) {
+
+    // definebyquery : rawExp only
+    // updaterun : rawExp, videophotRun
+    // pendingimfile : rawExp, videophotRun
+    // processedimfile : rawExp, videophotRun, videophotProcessedImfile
+    // revertprocessedimfile : rawExp, videophotProcessedImfile
+    // updateprocessedimfile : videophotProcessedImfile
+    PXOPT_COPY_S64(config->args, where, "-exp_id", "rawExp.exp_id", "==");
+    PXOPT_COPY_STR(config->args, where, "-exp_name", "rawExp.exp_name", "==");
+    PXOPT_COPY_STR(config->args, where, "-inst", "rawExp.camera", "==");
+    PXOPT_COPY_STR(config->args, where, "-telescope", "rawExp.telescope", "==");
+    PXOPT_COPY_TIME(config->args, where, "-dateobs_begin", "rawExp.dateobs", ">=");
+    PXOPT_COPY_TIME(config->args, where, "-dateobs_end", "rawExp.dateobs", "<=");
+    PXOPT_COPY_STR(config->args, where, "-exp_tag", "rawExp.exp_tag", "==");
+    PXOPT_COPY_STR(config->args, where, "-exp_type", "rawExp.exp_type", "==");
+    PXOPT_COPY_STR(config->args, where, "-filelevel", "rawExp.filelevel", "==");
+    PXOPT_COPY_STR(config->args, where, "-filter", "rawExp.filter", "LIKE");
+    PXOPT_COPY_F64(config->args, where, "-airmass_min", "rawExp.airmass", ">=");
+    PXOPT_COPY_F64(config->args, where, "-airmass_max", "rawExp.airmass", "<");
+    PXOPT_COPY_RADEC(config->args, where, "-ra_min", "rawExp.ra", ">=");
+    PXOPT_COPY_RADEC(config->args, where, "-ra_max", "rawExp.ra", "<");
+    PXOPT_COPY_RADEC(config->args, where, "-decl_min", "rawExp.decl", ">=");
+    PXOPT_COPY_RADEC(config->args, where, "-decl_max", "rawExp.decl", "<");
+    PXOPT_COPY_F32(config->args, where, "-exp_time_min", "rawExp.exp_time", ">=");
+    PXOPT_COPY_F32(config->args, where, "-exp_time_max", "rawExp.exp_time", "<");
+    PXOPT_COPY_F32(config->args, where, "-sat_pixel_frac_min", "rawExp.sat_pixel_frac", ">=");
+    PXOPT_COPY_F32(config->args, where, "-sat_pixel_frac_max", "rawExp.sat_pixel_frac", "<");
+    PXOPT_COPY_F64(config->args, where, "-bg_min", "rawExp.bg", ">=");
+    PXOPT_COPY_F64(config->args, where, "-bg_max", "rawExp.bg", "<");
+    PXOPT_COPY_F64(config->args, where, "-bg_stdev_min", "rawExp.bg_stdev", ">=");
+    PXOPT_COPY_F64(config->args, where, "-bg_stdev_max", "rawExp.bg_stdev", "<");
+    PXOPT_COPY_F64(config->args, where, "-bg_mean_stdev_min", "rawExp.bg_mean_stdev", ">=");
+    PXOPT_COPY_F64(config->args, where, "-bg_mean_stdev_max", "rawExp.bg_mean_stdev", "<");
+    PXOPT_COPY_F64(config->args, where, "-alt_min", "rawExp.alt", ">=");
+    PXOPT_COPY_F64(config->args, where, "-alt_max", "rawExp.alt", "<");
+    PXOPT_COPY_F64(config->args, where, "-az_min", "rawExp.az", ">=");
+    PXOPT_COPY_F64(config->args, where, "-az_max", "rawExp.az", "<");
+    PXOPT_COPY_F32(config->args, where, "-ccd_temp_min", "rawExp.ccd_temp", ">=");
+    PXOPT_COPY_F32(config->args, where, "-ccd_temp_max", "rawExp.ccd_temp", "<");
+    PXOPT_COPY_F64(config->args, where, "-posang_min", "rawExp.posang", ">=");
+    PXOPT_COPY_F64(config->args, where, "-posang_max", "rawExp.posang", "<");
+    PXOPT_COPY_STR(config->args, where, "-object", "rawExp.object", "LIKE");
+    PXOPT_COPY_F32(config->args, where, "-sun_angle_min", "rawExp.sun_angle", ">=");
+    PXOPT_COPY_F32(config->args, where, "-sun_angle_max", "rawExp.sun_angle", "<");
+    PXOPT_COPY_STR(config->args, where, "-comment", "rawExp.comment", "LIKE");
+    PXOPT_COPY_STR(config->args, where, "-obs_mode", "rawExp.obs_mode", "LIKE");
+    return true;
+}
+
+bool pxvideophotRunSetState(pxConfig *config, psS64 videophot_id, const char *state, const psS64 magicked)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+    PS_ASSERT_PTR_NON_NULL(state, false);
+
+    // check that state is a valid string value
+    if (!pxIsValidState(state)) {
+        psError(PS_ERR_UNKNOWN, false, "invalid videophotRun state: %s", state);
+        return false;
+    }
+
+    char *query = "UPDATE videophotRun SET state = '%s', magicked = %" PRId64 " WHERE videophot_id = %" PRId64;
+    if (!p_psDBRunQueryF(config->dbh, query, state, magicked, videophot_id)) {
+        psError(PS_ERR_UNKNOWN, false,
+                "failed to change state for videophot_id %" PRId64, videophot_id);
+        return false;
+    }
+
+    return true;
+}
+
+
+bool pxvideophotRunSetStateByQuery(pxConfig *config, psMetadata *where, const char *state)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+    PS_ASSERT_PTR_NON_NULL(state, false);
+
+    // check that state is a valid string value
+    if (!pxIsValidState(state)) {
+        psError(PS_ERR_UNKNOWN, false, "invalid videophotRun state: %s", state);
+        return false;
+    }
+
+    if (!strcmp(state, "full")) {
+        // There are states that need to be met for a run to be set to full that we don't
+        // check here.
+        // for example all of the run's Imfiles must have videophotProcessedImfile.data_state == "full"
+        // videophotRun.magicked = (SUM(!videophotProcessedImfile.magicked) = 0)
+        // so don't do allow setting the state to full
+        psError(PS_ERR_UNKNOWN, true, "cannot use -updaterun so set videophotRun state to full");
+        return false;
+    }
+
+
+    psString query = psStringCopy("UPDATE videophotRun JOIN rawExp USING(exp_id) SET state = '%s'");
+
+    if (!strcmp(state, "cleaned") || !strcmp(state, "purged")) {
+        // if magicked is non-zero set it to -1
+        psStringAppend(&query, ", videophotRun.magicked = IF(videophotRun.magicked = 0, 0, -1)");
+    }
+
+    if (where && psListLength(where->list) > 0) {
+        psString whereClause = psDBGenerateWhereSQL(where, NULL);
+        psStringAppend(&query, " %s", whereClause);
+        psFree(whereClause);
+    }
+
+    if (!p_psDBRunQueryF(config->dbh, query, state)) {
+        psFree(query);
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+
+    psFree(query);
+
+    return true;
+}
+
+bool pxvideophotProcessedImfileSetStateByQuery(pxConfig *config, psMetadata *where, const char *state)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+    PS_ASSERT_PTR_NON_NULL(state, false);
+
+    // check that state is a valid string value
+    if (!pxIsValidState(state)) {
+        psError(PS_ERR_UNKNOWN, false, "invalid videophotProcessedImfile state: %s", state);
+        return false;
+    }
+
+/*     if (!strcmp(state, "full")) { */
+/*         // There are states that need to be met for a run to be set to full that we don't */
+/*         // check here. */
+/*         // for example all of the run's Imfiles must have videophotProcessedImfile.data_state == "full" */
+/*         // videophotRun.magicked = (SUM(!videophotProcessedImfile.magicked) = 0) */
+/*         // so don't do allow setting the state to full */
+/*         psError(PS_ERR_UNKNOWN, true, "cannot use -updaterun so set videophotRun state to full"); */
+/*         return false; */
+/*     } */
+
+    psString query = psStringCopy("UPDATE videophotProcessedImfile JOIN videophotRun USING(videophot_id) JOIN rawExp ON videophotRun.exp_id = rawExp.exp_id SET data_state = '%s'");
+
+    if (where && psListLength(where->list) > 0) {
+        psString whereClause = psDBGenerateWhereSQL(where, NULL);
+        psStringAppend(&query, " %s", whereClause);
+        psFree(whereClause);
+    } else {
+        psError(PS_ERR_UNKNOWN, true, "search parameters are required");
+        return false;
+    }
+
+    if (!p_psDBRunQueryF(config->dbh, query, state)) {
+        psFree(query);
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+
+    psFree(query);
+
+    return true;
+}
+
+psS64 pxvideophotQueueByExpTag(pxConfig *config,
+                         psS64 exp_id,
+                         const char *workdir,
+                         const char *label,
+                         const char *data_group,
+                         const char *dist_group,
+                         const char *reduction,
+                         const char *expgroup,
+                         const char *dvodb,
+                         const char *tess_id,
+                         const char *end_stage,
+                         const char *note)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    if (!psDBTransaction(config->dbh)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+
+
+    // create a videophotRun
+    if (!videophotRunInsert(config->dbh,
+            0x0, // videophot_id
+            exp_id,
+            "new",      // state
+            workdir,
+            "dirty",    // workdir_state
+            label,
+            data_group,
+            dist_group,
+            reduction,
+            expgroup,
+            dvodb,
+            tess_id,
+            end_stage,
+            0,          // magicked
+		       NULL,
+		       0,
+		       NAN,
+		       NAN,
+		       NAN,
+		       NAN,
+            note
+            )
+    ) {
+        if (!psDBRollback(config->dbh)) {
+            psError(PS_ERR_UNKNOWN, false, "database error failed to rollback transaction");
+        }
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return 0;
+    }
+
+    psS64 videophot_id =  psDBLastInsertID(config->dbh);
+
+    // Create rows in videophotImfile table for each input exposure.
+    // This creates the videophot_image_id values
+    psString query = "INSERT INTO videophotImfile "
+                     "SELECT %" PRId64 ", class_id, 0 FROM rawImfile WHERE exp_id = %" PRId64;
+
+    if (!p_psDBRunQueryF(config->dbh, query, videophot_id, exp_id)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        // rollback
+        if (!psDBRollback(config->dbh)) {
+            psError(PS_ERR_UNKNOWN, false, "database error");
+        }
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return 0;
+    }
+    if (!psDBCommit(config->dbh)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return 0;
+    }
+
+    return videophot_id;
+}
+
Index: /branches/haf_branches/videophot2010/ippTools/src/pxvideophot.h
===================================================================
--- /branches/haf_branches/videophot2010/ippTools/src/pxvideophot.h	(revision 29945)
+++ /branches/haf_branches/videophot2010/ippTools/src/pxvideophot.h	(revision 29945)
@@ -0,0 +1,49 @@
+/*
+ * pxvideophot.h
+ *
+ * Copyright (C) 2007-2008  Joshua Hoblitt
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * program; see the file COPYING. If not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef PXVIDEOPHOT_H
+#define PXVIDEOPHOT_H 1
+
+#include <pslib.h>
+
+#include "pxtools.h"
+
+
+
+bool pxvideophotRunSetState(pxConfig *config, psS64 videophot_id, const char *state, const psS64 magicked);
+bool pxvideophotProcessedImfileSetStateByQuery(pxConfig *config, psMetadata *where, const char *state);
+
+psS64 pxvideophotQueueByExpTag(pxConfig *config,
+                         psS64 exp_id,
+                         const char *workdir,
+                         const char *label,
+                         const char *data_group,
+                         const char *dist_group,
+                         const char *reduction,
+                         const char *expgroup,
+                         const char *dvodb,
+                         const char *tess_id,
+                         const char *end_stage,
+                         const char *note);
+
+
+bool pxvideophotSetSearchArgs (psMetadata *md);
+bool pxvideophotGetSearchArgs (pxConfig *config, psMetadata *where);
+
+#endif // PXVIDEOPHOT_H
Index: /branches/haf_branches/videophot2010/ippTools/src/videophottool.c
===================================================================
--- /branches/haf_branches/videophot2010/ippTools/src/videophottool.c	(revision 29945)
+++ /branches/haf_branches/videophot2010/ippTools/src/videophottool.c	(revision 29945)
@@ -0,0 +1,2027 @@
+/*
+ * videophottool.c
+ *
+ * Copyright (C) 2006  Joshua Hoblitt
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * program; see the file COPYING. If not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <string.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <math.h>
+
+#include "pxtools.h"
+#include "pxdata.h"
+#include "pxvideophot.h"
+
+#include "videophottool.h"
+#include "camtool.h"
+
+static bool definebyqueryMode(pxConfig *config);
+static bool definecopyMode(pxConfig *config);
+static bool updaterunMode(pxConfig *config);
+static bool pendingimfileMode(pxConfig *config);
+static bool addprocessedimfileMode(pxConfig *config);
+static bool processedimfileMode(pxConfig *config);
+static bool revertprocessedimfileMode(pxConfig *config);
+static bool updateprocessedimfileMode(pxConfig *config);
+static bool advanceexpMode(pxConfig *config);
+static bool blockMode(pxConfig *config);
+static bool maskedMode(pxConfig *config);
+static bool unmaskedMode(pxConfig *config);
+static bool unblockMode(pxConfig *config);
+static bool pendingcleanuprunMode(pxConfig *config);
+static bool pendingcleanupimfileMode(pxConfig *config);
+static bool revertcleanupMode(pxConfig *config);
+static bool donecleanupMode(pxConfig *config);
+static bool runMode(pxConfig *config);
+static bool tocleanedimfileMode(pxConfig *config);
+static bool tofullimfileMode(pxConfig *config);
+static bool topurgedimfileMode(pxConfig *config);
+static bool toscrubbedimfileMode(pxConfig *config);
+static bool exportrunMode(pxConfig *config);
+static bool importrunMode(pxConfig *config);
+static bool runstateMode(pxConfig *config);
+static bool setimfiletoupdateMode(pxConfig *config);
+static bool listrunMode(pxConfig *config);
+
+# define MODECASE(caseName, func) \
+    case caseName: \
+    if (!func(config)) { \
+        goto FAIL; \
+    } \
+    break;
+
+int main(int argc, char **argv) {
+    psLibInit(NULL);
+
+    pxConfig *config = videophottoolConfig(NULL, argc, argv);
+    if (!config) {
+        psError(PXTOOLS_ERR_CONFIG, false, "failed to configure");
+        goto FAIL;
+    }
+
+    switch (config->mode) {
+        MODECASE(VIDEOPHOTTOOL_MODE_DEFINEBYQUERY,           definebyqueryMode);
+        MODECASE(VIDEOPHOTTOOL_MODE_DEFINECOPY,              definecopyMode);
+        MODECASE(VIDEOPHOTTOOL_MODE_UPDATERUN,               updaterunMode);
+        MODECASE(VIDEOPHOTTOOL_MODE_PENDINGIMFILE,           pendingimfileMode);
+        MODECASE(VIDEOPHOTTOOL_MODE_ADDPROCESSEDIMFILE,      addprocessedimfileMode);
+        MODECASE(VIDEOPHOTTOOL_MODE_PROCESSEDIMFILE,         processedimfileMode);
+        MODECASE(VIDEOPHOTTOOL_MODE_REVERTPROCESSEDIMFILE,   revertprocessedimfileMode);
+        MODECASE(VIDEOPHOTTOOL_MODE_UPDATEPROCESSEDIMFILE,   updateprocessedimfileMode);
+        MODECASE(VIDEOPHOTTOOL_MODE_ADVANCEEXP,              advanceexpMode);
+        MODECASE(VIDEOPHOTTOOL_MODE_BLOCK,                   blockMode);
+        MODECASE(VIDEOPHOTTOOL_MODE_MASKED,                  maskedMode);
+        MODECASE(VIDEOPHOTTOOL_MODE_UNMASKED,                unmaskedMode);
+        MODECASE(VIDEOPHOTTOOL_MODE_UNBLOCK,                 unblockMode);
+        MODECASE(VIDEOPHOTTOOL_MODE_PENDINGCLEANUPRUN,       pendingcleanuprunMode);
+        MODECASE(VIDEOPHOTTOOL_MODE_PENDINGCLEANUPIMFILE,    pendingcleanupimfileMode);
+        MODECASE(VIDEOPHOTTOOL_MODE_REVERTCLEANUP,           revertcleanupMode);
+        MODECASE(VIDEOPHOTTOOL_MODE_DONECLEANUP,             donecleanupMode);
+        MODECASE(VIDEOPHOTTOOL_MODE_RUN,                     runMode);
+        MODECASE(VIDEOPHOTTOOL_MODE_TOCLEANEDIMFILE,         tocleanedimfileMode);
+        MODECASE(VIDEOPHOTTOOL_MODE_TOFULLIMFILE,            tofullimfileMode);
+        MODECASE(VIDEOPHOTTOOL_MODE_TOPURGEDIMFILE,          topurgedimfileMode);
+        MODECASE(VIDEOPHOTTOOL_MODE_TOSCRUBBEDIMFILE,        toscrubbedimfileMode);
+        MODECASE(VIDEOPHOTTOOL_MODE_EXPORTRUN,               exportrunMode);
+        MODECASE(VIDEOPHOTTOOL_MODE_IMPORTRUN,               importrunMode);
+        MODECASE(VIDEOPHOTTOOL_MODE_RUNSTATE,                runstateMode);
+        MODECASE(VIDEOPHOTTOOL_MODE_SETIMFILETOUPDATE,       setimfiletoupdateMode);
+        MODECASE(VIDEOPHOTTOOL_MODE_LISTRUN,                 listrunMode);
+        default:
+            psAbort("invalid option (this should not happen)");
+    }
+
+    psFree(config);
+    pmConfigDone();
+    psLibFinalize();
+
+    exit(EXIT_SUCCESS);
+
+FAIL:
+    psErrorStackPrint(stderr, "\n");
+    int exit_status = pxerrorGetExitStatus();
+
+    psFree(config);
+    pmConfigDone();
+    psLibFinalize();
+
+    exit(exit_status);
+}
+
+// Queue exposures for videophot processing
+static bool queue_exposures(pxConfig *config,  // Configuration
+                            const psArray *exps, // Exposures information
+                            const char *workdir, // Working directory, or NULL to inherit
+                            const char *label,   // Label, or NULL to inherit
+                            const char *data_group, // Data group, or NULL to inherit
+                            const char *dist_group, // Distribution group, or NULL to inherit
+                            const char *reduction, // Reduction class, or NULL to inherit
+                            const char *expgroup,  // Exposure group
+                            const char *dvodb,     // DVO database, or NULL to inherit
+                            const char *tess_id,   // Tessellation identifier, or NULL to inherit
+                            const char *end_stage,  // End stage, or NULL to inherit
+                            const char *note        // Note
+    )
+{
+    // start a transaction so we don't end up with an exp without any associated
+    // imfiles
+    if (!psDBTransaction(config->dbh)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+
+    // if end_stage is warp (or NULL), check for valid tess_id
+    for (long i = 0; i < psArrayLength(exps); i++) {
+        psMetadata *md = exps->data[i];
+
+        bool status;
+        char *end_stage = psMetadataLookupStr(&status, md, "end_stage");
+        if (end_stage && strcasecmp(end_stage, "warp")) continue;
+
+        char *raw_tess_id   = psMetadataLookupStr(&status, md, "tess_id");
+        if (raw_tess_id || tess_id) continue;
+
+        char *label  = psMetadataLookupStr(&status, md, "label");
+        psS64 exp_id = psMetadataLookupS64(&status, md, "exp_id");
+
+        if (!status) {
+            psError(PS_ERR_UNKNOWN, false,
+                    "cannot queue analysis to WARP without a defined tess id: label: %s, exp_id %" PRId64,
+                    label, exp_id);
+            return false;
+        }
+    }
+
+
+# define GET_VALUE(PTYPE,CTYPE,VALUE,NAME)                              \
+    PTYPE VALUE;                                                        \
+    {                                                                   \
+        bool status;                                                    \
+        VALUE = psMetadataLookup##CTYPE(&status, md, NAME);             \
+        if (!status) {                                                  \
+            psError(PS_ERR_UNKNOWN, false, "failed to lookup value for %s", NAME); \
+            return false;                                               \
+        }                                                               \
+    }
+
+    // loop over our list of exp_ids
+    for (long i = 0; i < psArrayLength(exps); i++) {
+        psMetadata *md = exps->data[i];
+
+        rawExpRow *row = rawExpObjectFromMetadata(md);
+        if (!row) {
+            psError(PS_ERR_UNKNOWN, false, "failed to convert metadata into videophotRun");
+            return false;
+        }
+
+        GET_VALUE (psS64,    S64, exp_id,        "exp_id");
+        GET_VALUE (psString, Str, raw_workdir,   "workdir");
+        GET_VALUE (psString, Str, raw_label,     "label");
+        GET_VALUE (psString, Str, raw_reduction, "reduction");
+        // GET_VALUE (psString, Str, raw_expgroup,  "expgroup");
+        GET_VALUE (psString, Str, raw_dvodb,     "dvodb");
+        GET_VALUE (psString, Str, raw_tess_id,   "tess_id");
+        GET_VALUE (psString, Str, raw_end_stage, "end_stage");
+
+        if (!row->exp_id) {
+            psError(PS_ERR_UNKNOWN, false, "failed to find value for exp_id");
+            return false;
+        }
+
+        // queue the exp
+        if (!pxvideophotQueueByExpTag(config,
+                                 exp_id,
+                                 workdir     ? workdir   : raw_workdir,
+                                 label       ? label     : raw_label,
+                                 data_group  ? data_group : (label ? label : raw_label),
+                                 dist_group,
+                                 reduction   ? reduction : raw_reduction,
+                                 // expgroup    ? expgroup  : raw_expgroup,
+                                 // XXX how does expgroup get defined?
+                                 expgroup,
+                                 dvodb       ? dvodb     : raw_dvodb,
+                                 tess_id     ? tess_id   : raw_tess_id,
+                                 end_stage   ? end_stage : raw_end_stage,
+                                 note
+                                 )) {
+            if (!psDBRollback(config->dbh)) {
+                psError(PS_ERR_UNKNOWN, false, "database error");
+            }
+            psError(PS_ERR_UNKNOWN, false,
+                    "failed to trying to queue exp_id: %" PRId64, exp_id);
+            return false;
+        }
+    }
+
+    if (!psDBCommit(config->dbh)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+
+    return true;
+}
+
+static bool definebyqueryMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, NULL);
+
+    PXOPT_LOOKUP_S64(exp_id, config->args, "-exp_id", false, false);
+
+    psMetadata *where = psMetadataAlloc();
+    pxvideophotGetSearchArgs (config, where); // rawExp only
+    pxAddLabelSearchArgs (config, where, "-label", "newExp.label", "LIKE"); // define using newExp label
+
+    // psListLength(where->list) is at least 1 because exp_type defaults to "object"
+    // so we require a list longer than 1 entry
+    if ((psListLength(where->list) <= 1) && !psMetadataLookupBool(NULL, config->args, "-all")) {
+        psFree(where);
+        psError(PXTOOLS_ERR_CONFIG, false, "search parameters are required");
+        return false;
+    }
+
+    PXOPT_LOOKUP_STR(workdir, config->args, "-set_workdir", true, false);
+    PXOPT_LOOKUP_STR(label, config->args, "-set_label", false, false);
+    PXOPT_LOOKUP_STR(reduction, config->args, "-set_reduction", false, false);
+    PXOPT_LOOKUP_STR(expgroup, config->args, "-set_expgroup", false, false);
+    PXOPT_LOOKUP_STR(dvodb, config->args, "-set_dvodb", false, false);
+    PXOPT_LOOKUP_STR(tess_id, config->args, "-set_tess_id", false, false);
+    PXOPT_LOOKUP_STR(end_stage, config->args, "-set_end_stage", false, false);
+    PXOPT_LOOKUP_STR(dist_group, config->args, "-set_dist_group", false, false);
+    PXOPT_LOOKUP_STR(data_group, config->args, "-set_data_group", false, false);
+    PXOPT_LOOKUP_STR(note, config->args, "-set_note", false, false);
+
+    // default
+    PXOPT_LOOKUP_BOOL(pretend, config->args, "-pretend", false);
+    PXOPT_LOOKUP_BOOL(simple, config->args, "-simple", false);
+
+    // find the exp_id of all the exposures that we want to queue up.
+    psString query = pxDataGet("videophottool_find_rawexp.sql");
+    if (!query) {
+        psError(PXTOOLS_ERR_SYS, false, "failed to retreive SQL statement");
+        psFree(where);
+        return false;
+    }
+
+    psString whereClause = psDBGenerateWhereConditionSQL(where, NULL);
+    psStringAppend(&query, " AND %s", whereClause);
+
+    psFree(whereClause);
+    psFree(where);
+
+    if (!p_psDBRunQuery(config->dbh, query)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(query);
+        return false;
+    }
+    psFree(query);
+
+    psArray *output = p_psDBFetchResult(config->dbh);
+    if (!output) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+    if (!psArrayLength(output)) {
+        psTrace("videophottool", PS_LOG_INFO, "no rows found");
+        psFree(output);
+        return true;
+    }
+
+    if (pretend) {
+        // negative simple so the default is true
+        if (!ippdbPrintMetadatas(stdout, output, "rawExp", !simple)) {
+            psError(PS_ERR_UNKNOWN, false, "failed to print array");
+            psFree(output);
+            return false;
+        }
+        psFree(output);
+        return true;
+    }
+
+    if (!queue_exposures(config, output, workdir, label, data_group, dist_group, reduction, expgroup,
+                         dvodb, tess_id, end_stage, note)) {
+        psError(psErrorCodeLast(), false, "Unable to queue exposures for videophot.");
+        psFree(output);
+        return false;
+    }
+    psFree(output);
+
+    return true;
+}
+
+static bool definecopyMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, NULL);
+
+    PXOPT_LOOKUP_S64(exp_id, config->args, "-exp_id", false, false);
+
+    psMetadata *where = psMetadataAlloc();
+    pxvideophotGetSearchArgs (config, where); // rawExp only
+    pxAddLabelSearchArgs (config, where, "-label", "videophotOld.label", "LIKE");
+    pxAddLabelSearchArgs (config, where, "-data_group", "videophotOld.data_group", "LIKE");
+
+    // psListLength(where->list) is at least 1 because exp_type defaults to "object"
+    // so we require a list longer than 1 entry
+    if ((psListLength(where->list) <= 1) && !psMetadataLookupBool(NULL, config->args, "-all")) {
+        psFree(where);
+        psError(PXTOOLS_ERR_CONFIG, false, "search parameters are required");
+        return false;
+    }
+
+    PXOPT_LOOKUP_STR(workdir, config->args, "-set_workdir", true, false);
+    PXOPT_LOOKUP_STR(label, config->args, "-set_label", true, false);
+    PXOPT_LOOKUP_STR(reduction, config->args, "-set_reduction", false, false);
+    PXOPT_LOOKUP_STR(expgroup, config->args, "-set_expgroup", false, false);
+    PXOPT_LOOKUP_STR(dvodb, config->args, "-set_dvodb", false, false);
+    PXOPT_LOOKUP_STR(tess_id, config->args, "-set_tess_id", false, false);
+    PXOPT_LOOKUP_STR(end_stage, config->args, "-set_end_stage", false, false);
+    PXOPT_LOOKUP_STR(dist_group, config->args, "-set_dist_group", false, false);
+    PXOPT_LOOKUP_STR(data_group, config->args, "-set_data_group", false, false);
+    PXOPT_LOOKUP_STR(note, config->args, "-set_note", false, false);
+
+    // default
+    PXOPT_LOOKUP_BOOL(pretend, config->args, "-pretend", false);
+    PXOPT_LOOKUP_BOOL(simple, config->args, "-simple", false);
+
+    // find the exp_id of all the exposures that we want to queue up.
+    psString query = pxDataGet("videophottool_definecopy.sql");
+    if (!query) {
+        psError(PXTOOLS_ERR_SYS, false, "failed to retreive SQL statement");
+        psFree(where);
+        return false;
+    }
+
+    psString whereClause = psDBGenerateWhereConditionSQL(where, NULL);
+    psStringAppend(&query, " AND %s", whereClause);
+
+    psFree(whereClause);
+    psFree(where);
+
+    if (!p_psDBRunQueryF(config->dbh, query, label)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(query);
+        return false;
+    }
+    psFree(query);
+
+    psArray *output = p_psDBFetchResult(config->dbh);
+    if (!output) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+    if (!psArrayLength(output)) {
+        psTrace("videophottool", PS_LOG_INFO, "no rows found");
+        psFree(output);
+        return true;
+    }
+
+    if (pretend) {
+        // negative simple so the default is true
+        if (!ippdbPrintMetadatas(stdout, output, "rawExp", !simple)) {
+            psError(PS_ERR_UNKNOWN, false, "failed to print array");
+            psFree(output);
+            return false;
+        }
+        psFree(output);
+        return true;
+    }
+
+    if (!queue_exposures(config, output, workdir, label, data_group, dist_group, reduction, expgroup,
+                         dvodb, tess_id, end_stage, note)) {
+        psError(psErrorCodeLast(), false, "Unable to queue exposures for videophot.");
+        psFree(output);
+        return false;
+    }
+    psFree(output);
+
+    return true;
+}
+
+
+static bool updaterunMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, NULL);
+
+    psMetadata *where = psMetadataAlloc();
+    pxvideophotGetSearchArgs (config, where); // rawExp, videophotRun
+    PXOPT_COPY_S64(config->args,  where, "-videophot_id", "videophotRun.videophot_id", "==");
+    // we only allow a single label to match (do not use pxAddLabelSearchArgs here)
+    PXOPT_COPY_STR(config->args,  where, "-label",   "videophotRun.label",   "LIKE");
+    PXOPT_COPY_STR(config->args,  where, "-state",   "videophotRun.state",   "==");
+    PXOPT_COPY_STR(config->args,  where, "-data_group", "videophotRun.data_group",   "LIKE");
+    PXOPT_COPY_STR(config->args,  where, "-dist_group", "videophotRun.dist_group",   "LIKE");
+
+    if (!psListLength(where->list)) {
+        psFree(where);
+        psError(PXTOOLS_ERR_CONFIG, false, "search parameters are required");
+        return false;
+    }
+    psString query = psStringCopy("UPDATE videophotRun JOIN rawExp USING(exp_id) -- join hook %s\n");
+
+    // pxUpdateRun gets parameters from config->args and updates
+    bool result = pxUpdateRun(config, where, &query, "videophotRun", "videophot_id", "videophotProcessedImfile", true);
+    if (!result) {
+        psError(psErrorCodeLast(), false, "pxUpdateRun failed");
+    }
+
+    psFree(query);
+    psFree(where);
+
+    return result;
+}
+
+
+static bool pendingimfileMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, NULL);
+
+    PXOPT_LOOKUP_U64(limit, config->args, "-limit", false, false);
+    PXOPT_LOOKUP_BOOL(simple, config->args, "-simple", false);
+
+    psMetadata *where = psMetadataAlloc();
+    pxvideophotGetSearchArgs (config, where); //videophotRun, rawExp
+    PXOPT_COPY_S64(config->args, where, "-videophot_id", "videophotRun.videophot_id", "==");
+    pxAddLabelSearchArgs (config, where, "-label", "videophotRun.label", "==");
+
+    psString query = pxDataGet("videophottool_pendingimfile.sql");
+    if (!query) {
+        psError(PXTOOLS_ERR_SYS, false, "failed to retreive SQL statement");
+        return false;
+    }
+
+    if (where && psListLength(where->list)) {
+        psString whereClause = psDBGenerateWhereConditionSQL(where, NULL);
+        psStringAppend(&query, " AND %s", whereClause);
+        psFree(whereClause);
+    }
+    psFree(where);
+
+    psStringAppend(&query, "\nORDER BY priority DESC, videophot_id");
+
+    // treat limit == 0 as "no limit"
+    if (limit) {
+        psString limitString = psDBGenerateLimitSQL(limit);
+        psStringAppend(&query, " %s", limitString);
+        psFree(limitString);
+    }
+
+    if (!p_psDBRunQuery(config->dbh, query)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(query);
+        return false;
+    }
+    psFree(query);
+
+    psArray *output = p_psDBFetchResult(config->dbh);
+    if (!output) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+    if (!psArrayLength(output)) {
+        psTrace("videophottool", PS_LOG_INFO, "no rows found");
+        psFree(output);
+        return true;
+    }
+
+    // negative simple so the default is true
+    if (!ippdbPrintMetadatas(stdout, output, "videophotPendingImfile", !simple)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print array");
+        psFree(output);
+        return false;
+    }
+
+    psFree(output);
+
+    return true;
+}
+
+
+static bool addprocessedimfileMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    // videophot_id, exp_tag, class_id are required
+    PXOPT_LOOKUP_S64(videophot_id, config->args, "-videophot_id", true, false);
+    PXOPT_LOOKUP_S64(exp_id, config->args, "-exp_id", true, false);
+    PXOPT_LOOKUP_STR(class_id, config->args, "-class_id", true, false);
+
+    // optional
+    PXOPT_LOOKUP_STR(uri, config->args,            "-uri", false, false);
+    PXOPT_LOOKUP_F32(bg, config->args,             "-bg", false, false);
+    PXOPT_LOOKUP_F32(bg_stdev, config->args,       "-bg_stdev", false, false);
+    PXOPT_LOOKUP_F32(bg_mean_stdev, config->args,  "-bg_mean_stdev", false, false);
+    PXOPT_LOOKUP_F32(bias, config->args,           "-bias", false, false);
+    PXOPT_LOOKUP_F32(bias_stdev, config->args,     "-bias_stdev", false, false);
+    PXOPT_LOOKUP_F32(fringe_0, config->args,       "-fringe_0", false, false);
+    PXOPT_LOOKUP_F32(fringe_1, config->args,       "-fringe_1", false, false);
+    PXOPT_LOOKUP_F32(fringe_2, config->args,       "-fringe_2", false, false);
+    PXOPT_LOOKUP_F32(ap_resid, config->args,       "-ap_resid", false, false);
+    PXOPT_LOOKUP_F32(ap_resid_stdev, config->args, "-ap_resid_stdev", false, false);
+
+    PXOPT_LOOKUP_F32(fwhm_major, config->args,     "-fwhm_major", false, false);
+    PXOPT_LOOKUP_F32(fwhm_major_lq, config->args,     "-fwhm_major_lq", false, false);
+    PXOPT_LOOKUP_F32(fwhm_major_uq, config->args,     "-fwhm_major_uq", false, false);
+
+    PXOPT_LOOKUP_F32(fwhm_minor,    config->args,     "-fwhm_minor", false, false);
+    PXOPT_LOOKUP_F32(fwhm_minor_lq, config->args,     "-fwhm_minor_lq", false, false);
+    PXOPT_LOOKUP_F32(fwhm_minor_uq, config->args,     "-fwhm_minor_uq", false, false);
+
+    PXOPT_LOOKUP_F32(iq_fwhm_major,     config->args,      "-iq_fwhm_major",     false, false);
+    PXOPT_LOOKUP_F32(iq_fwhm_major_err, config->args,      "-iq_fwhm_major_err",     false, false);
+    PXOPT_LOOKUP_F32(iq_fwhm_minor,     config->args,      "-iq_fwhm_minor",     false, false);
+    PXOPT_LOOKUP_F32(iq_fwhm_minor_err, config->args,      "-iq_fwhm_minor_err",     false, false);
+
+    PXOPT_LOOKUP_F32(iq_m2,     config->args,      "-iq_m2",     false, false);
+    PXOPT_LOOKUP_F32(iq_m2_err, config->args,      "-iq_m2_err", false, false);
+    PXOPT_LOOKUP_F32(iq_m2_uq,  config->args,      "-iq_m2_uq",  false, false);
+    PXOPT_LOOKUP_F32(iq_m2_lq,  config->args,      "-iq_m2_lq",  false, false);
+
+    PXOPT_LOOKUP_F32(iq_m2c,     config->args,     "-iq_m2c",     false, false);
+    PXOPT_LOOKUP_F32(iq_m2c_err, config->args,     "-iq_m2c_err", false, false);
+    PXOPT_LOOKUP_F32(iq_m2c_uq,  config->args,     "-iq_m2c_uq",  false, false);
+    PXOPT_LOOKUP_F32(iq_m2c_lq,  config->args,     "-iq_m2c_lq",  false, false);
+
+    PXOPT_LOOKUP_F32(iq_m2s,     config->args,     "-iq_m2s",     false, false);
+    PXOPT_LOOKUP_F32(iq_m2s_err, config->args,     "-iq_m2s_err", false, false);
+    PXOPT_LOOKUP_F32(iq_m2s_uq,  config->args,     "-iq_m2s_uq",  false, false);
+    PXOPT_LOOKUP_F32(iq_m2s_lq,  config->args,     "-iq_m2s_lq",  false, false);
+
+    PXOPT_LOOKUP_F32(iq_m3,     config->args,      "-iq_m3",     false, false);
+    PXOPT_LOOKUP_F32(iq_m3_err, config->args,      "-iq_m3_err", false, false);
+    PXOPT_LOOKUP_F32(iq_m3_uq,  config->args,      "-iq_m3_uq",  false, false);
+    PXOPT_LOOKUP_F32(iq_m3_lq,  config->args,      "-iq_m3_lq",  false, false);
+
+    PXOPT_LOOKUP_F32(iq_m4,     config->args,      "-iq_m4",     false, false);
+    PXOPT_LOOKUP_F32(iq_m4_err, config->args,      "-iq_m4_err", false, false);
+    PXOPT_LOOKUP_F32(iq_m4_uq,  config->args,      "-iq_m4_uq",  false, false);
+    PXOPT_LOOKUP_F32(iq_m4_lq,  config->args,      "-iq_m4_lq",  false, false);
+
+    PXOPT_LOOKUP_F32(dtime_detrend, config->args,  "-dtime_detrend", false, false);
+    PXOPT_LOOKUP_F32(dtime_photom,  config->args,  "-dtime_photom",  false, false);
+    PXOPT_LOOKUP_F32(dtime_total,  config->args,   "-dtime_total",  false, false);
+    PXOPT_LOOKUP_F32(dtime_script, config->args,   "-dtime_script", false, false);
+    PXOPT_LOOKUP_STR(hostname, config->args,       "-hostname", false, false);
+    PXOPT_LOOKUP_F32(n_stars, config->args,        "-n_stars", false, false);
+    PXOPT_LOOKUP_F32(n_psfstars, config->args,     "-n_psfstars", false, false);
+    PXOPT_LOOKUP_F32(n_iqstars, config->args,      "-n_iqstars", false, false);
+    PXOPT_LOOKUP_F32(n_extended, config->args,     "-n_extended", false, false);
+    PXOPT_LOOKUP_F32(n_cr, config->args,           "-n_cr", false, false);
+    PXOPT_LOOKUP_STR(path_base, config->args,      "-path_base", false, false);
+    PXOPT_LOOKUP_S64(magicked, config->args,       "-magicked", false, false);
+
+    PXOPT_LOOKUP_STR(ver_pslib, config->args, "-ver_pslib", false, false);
+    PXOPT_LOOKUP_STR(ver_psmodules, config->args, "-ver_psmodules", false, false);
+    PXOPT_LOOKUP_STR(ver_psphot, config->args, "-ver_psphot", false, false);
+    PXOPT_LOOKUP_STR(ver_psastro, config->args, "-ver_psastro", false, false);
+    PXOPT_LOOKUP_STR(ver_ppstats, config->args, "-ver_ppstats", false, false);
+    PXOPT_LOOKUP_STR(ver_ppimage, config->args, "-ver_ppimage", false, false);
+    PXOPT_LOOKUP_STR(ver_streaks, config->args, "-ver_streaks", false, false);
+
+    PXOPT_LOOKUP_S32(maskfrac_npix, config->args, "-maskfrac_npix", false, false);
+    PXOPT_LOOKUP_F32(maskfrac_static, config->args, "-maskfrac_static", false, false);
+    PXOPT_LOOKUP_F32(maskfrac_dynamic, config->args, "-maskfrac_dynamic", false, false);
+    PXOPT_LOOKUP_F32(maskfrac_magic, config->args, "-maskfrac_magic", false, false);
+    PXOPT_LOOKUP_F32(maskfrac_advisory, config->args, "-maskfrac_advisory", false, false);
+
+    psTrace("czw.test",1,"Received versions: pslib %s psmodules %s psphot %s psastro %s ppstats %s ppImage %s streaks %s\n",
+	    ver_pslib,ver_psmodules,ver_psphot,ver_psastro,ver_ppstats,ver_ppimage,ver_streaks);
+    psString ver_code = NULL;
+    if ((ver_pslib)&&(ver_psmodules)) {
+      ver_code = pxMergeCodeVersions(ver_pslib,ver_psmodules);
+    }
+    if (ver_psphot) {
+      ver_code = pxMergeCodeVersions(ver_code,ver_psphot);
+    }
+    if (ver_psastro) {
+      ver_code = pxMergeCodeVersions(ver_code,ver_psastro);
+    }
+    if (ver_ppstats) {
+      ver_code = pxMergeCodeVersions(ver_code,ver_ppstats);
+    }
+    if (ver_ppimage) {
+      ver_code = pxMergeCodeVersions(ver_code,ver_ppimage);
+    }
+    if (ver_streaks) {
+      ver_code = pxMergeCodeVersions(ver_code,ver_streaks);
+    }
+    
+    // default values
+    PXOPT_LOOKUP_S16(fault, config->args, "-fault", false, false);
+    PXOPT_LOOKUP_S16(quality, config->args, "-quality", false, false);
+
+    if (!psDBTransaction(config->dbh)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+
+    if (!videophotProcessedImfileInsert(config->dbh,
+                                   videophot_id,
+                                   exp_id,
+                                   class_id,
+                                   "full",
+                                   uri,
+                                   bg,
+                                   bg_stdev,
+                                   bg_mean_stdev,
+                                   bias,
+                                   bias_stdev,
+                                   fringe_0,
+                                   fringe_1,
+                                   fringe_2,
+                                   ap_resid,
+                                   ap_resid_stdev,
+
+                                   fwhm_major,
+                                   fwhm_major_lq,
+                                   fwhm_major_uq,
+                                   fwhm_minor,
+                                   fwhm_minor_lq,
+                                   fwhm_minor_uq,
+
+                                   iq_fwhm_major,
+                                   iq_fwhm_major_err,
+                                   iq_fwhm_minor,
+                                   iq_fwhm_minor_err,
+
+                                   iq_m2,
+                                   iq_m2_err,
+                                   iq_m2_lq,
+                                   iq_m2_uq,
+
+                                   iq_m2c,
+                                   iq_m2c_err,
+                                   iq_m2c_lq,
+                                   iq_m2c_uq,
+
+                                   iq_m2s,
+                                   iq_m2s_err,
+                                   iq_m2s_lq,
+                                   iq_m2s_uq,
+
+                                   iq_m3,
+                                   iq_m3_err,
+                                   iq_m3_lq,
+                                   iq_m3_uq,
+
+                                   iq_m4,
+                                   iq_m4_err,
+                                   iq_m4_lq,
+                                   iq_m4_uq,
+
+                                   dtime_detrend,
+                                   dtime_photom,
+                                   dtime_total,
+                                   dtime_script,
+                                   hostname,
+                                   n_stars,
+                                   n_psfstars,
+                                   n_iqstars,
+                                   n_extended,
+                                   n_cr,
+                                   path_base,
+                                   fault,
+                                   quality,
+				   magicked,
+				   ver_code,
+				   maskfrac_npix,
+				   maskfrac_static,
+				   maskfrac_dynamic,
+				   maskfrac_magic,
+				   maskfrac_advisory
+            )) {
+        // rollback
+        if (!psDBRollback(config->dbh)) {
+            psError(PS_ERR_UNKNOWN, false, "database error");
+        }
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+
+#if 0
+    // XXX I've decided to make the transaction cover the Exp migration as
+    // well.  Otherwise, if the last imfile in an exp is moved and the exp
+    // migration fails then the data base is left in a situation where the exp
+    // migration can't happen.
+
+    if (!videophotProcessedCompleteExp(config)) {
+        // rollback
+        if (!psDBRollback(config->dbh)) {
+            psError(PS_ERR_UNKNOWN, false, "database error");
+        }
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+#endif
+
+    if (!psDBCommit(config->dbh)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+
+    return true;
+}
+
+
+static bool processedimfileMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, NULL);
+
+    PXOPT_LOOKUP_U64(limit, config->args, "-limit", false, false);
+    PXOPT_LOOKUP_BOOL(simple, config->args, "-simple", false);
+    PXOPT_LOOKUP_BOOL(faulted, config->args, "-faulted", false);
+    PXOPT_LOOKUP_BOOL(allfiles, config->args, "-allfiles", false);
+    if (allfiles) {
+        faulted = false;
+    }
+
+    psMetadata *where = psMetadataAlloc();
+    pxvideophotGetSearchArgs (config, where); // videophotRun, videophotProcessedImfile, rawExp
+    PXOPT_COPY_S64(config->args, where, "-videophot_id", "videophotRun.videophot_id", "==");
+    PXOPT_COPY_S64(config->args, where, "-videophot_imfile_id", "videophotImfile.videophot_imfile_id", "==");
+    PXOPT_COPY_STR(config->args, where, "-class_id", "videophotProcessedImfile.class_id", "==");
+    PXOPT_COPY_STR(config->args, where, "-reduction", "videophotRun.reduction", "==");
+    pxAddLabelSearchArgs (config, where, "-label", "videophotRun.label", "LIKE");
+    pxAddLabelSearchArgs (config, where, "-data_group", "videophotRun.data_group", "LIKE");
+    PXOPT_COPY_S64(config->args, where, "-magicked", "videophotProcessedImfile.magicked", "==");
+
+    psString where2 = NULL;
+    pxmagicAddWhere(config, &where2, "videophotProcessedImfile");
+    // add cuts on ra and decl if supplied
+    if (!pxspaceAddWhere(config, &where2, "rawExp")) {
+        psError(psErrorCodeLast(), false, "pxSpaceAddWhere failed");
+        return false;
+    }
+
+    psString query = pxDataGet("videophottool_processedimfile.sql");
+    if (!query) {
+        psError(PXTOOLS_ERR_SYS, false, "failed to retreive SQL statement");
+        return false;
+    }
+
+    if (psListLength(where->list)) {
+        psString whereClause = psDBGenerateWhereConditionSQL(where, NULL);
+        psStringAppend(&query, " WHERE %s %s", whereClause, where2 ? where2 : "");
+        psFree(whereClause);
+    } else if (psMetadataLookupBool(NULL, config->args, "-all") || (faulted || where2)) {
+        psStringAppend(&query, " WHERE videophotRun.videophot_id IS NOT NULL %s", where2 ? where2 : "");
+    } else {
+        psFree(where);
+        psError(PXTOOLS_ERR_CONFIG, false, "search parameters (or -all) are required");
+        return false;
+    }
+    psFree(where);
+
+    if (faulted) {
+        // list only faulted rows
+        psStringAppend(&query, " %s", "AND videophotProcessedImfile.fault != 0");
+    } else if (!allfiles) {
+        // don't list faulted rows
+        psStringAppend(&query, " %s", "AND videophotProcessedImfile.fault = 0");
+    }
+
+    // treat limit == 0 as "no limit"
+    if (limit) {
+        psString limitString = psDBGenerateLimitSQL(limit);
+        psStringAppend(&query, " %s", limitString);
+        psFree(limitString);
+    }
+
+    if (!p_psDBRunQuery(config->dbh, query)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(query);
+        return false;
+    }
+    psFree(query);
+
+    psArray *output = p_psDBFetchResult(config->dbh);
+    if (!output) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+    if (!psArrayLength(output)) {
+        psTrace("videophottool", PS_LOG_INFO, "no rows found");
+        psFree(output);
+        return true;
+    }
+
+    // negative simple so the default is true
+    if (!ippdbPrintMetadatas(stdout, output, "videophotProcessedImfile", !simple)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print array");
+        psFree(output);
+        return false;
+    }
+
+    psFree(output);
+
+    return true;
+}
+
+static bool revertprocessedimfileMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    psMetadata *where = psMetadataAlloc();
+    pxvideophotGetSearchArgs (config, where); // videophotProcessedImfile, rawExp
+    PXOPT_COPY_S64(config->args, where, "-videophot_id", "videophotRun.videophot_id", "==");
+    PXOPT_COPY_STR(config->args, where, "-class_id", "videophotProcessedImfile.class_id", "==");
+    pxAddLabelSearchArgs (config, where, "-label", "videophotRun.label", "==");
+    PXOPT_COPY_STR(config->args, where, "-reduction", "videophotRun.reduction", "==");
+    PXOPT_COPY_S16(config->args, where, "-fault", "videophotProcessedImfile.fault", "==");
+
+    if (!psListLength(where->list)
+        && !psMetadataLookupBool(NULL, config->args, "-all")) {
+        psFree(where);
+        psError(PXTOOLS_ERR_CONFIG, false, "search parameters are required");
+        return false;
+    }
+
+    psString query = pxDataGet("videophottool_revertprocessedimfile.sql");
+    if (!query) {
+        psError(PXTOOLS_ERR_SYS, false, "failed to retreive SQL statement");
+        psFree(where);
+        return false;
+    }
+    psString query_update = pxDataGet("videophottool_revertupdatedimfile.sql");
+    if (!query_update) {
+        psError(PXTOOLS_ERR_SYS, false, "failed to retreive SQL statement");
+        psFree(where);
+        return false;
+    }
+
+
+    if (where && psListLength(where->list)) {
+        psString whereClause = psDBGenerateWhereConditionSQL(where, NULL);
+        psStringAppend(&query, " AND %s", whereClause);
+        psStringAppend(&query_update, " AND %s", whereClause);
+        psFree(whereClause);
+    }
+
+    psFree(where);
+
+    if (!p_psDBRunQuery(config->dbh, query)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(query);
+        return false;
+    }
+    psFree(query);
+    if (!p_psDBRunQuery(config->dbh, query_update)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(query_update);
+        return false;
+    }
+    psFree(query_update);
+
+    return true;
+}
+static bool revertcleanupMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    psMetadata *where = psMetadataAlloc();
+    PXOPT_COPY_S64(config->args, where, "-videophot_id", "videophotRun.videophot_id", "==");
+    pxAddLabelSearchArgs (config, where, "-label", "videophotRun.label", "LIKE");
+    pxAddLabelSearchArgs (config, where, "-data_group", "videophotRun.data_group", "LIKE");
+    PXOPT_LOOKUP_STR(state, config->args, "-state", false, false);
+
+    char* newState = NULL;
+    if (!state) {
+        state = "error_cleaned";
+    }
+    if (!strcmp(state, "error_cleaned")) {
+        newState = "goto_cleaned";
+    } else if (!strcmp(state, "error_purged")) {
+        newState = "goto_purged";
+    } else if (!strcmp(state, "error_scrubbed")) {
+        newState = "goto_scrubbed";
+    } else {
+        psError(PXTOOLS_ERR_CONFIG, true, "-state must be either error_cleaned, error_purged, or error_scrubbed");
+        return false;
+    }
+
+    if (!psListLength(where->list)) {
+        psFree(where);
+        psError(PXTOOLS_ERR_CONFIG, false, "search parameters are required");
+        return false;
+    }
+
+    psString query = pxDataGet("videophottool_revertcleanup.sql");
+    if (!query) {
+        psError(PXTOOLS_ERR_SYS, false, "failed to retreive SQL statement");
+        psFree(where);
+        return false;
+    }
+
+    if (where && psListLength(where->list)) {
+        psString whereClause = psDBGenerateWhereConditionSQL(where, NULL);
+        psStringAppend(&query, " AND %s", whereClause);
+        psFree(whereClause);
+    }
+
+    psFree(where);
+
+    if (!p_psDBRunQueryF(config->dbh, query, newState, state, state)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(query);
+        return false;
+    }
+    psFree(query);
+
+    return true;
+}
+
+static bool updateprocessedimfileMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    psMetadata *where = psMetadataAlloc();
+    PXOPT_COPY_S64(config->args, where, "-videophot_id", "videophot_id", "==");
+    PXOPT_COPY_STR(config->args, where, "-class_id", "class_id", "==");
+    PXOPT_LOOKUP_STR(state, config->args, "-set_state", false, NULL);
+    if (psListLength(where->list) == 0) {
+        psError(PS_ERR_UNKNOWN, true, "search parameters are required");
+        return false;
+    }
+
+    if (!state) {
+      PXOPT_LOOKUP_S16(fault, config->args, "-fault", true, false);
+
+      if (!pxSetFaultCode(config->dbh, "videophotProcessedImfile", where, fault)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to set set fault flag");
+        return false;
+      }
+      psFree(where);
+    }
+    else {
+      if (!pxvideophotProcessedImfileSetStateByQuery(config,where,state)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to set videophotProcessedImfile state");
+        return(false);
+      }
+    }
+
+
+    return true;
+}
+
+
+static bool blockMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    PXOPT_LOOKUP_STR(label, config->args, "-label", true, false);
+
+    if (!videophotMaskInsert(config->dbh, label)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+
+    return true;
+}
+
+
+static bool maskedMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    PXOPT_LOOKUP_U64(limit, config->args, "-limit", false, false);
+
+    psMetadata *where = psMetadataAlloc();
+
+    PXOPT_COPY_STR(config->args, where, "-label", "label", "==");
+
+    if (where->list->n < 1) {
+        psFree(where);
+        where = NULL;
+    }
+
+    PXOPT_LOOKUP_BOOL(simple, config->args, "-simple", false);
+
+    psString query = psStringCopy("SELECT * FROM videophotMask");
+
+    if (where) {
+        psString whereClause = psDBGenerateWhereSQL(where, NULL);
+        psFree(where);
+        psStringAppend(&query, " %s", whereClause);
+        psFree(whereClause);
+    }
+
+    // treat limit == 0 as "no limit"
+    if (limit) {
+        psString limitString = psDBGenerateLimitSQL(limit);
+        psStringAppend(&query, " %s", limitString);
+        psFree(limitString);
+    }
+
+    if (!p_psDBRunQuery(config->dbh, query)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(query);
+        return false;
+    }
+    psFree(query);
+
+    psArray *output = p_psDBFetchResult(config->dbh);
+    if (!output) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+    if (!psArrayLength(output)) {
+        psTrace("videophottool", PS_LOG_INFO, "no rows found");
+        psFree(output);
+        return true;
+    }
+
+    // negative simple so the default is true
+    if (!ippdbPrintMetadatas(stdout, output, "videophotMask", !simple)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print array");
+        psFree(output);
+        return false;
+    }
+
+    psFree(output);
+
+    return true;
+}
+
+// return the list of labels which are NOT blocked
+static bool unmaskedMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    PXOPT_LOOKUP_BOOL(simple, config->args, "-simple", false);
+    PXOPT_LOOKUP_U64(limit, config->args, "-limit", false, false);
+
+    psMetadata *where = psMetadataAlloc();
+
+    PXOPT_COPY_STR(config->args, where, "-label", "label", "==");
+
+    if (where->list->n < 1) {
+        psFree(where);
+        where = NULL;
+    }
+
+    psString query = pxDataGet("videophottool_unmasked.sql");
+    if (!query) {
+        psError(PXTOOLS_ERR_SYS, false, "failed to retreive SQL statement");
+        return false;
+    }
+
+    if (where) {
+        psString whereClause = psDBGenerateWhereSQL(where, "videophotUnmask");
+        psFree(where);
+        psStringAppend(&query, " %s", whereClause);
+        psFree(whereClause);
+    }
+
+    // treat limit == 0 as "no limit"
+    if (limit) {
+        psString limitString = psDBGenerateLimitSQL(limit);
+        psStringAppend(&query, " %s", limitString);
+        psFree(limitString);
+    }
+
+    if (!p_psDBRunQuery(config->dbh, query)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(query);
+        return false;
+    }
+    psFree(query);
+
+    psArray *output = p_psDBFetchResult(config->dbh);
+    if (!output) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+    if (!psArrayLength(output)) {
+        psTrace("videophottool", PS_LOG_INFO, "no rows found");
+        psFree(output);
+        return true;
+    }
+
+    // negative simple so the default is true
+    if (!ippdbPrintMetadatas(stdout, output, "videophotUnmask", !simple)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print array");
+        psFree(output);
+        return false;
+    }
+
+    psFree(output);
+
+    return true;
+}
+
+
+static bool unblockMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    PXOPT_LOOKUP_STR(label, config->args, "-label", true, false);
+
+    char *query = "DELETE FROM videophotMask WHERE label = '%s'";
+
+    if (!p_psDBRunQueryF(config->dbh, query, label)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+
+    return true;
+}
+
+
+static bool pendingcleanuprunMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, NULL);
+
+    PXOPT_LOOKUP_U64(limit, config->args, "-limit", false, false);
+    PXOPT_LOOKUP_BOOL(simple, config->args, "-simple", false);
+
+    psMetadata *where = psMetadataAlloc();
+    pxAddLabelSearchArgs (config, where, "-label", "label", "==");
+
+    psString query = pxDataGet("videophottool_pendingcleanuprun.sql");
+    if (!query) {
+        psError(PXTOOLS_ERR_SYS, false, "failed to retreive SQL statement");
+        return false;
+    }
+
+    if (where && psListLength(where->list)) {
+        psString whereClause = psDBGenerateWhereConditionSQL(where, NULL);
+        psStringAppend(&query, " AND %s", whereClause);
+        psFree(whereClause);
+    }
+    psFree(where);
+
+    // treat limit == 0 as "no limit"
+    if (limit) {
+        psString limitString = psDBGenerateLimitSQL(limit);
+        psStringAppend(&query, " %s", limitString);
+        psFree(limitString);
+    }
+
+    if (!p_psDBRunQuery(config->dbh, query)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(query);
+        return false;
+    }
+    psFree(query);
+
+    psArray *output = p_psDBFetchResult(config->dbh);
+    if (!output) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+    if (!psArrayLength(output)) {
+        psTrace("videophottool", PS_LOG_INFO, "no rows found");
+        psFree(output);
+        return true;
+    }
+
+    // negative simple so the default is true
+    if (!ippdbPrintMetadatas(stdout, output, "videophotPendingCleanupRun", !simple)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print array");
+        psFree(output);
+        return false;
+    }
+
+    psFree(output);
+
+    return true;
+}
+
+static bool pendingcleanupimfileMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, NULL);
+
+    PXOPT_LOOKUP_S64(videophot_id, config->args, "-videophot_id", false, false);
+    PXOPT_LOOKUP_U64(limit, config->args, "-limit", false, false);
+    PXOPT_LOOKUP_BOOL(simple, config->args, "-simple", false);
+
+    psMetadata *where = psMetadataAlloc();
+    if (videophot_id) {
+        PXOPT_COPY_S64(config->args, where, "-videophot_id", "videophot_id", "==");
+    }
+    pxAddLabelSearchArgs (config, where, "-label", "label", "==");
+
+    psString query = pxDataGet("videophottool_pendingcleanupimfile.sql");
+    if (!query) {
+        psError(PXTOOLS_ERR_SYS, false, "failed to retreive SQL statement");
+        return false;
+    }
+
+    if (where && psListLength(where->list)) {
+        psString whereClause = psDBGenerateWhereConditionSQL(where, NULL);
+        psStringAppend(&query, " AND %s", whereClause);
+        psFree(whereClause);
+    }
+    psFree(where);
+
+    // treat limit == 0 as "no limit"
+    if (limit) {
+        psString limitString = psDBGenerateLimitSQL(limit);
+        psStringAppend(&query, " %s", limitString);
+        psFree(limitString);
+    }
+
+    if (!p_psDBRunQuery(config->dbh, query)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(query);
+        return false;
+    }
+    psFree(query);
+
+    psArray *output = p_psDBFetchResult(config->dbh);
+    if (!output) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+    if (!psArrayLength(output)) {
+        psTrace("videophottool", PS_LOG_INFO, "no rows found");
+        psFree(output);
+        return true;
+    }
+
+    // negative simple so the default is true
+    if (!ippdbPrintMetadatas(stdout, output, "videophotPendingCleanupImfile", !simple)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print array");
+        psFree(output);
+        return false;
+    }
+
+    psFree(output);
+
+    return true;
+}
+
+
+static bool donecleanupMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, NULL);
+
+    PXOPT_LOOKUP_U64(limit, config->args, "-limit", false, false);
+    PXOPT_LOOKUP_BOOL(simple, config->args, "-simple", false);
+
+    psMetadata *where = psMetadataAlloc();
+    PXOPT_COPY_STR(config->args, where, "-label", "label", "==");
+
+    psString query = pxDataGet("videophottool_donecleanup.sql");
+    if (!query) {
+        psError(PXTOOLS_ERR_SYS, false, "failed to retreive SQL statement");
+        return false;
+    }
+
+    if (where && psListLength(where->list)) {
+        psString whereClause = psDBGenerateWhereConditionSQL(where, NULL);
+        psStringAppend(&query, " AND %s", whereClause);
+        psFree(whereClause);
+    }
+    psFree(where);
+
+    // treat limit == 0 as "no limit"
+    if (limit) {
+        psString limitString = psDBGenerateLimitSQL(limit);
+        psStringAppend(&query, " %s", limitString);
+        psFree(limitString);
+    }
+
+    if (!p_psDBRunQuery(config->dbh, query)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(query);
+        return false;
+    }
+    psFree(query);
+
+    psArray *output = p_psDBFetchResult(config->dbh);
+    if (!output) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+    if (!psArrayLength(output)) {
+        psTrace("videophottool", PS_LOG_INFO, "no rows found");
+        psFree(output);
+        return true;
+    }
+
+    // negative simple so the default is true
+    if (!ippdbPrintMetadatas(stdout, output, "videophotDoneCleanup", !simple)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print array");
+        psFree(output);
+        return false;
+    }
+
+    psFree(output);
+
+    return true;
+}
+
+static bool runMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, NULL);
+
+    PXOPT_LOOKUP_STR(state, config->args, "-state", true, false);
+    PXOPT_LOOKUP_U64(limit, config->args, "-limit", false, false);
+    PXOPT_LOOKUP_BOOL(simple, config->args, "-simple", false);
+
+    // make sure that the state string is valid
+    if (!pxIsValidState(state)) {
+        psError(PXTOOLS_ERR_CONFIG, false, "%s is not a valid state", state);
+        return false;
+    }
+
+    psMetadata *where = psMetadataAlloc();
+    pxvideophotGetSearchArgs (config, where); // videophotRun, videophotProcessedImfile, rawExp
+    PXOPT_COPY_S64(config->args, where, "-videophot_id", "videophotRun.videophot_id", "==");
+    PXOPT_COPY_STR(config->args, where, "-reduction", "videophotRun.reduction", "==");
+    pxAddLabelSearchArgs (config, where, "-label", "label", "==");
+    PXOPT_COPY_STR(config->args, where, "-state", "state", "==");
+
+    psString query = pxDataGet("videophottool_run.sql");
+    if (!query) {
+        psError(PXTOOLS_ERR_SYS, false, "failed to retreive SQL statement");
+        return false;
+    }
+
+    if (where && psListLength(where->list)) {
+        psString whereClause = psDBGenerateWhereSQL(where, NULL);
+        psStringAppend(&query, " %s", whereClause);
+        psFree(whereClause);
+    }
+    psFree(where);
+
+    // treat limit == 0 as "no limit"
+    if (limit) {
+        psString limitString = psDBGenerateLimitSQL(limit);
+        psStringAppend(&query, " %s", limitString);
+        psFree(limitString);
+    }
+
+    if (!p_psDBRunQuery(config->dbh, query)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(query);
+        return false;
+    }
+    psFree(query);
+
+    psArray *output = p_psDBFetchResult(config->dbh);
+    if (!output) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+    if (!psArrayLength(output)) {
+        psTrace("videophottool", PS_LOG_INFO, "no rows found");
+        psFree(output);
+        return true;
+    }
+
+    // negative simple so the default is true
+    if (!ippdbPrintMetadatas(stdout, output, "videophotDoneCleanup", !simple)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print array");
+        psFree(output);
+        return false;
+    }
+
+    psFree(output);
+
+    return true;
+}
+
+
+static bool advanceexpMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    PXOPT_LOOKUP_U64(limit, config->args, "-limit", false, false);
+
+    psMetadata *where = psMetadataAlloc();
+    pxAddLabelSearchArgs (config, where, "-label", "label", "==");
+
+    // look for completed videophotPendingExp
+    // migrate them to videophotProccessedExp & camPendingExp
+    psString query = pxDataGet("videophottool_completely_processed_exp.sql");
+    if (!query) {
+        psError(PXTOOLS_ERR_SYS, false, "failed to retreive SQL statement");
+        return false;
+    }
+
+    if (where && psListLength(where->list)) {
+        psString whereClause = psDBGenerateWhereSQL(where, NULL);
+        psStringAppend(&query, " %s", whereClause);
+        psFree(whereClause);
+    }
+    psFree(where);
+
+    // treat limit == 0 as "no limit"
+    if (limit) {
+        psString limitString = psDBGenerateLimitSQL(limit);
+        psStringAppend(&query, " %s", limitString);
+        psFree(limitString);
+    }
+
+    if (!p_psDBRunQuery(config->dbh, query)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(query);
+        return false;
+    }
+    psFree(query);
+
+    psArray *output = p_psDBFetchResult(config->dbh);
+    if (!output) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+    if (!psArrayLength(output)) {
+        psTrace("videophottool", PS_LOG_INFO, "no rows found");
+        psFree(output);
+        return true;
+    }
+
+    for (long i = 0; i < psArrayLength(output); i++) {
+        psMetadata *row = output->data[i];
+
+	psS64 videophot_id = psMetadataLookupS64(NULL,row,"videophot_id");
+	
+	psString software_ver = NULL;
+	psS64 maskfrac_npix = 0;
+	psF32 maskfrac_static = 0;
+	psF32 maskfrac_dynamic = 0;
+	psF32 maskfrac_magic = 0;
+	psF32 maskfrac_advisory = 0;
+
+	// Calculate run level masking and software state
+	if (!pxCoalesceRunStatus(config,"videophottool_coalesce_run.sql",videophot_id,
+				 &software_ver,&maskfrac_npix,
+				 &maskfrac_static,&maskfrac_dynamic,
+				 &maskfrac_magic,&maskfrac_advisory)) {
+	  psError(PS_ERR_UNKNOWN, false, "failed to generate run level statistics");
+	  psFree(output);
+	  if (!psDBRollback(config->dbh)) {
+	    psError(PS_ERR_UNKNOWN, false, "database error");
+	  }
+	  return(false);
+	}
+	psMetadataAddStr(row, PS_LIST_TAIL, "software_ver",  0, "Software version", software_ver);
+	psMetadataAddS32(row, PS_LIST_TAIL, "maskfrac_npix",  0, "Number of pixels used for mask stats", (psS32) maskfrac_npix);
+	psMetadataAddF32(row, PS_LIST_TAIL, "maskfrac_static",  0, "Fraction of static masked pixels", maskfrac_static);
+	psMetadataAddF32(row, PS_LIST_TAIL, "maskfrac_dynamic",  0, "Fraction of dynamic masked pixels", maskfrac_dynamic);
+	psMetadataAddF32(row, PS_LIST_TAIL, "maskfrac_magic",  0, "Fraction of magic masked pixels", maskfrac_magic);
+	psMetadataAddF32(row, PS_LIST_TAIL, "maskfrac_advisory",  0, "Fraction of advisory pixels", maskfrac_advisory);
+/* 	psWarning("ADVANCE %ld %s %d %f %f %f %f\n",videophot_id,software_ver,maskfrac_npix,maskfrac_static,maskfrac_dynamic,maskfrac_magic,maskfrac_advisory); */
+        videophotRunRow *videophotRun = videophotRunObjectFromMetadata(row);
+        if (!psDBTransaction(config->dbh)) {
+            psError(PS_ERR_UNKNOWN, false, "database error");
+            return false;
+        }
+
+	// Set videophotRun.software_ver to the appropriate value
+	if (software_ver) {
+	  if (!pxSetRunSoftware(config, "videophotRun", "videophot_id", videophot_id, software_ver)) {
+	    psError(PS_ERR_UNKNOWN, false, "failed to set videophotRun.software_ver for videophot_id: %" PRId64,
+		    videophot_id);
+	    psFree(output);
+	    if (!psDBRollback(config->dbh)) {
+	      psError(PS_ERR_UNKNOWN, false, "database error");
+	    }
+	    return(false);
+	  }
+	}
+	// Set videophotRun.maskfrac* to the appropriate values.
+	if (maskfrac_npix) {
+	  if (!pxSetRunMaskfrac(config, "videophotRun", "videophot_id",videophot_id, maskfrac_npix, maskfrac_static,
+				maskfrac_dynamic, maskfrac_magic, maskfrac_advisory)) {
+	    psError(PS_ERR_UNKNOWN, false, "failed to set videophotRun.software_ver for videophot_id: %" PRId64,
+		    videophot_id);
+	    psFree(output);
+	    if (!psDBRollback(config->dbh)) {
+	      psError(PS_ERR_UNKNOWN, false, "database error");
+	    }
+	    return(false);
+	  }
+	}
+        // set videophotRun.state to 'stop' and update the magicked state
+        if (!pxvideophotRunSetState(config, videophotRun->videophot_id, "full", videophotRun->magicked)) {
+            psError(PS_ERR_UNKNOWN, false, "failed to change videophotRun.state for videophot_id: %" PRId64, videophotRun->videophot_id);
+            psFree(videophotRun);
+            psFree(output);
+            return false;
+        }
+
+        // should we stop here or proceed on to the cam stage?
+        // NULL for end_stage means go as far as possible
+        if (videophotRun->end_stage && psStrcasestr(videophotRun->end_stage, "videophot")) {
+            if (!psDBCommit(config->dbh)) {
+                psError(PS_ERR_UNKNOWN, false, "database error");
+                return false;
+            }
+
+            psFree(videophotRun);
+            continue;
+        }
+        // else continue on...
+
+        // camQueueVideophotID() can only be run after videophotRun.state has been set to
+        // stop
+        if (!pxcamQueueByVideophotID(config,
+				videophotRun->videophot_id,
+				videophotRun->workdir,
+				videophotRun->label,
+				videophotRun->data_group,
+				videophotRun->dist_group,
+				videophotRun->reduction,
+				videophotRun->expgroup,
+				videophotRun->dvodb,
+				videophotRun->tess_id,
+				videophotRun->end_stage,
+				videophotRun->magicked,
+				NULL    // note does not propagate
+        )) {
+           if (!psDBRollback(config->dbh)) {
+                psError(PS_ERR_UNKNOWN, false, "database error");
+            }
+            psError(PS_ERR_UNKNOWN, false, "failed to queue camPendingExp");
+            psFree(videophotRun);
+            psFree(output);
+            return false;
+        }
+        if (!psDBCommit(config->dbh)) {
+            psError(PS_ERR_UNKNOWN, false, "database error");
+            return false;
+        }
+        psFree(videophotRun);
+    }
+
+    psFree(output);
+
+    return true;
+}
+
+// update videophotProcessedImfile.data_state to given value.
+// afterwards, if all imfiles in the exposure have the new state, update the state for the exposure as well
+// shared code for the modes -tocleanedimfile -tofullimfile -topurgedimfile
+
+static bool change_imfile_data_state(pxConfig *config, psString data_state, psString run_state)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    // videophot_id, class_id are required
+    PXOPT_LOOKUP_S64(videophot_id, config->args, "-videophot_id", true, false);
+    PXOPT_LOOKUP_STR(class_id, config->args, "-class_id", true, false);
+
+    psString query = pxDataGet("videophottool_change_imfile_data_state.sql");
+
+    if (!psDBTransaction(config->dbh)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+
+    psString set_magicked_imfile = psStringCopy("");
+    psString set_magicked_run = psStringCopy("");
+    if (!strcmp(data_state, "full")) {
+        // if (videophotProcessedImfile.magicked < 0 and rawImfile.magicked = 0) leave magicked unchanged. This will
+        // block warp processing until destreaking has been done
+        // otherwise copy magicked from the rawImfile
+        // Same thing for videophotRun/rawExp
+        psStringAppend(&set_magicked_imfile, "\n , videophotProcessedImfile.magicked = IF((videophotProcessedImfile.magicked < 0"
+                                      " AND rawImfile.magicked = 0), videophotProcessedImfile.magicked, rawImfile.magicked)");
+        psStringAppend(&set_magicked_run, "\n , videophotRun.magicked = IF((videophotRun.magicked < 0 AND rawExp.magicked = 0), "
+                                      " videophotRun.magicked, rawExp.magicked)");
+
+    } else if (!strcmp(data_state, "cleaned") || !strcmp(data_state, "purged")) {
+        // if magicked is non-zero set it to -1
+        // Once one imfile has been cleaned, the videophotRun is no longer 'magicked'
+        psStringAppend(&set_magicked_imfile, "\n, videophotProcessedImfile.magicked = IF(videophotProcessedImfile.magicked = 0, 0, -1),"
+                                             " videophotRun.magicked = IF(videophotRun.magicked = 0, 0, -1)");
+    }
+
+    if (!p_psDBRunQueryF(config->dbh, query, data_state, set_magicked_imfile, videophot_id, class_id)) {
+        psFree(query);
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        // rollback
+        if (!psDBRollback(config->dbh)) {
+            psError(PS_ERR_UNKNOWN, false, "database error");
+        }
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+    psFree(query);
+    psFree(set_magicked_imfile);
+    psFree(set_magicked_run);
+    if (psDBAffectedRows(config->dbh) < 1) {
+        psError(PS_ERR_UNKNOWN, false, "should have affected atleast 1 row");
+        return false;
+    }
+
+    query = pxDataGet("videophottool_change_exp_state.sql");
+    if (!p_psDBRunQueryF(config->dbh, query, data_state, set_magicked_run, videophot_id, data_state)) {
+        psFree(query);
+        // rollback
+        if (!psDBRollback(config->dbh)) {
+            psError(PS_ERR_UNKNOWN, false, "database error");
+        }
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+    psFree(query);
+
+    if (!psDBCommit(config->dbh)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+
+    return true;
+}
+static bool tocleanedimfileMode(pxConfig *config)
+{
+    return change_imfile_data_state(config, "cleaned", "goto_cleaned");
+}
+static bool tofullimfileMode(pxConfig *config)
+{
+    return change_imfile_data_state(config, "full", "update");
+}
+static bool topurgedimfileMode(pxConfig *config)
+{
+    return change_imfile_data_state(config, "purged", "goto_purged");
+}
+static bool toscrubbedimfileMode(pxConfig *config)
+{
+  return change_imfile_data_state(config, "scrubbed", "goto_scrubbed");
+}
+bool exportrunMode(pxConfig *config)
+{
+  typedef struct ExportTable {
+    char tableName[80];
+    char sqlFilename[80];
+  } ExportTable;
+
+  int numExportTables = 3;
+
+  PS_ASSERT_PTR_NON_NULL(config, NULL);
+
+  PXOPT_LOOKUP_S64(dummy, config->args, "-videophot_id", true,  false);
+  PXOPT_LOOKUP_STR(outfile, config->args, "-outfile", true,  false);
+  PXOPT_LOOKUP_U64(limit,   config->args, "-limit",   false, false);
+  PXOPT_LOOKUP_BOOL(clean, config->args, "-clean", false);
+
+
+  FILE *f = fopen (outfile, "w");
+  if (f == NULL) {
+    psError(PS_ERR_UNKNOWN, false, "failed to open output file");
+    return false;
+  }
+
+  if (!pxExportVersion(config, f)) {
+    psError(PS_ERR_UNKNOWN, false, "failed to write dbversion output file");
+    return false;
+  }
+
+  psMetadata *where = psMetadataAlloc();
+  PXOPT_COPY_S64(config->args, where, "-videophot_id", "videophot_id", "==");
+
+  ExportTable tables [] = {
+    {"videophotRun", "videophottool_export_run.sql"},
+    {"videophotImfile", "videophottool_export_imfile.sql"},
+    {"videophotProcessedImfile", "videophottool_export_processed_imfile.sql"},
+  };
+
+
+  for (int i=0; i < numExportTables; i++) {
+    psString query = pxDataGet(tables[i].sqlFilename);
+    if (!query) {
+      psError(PXTOOLS_ERR_SYS, false, "failed to retreive SQL statement");
+      return false;
+    }
+
+    if (where && psListLength(where->list)) {
+      psString whereClause = psDBGenerateWhereSQL(where, NULL);
+      psStringAppend(&query, " %s", whereClause);
+      psFree(whereClause);
+    }
+
+    // treat limit == 0 as "no limit"
+    if (limit) {
+      psString limitString = psDBGenerateLimitSQL(limit);
+      psStringAppend(&query, " %s", limitString);
+      psFree(limitString);
+    }
+
+    if (!p_psDBRunQuery(config->dbh, query)) {
+      psError(PS_ERR_UNKNOWN, false, "database error");
+      psFree(query);
+      return false;
+    }
+    psFree(query);
+
+    psArray *output = p_psDBFetchResult(config->dbh);
+    if (!output) {
+      psError(PS_ERR_UNKNOWN, false, "database error");
+      return false;
+    }
+    if (!psArrayLength(output)) {
+      psTrace("videophottool", PS_LOG_INFO, "no rows found");
+      psFree(output);
+      return false;
+    }
+
+    if (clean) {
+        bool success = true;
+        if (!strcmp(tables[i].tableName, "videophotRun")) {
+            success = pxSetStateCleaned("videophotRun", "state", output);
+        } else if (!strcmp(tables[i].tableName, "videophotProcessedImfile")) {
+            success = pxSetStateCleaned("videophotProcessedImfile", "data_state", output);
+        }
+        if (!success) {
+            psFree(output);
+            psError(PS_ERR_UNKNOWN, false, "pxSetStateClean failed for table %s",  tables[i].tableName);
+            return false;
+        }
+    }
+
+    // we must write the export table in non-simple (true) format
+    if (!ippdbPrintMetadatas(f, output, tables[i].tableName, true)) {
+      psError(PS_ERR_UNKNOWN, false, "failed to print array");
+      psFree(output);
+      return false;
+    }
+    psFree(output);
+  }
+
+  fclose (f);
+
+  return true;
+}
+
+bool importrunMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, NULL);
+
+    PXOPT_LOOKUP_STR(infile, config->args, "-infile", true, false);
+    unsigned int nFail = 0;               // Number of failed lines
+    psMetadata *input = psMetadataConfigRead(NULL, &nFail, infile, false);
+    if (nFail) {
+        psError(PS_ERR_IO, false, "%d failed lines in input", nFail);
+        psFree(input);
+        return false;
+    }
+
+    psVector *identifiers = psVectorAllocEmpty(16, PS_TYPE_U64); // Identifiers inserted
+
+    psMetadataIterator *iter = psMetadataIteratorAlloc(input, PS_LIST_HEAD, NULL);       // Iterator
+
+    if (!pxCheckImportVersion(config, input)) {
+        psError(PS_ERR_UNKNOWN, false, "pxCheckImportVersion failed");
+        return false;
+    }
+    // first item is the dbversion, skip it
+    psMetadataItem *dbversion =  psMetadataGetAndIncrement(iter);
+    (void) dbversion;
+
+    if (!psDBTransaction(config->dbh)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+
+    psMetadataItem *item;               // Item from iteration
+    while ((item = psMetadataGetAndIncrement(iter))) {
+        PXMIRROR_PRIMARY(item, "videophotRun", videophotRunRow, videophotRunObjectFromMetadata, identifiers, videophot_id,
+                         videophotRunInsertObject, config->dbh,
+                         { psFree(iter); psFree(identifiers); psFree(input); });
+
+        PXMIRROR_OTHER(item, "videophotImfile", videophotImfileRow, videophotImfileObjectFromMetadata, identifiers, videophot_id,
+                       videophotImfileInsertObject, config->dbh,
+                       { psFree(iter); psFree(identifiers); psFree(input); });
+
+        PXMIRROR_OTHER(item, "videophotProcessedImfile", videophotProcessedImfileRow,
+                       videophotProcessedImfileObjectFromMetadata, identifiers, videophot_id,
+                       videophotProcessedImfileInsertObject, config->dbh,
+                       { psFree(iter); psFree(identifiers); psFree(input); });
+    }
+    psFree(iter);
+    psFree(input);
+
+    if (!psDBCommit(config->dbh)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+
+    psLogMsg("videophottool", PS_LOG_INFO, "%ld videophotRuns added", identifiers->n);
+    psFree(identifiers);
+
+    return true;
+}
+
+static bool runstateMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    psMetadata *where = psMetadataAlloc();
+    PXOPT_COPY_S64(config->args, where, "-videophot_id",    "videophotRun.videophot_id", "==");
+    PXOPT_COPY_S64(config->args, where, "-exp_id",     "rawExp.exp_id", "==");
+    PXOPT_COPY_STR(config->args, where, "-exp_name",   "rawExp.exp_name", "==");
+    pxAddLabelSearchArgs (config, where, "-label",     "videophotRun.label", "LIKE");
+
+//    PXOPT_LOOKUP_BOOL(all, config->args, "-all", false);
+    PXOPT_LOOKUP_BOOL(no_magic, config->args, "-no_magic", false);
+
+    PXOPT_LOOKUP_U64(limit, config->args, "-limit", false, false);
+    PXOPT_LOOKUP_BOOL(simple, config->args, "-simple", false);
+
+    psString query = pxDataGet("videophottool_runstate.sql");
+    if (!query) {
+        psError(PXTOOLS_ERR_SYS, false, "failed to retreive SQL statement");
+        return false;
+    }
+
+    if (psListLength(where->list)) {
+        psString whereClause = psDBGenerateWhereConditionSQL(where, NULL);
+        psStringAppend(&query, " WHERE %s", whereClause);
+        psFree(whereClause);
+    } else {
+        psError(PXTOOLS_ERR_CONFIG, true, "search parameters or -all are required");
+        return false;
+    }
+    psFree(where);
+
+    // treat limit == 0 as "no limit"
+    if (limit) {
+        psString limitString = psDBGenerateLimitSQL(limit);
+        psStringAppend(&query, " %s", limitString);
+        psFree(limitString);
+    }
+
+    if (!p_psDBRunQuery(config->dbh, query)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(query);
+        return false;
+    }
+    psFree(query);
+
+    psArray *output = p_psDBFetchResult(config->dbh);
+    if (!output) {
+        psErrorCode err = psErrorCodeLast();
+        switch (err) {
+            case PS_ERR_DB_CLIENT:
+                psError(PXTOOLS_ERR_SYS, false, "database error");
+            case PS_ERR_DB_SERVER:
+                psError(PXTOOLS_ERR_PROG, false, "database error");
+            default:
+                psError(PXTOOLS_ERR_PROG, false, "unknown error");
+        }
+
+        return false;
+    }
+    if (!psArrayLength(output)) {
+        psTrace("videophottool", PS_LOG_INFO, "no rows found");
+        psFree(output);
+        return true;
+    }
+
+    if (psArrayLength(output)) {
+        // negative simple so the default is true
+        if (!ippdbPrintMetadatas(stdout, output, "videophotRunState", !simple)) {
+            psError(PS_ERR_UNKNOWN, false, "failed to print array");
+            psFree(output);
+            return false;
+        }
+    }
+
+    psFree(output);
+
+    return true;
+}
+
+// a very specfic function to queue a cleaned videophotProcessedImfile to be updated
+static bool setimfiletoupdateMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, NULL);
+
+    PXOPT_LOOKUP_S64(videophot_id, config->args, "-videophot_id", true, false);
+    PXOPT_LOOKUP_STR(class_id, config->args, "-class_id", false, false);
+    PXOPT_LOOKUP_STR(label, config->args, "-set_label", false, false);
+
+    psString query = pxDataGet("videophottool_setimfiletoupdate.sql");
+    if (!query) {
+        psError(PXTOOLS_ERR_SYS, false, "failed to retreive SQL statement");
+        return false;
+    }
+
+    psString setHook = psStringCopy("");
+    if (label) {
+        psStringAppend(&setHook, "\n , videophotRun.label = '%s'", label);
+    }
+
+    if (class_id) {
+        psStringAppend(&query, " AND (videophotProcessedImfile.class_id = '%s')", class_id);
+    }
+
+    if (!p_psDBRunQueryF(config->dbh, query, setHook, videophot_id)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+
+    psFree(setHook);
+    psFree(query);
+
+    return true;
+}
+
+static bool listrunMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, NULL);
+
+    PXOPT_LOOKUP_U64(limit, config->args, "-limit", false, false);
+    PXOPT_LOOKUP_BOOL(simple, config->args, "-simple", false);
+    PXOPT_LOOKUP_BOOL(pstamp_order, config->args, "-pstamp_order", false);
+
+    psMetadata *where = psMetadataAlloc();
+    pxvideophotGetSearchArgs (config, where); // videophotRun, videophotProcessedImfile, rawExp
+    PXOPT_COPY_S64(config->args, where, "-videophot_id", "videophotRun.videophot_id", "==");
+    PXOPT_COPY_STR(config->args, where, "-reduction", "videophotRun.reduction", "==");
+    PXOPT_COPY_STR(config->args, where, "-state", "videophotRun.state", "==");
+    pxAddLabelSearchArgs (config, where, "-label", "videophotRun.label", "LIKE");
+    pxAddLabelSearchArgs (config, where, "-data_group", "videophotRun.data_group", "LIKE");
+    pxAddLabelSearchArgs (config, where, "-dist_group", "videophotRun.dist_group", "LIKE");
+    PXOPT_COPY_S64(config->args, where, "-magicked", "videophotRun.magicked", "==");
+
+    psString where2 = NULL;
+    pxmagicAddWhere(config, &where2, "videophotRun");
+    // add cuts on ra and decl if supplied
+    if (!pxspaceAddWhere(config, &where2, "rawExp")) {
+        psError(psErrorCodeLast(), false, "pxSpaceAddWhere failed");
+        return false;
+    }
+
+    psString query = pxDataGet("videophottool_listrun.sql");
+    if (!query) {
+        psError(PXTOOLS_ERR_SYS, false, "failed to retreive SQL statement");
+        return false;
+    }
+
+    if (psListLength(where->list)) {
+        psString whereClause = psDBGenerateWhereConditionSQL(where, NULL);
+        psStringAppend(&query, " WHERE %s %s", whereClause, where2 ? where2 : "");
+        psFree(whereClause);
+    } else if (psMetadataLookupBool(NULL, config->args, "-all") || where2) {
+        psStringAppend(&query, " WHERE videophotRun.videophot_id IS NOT NULL %s", where2 ? where2 : "");
+    } else {
+        psFree(where);
+        psError(PXTOOLS_ERR_CONFIG, false, "search parameters (or -all) are required");
+        return false;
+    }
+    psFree(where);
+
+    if (pstamp_order) {
+        // put runs in order of exposure id with newest videophot Runs first
+        // The postage stamp parser depends on this behavior
+        psStringAppend(&query, "\nORDER by exp_id, videophot_id DESC");
+    }
+
+    // treat limit == 0 as "no limit"
+    if (limit) {
+        psString limitString = psDBGenerateLimitSQL(limit);
+        psStringAppend(&query, " %s", limitString);
+        psFree(limitString);
+    }
+
+    if (!p_psDBRunQuery(config->dbh, query)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(query);
+        return false;
+    }
+    psFree(query);
+
+    psArray *output = p_psDBFetchResult(config->dbh);
+    if (!output) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+    if (!psArrayLength(output)) {
+        psTrace("videophottool", PS_LOG_INFO, "no rows found");
+        psFree(output);
+        return true;
+    }
+
+    // negative simple so the default is true
+    if (!ippdbPrintMetadatas(stdout, output, "videophotRun", !simple)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print array");
+        psFree(output);
+        return false;
+    }
+
+    psFree(output);
+
+    return true;
+}
Index: /branches/haf_branches/videophot2010/ippTools/src/videophottool.h
===================================================================
--- /branches/haf_branches/videophot2010/ippTools/src/videophottool.h	(revision 29945)
+++ /branches/haf_branches/videophot2010/ippTools/src/videophottool.h	(revision 29945)
@@ -0,0 +1,59 @@
+/*
+ * videophottool.h
+ *
+ * Copyright (C) 2006-2008  Joshua Hoblitt
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * program; see the file COPYING. If not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef VIDEOPHOTTOOL_H
+#define VIDEOPHOTTOOL_H 1
+
+#include "pxtools.h"
+
+typedef enum {
+    VIDEOPHOTTOOL_MODE_NONE      = 0x0,
+    VIDEOPHOTTOOL_MODE_DEFINEBYQUERY,
+    VIDEOPHOTTOOL_MODE_DEFINECOPY,
+    VIDEOPHOTTOOL_MODE_UPDATERUN,
+    VIDEOPHOTTOOL_MODE_PENDINGIMFILE,
+    VIDEOPHOTTOOL_MODE_ADDPROCESSEDIMFILE,
+    VIDEOPHOTTOOL_MODE_PROCESSEDIMFILE,
+    VIDEOPHOTTOOL_MODE_REVERTPROCESSEDIMFILE,
+    VIDEOPHOTTOOL_MODE_UPDATEPROCESSEDIMFILE,
+    VIDEOPHOTTOOL_MODE_SETIMFILETOUPDATE,
+    VIDEOPHOTTOOL_MODE_ADVANCEEXP,
+    VIDEOPHOTTOOL_MODE_BLOCK,
+    VIDEOPHOTTOOL_MODE_MASKED,
+    VIDEOPHOTTOOL_MODE_UNMASKED,
+    VIDEOPHOTTOOL_MODE_UNBLOCK,
+    VIDEOPHOTTOOL_MODE_RETRYPROCESSEDIMFILE,
+    VIDEOPHOTTOOL_MODE_PENDINGCLEANUPRUN,
+    VIDEOPHOTTOOL_MODE_PENDINGCLEANUPIMFILE,
+    VIDEOPHOTTOOL_MODE_REVERTCLEANUP,
+    VIDEOPHOTTOOL_MODE_DONECLEANUP,
+    VIDEOPHOTTOOL_MODE_RUN,
+    VIDEOPHOTTOOL_MODE_TOCLEANEDIMFILE,
+    VIDEOPHOTTOOL_MODE_TOFULLIMFILE,
+    VIDEOPHOTTOOL_MODE_TOPURGEDIMFILE,
+    VIDEOPHOTTOOL_MODE_TOSCRUBBEDIMFILE,
+    VIDEOPHOTTOOL_MODE_EXPORTRUN,
+    VIDEOPHOTTOOL_MODE_IMPORTRUN,
+    VIDEOPHOTTOOL_MODE_RUNSTATE,
+    VIDEOPHOTTOOL_MODE_LISTRUN
+} videophottoolMode;
+
+pxConfig *videophottoolConfig(pxConfig *config, int argc, char **argv);
+
+#endif // VIDEOPHOTTOOL_H
Index: /branches/haf_branches/videophot2010/ippTools/src/videophottoolConfig.c
===================================================================
--- /branches/haf_branches/videophot2010/ippTools/src/videophottoolConfig.c	(revision 29945)
+++ /branches/haf_branches/videophot2010/ippTools/src/videophottoolConfig.c	(revision 29945)
@@ -0,0 +1,411 @@
+/*
+ * videophottoolConfig.c
+ *
+ * Copyright (C) 2006-2008  Joshua Hoblitt
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * program; see the file COPYING. If not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdint.h>
+
+#include <psmodules.h>
+
+#include "pxtools.h"
+#include "videophottool.h"
+
+pxConfig *videophottoolConfig(pxConfig *config, int argc, char **argv)
+{
+    if (!config) {
+        config = pxConfigAlloc();
+    }
+
+    pmConfigReadParamsSet(false);
+
+    config->modules = pmConfigRead(&argc, argv, NULL);
+    if (! config->modules) {
+        psError(psErrorCodeLast(), false, "Can't find site configuration");
+        psFree(config);
+        return NULL;
+    }
+
+    // -definebyquery
+    psMetadata *definebyqueryArgs = psMetadataAlloc();
+    pxvideophotSetSearchArgs (definebyqueryArgs);
+    psMetadataAddStr(definebyqueryArgs, PS_LIST_TAIL, "-label", PS_META_DUPLICATE_OK, "search by rawExp label (LIKE comparison)", NULL);
+
+    psMetadataAddStr(definebyqueryArgs, PS_LIST_TAIL, "-exp_type",          PS_META_REPLACE, "search by exp_type", "object");
+
+    psMetadataAddStr(definebyqueryArgs, PS_LIST_TAIL, "-set_workdir",  0,            "define workdir (required)", NULL);
+    psMetadataAddStr(definebyqueryArgs, PS_LIST_TAIL, "-set_label",  0,            "define label", NULL);
+    psMetadataAddStr(definebyqueryArgs, PS_LIST_TAIL, "-set_reduction",  0,            "define reduction class", NULL);
+    psMetadataAddStr(definebyqueryArgs, PS_LIST_TAIL, "-set_expgroup",  0,            "define exposure group", NULL);
+    psMetadataAddStr(definebyqueryArgs, PS_LIST_TAIL, "-set_dvodb",  0,            "define DVO db", NULL);
+    psMetadataAddStr(definebyqueryArgs, PS_LIST_TAIL, "-set_tess_id",  0,            "define tessellation identifier", NULL);
+    psMetadataAddStr(definebyqueryArgs, PS_LIST_TAIL, "-set_end_stage",  0,            "define end stage", NULL);
+    psMetadataAddStr(definebyqueryArgs, PS_LIST_TAIL, "-set_data_group",  0,      "define data group", NULL);
+    psMetadataAddStr(definebyqueryArgs, PS_LIST_TAIL, "-set_dist_group",  0,      "define distribution group", NULL);
+    psMetadataAddStr(definebyqueryArgs, PS_LIST_TAIL, "-set_note",  0,           "define note", NULL);
+    psMetadataAddBool(definebyqueryArgs, PS_LIST_TAIL, "-pretend",  0,            "do not actually modify the database", false);
+    psMetadataAddBool(definebyqueryArgs, PS_LIST_TAIL, "-simple",  0,            "use the simple output format", false);
+
+    // -definecopy
+    psMetadata *definecopyArgs = psMetadataAlloc();
+    pxvideophotSetSearchArgs (definecopyArgs);
+    psMetadataAddStr(definecopyArgs, PS_LIST_TAIL, "-label", PS_META_DUPLICATE_OK, "search by videophotRun label (LIKE comparison)", NULL);
+    psMetadataAddStr(definecopyArgs, PS_LIST_TAIL, "-data_group", PS_META_DUPLICATE_OK, "search by videophotRun label (LIKE comparison)", NULL);
+
+    psMetadataAddStr(definecopyArgs, PS_LIST_TAIL, "-exp_type", PS_META_REPLACE, "search by exp_type", "object");
+
+    psMetadataAddStr(definecopyArgs, PS_LIST_TAIL, "-set_workdir", 0, "define workdir (required)", NULL);
+    psMetadataAddStr(definecopyArgs, PS_LIST_TAIL, "-set_label", 0, "define label (required)", NULL);
+    psMetadataAddStr(definecopyArgs, PS_LIST_TAIL, "-set_reduction", 0, "define reduction class", NULL);
+    psMetadataAddStr(definecopyArgs, PS_LIST_TAIL, "-set_expgroup", 0, "define exposure group", NULL);
+    psMetadataAddStr(definecopyArgs, PS_LIST_TAIL, "-set_dvodb", 0, "define DVO db", NULL);
+    psMetadataAddStr(definecopyArgs, PS_LIST_TAIL, "-set_tess_id", 0, "define tessellation identifier", NULL);
+    psMetadataAddStr(definecopyArgs, PS_LIST_TAIL, "-set_end_stage", 0, "define end stage", NULL);
+    psMetadataAddStr(definecopyArgs, PS_LIST_TAIL, "-set_data_group", 0, "define data group", NULL);
+    psMetadataAddStr(definecopyArgs, PS_LIST_TAIL, "-set_dist_group", 0, "define distribution group", NULL);
+    psMetadataAddStr(definecopyArgs, PS_LIST_TAIL, "-set_note", 0, "define note", NULL);
+    psMetadataAddBool(definecopyArgs, PS_LIST_TAIL, "-pretend", 0, "do not actually modify the database", false);
+    psMetadataAddBool(definecopyArgs, PS_LIST_TAIL, "-simple", 0, "use the simple output format", false);
+
+    // -updaterun
+    psMetadata *updaterunArgs = psMetadataAlloc();
+    pxvideophotSetSearchArgs (updaterunArgs);
+    psMetadataAddS64(updaterunArgs, PS_LIST_TAIL, "-videophot_id",              0,            "search by videophot ID", 0);
+    psMetadataAddStr(updaterunArgs, PS_LIST_TAIL, "-state", 0,            "search by state", NULL);
+    psMetadataAddStr(updaterunArgs, PS_LIST_TAIL, "-label",  0,           "search by label (LIKE comparison)", NULL);
+    psMetadataAddStr(updaterunArgs, PS_LIST_TAIL, "-data_group",  0,      "search by data_group (LIKE comparison)", NULL);
+    psMetadataAddStr(updaterunArgs, PS_LIST_TAIL, "-dist_group",  0,      "search by dist_group (LIKE comparison)", NULL);
+    psMetadataAddStr(updaterunArgs, PS_LIST_TAIL, "-set_state", 0,        "set state", NULL);
+    psMetadataAddStr(updaterunArgs, PS_LIST_TAIL, "-set_label", 0,        "set label", NULL);
+    psMetadataAddStr(updaterunArgs, PS_LIST_TAIL, "-set_data_group", 0,   "set data group", NULL);
+    psMetadataAddStr(updaterunArgs, PS_LIST_TAIL, "-set_dist_group", 0,   "set dist group", NULL);
+    psMetadataAddStr(updaterunArgs, PS_LIST_TAIL, "-set_note", 0,         "set note", NULL);
+
+    // -setimfiletoupdate
+    psMetadata *setimfiletoupdateArgs = psMetadataAlloc();
+    psMetadataAddS64(setimfiletoupdateArgs, PS_LIST_TAIL, "-videophot_id", 0,   "search by videophot ID (required)", 0);
+    psMetadataAddStr(setimfiletoupdateArgs, PS_LIST_TAIL, "-class_id", 0,  "search by class_id", NULL);
+    psMetadataAddStr(setimfiletoupdateArgs, PS_LIST_TAIL, "-set_label",  0,"new value for label", NULL);
+
+    // -pendingimfile
+    psMetadata *pendingimfileArgs = psMetadataAlloc();
+    psMetadataAddS64(pendingimfileArgs, PS_LIST_TAIL, "-videophot_id",  0,            "search by videophot ID", 0);
+    psMetadataAddStr(pendingimfileArgs, PS_LIST_TAIL, "-label",  PS_META_DUPLICATE_OK, "search by label", 0);
+    pxvideophotSetSearchArgs(pendingimfileArgs);
+    psMetadataAddU64(pendingimfileArgs, PS_LIST_TAIL, "-limit",  0,            "limit result set to N items", 0);
+    psMetadataAddBool(pendingimfileArgs, PS_LIST_TAIL, "-simple",  0,            "use the simple output format", false);
+
+    // -addprocessedimfile
+    psMetadata *addprocessedimfileArgs = psMetadataAlloc();
+    psMetadataAddS64(addprocessedimfileArgs, PS_LIST_TAIL, "-videophot_id",  0,            "define videophot ID (required)", 0);
+    psMetadataAddS64(addprocessedimfileArgs, PS_LIST_TAIL, "-exp_id",  0,            "define exposure ID (required)", 0);
+    psMetadataAddStr(addprocessedimfileArgs, PS_LIST_TAIL, "-class_id",  0,            "define class ID (required)", NULL);
+    psMetadataAddStr(addprocessedimfileArgs, PS_LIST_TAIL, "-uri",  0,            "define URL", NULL);
+    psMetadataAddF32(addprocessedimfileArgs, PS_LIST_TAIL, "-bg",  0,            "define exposure background", NAN);
+    psMetadataAddF32(addprocessedimfileArgs, PS_LIST_TAIL, "-bg_stdev",  0,            "define exposure background stdev", NAN);
+    psMetadataAddF32(addprocessedimfileArgs, PS_LIST_TAIL, "-bg_mean_stdev",  0,            "define exposure background mean stdev", NAN);
+    psMetadataAddF32(addprocessedimfileArgs, PS_LIST_TAIL, "-bias",  0,            "define bias", NAN);
+    psMetadataAddF32(addprocessedimfileArgs, PS_LIST_TAIL, "-bias_stdev",  0,            "define bias stdev", NAN);
+    psMetadataAddF32(addprocessedimfileArgs, PS_LIST_TAIL, "-fringe_0",  0,            "define fringe term 0", NAN);
+    psMetadataAddF32(addprocessedimfileArgs, PS_LIST_TAIL, "-fringe_1",  0,            "define fringe term 1", NAN);
+    psMetadataAddF32(addprocessedimfileArgs, PS_LIST_TAIL, "-fringe_2",  0,            "define fringe term 2", NAN);
+    psMetadataAddF32(addprocessedimfileArgs, PS_LIST_TAIL, "-ap_resid",  0,            "define aperture residual", NAN);
+    psMetadataAddF32(addprocessedimfileArgs, PS_LIST_TAIL, "-ap_resid_stdev",  0,            "define aperture residual stdev", NAN);
+    psMetadataAddF32(addprocessedimfileArgs, PS_LIST_TAIL, "-fwhm_major",  0,            "define fwhm (major axis; arcsec)", NAN);
+    psMetadataAddF32(addprocessedimfileArgs, PS_LIST_TAIL, "-fwhm_major_lq",  0,            "define fwhm (major axis; arcsec)", NAN);
+    psMetadataAddF32(addprocessedimfileArgs, PS_LIST_TAIL, "-fwhm_major_uq",  0,            "define fwhm (major axis; arcsec)", NAN);
+
+    psMetadataAddF32(addprocessedimfileArgs, PS_LIST_TAIL, "-fwhm_minor",  0,            "define fwhm (minor axis; arcsec)", NAN);
+    psMetadataAddF32(addprocessedimfileArgs, PS_LIST_TAIL, "-fwhm_minor_lq",  0,            "define fwhm (minor axis; arcsec)", NAN);
+    psMetadataAddF32(addprocessedimfileArgs, PS_LIST_TAIL, "-fwhm_minor_uq",  0,            "define fwhm (minor axis; arcsec)", NAN);
+
+    psMetadataAddF32(addprocessedimfileArgs, PS_LIST_TAIL, "-iq_fwhm_major",     0,  "define moment value m2 mean", NAN);
+    psMetadataAddF32(addprocessedimfileArgs, PS_LIST_TAIL, "-iq_fwhm_major_err",     0,  "define moment value m2 mean", NAN);
+    psMetadataAddF32(addprocessedimfileArgs, PS_LIST_TAIL, "-iq_fwhm_minor",     0,  "define moment value m2 mean", NAN);
+    psMetadataAddF32(addprocessedimfileArgs, PS_LIST_TAIL, "-iq_fwhm_minor_err",     0,  "define moment value m2 mean", NAN);
+
+    psMetadataAddF32(addprocessedimfileArgs, PS_LIST_TAIL, "-iq_m2",     0,  "define moment value m2 mean", NAN);
+    psMetadataAddF32(addprocessedimfileArgs, PS_LIST_TAIL, "-iq_m2_err", 0,  "define moment value m2 stdev", NAN);
+    psMetadataAddF32(addprocessedimfileArgs, PS_LIST_TAIL, "-iq_m2_lq",  0,  "define moment value m2 lower quartile", NAN);
+    psMetadataAddF32(addprocessedimfileArgs, PS_LIST_TAIL, "-iq_m2_uq",  0,  "define moment value m2 upper quartile", NAN);
+
+    psMetadataAddF32(addprocessedimfileArgs, PS_LIST_TAIL, "-iq_m2c",     0,  "define moment value m2c mean", NAN);
+    psMetadataAddF32(addprocessedimfileArgs, PS_LIST_TAIL, "-iq_m2c_err", 0,  "define moment value m2c stdev", NAN);
+    psMetadataAddF32(addprocessedimfileArgs, PS_LIST_TAIL, "-iq_m2c_lq",  0,  "define moment value m2c lower quartile", NAN);
+    psMetadataAddF32(addprocessedimfileArgs, PS_LIST_TAIL, "-iq_m2c_uq",  0,  "define moment value m2c upper quartile", NAN);
+
+    psMetadataAddF32(addprocessedimfileArgs, PS_LIST_TAIL, "-iq_m2s",     0,  "define moment value m2s mean", NAN);
+    psMetadataAddF32(addprocessedimfileArgs, PS_LIST_TAIL, "-iq_m2s_err", 0,  "define moment value m2s stdev", NAN);
+    psMetadataAddF32(addprocessedimfileArgs, PS_LIST_TAIL, "-iq_m2s_lq",  0,  "define moment value m2s lower quartile", NAN);
+    psMetadataAddF32(addprocessedimfileArgs, PS_LIST_TAIL, "-iq_m2s_uq",  0,  "define moment value m2s upper quartile", NAN);
+
+    psMetadataAddF32(addprocessedimfileArgs, PS_LIST_TAIL, "-iq_m3",     0,  "define moment value m3 mean", NAN);
+    psMetadataAddF32(addprocessedimfileArgs, PS_LIST_TAIL, "-iq_m3_err", 0,  "define moment value m3 stdev", NAN);
+    psMetadataAddF32(addprocessedimfileArgs, PS_LIST_TAIL, "-iq_m3_lq",  0,  "define moment value m3 lower quartile", NAN);
+    psMetadataAddF32(addprocessedimfileArgs, PS_LIST_TAIL, "-iq_m3_uq",  0,  "define moment value m3 upper quartile", NAN);
+
+    psMetadataAddF32(addprocessedimfileArgs, PS_LIST_TAIL, "-iq_m4",     0,  "define moment value m4 mean", NAN);
+    psMetadataAddF32(addprocessedimfileArgs, PS_LIST_TAIL, "-iq_m4_err", 0,  "define moment value m4 stdev", NAN);
+    psMetadataAddF32(addprocessedimfileArgs, PS_LIST_TAIL, "-iq_m4_lq",  0,  "define moment value m4 lower quartile", NAN);
+    psMetadataAddF32(addprocessedimfileArgs, PS_LIST_TAIL, "-iq_m4_uq",  0,  "define moment value m4 upper quartile", NAN);
+
+    psMetadataAddF32(addprocessedimfileArgs, PS_LIST_TAIL, "-dtime_detrend",  0,            "define elapsed time for detrend (seconds)", NAN);
+    psMetadataAddF32(addprocessedimfileArgs, PS_LIST_TAIL, "-dtime_photom",  0,            "define elapsed time for photometry (seconds)", NAN);
+    psMetadataAddF32(addprocessedimfileArgs, PS_LIST_TAIL, "-dtime_total",  0,            "define total elapsed time (seconds)", NAN);
+    psMetadataAddF32(addprocessedimfileArgs, PS_LIST_TAIL, "-dtime_script",  0, "define elapsed time in script (seconds)", NAN);
+    psMetadataAddStr(addprocessedimfileArgs, PS_LIST_TAIL, "-hostname",  0,            "define hostname", NULL);
+    psMetadataAddS32(addprocessedimfileArgs, PS_LIST_TAIL, "-n_stars",  0,            "define number of stars in image", 0);
+    psMetadataAddS32(addprocessedimfileArgs, PS_LIST_TAIL, "-n_psfstars",  0,          "define number of stars used for psf", 0);
+    psMetadataAddS32(addprocessedimfileArgs, PS_LIST_TAIL, "-n_iqstars",  0,          "define number of stars used for IQ stats", 0);
+    psMetadataAddS32(addprocessedimfileArgs, PS_LIST_TAIL, "-n_extended",  0,            "define number of extended objects in image", 0);
+    psMetadataAddS32(addprocessedimfileArgs, PS_LIST_TAIL, "-n_cr",  0,            "define number of cosmic rays in image", 0);
+
+    psMetadataAddStr(addprocessedimfileArgs, PS_LIST_TAIL, "-path_base",  0,            "define base output location", NULL);
+    psMetadataAddS16(addprocessedimfileArgs, PS_LIST_TAIL, "-fault",  0,            "set fault code", 0);
+    psMetadataAddS16(addprocessedimfileArgs, PS_LIST_TAIL, "-quality",  0,            "set quality", 0);
+    psMetadataAddS64(addprocessedimfileArgs, PS_LIST_TAIL, "-magicked",  0,        "define magicked status", 0);
+
+    psMetadataAddStr(addprocessedimfileArgs, PS_LIST_TAIL, "-ver_pslib", 0, "define psLib version", NULL);
+    psMetadataAddStr(addprocessedimfileArgs, PS_LIST_TAIL, "-ver_psmodules", 0, "define psModules version", NULL);
+    psMetadataAddStr(addprocessedimfileArgs, PS_LIST_TAIL, "-ver_psphot", 0, "define psphot version", NULL);
+    psMetadataAddStr(addprocessedimfileArgs, PS_LIST_TAIL, "-ver_psastro", 0, "define psastro version", NULL);
+    psMetadataAddStr(addprocessedimfileArgs, PS_LIST_TAIL, "-ver_ppstats", 0, "define ppStats version", NULL);
+    psMetadataAddStr(addprocessedimfileArgs, PS_LIST_TAIL, "-ver_ppimage", 0, "define ppImage version", NULL);
+    psMetadataAddStr(addprocessedimfileArgs, PS_LIST_TAIL, "-ver_streaks", 0, "define streaksremove version", NULL);
+    
+    psMetadataAddS32(addprocessedimfileArgs, PS_LIST_TAIL, "-maskfrac_npix", 0, "define number of pixels used for maskstats", 0);
+    psMetadataAddF32(addprocessedimfileArgs, PS_LIST_TAIL, "-maskfrac_static", 0, "define static mask fraction", NAN);
+    psMetadataAddF32(addprocessedimfileArgs, PS_LIST_TAIL, "-maskfrac_dynamic", 0, "define dynamic mask fraction", NAN);
+    psMetadataAddF32(addprocessedimfileArgs, PS_LIST_TAIL, "-maskfrac_magic", 0, "define magic mask fraction", NAN);
+    psMetadataAddF32(addprocessedimfileArgs, PS_LIST_TAIL, "-maskfrac_advisory", 0, "define advisory mask fraction", NAN);
+
+    // -processedimfile
+    psMetadata *processedimfileArgs = psMetadataAlloc();
+    pxvideophotSetSearchArgs(processedimfileArgs);
+    psMetadataAddS64(processedimfileArgs, PS_LIST_TAIL, "-videophot_id",  0,         "search by  videophot ID", 0);
+    psMetadataAddS64(processedimfileArgs, PS_LIST_TAIL, "-videophot_imfile_id",  0,  "search by videophot_file_id", 0);
+    psMetadataAddStr(processedimfileArgs,  PS_LIST_TAIL, "-class_id",           0, "search by class ID", NULL);
+    psMetadataAddStr(processedimfileArgs,  PS_LIST_TAIL, "-reduction",          0, "search by reduction class", NULL);
+    psMetadataAddStr(processedimfileArgs,  PS_LIST_TAIL, "-label",  PS_META_DUPLICATE_OK, "search by videophotRun label (LIKE comparison)", NULL);
+    psMetadataAddStr(processedimfileArgs,  PS_LIST_TAIL, "-data_group",  PS_META_DUPLICATE_OK, "search by videophotRun data_group (LIKE comparison)", NULL);
+    pxmagicAddArguments(processedimfileArgs);
+
+    psMetadataAddU64(processedimfileArgs,  PS_LIST_TAIL, "-limit",  0,           "limit result set to N items", 0);
+    psMetadataAddBool(processedimfileArgs, PS_LIST_TAIL, "-all",  0,            "list everything without search terms", false);
+    psMetadataAddBool(processedimfileArgs, PS_LIST_TAIL, "-faulted",  0,        "only return imfiles with a fault status set", false);
+    psMetadataAddBool(processedimfileArgs, PS_LIST_TAIL, "-allfiles",  0,       "only all matching imfiles regardless of fault status", false);
+    psMetadataAddBool(processedimfileArgs, PS_LIST_TAIL, "-simple",  0,         "use the simple output format", false);
+
+    pxspaceAddArguments(processedimfileArgs);
+
+    // -revertprocessedimfile
+    psMetadata *revertprocessedimfileArgs = psMetadataAlloc();
+    psMetadataAddS64(revertprocessedimfileArgs, PS_LIST_TAIL, "-videophot_id", 0,            "search by videophot ID", 0);
+    psMetadataAddStr(revertprocessedimfileArgs,  PS_LIST_TAIL, "-class_id",           0, "search by class ID", NULL);
+    psMetadataAddStr(revertprocessedimfileArgs,  PS_LIST_TAIL, "-reduction",          0, "search by reduction class", NULL);
+    psMetadataAddStr(revertprocessedimfileArgs,  PS_LIST_TAIL, "-label",              PS_META_DUPLICATE_OK, "search by videophotRun label (LIKE comparison)", NULL);
+    pxvideophotSetSearchArgs(revertprocessedimfileArgs);
+    psMetadataAddBool(revertprocessedimfileArgs, PS_LIST_TAIL, "-all",  0,            "allow everything to be queued without search terms", false);
+    psMetadataAddS16(revertprocessedimfileArgs, PS_LIST_TAIL, "-fault",  0,            "search by fault code", 0);
+
+    // -updateprocessedimfile
+    psMetadata *updateprocessedimfileArgs = psMetadataAlloc();
+    psMetadataAddS64(updateprocessedimfileArgs, PS_LIST_TAIL, "-videophot_id",  0,            "search by videophot ID", 0);
+    psMetadataAddStr(updateprocessedimfileArgs,  PS_LIST_TAIL, "-class_id",           0, "search by class ID", NULL);
+    psMetadataAddS16(updateprocessedimfileArgs, PS_LIST_TAIL, "-fault",  0,            "set fault code (required)", 0);
+    psMetadataAddStr(updateprocessedimfileArgs, PS_LIST_TAIL, "-set_state", 0,         "set state", NULL);
+
+    // -processedExp
+    psMetadata *listrunArgs = psMetadataAlloc();
+    pxvideophotSetSearchArgs(listrunArgs);
+    psMetadataAddS64(listrunArgs, PS_LIST_TAIL, "-videophot_id",  0,         "search by  videophot ID", 0);
+    psMetadataAddStr(listrunArgs,  PS_LIST_TAIL, "-reduction",          0, "search by reduction class", NULL);
+    psMetadataAddStr(listrunArgs,  PS_LIST_TAIL, "-label",  PS_META_DUPLICATE_OK, "search by videophotRun label (LIKE comparison)", NULL);
+    psMetadataAddStr(listrunArgs,  PS_LIST_TAIL, "-state",              0, "search by videophotRun state", NULL);
+    psMetadataAddStr(listrunArgs,  PS_LIST_TAIL, "-data_group",  PS_META_DUPLICATE_OK, "search by videophotRun data_group (LIKE comparison)", NULL);
+    psMetadataAddStr(listrunArgs,  PS_LIST_TAIL, "-dist_group",  PS_META_DUPLICATE_OK, "search by videophotRun dist_group (LIKE comparison)", NULL);
+    pxmagicAddArguments(listrunArgs);
+
+    psMetadataAddBool(listrunArgs, PS_LIST_TAIL, "-pstamp_order",  0,    "order results for postage stamp server", false);
+    psMetadataAddU64(listrunArgs,  PS_LIST_TAIL, "-limit",  0,           "limit result set to N items", 0);
+    psMetadataAddBool(listrunArgs, PS_LIST_TAIL, "-all",  0,            "list everything without search terms", false);
+    psMetadataAddBool(listrunArgs, PS_LIST_TAIL, "-simple",  0,         "use the simple output format", false);
+    pxspaceAddArguments(listrunArgs);
+
+    // -block
+    psMetadata *blockArgs = psMetadataAlloc();
+    psMetadataAddStr(blockArgs, PS_LIST_TAIL, "-label",  0,            "name of a label to mask out (required)", NULL);
+
+    // -masked
+    psMetadata *maskedArgs = psMetadataAlloc();
+    psMetadataAddStr(maskedArgs, PS_LIST_TAIL, "-label",  0,            "list blocks for specified label", NULL);
+    psMetadataAddBool(maskedArgs, PS_LIST_TAIL, "-simple",  0,            "use the simple output format", false);
+    psMetadataAddU64(maskedArgs, PS_LIST_TAIL, "-limit",  0,            "limit result set to N items", 0);
+
+    // -unmasked
+    psMetadata *unmaskedArgs = psMetadataAlloc();
+    psMetadataAddStr(unmaskedArgs, PS_LIST_TAIL, "-label",  0,            "restrict to specified label", NULL);
+    psMetadataAddBool(unmaskedArgs, PS_LIST_TAIL, "-simple",  0,            "use the simple output format", false);
+    psMetadataAddU64(unmaskedArgs, PS_LIST_TAIL, "-limit",  0,            "limit result set to N items", 0);
+
+    // -unblock
+    psMetadata *unblockArgs = psMetadataAlloc();
+    psMetadataAddStr(unblockArgs, PS_LIST_TAIL, "-label",  0,            "name of a label to unmask (required)", NULL);
+
+    // -pendingcleanuprun
+    psMetadata *pendingcleanuprunArgs = psMetadataAlloc();
+    psMetadataAddStr(pendingcleanuprunArgs, PS_LIST_TAIL, "-label", PS_META_DUPLICATE_OK, "list blocks for specified label", NULL);
+    psMetadataAddBool(pendingcleanuprunArgs, PS_LIST_TAIL, "-simple",  0,            "use the simple output format", false);
+    psMetadataAddU64(pendingcleanuprunArgs, PS_LIST_TAIL, "-limit",  0,            "limit result set to N items", 0);
+
+    // -pendingcleanupimfile
+    psMetadata *pendingcleanupimfileArgs = psMetadataAlloc();
+    psMetadataAddStr(pendingcleanupimfileArgs, PS_LIST_TAIL, "-label", PS_META_DUPLICATE_OK, "list blocks for specified label", NULL);
+    psMetadataAddS64(pendingcleanupimfileArgs, PS_LIST_TAIL, "-videophot_id", 0,          "search by videophot ID", 0);
+    psMetadataAddStr(pendingcleanupimfileArgs, PS_LIST_TAIL, "-exp_id",                 0,            "search by exp_id", NULL);
+    psMetadataAddBool(pendingcleanupimfileArgs, PS_LIST_TAIL, "-simple",  0,            "use the simple output format", false);
+    psMetadataAddU64(pendingcleanupimfileArgs, PS_LIST_TAIL, "-limit",  0,            "limit result set to N items", 0);
+
+    // -donecleanup
+    psMetadata *donecleanupArgs = psMetadataAlloc();
+    psMetadataAddStr(donecleanupArgs, PS_LIST_TAIL, "-label",  0,            "list blocks for specified label", NULL);
+    psMetadataAddBool(donecleanupArgs, PS_LIST_TAIL, "-simple",  0,            "use the simple output format", false);
+    psMetadataAddU64(donecleanupArgs, PS_LIST_TAIL, "-limit",  0,            "limit result set to N items", 0);
+
+    // -revertcleanup
+    psMetadata *revertcleanupArgs = psMetadataAlloc();
+    psMetadataAddS64(revertcleanupArgs, PS_LIST_TAIL, "-videophot_id", 0,            "search by videophot ID", 0);
+    psMetadataAddStr(revertcleanupArgs,  PS_LIST_TAIL, "-label", PS_META_DUPLICATE_OK, "search by videophotRun label (LIKE comparison)", NULL);
+    psMetadataAddStr(revertcleanupArgs,  PS_LIST_TAIL, "-data_group",  PS_META_DUPLICATE_OK, "search by videophotRun data_group (LIKE comparison)", NULL);
+    psMetadataAddStr(revertcleanupArgs, PS_LIST_TAIL, "-state", 0,             "search by current state", NULL);
+    psMetadataAddS16(revertcleanupArgs, PS_LIST_TAIL, "-fault",  0,            "search by fault code", 0);
+
+    // -run
+    psMetadata *runArgs = psMetadataAlloc();
+    psMetadataAddStr(runArgs, PS_LIST_TAIL, "-label", PS_META_DUPLICATE_OK, "search by label", NULL);
+    psMetadataAddBool(runArgs, PS_LIST_TAIL, "-simple",  0,     "use the simple output format", false);
+    psMetadataAddU64(runArgs, PS_LIST_TAIL, "-limit",  0,       "limit result set to N items", 0);
+    psMetadataAddStr(runArgs, PS_LIST_TAIL, "-state", 0,        "search by state (required)", NULL);
+    pxvideophotSetSearchArgs(runArgs);
+    psMetadataAddS64(runArgs, PS_LIST_TAIL, "-videophot_id",  0,         "search by  videophot ID", 0);
+    psMetadataAddStr(runArgs,  PS_LIST_TAIL, "-reduction",          0, "search by reduction class", NULL);
+
+    // -advanceexp
+    psMetadata *advanceexpArgs = psMetadataAlloc();
+    psMetadataAddS64(advanceexpArgs, PS_LIST_TAIL, "-videophot_id",  0,          "search by videophot ID", 0);
+    psMetadataAddStr(advanceexpArgs, PS_LIST_TAIL, "-label",  PS_META_DUPLICATE_OK, "advance exposures for specified label", NULL);
+    psMetadataAddU64(advanceexpArgs, PS_LIST_TAIL, "-limit",  0,            "limit result set to N items", 0);
+
+    // -tocleanedimfile
+    psMetadata *tocleanedimfileArgs = psMetadataAlloc();
+    psMetadataAddS64(tocleanedimfileArgs, PS_LIST_TAIL, "-videophot_id", 0,          "videophot ID to update", 0);
+    psMetadataAddStr(tocleanedimfileArgs, PS_LIST_TAIL, "-class_id",  0,        "class ID to update", NULL);
+
+    // -tofullimfile
+    psMetadata *tofullimfileArgs = psMetadataAlloc();
+    psMetadataAddS64(tofullimfileArgs, PS_LIST_TAIL, "-videophot_id", 0,          "videophot ID to update", 0);
+    psMetadataAddStr(tofullimfileArgs, PS_LIST_TAIL, "-class_id",  0,        "class ID to update", NULL);
+
+    // -topurgedimfile
+    psMetadata *topurgedimfileArgs = psMetadataAlloc();
+    psMetadataAddS64(topurgedimfileArgs, PS_LIST_TAIL, "-videophot_id", 0,          "videophot ID to update", 0);
+    psMetadataAddStr(topurgedimfileArgs, PS_LIST_TAIL, "-class_id",  0,        "class ID to update", NULL);
+
+    // -toscrubbedimfile
+    psMetadata *toscrubbedimfileArgs = psMetadataAlloc();
+    psMetadataAddS64(toscrubbedimfileArgs, PS_LIST_TAIL, "-videophot_id", 0,        "videophot ID to update", 0);
+    psMetadataAddStr(toscrubbedimfileArgs, PS_LIST_TAIL, "-class_id", 0,       "class ID to update", NULL);
+
+    // -exportrun
+    psMetadata *exportrunArgs = psMetadataAlloc();
+    // pxvideophotSetSearchArgs (exportrunArgs); XXX include search terms?
+    psMetadataAddS64(exportrunArgs, PS_LIST_TAIL, "-videophot_id", 0,          "export this videophot ID (required)", 0);
+    psMetadataAddStr(exportrunArgs, PS_LIST_TAIL, "-outfile", 0,          "export to this file (required)", NULL);
+    psMetadataAddBool(exportrunArgs, PS_LIST_TAIL, "-clean",  0,          "mark tables as cleaned", false);
+    psMetadataAddU64(exportrunArgs, PS_LIST_TAIL, "-limit",   0,          "limit result set to N items", 0);
+
+    // -importrun
+    psMetadata *importrunArgs = psMetadataAlloc();
+    psMetadataAddStr(importrunArgs, PS_LIST_TAIL, "-infile",  0,          "import from this file (required)", NULL);
+
+    // -runstate
+    psMetadata *runstateArgs = psMetadataAlloc();
+    psMetadataAddS64(runstateArgs, PS_LIST_TAIL, "-videophot_id", 0,           "search by videophot ID", 0);
+    psMetadataAddS64(runstateArgs, PS_LIST_TAIL, "-exp_id", 0,            "search by exposure tag", 0);
+    psMetadataAddStr(runstateArgs, PS_LIST_TAIL, "-exp_name", 0,          "search by exposure tag", 0);
+    psMetadataAddStr(runstateArgs, PS_LIST_TAIL,  "-label",  PS_META_DUPLICATE_OK, "search by warpRun label", NULL);
+    psMetadataAddBool(runstateArgs, PS_LIST_TAIL, "-no_magic",  0,        "magic is not necessary for result", false);
+
+    psMetadataAddU64(runstateArgs, PS_LIST_TAIL, "-limit",  0,            "limit result set to N items", 0);
+    psMetadataAddBool(runstateArgs, PS_LIST_TAIL, "-simple",  0,          "use the simple output format", false);
+
+    psMetadata *argSets = psMetadataAlloc();
+    psMetadata *modes = psMetadataAlloc();
+
+    PXOPT_ADD_MODE("-definebyquery",        "create runs from raw exposures",       VIDEOPHOTTOOL_MODE_DEFINEBYQUERY,        definebyqueryArgs);
+    PXOPT_ADD_MODE("-definecopy",           "create copy runs",                     VIDEOPHOTTOOL_MODE_DEFINECOPY,        definecopyArgs);
+    PXOPT_ADD_MODE("-updaterun",            "change videophot run properties",           VIDEOPHOTTOOL_MODE_UPDATERUN,            updaterunArgs);
+    PXOPT_ADD_MODE("-setimfiletoupdate",    "set imfile state to update",           VIDEOPHOTTOOL_MODE_SETIMFILETOUPDATE,    setimfiletoupdateArgs);
+    PXOPT_ADD_MODE("-pendingimfile",        "show pending imfiles",                 VIDEOPHOTTOOL_MODE_PENDINGIMFILE,        pendingimfileArgs);
+    PXOPT_ADD_MODE("-addprocessedimfile",   "add a processed imfile",               VIDEOPHOTTOOL_MODE_ADDPROCESSEDIMFILE,   addprocessedimfileArgs);
+    PXOPT_ADD_MODE("-processedimfile",      "show processed imfiles",               VIDEOPHOTTOOL_MODE_PROCESSEDIMFILE,      processedimfileArgs);
+    PXOPT_ADD_MODE("-updateprocessedimfile","change procesed imfile properties",    VIDEOPHOTTOOL_MODE_UPDATEPROCESSEDIMFILE,updateprocessedimfileArgs);
+    PXOPT_ADD_MODE("-revertprocessedimfile","undo a processed imfile",              VIDEOPHOTTOOL_MODE_REVERTPROCESSEDIMFILE,revertprocessedimfileArgs);
+    PXOPT_ADD_MODE("-runstate",             "list the states of videophot run",          VIDEOPHOTTOOL_MODE_RUNSTATE,             runstateArgs);
+    PXOPT_ADD_MODE("-listrun",              "list videophotRuns",                        VIDEOPHOTTOOL_MODE_LISTRUN,              listrunArgs);
+    PXOPT_ADD_MODE("-advanceexp",           "advance completed exposures",          VIDEOPHOTTOOL_MODE_ADVANCEEXP,           advanceexpArgs);
+    PXOPT_ADD_MODE("-block",                "set a label block",                    VIDEOPHOTTOOL_MODE_BLOCK,                blockArgs);
+    PXOPT_ADD_MODE("-masked",               "show blocked labels",                  VIDEOPHOTTOOL_MODE_MASKED,               maskedArgs);
+    PXOPT_ADD_MODE("-unmasked",             "",                                     VIDEOPHOTTOOL_MODE_UNMASKED,             unmaskedArgs);
+    PXOPT_ADD_MODE("-unblock",              "remove a label block",                 VIDEOPHOTTOOL_MODE_UNBLOCK,              unblockArgs);
+    PXOPT_ADD_MODE("-pendingcleanuprun",    "show runs that need to be cleaned up", VIDEOPHOTTOOL_MODE_PENDINGCLEANUPRUN,    pendingcleanuprunArgs);
+    PXOPT_ADD_MODE("-pendingcleanupimfile", "show runs that need to be cleaned up", VIDEOPHOTTOOL_MODE_PENDINGCLEANUPIMFILE, pendingcleanupimfileArgs);
+    PXOPT_ADD_MODE("-donecleanup",          "show runs that have been cleaned",     VIDEOPHOTTOOL_MODE_DONECLEANUP,          donecleanupArgs);
+    PXOPT_ADD_MODE("-revertcleanup",        "show runs that have been cleaned",     VIDEOPHOTTOOL_MODE_REVERTCLEANUP,        revertcleanupArgs);
+    PXOPT_ADD_MODE("-run",                  "show runs",                            VIDEOPHOTTOOL_MODE_RUN,                  runArgs);
+    PXOPT_ADD_MODE("-tocleanedimfile",      "set imfile state to cleaned",          VIDEOPHOTTOOL_MODE_TOCLEANEDIMFILE,      tocleanedimfileArgs);
+    PXOPT_ADD_MODE("-tofullimfile",         "set imfile state to full",              VIDEOPHOTTOOL_MODE_TOFULLIMFILE,         tofullimfileArgs);
+    PXOPT_ADD_MODE("-topurgedimfile",       "set imfile state to purged",            VIDEOPHOTTOOL_MODE_TOPURGEDIMFILE,       topurgedimfileArgs);
+    PXOPT_ADD_MODE("-toscrubbedimfile",     "set imfile state to scrubbed",          VIDEOPHOTTOOL_MODE_TOSCRUBBEDIMFILE,     toscrubbedimfileArgs);
+
+    PXOPT_ADD_MODE("-exportrun",            "export run for import on other database", VIDEOPHOTTOOL_MODE_EXPORTRUN, exportrunArgs);
+    PXOPT_ADD_MODE("-importrun",            "import run from metadata file",           VIDEOPHOTTOOL_MODE_IMPORTRUN, importrunArgs);
+
+    if (!pxGetOptions(stderr, argc, argv, config, modes, argSets)) {
+        psError(PS_ERR_UNKNOWN, false, "option parsing failed");
+        psFree(argSets);
+        psFree(modes);
+        psFree(config);
+        return NULL;
+    }
+
+    psFree(argSets);
+    psFree(modes);
+
+    // define Database handle, if used
+    config->dbh = psMemIncrRefCounter(pmConfigDB(config->modules));
+    if (!config->dbh) {
+        psError(PS_ERR_UNKNOWN, false, "Can't configure database");
+        psFree(config);
+        return NULL;
+    }
+
+    return config;
+}
Index: /branches/haf_branches/videophot2010/ippTools/src/warptool.c
===================================================================
--- /branches/haf_branches/videophot2010/ippTools/src/warptool.c	(revision 29944)
+++ /branches/haf_branches/videophot2010/ippTools/src/warptool.c	(revision 29945)
@@ -994,5 +994,5 @@
         psString thisWhere = NULL;
         if (whereStr) {
-            psStringAppend(&thisWhere, whereStr);
+	  psStringAppend(&thisWhere, "%s", whereStr);
         }
         psStringAppend(&thisWhere, "\nAND warpRun.label = '%s'", label); 
