Index: /trunk/Ohana/src/opihi/dvo/Makefile
===================================================================
--- /trunk/Ohana/src/opihi/dvo/Makefile	(revision 19578)
+++ /trunk/Ohana/src/opihi/dvo/Makefile	(revision 19579)
@@ -78,4 +78,5 @@
 $(SRC)/lightcurve.$(ARCH).o	  	\
 $(SRC)/mextract.$(ARCH).o	  	\
+$(SRC)/mmextract.$(ARCH).o	  	\
 $(SRC)/photcodes.$(ARCH).o	  	\
 $(SRC)/pmeasure.$(ARCH).o	  	\
Index: /trunk/Ohana/src/opihi/dvo/avextract.c
===================================================================
--- /trunk/Ohana/src/opihi/dvo/avextract.c	(revision 19578)
+++ /trunk/Ohana/src/opihi/dvo/avextract.c	(revision 19579)
@@ -3,5 +3,5 @@
 int avextract (int argc, char **argv) {
   
-  int i, j, n, m, N, Npts, NPTS, last, Nfields, Nreturn, Ncstack, Nstack;
+  int i, j, n, m, N, Npts, NPTS, last, next, state, Nfields, Nreturn, Ncstack, Nstack;
   int Nsecfilt, mode, VERBOSE;
   char **cstack, name[1024];
@@ -48,4 +48,9 @@
   fields = dbCmdlineFields (argc, argv, DVO_TABLE_AVERAGE, &last, &Nfields);
   if (fields == NULL) return (FALSE);
+
+  // examine line for 'where' or 'match to'.  'match to' is forbidden
+  state = dbCmdlineConditions (argc, argv, last, &next);
+  if (state == DVO_DB_CMDLINE_ERROR) goto escape;
+  if (state == DVO_DB_CMDLINE_IS_MATCH) goto escape; // not allowed for mextract
 
   // parse the remainder of the line as a boolean math expression
Index: /trunk/Ohana/src/opihi/dvo/dbCmdlineFields.c
===================================================================
--- /trunk/Ohana/src/opihi/dvo/dbCmdlineFields.c	(revision 19578)
+++ /trunk/Ohana/src/opihi/dvo/dbCmdlineFields.c	(revision 19579)
@@ -1,5 +1,46 @@
 # include "dvoshell.h"
 
-// identify the fields to be extracted (test for where, check syntax)
+// check for 'where' or 'matched to'; return first field after the word, and the value
+int dbCmdlineConditions (int argc, char **argv, int first, int *nextField) {
+
+  int isWhere, isMatch;
+
+  *nextField = argc;
+
+  // require 'where' or 'matched to' before boolean math
+  if (first == argc) return DVO_DB_CMDLINE_IS_END;
+
+  isWhere = !strcasecmp(argv[first], "where");
+  isMatch = !strcasecmp(argv[first], "match");
+    
+  if (!isMatch && !isWhere) {
+    gprint (GP_ERR, "WHERE or MATCH TO\n");
+    return DVO_DB_CMDLINE_ERROR;
+  }
+  if (isMatch && ((first + 1 >= argc - 1) || strcasecmp(argv[first+1], "to"))) {
+    gprint (GP_ERR, "missing boolean expression\n");
+    return DVO_DB_CMDLINE_ERROR;
+  }
+  if (isWhere && (first >= argc - 1)) {
+    gprint (GP_ERR, "missing boolean expression\n");
+    return DVO_DB_CMDLINE_ERROR;
+  }
+
+  if (isWhere) {
+    *nextField = first + 1;
+    return DVO_DB_CMDLINE_IS_WHERE;
+  }
+
+  if (isMatch) {
+    *nextField = first + 2;
+    return DVO_DB_CMDLINE_IS_MATCH;
+  }
+
+  gprint (GP_ERR, "programming error?\n");
+  return DVO_DB_CMDLINE_ERROR;
+}
+
+// identify the fields to be extracted (check syntax)
+// the 'last' pointer ends on the first word after the fields (where, match or EOL) 
 dbField *dbCmdlineFields (int argc, char **argv, int table, int *last, int *nfields) {
 
@@ -15,5 +56,5 @@
 
   // examine each argv[i] entry until we reach a 'where' or a 'matched' 
-  for (i = 1; (i < argc) && strcasecmp (argv[i], "where") && strcasecmp (argv[i], "matched"); i++) {
+  for (i = 1; (i < argc) && strcasecmp (argv[i], "where") && strcasecmp (argv[i], "match"); i++) {
     // split the word by ","
     p = argv[i];
@@ -50,20 +91,4 @@
   }
 
-  // require 'where' or 'matched to' before boolean math
-  // XXX push this to a test up one level
-  if (i != argc) {
-    if (strcasecmp(argv[i], "where")) {
-      gprint (GP_ERR, "syntax error\n");
-      free (fields);
-      return (NULL);
-    }
-    if (i > argc - 2) {
-      gprint (GP_ERR, "missing boolean expression\n");
-      free (fields);
-      return (NULL);
-    }
-    i++; // skip over the 'where'
-  }
-
   *last = i;
   *nfields = Nfields;
Index: /trunk/Ohana/src/opihi/dvo/init.c
===================================================================
--- /trunk/Ohana/src/opihi/dvo/init.c	(revision 19578)
+++ /trunk/Ohana/src/opihi/dvo/init.c	(revision 19579)
@@ -40,4 +40,5 @@
 int lightcurve      PROTO((int, char **));
 int mextract        PROTO((int, char **));
+int mmextract        PROTO((int, char **));
 int pcat            PROTO((int, char **));
 int photcodes       PROTO((int, char **));
@@ -90,4 +91,5 @@
   {1, "lightcurve",  lightcurve,   "extract lightcurve for a star"},
   {1, "mextract",    mextract,     "extract measure data values"},
+  {1, "mmextract",   mmextract,    "extract joined measurements"},
   {1, "pcat",        skycat,       "plot catalog boundaries"},
   {1, "photcodes",   photcodes,    "list photometry codes"},
Index: /trunk/Ohana/src/opihi/dvo/mextract.c
===================================================================
--- /trunk/Ohana/src/opihi/dvo/mextract.c	(revision 19578)
+++ /trunk/Ohana/src/opihi/dvo/mextract.c	(revision 19579)
@@ -3,5 +3,5 @@
 int mextract (int argc, char **argv) {
   
-  int i, j, k, m, n, N, Npts, NPTS, last, Nfields, Nreturn, Ncstack, Nstack;
+  int i, j, k, m, n, N, Npts, NPTS, last, next, state, Nfields, Nreturn, Ncstack, Nstack;
   int Nsecfilt, VERBOSE, loadImages, mosaicMode;
   char **cstack, name[1024];
@@ -50,8 +50,11 @@
   if (fields == NULL) return (FALSE);
 
-  // XXX add the skyregion limits as if it were a where statement
+  // examine line for 'where' or 'match to'.  'match to' is forbidden
+  state = dbCmdlineConditions (argc, argv, last, &next);
+  if (state == DVO_DB_CMDLINE_ERROR) goto escape;
+  if (state == DVO_DB_CMDLINE_IS_MATCH) goto escape; // not allowed for mextract
 
   // parse the remainder of the line as a boolean math expression
-  cstack = isolate_elements (argc-last, &argv[last], &Ncstack);
+  cstack = isolate_elements (argc-next, &argv[next], &Ncstack);
   
   // construct the db Boolean math stack (frees cstack)
Index: /trunk/Ohana/src/opihi/dvo/mmextract.c
===================================================================
--- /trunk/Ohana/src/opihi/dvo/mmextract.c	(revision 19578)
+++ /trunk/Ohana/src/opihi/dvo/mmextract.c	(revision 19579)
@@ -3,8 +3,10 @@
 int mmextract (int argc, char **argv) {
   
-  int i, j, k, m, n, N, Npts, NPTS, last, Nfields, Nreturn, Ncstack, Nstack;
+  int i, j, k, m, n, N, Npts, NPTS, last, next, state;
+  int Nfields, Nreturn, Nreturn_base, Ncstack1, Ncstack2, Nstack1, Nstack2;
+  int Nwhere, Iwhere, Nmatch, Imatch, NTABLE, Nt1, Nt2, n1, n2;
   int Nsecfilt, VERBOSE, loadImages, mosaicMode;
-  char **cstack, name1[1024], name2[1024];
-  float *values;
+  char **cstack1, **cstack2, name1[1024], name2[1024];
+  float *values, **table1, **table2;
   void *Signal;
 
@@ -14,5 +16,6 @@
   Vector **vec;
   dbField *fields;
-  dbStack *stack;
+  dbStack *stack1;
+  dbStack *stack2;
   SkyRegionSelection *selection;
 
@@ -21,5 +24,6 @@
   code = NULL;
   fields = NULL;
-  stack = NULL;
+  stack1 = NULL;
+  stack2 = NULL;
 
   if ((N = get_argument (argc, argv, "-h"))) goto help;
@@ -51,23 +55,44 @@
   // -noauto means imageID is not matched
 
-  // parse the fields to be extracted and returned
+  // parse the fields to be extracted and returned : last points to end, or first 'where' or 'matched'
   fields = dbCmdlineFields (argc, argv, DVO_TABLE_MEASURE, &last, &Nfields);
   if (fields == NULL) return (FALSE);
 
-  // XXX require a 'where' and a 'matched to'? if not, all are cross-matched (that's ok)
-  // XXX add the skyregion limits as if it were a where statement
-
-  // need to find the location of 'where' and 'matched to' expressions
-  for (i = last; i < argc; i++) {
-    if (!strcasecmp (argv[i], "matched")) {
-    }
-  }
-
-  whereS = N; whereE = N;
-  matchS = N; matchE = N;
+  // examine line for 'where' and 'match to'.  neither is required, but order is fixed
+  state = dbCmdlineConditions (argc, argv, last, &next);
+  if (state == DVO_DB_CMDLINE_ERROR) goto escape;
+
+  if (state == DVO_DB_CMDLINE_IS_END) {
+      Nwhere = Nmatch = 0;
+      Iwhere = Imatch = 0;
+  }
+
+  if (state == DVO_DB_CMDLINE_IS_MATCH) {
+      Nwhere = 0;
+      Iwhere = 0;
+      Imatch = next;
+      Nmatch = argc - next;
+  }
+
+  if (state == DVO_DB_CMDLINE_IS_WHERE) {
+      Iwhere = next;
+      // find the end or the 'match to'
+      for (last = next; (last < argc) && strcasecmp (argv[last], "match"); last++);
+      state = dbCmdlineConditions (argc, argv, last, &next);
+      if (state == DVO_DB_CMDLINE_ERROR) goto escape;
+      if (state == DVO_DB_CMDLINE_IS_WHERE) goto escape;
+      if (state == DVO_DB_CMDLINE_IS_END) {
+	  Nwhere = argc - Iwhere;
+	  Imatch = Nmatch = 0;
+      } else {
+	  Nwhere = last - Iwhere;
+	  Imatch = next;
+	  Nmatch = argc - Imatch;
+      }
+  }
 
   // parse the 'where' and 'matched to' segments of the line as boolean math expressions
-  cstack1 = isolate_elements (Nwhere, &argv[whereS], &Ncstack1);
-  cstack2 = isolate_elements (Nmatch, &argv[matchS], &Ncstack2);
+  cstack1 = isolate_elements (Nwhere, &argv[Iwhere], &Ncstack1);
+  cstack2 = isolate_elements (Nmatch, &argv[Imatch], &Ncstack2);
   
   // construct the db Boolean math stack (frees cstack)
@@ -88,5 +113,6 @@
 
   // add the skyregion limits to the where statement (or create)
-  dbAstroRegionLimits (&stack, &Nstack, selection, DVO_TABLE_MEASURE);
+  dbAstroRegionLimits (&stack1, &Nstack1, selection, DVO_TABLE_MEASURE);
+  dbAstroRegionLimits (&stack2, &Nstack2, selection, DVO_TABLE_MEASURE);
 
   // parse stack elements into fields and scalars as needed
@@ -133,9 +159,9 @@
   // we save the selected measures for each average to temporary tables 1 and 2
   NTABLE = 100;
-  ALLOCATE (table1, double *, Nreturn_base);
-  ALLOCATE (table2, double *, Nreturn_base);
+  ALLOCATE (table1, float *, Nreturn_base);
+  ALLOCATE (table2, float *, Nreturn_base);
   for (i = 0; i < Nreturn_base; i++) {
-    ALLOCATE (table1[i], double, NTABLE);
-    ALLOCATE (table2[i], double, NTABLE);
+    ALLOCATE (table1[i], float, NTABLE);
+    ALLOCATE (table2[i], float, NTABLE);
   }
 
@@ -188,7 +214,7 @@
 	  for (n = 0; n < Nreturn_base; n++) {
 	    table1[n][Nt1] = values[n];
-	    Nt1 ++;
 	    // fprintf (stderr, "keep : field: %s, value: %f\n", fields[n].name, values[n]);
 	  }
+	  Nt1 ++;
 	}
 	// test the second conditional statement
@@ -196,24 +222,22 @@
 	  for (n = 0; n < Nreturn_base; n++) {
 	    table2[n][Nt2] = values[n];
-	    Nt2 ++;
 	    // fprintf (stderr, "keep : field: %s, value: %f\n", fields[n].name, values[n]);
 	  }
-	}
-
-	// XXX now do the join :: need to filter against automatch if -noauto is selected (record the index value k to test)
-	for (n1 = 0; n1 < Nt1; n1++) {
-	  for (n2 = 0; n2 < Nt2; n2++) {
-	    for (n = 0; n < Nreturn_base; n++) {
-	      vec[n][0].elements[Npts] = table1[n][n1];
-	    }
-	    for (n = 0; n < Nreturn_base; n++) {
-	      vec[n+Nreturn_base][0].elements[Npts] = table2[n][n2];
-	    }
-	    Npts++;
-	    if (Npts >= NPTS) {
-	      NPTS += 2000;
-	      for (n = 0; n < Nreturn; n++) {
-		REALLOCATE (vec[n][0].elements, float, NPTS);
-	      }
+	  Nt2 ++;
+	}
+      }
+
+      // XXX now do the join :: need to filter against automatch if -noauto is selected (record the index value k to test)
+      for (n1 = 0; n1 < Nt1; n1++) {
+	for (n2 = 0; n2 < Nt2; n2++) {
+	  for (n = 0; n < Nreturn_base; n++) {
+	    vec[2*n+0][0].elements[Npts] = table1[n][n1];
+	    vec[2*n+1][0].elements[Npts] = table2[n][n2];
+	  }
+	  Npts++;
+	  if (Npts >= NPTS) {
+	    NPTS += 2000;
+	    for (n = 0; n < Nreturn; n++) {
+	      REALLOCATE (vec[n][0].elements, float, NPTS);
 	    }
 	  }
@@ -221,4 +245,5 @@
       }
     }
+
     dvo_catalog_free (&catalog);
     // dbStackAllocPrint ();
@@ -235,7 +260,18 @@
   }
 
+  for (n = 0; n < Nreturn_base; n++) {
+    free (table1[n]);
+    free (table2[n]);
+  }
+  free (table1);
+  free (table2);
+
+  free (values);
+
   dbFreeFields (fields, Nfields);
-  dbFreeStack (stack, Nstack);
-  free (stack);
+  dbFreeStack (stack1, Nstack1);
+  dbFreeStack (stack2, Nstack2);
+  free (stack1);
+  free (stack2);
   FreeImageSelection ();
   SkyListFree (skylist);
@@ -245,6 +281,8 @@
 escape:
   dbFreeFields (fields, Nfields);
-  dbFreeStack (stack, Nstack);
-  free (stack);
+  dbFreeStack (stack1, Nstack1);
+  dbFreeStack (stack2, Nstack2);
+  free (stack1);
+  free (stack2);
   FreeImageSelection ();
   SkyListFree (skylist);
Index: /trunk/Ohana/src/opihi/include/dvoshell.h
===================================================================
--- /trunk/Ohana/src/opihi/include/dvoshell.h	(revision 19578)
+++ /trunk/Ohana/src/opihi/include/dvoshell.h	(revision 19579)
@@ -119,4 +119,5 @@
 
 enum {DVO_TABLE_AVERAGE, DVO_TABLE_MEASURE};
+enum {DVO_DB_CMDLINE_ERROR, DVO_DB_CMDLINE_IS_END, DVO_DB_CMDLINE_IS_WHERE, DVO_DB_CMDLINE_IS_MATCH}; 
 
 // options for selecting the ra,dec limits of the db selections
