Index: /branches/pap_mops/dbconfig/changes.txt
===================================================================
--- /branches/pap_mops/dbconfig/changes.txt	(revision 24376)
+++ /branches/pap_mops/dbconfig/changes.txt	(revision 24377)
@@ -1132,7 +1132,7 @@
     client_id BIGINT AUTO_INCREMENT, -- unique identifier
     comment VARCHAR(255),            -- for human memory
-    stage VARCHAR(64),               -- stage of interest (chip, camera, diff, etc.)
+    stage VARCHAR(64) NOT NULL, -- stage of interest (chip, camera, diff, etc.)
     format VARCHAR(64),              -- format of data
-    destination VARCHAR(255),        -- where to send data
+    destination VARCHAR(255) NOT NULL, -- where to send data
     PRIMARY KEY(client_id)
 ) ENGINE=innodb DEFAULT CHARSET=latin1;
@@ -1140,9 +1140,13 @@
 CREATE TABLE publishRun (
     pub_id BIGINT AUTO_INCREMENT, -- unique identifier
-    client_id BIGINT,             -- link to publishClient
-    stage_id BIGINT,              -- link to various stage tables
+    client_id BIGINT NOT NULL,  -- link to publishClient
+    stage_id BIGINT NOT NULL,   -- link to various stage tables
+    label VARCHAR(64),          -- label for run
+    state VARCHAR(64),          -- state of run (new, full, etc.)
     PRIMARY KEY(pub_id),
     KEY(client_id),
     KEY(stage_id),
+    KEY(label),
+    KEY(state),
     FOREIGN KEY(client_id) REFERENCES publishClient(client_id)
 ) ENGINE=innodb DEFAULT CHARSET=latin1;
Index: /branches/pap_mops/dbconfig/publish.md
===================================================================
--- /branches/pap_mops/dbconfig/publish.md	(revision 24376)
+++ /branches/pap_mops/dbconfig/publish.md	(revision 24377)
@@ -13,4 +13,5 @@
     client_id   S64         0
     stage_id    S64         0
+    label       STR         64
     state       STR         64
 END
Index: /branches/pap_mops/ippTools/doc/publish_flow.txt
===================================================================
--- /branches/pap_mops/ippTools/doc/publish_flow.txt	(revision 24377)
+++ /branches/pap_mops/ippTools/doc/publish_flow.txt	(revision 24377)
@@ -0,0 +1,16 @@
+### Setup
+pubtool -defineclient -stage diff -comment Comment -destination DataStore -format MOPS
+
+### Regularly run by pantasks to generate publishing runs based on data that has been processed
+pubtool -definerun
+
+### Regularly run by pantasks to get list of files to publish
+pubtool -pending
+--> publish_file.pl --pub_id 12345 --component skycell.123.456 --destination DataStore --stage diff --stage_id 67890 --format MOPS
+----> pubtool -add -pub_id 12345 --component skycell.123.456 --fault 0
+
+### Run as required
+pubtool -revert
+
+### Regularly run by pantasks to mark running publishing runs as 'full'
+pubtool -advance
Index: /branches/pap_mops/ippTools/share/pxadmin_create_tables.sql
===================================================================
--- /branches/pap_mops/ippTools/share/pxadmin_create_tables.sql	(revision 24376)
+++ /branches/pap_mops/ippTools/share/pxadmin_create_tables.sql	(revision 24377)
@@ -1375,7 +1375,7 @@
     client_id BIGINT AUTO_INCREMENT, -- unique identifier
     comment VARCHAR(255),            -- for human memory
-    stage VARCHAR(64),               -- stage of interest (chip, camera, diff, etc.)
+    stage VARCHAR(64) NOT NULL, -- stage of interest (chip, camera, diff, etc.)
     format VARCHAR(64),              -- format of data
-    destination VARCHAR(255),        -- where to send data
+    destination VARCHAR(255) NOT NULL, -- where to send data
     PRIMARY KEY(client_id)
 ) ENGINE=innodb DEFAULT CHARSET=latin1;
@@ -1384,9 +1384,13 @@
 CREATE TABLE publishRun (
     pub_id BIGINT AUTO_INCREMENT, -- unique identifier
-    client_id BIGINT,             -- link to publishClient
-    stage_id BIGINT,              -- link to various stage tables
+    client_id BIGINT NOT NULL,  -- link to publishClient
+    stage_id BIGINT NOT NULL,   -- link to various stage tables
+    label VARCHAR(64),          -- label for run
+    state VARCHAR(64),          -- state of run (new, full, etc.)
     PRIMARY KEY(pub_id),
     KEY(client_id),
     KEY(stage_id),
+    KEY(label),
+    KEY(state),
     FOREIGN KEY(client_id) REFERENCES publishClient(client_id)
 ) ENGINE=innodb DEFAULT CHARSET=latin1;
Index: /branches/pap_mops/ippTools/src/Makefile.am
===================================================================
--- /branches/pap_mops/ippTools/src/Makefile.am	(revision 24376)
+++ /branches/pap_mops/ippTools/src/Makefile.am	(revision 24377)
@@ -20,5 +20,6 @@
 	stacktool \
 	warptool \
-	receivetool
+	receivetool \
+	pubtool
 
 
@@ -59,5 +60,6 @@
 	regtool.h \
 	stacktool.h \
-	warptool.h
+	warptool.h \
+	pubtool.h
 
 lib_LTLIBRARIES = libpxtools.la
@@ -220,4 +222,10 @@
     receivetoolConfig.c
 
+pubtool_CFLAGS = $(PSLIB_CFLAGS) $(PSMODULES_CFLAGS) $(IPPDB_CFLAGS)
+pubtool_LDADD = $(PSLIB_LIBS) $(PSMODULES_LIBS) $(IPPDB_LIBS) libpxtools.la
+pubtool_SOURCES = \
+    pubtool.c \
+    pubtoolConfig.c
+
 clean-local:
 	-rm -f TAGS
Index: /branches/pap_mops/ippTools/src/pubtool.c
===================================================================
--- /branches/pap_mops/ippTools/src/pubtool.c	(revision 24377)
+++ /branches/pap_mops/ippTools/src/pubtool.c	(revision 24377)
@@ -0,0 +1,635 @@
+/*
+ * receivetool.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 <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <inttypes.h>
+
+#include "pxtools.h"
+#include "pxdata.h"
+#include "receivetool.h"
+
+static bool definesourceMode(pxConfig *config);
+static bool listMode(pxConfig *config);
+static bool addfilesetMode(pxConfig *config);
+static bool updatelastMode(pxConfig *config);
+static bool pendingfilesetMode(pxConfig *config);
+static bool updatefilesetMode(pxConfig *config);
+static bool addfileMode(pxConfig *config);
+static bool pendingfileMode(pxConfig *config);
+static bool addresultMode(pxConfig *config);
+static bool toadvanceMode(pxConfig *config);
+static bool revertMode(pxConfig *config);
+
+# define MODECASE(caseName, func) \
+    case caseName: \
+    if (!func(config)) { \
+        goto FAIL; \
+    } \
+    break;
+
+
+int main(int argc, char **argv)
+{
+    psLibInit(NULL);
+
+    pxConfig *config = receivetoolConfig(NULL, argc, argv);
+    if (!config) {
+        psError(PXTOOLS_ERR_CONFIG, false, "failed to configure");
+        goto FAIL;
+    }
+
+    switch (config->mode) {
+        MODECASE(RECEIVETOOL_MODE_DEFINESOURCE, definesourceMode);
+        MODECASE(RECEIVETOOL_MODE_LIST, listMode);
+        MODECASE(RECEIVETOOL_MODE_ADDFILESET, addfilesetMode);
+        MODECASE(RECEIVETOOL_MODE_UPDATELAST, updatelastMode);
+        MODECASE(RECEIVETOOL_MODE_PENDINGFILESET, pendingfilesetMode);
+        MODECASE(RECEIVETOOL_MODE_UPDATEFILESET, updatefilesetMode);
+        MODECASE(RECEIVETOOL_MODE_TOADVANCE, toadvanceMode);
+        MODECASE(RECEIVETOOL_MODE_ADDFILE, addfileMode);
+        MODECASE(RECEIVETOOL_MODE_PENDINGFILE, pendingfileMode);
+        MODECASE(RECEIVETOOL_MODE_ADDRESULT, addresultMode);
+        MODECASE(RECEIVETOOL_MODE_REVERT, revertMode);
+      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 definesourceMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    // required
+    PXOPT_LOOKUP_STR(source, config->args, "-source", true, false);
+    PXOPT_LOOKUP_STR(product, config->args, "-product",  true, false);
+    PXOPT_LOOKUP_STR(workdir, config->args, "-workdir", false, false);
+
+    // optional
+    PXOPT_LOOKUP_STR(comment, config->args, "-comment",  false, false);
+    PXOPT_LOOKUP_STR(last, config->args, "-last",  false, false);
+    PXOPT_LOOKUP_STR(status_product, config->args, "-status_product",  false, false);
+    PXOPT_LOOKUP_STR(ds_dbname, config->args, "-ds_dbname",  false, false);
+    PXOPT_LOOKUP_STR(ds_dbhost, config->args, "-ds_dbhost",  false, false);
+
+    if (!receiveSourceInsert(config->dbh, 0, source, product, workdir, comment, last, status_product, ds_dbname, ds_dbhost)) {
+        psError(PS_ERR_UNKNOWN, false, "Database error");
+        return false;
+    }
+
+    return true;
+}
+
+static bool listMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    psMetadata *where = psMetadataAlloc(); // WHERE conditions
+
+    // required
+    PXOPT_COPY_STR(config->args, where, "-source", "receiveSource.source", "==");
+    PXOPT_COPY_STR(config->args, where, "-product", "receiveSource.product", "==");
+    PXOPT_COPY_S64(config->args, where, "-comment", "receiveSource.comment", "LIKE");
+
+    // optional
+    PXOPT_LOOKUP_U64(limit, config->args, "-limit", false, false);
+    PXOPT_LOOKUP_BOOL(simple, config->args, "-simple", false);
+
+    psString query = pxDataGet("receivetool_list.sql");
+    if (!query) {
+        psError(PXTOOLS_ERR_DATA, false, "Failed to retreive SQL statement");
+        psFree(where);
+        return false;
+    }
+
+    if (psListLength(where->list)) {
+        psString whereClause = psDBGenerateWhereConditionSQL(where, NULL);
+        psStringAppend(&query, " WHERE %s", whereClause);
+        psFree(whereClause);
+    }
+    psFree(where);
+
+    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("receivetool", PS_LOG_INFO, "No rows found");
+        psFree(output);
+        return true;
+    }
+    if (!ippdbPrintMetadatas(stdout, output, "receiveSource", !simple)) {
+        psError(PS_ERR_UNKNOWN, false, "Failed to print array");
+        psFree(output);
+        return false;
+    }
+    psFree(output);
+
+    return true;
+}
+
+static bool addfilesetMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    // required
+    PXOPT_LOOKUP_S64(source_id, config->args, "-src_id", true, false);
+    psMetadataItem *filesets = psMetadataLookup(config->args, "-fileset");
+    if (!filesets) {
+        psError(PS_ERR_UNKNOWN, true, "-fileset is required");
+        return false;
+    }
+
+    psString query = pxDataGet("receivetool_addfileset.sql");
+    if (!query) {
+        psError(PXTOOLS_ERR_DATA, false, "Failed to retreive SQL statement");
+        return false;
+    }
+
+    if (!psDBTransaction(config->dbh)) {
+        psError(PS_ERR_UNKNOWN, false, "Database error");
+        return false;
+    }
+
+    psString source_id_str = NULL;      // source_id as a string
+    psStringAppend(&source_id_str, "%" PRId64, source_id);
+
+    psListIterator *iter = psListIteratorAlloc(filesets->data.list, PS_LIST_HEAD, false); // Iterator
+    psMetadataItem *item = NULL;        // Item from iteration
+    while ((item = psListGetAndIncrement(iter))) {
+        psAssert(item && item->data.V && item->type == PS_DATA_STRING, "Argument is bad");
+        const char *fileset = item->data.str; // Fileset name
+
+        if (!p_psDBRunQueryF(config->dbh, query, source_id_str, fileset)) {
+            psError(PS_ERR_UNKNOWN, false, "Database error");
+            psFree(source_id_str);
+            psFree(query);
+            psFree(iter);
+            if (!psDBTransaction(config->dbh)) {
+                psError(PS_ERR_UNKNOWN, false, "Database error");
+            }
+            return false;
+        }
+
+        psArray *output = p_psDBFetchResult(config->dbh); // Output of query
+        if (!output) {
+            psError(PS_ERR_UNKNOWN, false, "Database error");
+            psFree(source_id_str);
+            psFree(query);
+            psFree(iter);
+            return false;
+        }
+        if (psArrayLength(output) > 0) {
+            psTrace("receivetool", PS_LOG_INFO, "Fileset %s is already present", fileset);
+            psFree(output);
+            continue;
+        }
+        psFree(output);
+
+        if (!receiveFilesetInsert(config->dbh, 0, source_id, fileset, "reg", NULL, NULL, 0)) {
+            psError(PS_ERR_UNKNOWN, false, "Unable to add fileset");
+            psFree(source_id_str);
+            psFree(query);
+            psFree(iter);
+            if (!psDBTransaction(config->dbh)) {
+                psError(PS_ERR_UNKNOWN, false, "Database error");
+            }
+            return false;
+        }
+    }
+    psFree(iter);
+    psFree(query);
+    psFree(source_id_str);
+
+    if (!psDBCommit(config->dbh)) {
+        psError(PS_ERR_UNKNOWN, false, "Database error");
+        return false;
+    }
+
+    return true;
+}
+
+static bool updatelastMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    // required
+    PXOPT_LOOKUP_S64(source_id, config->args, "-src_id", true, false);
+    PXOPT_LOOKUP_STR(fileset, config->args, "-fileset",  true, false);
+
+    psString query = NULL;              // Query to execute
+    psStringAppend(&query, "UPDATE receiveSource SET fileset_last = \'%s\' WHERE source_id = %" PRId64,
+                   fileset, source_id);
+
+    if (!p_psDBRunQuery(config->dbh, query)) {
+        psError(PS_ERR_UNKNOWN, false, "Database error");
+        psFree(query);
+        return false;
+    }
+    psFree(query);
+
+    return true;
+}
+
+
+static bool pendingfilesetMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    psMetadata *where = psMetadataAlloc(); // WHERE conditions
+
+    // required
+    PXOPT_COPY_STR(config->args, where, "-source", "receiveSource.source", "==");
+    PXOPT_COPY_STR(config->args, where, "-product", "receiveSource.product", "==");
+    PXOPT_COPY_STR(config->args, where, "-comment", "receiveSource.comment", "LIKE");
+
+    // optional
+    PXOPT_LOOKUP_U64(limit, config->args, "-limit", false, false);
+    PXOPT_LOOKUP_BOOL(simple, config->args, "-simple", false);
+
+    psString query = pxDataGet("receivetool_pendingfileset.sql");
+    if (!query) {
+        psError(PXTOOLS_ERR_DATA, false, "Failed to retreive SQL statement");
+        psFree(where);
+        return false;
+    }
+
+    if (psListLength(where->list)) {
+        psString whereClause = psDBGenerateWhereConditionSQL(where, NULL);
+        psStringAppend(&query, " AND %s", whereClause);
+        psFree(whereClause);
+    }
+    psFree(where);
+
+    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("receivetool", PS_LOG_INFO, "No rows found");
+        psFree(output);
+        return true;
+    }
+    if (!ippdbPrintMetadatas(stdout, output, "receiveFileset", !simple)) {
+        psError(PS_ERR_UNKNOWN, false, "Failed to print array");
+        psFree(output);
+        return false;
+    }
+    psFree(output);
+
+    return true;
+}
+
+static bool addfileMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    // required
+    PXOPT_LOOKUP_S64(fileset_id, config->args, "-fileset_id", true, false);
+    PXOPT_LOOKUP_STR(file_list, config->args, "-file_list", true, false);
+
+    unsigned int numBad;
+    psMetadata *files = psMetadataConfigRead(NULL, &numBad, file_list, false);
+    if (!files) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, false, "Unable to cleanly read MDC file with file list.");
+        return false;
+    }
+
+
+    if (!psDBTransaction(config->dbh)) {
+        psError(PS_ERR_UNKNOWN, false, "Database error");
+        return false;
+    }
+
+    psMetadataIterator *iter = psMetadataIteratorAlloc(files, PS_LIST_HEAD, NULL); // Iterator
+    psMetadataItem *item;
+    while ((item = psMetadataGetAndIncrement(iter))) {
+        psMetadata *md = item->data.md;
+        psString file = psMetadataLookupStr(NULL, md, "file");
+        psS64    bytes = psMetadataLookupS64(NULL, md, "bytes");
+        psString md5sum = psMetadataLookupStr(NULL, md, "md5sum");
+        psString file_type = psMetadataLookupStr(NULL, md, "file_type");
+        psString component = psMetadataLookupStr(NULL, md, "component");
+        
+        if (!file) {
+            psError(PS_ERR_UNKNOWN, false, "failed to find value for file");
+            return false;
+        }
+        if (!component) {
+            psError(PS_ERR_UNKNOWN, false, "failed to find value for component");
+            return false;
+        }
+
+        if (!receiveFileInsert(config->dbh, 0, fileset_id, file, bytes, md5sum, file_type, component)) {
+            psError(PS_ERR_UNKNOWN, false, "Unable to add file");
+            if (!psDBRollback(config->dbh)) {
+                psError(PS_ERR_UNKNOWN, false, "Database error");
+                return false;
+            }
+            psFree(iter);
+            return false;
+        }
+    }
+
+    psFree(iter);
+
+    if (!psDBCommit(config->dbh)) {
+        psError(PS_ERR_UNKNOWN, false, "Database error");
+        return false;
+    }
+
+    return true;
+}
+
+static bool pendingfileMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    psMetadata *where = psMetadataAlloc(); // WHERE conditions
+
+    // required
+    PXOPT_COPY_STR(config->args, where, "-source", "receiveSource.source", "==");
+    PXOPT_COPY_STR(config->args, where, "-product", "receiveSource.product", "==");
+    PXOPT_COPY_STR(config->args, where, "-comment", "receiveSource.comment", "LIKE");
+    PXOPT_COPY_S64(config->args, where, "-fileset_id", "receiveFile.fileset_id", "==");
+    PXOPT_COPY_S64(config->args, where, "-file_id", "receiveFile.file_id", "==");
+
+    // optional
+    PXOPT_LOOKUP_U64(limit, config->args, "-limit", false, false);
+    PXOPT_LOOKUP_BOOL(simple, config->args, "-simple", false);
+
+    psString query = pxDataGet("receivetool_pendingfile.sql");
+    if (!query) {
+        psError(PXTOOLS_ERR_DATA, false, "Failed to retreive SQL statement");
+        psFree(where);
+        return false;
+    }
+
+    if (psListLength(where->list)) {
+        psString whereClause = psDBGenerateWhereConditionSQL(where, NULL);
+        psStringAppend(&query, " AND %s", whereClause);
+        psFree(whereClause);
+    }
+    psFree(where);
+
+    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("receivetool", PS_LOG_INFO, "No rows found");
+        psFree(output);
+        return true;
+    }
+    if (!ippdbPrintMetadatas(stdout, output, "receiveFile", !simple)) {
+        psError(PS_ERR_UNKNOWN, false, "Failed to print array");
+        psFree(output);
+        return false;
+    }
+    psFree(output);
+
+    return true;
+}
+
+static bool addresultMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    // required
+    PXOPT_LOOKUP_S64(file_id, config->args, "-file_id", true, false);
+
+    // optional
+    PXOPT_LOOKUP_F32(dtime_copy, config->args, "-dtime_copy", false, false);
+    PXOPT_LOOKUP_F32(dtime_extract, config->args, "-dtime_extract", false, false);
+    PXOPT_LOOKUP_S32(fault, config->args, "-fault", false, false);
+
+    if (!receiveResultInsert(config->dbh, file_id, dtime_copy, dtime_extract, fault)) {
+        psError(PS_ERR_UNKNOWN, false, "Database error");
+        return false;
+    }
+
+    return true;
+}
+
+static bool revertMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    psMetadata *where = psMetadataAlloc(); // WHERE conditions
+
+    PXOPT_COPY_S64(config->args, where, "-fileset_id", "receiveResult.fileset_id", "==");
+    PXOPT_COPY_S32(config->args, where, "-fault", "receiveResult.fault", "==");
+    PXOPT_COPY_STR(config->args, where, "-source", "receiveSource.source", "==");
+    PXOPT_COPY_STR(config->args, where, "-product", "receiveSource.product", "==");
+    PXOPT_COPY_STR(config->args, where, "-comment", "receiveSource.comment", "LIKE");
+
+    psString query = pxDataGet("receivetool_revert.sql");
+    if (!query) {
+        psError(PXTOOLS_ERR_DATA, false, "Failed to retreive SQL statement");
+        psFree(where);
+        return false;
+    }
+
+    if (psListLength(where->list)) {
+        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);
+
+    return true;
+}
+static bool toadvanceMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    psMetadata *where = psMetadataAlloc(); // WHERE conditions
+
+    // optional
+    PXOPT_LOOKUP_U64(limit, config->args, "-limit", false, false);
+    PXOPT_LOOKUP_BOOL(simple, config->args, "-simple", false);
+
+    psString query = pxDataGet("receivetool_toadvance.sql");
+    if (!query) {
+        psError(PXTOOLS_ERR_DATA, false, "Failed to retreive SQL statement");
+        psFree(where);
+        return false;
+    }
+
+    if (psListLength(where->list)) {
+        psString whereClause = psDBGenerateWhereConditionSQL(where, NULL);
+        psStringAppend(&query, " AND %s", whereClause);
+        psFree(whereClause);
+    }
+    psFree(where);
+
+    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("receivetool", PS_LOG_INFO, "No rows found");
+        psFree(output);
+        return true;
+    }
+    if (!ippdbPrintMetadatas(stdout, output, "toadvanceFilesets", !simple)) {
+        psError(PS_ERR_UNKNOWN, false, "Failed to print array");
+        psFree(output);
+        return false;
+    }
+    psFree(output);
+
+    return true;
+}
+
+static bool updatefilesetMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    // required
+    PXOPT_LOOKUP_S64(fileset_id, config->args, "-fileset_id", true, false);
+
+    // to chanage
+    PXOPT_LOOKUP_S32(fault, config->args, "-fault", false, false);
+    PXOPT_LOOKUP_STR(destdir, config->args, "-destdir", false, false);
+    PXOPT_LOOKUP_STR(dirinfo, config->args, "-dirinfo", false, false);
+    PXOPT_LOOKUP_STR(dbinfo, config->args, "-dbinfo", false, false);
+    PXOPT_LOOKUP_STR(state, config->args, "-set_state", false, false);
+
+    if (!fault && !dirinfo &&!dbinfo && !state) {
+        psError(PS_ERR_UNKNOWN, true, "at least one of -fault, -dirinfo, -dbinfo, -set_state are required");
+        return false;
+    }
+
+    psString query = NULL;              // Query to execute
+    psStringAppend(&query, "UPDATE receiveFileset SET ");
+    
+    psString sep = "";
+    if (fault) {
+        psStringAppend(&query, "%s fault = %d", sep, fault);
+        sep = ",";
+    }
+    if (dirinfo) {
+        psStringAppend(&query, "%s dirinfo = '%s'", sep, dirinfo);
+        sep = ",";
+    }
+    if (dbinfo) {
+        psStringAppend(&query, "%s dbinfo = '%s'", sep, dbinfo);
+        sep = ",";
+    }
+    if (state) {
+        psStringAppend(&query, "%s state = '%s'", sep, state);
+        sep = ",";
+    }
+    
+    psStringAppend(&query, " WHERE fileset_id = %" PRId64, fileset_id);
+
+    if (!p_psDBRunQuery(config->dbh, query)) {
+        psError(PS_ERR_UNKNOWN, false, "Database error");
+        psFree(query);
+        return false;
+    }
+    psFree(query);
+
+    return true;
+}
Index: /branches/pap_mops/ippTools/src/pubtool.h
===================================================================
--- /branches/pap_mops/ippTools/src/pubtool.h	(revision 24377)
+++ /branches/pap_mops/ippTools/src/pubtool.h	(revision 24377)
@@ -0,0 +1,37 @@
+/*
+ * pubtool.h
+ *
+ * Copyright (C) 2008 IfA
+ *
+ * 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 PUBTOOL_H
+#define PUBTOOL_H 1
+
+#include "pxtools.h"
+
+typedef enum {
+    PUBTOOL_MODE_NONE      = 0x0,
+    PUBTOOL_MODE_DEFINECLIENT,
+    PUBTOOL_MODE_DEFINERUN,
+    PUBTOOL_MODE_PENDING,
+    PUBTOOL_MODE_ADD,
+    PUBTOOL_MODE_REVERT,
+    PUBTOOL_MODE_ADVANCE,
+} pubtoolMode;
+
+pxConfig *pubtoolConfig(pxConfig *config, int argc, char **argv);
+
+#endif // PUBTOOL_H
Index: /branches/pap_mops/ippTools/src/pubtoolConfig.c
===================================================================
--- /branches/pap_mops/ippTools/src/pubtoolConfig.c	(revision 24377)
+++ /branches/pap_mops/ippTools/src/pubtoolConfig.c	(revision 24377)
@@ -0,0 +1,118 @@
+/*
+ * pubtoolConfig.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 "pubtool.h"
+
+pxConfig *pubtoolConfig(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!");
+        psFree(config);
+        return NULL;
+    }
+
+    // -defineclient
+    psMetadata *defineclientArgs = psMetadataAlloc();
+    psMetadataAddStr(defineclientArgs, PS_LIST_TAIL, "-stage", 0, "define stage (required)", NULL);
+    psMetadataAddStr(defineclientArgs, PS_LIST_TAIL, "-comment", 0, "define comment", NULL);
+    psMetadataAddStr(defineclientArgs, PS_LIST_TAIL, "-destination", 0, "define destination (required)", NULL);
+    psMetadataAddStr(defineclientArgs, PS_LIST_TAIL, "-format", 0, "define format", NULL);
+
+    // -definerun
+    psMetadata *definerunArgs = psMetadataAlloc();
+    psMetadataAddS64(definerunArgs, PS_LIST_TAIL, "-client_id", 0, "define client_id (required)", 0);
+    psMetadataAddS64(definerunArgs, PS_LIST_TAIL, "-set_label", 0, "define label", 0);
+    psMetadataAddS64(definerunArgs, PS_LIST_TAIL, "-label", 0, "search by label", 0);
+
+    // -pending
+    psMetadata *pendingArgs = psMetadataAlloc();
+    psMetadataAddStr(pendingArgs, PS_LIST_TAIL, "-client_id", 0, "search on client_id", NULL);
+    psMetadataAddStr(pendingArgs, PS_LIST_TAIL, "-stage", 0, "search on source", NULL);
+    psMetadataAddStr(pendingArgs, PS_LIST_TAIL, "-comment", 0, "search on comment (LIKE)", NULL);
+    psMetadataAddBool(pendingArgs, PS_LIST_TAIL, "-simple",  0, "use simple output format?", false);
+    psMetadataAddU64(pendingArgs, PS_LIST_TAIL, "-limit",  0, "limit result set", 0);
+    psMetadataAddStr(pendingArgs, PS_LIST_TAIL, "-label", 0, "search on label", NULL);
+
+    // -add
+    psMetadata *addArgs = psMetadataAlloc();
+    psMetadataAddS64(addArgs, PS_LIST_TAIL, "-pub_id", 0, "define pub_id (required)", 0);
+    psMetadataAddStr(addArgs, PS_LIST_TAIL, "-component", 0, "define component (required)", NULL);
+    psMetadataAddS32(addArgs, PS_LIST_TAIL, "-fault", 0, "define fault code", 0);
+
+    // -revert
+    psMetadata *revertArgs = psMetadataAlloc();
+    psMetadataAddS64(revertArgs, PS_LIST_TAIL, "-pub_id", 0, "search on pub_id", 0);
+    psMetadataAddStr(revertArgs, PS_LIST_TAIL, "-component", 0, "search on component", NULL);
+    psMetadataAddS32(revertArgs, PS_LIST_TAIL, "-fault", 0, "search on fault code", 0);
+    psMetadataAddStr(revertArgs, PS_LIST_TAIL, "-client_id", 0, "search on client_id", NULL);
+    psMetadataAddStr(revertArgs, PS_LIST_TAIL, "-comment", 0, "search on comment (LIKE)", NULL);
+    psMetadataAddStr(revertArgs, PS_LIST_TAIL, "-label", 0, "search on label", NULL);
+
+    // -advance
+    psMetadata *advanceArgs = psMetadataAlloc();
+    psMetadataAddS64(advanceArgs, PS_LIST_TAIL, "-pub_id", 0, "search on pub_id", 0);
+    psMetadataAddStr(advanceArgs, PS_LIST_TAIL, "-label", 0, "search on label", NULL);
+
+    psMetadata *argSets = psMetadataAlloc();
+    psMetadata *modes = psMetadataAlloc();
+
+    PXOPT_ADD_MODE("-defineclient", "", PUBTOOL_MODE_DEFINECLIENT, defineclientArgs);
+    PXOPT_ADD_MODE("-definerun", "", PUBTOOL_MODE_DEFINERUN, definerunArgs);
+    PXOPT_ADD_MODE("-pending", "", PUBTOOL_MODE_PENDING, pendingArgs);
+    PXOPT_ADD_MODE("-add", "", PUBTOOL_MODE_ADD, addArgs);
+    PXOPT_ADD_MODE("-revert", "", PUBTOOL_MODE_REVERT, revertArgs);
+    PXOPT_ADD_MODE("-advance", "", PUBTOOL_MODE_ADVANCE, advanceArgs);
+
+    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);
+
+    // define database handle, if used
+    // do this last so we don't setup a connection before CLI options are validated
+    config->dbh = psMemIncrRefCounter(pmConfigDB(config->modules));
+    if (!config->dbh) {
+        psError(PS_ERR_UNKNOWN, false, "Can't configure database");
+        psFree(config);
+        return NULL;
+    }
+
+    return config;
+}
