Index: /trunk/ippTools/src/.cvsignore
===================================================================
--- /trunk/ippTools/src/.cvsignore	(revision 16133)
+++ /trunk/ippTools/src/.cvsignore	(revision 16134)
@@ -29,2 +29,3 @@
 caltool
 flatcorr
+pstamptool
Index: /trunk/ippTools/src/pstamptool.c
===================================================================
--- /trunk/ippTools/src/pstamptool.c	(revision 16134)
+++ /trunk/ippTools/src/pstamptool.c	(revision 16134)
@@ -0,0 +1,345 @@
+/*
+ * pstamptool.c
+ *
+ * Copyright (C) 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 <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <inttypes.h>
+
+#include "pxtools.h"
+#include "pxdata.h"
+#include "pstamptool.h"
+
+static bool adddatastoreMode(pxConfig *config);
+static bool datastoreMode(pxConfig *config);
+static bool addReqMode(pxConfig *config);
+static bool pendingReqMode(pxConfig *config);
+static bool processedReqMode(pxConfig *config);
+#ifdef notyet
+static bool addJobMode(pxConfig *config);
+static bool pendingJobMode(pxConfig *config);
+
+static bool copydoneMode(pxConfig *config);
+
+static bool copydoneCompleteExp(pxConfig *config);
+static psArray *pzGetPendingCameras(pxConfig *config);
+static psArray *pzArrayZip(psArray *arraySet, psS64 limit);
+#endif // notyet
+
+# define MODECASE(caseName, func) \
+    case caseName: \
+    if (!func(config)) { \
+                goto FAIL; \
+            } \
+    break;
+
+
+int main(int argc, char **argv)
+{
+    psLibInit(NULL);
+
+    pxConfig *config = pstamptoolConfig(NULL, argc, argv);
+    if (!config) {
+        psError(PXTOOLS_ERR_CONFIG, false, "failed to configure");
+        goto FAIL;
+    }
+
+    switch (config->mode) {
+        MODECASE(PSTAMPTOOL_MODE_ADDDATASTORE, adddatastoreMode);
+        MODECASE(PSTAMPTOOL_MODE_DATASTORE, datastoreMode);
+        MODECASE(PSTAMPTOOL_MODE_ADDREQ, addReqMode);
+        MODECASE(PSTAMPTOOL_MODE_PENDINGREQ, pendingReqMode);
+        MODECASE(PSTAMPTOOL_MODE_PROCESSEDREQ, processedReqMode);
+        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);
+}
+
+static bool adddatastoreMode(pxConfig *config)
+{
+    psError(PS_ERR_UNKNOWN, true, "-adddatastore not implemented yet");
+    return false;
+#ifdef notyet
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    bool status = false;
+    psString camera = psMetadataLookupStr(&status, config->args, "-inst");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -inst");
+        return false;
+    }
+    if (!camera) {
+        psError(PS_ERR_UNKNOWN, true, "-inst is required");
+        return false;
+    }
+
+    psString telescope = psMetadataLookupStr(&status, config->args, "-telescope");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -telescope");
+        return false;
+    }
+    if (!telescope) {
+        psError(PS_ERR_UNKNOWN, true, "-telescope is required");
+        return false;
+    }
+
+    psString uri = psMetadataLookupStr(&status, config->args, "-uri");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -uri");
+        return false;
+    }
+    if (!uri) {
+        psError(PS_ERR_UNKNOWN, true, "-uri is required");
+        return false;
+    }
+
+    if (!pzDataStoreInsert(config->dbh,
+            camera,
+            telescope,
+            uri
+        )) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+
+    return true;
+#endif // notyet
+}
+
+static bool datastoreMode(pxConfig *config)
+{
+    psError(PS_ERR_UNKNOWN, true, "-datastore not implemented yet");
+    return false;
+
+#ifdef notyet
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    if (!p_psDBRunQuery(config->dbh, "SELECT * FROM pzDataStore")) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+
+    psArray *output = p_psDBFetchResult(config->dbh);
+    if (!output) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+    if (!psArrayLength(output)) {
+        psTrace("pstamptool", PS_LOG_INFO, "no rows found");
+        psFree(output);
+        return true;
+    }
+
+    bool simple = false;
+    {
+        bool status = false;
+        simple = psMetadataLookupBool(&status, config->args, "-simple");
+        if (!status) {
+            psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -simple");
+            return false;
+        }
+    }
+
+    // negative simple so the default is true
+    if (!ippdbPrintMetadatas(stdout, output, "pzDataStore", !simple)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print array");
+        psFree(output);
+        return false;
+    }
+
+    psFree(output);
+
+    return true;
+#endif // notyet
+}
+
+static bool addReqMode(pxConfig *config)
+{
+    bool    status;
+
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    psString uri = psMetadataLookupStr(&status, config->args, "-uri");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -uri");
+        return false;
+    }
+    if (!uri) {
+        psError(PS_ERR_UNKNOWN, true, "-uri is required");
+        return false;
+    }
+
+    char *query ="INSERT INTO psRequest"
+                 " (state, uri)"
+                 " VALUES( 'new', '%s')";
+
+    if (!p_psDBRunQuery(config->dbh, query, uri)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(query);
+        return false;
+    }
+
+    psU64 affected = psDBAffectedRows(config->dbh);
+    if (affected != 1) {
+        psError(PS_ERR_UNKNOWN, false, 
+            "should have affected one row but %" PRIu64 " rows were modified",
+            affected);
+        return false;
+    }
+
+    return true;
+}
+
+static bool pendingReqMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    bool status = false;
+    psU64 limit = psMetadataLookupU64(&status, config->args, "-limit");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -limit");
+        return false;
+    }
+
+    psString query = psStringCopy(
+            "SELECT"
+            " *"
+            " FROM psRequest"
+            " WHERE state = 'new'"
+        );
+
+    if (config->where) {
+        psString whereClause = psDBGenerateWhereConditionSQL(config->where, "psRequest");
+        psStringAppend(&query, " AND %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("pstamptool", PS_LOG_INFO, "no rows found");
+        psFree(output);
+        return true;
+    }
+
+    bool simple = false;
+    {
+        bool status = false;
+        simple = psMetadataLookupBool(&status, config->args, "-simple");
+        if (!status) {
+            psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -simple");
+            psFree(output);
+            return false;
+        }
+    }
+
+    // negative simple so the default is true
+    if (!ippdbPrintMetadatas(stdout, output, "pzPendingExp", !simple)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print array");
+        psFree(output);
+        return false;
+    }
+
+    psFree(output);
+
+    return true;
+}
+static bool processedReqMode(pxConfig *config)
+{
+    bool    status;
+
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    psString req_id = psMetadataLookupStr(&status, config->args, "-req_id");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -req_id");
+        return false;
+    }
+    if (!req_id) {
+        psError(PS_ERR_UNKNOWN, true, "-req_id is required");
+        return false;
+    }
+    psString state = psMetadataLookupStr(&status, config->args, "-state");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -state");
+        return false;
+    }
+    if (!state) {
+        psError(PS_ERR_UNKNOWN, true, "-state is required");
+        return false;
+    }
+
+    // XXX: check state for a legal value
+
+    char *query ="UPDATE psRequest"
+                 " SET state = '%s'"
+                 " WHERE req_id = '%s'";
+
+    if (!p_psDBRunQuery(config->dbh, query, state, req_id)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(query);
+        return false;
+    }
+
+    psU64 affected = psDBAffectedRows(config->dbh);
+    if (affected != 1) {
+        psError(PS_ERR_UNKNOWN, false, "should have affected one row but %" PRIu64 " rows were modified",
+            affected);
+        return false;
+    }
+
+    return true;
+}
Index: /trunk/ippTools/src/pstamptool.h
===================================================================
--- /trunk/ippTools/src/pstamptool.h	(revision 16134)
+++ /trunk/ippTools/src/pstamptool.h	(revision 16134)
@@ -0,0 +1,40 @@
+/*
+ * pstamptool.h
+ *
+ * Copyright (C) 2008
+ *
+ * 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 PSTAMPTOOL_H
+#define PSTAMPTOOL_H 1
+
+#include "pxtools.h"
+
+typedef enum {
+    PSTAMPTOOL_MODE_NONE      = 0x0,
+    PSTAMPTOOL_MODE_ADDDATASTORE,
+    PSTAMPTOOL_MODE_DATASTORE,
+    PSTAMPTOOL_MODE_ADDREQ,
+    PSTAMPTOOL_MODE_PENDINGREQ,
+    PSTAMPTOOL_MODE_PROCESSEDREQ,
+    PSTAMPTOOL_MODE_ADDJOB,
+    PSTAMPTOOL_MODE_PENDINGJOB,
+    PSTAMPTOOL_MODE_JOBRESULT,
+    PSTAMPTOOL_MODE_PROCESSEDJOB,
+} pstamptoolMode;
+
+pxConfig *pstamptoolConfig(pxConfig *config, int argc, char **argv);
+
+#endif // PSTAMPTOOL_H
Index: /trunk/ippTools/src/pstamptoolConfig.c
===================================================================
--- /trunk/ippTools/src/pstamptoolConfig.c	(revision 16134)
+++ /trunk/ippTools/src/pstamptoolConfig.c	(revision 16134)
@@ -0,0 +1,212 @@
+/*
+ * pstamptoolConfig.c
+ *
+ * Copyright (C) 2008
+ *
+ * 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 <psmodules.h>
+
+#include "pxtools.h"
+#include "pstamptool.h"
+
+pxConfig *pstamptoolConfig(pxConfig *config, int argc, char **argv)
+{
+    if (!config) {
+        config = pxConfigAlloc();
+    }
+
+    pmConfigReadParamsSet(false);
+
+    // setup site config
+    config->modules = pmConfigRead(&argc, argv, NULL);
+    if (! config->modules) {
+        psError(PS_ERR_UNKNOWN, false, "Can't find site configuration!\n");
+        psFree(config);
+        return NULL;
+    }
+
+    // -adddatastore
+    psMetadata *adddatastoreArgs = psMetadataAlloc();
+    psMetadataAddStr(adddatastoreArgs, PS_LIST_TAIL, "-uri", 0,
+            "define storage uri", NULL);
+    
+    // -datastore
+    psMetadata *datastoreArgs = psMetadataAlloc();
+    psMetadataAddBool(datastoreArgs, PS_LIST_TAIL, "-simple", 0,
+            "use the simple output format", false);
+
+    // -addreq
+    psMetadata *addreqArgs = psMetadataAlloc();
+    psMetadataAddStr(addreqArgs, PS_LIST_TAIL, "-uri", 0,
+            "define request file uri", NULL); 
+
+    // -pendingreq
+    psMetadata *pendingreqArgs = psMetadataAlloc();
+    psMetadataAddStr(pendingreqArgs, PS_LIST_TAIL, "-req_id", 0,
+            "define req_id", NULL); 
+    psMetadataAddU64(pendingreqArgs, PS_LIST_TAIL, "-limit",  0,
+            "limit result set to N items", 0);
+    psMetadataAddBool(pendingreqArgs, PS_LIST_TAIL, "-simple", 0,
+            "use the simple output format", false);
+
+    // -processedreq
+    psMetadata *processedreqArgs = psMetadataAlloc();
+    psMetadataAddStr(processedreqArgs, PS_LIST_TAIL, "-req_id", 0,
+            "req_id for which to change state", NULL); 
+    psMetadataAddStr(processedreqArgs, PS_LIST_TAIL, "-state", 0,
+            "new state", NULL); 
+
+#ifdef notdef
+    // -pendingimfile
+    psMetadata *pendingimfileArgs = psMetadataAlloc();
+    psMetadataAddStr(pendingimfileArgs, PS_LIST_TAIL, "-exp_name", 0,
+            "define exposure ID", NULL); 
+    psMetadataAddStr(pendingimfileArgs, PS_LIST_TAIL, "-inst", 0,
+            "define camera ID", NULL); 
+    psMetadataAddStr(pendingimfileArgs, PS_LIST_TAIL, "-telescope", 0,
+            "define telescope ID", NULL); 
+    psMetadataAddStr(pendingimfileArgs, PS_LIST_TAIL, "-exp_type", 0,
+            "define exposure type", NULL); 
+    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);
+
+    // -copydone
+    psMetadata *copydoneArgs = psMetadataAlloc();
+    psMetadataAddStr(copydoneArgs, PS_LIST_TAIL, "-exp_name", 0,
+            "define exposure ID (required)", NULL); 
+    psMetadataAddStr(copydoneArgs, PS_LIST_TAIL, "-inst", 0,
+            "define camera ID (required)", NULL); 
+    psMetadataAddStr(copydoneArgs, PS_LIST_TAIL, "-telescope", 0,
+            "define telescope ID (required)", NULL); 
+    psMetadataAddStr(copydoneArgs, PS_LIST_TAIL, "-class", 0,
+            "define class", NULL);
+    psMetadataAddStr(copydoneArgs, PS_LIST_TAIL, "-class_id", 0,
+            "define class_id", NULL);
+    psMetadataAddStr(copydoneArgs, PS_LIST_TAIL, "-uri", 0,
+            "define storage uri", NULL);
+
+#endif // notyet
+
+#define PXTOOL_MODE(option, modeval, argset) \
+{ \
+    int N = 0; \
+    if ((N = psArgumentGet (argc, argv, option))) { \
+        psArgumentRemove (N, &argc, argv); \
+        if (config->mode) { \
+            psError(PS_ERR_UNKNOWN, true, "only one mode selection is allowed"); \
+            psFree(config); \
+            return NULL; \
+        } \
+        config->mode = modeval; \
+        config->args = psMemIncrRefCounter(argset); \
+    } \
+    if (!psMetadataAddMetadata(argSets, PS_LIST_TAIL, option, 0, NULL, argset)) {;\
+        psError(PS_ERR_UNKNOWN, false, "failed to add argset for %s", option); \
+    } \
+    psFree(argset); \
+}
+
+    psMetadata *argSets = psMetadataAlloc();
+    psMetadata *modes = psMetadataAlloc();
+
+    PXTOOL_ADD_MODE("-adddatastore",    "", PSTAMPTOOL_MODE_ADDDATASTORE, adddatastoreArgs);
+    PXTOOL_ADD_MODE("-datastore",       "", PSTAMPTOOL_MODE_DATASTORE,    datastoreArgs);
+
+    PXTOOL_ADD_MODE("-addreq",          "", PSTAMPTOOL_MODE_ADDREQ,       addreqArgs);
+    PXTOOL_ADD_MODE("-pendingreq",      "", PSTAMPTOOL_MODE_PENDINGREQ,   pendingreqArgs);
+    PXTOOL_ADD_MODE("-processedreq",    "", PSTAMPTOOL_MODE_PROCESSEDREQ, processedreqArgs);
+
+#ifdef notyet
+    PXTOOL_ADD_MODE("-addjob",          "", PSTAMPTOOL_MODE_ADDJOB,       addjobArgs);
+    PXTOOL_ADD_MODE("-pendingjob",      "", PSTAMPTOOL_MODE_PENDINGJOB,   pendingjobArgs);
+    PXTOOL_ADD_MODE("-processedjob"     "", PSTAMPTOOL_MODE_PROCESSEDJOB, processedjobArgs);
+    PXTOOL_ADD_MODE("-jobresult",       "", PSTAMPTOOL_MODE_JOBRESULT,    jobresultArgs);
+#endif
+
+    if (!pxGetOptions(stderr, argc, argv, config, modes, argSets)) {
+        psError(PS_ERR_UNKNOWN, true, "option parsing failed");
+        psFree(argSets);
+        psFree(modes);
+        psFree(config);
+        return NULL;
+    }
+
+    psFree(argSets);
+    psFree(modes);
+
+    // setup search criterion
+#define addWhereStr(name) \
+{ \
+    psString str = NULL; \
+    bool status = false; \
+    if ((str = psMetadataLookupStr(&status, config->args, "-" #name))) { \
+        if (!psMetadataAddStr(config->where, PS_LIST_TAIL, #name, 0, "==", str)) {\
+            psError(PS_ERR_UNKNOWN, false, "failed to add item " #name); \
+            psFree(config); \
+            return NULL; \
+        } \
+    } \
+}
+
+    // generate SQL where clause
+    config->where = psMetadataAlloc();
+
+    addWhereStr(req_id); 
+    addWhereStr(job_id); 
+
+#ifdef notdef
+    addWhereStr(exp_name); 
+    addWhereStr(telescope); 
+    // convert '-inst' to 'camera'
+    {
+        psString str = NULL;
+        bool status = false;
+        if ((str = psMetadataLookupStr(&status, config->args, "-inst"))) {
+            if (!psMetadataAddStr(config->where, PS_LIST_TAIL, "camera", 0, "==", str)) {
+                psError(PS_ERR_UNKNOWN, false, "failed to add item camera");
+                psFree(config);
+                return NULL;
+            }
+        }
+    }
+    addWhereStr(exp_type); 
+    addWhereStr(class); 
+    addWhereStr(class_id); 
+#endif
+
+    if (config->where->list->n < 1) {
+        psFree(config->where);
+        config->where = NULL;
+    }
+
+    // define Database handle, if used
+    // do this last so we don't setup a connection before CLI options are
+    // validated
+    config->dbh = pmConfigDB(config->modules);
+    if (!config->dbh) {
+        psError(PS_ERR_UNKNOWN, false, "Can't configure database");
+        psFree(config);
+        return NULL;
+    }
+
+    return config;
+}
