Index: trunk/ippTools/src/camtool.c
===================================================================
--- trunk/ippTools/src/camtool.c	(revision 9310)
+++ trunk/ippTools/src/camtool.c	(revision 9311)
@@ -53,5 +53,6 @@
 
     psString query = psStringCopy(
-            "SELECT * FROM p3PendingExp"
+            "SELECT p3PendingExp.*"
+            " FROM p3PendingExp"
             " LEFT JOIN p3ProcessedExp"
             "   USING(exp_tag)"
@@ -169,4 +170,177 @@
     PS_ASSERT_PTR_NON_NULL(config, false);
 
+    // det_id, exp_tag, recip, -bg, -bg_stdev, & -bg_mean_stdev
+    // are required
+    bool status = false;
+    psString exp_tag = psMetadataLookupStr(&status, config->args, "-exp_tag");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -exp_tag");
+        return false;
+    }
+    if (!exp_tag) {
+        psError(PS_ERR_UNKNOWN, true, "-exp_tag 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;
+    }
+    psString recipe = psMetadataLookupStr(&status, config->args, "-recip");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -recip");
+        return false;
+    }
+    if (!recipe) {
+        psError(PS_ERR_UNKNOWN, true, "-recip is required");
+        return false;
+    }
+    psF64 bg = psMetadataLookupF64(&status, config->args, "-bg");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -bg");
+        return false;
+    }
+    if (isnan(bg)) {
+        psError(PS_ERR_UNKNOWN, true, "-bg is required");
+        return false;
+    }
+    psF64 bg_stdev = psMetadataLookupF64(&status, config->args, "-bg_stdev");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -bg_stdev");
+        return false;
+    }
+    if (isnan(bg_stdev)) {
+        psError(PS_ERR_UNKNOWN, true, "-bg_stdev is required");
+        return false;
+    }
+    psF64 bg_mean_stdev = psMetadataLookupF64(&status, config->args, "-bg_mean_stdev");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -bg_mean_stdev");
+        return false;
+    }
+    if (isnan(bg_mean_stdev)) {
+        psError(PS_ERR_UNKNOWN, true, "-bg_mean_stdev is required");
+        return false;
+    }
+    psF32 sigma_ra = psMetadataLookupF32(&status, config->args, "-sigma_ra");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -sigma_ra");
+        return false;
+    }
+    if (isnan(sigma_ra)) {
+        psError(PS_ERR_UNKNOWN, true, "-sigma_ra is required");
+        return false;
+    }
+    psF32 sigma_dec = psMetadataLookupF32(&status, config->args, "-sigma_dec");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -sigma_dec");
+        return false;
+    }
+    if (isnan(sigma_dec)) {
+        psError(PS_ERR_UNKNOWN, true, "-sigma_dec is required");
+        return false;
+    }
+    psS32 nastro = psMetadataLookupS32(&status, config->args, "-nastro");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -nastro");
+        return false;
+    }
+    if (nastro < 0) {
+        psError(PS_ERR_UNKNOWN, true, "-nastro is required");
+        return false;
+    }
+    psF32 zp_mean = psMetadataLookupF32(&status, config->args, "-zp_mean");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -zp_mean");
+        return false;
+    }
+    if (isnan(zp_mean)) {
+        psError(PS_ERR_UNKNOWN, true, "-zp_mean is required");
+        return false;
+    }
+    psF32 zp_stdev = psMetadataLookupF32(&status, config->args, "-zp_stdev");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -zp_stdev");
+        return false;
+    }
+    if (isnan(zp_stdev)) {
+        psError(PS_ERR_UNKNOWN, true, "-zp_stdev is required");
+        return false;
+    }
+    // optional
+    psString b1_uri = psMetadataLookupStr(&status, config->args, "-b1_uri");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -b1_uri");
+        return false;
+    }
+    psString b2_uri = psMetadataLookupStr(&status, config->args, "-b2_uri");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -b2_uri");
+        return false;
+    }
+
+    psString query = psStringCopy(
+        "SELECT p3PendingExp.*"
+        " FROM p3PendingExp"
+        " LEFT JOIN p3ProcessedExp"
+        "   USING(exp_tag)"
+        " WHERE"
+        "   p3ProcessedExp.exp_tag IS NULL"
+        "   AND p3PendingExp.exp_tag = '%s'"
+    );
+
+    if (!p_psDBRunQuery(config->dbh, query, exp_tag)) {
+        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)) {
+        // XXX check psError here
+        psError(PS_ERR_UNKNOWN, false, "no p3PendingExp rows found");
+        return true;
+    }
+
+    p3PendingExpRow *pendingRow = p3PendingExpObjectFromMetadata(output->data[0]);
+    psFree(output);
+    // create a new p3ProcessedImfile object
+    p3ProcessedExpRow *row = p3ProcessedExpRowAlloc(
+        exp_tag,
+        uri,
+        recipe,
+        bg,
+        bg_stdev,
+        bg_mean_stdev,
+        sigma_ra,
+        sigma_dec,
+        nastro,
+        b1_uri,
+        b2_uri,
+        zp_mean,
+        zp_stdev,
+        pendingRow->p2_version,
+        pendingRow->p3_version
+    );
+    psFree(pendingRow);
+
+    // insert the new row into the p3ProcessedImfile table
+    if (!p3ProcessedExpInsertObject(config->dbh, row)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(row);
+        return false;
+    }
+
+    psFree(row);
+
     return true;
 }
