Index: trunk/Ohana/src/opihi/cmd.astro/Makefile
===================================================================
--- trunk/Ohana/src/opihi/cmd.astro/Makefile	(revision 33700)
+++ trunk/Ohana/src/opihi/cmd.astro/Makefile	(revision 33963)
@@ -46,4 +46,5 @@
 $(SRC)/ringflux.$(ARCH).o	   \
 $(SRC)/petrosian.$(ARCH).o	   \
+$(SRC)/kronflux.$(ARCH).o	   \
 $(SRC)/multifit.$(ARCH).o	   \
 $(SRC)/objload.$(ARCH).o	   \
Index: trunk/Ohana/src/opihi/cmd.astro/init.c
===================================================================
--- trunk/Ohana/src/opihi/cmd.astro/init.c	(revision 33700)
+++ trunk/Ohana/src/opihi/cmd.astro/init.c	(revision 33963)
@@ -32,4 +32,5 @@
 int ringflux                PROTO((int, char **));
 int petrosian               PROTO((int, char **));
+int kronflux                PROTO((int, char **));
 int mkgauss                 PROTO((int, char **));
 int mksersic                PROTO((int, char **));
@@ -88,4 +89,5 @@
   {1, "ringflux",    ringflux,     "mean flux in a ring"},
   {1, "petrosian",   petrosian,    "petrosian parameters given radial bins"},
+  {1, "kronflux",    kronflux,     "measure kronflux stats"},
   {1, "multifit",    multifit,     "fit multi-order spectrum"},
   {1, "objload",     objload,      "plot obj data on Ximage "},
Index: trunk/Ohana/src/opihi/cmd.astro/kronflux.c
===================================================================
--- trunk/Ohana/src/opihi/cmd.astro/kronflux.c	(revision 33963)
+++ trunk/Ohana/src/opihi/cmd.astro/kronflux.c	(revision 33963)
@@ -0,0 +1,173 @@
+# include "astro.h"
+int kronflux_stats (Matrix *matrix, int X, int Y, int radius, int maxradius, int border, float alpha);
+
+int VERBOSE = TRUE;
+
+// manual calculation of the Kron parameters  
+int kronflux (int argc, char **argv) {
+
+  int x, y, N, radius, border, maxradius;
+  float alpha;
+  Buffer *buf;
+
+  radius = 5;
+  if ((N = get_argument (argc, argv, "-radius"))) {
+    remove_argument (N, &argc, argv);
+    radius  = atof(argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  maxradius = 100;
+  if ((N = get_argument (argc, argv, "-maxradius"))) {
+    remove_argument (N, &argc, argv);
+    maxradius  = atof(argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  border = 5;
+  if ((N = get_argument (argc, argv, "-border"))) {
+    remove_argument (N, &argc, argv);
+    border  = atof(argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+  
+  alpha = 2.5;
+  if ((N = get_argument (argc, argv, "-alpha"))) {
+    remove_argument (N, &argc, argv);
+    alpha  = atof(argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+  
+  VERBOSE = TRUE;
+  if ((N = get_argument (argc, argv, "-q"))) {
+    remove_argument (N, &argc, argv);
+    VERBOSE = FALSE;
+  }
+  
+  if (argc != 4) {
+    gprint (GP_ERR, "USAGE: kronflux (buffer) x y [-radius N] [-border N] [-alpha F] [-q]\n");
+    gprint (GP_ERR, " -radius is the radius of the initial window (default 5)\n");
+    gprint (GP_ERR, " -border is the size of the sky annulus (default 5)\n");
+    gprint (GP_ERR, " -alpha  is the scale factor for kron radius vs 1st radial moment (default 2.5)\n");
+    return (FALSE);
+  }
+  if ((buf = SelectBuffer (argv[1], OLDBUFFER, TRUE)) == NULL) return (FALSE);
+  x = atof (argv[2]);
+  y = atof (argv[3]);
+
+  kronflux_stats (&buf[0].matrix, x, y, radius, maxradius, border, alpha);
+
+  return TRUE;
+}
+
+// I'd like to be able to skip the sky measurement
+// if Nborder > 0, calculate local sky as the median of the pixels in the region between radius and border
+int kronflux_stats (Matrix *matrix, int X, int Y, int radius, int maxradius, int border, float alpha) {
+
+  int i, j;
+
+  double gain;
+  char *string = get_variable ("GAIN");
+  if (string == (char *) NULL) {
+    if (VERBOSE) gprint (GP_ERR, "assuming a value of 1.0\n");
+    gain = 1.0;
+  } else {
+    gain = atof (string);
+  }
+
+  float *data = (float *) matrix->buffer;
+  int Nx = matrix->Naxis[0];
+  int Ny = matrix->Naxis[1];
+  
+  double sky = 0.0;
+  double dsky2 = 0.0;
+  if (border > 0) {
+    int radius2 = radius + border;
+
+    // allocate enough space for all pixels in the 2*radius2 + 1 square
+    int NRING = SQ(2*radius2 + 1);
+    double *ring = NULL;
+    ALLOCATE (ring, double, NRING);
+    bzero (ring, sizeof(double)*NRING);
+
+    int Nring = 0;  
+    for (j = -radius2; j <= radius2; j++) {
+      int iy = j + Y;
+      if (iy < 0) continue;
+      if (iy >= Ny) continue;
+      for (i = -radius2; i <= radius2; i++) {
+	if (hypot(i, j) <= radius) continue;
+	int ix = i + X;
+	if (ix < 0) continue;
+	if (ix >= Nx) continue;
+	ring[Nring] = data[ix + iy*Nx];
+	Nring ++;
+      }
+    }
+    dsort (ring, Nring);
+
+    int Npts = 0;
+    for (i = 0.25*Nring; i < 0.75*Nring; i++, Npts += 1.0) {
+      sky += ring[i];
+      dsky2 += ring[i]*ring[i];
+    }
+    sky = sky / Npts;
+    dsky2 = dsky2 / Npts - sky*sky;
+    free (ring);
+  }
+
+  // find the 1st radial moment
+  float flux1 = 0.0;
+  float Srf = 0.0;
+  for (j = -radius; j <= radius; j++) {
+    int iy = j + Y;
+    if (iy < 0) continue;
+    if (iy >= Ny) continue;
+    for (i = -radius; i <= radius; i++) {
+      float r = hypot(i, j);
+      if (r > radius) continue;
+      int ix = i + X;
+      if (ix < 0) continue;
+      if (ix >= Nx) continue;
+      float value = data[ix + iy*Nx] - sky;
+	
+      flux1 += value;
+      Srf += value * r;
+    }
+  }
+
+  float r1 = Srf / flux1;
+  float rK = r1 * alpha;
+
+  rK = MIN(MAX(rK, 5.0), maxradius);
+
+  // measure the flux within A*r1
+  float fluxK = 0.0;
+  for (j = -rK; j <= rK; j++) {
+    int iy = j + Y;
+    if (iy < 0) continue;
+    if (iy >= Ny) continue;
+    for (i = -rK; i <= rK; i++) {
+      float r = hypot(i, j);
+      if (r > rK) continue;
+      int ix = i + X;
+      if (ix < 0) continue;
+      if (ix >= Nx) continue;
+      float value = data[ix + iy*Nx] - sky;
+	
+      fluxK += value;
+    }
+  }
+
+  float magK = -2.5*log10(fluxK);
+  set_variable ("KronFlux", fluxK);
+  set_variable ("KronRadius", rK);
+  set_variable ("KronMag", magK);
+  set_variable ("KronF1", flux1);
+  set_variable ("KronR1", r1);
+  
+  if (VERBOSE) gprint (GP_LOG, "%f %f %f %f %f\n", magK, fluxK, rK, flux1, r1);
+
+  return TRUE;
+}
+
Index: trunk/Ohana/src/opihi/cmd.basic/Makefile
===================================================================
--- trunk/Ohana/src/opihi/cmd.basic/Makefile	(revision 33700)
+++ trunk/Ohana/src/opihi/cmd.basic/Makefile	(revision 33963)
@@ -44,4 +44,5 @@
 $(SRC)/quit.$(ARCH).o	     \
 $(SRC)/run_for.$(ARCH).o    \
+$(SRC)/run_foreach.$(ARCH).o    \
 $(SRC)/run_if.$(ARCH).o     \
 $(SRC)/run_while.$(ARCH).o  \
Index: trunk/Ohana/src/opihi/cmd.basic/init.c
===================================================================
--- trunk/Ohana/src/opihi/cmd.basic/init.c	(revision 33700)
+++ trunk/Ohana/src/opihi/cmd.basic/init.c	(revision 33963)
@@ -31,4 +31,5 @@
 int quit            PROTO((int, char **));
 int run_for         PROTO((int, char **));
+int run_foreach     PROTO((int, char **));
 int run_if          PROTO((int, char **));
 int run_while       PROTO((int, char **));
@@ -81,4 +82,5 @@
   {1, "quit",          quit,               "exit program *"},
   {1, "for",           run_for,            "for loop"}, 
+  {1, "foreach",       run_foreach,        "foreach loop"}, 
   {1, "if",            run_if,             "logical cases *"}, 
   {1, "while",         run_while,          "while loop"}, 
Index: trunk/Ohana/src/opihi/cmd.basic/input.c
===================================================================
--- trunk/Ohana/src/opihi/cmd.basic/input.c	(revision 33700)
+++ trunk/Ohana/src/opihi/cmd.basic/input.c	(revision 33963)
@@ -40,4 +40,6 @@
   fclose (infile);
 
+  if (!inlist.Nlines) gprint (GP_ERR, "WARNING: input file (%s) was empty\n", argv[1]);
+
   /* process this list */
   status = exec_loop (&inlist);
Index: trunk/Ohana/src/opihi/cmd.basic/run_for.c
===================================================================
--- trunk/Ohana/src/opihi/cmd.basic/run_for.c	(revision 33700)
+++ trunk/Ohana/src/opihi/cmd.basic/run_for.c	(revision 33963)
@@ -5,9 +5,15 @@
 int run_for (int argc, char **argv) {
 
-  int ThisList, depth, i, done, status, found, NLINES, j;
+  int N, ThisList, depth, i, done, status, found, NLINES, j;
   double start, end, delta;
   char *input, *endptr;
   double value, sign;
   Macro loop;
+
+  int Inclusive = FALSE;
+  if ((N = get_argument (argc, argv, "-incl"))) {
+    remove_argument (N, &argc, argv);
+    Inclusive = TRUE;
+  }
 
   if ((argc != 4) && (argc != 5)) {
@@ -95,4 +101,6 @@
   REALLOCATE (loop.line, char *, MAX (loop.Nlines, 1));
 
+  if (Inclusive) end += 0.01*delta;
+
   status = TRUE;
   interrupt = FALSE;
Index: trunk/Ohana/src/opihi/cmd.basic/run_foreach.c
===================================================================
--- trunk/Ohana/src/opihi/cmd.basic/run_foreach.c	(revision 33963)
+++ trunk/Ohana/src/opihi/cmd.basic/run_foreach.c	(revision 33963)
@@ -0,0 +1,114 @@
+# include "basic.h"
+# define D_NLINES 100
+static char prompt[] = ">> ";
+
+int run_foreach (int argc, char **argv) {
+
+  int ThisList, depth, i, done, status, NLINES, j;
+  char *input;
+  Macro loop;
+
+  if (argc < 2) {
+    gprint (GP_ERR, "USAGE: foreach (var) value [value value ...] -- terminate with 'END'\n");
+    return (FALSE);
+  }
+
+  NLINES = D_NLINES;
+  ALLOCATE (loop.line, char *, NLINES);
+
+  /* read in loop */
+  depth = 0;
+  ThisList = current_list_depth();
+  for (i = 0, done = FALSE; !done; ) {
+
+    /* get the next line (from correct place) */
+    if (ThisList == 0) 
+      input = opihi_readline (prompt);
+    else 
+      input = get_next_listentry (ThisList);
+    stripwhite (input);
+
+    /* check for end-of-data syntax error */
+    if (input == NULL) {
+      if (ThisList == 0)  {
+	gprint (GP_ERR, "end loop with 'END'\n");
+	continue;
+      } else {
+	gprint (GP_ERR, "misbalanced loop\n");
+	for (j = 0; j < loop.Nlines; j++) {
+	  free (loop.line[j]);
+	}
+	free (loop.line);
+	return (FALSE);
+      }	
+    }
+
+    /* test for new macro (or other list, in the future?) */
+    if (is_list (input)) depth ++;
+
+    /* test for end of nested list -- if not nested, END refers to this macro */
+    if (!strncasecmp (input, "END", 3)) {
+      depth --;
+      if (depth < 0) { 
+	free (input);
+	break;
+      }
+    }
+
+    /* if line has data, add to loop list */
+    if (*input) { 
+      loop.line[i] = input;
+      i++;
+      if (i == NLINES - 1) {
+	NLINES += D_NLINES;
+	REALLOCATE (loop.line, char *, NLINES);
+      }
+    }
+  }
+
+  /* cleanup loop data */
+  loop.Nlines = i;
+  REALLOCATE (loop.line, char *, MAX (loop.Nlines, 1));
+
+  status = TRUE;
+  interrupt = FALSE;
+  for (i = 2; (i < argc) && !interrupt; i++) {
+    set_str_variable (argv[1], argv[i]);
+    status = exec_loop (&loop);
+    if (loop_next) continue;
+    if (loop_last) break;
+    if (loop_break) break;
+  }
+  /* 'last' and 'next' should only affect one loop */
+  loop_last = loop_next = FALSE; 
+
+  /* break should propagate up if auto_break is set */
+  loop_break = FALSE;
+  if (auto_break && !status) loop_break = TRUE;
+
+  /* cleanup list */
+  for (j = 0; j < loop.Nlines; j++) {
+    free (loop.line[j]);
+  }
+  free (loop.line);
+
+  if (loop_break) return (FALSE);
+  return (TRUE);
+}
+
+/*
+  If we are entering at the keyboard (ThisList == 0), use readline.
+  Otherwise, read from the current list, remove list lines.
+  execute when we hit the final "END" (the true END -- we count macro defines!!) 
+*/
+     
+/* while processing the loop, the loop status variables may be set
+   by the loop commands, or the loop may quite on a failed command,
+   setting the exec_loop return status to false. the loop status variables
+   are:
+   loop_next : stop this loop, but try another loop
+   loop_last : stop loop processing, but return true so external loop may continue
+   loop_break : stop loop processing, and return false so external loop will break
+   interrupt : external interrupt signal
+*/
+
Index: trunk/Ohana/src/opihi/cmd.data/dot.c
===================================================================
--- trunk/Ohana/src/opihi/cmd.data/dot.c	(revision 33700)
+++ trunk/Ohana/src/opihi/cmd.data/dot.c	(revision 33963)
@@ -3,9 +3,16 @@
 int dot (int argc, char **argv) {
   
-  int kapa;
+  int kapa, N;
   Graphdata graphmode;
   float x, y;
 
   if (!style_args (&graphmode, &argc, argv, &kapa)) return FALSE;
+
+  /* FracPositions uses coordinates of 0-1 relative to axis range */
+  int FracPositions = FALSE;
+  if ((N = get_argument (argc, argv, "-frac"))) {
+    remove_argument (N, &argc, argv);
+    FracPositions = TRUE;
+  } 
 
   if (argc != 3) {
@@ -15,4 +22,9 @@
   x = atof(argv[1]);
   y = atof(argv[2]);
+
+  if (FracPositions) {
+    x =  x * (graphmode.xmax - graphmode.xmin) + graphmode.xmin;
+    y =  y * (graphmode.ymax - graphmode.ymin) + graphmode.ymin;
+  }    
 
   /* set point style and errorbar mode (these are NOT sticky) */
Index: trunk/Ohana/src/opihi/cmd.data/gridify.c
===================================================================
--- trunk/Ohana/src/opihi/cmd.data/gridify.c	(revision 33700)
+++ trunk/Ohana/src/opihi/cmd.data/gridify.c	(revision 33963)
@@ -5,9 +5,11 @@
   int i, Nx, Ny, Xb, Yb, Normalize, N;
   float Xmin, Xmax, dX, Ymin, Ymax, dY, initValue;
-  float *buf, *val;
+  float *buf, *val, *cnt;
   int *Nval;
-  Buffer *bf;
   Vector *vx, *vy, *vz;
   opihi_flt *x, *y, *z;
+
+  Buffer *bf = NULL;
+  Buffer *ct = NULL;
 
   Normalize = TRUE;
@@ -15,4 +17,6 @@
     remove_argument (N, &argc, argv);
     Normalize = FALSE;
+    if ((ct = SelectBuffer (argv[N], ANYBUFFER, TRUE)) == NULL) return (FALSE);
+    remove_argument (N, &argc, argv);
   }
 
@@ -24,6 +28,28 @@
   }
 
-  if (argc != 11) {
-    gprint (GP_ERR, "USAGE: gridify x y z buffer Xmin Xmax dX Ymin Ymax dY\n");
+  Xmin = Xmax = dX = NAN;
+  if ((N = get_argument (argc, argv, "-x"))) {
+    remove_argument (N, &argc, argv);
+    Xmin = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+    Xmax = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+    dX   = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+  }    
+
+  Ymin = Ymax = dY = NAN;
+  if ((N = get_argument (argc, argv, "-y"))) {
+    remove_argument (N, &argc, argv);
+    Ymin = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+    Ymax = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+    dY   = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+  }    
+
+  if (argc != 5) {
+    gprint (GP_ERR, "USAGE: gridify x y z buffer [-x Xmin Xmax dX] [-y Ymin Ymax dY] [-init-value value] [-raw]\n");
     return (FALSE);
   }
@@ -41,19 +67,25 @@
   REQUIRE_VECTOR_FLT (vz, FALSE); 
 
-  Xmin = atof (argv[5]);
-  Xmax = atof (argv[6]);
-  dX   = atof (argv[7]);
+  if (isnan(dX)) {
+    Xmin = 0;
+    Xmax = bf[0].matrix.Naxis[0];
+    dX = 1;
+  }
 
-  Ymin = atof (argv[8]);
-  Ymax = atof (argv[9]);
-  dY   = atof (argv[10]);
+  if (isnan(dY)) {
+    Ymin = 0;
+    Ymax = bf[0].matrix.Naxis[1];
+    dY = 1;
+  }
 
-  Nx = (Xmax - Xmin) / dX + 1;
-  Ny = (Ymax - Ymin) / dY + 1;
+  Nx = (Xmax - Xmin) / dX;
+  Ny = (Ymax - Ymin) / dY;
   
-  gfits_free_matrix (&bf[0].matrix);
-  gfits_free_header (&bf[0].header);
-  CreateBuffer (bf, Nx, Ny, -32, 0.0, 1.0);
-  strcpy (bf[0].file, "(empty)");
+  if ((Nx != bf[0].matrix.Naxis[0]) || (Ny != bf[0].matrix.Naxis[1])) {
+    gfits_free_matrix (&bf[0].matrix);
+    gfits_free_header (&bf[0].header);
+    CreateBuffer (bf, Nx, Ny, -32, 0.0, 1.0);
+    strcpy (bf[0].file, "(empty)");
+  }
 
   ALLOCATE (val, float, Nx*Ny);
@@ -68,4 +100,6 @@
     Xb = (*x - Xmin) / dX;
     Yb = (*y - Ymin) / dY;
+    if (Xb < 0) continue;
+    if (Yb < 0) continue;
     if (Xb >= Nx) continue;
     if (Yb >= Ny) continue;
@@ -74,15 +108,27 @@
   }
 
+  if (!Normalize) {
+    gfits_free_matrix (&ct[0].matrix);
+    gfits_free_header (&ct[0].header);
+    CreateBuffer (ct, Nx, Ny, -32, 0.0, 1.0);
+    strcpy (ct[0].file, "(empty)");
+
+    buf = (float *) bf[0].matrix.buffer;
+    cnt = (float *) ct[0].matrix.buffer;
+    for (i = 0; i < Nx*Ny; i++) {
+      if (Nval[i] == 0) continue;
+      buf[i] = val[i];
+      cnt[i] = Nval[i];
+    }
+    free (val);
+    free (Nval);
+    return TRUE;
+  }
+
   buf = (float *) bf[0].matrix.buffer;
   for (i = 0; i < Nx*Ny; i++) {
     buf[i] = initValue;
-    if (Normalize) {
-      if (Nval[i] == 0) {
-	continue;
-      }
-      buf[i] = val[i] / Nval[i];
-    } else {
-      buf[i] = val[i];
-    }
+    if (Nval[i] == 0) continue;
+    buf[i] = val[i] / Nval[i];
   }
 
Index: trunk/Ohana/src/opihi/cmd.data/match2d.c
===================================================================
--- trunk/Ohana/src/opihi/cmd.data/match2d.c	(revision 33700)
+++ trunk/Ohana/src/opihi/cmd.data/match2d.c	(revision 33963)
@@ -167,4 +167,6 @@
   NMATCH = X1->Nelements;
   ResetVector (index, OPIHI_INT, NMATCH);
+
+  for (i = 0; i < index->Nelements; i++) { index->elements.Int[i] = -1; }
 
   ALLOCATE (N1, off_t, X1->Nelements);
Index: trunk/Ohana/src/opihi/cmd.data/threshold.c
===================================================================
--- trunk/Ohana/src/opihi/cmd.data/threshold.c	(revision 33700)
+++ trunk/Ohana/src/opihi/cmd.data/threshold.c	(revision 33963)
@@ -69,5 +69,9 @@
     x0 = isFltX ? vecx[0].elements.Flt[Nhi] : vecy[0].elements.Int[Nhi];
     x1 = isFltX ? vecx[0].elements.Flt[Nhi+1]   : vecy[0].elements.Int[Nhi+1];
-    Xvalue = (value - y0) * (x1 - x0) / (y1 - y0) + x0;
+    if (y0 == y1) {
+      Xvalue = 0.5*(x0 + x1);
+    } else {
+      Xvalue = (value - y0) * (x1 - x0) / (y1 - y0) + x0;
+    }    
   } else {
     // interpolate to value:
@@ -76,5 +80,9 @@
     x0 = isFltX ? vecx[0].elements.Flt[Nhi-1] : vecy[0].elements.Int[Nhi-1];
     x1 = isFltX ? vecx[0].elements.Flt[Nhi]   : vecy[0].elements.Int[Nhi];
-    Xvalue = (value - y0) * (x1 - x0) / (y1 - y0) + x0;
+    if (y0 == y1) {
+      Xvalue = 0.5*(x0 + x1);
+    } else {
+      Xvalue = (value - y0) * (x1 - x0) / (y1 - y0) + x0;
+    }
   }
 
Index: trunk/Ohana/src/opihi/cmd.data/vgroup.c
===================================================================
--- trunk/Ohana/src/opihi/cmd.data/vgroup.c	(revision 33700)
+++ trunk/Ohana/src/opihi/cmd.data/vgroup.c	(revision 33963)
@@ -1,3 +1,5 @@
 # include "data.h"
+
+enum {USE_MEDIAN, USE_COUNT, USE_SUM, USE_MEAN};
 
 int vgroup (int argc, char **argv) {
@@ -23,14 +25,42 @@
   // }
 
+  int mode = USE_MEDIAN;
+  if ((N = get_argument (argc, argv, "-sum"))) {
+    remove_argument (N, &argc, argv);
+    mode = USE_SUM;
+  }
+  if ((N = get_argument (argc, argv, "-mean"))) {
+    remove_argument (N, &argc, argv);
+    mode = USE_MEAN;
+  }
+
+  float binsize = NAN;
+  if ((N = get_argument (argc, argv, "-binsize"))) {
+    remove_argument (N, &argc, argv);
+    binsize = atof(argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
   if (argc != 5) {
-    gprint (GP_ERR, "USAGE: vbin <xin> <yin> <xout> <yout>\n");
+    gprint (GP_ERR, "USAGE: vgroup <xin> <yin> <xout> <yout>\n");
     gprint (GP_ERR, " group x,y values in bins defined by <xout>\n");
+    gprint (GP_ERR, " by default, yout has the median of the associated input values\n");
+    gprint (GP_ERR, " use -sum to add values in the bin\n");
+    gprint (GP_ERR, " use <yin> = histogram count matching values\n");
+    gprint (GP_ERR, " use -binsize to specify a fixed bin width (<xout> will define the bin center)\n");
     return (FALSE);
   }
 
   if ((xin  = SelectVector (argv[1], OLDVECTOR, TRUE)) == NULL) return (FALSE);
-  if ((yin  = SelectVector (argv[2], OLDVECTOR, TRUE)) == NULL) return (FALSE);
   if ((xout = SelectVector (argv[3], OLDVECTOR, TRUE)) == NULL) return (FALSE);
   if ((yout = SelectVector (argv[4], ANYVECTOR, TRUE)) == NULL) return (FALSE);
+  yin = NULL;
+
+  // this should conflict with the -sum option...
+  if (!strcmp(argv[2], "histogram")) {
+    mode = USE_COUNT;
+  } else {
+    if ((yin  = SelectVector (argv[2], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+  }
 
   // re-binning creates a float vector
@@ -41,6 +71,11 @@
 
   for (i = 0; i < xout[0].Nelements - 1; i++) {
-    xmin = xout[0].elements.Flt[i];
-    xmax = xout[0].elements.Flt[i+1];
+    if (isnan(binsize)) {
+      xmin = xout[0].elements.Flt[i];
+      xmax = xout[0].elements.Flt[i+1];
+    } else {
+      xmin = xout[0].elements.Flt[i] - 0.5*binsize;
+      xmax = xout[0].elements.Flt[i] + 0.5*binsize;
+    }
 
     N = 0;
@@ -48,21 +83,40 @@
       if (xin[0].elements.Flt[j] < xmin) continue;
       if (xin[0].elements.Flt[j] > xmax) continue;
-      values[N] = yin[0].elements.Flt[j];
+      if (yin) {
+	values[N] = yin[0].elements.Flt[j];
+      }
       N++;
     }
     
-    dsort (values, N);
-    if (N > 1) {
-      sum = (N % 2) ? values[(int)(0.5*N)] : 0.5*(values[N/2] + values[N/2 + 1]);
-    } else {
-      sum = values[0];
+    sum = NAN;
+    switch (mode) {
+      case USE_MEDIAN:
+	dsort (values, N);
+	if (N > 1) {
+	  sum = (N % 2) ? values[(int)(0.5*N)] : 0.5*(values[N/2] + values[N/2 + 1]);
+	} else {
+	  sum = values[0];
+	}
+	break;
+
+      case USE_SUM:
+	sum = 0.0;
+	for (j = 0; j < N; j++) {
+	  sum += values[j];
+	}
+	break;
+	
+      case USE_COUNT:
+	sum = N;
+	break;
+
+      case USE_MEAN:
+	sum = 0.0;
+	for (j = 0; j < N; j++) {
+	  sum += values[j];
+	}
+	sum /= N;
+	break;
     }
-
-    // measure the stat for this bin
-    // sum = 0.0;
-    // for (j = 0; j < N; j++) {
-    //   sum += values[j];
-    // }
-    // sum /= N;
     yout[0].elements.Flt[i] = sum;
   }
Index: trunk/Ohana/src/opihi/cmd.data/vstats.c
===================================================================
--- trunk/Ohana/src/opihi/cmd.data/vstats.c	(revision 33700)
+++ trunk/Ohana/src/opihi/cmd.data/vstats.c	(revision 33963)
@@ -144,5 +144,5 @@
       }      
     }
-    stdev = sqrt (var / N);
+    stdev = sqrt (var / (N - 1));
 
     Nmode = 0;
Index: trunk/Ohana/src/opihi/cmd.data/write_vectors.c
===================================================================
--- trunk/Ohana/src/opihi/cmd.data/write_vectors.c	(revision 33700)
+++ trunk/Ohana/src/opihi/cmd.data/write_vectors.c	(revision 33963)
@@ -21,7 +21,29 @@
   FITS = NULL;
   if ((N = get_argument (argc, argv, "-fits"))) {
+    if (format) {
+      gprint (GP_ERR, "ERROR: do not mix -fits and -format\n");
+      free (format);
+      return (FALSE);
+    }
     remove_argument (N, &argc, argv);
     FITS = strcreate (argv[N]);
     remove_argument (N, &argc, argv);
+  }
+
+  /* option generate a FITS output table */
+  int CSV = FALSE;
+  if ((N = get_argument (argc, argv, "-csv"))) {
+    if (format) {
+      gprint (GP_ERR, "ERROR: do not mix -csv and -format\n");
+      free (format);
+      return (FALSE);
+    }
+    if (FITS) {
+      gprint (GP_ERR, "ERROR: do not mix -csv and -fits\n");
+      free (FITS);
+      return (FALSE);
+    }
+    remove_argument (N, &argc, argv);
+    CSV = TRUE;
   }
 
@@ -30,4 +52,10 @@
     remove_argument (N, &argc, argv);
     append = TRUE;
+  }
+
+  int ADD_HEADER = FALSE;
+  if ((N = get_argument (argc, argv, "-header"))) {
+    remove_argument (N, &argc, argv);
+    ADD_HEADER = TRUE;
   }
 
@@ -83,11 +111,32 @@
 
   /* default output format */
+  if (ADD_HEADER) {
+    for (j = 0; j < Nvec; j++) {
+      if (CSV) {
+	fprintf (f, "%s,", vec[j][0].name);
+      } else {
+	if (j == 0) fprintf (f, "# ");
+	fprintf (f, "%s ", vec[j][0].name);
+      }
+    }
+    fprintf (f, "\n");
+  }
+
+  /* default output format */
   if (format == (char *) NULL) {
     for (i = 0; i < vec[0][0].Nelements; i++) {
       for (j = 0; j < Nvec; j++) {
 	if (vec[j][0].type == OPIHI_FLT) {
-	  fprintf (f, "%.12g ", vec[j][0].elements.Flt[i]);
+	  if (CSV) {
+	    fprintf (f, "%.12g,", vec[j][0].elements.Flt[i]);
+	  } else {
+	    fprintf (f, "%.12g ", vec[j][0].elements.Flt[i]);
+	  }
 	} else {
-	  fprintf (f, "%d ", vec[j][0].elements.Int[i]);
+	  if (CSV) {
+	    fprintf (f, "%d,", vec[j][0].elements.Int[i]);
+	  } else {
+	    fprintf (f, "%d ", vec[j][0].elements.Int[i]);
+	  }
 	}
       }
Index: trunk/Ohana/src/opihi/dvo/avextract.c
===================================================================
--- trunk/Ohana/src/opihi/dvo/avextract.c	(revision 33700)
+++ trunk/Ohana/src/opihi/dvo/avextract.c	(revision 33963)
@@ -37,4 +37,11 @@
   }
 
+  int VERBOSE2 = FALSE;
+  if ((N = get_argument (argc, argv, "-vv"))) {
+    remove_argument (N, &argc, argv);
+    VERBOSE = TRUE;
+    VERBOSE2 = TRUE;
+  }
+
   int PARALLEL = FALSE;
   if ((N = get_argument (argc, argv, "-parallel"))) {
@@ -65,4 +72,13 @@
       return FALSE;
     }    
+  }
+
+  // this is used to NOT save the results in the results file
+  // use this option when mextract is used in a script which does its
+  // own job of packaging the results
+  int SKIP_RESULTS = FALSE;
+  if ((N = get_argument (argc, argv, "-skip-results"))) {
+    remove_argument (N, &argc, argv);
+    SKIP_RESULTS = TRUE;
   }
 
@@ -80,4 +96,7 @@
     goto escape; 
   }
+
+  // init locally static variables (time refs)
+  dbExtractAveragesInit (); 
 
   // command-line is of the form: avextract field,field, field [where (field op value)...]
@@ -173,7 +192,5 @@
     catalog.filename = (HOST_ID || PARALLEL_LOCAL) ? hostfile : skylist[0].filename[i];
     catalog.catflags = LOAD_AVES | LOAD_SECF;
-    if (needMeasures) {
-      catalog.catflags |= LOAD_MEAS;
-    }
+    catalog.catflags |= needMeasures ? LOAD_MEAS : SKIP_MEAS;
     catalog.Nsecfilt = 0;
 
@@ -181,5 +198,5 @@
       
     // an error exit status here is a significant error
-    if (!dvo_catalog_open (&catalog, NULL, FALSE, "r")) {
+    if (!dvo_catalog_open (&catalog, NULL, VERBOSE2, "r")) {
       gprint (GP_ERR, "ERROR: failure to open catalog file %s\n", catalog.filename);
       exit (2);
@@ -193,6 +210,6 @@
       m = catalog.average[j].measureOffset;
 
-      // reset counters for saved fields, extract fields
-      dbExtractAveragesInit (); 
+      dbExtractAveragesInitAve ();  // reset counters for saved fields (costs very little)
+
       for (n = 0; n < Nfields; n++) {
 	values[n] = dbExtractAverages (&catalog.average[j], &catalog.secfilt[j*Nsecfilt], &catalog.measure[m], &fields[n]);
@@ -229,7 +246,9 @@
 
   // write vectors to a table (this is used by parallel dvo operations, but can be used elsewhere)
-  if (RESULT_FILE) {
+  if (RESULT_FILE && !SKIP_RESULTS) {
     int status = WriteVectorTableFITS (RESULT_FILE, "RESULT", vec, Nreturn, FALSE, NULL);
-    if (!status) goto escape;
+    if (!status) {
+      goto escape;
+    }
   }
 
Index: trunk/Ohana/src/opihi/dvo/avmatch.c
===================================================================
--- trunk/Ohana/src/opihi/dvo/avmatch.c	(revision 33700)
+++ trunk/Ohana/src/opihi/dvo/avmatch.c	(revision 33963)
@@ -66,4 +66,7 @@
   if (!InitPhotcodes ()) goto escape;
   Nsecfilt = GetPhotcodeNsecfilt ();
+
+  // init locally static variables (time refs)
+  dbExtractAveragesInit (); 
 
   // this does all the work of re-packaging the command, calling it on the remote machines, then loading in the results
@@ -129,4 +132,13 @@
   }
 
+  // check the requested fields : are all average/secfilt entries, or do we need measures?
+  int needMeasures = FALSE;
+  for (i = 0; !needMeasures && (i < Nfields); i++) {
+    if (fields[i].magMode == MAG_NONE) continue;
+    if (fields[i].photcode == NULL) continue; // assert this?
+    if (fields[i].photcode[0].type == PHOT_REF) needMeasures = TRUE;
+    if (fields[i].photcode[0].type == PHOT_DEP) needMeasures = TRUE;
+  }
+
   /* load regions which contain all supplied RA,DEC coordinates */
   if ((skylist = SelectRegionsByCoordVectors (RAvec, DECvec)) == NULL) goto escape;
@@ -168,5 +180,6 @@
     snprintf (hostfile, 1024, "%s/%s.cpt", HOSTDIR, skylist[0].regions[i]->name);
     catalog.filename = HOST_ID ? hostfile : skylist[0].filename[i];
-    catalog.catflags = LOAD_AVES | LOAD_MEAS | LOAD_SECF;
+    catalog.catflags = LOAD_AVES | LOAD_SECF;
+    catalog.catflags |= needMeasures ? LOAD_MEAS : SKIP_MEAS;
     catalog.Nsecfilt = 0;
 
@@ -196,5 +209,5 @@
 
       // reset counters for saved fields, extract fields
-      dbExtractAveragesInit (); 
+      dbExtractAveragesInitAve (); 
       for (n = 0; n < Nfields; n++) {
 	values[n] = dbExtractAverages (&catalog.average[Ncat], &catalog.secfilt[Ncat*Nsecfilt], &catalog.measure[m], &fields[n]);
Index: trunk/Ohana/src/opihi/dvo/dvo_client.c
===================================================================
--- trunk/Ohana/src/opihi/dvo/dvo_client.c	(revision 33700)
+++ trunk/Ohana/src/opihi/dvo/dvo_client.c	(revision 33963)
@@ -98,5 +98,8 @@
 
   int status = input (2, &argv[0]);
-  if (!status) exit (2);
+  if (!status) {
+    fprintf (stderr, "WARNING: exit status 2 %d (%s)\n", HOST_ID, HOSTDIR);
+    exit (2);
+  }
   exit (0);
 
Index: trunk/Ohana/src/opihi/dvo/dvo_host_utils.c
===================================================================
--- trunk/Ohana/src/opihi/dvo/dvo_host_utils.c	(revision 33700)
+++ trunk/Ohana/src/opihi/dvo/dvo_host_utils.c	(revision 33963)
@@ -4,7 +4,9 @@
 # define PARALLEL_MANUAL 0
 # define PARALLEL_SERIAL 0
-# define MAX_PATH_LENGTH 1024
-
-int HostTableLaunchJobs (HostTable *table, char *basecmd, char *options) {
+# define DVO_MAX_PATH 1024
+
+# define DIE(WHO,MSG) { perror(WHO); myAbort(MSG); }
+
+int HostTableLaunchJobs (HostTable *table, char *basecmd, char *options, int VERBOSE) {
 
   char uniquer[12];
@@ -13,9 +15,10 @@
   snprintf (uniquer, 12, "%05d.%05d", PID, TIME % 100000);
 
+  int top_status = TRUE;
   int i;
   for (i = 0; i < table->Nhosts; i++) {
 
     // ensure that the paths are absolute path names
-    char *tmppath = abspath (table->hosts[i].pathname, MAX_PATH_LENGTH);
+    char *tmppath = abspath (table->hosts[i].pathname, DVO_MAX_PATH);
     free (table->hosts[i].pathname);
     table->hosts[i].pathname = tmppath;
@@ -23,14 +26,24 @@
     // need to save the results filename with the uniquer
     // XXX a bit of a waste (but only 1024 * 60 bytes or so
-    ALLOCATE (table->hosts[i].results, char, MAX_PATH_LENGTH);
-    snprintf (table->hosts[i].results, MAX_PATH_LENGTH, "%s/dvo.results.%s.fits", table->hosts[i].pathname, uniquer);
-
-    char commandBase[MAX_PATH_LENGTH];
-    snprintf (commandBase, MAX_PATH_LENGTH, "dvo.command.%s.txt", uniquer);
-    char *commandFile = abspath(commandBase, MAX_PATH_LENGTH);
+    ALLOCATE (table->hosts[i].results, char, DVO_MAX_PATH);
+    snprintf (table->hosts[i].results, DVO_MAX_PATH, "%s/dvo.results.%s.fits", table->hosts[i].pathname, uniquer);
+
+    char commandBase[DVO_MAX_PATH];
+    snprintf (commandBase, DVO_MAX_PATH, "dvo.command.%s.txt", uniquer);
+    char *commandFile = abspath(commandBase, DVO_MAX_PATH);
 
     FILE *f = fopen (commandFile, "w");
     fprintf (f, "%s\n", basecmd);
-    fclose (f);
+    if (fflush (f)) DIE("flush", "failed to flush");
+
+    int fd = fileno (f);
+    if (fsync (fd)) DIE("fsync", "failed to fsync");
+
+    if (fclose (f)) DIE("close", "failed to close");
+
+    // force NFS to write the file to disk
+    int state;
+    f = fsetlockfile (commandFile, 0.5, LCK_XCLD, &state);
+    fclearlockfile (commandFile, f, LCK_XCLD, &state);
 
     char command[1024];
@@ -38,5 +51,5 @@
     free (commandFile);
 
-    if (DEBUG || PARALLEL_MANUAL) fprintf (stderr, "command: %s\n", command);
+    if (VERBOSE) gprint (GP_ERR, "command: %s\n", command);
 
     if (PARALLEL_MANUAL) {
@@ -47,6 +60,6 @@
       int status = system (command);
       if (status) {
-	fprintf (stderr, "ERROR running relphot_client\n");
-	exit (2);
+	gprint (GP_ERR, "ERROR running relphot_client\n");
+	top_status = FALSE;
       }
     } else {
@@ -55,11 +68,12 @@
       int pid = rconnect ("ssh", table->hosts[i].hostname, command, table->hosts[i].stdio, &errorInfo, FALSE);
       if (!pid) {
-	if (DEBUG) fprintf (stderr, "failure to start %s (error %d)\n", table->hosts[i].hostname, errorInfo);
-	exit (1);
+	gprint (GP_ERR, "failure to start %s (error %d)\n", table->hosts[i].hostname, errorInfo);
+	top_status = FALSE;
+	continue;
       }
       table->hosts[i].pid = pid; // save for future reference
     }
   }
-  return TRUE;
+  return top_status;
 }
 
@@ -86,5 +100,5 @@
   }
 
-  char *CATDIR = abspath (tmppath, MAX_PATH_LENGTH);
+  char *CATDIR = abspath (tmppath, DVO_MAX_PATH);
   if (!CATDIR) {
     gprint (GP_ERR, "failed to make an absolute path from %s (too long)\n", tmppath);
@@ -126,5 +140,5 @@
 
   // launch this command remotely
-  HostTableLaunchJobs (table, basecmd, options);
+  HostTableLaunchJobs (table, basecmd, options, VERBOSE);
   free (options);
   free (basecmd);
@@ -184,2 +198,70 @@
   return TRUE;
 }
+
+// re-gather the remote results files: this can be used in case one of the clients failed,
+// and has since been re-run
+int HostTableReloadResults (char *uniquer, int VERBOSE) {
+
+  int i;
+
+  // load the list of hosts
+  SkyTable *sky = GetSkyTable();
+  if (!sky) {
+    gprint (GP_ERR, "failed to load sky table for database\n");
+    return FALSE;
+  }
+
+  char *CATDIR = GetCATDIR ();
+  if (!CATDIR) {
+    gprint (GP_ERR, "failed to get CATDIR for database\n");
+    return FALSE;
+  }
+
+  HostTable *table = HostTableLoad (CATDIR, sky->hosts);
+  if (!table) {
+    gprint (GP_ERR, "ERROR: failure reading Host Table %s for database %s\n", sky->hosts, CATDIR);
+    return FALSE;
+  }    
+
+  // load fields from file
+  int    Nvec = 0;
+  Vector **vec = NULL;
+  for (i = 0; i < table->Nhosts; i++) {
+    // ensure that the paths are absolute path names
+    char *tmppath = abspath (table->hosts[i].pathname, DVO_MAX_PATH);
+    free (table->hosts[i].pathname);
+    table->hosts[i].pathname = tmppath;
+
+    // need to save the results filename with the uniquer
+    // XXX a bit of a waste (but only 1024 * 60 bytes or so
+    ALLOCATE (table->hosts[i].results, char, DVO_MAX_PATH);
+    snprintf (table->hosts[i].results, DVO_MAX_PATH, "%s/dvo.results.%s.fits", table->hosts[i].pathname, uniquer);
+
+    int    Ninvec = 0;
+    Vector **invec = ReadVectorTableFITS (table->hosts[i].results, "RESULT", &Ninvec);
+    if (!invec) {
+      // failed to read the file, now what?
+      gprint (GP_ERR, "failed to read remote result file : %s\n", table->hosts[i].results);
+      free (table->hosts[i].results);
+      table->hosts[i].results = NULL;
+      continue;
+    }
+    free (table->hosts[i].results);
+    table->hosts[i].results = NULL;
+
+    // fprintf (stderr, "%s : %d\n", table->hosts[i].pathname, invec[0]->Nelements);
+
+    vec = MergeVectors (vec, &Nvec, invec, Ninvec);
+    if (vec != invec) {
+      FreeVectorArray (invec, Ninvec);
+    }
+  }
+
+  for (i = 0; i < Nvec; i++) {
+    AssignVector (vec[i], vec[i]->name, ANYVECTOR, TRUE);
+  }
+  free (vec);
+
+  free (table);
+  return TRUE;
+}
Index: trunk/Ohana/src/opihi/dvo/gstar.c
===================================================================
--- trunk/Ohana/src/opihi/dvo/gstar.c	(revision 33700)
+++ trunk/Ohana/src/opihi/dvo/gstar.c	(revision 33963)
@@ -116,5 +116,6 @@
 
   /* lock, load, unlock catalog */
-  catalog.catflags = GetMeasures ? LOAD_AVES | LOAD_MEAS | LOAD_SECF : LOAD_AVES | LOAD_SECF;
+  catalog.catflags = LOAD_AVES | LOAD_SECF;
+  catalog.catflags |= GetMeasures ? LOAD_MEAS : SKIP_MEAS;
   catalog.Nsecfilt = 0;
 
Index: trunk/Ohana/src/opihi/dvo/hosts.c
===================================================================
--- trunk/Ohana/src/opihi/dvo/hosts.c	(revision 33700)
+++ trunk/Ohana/src/opihi/dvo/hosts.c	(revision 33963)
@@ -1,5 +1,5 @@
 # include "dvoshell.h"
 # include <glob.h>
-# define MAX_PATH_LENGTH 1024
+# define DVO_MAX_PATH 1024
 
 // functions to manage the remote hosts
@@ -59,6 +59,6 @@
     for (i = 0; i < table->Nhosts; i++) {
       pglob.gl_offs = 0;
-      char name[MAX_PATH_LENGTH];
-      snprintf (name, MAX_PATH_LENGTH, "%s/dvo.results.%05d.*.fits", table->hosts[i].pathname, PID);
+      char name[DVO_MAX_PATH];
+      snprintf (name, DVO_MAX_PATH, "%s/dvo.results.%05d.*.fits", table->hosts[i].pathname, PID);
       if (VERBOSE) gprint (GP_ERR, "checking %s\n", name);
       glob (name, 0, NULL, &pglob);
Index: trunk/Ohana/src/opihi/dvo/mextract.c
===================================================================
--- trunk/Ohana/src/opihi/dvo/mextract.c	(revision 33700)
+++ trunk/Ohana/src/opihi/dvo/mextract.c	(revision 33963)
@@ -189,5 +189,5 @@
       }
 
-      dbExtractMeasuresInitAve (); // reset counters for saved fields (costs very little
+      dbExtractMeasuresInitAve (); // reset counters for saved fields (costs very little)
 
       for (k = 0; (k < catalog.average[j].Nmeasure); k++, m++) {
Index: trunk/Ohana/src/opihi/dvo/remote.c
===================================================================
--- trunk/Ohana/src/opihi/dvo/remote.c	(revision 33700)
+++ trunk/Ohana/src/opihi/dvo/remote.c	(revision 33963)
@@ -1,5 +1,5 @@
 # include "dvoshell.h"
 # include <glob.h>
-# define MAX_PATH_LENGTH 1024
+# define DVO_MAX_PATH 1024
 
 // functions to manage the remote hosts
@@ -8,4 +8,10 @@
   int N;
 
+  int VERBOSE = FALSE;
+  if ((N = get_argument (argc, argv, "-v"))) {
+    remove_argument (N, &argc, argv);
+    VERBOSE = TRUE;
+  }
+
   if (argc < 2) {
     gprint (GP_ERR, "USAGE: remote (command)\n");
@@ -13,4 +19,5 @@
     gprint (GP_ERR, "  options:\n");
     gprint (GP_ERR, "  -v : verbose mode:\n");
+    gprint (GP_ERR, "OR:    remote -reload (uniquer)\n");
     return FALSE;
   }
@@ -23,8 +30,14 @@
   // with a set of vectors to load.
 
-  int VERBOSE = FALSE;
-  if ((N = get_argument (argc, argv, "-v"))) {
+  if ((N = get_argument (argc, argv, "-reload"))) {
     remove_argument (N, &argc, argv);
-    VERBOSE = TRUE;
+    if (argc != 2) {
+      gprint (GP_ERR, "USAGE: remote -reload (uniquer)\n");
+      gprint (GP_ERR, " (uniquer) is the element in the middle of the results file\n");
+      gprint (GP_ERR, " eg: dvo.results.XXXXX.YYYYY.fits\n");
+      return FALSE;
+    }
+    int status = HostTableReloadResults (argv[1], VERBOSE);
+    return status;
   }
 
Index: trunk/Ohana/src/opihi/dvo/skycoverage.c
===================================================================
--- trunk/Ohana/src/opihi/dvo/skycoverage.c	(revision 33700)
+++ trunk/Ohana/src/opihi/dvo/skycoverage.c	(revision 33963)
@@ -2,5 +2,5 @@
 
 // enum to define possible modes
-enum {COVERAGE, DENSITY, MIN_UBERCAL, MIN_DMAG_SYS, MIN_MCAL, MAX_MCAL};
+enum {COVERAGE, DENSITY, MIN_UBERCAL, MIN_DMAG_SYS, MIN_MCAL, MAX_MCAL, MIN_TIME, MAX_TIME};
 
 int wordhash (char *word);
@@ -23,4 +23,9 @@
   PhotCode *PhotcodeValue;
 
+  time_t TimeReference;
+  int TimeFormat;
+
+  GetTimeFormat (&TimeReference, &TimeFormat);
+
   WITH_MOSAIC = FALSE;
   if ((N = get_argument (argc, argv, "+mosaic"))) {
@@ -71,4 +76,12 @@
     remove_argument (N, &argc, argv);
     mode = MIN_UBERCAL;
+  }
+  if ((N = get_argument (argc, argv, "-min-time"))) {
+    remove_argument (N, &argc, argv);
+    mode = MIN_TIME;
+  }
+  if ((N = get_argument (argc, argv, "-max-time"))) {
+    remove_argument (N, &argc, argv);
+    mode = MAX_TIME;
   }
   if ((N = get_argument (argc, argv, "-min-dmag-sys"))) {
@@ -231,8 +244,10 @@
 	  case MIN_DMAG_SYS:
 	  case MIN_MCAL:
-	    V[ys*Nx + xs] = 1E6;
+	  case MIN_TIME:
+	    V[ys*Nx + xs] = 1E9;
 	    break;
 	  case MAX_MCAL:
-	    V[ys*Nx + xs] = -1E6;
+	  case MAX_TIME:
+	    V[ys*Nx + xs] = -1E9;
 	    break;
 	}
@@ -247,4 +262,6 @@
 	  case MIN_MCAL:
 	  case MAX_MCAL:
+	  case MIN_TIME:
+	  case MAX_TIME:
 	    V[ys*Nx + xs] = NAN;
 	    break;
@@ -323,4 +340,12 @@
 	      V[ys*Nx + xs] = MAX(V[ys*Nx + xs], image[i].Mcal);
 	      break;
+	    case MIN_TIME: {
+	      double timeVal = TimeValue (image[i].tzero, TimeReference, TimeFormat);
+	      V[ys*Nx + xs] = MIN(V[ys*Nx + xs], timeVal);
+	      break; }
+	    case MAX_TIME: {
+	      double timeVal = TimeValue (image[i].tzero, TimeReference, TimeFormat);
+	      V[ys*Nx + xs] = MAX(V[ys*Nx + xs], timeVal);
+	      break; }
 	  }
 	}
Index: trunk/Ohana/src/opihi/include/dvoshell.h
===================================================================
--- trunk/Ohana/src/opihi/include/dvoshell.h	(revision 33700)
+++ trunk/Ohana/src/opihi/include/dvoshell.h	(revision 33963)
@@ -99,5 +99,7 @@
 dbValue      dbExtractImages        PROTO((Image *image, off_t Nimage, off_t N, dbField *field));
 
-int          HostTableLaunchJobs    PROTO((HostTable *table, char *basecmd, char *options));
+int          HostTableLaunchJobs    PROTO((HostTable *table, char *basecmd, char *options, int VERBOSE));
 int          HostTableParallelOps   PROTO((int argc, char **argv, char *ResultFile, int Nelements, int VERBOSE));
-# endif
+int          HostTableReloadResults PROTO((char *uniquer, int VERBOSE));
+
+# endif // DVOSHELL_H
Index: trunk/Ohana/src/opihi/lib.shell/ListOps.c
===================================================================
--- trunk/Ohana/src/opihi/lib.shell/ListOps.c	(revision 33700)
+++ trunk/Ohana/src/opihi/lib.shell/ListOps.c	(revision 33963)
@@ -116,4 +116,17 @@
   
   status = !strcmp (comm, "for");
+  free (comm);
+  return (status);
+}
+
+int is_foreach_loop (char *line) {
+
+  int status;
+  char *comm;
+
+  comm = thisword (line);
+  if (comm == (char *) NULL) return (FALSE);
+  
+  status = !strcmp (comm, "foreach");
   free (comm);
   return (status);
@@ -266,4 +279,5 @@
   status |= is_macro_create (line);
   status |= is_for_loop (line);
+  status |= is_foreach_loop (line);
   status |= is_list_data (line);
   status |= is_loop (line);
Index: trunk/Ohana/src/opihi/lib.shell/VectorIO.c
===================================================================
--- trunk/Ohana/src/opihi/lib.shell/VectorIO.c	(revision 33700)
+++ trunk/Ohana/src/opihi/lib.shell/VectorIO.c	(revision 33963)
@@ -20,5 +20,5 @@
   }
   if (f == (FILE *) NULL) {
-    gprint (GP_ERR, "can't open file for write\n");
+    gprint (GP_ERR, "can't open file for write : %s\n", filename);
     return (FALSE);
   }
Index: trunk/Ohana/src/opihi/lib.shell/startup.c
===================================================================
--- trunk/Ohana/src/opihi/lib.shell/startup.c	(revision 33700)
+++ trunk/Ohana/src/opihi/lib.shell/startup.c	(revision 33963)
@@ -138,4 +138,5 @@
     if (is_script) {
       /* first argument in input script, rest are argv */
+      set_str_variable ("SCRIPT_NAME", argv[1]);
       list[Nlist] = strcreate (argv[1]);
       Nlist ++;
