Index: trunk/ippTools/src/stacktool.c
===================================================================
--- trunk/ippTools/src/stacktool.c	(revision 16678)
+++ trunk/ippTools/src/stacktool.c	(revision 16687)
@@ -95,4 +95,189 @@
 {
     PS_ASSERT_PTR_NON_NULL(config, false);
+
+    // required options
+    PXOPT_LOOKUP_STR(workdir, config->args, "-workdir", true, false);
+
+    // default
+    PXOPT_LOOKUP_BOOL(simple, config->args, "-simple", false);
+    PXOPT_LOOKUP_TIME(registered, config->args, "-registered", false, false);
+
+
+    psString query = pxDataGet("stacktool_find_complete_warps.sql");
+    if (!query) {
+        psError(PXTOOLS_ERR_DATA, false, "failed to retreive SQL statement");
+        return false;
+    }
+
+    if (config->where) {
+        psString whereClause = psDBGenerateWhereConditionSQL(config->where, NULL);
+        psStringAppend(&query, " AND %s", whereClause);
+        psFree(whereClause);
+    }
+
+    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("stacktool", PS_LOG_INFO, "no rows found");
+        psFree(output);
+        return true;
+    }
+
+    psHash *stacks = psHashAlloc(psArrayLength(output));
+
+    // loop over the array of metadata
+    for (long i = 0; i < psArrayLength(output); i++) {
+        psMetadata *md = output->data[i];
+        
+        // pull out the warp_id, skycell_id, & tess_id
+        bool status;
+        psString skycell_id = psMetadataLookupStr(&status, md, "skycell_id");
+        if (!status) {
+            psError(PS_ERR_UNKNOWN, false, "failed to lookup skycell_id");
+            psFree(output);
+            return NULL;
+        }
+
+        psString tess_id = psMetadataLookupStr(&status, md, "tess_id");
+        if (!status) {
+            psError(PS_ERR_UNKNOWN, false, "failed to lookup tess_id");
+            psFree(output);
+            return NULL;
+        }
+
+        // group the warps by skycell_id & tess_id
+        psString key = NULL;
+        psStringAppend(&key, "%s:%s", skycell_id, tess_id);
+
+        // check to see if the hash key already exists
+        psArray *col = psHashLookup(stacks, key);
+        if (!col) {
+            // if it doesn't, create an new psArray for this stack
+            col = psArrayAllocEmpty(0);
+            psHashAdd(stacks, key, col);
+        }
+        // add this warp to the stack for this key
+        psArrayAdd(col, 0, md);
+        psFree(key);
+
+    }
+    psFree(output);
+
+    psArray *grouped = psHashToArray(stacks);
+    psFree(stacks);
+
+    if (!psDBTransaction(config->dbh)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+
+    // loop over groups
+    for (long i = 0; i < psArrayLength(grouped); i++) {
+        psArray *col = grouped->data[i];
+
+        // pull the skycell_id & tess_id from the first warp in the stack
+        bool status;
+        psString skycell_id = psMetadataLookupStr(&status, col->data[0], "skycell_id");
+        if (!status) {
+            psError(PS_ERR_UNKNOWN, false, "failed to lookup skycell_id");
+            psFree(grouped);
+            return NULL;
+        }
+
+        psString tess_id = psMetadataLookupStr(&status, col->data[0], "tess_id");
+        if (!status) {
+            psError(PS_ERR_UNKNOWN, false, "failed to lookup tess_id");
+            psFree(grouped);
+            return NULL;
+        }
+
+        // create a new stackRun for the group
+        stackRunRow *run = stackRunRowAlloc(
+            0,          // ID
+            "run",      // state
+            workdir,
+            NULL,       // dvodb
+            registered,
+            skycell_id,
+            tess_id
+        );
+        if (!stackRunInsertObject(config->dbh, run)) {
+            if (!psDBRollback(config->dbh)) {
+                psError(PS_ERR_UNKNOWN, false, "database error");
+            }
+            psError(PS_ERR_UNKNOWN, false, "database error");
+            psFree(run);
+            psFree(grouped);
+            return false;
+        }
+
+        // figure out the new stack_id
+        psS64 stack_id = psDBLastInsertID(config->dbh);
+        run->stack_id = stack_id;
+
+        if (!stackRunPrintObject(stdout, run, !simple)) {
+            psError(PS_ERR_UNKNOWN, false, "failed to print object");
+            psFree(run);
+            psFree(grouped);
+            return false;
+        }
+        psFree(run);
+
+        // loop over this stack and add each warp to the stackRun
+        for (long j = 0; j < psArrayLength(col); j++) {
+            bool status;
+            psS64 warp_id = psMetadataLookupS64(&status, col->data[0], "warp_id");
+            if (!status) {
+                if (!psDBRollback(config->dbh)) {
+                    psError(PS_ERR_UNKNOWN, false, "database error");
+                }
+                psError(PS_ERR_UNKNOWN, false, "failed to lookup warp_id");
+                psFree(grouped);
+                return NULL;
+            }
+
+            if (!stackInputSkyfileInsert(config->dbh,
+                stack_id,
+                warp_id
+            )) {
+                if (!psDBRollback(config->dbh)) {
+                    psError(PS_ERR_UNKNOWN, false, "database error");
+                }
+                psError(PS_ERR_UNKNOWN, false, "database error");
+                psFree(grouped);
+                return false;
+            }
+
+        }
+
+    }
+
+    // point of no return
+    if (!psDBCommit(config->dbh)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+
+    psFree(grouped);
+
     return true;
 }
