Index: /branches/sc_branches/pantasks_condor/Makefile
===================================================================
--- /branches/sc_branches/pantasks_condor/Makefile	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/Makefile	(revision 34783)
@@ -0,0 +1,84 @@
+
+### this is the top-level makefile for the opihi collection of programs
+
+default: all
+
+### the include file dependencies need to be cleaned up still
+
+mana:     	 lib.data lib.shell cmd.basic cmd.data cmd.astro
+dimm:     	 lib.data lib.shell cmd.basic cmd.data cmd.astro
+dvo:      	 lib.data lib.shell cmd.basic cmd.data cmd.astro
+dvo_client:    	 lib.data lib.shell cmd.basic cmd.data cmd.astro
+pantasks: 	 lib.data lib.shell cmd.basic cmd.data cmd.astro
+pantasks_server: lib.data lib.shell cmd.basic cmd.data cmd.astro
+pantasks_client: lib.data lib.shell cmd.basic cmd.data cmd.astro
+pclient:  	 lib.data lib.shell cmd.basic
+pcontrol: 	 lib.data lib.shell cmd.basic
+
+LIBS = lib.data lib.shell cmd.basic cmd.data cmd.astro
+
+PROGRAM = mana dvo pantasks pclient pcontrol
+SPECIAL = pantasks_client pantasks_server dvo_client
+
+EXTRAS = dimm
+
+all:
+	for i in $(PROGRAM) $(SPECIAL); do $(MAKE) $$i || exit; done
+
+libs:
+	for i in $(LIBS); do $(MAKE) $$i || exit; done
+
+extras:
+	for i in $(EXTRAS); do $(MAKE) $$i || exit; done
+
+install:
+	for i in $(PROGRAM) $(SPECIAL); do $(MAKE) $$i.install || exit; done
+
+extras-install:
+	for i in $(EXTRAS); do $(MAKE) $$i.install || exit; done
+
+clean:
+	for i in $(PROGRAM) $(SPECIAL) $(EXTRAS) $(LIBS); do $(MAKE) $$i.clean || exit; done
+
+libs-uninstall:
+	for i in $(LIBS); do $(MAKE) $$i.uninstall || exit; done
+
+dist: clean
+	for i in $(PROGRAM) $(SPECIAL) $(EXTRAS) $(LIBS); do $(MAKE) $$i.dist || exit; done
+	rm -rf bin
+	rm -rf lib
+
+#############################################################
+
+pantasks_client pantasks_server:
+	if [ -d pantasks ]; then (cd pantasks && $(MAKE) $@); fi
+
+pantasks_client.install: pantasks_client
+	if [ -d pantasks ]; then (cd pantasks && $(MAKE) $@); fi
+
+pantasks_server.install: pantasks_server
+	if [ -d pantasks ]; then (cd pantasks && $(MAKE) $@); fi
+
+dvo_client:
+	if [ -d dvo ]; then (cd dvo && $(MAKE) $@); fi
+
+dvo_client.install: dvo_client
+	if [ -d dvo ]; then (cd dvo && $(MAKE) $@); fi
+
+$(PROGRAM) $(LIBS) $(EXTRAS):
+	if [ -d "$@" ]; then (cd $@ && $(MAKE)); fi
+
+%.install:
+	if [ -d "$*" ]; then $(MAKE) $*; fi
+	if [ -d "$*" ]; then (cd $* && $(MAKE) install); fi
+
+%.clean:
+	if [ -d "$*" ]; then (cd $* && $(MAKE) clean); fi
+
+%.dist:
+	if [ -d "$*" ]; then (cd $* && $(MAKE) dist); fi
+
+%.uninstall:
+	if [ -d "$*" ]; then (cd $* && $(MAKE) uninstall); fi
+
+.PHONY: $(PROGRAM) $(LIBS) pantasks_server pantasks_client
Index: /branches/sc_branches/pantasks_condor/Makefile.Common
===================================================================
--- /branches/sc_branches/pantasks_condor/Makefile.Common	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/Makefile.Common	(revision 34783)
@@ -0,0 +1,88 @@
+
+# utilities #################################################
+# .SUFFIXES: .$(ARCH).o
+
+BASE_CFLAGS   =	$(CFLAGS)
+BASE_CPPFLAGS =	$(CPPFLAGS) -I$(INC) -I$(DESTINC) $(INCDIRS) -D$(ARCH)
+BASE_LDFLAGS  = $(LDFLAGS) -L$(LIB) -L$(DESTLIB) $(LIBDIRS) $(LIBFLAGS)
+
+%.c : %.c.in
+	sed "s|@DATADIR@|$(DATA)|" $< > $@
+
+%.$(ARCH).o : %.c
+	$(CC) $(FULL_CFLAGS) $(FULL_CPPFLAGS) -c $< -o $@
+
+$(BIN)/%.$(ARCH):
+	@if [ ! -d $(BIN) ]; then mkdir -p $(BIN); fi
+	$(CC) $(FULL_CFLAGS) -o $@ $^ $(FULL_LDFLAGS)
+	@echo "compiled $*"
+	@echo ""
+
+$(DESTBIN)/%: $(BIN)/%.$(ARCH) 
+	@if [ ! -d $(DESTBIN) ]; then mkdir -p $(DESTBIN); fi
+	rm -f $(DESTBIN)/$*
+	cp $(BIN)/$*.$(ARCH) $(DESTBIN)/$*
+	@echo "installed $*"
+	@echo ""
+
+$(LIB)/%.$(ARCH).a:
+	@if [ ! -d $(LIB) ]; then mkdir -p $(LIB); fi
+	rm -f $@
+	ar rcv $@ $^ 
+	$(RANLIB) $@
+	@echo "compiled library $*"
+	@echo ""
+
+$(DESTLIB)/%.a: $(LIB)/%.$(ARCH).a
+	@if [ ! -d $(DESTLIB) ]; then mkdir -p $(DESTLIB); fi
+	rm -f $@
+	cp $< $@
+
+$(LIB)/%.$(ARCH).so:
+	@if [ ! -d $(LIB) ]; then mkdir -p $(LIB); fi
+	rm -f $@
+	gcc -shared -Wl,-soname,$*.so -o $@ $^ -lc
+
+$(DESTLIB)/%.so: $(LIB)/%.$(ARCH).so
+	@if [ ! -d $(DESTLIB) ]; then mkdir -p $(DESTLIB); fi
+	rm -f $@
+	cp $< $@
+
+lib%.clean:
+	rm -f $(LIB)/lib$*.$(ARCH).a
+	rm -f $($*)
+	@echo ""
+
+%.help:
+	@echo "installing help files for $*"
+	@if [ ! -d $(DATA)/help ]; then mkdir -p $(DATA)/help; fi
+	@rm -f $(HOME)/$*/help/*~
+	@rm -f $(HOME)/$*/help/#*
+	@for i in `find $(HOME)/$*/help -maxdepth 1 -type f`; do cp -f $$i $(DATA)/help; done
+
+%.modules:
+	@echo "installing modules for $*"
+	@if [ ! -d $(DATA)/modules ]; then mkdir -p $(DATA)/modules; fi
+	@if [ ! -d $(HOME)/modules/$* ]; then echo "no modules for $*"; fi
+	@if [   -d $(HOME)/modules/$* ]; then rm -f $(HOME)/modules/$*/*~; fi
+	@if [   -d $(HOME)/modules/$* ]; then rm -f $(HOME)/modules/$*/#*; fi
+	@if [   -d $(HOME)/modules/$* ]; then for i in `find $(HOME)/modules/$* -name CVS -prune -o -type f -print`; do cp -f $$i $(DATA)/modules; done; fi
+
+%.clean:
+	rm -f $(BIN)/$*.$(ARCH)
+	@echo ""
+
+clean:
+	rm -f $(BIN)/*.$(ARCH)
+	rm -f $(LIB)/*.$(ARCH).a
+	rm -f `find . -name "*.o"`
+	rm -f `find . -name "*~"`
+	rm -f `find . -name "#*"`
+
+dist: clean
+	rm -rf $(BIN)/*
+	rm -rf $(LIB)/*
+
+clean-help:
+	@if [ ! -d $(DATA)/help ]; then mkdir -p $(DATA)/help; fi
+	rm -f $(DATA)/help/*
Index: /branches/sc_branches/pantasks_condor/cmd.astro/Makefile
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.astro/Makefile	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.astro/Makefile	(revision 34783)
@@ -0,0 +1,102 @@
+default: libastrocmd
+
+include ../../../Makefile.System
+HOME    =       $(ROOT)/src/opihi
+BIN     =       $(HOME)/bin
+LIB     =       $(HOME)/lib
+INC     =       $(HOME)/include
+SRC     =       $(HOME)/cmd.astro
+include ../Makefile.Common
+
+# programs may add their own internal requirements here
+FULL_CFLAGS   = $(BASE_CFLAGS) -fPIC
+FULL_CPPFLAGS = $(BASE_CPPFLAGS)
+FULL_LDFLAGS  = $(BASE_LDFLAGS)
+
+# astro user commands ########################
+srcs = \
+$(SRC)/init.$(ARCH).o             \
+$(SRC)/altaz.$(ARCH).o	           \
+$(SRC)/biassub.$(ARCH).o	   \
+$(SRC)/cgrid.$(ARCH).o		   \
+$(SRC)/coords.$(ARCH).o	   \
+$(SRC)/cdot.$(ARCH).o		   \
+$(SRC)/cline.$(ARCH).o             \
+$(SRC)/cplot.$(ARCH).o		   \
+$(SRC)/csystem.$(ARCH).o	   \
+$(SRC)/ctimes.$(ARCH).o	   \
+$(SRC)/cval.$(ARCH).o		   \
+$(SRC)/czplot.$(ARCH).o	   \
+$(SRC)/drizzle.$(ARCH).o	   \
+$(SRC)/flux.$(ARCH).o		   \
+$(SRC)/fixwrap.$(ARCH).o	   \
+$(SRC)/fixcols.$(ARCH).o	   \
+$(SRC)/fiximage.$(ARCH).o	   \
+$(SRC)/gauss.$(ARCH).o		   \
+$(SRC)/getvel.$(ARCH).o	           \
+$(SRC)/getlst.$(ARCH).o	           \
+$(SRC)/medianmap.$(ARCH).o	   \
+$(SRC)/mkgauss.$(ARCH).o	   \
+$(SRC)/mksersic.$(ARCH).o	   \
+$(SRC)/galradius.$(ARCH).o	   \
+$(SRC)/galradbins.$(ARCH).o	   \
+$(SRC)/galsectors.$(ARCH).o	   \
+$(SRC)/galprofiles.$(ARCH).o	   \
+$(SRC)/getcoords.$(ARCH).o	   \
+$(SRC)/elliprofile.$(ARCH).o	   \
+$(SRC)/ringflux.$(ARCH).o	   \
+$(SRC)/petrosian.$(ARCH).o	   \
+$(SRC)/kronflux.$(ARCH).o	   \
+$(SRC)/multifit.$(ARCH).o	   \
+$(SRC)/objload.$(ARCH).o	   \
+$(SRC)/outline.$(ARCH).o	   \
+$(SRC)/polar.$(ARCH).o		   \
+$(SRC)/precess.$(ARCH).o	   \
+$(SRC)/profile.$(ARCH).o	   \
+$(SRC)/radec.$(ARCH).o	           \
+$(SRC)/region.$(ARCH).o	           \
+$(SRC)/rotcurve.$(ARCH).o	   \
+$(SRC)/scale.$(ARCH).o		   \
+$(SRC)/sexigesimal.$(ARCH).o	   \
+$(SRC)/spec.$(ARCH).o		   \
+$(SRC)/specpairfit.$(ARCH).o	   \
+$(SRC)/spexseq.$(ARCH).o	   \
+$(SRC)/spex1dgas.$(ARCH).o	   \
+$(SRC)/spex2dgas.$(ARCH).o	   \
+$(SRC)/mkclusters.$(ARCH).o	   \
+$(SRC)/star.$(ARCH).o		   \
+$(SRC)/transform.$(ARCH).o         \
+$(SRC)/imsub.$(ARCH).o		   \
+$(SRC)/imfit.$(ARCH).o		   \
+$(SRC)/imfit-fgauss.$(ARCH).o	   \
+$(SRC)/imfit-pgauss.$(ARCH).o	   \
+$(SRC)/imfit-pgauss-psf.$(ARCH).o	   \
+$(SRC)/imfit-qgauss.$(ARCH).o	   \
+$(SRC)/imfit-qgauss-psf.$(ARCH).o	   \
+$(SRC)/imfit-sgauss.$(ARCH).o	   \
+$(SRC)/imfit-sgauss-psf.$(ARCH).o	   \
+$(SRC)/imfit-qfgauss.$(ARCH).o	   \
+$(SRC)/imfit-qrgauss.$(ARCH).o	   
+
+# dependancy rules for include files ########################
+incs = \
+$(INC)/opihi.h \
+$(INC)/external.h \
+$(INC)/shell.h \
+$(INC)/dvomath.h \
+$(INC)/display.h 
+
+$(srcs): $(incs)
+
+$(LIB)/libastrocmd.$(ARCH).a: $(srcs)
+$(LIB)/libastrocmd.$(ARCH).$(DLLTYPE): $(srcs)
+
+$(DESTLIB)/libastrocmd.a: $(LIB)/libastrocmd.$(ARCH).a
+$(DESTLIB)/libastrocmd.$(DLLTYPE): $(LIB)/libastrocmd.$(ARCH).$(DLLTYPE)
+
+libastrocmd: $(DESTLIB)/libastrocmd.a $(DESTLIB)/libastrocmd.$(DLLTYPE)
+
+uninstall:
+	rm -f $(DESTLIB)/libastrocmd.a
+	rm -f $(DESTLIB)/libastrocmd.$(DLLTYPE)
+
Index: /branches/sc_branches/pantasks_condor/cmd.astro/altaz.c
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.astro/altaz.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.astro/altaz.c	(revision 34783)
@@ -0,0 +1,167 @@
+# include "astro.h"
+
+# define dCOS(A)   ((double) cos ((double)RAD_DEG*A))
+# define dSIN(A)   ((double) sin ((double)RAD_DEG*A))
+
+int HD_to_AZ(double *alt, double *az, double *rot, double ha, double dec, double lat);
+int AZ_to_HD(double *ha, double *dec, double *rot, double alt, double az, double lat);
+
+double atan2 (double y, double x);
+
+int altaz (int argc, char **argv) {
+  
+  int i;
+  double alt, az, lat, rot;
+  double ha, dec;
+  char *latstr;
+  Vector *hvec, *dvec, *avec, *zvec, *rvec;
+  opihi_flt *Hv, *Dv, *Av, *Zv, *Rv;
+
+  if (argc != 7) goto usage;
+  if (strcasecmp (argv[4], "to")) goto usage;
+
+  latstr = get_variable ("LATITUDE");
+  if (latstr == (char *) NULL) {
+    gprint (GP_ERR, "please define $LATITUDE\n");
+    return FALSE;
+  }
+  lat = atof (latstr);
+  gprint (GP_ERR, "using latitude of %f\n", lat);
+  free (latstr);
+
+  if (!strcmp (argv[1], "-az")) goto radec;
+  if (!strcmp (argv[1], "-hd")) goto altaz;
+
+ radec:
+  if (ISNUM(argv[2][0]) && ISNUM(argv[3][0])) {
+    /* ha/dec -> alt/az */
+    ha  = atof (argv[2]);
+    dec = atof (argv[3]);
+  
+    HD_to_AZ (&alt, &az, &rot, ha, dec, lat);
+
+    set_variable (argv[5], alt);
+    set_variable (argv[6], az);
+    set_variable ("ROT", rot);
+    return TRUE;
+  } 
+
+  /* find vectors */
+  if ((hvec = SelectVector (argv[2], OLDVECTOR, TRUE)) == NULL) return FALSE;
+  if ((dvec = SelectVector (argv[3], OLDVECTOR, TRUE)) == NULL) return FALSE;
+  if ((avec = SelectVector (argv[5], ANYVECTOR, TRUE)) == NULL) return FALSE;
+  if ((zvec = SelectVector (argv[6], ANYVECTOR, TRUE)) == NULL) return FALSE;
+  if ((rvec = SelectVector ("ROT",   ANYVECTOR, TRUE)) == NULL) return FALSE;
+
+  if (hvec[0].Nelements != dvec[0].Nelements) {
+    gprint (GP_ERR, "vectors %s and %s not the same length\n", argv[2], argv[3]);
+    return FALSE;
+  }
+  
+  // cast xvec or yvec to FLT if it is type INT
+  CastVector (hvec, OPIHI_FLT);
+  CastVector (dvec, OPIHI_FLT);
+  ResetVector (avec, OPIHI_FLT, hvec[0].Nelements);
+  ResetVector (zvec, OPIHI_FLT, hvec[0].Nelements);
+  ResetVector (rvec, OPIHI_FLT, hvec[0].Nelements);
+
+  Hv = hvec[0].elements.Flt;
+  Dv = dvec[0].elements.Flt;
+  Av = avec[0].elements.Flt;
+  Zv = zvec[0].elements.Flt;
+  Rv = rvec[0].elements.Flt;
+
+  for (i = 0; i < hvec[0].Nelements; i++, Hv++, Dv++, Av++, Zv++, Rv++) {
+    HD_to_AZ (Av, Zv, Rv, *Hv, *Dv, lat);
+  }
+  return TRUE;
+  
+ altaz:
+  if (ISNUM(argv[2][0]) && ISNUM(argv[3][0])) {
+    /* alt/az -> ha/dec */
+    alt = atof (argv[2]);
+    az  = atof (argv[3]);
+
+    AZ_to_HD (&ha, &dec, &rot, alt, az, lat);
+
+    set_variable (argv[5], ha);
+    set_variable (argv[6], dec);
+    set_variable ("ROT", rot);
+    return TRUE;
+  }
+  
+  /* find vectors */
+  if ((avec = SelectVector (argv[2], OLDVECTOR, TRUE)) == NULL) return FALSE;
+  if ((zvec = SelectVector (argv[3], OLDVECTOR, TRUE)) == NULL) return FALSE;
+  if ((hvec = SelectVector (argv[5], ANYVECTOR, TRUE)) == NULL) return FALSE;
+  if ((dvec = SelectVector (argv[6], ANYVECTOR, TRUE)) == NULL) return FALSE;
+  if ((rvec = SelectVector ("ROT",   ANYVECTOR, TRUE)) == NULL) return FALSE;
+
+  if (avec[0].Nelements != zvec[0].Nelements) {
+    gprint (GP_ERR, "vectors %s and %s not the same length\n", argv[2], argv[3]);
+    return FALSE;
+  }
+  
+  // cast xvec or yvec to FLT if it is type INT
+  CastVector (avec, OPIHI_FLT);
+  CastVector (zvec, OPIHI_FLT);
+  ResetVector (hvec, OPIHI_FLT, avec[0].Nelements);
+  ResetVector (dvec, OPIHI_FLT, avec[0].Nelements);
+  ResetVector (rvec, OPIHI_FLT, avec[0].Nelements);
+
+  Hv = hvec[0].elements.Flt;
+  Dv = dvec[0].elements.Flt;
+  Av = avec[0].elements.Flt;
+  Zv = zvec[0].elements.Flt;
+  Rv = rvec[0].elements.Flt;
+
+  for (i = 0; i < hvec[0].Nelements; i++, Hv++, Dv++, Av++, Zv++, Rv++) {
+    AZ_to_HD (Hv, Dv, Rv, *Av, *Zv, lat);
+  }
+  return TRUE;
+  
+ usage:
+  gprint (GP_ERR, "USAGE: altaz -az (ha) (dec) to (alt) (az)\n");
+  gprint (GP_ERR, "USAGE: altaz -hd (alt) (az) to (ha) (dec)\n");
+  gprint (GP_ERR, "       -hd alt/az to ha/dec, -az ha/dec to alt/az\n");
+  gprint (GP_ERR, "       returned values in variables or vectors provided\n");
+  return FALSE;
+}
+
+int HD_to_AZ(double *alt, double *az, double *rot, double ha, double dec, double lat) {
+
+  double sind, sinh, cosh;
+
+  sind = dSIN (dec) * dSIN (lat) + dCOS (dec) * dCOS (ha) * dCOS (lat);
+  *alt  = DEG_RAD * asin (sind);
+
+  sinh = - dCOS (dec) * dSIN (ha);
+  cosh =   dSIN (dec) * dCOS (lat) - dCOS (dec) * dCOS (ha) * dSIN (lat);
+
+  *az = DEG_RAD * atan2 (sinh, cosh);
+  
+  sinh = -dCOS(*az) * dSIN(*alt) * dSIN(ha) * dSIN(lat) + dSIN(*az) * dSIN(*alt) * dCOS(ha) - dSIN(ha) * dCOS(*alt) * dCOS(lat);
+  cosh = -dSIN(*az) * dSIN(ha) * dSIN(lat) - dCOS(*az) * dCOS(ha);
+  *rot = -DEG_RAD * atan2 (sinh, cosh);
+  
+  return TRUE;
+}
+
+int AZ_to_HD(double *ha, double *dec, double *rot, double alt, double az, double lat) {
+
+  double sind, sinh, cosh;
+
+  sind = dSIN (alt) * dSIN (lat) + dCOS (alt) * dCOS (az) * dCOS (lat);
+  *dec  = DEG_RAD * asin (sind);
+
+  sinh = -dCOS (alt) * dSIN (az);
+  cosh =  dSIN (alt) * dCOS (lat) - dCOS (alt) * dCOS (az) * dSIN (lat);
+
+  *ha = DEG_RAD * atan2 (sinh, cosh);
+
+  sinh = -dCOS(az) * dSIN(alt) * dSIN(*ha) * dSIN(lat) + dSIN(az) * dSIN(alt) * dCOS(*ha) - dSIN(*ha) * dCOS(alt) * dCOS(lat);
+  cosh = -dSIN(az) * dSIN(*ha) * dSIN(lat) - dCOS(az) * dCOS(*ha);
+  *rot = -DEG_RAD * atan2 (sinh, cosh);
+
+  return TRUE;
+}
Index: /branches/sc_branches/pantasks_condor/cmd.astro/biassub.c
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.astro/biassub.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.astro/biassub.c	(revision 34783)
@@ -0,0 +1,126 @@
+# include "astro.h"
+
+int biassub (int argc, char **argv) {
+  
+  int i, j, k, N, dir, nlong, nwide, start;
+  int sx, sy, nx, ny, NX, NY, NoVector, Nval;
+  float *V, dV, *segment, val;
+  opihi_flt *DV, *vect;
+  Vector *xvec, *yvec;
+  Buffer *buf;
+
+  xvec = yvec = NULL;
+  NoVector = TRUE;
+  if ((N = get_argument (argc, argv, "-v"))) {
+    NoVector = FALSE;
+    remove_argument (N, &argc, argv);
+    if ((xvec = SelectVector (argv[N], ANYVECTOR, TRUE)) == NULL) return (FALSE);
+    remove_argument (N, &argc, argv);
+    if ((yvec = SelectVector (argv[N], ANYVECTOR, TRUE)) == NULL) return (FALSE);
+    remove_argument (N, &argc, argv);
+  }
+
+  if (argc != 7) {
+    gprint (GP_ERR, "USAGE: biassub <buffer> sx sy nx ny dir [-v N V]\n");
+    gprint (GP_ERR, "  optional storage of vector and sequence in N and V\n");
+    return (FALSE);
+  }
+  if ((buf = SelectBuffer (argv[1], OLDBUFFER, TRUE)) == NULL) return (FALSE);
+
+  sx = atof (argv[2]);
+  sy = atof (argv[3]);
+  nx = atof (argv[4]);
+  ny = atof (argv[5]);
+  dir = atof (argv[6]);
+  if ((dir != 0) && (dir != 1)) {
+    gprint (GP_ERR, " dir must be either 0 (x) or 1 (y)\n");
+    return (FALSE);
+  }
+  if (dir) {
+    start = sy;
+    nwide = nx;
+    nlong = ny;
+  } else {
+    start = sx;
+    nwide = ny;
+    nlong = nx;
+  }    
+  gprint (GP_LOG, "start: %d %d  size: %d %d\n", sx, sy, nx, ny);
+
+    if ((sx < 0) || (sy < 0) || 
+      (sx+nx > buf[0].matrix.Naxis[0]) || 
+      (sy+ny > buf[0].matrix.Naxis[1])) {
+    gprint (GP_ERR, "region out of range\n");
+    return (FALSE);
+  }
+
+  ALLOCATE (vect, opihi_flt, nlong);
+  ALLOCATE (segment, float, nwide);
+
+  NX = buf[0].matrix.Naxis[0];
+  NY = buf[0].matrix.Naxis[1];
+  if (dir) {
+    for (j = sy; j < sy + ny; j++) {
+      V = (float *)(buf[0].matrix.buffer) + j*NX + sx; 
+      for (i = 0; i < nx; i++, V++) {
+	segment[i] = *V;
+      }
+      fsort (segment, nwide);
+      val = Nval = 0;
+      for (k = 0.25*nwide; k <=0.75*nwide; k++) {
+	val += segment[k];
+	Nval ++;
+      }
+      vect[j-sy] = val / Nval;
+    }
+  } else {
+    for (i = 0; i < nx; i++) {
+      V = (float *)(buf[0].matrix.buffer) + sy*NX + sx + i; 
+      for (j = 0; j < ny; j++, V+=NX) {
+	segment[j] = *V;
+      }
+      fsort (segment, nwide);
+      val = Nval = 0;
+      for (k = 0.25*nwide; k <=0.75*nwide; k++) {
+	val += segment[k];
+	Nval ++;
+      }
+      vect[i] = val / Nval;
+    }
+  }
+
+  if (!NoVector) {
+    ResetVector (xvec, OPIHI_FLT, nlong);
+    ResetVector (yvec, OPIHI_FLT, nlong);
+    for (i = 0; i < nlong; i++) {
+      xvec[0].elements.Flt[i] = i + start;
+      yvec[0].elements.Flt[i] = vect[i];
+    }
+  }
+
+  if (dir) {
+    /* here we run all the way across in X for the defined Y range */
+    for (j = sy; j < sy + ny; j++) {
+      V = (float *)(buf[0].matrix.buffer) + j*NX; 
+      dV = vect[j];
+      for (i = 0; i < NX; i++, V++) {
+	*V -= dV;
+      }
+    }
+  } else {
+    /* here we run all the way across in Y for the defined X range */
+    for (j = 0; j < NY; j++) {
+      V = (float *)(buf[0].matrix.buffer) + j*NX + sx; 
+      DV = vect;
+      for (i = 0; i < nx; i++, V++, DV++) {
+	*V -= *DV;
+      }
+    }
+  }
+
+  free (segment);
+  free (vect);
+
+  return (TRUE);
+}
+
Index: /branches/sc_branches/pantasks_condor/cmd.astro/cdot.c
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.astro/cdot.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.astro/cdot.c	(revision 34783)
@@ -0,0 +1,39 @@
+# include "data.h"
+
+int cdot (int argc, char **argv) {
+  
+  int kapa, status;
+  Graphdata graphmode;
+  float x, y;
+  double r, d, Rmin, Rmax;
+
+  if (!style_args (&graphmode, &argc, argv, &kapa)) return FALSE;
+
+  if (argc != 3) {
+    gprint (GP_ERR, "USAGE: dot <ra> <dec>\n");
+    return (FALSE);
+  }
+  r = atof(argv[1]);
+  d = atof(argv[2]);
+
+  Rmin = graphmode.coords.crval1 - 182.0;
+  Rmax = graphmode.coords.crval1 + 182.0;
+  // Rmid = 0.5*(Rmin + Rmax);
+
+  /* set point style and errorbar mode (these are NOT sticky) */
+  graphmode.style = 2;
+  graphmode.etype = 0;
+
+  r = ohana_normalize_angle (r);
+  while (r < Rmin) r += 360.0;
+  while (r > Rmax) r -= 360.0;
+
+  status = fRD_to_XY (&x, &y, r, d, &graphmode.coords);
+  if (!status) return TRUE;
+  
+  if (!KapaPrepPlot (kapa, 1, &graphmode)) return (FALSE);
+  KapaPlotVector (kapa, 1, &x, "x");
+  KapaPlotVector (kapa, 1, &y, "y");
+  
+  return (TRUE);
+}
Index: /branches/sc_branches/pantasks_condor/cmd.astro/cgrid.c
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.astro/cgrid.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.astro/cgrid.c	(revision 34783)
@@ -0,0 +1,287 @@
+# include "astro.h"
+
+# define GRID_SPACING 0.002
+
+# define CHECKELEMENTS						\
+  if (N == NELEMENTS) {						\
+    NELEMENTS +=200;						\
+    REALLOCATE (Xvec.elements.Flt, opihi_flt, NELEMENTS);	\
+    REALLOCATE (Yvec.elements.Flt, opihi_flt, NELEMENTS);	\
+  }
+
+# define ADD_COORDINATE(RA,DEC)						\
+  status = RD_to_XY (&Xvec.elements.Flt[N], &Yvec.elements.Flt[N], (RA), (DEC), &graphmode.coords); \
+  if ((Xvec.elements.Flt[N] >= graphmode.xmin) && (Xvec.elements.Flt[N] <= graphmode.xmax) && \
+      (Yvec.elements.Flt[N] >= graphmode.ymin) && (Yvec.elements.Flt[N] <= graphmode.ymax) && status) { \
+    N++;								\
+    CHECKELEMENTS;							\
+    OnPic = TRUE;							\
+    if (!First) {							\
+      Xvec.elements.Flt[N] = Xvec.elements.Flt[N-1];			\
+      Yvec.elements.Flt[N] = Yvec.elements.Flt[N-1];			\
+      N++;								\
+      CHECKELEMENTS;							\
+    } else {								\
+      if (N > 1) {							\
+	Xvec.elements.Flt[N-2] = Xvec.elements.Flt[N-1];		\
+	Yvec.elements.Flt[N-2] = Yvec.elements.Flt[N-1];		\
+	N--;								\
+      }									\
+      First = FALSE;							\
+    }									\
+  } else {								\
+    LOnPic = FALSE;							\
+    First = TRUE;							\
+  }
+
+# define ADD_DEC_LINE(RA)						\
+  /* first, DEC increasing */						\
+  LOnPic = TRUE;							\
+  OnPic = FALSE;							\
+  First = TRUE;								\
+  for (d = firstDEC; (d < 90 + dD) && (LOnPic || NorthPole || SouthPole); d += dD) { \
+    D = MAX (-90, MIN(90, d));						\
+    ADD_COORDINATE((RA), D);						\
+  }									\
+  /* next, DEC decreasing */						\
+  First = TRUE;								\
+  LOnPic = TRUE;							\
+  for (d = firstDEC; (d > -90 - dD) && (LOnPic || NorthPole || SouthPole); d -= dD) { \
+    D = MAX (-90, MIN(90, d));						\
+    ADD_COORDINATE((RA), D);						\
+  } 
+
+# define ADD_RA_LINE(DEC)						\
+  D = MAX (-90, MIN(90, (DEC)));					\
+  /* first, RA increasing */						\
+  LOnPic = TRUE;							\
+  OnPic = FALSE;							\
+  First = TRUE;								\
+  lastRA = graphmode.coords.crval1 + 180.0;				\
+  for (r = firstRA; (r < lastRA + dR) && (LOnPic || NorthPole || SouthPole); r += dR) { \
+    R = MIN (r, lastRA);					\
+    ADD_COORDINATE(R, D);						\
+  }									\
+  /* next, RA decreasing */						\
+  First = TRUE;								\
+  LOnPic = TRUE;							\
+  lastRA = graphmode.coords.crval1 - 180.0;				\
+  for (r = firstRA; (r > lastRA - dR) && (LOnPic || NorthPole || SouthPole); r -= dR) { \
+    R = MAX (r, lastRA);					\
+    ADD_COORDINATE(R, D);						\
+  }
+
+int cgrid (int argc, char **argv) {
+  
+  double range, minor, major;
+  double firstRA, lastRA, firstDEC, minorRA, minorDEC;
+  double r, d, dR, dD, R, D;
+  double x, y;
+  Vector Xvec, Yvec;
+  int kapa, NorthPole, SouthPole, N, OnPic, LOnPic, status, NELEMENTS;
+  int First, RAbyHour, Labels;
+  Graphdata graphmode;
+
+  RAbyHour = FALSE;
+  if ((N = get_argument (argc, argv, "-ra-by-hour"))) {
+    remove_argument (N, &argc, argv);
+    RAbyHour = TRUE;
+  }
+
+  Labels = FALSE;
+  if ((N = get_argument (argc, argv, "-labels"))) {
+    remove_argument (N, &argc, argv);
+    Labels = TRUE;
+  }
+
+  if (!style_args (&graphmode, &argc, argv, &kapa)) return FALSE;
+
+  if (argc != 1) {
+    gprint (GP_ERR, "USAGE: cgrid [style] [-ra-by-hour] [-labels]\n");
+    return (FALSE);
+  }
+
+  /* are we plotting one of the poles? */
+  NorthPole = SouthPole = FALSE;
+  status = RD_to_XY (&x, &y, 0.0, 90.0, &graphmode.coords);
+  if ((x >= graphmode.xmin) && (x <= graphmode.xmax) && 
+      (y >= graphmode.ymin) && (y <= graphmode.ymax) && status)
+    NorthPole = TRUE;
+  status = RD_to_XY (&x, &y, 0.0, -90.0, &graphmode.coords);
+  if ((x >= graphmode.xmin) && (x <= graphmode.xmax) && 
+      (y >= graphmode.ymin) && (y <= graphmode.ymax) && status)
+    SouthPole = TRUE;
+
+  /* set spacings for RA */
+  minorRA = minorDEC = 0.1;
+  range = MIN (fabs(graphmode.coords.cdelt1*(graphmode.xmax-graphmode.xmin)), fabs(graphmode.coords.cdelt2*(graphmode.ymax-graphmode.ymin)));
+  if (NorthPole || SouthPole) range = 360;
+
+  if (RAbyHour) {
+    SetGridScales (&major, &minor, range / 15.0);
+    minorRA = minor * 15.0;
+  } else {
+    SetGridScales (&major, &minor, range);
+    minorRA = minor;
+  }
+  dR = range * GRID_SPACING;
+
+  /* set spacings for DEC */
+  range = MIN (fabs(graphmode.coords.cdelt1*(graphmode.xmax-graphmode.xmin)), fabs(graphmode.coords.cdelt2*(graphmode.ymax-graphmode.ymin)));
+  SetGridScales (&major, &minor, range);
+  minorDEC = minor;
+  dD = range * GRID_SPACING;
+
+  /* choose a starting point */
+  if ((int)(graphmode.coords.crval1/minorRA) == (graphmode.coords.crval1/minorRA)) {
+    firstRA = graphmode.coords.crval1;
+  } else {
+    firstRA = minorRA + minorRA*((int)(graphmode.coords.crval1/minorRA));
+  }
+  if ((int)(graphmode.coords.crval2/minorDEC) == (graphmode.coords.crval2/minorDEC)) {
+    firstDEC = graphmode.coords.crval2;
+  } else {
+    firstDEC = minorDEC + minorDEC*((int)(graphmode.coords.crval2/minorDEC));
+  }
+  if (SouthPole) firstDEC = -90;
+  if (NorthPole) firstDEC = 90;
+  
+  /* prepare vectors to hold data */
+  N = 0;
+  NELEMENTS = 200;
+  SetVector (&Xvec, OPIHI_FLT, NELEMENTS);
+  SetVector (&Yvec, OPIHI_FLT, NELEMENTS);
+  
+  { // sanity check
+    float Nelem;
+    Nelem = 180.0 / minorRA;
+    if (!isfinite(Nelem) || (fabs(Nelem) > 10000)) { fprintf (stderr, "absurd cgrid spacing %f\n", minorRA); return FALSE; }
+    Nelem = 180.0 / minorDEC;
+    if (!isfinite(Nelem) || (fabs(Nelem) > 10000)) { fprintf (stderr, "absurd cgrid spacing %f\n", minorDEC); return FALSE; }
+  }
+
+  /***  do consecutive RA lines, first increasing **/
+  OnPic = TRUE;
+  lastRA = graphmode.coords.crval1 + 180.0;
+  for (r = firstRA; (r <= lastRA) && (OnPic); r += minorRA) {
+    ADD_DEC_LINE (r);
+  }
+  if (r != lastRA) {
+    ADD_DEC_LINE (lastRA);
+  }
+
+  /***  do consecutive RA lines, decreasing **/
+  OnPic = TRUE;
+  lastRA = graphmode.coords.crval1 - 180.0;
+  for (r = firstRA; (r >=  lastRA) && (OnPic); r -= minorRA) {
+    ADD_DEC_LINE (r);
+  }
+  if (r != lastRA) {
+    ADD_DEC_LINE (lastRA);
+  }
+
+  /***  do consecutive DEC lines, first increasing **/
+  OnPic = TRUE;
+  for (d = firstDEC; (d < 90 + dD) && (OnPic); d += minorDEC) {
+    ADD_RA_LINE (d);
+  }
+
+  /***  do consecutive DEC lines, decreasing **/
+  OnPic = TRUE;
+  for (d = firstDEC; (d > -90 - dD) && (OnPic); d -= minorDEC) {
+    ADD_RA_LINE (d);
+  }
+  
+  // add labels for center lines:
+  if (Labels) { 
+    char line[16], format[8];
+    double xt, yt, dx, dy, frac;
+    dx = +0.01 * (graphmode.xmax - graphmode.xmin);
+    dy = -0.02 * (graphmode.ymax - graphmode.ymin);
+
+    for (r = firstRA; r <= graphmode.coords.crval1 + 180.0; r += minorRA) {
+      status = RD_to_XY (&xt, &yt, r, graphmode.coords.crval2, &graphmode.coords);
+      if (!status) continue;
+      if (xt < graphmode.xmin) continue;
+      if (xt > graphmode.xmax) continue;
+      if (yt < graphmode.ymin) continue;
+      if (yt > graphmode.ymax) continue;
+      frac = -1.0 * log10(minorRA);
+      if (frac != (int)frac) {
+	frac += 1.0;
+      }
+      if (frac <= 0.0) frac = 0.0;
+      snprintf (format, 8, "%%.%df", (int) frac);
+      if (RAbyHour) {
+	snprintf (line, 16, format, r / 15.0);
+      } else {
+	snprintf (line, 16, format, r);
+      }
+      KapaSendTextline (kapa, line, xt + dx, yt + dy, 0.0);
+    }
+    for (r = firstRA; r >= graphmode.coords.crval1 - 180.0; r -= minorRA) {
+      status = RD_to_XY (&xt, &yt, r, graphmode.coords.crval2, &graphmode.coords);
+      if (!status) continue;
+      if (xt < graphmode.xmin) continue;
+      if (xt > graphmode.xmax) continue;
+      if (yt < graphmode.ymin) continue;
+      if (yt > graphmode.ymax) continue;
+      frac = -1.0 * log10(minorRA);
+      if (frac != (int)frac) {
+	frac += 1.0;
+      }
+      if (frac <= 0.0) frac = 0.0;
+      snprintf (format, 8, "%%.%df", (int) frac);
+      if (RAbyHour) {
+	snprintf (line, 16, format, r / 15.0);
+      } else {
+	snprintf (line, 16, format, r);
+      }
+      KapaSendTextline (kapa, line, xt + dx, yt + dy, 0.0);
+    }
+    for (d = firstDEC; d <= graphmode.coords.crval2 + 90.0; d += minorDEC) {
+      status = RD_to_XY (&xt, &yt, graphmode.coords.crval1, d, &graphmode.coords);
+      if (!status) continue;
+      if (xt < graphmode.xmin) continue;
+      if (xt > graphmode.xmax) continue;
+      if (yt < graphmode.ymin) continue;
+      if (yt > graphmode.ymax) continue;
+      frac = -1.0 * log10(minorDEC);
+      if (frac != (int)frac) {
+	frac += 1.0;
+      }
+      if (frac <= 0.0) frac = 0.0;
+      snprintf (format, 8, "%%.%df", (int) frac);
+      snprintf (line, 16, format, d);
+      KapaSendTextline (kapa, line, xt + dx, yt + dy, 0.0);
+    }
+    for (d = firstDEC; d >= graphmode.coords.crval2 - 90.0; d -= minorDEC) {
+      status = RD_to_XY (&xt, &yt, graphmode.coords.crval1, d, &graphmode.coords);
+      if (!status) continue;
+      if (xt < graphmode.xmin) continue;
+      if (xt > graphmode.xmax) continue;
+      if (yt < graphmode.ymin) continue;
+      if (yt > graphmode.ymax) continue;
+      frac = -1.0 * log10(minorDEC);
+      if (frac != (int)frac) {
+	frac += 1.0;
+      }
+      if (frac <= 0.0) frac = 0.0;
+      snprintf (format, 8, "%%.%df", (int) frac);
+      snprintf (line, 16, format, d);
+      KapaSendTextline (kapa, line, xt + dx, yt + dy, 0.0);
+    }
+  }
+
+  /* send the line segments as connect-points */
+  Xvec.Nelements = Yvec.Nelements = N;
+  graphmode.style = 2; /* points */
+  graphmode.ptype = 100; /* connect a pair */
+  graphmode.etype = 0;
+  PlotVectorPair (kapa, &Xvec, &Yvec, NULL, &graphmode);
+
+  free (Xvec.elements.Ptr);
+  free (Yvec.elements.Ptr);
+  return (TRUE);
+
+}
Index: /branches/sc_branches/pantasks_condor/cmd.astro/cline.c
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.astro/cline.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.astro/cline.c	(revision 34783)
@@ -0,0 +1,40 @@
+# include "data.h"
+
+int cline (int argc, char **argv) {
+  
+  int kapa, status;
+  Graphdata graphmode;
+  float x[2], y[2];
+  double r[2], d[2];
+  
+  if (!style_args (&graphmode, &argc, argv, &kapa)) return FALSE;
+
+  if (argc != 6) {
+    gprint (GP_ERR, "USAGE: cline <x> <y> to <x> <y>\n");
+    return (FALSE);
+  }
+  r[0] = atof(argv[1]);
+  d[0] = atof(argv[2]);
+  r[1] = atof(argv[4]);
+  d[1] = atof(argv[5]);
+
+  /* set point style and errorbar mode (these are NOT sticky) */
+  graphmode.style = 0;
+  graphmode.etype = 0;
+
+  // need to plot to edge of picture..
+  status = fRD_to_XY (&x[0], &y[0], r[0], d[0], &graphmode.coords);
+  if (!status) {
+    return FALSE;
+  }
+  status = fRD_to_XY (&x[1], &y[1], r[1], d[1], &graphmode.coords);
+  if (!status) {
+    return FALSE;
+  }
+  
+  if (!KapaPrepPlot (kapa, 2, &graphmode)) return (FALSE);
+  KapaPlotVector (kapa, 2, x, "x");
+  KapaPlotVector (kapa, 2, y, "y");
+  
+  return (TRUE);
+}
Index: /branches/sc_branches/pantasks_condor/cmd.astro/coords.c
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.astro/coords.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.astro/coords.c	(revision 34783)
@@ -0,0 +1,125 @@
+# include "astro.h"
+
+enum {NONE, SKY, PIXEL, VECTOR, SCALAR};
+
+int coords (int argc, char **argv) {
+
+  int i, mode, form, N, Quiet;
+  double Xin, Yin, Xout, Yout;
+  char *MOSAIC;
+  Coords coords, moscoords;
+  Buffer *buf, *mosbuffer;
+  Vector *xvec = NULL;
+  Vector *yvec = NULL;
+
+  MOSAIC = NULL;
+  if ((N = get_argument (argc, argv, "-mosaic"))) {
+    remove_argument (N, &argc, argv);
+    MOSAIC = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  Quiet = FALSE;
+  if ((N = get_argument (argc, argv, "-q"))) {
+    Quiet = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+  if ((N = get_argument (argc, argv, "-quiet"))) {
+    Quiet = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+
+  form = NONE;
+  mode = NONE;
+  if ((N = get_argument (argc, argv, "-p"))) {
+    remove_argument (N, &argc, argv);
+    mode = SKY;
+  }
+  if ((N = get_argument (argc, argv, "-c"))) {
+    if (mode == SKY) goto syntax;
+    remove_argument (N, &argc, argv);
+    mode = PIXEL;
+  }
+  if (mode == NONE) goto syntax;
+  if (argc != 4) goto syntax;
+
+  if (SelectScalar (argv[2], &Xin)) {
+    if (!SelectScalar (argv[3], &Yin)) {
+      gprint (GP_ERR, "syntax error: mixed vector and scalar?\n");
+      return (FALSE);
+    }
+    form = SCALAR;
+  } else {
+    if ((xvec = SelectVector (argv[2], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+    if ((yvec = SelectVector (argv[3], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+    if (xvec[0].Nelements != yvec[0].Nelements) {
+      fprintf (stderr, "mis-matched vector lengths\n");
+      return (FALSE);
+    }
+    REQUIRE_VECTOR_FLT (xvec, FALSE); 
+    REQUIRE_VECTOR_FLT (yvec, FALSE); 
+    form = VECTOR;
+  }      
+
+  if ((buf = SelectBuffer (argv[1], OLDBUFFER, TRUE)) == NULL) goto escape;
+  GetCoords (&coords, &buf[0].header);
+  if (!strcmp(&coords.ctype[4], "-WRP")) {
+    if (MOSAIC == NULL) {
+      gprint (GP_ERR, "must supply mosaic for WRP coords\n");
+      return (FALSE);
+    }
+    if ((mosbuffer = SelectBuffer (MOSAIC, OLDBUFFER, TRUE)) == NULL) goto escape;
+    GetCoords (&moscoords, &mosbuffer[0].header);
+    RegisterMosaic (&moscoords);
+  }
+  
+  if (form == SCALAR) {
+    if (mode == SKY) {
+      XY_to_RD (&Xout, &Yout, Xin, Yin, &coords);
+      if (!Quiet) gprint (GP_LOG, "%10.6f %10.6f\n", Xout, Yout);
+      set_variable ("RA", Xout);
+      set_variable ("DEC", Yout);
+      return (TRUE);
+    }
+    if (mode == PIXEL) {
+      Xin = ohana_normalize_angle_to_midpoint (Xin, coords.crval1);
+      RD_to_XY (&Xout, &Yout, Xin, Yin, &coords);
+      if (!Quiet) gprint (GP_LOG, "%7.2f %7.2f\n", Xout, Yout);
+      set_variable ("Xc", Xout);
+      set_variable ("Yc", Yout);
+      return (TRUE);
+    }
+  }
+  if (mode == SKY) {
+    for (i = 0; i < xvec[0].Nelements; i++) {
+      double Xin = xvec[0].elements.Flt[i];
+      double Yin = yvec[0].elements.Flt[i];
+      xvec[0].elements.Flt[i] = NAN;
+      yvec[0].elements.Flt[i] = NAN;
+      XY_to_RD (&xvec[0].elements.Flt[i], &yvec[0].elements.Flt[i], Xin, Yin, &coords);
+    }
+    return (TRUE);
+  }
+  if (mode == PIXEL) {
+    for (i = 0; i < xvec[0].Nelements; i++) {
+      double Xin = xvec[0].elements.Flt[i];
+      double Yin = yvec[0].elements.Flt[i];
+      xvec[0].elements.Flt[i] = NAN;
+      yvec[0].elements.Flt[i] = NAN;
+      Xin = ohana_normalize_angle_to_midpoint (Xin, coords.crval1);
+      RD_to_XY (&xvec[0].elements.Flt[i], &yvec[0].elements.Flt[i], Xin, Yin, &coords);
+    }
+    return (TRUE);
+  }
+  return (FALSE);
+
+ syntax:
+  gprint (GP_ERR, "USAGE: coords [buffer] (-c R D) | (-p X Y)\n");
+  gprint (GP_ERR, "only one of -p or -c can be used\n");
+  gprint (GP_ERR, " -p : from pixels to ra/dec\n");
+  gprint (GP_ERR, " -c : from ra/dec to pixels\n");
+  gprint (GP_ERR, " coordinates are in degrees\n");
+ escape:
+  if (MOSAIC != NULL) free (MOSAIC);
+  return (FALSE);
+}
Index: /branches/sc_branches/pantasks_condor/cmd.astro/cplot.c
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.astro/cplot.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.astro/cplot.c	(revision 34783)
@@ -0,0 +1,92 @@
+# include "astro.h"
+
+int cplot (int argc, char **argv) {
+  
+  double ra_prev = 0;
+  int i, kapa, Npts, status, leftside;
+  opihi_flt *x, *y, *r, *d, Rmin, Rmax, Rmid;
+  Vector Xvec, Yvec, *xvec, *yvec;
+  Graphdata graphmode;
+
+  if (!style_args (&graphmode, &argc, argv, &kapa)) return FALSE;
+
+  if (argc != 3) {
+    gprint (GP_ERR, "USAGE: cplot <ra> <dec> [style]\n");
+    return (FALSE);
+  }
+
+  Rmin = graphmode.coords.crval1 - 182.0;
+  Rmax = graphmode.coords.crval1 + 182.0;
+  Rmid = 0.5*(Rmin + Rmax);
+
+  /* find vectors */
+  if ((xvec = SelectVector (argv[1], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((yvec = SelectVector (argv[2], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+
+  REQUIRE_VECTOR_FLT (xvec, FALSE); 
+  REQUIRE_VECTOR_FLT (yvec, FALSE); 
+
+  if (xvec[0].Nelements != yvec[0].Nelements) {
+    gprint (GP_ERR, "vectors are not the same length\n");
+    return (FALSE);
+  }
+
+  SetVector (&Xvec, OPIHI_FLT, xvec[0].Nelements);
+  SetVector (&Yvec, OPIHI_FLT, xvec[0].Nelements);
+  
+  r = xvec[0].elements.Flt;
+  d = yvec[0].elements.Flt;
+  x = Xvec.elements.Flt;
+  y = Yvec.elements.Flt;
+  
+  Npts = 0;
+  for (i = 0; i < Xvec.Nelements; i++, r++, d++) {
+    double ra = ohana_normalize_angle (*r);
+    while (ra < Rmin) ra += 360.0;
+    while (ra > Rmax) ra -= 360.0;
+
+    // for pair-by-pair connections, check on second point if we straddle the back midline
+    if (graphmode.ptype == 100) {
+      if (i % 2) {
+	leftside = (ra_prev < Rmid); // if first of the pair is left, second must be as well
+	if ( leftside && (ra > Rmid + 90)) { ra -= 360.0; }
+	if (!leftside && (ra < Rmid - 90)) { ra += 360.0; }
+      } else {
+	ra_prev = ra;
+      }
+    }
+    status = RD_to_XY (x, y, ra, *d, &graphmode.coords);
+
+    // if we fail on one of the points, drop the corresponding pair
+    if (!status) {
+      if (graphmode.ptype == 100) {
+	if (i % 2) {
+	  // for odd points, skip previous also
+	  x--;
+	  y--;
+	  Npts--;
+	} else {
+	  // for even points, skip previous also
+	  i++;
+	  r++;
+	  d++;
+	}
+      }
+      continue;
+    }
+    x++;
+    y++;
+    Npts++;
+  }
+  Xvec.Nelements = Npts;
+  Yvec.Nelements = Npts;
+
+  graphmode.etype = 0;
+  PlotVectorPair (kapa, &Xvec, &Yvec, NULL, &graphmode);
+  
+  free (Xvec.elements.Ptr);
+  free (Yvec.elements.Ptr);
+    
+  return (TRUE);
+}
+
Index: /branches/sc_branches/pantasks_condor/cmd.astro/csystem.c
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.astro/csystem.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.astro/csystem.c	(revision 34783)
@@ -0,0 +1,68 @@
+# include "astro.h"
+
+int csystem (int argc, char **argv) {
+
+  /* USAGE: csystem [C/G/E/H] [C/G/E/H] [epoch] */
+  int i;
+  double X, Y, x, y;
+  opihi_flt *xptr, *yptr;
+  Vector *xvec, *yvec;
+  CoordTransformSystem input, output;
+  CoordTransform *transform;
+
+  if (argc != 5) goto syntax;
+
+  switch (argv[1][0]) {
+    case 'C': input = COORD_CELESTIAL; break;
+    case 'G': input = COORD_GALACTIC; break;
+    case 'E': input = COORD_ECLIPTIC; break;
+    default: goto syntax;
+  }
+
+  switch (argv[2][0]) {
+    case 'C': output = COORD_CELESTIAL; break;
+    case 'G': output = COORD_GALACTIC; break;
+    case 'E': output = COORD_ECLIPTIC; break;
+    default: goto syntax;
+  }
+
+  transform = InitTransform (input, output);
+  if (transform == NULL) {
+    gprint (GP_ERR, "transform %c to %c is not yet defined\n", argv[1][0], argv[2][0]);
+    return (FALSE);
+  }
+    
+  if (SelectScalar (argv[3], &X)) {
+    if (!SelectScalar (argv[4], &Y)) return (FALSE);
+      
+    ApplyTransform (&x, &y, X, Y, transform);
+
+    gprint (GP_LOG, "%10.6f %10.6f\n", x, y);
+    return (TRUE);
+  }
+
+  /* find vectors */
+  if ((xvec = SelectVector (argv[3], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((yvec = SelectVector (argv[4], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+
+  if (xvec[0].Nelements != yvec[0].Nelements) {
+    gprint (GP_ERR, "vectors %s and %s not the same length\n", argv[3], argv[4]);
+    return (FALSE);
+  }
+  
+  CastVector (xvec, OPIHI_FLT);
+  CastVector (yvec, OPIHI_FLT);
+
+  xptr = xvec[0].elements.Flt;
+  yptr = yvec[0].elements.Flt;
+
+  for (i = 0; i < xvec[0].Nelements; i++, xptr++, yptr++) {
+    ApplyTransform (xptr, yptr, *xptr, *yptr, transform);
+  }
+
+  return (TRUE);
+
+syntax:
+  gprint (GP_ERR, "USAGE: csystems [C/G/E/H] [C/G/E/H] X Y\n");
+  return (FALSE);
+}
Index: /branches/sc_branches/pantasks_condor/cmd.astro/ctimes.c
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.astro/ctimes.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.astro/ctimes.c	(revision 34783)
@@ -0,0 +1,66 @@
+# include "astro.h"
+
+int ctimes (int argc, char **argv) {
+
+  int Reference, TimeFormat, N;
+  double value;
+  time_t time, TimeReference;
+  char *date, *Variable;
+
+  Variable = (char *) NULL;
+  if ((N = get_argument (argc, argv, "-var"))) {
+    remove_argument (N, &argc, argv);
+    Variable = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  if (argc != 3) {
+    gprint (GP_ERR, "USAGE: ctimes [-ref (value) / -abs (date)] [-var name]\n");
+    return (FALSE);
+  }
+
+  GetTimeFormat (&TimeReference, &TimeFormat);
+
+  Reference = FALSE;
+  if (!strcmp (argv[1], "-ref")) Reference = TRUE;
+
+  if (Reference) {
+
+    value = atof (argv[2]);
+    time = TimeRef (value, TimeReference, TimeFormat);
+    date = ohana_sec_to_date (time);
+    
+    if (Variable != (char *) NULL) {
+      set_str_variable (Variable, date);
+      free (Variable);
+    } else {
+      gprint (GP_ERR, "time: %s\n", date);
+    }
+
+    free (date);
+    return (TRUE);
+
+  } else {
+
+    if (strcmp (argv[1], "-abs")) {
+      gprint (GP_ERR, "syntax error\n");
+      return (FALSE);
+    }
+
+    if (!ohana_str_to_time (argv[2], &time)) { 
+      gprint (GP_ERR, "syntax error\n");
+      return (FALSE);
+    }
+    
+    value = TimeValue (time, TimeReference, TimeFormat);
+    
+    if (Variable != (char *) NULL) {
+      set_variable (Variable, value);
+      free (Variable);
+      return (TRUE);
+    }
+    gprint (GP_ERR, "time: %f\n", value);
+    return (TRUE);
+  }
+}
+
Index: /branches/sc_branches/pantasks_condor/cmd.astro/cval.c
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.astro/cval.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.astro/cval.c	(revision 34783)
@@ -0,0 +1,51 @@
+# include "astro.h"
+
+int cval (int argc, char **argv) {
+  
+  int i, j, Nx;
+  int sx, sy, nx, ny, xo, yo, dx, dy;
+  float *V, cval, val, sn, sky;
+  Buffer *buf;
+
+  if (argc != 7) {
+    gprint (GP_ERR, "USAGE: cval <buffer> x y dx dy sky\n");
+    return (FALSE);
+  }
+
+  if ((buf = SelectBuffer (argv[1], OLDBUFFER, TRUE)) == NULL) return (FALSE);
+
+  xo = atof (argv[2]);
+  yo = atof (argv[3]);
+  dx = atof (argv[4]);
+  dy = atof (argv[5]);
+  sky = atof (argv[6]);
+
+  sx = xo - dx;
+  sy = yo - dy;
+  nx = 2*dx + 1;
+  ny = 2*dy + 1;
+  if ((sx < 0) || (sy < 0) || 
+      (sx+nx > buf[0].matrix.Naxis[0]) || 
+      (sy+ny > buf[0].matrix.Naxis[1])) {
+    gprint (GP_ERR, "region out of range\n");
+    return (FALSE);
+  }
+
+  V = (float *)buf[0].matrix.buffer;
+  Nx = buf[0].matrix.Naxis[0];
+  val = V[xo + yo*Nx];
+
+  sn = 0;
+  cval = 0;
+  for (j = sy; j < sy + ny; j++) {
+    for (i = sx; i < sx + nx; i++) {
+      cval += (val - V[i + j*Nx]) / sqrt(V[i + j*Nx]);
+      sn += SQ (V[i + j*Nx] - sky) / V[i + j*Nx];
+    }
+  }
+
+  gprint (GP_ERR, "cval: %f  sn: %f\n", cval, sqrt(sn));
+
+  return (TRUE);
+}
+
Index: /branches/sc_branches/pantasks_condor/cmd.astro/czplot.c
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.astro/czplot.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.astro/czplot.c	(revision 34783)
@@ -0,0 +1,86 @@
+# include "astro.h"
+
+int czplot (int argc, char **argv) {
+  
+  int i, kapa, Npts, status;
+  double min, range, Rmin, Rmax;
+  opihi_flt *out, *r, *d, *x, *y;
+  Vector Xvec, Yvec, Zvec, *xvec, *yvec, *zvec;
+  Graphdata graphmode;
+
+  if (!style_args (&graphmode, &argc, argv, &kapa)) return FALSE;
+
+  if (argc != 6) {
+    gprint (GP_ERR, "USAGE: czplot <x> <y> <z> min max\n");
+    return (FALSE);
+  }
+
+  min = atof(argv[4]);
+  range = atof(argv[5]) - min;
+  Rmin = graphmode.coords.crval1 - 182.0;
+  Rmax = graphmode.coords.crval1 + 182.0;
+
+  /* find vectors */
+  if ((xvec = SelectVector (argv[1], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((yvec = SelectVector (argv[2], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((zvec = SelectVector (argv[3], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+
+  REQUIRE_VECTOR_FLT (xvec, FALSE); 
+  REQUIRE_VECTOR_FLT (yvec, FALSE); 
+  REQUIRE_VECTOR_FLT (zvec, FALSE); 
+
+  if (xvec[0].Nelements != yvec[0].Nelements) {
+    gprint (GP_ERR, "vectors %s and %s not the same length\n", argv[1], argv[2]);
+    return (FALSE);
+  }
+  if (xvec[0].Nelements != zvec[0].Nelements) {
+    gprint (GP_ERR, "vectors %s and %s not the same length\n", argv[1], argv[3]);
+    return (FALSE);
+  }
+  SetVector (&Xvec, OPIHI_FLT, xvec[0].Nelements);
+  SetVector (&Yvec, OPIHI_FLT, xvec[0].Nelements);
+  SetVector (&Zvec, OPIHI_FLT, xvec[0].Nelements);
+  
+  r   = xvec[0].elements.Flt;
+  d   = yvec[0].elements.Flt;
+  x   = Xvec.elements.Flt;
+  y   = Yvec.elements.Flt;
+  out = Zvec.elements.Flt;
+
+  Npts = 0;
+  for (i = 0; i < xvec[0].Nelements; i++, r++, d++) {
+    *r = ohana_normalize_angle (*r);
+    while (*r < Rmin) *r += 360.0;
+    while (*r > Rmax) *r -= 360.0;
+
+    status = RD_to_XY (x, y, *r, *d, &graphmode.coords);
+    if (!status) continue;
+
+    if (zvec->type == OPIHI_FLT) {
+      opihi_flt *in  = zvec[0].elements.Flt;
+      *out = MIN (1.0, MAX (0.01, (in[i] - min) / range));
+    } else {
+      opihi_int *in  = zvec[0].elements.Int;
+      *out = MIN (1.0, MAX (0.01, (in[i] - min) / range));
+    }
+    x++;
+    y++;
+    out++;
+    Npts++;
+  }
+  Xvec.Nelements = Npts;
+  Yvec.Nelements = Npts;
+  Zvec.Nelements = Npts;
+
+  graphmode.style = 2;
+  graphmode.size = -1; /* point size determined by Zvec */
+  graphmode.etype = 0;
+  PlotVectorTriplet (kapa, &Xvec, &Yvec, &Zvec, NULL, &graphmode);
+
+  free (Xvec.elements.Flt);
+  free (Yvec.elements.Flt);
+  free (Zvec.elements.Flt);
+
+  return (TRUE);
+
+}
Index: /branches/sc_branches/pantasks_condor/cmd.astro/drizzle.c
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.astro/drizzle.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.astro/drizzle.c	(revision 34783)
@@ -0,0 +1,301 @@
+# include "astro.h"
+
+/*** needs mosaic astrometry ***/
+
+static double XO, XX, XY;
+static double YO, YX, YY;
+static int ZERO;
+
+static int ROTATE;
+static double rot_phi, rot_alpha, rot_delta;
+static double rot_cdp, rot_sdp;
+
+int map_output_to_input (int Npix, double df);
+int map_input_to_output (int Npix, double df);
+int set_linear_terms (Coords *in, Coords *out, int i, int j, int Npix);
+void apply_terms (double *Xout, double *Yout, double Xin, double Yin);
+
+Coords coords_in, coords_out;
+Buffer *in, *out, *wt, *mask;
+
+int drizzle (int argc, char **argv) {
+
+  int Nlinear, Np, N;
+  double scale_in, scale_out, df;
+
+  ZERO = FALSE;
+  if ((N = get_argument (argc, argv, "-zero"))) {
+    ZERO = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+
+  ROTATE = FALSE;
+  if ((N = get_argument (argc, argv, "-roll"))) {
+    /* -roll phi alpha_pole delta_pole */
+    /* XXX need to clarify the meaning of phi, alpha, delta */
+    ROTATE = TRUE;
+    remove_argument (N, &argc, argv);
+    rot_phi   = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+    rot_alpha = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+    rot_delta = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+
+    rot_cdp = cos(RAD_DEG*rot_delta);
+    rot_sdp = sin(RAD_DEG*rot_delta);
+  }
+
+  mask = NULL;
+  if ((N = get_argument (argc, argv, "-mask"))) {
+    remove_argument (N, &argc, argv);
+    if ((mask = SelectBuffer (argv[N], OLDBUFFER, TRUE)) == NULL) return (FALSE);
+    remove_argument (N, &argc, argv);
+  }
+
+  if (argc != 5) {
+    gprint (GP_ERR, "USAGE: transform <from> <to> <weight> (Nlinear)\n");
+    gprint (GP_ERR, "  output buffer must exist with target astrometry header\n");
+    gprint (GP_ERR, "  Nlinear is the pixel scale for linear astrometric transformation\n");
+    return (FALSE);
+  }
+
+  if ((in  = SelectBuffer (argv[1], OLDBUFFER, TRUE)) == NULL) return (FALSE);
+  if ((out = SelectBuffer (argv[2], OLDBUFFER, TRUE)) == NULL) return (FALSE);
+  if ((wt  = SelectBuffer (argv[3], OLDBUFFER, TRUE)) == NULL) return (FALSE);
+  Nlinear = atoi (argv[4]);
+
+  GetCoords (&coords_in, &in[0].header);
+  GetCoords (&coords_out, &out[0].header);
+
+  /* for the moment, disable WRP / DIS */
+  if (!strcmp(&coords_in.ctype[4], "-WRP") || !strcmp(&coords_out.ctype[4], "-WRP")) {
+    gprint (GP_ERR, "WRP mode not implemented for astrom\n");
+    return (FALSE);
+  }
+  
+  scale_in = sqrt(fabs(coords_in.cdelt1*coords_in.cdelt2*(coords_in.pc1_1*coords_in.pc2_2 - coords_in.pc1_2*coords_in.pc2_1)));
+  scale_out = sqrt(fabs(coords_out.cdelt1*coords_out.cdelt2*(coords_out.pc1_1*coords_out.pc2_2 - coords_out.pc1_2*coords_out.pc2_1)));
+  
+  gprint (GP_ERR, "%f - %f\n", scale_in, scale_out);
+
+  if (scale_in > scale_out) {
+    Np = MAX (1, 3*scale_out / scale_in);
+    df = 1.0 / Np;
+    map_output_to_input (Nlinear, df);
+  } else {
+    Np = MAX (1, 3*scale_in / scale_out);
+    df = 1.0 / Np;
+    map_input_to_output (Nlinear, df);
+  }
+  return (TRUE);
+}
+
+/* mode 1: input pixels >> output pixels: loop over output pixels */
+/* mode 2: input pixels << output pixels: loop over input pixels */
+/* mode 3: input pixels ~= output pixels: drizzle input to output */
+
+/* loop over the input pixels, map input output image */
+int map_output_to_input (int Npix, double df) {
+
+  int i, j, Ni, No, Nx, Ny, nx, ny;
+  float *Vin, *Vout, *Vwt, *Vmk;
+  double x, y, X, Y;
+
+  /* loop over output pixels */
+  /* set up pointers for buffers */
+  Vin  = (float *) in[0].matrix.buffer;
+  Vout = (float *) out[0].matrix.buffer;
+  Vwt  = (float *) wt[0].matrix.buffer;
+  Vmk  = NULL;
+  Vmk = (mask == NULL) ? NULL : (float *) mask[0].matrix.buffer;
+
+  nx = in[0].header.Naxis[0];
+  ny = in[0].header.Naxis[1];
+  Nx = out[0].header.Naxis[0];
+  Ny = out[0].header.Naxis[1];
+
+  if (ZERO) {
+    bzero (Vout, Nx*Ny*sizeof(float));
+    bzero (Vwt,  Nx*Ny*sizeof(float));
+  }
+
+  gprint (GP_ERR, "mapping output to input\n");
+
+  for (j = 0; j < Ny; j+=Npix) {
+    for (i = 0; i < Nx; i+=Npix) {
+      
+      /* define linear transformation in region */
+      if (!set_linear_terms (&coords_out, &coords_in, i, j, Npix)) continue;
+
+      for (X = i; (X < i + Npix) && (X < Nx); X += df) {
+	for (Y = j; (Y < j + Npix) && (Y < Ny); Y += df) {
+	  
+	  No = (int)X + ((int)Y)*Nx;
+	  apply_terms (&x, &y, X, Y);
+	  if (x < 0) continue;
+	  if (x >= nx) continue;
+	  if (y < 0) continue;
+	  if (y >= ny) continue;
+	  Ni = (int)x + ((int)y)*nx;
+
+	  if (Vmk && Vmk[Ni]) continue;
+	  Vout[No] += Vin[Ni];
+	  Vwt[No] ++;
+	}
+      }
+    }
+  }
+  return (TRUE);
+}
+
+/* loop over the input pixels, map input output image */
+int map_input_to_output (int Npix, double df) {
+
+  int i, j, Ni, No, Nx, Ny, nx, ny;
+  float *Vin, *Vout, *Vwt, *Vmk;
+  double x, y, X, Y;
+
+  /* loop over output pixels */
+  /* set up pointers for buffers */
+  Vin  = (float *) in[0].matrix.buffer;
+  Vout = (float *) out[0].matrix.buffer;
+  Vwt  = (float *) wt[0].matrix.buffer;
+  Vmk  = NULL;
+  Vmk = (mask == NULL) ? NULL : (float *) mask[0].matrix.buffer;
+
+  Nx = in[0].header.Naxis[0];
+  Ny = in[0].header.Naxis[1];
+  nx = out[0].header.Naxis[0];
+  ny = out[0].header.Naxis[1];
+
+  if (ZERO) {
+    bzero (Vout, nx*ny*sizeof(float));
+    bzero (Vwt,  nx*ny*sizeof(float));
+  }
+
+  gprint (GP_ERR, "mapping input to output\n");
+
+  for (j = 0; j < Ny; j+=Npix) {
+    for (i = 0; i < Nx; i+=Npix) {
+      
+      /* define linear transformation in region */
+      if (!set_linear_terms (&coords_in, &coords_out, i, j, Npix)) continue;
+
+      for (X = i; (X < i + Npix) && (X < Nx); X += df) {
+	for (Y = j; (Y < j + Npix) && (Y < Ny); Y += df) {
+	  
+	  Ni = (int)X + ((int)Y)*Nx;
+	  apply_terms (&x, &y, X, Y);
+	  if (x < 0) continue;
+	  if (x >= nx) continue;
+	  if (y < 0) continue;
+	  if (y >= ny) continue;
+	  No = (int)x + ((int)y)*nx;
+
+	  if (Vmk && Vmk[Ni]) continue;
+	  Vout[No] += Vin[Ni];
+	  Vwt[No] ++;
+	}
+      }
+    }
+  }
+  return (TRUE);
+}
+
+int rotate_coords (double *phi, double *theta, double alpha, double delta) {
+
+  double sda, cda, cd, sd, sth;
+  double x, y;
+  
+  sda = sin(RAD_DEG*(alpha - rot_alpha));
+  cda = cos(RAD_DEG*(alpha - rot_alpha));
+  sd = sin(RAD_DEG*delta);
+  cd = cos(RAD_DEG*delta);
+  
+  sth = -cd*sda*rot_cdp + sd*rot_sdp;
+  y   = +cd*sda*rot_sdp + sd*rot_cdp;
+  x   = +cd*cda;
+
+  *theta = DEG_RAD*asin(sth);
+  *phi   = DEG_RAD*atan2(y,x) + rot_phi;
+  
+  *phi = ohana_normalize_angle(*phi);
+  return (TRUE);
+}
+
+/* find the linear astrometric fix between images at this location */
+int set_linear_terms (Coords *in, Coords *out, int i, int j, int Npix) {
+
+  int n;
+  double x, y, x2, y2, xy, X, Y, Xx, Xy, Yx, Yy;
+  double Xin, Yin, Xout, Yout;
+  double Sx2, Sy2, Sxy, SXx, SXy, SYx, SYy;
+  double N, r, d, phi, theta;
+
+  Xin = Yin = 0;
+  N = x = y = x2 = y2 = xy = X = Y = Xx = Xy = Yx = Yy = 0;
+
+  /* define several test points, fit a line to the input,output pairs */
+  for (n = 0; n < 3; n++) {
+
+    switch (n) {
+    case 0:
+      Xin = i;
+      Yin = j;
+      break;
+    case 1:
+      Xin = i + Npix;
+      Yin = j;
+      break;
+    case 2:
+      Xin = i;
+      Yin = j + Npix;
+      break;
+    }
+
+    if (!XY_to_RD (&r, &d, Xin, Yin, in)) return (FALSE);
+    if (ROTATE) { 
+      rotate_coords (&phi, &theta, r, d);
+      r = phi;
+      d = theta;
+    }
+    if (!RD_to_XY (&Xout, &Yout, r, d, out)) return (FALSE);
+
+    x  += Xin;
+    y  += Yin;
+    x2 += Xin*Xin;
+    y2 += Yin*Yin;
+    xy += Xin*Yin;
+    X  += Xout;
+    Y  += Yout;
+    Xx += Xout*Xin;
+    Xy += Xout*Yin;
+    Yx += Yout*Xin;
+    Yy += Yout*Yin;
+    N  += 1.0;
+  }
+
+  Sx2 = x2 - x*x/N;
+  Sy2 = y2 - y*y/N;
+  Sxy = xy - x*y/N;
+  SXx = Xx - X*x/N;
+  SXy = Xy - X*y/N;
+  SYx = Yx - Y*x/N;
+  SYy = Yy - Y*y/N;
+  
+  XX = (SXx*Sy2 - SXy*Sxy) / (Sx2*Sy2 - Sxy*Sxy);
+  XY = (SXy*Sx2 - SXx*Sxy) / (Sx2*Sy2 - Sxy*Sxy);
+  XO = X/N - XX*x/N - XY*y/N;
+
+  YX = (SYx*Sy2 - SYy*Sxy) / (Sx2*Sy2 - Sxy*Sxy);
+  YY = (SYy*Sx2 - SYx*Sxy) / (Sx2*Sy2 - Sxy*Sxy);
+  YO = Y/N - YX*x/N - YY*y/N;
+  return (TRUE);
+}
+
+
+void apply_terms (double *Xout, double *Yout, double Xin, double Yin) {
+  *Xout = XO + XX*Xin + XY*Yin;
+  *Yout = YO + YX*Xin + YY*Yin;
+}
Index: /branches/sc_branches/pantasks_condor/cmd.astro/elliprofile.c
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.astro/elliprofile.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.astro/elliprofile.c	(revision 34783)
@@ -0,0 +1,64 @@
+# include "astro.h"
+
+int elliprofile (int argc, char **argv) {
+  
+  int i, ix, iy, Nx, Ny;
+  float *in;
+  double Rmaj, Rmin, phi;
+  double root1, root2, R, A1, A2, A3;
+  double Sx, Sy, Sxy;
+  double x, y, Xo, Yo;
+  Buffer *buf;
+  Vector *rvec, *fvec;
+
+  if (argc != 9) {
+    gprint (GP_ERR, "USAGE: elliprofile (buffer) (rvec) (fvec) (Xo) (Yo) (Rmaj) (Rmin) (phi)\n");
+    return (FALSE);
+  }
+
+  /* select input / output buffers */
+  if ((buf = SelectBuffer (argv[1], OLDBUFFER, TRUE)) == NULL) return (FALSE);
+  Nx = buf[0].header.Naxis[0];
+  Ny = buf[0].header.Naxis[1];
+
+  if ((rvec = SelectVector (argv[2], ANYVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((fvec = SelectVector (argv[3], ANYVECTOR, TRUE)) == NULL) return (FALSE);
+
+  ResetVector (rvec, OPIHI_FLT, Nx*Ny);
+  ResetVector (fvec, OPIHI_FLT, Nx*Ny);
+
+  Xo = atof(argv[4]);
+  Yo = atof(argv[5]);
+
+  /* shape parameters */
+  Rmaj = atof (argv[6]);
+  Rmin = atof (argv[7]);
+  phi  = atof (argv[8]);
+
+  /* given Rmaj, Rmin, phi, find Sx, Sy, Sxy */
+  root1 = SQ(1.0 / Rmaj);
+  root2 = SQ(1.0 / Rmin);
+
+  // XXX check this
+  R = 0.5 * (root1 - root2);
+  A1 = 0.25*(root1 + root2) - 0.5*R*cos(2*RAD_DEG*phi);
+  A2 = 0.25*(root1 + root2) + 0.5*R*cos(2*RAD_DEG*phi);
+  A3 = -R*sin(2*RAD_DEG*phi);
+
+  Sx = 0.5/A1;
+  Sy = 0.5/A2;
+  Sxy = A3;
+
+  /* f = exp (-r^alpha), r^2 = (x^2 / 2Sx) + (y^2 / 2Sy) + Sxy*x*y */
+  in = (float *) buf[0].matrix.buffer;
+  for (i = iy = 0; iy < Ny; iy++) {
+      for (ix = 0; ix < Nx; ix++, in++, i++) {
+      x = ix + 0.5 - Xo;
+      y = iy + 0.5 - Yo;
+      rvec[0].elements.Flt[i] = sqrt(0.5*x*x/Sx + 0.5*y*y/Sy + x*y*Sxy);
+      fvec[0].elements.Flt[i] = *in;
+    }
+  }
+  rvec[0].Nelements = fvec[0].Nelements = i;
+  return (TRUE);
+}
Index: /branches/sc_branches/pantasks_condor/cmd.astro/fixcols.c
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.astro/fixcols.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.astro/fixcols.c	(revision 34783)
@@ -0,0 +1,246 @@
+# include "astro.h"
+
+// this function is a hack to mask bad columns / rows in an image.  we assume that there
+// are columns or rows which are, as a whole, significant deviant from the others.
+// in fact, we measure this by segments
+
+// SIGMA is a robust measure of the sigma, STDEV is the formal stdev of the values in range
+int vector_stats (float *vect, int Nvect, float *MEDIAN, float *SIGMA, float *STDEV);
+
+int fixrows (int argc, char **argv) {
+  
+  int ix, iy, Nx, Ny, Nvect, start, stop;
+  float *Vin, *vect, *meds, median, stdev, sigma, Nsigma, value;
+  Buffer *in;
+
+  if (argc != 6) {
+    gprint (GP_ERR, "USAGE: fixrows (buffer) (col_start) (col_stop) (Nsigma) (value)\n");
+    return (FALSE);
+  }
+
+  if ((in = SelectBuffer (argv[1], OLDBUFFER, TRUE)) == NULL) return (FALSE);
+  start = atoi (argv[2]);
+  stop = atoi (argv[3]);
+  Nsigma = atof (argv[4]);
+  value = atof (argv[5]);
+
+  Nx = in[0].matrix.Naxis[0];
+  Ny = in[0].matrix.Naxis[1];
+
+  ALLOCATE (meds, float, Ny);
+  ALLOCATE (vect, float, stop - start);
+
+  Vin  = (float *)in[0].matrix.buffer;
+
+  // measure the median for each column segment, accumulate the stats
+  for (iy = 0; iy < Ny; iy++) {
+
+    Nvect = 0;
+    for (ix = start; ix < stop; ix++) {
+      vect[Nvect] = Vin[ix + iy*Nx];
+      Nvect ++;
+    }
+
+    // get the median, robust sigma, and formal stdev of the column segment
+    vector_stats (vect, Nvect, &median, &sigma, &stdev);
+    meds[iy] = median;
+    // fprintf (stderr, "median : %f +/- %f (%f)\n", median, sigma, stdev);
+  }
+
+  // get the median, robust sigma, and formal stdev of the medians
+  vector_stats (meds, Nx, &median, &sigma, &stdev);
+  // fprintf (stderr, "median of medians : %f +/- %f (%f)\n", median, sigma, stdev);
+
+  // mask values which are more than Nsigma out of range
+  for (iy = 0; iy < Ny; iy++) {
+    if (fabs(meds[iy] - median) < Nsigma * sigma) continue;
+    for (ix = start; ix < stop; ix++) {
+      Vin[ix + iy*Nx] = value;
+    }
+  }
+  
+  free (meds);
+  free (vect);
+
+  return (TRUE);
+}
+
+int fixcols (int argc, char **argv) {
+  
+  int ix, iy, Nx, Nvect, start, stop;
+  float *Vin, *vect, *meds, median, stdev, sigma, Nsigma, value;
+  Buffer *in;
+
+  if (argc != 6) {
+    gprint (GP_ERR, "USAGE: fixcols (buffer) (row_start) (row_stop) (Nsigma) (value)\n");
+    return (FALSE);
+  }
+
+  if ((in = SelectBuffer (argv[1], OLDBUFFER, TRUE)) == NULL) return (FALSE);
+  start = atoi (argv[2]);
+  stop = atoi (argv[3]);
+  Nsigma = atof (argv[4]);
+  value = atof (argv[5]);
+
+  Nx = in[0].matrix.Naxis[0];
+  // Ny = in[0].matrix.Naxis[1];
+
+  ALLOCATE (meds, float, Nx);
+  ALLOCATE (vect, float, stop - start);
+
+  Vin  = (float *)in[0].matrix.buffer;
+
+  // measure the median for each column segment, accumulate the stats
+  for (ix = 0; ix < Nx; ix++) {
+
+    Nvect = 0;
+    for (iy = start; iy < stop; iy++) {
+      vect[Nvect] = Vin[ix + iy*Nx];
+      Nvect ++;
+    }
+
+    // get the median, robust sigma, and formal stdev of the column segment
+    vector_stats (vect, Nvect, &median, &sigma, &stdev);
+    meds[ix] = median;
+    // fprintf (stderr, "median : %f +/- %f (%f)\n", median, sigma, stdev);
+  }
+
+  // get the median, robust sigma, and formal stdev of the medians
+  vector_stats (meds, Nx, &median, &sigma, &stdev);
+  // fprintf (stderr, "median of medians : %f +/- %f (%f)\n", median, sigma, stdev);
+
+  // mask values which are more than Nsigma out of range
+  for (ix = 0; ix < Nx; ix++) {
+    if (fabs(meds[ix] - median) < Nsigma * sigma) continue;
+    for (iy = start; iy < stop; iy++) {
+      Vin[ix + iy*Nx] = value;
+    }
+  }
+  
+  free (meds);
+  free (vect);
+
+  return (TRUE);
+}
+
+// this macro is accumulating the histogram until the sum passes the desired threshold
+// point the fractional position of that threshold is found by interpolation
+# define SUMPOINT(POINT, NPOINT, FRACTION)				\
+  if ((NPOINT) == -1) {							\
+    (POINT) += Nval[i];							\
+    if ((POINT) >= FRACTION*N) {					\
+      (NPOINT) = i;							\
+      value = i * dx + min;						\
+      if (Nval[i] != 0) {						\
+	/* POINT for the bin center is not exactly FRACTION*N */	\
+	/* interpolate to the true value, but limit the scale of the */	\
+	/* interpolation to a single bin */				\
+	adjust = dx * (FRACTION*N - (POINT)) / Nval[i];			\
+	value += MAX (MIN (adjust, dx), -dx);				\
+      }									\
+      (POINT) = value;							\
+    }									\
+  }
+
+// for testing, this is a useful line:
+// fprintf (stderr, "for point %f (%f): bin: %d = %f, dx: %f, adjust: %f, value: %f, POINT: %f, Nval[i]: %d, Nval[i-1]: %d\n",
+// FRACTION, FRACTION*N, i, i*dx + min, dx, adjust, value, POINT, Nval[i], Nval[i-1]);
+
+int vector_stats (float *vect, int Nvect, float *MEDIAN, float *SIGMA, float *STDEV) {
+  
+  int i, iter, N, done;
+  double max, min, sum, var, dvar, mean, stdev, sigma, adjust, value;
+  int Nval[1001], bin, Nmode, N_MED, N_LQ, N_UQ, N_SM, N_SP;
+  double dx, mode, MED, UQ, LQ, SM, SP;
+  float *X;
+
+  /* we need two passes, one for max, min, mean, sum, one for median, stdev, etc */
+
+  /* calculate max, min, mean, sum, npix */
+  max = -HUGE_VAL;
+  min = HUGE_VAL;
+  sum = N = 0;
+  X = vect;
+  for (i = 0; i < Nvect; i++, X++) {
+    if (!finite (*X)) continue;
+    max = MAX (*X, max);
+    min = MIN (*X, min);
+    sum += *X;
+    N++;
+  }      
+  mean = sum / N;
+
+  // calculate median and mode with initial resolution dx = (max - min) / 1000 
+  // we adjust the resolution and retry if the resulting robust sigma < dx
+  dx = (max - min) / 1000;
+  if (dx == 0) {
+    MED = mode = min;
+    stdev = 0.0;
+    sigma = 0.0;
+    goto skip;
+  }
+
+  done = FALSE;
+  for (iter = 0; (iter < 3) && !done; iter ++) {
+
+    bzero (Nval, 1001*sizeof(int));
+    var = 0;
+    X = vect;
+    for (i = 0; i < Nvect; i++, X++) {
+      if (!finite (*X)) continue;
+      bin = MAX (0, MIN (1000, (*X - min) / dx));
+      Nval[bin] ++;
+      if (bin == 0) continue;
+      if (bin == 1000) continue;
+      // only count the values in range for the stdev
+      dvar = (*X - mean);
+      var += dvar*dvar;
+    }      
+    stdev = sqrt (var / N);
+
+    Nmode = 0;
+    mode = Nval[Nmode];
+    MED = SM = SP = LQ = UQ = 0;
+    N_MED = N_SM = N_SP = N_LQ = N_UQ = -1;
+    for (i = 0; i < 1001; i++) {
+      SUMPOINT (MED, N_MED, 0.5000);
+      SUMPOINT (LQ,  N_LQ,  0.2500);
+      SUMPOINT (UQ,  N_UQ,  0.7500);
+      SUMPOINT (SM,  N_SM,  0.1587);
+      SUMPOINT (SP,  N_SP,  0.8413);
+
+      if (mode < Nval[i]) {
+	Nmode = i;
+	mode = Nval[Nmode];
+      }
+    }
+
+    sigma = 0.5*(SP - SM);
+    mode = Nmode * dx + min;
+
+    // if (sigma < dx), we adjust the range to be centered on the median
+    if (sigma < dx) {
+      dx *= 0.1;
+      min = MED - 500*dx;
+    } else {
+      done = TRUE;
+    }
+  }
+
+skip:
+
+  *MEDIAN = MED;
+  *SIGMA = sigma;
+  *STDEV = stdev;
+
+  // set_variable ("MIN",      min);
+  // set_variable ("MAX",      max);
+  // set_variable ("MEAN",     mean);
+  // set_variable ("MODE",     mode);
+  // set_variable ("TOTAL",    sum);
+  // set_int_variable ("NPIX", N);
+  // set_variable ("SIGMA",    stdev);
+
+  return (TRUE);
+}
+
Index: /branches/sc_branches/pantasks_condor/cmd.astro/fiximage.c
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.astro/fiximage.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.astro/fiximage.c	(revision 34783)
@@ -0,0 +1,95 @@
+# include "astro.h"
+
+int fiximage (int argc, char **argv) {
+
+  int ix, iy;
+  Buffer *in, *ct, *mask;
+
+  // int VERBOSE = FALSE;
+  // if ((N = get_argument (argc, argv, "-v"))) {
+  //   VERBOSE = TRUE;
+  //   remove_argument (N, &argc, argv);
+  // }
+
+  if (argc != 4) {
+    gprint (GP_ERR, "USAGE: fiximage <data> <count> (mask)\n");
+    return (FALSE);
+  }
+
+  if ((in   = SelectBuffer (argv[1], OLDBUFFER, TRUE)) == NULL) return (FALSE);
+  if ((ct   = SelectBuffer (argv[2], OLDBUFFER, TRUE)) == NULL) return (FALSE);
+  if ((mask = SelectBuffer (argv[3], ANYBUFFER, TRUE)) == NULL) return (FALSE);
+
+  int Nx = in[0].matrix.Naxis[0];
+  int Ny = in[0].matrix.Naxis[1];
+
+  gfits_free_matrix (&mask[0].matrix);
+  gfits_free_header (&mask[0].header);
+  CreateBuffer (mask, Nx, Ny, -32, 0.0, 1.0);
+  strcpy (mask[0].file, "(empty)");
+  memset (mask[0].matrix.buffer, 0, Nx*Ny*sizeof(float));
+  // 0 -- init, cannot be repaired
+  // 1 -- does not need to be repaired
+  // 2 -- repair x only
+  // 4 -- repair y only
+  // 6 -- repair x & y only
+
+  float *inB = (float *) in[0].matrix.buffer;
+  float *ctB = (float *) ct[0].matrix.buffer;
+  float *mkB = (float *) mask[0].matrix.buffer;
+
+  // loop over input ct and find pixels that can be repaired.
+  for (iy = 1; iy < Ny - 1; iy++) {
+    for (ix = 1; ix < Nx - 1; ix++) {
+
+      int Npix = ix + Nx*iy;
+
+      // does not need to be repaired
+      if (ctB[Npix]) { mkB[Npix] = 1.0; continue; }
+
+      // if we have all 4 valid neighbors, we can fix
+      if (ctB[Npix - 1 ] && ctB[Npix + 1 ]) mkB[Npix] += 2.0;
+      if (ctB[Npix - Nx] && ctB[Npix + Nx]) mkB[Npix] += 4.0;
+    }
+  }
+
+  // loop over input ct and repair the reparable pixels
+  for (iy = 1; iy < Ny - 1; iy++) {
+    for (ix = 1; ix < Nx - 1; ix++) {
+
+      int Npix = ix + Nx*iy;
+
+      if (mkB[Npix] == 0.0) continue; // cannot be repaired
+      if (mkB[Npix] == 1.0) continue; // does not need to be repaired
+
+      float Vxm = 0.0;
+      float Vxp = 0.0;
+      float Vym = 0.0;
+      float Vyp = 0.0;
+      float Value = 0.0;
+      if ((mkB[Npix] == 2) || (mkB[Npix] == 6)) {
+        Vxm = inB[Npix - 1 ] / ctB[Npix - 1 ];
+        Vxp = inB[Npix + 1 ] / ctB[Npix + 1 ];
+      }
+      if ((mkB[Npix] == 4) || (mkB[Npix] == 6)) {
+        Vym = inB[Npix - Nx] / ctB[Npix - Nx];
+        Vyp = inB[Npix + Nx] / ctB[Npix + Nx];
+      }
+
+      if (mkB[Npix] == 2) {
+        Value = 0.5*(Vxm + Vxp);
+      }
+      if (mkB[Npix] == 4) {
+        Value = 0.5*(Vym + Vyp);
+      }
+      if (mkB[Npix] == 6) {
+        Value = 0.25*(Vxm + Vxp + Vym + Vyp);
+      }
+
+      ctB[Npix] = 1;
+      inB[Npix] = Value;
+    }
+  }
+
+  return TRUE;
+}
Index: /branches/sc_branches/pantasks_condor/cmd.astro/fixwrap.c
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.astro/fixwrap.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.astro/fixwrap.c	(revision 34783)
@@ -0,0 +1,110 @@
+# include "astro.h"
+
+int fixwrap (int argc, char **argv) {
+  
+  int i, j, Nflip, n, Ny, Nx, flip, sat, rowfix;
+  float *Vin, *outf, *outb, dO;
+  Buffer *in;
+
+  if (argc != 3) {
+    gprint (GP_ERR, "USAGE: fixwrap <in> (rowfix)\n");
+    return (FALSE);
+  }
+
+  if ((in = SelectBuffer (argv[1], OLDBUFFER, TRUE)) == NULL) return (FALSE);
+  rowfix = atoi (argv[2]);
+
+  Nx = in[0].matrix.Naxis[0];
+  Ny = in[0].matrix.Naxis[1];
+
+  ALLOCATE (outf, float, MAX(Nx, Ny));
+  ALLOCATE (outb, float, MAX(Nx, Ny));
+
+  for (j = 0; j < Ny; j++) {
+    Vin  = (float *)(in[0].matrix.buffer)  + j*Nx;
+
+    /* measure forward flips */ 
+    sat = FALSE;
+    for (i = 0; i < Nx; i++) {
+      if ((i < 1056) || (i > 2079)) {
+	outf[i] = Vin[i];
+	continue;
+      }
+
+      dO = 2*outf[i-1] - outf[i-2] - Vin[i];
+      flip = (fabs(dO - 0x8000) < fabs(dO));
+
+      /* going onto saturation */
+      if (!sat && (Vin[i] > 32766.5) && (Vin[i+1]  > 32766.5)) sat  = TRUE;
+      if (!sat && (Vin[i] > 32766.5) && (Vin[i-Nx] > 65534.5)) sat  = TRUE;
+
+      /* exiting saturation region */
+      if ( sat && (Vin[i] < 32766.5) && (Vin[i-1] < 32766.5)) sat = FALSE;
+
+      if (sat) flip = TRUE;
+
+      outf[i] = flip ? (Vin[i] + 0x8000) : Vin[i];
+    }
+
+    /* measure backward flips */ 
+    sat = FALSE;
+    for (i = Nx - 1; i >= 0; i--) {
+      if ((i < 1056) || (i > 2077)) {
+	outb[i] = Vin[i];
+	continue;
+      }
+
+      dO = 2*outb[i+1] - outb[i+2] - Vin[i];
+      flip = (fabs(dO - 0x8000) < fabs(dO));
+
+      /* going onto saturation */
+      if (!sat && (Vin[i] > 32766.5) && (Vin[i-1] > 32766.5)) sat  = TRUE;
+
+      /* exiting saturation region */
+      if ( sat && (Vin[i] < 32766.5) && (Vin[i+1] < 32766.5)) sat = FALSE;
+
+      if (sat) flip = TRUE;
+
+      outb[i] = flip ? (Vin[i] + 0x8000) : Vin[i];
+    }
+
+    /* compare forward and backward flips: where they disagree, use column to predict */
+    for (i = 0; (j > 1) && (i < Nx); i++) {
+      if ((i < 1056) || (i > 2077)) continue;
+      if (outf[i] != outb[i]) {
+	/* use this column to predict, not the row */
+	dO = 2*Vin[i - Nx] - Vin[i-2*Nx] - Vin[i];
+	flip = (fabs(dO - 0x8000) < fabs(dO));
+	outf[i] = flip ? Vin[i] + 0x8000 : Vin[i];
+	outb[i] = outf[i];  /* save this for the row segments below */
+      }
+    }
+
+    /* compare this row and previous (now fixed) row. if large segments are flipped, fix them */
+    for (i = 0; rowfix && (j > 1) && (i < Nx); i++) {
+      if ((i < 1056) || (i > 2077)) continue;
+
+      if (fabs(outf[i] - Vin[i-Nx]) > 15000) {
+	Nflip = 0;
+	for (n = i - 8; n < i + 9; n++) {
+	  if (fabs(outb[n] - Vin[n-Nx]) > 15000) {
+	    Nflip ++;
+	  }
+	}
+	if (Nflip > 5) {
+	  if (outf[i] - Vin[i-Nx] > 15000) {
+	    outf[i] -= 0x8000;
+	  } else {
+	    outf[i] += 0x8000;
+	  }	    
+	}
+      }
+    }
+    for (i = 0; i < Nx; i++) {
+      Vin[i] = outf[i];
+    }
+  }
+
+  return (TRUE);
+}
+
Index: /branches/sc_branches/pantasks_condor/cmd.astro/flux.c
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.astro/flux.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.astro/flux.c	(revision 34783)
@@ -0,0 +1,80 @@
+# include "astro.h"
+
+int flux (int argc, char **argv) {
+  
+  int i, j, k, xmin, ymin, xmax, ymax;
+  void *oldsignal;
+  double ax, ay, s, S, flux;
+  double bx[5], by[5], x[5], y[5], bb[5];
+  float *V;
+  FILE *f;
+  Buffer *buf;
+
+  if (argc != 3) {
+    gprint (GP_ERR, "USAGE: flux <buffer> (region)\n");
+    return (FALSE);
+  }
+
+  if ((buf = SelectBuffer (argv[1], OLDBUFFER, TRUE)) == NULL) return (FALSE);
+
+  f = fopen (argv[2], "r");
+  if (f == (FILE *) NULL) {
+    gprint (GP_ERR, "file %s not found\n", argv[2]);
+    return (FALSE);
+  }
+
+  xmin = buf[0].matrix.Naxis[0];
+  xmax = 0;
+  ymin = buf[0].matrix.Naxis[1];
+  ymax = 0;
+  for (i = 0; i < 4; i++) {
+    if (fscanf (f, "%lf %lf", &x[i], &y[i]) != 2) {
+      fprintf (stderr, "error reading coordinates from %s\n", argv[2]);
+      fclose (f);
+      return (FALSE);
+    }
+    xmin = MAX (0, MIN (xmin, x[i] - 1));
+    ymin = MAX (0, MIN (ymin, y[i] - 1));
+    xmax = MIN (MAX (xmax, x[i] + 1), buf[0].matrix.Naxis[0]);
+    ymax = MIN (MAX (ymax, y[i] + 1), buf[0].matrix.Naxis[1]);
+  }
+  fclose (f);
+
+  x[4] = x[0]; y[4] = y[0];
+  for (i = 0; i < 4; i++) {
+    bx[i] = x[i+1] - x[i];
+    by[i] = y[i+1] - y[i];
+  }
+  bx[4] = bx[0]; by[4] = by[0];
+  for (i = 0; i < 4; i++) {
+    bb[i] = hypot (bx[i], by[i]) * SIGN (bx[i]*by[i+1] - bx[i+1]*by[i]);
+  }
+  gprint (GP_ERR, "%f %f %f %f\n", bb[0], bb[1], bb[2], bb[3]);
+
+  /* this only works for convex contours --
+   we have to add up the angles for concave contours */
+  flux = 0;
+  oldsignal = signal (SIGINT, handle_interrupt);
+  interrupt = FALSE;
+  for (j = ymin; (j < ymax) && !interrupt; j++) {
+    V = (float *)(buf[0].matrix.buffer) + j*buf[0].matrix.Naxis[0] + xmin; 
+    for (i = xmin; (i < xmax) && !interrupt; i++, V++) {
+      S = 1.0;
+      for (k = 0; k < 4; k++) {
+	ax = i - x[k];
+	ay = j - y[k];
+	s = (ay*bx[k] - ax*by[k]) / bb[k] + 0.5;
+	/* s = b x a / |b|, with the correct sign (above) so inside is positive */
+	s = MAX (0.0, MIN (1.0, s));  /* s is between 0.0 and 1.0 */
+	S *= s;
+      }
+      flux += S * (*V);
+    }
+  }
+  signal (SIGINT, oldsignal);
+
+  gprint (GP_LOG, "flux: %f\n", flux);
+  set_variable ("FLUX", flux);
+  return (TRUE);
+}
+
Index: /branches/sc_branches/pantasks_condor/cmd.astro/galcontour.c
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.astro/galcontour.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.astro/galcontour.c	(revision 34783)
@@ -0,0 +1,89 @@
+# include "astro.h"
+
+XXX probably not a great algorithm...
+
+
+int galcontour (int argc, char **argv) {
+  
+  int i, j, Nx, Ny, N, Nsec, NELEMENTS, Npt;
+  float *in;
+  double theta, dtheta, x, y, Xo, Yo;
+  Buffer *buf;
+  Vector *xvec, *yvec, *tvec;
+  char name[128];
+
+  if (argc != 5) {
+    gprint (GP_ERR, "USAGE: galcontour (buffer) (Xo) (Yo) (flux)\n");
+    gprint (GP_ERR, "  generate contour points (Cx) (Cy) (Ct)\n");
+    return (FALSE);
+  }
+
+  /* select input / output buffers */
+  if ((buf = SelectBuffer (argv[1], OLDBUFFER, TRUE)) == NULL) return (FALSE);
+  Nx = buf[0].header.Naxis[0];
+  Ny = buf[0].header.Naxis[1];
+  
+  Xo = atof(argv[2]);
+  Yo = atof(argv[3]);
+  flux = atof(argv[4]);
+
+  if ((xvec = SelectVector ("Cx", ANYVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((yvec = SelectVector ("Cy", ANYVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((tvec = SelectVector ("Ct", ANYVECTOR, TRUE)) == NULL) return (FALSE);
+
+  N = 0;
+  NELEMENTS = 100;
+  ResetVector (xvec, OPIHI_FLT, NELEMENTS);
+  ResetVector (yvec, OPIHI_FLT, NELEMENTS);
+  ResetVector (tvec, OPIHI_FLT, NELEMENTS);
+
+  in = (float *) buf[0].matrix.buffer;
+
+  // find the flux transition for each row until not found
+
+  // first go up:
+  for (j = Yo; j < Ny; j++) {
+    
+    // first go left
+    for (i = Xo; i >= 0; i--) {
+      F = in[i + j*Nx];
+      if (F < flux) {
+	// interpolate to flux value between f[i,j] and f[i+1,j]
+      }
+    }
+
+  for (j = 0; j < Ny; j++) {
+    for (i = 0; i < Nx; i++, in++) {
+      x = i - Xo;
+      y = j - Yo;
+      theta = DEG_RAD*atan2(y, x);
+      if (theta < -0.5*dtheta) theta += 360.0; // need to allow -0.5dtheta for first bin
+      if (theta > 360 - 0.5*dtheta) theta -= 360.0; // need to allow -0.5dtheta for first bin
+      N = (int)(theta / dtheta + 0.5);
+      if (N < 0) {
+	fprintf (stderr, "?");
+	continue;
+      }
+      if (N >= Nsec) {
+	fprintf (stderr, "!");
+	continue;
+      }
+      
+      fvec[N][0].elements.Flt[fvec[N][0].Nelements] = *in;
+      rvec[N][0].elements.Flt[rvec[N][0].Nelements] = hypot(x, y);
+      fvec[N][0].Nelements ++;
+      rvec[N][0].Nelements ++;
+
+      *in = N;
+
+      if (fvec[N][0].Nelements >= NELEMENTS) {
+	NELEMENTS += 1000;
+	for (N = 0; N < Nsec; N++) {
+	  REALLOCATE (fvec[N][0].elements.Flt, opihi_flt, NELEMENTS);
+	  REALLOCATE (rvec[N][0].elements.Flt, opihi_flt, NELEMENTS);
+	}
+      }
+    }
+  }
+  return (TRUE);
+}
Index: /branches/sc_branches/pantasks_condor/cmd.astro/galprofiles.c
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.astro/galprofiles.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.astro/galprofiles.c	(revision 34783)
@@ -0,0 +1,187 @@
+# include "astro.h"
+
+double Interpolate (const double x, const double y, float *in, int Nx, int Ny);
+
+int galprofiles (int argc, char **argv) {
+  
+  int i, Nx, Ny, N, Nsec, NELEMENTS;
+  float *in;
+  double theta, dtheta, x, y, Xo, Yo, r, Rmax, value;
+  Buffer *buf;
+  Vector **fvec, **rvec, *tvec;
+  char name[128];
+
+  if (argc != 5) {
+    gprint (GP_ERR, "USAGE: galprofiles (buffer) (Xo) (Yo) Nsec\n");
+    gprint (GP_ERR, "  generates Nsec profiles around the circle; the first is centered on 0.0 degrees\n");
+    return (FALSE);
+  }
+
+  /* select input / output buffers */
+  if ((buf = SelectBuffer (argv[1], OLDBUFFER, TRUE)) == NULL) return (FALSE);
+  Nx = buf[0].header.Naxis[0];
+  Ny = buf[0].header.Naxis[1];
+  Rmax = 0.5*hypot(Nx, Ny);
+  
+  Xo = atof(argv[2]);
+  Yo = atof(argv[3]);
+  Nsec = atof(argv[4]);
+
+  dtheta = 360.0 / Nsec;
+  // sector i ranges from (i-0.5)*dtheta to (i+0.5)*dtheta
+  // i = theta / dtheta + 0.5;
+
+  ALLOCATE (fvec, Vector *, Nsec);
+  ALLOCATE (rvec, Vector *, Nsec);
+  if ((tvec = SelectVector ("theta", ANYVECTOR, TRUE)) == NULL) return (FALSE);
+  ResetVector (tvec, OPIHI_FLT, Nsec);
+
+  // dereference pointer
+  in = (float *) buf[0].matrix.buffer;
+
+  for (i = 0; i < Nsec; i++) {
+
+    // generate the output vector names & vectors
+    sprintf (name, "flux_%d", i);
+    if ((fvec[i] = SelectVector (name, ANYVECTOR, TRUE)) == NULL) return (FALSE);
+    sprintf (name, "rad_%d", i);
+    if ((rvec[i] = SelectVector (name, ANYVECTOR, TRUE)) == NULL) return (FALSE);
+
+    // allocate initial data space
+    N = 0;
+    NELEMENTS = 1000;
+    ResetVector (fvec[i], OPIHI_FLT, NELEMENTS);
+    ResetVector (rvec[i], OPIHI_FLT, NELEMENTS);
+
+    // angle for this profile
+    tvec[0].elements.Flt[i] = i*dtheta;
+    theta = RAD_DEG*i*dtheta;
+
+    // start at Xo,Yo and find the x,y locations for r_i, theta where r_i increments by 1 pixel
+    for (r = 0; r < Rmax; r++) {
+
+      // XXX Xo,Yo are referenced to pixels with bounds i+0.0, i+1.0
+      x = r * cos (theta) + Xo;
+      y = r * sin (theta) + Yo;
+
+      // value is NAN if we run off the image
+      value = Interpolate(x, y, in, Nx, Ny);
+      if (isnan(value)) break;
+
+      rvec[i][0].elements.Flt[N] = r;
+      fvec[i][0].elements.Flt[N] = value;
+      N++;
+
+      if (N >= NELEMENTS) {
+	NELEMENTS += 100;
+	REALLOCATE (rvec[N][0].elements.Flt, opihi_flt, NELEMENTS);
+	REALLOCATE (fvec[N][0].elements.Flt, opihi_flt, NELEMENTS);
+      }
+    }
+    fvec[i][0].Nelements = N;
+    rvec[i][0].Nelements = N;
+  }
+  return (TRUE);
+}
+
+// fast & simple API to interpolate to a subpixel position using bilinear interpolation
+// x,y in parent image coordinates (pixel centers at 0.5, 0.5)
+// stolen from psLib
+double Interpolate (const double x, const double y, float *in, int Nx, int Ny) {
+
+  // allow extrapolation a small distance beyond the edge of valid pixels, but no
+  // further (this allows the nXskip,nYskip boundary areas to be used as well)
+  float nXedge = 0.125*Nx;
+  float nYedge = 0.125*Ny;
+
+  if ((x < -nXedge) || (x > Nx + nXedge) || (y < -nYedge) || (y > Ny + nYedge)) {
+    return NAN;
+  }
+
+  // limiting cases: Nx == 1 and/or Ny == 1
+
+  // if we have a single pixel, there is no spatial information
+  if ((Nx == 1) && (Ny == 1)) {
+    return in[0];
+  }
+
+  // handle edge cases with extrapolation
+  const int ix = x - 0.5; // index of reference pixel
+  const int iy = y - 0.5; // index of reference pixel
+
+  // do numCols,Rows first so we are never < 0
+  const int Xs = MAX (MIN (ix, Nx - 2), 0);
+  const int Ys = MAX (MIN (iy, Ny - 2), 0);
+
+  const int Xe = Xs + 1;
+  const int Ye = Ys + 1;
+
+  // dx,dy range from 0.0 to 1.0 for interpolated pixels, and -0.5 to 1.5 for extrapolation
+  const double dx = x - 0.5 - Xs;
+  const double dy = y - 0.5 - Ys;
+
+  const double rx = 1.0 - dx;
+  const double ry = 1.0 - dy;
+
+  // if Nx == 1, we have no x-dir spatial information
+  if (Nx == 1) {
+    double V0 = in[Ys*Nx + Xs];
+    double V1 = in[Ye*Nx + Xs];
+
+    const double value = V0*ry + V1*dy;
+    return value;
+  }
+
+  // if Ny == 1, we have no y-dir spatial information
+  if (Ny == 1) {
+    double V0 = in[Ys*Nx + Xs];
+    double V1 = in[Ys*Nx + Xe];
+
+    const double value = V0*rx + V1*dx;
+    return value;
+  }
+
+  // Vxy
+  double V00 = in[Ys*Nx + Xs];
+  double V01 = in[Ye*Nx + Xs];
+  double V10 = in[Ys*Nx + Xe];
+  double V11 = in[Ye*Nx + Xe];
+
+  double value;
+
+  // corners
+  if ((dx < 0.0) && (dy < 0.0)) {
+    return V00;
+  }
+  if ((dx > 1.0) && (dy < 0.0)) {
+    return V10;
+  }
+  if ((dx < 0.0) && (dy > 1.0)) {
+    return V01;
+  }
+  if ((dx > 1.0) && (dy > 1.0)) {
+    return V11;
+  }
+
+  // sides
+  if (dx < 0.0) {
+    value = V00*ry + V01*dy;
+    return value;
+  }
+  if (dy < 0.0) {
+    value = V00*rx + V10*dx;
+    return value;
+  }
+  if (dx > 1.0) {
+    value = V10*ry + V11*dy;
+    return value;
+  }
+  if (dy > 1.0) {
+    value = V01*rx + V11*dx;
+    return value;
+  }
+
+  // bilinear interpolation
+  value = V00*rx*ry + V10*dx*ry + V01*rx*dy + V11*dx*dy;
+  return value;
+}
Index: /branches/sc_branches/pantasks_condor/cmd.astro/galradbins.c
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.astro/galradbins.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.astro/galradbins.c	(revision 34783)
@@ -0,0 +1,84 @@
+# include "astro.h"
+
+int MeanSurfaceBrightness (Vector *rvec, Vector *fvec, Vector *Rvec, Vector *Fvec, Vector *Avec, float Rmin, float Rmax, int bin);
+
+// given a collection of r, f points sampled at pixels, generate a pair of vectors r, f
+// where f is defined as the mean surface brightness for \alpha r_i < r < \beta r_i.
+// sample r at r_i = i
+
+// this function can be much more efficient if the input vectors are sorted by R and that
+// fact is used in generating the radial bins...
+int galradbins (int argc, char **argv) {
+  
+  int i, Nbin;
+  double Rmax;
+  Vector *fvec, *rvec, *Fvec, *Rvec, *Avec;
+
+  if (argc != 6) {
+    gprint (GP_ERR, "USAGE: galradius (r_in) (f_in) (r_out) (f_out) (area_out)\n");
+    return (FALSE);
+  }
+
+  /* select input / output buffers */
+  if ((rvec = SelectVector (argv[1], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((fvec = SelectVector (argv[2], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((Rvec = SelectVector (argv[3], ANYVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((Fvec = SelectVector (argv[4], ANYVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((Avec = SelectVector (argv[5], ANYVECTOR, TRUE)) == NULL) return (FALSE);
+
+  Rmax = rvec[0].elements.Flt[0];
+  for (i = 0; i < rvec[0].Nelements; i++) {
+    Rmax = MAX(Rmax, rvec[0].elements.Flt[i]);
+  }
+  Nbin = 0.8*Rmax + 2;
+  ResetVector (Rvec, OPIHI_FLT, Nbin);
+  ResetVector (Fvec, OPIHI_FLT, Nbin);
+  ResetVector (Avec, OPIHI_FLT, Nbin);
+
+  // the first three bins are specially defined:
+
+  // 0 : r < 1.0 -- Area is pi
+  MeanSurfaceBrightness (rvec, fvec, Rvec, Fvec, Avec, 0.00, 1.00, 0);
+
+  // area is \int 2 \pi r dr = \pi (r2^2 - r1^2)
+
+  // 1 : 1.0 < r < 1.25 -- Area is \pi (1.25^2 - 1^2)
+  MeanSurfaceBrightness (rvec, fvec, Rvec, Fvec, Avec, 1.00, 1.25, 1);
+
+  // 2 : 1.25 < r < 1.60 -- Area is \pi (1.6^2 - 1.25^2)
+  MeanSurfaceBrightness (rvec, fvec, Rvec, Fvec, Avec, 1.25, 1.60, 2);
+
+  // from bin 3 on out, r = i - 1 
+  for (i = 3; i < Nbin; i++) {
+    MeanSurfaceBrightness (rvec, fvec, Rvec, Fvec, Avec, 0.8*(i-1), 1.25*(i-1), i);
+  }
+
+  return (TRUE);
+}
+
+int MeanSurfaceBrightness (Vector *rvec, Vector *fvec, Vector *Rvec, Vector *Fvec, Vector *Avec, float Rmin, float Rmax, int bin) {
+
+  int i, Npts;
+  double Fsum;
+
+  Fsum = Npts = 0;
+
+  for (i = 0; i < rvec[0].Nelements; i++) {
+    if (rvec[0].elements.Flt[i] < Rmin) continue;
+    if (rvec[0].elements.Flt[i] > Rmax) continue;
+    Fsum += fvec[0].elements.Flt[i];
+    Npts ++;
+  }
+  if (Rmin > 0.0) {
+    Rvec[0].elements.Flt[bin] = sqrt(Rmin * Rmax); // XXX what is the correct mean radius?
+  } else {
+    Rvec[0].elements.Flt[bin] = 0.5 * Rmax; // XXX what is the correct mean radius?
+  }
+  if (Npts > 0) {
+    Fvec[0].elements.Flt[bin] = Fsum / Npts; // XXX what is the correct mean radius?
+  } else {
+    Fvec[0].elements.Flt[bin] = NAN; // XXX what is the correct mean radius?
+  }
+  Avec[0].elements.Flt[bin] = M_PI * (SQ(Rmax) - SQ(Rmin));
+  return (TRUE);
+}
Index: /branches/sc_branches/pantasks_condor/cmd.astro/galradius.c
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.astro/galradius.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.astro/galradius.c	(revision 34783)
@@ -0,0 +1,122 @@
+# include "astro.h"
+
+// given an image and associated galaxy profile, find:
+// a) a characteristic flux level that defines a useful isophot
+// b) radius at which the profile crosses that isophot
+int galradius (int argc, char **argv) {
+  
+  int i, j, N, Rbin, Nout, above;
+  double Rmax, Rmin;
+  double Fmin, Fmax, dF, Fm, Fp, Fo, Rsum, Rnpt, Ro;
+  opihi_flt *flux, *radius, *values;
+  Vector *fvec, *rvec, *Fvec, *Rvec;
+
+  if (argc != 5) {
+    gprint (GP_ERR, "USAGE: galradius (radius) (flux) (min_flux) (max_flux)\n");
+    return (FALSE);
+  }
+
+  /* select input / output buffers */
+  if ((rvec = SelectVector (argv[1], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((fvec = SelectVector (argv[2], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+
+  if ((Rvec = SelectVector ("rg", ANYVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((Fvec = SelectVector ("fg", ANYVECTOR, TRUE)) == NULL) return (FALSE);
+
+  Fmin = atof(argv[3]);
+  Fmax = atof(argv[4]);
+
+  // fvec is a noise sample of the galaxy radial profile at points rvec
+  // rebin fvec into samples defined by the isophot 
+
+  // base selections on fluxes defined by the flux range dF
+  dF = Fmax - Fmin;
+
+  // select all points with flux in the range Fmin + 0.25*dF to Fmin + 0.75*dF
+  Fm = Fmin + 0.25*dF;
+  Fp = Fmin + 0.75*dF;
+  Fo = Fmin + 0.50*dF;
+      
+  Rsum = 0;
+  Rnpt = 0;
+  for (i = 0; i < fvec[0].Nelements; i++) {
+    if (fvec[0].elements.Flt[i] < Fm) continue;
+    if (fvec[0].elements.Flt[i] > Fp) continue;
+      
+    Rsum += rvec[0].elements.Flt[i];
+    Rnpt ++;
+  }
+
+  // determine the binned radius values
+  if (Rnpt == 0) {
+    Rbin = 1;
+  } else {
+    Rbin = MAX(1, 0.5*(Rsum / Rnpt));
+  }
+
+  // do not bother rebinning if the bin size is only 2 or less
+  if (Rbin <= 2) {
+    Nout   = fvec[0].Nelements;
+    ResetVector (Rvec, OPIHI_FLT, Nout);
+    ResetVector (Fvec, OPIHI_FLT, Nout);
+    memcpy (Fvec[0].elements.Flt, fvec[0].elements.Flt, Fvec[0].Nelements*sizeof(opihi_flt));
+    memcpy (Rvec[0].elements.Flt, rvec[0].elements.Flt, Rvec[0].Nelements*sizeof(opihi_flt));
+    flux   = fvec[0].elements.Flt;
+    radius = rvec[0].elements.Flt;
+  } else {
+    // rebin the vectors by Rbin values:
+    Nout = fvec[0].Nelements / Rbin + 1;
+    ALLOCATE (values, opihi_flt, fvec[0].Nelements);
+  
+    ResetVector (Rvec, OPIHI_FLT, Nout);
+    ResetVector (Fvec, OPIHI_FLT, Nout);
+    flux   = Fvec[0].elements.Flt;
+    radius = Rvec[0].elements.Flt;
+
+    for (i = 0; i < Nout; i++) {
+      radius[i] = (i + 0.5)*Rbin;
+      Rmin = radius[i] - 0.5*Rbin;
+      Rmax = radius[i] + 0.5*Rbin;
+
+      N = 0;
+      for (j = 0; j < fvec[0].Nelements; j++) {
+	if (rvec[0].elements.Flt[j] < Rmin) continue;
+	if (rvec[0].elements.Flt[j] > Rmax) continue;
+	values[N] = fvec[0].elements.Flt[j];
+	N++;
+      }
+
+      // take the median of the values
+      dsort (values, N);
+      if (N > 1) {
+	flux[i] = (N % 2) ? values[(int)(0.5*N)] : 0.5*(values[N/2] + values[N/2 + 1]);
+      } else {
+	flux[i] = values[0];
+      }
+    }
+    free (values);
+  }
+
+  above = TRUE;
+  Ro = 0;
+  for (i = 0; i < Nout; i++) {
+
+    // find the largest radius that matches the flux transition
+    if (above && (flux[i] < Fo)) {
+      if (i == 0) { 
+	// assume Fmax @ R = 0.0
+	Ro = radius[i] * (Fo - Fmax) / (flux[i] - Fmax);
+      } else {
+	Ro = radius[i-1] + (radius[i] - radius[i-1]) * (Fo - flux[i-1]) / (flux[i] - flux[i-1]);
+      }
+      above = FALSE;
+    }
+  
+    if (!above && (flux[i] >= Fo)) {
+      above = TRUE;
+    }
+  }
+
+  set_variable ("G_R50", Ro);
+  return (TRUE);
+}
Index: /branches/sc_branches/pantasks_condor/cmd.astro/galsectors.c
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.astro/galsectors.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.astro/galsectors.c	(revision 34783)
@@ -0,0 +1,107 @@
+# include "astro.h"
+
+int galsectors (int argc, char **argv) {
+  
+  int i, j, Nx, Ny, N, Nsec, NELEMENTS, Npt;
+  float *in;
+  double theta, dtheta, x, y, Xo, Yo;
+  Buffer *buf;
+  Vector **fvec, **rvec, *tvec, *Rv, *Tv, *Fv;
+  char name[128];
+
+  if (argc != 5) {
+    gprint (GP_ERR, "USAGE: galsectors (buffer) (Xo) (Yo) Nsec\n");
+    gprint (GP_ERR, "  generates Nsec sectors around the circle; the first is centered on 0.0 degrees\n");
+    return (FALSE);
+  }
+
+  /* select input / output buffers */
+  if ((buf = SelectBuffer (argv[1], OLDBUFFER, TRUE)) == NULL) return (FALSE);
+  Nx = buf[0].header.Naxis[0];
+  Ny = buf[0].header.Naxis[1];
+  
+  Xo = atof(argv[2]);
+  Yo = atof(argv[3]);
+  Nsec = atof(argv[4]);
+
+  // for a test, generate R, theta vectors for the whole image:
+  if ((Rv = SelectVector ("Rvec", ANYVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((Tv = SelectVector ("Tvec", ANYVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((Fv = SelectVector ("Fvec", ANYVECTOR, TRUE)) == NULL) return (FALSE);
+  ResetVector (Rv, OPIHI_FLT, Nx*Ny);
+  ResetVector (Tv, OPIHI_FLT, Nx*Ny);
+  ResetVector (Fv, OPIHI_FLT, Nx*Ny);
+
+  in = (float *) buf[0].matrix.buffer;
+  Npt = 0;
+  for (j = 0; j < Ny; j++) {
+    for (i = 0; i < Nx; i++, in++) {
+      x = i - Xo;
+      y = j - Yo;
+      Rv[0].elements.Flt[Npt] = hypot(x, y);
+      Tv[0].elements.Flt[Npt] = DEG_RAD*atan2(y, x);
+      Fv[0].elements.Flt[Npt] = *in;
+      Npt ++;
+    }
+  }
+  return (TRUE);
+
+  dtheta = 360.0 / Nsec;
+  // sector i ranges from (i-0.5)*dtheta to (i+0.5)*dtheta
+  // i = theta / dtheta + 0.5;
+
+  ALLOCATE (fvec, Vector *, Nsec);
+  ALLOCATE (rvec, Vector *, Nsec);
+  if ((tvec = SelectVector ("theta", ANYVECTOR, TRUE)) == NULL) return (FALSE);
+  ResetVector (tvec, OPIHI_FLT, Nsec);
+
+  // generate the output vector names
+  NELEMENTS = 1000;
+  for (i = 0; i < Nsec; i++) {
+    sprintf (name, "flux_%d", i);
+    if ((fvec[i] = SelectVector (name, ANYVECTOR, TRUE)) == NULL) return (FALSE);
+    sprintf (name, "rad_%d", i);
+    if ((rvec[i] = SelectVector (name, ANYVECTOR, TRUE)) == NULL) return (FALSE);
+    ResetVector (fvec[i], OPIHI_FLT, NELEMENTS);
+    ResetVector (rvec[i], OPIHI_FLT, NELEMENTS);
+    fvec[i][0].Nelements = 0;
+    rvec[i][0].Nelements = 0;
+    tvec[0].elements.Flt[i] = i*dtheta;
+  }
+
+  in = (float *) buf[0].matrix.buffer;
+  for (j = 0; j < Ny; j++) {
+    for (i = 0; i < Nx; i++, in++) {
+      x = i - Xo;
+      y = j - Yo;
+      theta = DEG_RAD*atan2(y, x);
+      if (theta < -0.5*dtheta) theta += 360.0; // need to allow -0.5dtheta for first bin
+      if (theta > 360 - 0.5*dtheta) theta -= 360.0; // need to allow -0.5dtheta for first bin
+      N = (int)(theta / dtheta + 0.5);
+      if (N < 0) {
+	fprintf (stderr, "?");
+	continue;
+      }
+      if (N >= Nsec) {
+	fprintf (stderr, "!");
+	continue;
+      }
+      
+      fvec[N][0].elements.Flt[fvec[N][0].Nelements] = *in;
+      rvec[N][0].elements.Flt[rvec[N][0].Nelements] = hypot(x, y);
+      fvec[N][0].Nelements ++;
+      rvec[N][0].Nelements ++;
+
+      *in = N;
+
+      if (fvec[N][0].Nelements >= NELEMENTS) {
+	NELEMENTS += 1000;
+	for (N = 0; N < Nsec; N++) {
+	  REALLOCATE (fvec[N][0].elements.Flt, opihi_flt, NELEMENTS);
+	  REALLOCATE (rvec[N][0].elements.Flt, opihi_flt, NELEMENTS);
+	}
+      }
+    }
+  }
+  return (TRUE);
+}
Index: /branches/sc_branches/pantasks_condor/cmd.astro/gauss.c
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.astro/gauss.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.astro/gauss.c	(revision 34783)
@@ -0,0 +1,65 @@
+# include "astro.h"
+
+int gauss (int argc, char **argv) {
+
+  char key[20];
+  int i, N, Npix, Nborder, Nspot;
+  double X, Y, ZP, RA, DEC, max;
+  int kapa;
+  char *name;
+  Buffer *buf;
+  KapaImageData data;
+
+  name = NULL;
+  if ((N = get_argument (argc, argv, "-n"))) {
+    remove_argument (N, &argc, argv);
+    name = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+  if (!GetImageData (&data, &kapa, name)) return (FALSE);
+  FREE (name);
+
+  Nborder = 3;
+  if ((N = get_argument (argc, argv, "-border"))) {
+    remove_argument (N, &argc, argv);
+    Nborder  = atof(argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+  Nborder = MAX (Nborder, 1);
+  
+  max = 60000;
+  if ((N = get_argument (argc, argv, "-sat"))) {
+    remove_argument (N, &argc, argv);
+    max  = atof(argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+  
+  if ((argc != 2) && (argc != 3)) {
+    gprint (GP_ERR, "USAGE: gauss Npix [Nspots] [-border N] [-sat cnts]\n");
+    return (FALSE);
+  }
+  
+  if (kapa < 1) {
+    gprint (GP_ERR, "no active TV\n");
+    return (FALSE);
+  }
+
+  Nspot = 0;
+  Npix = atof (argv[1]);
+  if (argc == 3) {
+    Nspot = atof (argv[2]);
+  }
+
+  if ((buf = SelectBuffer (data.name, OLDBUFFER, TRUE)) == NULL) return (FALSE);
+
+  KiiCursorOn (kapa);
+
+  for (i = 0; (i < Nspot) || (Nspot == 0); i++) {
+    KiiCursorRead (kapa, &X, &Y, &ZP, &RA, &DEC, key);
+    if (!strcasecmp (key, "Q")) break;
+    get_aperture_stats (&buf[0].matrix, (int)(X+0.5), (int)(Y+0.5), Npix, Nborder, max);
+  }
+  KiiCursorOff (kapa);
+  return (TRUE);
+}
+
Index: /branches/sc_branches/pantasks_condor/cmd.astro/getcoords.c
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.astro/getcoords.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.astro/getcoords.c	(revision 34783)
@@ -0,0 +1,113 @@
+# include "astro.h"
+
+enum {NONE, SKY, PIXEL, VECTOR, SCALAR};
+
+int getcoords (int argc, char **argv) {
+
+  int ix, iy, N;
+  double c1, c2, t1, t2;
+  Coords coords, moscoords;
+  Buffer *input, *mosbuffer, *outC1, *outC2;
+  CoordTransformSystem inTrans, outTrans;
+
+  CoordTransform *transform = NULL;
+  if ((N = get_argument (argc, argv, "-transform"))) {
+    if (argc < N + 3) goto syntax;
+    remove_argument (N, &argc, argv);
+
+    switch (argv[N][0]) {
+      case 'C': inTrans = COORD_CELESTIAL; break;
+      case 'G': inTrans = COORD_GALACTIC; break;
+      case 'E': inTrans = COORD_ECLIPTIC; break;
+      default: goto syntax;
+    }
+    remove_argument (N, &argc, argv);
+
+    switch (argv[N][0]) {
+      case 'C': outTrans = COORD_CELESTIAL; break;
+      case 'G': outTrans = COORD_GALACTIC; break;
+      case 'E': outTrans = COORD_ECLIPTIC; break;
+      default: goto syntax;
+    }
+    remove_argument (N, &argc, argv);
+    
+    transform = InitTransform (inTrans, outTrans);
+    if (transform == NULL) {
+      gprint (GP_ERR, "transform %c to %c is not yet defined\n", argv[1][0], argv[2][0]);
+      return (FALSE);
+    }
+  }
+
+  char *MOSAIC = NULL;
+  if ((N = get_argument (argc, argv, "-mosaic"))) {
+    remove_argument (N, &argc, argv);
+    MOSAIC = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  if (argc != 4) goto syntax;
+  if ((input = SelectBuffer (argv[1], OLDBUFFER, TRUE)) == NULL) goto syntax;
+  if ((outC1 = SelectBuffer (argv[2], ANYBUFFER, TRUE)) == NULL) goto syntax;
+  if ((outC2 = SelectBuffer (argv[3], ANYBUFFER, TRUE)) == NULL) goto syntax;
+
+  GetCoords (&coords, &input[0].header);
+  if (!strcmp(&coords.ctype[4], "-WRP")) {
+    if (MOSAIC == NULL) {
+      gprint (GP_ERR, "must supply mosaic for WRP coords\n");
+      return (FALSE);
+    }
+    if ((mosbuffer = SelectBuffer (MOSAIC, OLDBUFFER, TRUE)) == NULL) {
+        // Using goto_escape triggers a compiler bug with our build flags when using gcc 4.3.2
+        // It thinks MOSAIC may be uninitialzed even when it can't be. Just free 
+        // goto escape;
+        free (MOSAIC);
+        return (FALSE);
+    }
+    GetCoords (&moscoords, &mosbuffer[0].header);
+    RegisterMosaic (&moscoords);
+  }
+  
+  int Nx = input[0].matrix.Naxis[0];
+  int Ny = input[0].matrix.Naxis[1];
+
+  gfits_free_matrix (&outC1[0].matrix);
+  gfits_free_header (&outC1[0].header);
+  CreateBuffer (outC1, Nx, Ny, -32, 1.0, 0.0);
+
+  gfits_free_matrix (&outC2[0].matrix);
+  gfits_free_header (&outC2[0].header);
+  CreateBuffer (outC2, Nx, Ny, -32, 1.0, 0.0);
+
+  float *valC1 = (float *) outC1[0].matrix.buffer;
+  float *valC2 = (float *) outC2[0].matrix.buffer;
+
+  for (ix = 0; ix < Nx; ix++) {
+    for (iy = 0; iy < Ny; iy++) {
+      int status = XY_to_RD (&c1, &c2, (double) ix, (double) iy, &coords);
+      if (!status) {
+	valC1[ix + Nx*iy] = NAN;
+	valC2[ix + Nx*iy] = NAN;
+	continue;
+      }
+      if (transform) {
+	ApplyTransform (&t1, &t2, c1, c2, transform);
+	c1 = t1;
+	c2 = t2;
+      }
+      valC1[ix + Nx*iy] = c1;
+      valC2[ix + Nx*iy] = c2;
+    }
+  }
+  if (transform) free (transform);
+
+  return (TRUE);
+
+ syntax:
+  gprint (GP_ERR, "USAGE: getcoords [buffer] (crval1) (crval2) [-transform C/E/G C/E/G]\n");
+  gprint (GP_ERR, "generate images of the two coordinate dimensions for the image WCS\n");
+  // escape:
+  // Jumping here and trying to free this here causes the compiler to fail on optimized build to fail See comments
+  // above.
+  // if (MOSAIC != NULL) free (MOSAIC);
+  return (FALSE);
+}
Index: /branches/sc_branches/pantasks_condor/cmd.astro/getlst.c
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.astro/getlst.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.astro/getlst.c	(revision 34783)
@@ -0,0 +1,41 @@
+# include "astro.h"
+
+int getlst (int argc, char **argv) {
+
+  int N;
+  time_t time;
+  double jd, lst, longitude;
+  char *Variable;
+
+  Variable = NULL;
+  if ((N = get_argument (argc, argv, "-var"))) {
+    remove_argument (N, &argc, argv);
+    Variable = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+  if (argc != 3) goto syntax;
+
+  if (!ohana_str_to_time (argv[1], &time)) {
+      if (Variable != NULL) free (Variable);
+      return (FALSE);
+  }
+
+  longitude = atof (argv[2]);
+
+  jd = ohana_sec_to_jd (time);
+  fprintf (stderr, "jd: %f\n", jd);
+
+  lst = ohana_lst (jd, longitude);
+
+  if (Variable != NULL) {
+      set_variable (Variable, lst);
+      free (Variable);
+      return (TRUE);
+  }
+  gprint (GP_ERR, "lst: %f\n", lst);
+  return (TRUE);
+
+ syntax:
+  gprint (GP_ERR, "USAGE: getlst (time) (longitude) [-var name]\n");
+  return (FALSE);
+}
Index: /branches/sc_branches/pantasks_condor/cmd.astro/getvel.c
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.astro/getvel.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.astro/getvel.c	(revision 34783)
@@ -0,0 +1,79 @@
+# include "astro.h"
+
+int getvel (int argc, char **argv) {
+  
+  int i, n, Ncurve;
+  double L, V, Vo, dV, Bo, dB;
+  double xo, yo;
+  double sl, cl, wo, Ro, Rs, wr, r, fr, d, x;
+  double R[100], T[100], W[100];
+  FILE *f;
+  Buffer *buf;
+
+  if (argc != 5) {
+    gprint (GP_ERR, "USAGE: rotcurve buf X Y curve.txt\n");
+    return (FALSE);
+  }
+
+  f = fopen (argv[4], "r");
+  if (f == (FILE *) NULL) {
+    gprint (GP_ERR, "can't find rotation curve data file %s\n", argv[4]);
+    return (FALSE);
+  }
+  for (i = 0; fscanf (f, "%lf %lf", &R[i], &T[i]) != EOF; i++) {
+    W[i] = T[i] / R[i];
+  }  
+  fclose (f);
+  Ncurve = i;
+
+  if ((buf = SelectBuffer (argv[1], OLDBUFFER, TRUE)) == NULL) return (FALSE);
+  // int nx = buf[0].matrix.Naxis[0];
+  // int ny = buf[0].matrix.Naxis[1];
+
+  /* we expect the input image to have units of velocity, lattitude, and longitude */
+  gfits_scan (&buf[0].header, "CRVAL1", "%lf", 1, &Vo);
+  gfits_scan (&buf[0].header, "CDELT1", "%lf", 1, &dV);
+  gfits_scan (&buf[0].header, "CRPIX1", "%lf", 1, &xo);
+  gfits_scan (&buf[0].header, "CRVAL2", "%lf", 1, &Bo);
+  gfits_scan (&buf[0].header, "CDELT2", "%lf", 1, &dB);
+  gfits_scan (&buf[0].header, "CRPIX2", "%lf", 1, &yo);
+  gfits_scan (&buf[0].header, "CRVAL3", "%lf", 1, &L);
+  Vo *= 0.001;
+  dV *= 0.001;
+
+  L = ohana_normalize_angle (L);
+  gprint (GP_ERR, "L: %f\n", L);
+
+  cl = cos (L*RAD_DEG);
+  sl = sin (L*RAD_DEG);
+  wo = 25.0;
+  Ro = 10.0;
+  Rs = Ro*sl;
+  x = atof (argv[2]);
+  /* this method depends on wr monotonically decreasing */
+
+  V = (x - xo) * dV + Vo;
+  wr = V/Rs + wo;
+  for (n = 0; (n < Ncurve) && (wr < W[n]); n++);
+  if ((n == 0) || (n == Ncurve)) {
+    gprint (GP_ERR, "velocity out of reasonable range\n");
+    gprint (GP_ERR, "%f %f %f %f\n", V, wr, W[0], W[Ncurve-1]);
+    return (TRUE);
+  }
+  r = (wr - W[n]) *  (R[n-1] - R[n]) / (W[n-1] - W[n]) + R[n];
+  fr = (Ro/r);
+  if (r < fabs(Rs)) { /* can't be on rotation curve */
+    gprint (GP_ERR, "velocity out of reasonable range\n");
+    gprint (GP_ERR, "%f %f %f %f %f %f %f\n", V, wr, W[0], W[Ncurve-1], r, fr, Rs);
+    return (TRUE);
+  }
+  if (r < Ro)
+    d = Ro*cl - sqrt(r*r - Rs*Rs);
+  else 
+    d = Ro*cl + sqrt(r*r - Rs*Rs);
+  
+  gprint (GP_ERR, "dist: %f, vel: %f\n", d, V);
+
+  return (TRUE);
+
+} 
Index: /branches/sc_branches/pantasks_condor/cmd.astro/help/biassub
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.astro/help/biassub	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.astro/help/biassub	(revision 34783)
@@ -0,0 +1,9 @@
+
+  biassub buffer sx sy nx ny dir [-v N V]
+
+  Apply a bias correction.  The region defines a vector 
+  (median is taken perpendicular to the dir direction) which 
+  is subtracted from the image.
+
+
+
Index: /branches/sc_branches/pantasks_condor/cmd.astro/help/cgrid
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.astro/help/cgrid	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.astro/help/cgrid	(revision 34783)
@@ -0,0 +1,6 @@
+
+  cgrid
+
+  draws a grid in RA and DEC on window 0 approriate for the current
+  region.
+
Index: /branches/sc_branches/pantasks_condor/cmd.astro/help/coords
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.astro/help/coords	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.astro/help/coords	(revision 34783)
@@ -0,0 +1,19 @@
+
+  coords <buffer> (filename)
+
+  "coords" loads astrometric parameters for the given buffer from the
+header of the named file.  The parameters are applied to the buffer,
+and will register in the Ki'i window if the buffer it tv'ed again.
+This is particularly convenient for intercomparing coordinate system
+or in the event the astrometry is stored in a different file (eg, a
+stripped off header).  Currently, the only format of coordinate
+information that is looked for are gene's linear transformation: RA_O,
+RA_X, RA_Y, DEC_O, DEC_X, DEC_Y such that the RA,DEC of an object is
+given by:
+
+RA  = RA_O  + x*RA_X  + y*RA_Y
+DEC = DEC_O + x*DEC_X + y*DEC_Y.
+
+
+  See also: rd, tv
+
Index: /branches/sc_branches/pantasks_condor/cmd.astro/help/flux
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.astro/help/flux	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.astro/help/flux	(revision 34783)
@@ -0,0 +1,7 @@
+
+   flux buffer (file)
+
+   calculate the flux enclosed by the contour given in the file.  The
+   file contains the coordinates of the polygon corners.
+
+
Index: /branches/sc_branches/pantasks_condor/cmd.astro/help/gauss
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.astro/help/gauss	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.astro/help/gauss	(revision 34783)
@@ -0,0 +1,15 @@
+
+   gauss Npix [Nspots] [-border N] [-sat cnts]
+
+   calculate statistics on stars, assuming a Gaussian profile.  The
+   user types a key on a star in the Kii window, and the resulting
+   information is printed on the screen.  This is really aperture
+   photometry.  Npix defines the aperture width (square aperture), the
+   optional -border defines the number of pixels in an annulus used to
+   find the background, -sat defines the value of a saturated pixel.
+   If the Nspots option is given, exactly Nspots stars are chosen.
+   Otherwise, the user may type on stars until the 'q' is typed.  The
+   results of the last star are stored the a set of Mana variables.
+
+   See also: star, cursor
+
Index: /branches/sc_branches/pantasks_condor/cmd.astro/help/profile
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.astro/help/profile	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.astro/help/profile	(revision 34783)
@@ -0,0 +1,6 @@
+
+   profile <buffer> <X vector> <Y vector> x y N
+
+   Find the radial profile of an image at the location x, y.  the
+   radius is placed in the X vector and the pixel value is placed in
+   the Y vector.   
Index: /branches/sc_branches/pantasks_condor/cmd.astro/help/region
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.astro/help/region	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.astro/help/region	(revision 34783)
@@ -0,0 +1,18 @@
+
+  region RA DEC Radius [projection]
+
+  define the current sky region and optionally the projection.  
+  RA, DEC and Radius are all values in decimal degrees.  The possible 
+  values for "projection" are:
+
+  TAN - a tangent plane projection (default, and typical for optical images)
+  SIN - a sine plane projection (more appropriate for polar regions)
+  AIT - aitoff projection (good for the full sky)
+  GLS - ?? (also good for the full sky)
+
+  optional orientation flags:
+
+  -ew : display with East Left (default)
+  +ew : display with East Right 
+  -ns : display with North Down
+  +ns : display with North Up (default)
Index: /branches/sc_branches/pantasks_condor/cmd.astro/imfit-fgauss.c
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.astro/imfit-fgauss.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.astro/imfit-fgauss.c	(revision 34783)
@@ -0,0 +1,68 @@
+# include "imfit.h"
+
+opihi_flt fgaussTD (opihi_flt, opihi_flt, opihi_flt *, int, opihi_flt *);
+void  fgaussCL ();
+
+void fgauss_setup (char *name) {
+
+  if (strcmp(name, "fgauss")) return;
+
+  fitfunc = fgaussTD;
+  imfit_cleanup = fgaussCL;
+  Npar = 7;
+  Nfpar = 0;
+
+  /* allocate free and fixed parameters */
+  ALLOCATE (par, opihi_flt, MAX (Npar, 1));
+  bzero (par, Npar*sizeof(opihi_flt));
+  ALLOCATE (fpar, opihi_flt, MAX (Nfpar, 1));
+  bzero (fpar, Nfpar*sizeof(opihi_flt));
+
+  par[0] = get_variable_default ("Xg", 0);
+  par[1] = get_variable_default ("Yg", 0);
+  par[2] = 2.35 / get_variable_default ("SXg", 2.0);
+  par[3] = 2.35 / get_variable_default ("SYg", 2.0);
+  par[4] = get_variable_default ("SXYg", 0);
+  par[5] = get_variable_default ("Zpk", 10000);
+  par[6] = get_variable_default ("Sg", 0.0);
+  sky = &par[6];
+}
+
+void fgaussCL () {
+  set_variable ("Xg",   par[0]);
+  set_variable ("Yg",   par[1]);
+  set_variable ("SXg",  2.35 / par[2]);
+  set_variable ("SYg",  2.35 / par[3]);
+  set_variable ("SXYg", par[4]);
+  set_variable ("Zpk",  par[5]);
+  set_variable ("Sg",   par[6]);
+}
+
+/* real 2D gaussian -- x, y, sx, sy, sxy, I, sky */
+opihi_flt fgaussTD (opihi_flt x, opihi_flt y, opihi_flt *par, int Npar, opihi_flt *dpar) {
+
+  opihi_flt X, Y, px, py;
+  opihi_flt z, r, q, f;
+
+  X = x - par[0];
+  Y = y - par[1];
+  
+  px = par[2]*X;
+  py = par[3]*Y;
+
+  z = 0.5*SQ(px) + 0.5*SQ(py) + par[4]*X*Y;
+  r = exp (-z);
+  q = par[5]*r;
+  f = q + par[6];
+
+  if (dpar != NULL) {
+    dpar[0] = q*(2*px*par[2] + par[4]*Y);
+    dpar[1] = q*(2*py*par[3] + par[4]*X);
+    dpar[2] = -2*q*px*X;
+    dpar[3] = -2*q*py*Y;
+    dpar[4] = -q*X*Y;
+    dpar[5] = +r;
+    dpar[6] = +1;
+  }
+  return (f);
+}
Index: /branches/sc_branches/pantasks_condor/cmd.astro/imfit-pgauss-psf.c
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.astro/imfit-pgauss-psf.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.astro/imfit-pgauss-psf.c	(revision 34783)
@@ -0,0 +1,66 @@
+# include "imfit.h"
+
+opihi_flt pgauss_psfTD (opihi_flt, opihi_flt, opihi_flt *, int, opihi_flt *);
+void  pgauss_psfCL ();
+
+void  pgauss_psf_setup (char *name) {
+
+  if (strcmp(name, "pgauss_psf")) return;
+
+  fitfunc = pgauss_psfTD;
+  imfit_cleanup = pgauss_psfCL;
+  Npar = 4;
+  Nfpar = 3;
+
+  /* allocate free and fixed parameters */
+  ALLOCATE (par, opihi_flt, MAX (Npar, 1));
+  bzero (par, Npar*sizeof(opihi_flt));
+  ALLOCATE (fpar, opihi_flt, MAX (Nfpar, 1));
+  bzero (fpar, Nfpar*sizeof(opihi_flt));
+
+  par[0] = get_variable_default ("Xg", 0);
+  par[1] = get_variable_default ("Yg", 0);
+  par[2] = get_variable_default ("Zpk", 10000);
+  par[3] = get_variable_default ("Sg", 0.0);
+  sky = &par[3];
+
+  fpar[0] = 2.35 / get_variable_default ("SXg", 2.0);
+  fpar[1] = 2.35 / get_variable_default ("SYg", 2.0);
+  fpar[2] = get_variable_default ("SXYg", 0);
+}
+
+void pgauss_psfCL () {
+  set_variable ("Xg",   par[0]);
+  set_variable ("Yg",   par[1]);
+  set_variable ("Zpk",  par[2]);
+  set_variable ("Sg",   par[3]);
+}
+
+/* pseudo 2D gaussian -- x, y, (sx), (sy), (sxy), I, sky */
+opihi_flt pgauss_psfTD (opihi_flt x, opihi_flt y, opihi_flt *par, int Npar, opihi_flt *dpar) {
+
+  opihi_flt X, Y, px, py;
+  opihi_flt z, r, q, f;
+
+  /* par -> fpar: (2,0), (3,1), (4,2) */
+
+  X = x - par[0];
+  Y = y - par[1];
+  
+  px = fpar[0]*X;
+  py = fpar[1]*Y;
+
+  z = 0.5*SQ(px) + 0.5*SQ(py) + fpar[2]*X*Y;
+  r = 1.0 / (1 + z + 0.5*z*z*(1 + z/3)); /* ~ exp (-Z) */
+  f = par[2]*r + par[3];
+  q = par[2]*r*r*(1 + z + 0.5*z*z);
+  /* note difference from gaussian: q = par[5]*r */
+
+  if (dpar != NULL) {
+    dpar[0] = q*(2*px*fpar[0] + fpar[2]*Y);
+    dpar[1] = q*(2*py*fpar[1] + fpar[2]*X);
+    dpar[2] = +r;
+    dpar[3] = +1;
+  }
+  return (f);
+}
Index: /branches/sc_branches/pantasks_condor/cmd.astro/imfit-pgauss.c
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.astro/imfit-pgauss.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.astro/imfit-pgauss.c	(revision 34783)
@@ -0,0 +1,69 @@
+# include "imfit.h"
+
+opihi_flt pgaussTD (opihi_flt, opihi_flt, opihi_flt *, int, opihi_flt *);
+void  pgaussCL ();
+
+void  pgauss_setup (char *name) {
+
+  if (strcmp(name, "pgauss")) return;
+
+  fitfunc = pgaussTD;
+  imfit_cleanup = pgaussCL;
+  Npar = 7;
+  Nfpar = 0;
+
+  /* allocate free and fixed parameters */
+  ALLOCATE (par, opihi_flt, MAX (Npar, 1));
+  bzero (par, Npar*sizeof(opihi_flt));
+  ALLOCATE (fpar, opihi_flt, MAX (Nfpar, 1));
+  bzero (fpar, Nfpar*sizeof(opihi_flt));
+
+  par[0] = get_variable_default ("Xg", 0.0);
+  par[1] = get_variable_default ("Yg", 0.0);
+  par[2] = 2.35 / get_variable_default ("SXg", 2.0);
+  par[3] = 2.35 / get_variable_default ("SYg", 2.0);
+  par[4] = get_variable_default ("SXYg", 0.0);
+  par[5] = get_variable_default ("Zpk", 10000);
+  par[6] = get_variable_default ("Sg", 0.0);
+  sky = &par[6];
+}
+
+void pgaussCL () {
+  set_variable ("Xg",   par[0]);
+  set_variable ("Yg",   par[1]);
+  set_variable ("SXg",  2.35 / par[2]);
+  set_variable ("SYg",  2.35 / par[3]);
+  set_variable ("SXYg", par[4]);
+  set_variable ("Zpk",  par[5]);
+  set_variable ("Sg",   par[6]);
+}
+
+/* pseudo 2D gaussian -- x, y, sx, sy, sxy, I, sky */
+opihi_flt pgaussTD (opihi_flt x, opihi_flt y, opihi_flt *par, int Npar, opihi_flt *dpar) {
+
+  opihi_flt X, Y, px, py;
+  opihi_flt z, r, q, f;
+
+  X = x - par[0];
+  Y = y - par[1];
+  
+  px = par[2]*X;
+  py = par[3]*Y;
+
+  z = 0.5*SQ(px) + 0.5*SQ(py) + par[4]*X*Y;
+  r = 1.0 / (1 + z + 0.5*z*z*(1 + z/3)); /* ~ exp (-Z) */
+  f = par[5]*r + par[6];
+  q = par[5]*r*r*(1 + z + 0.5*z*z);
+  /* note difference from gaussian: q = par[5]*r */
+
+  if (dpar != NULL) {
+    dpar[0] = q*(2*px*par[2] + par[4]*Y);
+    dpar[1] = q*(2*py*par[3] + par[4]*X);
+    dpar[2] = -2*q*px*X;
+    dpar[3] = -2*q*py*Y;
+    dpar[4] = -q*X*Y;
+    dpar[5] = +r;
+    dpar[6] = +1;
+  }
+  return (f);
+}
Index: /branches/sc_branches/pantasks_condor/cmd.astro/imfit-qfgauss.c
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.astro/imfit-qfgauss.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.astro/imfit-qfgauss.c	(revision 34783)
@@ -0,0 +1,74 @@
+# include "imfit.h"
+
+opihi_flt qfgaussTD (opihi_flt, opihi_flt, opihi_flt *, int, opihi_flt *);
+void  qfgaussCL ();
+
+void qfgauss_setup (char *name) {
+
+  if (strcmp(name, "qfgauss")) return;
+
+  fitfunc = qfgaussTD;
+  imfit_cleanup = qfgaussCL;
+  Npar = 7;
+  Nfpar = 2;
+
+  /* allocate free and fixed parameters */
+  ALLOCATE (par, opihi_flt, MAX (Npar, 1));
+  bzero (par, Npar*sizeof(opihi_flt));
+  ALLOCATE (fpar, opihi_flt, MAX (Nfpar, 1));
+  bzero (fpar, Nfpar*sizeof(opihi_flt));
+
+  par[0]  = get_variable_default ("Xg", 0);
+  par[1]  = get_variable_default ("Yg", 0);
+  par[2]  = 2.35 / get_variable_default ("SXg", 2.0);
+  par[3]  = 2.35 / get_variable_default ("SYg", 2.0);
+  par[4]  = get_variable_default ("SXYg", 0);
+  par[5]  = get_variable_default ("Zpk", 10000);
+  par[6]  = get_variable_default ("Sg", 0.0);
+
+  fpar[0] = get_variable_default ("Npow", 2.25);
+  fpar[1]  = get_variable_default ("Sr", 1.0);
+
+  sky = &par[6];
+}
+
+void qfgaussCL () {
+  set_variable ("Xg",   par[0]);
+  set_variable ("Yg",   par[1]);
+  set_variable ("SXg",  2.35 / par[2]);
+  set_variable ("SYg",  2.35 / par[3]);
+  set_variable ("SXYg", par[4]);
+  set_variable ("Zpk",  par[5]);
+  set_variable ("Sg",   par[6]);
+}
+
+/* one component, two slopes: (1 + z^M + z^N)^(-1) -- x, y, sx, sy, sxy, I, sky */
+opihi_flt qfgaussTD (opihi_flt x, opihi_flt y, opihi_flt *par, int Npar, opihi_flt *dpar) {
+
+  opihi_flt X, Y, px, py;
+  opihi_flt z, r, q, f;
+
+  X = x - par[0];
+  Y = y - par[1];
+  
+  px = par[2]*X;
+  py = par[3]*Y;
+
+  z = 0.5*SQ(px) + 0.5*SQ(py) + par[4]*X*Y;
+
+  r = 1.0 / (1 + fpar[1]*z + pow(z,fpar[0]));
+  f = par[5]*r + par[6];
+  q = par[5]*SQ(r)*(fpar[1] + fpar[0]*pow(z,(fpar[0]-1)));
+
+  if (dpar != NULL) {
+    dpar[0] = q*(2*px*par[2] + par[4]*Y);
+    dpar[1] = q*(2*py*par[3] + par[4]*X);
+    dpar[2] = -2*q*px*X*2;
+    dpar[3] = -2*q*py*Y*2;
+    dpar[4] = -q*X*Y;
+    dpar[5] = +r;
+    dpar[6] = +1;
+  }
+  return (f);
+}
+
Index: /branches/sc_branches/pantasks_condor/cmd.astro/imfit-qgauss-psf.c
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.astro/imfit-qgauss-psf.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.astro/imfit-qgauss-psf.c	(revision 34783)
@@ -0,0 +1,68 @@
+# include "imfit.h"
+
+opihi_flt qgauss_psfTD (opihi_flt, opihi_flt, opihi_flt *, int, opihi_flt *);
+void  qgauss_psfCL ();
+
+void qgauss_psf_setup (char *name) {
+
+  if (strcmp(name, "qgauss_psf")) return;
+
+  fitfunc = qgauss_psfTD;
+  imfit_cleanup = qgauss_psfCL;
+  Npar = 4;
+  Nfpar = 5;
+
+  /* allocate free and fixed parameters */
+  ALLOCATE (par, opihi_flt, MAX (Npar, 1));
+  bzero (par, Npar*sizeof(opihi_flt));
+  ALLOCATE (fpar, opihi_flt, MAX (Nfpar, 1));
+  bzero (fpar, Nfpar*sizeof(opihi_flt));
+
+  par[0]  = get_variable_default ("Xg", 0);
+  par[1]  = get_variable_default ("Yg", 0);
+  par[2]  = get_variable_default ("Zpk", 10000);
+  par[3]  = get_variable_default ("Sg", 0.0);
+
+  fpar[0] = 2.35 / get_variable_default ("SXg", 15.0);
+  fpar[1] = 2.35 / get_variable_default ("SYg", 15.0);
+  fpar[2] = get_variable_default ("SXYg", 0.0);
+  fpar[3] = get_variable_default ("Sr", 1.0);
+  fpar[4] = get_variable_default ("Npow", 2.25);
+
+  sky = &par[3];
+}
+
+void qgauss_psfCL () {
+  set_variable ("Xg",  par[0]);
+  set_variable ("Yg",  par[1]);
+  set_variable ("Zpk", par[2]);
+  set_variable ("Sg",  par[3]);
+}
+
+/* one component, two slopes: (1 + z^M + z^N)^(-1) -- x, y, sx, sy, sxy, I, sky, sr */
+opihi_flt qgauss_psfTD (opihi_flt x, opihi_flt y, opihi_flt *par, int Npar, opihi_flt *dpar) {
+
+  opihi_flt X, Y, px, py;
+  opihi_flt z, r, q, f;
+
+  X = x - par[0];
+  Y = y - par[1];
+  
+  px = fpar[0]*X;
+  py = fpar[1]*Y;
+
+  z = 0.5*SQ(px) + 0.5*SQ(py) + fpar[2]*X*Y;
+
+  r = 1.0 / (1 + fpar[3]*z + pow(z,fpar[4]));
+  f = par[2]*r + par[3];
+  q = par[2]*SQ(r)*(fpar[3] + fpar[4]*pow(z,(fpar[4]-1)));
+
+  if (dpar != NULL) {
+    dpar[0] = q*(2*px*fpar[0] + fpar[2]*Y);
+    dpar[1] = q*(2*py*fpar[1] + fpar[2]*X);
+    dpar[2] = +r;
+    dpar[3] = +1;
+  }
+  return (f);
+}
+
Index: /branches/sc_branches/pantasks_condor/cmd.astro/imfit-qgauss.c
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.astro/imfit-qgauss.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.astro/imfit-qgauss.c	(revision 34783)
@@ -0,0 +1,75 @@
+# include "imfit.h"
+
+opihi_flt qgaussTD (opihi_flt, opihi_flt, opihi_flt *, int, opihi_flt *);
+void  qgaussCL ();
+
+void qgauss_setup (char *name) {
+
+  if (strcmp(name, "qgauss")) return;
+
+  fitfunc = qgaussTD;
+  imfit_cleanup = qgaussCL;
+  Npar = 8;
+  Nfpar = 1;
+
+  /* allocate free and fixed parameters */
+  ALLOCATE (par, opihi_flt, MAX (Npar, 1));
+  bzero (par, Npar*sizeof(opihi_flt));
+  ALLOCATE (fpar, opihi_flt, MAX (Nfpar, 1));
+  bzero (fpar, Nfpar*sizeof(opihi_flt));
+
+  par[0]  = get_variable_default ("Xg", 0);
+  par[1]  = get_variable_default ("Yg", 0);
+  par[2]  = 2.35 / get_variable_default ("SXg", 2.0);
+  par[3]  = 2.35 / get_variable_default ("SYg", 2.0);
+  par[4]  = get_variable_default ("SXYg", 0);
+  par[5]  = get_variable_default ("Zpk", 10000);
+  par[6]  = get_variable_default ("Sg", 0.0);
+  par[7]  = get_variable_default ("Sr", 1.0);
+  fpar[0] = get_variable_default ("Npow", 2.25);
+
+  sky = &par[6];
+}
+
+void qgaussCL () {
+  set_variable ("Xg",   par[0]);
+  set_variable ("Yg",   par[1]);
+  set_variable ("SXg",  2.35 / par[2]);
+  set_variable ("SYg",  2.35 / par[3]);
+  set_variable ("SXYg", par[4]);
+  set_variable ("Zpk",  par[5]);
+  set_variable ("Sg",   par[6]);
+  set_variable ("Sr", par[7]);
+}
+
+/* one component, two slopes: (1 + z^M + z^N)^(-1) -- x, y, sx, sy, sxy, I, sky, sr */
+opihi_flt qgaussTD (opihi_flt x, opihi_flt y, opihi_flt *par, int Npar, opihi_flt *dpar) {
+
+  opihi_flt X, Y, px, py;
+  opihi_flt z, r, q, f;
+
+  X = x - par[0];
+  Y = y - par[1];
+  
+  px = par[2]*X;
+  py = par[3]*Y;
+
+  z = 0.5*SQ(px) + 0.5*SQ(py) + par[4]*X*Y;
+
+  r = 1.0 / (1 + par[7]*z + pow(z,fpar[0]));
+  f = par[5]*r + par[6];
+  q = par[5]*SQ(r)*(par[7] + fpar[0]*pow(z,(fpar[0]-1)));
+
+  if (dpar != NULL) {
+    dpar[0] = q*(2*px*par[2] + par[4]*Y);
+    dpar[1] = q*(2*py*par[3] + par[4]*X);
+    dpar[2] = -2*q*px*X*2;
+    dpar[3] = -2*q*py*Y*2;
+    dpar[4] = -q*X*Y;
+    dpar[5] = +r;
+    dpar[6] = +1;
+    dpar[7] = -par[5]*SQ(r)*z;
+  }
+  return (f);
+}
+
Index: /branches/sc_branches/pantasks_condor/cmd.astro/imfit-qrgauss.c
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.astro/imfit-qrgauss.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.astro/imfit-qrgauss.c	(revision 34783)
@@ -0,0 +1,110 @@
+# include "imfit.h"
+
+opihi_flt qrgaussTD (opihi_flt, opihi_flt, opihi_flt *, int, opihi_flt *);
+void  qrgaussCL ();
+
+void qrgauss_setup (char *name) {
+
+  if (strcmp(name, "qrgauss")) return;
+
+  fitfunc = qrgaussTD;
+  imfit_cleanup = qrgaussCL;
+  Npar = 8;
+  Nfpar = 1;
+
+  /* allocate free and fixed parameters */
+  ALLOCATE (par, opihi_flt, MAX (Npar, 1));
+  bzero (par, Npar*sizeof(opihi_flt));
+  ALLOCATE (fpar, opihi_flt, MAX (Nfpar, 1));
+  bzero (fpar, Nfpar*sizeof(opihi_flt));
+
+  par[0]  = get_variable_default ("Xg", 0);
+  par[1]  = get_variable_default ("Yg", 0);
+  par[2]  = 2.35 / get_variable_default ("SXg", 2.0);
+  par[3]  = 2.35 / get_variable_default ("SYg", 2.0);
+  par[4]  = get_variable_default ("SXYg", 0);
+  par[5]  = get_variable_default ("Zpk", 10000);
+  par[6]  = get_variable_default ("Sg", 0.0);
+  par[7]  = get_variable_default ("Npow", 2.25);
+
+  fpar[0] = get_variable_default ("Sr", 1.0);
+
+  sky = &par[6];
+}
+
+void qrgaussCL () {
+  set_variable ("Xg",   par[0]);
+  set_variable ("Yg",   par[1]);
+  set_variable ("SXg",  2.35 / par[2]);
+  set_variable ("SYg",  2.35 / par[3]);
+  set_variable ("SXYg", par[4]);
+  set_variable ("Zpk",  par[5]);
+  set_variable ("Sg",   par[6]);
+  set_variable ("Npow", par[7]);
+}
+
+opihi_flt qrgaussTD (opihi_flt x, opihi_flt y, opihi_flt *par, int Npar, opihi_flt *dpar) {
+
+  opihi_flt X, Y, px, py;
+  opihi_flt z, r, q, f;
+
+  X = x - par[0];
+  Y = y - par[1];
+  
+  px = par[2]*X;
+  py = par[3]*Y;
+
+  z = 0.5*SQ(px) + 0.5*SQ(py) + par[4]*X*Y;
+
+  r = 1.0 / (1 + fpar[0]*z + pow(z,par[7]));
+  f = par[5]*r + par[6];
+  q = par[5]*SQ(r)*(fpar[0] + par[7]*pow(z,(par[7]-1)));
+
+  if (dpar != NULL) {
+    dpar[0] = q*(2*px*par[2] + par[4]*Y);
+    dpar[1] = q*(2*py*par[3] + par[4]*X);
+    dpar[2] = -2*q*px*X*2;
+    dpar[3] = -2*q*py*Y*2;
+    dpar[4] = -q*X*Y;
+    dpar[5] = +r;
+    dpar[6] = +1;
+    dpar[7] = -5*par[5]*SQ(r)*log(z)*pow(z,par[7]);
+  }
+  return (f);
+}
+
+
+# if (0)
+/* one component, two slopes: (1 + z^M + z^N)^(-1) -- x, y, sx, sy, sxy, I, sky, sr */
+opihi_flt qrgaussTD (opihi_flt x, opihi_flt y, opihi_flt *par, int Npar, opihi_flt *dpar) {
+
+  opihi_flt X, Y, px, py;
+  opihi_flt z, r, q, f;
+
+  /* if (par[7] < 1.0) par[7] = 1.0; */
+
+  X = x - par[0];
+  Y = y - par[1];
+  
+  px = par[2]*X;
+  py = par[3]*Y;
+
+  z = 0.5*SQ(px) + 0.5*SQ(py) + par[4]*X*Y;
+
+  r = 1.0 / (1 + fpar[0]*z + pow(z,par[7]) + 0.05*z*z*z);
+  f = par[5]*r + par[6];
+  q = par[5]*SQ(r)*(fpar[0] + par[7]*pow(z,(par[7]-1)) + 0.15*z*z);
+
+  if (dpar != NULL) {
+    dpar[0] = q*(2*px*par[2] + par[4]*Y);
+    dpar[1] = q*(2*py*par[3] + par[4]*X);
+    dpar[2] = -2*q*px*X*2;
+    dpar[3] = -2*q*py*Y*2;
+    dpar[4] = -q*X*Y;
+    dpar[5] = +r;
+    dpar[6] = +1;
+    dpar[7] = -5*par[5]*SQ(r)*log(z)*pow(z,par[7]);
+  }
+  return (f);
+}
+# endif
Index: /branches/sc_branches/pantasks_condor/cmd.astro/imfit-rgauss.c
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.astro/imfit-rgauss.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.astro/imfit-rgauss.c	(revision 34783)
@@ -0,0 +1,86 @@
+# include "imfit.h"
+
+opihi_flt rgaussTD (opihi_flt, opihi_flt, opihi_flt *, int, opihi_flt *);
+void  rgaussCL ();
+
+void rgauss_setup (char *name) {
+
+  if (strcmp(name, "rgauss")) return;
+
+  fitfunc = rgaussTD;
+  cleanup = rgaussCL;
+  Npar = 10;
+  Nfpar = 1;
+
+  /* allocate free and fixed parameters */
+  ALLOCATE (par, opihi_flt, MAX (Npar, 1));
+  bzero (par, Npar*sizeof(opihi_flt));
+  ALLOCATE (fpar, opihi_flt, MAX (Nfpar, 1));
+  bzero (fpar, Nfpar*sizeof(opihi_flt));
+
+  par[0] = get_variable_default ("Xg", 0);
+  par[1] = get_variable_default ("Yg", 0);
+  par[2] = 2.35 * sqrt(2.0) / get_variable_default ("SXg", 2.0);
+  par[3] = 2.35 * sqrt(2.0) / get_variable_default ("SYg", 2.0);
+  par[4] = 0.0;
+  par[5] = get_variable_default ("Zpk", 10000);
+  par[6] = get_variable_default ("Sg", 0.0);
+  par[7] = 2.35 * sqrt(2.0) / get_variable_default ("SXf", 15.0);
+  par[8] = 2.35 * sqrt(2.0) / get_variable_default ("SYf", 15.0);
+  par[9] = get_variable_default ("SXYf", 0.0);
+
+  fpar[0] = get_variable_default ("Npow", 2.25);
+
+  sky = &par[6];
+}
+
+/* two components: (1 + z_1 + 0.5*z_1^2 + z_2^N)^(-1) -- x, y, sx1, sy1, sxy1, I, sky, sx2, sy2, sxy2 */
+opihi_flt rgaussTD (opihi_flt x, opihi_flt y, opihi_flt *par, int Npar, opihi_flt *dpar) {
+
+  opihi_flt X, Y, px1, py1, px2, py2;
+  opihi_flt z1, z2, r, q1, q2, f;
+
+  X = x - par[0];
+  Y = y - par[1];
+  
+  px1 = par[2]*X;
+  py1 = par[3]*Y;
+  px2 = par[7]*X;
+  py2 = par[8]*Y;
+
+  z1 = 0.5*SQ(px1) + 0.5*SQ(py1) + par[4]*X*Y;
+  z2 = 0.5*SQ(px2) + 0.5*SQ(py2) + par[9]*X*Y;
+
+  r = 1.0 / (1 + z1 + 0.5*SQ(z1)+ pow(z2,fpar[0]));
+  f = par[5]*r + par[6];
+
+  q1 = par[5]*SQ(r)*(1 + z1);
+  q2 = par[5]*SQ(r)*fpar[0]*pow(z2,(fpar[0]-1));
+
+  if (dpar != NULL) {
+    dpar[0] = q1*(2*px1*par[2] + par[4]*Y) + q2*(2*px2*par[7] + par[9]*Y);
+    dpar[1] = q1*(2*py1*par[3] + par[4]*X) + q2*(2*py2*par[8] + par[9]*X);
+    dpar[2] = -2*q1*px1*X;
+    dpar[3] = -2*q1*py1*Y;
+    dpar[4] = -q1*X*Y;
+    dpar[5] = +r;
+    dpar[6] = +1;
+    dpar[7] = -2*q2*px2*X;
+    dpar[8] = -2*q2*py2*Y;
+    dpar[9] = -q2*X*Y;
+  }
+  return (f);
+}
+
+int rgaussCL () {
+  set_variable ("Xg",   par[0]);
+  set_variable ("Yg",   par[1]);
+  set_variable ("SXg",  2.35 * sqrt(2.0) / par[2]);
+  set_variable ("SYg",  2.35 * sqrt(2.0) / par[3]);
+  set_variable ("SXYg", par[4]);
+  set_variable ("Zpk",  par[5]);
+  set_variable ("Sg",   par[6]);
+  set_variable ("SXf", 2.35 * sqrt(2.0) / par[7]);
+  set_variable ("SYf", 2.35 * sqrt(2.0) / par[8]);
+  set_variable ("SXYf", par[9]);
+}
Index: /branches/sc_branches/pantasks_condor/cmd.astro/imfit-serbulge.c
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.astro/imfit-serbulge.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.astro/imfit-serbulge.c	(revision 34783)
@@ -0,0 +1,102 @@
+# include "imfit.h"
+
+opihi_flt serbulgeTD (opihi_flt, opihi_flt, opihi_flt *, int, opihi_flt *);
+void  serbulgeCL ();
+
+void serbulge_setup (char *name) {
+
+  if (strcmp(name, "serbulge")) return;
+
+  fitfunc = serbulgeTD;
+  cleanup = serbulgeCL;
+  Npar = 12;
+  Nfpar = 0;
+
+  /* allocate free and fixed parameters */
+  ALLOCATE (par, opihi_flt, MAX (Npar, 1));
+  bzero (par, Npar*sizeof(opihi_flt));
+  ALLOCATE (fpar, opihi_flt, MAX (Nfpar, 1));
+  bzero (fpar, Nfpar*sizeof(opihi_flt));
+
+  par[0] = get_variable_default ("Xg", 0);
+  par[1] = get_variable_default ("Yg", 0);
+  par[2] = 2.35 * sqrt(2.0) / get_variable_default ("SXg", 2.0);
+  par[3] = 2.35 * sqrt(2.0) / get_variable_default ("SYg", 2.0);
+  par[4] = 0.0;
+  par[5] = get_variable_default ("Zpk", 10000) / 2.0;
+
+  par[6] = get_variable_default ("Sg", 0.0);
+
+  par[7] = 2.35 * sqrt(2.0) / get_variable_default ("SXf", 15.0);
+  par[8] = 2.35 * sqrt(2.0) / get_variable_default ("SYf", 15.0);
+  par[9] = get_variable_default ("SXYf", 0.0);
+  par[10] = get_variable_default ("Zpk", 10000) / 2.0;
+
+  par[11] = get_variable_default ("Sr", 1.0);
+
+  sky = &par[6];
+}
+
+/*                                  0  1    2   3   4      5    6     7   8   9       10  11 */
+/* sersic galaxy model w/ bulge: -- x, y, (sx, sy, sxy)_1, I_1, sky, (sx, sy, sxy)_2, I_2, n */
+/* exp (-b (r/r_e)^(1/n)) + pgauss (r) */
+opihi_flt serbulgeTD (opihi_flt x, opihi_flt y, opihi_flt *par, int Npar, opihi_flt *dpar) {
+
+  opihi_flt X, Y, px1, px2, py1, py2;
+  opihi_flt z1, z2, r1, r2, t, q1, q2, f;
+
+  X = x - par[0];
+  Y = y - par[1];
+  
+  px1 = par[2]*X;
+  py1 = par[3]*Y;
+  px2 = par[7]*X;
+  py2 = par[8]*Y;
+
+  z1 = 0.5*SQ(px1) + 0.5*SQ(py1) + par[4]*X*Y;
+  z2 = 0.5*SQ(px2) + 0.5*SQ(py2) + par[9]*X*Y;
+
+  /* bulge component */
+  r1 = 1.0 / (1 + z1 + 0.5*z1*z1*(1 + z1/3)); /* ~ exp (-Z) */
+
+  /* disk component */
+  t = pow (z2, par[11]);
+  r2 = exp (-t);
+
+  f = par[5]*r1 + par[10]*r2 + par[6];
+
+  q1 = par[5]*r1*r1*(1 + z1 + 0.5*z1*z1);
+  q2 = par[10]*r2*par[11]*pow(z2, par[11]-1);
+
+  if (dpar != NULL) {
+    dpar[0] = q1*(2*px1*par[2] + par[4]*Y) + q2*(2*px2*par[7] + par[9]*Y);
+    dpar[1] = q1*(2*py1*par[3] + par[4]*X) + q2*(2*py2*par[8] + par[9]*X);
+    dpar[2] = -2*q1*px1*X;
+    dpar[3] = -2*q1*py1*Y;
+    dpar[4] = -q1*X*Y;
+    dpar[5] = +r1;
+    dpar[6] = +1;
+    dpar[7] = -2*q2*px2*X*50;
+    dpar[8] = -2*q2*py2*Y*50;
+    dpar[9] = -q2*X*Y*50;
+    dpar[10] = +r2*50;
+    dpar[11] = -q2*log(z2)*t*50;
+  }
+  return (f);
+}
+
+int serbulgeCL () {
+  set_variable ("Xg",   par[0]);
+  set_variable ("Yg",   par[1]);
+  set_variable ("SXg",  2.35 * sqrt(2.0) / par[2]);
+  set_variable ("SYg",  2.35 * sqrt(2.0) / par[3]);
+  set_variable ("SXYg", par[4]);
+  set_variable ("Zb",   par[5]);
+  set_variable ("Sg",   par[6]);
+
+  set_variable ("SXf",  2.35 * sqrt(2.0) / par[7]);
+  set_variable ("SYf",  2.35 * sqrt(2.0) / par[8]);
+  set_variable ("SXYf", par[9]);
+  set_variable ("Zd",   par[10]);
+  set_variable ("Sr",   par[11]);
+}
Index: /branches/sc_branches/pantasks_condor/cmd.astro/imfit-sersic.c
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.astro/imfit-sersic.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.astro/imfit-sersic.c	(revision 34783)
@@ -0,0 +1,74 @@
+# include "imfit.h"
+
+opihi_flt sersicTD (opihi_flt, opihi_flt, opihi_flt *, int, opihi_flt *);
+void  sersicCL ();
+
+void sersic_setup (char *name) {
+
+  if (strcmp(name, "sersic")) return;
+
+  fitfunc = sersicTD;
+  cleanup = sersicCL;
+  Npar = 8;
+  Nfpar = 0;
+
+  /* allocate free and fixed parameters */
+  ALLOCATE (par, opihi_flt, MAX (Npar, 1));
+  bzero (par, Npar*sizeof(opihi_flt));
+  ALLOCATE (fpar, opihi_flt, MAX (Nfpar, 1));
+  bzero (fpar, Nfpar*sizeof(opihi_flt));
+
+  par[0] = get_variable_default ("Xg", 0);
+  par[1] = get_variable_default ("Yg", 0);
+  par[2] = 2.35 * sqrt(2.0) / get_variable_default ("SXg", 2.0);
+  par[3] = 2.35 * sqrt(2.0) / get_variable_default ("SYg", 2.0);
+  par[4] = 0.0;
+  par[5] = get_variable_default ("Zpk", 10000);
+  par[6] = get_variable_default ("Sg", 0.0);
+  par[7] = get_variable_default ("Sr", 1.0);
+
+  sky = &par[6];
+}
+
+/* sersic galaxy model -- x, y, sx, sy, sxy, I, sky, n */
+/* exp (-b (r/r_e)^(1/n)) */
+opihi_flt sersicTD (opihi_flt x, opihi_flt y, opihi_flt *par, int Npar, opihi_flt *dpar) {
+
+  opihi_flt X, Y, px, py;
+  opihi_flt z, r, t, q, f;
+
+  X = x - par[0];
+  Y = y - par[1];
+  
+  px = par[2]*X;
+  py = par[3]*Y;
+
+  z = 0.5*SQ(px) + 0.5*SQ(py) + par[4]*X*Y;
+  t = pow (z, par[7]);
+  r = exp (-t);
+  f = par[5]*r + par[6];
+  q = par[5]*r*par[7]*pow(z, par[7]-1);
+
+  if (dpar != NULL) {
+    dpar[0] = q*(2*px*par[2] + par[4]*Y);
+    dpar[1] = q*(2*py*par[3] + par[4]*X);
+    dpar[2] = -2*q*px*X;
+    dpar[3] = -2*q*py*Y;
+    dpar[4] = -q*X*Y;
+    dpar[5] = +r;
+    dpar[6] = +1;
+    dpar[7] = -q*log(z)*t;
+  }
+  return (f);
+}
+
+int sersicCL () {
+  set_variable ("Xg",   par[0]);
+  set_variable ("Yg",   par[1]);
+  set_variable ("SXg",  2.35 * sqrt(2.0) / par[2]);
+  set_variable ("SYg",  2.35 * sqrt(2.0) / par[3]);
+  set_variable ("SXYg", par[4]);
+  set_variable ("Zpk",  par[5]);
+  set_variable ("Sg",   par[6]);
+  set_variable ("Sr", par[7]);
+}
Index: /branches/sc_branches/pantasks_condor/cmd.astro/imfit-sgauss-psf.c
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.astro/imfit-sgauss-psf.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.astro/imfit-sgauss-psf.c	(revision 34783)
@@ -0,0 +1,74 @@
+# include "imfit.h"
+
+opihi_flt sgauss_psfTD (opihi_flt, opihi_flt, opihi_flt *, int, opihi_flt *);
+void  sgauss_psfCL ();
+
+void sgauss_psf_setup (char *name) {
+
+  if (strcmp(name, "sgauss_psf")) return;
+
+  fitfunc = sgauss_psfTD;
+  imfit_cleanup = sgauss_psfCL;
+  Npar = 4;
+  Nfpar = 7;
+
+  /* allocate free and fixed parameters */
+  ALLOCATE (par, opihi_flt, MAX (Npar, 1));
+  bzero (par, Npar*sizeof(opihi_flt));
+  ALLOCATE (fpar, opihi_flt, MAX (Nfpar, 1));
+  bzero (fpar, Nfpar*sizeof(opihi_flt));
+
+  par[0] = get_variable_default ("Xg", 0);
+  par[1] = get_variable_default ("Yg", 0);
+  par[2] = get_variable_default ("Zpk", 10000);
+  par[3] = get_variable_default ("Sg", 0.0);
+
+  fpar[0] = 2.35 / get_variable_default ("SXg", 15.0);
+  fpar[1] = 2.35 / get_variable_default ("SYg", 15.0);
+  fpar[2] = get_variable_default ("SXYg", 0.0);
+  fpar[3] = 2.35 / get_variable_default ("SXf", 15.0);
+  fpar[4] = 2.35 / get_variable_default ("SYf", 15.0);
+  fpar[5] = get_variable_default ("SXYf", 0.0);
+  fpar[6] = get_variable_default ("Npow", 2.25);
+
+  sky = &par[3];
+}
+
+void sgauss_psfCL () {
+  set_variable ("Xg",   par[0]);
+  set_variable ("Yg",   par[1]);
+  set_variable ("Zpk",  par[2]);
+  set_variable ("Sg",   par[3]);
+}
+
+/* two components: (1 + z_1 + z_2^N)^(-1) -- x, y, sx1, sy1, sxy1, I, sky, sx2, sy2, sxy2 */
+opihi_flt sgauss_psfTD (opihi_flt x, opihi_flt y, opihi_flt *par, int Npar, opihi_flt *dpar) {
+
+  opihi_flt X, Y, px1, py1, px2, py2;
+  opihi_flt z1, z2, r, q1, q2, f;
+
+  X = x - par[0];
+  Y = y - par[1];
+  
+  px1 = fpar[0]*X;
+  py1 = fpar[1]*Y;
+  px2 = fpar[3]*X;
+  py2 = fpar[4]*Y;
+
+  z1 = 0.5*SQ(px1) + 0.5*SQ(py1) + fpar[2]*X*Y;
+  z2 = 0.5*SQ(px2) + 0.5*SQ(py2) + fpar[5]*X*Y;
+
+  r = 1.0 / (1 + z1 + pow(z2,fpar[6]));
+  f = par[2]*r + par[3];
+
+  q1 = par[2]*SQ(r);
+  q2 = par[2]*SQ(r)*fpar[6]*pow(z2,(fpar[6]-1));
+
+  if (dpar != NULL) {
+    dpar[0] = q1*(2*px1*fpar[0] + fpar[2]*Y) + q2*(2*px2*fpar[3] + fpar[5]*Y);
+    dpar[1] = q1*(2*py1*fpar[1] + fpar[2]*X) + q2*(2*py2*fpar[4] + fpar[5]*X);
+    dpar[2] = +r;
+    dpar[3] = +1;
+  }
+  return (f);
+}
Index: /branches/sc_branches/pantasks_condor/cmd.astro/imfit-sgauss.c
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.astro/imfit-sgauss.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.astro/imfit-sgauss.c	(revision 34783)
@@ -0,0 +1,98 @@
+# include "imfit.h"
+# define FFACTOR 200
+# define FSCALE 1.2
+
+opihi_flt sgaussTD (opihi_flt, opihi_flt, opihi_flt *, int, opihi_flt *);
+void  sgaussCL ();
+
+void sgauss_setup (char *name) {
+
+  if (strcmp(name, "sgauss")) return;
+
+  fitfunc = sgaussTD;
+  imfit_cleanup = sgaussCL;
+  Npar = 10;
+  Nfpar = 2;
+
+  /* allocate free and fixed parameters */
+  ALLOCATE (par, opihi_flt, MAX (Npar, 1));
+  bzero (par, Npar*sizeof(opihi_flt));
+  ALLOCATE (fpar, opihi_flt, MAX (Nfpar, 1));
+  bzero (fpar, Nfpar*sizeof(opihi_flt));
+
+  par[0] = get_variable_default ("Xg", 0);
+  par[1] = get_variable_default ("Yg", 0);
+  par[2] = 2.35 / get_variable_default ("SXg", 2.0);
+  par[3] = 2.35 / get_variable_default ("SYg", 2.0);
+  par[4] = get_variable_default ("SXYg", 0);
+  par[5] = get_variable_default ("Zpk", 10000);
+  par[6] = get_variable_default ("Sg", 0.0);
+  par[7] = 2.35 / get_variable_default ("SXf", 15.0);
+  par[8] = 2.35 / get_variable_default ("SYf", 15.0);
+  par[9] = get_variable_default ("SXYf", 0.0);
+
+  fpar[0] = get_variable_default ("Npow", 2.25);
+  fpar[1] = get_variable_default ("Npin", 1.00); // drop this?
+
+  sky = &par[6];
+}
+
+void sgaussCL () {
+  set_variable ("Xg",   par[0]);
+  set_variable ("Yg",   par[1]);
+  set_variable ("SXg",  2.35 / par[2]);
+  set_variable ("SYg",  2.35 / par[3]);
+  set_variable ("SXYg", par[4]);
+  set_variable ("Zpk",  par[5]);
+  set_variable ("Sg",   par[6]);
+  set_variable ("SXf", 2.35 / par[7]);
+  set_variable ("SYf", 2.35 / par[8]);
+  set_variable ("SXYf", par[9]);
+}
+
+/* two components: (1 + z_1 + z_2^N)^(-1) -- x, y, sx1, sy1, sxy1, I, sky, sx2, sy2, sxy2 */
+opihi_flt sgaussTD (opihi_flt x, opihi_flt y, opihi_flt *par, int Npar, opihi_flt *dpar) {
+
+  opihi_flt X, Y, px1, py1, px2, py2;
+  opihi_flt z1, z2, r, q1, q2, f, f1, f2;
+
+  X = x - par[0];
+  Y = y - par[1];
+  
+  px1 = par[2]*X;
+  py1 = par[3]*Y;
+  px2 = par[7]*X;
+  py2 = par[8]*Y;
+
+  z1 = 0.5*SQ(px1) + 0.5*SQ(py1) + par[4]*X*Y;
+  z2 = 0.5*SQ(px2) + 0.5*SQ(py2) + par[9]*X*Y;
+
+  r = 1.0 / (1 + z1 + pow(z2,fpar[0]));
+  f = par[5]*r + par[6];
+
+  q1 = par[5]*SQ(r);
+  q2 = par[5]*SQ(r)*fpar[0]*pow(z2,(fpar[0]-1));
+
+  if (dpar != NULL) {
+    dpar[0] = q1*(2*px1*par[2] + par[4]*Y) + q2*(2*px2*par[7] + par[9]*Y);
+    dpar[1] = q1*(2*py1*par[3] + par[4]*X) + q2*(2*py2*par[8] + par[9]*X);
+
+    /* these fudge factors impede the growth of par[2] beyond par[7] */
+    f1 = fabs(par[7]) / fabs(par[2]);
+    f2 = (f1 < FSCALE) ? 1 : FFACTOR*(f1 - FSCALE) + 1;
+    dpar[2] = -2*q1*px1*X*f2;
+
+    f1 = fabs(par[8]) / fabs(par[3]);
+    f2 = (f1 < FSCALE) ? 1 : FFACTOR*(f1 - FSCALE) + 1;
+    dpar[3] = -2*q1*py1*Y*f2;
+
+    dpar[4] = -q1*X*Y;
+    dpar[5] = +r;
+    dpar[6] = +1;
+    dpar[7] = -2*q2*px2*X;
+    dpar[8] = -2*q2*py2*Y;
+    dpar[9] = -q2*X*Y;
+  }
+  return (f);
+}
+
Index: /branches/sc_branches/pantasks_condor/cmd.astro/imfit-test.c
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.astro/imfit-test.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.astro/imfit-test.c	(revision 34783)
@@ -0,0 +1,314 @@
+  if (ShapeVariation) {
+    /* find dChi/dSx and dChi/dSy given by increasing shape terms by 5% */
+    float tp1, tp2, chix, chiy;
+    chix = chiy = 0;
+    if (fitfunc == sgaussTD) {
+      tp1 = par[2];
+      tp2 = par[7];
+      par[2] = par[2]*1.05;
+      par[7] = par[7]*1.05;
+      chix = mrq2dchi (x, y, z, dz, Npts, par, Npar, fitfunc) - chisq;
+      par[2] = tp1;
+      par[7] = tp2;
+
+      tp1 = par[3];
+      tp2 = par[8];
+      par[3] = par[3]*1.05;
+      par[8] = par[8]*1.05;
+      chiy = mrq2dchi (x, y, z, dz, Npts, par, Npar, fitfunc) - chisq;
+      par[3] = tp1;
+      par[8] = tp2;
+    }
+    if (fitfunc == pgaussTD) {
+      tp1 = par[2];
+      par[2] = par[2]*1.05;
+      chix = mrq2dchi (x, y, z, dz, Npts, par, Npar, fitfunc) - chisq;
+      par[2] = tp1;
+
+      tp1 = par[3];
+      par[3] = par[3]*1.05;
+      chiy = mrq2dchi (x, y, z, dz, Npts, par, Npar, fitfunc) - chisq;
+      par[3] = tp1;
+    }
+    if (fitfunc == sgauss_psfTD) {
+      tp1 = par[0];
+      tp2 = par[3];
+      par[0] = par[0]*1.05;
+      par[3] = par[3]*1.05;
+      chix = mrq2dchi (x, y, z, dz, Npts, par, Npar, fitfunc) - chisq;
+      par[0] = tp1;
+      par[3] = tp2;
+
+      tp1 = par[1];
+      tp2 = par[4];
+      par[1] = par[1]*1.05;
+      par[4] = par[4]*1.05;
+      chiy = mrq2dchi (x, y, z, dz, Npts, par, Npar, fitfunc) - chisq;
+      par[1] = tp1;
+      par[4] = tp2;
+    }
+    if (fitfunc == pgauss_psfTD) {
+      tp1 = par[0];
+      par[0] = par[0]*1.05;
+      chix = mrq2dchi (x, y, z, dz, Npts, par, Npar, fitfunc) - chisq;
+      par[0] = tp1;
+
+      tp1 = par[1];
+      par[1] = par[1]*1.05;
+      chiy = mrq2dchi (x, y, z, dz, Npts, par, Npar, fitfunc) - chisq;
+      par[1] = tp1;
+    }
+    set_variable ("dChiX", chix/chisq);
+    set_variable ("dChiY", chiy/chisq);
+  }
+
+# if (0)
+/* pars: x, y, sx, sy, sxy, I, sky */
+float fgalaxyTD (float x, float y, float *par, int Npar, float *dpar) {
+
+  float X, Y, Z, E, F, q, R, f, p2, p3;
+
+  X = x - par[0];
+  Y = y - par[1];
+  
+  p2 = X / par[2];
+  p3 = Y / par[3];
+
+  Z = sqrt (0.5*p2*X + X*Y*par[4] + 0.5*p3*Y);                 /* R */
+  E = 1.0 / (1 + Z);   
+
+  q = par[5] * E;
+  R = q*E;
+  F = 0.5 / Z;
+  
+  f = q + par[6];
+
+  dpar[0] = F*R*(p2 + par[4]*Y);
+  dpar[1] = F*R*(p3 + par[4]*X);
+  dpar[2] = F*0.5*R*p2*p2;
+  dpar[3] = F*0.5*R*p3*p3;
+  dpar[4] = -R*X*Y*F;
+    
+  dpar[5] = E;
+  dpar[6] = 1;
+  return (f);
+}
+
+/* pars: x, y, sx, sy, sxy, I, sky */
+float fbarTD (float x, float y, float *par, int Npar, float *dpar) {
+
+  float X, Y, Z, E, F, q, R, f, p2, p3;
+
+  X = x - par[0];
+  Y = y - par[1];
+  
+  p2 = X / par[2];
+  p3 = Y / par[3];
+
+  Z = 0.5*p2*X + X*Y*par[4] + 0.5*p3*Y;                 /* R */
+  E = 1.0 / (1 + Z*Z*Z);   
+
+  q = par[5] * E;
+  F = 3*Z*Z;
+  R = q*E*F;
+  
+  f = q + par[6];
+
+  dpar[0] = R*(p2 + par[4]*Y);
+  dpar[1] = R*(p3 + par[4]*X);
+  dpar[2] = 0.5*R*p2*p2;
+  dpar[3] = 0.5*R*p3*p3;
+  dpar[4] = -R*X*Y;
+    
+  dpar[5] = E;
+  dpar[6] = 1;
+  return (f);
+}
+
+/* convert from x,y to major,minor */
+void fix_ellipsegauss_pars (float *par, int Npar) {
+
+  float p2, p4, angle, t1, t2, tmp, area;
+
+  /* par[0], par[1] = Xo, Yo - stay the same */
+
+  p2 = 1/par[2];
+  p4 = 1/par[3];
+
+  angle = 0.5 * atan2 (-2*par[4], p4 - p2); 
+
+  tmp = sqrt (SQ(p2 - p4) + 4*SQ(par[4]));
+  t1 = (p2 + p4 + tmp) / 2;
+  t2 = t1 - tmp;
+
+  par[2] = 2.35482*sqrt(1/t2);
+  par[3] = 2.35482*sqrt(1/t1);
+  par[4] = DEG_RAD * angle;
+
+  area = 2*M_PI/sqrt(t1*t2);
+
+  par[5] *= area;
+
+}
+# endif
+
+/***  options for later
+
+  Subtract = FALSE;
+  if ((N = get_argument (argc, argv, "-sub"))) {
+    remove_argument (N, &argc, argv);
+    Subtract  = TRUE;
+  }
+
+  DFact = 1;
+  if ((N = get_argument (argc, argv, "-D"))) {
+    remove_argument (N, &argc, argv);
+    DFact  = atof(argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  fitfunc = fgaussTD;
+  if ((N = get_argument (argc, argv, "-gal"))) {
+    remove_argument (N, &argc, argv);
+    fitfunc = fgalaxyTD; 
+  }
+  if ((N = get_argument (argc, argv, "-bar"))) {
+    remove_argument (N, &argc, argv);
+    fitfunc = fbarTD; 
+  }
+
+
+  f1 = 1;
+  if ((c = get_variable ("BETA1")) != (char *) NULL) f1 = atof (c);
+
+  f2 = 1;
+  if ((c = get_variable ("BETA2")) != (char *) NULL) f2 = atof (c);
+
+  if (Subtract) {
+    tmpsky = par[6];
+    par[6] = 0;
+    for (N = j = 0; j < ny; j++) {
+      V = (float *)(buf[0].matrix.buffer) + (j+sy)*buf[0].matrix.Naxis[0] + sx; 
+      for (i = 0; i < nx; i++, V++, N++) {
+	dx = i + sx;
+	dy = j + sy;
+	*V -= fitfunc (dx, dy, par, Npar, (float *) NULL);
+      }
+    }
+    par[6] = tmpsky;
+  }
+
+***/
+
+# if (0)
+
+/* these two tests were not very succcessful.  the first did not model the shape well because 
+   it could not match the change in roundness with radius.  the second did not work because the 
+   parameters were degenerate (amplitude and slope of second component) */
+
+/* test: fixed, non-integer higher-order term -- x, y, sx, sy, sxy, I, sky, f1, f2 */
+float qgaussTD (float x, float y, float *par, int Npar, float *dpar) {
+
+  float X, Y, px, py;
+  float z, r, q, f, k;
+
+  X = x - par[0];
+  Y = y - par[1];
+  
+  px = par[2]*X;
+  py = par[3]*Y;
+
+  z = 0.5*SQ(px) + 0.5*SQ(py) + par[4]*X*Y;
+  k = pow(z,1.75*par[8]);
+  r = 1.0 / (1 + z + par[7]*k); /* ~ exp (-Z) */
+  q = par[5]*r*r*(1 + 1.75*par[7]*par[8]*pow(z,1.75*par[8]-1));
+  /* note difference from gaussian: q = par[5]*r */
+  f = par[5]*r + par[6];
+
+  dpar[0] = q*(2*px*par[2] + par[4]*Y);
+  dpar[1] = q*(2*py*par[3] + par[4]*X);
+  dpar[2] = -2*q*px*X;
+  dpar[3] = -2*q*py*Y;
+  dpar[4] = -q*X*Y;
+  dpar[5] = +r;
+  dpar[6] = +1;
+  dpar[7] = -10*par[5]*r*r*k;
+  dpar[8] = -10*par[5]*r*r*par[7]*k*1.75*log(z);
+
+  return (f);
+}
+
+/* test: two component model: inner pseudo gaussian with outer z^1.75 x, y, sx, sy, sxy, I, sky */
+float rgaussTD (float x, float y, float *par, int Npar, float *dpar) {
+
+  float X, Y, px1, py1, px2, py2;
+  float z1, z2, r1, r2, q1, q2, f;
+
+  X = x - par[0];
+  Y = y - par[1];
+  
+  px1 = par[2]*X;
+  py1 = par[3]*Y;
+  px2 = par[8]*X;
+  py2 = par[9]*Y;
+
+  z1 = 0.5*SQ(px1) + 0.5*SQ(py1) + par[4]*X*Y;
+  z2 = 0.5*SQ(px2) + 0.5*SQ(py2) + par[10]*X*Y;
+
+  r1 = 1.0 / (1 + z1 + 0.5*SQ(z1)*(1 + z1/3)); /* ~ exp (-Z) */
+  r2 = 1.0 / (1 + pow(z2,1.75));
+
+  f = par[5]*r1 + par[6] + par[7]*r2;
+
+  q1 = par[5]*SQ(r1)*(1 + z1 + 0.5*SQ(z1));
+  q2 = par[7]*SQ(r2)*(1.75*pow(z2,0.75));
+
+  dpar[	0] = q1*(2*px1*par[2] + par[4]*Y) + q2*(2*px2*par[8] + par[10]*Y);
+  dpar[	1] = q1*(2*py1*par[3] + par[4]*X) + q2*(2*py2*par[9] + par[10]*X);
+  dpar[	2] = -2*q1*px1*X;
+  dpar[	3] = -2*q1*py1*Y;
+  dpar[	4] = -q1*X*Y;
+  dpar[	5] = +r1;
+  dpar[	6] = +1;
+  dpar[	7] = +r2*2;
+  dpar[	8] = -2*q2*px2*X*2;
+  dpar[	9] = -2*q2*py2*Y*2;
+  dpar[10] = -q2*X*Y;
+
+  return (f);
+}
+
+# endif
+
+  /* forcing values to have a rational range
+  ALLOCATE (parmin, float, Npar);
+  ALLOCATE (parmax, float, Npar);
+  bzero (parmin, Npar*sizeof(float));
+  bzero (parmax, Npar*sizeof(float));
+  parmin[0] = parmin[1] = 0;
+  parmax[0] = buf[0].matrix.Naxis[0];
+  parmax[1] = buf[0].matrix.Naxis[1];
+
+  parmin[2] = parmin[3] = 0.01;
+  parmax[2] = parmax[3] = 100.0;
+  parmin[4] = -1000;
+  parmax[4] = -1000;
+  
+  parmin[5] = 1;
+  parmax[5] = 1e5;
+
+  parmin[6] = 0.0;
+  parmax[6] = 1e5;
+
+  if (Npar == 9) {
+    parmin[7] = parmin[8] = 0.01;
+    parmax[7] = parmax[8] = 10.0;
+  }
+  if (Npar == 10) {
+    parmin[7] = parmin[8] = 0.01;
+    parmax[7] = parmax[8] = 10.0;
+    parmin[9] = -1000;
+    parmax[9] = -1000;
+  }
+  */
+
Index: /branches/sc_branches/pantasks_condor/cmd.astro/imfit-tgauss.c
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.astro/imfit-tgauss.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.astro/imfit-tgauss.c	(revision 34783)
@@ -0,0 +1,81 @@
+# include "imfit.h"
+
+opihi_flt tgaussTD (opihi_flt, opihi_flt, opihi_flt *, int, opihi_flt *);
+void  tgaussCL ();
+
+void tgauss_setup (char *name) {
+
+  if (strcmp(name, "tgauss")) return;
+
+  fitfunc = tgaussTD;
+  cleanup = tgaussCL;
+  Npar = 10;
+  Nfpar = 2;
+
+  par[0] = get_variable_default ("Xg", 0);
+  par[1] = get_variable_default ("Yg", 0);
+  par[2] = 2.35 * sqrt(2.0) / get_variable_default ("SXg", 2.0);
+  par[3] = 2.35 * sqrt(2.0) / get_variable_default ("SYg", 2.0);
+  par[4] = 0.0;
+  par[5] = get_variable_default ("Zpk", 10000);
+  par[6] = get_variable_default ("Sg", 0.0);
+  par[7] = 2.35 * sqrt(2.0) / get_variable_default ("SXf", 15.0);
+  par[8] = 2.35 * sqrt(2.0) / get_variable_default ("SYf", 15.0);
+  par[9] = get_variable_default ("SXYf", 0.0);
+
+  fpar[0] = get_variable_default ("Npow", 2.25);
+  fpar[1] = get_variable_default ("Npin", 1.00); // drop this?
+
+  sky = &par[6];
+}
+
+/* two components: (1 + z_1^M + z_2^N)^(-1) -- x, y, sx1, sy1, sxy1, I, sky, sx2, sy2, sxy2 */
+opihi_flt tgaussTD (opihi_flt x, opihi_flt y, opihi_flt *par, int Npar, opihi_flt *dpar) {
+
+  opihi_flt X, Y, px1, py1, px2, py2;
+  opihi_flt z1, z2, r, q1, q2, f;
+
+  X = x - par[0];
+  Y = y - par[1];
+  
+  px1 = par[2]*X;
+  py1 = par[3]*Y;
+  px2 = par[7]*X;
+  py2 = par[8]*Y;
+
+  z1 = 0.5*SQ(px1) + 0.5*SQ(py1) + par[4]*X*Y;
+  z2 = 0.5*SQ(px2) + 0.5*SQ(py2) + par[9]*X*Y;
+
+  r = 1.0 / (1 + pow(z1,fpar[1]) + pow(z2,fpar[0]));
+  f = par[5]*r + par[6];
+
+  q1 = par[5]*SQ(r)*fpar[1]*pow(z1,(fpar[1]-1));
+  q2 = par[5]*SQ(r)*fpar[0]*pow(z2,(fpar[0]-1));
+
+  if (dpar != NULL) {
+    dpar[0] = q1*(2*px1*par[2] + par[4]*Y) + q2*(2*px2*par[7] + par[9]*Y);
+    dpar[1] = q1*(2*py1*par[3] + par[4]*X) + q2*(2*py2*par[8] + par[9]*X);
+    dpar[2] = -2*q1*px1*X*2;
+    dpar[3] = -2*q1*py1*Y*2;
+    dpar[4] = -q1*X*Y;
+    dpar[5] = +r;
+    dpar[6] = +1;
+    dpar[7] = -2*q2*px2*X;
+    dpar[8] = -2*q2*py2*Y;
+    dpar[9] = -q2*X*Y;
+  }
+  return (f);
+}
+
+int tgaussCL () {
+  set_variable ("Xg",   par[0]);
+  set_variable ("Yg",   par[1]);
+  set_variable ("SXg",  2.35 * sqrt(2.0) / par[2]);
+  set_variable ("SYg",  2.35 * sqrt(2.0) / par[3]);
+  set_variable ("SXYg", par[4]);
+  set_variable ("Zpk",  par[5]);
+  set_variable ("Sg",   par[6]);
+  set_variable ("SXf",  2.35 * sqrt(2.0) / par[7]);
+  set_variable ("SYf",  2.35 * sqrt(2.0) / par[8]);
+  set_variable ("SXYf", par[9]);
+}
Index: /branches/sc_branches/pantasks_condor/cmd.astro/imfit-vgauss.c
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.astro/imfit-vgauss.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.astro/imfit-vgauss.c	(revision 34783)
@@ -0,0 +1,70 @@
+# include "imfit.h"
+
+opihi_flt vgaussTD (opihi_flt, opihi_flt, opihi_flt *, int, opihi_flt *);
+void  vgaussCL ();
+
+void  vgauss_setup (char *name) {
+
+  if (strcmp(name, "vgauss")) return;
+
+  fitfunc = vgaussTD;
+  cleanup = vgaussCL;
+  Npar = 9;
+  Nfpar = 0;
+
+  par[0] = get_variable_default ("Xg", 0);
+  par[1] = get_variable_default ("Yg", 0);
+  par[2] = 2.35 * sqrt(2.0) / get_variable_default ("SXg", 2.0);
+  par[3] = 2.35 * sqrt(2.0) / get_variable_default ("SYg", 2.0);
+  par[4] = 0.0;
+  par[5] = get_variable_default ("Zpk", 10000);
+  par[6] = get_variable_default ("Sg", 0.0);
+  par[7] = 1;
+  par[8] = 1;
+  sky = &par[6];
+}
+
+/* pseudo 2D gaussian with opihi_flting 2nd and 3rd order terms -- x, y, sx, sy, sxy, I, sky, f1, f2 */
+opihi_flt vgaussTD (opihi_flt x, opihi_flt y, opihi_flt *par, int Npar, opihi_flt *dpar) {
+
+  opihi_flt X, Y, px, py;
+  opihi_flt z, r, q, f, k;
+
+  X = x - par[0];
+  Y = y - par[1];
+  
+  px = par[2]*X;
+  py = par[3]*Y;
+
+  z = 0.5*SQ(px) + 0.5*SQ(py) + par[4]*X*Y;
+  k = 0.5*z*z*(1 + par[8]*z/3);
+  r = 1.0 / (1 + z + par[7]*k); /* ~ exp (-Z) */
+  f = par[5]*r + par[6];
+  q = par[5]*r*r*(1 + par[7]*z*(1 + par[8]*z/2));
+  /* note difference from gaussian: q = par[5]*r */
+
+  if (dpar != NULL) {
+    dpar[0] = q*(2*px*par[2] + par[4]*Y);
+    dpar[1] = q*(2*py*par[3] + par[4]*X);
+    dpar[2] = -2*q*px*X;
+    dpar[3] = -2*q*py*Y;
+    dpar[4] = -q*X*Y;
+    dpar[5] = +r;
+    dpar[6] = +1;
+    dpar[7] = -100*par[5]*r*r*k;
+    dpar[8] = -100*par[5]*r*r*par[7]*(z*z*z)/6;
+  }
+  return (f);
+}
+
+int vgaussCL () {
+  set_variable ("Xg",   par[0]);
+  set_variable ("Yg",   par[1]);
+  set_variable ("SXg",  2.35 * sqrt(2.0) / par[2]);
+  set_variable ("SYg",  2.35 * sqrt(2.0) / par[3]);
+  set_variable ("SXYg", par[4]);
+  set_variable ("Zpk",  par[5]);
+  set_variable ("Sg",   par[6]);
+  set_variable ("SXf", par[7]);
+  set_variable ("SYf", par[8]);
+}
Index: /branches/sc_branches/pantasks_condor/cmd.astro/imfit.c
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.astro/imfit.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.astro/imfit.c	(revision 34783)
@@ -0,0 +1,186 @@
+# include "imfit.h"
+
+int imfit (int argc, char **argv) {
+
+  int i, j, N, Npts, Save, VERBOSE;
+  int sx, sy, nx, ny, Nx, Ny;
+  float chisq, ochisq, dchisq, Gain, RDnoise, SatThreshold;
+  opihi_flt *x, *y, *z, *dz;
+  float *V;
+  Buffer *buf;
+
+  Save = FALSE;
+  if ((N = get_argument (argc, argv, "-save"))) {
+    remove_argument (N, &argc, argv);
+    Save = TRUE;
+  }
+
+  // int ShapeVariation = FALSE;
+  // if ((N = get_argument (argc, argv, "-shapes"))) {
+  //   remove_argument (N, &argc, argv);
+  //   ShapeVariation = TRUE;
+  // }
+
+  SatThreshold = 0xffff;
+  if ((N = get_argument (argc, argv, "-sat"))) {
+    remove_argument (N, &argc, argv);
+    SatThreshold = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  /* Gain in e/DN */
+  Gain = 1.0;
+  if ((N = get_argument (argc, argv, "-gain"))) {
+    remove_argument (N, &argc, argv);
+    Gain = atof(argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  /* RD noise in DN */
+  RDnoise = 0.0;
+  if ((N = get_argument (argc, argv, "-rdnoise"))) {
+    remove_argument (N, &argc, argv);
+    RDnoise = atof(argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  VERBOSE = FALSE;
+  if ((N = get_argument (argc, argv, "-v"))) {
+    remove_argument (N, &argc, argv);
+    VERBOSE = TRUE;
+  }
+
+  /* set fitting function */
+  fgauss_setup ("fgauss");
+  if ((N = get_argument (argc, argv, "-func"))) {
+    fitfunc = NULL;
+    remove_argument (N, &argc, argv);
+    fgauss_setup (argv[N]);
+    pgauss_setup (argv[N]);
+    pgauss_psf_setup (argv[N]);
+    sgauss_setup (argv[N]);
+    sgauss_psf_setup (argv[N]);
+    qgauss_setup (argv[N]);
+    qgauss_psf_setup (argv[N]);
+    qfgauss_setup (argv[N]);
+    qrgauss_setup (argv[N]);
+    if (fitfunc == NULL) {
+      gprint (GP_ERR, "unknown function %s\n", argv[N]);
+      return (FALSE);
+    }
+    remove_argument (N, &argc, argv);
+  }
+
+  if (argc != 6) {
+    gprint (GP_ERR, "USAGE: imfit <buffer> sx sy nx ny\n");
+    return (FALSE);
+  }
+
+  /* non-optional arguments */
+  if ((buf = SelectBuffer (argv[1], OLDBUFFER, TRUE)) == NULL) return (FALSE);
+  sx = atof (argv[2]);
+  sy = atof (argv[3]);
+  nx = atof (argv[4]);
+  ny = atof (argv[5]);
+  Nx = buf[0].matrix.Naxis[0];
+  Ny = buf[0].matrix.Naxis[1];
+
+  /* check if region is valid */
+  if (sx + 0.5*nx < 0) goto range;
+  if (sy + 0.5*ny < 0) goto range;
+  if (sx + 0.5*nx >= Nx) goto range;
+  if (sy + 0.5*ny >= Ny) goto range;
+
+  /* convert array z[x,y] to x[i], y[i], z[i] */
+  N = 0;
+  Npts = nx*ny;
+  ALLOCATE (x,  opihi_flt, 2*Npts);
+  ALLOCATE (y,  opihi_flt, 2*Npts);
+  ALLOCATE (z,  opihi_flt, 2*Npts);
+  ALLOCATE (dz, opihi_flt, 2*Npts);
+  for (j = 0; j < ny; j++) {
+    if (j + sy < 0) continue;
+    if (j + sy >= Ny) continue;
+    V = (float *)(buf[0].matrix.buffer) + (j+sy)*buf[0].matrix.Naxis[0] + sx; 
+    for (i = 0; i < nx; i++) {
+      if (i + sx < 0) continue;
+      if (i + sx >= Nx) continue;
+      if (*V > SatThreshold) goto next;
+      dz[N] = (SQ(RDnoise) + *V/Gain);
+      if (dz[N] <= 0) goto next;
+      dz[N] = 1.0 / dz[N];
+      x[N] = i + sx;
+      y[N] = j + sy;
+      z[N] = *V;
+      N++;
+    next:
+      V++;
+    }
+  }
+  Npts = N;
+
+  /* run fit routine */
+  ochisq = mrq2dinit (x, y, z, dz, Npts, par, Npar, fitfunc, VERBOSE);
+  dchisq = ochisq;
+  chisq  = ochisq;
+  for (i = 0; (i < 25) && ((dchisq <= 0.0) || (dchisq > 0.01*(Npts - Npar))); i++) {
+    chisq = mrq2dmin (x, y, z, dz, Npts, par, Npar, fitfunc, VERBOSE);
+    dchisq = ochisq - chisq;
+    ochisq = chisq;
+  }  
+  set_int_variable ("Niter",  i);
+
+  /** create output image (keep in sky) **/
+  if (Save) {
+    Buffer *out;
+    float *Vi, *Vo, vr, vf;
+
+    if ((out = SelectBuffer ("out",   ANYBUFFER, TRUE)) == NULL) return (FALSE);
+    free (out[0].header.buffer);
+    free (out[0].matrix.buffer);
+
+    strcpy (out[0].file, "(empty)");
+    CreateBuffer (out, 2*nx, 2*ny, -32, 0.0, 1.0);
+
+    /* four panels: 1) raw image. 2) fit  3) raw - fit   4) ?? */
+    Vi = (float *)buf[0].matrix.buffer;
+    Vo = (float *)out[0].matrix.buffer;
+    for (j = 0; j < ny; j++) {
+      for (i = 0; i < nx; i++) {
+	vf = fitfunc ((float)(i+sx), (float)(j+sy), par, Npar, NULL);
+	vr = Vi[(i+sx)+(j+sy)*Nx];
+	Vo[(i   )+(j   )*2*nx] = vr;
+	Vo[(i+nx)+(j   )*2*nx] = vf;
+	Vo[(i   )+(j+ny)*2*nx] = vr - vf + *sky;
+	Vo[(i+nx)+(j+ny)*2*nx] = fabs(vr-vf) + *sky;
+      }
+    }
+  }
+
+  /* save parameters to opihi variables */
+  imfit_cleanup ();
+
+  set_variable ("ChiSq", chisq/(Npts - Npar));
+
+  if (VERBOSE) {
+    for (i = 0; i < Npar; i++) {
+      gprint (GP_ERR, "%g ", par[i]);
+    }
+    gprint (GP_ERR, "\n");
+  }
+
+  free (x);
+  free (y);
+  free (z);
+  free (dz);
+  free (par);
+  free (fpar);
+
+  mrq2dfree (Npar);
+  return (TRUE);
+
+range:
+  gprint (GP_ERR, "region out of range\n");
+  return (FALSE);
+}
+
Index: /branches/sc_branches/pantasks_condor/cmd.astro/imsub.c
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.astro/imsub.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.astro/imsub.c	(revision 34783)
@@ -0,0 +1,79 @@
+# include "imfit.h"
+
+int imsub (int argc, char **argv) {
+
+  int i, j, N;
+  int sx, sy, nx, ny, Nx, Ny;
+  float value;
+  float *V;
+  Buffer *buf;
+
+  // int VERBOSE = FALSE;
+  // if ((N = get_argument (argc, argv, "-v"))) {
+  //   remove_argument (N, &argc, argv);
+  //   VERBOSE = TRUE;
+  // }
+
+  /* set fitting function */
+  fgauss_setup ("fgauss");
+  if ((N = get_argument (argc, argv, "-func"))) {
+    fitfunc = NULL;
+    remove_argument (N, &argc, argv);
+    fgauss_setup (argv[N]);
+    pgauss_setup (argv[N]);
+    pgauss_psf_setup (argv[N]);
+    sgauss_setup (argv[N]);
+    sgauss_psf_setup (argv[N]);
+    qgauss_setup (argv[N]);
+    qgauss_psf_setup (argv[N]);
+    qfgauss_setup (argv[N]);
+    qrgauss_setup (argv[N]);
+    if (fitfunc == NULL) {
+      gprint (GP_ERR, "unknown function %s\n", argv[N]);
+      return (FALSE);
+    }
+    remove_argument (N, &argc, argv);
+  }
+
+  if (argc != 6) {
+    gprint (GP_ERR, "USAGE: imfit <buffer> sx sy nx ny\n");
+    return (FALSE);
+  }
+
+  /* non-optional arguments */
+  if ((buf = SelectBuffer (argv[1], OLDBUFFER, TRUE)) == NULL) return (FALSE);
+  sx = atof (argv[2]);
+  sy = atof (argv[3]);
+  nx = atof (argv[4]);
+  ny = atof (argv[5]);
+  Nx = buf[0].matrix.Naxis[0];
+  Ny = buf[0].matrix.Naxis[1];
+
+  /* check if region is valid */
+  if (sx >= Nx) goto range;
+  if (sy >= Ny) goto range;
+  if (sx + nx < 0) goto range;
+  if (sy + ny < 0) goto range;
+
+  /* subtract model fit, but not local sky */
+  for (j = 0; j < ny; j++) {
+    if (j + sy < 0) continue;
+    if (j + sy >= Ny) continue;
+    V = (float *)(buf[0].matrix.buffer) + (j+sy)*buf[0].matrix.Naxis[0] + sx; 
+    for (i = 0; i < nx; i++, V++) {
+      if (i + sx < 0) continue;
+      if (i + sx >= Nx) continue;
+      value = fitfunc ((float)(i+sx), (float)(j+sy), par, Npar, NULL);
+      *V -= value;
+    }
+  }
+
+  free (par);
+  free (fpar);
+  return (TRUE);
+
+range:
+  gprint (GP_ERR, "region out of range\n");
+  return (FALSE);
+}
+
Index: /branches/sc_branches/pantasks_condor/cmd.astro/init.c
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.astro/init.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.astro/init.c	(revision 34783)
@@ -0,0 +1,132 @@
+# include "astro.h"
+
+int altaz                   PROTO((int, char **));
+int biassub                 PROTO((int, char **));
+int cgrid                   PROTO((int, char **));
+int coords                  PROTO((int, char **));
+int cdot                    PROTO((int, char **));
+int cline                   PROTO((int, char **));
+int cplot                   PROTO((int, char **));
+int csystem                 PROTO((int, char **));
+int ctimes                  PROTO((int, char **));
+int cval                    PROTO((int, char **));
+int czplot                  PROTO((int, char **));
+int drizzle                 PROTO((int, char **));
+int flux                    PROTO((int, char **));
+int fixwrap                 PROTO((int, char **));
+int fiximage                PROTO((int, char **));
+int fixcols                 PROTO((int, char **));
+int fixrows                 PROTO((int, char **));
+int gauss                   PROTO((int, char **));
+int gaussfit                PROTO((int, char **));
+int getvel                  PROTO((int, char **));
+int getlst                  PROTO((int, char **));
+int imfit                   PROTO((int, char **));
+int imsub                   PROTO((int, char **));
+int medianmap               PROTO((int, char **));
+int galsectors              PROTO((int, char **));
+int galprofiles             PROTO((int, char **));
+int galradius               PROTO((int, char **));
+int galradbins              PROTO((int, char **));
+int getcoords               PROTO((int, char **));
+int elliprofile             PROTO((int, char **));
+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 **));
+int multifit                PROTO((int, char **));
+int objload                 PROTO((int, char **));
+int outline                 PROTO((int, char **));
+int polar                   PROTO((int, char **));
+int precess                 PROTO((int, char **));
+int profile                 PROTO((int, char **));
+int radec                   PROTO((int, char **));
+int region                  PROTO((int, char **));
+int rotcurve                PROTO((int, char **));
+int scale                   PROTO((int, char **));
+int sexigesimal             PROTO((int, char **));
+int spec                    PROTO((int, char **));
+int specpairfit             PROTO((int, char **));
+int spexseq                 PROTO((int, char **));
+int spex1dgas               PROTO((int, char **));
+int spex2dgas               PROTO((int, char **));
+int mkclusters              PROTO((int, char **));
+int star                    PROTO((int, char **));
+int times                   PROTO((int, char **));
+int transform               PROTO((int, char **));
+
+static Command cmds[] = {  
+  {1, "altaz",       altaz,        "convert alt/az to/from ra/dec"},
+  {1, "biassub",     biassub,      "subtract medianed overscan row or column"},
+  {1, "cgrid",       cgrid,        "plot sky coordinate grid"},
+  {1, "coords",      coords,       "load coordinates for buffer from file"},
+  {1, "cdot",        cdot,         "plot point in sky coordinates"},
+  {1, "cline",       cline,        "plot line connecting two sky coordinates"},
+  {1, "cplot",       cplot,        "plot vectors in sky coordinates"},
+  {1, "csystem",     csystem,      "convert between coordinate systems"},
+  {1, "ctimes",      ctimes,       "convert between time formats"},
+  {1, "cval",        cval,         "cosmic ray flux?"},
+  {1, "czplot",      czplot,       "plot scaled vectors in sky coordinates"},
+  {1, "drizzle",     drizzle,      "transform image to image"},
+  {1, "flux",        flux,         "flux in a convex contour"},
+  {1, "fixwrap",     fixwrap,      "fix megacam over-wrapped pixels"},
+  {1, "fiximage",    fiximage,     "fix pixels in an image by interpolation"},
+  {1, "fixcols",     fixcols,      "fix bad columns by comparing with others"},
+  {1, "fixrows",     fixrows,      "fix bad rows by comparing with others"},
+  {1, "gauss",       gauss,        "get statistics on a star, assuming gaussian profile"},
+  {1, "getvel",      getvel,       "rotcurve to velocities"},
+  {1, "getlst",      getlst,       "return LST given time and longitude"},
+  {1, "imfit",       imfit,        "fit function"},
+  {1, "imsub",       imsub,        "subtract function"},
+  {1, "medianmap",   medianmap,    "small median image"},
+  {1, "mkgauss",     mkgauss,      "generate a 2-D gaussian centered in image"},
+  {1, "mksersic",    mksersic,     "generate a 2-D sersic profile"},
+  {1, "galsectors",  galsectors,   "generate radial vectors for sectors of width dtheta"},
+  {1, "galprofiles", galprofiles,  "generate radial vectors with interpolation along paths"},
+  {1, "galradius",   galradius,    "generate radial vectors with interpolation along paths"},
+  {1, "galradbins",  galradbins,   "generate radial vectors with interpolation along paths"},
+  {1, "getcoords",   getcoords,    "generate images containing the RA,DEC coord for each pixel"},
+  {1, "elliprofile", elliprofile,  "generate radial vectors with interpolation along paths"},
+  {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 "},
+  {1, "outline",     outline,      "fit outline region"},
+  {1, "polar",       polar,        "convert polar image to cartesian"},
+  {1, "precess",     precess,      "precess coordinates"},
+  {1, "profile",     profile,      "radial profile at X, Y"},
+  {1, "radec",       radec,        "convert to/from radec in hms or dd"},
+  {1, "region",      region,       "define sky region for plot"},
+  {1, "rotcurve",    rotcurve,     "convert CO images to polar coords"},
+  {1, "scale",       scale,        "get / set real bzero / bscale values"},
+  {1, "sexigesimal", sexigesimal,  "convert to/from sexigesimal/decimal"},
+  {1, "spec",        spec,         "extract a spectrum"},
+  {1, "specpairfit", specpairfit,  "fit spectrum to another spectrum"},
+  {1, "spexseq",     spexseq,      "generate the spectral sequence"},
+  {1, "spex1dgas",   spex1dgas,    "minimize distances in 1D"},
+  {1, "spex2dgas",   spex2dgas,    "minimize distances in 2D"},
+  {1, "mkclusters",  mkclusters,   "group spectra by distance"},
+  {1, "star",        star,         "star stats at rough coords"},
+  {1, "transform",   transform,    "geometric transformation of image"},
+}; 
+
+/* not currently implemented 
+  {"gaussfit",    gaussfit,     "fit a gaussian to pixels in a region"},
+  {"testfit",     testfit, ""},
+  {"times", , ""},
+*/
+
+void InitAstro () {
+  
+  int i;
+
+  for (i = 0; i < sizeof (cmds) / sizeof (Command); i++) {
+    AddCommand (&cmds[i]);
+  }
+
+}
+
+void FreeAstro () {
+}
Index: /branches/sc_branches/pantasks_condor/cmd.astro/kronflux.c
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.astro/kronflux.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.astro/kronflux.c	(revision 34783)
@@ -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: /branches/sc_branches/pantasks_condor/cmd.astro/medianmap.c
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.astro/medianmap.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.astro/medianmap.c	(revision 34783)
@@ -0,0 +1,114 @@
+# include "astro.h"
+
+int medianmap (int argc, char **argv) {
+  
+  float *temp, *tp;
+  int i, j, k, I0, I1, J0, J1, I, J, n, N;
+  int nx, ny, Nx, Ny, NX, NY, Ignore;
+  float value, min, max, IgnoreValue;
+  float *In, *Out, *ip;
+  float fx, fy;
+  Buffer *in, *out;
+
+  IgnoreValue = 0;
+  Ignore = FALSE;
+  if ((N = get_argument (argc, argv, "-ignore"))) {
+    Ignore = TRUE;
+    remove_argument (N, &argc, argv);
+    IgnoreValue = atof(argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  min = 0.45;
+  max = 0.55;
+  if ((N = get_argument (argc, argv, "-range"))) {
+    remove_argument (N, &argc, argv);
+    min  = atof(argv[N]);
+    remove_argument (N, &argc, argv);
+    max  = atof(argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  if (argc != 5) {
+    gprint (GP_ERR, "USAGE: medianmap (in) (out) Nx Ny [-range min max]\n");
+    gprint (GP_ERR, "       Nx, Ny specify dimensions of output image\n");
+    gprint (GP_ERR, "       min, max specify fractional range for sorted average\n");
+    return (FALSE);
+  }
+
+  if ((in  = SelectBuffer (argv[1], OLDBUFFER, TRUE)) == NULL) return (FALSE);
+  if ((out = SelectBuffer (argv[2], ANYBUFFER, TRUE)) == NULL) return (FALSE);
+
+  Nx = atof (argv[3]);
+  Ny = atof (argv[4]);
+  NX = in[0].header.Naxis[0];
+  NY = in[0].header.Naxis[1];
+
+  /* duplicate the (in) buffer to the (out), with different size */
+  /* this should probably be a function in misc */
+  gfits_free_matrix (&out[0].matrix);
+  gfits_free_header (&out[0].header);
+  out[0].bitpix = in[0].bitpix;
+  out[0].unsign = in[0].unsign;
+  out[0].bscale = in[0].bscale;
+  out[0].bzero  = in[0].bzero;
+  gfits_copy_header (&in[0].header, &out[0].header);
+  gfits_modify (&out[0].header, "NAXIS1", "%d", 1, Nx);
+  gfits_modify (&out[0].header, "NAXIS2", "%d", 1, Ny);
+  out[0].header.Naxis[0] = Nx;
+  out[0].header.Naxis[1] = Ny;
+  gfits_create_matrix (&out[0].header, &out[0].matrix);
+
+  In = (float *) in[0].matrix.buffer;
+  Out = (float *) out[0].matrix.buffer;
+
+  fx = (float) Nx / NX;
+  fy = (float) Ny / NY;
+
+  nx = 1 + 1/fx;
+  ny = 1 + 1/fy;
+
+  ALLOCATE (temp, float, 2*nx*ny);
+
+  // float Mv = Mv2 = 0.0;
+
+  for (j = 0; j < Ny; j++) {
+    J0 = j / fy;
+    J1 = (j + 1) / fy;
+    for (i = 0; i < Nx; i++) {
+      
+      I0 = i / fx;
+      I1 = (i + 1) / fx;
+
+      n = 0;
+      tp = temp;
+      for (J = J0; J < J1; J++) {
+	ip = &In[J*NX + I0];
+	for (I = I0; I < I1; I++, tp++, ip++) {
+	  if (Ignore && (fabs (*ip - IgnoreValue) < 0.01)) continue;
+	  if (isnan (*ip)) continue;
+	  if (isinf (*ip)) continue;
+	  *tp = *ip;
+	  n++;
+	}
+      }
+
+      fsort (temp, n);
+
+      value = 0;
+      N = 0;
+      for (k = min*n; k < max*n; k++) {
+	value += temp[k];
+	N ++;
+      }
+      if (N == 0)
+	Out[j*Nx + i] = 0;
+      else 
+	Out[j*Nx + i] = value / N;
+    }
+  }
+
+  return (TRUE);
+
+}
+
Index: /branches/sc_branches/pantasks_condor/cmd.astro/mkclusters.c
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.astro/mkclusters.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.astro/mkclusters.c	(revision 34783)
@@ -0,0 +1,233 @@
+# include "astro.h"
+
+typedef struct {
+  int *friend; // indices for all tested relationships
+  float *dist; // distance for this relationship
+  int Ndist;   // number of tested relationships
+  int NDIST;   // number of allocated relationships
+  int group;   // assigned group
+  int Nfriend; // number of friends brought into group
+} Object;
+
+typedef struct {
+  int *entry;
+  int Nentry;
+  int NENTRY;
+} Group;
+
+static Group  *group = NULL;
+static int Ngroup = 0;
+static int NGROUP = 0;
+
+static Object *object = NULL;
+static int Nobject = 0;
+// static int NOBJECT = 0;
+
+void add_to_group (int Nobj, float scale, int Nfriends) {
+
+  int j;
+
+  // fprintf (stderr, "add object %d to group %d\n", Nobj, Ngroup);
+
+  object[Nobj].group = Ngroup;
+
+  // add to this group
+  int N = group[Ngroup].Nentry;
+  group[Ngroup].entry[N] = Nobj;
+  group[Ngroup].Nentry ++;
+  CHECK_REALLOCATE (group[Ngroup].entry, int, group[Ngroup].NENTRY, group[Ngroup].Nentry, 100);
+
+  // add all friends of this object (up to Nfriensd)
+  // friends are already sorted by distance, so we add closest friends first
+  for (j = 0; (object[Nobj].Nfriend < Nfriends) && (j < object[Nobj].Ndist); j++) {
+    if (object[Nobj].dist[j] > scale) continue;
+
+    int Nnew = object[Nobj].friend[j];
+
+    if (object[Nnew].group != -1) continue;
+
+    // found a friend : 
+    object[Nobj].Nfriend ++;
+    add_to_group (Nnew, scale, Nfriends);
+  }
+  return;
+}
+
+void sortvecset (opihi_flt *X, opihi_int *IDX1, opihi_int *IDX2, int N) {
+
+# define SWAPFUNC(A,B){ opihi_flt tmp; opihi_int itmp; \
+  tmp  = X[A];    X[A]    = X[B];    X[B]    = tmp; \
+  itmp = IDX1[A]; IDX1[A] = IDX1[B]; IDX1[B] = itmp; \
+  itmp = IDX2[A]; IDX2[A] = IDX2[B]; IDX2[B] = itmp; \
+}
+
+# define COMPARE(A,B)(X[A] < X[B])
+
+  OHANA_SORT (N, COMPARE, SWAPFUNC);
+
+# undef SWAPFUNC
+# undef COMPARE
+
+}
+
+void sortfriends (float *X, int *IDX1, int N) {
+
+# define SWAPFUNC(A,B){ float tmp; int itmp; \
+  tmp  = X[A];    X[A]    = X[B];    X[B]    = tmp; \
+  itmp = IDX1[A]; IDX1[A] = IDX1[B]; IDX1[B] = itmp; \
+}
+
+# define COMPARE(A,B)(X[A] < X[B])
+
+  OHANA_SORT (N, COMPARE, SWAPFUNC);
+
+# undef SWAPFUNC
+# undef COMPARE
+
+}
+
+int mkclusters (int argc, char **argv) {
+  
+  int i, j;
+  Vector *index1, *index2, *distance;
+
+  if (argc != 6) goto usage;
+
+  if ((index1   = SelectVector (argv[1], OLDVECTOR, TRUE)) == NULL) goto escape;
+  if ((index2   = SelectVector (argv[2], OLDVECTOR, TRUE)) == NULL) goto escape;
+  if ((distance = SelectVector (argv[3], OLDVECTOR, TRUE)) == NULL) goto escape;
+  float scale = atof (argv[4]);
+  int Nfriends = atoi (argv[5]);
+
+  // XXX enforce matching lengths on the three vectors
+
+  CastVector (index1, OPIHI_INT);
+  CastVector (index2, OPIHI_INT);
+
+  Nobject = 0;
+  for (i = 0; i < index1->Nelements; i++) {
+    Nobject = MAX (Nobject, index1->elements.Int[i]);
+    Nobject = MAX (Nobject, index2->elements.Int[i]);
+  }
+  Nobject ++;  // after the loop, Nobject has the value of the highest index, not the count
+
+  // allocate the list of Object -- these list the possible friends of the given object[i] 
+  ALLOCATE (object, Object, Nobject);
+  for (i = 0; i < Nobject; i++) {
+    ALLOCATE (object[i].friend, int, Nobject);
+    memset (object[i].friend, 0, Nobject*sizeof(int));
+    ALLOCATE (object[i].dist, float, Nobject);
+    memset (object[i].dist, 0, Nobject*sizeof(float));
+    object[i].NDIST = Nobject;
+    object[i].Ndist = 0;
+    object[i].group = -1; // unassigned
+    object[i].Nfriend = 0; // no friends yet
+  }
+
+  // generate the set of vectors of all distances for each entry
+  // the object seq number (object[i]) will be used to match the index1 and index2 values
+  for (i = 0; i < index1->Nelements; i++) {
+    int N, m;
+    N = index1->elements.Int[i];
+    if (N >= Nobject) abort();
+    m = object[N].Ndist;
+    object[N].friend[m] = index2->elements.Int[i];
+    object[N].dist[m] = distance->elements.Flt[i];
+    object[N].Ndist ++;
+    if (object[N].Ndist == object[N].NDIST) {
+      object[N].NDIST += 100;
+      REALLOCATE (object[N].dist, float, object[N].NDIST);
+      REALLOCATE (object[N].friend, int, object[N].NDIST);
+    }
+
+    N = index2->elements.Int[i];
+    if (N >= Nobject) abort();
+    m = object[N].Ndist;
+    object[N].friend[m] = index1->elements.Int[i];
+    object[N].dist[m] = distance->elements.Flt[i];
+    object[N].Ndist ++;
+    if (object[N].Ndist == object[N].NDIST) {
+      object[N].NDIST += 100;
+      REALLOCATE (object[N].dist, float, object[N].NDIST);
+      REALLOCATE (object[N].friend, int, object[N].NDIST);
+    }
+  }
+
+  for (i = 0; i < Nobject; i++) {
+    sortfriends (object[i].dist, object[i].friend, object[i].Ndist);
+  }
+
+  // generate the set of groups. each group is a list of friends and their friends
+  Ngroup = 0;
+  NGROUP = Nobject;
+  ALLOCATE (group, Group, NGROUP);
+  for (i = 0; i < NGROUP; i++) {
+    group[i].Nentry = 0;
+    group[i].NENTRY = 100;
+    ALLOCATE (group[i].entry, int, group[i].NENTRY);
+  }
+
+  sortvecset (distance->elements.Flt, index1->elements.Int, index2->elements.Int, index2->Nelements);
+  
+  for (i = 0; i < distance->Nelements; i++) {
+    int Nnew;
+    int found;
+
+    if (distance->elements.Flt[i] > scale) continue;
+
+    found = FALSE;
+
+    Nnew = index1->elements.Int[i];
+    if (object[Nnew].group == -1) {
+      add_to_group (Nnew, scale, Nfriends);
+      // fprintf (stderr, "new group %d part 1 with %d elements\n", Ngroup, group[Ngroup].Nentry);
+      found = TRUE;
+    }
+
+    Nnew = index2->elements.Int[i];
+    if (object[Nnew].group == -1) {
+      add_to_group (Nnew, scale, Nfriends);
+      // fprintf (stderr, "new group %d part 1 with %d elements\n", Ngroup, group[Ngroup].Nentry);
+      found = TRUE;
+    }
+
+    if (found) {
+      fprintf (stderr, "group %d with %d elements\n", Ngroup, group[Ngroup].Nentry);
+      Ngroup ++;
+      if (Ngroup >= NGROUP) abort();
+    }
+  }
+
+  if (1) {
+    int Nmiss = 0;
+    for (i = 0; i < Nobject; i++) {
+      if (object[i].group > -1) continue;
+      Nmiss ++;
+      // fprintf (stderr, "object %d not assigned\n", i);
+      for (j = 0; FALSE && j < object[i].Ndist; j++) {
+	fprintf (stderr, "friend %d, dist %f\n", object[i].friend[j], object[i].dist[j]);
+      }
+    }
+    fprintf (stderr, "%d objects not assigned\n", Nmiss);
+  } 
+
+  return TRUE;
+  
+ escape: 
+  gprint (GP_ERR, "invalid vector\n");
+  return FALSE;
+  
+usage:
+  gprint (GP_ERR, "USAGE: mkclusters (index1) (index2) (distance) scale\n");
+  return FALSE;
+}
+
+/* this function takes 3 vectors: a set of distances and a pair of indicies identifying the end points.  
+   
+   the goal is to generate groups of the end points that are relatively closer.
+
+   note that the distances may not be Euclidean: nothing can be assumed about relationships between d(1,2), d(2,3), and d(1,3)
+
+   the indicies should be sequential.  the algorithm does not require it, but storage is much cleaner if it is
+
+*/
Index: /branches/sc_branches/pantasks_condor/cmd.astro/mkgauss.c
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.astro/mkgauss.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.astro/mkgauss.c	(revision 34783)
@@ -0,0 +1,75 @@
+# include "astro.h"
+
+int mkgauss (int argc, char **argv) {
+  
+  int i, j, Nx, Ny, N;
+  float *in;
+  double Sig_x, Sig_y, Theta;
+  double root1, root2, R, A1, A2, A3;
+  double Sx, Sy, Sxy;
+  double x, y, r, f, Xo, Yo;
+  Buffer *buf;
+
+  // this should be Nx/2, Ny/2 if not set
+  Xo = Yo = NAN;
+  if ((N = get_argument (argc, argv, "-c"))) {
+    remove_argument (N, &argc, argv);
+    Xo = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+    Yo = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  if ((argc < 3) || (argc > 5)) {
+    gprint (GP_ERR, "USAGE: mkgauss (buffer) (sigma) [[sy/sx] angle]\n");
+    return (FALSE);
+  }
+
+  /* select input / output buffers */
+  if ((buf = SelectBuffer (argv[1], OLDBUFFER, TRUE)) == NULL) return (FALSE);
+  Nx = buf[0].header.Naxis[0];
+  Ny = buf[0].header.Naxis[1];
+  
+  if (isnan(Xo)) Xo = Nx / 2;
+  if (isnan(Yo)) Yo = Ny / 2;
+
+  /* gaussian parameters */
+  Sig_x = atof (argv[2]);
+  Sig_y = Sig_x;
+  Theta = 0.0;
+  if (argc > 3) {
+    Sig_y = Sig_x*atof (argv[3]);
+    if (argc == 5) {
+      Theta = atof (argv[4]);
+    }
+  }
+
+  /* given Sig_x, Sig_y, Theta, find Sx, Sy, Sxy */
+  root1 = SQ(1.0 / Sig_y);
+  root2 = SQ(1.0 / Sig_x);
+
+  R = 0.5 * (root1 - root2);
+  A1 = 0.25*(root1 + root2) - 0.5*R*cos(2*RAD_DEG*Theta);
+  A2 = 0.25*(root1 + root2) + 0.5*R*cos(2*RAD_DEG*Theta);
+  A3 = -R*sin(2*RAD_DEG*Theta);
+
+  Sx = 0.5/A1;
+  Sy = 0.5/A2;
+  Sxy = A3;
+
+  /* f = exp (-r), r = (x^2 / 2Sx) + (y^2 / 2Sy) + Sxy*x*y */
+
+  in = (float *) buf[0].matrix.buffer;
+  for (j = 0; j < Ny; j++) {
+    for (i = 0; i < Nx; i++, in++) {
+
+      x = i - Xo;
+      y = j - Yo;
+      r = 0.5*x*x/Sx + 0.5*y*y/Sy + x*y*Sxy;
+      f = exp (-r);
+      *in += f;
+    }
+  }
+
+  return (TRUE);
+}
Index: /branches/sc_branches/pantasks_condor/cmd.astro/mksersic.c
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.astro/mksersic.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.astro/mksersic.c	(revision 34783)
@@ -0,0 +1,87 @@
+# include "astro.h"
+
+int mksersic (int argc, char **argv) {
+  
+  int i, j, Nx, Ny, N, center;
+  float *in;
+  double Rmaj, Rmin, Theta, alpha, Io, ARatio;
+  double root1, root2, R, A1, A2, A3;
+  double Sx, Sy, Sxy;
+  double x, y, r, f, Xo, Yo;
+  Buffer *buf;
+
+  Xo = Yo = 0;
+  center = TRUE;
+  if ((N = get_argument (argc, argv, "-coord"))) {
+    remove_argument (N, &argc, argv);
+    Xo = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+    Yo = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+    center = FALSE;
+  }
+
+  Theta = 0.0;
+  if ((N = get_argument (argc, argv, "-angle"))) {
+    remove_argument (N, &argc, argv);
+    Theta = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  ARatio = 1.0;
+  if ((N = get_argument (argc, argv, "-aratio"))) {
+    remove_argument (N, &argc, argv);
+    ARatio = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  if (argc != 5) {
+    gprint (GP_ERR, "USAGE: mksersic (buffer) (Io) (Rmaj) (alpha)\n");
+    return (FALSE);
+  }
+
+  /* select input / output buffers */
+  if ((buf = SelectBuffer (argv[1], OLDBUFFER, TRUE)) == NULL) return (FALSE);
+  Nx = buf[0].header.Naxis[0];
+  Ny = buf[0].header.Naxis[1];
+  if (center) {
+    Xo = 0.5*Nx;
+    Yo = 0.5*Ny;
+  }
+  
+  Io = atof (argv[2]);
+
+  /* shape parameters */
+  Rmaj = atof (argv[3]);
+  Rmin = Rmaj / ARatio;
+
+  alpha = atof (argv[4]);
+
+  /* given Rmaj, Rmin, Theta, find Sx, Sy, Sxy */
+  root1 = SQ(1.0 / Rmaj);
+  root2 = SQ(1.0 / Rmin);
+
+  // XXX check this
+  R = 0.5 * (root1 - root2);
+  A1 = 0.25*(root1 + root2) - 0.5*R*cos(2*RAD_DEG*Theta);
+  A2 = 0.25*(root1 + root2) + 0.5*R*cos(2*RAD_DEG*Theta);
+  A3 = -R*sin(2*RAD_DEG*Theta);
+
+  Sx = 0.5/A1;
+  Sy = 0.5/A2;
+  Sxy = A3;
+
+  /* f = exp (-r^alpha), r = (x^2 / 2Sx) + (y^2 / 2Sy) + Sxy*x*y */
+  in = (float *) buf[0].matrix.buffer;
+  for (j = 0; j < Ny; j++) {
+    for (i = 0; i < Nx; i++, in++) {
+      x = i + 0.5 - Xo;
+      y = j + 0.5 - Yo;
+      r = pow((0.5*x*x/Sx + 0.5*y*y/Sy + x*y*Sxy), alpha);
+      f = Io*exp (-r);
+      *in += f;
+    }
+  }
+
+  return (TRUE);
+}
Index: /branches/sc_branches/pantasks_condor/cmd.astro/multifit.c
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.astro/multifit.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.astro/multifit.c	(revision 34783)
@@ -0,0 +1,186 @@
+# include "astro.h"
+
+int multifit (int argc, char **argv) {
+  
+  char *p, name[64];
+  double **a, **b, v;
+  int i, j, I, J, n, valid;
+  Vector **Nc, **Nmb, **NMb, **Nwb, **Nmh, **Nml, **Nwo;
+  int *nterm;
+  int Ndim, Norder, Nx, Ny;
+
+  if (argc != 2) {
+    gprint (GP_ERR, "USAGE: multifit (Norder)\n");
+    return (FALSE);
+  }
+
+  Ndim = 0;
+  Norder = atoi (argv[1]);
+  ALLOCATE (nterm, int, Norder);
+  for (i = 0; i < Norder; i++) {
+    sprintf (name, "nterm:%d", i);
+    p = get_variable (name);
+    nterm[i] = atoi (p); 
+    Ndim += nterm[i];
+    free (p);
+  }
+
+  ALLOCATE (a, double *, Ndim);
+  ALLOCATE (b, double *, Ndim);
+  for (i = 0; i < Ndim; i++) {
+    ALLOCATE (a[i], double, Ndim);
+    ALLOCATE (b[i], double, 1);
+    bzero (a[i], Ndim*sizeof(double));
+    bzero (b[i], sizeof(double));
+  }
+ 
+  ALLOCATE (Nc,  Vector *, Norder);
+  ALLOCATE (NMb, Vector *, Norder);
+  ALLOCATE (Nmb, Vector *, Norder);
+  ALLOCATE (Nwb, Vector *, Norder);
+  ALLOCATE (Nmh, Vector *, Norder - 1);
+  ALLOCATE (Nml, Vector *, Norder - 1);
+  ALLOCATE (Nwo, Vector *, Norder - 1);
+  
+  for (i = 0; i < Norder; i++) {
+    sprintf (name, "c%d", i);
+    if ((Nc[i]  = SelectVector (name, ANYVECTOR, TRUE)) == NULL) goto escape;
+    ResetVector (Nc[i], OPIHI_FLT, 1); 
+    sprintf (name, "Mb%d", i);
+    if ((NMb[i] = SelectVector (name, OLDVECTOR, TRUE)) == NULL) goto escape;
+    REQUIRE_VECTOR_FLT (NMb[i], FALSE); 
+    sprintf (name, "mb%d", i);
+    if ((Nmb[i] = SelectVector (name, OLDVECTOR, TRUE)) == NULL) goto escape;
+    REQUIRE_VECTOR_FLT (Nmb[i], FALSE); 
+    sprintf (name, "wb%d", i);
+    if ((Nwb[i] = SelectVector (name, OLDVECTOR, TRUE)) == NULL) goto escape;
+    REQUIRE_VECTOR_FLT (Nwb[i], FALSE); 
+  }
+  for (i = 0; i < Norder - 1; i++) {
+    sprintf (name, "ml%d", i);
+    if ((Nml[i] = SelectVector (name, OLDVECTOR, TRUE)) == NULL) goto escape;
+    REQUIRE_VECTOR_FLT (Nml[i], FALSE); 
+    sprintf (name, "mh%d", i);
+    if ((Nmh[i] = SelectVector (name, OLDVECTOR, TRUE)) == NULL) goto escape;
+    REQUIRE_VECTOR_FLT (Nmh[i], FALSE); 
+    sprintf (name, "wo%d", i);
+    if ((Nwo[i] = SelectVector (name, OLDVECTOR, TRUE)) == NULL) goto escape;
+    REQUIRE_VECTOR_FLT (Nwo[i], FALSE); 
+  }
+
+  Ny = 0;
+  for (i = 0; i < Norder; i++) {
+    for (j = 0; j < nterm[i]; j++, Ny++) {
+      Nx = 0;
+      valid = FALSE;
+      for (I = 0; I < Norder; I++) {
+	if (I == i - 1) { 
+	  for (J = 0; J < nterm[I]; J++) {
+	    v = 0;
+	    for (n = 0; n < Nwo[i-1][0].Nelements; n++) {
+	      v += -pow (Nwo[i-1][0].elements.Flt[n], (double)(j+J));
+	    }
+	    a[Ny][Nx] = v;
+	    Nx ++;
+	  }
+	  valid = TRUE;
+	}
+	if (I == i + 1) { 
+	  for (J = 0; J < nterm[I]; J++) {
+	    v = 0;
+	    for (n = 0; n < Nwo[i][0].Nelements; n++) {
+	      v += -pow (Nwo[i][0].elements.Flt[n], (double)(j+J));
+	    }
+	    a[Ny][Nx] = v;
+	    Nx ++;
+	  }
+	  valid = TRUE;
+	}
+	if (I == i) { 
+	  for (J = 0; J < nterm[I]; J++) {
+	    v = 0;
+	    for (n = 0; n < Nwb[i][0].Nelements; n++) {
+	      v += pow (Nwb[i][0].elements.Flt[n], (double)(j+J));
+	    }
+	    if (i > 0) {
+	      for (n = 0; n < Nwo[i-1][0].Nelements; n++) {
+		v += pow (Nwo[i-1][0].elements.Flt[n], (double)(j+J));
+	      }
+	    }
+	    if (i < Norder - 1) {
+	      for (n = 0; n < Nwo[i][0].Nelements; n++) {
+		v += pow (Nwo[i][0].elements.Flt[n], (double)(j+J));
+	      }
+	    }
+	    a[Ny][Nx] = v;
+	    Nx ++;
+	  }
+	  valid = TRUE;
+	}
+	if (!valid) {
+	  Nx += nterm[I];
+	}
+      }
+    }
+  }
+
+  Ny = 0;
+  for (i = 0; i < Norder; i++) {
+    for (j = 0; j < nterm[i]; j++, Ny++) {
+      v = 0;
+      for (n = 0; n < Nwb[i][0].Nelements; n++) {
+	v += NMb[i][0].elements.Flt[n]*pow (Nwb[i][0].elements.Flt[n], (double)j);
+	v -= Nmb[i][0].elements.Flt[n]*pow (Nwb[i][0].elements.Flt[n], (double)j);
+      }
+      if (i > 0) {
+	for (n = 0; n < Nwo[i-1][0].Nelements; n++) {
+	  v += Nmh[i-1][0].elements.Flt[n] * pow (Nwo[i-1][0].elements.Flt[n], (double)j);
+	  v -= Nml[i-1][0].elements.Flt[n] * pow (Nwo[i-1][0].elements.Flt[n], (double)j);
+	}
+      }
+      if (i < Norder - 1) {
+	for (n = 0; n < Nwo[i][0].Nelements; n++) {
+	  v += Nml[i][0].elements.Flt[n] * pow (Nwo[i][0].elements.Flt[n], (double)j);
+	  v -= Nmh[i][0].elements.Flt[n] * pow (Nwo[i][0].elements.Flt[n], (double)j);
+	}
+      }
+      b[Ny][0] = v;
+    }
+  }
+  dgaussjordan (a, b, Ndim, 1);
+
+  Ny = 0;
+  for (i = 0; i < Norder; i++) {
+    Nc[i][0].Nelements = nterm[i];
+    REALLOCATE (Nc[i][0].elements.Flt, opihi_flt, nterm[i]);
+    for (j = 0; j < nterm[i]; j++, Ny++) {
+      Nc[i][0].elements.Flt[j] = b[Ny][0];
+    }
+  }
+
+  for (i = 0; i < Ndim; i++) {
+    free (a[i]);
+    free (b[i]);
+  }
+  free (a);
+  free (b);
+  free (nterm);
+  free (Nc);
+  free (NMb);
+  free (Nmb);
+  free (Nwb);
+  free (Nmh);
+  free (Nml);
+  free (Nwo);
+  
+  return (TRUE);
+  
+ escape: 
+  gprint (GP_ERR, "syntax error\n");
+  return (FALSE);
+  
+}
+
+
+
+
Index: /branches/sc_branches/pantasks_condor/cmd.astro/objload.c
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.astro/objload.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.astro/objload.c	(revision 34783)
@@ -0,0 +1,77 @@
+# include "dvoshell.h"
+# define CHAR_LINE 104
+# define NBLOCK 100
+
+int objload (int argc, char **argv) {
+  
+  int i, N, Objtype, type, Nline, status;
+  FILE *f;
+  char *buffer, *line, *name;
+  int kapa, Noverlay, NOVERLAY;
+  KiiOverlay *overlay;
+  
+  name = NULL;
+  if ((N = get_argument (argc, argv, "-n"))) {
+    remove_argument (N, &argc, argv);
+    name = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+  if (!GetImage (NULL, &kapa, name)) return (FALSE);
+  FREE (name);
+
+  Objtype = 0;
+  if ((N = get_argument (argc, argv, "-t"))) {
+    remove_argument (N, &argc, argv);
+    Objtype = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  if (argc != 3) {
+    gprint (GP_ERR, "USAGE: objload (overlay) <filename>\n");
+    return (FALSE);
+  }
+
+  f = fopen (argv[2], "r");
+  if (f == (FILE *) NULL) {
+    gprint (GP_ERR, "ERROR: can't find object file %s\n", argv[2]);
+    return (FALSE);
+  }
+
+  /* read average values from first line */
+  ALLOCATE (line, char, 129);
+  scan_line (f, line);
+
+  ALLOCATE (buffer, char, CHAR_LINE*NBLOCK);
+
+  Noverlay = 0;
+  NOVERLAY = 1000;
+  ALLOCATE (overlay, KiiOverlay, Noverlay);
+  
+  /* read in data from obj file */
+  while ((Nline = fread (buffer, CHAR_LINE, NBLOCK, f)) > 0) {
+    for (i = 0; i < Nline; i++) {
+      /* we are now using all entries on the *.obj line */
+      status = sscanf (&buffer[i*CHAR_LINE], "%d %f %f",  &type, &overlay[Noverlay].x, &
+overlay[Noverlay].y);
+      if (status != 3) continue;
+      if (Objtype && (Objtype != type)) continue;
+      overlay[Noverlay].type = KII_OVERLAY_BOX;
+      overlay[Noverlay].dx = 5.0;
+      overlay[Noverlay].dy = 5.0;
+      Noverlay ++;
+      CHECK_REALLOCATE (overlay, KiiOverlay, NOVERLAY, Noverlay, 1000);
+    }
+  }
+  fclose (f);
+  free (buffer);
+
+  KiiLoadOverlay (kapa, overlay, Noverlay, argv[1]);
+
+  free (overlay);
+  free (buffer);
+  free (line);
+
+  gprint (GP_ERR, "loaded %d objects\n", Noverlay);
+  return (TRUE);
+}
+
Index: /branches/sc_branches/pantasks_condor/cmd.astro/outline.c
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.astro/outline.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.astro/outline.c	(revision 34783)
@@ -0,0 +1,289 @@
+# include "astro.h"
+
+float par[5];
+float dpar[5];
+float Dpar[5];
+float outline_chi (float, float *, int, int, float *);
+
+int outline (int argc, char **argv) {
+  
+  int i, j, k, BigChange, ABigChange;
+  float Io, *in, ochisq, dchi, chisq, chisq_p, chisq_m, dp, tmp_par;
+  float curve, frac;
+  Buffer *buf;
+
+  if (argc != 9) {
+    gprint (GP_ERR, "USAGE: outline x y dx dy dxy Io (buffer)\n");
+    return (FALSE);
+  }
+
+  if ((buf = SelectBuffer (argv[7], OLDBUFFER, TRUE)) == NULL) return (FALSE);
+
+  par[0] = atof(argv[1]);
+  par[1] = atof(argv[2]);
+  par[2] = atof(argv[3]);
+  par[3] = atof(argv[4]);
+  par[4] = atof(argv[5]);
+  Io = atof(argv[6]);
+  // int Npar = atof (argv[8]);
+
+  dpar[0] = 10;
+  dpar[1] = 10;
+  dpar[2] = 10;
+  dpar[3] = 10;
+  dpar[4] = 10;
+
+  Dpar[0] = 10;
+  Dpar[1] = 10;
+  Dpar[2] = 10;
+  Dpar[3] = 10;
+  Dpar[4] = 10;
+
+  in = (float *) buf[0].matrix.buffer;
+
+  chisq = outline_chi (Io, in, buf[0].matrix.Naxis[0], buf[0].matrix.Naxis[1], &frac);
+  gprint (GP_ERR, "chisq (1): %f\n", chisq);
+  
+  for (j = 0; j < 15; j++) {
+
+    /*
+    if (!(j % 3)) {
+      chisq = outline_chi (Io, in, buf[0].matrix.Naxis[0], buf[0].matrix.Naxis[1], &frac);
+      for (k = 0; (k < 3) && (fabs (frac) > 0.3); k++) {
+	tmp1 = par[2];
+	tmp2 = par[3];
+	par[2] *= 1 + 0.1*frac;
+	par[3] *= 1 + 0.1*frac;
+	nchisq = outline_chi (Io, in, buf[0].matrix.Naxis[0], buf[0].matrix.Naxis[1], &frac);
+	if (nchisq > chisq) {
+	  par[2] = tmp1;
+	  par[3] = tmp2;
+	  k = 3;
+	} else {
+	  chisq = nchisq;
+	}
+	gprint (GP_ERR, "frac: %f  %f %f   %f\n", frac, par[2], par[3], chisq);
+      }
+    }
+    */
+    
+    ABigChange = FALSE;
+    ochisq = chisq;
+    for (i = 4; i >= 0; i--) {
+      /* find +chisq, -chisq for this par & adjust par as needed */
+
+      for (k = 0, BigChange = TRUE; (k < 3) && BigChange; k++) {
+	tmp_par = par[i];
+	par[i] = tmp_par + dpar[i];
+	chisq_p = outline_chi (Io, in, buf[0].matrix.Naxis[0], buf[0].matrix.Naxis[1], &frac);
+	par[i] = tmp_par - dpar[i];
+	chisq_m = outline_chi (Io, in, buf[0].matrix.Naxis[0], buf[0].matrix.Naxis[1], &frac);
+	
+	/* have we braketted a minimum? (curve < 0) */
+	curve = (chisq_p - chisq) * (chisq - chisq_m);
+	if (curve > 0) {
+	  dp = 2*dpar[i];
+	} else {
+	  dp = 0.5 * dpar[i] * (chisq_m - chisq_p) / (chisq_m + chisq_p - 2*chisq);
+	}      
+	if (chisq_m + chisq_p - 2*chisq == 0) dp = 0;
+	/* don't let extrapolation go too far */
+	if (fabs (dp) > 2*fabs(dpar[i])) { dp = SIGN(dp) * fabs (2*dpar[i]); }
+	
+	par[i] = tmp_par + dp;
+	chisq = outline_chi (Io, in, buf[0].matrix.Naxis[0], buf[0].matrix.Naxis[1], &frac);
+	
+	BigChange = FALSE;
+	if (chisq <= 1.001*ochisq) {
+	  /* got better */
+	  dchi = (ochisq - chisq) / ochisq; 
+	  if ((dchi > 0.03) || (curve > 0)) BigChange = TRUE;
+	} else {
+	  par[i] = tmp_par;
+	  chisq = ochisq;
+	  if (chisq_m < chisq) {
+	    chisq = chisq_m;
+	    par[i] = tmp_par - dpar[i];
+	  }	
+	  if (chisq_p < chisq) {
+	    chisq = chisq_p;
+	    par[i] = tmp_par + dpar[i];
+	  }	
+	}	
+	/*
+	gprint (GP_ERR, "try: %d  %f   ", i, chisq);
+	for (k = 0; k < 5; k++) {
+	  gprint (GP_ERR, "%f ", par[k]);
+	}
+	gprint (GP_ERR, "\n");
+	*/
+	ochisq = chisq;
+	ABigChange |= BigChange;
+      }
+      if (!BigChange) dpar[i] *= 0.8;
+    }
+
+    if (ABigChange) {
+      for (i = 0; i < 5; i++) {
+	dpar[i] = Dpar[i];
+      }
+    }
+
+    gprint (GP_ERR, "try: %d  %f   ", j, chisq);
+    for (i = 0; i < 5; i++) {
+      gprint (GP_ERR, "%f ", par[i]);
+    }
+    gprint (GP_ERR, "\n          ");
+    for (i = 0; i < 5; i++) {
+      gprint (GP_ERR, "%f ", dpar[i]);
+    }
+    gprint (GP_ERR, "\n");
+    dchi -= chisq;
+
+  }
+
+    /* code to draw dots on Ximage */
+    {
+      int kapa;
+      float xp, yp, x, y;
+      float dx, dy, theta, t, dt;
+      int Noverlay, NOVERLAY;
+      KiiOverlay *overlay;
+      
+      if (!GetImage (NULL, &kapa, NULL)) return (FALSE);
+      
+      Noverlay = 0;
+      NOVERLAY = 1000;
+      ALLOCATE (overlay, KiiOverlay, Noverlay);
+  
+      dx = par[2];
+      dy = par[3];
+      dt = 1 / MAX (dx, dy);
+      theta = par[4];
+      
+      for (t = 0; t < 6.3; t += dt) {
+	xp = dx * cos (t);
+	yp = dy * sin (t);
+	
+	x = xp * cos (theta * RAD_DEG) - yp * sin (theta * RAD_DEG) + par[0];
+	y = xp * sin (theta * RAD_DEG) + yp * cos (theta * RAD_DEG) + par[1];
+	
+	overlay[Noverlay].type = KII_OVERLAY_BOX;
+	overlay[Noverlay].x = x;
+	overlay[Noverlay].y = y;
+	overlay[Noverlay].dx = 1.0;
+	overlay[Noverlay].dy = 1.0;
+
+	Noverlay ++;
+	CHECK_REALLOCATE (overlay, KiiOverlay, NOVERLAY, Noverlay, 1000);
+      }
+      KiiLoadOverlay (kapa, overlay, Noverlay, "red");
+      free (overlay);
+    }
+
+  return (TRUE);
+
+}
+
+/* par[0] = x
+   par[1] = y
+   par[2] = dx
+   par[3] = dy
+   par[4] = dxy
+   
+   ellipse is:  
+
+   ((X-x)/dx)^2 + ((Y-y)/dy)^2 + (X-x)(Y-y)dxy = 1
+  
+   (yp/dy)^2 + xp yp dxy + (xp/dx)^2 - 1 = 0
+
+   yp^2 + yp xp dxy dy^2 + xp^2 (dy/dx)^2 - dy^2 = 0
+
+*/
+
+float outline_chi (float Io, float *in, int Nx, int Ny, float *frac) {
+
+  int npts, xo, yo, x, y;
+  float xp, yp, dx, dy, theta;
+  float t, dt, dv, Dv;
+  float chisq, v, Frac;
+
+  /* 
+  if (!SelectVector (&Nvec, "diffs", ANYVECTOR)) return (FALSE);
+  if (!SelectVector (&Nvec2, "angle", ANYVECTOR)) return (FALSE);
+  if (!SelectVector (&Nvecx, "xdif", ANYVECTOR)) return (FALSE);
+  if (!SelectVector (&Nvecy, "ydif", ANYVECTOR)) return (FALSE);
+  */
+
+  dx = par[2];
+  dy = par[3];
+  theta = par[4];
+  dt = 1 / MAX (dx, dy);
+
+  Frac = 0;
+  chisq = 0;
+  npts = 0;
+  xo = -1; yo = -1;  /* an impossible coordinate */
+
+  /*
+  Npts = 1000;
+  REALLOCATE (vectors[Nvec].elements, float, Npts);
+  REALLOCATE (vectors[Nvec2].elements, float, Npts);
+  REALLOCATE (vectors[Nvecx].elements, float, Npts);
+  REALLOCATE (vectors[Nvecy].elements, float, Npts);
+  */
+
+  for (t = 0; t < 6.3; t += dt) {
+    xp = dx * cos (t);
+    yp = dy * sin (t);
+    
+    x = xp * cos (theta * RAD_DEG) - yp * sin (theta * RAD_DEG) + par[0];
+    y = xp * sin (theta * RAD_DEG) + yp * cos (theta * RAD_DEG) + par[1];
+    
+    if ((x == xo) && (y == yo)) continue;
+    xo = x; yo = y;
+
+    if ((x >= 0) && (x < Nx) && (y >= 0) && (y < Ny)) {
+      v = in[y*Nx + x];
+      if (v > 0) {
+	Dv = v - Io;
+	dv = v + 0.2 * fabs (Dv);
+	chisq += Dv * Dv / dv;
+	if (Dv > sqrt(dv)) Frac += 1.0;
+	if (Dv < sqrt(dv)) Frac -= 1.0;
+	/*
+	vectors[Nvec].elements[npts] = Dv;
+	vectors[Nvec2].elements[npts] = t*DEG_RAD;
+	vectors[Nvecx].elements[npts] = x;
+	vectors[Nvecy].elements[npts] = y;
+	*/
+	npts ++;
+	/* 
+	if (npts == Npts - 1) {
+	  Npts += 1000;
+	  REALLOCATE (vectors[Nvec].elements, float, Npts);
+	  REALLOCATE (vectors[Nvec2].elements, float, Npts);
+	  REALLOCATE (vectors[Nvecx].elements, float, Npts);
+	  REALLOCATE (vectors[Nvecy].elements, float, Npts);
+	}
+	*/
+      }
+    }
+  }
+  /* 
+  vectors[Nvec].Nelements = npts;
+  vectors[Nvec2].Nelements = npts;
+  vectors[Nvecx].Nelements = npts;
+  vectors[Nvecy].Nelements = npts;
+  */
+
+  chisq = chisq / npts;
+  *frac = Frac / npts;
+  if (npts == 0) {
+    chisq = 1e8;
+    *frac = -1.0;
+  }
+
+  return (chisq);
+
+}
Index: /branches/sc_branches/pantasks_condor/cmd.astro/outline2.c
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.astro/outline2.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.astro/outline2.c	(revision 34783)
@@ -0,0 +1,341 @@
+# include "astro.h"
+
+int Npts;
+float *xs, *ys, *zs;
+float par[5];
+float dpar[5];
+float Dpar[5];
+float outline_chi (float, float *);
+int plot_outline ();
+
+int outline (int argc, char **argv) {
+  
+  int i, j, k, Nx, Ny, NPTS, BigChange;
+  float dIo, Io, ochisq, dchi, chisq, chisq_p, chisq_m, dp;
+  float tmp_par, curve, value;
+  float *in;
+  Buffer *buf;
+
+  if (argc != 9) {
+    gprint (GP_ERR, "USAGE: outline x y dx dy dxy Io dIo (buffer)\n");
+    return (FALSE);
+  }
+
+  if ((buf = SelectBuffer (argv[8], OLDBUFFER, TRUE)) == NULL) return (FALSE);
+
+  par[0] = atof(argv[1]);
+  par[1] = atof(argv[2]);
+  par[2] = atof(argv[3]);
+  par[3] = atof(argv[4]);
+  par[4] = atof(argv[5]);
+  Io = atof(argv[6]);
+  dIo = atof(argv[7]);
+
+  dpar[0] = 10;
+  dpar[1] = 10;
+  dpar[2] = 10;
+  dpar[3] = 10;
+  dpar[4] = 10;
+
+  Dpar[0] = 10;
+  Dpar[1] = 10;
+  Dpar[2] = 10;
+  Dpar[3] = 10;
+  Dpar[4] = 10;
+
+  in = (float *) buf[0].matrix.buffer;
+
+  /* find all pixels within range Io-dIo : Io+dIo, in region about center guess */
+
+  Nx = buf[0].matrix.Naxis[0];  
+  Ny = buf[0].matrix.Naxis[1];
+  Npts = 0;
+  NPTS = 1000;
+  ALLOCATE (xs, float, NPTS);
+  ALLOCATE (ys, float, NPTS);
+  ALLOCATE (zs, float, NPTS);
+  for (j = par[1]-2*par[3]; j < par[1]+2*par[3]; j++) {
+    if (j < 0) continue;
+    if (j >= buf[0].matrix.Naxis[1]) continue;
+    for (i = par[0]-2*par[2]; i < par[0]+2*par[2]; i++) {
+      if (i < 0) continue;
+      if (i >= buf[0].matrix.Naxis[0]) continue;
+      value = in[i + Nx*j];
+      if (fabs (value - Io) < dIo) {
+	xs[Npts] = i;
+	ys[Npts] = j;
+	zs[Npts] = value;
+	Npts ++;
+	if (Npts == NPTS) {
+	  NPTS += 1000;
+	  REALLOCATE (xs, float, NPTS);
+	  REALLOCATE (ys, float, NPTS);
+	  REALLOCATE (zs, float, NPTS);
+	}
+      }
+    }
+  }
+
+  if (Npts == 0) {
+    gprint (GP_ERR, "no valid points in box, try again\n");
+    free (xs);
+    free (ys);
+    free (zs);
+    return (FALSE);
+  }
+
+  plot_outline ();
+  chisq = outline_chi (Io, in);
+  gprint (GP_ERR, "starting chisq: %f for %d pts\n", chisq, Npts);
+
+# if (1)
+  for (j = 0; j < 15; j++) {
+    
+    ochisq = chisq;
+    for (i = 0; i < 5; i++) {
+      /* find +chisq, -chisq for this par & adjust par as needed */
+
+      for (k = 0, BigChange = TRUE; (k < 3) && BigChange; k++) {
+	tmp_par = par[i];
+	par[i] = tmp_par + dpar[i];
+	chisq_p = outline_chi (Io, in);
+	par[i] = tmp_par - dpar[i];
+	chisq_m = outline_chi (Io, in);
+	
+	/* have we braketted a minimum? (curve < 0) */
+	curve = (chisq_p - chisq) * (chisq - chisq_m);
+	if (curve > 0) {
+	  dp = 2*dpar[i];
+	} else {
+	  dp = 0.5 * dpar[i] * (chisq_m - chisq_p) / (chisq_m + chisq_p - 2*chisq);
+	}      
+	if (chisq_m + chisq_p - 2*chisq == 0) dp = 0;
+	/* don't let extrapolation go too far */
+	if (fabs (dp) > 2*fabs(dpar[i])) { dp = SIGN(dp) * fabs (2*dpar[i]); }
+	
+	par[i] = tmp_par + dp;
+	chisq = outline_chi (Io, in);
+	
+	BigChange = FALSE;
+	if (chisq <= 1.001*ochisq) {
+	  /* got better */
+	  dchi = (ochisq - chisq) / ochisq; 
+	  if ((dchi > 0.03) || (curve > 0)) BigChange = TRUE;
+	} else {
+	  par[i] = tmp_par;
+	  chisq = ochisq;
+	  if (chisq_m < chisq) {
+	    chisq = chisq_m;
+	    par[i] = tmp_par - dpar[i];
+	  }	
+	  if (chisq_p < chisq) {
+	    chisq = chisq_p;
+	    par[i] = tmp_par + dpar[i];
+	  }	
+	}	
+	ochisq = chisq;
+      }
+      if (!BigChange) dpar[i] *= 0.8;
+    }
+
+    gprint (GP_ERR, "try: %d  %f   ", j, chisq);
+    for (i = 0; i < 5; i++) {
+      gprint (GP_ERR, "%f ", par[i]);
+    }
+    gprint (GP_ERR, "\n          ");
+    for (i = 0; i < 5; i++) {
+      gprint (GP_ERR, "%f ", dpar[i]);
+    }
+    gprint (GP_ERR, "\n");
+    dchi -= chisq;
+
+  }
+# endif
+
+  free (xs);
+  free (ys);
+  free (zs);
+  
+  plot_outline ();
+  return (TRUE);
+
+}
+
+/* par[0] = x
+   par[1] = y
+   par[2] = dx
+   par[3] = dy
+   par[4] = dxy
+   
+    xp = par[2] * cos (t);
+    yp = par[3] * sin (t);
+    
+    x = xp * cos (par[4] * RAD_DEG) - yp * sin (par[4] * RAD_DEG) + par[0];
+    y = xp * sin (par[4] * RAD_DEG) + yp * cos (par[4] * RAD_DEG) + par[1];
+
+*/
+
+# if (1)
+
+float outline_chi (float Io, float *in) {
+
+  int i;
+  float theta, phi;
+  float xp, yp, x, y;
+  float chisq, R2;
+
+  chisq = 0;
+
+  for (i = 0; i < Npts; i++) {
+    
+    phi = atan2 (ys[i] - par[1], xs[i] - par[0]) - RAD_DEG * par[4];
+    /* find a point:
+
+       xp, yp such that atan (r2 sin(phi), r1 cos(phi)) == theta 
+
+       tan (theta) = r2 sin(phi) / r1 cos (phi)
+
+       (r1/r2) tan(theta) = sin(phi) / cos (phi);
+       (r1/r2) tan(theta) = tan (phi)
+
+       phi = atan2 (r1 sin(theta), r2 cos(theta))
+    */
+
+    theta = atan2 (par[2]*sin(phi), par[3]*cos(phi));
+
+    /* this is the point on the ellipse at the same angle as ref point */
+    /* this is wrong, but close -- tends to make ellipses too fat */
+    xp = par[2] * cos (theta);
+    yp = par[3] * sin (theta);
+    
+    x = xp * cos (par[4] * RAD_DEG) - yp * sin (par[4] * RAD_DEG) + par[0];
+    y = xp * sin (par[4] * RAD_DEG) + yp * cos (par[4] * RAD_DEG) + par[1];
+
+    R2 = sqrt (SQ (x - xs[i]) + SQ (y - ys[i]));
+
+    /*
+    Dv = zs[i] - Io;
+    dv = fabs(zs[i]);
+    F2 = Dv * Dv / dv;
+    */
+
+    chisq += R2;
+
+  }
+
+  chisq = chisq / Npts;
+  return (chisq);
+
+}
+
+# else 
+
+float outline_chi (float Io, float *in) {
+
+  int i;
+  float theta, theta1, theta2;
+  float xp, yp, x, y;
+  float chisq, dv, Dv, R2, F2, R, dR;
+
+  int Nvec, Nvec2, Nvecx, Nvecy, Nv, nv;
+
+  chisq = 0;
+
+  nv = 0;
+  Nv = 1000;
+  if (!SelectVector (&Nvec, "dR", ANYVECTOR)) return (FALSE);
+  if (!SelectVector (&Nvec2, "dF", ANYVECTOR)) return (FALSE);
+  if (!SelectVector (&Nvecx, "x", ANYVECTOR)) return (FALSE);
+  if (!SelectVector (&Nvecy, "y", ANYVECTOR)) return (FALSE);
+  REALLOCATE (vectors[Nvec].elements, float, Nv);
+  REALLOCATE (vectors[Nvec2].elements, float, Nv);
+  REALLOCATE (vectors[Nvecx].elements, float, Nv);
+  REALLOCATE (vectors[Nvecy].elements, float, Nv);
+
+  for (i = 0; i < Npts; i++) {
+    
+    theta1 = atan2 (ys[i] - par[1], xs[i] - par[0]) - RAD_DEG * par[4];
+    theta = atan2 (par[2]*sin(theta1), par[3]*cos(theta1));
+
+    xp = par[2] * cos (theta);
+    yp = par[3] * sin (theta);
+    
+    x = xp * cos (par[4] * RAD_DEG) - yp * sin (par[4] * RAD_DEG) + par[0];
+    y = xp * sin (par[4] * RAD_DEG) + yp * cos (par[4] * RAD_DEG) + par[1];
+
+    R2 = SQ (x - xs[i]) + SQ (y - ys[i]);
+
+    /* 
+    Dv = fabs (zs[i] - Io) + 1;
+    dv = zs[i] + 0.2 * fabs (Dv);
+    F2 = Dv * Dv / dv;
+    */
+
+    vectors[Nvec].elements[nv] = x;
+    vectors[Nvec2].elements[nv] = y;
+    vectors[Nvecx].elements[nv] = xs[i];
+    vectors[Nvecy].elements[nv] = ys[i];
+    nv ++;
+    if (nv == Nv - 1) {
+      Nv += 1000;
+      REALLOCATE (vectors[Nvec].elements, float, Nv);
+      REALLOCATE (vectors[Nvec2].elements, float, Nv);
+      REALLOCATE (vectors[Nvecx].elements, float, Nv);
+      REALLOCATE (vectors[Nvecy].elements, float, Nv);
+    }
+
+    /* typical distance might be 1 - 10 pix,
+       typical z error might be 100 cts */
+    chisq += R2; 
+
+  }
+  vectors[Nvec].Nelements = nv;
+  vectors[Nvec2].Nelements = nv;
+  vectors[Nvecx].Nelements = nv;
+  vectors[Nvecy].Nelements = nv;
+
+  chisq = chisq / Npts;
+  return (chisq);
+
+}
+# endif
+
+int plot_outline () {
+  
+  int kapa;
+  float xp, yp, x, y;
+  float dx, dy, theta, t, dt;
+  int Noverlay, NOVERLAY;
+  KiiOverlay *overlay;
+  
+  if (!GetImage (NULL, &kapa, NULL)) return (FALSE);
+  
+  Noverlay = 0;
+  NOVERLAY = 1000;
+  ALLOCATE (overlay, KiiOverlay, Noverlay);
+  
+  dx = par[2];
+  dy = par[3];
+  dt = 1 / MAX (dx, dy);
+  theta = par[4];
+  
+  for (t = 0; t < 6.3; t += dt) {
+    xp = dx * cos (t);
+    yp = dy * sin (t);
+    
+    x = xp * cos (theta * RAD_DEG) - yp * sin (theta * RAD_DEG) + par[0];
+    y = xp * sin (theta * RAD_DEG) + yp * cos (theta * RAD_DEG) + par[1];
+    
+    overlay[Noverlay].type = KII_OVERLAY_BOX;
+    overlay[Noverlay].x = x;
+    overlay[Noverlay].y = y;
+    overlay[Noverlay].dx = 1.0;
+    overlay[Noverlay].dy = 1.0;
+
+    Noverlay ++;
+    CHECK_REALLOCATE (overlay, KiiOverlay, NOVERLAY, Noverlay, 1000);
+  }
+  KiiLoadOverlay (kapa, overlay, Noverlay, "red");
+  free (overlay);
+  return (TRUE);
+}
Index: /branches/sc_branches/pantasks_condor/cmd.astro/petrosian.c
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.astro/petrosian.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.astro/petrosian.c	(revision 34783)
@@ -0,0 +1,91 @@
+# include "astro.h"
+
+int petrosian (int argc, char **argv) {
+  
+  int i, above;
+  double Fsum, Asum, Area, R_90, rad_90, flux_90;
+  Vector *rvec, *avec, *fvec, *Rvec, *Fvec, *Svec, *Avec;
+
+  if (argc != 8) {
+    gprint (GP_ERR, "USAGE: petrosian (radius) (area) (flux) (P_ratio) (P_flux) (P_sb) (P_area)\n");
+    return (FALSE);
+  }
+
+  /* select input vectors */
+  if ((rvec = SelectVector (argv[1], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((avec = SelectVector (argv[2], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((fvec = SelectVector (argv[3], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((Rvec = SelectVector (argv[4], ANYVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((Fvec = SelectVector (argv[5], ANYVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((Svec = SelectVector (argv[6], ANYVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((Avec = SelectVector (argv[7], ANYVECTOR, TRUE)) == NULL) return (FALSE);
+
+  // difficult work goes into cleaning the input galaxy profile:
+  // a) extract radial profiles applying 180 degree symmetry requirement
+  // b) determine the axial ratio and position angle (at r_50)
+  // c) extract normalized radial profiles applying outlier clipping
+  // 
+  // the resulting vectors r, f represent the mean surface brightness at a radius r_i, 
+  // measured between \alpha*r_i and \beta*r_i: f(r_i) = \sum_\alpha*r_i^\beta*r_i flux(r)
+
+  // given a clean set of vectors representing the f_i, r_i, determine F_P, R_P such that:
+  // f(r_i) / \sum_0^r_i f(r) = f_P 
+
+  // generate a vector representing f_P(r_i):
+
+  ResetVector (Fvec, OPIHI_FLT, fvec[0].Nelements);
+  ResetVector (Rvec, OPIHI_FLT, fvec[0].Nelements);
+  ResetVector (Svec, OPIHI_FLT, fvec[0].Nelements);
+  ResetVector (Avec, OPIHI_FLT, fvec[0].Nelements);
+
+  flux_90 = rad_90 = 0.0;
+  R_90 = 0.1;
+  above = TRUE;
+  Fsum = 0.0;
+  Asum = 0.0;
+  Area = avec[0].elements.Flt[0];
+  for (i = 0; i < fvec[0].Nelements; i++) {
+    // for nan bins, we keep the area for use with the next valid bin
+    if (isnan(fvec[0].elements.Flt[i])) {
+      Area += avec[0].elements.Flt[i];
+      continue;
+    } 
+    Fsum += fvec[0].elements.Flt[i] * Area;
+    Asum += Area;
+    if (i+1 < fvec[0].Nelements) {
+      Area = avec[0].elements.Flt[i+1];
+    }
+
+    Rvec[0].elements.Flt[i] = Asum * fvec[0].elements.Flt[i] / Fsum;
+    Fvec[0].elements.Flt[i] = Fsum;
+    Svec[0].elements.Flt[i] = Fsum / Asum;
+    Avec[0].elements.Flt[i] = Asum;
+
+    // anytime we transition below the petrosian ratio R_90, calculate the radius and flux
+    // we will keep and report the last (largest radius) value
+    if (above && (Rvec[0].elements.Flt[i] < R_90)) {
+      // interpolate Rvec between i-1 and i to R_90 to get flux (Fvec) and radius (rvec)
+
+      if (i == 0) { 
+	// assume Fmax @ R = 0.0
+	rad_90  = rvec[0].elements.Flt[i] * (R_90 - 1.0) / (Rvec[0].elements.Flt[i] - 1.0);
+	flux_90 = Fvec[0].elements.Flt[i] * (R_90 - 1.0) / (Rvec[0].elements.Flt[i] - 1.0);
+      } else {
+	rad_90  = rvec[0].elements.Flt[i-1] + (rvec[0].elements.Flt[i] - rvec[0].elements.Flt[i-1]) * (R_90 - Rvec[0].elements.Flt[i-1]) / (Rvec[0].elements.Flt[i] - Rvec[0].elements.Flt[i-1]);
+	flux_90 = Fvec[0].elements.Flt[i-1] + (Fvec[0].elements.Flt[i] - Fvec[0].elements.Flt[i-1]) * (R_90 - Rvec[0].elements.Flt[i-1]) / (Rvec[0].elements.Flt[i] - Rvec[0].elements.Flt[i-1]);
+      }
+
+      above = FALSE;
+    }
+    
+    // reset on transitions up, but do not re-calculate rad_90, flux_90
+    if (!above && (Rvec[0].elements.Flt[i] >= R_90)) {
+      above = TRUE;
+    }
+  }
+
+  set_variable ("P_R90", rad_90);
+  set_variable ("P_F90", flux_90);
+
+  return (TRUE);
+}
Index: /branches/sc_branches/pantasks_condor/cmd.astro/polar.c
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.astro/polar.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.astro/polar.c	(revision 34783)
@@ -0,0 +1,69 @@
+# include "astro.h"
+
+int polar (int argc, char **argv) {
+  
+  double Lo, dL, Do, dD, Mo, dM, No, dN;
+  double xo, yo, Xo, Yo;
+  double x, y, r, t;
+  float *Vin, *Vout, *Vmask;
+  int i, j, nx, ny, Nx, Ny;
+  int X, Y;
+  Buffer *in, *out, *mask;
+
+  if (argc != 4) {
+    gprint (GP_ERR, "USAGE: polar in out\n");
+    return (FALSE);
+  }
+
+  if ((in   = SelectBuffer (argv[1], OLDBUFFER, TRUE)) == NULL) return (FALSE);
+  if ((out  = SelectBuffer (argv[2], OLDBUFFER, TRUE)) == NULL) return (FALSE);
+  if ((mask = SelectBuffer (argv[3], OLDBUFFER, TRUE)) == NULL) return (FALSE);
+
+  Nx = out[0].matrix.Naxis[0];
+  Ny = out[0].matrix.Naxis[1];
+  nx = mask[0].matrix.Naxis[0];
+  ny = mask[0].matrix.Naxis[1];
+  if ((Nx != nx) && (Ny != ny)) {
+    gprint (GP_ERR, "output and mask must have same dimensions\n");
+    return (FALSE);
+  }
+  nx = in[0].matrix.Naxis[0];
+  ny = in[0].matrix.Naxis[1];
+
+  /* we expect the output image to have units of longitude and distance */
+  gfits_scan (&in[0].header, "CRVAL1", "%lf", 1, &Lo);
+  gfits_scan (&in[0].header, "CDELT1", "%lf", 1, &dL);
+  gfits_scan (&in[0].header, "CRPIX1", "%lf", 1, &xo);
+  gfits_scan (&in[0].header, "CRVAL2", "%lf", 1, &Do);
+  gfits_scan (&in[0].header, "CDELT2", "%lf", 1, &dD);
+  gfits_scan (&in[0].header, "CRPIX2", "%lf", 1, &yo);
+
+  /* we expect the input image to have units of distance X and Y */
+  gfits_scan (&out[0].header, "CRVAL1", "%lf", 1, &Mo);
+  gfits_scan (&out[0].header, "CDELT1", "%lf", 1, &dM);
+  gfits_scan (&out[0].header, "CRPIX1", "%lf", 1, &Xo);
+  gfits_scan (&out[0].header, "CRVAL2", "%lf", 1, &No);
+  gfits_scan (&out[0].header, "CDELT2", "%lf", 1, &dN);
+  gfits_scan (&out[0].header, "CRPIX2", "%lf", 1, &Yo);
+
+  Vin  = (float *)in[0].matrix.buffer;
+  Vout = (float *)out[0].matrix.buffer;
+  Vmask = (float *)mask[0].matrix.buffer;
+  for (j = 0; j < Ny; j++) {
+    for (i = 0; i < Nx; i++, Vout++, Vmask++) {
+      x = (i - Xo) * dM + Mo;
+      y = (j - Yo) * dN + No;
+      r = hypot(x, y);
+      t = ohana_normalize_angle(DEG_RAD*atan2 (y, x));
+      X = (t - Lo) / dL + xo;
+      Y = (r - Do) / dD + yo;
+      if ((X >= 0) && (X < nx) && (Y >= 0) && (Y < ny)) {
+	*Vout += Vin[Y*nx + X];
+	*Vmask += 1;
+      }
+    }
+  }
+
+ return (TRUE);
+
+}
Index: /branches/sc_branches/pantasks_condor/cmd.astro/precess.c
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.astro/precess.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.astro/precess.c	(revision 34783)
@@ -0,0 +1,118 @@
+# include "astro.h"
+
+int precess (int argc, char **argv) {
+
+  int i, Julian, Besselian;
+  double T, in_epoch, out_epoch;
+  double A, D, RA, DEC, zeta, z, theta;
+  double SA, CA, SD, CD;
+  Vector *xvec, *yvec;
+
+  Besselian = Julian = 0;
+  Besselian = get_argument (argc, argv, "B");
+  Julian    = get_argument (argc, argv, "J");
+
+  in_epoch = out_epoch = 2000.0;
+  if (argc != 5) {
+    gprint (GP_ERR, "USAGE:  precess (from) (to) RA DEC \n");
+    gprint (GP_ERR, "   you may use B for B1950.0 or J for J2000.0\n");
+    return (FALSE);
+  }
+
+  if (!Julian && !Besselian) { /* assume Julian! */
+    in_epoch  = get_epoch (argv[1], 'J');
+    out_epoch = get_epoch (argv[2], 'J');
+  }
+
+  if ((Julian == 1) && !Besselian) {
+    in_epoch  = 2000.0;
+    out_epoch = get_epoch(argv[2], 'J');
+  }
+
+  if ((Julian == 2) && !Besselian) {
+    in_epoch  = get_epoch(argv[1], 'J');
+    out_epoch = 2000.0;
+  }
+
+  if ((Besselian == 1) && !Julian) {
+    in_epoch  = BtoJ(1950.0); 
+    out_epoch = get_epoch(argv[2], 'B'); 
+  }
+
+  if ((Besselian == 2) && !Julian) {
+    in_epoch  = get_epoch(argv[1], 'B'); 
+    out_epoch = BtoJ(1950.0); 
+  }
+  
+  if (Julian && Besselian) {
+    if (Julian > Besselian) {
+      in_epoch  = BtoJ(1950.0); 
+      out_epoch = 2000.0;
+    }
+    else {
+      in_epoch  = 2000.0;
+      out_epoch = BtoJ(1950.0); 
+    }
+  }
+
+  gprint (GP_ERR, "converting from J%f to J%f\n", in_epoch, out_epoch);
+
+  T = (out_epoch - in_epoch) / 100.0;
+  
+  zeta  = RAD_DEG*(0.6406161*T + 0.0000839*T*T + 0.0000050*T*T*T);
+  theta = RAD_DEG*(0.5567530*T - 0.0001185*T*T - 0.0000116*T*T*T);
+  z     =          0.6406161*T + 0.0003041*T*T + 0.0000051*T*T*T;
+
+  if (ISNUM(argv[3][0]) && ISNUM(argv[4][0])) {
+    A = atof (argv[3]);
+    D = atof (argv[4]);
+    SD =  cos(RAD_DEG*A + zeta)*sin(theta)*cos(RAD_DEG*D) + cos(theta)*sin(RAD_DEG*D);
+    CD = sqrt (1 - SD*SD);
+    SA =  sin(RAD_DEG*A + zeta)*cos(RAD_DEG*D)/CD;
+    CA = (cos(RAD_DEG*A + zeta)*cos(theta)*cos(RAD_DEG*D) - sin(theta)*sin(RAD_DEG*D))/CD;
+
+    DEC = DEG_RAD*asin(SD);
+    RA  = DEG_RAD*atan2(SA, CA) + z;
+
+    if (RA < 0)
+      RA += 360;
+    gprint (GP_LOG, "%f %f -> %f %f\n", A, D, RA, DEC);
+    return (TRUE);
+  }    
+
+  /* find vectors */
+  if ((xvec = SelectVector (argv[3], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((yvec = SelectVector (argv[4], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+
+  if (xvec[0].Nelements != yvec[0].Nelements) {
+    gprint (GP_ERR, "vectors %s and %s not the same length\n", argv[3], argv[4]);
+    return (FALSE);
+  }
+  
+  // cast xvec or yvec to FLT if it is type INT
+  CastVector (xvec, OPIHI_FLT);
+  CastVector (yvec, OPIHI_FLT);
+
+  opihi_flt *Av = xvec[0].elements.Flt;
+  opihi_flt *Dv = yvec[0].elements.Flt;
+
+  for (i = 0; i < xvec[0].Nelements; i++, Av++, Dv++) {
+    A = *Av;
+    D = *Dv;
+    SD =  cos(RAD_DEG*A + zeta)*sin(theta)*cos(RAD_DEG*D) + cos(theta)*sin(RAD_DEG*D);
+    CD = sqrt (1 - SD*SD);
+    SA =  sin(RAD_DEG*A + zeta)*cos(RAD_DEG*D)/CD;
+    CA = (cos(RAD_DEG*A + zeta)*cos(theta)*cos(RAD_DEG*D) - sin(theta)*sin(RAD_DEG*D))/CD;
+
+    DEC = DEG_RAD*asin(SD);
+    RA  = DEG_RAD*atan2(SA, CA) + z;
+
+    if (RA < 0) RA += 360;
+    
+    *Av = RA;
+    *Dv = DEC; 
+  }
+
+  return (TRUE);
+
+}  
Index: /branches/sc_branches/pantasks_condor/cmd.astro/profile.c
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.astro/profile.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.astro/profile.c	(revision 34783)
@@ -0,0 +1,52 @@
+# include "astro.h"
+
+int profile (int argc, char **argv) {
+  
+  int i, j, N, Nx, Npt;
+  float *V;
+  double sx, sy;
+  Vector *xvec, *yvec;
+  Buffer *buf;
+
+  if (argc != 7) {
+    gprint (GP_ERR, "USAGE: profile <buffer> <X vector> <Y vector> x y N\n");
+    return (FALSE);
+  }
+
+  if ((buf = SelectBuffer (argv[1], OLDBUFFER, TRUE)) == NULL) return (FALSE);
+  
+  sx = atof (argv[4]);
+  sy = atof (argv[5]);
+  N  = atof (argv[6]);
+
+  if (sx - N < 0) goto range_error;
+  if (sy - N < 0) goto range_error;
+  if (sx + N > buf[0].matrix.Naxis[0]) goto range_error;
+  if (sy + N > buf[0].matrix.Naxis[1]) goto range_error;
+
+  if ((xvec = SelectVector (argv[2], ANYVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((yvec = SelectVector (argv[3], ANYVECTOR, TRUE)) == NULL) return (FALSE);
+
+  ResetVector (xvec, OPIHI_FLT, (int)SQ(2*N+1));
+  ResetVector (yvec, OPIHI_FLT, (int)SQ(2*N+1));
+
+  bzero (yvec[0].elements.Flt, (int)SQ(2*N+1)*sizeof(opihi_flt));
+  V = (float *)(buf[0].matrix.buffer); 
+  Npt = 0;
+  Nx = buf[0].matrix.Naxis[0];
+  for (i = sx - N; i <= sx + N; i++) {
+    for (j = sy - N; j <= sy + N; j++, Npt++) {
+      yvec[0].elements.Flt[Npt] = V[i + j*Nx];
+      xvec[0].elements.Flt[Npt] = hypot (i + 0.5 - sx, j + 0.5 - sy);
+    }
+  }
+
+  dsortpair (xvec[0].elements.Flt, yvec[0].elements.Flt, xvec[0].Nelements);
+
+  return (TRUE);
+
+range_error:
+  gprint (GP_ERR, "region out of range\n");
+  return (FALSE);
+}
+
Index: /branches/sc_branches/pantasks_condor/cmd.astro/radec.c
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.astro/radec.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.astro/radec.c	(revision 34783)
@@ -0,0 +1,56 @@
+# include "astro.h"
+
+int radec (int argc, char **argv) {
+
+  int N, QUIET;
+  double ra, dec;
+  char ra_string[32], dec_string[32];
+
+  QUIET = FALSE;
+  if ((N = get_argument (argc, argv, "-q"))) {
+    QUIET = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+  if ((N = get_argument (argc, argv, "-quiet"))) {
+    QUIET = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+
+  if (argc != 4) {
+    gprint (GP_ERR, "USAGE:  radec (-hh | -hms) RA DEC [-q | -quiet]\n");
+    gprint (GP_ERR, "   convert to decimal (-hh) or sexigesimal (-hms)\n");
+    gprint (GP_ERR, "   output goes to $RA, $DEC\n");
+    return (FALSE);
+  }
+
+  if (strcmp (argv[1], "-hh") && strcmp (argv[1], "-hms")) {
+    gprint (GP_ERR, "USAGE:  radec (-hh | -hms) RA DEC [-q | -quiet]\n");
+    gprint (GP_ERR, "   convert to decimal (-hh) or sexigesimal (-hms)\n");
+    gprint (GP_ERR, "   output goes to $RA, $DEC\n");
+    return (FALSE);
+  }    
+
+  if (!ohana_str_to_radec (&ra, &dec, argv[2], argv[3])) {
+    return (FALSE);
+  }
+
+  if (!strcmp (argv[1], "-hh")) {
+    set_variable ("RA", ra);
+    set_variable ("DEC", dec);
+    if (!QUIET) {
+      gprint (GP_LOG, "%10.6f %10.6f\n", ra, dec);
+    }
+    return (TRUE);
+  }
+
+  // convert to hms, dms
+  hms_format (ra_string, ra/15.0);
+  hms_format (dec_string, dec);
+
+  set_str_variable ("RA", ra_string);
+  set_str_variable ("DEC", dec_string);
+  if (!QUIET) {
+    gprint (GP_LOG, "%s %s\n", ra_string, dec_string);
+  }
+  return (TRUE);
+}  
Index: /branches/sc_branches/pantasks_condor/cmd.astro/region.c
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.astro/region.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.astro/region.c	(revision 34783)
@@ -0,0 +1,156 @@
+# include "astro.h"
+
+int region (int argc, char **argv) {
+  
+  double Ra, Dec, Radius;
+  float dx, dy;
+  int N, kapa, NoClear, dXpix, dYpix;
+  char *name;
+  Graphdata graphmode;
+
+  name = NULL;
+  if ((N = get_argument (argc, argv, "-n"))) {
+    remove_argument (N, &argc, argv);
+    name = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+  if (!GetGraph (&graphmode, &kapa, name)) return (FALSE);
+  FREE (name);
+
+  NoClear = FALSE;
+  if ((N = get_argument (argc, argv, "-no-clear"))) {
+    remove_argument (N, &argc, argv);
+    NoClear = TRUE;
+  }
+
+  if ((N = get_argument (argc, argv, "-image"))) {
+    remove_argument (N, &argc, argv);
+    KapaGetImageCoords (kapa, &graphmode.coords);
+    KapaGetImageRange (kapa, &graphmode.xmin, &graphmode.xmax, &graphmode.ymax, &graphmode.ymin, &dXpix, &dYpix);
+
+    set_variable ("XMIN", graphmode.xmin);
+    set_variable ("XMAX", graphmode.xmax);
+    set_variable ("YMIN", graphmode.ymin);
+    set_variable ("YMAX", graphmode.ymax);
+
+    set_variable ("RMIN", Ra  + graphmode.xmin);
+    set_variable ("RMAX", Ra  + graphmode.xmax);
+    set_variable ("DMIN", Dec + graphmode.ymin);
+    set_variable ("DMAX", Dec + graphmode.ymax);
+
+    // if (!NoClear) KapaClearSections (kapa);
+    KapaSetLimits (kapa, &graphmode);
+
+    SetGraph (&graphmode);
+    return (TRUE);
+    // Set Region based on image
+  }
+
+  if ((N = get_argument (argc, argv, "-ew"))) {
+    remove_argument (N, &argc, argv);
+    graphmode.flipeast = TRUE;
+  }
+
+  if ((N = get_argument (argc, argv, "+ew"))) {
+    remove_argument (N, &argc, argv);
+    graphmode.flipeast = FALSE;
+  }
+
+  if ((N = get_argument (argc, argv, "-ns"))) {
+    remove_argument (N, &argc, argv);
+    graphmode.flipnorth = TRUE;
+  }
+
+  if ((N = get_argument (argc, argv, "+ns"))) {
+    remove_argument (N, &argc, argv);
+    graphmode.flipnorth = FALSE;
+  }
+
+  if ((argc != 4) && (argc != 5)) {
+    gprint (GP_ERR, "USAGE: region Ra Dec Radius [projection] [orientation]\n");
+    gprint (GP_ERR, "  [-image] [-ew] [+ew] [-ns] [+ns] [-no-clear]\n");
+    gprint (GP_ERR, " current: %f %f (%f x %f) (%s)\n", 
+	     graphmode.coords.crval1, graphmode.coords.crval2, 
+	     fabs(graphmode.xmax - graphmode.xmin), 
+	     fabs(graphmode.ymax - graphmode.ymin), 
+	     &graphmode.coords.ctype[5]);
+    return (FALSE);
+  }
+  
+  if (!ohana_str_to_radec (&Ra, &Dec, argv[1], argv[2])) return (FALSE);
+  Radius = atof (argv[3]);
+  strcpy (graphmode.coords.ctype, "RA---TAN");
+  if (argc == 5) {
+    if (!strcasecmp (argv[4], "TAN")) 
+      strcpy (graphmode.coords.ctype, "RA---TAN");
+    if (!strcasecmp (argv[4], "SIN")) 
+      strcpy (graphmode.coords.ctype, "RA---SIN");
+    if (!strcasecmp (argv[4], "ARC")) 
+      strcpy (graphmode.coords.ctype, "RA---ARC");
+    if (!strcasecmp (argv[4], "STG")) 
+      strcpy (graphmode.coords.ctype, "RA---STG");
+    if (!strcasecmp (argv[4], "ZEA"))
+      strcpy (graphmode.coords.ctype, "RA---ZEA");
+    if (!strcasecmp (argv[4], "AIT")) 
+      strcpy (graphmode.coords.ctype, "RA---AIT");
+    if (!strcasecmp (argv[4], "GLS")) 
+      strcpy (graphmode.coords.ctype, "RA---GLS");
+    if (!strcasecmp (argv[4], "PAR")) 
+      strcpy (graphmode.coords.ctype, "RA---PAR");
+  }
+
+  
+  /* ask kapa for coordinate limits, to get the right aspect ratio */
+  KapaGetLimits (kapa, &dx, &dy);
+  dx = fabs (dx);
+  dy = fabs (dy); 
+
+  /* define limits for Ra, Dec at center, grid in degrees */
+  if (dy < dx) {
+    graphmode.xmin = -(dx/dy)*Radius;
+    graphmode.ymin = -Radius;
+    graphmode.xmax = (dx/dy)*Radius;
+    graphmode.ymax = Radius;
+  } else {
+    graphmode.xmin = -Radius;
+    graphmode.ymin = -(dy/dx)*Radius;
+    graphmode.xmax = Radius;
+    graphmode.ymax = (dy/dx)*Radius;
+  } 
+
+  set_variable ("XMIN", graphmode.xmin);
+  set_variable ("XMAX", graphmode.xmax);
+  set_variable ("YMIN", graphmode.ymin);
+  set_variable ("YMAX", graphmode.ymax);
+
+  set_variable ("RMIN", Ra  + graphmode.xmin);
+  set_variable ("RMAX", Ra  + graphmode.xmax);
+  set_variable ("DMIN", Dec + graphmode.ymin);
+  set_variable ("DMAX", Dec + graphmode.ymax);
+
+  set_int_variable ("EAST_RIGHT", !graphmode.flipeast);
+  set_int_variable ("NORTH_UP", !graphmode.flipnorth);
+
+  graphmode.coords.pc1_1 = (graphmode.flipeast) ? -1 : 1;
+  graphmode.coords.pc2_2 = (graphmode.flipnorth) ? -1 : 1;
+
+  graphmode.coords.pc1_2 = graphmode.coords.pc2_1 = 0.0;
+  graphmode.coords.crval1 = Ra;
+  graphmode.coords.crval2 = Dec;
+  graphmode.coords.crpix1 = 0.0;
+  graphmode.coords.crpix2 = 0.0;
+  graphmode.coords.cdelt1 = graphmode.coords.cdelt2 = 1.0;
+
+  if (!NoClear) KapaClearSections (kapa);
+  KapaSetLimits (kapa, &graphmode);
+
+  /* drop this? */
+  // sprintf (string, "%8.4f %8.4f (%f)", Ra, Dec, Radius);
+  // KapaSendLabel (kapa, string, 2);
+
+  // XXX is this the right thing to be doing?
+  SetGraph (&graphmode);
+  return (TRUE);
+}
+
+
Index: /branches/sc_branches/pantasks_condor/cmd.astro/ringflux.c
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.astro/ringflux.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.astro/ringflux.c	(revision 34783)
@@ -0,0 +1,100 @@
+# include "astro.h"
+
+# define FLUX(BUF,X,Y) {						\
+	char valid = TRUE;						\
+	valid &= (X >= 0);						\
+	valid &= (Y >= 0);						\
+	valid &= (X < Nx);						\
+	valid &= (Y < Ny);						\
+	if (valid) {							\
+	    out[Nvec] = BUF[(int)(X) + ((int)(Y))*Nx];			\
+	    Nvec++;							\
+	    if (Nvec >= vec[0].Nelements) abort();			\
+	}								\
+    }
+
+# define C1 0
+
+int ringflux (int argc, char **argv) {
+  
+  int Nx, Ny, x, y, d, radius, Radius, dRadius, Nvec;
+  double *out;
+  float *in;
+  float Xo, Yo;
+  Buffer *buf;
+  Vector *vec;
+
+  if (argc != 7) {
+    gprint (GP_ERR, "USAGE: ringflux (buffer) (vector) (Xo) (Yo) (Radius) (dRadius)\n");
+    return (FALSE);
+  }
+
+  /* select input / output buffers */
+  if ((buf = SelectBuffer (argv[1], OLDBUFFER, TRUE)) == NULL) return (FALSE);
+  if ((vec = SelectVector (argv[2], ANYVECTOR, TRUE)) == NULL) return (FALSE);
+
+  Nx = buf[0].header.Naxis[0];
+  Ny = buf[0].header.Naxis[1];
+
+  Xo = atof(argv[3]);
+  Yo = atof(argv[4]);
+
+  Radius = atoi (argv[5]);
+  dRadius = atoi (argv[6]);
+
+  // just a rough guess at the number needed
+  ResetVector (vec, OPIHI_FLT, MAX (6*Radius*dRadius, 8));
+
+  Nvec = 0;
+  for (radius = Radius; radius < Radius + dRadius; radius ++) {
+
+    x = 0;
+    y = radius;
+
+# if (C1)
+    d = 3 - 2*radius;
+# else
+    d = 5 - 4*radius;
+# endif
+
+    in = (float *) buf[0].matrix.buffer;
+    out = vec[0].elements.Flt;
+
+    while (x <= y) {
+      FLUX(in, (Xo + x), (Yo + y));
+      FLUX(in, (Xo + x), (Yo - y));
+      FLUX(in, (Xo + y), (Yo + x));
+      FLUX(in, (Xo - y), (Yo + x));
+
+      if (x > 0) {
+	FLUX(in, (Xo - x), (Yo + y));
+	FLUX(in, (Xo - x), (Yo - y));
+	FLUX(in, (Xo - y), (Yo - x));
+	FLUX(in, (Xo + y), (Yo - x));
+      }
+      if (Nvec >= vec[0].Nelements - 8) {
+	  // counting error here
+	ResetVector (vec, OPIHI_FLT, vec[0].Nelements + 64);
+	out = vec[0].elements.Flt;
+      }
+
+      if (d < 0) {
+# if (C1)      
+	d = d + 4*x + 6;
+# else
+	d = d + 8*x + 4;
+# endif
+      } else {
+# if (C1)      
+	d = d + 4*(x-y) + 10;
+# else
+	d = d + 8*(x-y) + 8;
+# endif
+	y--;
+      }
+      x++;
+    }
+  }
+  vec[0].Nelements = Nvec;
+  return (TRUE);
+}
Index: /branches/sc_branches/pantasks_condor/cmd.astro/rotcurve.c
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.astro/rotcurve.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.astro/rotcurve.c	(revision 34783)
@@ -0,0 +1,120 @@
+# include "astro.h"
+
+int rotcurve (int argc, char **argv) {
+  
+  int i, j, X, Y, n, Ncurve;
+  float *Vin, *Vout, *Vmask;
+  int nx, ny, Nx, Ny, N;
+  double L, dL, Lo, V, Vo, dV, Bo, dB, Do, dD;
+  double xo, yo, Xo, Yo;
+  double sl, cl, wo, Ro, Rs, wr, r, d, min;
+  double R[100], T[100], W[100];
+  FILE *f;
+  Buffer *in, *out, *mask;
+
+  min = -1000;
+  if ((N = get_argument (argc, argv, "-min"))) {
+    remove_argument (N, &argc, argv);
+    min = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  if (argc != 5) {
+    gprint (GP_ERR, "USAGE: rotcurve in out mask curve.txt\n");
+    return (FALSE);
+  }
+
+  f = fopen (argv[4], "r");
+  if (f == (FILE *) NULL) {
+    gprint (GP_ERR, "can't find rotation curve data file %s\n", argv[4]);
+    return (FALSE);
+  }
+  for (i = 0; fscanf (f, "%lf %lf", &R[i], &T[i]) != EOF; i++) {
+    W[i] = T[i] / R[i];
+  }  
+  fclose (f);
+  Ncurve = i;
+
+  if ((in   = SelectBuffer (argv[1], OLDBUFFER, TRUE)) == NULL) return (FALSE);
+  if ((out  = SelectBuffer (argv[2], OLDBUFFER, TRUE)) == NULL) return (FALSE);
+  if ((mask = SelectBuffer (argv[3], OLDBUFFER, TRUE)) == NULL) return (FALSE);
+
+  Nx = out[0].matrix.Naxis[0];
+  Ny = out[0].matrix.Naxis[1];
+  nx = mask[0].matrix.Naxis[0];
+  ny = mask[0].matrix.Naxis[1];
+  if ((Nx != nx) && (Ny != ny)) {
+    gprint (GP_ERR, "output and mask must have same dimensions\n");
+    return (FALSE);
+  }
+  nx = in[0].matrix.Naxis[0];
+  ny = in[0].matrix.Naxis[1];
+
+  /* we expect the input image to have units of velocity, lattitude, and longitude */
+  gfits_scan (&in[0].header, "CRVAL1", "%lf", 1, &Vo);
+  gfits_scan (&in[0].header, "CDELT1", "%lf", 1, &dV);
+  gfits_scan (&in[0].header, "CRPIX1", "%lf", 1, &xo);
+  gfits_scan (&in[0].header, "CRVAL2", "%lf", 1, &Bo);
+  gfits_scan (&in[0].header, "CDELT2", "%lf", 1, &dB);
+  gfits_scan (&in[0].header, "CRPIX2", "%lf", 1, &yo);
+  gfits_scan (&in[0].header, "CRVAL3", "%lf", 1, &L);
+  Vo *= 0.001;
+  dV *= 0.001;
+
+  /* we expect the output image to have units of longitude and distance */
+  gfits_scan (&out[0].header, "CRVAL1", "%lf", 1, &Lo);
+  gfits_scan (&out[0].header, "CDELT1", "%lf", 1, &dL);
+  gfits_scan (&out[0].header, "CRPIX1", "%lf", 1, &Xo);
+  gfits_scan (&out[0].header, "CRVAL2", "%lf", 1, &Do);
+  gfits_scan (&out[0].header, "CDELT2", "%lf", 1, &dD);
+  gfits_scan (&out[0].header, "CRPIX2", "%lf", 1, &Yo);
+
+  L = ohana_normalize_angle (L);
+
+  X = (L - Lo) / dL + Xo;
+  if ((X >= Nx) || (X < 0)) {
+    gprint (GP_ERR, "X out of range\n");
+    return (FALSE);
+  }
+  gprint (GP_ERR, "L: %f (%d)\n", L, X);
+
+  cl = cos (L*RAD_DEG);
+  sl = sin (L*RAD_DEG);
+  wo = 25.0;
+  Ro = 10.0;
+  Rs = Ro*sl;
+  /* this method depends on wr monotonically decreasing */
+
+  Vin  = (float *)in[0].matrix.buffer;
+  Vout = (float *)out[0].matrix.buffer;
+  Vmask = (float *)mask[0].matrix.buffer;
+  for (j = 0; j < ny; j++) {
+    for (i = 0; i < nx; i++, Vin++) {
+      if (*Vin <= min) continue;
+      V = (i - xo) * dV + Vo;
+      wr = V/Rs + wo;
+      for (n = 0; (n < Ncurve) && (wr < W[n]); n++);
+      if ((n == 0) || (n == Ncurve)) {
+	continue;
+      }
+      r = (wr - W[n]) *  (R[n-1] - R[n]) / (W[n-1] - W[n]) + R[n];
+      // fr = (Ro/r);
+      if (r < fabs(Rs)) { /* can't be on rotation curve */
+	continue;
+      }
+      if (r < Ro)
+	d = Ro*cl - sqrt(r*r - Rs*Rs);
+      else 
+	d = Ro*cl + sqrt(r*r - Rs*Rs);
+      Y = (d - Do) / dD + Yo;
+      if ((Y < Ny) && (Y >= 0)) {
+	Vout[Y*Nx + X] += *Vin;
+	Vmask[Y*Nx + X] += 1.0;
+      }
+    }
+  }
+
+
+  return (TRUE);
+
+} 
Index: /branches/sc_branches/pantasks_condor/cmd.astro/scale.c
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.astro/scale.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.astro/scale.c	(revision 34783)
@@ -0,0 +1,43 @@
+# include "astro.h"
+
+int scale (int argc, char **argv) {
+
+  Buffer *buf;
+
+  if (argc != 5) {
+    gprint (GP_ERR, "USAGE: scale (buffer) (key) [-r/-w] (value)\n");
+    return (FALSE);
+  }  
+
+  if ((buf = SelectBuffer (argv[1], OLDBUFFER, TRUE)) == NULL) return (FALSE);
+
+  if (strcasecmp (argv[2], "bzero") && strcasecmp (argv[2], "bscale")) {
+    gprint (GP_ERR, "use bzero or bscale only\n");
+    return (FALSE);
+  }
+    
+  if (strcmp (argv[3], "-r") && strcmp (argv[3], "-w")) {
+    gprint (GP_ERR, "use -r or -w only\n");
+    return (FALSE);
+  }
+    
+  if (!strcmp (argv[3], "-r")) {
+    if (!strcasecmp (argv[2], "bzero")) {
+      set_variable (argv[4], (double) buf[0].bzero);
+    } else {
+      set_variable (argv[4], (double) buf[0].bscale);
+    }      
+  } else {
+    if (!strcasecmp (argv[2], "bzero")) {
+      buf[0].bzero = atof (argv[4]);
+    } else {
+      buf[0].bscale = atof (argv[4]);
+    }      
+  }
+
+  return (TRUE);
+}
+
+/* get or set external bzero / bscale values 
+   (these keywords are set to 0,1 internally, 
+   so we can't just manipulate them like other keywords */
Index: /branches/sc_branches/pantasks_condor/cmd.astro/sexigesimal.c
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.astro/sexigesimal.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.astro/sexigesimal.c	(revision 34783)
@@ -0,0 +1,49 @@
+# include "astro.h"
+
+int sexigesimal (int argc, char **argv) {
+  
+  int HMS, N;
+  double value;
+  char string[80];
+
+  HMS = TRUE;
+  if ((N = get_argument (argc, argv, "-hms"))) {
+    HMS = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+  if ((N = get_argument (argc, argv, "-hh"))) {
+    HMS = FALSE;
+    remove_argument (N, &argc, argv);
+  }
+
+  if ((argc != 3) && (argc != 2)) {
+    gprint (GP_ERR, "USAGE: sexigesimal (from) [to]\n");
+    return (FALSE);
+  }
+
+  if (HMS) {
+    if (!ohana_dms_to_ddd (&value, argv[1])) {
+      gprint (GP_ERR, "syntax error in input\n");
+      return (FALSE);
+    }
+    if (argc == 3) {
+      set_variable (argv[2], value);
+    } else {
+      gprint (GP_LOG, "%10.6f\n", value);
+    }
+    return (TRUE);
+  } else {
+    value = atof (argv[1]);
+    hms_format (string, value);
+    if (argc == 3) {
+      set_str_variable (argv[2], string);
+    } else {
+      gprint (GP_LOG, "%s\n", string);
+    }
+    return (TRUE);
+  }      
+
+  return (TRUE);
+
+}
+
Index: /branches/sc_branches/pantasks_condor/cmd.astro/spec.c
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.astro/spec.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.astro/spec.c	(revision 34783)
@@ -0,0 +1,110 @@
+# include "astro.h"
+
+int spec (int argc, char **argv) {
+
+  int i, j, Xo, X1, y1, y2, Nx;
+  int Nlong, Ngap, Nrow, N, Nring;
+  float *buffer, *V;
+  double sky, sky2, S, SX, F, R, Npts;
+  Vector *xvec, *yvec;
+  Buffer *buf;
+
+  Nlong = 31;
+  if ((N = get_argument (argc, argv, "-Nlong"))) {
+    remove_argument (N, &argc, argv);
+    Nlong  = 0.5*atof(argv[N]);
+    Nlong = 2*Nlong + 1;  /* force an odd number */
+    remove_argument (N, &argc, argv);
+  }
+  
+  Ngap = 15;
+  if ((N = get_argument (argc, argv, "-Ngap"))) {
+    remove_argument (N, &argc, argv);
+    Ngap  = 0.5*atof(argv[N]);
+    Ngap = 2*Ngap + 1;  /* force an odd number */
+    remove_argument (N, &argc, argv);
+  }
+  
+  Nrow = 1;
+  if ((N = get_argument (argc, argv, "-Nrow"))) {
+    remove_argument (N, &argc, argv);
+    Nrow  = atof(argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+  
+  if (argc != 7) {
+    gprint (GP_ERR, "USAGE: spec buffer x y1 y2 X Y [-Nlong N] [-Ngap N] [-Nrow N]\n");
+    return (FALSE);
+  }
+  
+  if ((Nrow < 1) || (Nlong < 2) || (Ngap < 1) || (Nlong - Ngap < 2)) {
+    gprint (GP_ERR, "bad values for options\n");
+    return (FALSE);
+  }
+
+  if ((buf = SelectBuffer (argv[1], OLDBUFFER, TRUE)) == NULL) return (FALSE);
+  Nx = buf[0].matrix.Naxis[0];
+  // int Ny = buf[0].matrix.Naxis[1];
+
+  Xo = atof (argv[2]);
+  y1 = atof (argv[3]);
+  y2 = atof (argv[4]);
+
+  if ((xvec = SelectVector (argv[5], ANYVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((yvec = SelectVector (argv[6], ANYVECTOR, TRUE)) == NULL) return (FALSE);
+
+  N = y2 - y1;
+  ResetVector (xvec, OPIHI_FLT, N);
+  ResetVector (yvec, OPIHI_FLT, N);
+  
+  ALLOCATE (buffer, float, Nlong);
+
+  for (j = 0; j < y2 - y1; j++) {
+    V = (float *) (buf[0].matrix.buffer) + Nx*(y1 + j) + Xo - (int)(0.5*Nlong);
+    /* find sky on edge */
+    for (i = 0, Nring = 0; i < 0.5*(Nlong - Ngap); i++, V++, Nring++) {
+      buffer[i] = *V;
+    }
+    fsort (buffer, Nring);
+    for (Npts = sky = 0, i = 0.25*Nring; i < 0.75*Nring; i++, Npts += 1.0) {
+      sky += buffer[i];
+    }
+    sky = sky / Npts;
+    /* find center column for this row */
+    for (S = SX = i = 0, Nring = 0; i < Ngap; i++, V++, Nring++) {
+      S += (*V - sky);
+      SX += (*V - sky)*(i + Xo - 0.5*Ngap);
+    }
+    X1 = SX / S;
+    gprint (GP_ERR, "%4d %4d %5.1f ", j+y1, X1, sky);
+    /*    X1 = MAX (MIN (X1, Xo + 0.5+Ngap), Xo - 0.5+Ngap); */
+    V = (float *) (buf[0].matrix.buffer) + Nx*(y1 + j) + X1 - (int)(0.5*Nlong);
+    /* find sky on edges */
+    for (i = 0, Nring = 0; i < 0.5*(Nlong - Ngap); i++, V++, Nring++) {
+      buffer[Nring] = *V;
+    }
+    V = (float *) (buf[0].matrix.buffer) + Nx*(y1 + j) + X1 + (int)(0.5*Ngap);
+    for (i = 0; i < 0.5*(Nlong - Ngap); i++, V++, Nring++) {
+      buffer[Nring] = *V;
+    }
+    fsort (buffer, Nring);
+    for (Npts = sky = sky2 = 0, i = 0.25*Nring; i < 0.75*Nring; i++, Npts += 1.0) {
+      sky += buffer[i];
+      sky2 += buffer[i]*buffer[i];
+    }
+    sky = sky / Npts;
+    sky2 = (sky2 / Npts - sky*sky);
+    /* find weighted flux */
+    V = (float *) (buf[0].matrix.buffer) + Nx*(y1 + j) + X1 - (int)(0.5*Ngap);
+    for (F = R = i = 0; i < Ngap; i++, V++) {
+      F += (*V - sky) / sky2;
+      R += 1.0 / sky2;
+    }
+    xvec[0].elements.Flt[j] = j + y1; 
+    yvec[0].elements.Flt[j] = F / R; 
+    gprint (GP_ERR, " %5.1f %7.1f  %6.2f\n", sky, sky2, (F/R));
+  }    
+
+  free (buffer);
+  return (TRUE);
+}
Index: /branches/sc_branches/pantasks_condor/cmd.astro/specpairfit.c
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.astro/specpairfit.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.astro/specpairfit.c	(revision 34783)
@@ -0,0 +1,61 @@
+# include "astro.h"
+
+int specpairfit (int argc, char **argv) {
+  
+  int i;
+  Vector *flux1, *flux2, *dflux1, *dflux2, *window;
+
+  if (argc != 7) goto usage;
+
+  if ((flux1  = SelectVector (argv[1], OLDVECTOR, TRUE)) == NULL) goto escape;
+  if ((dflux1 = SelectVector (argv[2], OLDVECTOR, TRUE)) == NULL) goto escape;
+  if ((flux2  = SelectVector (argv[3], OLDVECTOR, TRUE)) == NULL) goto escape;
+  if ((dflux2 = SelectVector (argv[4], OLDVECTOR, TRUE)) == NULL) goto escape;
+  if ((window = SelectVector (argv[5], OLDVECTOR, TRUE)) == NULL) goto escape;
+  
+  // XXX enforce matching lengths on the 6 vectors
+
+  int mask = atoi (argv[6]);
+  CastVector (window, OPIHI_INT);
+
+  // minimize (flux1 - flux2*alpha) in window defined by mask
+  // note that the mask is a SELECTION mask not an EXCLUSION mask
+
+  double F12 = 0.0; 
+  double F22 = 0.0;
+  for (i = 0; i < flux1->Nelements; i++) {
+    if ((mask & window->elements.Int[i]) == 0) continue;
+    double weight = 1.0 / (SQ(dflux1->elements.Flt[i]) + SQ(dflux2->elements.Flt[i]));
+    F12 += flux1->elements.Flt[i] * flux2->elements.Flt[i] * weight;
+    F22 += flux2->elements.Flt[i] * flux2->elements.Flt[i] * weight;
+  }
+
+  double Ao = F12 / F22;
+  double dA = sqrt(1.0 / F22);
+
+  int Ndof = -1; // 1 parameter fit
+  double chisq = 0.0;
+  for (i = 0; i < flux1->Nelements; i++) {
+    if ((mask & window->elements.Int[i]) == 0) continue;
+    double weight = 1.0 / (SQ(dflux1->elements.Flt[i]) + SQ(dflux2->elements.Flt[i]));
+    chisq += SQ(flux1->elements.Flt[i] - Ao * flux2->elements.Flt[i]) * weight;
+    Ndof ++;
+  }
+
+  double chisqNu = chisq / Ndof;
+
+  // fprintf (stderr, "Ao: %f +/- %f, chisq: %f, chisq_nu : %f for %d dof\n", Ao, dA, chisq, chisqNu, Ndof);
+  set_variable ("Ao", Ao);
+  set_variable ("dA", dA);
+  set_variable ("Xv", chisqNu);
+  set_variable ("Nd", Ndof);
+  return (TRUE);
+  
+ escape: 
+  gprint (GP_ERR, "invalid vector\n");
+  return (FALSE);
+  
+usage:
+  gprint (GP_ERR, "USAGE: specpairfit (flux1) (dflux1) (flux2) (dflux2) (window) (mask) [options]\n");
+  return FALSE;
+}
Index: /branches/sc_branches/pantasks_condor/cmd.astro/spex1dgas.c
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.astro/spex1dgas.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.astro/spex1dgas.c	(revision 34783)
@@ -0,0 +1,438 @@
+# include "astro.h"
+
+/* We have N objects with N(N-1)/2 paired distances.  We want to find the 1D distribution that
+   best describes the observed distances.  Assume the distances are in a 1D space, though in
+   principle it could be N-D or even non-euclidean.
+
+   Model the points as a gas under pressure.  
+
+   Start with the a guess for positions of the objects at some locations in 2D
+
+   P = (d_now - d_tru)
+
+   Iterate over the points and move in X,Y
+
+   Find (dP/dX,dP/dY) for modest moves in X,Y
+
+   Move based on dP
+
+   USAGE: spex1dgas incdex1 index2 distance
+
+   outline:
+
+   * load data
+   * generate the unique objects
+   * determine the max distance needed
+   * place each object in the 2D space
+
+   * iterate:
+   ** calculate dP/dX,dPdY for each object
+   ** move each object proportionally to the pressure gradient
+
+   */
+
+typedef struct {
+  int Nindex;   // number of tested relationships
+  int NINDEX;   // number of allocated relationships
+  int *index;  // indices for all other objects
+  float *Dtgt; // target distance for this relationship
+  float *Dcur; // current distance for this relationship
+  float Xo,Yo; // current X,Y position of this object
+  float dPdX;  // pressure in X
+  float dPdY;  // pressure in Y
+} Object;
+
+static Object *object = NULL;
+static int Nobject = 0;
+// static int NOBJECT = 0;
+
+void sortfriends (float *X, int *IDX1, int N);
+
+static void get_pressure_gradient (int iObj, int nCloseMax, float farFrac, int nearNeighbors, float maxPressure) {
+
+  int i;
+  
+  float dPdX = 0.0;
+
+  // only use the first N friends
+  for (i = 0; (i < object[iObj].Nindex); i++) {
+    int jObj = object[iObj].index[i];
+
+    // some options:
+    // if the iterations are small, we should only worry about getting the near neighbors right
+    // if the iterations are large, we should add in more distant objects
+    if (nearNeighbors && (i >= nCloseMax)) break;
+    if (!nearNeighbors && (i >= nCloseMax)) {
+      if (drand48() > farFrac) continue;
+    }
+
+    float Dtgt = object[iObj].Dtgt[i];
+    float dX = object[jObj].Xo - object[iObj].Xo;
+    float Dcur = dX;
+
+    // the force law as a function of (Dcur - Dtgt) : if Dcur is too large, dF is negative
+    // float dF = (Dcur < 0.01*Dtgt) ? -100.0 : (Dcur - Dtgt) / Dcur; XXX modified spring constant : too crazy
+    float dF = (Dcur - Dtgt);
+    dF = MIN (maxPressure, MAX (-maxPressure, dF));
+
+    float dPdXi;
+    if (fabs(Dcur) < 1e-6) {
+      dPdXi = 0.0;
+    } else {
+      dPdXi = dF;
+    }
+
+    if (isnan(Dtgt) || isnan(dX) || isnan(Dcur) || isnan(dF) || isnan(dPdXi)) abort();
+    dPdX += dPdXi;
+  }
+  object[iObj].dPdX = dPdX;
+  object[iObj].dPdY = 0.0;
+
+  return;
+}
+
+static void move_object (int iObj) {
+
+  object[iObj].Xo += 0.25*object[iObj].dPdX;
+  return;
+}
+
+int spex1dgas (int argc, char **argv) {
+  
+  int i, j, iter;
+  Vector *index1, *index2, *distance;
+  float *XoList;
+  int *IDList, *MidObj;
+
+  MidObj = NULL;
+  IDList = NULL;
+  XoList = NULL;
+  { 
+    // init random numbers
+    long A, B;
+    A = time(NULL);
+    for (B = 0; A == time(NULL); B++);
+    srand48(B);
+  }
+ 
+  if (argc != 11) goto usage;
+
+  // XXX enforce matching lengths on the three vectors
+  if ((index1   = SelectVector (argv[1], OLDVECTOR, TRUE)) == NULL) goto escape;
+  if ((index2   = SelectVector (argv[2], OLDVECTOR, TRUE)) == NULL) goto escape;
+  if ((distance = SelectVector (argv[3], OLDVECTOR, TRUE)) == NULL) goto escape;
+
+  int Niter = atoi (argv[4]);
+  int nCloseMax = atoi (argv[5]);
+  int nCloseIter = atoi (argv[6]);
+  float farFrac = atof (argv[7]);
+  float maxPressure = atof (argv[8]);
+
+  int idx1 = atoi(argv[9]);
+  int idx2 = atoi(argv[10]);
+
+  CastVector (index1, OPIHI_INT);
+  CastVector (index2, OPIHI_INT);
+
+  // how many objects do we have?
+  Nobject = 0;
+  for (i = 0; i < index1->Nelements; i++) {
+    Nobject = MAX (Nobject, index1->elements.Int[i]);
+    Nobject = MAX (Nobject, index2->elements.Int[i]);
+  }
+  Nobject ++;  // after the loop, Nobject has the value of the highest index, not the count
+
+  // allocate the list of Object -- these list the possible friends of the given object[i] 
+  ALLOCATE (object, Object, Nobject);
+  for (i = 0; i < Nobject; i++) {
+    ALLOCATE (object[i].index, int, Nobject);
+    memset (object[i].index, 0, Nobject*sizeof(int));
+    ALLOCATE (object[i].Dtgt, float, Nobject);
+    memset (object[i].Dtgt, 0, Nobject*sizeof(float));
+    ALLOCATE (object[i].Dcur, float, Nobject);
+    memset (object[i].Dcur, 0, Nobject*sizeof(float));
+    object[i].NINDEX = Nobject;
+    object[i].Nindex = 0;
+  }
+
+  // generate the set of vectors of all distances for each entry
+  // the object seq number (object[i]) will be used to match the index1 and index2 values
+  for (i = 0; i < index1->Nelements; i++) {
+    int N, m;
+
+    N = index1->elements.Int[i];
+    if (N >= Nobject) abort();
+    m = object[N].Nindex;
+    object[N].index[m] = index2->elements.Int[i];
+    object[N].Dtgt[m] = distance->elements.Flt[i];
+    object[N].Dcur[m] = 0.0;
+    object[N].Nindex ++;
+    if (object[N].Nindex == object[N].NINDEX) {
+      object[N].NINDEX += 100;
+      REALLOCATE (object[N].Dtgt, float, object[N].NINDEX);
+      REALLOCATE (object[N].Dcur, float, object[N].NINDEX);
+      REALLOCATE (object[N].index, int, object[N].NINDEX);
+    }
+
+    // Above we used the set (N,M,D_N,M) to assigned the distance of obj M to obj N.
+    // Below we use that set to assign the distance of obj N to obj M.  IF we supply all
+    // pairwise distances, then this duplicates all entries, so we need to skip that step
+    // in such a case.
+
+    // XX N = index2->elements.Int[i];
+    // XX if (N >= Nobject) abort();
+    // XX m = object[N].Nindex;
+    // XX object[N].index[m] = index1->elements.Int[i];
+    // XX object[N].Dtgt[m] = distance->elements.Flt[i];
+    // XX object[N].Dcur[m] = 0.0;
+    // XX object[N].Nindex ++;
+    // XX if (object[N].Nindex == object[N].NINDEX) {
+    // XX   object[N].NINDEX += 100;
+    // XX   REALLOCATE (object[N].Dtgt, float, object[N].NINDEX);
+    // XX   REALLOCATE (object[N].Dcur, float, object[N].NINDEX);
+    // XX   REALLOCATE (object[N].index, int, object[N].NINDEX);
+    // XX }
+  }
+
+  for (i = 0; i < Nobject; i++) {
+    // sort so closest friends are first
+    sortfriends (object[i].Dtgt, object[i].index, object[i].Nindex);
+  }
+
+  // find and save the max distance
+  float Dmax = 0;
+  for (i = 0; i < distance->Nelements; i++) {
+    Dmax = MAX (Dmax, distance->elements.Flt[i]);
+  }
+
+  // place the objects at the initial guess locations
+  // XXX let's try with just a simple grid dividing up the max range
+  // int Ngrid = sqrt(Nobject);
+  // float dgrid = 1.5 * Dmax / Ngrid; // XXX remove the fudge factor
+  // XXX for (i = 0; i < Nobject; i++) {
+  // XXX   // object[i].Xo = dgrid * (int) (i % Ngrid);
+  // XXX   // object[i].Yo = dgrid * (int) (i / Ngrid);
+  // XXX   object[i].Xo = Dmax*drand48();
+  // XXX   object[i].Yo = Dmax*drand48();
+  // XXX   object[i].dPdX = 0.0;
+  // XXX   object[i].dPdY = 0.0;
+  // XXX }
+
+  // place the objects at the initial guess locations
+  // use object 0 and its most distant friend to constrain:
+
+  // we are going to use the projected X distance as the location in X, and ignore the Y
+  // coordinate
+  
+  // need to get pretty close to a good starting point.  start with a guess using the first entry & its extremum
+  // int idx1 = object[0].index[(int)(0.75*object[0].Nindex)];
+  // int idx2 = object[idx1].index[(int)(0.75*object[idx1].Nindex)];
+  float A = NAN;
+  for (i = 0; isnan (A) && (i < object[idx2].Nindex); i++) {
+      if (object[idx2].index[i] == idx1) {
+	  A = object[idx2].Dtgt[i];
+      }
+  }
+  fprintf (stderr, "idx1: %d, idx2: %d, A: %f\n", idx1, idx2, A);
+  set_variable ("SP1D_A", A);
+  object[idx1].Xo = 0.0;
+  object[idx1].Yo = 0.0;
+  object[idx1].dPdX = 0.0;
+  object[idx1].dPdY = 0.0;
+
+  object[idx2].Xo = A;
+  object[idx2].Yo = 0.0;
+  object[idx2].dPdX = 0.0;
+  object[idx2].dPdY = 0.0;
+		 
+  Vector *tmpB = SelectVector ("sp1d_B", ANYVECTOR, TRUE); 
+  Vector *tmpC = SelectVector ("sp1d_C", ANYVECTOR, TRUE); 
+  ResetVector (tmpB, OPIHI_FLT, Nobject);
+  ResetVector (tmpC, OPIHI_FLT, Nobject);
+
+  for (i = 0; i < Nobject; i++) {
+    if (i == idx1) continue;
+    if (i == idx2) continue;
+    float B = NAN;
+    float C = NAN;
+    for (j = 0; (isnan(B) || isnan(C)) && (j < object[i].Nindex); j++) {
+      if (object[i].index[j] == idx1) { B = object[i].Dtgt[j]; }
+      if (object[i].index[j] == idx2) { C = object[i].Dtgt[j]; }
+    }
+    if (isnan(B) || isnan(C)) abort();
+
+    // C^2 = A^2 + B^2 - 2AB cos(t)
+    // Xo = B cos(t)
+    // Xo = A^2 + B^2 - C^2 / 2 A
+    float Xo = (SQ(A) + SQ(B) - SQ(C)) / (2*A);
+
+    object[i].Xo = Xo;
+    object[i].dPdX = 0.0;
+    object[i].Yo = 0.0;
+    object[i].dPdY = 0.0;
+    tmpB->elements.Flt[i] = B;
+    tmpC->elements.Flt[i] = C;
+  }
+  tmpB->elements.Flt[idx1] = 0;
+  tmpB->elements.Flt[idx2] = A;
+  tmpC->elements.Flt[idx1] = A;
+  tmpC->elements.Flt[idx2] = 0;
+
+  if (Niter >= -1) {
+    // now choose several in the middle of the range, and find the mean distance for each relative to that group
+    ALLOCATE (XoList, float, Nobject);
+    ALLOCATE (IDList, int, Nobject);
+    for (i = 0; i < Nobject; i++) {
+      XoList[i] = object[i].Xo;
+      IDList[i] = i;
+    }
+    sortfriends (XoList, IDList, Nobject);
+    float XoMid = XoList[(int)(Nobject/2)];
+
+# define NMID 5
+    ALLOCATE (MidObj, int, NMID);
+    MidObj[0] = IDList[(int)(Nobject/2) + 0];
+    MidObj[1] = IDList[(int)(Nobject/2) + 1];
+    MidObj[2] = IDList[(int)(Nobject/2) - 1];
+    MidObj[3] = IDList[(int)(Nobject/2) + 2];
+    MidObj[4] = IDList[(int)(Nobject/2) - 2];
+
+    for (i = 0; i < Nobject; i++) {
+      for (j = 0; j < NMID; j++) {
+	if (MidObj[j] == i) goto skip_object;
+      }
+      float Dmean = 0.0;
+      for (j = 0; j < NMID; j++) {
+	Dmean += object[i].Dtgt[MidObj[j]]; // desired distance between object (i) and object in middle section
+      }
+      Dmean = Dmean / NMID;
+      if (object[i].Xo < XoMid) {
+	object[i].Xo = XoMid - Dmean;
+      } else {
+	object[i].Xo = XoMid + Dmean;
+      }
+    skip_object:
+      continue;
+    }
+  }
+
+  if (Niter >= 0) {
+    float XoMean = 0.0;
+    // now choose one end of the range, and find the mean distance for each relative to THAT group
+    for (i = 0; i < Nobject; i++) {
+      XoList[i] = object[i].Xo;
+      IDList[i] = i;
+      XoMean += object[i].Xo;
+    }
+    XoMean = XoMean / Nobject;
+    sortfriends (XoList, IDList, Nobject);
+    float XoMin = XoList[0];
+
+    MidObj[0] = IDList[0];
+    MidObj[1] = IDList[1];
+    MidObj[2] = IDList[2];
+    MidObj[3] = IDList[3];
+    MidObj[4] = IDList[4];
+
+    for (i = 0; i < Nobject; i++) {
+      for (j = 0; j < NMID; j++) {
+	if (MidObj[j] == i) goto skip_object_p2;
+      }
+      float Dmean = 0.0;
+      for (j = 0; j < NMID; j++) {
+	Dmean += object[i].Dtgt[MidObj[j]]; // desired distance between object (i) and object in middle section
+      }
+      Dmean = Dmean / NMID;
+      if (object[i].Xo < XoMin) {
+	object[i].Xo = XoMin - Dmean;
+      } else {
+	object[i].Xo = XoMin + Dmean;
+      }
+    skip_object_p2:
+      continue;
+    }
+  }
+
+  for (iter = 0; iter < Niter; iter ++) {
+    fprintf (stderr, "iter %d\n", iter);
+
+    int nearNeighbors = (iter < nCloseIter);
+
+    // save the result
+    if (0) {
+      char name[64];
+      snprintf (name, 64, "output.%02d.dat", iter);
+      FILE *output = fopen (name, "w");
+      for (i = 0; i < Nobject; i++) {
+	fprintf (output, "%f %f : %f %f\n", object[i].Xo, object[i].Yo, object[i].dPdX, object[i].dPdY);
+      }
+      fclose (output);
+    }
+
+    // measure (dP/dX) & move object
+    for (i = 0; i < Nobject; i++) {
+      get_pressure_gradient (i, nCloseMax, farFrac, nearNeighbors, maxPressure);
+      move_object (i);
+    }
+  }
+
+  // find the mean Xo, Yo positions for all objects
+  float XoSum = 0.0;
+  for (i = 0; i < Nobject; i++) {
+    float dY2sum = 0.0;
+    int Npts = 0;
+    for (j = 0; (j < nCloseMax) && (j < object[i].Nindex); j++) {
+      int idx = object[i].index[j];
+      float dR2 = SQ(object[i].Dtgt[j]);
+      float dX2 = SQ(object[i].Xo - object[idx].Xo);
+      float dY2 = dR2 - dX2;
+      dY2sum += dY2;
+      Npts ++;
+    }
+    XoSum += object[i].Xo;
+    object[i].Yo = (dY2sum > 0.0) ? sqrt(fabs(dY2sum / Npts)) : -sqrt(fabs(dY2sum / Npts));
+  }
+  float XoMean = XoSum / Nobject;
+
+  // remove the mean Xo (uninteresting)
+  for (i = 0; i < Nobject; i++) {
+    object[i].Xo -= XoMean;
+  }
+
+  // save the result
+  {
+    Vector *outindex = SelectVector ("sp1d_idx", ANYVECTOR, TRUE); if (!outindex) goto escape;
+    Vector *outXo    = SelectVector ("sp1d_Xo",  ANYVECTOR, TRUE); if (!outXo) goto escape;
+    Vector *outYo    = SelectVector ("sp1d_Yo",  ANYVECTOR, TRUE); if (!outYo) goto escape;
+
+    ResetVector (outindex, OPIHI_INT, Nobject);
+    ResetVector (outXo, OPIHI_FLT, Nobject);
+    ResetVector (outYo, OPIHI_FLT, Nobject);
+
+    for (i = 0; i < Nobject; i++) {
+      outindex->elements.Int[i] = i;
+      outXo->elements.Flt[i] = object[i].Xo;
+      outYo->elements.Flt[i] = object[i].Yo;
+    }
+  }
+
+  return TRUE;
+  
+escape: 
+  gprint (GP_ERR, "invalid vector\n");
+  return FALSE;
+  
+usage:
+  gprint (GP_ERR, "USAGE: spex2dgas (index1) (index2) (distance) (Niter) (nCloseMax) (nCloseIter) (farFrac) (maxPressure) (idx1) (idx2)\n");
+  return FALSE;
+}
+
+/* this function takes 3 vectors: a set of distances and a pair of indicies identifying the end points.  
+   
+   the goal is to generate groups of the end points that are relatively closer.
+
+   note that the distances may not be Euclidean: nothing can be assumed about relationships between d(1,2), d(2,3), and d(1,3)
+
+   the indicies should be sequential.  the algorithm does not require it, but storage is much cleaner if it is
+
+*/
Index: /branches/sc_branches/pantasks_condor/cmd.astro/spex2dgas.c
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.astro/spex2dgas.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.astro/spex2dgas.c	(revision 34783)
@@ -0,0 +1,344 @@
+# include "astro.h"
+
+/* We have N objects with N(N-1)/2 paired distances.  We want to find the 2D distribution that
+   best describes the observed distances.  Assume the distances are in a 2D space, though in
+   principle it could be N-D or even non-euclidean.
+
+   Model the points as a gas under pressure.  
+
+   Start with the a guess for positions of the objects at some locations in 2D
+
+   P = (d_now - d_tru)
+
+   Iterate over the points and move in X,Y
+
+   Find (dP/dX,dP/dY) for modest moves in X,Y
+
+   Move based on dP
+
+   USAGE: spex2dgas incdex1 index2 distance
+
+   outline:
+
+   * load data
+   * generate the unique objects
+   * determine the max distance needed
+   * place each object in the 2D space
+
+   * iterate:
+   ** calculate dP/dX,dPdY for each object
+   ** move each object proportionally to the pressure gradient
+
+   */
+
+typedef struct {
+  int Nindex;   // number of tested relationships
+  int NINDEX;   // number of allocated relationships
+  int *index;  // indices for all other objects
+  float *Dtgt; // target distance for this relationship
+  float *Dcur; // current distance for this relationship
+  float Xo,Yo; // current X,Y position of this object
+  float dPdX;  // pressure in X
+  float dPdY;  // pressure in Y
+} Object;
+
+static Object *object = NULL;
+static int Nobject = 0;
+// static int NOBJECT = 0;
+
+void sortfriends (float *X, int *IDX1, int N);
+
+static void get_pressure_gradient (int iObj, int nCloseMax, float farFrac, int nearNeighbors, float maxPressure) {
+
+  int i;
+  
+  float dPdX = 0.0;
+  float dPdY = 0.0;
+
+  // only use the first N friends
+  for (i = 0; (i < object[iObj].Nindex); i++) {
+    int jObj = object[iObj].index[i];
+
+    // some options:
+    // if the iterations are small, we should only worry about getting the near neighbors right
+    // if the iterations are large, we should add in more distant objects
+    if (nearNeighbors && (i >= nCloseMax)) break;
+    if (!nearNeighbors && (i >= nCloseMax)) {
+      if (drand48() > farFrac) continue;
+    }
+
+    float Dtgt = object[iObj].Dtgt[i];
+    float dX = object[jObj].Xo - object[iObj].Xo;
+    float dY = object[jObj].Yo - object[iObj].Yo;
+    float Dcur = hypot(dX,dY);
+
+    // the force law as a function of (Dcur - Dtgt) : if Dcur is too large, dF is negative
+    // float dF = (Dcur < 0.01*Dtgt) ? -100.0 : (Dcur - Dtgt) / Dcur; XXX modified spring constant : too crazy
+    float dF = (Dcur - Dtgt);
+    dF = MIN (maxPressure, MAX (-maxPressure, dF));
+
+    float dPdXi, dPdYi;
+    if (fabs(Dcur) < 1e-6) {
+      dPdXi = 0.0;
+      dPdYi = 0.0;
+    } else {
+      dPdXi = dF * dX / Dcur;
+      dPdYi = dF * dY / Dcur;
+    }
+
+    if (isnan(Dtgt) || isnan(dX) || isnan(dY) || isnan(Dcur) || isnan(dF) || isnan(dPdXi) || isnan(dPdYi)) abort();
+
+    // if we are too close, then dX/Dcur is too ill-defined, just jump away by 5% of Dtgt
+    if (Dcur < 0.01*Dtgt) {
+      dPdXi = (drand48() - 0.5)*0.1*Dtgt;
+      dPdYi = (drand48() - 0.5)*0.1*Dtgt;
+    }
+
+    if (i >= nCloseMax) {
+      fprintf (stderr, "Dcur,Dtgt : %f %f : dX,dY,dP : %f %f : %f : %f %f\n", Dcur, Dtgt, dX, dY, dF, dPdXi, dPdYi);
+    }
+    dPdX += dPdXi;
+    dPdY += dPdYi;
+  }
+  object[iObj].dPdX = dPdX;
+  object[iObj].dPdY = dPdY;
+
+  return;
+}
+
+static void move_object (int iObj) {
+
+  object[iObj].Xo += 0.25*object[iObj].dPdX;
+  object[iObj].Yo += 0.25*object[iObj].dPdY;
+  return;
+}
+
+int spex2dgas (int argc, char **argv) {
+  
+  int i, j, iter, IoMax;
+  Vector *index1, *index2, *distance;
+  float XoMax, YoMax;
+
+  { 
+    // init random numbers
+    long A, B;
+    A = time(NULL);
+    for (B = 0; A == time(NULL); B++);
+    srand48(B);
+  }
+ 
+  if (argc != 9) goto usage;
+
+  if ((index1   = SelectVector (argv[1], OLDVECTOR, TRUE)) == NULL) goto escape;
+  if ((index2   = SelectVector (argv[2], OLDVECTOR, TRUE)) == NULL) goto escape;
+  if ((distance = SelectVector (argv[3], OLDVECTOR, TRUE)) == NULL) goto escape;
+  int Niter = atoi (argv[4]);
+  int nCloseMax = atoi (argv[5]);
+  int nCloseIter = atoi (argv[6]);
+  float farFrac = atof (argv[7]);
+  float maxPressure = atof (argv[8]);
+  // XXX enforce matching lengths on the three vectors
+
+  CastVector (index1, OPIHI_INT);
+  CastVector (index2, OPIHI_INT);
+
+  // how many objects do we have?
+  Nobject = 0;
+  for (i = 0; i < index1->Nelements; i++) {
+    Nobject = MAX (Nobject, index1->elements.Int[i]);
+    Nobject = MAX (Nobject, index2->elements.Int[i]);
+  }
+  Nobject ++;  // after the loop, Nobject has the value of the highest index, not the count
+
+  // allocate the list of Object -- these list the possible friends of the given object[i] 
+  ALLOCATE (object, Object, Nobject);
+  for (i = 0; i < Nobject; i++) {
+    ALLOCATE (object[i].index, int, Nobject);
+    memset (object[i].index, 0, Nobject*sizeof(int));
+    ALLOCATE (object[i].Dtgt, float, Nobject);
+    memset (object[i].Dtgt, 0, Nobject*sizeof(float));
+    ALLOCATE (object[i].Dcur, float, Nobject);
+    memset (object[i].Dcur, 0, Nobject*sizeof(float));
+    object[i].NINDEX = Nobject;
+    object[i].Nindex = 0;
+  }
+
+  // generate the set of vectors of all distances for each entry
+  // the object seq number (object[i]) will be used to match the index1 and index2 values
+  for (i = 0; i < index1->Nelements; i++) {
+    int N, m;
+
+    N = index1->elements.Int[i];
+    if (N >= Nobject) abort();
+    m = object[N].Nindex;
+    object[N].index[m] = index2->elements.Int[i];
+    object[N].Dtgt[m] = distance->elements.Flt[i];
+    object[N].Dcur[m] = 0.0;
+    object[N].Nindex ++;
+    if (object[N].Nindex == object[N].NINDEX) {
+      object[N].NINDEX += 100;
+      REALLOCATE (object[N].Dtgt, float, object[N].NINDEX);
+      REALLOCATE (object[N].Dcur, float, object[N].NINDEX);
+      REALLOCATE (object[N].index, int, object[N].NINDEX);
+    }
+
+    N = index2->elements.Int[i];
+    if (N >= Nobject) abort();
+    m = object[N].Nindex;
+    object[N].index[m] = index1->elements.Int[i];
+    object[N].Dtgt[m] = distance->elements.Flt[i];
+    object[N].Dcur[m] = 0.0;
+    object[N].Nindex ++;
+    if (object[N].Nindex == object[N].NINDEX) {
+      object[N].NINDEX += 100;
+      REALLOCATE (object[N].Dtgt, float, object[N].NINDEX);
+      REALLOCATE (object[N].Dcur, float, object[N].NINDEX);
+      REALLOCATE (object[N].index, int, object[N].NINDEX);
+    }
+  }
+
+  for (i = 0; i < Nobject; i++) {
+    // sort so closest friends are first
+    sortfriends (object[i].Dtgt, object[i].index, object[i].Nindex);
+  }
+
+  // find and save the max distance
+  float Dmax = 0;
+  for (i = 0; i < distance->Nelements; i++) {
+    Dmax = MAX (Dmax, distance->elements.Flt[i]);
+  }
+
+  // place the objects at the initial guess locations
+  // XXX let's try with just a simple grid dividing up the max range
+  // int Ngrid = sqrt(Nobject);
+  // float dgrid = 1.5 * Dmax / Ngrid; // XXX remove the fudge factor
+  // XXX for (i = 0; i < Nobject; i++) {
+  // XXX   // object[i].Xo = dgrid * (int) (i % Ngrid);
+  // XXX   // object[i].Yo = dgrid * (int) (i / Ngrid);
+  // XXX   object[i].Xo = Dmax*drand48();
+  // XXX   object[i].Yo = Dmax*drand48();
+  // XXX   object[i].dPdX = 0.0;
+  // XXX   object[i].dPdY = 0.0;
+  // XXX }
+
+  // place the objects at the initial guess locations
+  // use object 0 and its most distant friend to constrain:
+  
+  int idx1 = 0;
+  int idx2 = object[idx1].index[object[idx1].Nindex-1];
+  float A = object[idx1].Dtgt[object[idx1].Nindex-1];
+  object[idx1].Xo = 0.0;
+  object[idx1].Yo = 0.0;
+  object[idx1].dPdX = 0.0;
+  object[idx1].dPdY = 0.0;
+
+  object[idx2].Xo = A;
+  object[idx2].Yo = 0.0;
+  object[idx2].dPdX = 0.0;
+  object[idx2].dPdY = 0.0;
+		 
+  // choose the correct Y side by comparing to the distance from the most deviant
+  YoMax = 0;
+  XoMax = 0;
+  IoMax = 0;
+
+  for (i = 0; i < Nobject; i++) {
+    if (i == idx1) continue;
+    if (i == idx2) continue;
+    float B = NAN;
+    float C = NAN;
+    for (j = 0; (isnan(B) || isnan(C)) && (j < object[i].Nindex); j++) {
+      if (object[i].index[j] == idx1) { B = object[i].Dtgt[j]; }
+      if (object[i].index[j] == idx2) { C = object[i].Dtgt[j]; }
+    }
+    if (isnan(B) || isnan(C)) abort();
+
+    float Xo = (SQ(A) + SQ(B) - SQ(C)) / (2*A);
+    float Y2 = SQ(B) - SQ(Xo);
+
+    float Yo = (Y2 < 0) ? 0.0 : sqrt(Y2);
+    if (isnan(Yo)) abort();
+
+    object[i].Xo = Xo;
+    object[i].Yo = Yo;
+    object[i].dPdX = 0.0;
+    object[i].dPdY = 0.0;
+    if (Yo > YoMax) {
+      YoMax = Yo;
+      XoMax = Xo;
+      IoMax = i;
+    }
+  }
+
+  for (i = 0; i < Nobject; i++) {
+    if (i == idx1) continue;
+    if (i == idx2) continue;
+    if (i == IoMax) continue;
+    for (j = 0; j < object[i].Nindex; j++) {
+      if (object[i].index[j] == IoMax) { 
+	float d02 = SQ(object[i].Dtgt[j]);
+	float dX  = XoMax - object[i].Xo;
+	float dY1 = YoMax - object[i].Yo;
+	float dY2 = YoMax + object[i].Yo;
+	float d12 = SQ(dX) + SQ(dY1);
+	float d22 = SQ(dX) + SQ(dY2);
+	if (fabs(d12 - d02) > fabs(d22 - d02)) {
+	  object[i].Yo = -object[i].Yo;
+	  break;
+	}
+      }
+    }
+  }
+
+  for (iter = 0; iter < Niter; iter ++) {
+    fprintf (stderr, "iter %d\n", iter);
+
+    int nearNeighbors = (iter < nCloseIter);
+
+    // save the result
+    char name[64];
+    snprintf (name, 64, "output.%02d.dat", iter);
+    FILE *output = fopen (name, "w");
+    for (i = 0; i < Nobject; i++) {
+      fprintf (output, "%f %f : %f %f\n", object[i].Xo, object[i].Yo, object[i].dPdX, object[i].dPdY);
+    }
+    fclose (output);
+
+    // measure (dP/dX),(dP/dY) for all objects
+    for (i = 0; i < Nobject; i++) {
+      get_pressure_gradient (i, nCloseMax, farFrac, nearNeighbors, maxPressure);
+      move_object (i);
+    }
+
+    // given (dP/dX),(dP/dY), move each object 
+    // for (i = 0; i < Nobject; i++) {
+    // }
+  }
+
+  // save the result
+  FILE *output = fopen ("output.dat", "w");
+  for (i = 0; i < Nobject; i++) {
+    fprintf (output, "%f %f : %f %f\n", object[i].Xo, object[i].Yo, object[i].dPdX, object[i].dPdY);
+  }
+  fclose (output);
+
+  return TRUE;
+  
+escape: 
+  gprint (GP_ERR, "invalid vector\n");
+  return FALSE;
+  
+usage:
+  gprint (GP_ERR, "USAGE: spex2dgas (index1) (index2) (distance) (Niter) (nCloseMax) (nCloseIter) (farFrac) (maxPressure)\n");
+  return FALSE;
+}
+
+/* this function takes 3 vectors: a set of distances and a pair of indicies identifying the end points.  
+   
+   the goal is to generate groups of the end points that are relatively closer.
+
+   note that the distances may not be Euclidean: nothing can be assumed about relationships between d(1,2), d(2,3), and d(1,3)
+
+   the indicies should be sequential.  the algorithm does not require it, but storage is much cleaner if it is
+
+*/
Index: /branches/sc_branches/pantasks_condor/cmd.astro/spexseq.c
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.astro/spexseq.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.astro/spexseq.c	(revision 34783)
@@ -0,0 +1,381 @@
+# include "astro.h"
+
+/* We have N objects with N(N-1)/2 paired chisq.  We want to find the 1D sequence / index that
+   best describes the observed distances.  
+
+   A sequence is best described by the distances from objects which are only modestly close.
+   If we get too close to the reference, we (a) hit a non-euclidean (or N-D) minimum (A-B ~ A-C
+   ~ B-C) and (b) the positive and negative sides of the sequence are ambiguous.  If we get too
+   far away, the distances are all either equivalent or less consistent
+
+   1) find the dynamic range of distances
+
+   2) choose object (1) and select a friend (2) > a fixed fraction of the full dynamic range (say,
+   10-20%)
+
+   3) all nearby friends of (1) (D < f MaxDist) have sequences assigned based on the distance
+   to (2).  center based on the distance (1)-(2) ie, (1) gets a sequence value of 0.0
+
+   4) choose another object (1') and repeat [need to choose (2') on the same side as (2)?]
+
+   ** reconciliation is a bit tricky
+   ** edge effects imply that we prefer to start in the center
+   ** is this well-defined? 
+
+   */
+
+typedef struct {
+  int Nindex;   // number of tested relationships
+  int NINDEX;   // number of allocated relationships
+  int *index;  // index for distances to other objects: object[i].Dtgt[j] is distance from object[i] to object[idx] where idx = object[i].index[j] (sorted by Dtgt)
+  int *rindex;  // reverse index for Dtgt: distance from object[i] to object[idx] is object[i].Dtgt[j] where object[i].rindex[idx] = j
+  int shifted; // has this object already had the sequence adjusted?
+  float *Dtgt; // target distance for this relationship
+  float *seq;  // sequence value for this friend
+  float So, dSo;
+  float Xo, dXo;
+  float Yo, dYo;
+  float Srange;
+  int nSeq;
+} Object;
+
+void sortfriends (float *X, int *IDX1, int N);
+
+void get_sequence (Object *object, int Nobject, int idx1, float Dmax, float f1, float f2, int pin1, int pin2) {
+
+  int i, j, N;
+
+  // object (1) is provided above; choose object (2)
+  int idx2 = -1;
+  float A = NAN;
+  for (i = 0; (idx2 == -1) && (i < object[idx1].Nindex); i++) {
+    if (object[idx1].Dtgt[i] > f1*Dmax) {
+      idx2 = object[idx1].index[i];
+    }
+    A = object[idx1].Dtgt[i];
+  }
+  // fprintf (stderr, "idx1: %d, idx2: %d, A: %f\n", idx1, idx2, A);
+  // set_variable ("SP1D_A", A);
+
+  // is idx2 closer to pin1 or pin2?
+  // XX N = object[idx2].rindex[pin1];
+  // XX float D1 = object[idx2].Dtgt[N];
+  // XX N = object[idx2].rindex[pin2];
+  // XX float D2 = object[idx2].Dtgt[N];
+  // XX int parity = (D1 < D2) ? +1 : -1;
+
+  int Nfriends = 0;
+  for (i = 0; (object[idx1].Dtgt[i] < f2*Dmax) && (i < object[idx1].Nindex); i++) {
+    // what is the distance from this friend to object (2)?
+    N = object[idx1].index[i];
+    j = object[idx2].rindex[N];
+    assert (object[idx2].index[j] == N);
+    // XX object[idx1].seq[i] = parity*(object[idx2].Dtgt[j] - A);
+    object[idx1].seq[i] = (object[idx2].Dtgt[j] - A);
+    Nfriends ++;
+  }
+  assert (Nfriends <= object[idx1].Nindex);
+  assert (Nfriends <= Nobject);
+  fprintf (stderr, "object %d sequenced by object %d, %d friends sequenced\n", idx1, idx2, Nfriends);
+  return;
+}
+
+int spexseq (int argc, char **argv) {
+  
+  int i, j, k;
+  Vector *index1, *index2, *distance;
+  Object *object = NULL;
+  int Nobject = 0;
+
+  { 
+    // init random numbers
+    long A, B;
+    A = time(NULL);
+    for (B = 0; A == time(NULL); B++);
+    srand48(B);
+  }
+ 
+  if (argc != 6) goto usage;
+
+  // XXX enforce matching lengths on the three vectors
+  if ((index1   = SelectVector (argv[1], OLDVECTOR, TRUE)) == NULL) goto escape;
+  if ((index2   = SelectVector (argv[2], OLDVECTOR, TRUE)) == NULL) goto escape;
+  if ((distance = SelectVector (argv[3], OLDVECTOR, TRUE)) == NULL) goto escape;
+
+  float f1 = atof (argv[4]);
+  float f2 = atof (argv[5]);
+
+  // int idx1 = atoi(argv[9]);
+  // int idx2 = atoi(argv[10]);
+
+  CastVector (index1, OPIHI_INT);
+  CastVector (index2, OPIHI_INT);
+
+  // how many objects do we have?
+  Nobject = 0;
+  for (i = 0; i < index1->Nelements; i++) {
+    Nobject = MAX (Nobject, index1->elements.Int[i]);
+    Nobject = MAX (Nobject, index2->elements.Int[i]);
+  }
+  Nobject ++;  // after the loop, Nobject has the value of the highest index, not the count
+
+  // allocate the list of Object -- these list the possible friends of the given object[i] 
+  ALLOCATE (object, Object, Nobject);
+  for (i = 0; i < Nobject; i++) {
+    ALLOCATE (object[i].index, int, Nobject);
+    memset (object[i].index, 0, Nobject*sizeof(int));
+    ALLOCATE (object[i].Dtgt, float, Nobject);
+    memset (object[i].Dtgt, 0, Nobject*sizeof(float));
+    object[i].NINDEX = Nobject;
+    object[i].Nindex = 0;
+  }
+
+  // generate the set of vectors of all distances for each entry
+  // the object seq number (object[i]) will be used to match the index1 and index2 values
+  for (i = 0; i < index1->Nelements; i++) {
+    int N, m;
+
+    N = index1->elements.Int[i];
+    if (N >= Nobject) abort();
+    m = object[N].Nindex;
+    object[N].index[m] = index2->elements.Int[i];
+    object[N].Dtgt[m] = distance->elements.Flt[i];
+    object[N].Nindex ++;
+    if (object[N].Nindex == object[N].NINDEX) {
+      object[N].NINDEX += 100;
+      REALLOCATE (object[N].Dtgt, float, object[N].NINDEX);
+      REALLOCATE (object[N].index, int, object[N].NINDEX);
+    }
+
+    // Above we used the set (N,M,D_N,M) to assigned the distance of obj M to obj N.
+    // Below we use that set to assign the distance of obj N to obj M.  IF we supply all
+    // pairwise distances, then this duplicates all entries, so we need to skip that step
+    // in such a case.
+
+    // XX N = index2->elements.Int[i];
+    // XX if (N >= Nobject) abort();
+    // XX m = object[N].Nindex;
+    // XX object[N].index[m] = index1->elements.Int[i];
+    // XX object[N].Dtgt[m] = distance->elements.Flt[i];
+    // XX object[N].Nindex ++;
+    // XX if (object[N].Nindex == object[N].NINDEX) {
+    // XX   object[N].NINDEX += 100;
+    // XX   REALLOCATE (object[N].Dtgt, float, object[N].NINDEX);
+    // XX   REALLOCATE (object[N].index, int, object[N].NINDEX);
+    // XX }
+  }
+
+  for (i = 0; i < Nobject; i++) {
+    // sort so closest friends are first
+    sortfriends (object[i].Dtgt, object[i].index, object[i].Nindex);
+    object[i].shifted = FALSE;
+    object[i].So = NAN;
+    object[i].dSo = NAN;
+    object[i].Xo = NAN;
+    object[i].dXo = NAN;
+    // generate the reverse index
+    REALLOCATE (object[i].rindex, int, object[i].Nindex);
+    REALLOCATE (object[i].seq, float, object[i].Nindex);
+    for (j = 0; j < object[i].Nindex; j++) {
+      int N = object[i].index[j];
+      object[i].rindex[N] = j;
+      object[i].seq[j] = NAN;
+    }
+  }
+
+  // find and save the max distance
+  float Dmax = 0;
+  for (i = 0; i < distance->Nelements; i++) {
+    Dmax = MAX (Dmax, distance->elements.Flt[i]);
+  }
+
+  // find two objects at the opposite extremes
+  float Dm1 = object[0].Dtgt[object[0].Nindex - 1];
+  for (i = 0; (object[0].Dtgt[i] < 0.75*Dm1) && (i < object[0].Nindex); i++);
+  int pin1 = object[0].index[i];
+  assert (pin1 >= 0);
+  assert (pin1 < Nobject);
+
+  float Dm2 = object[pin1].Dtgt[object[pin1].Nindex - 1];
+  for (i = 0; (object[pin1].Dtgt[i] < 0.75*Dm2) && (i < object[pin1].Nindex); i++);
+  int pin2 = object[pin1].index[i];
+  assert (pin2 >= 0);
+  assert (pin2 < Nobject);
+
+  // generate sequences for the near friends of all objects based on a somewhat distant friend
+  for (i = 0; i < Nobject; i++) {
+    get_sequence (object, Nobject, i, Dmax, f1, f2, pin1, pin2);
+  }
+
+  // now we need to reconcile these sequences.
+  // start with object 0, find shift of all friends relative to 0
+  object[0].shifted = TRUE;
+  for (i = 0; i < Nobject; i++) {
+
+    // don't use unreconciled objects as a reference (we'll catch them eventually)
+    if (!object[i].shifted) continue;
+
+    int Nfixed = 0;
+
+    // shift all of the close friends of object (i) wrt object (i)
+    for (j = 0; (j < object[i].Nindex) && (object[i].Dtgt[j] < f2*Dmax); j++) {
+
+      // loop over the sequence for object Nj
+      int Nj = object[i].index[j];
+
+      // only reconcile once
+      if (object[Nj].shifted == TRUE) continue;
+
+      float dS1p = 0.0;
+      float dS2p = 0.0;
+      float dS1m = 0.0;
+      float dS2m = 0.0;
+      int nS = 0;
+
+      float Smin = +1000.0;
+      float Smax = -1000.0;
+
+      for (k = 0; k < object[Nj].Nindex; k++) {
+	int Nsj = object[Nj].index[k];
+	int Nsi = object[i].rindex[Nsj];
+	
+	if (isnan(object[Nj].seq[k])) continue;
+	if (isnan(object[i].seq[Nsi])) continue;
+	
+	Smin = MIN(object[Nj].seq[k], Smin);
+	Smax = MAX(object[Nj].seq[k], Smax);
+
+	float dSp = object[Nj].seq[k] - object[i].seq[Nsi];
+	float dSm = -object[Nj].seq[k] - object[i].seq[Nsi];
+	dS1p += dSp;
+	dS2p += dSp*dSp;
+	dS1m += dSm;
+	dS2m += dSm*dSm;
+	nS ++;
+      }
+      float Sp = dS1p / nS;
+      float Sm = dS1m / nS;
+      float dSp = sqrt(dS2p / nS - Sp*Sp);
+      float dSm = sqrt(dS2m / nS - Sm*Sm);
+      fprintf (stderr, "%d: %f +/- %f or %f +/- %f : %f range, %d matches\n", Nj, Sp, dSp, Sm, dSm, Smax - Smin, nS);
+
+      if (dSm > dSp) {
+	// parity is always 1 if we use the pins (but it does not seem to work)
+	for (k = 0; k < object[Nj].Nindex; k++) {
+	  if (isnan(object[Nj].seq[k])) continue;
+	  object[Nj].seq[k] -= Sp;
+	}
+	object[Nj].So = Sp;
+	object[Nj].dSo = dSp;
+      } else {
+	// flip the sequence and offset
+	for (k = 0; k < object[Nj].Nindex; k++) {
+	  if (isnan(object[Nj].seq[k])) continue;
+	  object[Nj].seq[k] = -object[Nj].seq[k] - Sm; 
+	}
+	object[Nj].So = Sm;
+	object[Nj].dSo = dSm;
+      }
+      object[Nj].Srange = Smax - Smin;
+      object[Nj].nSeq = nS;
+      object[Nj].shifted = TRUE;
+      Nfixed ++;
+    }
+    // fprintf (stderr, "%d used to reconcile %d friends\n", i, Nfixed);
+  }
+
+  int Nfixed = 0;
+  int Nunfix = 0;
+  for (i = 0; i < Nobject; i++) {
+    if (object[i].shifted) { 
+      Nfixed ++;
+    } else {
+      Nunfix ++;
+    }
+  }
+  fprintf (stderr, "%d fixed, %d unfixed\n", Nfixed, Nunfix);
+      
+
+  // calculate the mean Xo for each object using the values of the sequences calculated
+  for (i = 0; i < Nobject; i++) {
+
+    if (!object[i].shifted) {
+      fprintf (stderr, "this object was not reconciled\n");
+    }
+    float XoSum = 0.0;
+    float XoS2 = 0.0;
+    int XoNum = 0;
+
+    // find all the measurements of this object's sequence (from its friends values)
+    for (j = 0; j < Nobject; j++) {
+      int Ns = object[j].rindex[i];
+      if (isnan(object[j].seq[Ns])) continue;
+      XoSum += object[j].seq[Ns];
+      XoS2  += SQ(object[j].seq[Ns]);
+      XoNum ++;
+    }
+    float Xo = XoSum / XoNum;
+    float dXo = sqrt(XoS2 / XoNum - Xo*Xo);
+    object[i].Xo = Xo;
+    object[i].dXo = dXo;
+  }
+
+  // XXX seq is effectively Xo, calculate Yo for the objects based on the local 
+  // objects
+
+  // save the result
+  {
+    Vector *outindex = SelectVector ("sp1d_idx", ANYVECTOR, TRUE); if (!outindex) goto escape;
+    Vector *outXo    = SelectVector ("sp1d_Xo",  ANYVECTOR, TRUE); if (!outXo) goto escape;
+    Vector *outYo    = SelectVector ("sp1d_Yo",  ANYVECTOR, TRUE); if (!outYo) goto escape;
+
+    Vector *outSo    = SelectVector ("sp1d_So",  ANYVECTOR, TRUE); if (!outXo) goto escape;
+    Vector *outdSo   = SelectVector ("sp1d_dSo", ANYVECTOR, TRUE); if (!outYo) goto escape;
+    Vector *outdXo   = SelectVector ("sp1d_dXo", ANYVECTOR, TRUE); if (!outXo) goto escape;
+
+    Vector *outSr   = SelectVector ("sp1d_Sr", ANYVECTOR, TRUE); if (!outSr) goto escape;
+    Vector *outSn   = SelectVector ("sp1d_nS", ANYVECTOR, TRUE); if (!outSn) goto escape;
+
+    ResetVector (outindex, OPIHI_INT, Nobject);
+    ResetVector (outXo, OPIHI_FLT, Nobject);
+    ResetVector (outYo, OPIHI_FLT, Nobject);
+
+    ResetVector (outSo, OPIHI_FLT, Nobject);
+    ResetVector (outdSo, OPIHI_FLT, Nobject);
+    ResetVector (outdXo, OPIHI_FLT, Nobject);
+
+    ResetVector (outSr, OPIHI_FLT, Nobject);
+    ResetVector (outSn, OPIHI_INT, Nobject);
+
+    for (i = 0; i < Nobject; i++) {
+      outindex->elements.Int[i] = i;
+      outXo->elements.Flt[i] = object[i].Xo;
+      outYo->elements.Flt[i] = object[i].Yo;
+      outSo->elements.Flt[i] = object[i].So;
+      outdSo->elements.Flt[i] = object[i].dSo;
+      outdXo->elements.Flt[i] = object[i].dXo;
+      outSr->elements.Flt[i] = object[i].Srange;
+      outSn->elements.Int[i] = object[i].nSeq;
+    }
+  }
+
+  return TRUE;
+  
+escape: 
+  gprint (GP_ERR, "invalid vector\n");
+  return FALSE;
+  
+usage:
+  gprint (GP_ERR, "USAGE: spexseq (index1) (index2) (distance) (Niter) (nCloseMax) (nCloseIter) (farFrac) (maxPressure) (idx1) (idx2)\n");
+  return FALSE;
+}
+
+/* this function takes 3 vectors: a set of distances and a pair of indicies identifying the end points.  
+   
+   the goal is to generate groups of the end points that are relatively closer.
+
+   note that the distances may not be Euclidean: nothing can be assumed about relationships between d(1,2), d(2,3), and d(1,3)
+
+   the indicies should be sequential.  the algorithm does not require it, but storage is much cleaner if it is
+
+*/
Index: /branches/sc_branches/pantasks_condor/cmd.astro/star.c
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.astro/star.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.astro/star.c	(revision 34783)
@@ -0,0 +1,42 @@
+# include "astro.h"
+
+int star (int argc, char **argv) {
+
+  int x, y, N, dx, Nborder;
+  double max;
+  Buffer *buf;
+
+  Nborder = 3;
+  if ((N = get_argument (argc, argv, "-border"))) {
+    remove_argument (N, &argc, argv);
+    Nborder  = atof(argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+  Nborder = MAX (Nborder, 1);
+  
+  max = 60000;
+  if ((N = get_argument (argc, argv, "-sat"))) {
+    remove_argument (N, &argc, argv);
+    max  = atof(argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+  
+  if ((argc != 4) && (argc != 5)) {
+    gprint (GP_ERR, "USAGE: star (buffer) x y [dx] [-border N] [-sat cnts]\n");
+    gprint (GP_ERR, " dx is the aperture diameter, but is adjusted up to the next odd number\n");
+    return (FALSE);
+  }
+  if ((buf = SelectBuffer (argv[1], OLDBUFFER, TRUE)) == NULL) return (FALSE);
+
+  dx = 11;
+  x = atof (argv[2]);
+  y = atof (argv[3]);
+  if (argc == 5) {
+    dx = atof (argv[4]);
+  }
+
+  get_aperture_stats (&buf[0].matrix, x, y, dx, Nborder, max);
+  
+  return (TRUE);
+}
+
Index: /branches/sc_branches/pantasks_condor/cmd.astro/test/region.sh
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.astro/test/region.sh	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.astro/test/region.sh	(revision 34783)
@@ -0,0 +1,185 @@
+
+macro mkimage2
+  if ($0 != 2)
+    echo "USAGE: mkimage2 (angle)"
+    break
+  end
+
+  # create an image and set the WCS
+
+  mcreate im 500 500
+
+  keyword im CTYPE1 -w "RA---TAN"
+  keyword im CTYPE2 -w "DEC--TAN"
+
+  keyword im CRVAL1 -wf 10.00
+  keyword im CRVAL2 -wf 45.00
+ 
+  keyword im CRPIX1 -wf 250
+  keyword im CRPIX2 -wf 250
+
+  keyword im CDELT1 -wf {0.25/3600}
+  keyword im CDELT2 -wf {0.25/3600}
+
+  keyword im PC001001 -wf {dcos($1)}
+  keyword im PC002001 -wf {dsin($1)}
+  keyword im PC001002 -wf {-1.0*dsin($1)}
+  keyword im PC002002 -wf {dcos($1)}
+
+  for ix 50 500 100
+    for iy 50 500 100
+      im[$ix][$iy] = 1
+      concat $ix Xc
+      concat $iy Yc
+    end
+  end
+
+  set Rc = Xc
+  set Dc = Yc
+
+  coords im -p Rc Dc
+  
+  clear -s 
+
+  tv im -0.2 1.5
+  region -image -no-clear
+  cplot Rc Dc -pt 7 -sz 3.0 -x 2 -c red
+
+  create r1 9.0 11.0 0.001
+  create d2 44.0 46.0 0.001
+  set d1 = 45.0 + zero(r1)
+  set r2 = 10.0 + zero(d2)
+  cplot r1 d1 -c blue -x 0 ; 
+  cplot r2 d2 -c red -x 0
+
+  set d1 = Dc[0] + zero(r1)
+  set r2 = Rc[0] + zero(d2)
+  cplot r1 d1 -c blue -x 0 ; 
+  cplot r2 d2 -c red -x 0
+end
+
+macro mkimage
+
+  # create an image and set the WCS
+
+  mcreate im 500 500
+
+  keyword im CTYPE1 -w "RA---TAN"
+  keyword im CTYPE2 -w "DEC--TAN"
+
+  keyword im CRVAL1 -wf 10.00
+  keyword im CRVAL2 -wf 45.00
+ 
+  keyword im CRPIX1 -wf 250
+  keyword im CRPIX2 -wf 250
+
+  keyword im CDELT1 -wf {0.25/3600}
+  keyword im CDELT2 -wf {0.25/3600}
+
+  keyword im PC001001 -wf 1.0
+  keyword im PC002001 -wf 0.0
+  keyword im PC001002 -wf 0.0
+  keyword im PC002002 -wf 1.0
+
+  for ix 50 500 100
+    for iy 50 500 100
+      im[$ix][$iy] = 1
+      concat $ix Xc
+      concat $iy Yc
+    end
+  end
+
+  tv im -0.2 1.5
+
+  set Rc = Xc
+  set Dc = Yc
+
+  coords im -p Rc Dc
+  
+  region -image 
+  cplot Rc Dc -pt 7 -sz 3.0 -x 2 -c red
+end
+
+macro raline
+  create rx 0 360
+  set dx = zero(rx) + $1
+  coords im -c rx dx
+  break -auto off
+  for i 0 rx[]
+    $ix = rx[$i]
+    $iy = dx[$i]
+    im[$ix][$iy] = 1.0
+  end
+  break -auto on
+end
+
+macro decline
+  create dx -90 90
+  set rx = zero(dx) + $1
+  coords im -c rx dx
+  break -auto off
+  for i 0 rx[]
+    $ix = rx[$i]
+    $iy = dx[$i]
+    im[$ix][$iy] = 1.0
+  end
+  break -auto on
+end
+
+macro mkallsky
+
+  # create an image and set the WCS
+
+  mcreate im 400 200
+
+  keyword im CTYPE1 -w "RA---AIT"
+  keyword im CTYPE2 -w "DEC--AIT"
+
+  keyword im CRVAL1 -wf 0.00
+  keyword im CRVAL2 -wf 0.00
+ 
+  keyword im CRPIX1 -wf 200
+  keyword im CRPIX2 -wf 100
+
+  keyword im CDELT1 -wf 1.0
+  keyword im CDELT2 -wf 1.0
+
+  keyword im PC001001 -wf 1.0
+  keyword im PC002001 -wf 0.0
+  keyword im PC001002 -wf 0.0
+  keyword im PC002002 -wf 1.0
+
+  raline -45.0
+  raline 0.0
+  raline 45.0
+
+  decline -90.0
+  decline 0.0
+  decline 90.0
+
+  clear -s
+  tv im -0.2 1.5
+  box
+
+  region -image -no-clear
+
+  skylines
+end
+
+macro skylines
+  create rx 0 360
+  set dx1 = zero(rx) - 45.0
+  cplot rx dx1 -x 2 -pt 0 -sz 0.5 -c blue
+  set dx2 = zero(rx) - 0.0
+  cplot rx dx2 -x 2 -pt 0 -sz 0.5 -c blue
+  set dx3 = zero(rx) + 45.0
+  cplot rx dx3 -x 2 -pt 0 -sz 0.5 -c blue
+
+  create dy -90 90
+  set ry1 = zero(dy) - 90.0
+  cplot ry1 dy -x 2 -pt 0 -sz 0.5 -c blue
+  set ry2 = zero(dy) - 0.0
+  cplot ry2 dy -x 2 -pt 0 -sz 0.5 -c blue
+  set ry3 = zero(dy) + 90.0
+  cplot ry3 dy -x 2 -pt 0 -sz 0.5 -c blue
+end
Index: /branches/sc_branches/pantasks_condor/cmd.astro/testfit.c
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.astro/testfit.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.astro/testfit.c	(revision 34783)
@@ -0,0 +1,161 @@
+# include "astro.h"
+
+/* local private functions */
+float fgaussOD (float, float *, int, float *);
+
+int imfit (int argc, char **argv) {
+
+  float par[4], *v1, *v2, *dy, chisq, **covar;
+  int i, Npts, Npar;
+  Vector *xvec, *yvec, *svec;
+
+  if (argc != 4) {
+    gprint (GP_ERR, "USAGE: imfit <x> <y> <dy>\n");
+    return (FALSE);
+  }
+  
+  if ((xvec = SelectVector (argv[1], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((yvec = SelectVector (argv[2], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((svec = SelectVector (argv[3], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+
+  Npts = xvec[0].Nelements;
+  ALLOCATE (dy, float, Npts);
+  v1 = svec[0].elements;
+  v2 = dy;
+  
+  for (i = 0; i < Npts; i++, v1++, v2++) *v2 = 1.0 / (*v1 * *v1);
+  
+  par[0] = 7;
+  par[1] = 2;
+  par[2] = 6;
+  par[3] = 1;
+  Npar = 4;
+
+  mrqinit (xvec[0].elements, yvec[0].elements, dy, Npts, par, Npar, fgaussOD);
+
+  for (i = 0; i < 10; i++) {
+
+    chisq = mrqmin (xvec[0].elements, yvec[0].elements, dy, Npts, par, Npar, fgaussOD);
+    gprint (GP_ERR, "chisq: %f, %f %f %f %f\n", chisq, par[0], par[1], par[2], par[3]);
+
+  }  
+
+  covar = mrqcovar (Npar);
+
+  for (i = 0; i < Npar; i++) {
+    gprint (GP_ERR, "%d  %f  %f\n", i, par[i], covar[i][i]);
+  }
+
+  mrqfree (Npar);
+  return (TRUE);
+}
+
+
+/* pars: x, y, sx, sy, sxy, sky I, */
+float fgaussOD (float x, float *par, int Npar, float *dpar) {
+
+  float X, S, Z, R, f;
+
+  X = x - par[0];
+  S = 1.0 / (par[1]*par[1]);
+  Z = -0.5*X*X*S;
+  R = exp (Z);
+  f = par[2]*R + par[3];
+
+  dpar[0] = par[2]*R*X*S;
+  dpar[1] = dpar[0]*X/par[1];
+  dpar[2] = R;
+  dpar[3] = 1;
+  
+  return (f);
+
+}
+
+# if (0)
+
+/* pars: x, y, sx, sy, sxy, sky I, */
+float testF (float x, float *par, int Npar, float *dpar) {
+
+  float f;
+
+  f = par[0]*x + par[1];
+
+  dpar[0] = x;
+  dpar[1] = 1;
+  
+  return (f);
+
+}
+
+
+/* pars: x, y, sx, sy, sxy, sky I, */
+float fgaussTD (float x, float y, float *par, int Npar) {
+
+  X = x - par[0];
+  Y = y - par[1];
+  
+  t1 = X / par[2];
+  t2 = Y * Y / par[3];
+  t3 = Y * par[4] * 2.0;
+
+  r = 0.5 * ((t1 + t3)*X + t2);
+  f = par[5] + par[6] / (1.0 + r*(1.0 + 0.5*r*(1.0 + 0.33333333*r)));
+  
+  return (f);
+
+}
+
+float chisq (float *buf, float *sig, int Nx, int Ny, float (func)(), float *par, int Npar) {
+
+  float *ptr;
+
+  X = 0;
+  ptr = buf;
+  for (i = 0; i < Nx; i++) {
+    for (j = 0; j < Ny; j++, ptr++, sig++) {
+      f = *ptr - func ((float) i, (float) j, par, Npar);
+      X += (f * f) / *sig;
+    }
+  }    
+  return (X);
+}
+
+  int i, j, Nbuf, status;
+  char *string;
+  double Npix, N1, N2, max, min, range, median;
+  float *V;
+  int sx, sy, nx, ny, *hist, Nhist, bin;
+
+  if (argc != 6) {
+    gprint (GP_ERR, "USAGE: imfit <buffer> sx sy nx ny\n");
+    return (FALSE);
+  }
+
+  if (!SelectBuffer (&Nbuf, argv[1], OLDBUFFER)) return (FALSE);
+
+  sx = atof (argv[2]);
+  sy = atof (argv[3]);
+  nx = atof (argv[4]);
+  ny = atof (argv[5]);
+
+  Npix = N1 = N2 = 0;
+  if ((sx < 0) || (sy < 0) || 
+      (sx+nx > buffers[Nbuf].matrix.Naxis[0]) || 
+      (sy+ny > buffers[Nbuf].matrix.Naxis[1])) {
+    gprint (GP_ERR, "region out of range\n");
+    return (FALSE);
+  }
+
+  Npix = nx*ny;
+
+  ALLOCATE (tempbuf, float, Npix);
+
+  buf = tempbuf;
+  for (j = 0; j < ny; j++) {
+    V = (float *)(buffers[Nbuf].matrix.buffer) + (j+sy)*buffers[Nbuf].matrix.Naxis[0] + sx; 
+    for (i = 0; i < nx; i++, V++) {
+      *buf = *V;
+    }
+  }
+
+# endif
Index: /branches/sc_branches/pantasks_condor/cmd.astro/transform.c
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.astro/transform.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.astro/transform.c	(revision 34783)
@@ -0,0 +1,73 @@
+# include "astro.h"
+
+int transform (int argc, char **argv) {
+
+  int i, j, Nx, Ny;
+  Coords coords_in, coords_out;
+  int X, Y;
+  double x, y, r, d, dx, dy;
+  double frac;
+  char *Sout, *S;
+  float *Vin, *Vout;
+  Buffer *in, *out;
+
+  if (argc != 3) {
+    gprint (GP_ERR, "USAGE: transform <from> <to>\n");
+    return (FALSE);
+  }
+
+  if ((in  = SelectBuffer (argv[1], OLDBUFFER, TRUE)) == NULL) return (FALSE);
+  if ((out = SelectBuffer (argv[2], OLDBUFFER, TRUE)) == NULL) return (FALSE);
+
+  GetCoords (&coords_in, &in[0].header);
+  GetCoords (&coords_out, &out[0].header);
+
+  /* for the moment, disable WRP / DIS */
+  if (!strcmp(&coords_in.ctype[4], "-WRP") || !strcmp(&coords_out.ctype[4], "-WRP")) {
+    gprint (GP_ERR, "WRP mode not implemented for astrom\n");
+    return (FALSE);
+  }
+  
+  // double scale_in = sqrt(fabs(coords_in.cdelt1*coords_in.cdelt2*(coords_in.pc1_1*coords_in.pc2_2 - coords_in.pc1_2*coords_in.pc2_1)));
+  // double scale_out = sqrt(fabs(coords_out.cdelt1*coords_out.cdelt2*(coords_out.pc1_1*coords_out.pc2_2 - coords_out.pc1_2*coords_out.pc2_1)));
+
+  Vin  = (float *) in[0].matrix.buffer;
+  Vout = (float *) out[0].matrix.buffer;
+  Nx = out[0].header.Naxis[0];
+  Ny = out[0].header.Naxis[1];
+  bzero (Vout, Nx*Ny*sizeof(float));
+  ALLOCATE (S, char, Nx*Ny);
+  Sout = S;
+  bzero (Sout, Nx*Ny*sizeof(char));
+  frac = 0.333;
+
+  /* if (scale_in < scale_out) { */
+
+  for (j = 0; j < in[0].header.Naxis[1]; j++) {
+    gprint (GP_ERR, ".");
+    for (i = 0; i < in[0].header.Naxis[0]; i++, Vin++) {
+      for (dx = 0.0 + 0.5*frac; dx < 1.0 - 0.5*frac; dx += frac) {
+	for (dy = 0.0 + 0.5*frac; dy < 1.0 - 0.5*frac; dy += frac) {
+	  XY_to_RD (&r, &d, i + dx, j + dy, &coords_in);
+	  RD_to_XY (&x, &y, r, d, &coords_out);
+	  X = x; Y = y;
+	  if ((X > -1) && (X < Nx) && (Y > -1) && (Y < Ny)) {
+	    Vout[X + Y*Nx] += *Vin;
+	    Sout[X + Y*Nx] ++;
+	  }
+	}
+      }
+    }
+  }
+
+  Sout = S;
+  Vout = (float *) out[0].matrix.buffer;
+  for (i = 0; i < Nx*Ny; i++, Vout++, Sout++) {
+    *Vout = *Vout / *Sout;
+  }
+
+  free (S);
+    
+  return (TRUE);
+}
+
Index: /branches/sc_branches/pantasks_condor/cmd.astro/warp.c
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.astro/warp.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.astro/warp.c	(revision 34783)
@@ -0,0 +1,237 @@
+# include "astro.h"
+
+/*** needs mosaic astrometry ***/
+
+static double XO, XX, XY;
+static double YO, YX, YY;
+int ZERO;
+
+int map_output_to_input (int Npix, double df);
+int map_input_to_output (int Npix, double df);
+void set_linear_terms (Coords *in, Coords *out, int i, int j, int Npix);
+void apply_terms (double *Xout, double *Yout, double Xin, double Yin);
+
+Coords coords_in, coords_out;
+Buffer *in, *out, *wt;
+
+int warp (int argc, char **argv) {
+
+  int Nlinear, Np, N;
+  double scale_in, scale_out, df;
+
+  ZERO = FALSE;
+  if ((N = get_argument (argc, argv, "-zero"))) {
+    ZERO = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+
+  if (argc != 5) {
+    gprint (GP_ERR, "USAGE: transform <from> <to> <weight> (Nlinear)\n");
+    gprint (GP_ERR, "  output buffer must exist with target astrometry header\n");
+    gprint (GP_ERR, "  Nlinear is the pixel scale for linear astrometric transformation\n");
+    return (FALSE);
+  }
+
+  if ((in  = SelectBuffer (argv[1], OLDBUFFER, TRUE)) == NULL) return (FALSE);
+  if ((out = SelectBuffer (argv[2], OLDBUFFER, TRUE)) == NULL) return (FALSE);
+  if ((wt  = SelectBuffer (argv[3], OLDBUFFER, TRUE)) == NULL) return (FALSE);
+  Nlinear = atoi (argv[4]);
+
+  GetCoords (&coords_in, &in[0].header);
+  GetCoords (&coords_out, &out[0].header);
+
+  /* for the moment, disable WRP / DIS */
+  if (!strcmp(&coords_in.ctype[4], "-WRP") || !strcmp(&coords_out.ctype[4], "-WRP")) {
+    gprint (GP_ERR, "WRP mode not implemented for astrom\n");
+    return (FALSE);
+  }
+  
+  scale_in = sqrt(fabs(coords_in.cdelt1*coords_in.cdelt2*(coords_in.pc1_1*coords_in.pc2_2 - coords_in.pc1_2*coords_in.pc2_1)));
+  scale_out = sqrt(fabs(coords_out.cdelt1*coords_out.cdelt2*(coords_out.pc1_1*coords_out.pc2_2 - coords_out.pc1_2*coords_out.pc2_1)));
+  
+  gprint (GP_ERR, "%f - %f\n", scale_in, scale_out);
+
+  if (scale_in > scale_out) {
+    Np = MAX (1, 3*scale_out / scale_in);
+    df = 1.0 / Np;
+    map_output_to_input (Nlinear, df);
+  } else {
+    Np = MAX (1, 3*scale_in / scale_out);
+    df = 1.0 / Np;
+    map_input_to_output (Nlinear, df);
+  }
+  return (TRUE);
+}
+
+/* mode 1: input pixels >> output pixels: loop over output pixels */
+/* mode 2: input pixels << output pixels: loop over input pixels */
+/* mode 3: input pixels ~= output pixels: drizzle input to output */
+
+/* loop over the input pixels, map input output image */
+int map_output_to_input (int Npix, double df) {
+
+  int i, j, Ni, No, Nx, Ny, nx, ny;
+  float *Vin, *Vout, *Vwt;
+  double x, y, X, Y;
+
+  /* loop over output pixels */
+  /* set up pointers for buffers */
+  Vin  = (float *) in[0].matrix.buffer;
+  Vout = (float *) out[0].matrix.buffer;
+  Vwt  = (float *) wt[0].matrix.buffer;
+
+  nx = in[0].header.Naxis[0];
+  ny = in[0].header.Naxis[1];
+  Nx = out[0].header.Naxis[0];
+  Ny = out[0].header.Naxis[1];
+
+  if (ZERO) {
+    bzero (Vout, Nx*Ny*sizeof(float));
+    bzero (Vwt,  Nx*Ny*sizeof(float));
+  }
+
+  for (j = 0; j < Ny; j+=Npix) {
+    for (i = 0; i < Nx; i+=Npix) {
+      
+      /* define linear transformation in region */
+      set_linear_terms (&coords_out, &coords_in, i, j, Npix);
+
+      for (X = i; (X < i + Npix) && (X < Nx); X += df) {
+	for (Y = j; (Y < j + Npix) && (Y < Ny); Y += df) {
+	  
+	  No = (int)X + ((int)Y)*Nx;
+	  apply_terms (&x, &y, X, Y);
+	  if (x < 0) continue;
+	  if (x >= nx) continue;
+	  if (y < 0) continue;
+	  if (y >= ny) continue;
+	  Ni = (int)x + ((int)y)*nx;
+
+	  Vout[No] += Vin[Ni];
+	  Vwt[No] ++;
+	}
+      }
+    }
+  }
+  return (TRUE);
+}
+
+/* loop over the input pixels, map input output image */
+int map_input_to_output (int Npix, double df) {
+
+  int i, j, Ni, No, Nx, Ny, nx, ny;
+  float *Vin, *Vout, *Vwt;
+  double x, y, X, Y;
+
+  /* loop over output pixels */
+  /* set up pointers for buffers */
+  Vin  = (float *) in[0].matrix.buffer;
+  Vout = (float *) out[0].matrix.buffer;
+  Vwt  = (float *) wt[0].matrix.buffer;
+
+  Nx = in[0].header.Naxis[0];
+  Ny = in[0].header.Naxis[1];
+  nx = out[0].header.Naxis[0];
+  ny = out[0].header.Naxis[1];
+
+  if (ZERO) {
+    bzero (Vout, nx*ny*sizeof(float));
+    bzero (Vwt,  nx*ny*sizeof(float));
+  }
+
+  for (j = 0; j < Ny; j+=Npix) {
+    for (i = 0; i < Nx; i+=Npix) {
+      
+      /* define linear transformation in region */
+      set_linear_terms (&coords_in, &coords_out, i, j, Npix);
+
+      for (X = i; (X < i + Npix) && (X < Nx); X += df) {
+	for (Y = j; (Y < j + Npix) && (Y < Ny); Y += df) {
+	  
+	  Ni = (int)X + ((int)Y)*Nx;
+	  apply_terms (&x, &y, X, Y);
+	  if (x < 0) continue;
+	  if (x >= nx) continue;
+	  if (y < 0) continue;
+	  if (y >= ny) continue;
+	  No = (int)x + ((int)y)*nx;
+
+	  Vout[No] += Vin[Ni];
+	  Vwt[No] ++;
+	}
+      }
+    }
+  }
+  return (TRUE);
+}
+
+/* find the linear astrometric fix between images at this location */
+void set_linear_terms (Coords *in, Coords *out, int i, int j, int Npix) {
+
+  int n;
+  double x, y, x2, y2, xy, X, Y, Xx, Xy, Yx, Yy;
+  double Xin, Yin, Xout, Yout;
+  double Sx2, Sy2, Sxy, SXx, SXy, SYx, SYy;
+  double N, r, d;
+
+  Xin = Yin = 0;
+  N = x = y = x2 = y2 = xy = X = Y = Xx = Xy = Yx = Yy = 0;
+
+  /* define several test points, fit a line to the input,output pairs */
+  for (n = 0; n < 3; n++) {
+
+    switch (n) {
+    case 0:
+      Xin = i;
+      Yin = j;
+      break;
+    case 1:
+      Xin = i + Npix;
+      Yin = j;
+      break;
+    case 2:
+      Xin = i;
+      Yin = j + Npix;
+      break;
+    }
+
+    XY_to_RD (&r, &d, Xin, Yin, in);
+    RD_to_XY (&Xout, &Yout, r, d, out);
+
+    x  += Xin;
+    y  += Yin;
+    x2 += Xin*Xin;
+    y2 += Yin*Yin;
+    xy += Xin*Yin;
+    X  += Xout;
+    Y  += Yout;
+    Xx += Xout*Xin;
+    Xy += Xout*Yin;
+    Yx += Yout*Xin;
+    Yy += Yout*Yin;
+    N  += 1.0;
+  }
+
+  Sx2 = x2 - x*x/N;
+  Sy2 = y2 - y*y/N;
+  Sxy = xy - x*y/N;
+  SXx = Xx - X*x/N;
+  SXy = Xy - X*y/N;
+  SYx = Yx - Y*x/N;
+  SYy = Yy - Y*y/N;
+  
+  XX = (SXx*Sy2 - SXy*Sxy) / (Sx2*Sy2 - Sxy*Sxy);
+  XY = (SXy*Sx2 - SXx*Sxy) / (Sx2*Sy2 - Sxy*Sxy);
+  XO = X/N - XX*x/N - XY*y/N;
+
+  YX = (SYx*Sy2 - SYy*Sxy) / (Sx2*Sy2 - Sxy*Sxy);
+  YY = (SYy*Sx2 - SYx*Sxy) / (Sx2*Sy2 - Sxy*Sxy);
+  YO = Y/N - YX*x/N - YY*y/N;
+
+}
+
+
+void apply_terms (double *Xout, double *Yout, double Xin, double Yin) {
+  *Xout = XO + XX*Xin + XY*Yin;
+  *Yout = YO + YX*Xin + YY*Yin;
+}
Index: /branches/sc_branches/pantasks_condor/cmd.basic/Makefile
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.basic/Makefile	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.basic/Makefile	(revision 34783)
@@ -0,0 +1,83 @@
+default: libbasiccmd
+
+include ../../../Makefile.System
+HOME    =       $(ROOT)/src/opihi
+SRC     =       $(HOME)/cmd.basic
+BIN     =       $(HOME)/bin
+LIB     =       $(HOME)/lib
+INC     =       $(HOME)/include
+include ../Makefile.Common
+
+# programs may add their own internal requirements here
+FULL_CFLAGS   = $(BASE_CFLAGS) -fPIC
+FULL_CPPFLAGS = $(BASE_CPPFLAGS)
+FULL_LDFLAGS  = $(BASE_LDFLAGS)
+
+# basic user commands ########################
+
+srcs = \
+$(SRC)/init.$(ARCH).o       \
+$(SRC)/break.$(ARCH).o	     \
+$(SRC)/cd.$(ARCH).o	     \
+$(SRC)/config.$(ARCH).o     \
+$(SRC)/continue.$(ARCH).o   \
+$(SRC)/basename.$(ARCH).o     \
+$(SRC)/dirname.$(ARCH).o     \
+$(SRC)/date.$(ARCH).o	     \
+$(SRC)/echo.$(ARCH).o	     \
+$(SRC)/file.$(ARCH).o	     \
+$(SRC)/getchr.$(ARCH).o     \
+$(SRC)/help.$(ARCH).o	     \
+$(SRC)/input.$(ARCH).o	     \
+$(SRC)/inthash.$(ARCH).o     \
+$(SRC)/list.$(ARCH).o	     \
+$(SRC)/list_help.$(ARCH).o  \
+$(SRC)/list_vars.$(ARCH).o  \
+$(SRC)/local.$(ARCH).o	     \
+$(SRC)/macro.$(ARCH).o	     \
+$(SRC)/memory.$(ARCH).o     \
+$(SRC)/mkdir.$(ARCH).o     \
+$(SRC)/module.$(ARCH).o     \
+$(SRC)/nop.$(ARCH).o        \
+$(SRC)/opihi.$(ARCH).o     \
+$(SRC)/output.$(ARCH).o     \
+$(SRC)/quit.$(ARCH).o	     \
+$(SRC)/run_for.$(ARCH).o    \
+$(SRC)/run_foreach.$(ARCH).o    \
+$(SRC)/run_if.$(ARCH).o     \
+$(SRC)/run_while.$(ARCH).o  \
+$(SRC)/scan.$(ARCH).o	     \
+$(SRC)/shell.$(ARCH).o	     \
+$(SRC)/sprintf.$(ARCH).o    \
+$(SRC)/fprintf.$(ARCH).o    \
+$(SRC)/strlen.$(ARCH).o     \
+$(SRC)/substr.$(ARCH).o     \
+$(SRC)/strhash.$(ARCH).o     \
+$(SRC)/strpop.$(ARCH).o     \
+$(SRC)/strsub.$(ARCH).o     \
+$(SRC)/usleep.$(ARCH).o     \
+$(SRC)/sleep.$(ARCH).o	     \
+$(SRC)/wait.$(ARCH).o	     \
+$(SRC)/which.$(ARCH).o
+
+# dependancy rules for include files ########################
+incs = \
+$(INC)/opihi.h \
+$(INC)/external.h \
+$(INC)/shell.h \
+$(INC)/dvomath.h \
+$(INC)/display.h 
+
+$(srcs): $(incs)
+
+$(LIB)/libbasiccmd.$(ARCH).a: $(srcs)
+$(LIB)/libbasiccmd.$(ARCH).$(DLLTYPE): $(srcs)
+
+$(DESTLIB)/libbasiccmd.a: $(LIB)/libbasiccmd.$(ARCH).a
+$(DESTLIB)/libbasiccmd.$(DLLTYPE): $(LIB)/libbasiccmd.$(ARCH).$(DLLTYPE)
+
+libbasiccmd: $(DESTLIB)/libbasiccmd.a $(DESTLIB)/libbasiccmd.$(DLLTYPE)
+
+uninstall:
+	rm -f $(DESTLIB)/libbasiccmd.a
+	rm -f $(DESTLIB)/libbasiccmd.$(DLLTYPE)
Index: /branches/sc_branches/pantasks_condor/cmd.basic/basename.c
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.basic/basename.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.basic/basename.c	(revision 34783)
@@ -0,0 +1,51 @@
+# include "basic.h"
+
+int basename_opihi (int argc, char **argv) {
+
+  int N;
+  char *baseName, *varName, *suffixName;
+
+  varName = NULL;
+  if ((N = get_argument (argc, argv, "-var"))) {
+    remove_argument (N, &argc, argv);
+    varName = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  suffixName = NULL;
+  if ((N = get_argument (argc, argv, "-suffix"))) {
+    remove_argument (N, &argc, argv);
+    suffixName = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  if (argc < 2) {
+    gprint (GP_ERR, "USAGE: dirname (path) [-var name] [-suffix suffix]\n");
+    return (FALSE);
+  }
+
+  baseName = filebasename (argv[1]);
+
+  // strip suffix, if supplied
+  if (suffixName != NULL) {
+      if (strlen(baseName) > strlen(suffixName)) {
+	  char *ptr = baseName + strlen(baseName) - strlen(suffixName);
+	  if (!strcmp (ptr, suffixName)) {
+	      *ptr = 0;
+	  }
+      }
+  }
+
+  if (varName == NULL) {
+    gprint (GP_LOG, "%s\n", baseName);
+  } else {
+    set_str_variable (varName, baseName);
+    free (varName);
+  }    
+
+  free (baseName);
+  return (TRUE);
+}
+
+// XXX need to add mode option
+// XXX need to respect umask (need umask command?)
Index: /branches/sc_branches/pantasks_condor/cmd.basic/break.c
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.basic/break.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.basic/break.c	(revision 34783)
@@ -0,0 +1,35 @@
+# include "basic.h"
+
+// auto_break is currently a global
+int exec_break (int argc, char **argv) {
+
+  int N, value;
+
+  if ((N = get_argument (argc, argv, "-h"))) goto usage;
+  if ((N = get_argument (argc, argv, "-help"))) goto usage;
+  if ((N = get_argument (argc, argv, "--help"))) goto usage;
+
+  if ((N = get_argument (argc, argv, "-auto"))) {
+    remove_argument (N, &argc, argv);
+    if (N == argc) {
+      if (auto_break) 
+	gprint (GP_ERR, "auto break on\n");
+      else 
+	gprint (GP_ERR, "auto break off\n");
+      return (FALSE);
+    }
+    value = -1;
+    if (!strcasecmp (argv[N], "on")) value = 1;
+    if (!strcasecmp (argv[N], "off")) value = 0;
+    if (value == -1) goto usage;
+    auto_break = value;
+    return (TRUE);
+  }
+  
+  loop_break = TRUE;
+  return (FALSE);
+  
+usage:
+  gprint (GP_ERR, "USAGE: break -auto [on / off]\n");
+  return (FALSE);
+}
Index: /branches/sc_branches/pantasks_condor/cmd.basic/cd.c
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.basic/cd.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.basic/cd.c	(revision 34783)
@@ -0,0 +1,66 @@
+# include "basic.h"
+
+int cd (int argc, char **argv) {
+
+  int N, VERBOSE, status;
+  char *cwd;
+
+  VERBOSE = TRUE;
+  if ((N = get_argument (argc, argv, "-q"))) {
+    remove_argument (N, &argc, argv);
+    VERBOSE = FALSE;
+  }
+
+  if (argc != 2) {
+    gprint (GP_ERR, "USAGE: cd <path>\n");
+    return (FALSE);
+  }
+
+  status = chdir (argv[1]);
+  if (!status) {
+    if ((cwd = getcwd (NULL, 1024)) == NULL) {
+      gprint (GP_ERR, "error getting cwd\n");
+      return (FALSE);
+    }
+    if (VERBOSE) gprint (GP_LOG, "cwd: %s\n", cwd);
+    real_free (cwd);
+    return (TRUE);
+  }
+
+  gprint (GP_ERR, "error changing to %s\n", argv[1]);
+  return (FALSE);
+
+}
+
+int pwd (int argc, char **argv) {
+
+  int N;
+  char *cwd, *var;
+
+  var = NULL;
+  if ((N = get_argument (argc, argv, "-var"))) {
+    remove_argument (N, &argc, argv);
+    var = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  if (argc != 1) {
+    gprint (GP_ERR, "USAGE: pwd [-var variable]\n");
+    return (FALSE);
+  }
+  
+  if ((cwd = getcwd(NULL, 1024)) == NULL) {
+    gprint (GP_ERR, "error getting cwd\n");
+    if (var != NULL) free (var);
+    return (FALSE);
+  }
+  if (var == NULL) {
+      gprint (GP_LOG, "cwd: %s\n", cwd);
+  } else {
+      set_str_variable (var, cwd);
+      free (var);
+  }
+  real_free (cwd);
+  return (TRUE);
+  
+}
Index: /branches/sc_branches/pantasks_condor/cmd.basic/config.c
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.basic/config.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.basic/config.c	(revision 34783)
@@ -0,0 +1,9 @@
+# include "basic.h"
+
+int config (int argc, char **argv) {
+
+  if (!ConfigInit (&argc, argv)) return (FALSE);
+
+  return (TRUE);
+
+}
Index: /branches/sc_branches/pantasks_condor/cmd.basic/continue.c
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.basic/continue.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.basic/continue.c	(revision 34783)
@@ -0,0 +1,12 @@
+# include "basic.h"
+
+int exec_next (int argc, char **argv) {
+  loop_next = TRUE;
+  return (TRUE);
+}
+
+int exec_last (int argc, char **argv) {
+  loop_last = TRUE;
+  return (TRUE);
+}
+
Index: /branches/sc_branches/pantasks_condor/cmd.basic/date.c
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.basic/date.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.basic/date.c	(revision 34783)
@@ -0,0 +1,57 @@
+# include "basic.h"
+
+int date (int argc, char **argv) {
+  
+  int N, SECONDS, REFTIME;
+  struct timeval now;
+  char *tstring = NULL;
+  char *varName = NULL;
+
+  SECONDS = FALSE;
+  if ((N = get_argument (argc, argv, "-seconds"))) {
+    remove_argument (N, &argc, argv);
+    SECONDS = TRUE;
+  } else {
+    ALLOCATE (tstring, char, 32);
+  }
+
+  REFTIME = 0;
+  if ((N = get_argument (argc, argv, "-reftime"))) {
+    remove_argument (N, &argc, argv);
+    REFTIME = atoi (argv[N]);
+    remove_argument (N, &argc, argv);
+  } 
+
+  varName = NULL;
+  if ((N = get_argument (argc, argv, "-var"))) {
+    remove_argument (N, &argc, argv);
+    varName = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  if (argc != 1) {
+    gprint (GP_ERR, "USAGE: date [-var variable] [-seconds] [-reftime seconds]\n");
+    return (FALSE);
+  }
+
+  gettimeofday (&now, NULL);
+  if (SECONDS) {
+    if (varName) {
+      set_int_variable (varName, now.tv_sec - REFTIME);
+    } else {
+      gprint (GP_ERR, "%d\n", (int) now.tv_sec - REFTIME);
+    }
+  } else {
+    ctime_r (&now.tv_sec, tstring);
+    N = strlen (tstring) - 1;
+    tstring[N] = 0;
+
+    if (varName) {
+      set_str_variable (varName, tstring);
+    } else {
+      gprint (GP_ERR, "%s\n", tstring);
+    }
+    free (tstring);
+  }
+  return (TRUE);
+}
Index: /branches/sc_branches/pantasks_condor/cmd.basic/dirname.c
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.basic/dirname.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.basic/dirname.c	(revision 34783)
@@ -0,0 +1,34 @@
+# include "basic.h"
+
+int dirname_opihi (int argc, char **argv) {
+
+  int N;
+  char *dirName, *varName;
+
+  varName = NULL;
+  if ((N = get_argument (argc, argv, "-var"))) {
+    remove_argument (N, &argc, argv);
+    varName = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  if (argc < 2) {
+    gprint (GP_ERR, "USAGE: dirname (path) [-var name]\n");
+    return (FALSE);
+  }
+
+  dirName = pathname (argv[1]);
+
+  if (varName == NULL) {
+    gprint (GP_LOG, "%s\n", dirName);
+  } else {
+    set_str_variable (varName, dirName);
+    free (varName);
+  }    
+
+  free (dirName);
+  return (TRUE);
+}
+
+// XXX need to add mode option
+// XXX need to respect umask (need umask command?)
Index: /branches/sc_branches/pantasks_condor/cmd.basic/echo.c
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.basic/echo.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.basic/echo.c	(revision 34783)
@@ -0,0 +1,24 @@
+# include "basic.h"
+
+int echo (int argc, char **argv) {
+  
+  int i, N, RETURN_CHAR;
+
+  RETURN_CHAR = TRUE;
+  if ((N = get_argument (argc, argv, "-no-return"))) {
+    remove_argument (N, &argc, argv);
+    RETURN_CHAR = FALSE;
+  }
+
+  for (i = 1; i < argc - 1; i++) {
+    gprint (GP_LOG, "%s ", argv[i]);
+  }
+  if (argc >= 2) {
+    if (RETURN_CHAR) {
+      gprint (GP_LOG, "%s\n", argv[argc - 1]);
+    } else {
+      gprint (GP_LOG, "%s", argv[argc - 1]);
+    }
+  }
+  return (TRUE);
+}
Index: /branches/sc_branches/pantasks_condor/cmd.basic/file.c
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.basic/file.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.basic/file.c	(revision 34783)
@@ -0,0 +1,32 @@
+# include "basic.h"
+
+int file (int argc, char **argv) {
+  
+  /* usage: file (filename) [var] */
+
+  int status, vstat;
+  struct stat fstats;
+
+  if (argc < 2) {
+    gprint (GP_ERR, "USAGE: file (filename) [var]\n");
+    return (FALSE);
+  }
+
+  status = stat (argv[1], &fstats);
+
+  vstat = !status;
+
+  if (argc == 3) {
+      
+    set_int_variable (argv[2], vstat);
+
+  } else {
+
+    gprint (GP_ERR, "file %s is ", argv[1]);
+    if (!vstat) gprint (GP_ERR, "not ");
+    gprint (GP_ERR, "found\n");
+    
+  }
+  return (TRUE);
+}
+
Index: /branches/sc_branches/pantasks_condor/cmd.basic/fprintf.c
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.basic/fprintf.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.basic/fprintf.c	(revision 34783)
@@ -0,0 +1,89 @@
+# include "basic.h"
+# define NCHAR 1024
+
+// XXX this function should ALLOCATE the output buffers
+int fprintf_opihi (int argc, char **argv) {
+
+  int i;
+  char line[NCHAR], tmp[NCHAR], fmt[NCHAR];
+  char *p1, *p2, *q;
+
+  if (argc < 2) {
+    gprint (GP_ERR, "USAGE: fprintf format value value ...\n");
+    return (FALSE);
+  }
+
+  q  = line;
+  bzero (line, NCHAR);
+
+  p1 = argv[1];
+  for (i = 2; i < argc; i++) {
+    bzero (tmp, NCHAR);
+    bzero (fmt, NCHAR);
+
+    /* find next format character */
+    p2 = strchr (p1, '%');
+    if (p2 == (char *) NULL) {
+      gprint (GP_ERR, "mismatch between format and values\n");
+      return (FALSE);
+    }
+    if (strlen(q) + p2 - p1 > NCHAR) {
+      gprint (GP_ERR, "line too long");
+      return (FALSE);
+    }
+    memcpy (q, p1, p2-p1);
+    q = line + strlen(line);
+    
+    /* identify type (%NNNs %NNNNd %NNNNf) */
+    for (p1 = p2 + 1; (*p1 == '.') || (*p1 == '-') || (*p1 == '+') || (*p1 == ' ') || isdigit(*p1); p1++);
+    memcpy (fmt, p2, p1 - p2 + 1);
+    switch (*p1) {
+      case 'e':
+      case 'g':
+      case 'f':
+      case 'E':
+      case 'F':
+      case 'G':
+	sprintf (tmp, fmt, strtod (argv[i], NULL));
+	break;
+      case 's':
+	sprintf (tmp, fmt, argv[i]);
+	break;
+      case 'd':
+      case 'o':
+      case 'i':
+      case 'u':
+      case 'c':
+      case 'x':
+      case 'X':
+	sprintf (tmp, fmt, strtol(argv[i], NULL, 0));
+	break;
+      default:
+	gprint (GP_ERR, "syntax error in format (e,f,g,E,F,G,c,s,d,i,o,x,X conversions allowed)\n");
+	return (FALSE);
+    }
+    if (strlen(q) + strlen(tmp) > NCHAR) {
+      gprint (GP_ERR, "line too long");
+      return (FALSE);
+    }
+    memcpy (q, tmp, strlen(tmp));
+    q = line + strlen(line);
+    p1++;
+  }
+  p2 = strchr (p1, '%');
+  if (p2 != (char *) NULL) {
+    gprint (GP_ERR, "mismatch between format and values\n");
+    return (FALSE);
+  }
+  
+  p2 = p1 + strlen (p1);
+  if (strlen(q) + p2 - p1 > NCHAR) {
+    gprint (GP_ERR, "line too long");
+    return (FALSE);
+  }
+  memcpy (q, p1, p2-p1);
+  gprint (GP_LOG, "%s\n", line);
+
+  return (TRUE);
+}
+
Index: /branches/sc_branches/pantasks_condor/cmd.basic/getchr.c
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.basic/getchr.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.basic/getchr.c	(revision 34783)
@@ -0,0 +1,29 @@
+# include "basic.h"
+
+int getchr_func (int argc, char **argv) {
+
+  /* returns position of the first given character */ 
+  char *c;
+  int pos;
+
+  if ((argc != 3) && (argc != 4)) {
+    gprint (GP_ERR, "USAGE: getchr (string) (char) [var]\n");
+    return (FALSE);
+  }
+
+  c = strchr (argv[1], argv[2][0]);
+
+  if (c == (char *) NULL) {
+    pos = -1;
+  } else {
+    pos = c - argv[1];
+  }
+
+  if (argc == 4) {
+    set_int_variable (argv[3], pos);
+  } else {
+    gprint (GP_ERR, "%d\n", pos);
+  }
+  return (TRUE);
+
+}
Index: /branches/sc_branches/pantasks_condor/cmd.basic/help.c
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.basic/help.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.basic/help.c	(revision 34783)
@@ -0,0 +1,42 @@
+# include "basic.h"
+
+int help (int argc, char **argv) {
+
+  int Nbytes;
+  FILE *f;
+  char *helpdir, *file, buff[512];
+
+  helpdir = get_variable ("HELPDIR");
+  if (helpdir == (char *) NULL) {
+    gprint (GP_ERR, "variable HELPDIR not found\n");
+    return (FALSE);
+  }
+
+  if (argc == 1) {
+    sprintf (buff, "ls %s", helpdir);
+    if (system (buff) == -1) {
+      fprintf (stderr, "help directory unavailable\n");
+      return (FALSE);
+    }
+    return (TRUE);
+  }
+
+  Nbytes = strlen(helpdir) + strlen(argv[1]) + 2;
+  ALLOCATE (file, char, Nbytes);
+  snprintf (file, Nbytes, "%s/%s", helpdir, argv[1]);
+
+  f = fopen (file, "r");
+  free (file);
+
+  if (f == NULL) {
+    gprint (GP_ERR, "No help for: %s\n", argv[1]);
+    return (FALSE);
+  }
+
+  while (scan_line (f, buff) != EOF)
+    gprint (GP_LOG, "%s\n", buff);
+
+  fclose (f);
+  return (TRUE);
+}
+
Index: /branches/sc_branches/pantasks_condor/cmd.basic/help/!
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.basic/help/!	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.basic/help/!	(revision 34783)
@@ -0,0 +1,6 @@
+
+  exec (line)
+
+  perform a system call.  this can also be invoked with "!".  
+  don't use ! in a macro or input script as it is interpreted as a
+  comment character. 
Index: /branches/sc_branches/pantasks_condor/cmd.basic/help/?
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.basic/help/?	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.basic/help/?	(revision 34783)
@@ -0,0 +1,6 @@
+
+  exec (line)
+
+  perform a system call.  this can also be invoked with "!".  
+  don't use ! in a macro or input script as it is interpreted as a
+  comment character. 
Index: /branches/sc_branches/pantasks_condor/cmd.basic/help/Opihi
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.basic/help/Opihi	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.basic/help/Opihi	(revision 34783)
@@ -0,0 +1,5 @@
+
+
+    Opihi
+
+  
Index: /branches/sc_branches/pantasks_condor/cmd.basic/help/Shell
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.basic/help/Shell	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.basic/help/Shell	(revision 34783)
@@ -0,0 +1,5 @@
+
+
+    Opihi
+
+  
Index: /branches/sc_branches/pantasks_condor/cmd.basic/help/Variables
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.basic/help/Variables	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.basic/help/Variables	(revision 34783)
@@ -0,0 +1,17 @@
+
+  Opihi shell variables (scalars):
+
+  The Opihi shell can have variables which are designated by a word
+following a dollar sign: $foo.  Certain Mana function create and set
+variables (ie, stats, cursor).  You can also set a variable to the
+result of an arithmetic expression or just a number like this:
+
+  $foo = 10.0
+  $bar = 5*$foo + 0.6
+
+Mana will replace the variables on a line before executing a command.
+See also "Math" for more description of arithmetic operations.
+
+  See Also:  Math
+
+
Index: /branches/sc_branches/pantasks_condor/cmd.basic/help/break
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.basic/help/break	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.basic/help/break	(revision 34783)
@@ -0,0 +1,10 @@
+
+  break [-auto on/off]
+
+  halts macro, if, or for processes and returns to the basic prompt.
+
+  -auto: enables or disables DVO from automatically breaking
+   when it encounters an error
+
+  See also: macro, if, for
+
Index: /branches/sc_branches/pantasks_condor/cmd.basic/help/cd
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.basic/help/cd	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.basic/help/cd	(revision 34783)
@@ -0,0 +1,9 @@
+
+   cd (path)
+
+   change current working directory. 
+
+   Note that Kii or Kapa will be launched in the original 
+   directory, which means PS files created by Kii or Kapa 
+   will land in that directory (perhaps an unexpected result).
+
Index: /branches/sc_branches/pantasks_condor/cmd.basic/help/continue
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.basic/help/continue	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.basic/help/continue	(revision 34783)
@@ -0,0 +1,4 @@
+
+   continue
+
+   Return to the start of a for-loop.
Index: /branches/sc_branches/pantasks_condor/cmd.basic/help/date
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.basic/help/date	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.basic/help/date	(revision 34783)
@@ -0,0 +1,38 @@
+
+  date [-var variable] [-seconds] [-reftime seconds]
+
+  Return the date and place it into (variable).
+
+  Default: print to window
+
+* by itself, just echoes the date & time
+
+* to set a variable with the date, add -var name, where 'name' is the
+  name of your variable.  by default, this is a human readable date,
+  eg Wed Mar 12 08:30:11 2008
+
+* to calculate time differences, add the -seconds flag, and the value
+  will be in seconds since 1970 Jan 01 00:00:00.  NOTE: There is a bug
+  in that the variable is represented as a float, and the value is now
+  > 1.2e9 seconds.  The difference is thus not very accurate.  To get
+  around this, use the -reftime feature to report the seconds since a
+  more recent date.  A value of something like 1205340000 works at the
+  moment.
+
+examples:
+
+  dvo: date
+  Wed Mar 12 08:30:11 2008
+
+  dvo: date -var foo -seconds
+  dvo: echo $foo
+  1205346664
+
+  dvo: date -var foo -seconds -reftime 1205340000
+  dvo: echo $foo
+  6700
+  dvo: date -var bar -seconds -reftime 1205340000
+  dvo: echo $bar
+  6708
+  dvo: echo {$bar - $foo}
+  8
Index: /branches/sc_branches/pantasks_condor/cmd.basic/help/echo
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.basic/help/echo	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.basic/help/echo	(revision 34783)
@@ -0,0 +1,10 @@
+
+  echo [anything]
+
+  prints the rest of the line, after variables and math expressions
+are parsed.  
+
+
+  See Also:  Opihi, math
+
+
Index: /branches/sc_branches/pantasks_condor/cmd.basic/help/exec
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.basic/help/exec	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.basic/help/exec	(revision 34783)
@@ -0,0 +1,6 @@
+
+   exec (shell command)
+
+   Allows a shell command to be run while running DVO
+   On the command line, this can also be done by placing a "!" before the shell
+    command
Index: /branches/sc_branches/pantasks_condor/cmd.basic/help/file
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.basic/help/file	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.basic/help/file	(revision 34783)
@@ -0,0 +1,9 @@
+
+   file (filename) (variable)
+
+   Checks to see if a file exists
+
+   If a variable is specified, then it is set to 1 if the file is present
+    and set to 0 if it is not.
+
+   Default: output printed to the window
Index: /branches/sc_branches/pantasks_condor/cmd.basic/help/for
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.basic/help/for	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.basic/help/for	(revision 34783)
@@ -0,0 +1,19 @@
+
+   for var (start) (stop) [delta]
+
+    perform a loop, with $var as the loop variable. 
+    the value of $var runs from (start) to (stop) inclusive, 
+    and is incremented by [delta] (1 by default).
+
+    The loop commands are entered until the appropriate "end" is typed;
+    if there are nested loops, macro definitions, or logical blocks,
+    the correct "end" is used!
+
+    for may be used at the command line, in a macro, or in an input
+    file with no ill effects.
+
+    the variable $var may be accessed during the loop execution.  if
+    the variable $var is assigned a value beyond (stop) during the
+    loop, the loop execution will end.
+
+    See also: macro, if
Index: /branches/sc_branches/pantasks_condor/cmd.basic/help/fprintf
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.basic/help/fprintf	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.basic/help/fprintf	(revision 34783)
@@ -0,0 +1,15 @@
+
+   fprintf ("format codes") (value1 value2 ...)
+
+   Prints output according to a specified format
+
+   Fomat codes must match the order and number of values.
+
+   FORMAT CODES:
+    %x.yf : floating point number with x total places and y places after
+             the decimal point; further decimal places are rounded
+    %x.ye : number in scientific notation with x places and y places after
+             the decimal point; further decimal places are rounded
+    %xs : ASCII output with x places
+    %xd : Integers with x places
+    %x : Express number in hexidesimal
Index: /branches/sc_branches/pantasks_condor/cmd.basic/help/getchr
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.basic/help/getchr	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.basic/help/getchr	(revision 34783)
@@ -0,0 +1,8 @@
+
+   getchr (string) (char) [variable]
+
+   Return the index of the string in the position of the specified character
+
+   Only the first instance of a character is returned.
+
+   If not found, the variable is set to -1.
Index: /branches/sc_branches/pantasks_condor/cmd.basic/help/help
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.basic/help/help	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.basic/help/help	(revision 34783)
@@ -0,0 +1,7 @@
+
+   help (function)
+
+   print the contents of the help file.  
+   typing "help" without an argument will list the available help
+   files.  
+
Index: /branches/sc_branches/pantasks_condor/cmd.basic/help/if
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.basic/help/if	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.basic/help/if	(revision 34783)
@@ -0,0 +1,22 @@
+
+   if (logic)
+     (commands)
+   end
+
+   or:
+
+   if (logic)
+     (commands)
+   else 
+     (commnds)
+   end
+
+   logical block.  The commands are performed subject to the logical
+   condition.  The syntax is simplified C:  
+   examples: (x = 6), (x < 6) , (x ! 6), ((x = 6) | (x > 10))
+
+   there are no delimiter characters.
+
+   See also: for, macro
+
+   
Index: /branches/sc_branches/pantasks_condor/cmd.basic/help/input
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.basic/help/input	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.basic/help/input	(revision 34783)
@@ -0,0 +1,15 @@
+
+   input (filename)
+
+   execute a series of commands given in the file.
+
+   Two special examples:  
+   ~/.manarc is loaded on startup, which give the user a place to
+   define personal macros, or interesting variables, etc.
+
+   If mana is called in the form "mana file", the file is loaded
+   (after .manarc) before the user is given the command-line prompt.
+   This can be used to invoke mana in a shell script, but care should
+   be taken in a shell-script that the file ends with the word "exit"
+   or "quit", or strange things may happen...
+
Index: /branches/sc_branches/pantasks_condor/cmd.basic/help/list
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.basic/help/list	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.basic/help/list	(revision 34783)
@@ -0,0 +1,23 @@
+
+   list (name) (string1) (string2) ... end
+   OR
+   list (name) -split (string1) (string2) ...
+   OR
+   list (name) -x "(ls command)"
+   OR
+   list (name) -x "cat (file)" 
+
+   Create a list of strings that are zero indexed.
+
+   $(name):n = the number of elements in the list (name)
+
+   $(name):5 = the 6th element of the list (name)
+
+   -split option: Turn a list of strings (separated by white spaces)
+     into a list
+
+   -x option: turn an ls command or file into a list (one list element per
+     line in the file + 1 white space)
+
+   ***Note: the list ... end version of the command does not currently work
+             within macros
Index: /branches/sc_branches/pantasks_condor/cmd.basic/help/local
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.basic/help/local	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.basic/help/local	(revision 34783)
@@ -0,0 +1,7 @@
+
+   local (var)
+
+   Creates a variable that is accessible only within the macro
+    in which it is created.
+
+   Variable value is listed as (macro).(var) using the "??" command
Index: /branches/sc_branches/pantasks_condor/cmd.basic/help/macro
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.basic/help/macro	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.basic/help/macro	(revision 34783)
@@ -0,0 +1,11 @@
+
+   macro (name)
+
+   create a macro.  the commands are entered until the word 'end' is
+   reached.  nested if, for, and macro definitions use the
+   appropriately matched occurances of the 'end'.
+
+   Within a macro, the command-line arguments are refered to as $1,
+   $2, etc.  The number of command-line arguments is given by $0.
+
+   
Index: /branches/sc_branches/pantasks_condor/cmd.basic/help/memory
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.basic/help/memory	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.basic/help/memory	(revision 34783)
@@ -0,0 +1,12 @@
+
+  memory [x]
+
+  list the currently used memory.  This gives a (possibly very) long
+  listing of the size of every allocated piece of memory.  Placing
+  anything after the word "memory" gives a summary also.  This is
+  useful to see just how close you are to filling your computer's
+  entire memory (Does not include memory allocated to Kii!)
+
+  See Also: buffers
+
+
Index: /branches/sc_branches/pantasks_condor/cmd.basic/help/output
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.basic/help/output	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.basic/help/output	(revision 34783)
@@ -0,0 +1,10 @@
+
+  output (filename or "stdout")
+
+  redirect the output to a file (concat to the end of the file).  This
+  can also be done with the UNIX ">".  However, this command is useful
+  to send information to a file for a part of a Mana session.  For
+  example, you can save results of a "stats" command.
+
+  See Also: input, stats
+
Index: /branches/sc_branches/pantasks_condor/cmd.basic/help/pwd
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.basic/help/pwd	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.basic/help/pwd	(revision 34783)
@@ -0,0 +1,7 @@
+
+   pwd -var (variable)
+
+   print current working directory
+
+   Using -var will place the output into the specified variable.
+   Default: print to window
Index: /branches/sc_branches/pantasks_condor/cmd.basic/help/quit
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.basic/help/quit	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.basic/help/quit	(revision 34783)
@@ -0,0 +1,4 @@
+
+   quit
+
+   Exit from DVO
Index: /branches/sc_branches/pantasks_condor/cmd.basic/help/scan
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.basic/help/scan	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.basic/help/scan	(revision 34783)
@@ -0,0 +1,8 @@
+
+   scan (filename) (variable) [Nline]
+
+   Place the Nth line of the file into the named variable
+
+   Default N value: 1 (the 1st line)
+
+   The variable recieves the line + 1 white space
Index: /branches/sc_branches/pantasks_condor/cmd.basic/help/sleep
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.basic/help/sleep	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.basic/help/sleep	(revision 34783)
@@ -0,0 +1,4 @@
+
+   sleep (N)
+
+   Wait for (N) seconds before continuing
Index: /branches/sc_branches/pantasks_condor/cmd.basic/help/sprintf
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.basic/help/sprintf	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.basic/help/sprintf	(revision 34783)
@@ -0,0 +1,17 @@
+
+   sprintf (var) ("format codes") (value1 value2 ...)
+
+   Prints output according to a specified format
+
+   Works same as fprintf, except it places the output as a string into (var)
+
+   Fomat codes must match the order and number of values.
+
+   FORMAT CODES:
+    %x.yf : floating point number with x total places and y places after
+             the decimal point; further decimal places are rounded
+    %x.ye : number in scientific notation with x places and y places after
+             the decimal point; further decimal places are rounded
+    %xs : ASCII output with x places
+    %xd : Integers with x places
+    %x : Express number in hexidesimal
Index: /branches/sc_branches/pantasks_condor/cmd.basic/help/strlen
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.basic/help/strlen	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.basic/help/strlen	(revision 34783)
@@ -0,0 +1,10 @@
+
+   strlen (string) [var]
+
+   Determines the number of characters (plus white spaces) in a string
+    and places it into var
+
+   Default: print to window
+
+   Note: If the string has white spaces, you need to place it within
+    double-quotes.
Index: /branches/sc_branches/pantasks_condor/cmd.basic/help/strpop
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.basic/help/strpop	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.basic/help/strpop	(revision 34783)
@@ -0,0 +1,12 @@
+
+   strpop (string) [var]
+
+   Pops part of a string or a list element that can be placed
+    into [var]
+
+   Default: print to window
+
+   The output is the first set of characters bordered by white space
+
+   ***Note: Both (string) and [var] must be entered without the "$"
+    variable identifier for the program to work properly.
Index: /branches/sc_branches/pantasks_condor/cmd.basic/help/substr
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.basic/help/substr	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.basic/help/substr	(revision 34783)
@@ -0,0 +1,11 @@
+
+   substr (string) (index) (length) [var]
+
+   Returns part of a string starting at the position (index)
+    and of size (length), which can then be placed into [var]
+
+   Default: print to window
+
+   The sub-string is not removed from the original string
+
+   ***Note: Character positions in strings are zero-indexed
Index: /branches/sc_branches/pantasks_condor/cmd.basic/help/usleep
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.basic/help/usleep	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.basic/help/usleep	(revision 34783)
@@ -0,0 +1,4 @@
+
+   usleep (N)
+
+   Wait for (N) microseconds before continuing
Index: /branches/sc_branches/pantasks_condor/cmd.basic/help/wait
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.basic/help/wait	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.basic/help/wait	(revision 34783)
@@ -0,0 +1,5 @@
+
+  wait [anything]
+
+  wait echos the rest of the line and waits for <return> to be typed.
+Very useful for pausing the processing of an input file or a macro.
Index: /branches/sc_branches/pantasks_condor/cmd.basic/help/which
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.basic/help/which	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.basic/help/which	(revision 34783)
@@ -0,0 +1,5 @@
+
+   which (command)
+
+   give a short summary info line on a command
+
Index: /branches/sc_branches/pantasks_condor/cmd.basic/help/while
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.basic/help/while	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.basic/help/while	(revision 34783)
@@ -0,0 +1,5 @@
+
+   while (condition) ... end
+
+   Perform a set of actions until the condition is true
+
Index: /branches/sc_branches/pantasks_condor/cmd.basic/init.c
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.basic/init.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.basic/init.c	(revision 34783)
@@ -0,0 +1,149 @@
+# include "basic.h"
+
+int basename_opihi  PROTO((int, char **));
+int config          PROTO((int, char **));
+int exec_sleep      PROTO((int, char **));
+int exec_usleep     PROTO((int, char **));
+int cd              PROTO((int, char **));
+int date            PROTO((int, char **));
+int dirname_opihi   PROTO((int, char **));
+int echo            PROTO((int, char **));
+int exec_last       PROTO((int, char **));
+int exec_next       PROTO((int, char **));
+int exec_break      PROTO((int, char **));
+int file            PROTO((int, char **));
+int getchr_func     PROTO((int, char **));
+int help            PROTO((int, char **));
+int input           PROTO((int, char **));
+int inthash         PROTO((int, char **));
+int list            PROTO((int, char **));
+int list_help       PROTO((int, char **));
+int list_vars       PROTO((int, char **));
+int local           PROTO((int, char **)); /* data? */
+int macro           PROTO((int, char **));
+int memory          PROTO((int, char **));
+int mkdir_opihi     PROTO((int, char **));
+int module          PROTO((int, char **));
+int nop             PROTO((int, char **));
+int opihi_setmode   PROTO((int, char **));
+int output          PROTO((int, char **));
+int pwd             PROTO((int, char **));
+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 **));
+int scan            PROTO((int, char **));
+int shell           PROTO((int, char **));
+int sprintf_opihi   PROTO((int, char **));
+int fprintf_opihi   PROTO((int, char **));
+int strlen_func     PROTO((int, char **));
+int substr_func     PROTO((int, char **));
+int strpop          PROTO((int, char **));
+int strhash         PROTO((int, char **));
+int strsub          PROTO((int, char **));
+int wait_func       PROTO((int, char **));
+int which           PROTO((int, char **));
+
+/** mapping of the command names to command functions **/
+static Command cmds[] = {  
+  {1, "config",        config,             "(re)load config file?"},
+  {1, "sleep",         exec_sleep,         "sleep for N seconds"},
+  {1, "usleep",        exec_usleep,        "sleep for N microseconds"},
+  {1, "cd",            cd,                 "change directory"},
+  {1, "date",          date,               "get current date"},
+  {1, "basename",      basename_opihi,     "built-in basename function"},
+  {1, "dirname",       dirname_opihi,      "built-in dirname function"},
+  {1, "echo",          echo,               "type this line *"},
+  {1, "break",         exec_break,         "escape from function *"},
+  {1, "continue",      exec_next,          "next loop iteration"},
+  {1, "next",          exec_next,          "next loop iteration"},
+  {1, "last",          exec_last,          "last loop iteration"},
+  {1, "return",        exec_last,          "exit from macro"},
+  {1, "file",          file,               "test file existence"},
+  {1, "getchr",        getchr_func,        "find character in string"},
+  {1, "help",          help,               "get help on a function *"},
+  {1, "input",         input,              "read command lines from a file *"},
+  {1, "inthash",       inthash,            "generate a hash for a word treated as an integer"},
+  {1, "list",          list,               "get variable list"},
+  {1, "?",             list_help,          "list commands *"},
+  {1, "??",            list_vars,          "list variables *"},
+  {1, "#",             nop,                "a NOP function"},
+  {1, "local",         local,              "define local variables"},
+  {1, "macro",         macro,              "deal with the macros *"}, 
+  {1, "memory",        memory,             "long listing of the allocated memory"},
+  {1, "mkdir",         mkdir_opihi,        "built-in mkdir command"},
+  {1, "module",        module,             "load script file from the modules directories"},
+  {1, "nop",           nop,                "a NOP function"},
+  {1, "opihi",         opihi_setmode,      "get / set opihi behavior options"},
+  {1, "output",        output,             "redirect output to file"},
+  {1, "pwd",           pwd,                "print current working directory"},
+  {1, "exit",          quit,               "exit program *"}, 
+  {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"}, 
+  {1, "scan",          scan,               "scan line from keyboard or file to variable *"},
+  {1, "!",             shell,              "system call"},
+  {1, "exec",          shell,              "system call"},
+  {1, "sprintf",       sprintf_opihi,      "formatted print to a variable"},
+  {1, "fprintf",       fprintf_opihi,      "formatted print to standard output"},
+  {1, "strlen",        strlen_func,        "string length"},
+  {1, "substr",        substr_func,        "substring"},
+  {1, "strhash",       strhash,            "generate a hash for a string"},
+  {1, "strpop",        strpop,             "pop a string"},
+  {1, "strsub",        strsub,             "replace instances of a key in a string"},
+  {1, "wait",          wait_func,          "wait until return is typed"},
+  {1, "which",         which,              "show command *"}
+};
+
+void InitBasic () {
+  
+  int i;
+
+  InitCommands ();
+  InitMacros ();
+  InitBuffers ();
+  InitVectors ();
+  InitVariables ();
+  InitLists ();
+
+  for (i = 0; i < sizeof (cmds) / sizeof (Command); i++) {
+    AddCommand (&cmds[i]);
+  }
+  
+}
+
+void InitBasic_PantasksClient () {
+  
+  int i;
+
+  InitCommands ();
+  InitMacros ();
+  InitBuffers ();
+  InitVectors ();
+  InitVariables ();
+  InitLists ();
+
+  for (i = 0; i < sizeof (cmds) / sizeof (Command); i++) {
+    if (!strcmp (cmds[i].name, "quit")) goto valid;
+    if (!strcmp (cmds[i].name, "exit")) goto valid;
+    if (!strcmp (cmds[i].name, "exec")) goto valid;
+    if (!strcmp (cmds[i].name, "!")) goto valid;
+    continue;
+
+  valid:
+    AddCommand (&cmds[i]);
+  }
+}
+
+void FreeBasic () {
+
+  FreeCommands ();
+  FreeMacros ();
+  FreeBuffers ();
+  FreeVectors ();
+  FreeVariables ();
+  FreeLists ();
+}
Index: /branches/sc_branches/pantasks_condor/cmd.basic/input.c
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.basic/input.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.basic/input.c	(revision 34783)
@@ -0,0 +1,55 @@
+# include "basic.h"
+# define D_NLINES 100
+
+int input (int argc, char **argv) {
+  
+  int i, NLINES, status;
+  FILE *infile;
+  Macro inlist;
+
+  if (argc != 2) {
+    gprint (GP_ERR, "USAGE: input <filename>\n");
+    return (FALSE);
+  }
+
+  infile = fopen (argv[1], "r");
+  if (infile == NULL) {
+    gprint (GP_ERR, "no file %s\n", argv[1]); 
+    return (FALSE);
+  }
+
+  /* read file into the current list */
+  NLINES = D_NLINES;
+  ALLOCATE (inlist.line, char *, NLINES);
+  ALLOCATE (inlist.line[0], char, 4096);
+  for (i = 0; (scan_line (infile, inlist.line[i]) != EOF);) {
+    stripwhite (inlist.line[i]);
+    if (inlist.line[i][0] == 0) continue;
+    if (inlist.line[i][0] == '#') continue;
+    if (inlist.line[i][0] == '!') continue;
+
+    REALLOCATE (inlist.line[i], char, strlen(inlist.line[i]) + 1);
+    if (i == NLINES - 1) {
+      NLINES += D_NLINES;
+      REALLOCATE (inlist.line, char *, NLINES)
+    }
+    i++;
+    ALLOCATE (inlist.line[i], char, 4096);
+  }
+  inlist.Nlines = i;
+  fclose (infile);
+
+  if (!inlist.Nlines) gprint (GP_ERR, "WARNING: input file (%s) was empty\n", argv[1]);
+
+  /* process this list */
+  status = exec_loop (&inlist);
+
+  /* cleanup list */
+  for (i = 0; i < inlist.Nlines; i++) {
+    free (inlist.line[i]);
+  }
+  free (inlist.line[i]); /* note that we always alloc one extra line */
+  free (inlist.line);
+  return (status);
+
+}
Index: /branches/sc_branches/pantasks_condor/cmd.basic/inthash.c
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.basic/inthash.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.basic/inthash.c	(revision 34783)
@@ -0,0 +1,37 @@
+# include "basic.h"
+
+int inthash (int argc, char **argv) {
+
+  int N, modulus, value, input;
+  char *varName;
+
+  varName = NULL;
+  if ((N = get_argument (argc, argv, "-var"))) {
+    remove_argument (N, &argc, argv);
+    varName = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  if (argc != 3) {
+    gprint (GP_ERR, "USAGE: inthash (word) (modulus) [-var value]\n");
+    gprint (GP_ERR, "  treats the word as an integer when applying the modulus\n");
+    return (FALSE);
+  }
+
+  modulus = atoi(argv[2]);
+  if (modulus > 255) {
+    gprint (GP_ERR, "for the moment, (modulus) is limited to 255\n");
+    return (FALSE);
+  }
+
+  input = atoi(argv[1]);
+  value = input % modulus;
+  
+  if (varName) {
+    set_int_variable (varName, value);
+  } else {
+    gprint (GP_LOG, "%d\n", value);
+  }
+
+  return (TRUE);
+}
Index: /branches/sc_branches/pantasks_condor/cmd.basic/list.c
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.basic/list.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.basic/list.c	(revision 34783)
@@ -0,0 +1,292 @@
+# include "basic.h"
+# define D_NLINES 100
+static char prompt[] = ">> ";
+
+int list (int argc, char **argv) {
+
+  int ThisList, depth, i, done, found;
+  char *input, line[1024];
+  int N, Nbytes, NBYTES, Nread, status;
+  int RunCommand;
+  char *A, *B, *val, *Cmd;
+  FILE *f;
+
+  Cmd = NULL;
+  RunCommand = FALSE;
+  if ((N = get_argument (argc, argv, "-x"))) {
+    remove_argument (N, &argc, argv);
+    Cmd = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+    RunCommand = TRUE;
+  }
+
+  if ((N = get_argument (argc, argv, "-vectors"))) {
+    remove_argument (N, &argc, argv);
+    if (argc != 2) {
+      gprint (GP_ERR, "USAGE: list (root) -vectors\n");
+      return (FALSE);
+    }
+    ListVectorsToList (argv[1]);
+    return TRUE;
+  }
+
+  if ((N = get_argument (argc, argv, "-buffers"))) {
+    remove_argument (N, &argc, argv);
+    if (argc != 2) {
+      gprint (GP_ERR, "USAGE: list (root) -buffers\n");
+      return (FALSE);
+    }
+    ListBuffersToList (argv[1]);
+    return TRUE;
+  }
+
+  // return an error if -add is given with no other args
+  if ((argc > 2) && (!strcmp (argv[2], "-split"))) {
+    if (argc == 3) {
+      gprint (GP_ERR, "USAGE: list (root) -split (word) (word) ...\n");
+      return (FALSE);
+    }
+    
+    for (i = 0; i < argc - 3; i++) {
+      sprintf (line, "%s:%d", argv[1], i);
+      set_str_variable (line, argv[i+3]);
+    }
+    sprintf (line, "%s:n", argv[1]);
+    set_int_variable (line, i);
+
+    return (TRUE);
+  }
+
+  // return an error if -add is given with no other args
+  if ((argc > 2) && (!strcmp (argv[2], "-splitbychar"))) {
+    if (argc < 4) {
+      gprint (GP_ERR, "USAGE: list (root) -splitbychar (char) (word) [(word)...]\n");
+      return (FALSE);
+    }
+    
+    int j, nWords;
+    char splitter;
+    char *new, *word, *ptr;
+
+    nWords = 0;
+    splitter = argv[3][0];
+
+    for (i = 0; i < argc - 3; i++) {
+      new = strcreate (argv[i+3]);
+      for (j = 0; new[j]; j++) {
+	if (new[j] == splitter) new[j] = ' ';
+      }
+
+      ptr = new;
+      while (ptr) {
+	word = thisword (ptr);
+	if (!word) break;
+	
+	sprintf (line, "%s:%d", argv[1], nWords);
+	set_str_variable (line, word);
+	FREE (word);
+	ptr = nextword (ptr);
+	nWords ++;
+      }
+      FREE (new);
+    }
+    sprintf (line, "%s:n", argv[1]);
+    set_int_variable (line, nWords);
+
+    return (TRUE);
+  }
+
+  // return an error if -copy is given with no other args
+  if ((argc > 2) && (!strcmp (argv[2], "-copy"))) {
+    char *value;
+    if (argc == 3) {
+      gprint (GP_ERR, "USAGE: list (newlist) -copy (oldlist) ...\n");
+      return (FALSE);
+    }
+    
+    // old list must exist, or give an error
+    sprintf (line, "%s:n", argv[3]);
+    N = get_int_variable (line, &found);
+    if (!found) {
+      gprint (GP_ERR, "USAGE: list (newlist) -copy (oldlist) ...\n");
+      gprint (GP_ERR, "ERROR: missing input list\n");
+      return (FALSE);
+    }
+      
+
+    sprintf (line, "%s:n", argv[1]);
+    set_int_variable (line, N);
+    for (i = 0; i < N; i++) {
+      sprintf (line, "%s:%d", argv[3], i);
+      value = get_variable (line);
+      sprintf (line, "%s:%d", argv[1], i);
+      set_str_variable (line, value);
+    }
+    return (TRUE);
+  }
+
+  // return an error if -add is given with no other args
+  if ((argc > 2) && (!strcmp (argv[2], "-add"))) {
+    if (argc == 3) {
+      gprint (GP_ERR, "USAGE: list (root) -add (word) (word) ...\n");
+      return (FALSE);
+    }
+    
+    sprintf (line, "%s:n", argv[1]);
+    N = get_int_variable (line, &found);
+    for (i = 0; i < argc - 3; i++) {
+      sprintf (line, "%s:%d", argv[1], N + i);
+      set_str_variable (line, argv[i+3]);
+    }
+    sprintf (line, "%s:n", argv[1]);
+    set_int_variable (line, N + i);
+
+    return (TRUE);
+  }
+
+  // remove the single named entry from the list (finds entry with given name, reduces list length by one)
+  if ((argc > 2) && (!strcmp (argv[2], "-del"))) {
+    if (argc != 4) {
+      gprint (GP_ERR, "USAGE: list (root) -del (word)\n");
+      return (FALSE);
+    }
+    
+    int j;
+    char *value, *next_value;
+    char line2[1024];
+
+    sprintf (line, "%s:n", argv[1]);
+    N = get_int_variable (line, &found);
+    for (i = 0; i < N; i++) {
+      sprintf (line, "%s:%d", argv[1], i);
+      value = get_variable (line);
+      if (value == NULL) continue;
+      if (!strcmp (value, argv[3])) {
+	free (value);
+	for (j = i + 1; j < N; j++) {
+	  sprintf (line2, "%s:%d", argv[1], j);
+	  next_value = get_variable (line2);
+	  set_str_variable (line, next_value);
+	  strcpy (line, line2);
+	}
+	DeleteNamedScalar (line);
+	sprintf (line2, "%s:n", argv[1]);
+	set_int_variable (line2, N - 1);
+	return (TRUE);
+      }
+      free (value);
+    }      
+    gprint (GP_ERR, "value %s not found in list\n", argv[3]);
+    return (FALSE);
+  }
+
+  if (argc != 2) {
+    gprint (GP_ERR, "USAGE: list (root)                			       : supply list data, terminate with 'END'\n");
+    gprint (GP_ERR, "USAGE: list (root) -x (command)   			       : create list from shell output\n");
+    gprint (GP_ERR, "USAGE: list (root) -vectors   			       : create list from vector names\n");
+    gprint (GP_ERR, "USAGE: list (root) -buffers   			       : create list from buffer names\n");
+    gprint (GP_ERR, "USAGE: list (root) -split (words) 			       : create list from words\n");
+    gprint (GP_ERR, "USAGE: list (root) -splitbychar (char) (word) [(words)..] : create list from words\n");
+    gprint (GP_ERR, "USAGE: list (root) -add (words)   			       : extend a list\n");    
+    gprint (GP_ERR, "USAGE: list (root) -copy (list)   			       : copy a list to a new name\n");
+    gprint (GP_ERR, "USAGE: list (root) -del (word)   			       : delete the entry by value\n");
+    return (FALSE);
+  }
+
+  if (RunCommand) {
+    
+    /* val will hold the result */
+    NBYTES = 1024;
+    ALLOCATE (val, char, NBYTES);
+    
+    /* need to loop until command produces no more output, 
+       REALLOCATING as needed. */
+    f = popen (Cmd, "r");
+    done = FALSE;
+    Nbytes = 0;
+    while (!done) {
+      Nread = fread (&val[Nbytes], 1, 1023, f);
+      if (Nread < 0) { 
+	gprint (GP_ERR, "error reading from command\n");
+	done = TRUE;
+      }
+      if (Nread > 0) {
+	Nbytes += Nread;
+	NBYTES = 1024 + Nbytes;
+	REALLOCATE (val, char, NBYTES);
+      }
+      if (Nread == 0) {
+	done = TRUE;
+      }
+      
+    }
+    val[Nbytes] = 0;
+    status = pclose (f);
+    free (Cmd);
+    
+    if (status) {
+      gprint (GP_ERR, "warning: exit status of command %d\n", status);
+    }
+      
+    A = B = val;
+    for (i = 0; B != (char *) NULL;) {
+      while (isspace (*A) && (*A != 0)) A++;
+      B = strchr (A, '\n');
+      if (B != (char *) NULL) { *B = 0; }
+      if (*A != 0) {
+	sprintf (line, "%s:%d", argv[1], i);
+	set_str_variable (line, A);
+	A = B + 1;
+	i++;
+      }
+    }      
+    free (val);
+    
+    sprintf (line, "%s:n", argv[1]);
+    set_int_variable (line, i);
+    return (TRUE);
+  }
+
+  /* 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);
+    }
+
+    if ((ThisList == 0) && (input == NULL)) {
+      gprint (GP_ERR, "end list with 'END'\n");
+      continue;
+    }
+    if ((ThisList >  0) && (input == NULL)) {
+      gprint (GP_ERR, "missing 'END' in list\n");
+      input = strcreate ("end");
+    }
+
+    stripwhite (input);
+
+    /* test for end of nested list -- if not nested, END refers to this macro */
+    if (!strncasecmp (input, "END", 3)) {
+      depth --;
+      if (depth < 0) { /* we hit the last "END", loop is done */
+	sprintf (line, "%s:n", argv[1]);
+	set_int_variable (line, i);
+	free (input);
+	return (TRUE);
+      }
+    }
+
+    if (*input) { 
+      sprintf (line, "%s:%d", argv[1], i);
+      set_str_variable (line, input);
+      free (input);
+      i++;
+   }
+  }
+  return (TRUE);
+}
Index: /branches/sc_branches/pantasks_condor/cmd.basic/list_help.c
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.basic/list_help.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.basic/list_help.c	(revision 34783)
@@ -0,0 +1,26 @@
+# include "basic.h"
+
+int list_help (int argc, char **argv) {
+
+  FILE *f;
+  int fd;
+  char filename[128], line[256];
+
+  sprintf (filename, "/tmp/status.XXXXXX");
+  if ((fd = mkstemp (filename)) == -1) {
+    gprint (GP_ERR, "error opening output\n");
+    return (FALSE);
+  }
+  f = fdopen (fd, "w");
+  if (f == (FILE *) NULL) f = stdout;
+  print_commands (f);
+  if (f != stdout) {
+    fclose (f);
+    sprintf (line, "more %s", filename);
+    if (system (line) == -1) {
+      fprintf (stderr, "help list unavailable\n");
+    }
+  }
+  unlink (filename);
+  return (TRUE);
+}
Index: /branches/sc_branches/pantasks_condor/cmd.basic/list_vars.c
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.basic/list_vars.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.basic/list_vars.c	(revision 34783)
@@ -0,0 +1,8 @@
+# include "basic.h"
+
+int list_vars (int argc, char **argv) {
+
+  ListVariables ();
+  return (TRUE);
+}
+
Index: /branches/sc_branches/pantasks_condor/cmd.basic/local.c
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.basic/local.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.basic/local.c	(revision 34783)
@@ -0,0 +1,33 @@
+# include "basic.h"
+
+int local (int argc, char **argv) {
+
+  int i, N, STATIC;
+  char *p;
+
+  /* create a variable named MacroDepth.argv[1] */
+
+  STATIC = FALSE;
+  if ((N = get_argument (argc, argv, "-static"))) {
+    remove_argument (N, &argc, argv);
+    STATIC = TRUE;
+  }
+
+  if (argc < 2) {
+    gprint (GP_ERR, "USAGE: local (variable)\n");
+    return (FALSE);
+  }
+  
+  for (i = 1; i < argc; i++) {
+    if (STATIC) {
+      p = get_local_variable_ptr (argv[i]);
+      if (p == NULL) {
+	set_local_variable (argv[i], "NULL");
+      }
+    } else {
+      set_local_variable (argv[i], "NULL");
+    }      
+  }
+  return (TRUE);
+
+}
Index: /branches/sc_branches/pantasks_condor/cmd.basic/macro.c
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.basic/macro.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.basic/macro.c	(revision 34783)
@@ -0,0 +1,43 @@
+# include "basic.h"
+
+int macro (int argc, char **argv) {
+
+  int status;
+  CommandF *cmd;
+
+  if ((argc != 2) && (argc != 3) && (argc != 4)) {
+    gprint (GP_ERR, "USAGE: macro (cmd)\n");
+    gprint (GP_ERR, "  (cmd) can be one of:\n");
+    gprint (GP_ERR, "    (name)         -- create macro (name)\n");
+    gprint (GP_ERR, "    create (name)  -- create macro (name)\n");
+    gprint (GP_ERR, "    delete (name)  -- delete macro (name)\n");
+    gprint (GP_ERR, "    list   (name)  -- list macro (name)\n");
+    gprint (GP_ERR, "    edit   (name)  -- edit macro (name) <not working yet!> *\n");
+    gprint (GP_ERR, "    read   (name)  -- read macro(s) from file (name) <not working yet!> *\n");
+    gprint (GP_ERR, "    write  (name)  -- write macro (name) to a file <not working yet!> *\n");
+    return (FALSE);
+  }
+
+  cmd = find_macro_command (argv[1]);
+  if (cmd != NULL) {
+    status = (*cmd) (argc - 1, argv + 1);
+  } else {
+    /* sub-command was not found, pass argv[1..N] to macro_create */
+    status = macro_create (argc, argv);
+  }
+
+  return (status);
+}
+
+/* macro is called with the command "macro".  
+   the command line word "macro" is meant to be followed the one of several 
+   possible options:
+   
+   macro create
+   macro delete
+   macro list
+   macro edit
+   macro read
+   macro write
+
+*/
Index: /branches/sc_branches/pantasks_condor/cmd.basic/memory.c
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.basic/memory.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.basic/memory.c	(revision 34783)
@@ -0,0 +1,48 @@
+# include "basic.h"
+
+// XXX add a warning if ohana_memory is not compiled in 
+int memory (int argc, char **argv) {
+  
+  if (argc < 2) goto usage;
+
+  if (!strcasecmp (argv[1], "all")) {
+    ohana_memdump (1);
+    return (TRUE);
+  }
+  if (!strcasecmp (argv[1], "leaks")) {
+    ohana_memdump (0);
+    return (TRUE);
+  }
+  if (!strcasecmp (argv[1], "check")) {
+    ohana_memcheck (0);
+    return (TRUE);
+  }
+  if (!strcasecmp (argv[1], "checkfree")) {
+    ohana_memcheck (1);
+    return (TRUE);
+  }
+  if (!strncasecmp ("variables", argv[1], strlen(argv[1]))) {
+    ListVariables ();
+    return (TRUE);
+  }
+  if (!strncasecmp ("vectors", argv[1], strlen(argv[1]))) {
+    ListVectors ();
+    return (TRUE);
+  }
+  if (!strncasecmp ("buffers", argv[1], strlen(argv[1]))) {
+    PrintBuffers (0);
+    return (TRUE);
+  }
+  if (!strncasecmp ("macros", argv[1], strlen(argv[1]))) {
+    ListMacros();
+    return (TRUE);
+  }
+  if (!strncasecmp ("commands", argv[1], strlen(argv[1]))) {
+    print_commands (stderr);
+    return (TRUE);
+  }
+
+usage:
+  gprint (GP_ERR, "USAGE: memory (all/leaks)\n");
+  return (FALSE);
+}
Index: /branches/sc_branches/pantasks_condor/cmd.basic/mkdir.c
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.basic/mkdir.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.basic/mkdir.c	(revision 34783)
@@ -0,0 +1,34 @@
+# include "basic.h"
+
+int mkdir_opihi (int argc, char **argv) {
+
+  int mode, status;
+  struct stat fstats;
+
+  if (argc < 2) {
+    gprint (GP_ERR, "USAGE: mkdir (path)\n");
+    return (FALSE);
+  }
+
+  mode = S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH;
+
+  status = stat (argv[1], &fstats);
+  if (!status) {
+    // argv[1] exists, is it a directory?
+    if (!S_ISDIR(fstats.st_mode)) {
+      gprint (GP_ERR, "cannot create directory %s: is an existing file\n", argv[1]);
+      return (FALSE);
+    }
+    return (TRUE);
+  }
+
+  status = mkdirhier (argv[1], mode);
+  if (status == -1) {
+    gprint (GP_ERR, "cannot create directory %s\n", argv[1]);
+    return (FALSE);
+  }
+  return (TRUE);
+}
+
+// XXX need to add mode option
+// XXX need to respect umask (need umask command?)
Index: /branches/sc_branches/pantasks_condor/cmd.basic/module.c
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.basic/module.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.basic/module.c	(revision 34783)
@@ -0,0 +1,77 @@
+# include "basic.h"
+# define D_NLINES 100
+
+/* module loads an opihi script files from the installed module tree */
+int module (int argc, char **argv) {
+  
+  int i, NLINES, Nmodules, Nbytes, status;
+  Macro inlist;
+  char modname[16], *modpath, *filename;
+  FILE *infile = NULL;
+
+  if (argc != 2) {
+    gprint (GP_ERR, "USAGE: module <filename>\n");
+    return (FALSE);
+  }
+
+  Nmodules = get_int_variable ("MODULES:n", &status);
+  if (!status) {
+    gprint (GP_ERR, "MODULES list not found\n");
+    return (FALSE);
+  }
+
+  /* search for requested file in MODULES:0 - MODULES:n */
+  for (i = 0; i < Nmodules; i++) {
+    snprintf (modname, 16, "MODULES:%d", i);
+    modpath = get_variable (modname);
+    if (modpath == NULL) {
+      gprint (GP_ERR, "MODULES list element %d not found\n", i);
+      return (FALSE);
+    }
+
+    Nbytes = strlen(modpath) + strlen(argv[1]) + 2;
+    ALLOCATE (filename, char, Nbytes);
+    snprintf (filename, Nbytes, "%s/%s", modpath, argv[1]);
+    
+    infile = fopen (filename, "r");
+    free (filename);
+
+    if (infile != NULL) break;
+  }
+  if (infile == NULL) {
+    gprint (GP_ERR, "module %s not found\n", argv[1]); 
+    return (FALSE);
+  }
+    
+  /* read file into the current list */
+  NLINES = D_NLINES;
+  ALLOCATE (inlist.line, char *, NLINES);
+  ALLOCATE (inlist.line[0], char, 1024);
+  for (i = 0; (scan_line (infile, inlist.line[i]) != EOF);) {
+    stripwhite (inlist.line[i]);
+    if (inlist.line[i][0] == 0) continue;
+    if (inlist.line[i][0] == '#') continue;
+    if (inlist.line[i][0] == '!') continue;
+
+    REALLOCATE (inlist.line[i], char, strlen(inlist.line[i]) + 1);
+    if (i == NLINES - 1) {
+      NLINES += D_NLINES;
+      REALLOCATE (inlist.line, char *, NLINES)
+    }
+    i++;
+    ALLOCATE (inlist.line[i], char, 1024);
+  }
+  inlist.Nlines = i;
+  fclose (infile);
+
+  /* process this list */
+  status = exec_loop (&inlist);
+
+  /* cleanup list */
+  for (i = 0; i < inlist.Nlines; i++) {
+    free (inlist.line[i]);
+  }
+  free (inlist.line[i]); /* note that we always alloc one extra line */
+  free (inlist.line);
+  return (status);
+}
Index: /branches/sc_branches/pantasks_condor/cmd.basic/nop.c
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.basic/nop.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.basic/nop.c	(revision 34783)
@@ -0,0 +1,5 @@
+# include "basic.h"
+
+int nop (int argc, char **argv) {
+  return (TRUE);
+}
Index: /branches/sc_branches/pantasks_condor/cmd.basic/opihi.c
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.basic/opihi.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.basic/opihi.c	(revision 34783)
@@ -0,0 +1,49 @@
+# include "basic.h"
+
+int opihi_setmode (int argc, char **argv) {
+
+  OpihiVerboseMode value;
+
+  if (argc < 2) {
+    gprint (GP_ERR, "USAGE: opihi (verbose) [on/off/error]\n");
+    return (FALSE);
+  }
+
+  if (!strcasecmp(argv[1], "verbose")) {
+      if (argc == 2) {
+	  value = get_verbose_shell();
+	  switch (value) {
+	    case OPIHI_VERBOSE_OFF:
+	      gprint (GP_ERR, "opihi verbose mode: off\n");
+	      break;
+	    case OPIHI_VERBOSE_ON:
+	      gprint (GP_ERR, "opihi verbose mode: on\n");
+	      break;
+	    case OPIHI_VERBOSE_ERROR:
+	      gprint (GP_ERR, "opihi verbose mode: error\n");
+	      break;
+	    default:
+	      fprintf (stderr, "impossible condition\n");
+	      abort();
+	  } 
+	  return (TRUE);
+      }
+      if (!strcasecmp(argv[2], "on")) {
+	set_verbose_shell (OPIHI_VERBOSE_ON);
+	return (TRUE);
+      }
+      if (!strcasecmp(argv[2], "off")) {
+	set_verbose_shell (OPIHI_VERBOSE_OFF);
+	return (TRUE);
+      }
+      if (!strcasecmp(argv[2], "error")) {
+	set_verbose_shell (OPIHI_VERBOSE_ERROR);
+	return (TRUE);
+      }
+      gprint (GP_ERR, "unknown verbose mode %s\n", argv[2]);
+      return (FALSE);
+  }
+
+  gprint (GP_ERR, "unknown mode %s\n", argv[1]);
+  return (FALSE);
+}
Index: /branches/sc_branches/pantasks_condor/cmd.basic/output.c
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.basic/output.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.basic/output.c	(revision 34783)
@@ -0,0 +1,58 @@
+# include "basic.h"
+
+int output (int argc, char **argv) {
+  
+  int N, Noutput;
+  gpDest dest;
+  IOBuffer *buffer;
+  char *output, *current;
+
+  dest = GP_LOG;
+  if ((N = get_argument (argc, argv, "-err"))) {
+    dest = GP_ERR;
+    remove_argument (N, &argc, argv);
+  }
+
+  if ((N = get_argument (argc, argv, "-current"))) {
+    current = gprintGetName (dest);
+    remove_argument (N, &argc, argv);
+    set_str_variable (argv[N], current);
+    remove_argument (N, &argc, argv);
+    return (TRUE);
+  }
+
+  if ((N = get_argument (argc, argv, "-buffer"))) {
+    remove_argument (N, &argc, argv);
+    gprintSetBuffer (dest);
+    return (TRUE);
+  }
+    
+  /* set the output target and dump the current buffer there */
+  if ((N = get_argument (argc, argv, "-dump"))) {
+    buffer = gprintGetBuffer (dest);
+    if (buffer == NULL) return (FALSE);
+
+    /* save the current buffer contents */
+    Noutput = buffer[0].Nbuffer;
+    ALLOCATE (output, char, Noutput);
+    memcpy (output, buffer[0].buffer, Noutput);
+    
+    /* set the output target to the specified name */
+    remove_argument (N, &argc, argv);
+    gprintSetFileAllThreads (dest, argv[N]);
+    remove_argument (N, &argc, argv);
+
+    /* send the output to the appropriate destination */
+    gwrite (output, 1, Noutput, dest);
+    free (output);
+    return (TRUE);
+  }
+    
+  if (argc != 2) {
+    gprint (GP_ERR, "USAGE: output <filename> [-err] [-buffer] [-current var] [-dump filename]\n");
+    return (FALSE);
+  }
+
+  gprintSetFileAllThreads (dest, argv[1]);
+  return (TRUE);
+}
Index: /branches/sc_branches/pantasks_condor/cmd.basic/quit.c
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.basic/quit.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.basic/quit.c	(revision 34783)
@@ -0,0 +1,22 @@
+# include "basic.h"
+
+int quit (int argc, char **argv) {
+
+  int state;
+
+  cleanup ();
+
+  state = 0;
+  if (argc > 1) {
+    state = atof (argv[1]);
+  } 
+
+// the libedit version of readline does not support an incremental write to history file
+# ifndef RL_READLINE_VERSION
+  history = get_variable("HISTORY");
+  if (history != NULL) write_history (history);
+# endif
+
+  exit (state);
+
+}
Index: /branches/sc_branches/pantasks_condor/cmd.basic/run_for.c
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.basic/run_for.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.basic/run_for.c	(revision 34783)
@@ -0,0 +1,150 @@
+# include "basic.h"
+# define D_NLINES 100
+static char prompt[] = ">> ";
+
+int run_for (int argc, char **argv) {
+
+  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)) {
+    gprint (GP_ERR, "USAGE: for (var) (start) (stop) [delta] -- terminate with 'END'\n");
+    return (FALSE);
+  }
+
+  start = strtod (argv[2], &endptr);
+  if (*endptr) {
+    gprint (GP_ERR, "for loop starting value must be numerical (%s)\n", argv[2]);
+    return (FALSE);
+  }
+
+  end = strtod (argv[3], &endptr);
+  if (*endptr) {
+    gprint (GP_ERR, "for loop ending value must be numerical (%s)\n", argv[3]);
+    return (FALSE);
+  }
+
+  delta = 1.0;
+  if (argc == 5) { 
+    delta = strtod (argv[4], &endptr);
+    if (*endptr) {
+      gprint (GP_ERR, "for loop delta value must be numerical (%s)\n", argv[4]);
+      return (FALSE);
+    }
+  }
+  sign = SIGN(delta);
+
+  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));
+
+  if (Inclusive) end += 0.01*delta;
+
+  status = TRUE;
+  interrupt = FALSE;
+  for (value = start; (sign*value < sign*end) && !interrupt; value += delta) {
+    if ((int)value == value) 
+      set_int_variable (argv[1], (int) value);
+    else 
+      set_variable (argv[1], value);
+    status = exec_loop (&loop);
+    value = get_double_variable (argv[1], &found);
+    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: /branches/sc_branches/pantasks_condor/cmd.basic/run_foreach.c
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.basic/run_foreach.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.basic/run_foreach.c	(revision 34783)
@@ -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: /branches/sc_branches/pantasks_condor/cmd.basic/run_if.c
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.basic/run_if.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.basic/run_if.c	(revision 34783)
@@ -0,0 +1,129 @@
+# include "basic.h"
+# define prompt    "if: "
+
+int run_if (int argc, char **argv) {
+
+  int ThisList, depth, done, status, InlineCommand;
+  int i, length, logic;
+  char *input, *val, *line;
+  int nloop, size;
+
+  InlineCommand = FALSE;
+
+  if (argc < 2) {
+    gprint (GP_ERR, "USAGE: if (conditional) : follow with commands, end with the word 'END'\n");
+    gprint (GP_ERR, "   OR: if (conditional) command\n");
+    return (FALSE);
+  }
+  if (argc > 2) {
+    InlineCommand = TRUE;
+  }
+
+  /* determine value of conditional expression */
+  val = dvomath (1, &argv[1], &size, 0);
+  if (val == NULL) {
+    gprint (GP_ERR, "syntax error in logic: %s\n", argv[1]);
+    return (FALSE);
+  }
+  logic = atof (val); /* is round-off error a danger? */ 
+  free (val);
+
+  if (InlineCommand) {
+    if (logic) {
+      /* re-build a command line from the remaining strings */
+      length = 0;
+      for (i = 2; i < argc; i++) {
+	length += strlen(argv[i]) + 1;
+      }
+      length++;
+      ALLOCATE (line, char, length);
+      memset (line, 0, length);
+      for (i = 2; i < argc; i++) {
+	if (i == 2) {
+	  strcpy (line, argv[i]);
+	} else {
+	  strcat (line, " ");
+	  strcat (line, argv[i]);
+	}
+      }
+      status = multicommand (line);
+      free (line);
+      return (status);
+    } else {
+      return (TRUE);
+    }
+  }    
+
+  /* read in if-list */
+  nloop = 0;
+  depth = 0;
+  ThisList = current_list_depth();
+
+  done = FALSE;
+  while (!done) {
+
+    nloop ++;
+    /* get the next line (from correct place) */
+    if (ThisList == 0) {
+      input = opihi_readline (prompt);
+    } else {
+      input = get_next_listentry (ThisList);
+    }
+
+    if ((ThisList == 0) && (input == NULL)) {
+      gprint (GP_ERR, "end if-block with 'END'\n");
+      continue;
+    }
+    if ((ThisList >  0) && (input == NULL)) {
+      gprint (GP_ERR, "missing 'END' in if-block\n");
+      input = strcreate ("end");
+    }
+
+    stripwhite (input);
+
+    /* test for new macro, search for "end" statement */
+    if (!logic && is_list (input)) {
+      depth ++;
+      free (input);
+      continue;
+    }
+    
+    /* check for an "else", invert logic */
+    if ((depth == 0) && !strncasecmp (input, "ELSE", 4)) {
+      logic ^= TRUE;
+      free (input);
+      continue;
+    }
+
+    /* test for end of nested block -- if not nested, END refers to this if */
+    if (!strncasecmp (input, "END", 3)) {
+      depth --;
+      if (depth < 0) { 
+	/* we hit the last "END", if-block is done */
+	free (input);
+	return (TRUE);	
+      }
+      free (input);  /* a do-nothing line */
+      continue;
+    }
+
+    if (logic) {
+      if (*input) { 
+	status = multicommand (input);
+	if (ThisList == 0) add_history (input);
+	if (auto_break && !status) {
+	  free (input);
+	  return (FALSE);
+	}
+      }
+    } 
+    free (input);
+  }
+  return (TRUE);
+}
+
+/*
+     If we are entering at the keyboard (ThisList == 0), use readline.
+     Otherwise, read from the current list, remove list lines.
+     End when we hit the final "END" (the true END -- we count macro defines!!) 
+     */
Index: /branches/sc_branches/pantasks_condor/cmd.basic/run_while.c
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.basic/run_while.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.basic/run_while.c	(revision 34783)
@@ -0,0 +1,117 @@
+# include "basic.h"
+# define D_NLINES 100
+static char prompt[] = ">> ";
+
+int run_while (int argc, char **argv) {
+
+  int ThisList, depth, i, done, status, NLINES, j;
+  char *input, *val, *logic_line;
+  int logic, size;
+  Macro loop;
+
+  if (argc != 2) {
+    gprint (GP_ERR, "USAGE: while (condition)\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 == (char *) NULL) {
+      if (ThisList == 0)  {
+	gprint (GP_ERR, "end 'while' loop with 'END'\n");
+	continue;
+      } else {
+	gprint (GP_ERR, "misbalanced 'while' 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) 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 */
+  free (input);
+  loop.Nlines = i;
+  REALLOCATE (loop.line, char *, MAX (loop.Nlines, 1));
+
+  // test the logic once before running the loop
+  logic_line = strcreate (argv[1]);
+  logic_line = expand_vars (logic_line);
+  val = dvomath (1, &logic_line, &size, 0);
+  free (logic_line);
+
+  // if we have a parse failure, return FALSE
+  if (val == NULL) return (FALSE);
+  logic = atof (val); /* warning: round-off error is a danger */
+  free (val);
+
+  /* execute for loop */
+  status = FALSE;
+  while (logic) { 
+    status = exec_loop (&loop);
+    if (loop_next) continue;
+    if (loop_last) break;
+    if (loop_break) break;
+
+    logic_line = strcreate (argv[1]);
+    logic_line = expand_vars (logic_line);
+    val = dvomath (1, &logic_line, &size, 0);
+    free (logic_line);
+
+    logic = FALSE;
+    if (val) {
+      logic = atof (val); /* warning: round-off error is a danger */
+      free (val);
+    }
+  }
+  /* '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);
+}
Index: /branches/sc_branches/pantasks_condor/cmd.basic/scan.c
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.basic/scan.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.basic/scan.c	(revision 34783)
@@ -0,0 +1,47 @@
+# include "basic.h"
+
+int scan (int argc, char **argv) {
+
+  int i, N, status;
+  char *line;
+  FILE *f;
+
+  if ((argc != 3) && (argc != 4)) {
+    gprint (GP_ERR, "USAGE: scan <filename> <var> [N]\n");
+    return (FALSE);
+  }
+
+  f = stdin;
+  if (strcmp (argv[1], "stdin")) {
+    f = fopen (argv[1], "r");
+    if (f == (FILE *) NULL) {
+      gprint (GP_ERR, "file %s not found\n", argv[1]);
+      return (FALSE);
+    }
+  }
+  
+  ALLOCATE (line, char, 1024);
+  N = 1;
+  if (argc == 4) {
+    N = atof(argv[3]);
+    if (N < 1) {
+      gprint (GP_ERR, "scan: line numbers must start at 1\n");
+      return (FALSE);
+    }
+  }
+
+  for (i = 0; (i < N) && ((status = scan_line (f, line)) != EOF); i++);
+  if (i < N) {
+    set_str_variable (argv[2], "EOF");
+  } else {
+    set_str_variable (argv[2], line);
+  }
+  free (line);
+
+  if (f != stdin) {
+    fclose (f);
+  }
+  return (TRUE);
+ 
+
+}
Index: /branches/sc_branches/pantasks_condor/cmd.basic/shell.c
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.basic/shell.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.basic/shell.c	(revision 34783)
@@ -0,0 +1,118 @@
+# include "basic.h"
+# define DTIME(A,B) ((A.tv_sec - B.tv_sec) + 1e-6*(A.tv_usec - B.tv_usec))
+
+static char *defshell = "/bin/sh";
+static char *cmdflag = "-c";
+
+// XXX add the ability to exec without a separate shell
+// XXX add an option to modify the timeout
+
+int shell (int argc, char **argv) {
+  
+  int i, pid, N;
+  int exit_status;
+  int wait_status;
+  int result;
+  char **args, *shell;
+  struct timeval start, now;
+  float timeout;
+
+  timeout = 0;
+  if ((N = get_argument (argc, argv, "-timeout"))) {
+    remove_argument (N, &argc, argv);
+    timeout = atof(argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  shell = getenv ("SHELL");
+  if (shell == NULL) shell = defshell;
+
+  // we are creating a command of the form /bin/sh -c argv[1] argv[2] etc, where
+  // the argv[1], etc elements are concatenated into a single string
+  ALLOCATE (args, char *, 4);
+  args[0] = shell;
+  args[1] = cmdflag;
+  args[2] = paste_args (argc - 1, &argv[1]);
+  args[3] = NULL;
+
+  // send the commands to the shell specified in the env variable SHELL, or else /bin/sh
+
+  gettimeofday (&start, NULL);
+
+  // use execvp to enable a timeout on the system call 
+  pid = fork ();
+  if (!pid) { /* must be child process */
+    execvp (shell, args);
+    exit (1);
+  }
+  free (args[2]);
+  free (args);
+  
+  // wait for process to finish or timeout
+  // loop forever if desired, but catch C-C and stop the process on interrupt
+  interrupt = FALSE;
+  while (!interrupt) {
+    result = waitpid (pid, &wait_status, WNOHANG);
+    switch (result) {
+      case -1:   // error on waitpid
+	switch (errno) {
+	  case ECHILD:
+	    gprint (GP_ERR, "unknown PID, not a child process: %d\n", pid);
+	    return (FALSE);
+	  default:
+	    gprint (GP_ERR, "unexpected response to waitpid: %d\n", result);
+	    abort();
+	}
+	break;
+
+      case 0:   // child not yet exited
+	usleep (10000);
+	if (timeout > 0.0) {
+	  gettimeofday (&now, NULL);
+	  if (DTIME(now, start) > timeout) {
+	    gprint (GP_ERR, "timeout on %s (pid %d)\n", argv[1], pid);
+	    return (FALSE);
+	  }
+	}
+	continue;
+
+      default:
+	if (result != pid) {
+	  gprint (GP_ERR, "waitpid error: mis-matched PID (%d vs %d).  programming error\n", result, pid);
+	  abort();
+	}
+	if (WIFEXITED(wait_status)) {
+	  exit_status = WEXITSTATUS(wait_status);
+	  if (exit_status) {
+	    return FALSE;
+	  } else {
+	    return TRUE;
+	  }
+	}
+	if (WIFSIGNALED(wait_status)) {
+	  gprint (GP_ERR, "job %d exited on signal %d\n", pid, WTERMSIG(wait_status));
+	  return (FALSE);
+	}
+	if (WIFSTOPPED(wait_status)) {
+	  gprint (GP_ERR, "waitpid returns 'stopped' programming error\n");
+	  abort();
+	}
+    }
+  }
+  gprint (GP_ERR, "caught interrupt, killing %s (%d)\n", argv[1], pid);
+
+  // user hit interrupt: kill the process and return
+  kill (pid, SIGKILL);
+  result = 0;
+  for (i = 0; (i < 10) && (result == 0); i++) {
+    usleep (10000);  // wait for job to exit
+    result = waitpid (pid, &wait_status, WNOHANG);
+  }
+  if (!result) {
+    gprint (GP_ERR, "trouble killing %s (pid %d)\n", argv[1], pid);
+  } else {
+    gprint (GP_ERR, "killed %s (pid %d)\n", argv[1], pid);
+  }
+
+  return (FALSE);
+}
Index: /branches/sc_branches/pantasks_condor/cmd.basic/sleep.c
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.basic/sleep.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.basic/sleep.c	(revision 34783)
@@ -0,0 +1,15 @@
+# include "basic.h"
+
+int exec_sleep (int argc, char **argv) {
+
+  int i;
+
+  if (argc < 2) {
+    gprint (GP_ERR, "usage: sleep N\n");
+    return (FALSE);
+  }
+
+  i = atof (argv[1]);
+  sleep (i);
+  return (TRUE);
+}
Index: /branches/sc_branches/pantasks_condor/cmd.basic/sprintf.c
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.basic/sprintf.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.basic/sprintf.c	(revision 34783)
@@ -0,0 +1,83 @@
+# include "basic.h"
+# define NCHAR 1024
+
+// XXX this function uses fixed string lengths....
+/* convert line, tmp, fmt to dynamic strings? */
+
+int sprintf_opihi (int argc, char **argv) {
+
+  int i;
+  char line[NCHAR], tmp[NCHAR], fmt[NCHAR];
+  char *p1, *p2, *q;
+
+  if (argc < 3) {
+    gprint (GP_ERR, "USAGE: sprintf var format value value ...\n");
+    return (FALSE);
+  }
+
+  q  = line;
+  bzero (line, NCHAR);
+
+  p1 = argv[2];
+  for (i = 3; i < argc; i++) {
+    bzero (tmp, NCHAR);
+    bzero (fmt, NCHAR);
+
+    /* find next format character */
+    p2 = strchr (p1, '%');
+    if (p2 == (char *) NULL) {
+      gprint (GP_ERR, "mismatch between format and values\n");
+      return (FALSE);
+    }
+    if (strlen(q) + p2 - p1 > NCHAR) {
+      gprint (GP_ERR, "line too long");
+      return (FALSE);
+    }
+    memcpy (q, p1, p2-p1);
+    q = line + strlen(line);
+    
+    /* identify type (%NNNs %NNNNd %NNNNf) */
+    for (p1 = p2 + 1; (*p1 == '.') || (*p1 == '-') || (*p1 == '+') || (*p1 == ' ') || isdigit(*p1); p1++);
+    memcpy (fmt, p2, p1 - p2 + 1);
+    switch (*p1) {
+      case 'e':
+      case 'f':
+	sprintf (tmp, fmt, atof(argv[i]));
+	break;
+      case 's':
+	sprintf (tmp, fmt, argv[i]);
+	break;
+      case 'd':
+      case 'c':
+      case 'x':
+	sprintf (tmp, fmt, atoi(argv[i]));
+	break;
+      default:
+	gprint (GP_ERR, "syntax error in format (only e,f,s,d,c,x allowed)\n");
+	return (FALSE);
+    }
+    if (strlen(q) + strlen(tmp) > NCHAR) {
+      gprint (GP_ERR, "line too long");
+      return (FALSE);
+    }
+    memcpy (q, tmp, strlen(tmp));
+    q = line + strlen(line);
+    p1++;
+  }
+  p2 = strchr (p1, '%');
+  if (p2 != (char *) NULL) {
+    gprint (GP_ERR, "mismatch between format and values\n");
+    return (FALSE);
+  }
+  
+  p2 = p1 + strlen (p1);
+  if (strlen(q) + p2 - p1 > NCHAR) {
+    gprint (GP_ERR, "line too long");
+    return (FALSE);
+  }
+  memcpy (q, p1, p2-p1);
+  set_str_variable (argv[1], line);
+
+  return (TRUE);
+}
+
Index: /branches/sc_branches/pantasks_condor/cmd.basic/strchr.c
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.basic/strchr.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.basic/strchr.c	(revision 34783)
@@ -0,0 +1,29 @@
+# include "basic.h"
+
+int getchr_func (int argc, char **argv) {
+
+  /* returns position of the first given character */ 
+  char *c;
+  int pos;
+
+  if ((argc != 3) && (argc != 4)) {
+    gprint (GP_ERR, "USAGE: strchr (string) (char) [var]\n");
+    return (FALSE);
+  }
+
+  c = strchr (argv[1], argv[2][0]);
+
+  if (c == (char *) NULL) {
+    pos = -1;
+  } else {
+    pos = c - argv[1];
+  }
+
+  if (argc == 5) {
+    set_variable (argv[3], pos);
+  } else {
+    gprint (GP_ERR, "%d\n", pos);
+  }
+  return (TRUE);
+
+}
Index: /branches/sc_branches/pantasks_condor/cmd.basic/strhash.c
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.basic/strhash.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.basic/strhash.c	(revision 34783)
@@ -0,0 +1,42 @@
+# include "basic.h"
+
+int strhash (int argc, char **argv) {
+
+  int i, N, Nchar, Nsum, modulus, value;
+  char *varName;
+
+  varName = NULL;
+  if ((N = get_argument (argc, argv, "-var"))) {
+    remove_argument (N, &argc, argv);
+    varName = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  if (argc != 3) {
+    gprint (GP_ERR, "USAGE: strhash (string) (modulus) [-var value]\n");
+    return (FALSE);
+  }
+
+  modulus = atoi(argv[2]);
+  if (modulus > 255) {
+    gprint (GP_ERR, "for the moment, (modulus) is limited to 255\n");
+    return (FALSE);
+  }
+
+  Nchar = strlen (argv[1]);
+
+  Nsum = 0;
+  for (i = 0; i < Nchar; i++) {
+    Nsum += (argv[1][i] % modulus);
+  }
+  
+  value = Nsum % modulus;
+  
+  if (varName) {
+    set_int_variable (varName, value);
+  } else {
+    gprint (GP_LOG, "%d\n", value);
+  }
+
+  return (TRUE);
+}
Index: /branches/sc_branches/pantasks_condor/cmd.basic/strlen.c
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.basic/strlen.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.basic/strlen.c	(revision 34783)
@@ -0,0 +1,22 @@
+# include "basic.h"
+
+int strlen_func (int argc, char **argv) {
+
+  /* returns length of the given string */ 
+  int len;
+
+  if ((argc != 2) && (argc != 3)) {
+    gprint (GP_ERR, "USAGE: strlen (string) [var]\n");
+    return (FALSE);
+  }
+
+  len = strlen (argv[1]);
+
+  if (argc == 3) {
+    set_int_variable (argv[2], len);
+  } else {
+    gprint (GP_ERR, "%d\n", len);
+  }
+  return (TRUE);
+
+}
Index: /branches/sc_branches/pantasks_condor/cmd.basic/strpop.c
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.basic/strpop.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.basic/strpop.c	(revision 34783)
@@ -0,0 +1,35 @@
+# include "basic.h"
+
+int strpop (int argc, char **argv) {
+
+  char *p, *q, *string;
+
+  if ((argc != 2) && (argc != 3)) {
+    gprint (GP_ERR, "USAGE: strpop (var) [out]\n");
+    return (FALSE);
+  }
+
+  /* string is a copy of the value on the variable stack */
+  string = get_variable (argv[1]);
+  if (string == NULL) return (FALSE);
+  
+  /* thisword is an allocated string */
+  p = thisword (string);
+
+  q = nextword (string);
+  if (q == NULL) {
+    set_str_variable (argv[1], "NULL");
+  } else {
+    set_str_variable (argv[1], q);
+  }
+  
+  if (argc == 3) {
+    set_str_variable (argv[2], p);
+  } else {
+    gprint (GP_LOG, "%s\n", p);
+  }
+
+  free (p);
+  free (string);
+  return (TRUE);
+}
Index: /branches/sc_branches/pantasks_condor/cmd.basic/strsub.c
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.basic/strsub.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.basic/strsub.c	(revision 34783)
@@ -0,0 +1,59 @@
+# include "basic.h"
+
+int strsub (int argc, char **argv) {
+
+  int N, Nkey, Noutput;
+  char *varName;
+  char *p, *q, *input, *output, *key, *value;
+
+  // clear all sections
+  varName = NULL;
+  if ((N = get_argument (argc, argv, "-var"))) {
+    remove_argument (N, &argc, argv);
+    varName = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  if (argc != 4) {
+    gprint (GP_ERR, "USAGE: strsub (string) (key) (value) [-var out]\n");
+    gprint (GP_ERR, "  replace instances of 'key' with the 'value'\n");
+    return (FALSE);
+  }
+
+  // input string 
+  input = argv[1];
+  
+  // find this in the string
+  key = argv[2];
+  Nkey = strlen(key);
+
+  // replace with this
+  value = argv[3];
+
+  Noutput = MAX (16, strlen(input));
+  ALLOCATE (output, char, Noutput);
+  memset (output, 0, Noutput);
+
+  // 0123456789
+  // wordKEYnew
+  // p = 0, q = 4, q-p = 4 -> p = 7
+
+  p = input;
+  while (*p && ((q = strstr (p, key)) != NULL)) {
+    output = opihi_append (output, &Noutput, p, q);
+    output = opihi_append (output, &Noutput, value, value + strlen(value));
+    p = q + Nkey;
+  }
+  if (*p) {
+    output = opihi_append (output, &Noutput, p, p + strlen(p));
+  }
+  
+  if (varName) {
+    set_str_variable (varName, output);
+  } else {
+    gprint (GP_LOG, "%s\n", output);
+  }
+
+  free (output);
+  return (TRUE);
+}
Index: /branches/sc_branches/pantasks_condor/cmd.basic/substr.c
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.basic/substr.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.basic/substr.c	(revision 34783)
@@ -0,0 +1,48 @@
+# include "basic.h"
+
+int substr_func (int argc, char **argv) {
+
+  int N1, N2, len;
+  char *c, *string;
+
+  if ((argc != 4) && (argc != 5)) {
+    gprint (GP_ERR, "USAGE: substr (string) N1 N2 [var]\n");
+    return (FALSE);
+  }
+
+  N1 = atof (argv[2]);
+  N2 = atof (argv[3]);
+
+  // add a range check here
+  if ((N1 < 0) || (N1 >=  strlen(argv[1]))) {
+      gprint (GP_ERR, "ERROR: start value out of range in substr command\n");
+      return (FALSE);
+  }
+  if ((N2 < 0) || ((N2+N1) >  strlen(argv[1]))) {
+      gprint (GP_ERR, "ERROR: end value out of range in substr command\n");
+      return (FALSE);
+  }
+
+  len = strlen (argv[1]);
+  if ((N1 >= len) || (N1 + N2 > len)) {
+    c = (char *) NULL;
+  } else {
+    c = strncreate (&argv[1][N1], N2);
+  }
+
+  if (c == (char *) NULL) {
+    string = strcreate ("");
+  } else {
+    string = strcreate (c);
+  }
+
+  if (argc == 5) {
+    set_str_variable (argv[4], string);
+  } else {
+    gprint (GP_ERR, "%s\n", string);
+  }
+  free (c);
+  free (string);
+  return (TRUE);
+
+}
Index: /branches/sc_branches/pantasks_condor/cmd.basic/test/break.sh
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.basic/test/break.sh	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.basic/test/break.sh	(revision 34783)
@@ -0,0 +1,19 @@
+
+list tests
+ test1
+end
+
+# test that break will halt operation
+macro test1
+ $PASS = 1
+ break -auto off
+ for i 0 10
+   if ($i == 5)
+     break
+   end
+ end
+ if ($i != 5)
+   $PASS = 0
+ end
+end
+
Index: /branches/sc_branches/pantasks_condor/cmd.basic/test/cd.sh
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.basic/test/cd.sh	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.basic/test/cd.sh	(revision 34783)
@@ -0,0 +1,34 @@
+
+list tests
+ test1
+ test2
+end
+
+# test that cd will go into a new directory
+macro test1
+ $PASS = 1
+ exec mkdir test.dir
+ output /dev/null
+ cd test.dir
+ exec touch foo.test
+ cd ..
+ output stdout
+ file test.dir/foo.test exists
+ if ($exists != 1)
+   $PASS = 0
+ end
+ exec rm -f test.dir/foo.test
+ exec rmdir test.dir
+end
+
+# test that pwd output is correct
+macro test2
+ $PASS = 1
+ exec touch foo.test
+ pwd -var testdir
+ file $testdir\/foo.test exists
+ if ($exists != 1)
+  $PASS = 0
+ end
+ exec rm foo.test
+end
Index: /branches/sc_branches/pantasks_condor/cmd.basic/test/config.sh
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.basic/test/config.sh	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.basic/test/config.sh	(revision 34783)
@@ -0,0 +1,14 @@
+
+list tests
+ test1
+end
+
+# test that config does not return an error
+macro test1
+ $PASS = 1
+ config
+ if ($STATUS == 0)
+   $PASS = 0
+ end
+end
+
Index: /branches/sc_branches/pantasks_condor/cmd.basic/test/continue.sh
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.basic/test/continue.sh	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.basic/test/continue.sh	(revision 34783)
@@ -0,0 +1,18 @@
+
+list tests
+ test1
+end
+
+# test that continue skips within a loop
+macro test1
+ $PASS = 1
+ for i 0 10
+   if ($i > 5)
+      continue
+   end
+   $j = $i
+ end
+ if ($j != 5)
+   $PASS = 0
+ end
+end
Index: /branches/sc_branches/pantasks_condor/cmd.basic/test/date.sh
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.basic/test/date.sh	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.basic/test/date.sh	(revision 34783)
@@ -0,0 +1,33 @@
+
+list tests
+ test1
+ test2
+end
+
+# test that date does not return an error
+macro test1
+ $PASS = 1
+ date -var var
+ if ($STATUS == 0)
+   $PASS = 0
+ end
+end
+
+# test that date constructs a reasonable sample date
+macro test2
+ $PASS = 1
+ date -var date1
+ $date2 = `date`
+ list w1 -split $date1
+ list w2 -split $date2
+ # check the first 3 entries (day, month, date)
+ if ($w1:0 != $w2:0)
+   $PASS = 0
+ end
+ if ($w1:1 != $w2:1)
+   $PASS = 0
+ end
+ if ($w1:2 != $w2:2)
+   $PASS = 0
+ end
+end
Index: /branches/sc_branches/pantasks_condor/cmd.basic/test/echo.sh
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.basic/test/echo.sh	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.basic/test/echo.sh	(revision 34783)
@@ -0,0 +1,19 @@
+
+list tests
+ test1
+end
+
+# test that echo actually echoes
+macro test1
+ $PASS = 1
+ exec rm -f test.dat
+ output test.dat
+ echo foobar
+ output stdout
+ $line = `cat test.dat`
+ # exec rm -f test.dat
+ if ($line != foobar)
+   $PASS = 0
+ end
+end
+
Index: /branches/sc_branches/pantasks_condor/cmd.basic/test/file.sh
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.basic/test/file.sh	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.basic/test/file.sh	(revision 34783)
@@ -0,0 +1,19 @@
+
+list tests
+ test1
+end
+
+# test that the file test function works at all
+macro test1
+ $PASS = 1
+ exec touch foo.test
+ file foo.test exists
+ if ($exists != 1)
+   $PASS = 0
+ end
+ exec rm -f foo.test
+ file foo.test exists
+ if ($exists != 0)
+   $PASS = 0
+ end
+end
Index: /branches/sc_branches/pantasks_condor/cmd.basic/test/for.sh
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.basic/test/for.sh	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.basic/test/for.sh	(revision 34783)
@@ -0,0 +1,216 @@
+
+list tests
+ test1
+ test2
+ test3
+ test4
+ test5
+ memtest1
+ memtest2
+ memtest3
+end
+
+# do we loop up correctly?
+macro test1
+
+  $PASS = 0
+
+  local i
+
+  for i 0 100
+  end    
+  
+ if ($i == 99)
+   $PASS = 1
+ else
+   $PASS = 0
+ end
+
+end
+
+# do we loop down correctly?
+macro test2
+
+  $PASS = 0
+
+  local i
+
+  for i 100 0 -1
+  end    
+  
+ if ($i == 1)
+   $PASS = 1
+ else
+   $PASS = 0
+   echo "i : $i"
+ end
+
+end
+
+# do we loop up in small steps correctly?
+macro test3
+
+  $PASS = 0
+
+  local i N
+
+  $N = 0
+  for i 0 100 0.1
+   $N = $N + 1
+  end    
+  
+ if (($i == 99.9) && ($N == 1000))
+   $PASS = 1
+ else
+   $PASS = 0
+   echo "i: $i"
+   echo "N: $N"
+ end
+
+end
+
+# do we loop down in small steps correctly?
+macro test4
+
+  $PASS = 0
+
+  local i N
+
+  $N = 0
+  for i 100 0 -0.1
+   $N = $N + 1
+  end    
+  
+ if (($i == 0.1) && ($N == 1000))
+   $PASS = 1
+ else
+   $PASS = 0
+   echo "i: $i"
+   echo "N: $N"
+ end
+
+end
+
+# do we break from a loop correctly
+macro test5
+
+  $PASS = 0
+
+  break -auto off
+
+  local i N
+
+  $N = 0
+  for i 0 100
+   $N = $N + 1
+   if ($i == 30)
+     break
+   end
+  end    
+
+  $PASS = 1
+  
+  if (($i != 30) || ($N != 31))
+    $PASS = 0
+    echo "i: $i"
+    echo "N: $N"
+  end
+end
+
+# check memleaks
+macro memtest1
+
+ $PASS = 0
+
+ local i N
+
+
+ list word -x "ps -p $PID -o rss"
+ $startmem = $word:1
+
+ for i 0 10000
+ end    
+  
+ list word -x "ps -p $PID -o rss"
+ $endmem = $word:1
+
+ if ({$endmem - $startmem} < 10)
+   $PASS = 1
+ else
+   $PASS = 0
+   echo "growth: {$endmem-$startmem}"
+   echo "kB/loop: {($endmem-$startmem)/10000}"
+ end
+end
+
+# check memleaks with many loop lines
+macro memtest2
+
+ $PASS = 0
+
+ local i N
+
+ list word -x "ps -p $PID -o rss"
+ $startmem = $word:1
+
+ output /dev/null
+ for i 0 10000
+  echo "test line in loop"
+  echo "test line in loop"
+  echo "test line in loop"
+  echo "test line in loop"
+  echo "test line in loop"
+ end    
+ output stdout
+  
+ list word -x "ps -p $PID -o rss"
+ $endmem = $word:1
+
+ $PASS = 1
+
+ if ($endmem - $startmem > 80)
+   $PASS = 0
+   echo "growth: {$endmem-$startmem}"
+   echo "kB/loop: {($endmem-$startmem)/10000}"
+ end
+end
+
+# check memleaks on break
+macro memtest3
+
+ $PASS = 0
+
+ local i N
+
+ break -auto off
+
+ list word -x "ps -p $PID -o rss"
+ $startmem = $word:1
+
+ for i 0 10000
+  for j 0 5
+    break
+    echo "test line in loop"
+    echo "test line in loop"
+    echo "test line in loop"
+    echo "test line in loop"
+    echo "test line in loop"
+  end
+ end    
+  
+ list word -x "ps -p $PID -o rss"
+ $endmem = $word:1
+
+ $PASS = 1
+
+ if ($i != 9999)
+   $PASS = 0
+   echo "break jumped outer loop: i = $i" 
+ end
+
+ if ($endmem - $startmem > 10)
+   $PASS = 0
+   echo "growth: {$endmem-$startmem}"
+   echo "kB/loop: {($endmem-$startmem)/10000}"
+ end
+end
Index: /branches/sc_branches/pantasks_condor/cmd.basic/test/fprintf.sh
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.basic/test/fprintf.sh	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.basic/test/fprintf.sh	(revision 34783)
@@ -0,0 +1,84 @@
+
+list tests
+ test1
+ test2
+ test3
+ test4
+ test5
+end
+
+macro test1
+ exec rm -f test.dat
+ output test.dat
+ fprintf "test %03d" 50
+ output stdout
+ $line = `cat test.dat`
+ if ("$line" == "test 050")
+   $PASS = 1
+ else
+   $PASS = 0
+ end
+end
+
+macro test2
+ exec rm -f test.dat
+ output test.dat
+ fprintf "test %6.3f" 123.45678
+ output stdout
+ $line = `cat test.dat`
+ if ("$line" == "test 123.457")
+   $PASS = 1
+ else
+   $PASS = 0
+ end
+end
+
+macro test3
+ exec rm -f test.dat
+ output test.dat
+ fprintf "test %x" 32
+ output stdout
+ $line = `cat test.dat`
+ if ("$line" == "test 20")
+   $PASS = 1
+ else
+   $PASS = 0
+ end
+end
+
+macro test4
+ exec rm -f test.dat
+ output test.dat
+ fprintf "test %10s" foobar
+ output stdout
+ $line = `cat test.dat`
+ if ("$line" == "test     foobar")
+   $PASS = 1
+ else
+   $PASS = 0
+ end
+end
+
+# check for memory leaks
+macro test5
+
+ list word -x "ps -p $PID -o rss"
+ $startmem = $word:1
+
+ output /dev/null
+ for i 0 1000
+   fprintf "test %10s" foobar
+ end
+ output stdout
+
+ list word -x "ps -p $PID -o rss"
+ $endmem = $word:1
+
+ if ($endmem - $startmem < 10)
+   $PASS = 1
+ else
+   $PASS = 0
+   echo growth: {$endmem - $startmem}
+   echo kB/loop: {($endmem - $startmem)/1000}
+ end
+end
Index: /branches/sc_branches/pantasks_condor/cmd.basic/test/getchr.sh
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.basic/test/getchr.sh	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.basic/test/getchr.sh	(revision 34783)
@@ -0,0 +1,23 @@
+
+list tests
+ test1
+ test2
+end
+
+# index test
+macro test1
+ $PASS = 1
+ getchr "a long string.string" . var
+ if ($var != 13)
+   $PASS = 0
+ end
+end
+
+# null test
+macro test2
+ $PASS = 1
+ getchr "a long string.string" x var
+ if ($var != -1)
+   $PASS = 0
+ end
+end
Index: /branches/sc_branches/pantasks_condor/cmd.basic/test/if.sh
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.basic/test/if.sh	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.basic/test/if.sh	(revision 34783)
@@ -0,0 +1,321 @@
+
+list tests
+ test1
+ test2
+ test3
+ test4
+ test5
+ test6
+ test7
+ test8
+ test9
+ test10
+ test11
+ test12
+ test13a
+ test13
+ test14
+end
+
+# basic logical test
+macro test1
+
+ local a
+
+ $a = 1
+
+ if ($a == 1)
+   $PASS = 1
+ else
+   $PASS = 0
+ end
+end
+
+
+# basic logical test
+macro test2
+
+ local a b
+
+ $a = 1
+ $b = 2
+
+ if (($a == 1) && ($b == 2))
+   $PASS = 1
+ else
+   $PASS = 0
+ end
+end
+
+
+# basic logical test
+macro test3
+
+ local a b
+
+ $a = 1
+ $b = 5
+
+ if (($a == 1) && ($b < 10))
+   $PASS = 1
+ else
+   $PASS = 0
+ end
+end
+
+
+# basic logical test
+macro test4
+
+ local a
+
+ $a = 0
+
+ if ($a == 1)
+   $PASS = 0
+ else
+   $PASS = 1
+ end
+end
+
+
+# basic logical test
+macro test5
+
+ local a
+
+ $a = 1
+
+ if ($a > 0)
+   $PASS = 1
+ else
+   $PASS = 0
+ end
+end
+
+
+# basic logical test
+macro test6
+
+ local a
+
+ $a = 1
+
+ if ($a < 2)
+   $PASS = 1
+ else
+   $PASS = 0
+ end
+end
+
+
+# basic logical test
+macro test7
+
+ local a
+
+ $a = test
+
+ if ("$a" == "test")
+   $PASS = 1
+ else
+   $PASS = 0
+ end
+end
+
+
+# basic logical test
+macro test8
+
+ local a
+
+ $a = foobar
+
+ if ("$a" == "test")
+   $PASS = 0
+ else
+   $PASS = 1
+ end
+end
+
+
+# basic logical test
+macro test9
+
+ local a b
+
+ $a = 1
+ $b = 2
+
+ if (($a == 0) || ($b == 2))
+   $PASS = 1
+ else
+   $PASS = 0
+ end
+end
+
+
+# basic logical test
+macro test10
+
+ local a b
+
+ $a = 1
+ $b = 2
+
+ if (($a == 0) || ($b == 1))
+   $PASS = 0
+ else
+   $PASS = 1
+ end
+end
+
+
+# basic logical test
+macro test11
+
+ local a b
+
+ $a = 1
+ $b = 2
+
+ if (($a <= 0) || ($b >= 3))
+   $PASS = 0
+ else
+   $PASS = 1
+ end
+end
+
+
+# basic logical test
+macro test12
+
+ local a
+
+ $a = 1
+
+ if ($a != 1)
+   $PASS = 0
+ else
+   $PASS = 1
+ end
+end
+
+
+# check memleaks
+macro test13a
+
+ local a b i N
+
+ list word -x "ps -p $PID -o rss"
+ $startmem = $word:1
+
+ output /dev/null
+ $a = 1
+ for i 0 1000
+   if ($a == 1)
+    echo "run"    
+   end
+ end    
+ output stdout
+  
+ list word -x "ps -p $PID -o rss"
+ $endmem = $word:1
+
+ if ({$endmem - $startmem} < 10)
+   $PASS = 1
+ else
+   $PASS = 0
+   echo "growth: {$endmem-$startmem}"
+   echo "kB/loop: {($endmem-$startmem)/1000}"
+ end
+end
+
+# check memleaks
+macro test13
+
+ local a b i N
+
+ list word -x "ps -p $PID -o rss"
+ $startmem = $word:1
+
+ output /dev/null
+ $a = 1
+ $b = 2
+ for i 0 1000
+   if (($a == 1) && ($b == 2))
+    echo "run"    
+   end
+ end    
+ output stdout
+  
+ list word -x "ps -p $PID -o rss"
+ $endmem = $word:1
+
+ if ({$endmem - $startmem} < 10)
+   $PASS = 1
+ else
+   $PASS = 0
+   echo "growth: {$endmem-$startmem}"
+   echo "kB/loop: {($endmem-$startmem)/1000}"
+ end
+end
+
+# memory test using continue
+macro test14
+
+ local a b i N
+
+ list word -x "ps -p $PID -o rss"
+ $startmem = $word:1
+
+ output /dev/null
+ $a = 1
+ $b = 2
+ for i 0 1000
+   if (($a == 1) && ($b == 2))
+    continue
+    echo "run"    
+   end
+ end    
+ output stdout
+  
+ list word -x "ps -p $PID -o rss"
+ $endmem = $word:1
+
+ if ({$endmem - $startmem} < 10)
+   $PASS = 1
+ else
+   $PASS = 0
+   echo "growth: {$endmem-$startmem}"
+   echo "kB/loop: {($endmem-$startmem)/1000}"
+ end
+end
+
+# memory test using continue
+macro test15
+
+ local a b i N bool
+
+ list word -x "ps -p $PID -o rss"
+ $startmem = $word:1
+
+ output /dev/null
+ $a = 1
+ $b = 2
+ for i 0 1000
+   $bool = (($a == 1) && ($b == 2))
+ end    
+ output stdout
+  
+ list word -x "ps -p $PID -o rss"
+ $endmem = $word:1
+
+ if ({$endmem - $startmem} < 10)
+   $PASS = 1
+ else
+   $PASS = 0
+   echo "growth: {$endmem-$startmem}"
+   echo "kB/loop: {($endmem-$startmem)/1000}"
+ end
+end
Index: /branches/sc_branches/pantasks_condor/cmd.basic/test/list.sh
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.basic/test/list.sh	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.basic/test/list.sh	(revision 34783)
@@ -0,0 +1,174 @@
+list tests
+ test1
+ test2
+ test3
+ test4
+ memtest2
+ memtest3
+ memtest4
+end
+
+list check
+  this
+  is
+  a
+  list
+end
+
+# Does list work?
+macro test1
+ $PASS = 1
+ if ($check:n != 4)
+  $PASS = 0
+  echo "Number of list elements: $check:n"
+ end
+ if (("$check:0" != "this") || ("$check:1" != "is") || ("$check:2" != "a") || ("$check:3" != "list"))
+  $PASS = 0
+  echo "List element does not return correctly!"
+ end
+end
+
+# Test split option
+macro test2
+ $PASS = 1
+ list check2 -split This is a list
+ if ($check2:n != 4)
+  $PASS = 0
+  echo "Number of list elements: $check2:n"
+ end
+ if (("$check2:0" != "This") || ("$check2:1" != "is") || ("$check2:2" != "a") || ("$check2:3" != "list"))
+  $PASS = 0
+  echo "List element does not return correctly!"
+ end
+end
+
+# Test -x option for the ls command
+macro test3
+ $PASS = 1
+ list check3 -x "ls /dev/null"
+ if ($check3:n != 1)
+  $PASS = 0
+  echo "Number of list elements: $check3:n"
+ end
+ if ("$check3:0" != "/dev/null")
+  $PASS = 0
+  echo "List element does not return correctly!"
+ end
+end
+
+# Test -x for files
+macro test4
+ $PASS = 1
+ file list_test.txt fchk
+ if ($fchk)
+  exec rm list_test.txt
+ end
+ output list_test.txt
+ echo This
+ echo is
+ echo a
+ echo list
+ output stdout
+ list check4 -x "cat list_test.txt"
+ if ($check4:n != 4)
+  $PASS = 0
+  echo "Number of list elements: $check4:n"
+ end
+ if (("$check4:0" != "This") || ("$check4:1" != "is") || ("$check4:2" != "a") || ("$check4:3" != "list"))
+  $PASS = 0
+  echo "List element does not return correctly!"
+ end
+ if ($PASS)
+  exec rm list_test.txt
+ end
+end
+
+# Memory test for list -split
+macro memtest2
+
+ local i
+
+ list word -x "ps -p $PID -o rss"
+ $startmem = $word:1
+
+ for i 0 1000
+  list check2 -split This is a list
+ end    
+  
+ list word -x "ps -p $PID -o rss"
+ $endmem = $word:1
+
+ $PASS = 1
+
+ if ($endmem - $startmem > 10)
+   $PASS = 0
+   echo "growth: {$endmem-$startmem}"
+   echo "kB/loop: {($endmem-$startmem)/1000}"
+ end
+
+end
+
+# Memory test for list -x
+macro memtest3
+
+ local i
+
+ list word -x "ps -p $PID -o rss"
+ $startmem = $word:1
+
+ for i 0 100
+  list check3 -x "ls /dev/null"
+ end    
+  
+ list word -x "ps -p $PID -o rss"
+ $endmem = $word:1
+
+ $PASS = 1
+
+ if ($endmem - $startmem > 10)
+   $PASS = 0
+   echo "growth: {$endmem-$startmem}"
+   echo "kB/loop: {($endmem-$startmem)/100}"
+ end
+
+end
+
+# Memory test for list -x
+macro memtest4
+
+ file list_test.txt fchk
+ if ($fchk)
+  exec rm list_test.txt
+ end
+ output list_test.txt
+ echo This
+ echo is
+ echo a
+ echo list
+ output stdout
+
+ local i
+
+ list word -x "ps -p $PID -o rss"
+ $startmem = $word:1
+
+ for i 0 100
+  list check4 -x "cat list_test.txt"
+ end    
+  
+ list word -x "ps -p $PID -o rss"
+ $endmem = $word:1
+
+ $PASS = 1
+
+ if ($endmem - $startmem > 10)
+   $PASS = 0
+   echo "growth: {$endmem-$startmem}"
+   echo "kB/loop: {($endmem-$startmem)/100}"
+ end
+
+ if ($PASS)
+  exec rm list_test.txt
+ end
+
+end
Index: /branches/sc_branches/pantasks_condor/cmd.basic/test/local.sh
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.basic/test/local.sh	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.basic/test/local.sh	(revision 34783)
@@ -0,0 +1,51 @@
+list tests
+ test1
+ memtest1
+end
+
+# Does local work?
+macro test1
+
+ $PASS = 1
+
+ local lvar
+
+ $lvar = 5
+
+ if ($?lvar != 1)
+  $PASS = 0
+  echo "Local variable failed to be created!"
+ end
+
+ if ($lvar != 5)
+  $PASS = 0
+  echo "Local variable value not assigned!"
+ end
+
+end
+
+
+# Memory test for local
+macro memtest1
+
+ local i
+
+ list word -x "ps -p $PID -o rss"
+ $startmem = $word:1
+
+ for i 0 1000
+  local lvar2
+  $lvar2 = 9
+ end
+  
+ list word -x "ps -p $PID -o rss"
+ $endmem = $word:1
+
+ $PASS = 1
+
+ if ($endmem - $startmem > 10)
+   $PASS = 0
+   echo "growth: {$endmem-$startmem}"
+   echo "kB/loop: {($endmem-$startmem)/1000}"
+ end
+end
Index: /branches/sc_branches/pantasks_condor/cmd.basic/test/macro.sh
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.basic/test/macro.sh	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.basic/test/macro.sh	(revision 34783)
@@ -0,0 +1,48 @@
+list tests
+ test_prep
+ test1
+ memtest1
+end
+
+# Is the macro working?
+macro test_prep
+ $test_var1 = check1
+ $test_var2 = check2
+ $var_count = $0
+end
+
+macro test1
+ $PASS = 1
+ $var_count = 0
+ $test_var1 = blank
+ $test_var2 = blank
+ test_prep var1 var2 var3
+ if ($var_count != 4)
+  $PASS = 0
+  echo "Number of parameters (should be 4): $var_count"
+ end
+ if (("$test_var1" != "check1") || ("$test_var2" != "check2"))
+  $PASS = 0
+  echo "Paramaters not assigned correctly!: $test_var1 $test_var2"
+ end
+end
+
+# Memory Test for macro
+macro memtest1
+ $PASS = 1
+ list word -x "ps -p $PID -o rss"
+ $startmem = $word:1
+
+ for i 0 10000
+  test_prep
+ end    
+  
+ list word -x "ps -p $PID -o rss"
+ $endmem = $word:1
+
+ if ($endmem - $startmem > 10)
+   $PASS = 0
+   echo "growth: {$endmem-$startmem}"
+   echo "kB/loop: {($endmem-$startmem)/10000}"
+ end
+end
Index: /branches/sc_branches/pantasks_condor/cmd.basic/test/math.sh
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.basic/test/math.sh	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.basic/test/math.sh	(revision 34783)
@@ -0,0 +1,149 @@
+
+list tests
+ test1
+ test2
+ testmem1
+ testmem2
+ testmem3
+ testmem4
+end
+
+# test subtraction
+macro test1
+
+  $PASS = 1
+  local i
+
+  exec rm -f tmp.txt
+  output tmp.txt
+  echo {99 - 98}
+  output stdout
+
+  $i = `cat tmp.txt` 
+ 
+ if ($i != 1)
+   $PASS = 0
+   echo "i : $i"
+ end
+
+end
+
+# test addition, division, and multiplication
+macro test2
+
+ $PASS = 1
+ local a b c
+
+ $a = {2 + 4}
+ $b = {12 / 2}
+ $c = {2 * 3}
+
+ if (($a != 6) || ($b != 6) || ($c != 6))
+   $PASS = 0
+   echo "ALL VALUES NOT 6: a=$a\ b=$b\ c=$c\"
+ end
+
+end
+
+# check memleaks (set global)
+macro testmem1
+
+ $PASS = 1
+ local i
+
+ list word -x "ps -p $PID -o rss"
+ $startmem = $word:1
+
+ output /dev/null
+ for i 0 10000
+   echo {99 - 98}
+ end    
+ output stdout
+  
+ list word -x "ps -p $PID -o rss"
+ $endmem = $word:1
+
+ if ({$endmem - $startmem} > 10)
+   $PASS = 0
+   echo "growth: {$endmem-$startmem}"
+   echo "kB/loop: {($endmem-$startmem)/10000}"
+ end
+end
+
+# check memleaks (set global)
+macro testmem2
+
+ $PASS = 1
+ local i N
+
+ list word -x "ps -p $PID -o rss"
+ $startmem = $word:1
+
+ output /dev/null
+ for i 0 10000
+   $N = 99 - 98
+   $N = 1 + 1
+   $N = 2 * 2
+   $N = 22/7
+ end    
+ output stdout
+  
+ list word -x "ps -p $PID -o rss"
+ $endmem = $word:1
+
+ if ({$endmem - $startmem} > 10)
+   $PASS = 0
+   echo "growth: {$endmem-$startmem}"
+   echo "kB/loop: {($endmem-$startmem)/10000}"
+ end
+end
+
+# check memleaks (set global)
+macro testmem3
+
+ $PASS = 1
+ local i N
+
+ list word -x "ps -p $PID -o rss"
+ $startmem = $word:1
+
+ output /dev/null
+ for i 0 10000
+   $N = 99
+ end    
+ output stdout
+  
+ list word -x "ps -p $PID -o rss"
+ $endmem = $word:1
+
+ if ({$endmem - $startmem} > 10)
+   $PASS = 0
+   echo "growth: {$endmem-$startmem}"
+   echo "kB/loop: {($endmem-$startmem)/10000}"
+ end
+end
+
+# check memleaks (set global)
+macro testmem4
+
+ $PASS = 1
+ local i N
+
+ list word -x "ps -p $PID -o rss"
+ $startmem = $word:1
+
+ output /dev/null
+ for i 0 10000
+   $N = word
+ end    
+ output stdout
+  
+ list word -x "ps -p $PID -o rss"
+ $endmem = $word:1
+
+ if ({$endmem - $startmem} > 10)
+   $PASS = 0
+   echo "growth: {$endmem-$startmem}"
+   echo "kB/loop: {($endmem-$startmem)/10000}"
+ end
+end
Index: /branches/sc_branches/pantasks_condor/cmd.basic/test/module.sh
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.basic/test/module.sh	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.basic/test/module.sh	(revision 34783)
@@ -0,0 +1,16 @@
+
+list tests
+ test1
+end
+
+# Test if the $MODULES list is available
+macro test1
+
+ $PASS = 1
+
+ if ($?MODULES:0 != 1)
+  $PASS = 0
+  echo "Modules list not loaded!"
+ end
+
+end
Index: /branches/sc_branches/pantasks_condor/cmd.basic/test/output.sh
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.basic/test/output.sh	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.basic/test/output.sh	(revision 34783)
@@ -0,0 +1,63 @@
+
+list tests
+ test1
+ testmem1
+end
+
+# test subtraction
+macro test1
+
+ $PASS = 1
+
+ output testout.txt
+
+ file testout.txt fchk
+ output stdout
+ if ($fchk != 1)
+  $PASS = 0
+  echo "Output did not create test file!"
+ end
+
+ output testout.txt
+ echo "This is a test."
+ output stdout
+ $line = `cat testout.txt`
+ if ("$line" == "This is a test.")
+   $PASS = 1
+ else
+   $PASS = 0
+   echo "Output: $line"
+ end
+
+ exec rm testout.txt
+
+end
+
+
+# check memleaks
+macro testmem1
+
+ $PASS = 1
+ local i
+
+ list word -x "ps -p $PID -o rss"
+ $startmem = $word:1
+
+ output /dev/null
+ for i 0 100
+  output testout.txt
+  echo "This is a test."
+  output stdout
+  exec rm testout.txt
+ end    
+ output stdout
+  
+ list word -x "ps -p $PID -o rss"
+ $endmem = $word:1
+
+ if ({$endmem - $startmem} > 10)
+   $PASS = 0
+   echo "growth: {$endmem-$startmem}"
+   echo "kB/loop: {($endmem-$startmem)/100}"
+ end
+end
Index: /branches/sc_branches/pantasks_condor/cmd.basic/test/scan.sh
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.basic/test/scan.sh	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.basic/test/scan.sh	(revision 34783)
@@ -0,0 +1,117 @@
+list tests
+ test1
+ memtest1
+ memtest2
+end
+
+# Does scan work?
+macro test1
+ $PASS = 1
+ file test_file.txt fchk
+ if ($fchk)
+  exec rm test_file.txt
+ end
+ output test_file.txt
+ echo This
+ echo is
+ echo a
+ echo test
+ echo file
+ output stdout
+ scan test_file.txt fscan
+ if ("$fscan" != "This")
+  $PASS = 0
+  echo "Default not working!"
+ end
+ scan test_file.txt fscan 4
+ if ("$fscan" != "test")
+  $PASS = 0
+  echo "Scan failure!"
+ end
+ if ($PASS)
+  exec rm test_file.txt
+ end
+end
+
+# Memory test for scan (default)
+macro memtest1
+
+ file test_file.txt fchk
+ if ($fchk)
+  exec rm test_file.txt
+ end
+ output test_file.txt
+ echo This
+ echo is
+ echo a
+ echo test
+ echo file
+ output stdout
+
+ local i
+
+ list word -x "ps -p $PID -o rss"
+ $startmem = $word:1
+
+ for i 0 1000
+  scan test_file.txt fscan
+ end    
+  
+ list word -x "ps -p $PID -o rss"
+ $endmem = $word:1
+
+ $PASS = 1
+
+ if ($endmem - $startmem > 10)
+   $PASS = 0
+   echo "growth: {$endmem-$startmem}"
+   echo "kB/loop: {($endmem-$startmem)/1000}"
+ end
+
+ if ($PASS)
+  exec rm test_file.txt
+ end
+
+end
+
+
+# Memory test for scan (specified)
+macro memtest2
+
+ file test_file.txt fchk
+ if ($fchk)
+  exec rm test_file.txt
+ end
+ output test_file.txt
+ echo This
+ echo is
+ echo a
+ echo test
+ echo file
+ output stdout
+
+ local i
+
+ list word -x "ps -p $PID -o rss"
+ $startmem = $word:1
+
+ for i 0 1000
+  scan test_file.txt fscan 5
+ end    
+  
+ list word -x "ps -p $PID -o rss"
+ $endmem = $word:1
+
+ $PASS = 1
+
+ if ($endmem - $startmem > 10)
+   $PASS = 0
+   echo "growth: {$endmem-$startmem}"
+   echo "kB/loop: {($endmem-$startmem)/1000}"
+ end
+
+ if ($PASS)
+  exec rm test_file.txt
+ end
+
+end
Index: /branches/sc_branches/pantasks_condor/cmd.basic/test/shell.sh
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.basic/test/shell.sh	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.basic/test/shell.sh	(revision 34783)
@@ -0,0 +1,35 @@
+
+list tests
+ test1
+ test2
+end
+
+# test that a shell function works at all
+macro test1
+ $PASS = 1
+ exec touch foo.test
+ file foo.test exists
+ if ($exists != 1)
+   $PASS = 0
+ end
+ exec rm -f foo.test
+ file foo.test exists
+ if ($exists != 0)
+   $PASS = 0
+ end
+end
+
+# test that the shell status is returned
+macro test2
+ $PASS = 1
+ exec touch foo.test
+ exec ls foo.test >& /dev/null
+ if ($STATUS != 1)
+   $PASS = 0
+ end
+ exec rm -f foo.test
+ exec ls foo.test >& /dev/null
+ if ($STATUS != 0)
+   $PASS = 0
+ end
+end
Index: /branches/sc_branches/pantasks_condor/cmd.basic/test/sleep.sh
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.basic/test/sleep.sh	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.basic/test/sleep.sh	(revision 34783)
@@ -0,0 +1,25 @@
+list tests
+ test1
+end
+
+# Does sleep work?
+macro test1
+ $PASS = 1
+
+ list tstart -x "date +%S"
+
+ sleep 3
+
+ list tend -x "date +%S"
+
+ if ($tstart:0 >= 57)
+  $tend:0 = $tend:0 + 60
+ end
+
+ $diff = abs (3 - abs($tstart:0 - $tend:0))
+
+ if ($diff > 1.1)
+  $PASS = 0
+ end
+
+end
Index: /branches/sc_branches/pantasks_condor/cmd.basic/test/sprintf.sh
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.basic/test/sprintf.sh	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.basic/test/sprintf.sh	(revision 34783)
@@ -0,0 +1,43 @@
+list tests
+ test1
+ memtest1
+end
+
+# Does sprintf work?
+macro test1
+ $PASS = 1
+
+ local test_var
+
+ sprint test_var "%7s %5.2f %9.3e" float 34.5 12630000
+
+ if ("$test_var" != "  float 34.50 1.263e+07")
+  $PASS = 0
+ end
+
+end
+
+# Memory test
+macro memtest1
+
+ local i
+
+ list word -x "ps -p $PID -o rss"
+ $startmem = $word:1
+
+ for i 0 10000
+  sprint test_var "%7s %5.2f %9.3e" float 34.5 12630000
+ end    
+  
+ list word -x "ps -p $PID -o rss"
+ $endmem = $word:1
+
+ $PASS = 1
+
+ if (($endmem - $startmem)/10000 > 1.0)
+   $PASS = 0
+   echo "growth: {$endmem-$startmem}"
+   echo "kB/loop: {($endmem-$startmem)/10000}"
+ end
+
+end
Index: /branches/sc_branches/pantasks_condor/cmd.basic/test/strlen.sh
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.basic/test/strlen.sh	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.basic/test/strlen.sh	(revision 34783)
@@ -0,0 +1,47 @@
+list tests
+ test1
+ memtest1
+end
+
+# Does strlen work?
+macro test1
+ 
+ $PASS = 1
+
+ $tstr = "Test string"
+
+ strlen $tstr len
+
+ if ($len != 11)
+  $PASS = 0
+  echo "Incorrect length: $len"
+ end
+
+end
+
+# Memory Test
+macro memtest1
+
+ local i
+
+ $tstr = "Test string"
+
+ list word -x "ps -p $PID -o rss"
+ $startmem = $word:1
+
+ for i 0 1000
+  strlen $tstr len
+ end    
+  
+ list word -x "ps -p $PID -o rss"
+ $endmem = $word:1
+
+ $PASS = 1
+
+ if ($endmem - $startmem > 10)
+   $PASS = 0
+   echo "growth: {$endmem-$startmem}"
+   echo "kB/loop: {($endmem-$startmem)/1000}"
+ end
+
+end
Index: /branches/sc_branches/pantasks_condor/cmd.basic/test/strpop.sh
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.basic/test/strpop.sh	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.basic/test/strpop.sh	(revision 34783)
@@ -0,0 +1,105 @@
+list tests
+ test1
+ test2
+ memtest1
+ memtest2
+end
+
+# Does strpop work with variables?
+macro test1
+
+ $PASS = 1
+
+ $tstr = a test string
+
+ local a b c
+
+ strpop tstr a
+ strpop tstr b
+ strpop tstr c
+
+ if (("$a" != "a") || ("$b" != "test") || ("$c" != "string"))
+  $PASS = 0
+  echo "Incorrect value returned!"
+ end
+end
+
+
+# Does strpop work with lists?
+macro test2
+
+ $PASS = 1
+
+ list tlis -split list of strings
+
+ local a b c
+
+ strpop tlis:0 a
+ strpop tlis:1 b
+ strpop tlis:2 c
+
+ if (("$a" != "list") || ("$b" != "of") || ("$c" != "strings"))
+  $PASS = 0
+  echo "Incorrect value returned!"
+ end
+end
+
+
+# Memory test for variables
+macro memtest1
+
+ local i tstr a b c d
+
+ list word -x "ps -p $PID -o rss"
+ $startmem = $word:1
+
+ for i 0 1000
+  $tstr = one two three
+  strlen tstr a
+  strlen tstr b
+  strlen tstr c
+  strlen tstr d
+ end    
+  
+ list word -x "ps -p $PID -o rss"
+ $endmem = $word:1
+
+ $PASS = 1
+
+ if ($endmem - $startmem > 10)
+   $PASS = 0
+   echo "growth: {$endmem-$startmem}"
+   echo "kB/loop: {($endmem-$startmem)/1000}"
+ end
+
+end
+
+
+# Memory test for lists
+macro memtest2
+
+ local i tstr a b c d
+
+ list word -x "ps -p $PID -o rss"
+ $startmem = $word:1
+
+ for i 0 1000
+  list tstr -split one two three
+  strlen tstr:0 a
+  strlen tstr:1 b
+  strlen tstr:2 c
+  strlen tstr:0 d
+ end    
+  
+ list word -x "ps -p $PID -o rss"
+ $endmem = $word:1
+
+ $PASS = 1
+
+ if ($endmem - $startmem > 10)
+   $PASS = 0
+   echo "growth: {$endmem-$startmem}"
+   echo "kB/loop: {($endmem-$startmem)/1000}"
+ end
+
+end
Index: /branches/sc_branches/pantasks_condor/cmd.basic/test/substr.sh
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.basic/test/substr.sh	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.basic/test/substr.sh	(revision 34783)
@@ -0,0 +1,49 @@
+list tests
+ test1
+ memtest1
+end
+
+# Does substr work?
+macro test1
+
+ $PASS = 1
+
+ local tstr ss
+
+ $tstr = "riddle me this"
+
+ substr $tstr 4 8 ss
+
+ if ("$ss" != "le me th")
+  $PASS = 0
+  echo "Incorrect substring returned!"
+ end
+end
+
+
+# Memory test
+macro memtest1
+
+ local i tstr ss
+
+ $tstr = "riddle me this"
+
+ list word -x "ps -p $PID -o rss"
+ $startmem = $word:1
+
+ for i 0 10000
+  substr $tstr 4 8 ss
+ end    
+  
+ list word -x "ps -p $PID -o rss"
+ $endmem = $word:1
+
+ $PASS = 1
+
+ if ($endmem - $startmem > 10)
+   $PASS = 0
+   echo "growth: {$endmem-$startmem}"
+   echo "kB/loop: {($endmem-$startmem)/10000}"
+ end
+
+end
Index: /branches/sc_branches/pantasks_condor/cmd.basic/test/test.dat
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.basic/test/test.dat	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.basic/test/test.dat	(revision 34783)
@@ -0,0 +1,1 @@
+test     foobar
Index: /branches/sc_branches/pantasks_condor/cmd.basic/test/usleep.sh
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.basic/test/usleep.sh	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.basic/test/usleep.sh	(revision 34783)
@@ -0,0 +1,25 @@
+list tests
+ test1
+end
+
+# Does usleep work?
+macro test1
+ $PASS = 1
+
+ list tstart -x "date +%S"
+
+ usleep 3000000
+
+ list tend -x "date +%S"
+
+ if ($tstart:0 >= 57)
+  $tend:0 = $tend:0 + 60
+ end
+
+ $diff = abs (3 - abs($tstart:0 - $tend:0))
+
+ if ($diff > 1.1)
+  $PASS = 0
+ end
+
+end
Index: /branches/sc_branches/pantasks_condor/cmd.basic/test/variable.sh
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.basic/test/variable.sh	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.basic/test/variable.sh	(revision 34783)
@@ -0,0 +1,506 @@
+
+list tests
+ test1
+ test2
+ test3
+ test4
+ test5
+ test6
+ test7
+ test8
+ test9
+ test10
+ test11
+ test12
+ test13
+ test14
+ testmem1
+ testmem2
+ testmem3
+ testmem4
+ testmem5
+ testmem6
+ testmem7
+ testmem8
+ testmem9
+ testmem10
+end
+
+# do we set variables correctly?
+macro test1
+
+ local i
+
+ $i = 99
+  
+ if ($i == 99)
+   $PASS = 1
+ else
+   $PASS = 0
+ end
+
+end
+
+# do math expressions assign the correct value (test2 -> test8)
+macro test2
+
+  local i
+
+  $i = 99 - 98
+  
+ if ($i == 1)
+   $PASS = 1
+ else
+   $PASS = 0
+   echo "i : $i"
+ end
+
+end
+
+macro test3
+
+  local i
+
+  $i = 2*3
+  
+ if ($i == 6)
+   $PASS = 1
+ else
+   $PASS = 0
+   echo "i: $i"
+ end
+
+end
+
+macro test4
+
+  local i
+
+  $i = 2^3
+
+ if ($i == 8)
+   $PASS = 1
+ else
+   $PASS = 0
+   echo "i: $i"
+ end
+
+end
+
+macro test5
+
+  local i
+
+  $i = 2+3
+
+ if ($i == 5)
+   $PASS = 1
+ else
+   $PASS = 0
+   echo "i: $i"
+ end
+
+end
+
+macro test6
+
+  local i
+
+  $i = 6/3
+
+ if ($i == 2)
+   $PASS = 1
+ else
+   $PASS = 0
+   echo "i: $i"
+ end
+
+end
+
+# testing operation priority
+macro test7
+
+  local i j
+
+  $i = 2 - 3*2
+  $j = 10/2*5+5
+
+ if (($i == -4) && ($j == 30))
+   $PASS = 1
+ else
+   $PASS = 0
+   echo "i: $i\ j: $j"
+ end
+
+end
+
+# testing math on negative numbers
+macro test8
+
+  local i
+
+  $i = -2 - -3
+
+ if ($i == 1)
+   $PASS = 1
+ else
+   $PASS = 0
+   echo "i: $i"
+ end
+
+end
+
+# testing the existance of variables
+macro test9
+
+  local i
+
+  $i = 0
+
+ if ($?i == 1)
+   $PASS = 1
+ else
+   $PASS = 0
+   echo "i: $i"
+ end
+end
+
+# test increment
+macro test10
+
+  local i N
+
+  $N = 0
+  for i 0 100
+    $N ++
+  end
+
+  if ($N == 100)
+   $PASS = 1
+  else
+   $PASS = 0
+   echo "N: $N"
+ end
+end
+
+# test decrement
+macro test11
+
+  local i N
+
+  $N = 100
+  for i 0 100
+    $N --
+  end
+
+  if ($N == 0)
+   $PASS = 1
+  else
+   $PASS = 0
+   echo "N: $N"
+ end
+end
+
+# test command assign
+macro test12
+
+  local i N
+
+  $N = `ls -dF /etc`
+
+  if ("$N" == "/etc/")
+   $PASS = 1
+  else
+   $PASS = 0
+   echo "N: $N"
+ end
+end
+
+# test vector assign
+macro test13
+
+  local i N
+
+  create v1 0 100
+  v1[5] = 10
+  set v2 = v1 + 20
+
+  if (v2[5] == 30)
+   $PASS = 1
+  else
+   $PASS = 0
+   echo "v1\[5\]: v1[5]"
+   echo "v2\[5\]: v2[5]"
+ end
+end
+
+# test local variables
+macro test14
+
+ $PASS = 1
+
+ # a slightly weak test: depends on no prior macros defining variables
+ # with the names below
+ if (($?very_obscure_name == 1) || ($?another_obscure_name == 1))
+  $PASS = 0
+ end
+end
+
+# check memleaks (set global)
+macro testmem1
+
+ local i
+
+ list word -x "ps -p $PID -o rss"
+ $startmem = $word:1
+
+ for i 0 10000
+   $Nvar = 10
+ end    
+  
+ list word -x "ps -p $PID -o rss"
+ $endmem = $word:1
+
+ if ({$endmem - $startmem} < 10)
+   $PASS = 1
+ else
+   $PASS = 0
+   echo "growth: {$endmem-$startmem}"
+   echo "kB/loop: {($endmem-$startmem)/10000}"
+ end
+end
+
+# check memleaks (set string)
+macro testmem2
+
+ local i
+
+ list word -x "ps -p $PID -o rss"
+ $startmem = $word:1
+
+ output /dev/null
+ for i 0 10000
+   $Nvar = test line
+ end    
+ output stdout
+  
+ list word -x "ps -p $PID -o rss"
+ $endmem = $word:1
+
+ if ({$endmem - $startmem} < 10)
+   $PASS = 1
+ else
+   $PASS = 0
+   echo "growth: {$endmem-$startmem}"
+   echo "kB/loop: {($endmem-$startmem)/10000}"
+ end
+end
+
+# check memleaks (set double)
+macro testmem3
+
+ local i
+
+ list word -x "ps -p $PID -o rss"
+ $startmem = $word:1
+
+ for i 0 10000
+   $Nvar = 5.212
+ end    
+  
+ list word -x "ps -p $PID -o rss"
+ $endmem = $word:1
+
+ if ({$endmem - $startmem} < 10)
+   $PASS = 1
+ else
+   $PASS = 0
+   echo "growth: {$endmem-$startmem}"
+   echo "kB/loop: {($endmem-$startmem)/10000}"
+ end
+end
+
+# check memleaks (set local)
+macro testmem4
+
+ local i N
+
+ list word -x "ps -p $PID -o rss"
+ $startmem = $word:1
+
+ for i 0 10000
+   $N = 10
+ end    
+  
+ list word -x "ps -p $PID -o rss"
+ $endmem = $word:1
+
+ if ({$endmem - $startmem} < 10)
+   $PASS = 1
+ else
+   $PASS = 0
+   echo "growth: {$endmem-$startmem}"
+   echo "kB/loop: {($endmem-$startmem)/10000}"
+ end
+end
+
+# check memleaks (existence)
+macro testmem5
+
+ local i N
+
+ list word -x "ps -p $PID -o rss"
+ $startmem = $word:1
+
+ output /dev/null
+ for i 0 10000
+   echo $?N
+ end    
+ output stdout
+  
+ list word -x "ps -p $PID -o rss"
+ $endmem = $word:1
+
+ if ({$endmem - $startmem} < 10)
+   $PASS = 1
+ else
+   $PASS = 0
+   echo "growth: {$endmem-$startmem}"
+   echo "kB/loop: {($endmem-$startmem)/10000}"
+ end
+end
+
+# check memleaks (get variable)
+macro testmem6
+
+ local i
+
+ list word -x "ps -p $PID -o rss"
+ $startmem = $word:1
+
+ $Nvar = 5
+ output /dev/null
+ for i 0 10000
+   echo $Nvar
+ end    
+ output stdout
+  
+ list word -x "ps -p $PID -o rss"
+ $endmem = $word:1
+
+ if ({$endmem - $startmem} < 10)
+   $PASS = 1
+ else
+   $PASS = 0
+   echo "growth: {$endmem-$startmem}"
+   echo "kB/loop: {($endmem-$startmem)/10000}"
+ end
+end
+
+# check memleaks (increment)
+macro testmem7
+
+ local i
+
+ list word -x "ps -p $PID -o rss"
+ $startmem = $word:1
+
+ $Nvar = 0
+ output /dev/null
+ for i 0 10000
+   $Nvar ++
+ end    
+ output stdout
+  
+ list word -x "ps -p $PID -o rss"
+ $endmem = $word:1
+
+ if ({$endmem - $startmem} < 10)
+   $PASS = 1
+ else
+   $PASS = 0
+   echo "growth: {$endmem-$startmem}"
+   echo "kB/loop: {($endmem-$startmem)/10000}"
+ end
+end
+
+# check memleaks (decrement)
+macro testmem8
+
+ local i
+
+ list word -x "ps -p $PID -o rss"
+ $startmem = $word:1
+
+ $Nvar = 10000
+ output /dev/null
+ for i 0 10000
+   $Nvar --
+ end    
+ output stdout
+  
+ list word -x "ps -p $PID -o rss"
+ $endmem = $word:1
+
+ if ({$endmem - $startmem} < 10)
+   $PASS = 1
+ else
+   $PASS = 0
+   echo "growth: {$endmem-$startmem}"
+   echo "kB/loop: {($endmem-$startmem)/10000}"
+ end
+end
+
+# check memleaks (command)
+macro testmem9
+
+ local i
+
+ list word -x "ps -p $PID -o rss"
+ $startmem = $word:1
+
+ output /dev/null
+ for i 0 100
+   $Nvar = `ls -d /etc`
+ end    
+ output stdout
+  
+ list word -x "ps -p $PID -o rss"
+ $endmem = $word:1
+
+ if ({$endmem - $startmem} < 10)
+   $PASS = 1
+ else
+   $PASS = 0
+   echo "growth: {$endmem-$startmem}"
+   echo "kB/loop: {($endmem-$startmem)/100}"
+ end
+end
+
+# check memleaks (vector assign)
+macro testmem10
+
+ local i
+
+ create v1 0 100
+
+ list word -x "ps -p $PID -o rss"
+ $startmem = $word:1
+
+ output /dev/null
+ for i 0 10000
+   v1[5] = $i
+ end    
+ output stdout
+  
+ list word -x "ps -p $PID -o rss"
+ $endmem = $word:1
+
+ if ({$endmem - $startmem} < 10)
+   $PASS = 1
+ else
+   $PASS = 0
+   echo "growth: {$endmem-$startmem}"
+   echo "kB/loop: {($endmem-$startmem)/10000}"
+ end
+end
Index: /branches/sc_branches/pantasks_condor/cmd.basic/test/while.sh
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.basic/test/while.sh	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.basic/test/while.sh	(revision 34783)
@@ -0,0 +1,19 @@
+list tests
+ test1
+end
+
+# Does while work?
+macro test1
+ $PASS = 1
+ local i
+ $i = 0
+ while ($i <= 10)
+  if ($i == 11)
+   $PASS = 0
+   echo "While loop failure!"
+   echo "i: $i"
+   break
+  end
+  $i++
+ end
+end
Index: /branches/sc_branches/pantasks_condor/cmd.basic/usleep.c
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.basic/usleep.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.basic/usleep.c	(revision 34783)
@@ -0,0 +1,15 @@
+# include "basic.h"
+
+int exec_usleep (int argc, char **argv) {
+
+  int i;
+
+  if (argc < 2) {
+    gprint (GP_ERR, "usage: usleep N\n");
+    return (FALSE);
+  }
+
+  i = atof (argv[1]);
+  usleep (i);
+  return (TRUE);
+}
Index: /branches/sc_branches/pantasks_condor/cmd.basic/wait.c
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.basic/wait.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.basic/wait.c	(revision 34783)
@@ -0,0 +1,14 @@
+# include "basic.h"
+
+int wait_func (int argc, char **argv) {
+
+  char buff[1024];
+  int i;
+
+  for (i = 1; i < argc; i++) {
+    gprint (GP_ERR, "%s ", argv[i]);
+  }
+  gprint (GP_ERR, "\n");
+  scan_line (stdin, buff);
+  return (TRUE);
+}
Index: /branches/sc_branches/pantasks_condor/cmd.basic/which.c
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.basic/which.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.basic/which.c	(revision 34783)
@@ -0,0 +1,18 @@
+# include "basic.h"
+
+int which (int argc, char **argv) {
+
+  Command *cmd;
+ 
+  if (argc != 2) {
+    gprint (GP_ERR, "USAGE: which <filename>\n");
+    return (FALSE);
+  }
+
+  cmd = MatchCommand (argv[1], TRUE, TRUE);
+  if (cmd == NULL) return (FALSE);
+
+  gprint (GP_ERR, "%-25s -- %s\n", cmd[0].name, cmd[0].help);
+  return (TRUE);
+
+}
Index: /branches/sc_branches/pantasks_condor/cmd.data/Makefile
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.data/Makefile	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.data/Makefile	(revision 34783)
@@ -0,0 +1,174 @@
+default: libdatacmd
+
+include ../../../Makefile.System
+HOME    =       $(ROOT)/src/opihi
+SRC     =       $(HOME)/cmd.data
+BIN     =       $(HOME)/bin
+LIB     =       $(HOME)/lib
+INC     =       $(HOME)/include
+DATA    =       $(DESTDATA)/mana
+include ../Makefile.Common
+
+# programs may add their own internal requirements here
+FULL_CFLAGS   = $(BASE_CFLAGS) -fPIC
+FULL_CPPFLAGS = $(BASE_CPPFLAGS)
+FULL_LDFLAGS  = $(BASE_LDFLAGS)
+
+# data user commands ########################
+srcs = \
+$(SRC)/init.$(ARCH).o          \
+$(SRC)/accum.$(ARCH).o		\
+$(SRC)/applyfit1d.$(ARCH).o	\
+$(SRC)/applyfit2d.$(ARCH).o	\
+$(SRC)/applyfit3d.$(ARCH).o	\
+$(SRC)/box.$(ARCH).o		\
+$(SRC)/book.$(ARCH).o		\
+$(SRC)/book_commands.$(ARCH).o	\
+$(SRC)/center.$(ARCH).o	\
+$(SRC)/clear.$(ARCH).o		\
+$(SRC)/imclip.$(ARCH).o		\
+$(SRC)/close.$(ARCH).o		\
+$(SRC)/concat.$(ARCH).o	\
+$(SRC)/contour.$(ARCH).o	\
+$(SRC)/create.$(ARCH).o	\
+$(SRC)/cumulative.$(ARCH).o	\
+$(SRC)/cursor.$(ARCH).o	\
+$(SRC)/cut.$(ARCH).o		\
+$(SRC)/delete.$(ARCH).o	\
+$(SRC)/densify.$(ARCH).o	\
+$(SRC)/device.$(ARCH).o	\
+$(SRC)/dimendown.$(ARCH).o	\
+$(SRC)/dimenup.$(ARCH).o	\
+$(SRC)/dbconnect.$(ARCH).o	\
+$(SRC)/dbselect.$(ARCH).o	\
+$(SRC)/erase.$(ARCH).o		\
+$(SRC)/extract.$(ARCH).o	\
+$(SRC)/fft1d.$(ARCH).o		\
+$(SRC)/fft2d.$(ARCH).o		\
+$(SRC)/fit1d.$(ARCH).o		\
+$(SRC)/fit2d.$(ARCH).o		\
+$(SRC)/fit3d.$(ARCH).o		\
+$(SRC)/gaussj.$(ARCH).o	\
+$(SRC)/gaussdeviate.$(ARCH).o	\
+$(SRC)/grid.$(ARCH).o		\
+$(SRC)/gridify.$(ARCH).o       \
+$(SRC)/grow.$(ARCH).o		\
+$(SRC)/ungridify.$(ARCH).o     \
+$(SRC)/histogram.$(ARCH).o	\
+$(SRC)/hermitian1d.$(ARCH).o	\
+$(SRC)/hermitian2d.$(ARCH).o	\
+$(SRC)/imcut.$(ARCH).o	 	\
+$(SRC)/imhist.$(ARCH).o	\
+$(SRC)/imsmooth.$(ARCH).o	\
+$(SRC)/integrate.$(ARCH).o	\
+$(SRC)/interpolate.$(ARCH).o	\
+$(SRC)/jpeg.$(ARCH).o		\
+$(SRC)/kern.$(ARCH).o		\
+$(SRC)/keyword.$(ARCH).o	\
+$(SRC)/labels.$(ARCH).o	\
+$(SRC)/limits.$(ARCH).o	\
+$(SRC)/line.$(ARCH).o		\
+$(SRC)/list_buffers.$(ARCH).o	\
+$(SRC)/list_header.$(ARCH).o	\
+$(SRC)/list_vectors.$(ARCH).o	\
+$(SRC)/load.$(ARCH).o		\
+$(SRC)/lookup.$(ARCH).o	\
+$(SRC)/matrix.$(ARCH).o	\
+$(SRC)/match2d.$(ARCH).o	\
+$(SRC)/mkrgb.$(ARCH).o	\
+$(SRC)/mcreate.$(ARCH).o	\
+$(SRC)/medacc.$(ARCH).o	\
+$(SRC)/mget.$(ARCH).o		\
+$(SRC)/minterpolate.$(ARCH).o	\
+$(SRC)/mset.$(ARCH).o		\
+$(SRC)/peak.$(ARCH).o		\
+$(SRC)/periodogram.$(ARCH).o	\
+$(SRC)/plot.$(ARCH).o		\
+$(SRC)/dot.$(ARCH).o		\
+$(SRC)/point.$(ARCH).o		\
+$(SRC)/ps.$(ARCH).o		\
+$(SRC)/queuedelete.$(ARCH).o	\
+$(SRC)/queuedrop.$(ARCH).o	\
+$(SRC)/queuelist.$(ARCH).o	\
+$(SRC)/queueload.$(ARCH).o	\
+$(SRC)/queuesize.$(ARCH).o	\
+$(SRC)/queuepush.$(ARCH).o	\
+$(SRC)/queuepop.$(ARCH).o	\
+$(SRC)/queueprint.$(ARCH).o	\
+$(SRC)/queuesubstr.$(ARCH).o	\
+$(SRC)/queueinit.$(ARCH).o	\
+$(SRC)/radial.$(ARCH).o	\
+$(SRC)/rd.$(ARCH).o		\
+$(SRC)/rdseg.$(ARCH).o		\
+$(SRC)/read_vectors.$(ARCH).o	\
+$(SRC)/rebin.$(ARCH).o		\
+$(SRC)/reindex.$(ARCH).o	\
+$(SRC)/resize.$(ARCH).o		\
+$(SRC)/relocate.$(ARCH).o	\
+$(SRC)/roll.$(ARCH).o		\
+$(SRC)/rotate.$(ARCH).o	\
+$(SRC)/save.$(ARCH).o		\
+$(SRC)/section.$(ARCH).o	\
+$(SRC)/select.$(ARCH).o	\
+$(SRC)/set.$(ARCH).o		\
+$(SRC)/shift.$(ARCH).o		\
+$(SRC)/sort.$(ARCH).o		\
+$(SRC)/spline.$(ARCH).o		\
+$(SRC)/spline_commands.$(ARCH).o \
+$(SRC)/imspline_apply.$(ARCH).o	\
+$(SRC)/imspline_construct.$(ARCH).o \
+$(SRC)/imstats.$(ARCH).o	   \
+$(SRC)/style.$(ARCH).o		   \
+$(SRC)/subraster.$(ARCH).o	   \
+$(SRC)/subset.$(ARCH).o	           \
+$(SRC)/svd.$(ARCH).o		   \
+$(SRC)/swapbytes.$(ARCH).o	   \
+$(SRC)/textline.$(ARCH).o	   \
+$(SRC)/threshold.$(ARCH).o		\
+$(SRC)/tv.$(ARCH).o		   \
+$(SRC)/tvchannel.$(ARCH).o	   \
+$(SRC)/tvcolors.$(ARCH).o	   \
+$(SRC)/tvcontour.$(ARCH).o	   \
+$(SRC)/tvgrid.$(ARCH).o	           \
+$(SRC)/uniq.$(ARCH).o		   \
+$(SRC)/unsign.$(ARCH).o	           \
+$(SRC)/vbin.$(ARCH).o		   \
+$(SRC)/vgroup.$(ARCH).o		   \
+$(SRC)/vclip.$(ARCH).o		   \
+$(SRC)/vgauss.$(ARCH).o            \
+$(SRC)/vellipse.$(ARCH).o          \
+$(SRC)/vmaxwell.$(ARCH).o          \
+$(SRC)/vgrid.$(ARCH).o		   \
+$(SRC)/vload.$(ARCH).o		   \
+$(SRC)/vzload.$(ARCH).o		   \
+$(SRC)/vpop.$(ARCH).o		   \
+$(SRC)/vroll.$(ARCH).o		   \
+$(SRC)/vshift.$(ARCH).o		   \
+$(SRC)/vsmooth.$(ARCH).o	   \
+$(SRC)/vstats.$(ARCH).o		   \
+$(SRC)/wd.$(ARCH).o		   \
+$(SRC)/write_vectors.$(ARCH).o	   \
+$(SRC)/zap.$(ARCH).o		   \
+$(SRC)/zplot.$(ARCH).o
+
+# dependancy rules for include files ########################
+incs = \
+$(INC)/opihi.h \
+$(INC)/external.h \
+$(INC)/shell.h \
+$(INC)/dvomath.h \
+$(INC)/display.h 
+
+$(srcs): $(incs)
+
+$(LIB)/libdatacmd.$(ARCH).a: $(srcs)
+$(LIB)/libdatacmd.$(ARCH).$(DLLTYPE): $(srcs)
+
+$(DESTLIB)/libdatacmd.a: $(LIB)/libdatacmd.$(ARCH).a
+$(DESTLIB)/libdatacmd.$(DLLTYPE): $(LIB)/libdatacmd.$(ARCH).$(DLLTYPE)
+
+libdatacmd: $(DESTLIB)/libdatacmd.a  $(DESTLIB)/libdatacmd.$(DLLTYPE)
+
+uninstall:
+	rm -f $(DESTLIB)/libdatacmd.a
+	rm -f $(DESTLIB)/libdatacmd.$(DLLTYPE)
Index: /branches/sc_branches/pantasks_condor/cmd.data/accum.c
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.data/accum.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.data/accum.c	(revision 34783)
@@ -0,0 +1,77 @@
+# include "data.h"
+
+int accum (int argc, char **argv) {
+  
+  int i, Nbins, bin, N, Normalize;
+  float start, end, delta;
+  int *NV;
+  opihi_flt *V, *K, *O;
+  Vector *val, *key, *out;
+
+  NV = NULL;
+  Normalize = FALSE;
+  if ((N = get_argument (argc, argv, "-norm"))) {
+    remove_argument (N, &argc, argv);
+    Normalize = TRUE;
+  }
+
+  if ((argc != 6) && (argc != 7)) {
+    gprint (GP_ERR, "USAGE: accum <value> <vector> <key> start end [delta]\n");
+    gprint (GP_ERR, "  sum <value> in bins corresponding to the value of <key>\n");
+    return (FALSE);
+  }
+
+  if ((val = SelectVector (argv[1], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((key = SelectVector (argv[3], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+  if (val[0].Nelements != key[0].Nelements) {
+    gprint (GP_ERR, "key and value don't match\n");
+    return (FALSE);
+  }
+  if ((out = SelectVector (argv[2], ANYVECTOR, TRUE)) == NULL) return (FALSE);
+
+  REQUIRE_VECTOR_FLT (val, FALSE); 
+  REQUIRE_VECTOR_FLT (key, FALSE); 
+
+  start = atof (argv[4]);
+  end   = atof (argv[5]);
+  if (argc == 7) 
+    delta = atof (argv[6]);
+  else 
+    delta = 1;
+  if ((start == end) || (delta == 0)) {
+    gprint (GP_ERR, "error in value: %f to %f, %f\n", start, end, delta);
+    return (FALSE);
+  }
+  delta = fabs (delta);
+  if (end - start < 0) {
+    delta = -1.0 * delta;
+  }
+  Nbins = (end - start) / delta;
+
+  ResetVector (out, OPIHI_FLT, Nbins);
+  bzero (out[0].elements.Flt, sizeof(opihi_flt)*out[0].Nelements);
+  if (Normalize) {
+    ALLOCATE (NV, int, Nbins);
+    bzero (NV, sizeof(int)*Nbins);
+  }
+
+  V = val[0].elements.Flt;
+  K = key[0].elements.Flt;
+  O = out[0].elements.Flt;
+
+  for (i = 0; i < val[0].Nelements; i++, V++, K++) {
+    bin = MIN (MAX (0, (*K - start) / delta), Nbins - 1);
+    O[bin] += *V;
+    if (Normalize) NV[bin] ++;
+  }      
+
+  if (Normalize) {
+    for (i = 0; i < Nbins; i++) {
+      O[i] /= (float) NV[i];
+    }
+    free (NV);
+  }
+
+  return (TRUE);
+}
+
Index: /branches/sc_branches/pantasks_condor/cmd.data/applyfit1d.c
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.data/applyfit1d.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.data/applyfit1d.c	(revision 34783)
@@ -0,0 +1,66 @@
+# include "data.h"
+
+int applyfit1d (int argc, char **argv) {
+  
+  int i, j, order;
+  char *c, name[64];
+  double *C, X;
+  opihi_flt *y;
+  Vector *xvec, *yvec;
+
+  if (argc != 3) {
+    gprint (GP_ERR, "USAGE: applyfit x y\n");
+    return (FALSE);
+  }
+
+  c = get_variable ("Cn");
+  if (c == NULL) {
+    gprint (GP_ERR, "no fit available\n");
+    return (FALSE);
+  }
+  order = atof (c);
+  free (c);
+
+  if ((xvec = SelectVector (argv[1], OLDVECTOR, TRUE)) == NULL) return (FALSE);    
+  if ((yvec = SelectVector (argv[2], ANYVECTOR, TRUE)) == NULL) return (FALSE);    
+
+  ALLOCATE (C, double, order+1);
+  for (i = 0; i < order + 1; i++) {
+    sprintf (name, "C%d", i);
+    c = get_variable (name);
+    if (c == NULL) {
+      gprint (GP_ERR, "missing fit term %d\n", i);
+      return (FALSE);
+    }
+    C[i] = atof (c);
+    free (c);
+  }
+
+  ResetVector (yvec, OPIHI_FLT, xvec[0].Nelements);
+  bzero (yvec[0].elements.Flt, sizeof(opihi_flt)*yvec[0].Nelements);
+  y = yvec[0].elements.Flt;
+
+  if (xvec[0].type == OPIHI_FLT) {
+    opihi_flt *x = xvec[0].elements.Flt;
+    for (j = 0; j < xvec[0].Nelements; j++, x++, y++) {
+      X = 1;
+      for (i = 0; i < order + 1; i++) {
+	*y += C[i]*X;
+	X = X * (*x);
+      }
+    }
+  } else {
+    opihi_int *x = xvec[0].elements.Int;
+    for (j = 0; j < xvec[0].Nelements; j++, x++, y++) {
+      X = 1;
+      for (i = 0; i < order + 1; i++) {
+	*y += C[i]*X;
+	X = X * (*x);
+      }
+    }
+  }
+
+  free (C);
+  return (TRUE);
+
+}
Index: /branches/sc_branches/pantasks_condor/cmd.data/applyfit2d.c
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.data/applyfit2d.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.data/applyfit2d.c	(revision 34783)
@@ -0,0 +1,117 @@
+# include "data.h"
+
+int applyfit2d (int argc, char **argv) {
+  
+  int i, j, n, order;
+  char *c, name[64];
+  double **C, X, Y;
+  opihi_flt *z;
+  Vector *xvec, *yvec, *zvec;
+
+  if (argc != 4) {
+    gprint (GP_ERR, "USAGE: applyfit2d x y z\n");
+    return (FALSE);
+  }
+
+  c = get_variable ("Cnn");
+  if (c == NULL) {
+    gprint (GP_ERR, "no fit available\n");
+    return (FALSE);
+  }
+  order = atof (c);
+  free (c);
+
+  if ((xvec = SelectVector (argv[1], OLDVECTOR, TRUE)) == NULL) return (FALSE);    
+  if ((yvec = SelectVector (argv[2], OLDVECTOR, TRUE)) == NULL) return (FALSE);    
+  if ((zvec = SelectVector (argv[3], ANYVECTOR, TRUE)) == NULL) return (FALSE);    
+
+  ALLOCATE (C, double *, order+1);
+  for (i = 0; i < order + 1; i++) {
+    ALLOCATE (C[i], double, order+1);
+    for (j = 0; j < order + 1 - i; j++) {
+      sprintf (name, "CX%dY%d", i, j);
+      c = get_variable (name);
+      if (c == NULL) {
+	gprint (GP_ERR, "missing fit term %d,%d\n", i, j);
+	for (j = 0; j < i; j++) free (C[j]);
+	free (C);
+	return (FALSE);
+      }
+      C[i][j] = atof (c);
+      free (c);
+    }
+  }
+
+  ResetVector (zvec, OPIHI_FLT, xvec[0].Nelements);
+  bzero (zvec[0].elements.Flt, sizeof(opihi_flt)*zvec[0].Nelements);
+  z = zvec[0].elements.Flt;
+
+  // we have four cases (x == flt or int) and (y == flt or int)
+  if ((xvec[0].type == OPIHI_FLT) && (yvec[0].type == OPIHI_FLT)) {
+    opihi_flt *x = xvec[0].elements.Flt;
+    opihi_flt *y = yvec[0].elements.Flt;
+    for (n = 0; n < xvec[0].Nelements; n++, x++, y++, z++) {
+      Y = X = 1;
+      for (j = 0; j < order + 1; j++) {
+	X = Y;
+	for (i = 0; i < order + 1 - j; i++) {
+	  *z += C[i][j]*X;
+	  X = X * (*x);
+	}
+	Y = Y * (*y);
+      }
+    }
+  }
+  if ((xvec[0].type == OPIHI_FLT) && (yvec[0].type == OPIHI_INT)) {
+    opihi_flt *x = xvec[0].elements.Flt;
+    opihi_int *y = yvec[0].elements.Int;
+    for (n = 0; n < xvec[0].Nelements; n++, x++, y++, z++) {
+      Y = X = 1;
+      for (j = 0; j < order + 1; j++) {
+	X = Y;
+	for (i = 0; i < order + 1 - j; i++) {
+	  *z += C[i][j]*X;
+	  X = X * (*x);
+	}
+	Y = Y * (*y);
+      }
+    }
+  }
+  if ((xvec[0].type == OPIHI_INT) && (yvec[0].type == OPIHI_FLT)) {
+    opihi_int *x = xvec[0].elements.Int;
+    opihi_flt *y = yvec[0].elements.Flt;
+    for (n = 0; n < xvec[0].Nelements; n++, x++, y++, z++) {
+      Y = X = 1;
+      for (j = 0; j < order + 1; j++) {
+	X = Y;
+	for (i = 0; i < order + 1 - j; i++) {
+	  *z += C[i][j]*X;
+	  X = X * (*x);
+	}
+	Y = Y * (*y);
+      }
+    }
+  }
+  if ((xvec[0].type == OPIHI_INT) && (yvec[0].type == OPIHI_INT)) {
+    opihi_int *x = xvec[0].elements.Int;
+    opihi_int *y = yvec[0].elements.Int;
+    for (n = 0; n < xvec[0].Nelements; n++, x++, y++, z++) {
+      Y = X = 1;
+      for (j = 0; j < order + 1; j++) {
+	X = Y;
+	for (i = 0; i < order + 1 - j; i++) {
+	  *z += C[i][j]*X;
+	  X = X * (*x);
+	}
+	Y = Y * (*y);
+      }
+    }
+  }
+
+  for (i = 0; i < order + 1; i++) free (C[i]);
+  free (C);
+
+  return (TRUE);
+}
+
+
Index: /branches/sc_branches/pantasks_condor/cmd.data/applyfit3d.c
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.data/applyfit3d.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.data/applyfit3d.c	(revision 34783)
@@ -0,0 +1,94 @@
+# include "data.h"
+
+int applyfit3d (int argc, char **argv) {
+  
+  int i, j, k, io, jo, n, order, nterm;
+  char *c, name[64];
+  double ***C, X, Y, Z;
+  opihi_flt *F;
+  Vector *xvec, *yvec, *zvec, *Fvec;
+
+  if (argc != 5) {
+    gprint (GP_ERR, "USAGE: applyfit x y z F\n");
+    return (FALSE);
+  }
+
+  c = get_variable ("Cnnn");
+  if (c == NULL) {
+    gprint (GP_ERR, "no fit available\n");
+    return (FALSE);
+  }
+  order = atof (c);
+  free (c);
+
+  if ((xvec = SelectVector (argv[1], OLDVECTOR, TRUE)) == NULL) return (FALSE);    
+  if ((yvec = SelectVector (argv[2], OLDVECTOR, TRUE)) == NULL) return (FALSE);    
+  if ((zvec = SelectVector (argv[3], OLDVECTOR, TRUE)) == NULL) return (FALSE);    
+  if ((Fvec = SelectVector (argv[4], ANYVECTOR, TRUE)) == NULL) return (FALSE);    
+
+  // this is a bit crude, but perhaps needed
+  CastVector (xvec, OPIHI_FLT);
+  CastVector (yvec, OPIHI_FLT);
+  CastVector (zvec, OPIHI_FLT);
+  CastVector (Fvec, OPIHI_FLT);
+
+  nterm = order + 1;
+  ALLOCATE (C, double **, nterm);
+  for (i = 0; i < order + 1; i++) {
+    ALLOCATE (C[i], double *, nterm);
+    for (j = 0; j < nterm - i; j++) {
+      ALLOCATE (C[i][j], double, nterm);
+      for (k = 0; k < nterm - i - j; k++) {
+	sprintf (name, "CX%dY%dZ%d", i, j, k);
+	c = get_variable (name);
+	if (c == NULL) {
+	  gprint (GP_ERR, "missing fit term %d,%d,%d\n", i, j, k);
+	  for (io = 0; io < i; io++) {
+	    for (jo = 0; jo < j; jo++) {
+	      free (C[io][jo]);
+	    }
+	    free (C[io]);
+	  }
+	  free (C);
+	  return (FALSE);
+	}
+	C[i][j][k] = atof (c);
+	free (c);
+      }
+    }
+  }
+
+  ResetVector (Fvec, OPIHI_FLT, xvec[0].Nelements);
+  bzero (Fvec[0].elements.Flt, sizeof(opihi_flt)*zvec[0].Nelements);
+  F = Fvec[0].elements.Flt;
+
+  opihi_flt *x = xvec[0].elements.Flt;
+  opihi_flt *y = yvec[0].elements.Flt;
+  opihi_flt *z = zvec[0].elements.Flt;
+
+  for (n = 0; n < xvec[0].Nelements; n++, x++, y++, z++, F++) {
+    Z = Y = X = 1;
+    for (k = 0; k < nterm; k++) {
+      Y = Z;
+      for (j = 0; j < nterm - k; j++) {
+	X = Y;
+	for (i = 0; i < nterm - k - j; i++) {
+	  *F += C[i][j][k]*X;
+	  X = X * (*x);
+	}
+	Y = Y * (*y);
+      }
+      Z = Z * (*z);
+    }
+  }
+
+  for (i = 0; i < nterm; i++) {
+    for (j = 0; j < nterm - i; j++) {
+      free (C[i][j]);
+    }
+    free (C[i]);
+  }
+  free (C);
+
+  return (TRUE);
+}
Index: /branches/sc_branches/pantasks_condor/cmd.data/book.c
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.data/book.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.data/book.c	(revision 34783)
@@ -0,0 +1,84 @@
+# include "data.h"
+
+int book_list (int argc, char **argv);
+int book_init (int argc, char **argv);
+int book_create (int argc, char **argv);
+int book_delete (int argc, char **argv);
+int book_getbook (int argc, char **argv);
+int book_listbook (int argc, char **argv);
+int book_shuffle (int argc, char **argv);
+int book_npages (int argc, char **argv);
+int book_newpage (int argc, char **argv);
+int book_getpage (int argc, char **argv);
+int book_delpage (int argc, char **argv);
+int book_listpage (int argc, char **argv);
+int book_setword (int argc, char **argv);
+int book_getword (int argc, char **argv);
+
+static Command book_commands[] = {
+  {1, "list",     book_list,     "list books"},
+  {1, "init",     book_init,     "initialize a book"},
+  {1, "create",   book_create,   "create a book"},
+  {1, "delete",   book_delete,   "delete a book"},
+  {1, "getbook",  book_getbook,  "get book name by location"},
+  {1, "listbook", book_listbook, "list pages in a book"},
+  {1, "shuffle",  book_shuffle,  "shuffle pages in a book"},
+  {1, "npages",   book_npages,   "return number of pages in a book"},
+  {1, "newpage",  book_newpage,  "create a new page in a book"},
+  {1, "getpage",  book_getpage,  "get page name by location"},
+  {1, "delpage",  book_delpage,  "delete a page in a book"},
+  {1, "listpage", book_listpage, "list a page in a book"},
+  {1, "setword",  book_setword,  "set the value of a word in a page"},
+  {1, "getword",  book_getword,  "set the value of a word from a page"},
+};
+
+int book_command (int argc, char **argv) {
+
+  int i, N, status;
+
+  if (argc < 2) {
+    gprint (GP_ERR, "USAGE: book (command)\n");
+    gprint (GP_ERR, "    book list                                  : list books\n");
+    gprint (GP_ERR, "    book init     (book)                       : removes all pages from book\n");
+    gprint (GP_ERR, "    book create   (book)                       : create a book\n");
+    gprint (GP_ERR, "    book delete   (book)                       : delete a book\n");
+    gprint (GP_ERR, "    book getbook  (where) [-var var]           : get book name\n");
+    gprint (GP_ERR, "    book listbook (book)                       : list a book\n");
+    gprint (GP_ERR, "    book shuffle  (book)                       : randomize pages in a book\n");
+    gprint (GP_ERR, "    book npages   (book) [-var var] [-key key] : return number of pages in a book\n");
+    gprint (GP_ERR, "    book newpage  (book) (page)                : create a new page in a book\n");
+    gprint (GP_ERR, "    book getpage  (book) (where) [-var var] [-key key value] : get page name in a book\n");
+    gprint (GP_ERR, "    book delpage  (book) (page) [-key key]     : delete a page in a book\n");
+    gprint (GP_ERR, "    book delpage  (book) -key name value       : delete a page in a book\n");
+    gprint (GP_ERR, "    book listpage (book) (page)                : list a page in a book\n");
+    gprint (GP_ERR, "    book setword  (book) (page) (word) (value) : set the value of a word in a page\n");
+    gprint (GP_ERR, "    book getword  (book) (page) (word) [-var var] : set the value of a word from a page\n");
+    return (FALSE);
+  }
+
+  N = sizeof (book_commands) / sizeof (Command);
+
+  /* find the book sub-command which matches */
+  for (i = 0; i < N; i++) {
+    if (!strcmp (book_commands[i].name, argv[1])) {
+      status = (*book_commands[i].func) (argc - 1, argv + 1);
+      return (status);
+    }
+  }
+
+  gprint (GP_ERR, "unknown book command %s\n", argv[1]);
+  return (FALSE);
+}
+
+/* book is called with the command "book".  
+   the command line word "book" is meant to be followed the one of several 
+   possible options:
+   
+   book create
+   book delete
+   book list
+   book edit
+   book read
+   book write
+
+*/
Index: /branches/sc_branches/pantasks_condor/cmd.data/book_commands.c
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.data/book_commands.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.data/book_commands.c	(revision 34783)
@@ -0,0 +1,489 @@
+# include "data.h"
+
+int book_list (int argc, char **argv) {
+  if (argc != 1) {
+    gprint (GP_ERR, "USAGE: book list\n");
+    return FALSE;
+  }
+
+  ListBooks();
+  return TRUE;
+}
+
+int book_create (int argc, char **argv) {
+  if (argc != 2) {
+    gprint (GP_ERR, "USAGE: book create (book)\n");
+    return FALSE;
+  }
+
+  CreateBook (argv[1]);
+  return TRUE;
+}
+
+int book_delete (int argc, char **argv) {
+
+  int status;
+  Book *book;
+
+  if (argc != 2) {
+    gprint (GP_ERR, "USAGE: book delete (book)\n");
+    return FALSE;
+  }
+
+  book = FindBook (argv[1]);
+  if (book == NULL) {
+    gprint (GP_ERR, "book %s not found\n", argv[1]);
+    return FALSE;
+  }
+
+  status = DeleteBook (book);
+  if (!status) abort ();
+  return TRUE;
+}
+
+int book_init (int argc, char **argv) {
+
+  int status;
+  Book *book;
+
+  if (argc != 2) {
+    gprint (GP_ERR, "USAGE: book init (book)\n");
+    return FALSE;
+  }
+
+  book = FindBook (argv[1]);
+  if (book != NULL) {
+      status = DeleteBook (book);
+      if (!status) abort ();
+  }
+
+  CreateBook (argv[1]);
+  return TRUE;
+}
+
+int book_listbook (int argc, char **argv) {
+
+  Book *book;
+
+  if (argc != 2) {
+    gprint (GP_ERR, "USAGE: book listbook (book)\n");
+    return FALSE;
+  }
+
+  book = FindBook (argv[1]);
+  if (book == NULL) {
+    gprint (GP_ERR, "book %s not found\n", argv[1]);
+    return FALSE;
+  }
+
+  ListPages (book);
+  return TRUE;
+}
+
+int book_npages (int argc, char **argv) {
+
+  int i, N, Npages;
+  Book *book;
+  char *varName;
+  char *Key, *Value, *value;
+
+  varName = NULL;
+  if ((N = get_argument (argc, argv, "-var"))) {
+    remove_argument (N, &argc, argv);
+    varName = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  Key = NULL;
+  Value = NULL;
+  if ((N = get_argument (argc, argv, "-key"))) {
+    remove_argument (N, &argc, argv);
+    Key = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+    Value = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  if (argc != 2) {
+    gprint (GP_ERR, "USAGE: book npages (book) [-var result] [-key name value]\n");
+    gprint (GP_ERR, "  reports the number of pages (optionally limited to those matching the key)\n");
+    FREE (varName);
+    return FALSE;
+  }
+
+  book = FindBook (argv[1]);
+  if (book == NULL) {
+    gprint (GP_ERR, "book %s not found\n", argv[1]);
+    FREE (varName);
+    return FALSE;
+  }
+
+  Npages = book[0].Npages;
+  if (Key) {
+    /* count only matching key */
+    Npages = 0;
+    for (i = 0; i < book[0].Npages; i++) {
+      value = BookGetWord (book[0].pages[i], Key);
+      if (value == NULL) {
+	if (!strcmp(Value, "NULL")) { 
+	  Npages ++;
+	} else {
+	  continue;
+	}
+      } else {
+	if (!strcmp(value, Value)) Npages ++;
+      }
+    }
+  }
+
+  if (varName) {
+    set_int_variable (varName, Npages);
+  } else {
+    gprint (GP_ERR, "%d pages\n", Npages);
+  }
+  FREE (Key);
+  FREE (Value);
+  FREE (varName);
+  return TRUE;
+}
+
+int book_getbook (int argc, char **argv) {
+
+  int where, N;
+  char *varName;
+  Book *book;
+
+  varName = NULL;
+  if ((N = get_argument (argc, argv, "-var"))) {
+    remove_argument (N, &argc, argv);
+    varName = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  if (argc != 2) {
+    gprint (GP_ERR, "USAGE: book getbook (where) [-var var]\n");
+    FREE (varName);
+    return FALSE;
+  }
+
+  where = atoi (argv[1]);
+  book = GetBook (where);
+  if (book == NULL) {
+    if (varName) {
+      set_str_variable (varName, "NULL");
+      FREE (varName);
+      return TRUE;
+    } 
+
+    gprint (GP_ERR, "book %s not found\n", argv[1]);
+    FREE (varName);
+    return FALSE;
+  }
+
+  if (varName) {
+    set_str_variable (varName, book[0].name);
+  } else {
+    gprint (GP_LOG, "%s\n", book[0].name);
+  }
+  FREE (varName);
+  return TRUE;
+}
+
+int book_newpage (int argc, char **argv) {
+
+  Book *book;
+
+  if (argc != 3) {
+    gprint (GP_ERR, "USAGE: book newpage (book) (page)\n");
+    return FALSE;
+  }
+
+  book = FindBook (argv[1]);
+  if (book == NULL) {
+    gprint (GP_ERR, "book %s not found\n", argv[1]);
+    return FALSE;
+  }
+  
+  CreatePage (book, argv[2]);
+  return TRUE;
+}
+
+int book_delpage (int argc, char **argv) {
+
+  int i, N;
+  Page *page;
+  Book *book;
+  char *Key, *Value, *value;
+
+  Key = NULL;
+  Value = NULL;
+  if ((N = get_argument (argc, argv, "-key"))) {
+    remove_argument (N, &argc, argv);
+    Key = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+    Value = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  if ((argc != 3) && (argc != 2)) {
+    gprint (GP_ERR, "USAGE: book delpage (book) (page)\n");
+    gprint (GP_ERR, "USAGE: book delpage (book) -key name value\n");
+    FREE (Key);
+    FREE (Value);
+    return FALSE;
+  }
+
+  book = FindBook (argv[1]);
+  if (book == NULL) {
+    gprint (GP_ERR, "book %s not found\n", argv[1]);
+    FREE (Key);
+    FREE (Value);
+    return FALSE;
+  }
+  
+  if (Key) {
+    /* delete by matching key */
+    for (i = 0; i < book[0].Npages; i++) {
+      value = BookGetWord (book[0].pages[i], Key);
+      if (value == NULL) continue;
+      if (!strcmp(value, Value)) {
+	DeletePage (book, book[0].pages[i]);
+	i--; /* if we delete this page, don't advance the counter */
+      }
+    }
+    FREE (Key);
+    FREE (Value);
+    return TRUE;
+  }
+
+  page = FindPage (book, argv[2]);
+  if (page == NULL) {
+    gprint (GP_ERR, "page %s in book %s not found\n", argv[2], argv[1]);
+    FREE (Key);
+    FREE (Value);
+    return FALSE;
+  }
+
+  DeletePage (book, page);
+  FREE (Key);
+  FREE (Value);
+  return TRUE;
+}
+
+int book_listpage (int argc, char **argv) {
+
+  Page *page;
+  Book *book;
+
+  if (argc != 3) {
+    gprint (GP_ERR, "USAGE: book listpage (book) (page)\n");
+    return FALSE;
+  }
+
+  book = FindBook (argv[1]);
+  if (book == NULL) {
+    gprint (GP_ERR, "book %s not found\n", argv[1]);
+    return FALSE;
+  }
+  
+  page = FindPage (book, argv[2]);
+  if (page == NULL) {
+    gprint (GP_ERR, "page %s in book %s not found\n", argv[2], argv[1]);
+    return FALSE;
+  }
+
+  ListWords (page);
+  return TRUE;
+}
+
+int book_getpage (int argc, char **argv) {
+
+  int where, N;
+  char *varName, *keyName, *keyValue;
+  Book *book;
+  Page *page;
+
+  varName = NULL;
+  if ((N = get_argument (argc, argv, "-var"))) {
+    remove_argument (N, &argc, argv);
+    varName = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  keyValue = keyName = NULL;
+  if ((N = get_argument (argc, argv, "-key"))) {
+    remove_argument (N, &argc, argv);
+    keyName = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+    keyValue = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  if (argc != 3) {
+    gprint (GP_ERR, "USAGE: book getpage (book) (where) [-var var] [-key key value]\n");
+    FREE (varName);
+    FREE (keyName);
+    FREE (keyValue);
+    return FALSE;
+  }
+
+  where = atoi (argv[2]);
+
+  book = FindBook (argv[1]);
+  if (book == NULL) {
+    if (varName) {
+      set_str_variable (varName, "NULL");
+      FREE (varName);
+      FREE (keyName);
+      FREE (keyValue);
+      return TRUE;
+    } 
+    gprint (GP_ERR, "book %s not found\n", argv[1]);
+    FREE (varName);
+    FREE (keyName);
+    FREE (keyValue);
+    return FALSE;
+  }
+
+  if (keyName == NULL) {
+    page = GetPage (book, where);
+  } else {
+    page = GetPageRestricted (book, where, keyName, keyValue);
+  }
+
+  if (page == NULL) {
+    if (varName) {
+      set_str_variable (varName, "NULL");
+      FREE (varName);
+      FREE (keyName);
+      FREE (keyValue);
+      return TRUE;
+    } 
+    gprint (GP_ERR, "page %d not found in %s\n", where, argv[1]);
+    FREE (varName);
+    FREE (keyName);
+    FREE (keyValue);
+    return FALSE;
+  }
+
+  if (varName) {
+    set_str_variable (varName, page[0].name);
+  } else {
+    gprint (GP_LOG, "%s\n", page[0].name);
+  }
+  FREE (varName);
+  FREE (keyName);
+  FREE (keyValue);
+  return TRUE;
+}
+
+int book_shuffle (int argc, char **argv) {
+
+  Book *book;
+
+  if (argc != 2) {
+    gprint (GP_ERR, "USAGE: book shuffle (book)\n");
+    return FALSE;
+  }
+
+  book = FindBook (argv[1]);
+  if (book == NULL) {
+    gprint (GP_ERR, "book %s not found\n", argv[1]);
+    return FALSE;
+  }
+
+  ShufflePages (book);
+  return TRUE;
+}
+
+int book_setword (int argc, char **argv) {
+
+  Page *page;
+  Book *book;
+
+  if (argc != 5) {
+    gprint (GP_ERR, "USAGE: book setword (book) (page) (word) (value)\n");
+    return FALSE;
+  }
+
+  book = FindBook (argv[1]);
+  if (book == NULL) {
+    gprint (GP_ERR, "book %s not found\n", argv[1]);
+    return FALSE;
+  }
+  
+  page = FindPage (book, argv[2]);
+  if (page == NULL) {
+    gprint (GP_ERR, "page %s in book %s not found\n", argv[2], argv[1]);
+    return FALSE;
+  }
+
+  BookSetWord (page, argv[3], argv[4]);
+  return TRUE;
+}
+
+int book_getword (int argc, char **argv) {
+
+  int N;
+  Page *page;
+  Book *book;
+  char *value, *varName;
+
+  varName = NULL;
+  if ((N = get_argument (argc, argv, "-var"))) {
+    remove_argument (N, &argc, argv);
+    varName = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  if (argc != 4) {
+    gprint (GP_ERR, "USAGE: book getword (book) (page) (word)\n");
+    FREE (varName);
+    return FALSE;
+  }
+
+  book = FindBook (argv[1]);
+  if (book == NULL) {
+    if (varName) {
+      set_str_variable (varName, "NULL");
+      FREE (varName);
+      return TRUE;
+    } 
+    gprint (GP_ERR, "book %s not found\n", argv[1]);
+    FREE (varName);
+    return FALSE;
+  }
+  
+  page = FindPage (book, argv[2]);
+  if (page == NULL) {
+    if (varName) {
+      set_str_variable (varName, "NULL");
+      FREE (varName);
+      return TRUE;
+    } 
+    gprint (GP_ERR, "page %s in book %s not found\n", argv[2], argv[1]);
+    FREE (varName);
+    return FALSE;
+  }
+
+  value = BookGetWord (page, argv[3]);
+  if (value == NULL) {
+    if (varName) {
+      set_str_variable (varName, "NULL");
+      FREE (varName);
+      return TRUE;
+    }
+    gprint (GP_ERR, "value %s on page %s in book %s not found\n", argv[3], argv[2], argv[1]);
+    FREE (varName);
+    return FALSE;
+  }
+
+  if (varName) {
+    set_str_variable (varName, value);
+  } else {
+    gprint (GP_LOG, "%s\n", value);
+  }
+    
+  FREE (varName);
+  return TRUE;
+}
Index: /branches/sc_branches/pantasks_condor/cmd.data/box.c
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.data/box.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.data/box.c	(revision 34783)
@@ -0,0 +1,184 @@
+# include "data.h"
+
+int box (int argc, char **argv) {
+  
+  int i, N, kapa, size;
+  char *name, fontname[64];
+  Graphdata graphmode;
+  
+  name = NULL;
+  if ((N = get_argument (argc, argv, "-n"))) {
+    remove_argument (N, &argc, argv);
+    name = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+  if (!GetGraph (&graphmode, &kapa, name)) return (FALSE);
+  FREE (name);
+
+  if ((N = get_argument (argc, argv, "-fn"))) {
+    remove_argument (N, &argc, argv);
+    strcpy (fontname, argv[N]);
+    remove_argument (N, &argc, argv);
+    size = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+    KapaSetFont (kapa, fontname, size);
+  } 
+
+  // graphmode.lweight = 1;
+  if ((N = get_argument (argc, argv, "-lw"))) {
+    remove_argument (N, &argc, argv);
+    graphmode.lweight = atof(argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  // graphmode.color = KapaColorByName ("black");
+  if ((N = get_argument (argc, argv, "-c"))) {
+    remove_argument (N, &argc, argv);
+    graphmode.color = KapaColorByName (argv[N]);
+    if (graphmode.color == -1) return (FALSE);
+    remove_argument (N, &argc, argv);
+  }
+
+  // XXX need to get the current values from kapa
+  // strcpy (graphmode.ticks, "2222");
+  if ((N = get_argument (argc, argv, "-ticks"))) {
+    remove_argument (N, &argc, argv);
+    strcpy (graphmode.ticks, argv[N]);
+    remove_argument (N, &argc, argv);
+    if (strlen (graphmode.ticks) != 4) { goto usage; }
+    for (i = 0; i < strlen (graphmode.ticks); i++) {
+      if ((graphmode.ticks[i] != '0') && (graphmode.ticks[i] != '1') && (graphmode.ticks[i] != '2')) { goto usage; }
+    }
+  }
+  
+  // strcpy (graphmode.labels, "2222");
+  if ((N = get_argument (argc, argv, "-labels"))) {
+    remove_argument (N, &argc, argv);
+    strcpy (graphmode.labels, argv[N]);
+    remove_argument (N, &argc, argv);
+    if (strlen (graphmode.labels) != 4) { goto usage; }
+    for (i = 0; i < strlen (graphmode.labels); i++) {
+      if ((graphmode.labels[i] != '0') && (graphmode.labels[i] != '1') && (graphmode.labels[i] != '2')) { goto usage; }
+    }
+  }
+
+  // strcpy (graphmode.axis, "2222");
+  if ((N = get_argument (argc, argv, "-axis"))) {
+    remove_argument (N, &argc, argv);
+    strcpy (graphmode.axis, argv[N]);
+    remove_argument (N, &argc, argv);
+    if (strlen (graphmode.axis) != 4) { goto usage; }
+    for (i = 0; i < strlen (graphmode.axis); i++) {
+      if ((graphmode.axis[i] != '0') && (graphmode.axis[i] != '1') && (graphmode.axis[i] != '2')) { goto usage; }
+    }
+  }
+
+  if ((N = get_argument (argc, argv, "-tickpad"))) {
+    remove_argument (N, &argc, argv);
+    graphmode.ticktextPad = atof(argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  if ((N = get_argument (argc, argv, "-labelpadx"))) {
+    remove_argument (N, &argc, argv);
+    graphmode.labelPadXm = atof(argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  if ((N = get_argument (argc, argv, "-labelpady"))) {
+    remove_argument (N, &argc, argv);
+    graphmode.labelPadYm = atof(argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  if ((N = get_argument (argc, argv, "+labelpadx"))) {
+    remove_argument (N, &argc, argv);
+    graphmode.labelPadXp = atof(argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  if ((N = get_argument (argc, argv, "+labelpady"))) {
+    remove_argument (N, &argc, argv);
+    graphmode.labelPadYp = atof(argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  if ((N = get_argument (argc, argv, "-labelpad"))) {
+    remove_argument (N, &argc, argv);
+    graphmode.labelPadXm = atof(argv[N]);
+    graphmode.labelPadXp = atof(argv[N]);
+    graphmode.labelPadYm = atof(argv[N]);
+    graphmode.labelPadYp = atof(argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  if ((N = get_argument (argc, argv, "-pad"))) {
+    remove_argument (N, &argc, argv);
+    graphmode.padXm = atof(argv[N]);
+    graphmode.padXp = atof(argv[N]);
+    graphmode.padYm = atof(argv[N]);
+    graphmode.padYp = atof(argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+  if ((N = get_argument (argc, argv, "-xpad"))) {
+    remove_argument (N, &argc, argv);
+    graphmode.padXm = atof(argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+  if ((N = get_argument (argc, argv, "+xpad"))) {
+    remove_argument (N, &argc, argv);
+    graphmode.padXp = atof(argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+  if ((N = get_argument (argc, argv, "-ypad"))) {
+    remove_argument (N, &argc, argv);
+    graphmode.padYm = atof(argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+  if ((N = get_argument (argc, argv, "+ypad"))) {
+    remove_argument (N, &argc, argv);
+    graphmode.padYp = atof(argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  if (argc != 1) goto usage;
+
+  KapaBox (kapa, &graphmode);
+  return (TRUE);
+      
+ usage:
+  gprint (GP_ERR, "USAGE: box [-ticks NNNN] [-axis NNNN] [-labels NNNN]\n");
+  return (FALSE);
+}
+
+
+/* box has:
+   axis
+   labels
+   ticks
+
+   assign like this:   
+   -axis 0000
+   -labels 0000
+   -ticks 0000
+
+   default:
+   -axis 1111
+   -labels 1100
+   -ticks 1111
+
+   messages to kapa:
+
+   DBOX
+   (xmin) (xmax) (ymin) (ymax)
+   AAAA LLLL TTTT
+
+   A = axis
+   L = label
+   T = ticks
+   
+   0 = off
+   1 = on
+   2 = default / maintain
+
+*/
Index: /branches/sc_branches/pantasks_condor/cmd.data/center.c
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.data/center.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.data/center.c	(revision 34783)
@@ -0,0 +1,60 @@
+# include "data.h"
+
+int center (int argc, char **argv) {
+  
+  double x, y;
+  int zoom;
+  int kapa, N;
+  char *name;
+  
+  name = NULL;
+  if ((N = get_argument (argc, argv, "-n"))) {
+    remove_argument (N, &argc, argv);
+    name = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+  if (!GetImage (NULL, &kapa, name)) return (FALSE);
+  FREE (name);
+
+  // XXX need an option to center the image based on the current plot limits
+
+  if ((argc != 3) && (argc != 4)) {
+    gprint (GP_ERR, "USAGE: center x y [zoom]\n");
+    return (FALSE);
+  }
+
+  x = atof (argv[1]);
+  y = atof (argv[2]);
+  zoom = 0;
+  if (argc == 4) zoom = atof (argv[3]);
+
+  KiiCenter (kapa, x, y, zoom);
+  return (TRUE);
+}
+
+int parity (int argc, char **argv) {
+  
+  int x, y;
+  int kapa, N;
+  char *name;
+  
+  name = NULL;
+  if ((N = get_argument (argc, argv, "-n"))) {
+    remove_argument (N, &argc, argv);
+    name = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+  if (!GetImage (NULL, &kapa, name)) return (FALSE);
+  FREE (name);
+
+  if (argc != 3) {
+    gprint (GP_ERR, "USAGE: parity x y\n");
+    return (FALSE);
+  }
+
+  x = atof (argv[1]);
+  y = atof (argv[2]);
+
+  KiiParity (kapa, x, y);
+  return (TRUE);
+}
Index: /branches/sc_branches/pantasks_condor/cmd.data/clear.c
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.data/clear.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.data/clear.c	(revision 34783)
@@ -0,0 +1,48 @@
+# include "data.h"
+
+// default is to clear all plots, but not the sections or the images
+int clear (int argc, char **argv) {
+  
+  int N;
+  int kapa;
+  char *name;
+
+  name = NULL;
+  if ((N = get_argument (argc, argv, "-n"))) {
+    remove_argument (N, &argc, argv);
+    name = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+  if (!GetGraph (NULL, &kapa, name)) return (FALSE);
+  FREE (name);
+
+  // clear all sections
+  if ((N = get_argument (argc, argv, "-s")) || 
+      (N = get_argument (argc, argv, "-section"))) {
+      KapaClearSections (kapa);
+      return (TRUE);
+  }
+
+  // clear all sections
+  if ((N = get_argument (argc, argv, "-graph"))) {
+      KapaClearCurrentPlot (kapa);
+      return (TRUE);
+  }
+
+  // clear image
+  if ((N = get_argument (argc, argv, "-image"))) {
+      KapaClearImage (kapa);
+      return (TRUE);
+  }
+
+  if (argc != 1) {
+    gprint (GP_ERR, "USAGE: clear [-n Xgraph] [-s|-section] [-image] [-graph]\n");
+    gprint (GP_ERR, "       [-s|-section] : clear all sections\n");
+    gprint (GP_ERR, "       [-graph]      : clear current graph\n");
+    gprint (GP_ERR, "       [-image]      : clear current image\n");
+    return (FALSE);
+  }
+
+  KapaClearPlots (kapa);
+  return (TRUE);
+}
Index: /branches/sc_branches/pantasks_condor/cmd.data/close.c
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.data/close.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.data/close.c	(revision 34783)
@@ -0,0 +1,22 @@
+# include "data.h"
+
+int close_device (int argc, char **argv) {
+
+  int N, kapa;
+  char *name;
+  /* close current graphics device */
+
+  name = NULL;
+  if ((N = get_argument (argc, argv, "-n"))) {
+    remove_argument (N, &argc, argv);
+    name = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  if (!GetGraph (NULL, &kapa, name)) return (FALSE);
+
+  close_kapa (name); 
+  FREE (name);
+
+  return (TRUE);
+}
Index: /branches/sc_branches/pantasks_condor/cmd.data/concat.c
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.data/concat.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.data/concat.c	(revision 34783)
@@ -0,0 +1,70 @@
+# include "data.h"
+
+int concat (int argc, char **argv) {
+
+  int  i, j, Nin;
+  double value;
+  opihi_flt *temp;
+  Vector *ivec, *ovec;
+
+  /** check basic syntax **/
+  if (argc != 3) {
+    gprint (GP_ERR, "SYNTAX: concat (vector/value) vector\n");
+    gprint (GP_ERR, "  concatanate (vector/value) to vector\n");
+    return (FALSE);
+  }
+
+  if ((ovec = SelectVector (argv[2], ANYVECTOR, TRUE)) == NULL) return (FALSE);
+
+  if (SelectScalar (argv[1], &value)) {
+    Nin = ovec[0].Nelements;
+    ovec[0].Nelements++;
+    if (ovec[0].type == OPIHI_FLT) {
+      REALLOCATE (ovec[0].elements.Flt, opihi_flt, ovec[0].Nelements);
+      ovec[0].elements.Flt[Nin] = value;
+    } else {
+      REALLOCATE (ovec[0].elements.Int, opihi_int, ovec[0].Nelements);
+      ovec[0].elements.Int[Nin] = value;
+    }
+    return (TRUE);
+  } 
+  
+  if ((ivec = SelectVector (argv[1], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+  
+  // we have 4 cases: (ivec == flt or int) and (ovec == flt or int)
+  if ((ovec[0].type == OPIHI_FLT) && (ivec[0].type == OPIHI_FLT)) {
+    REALLOCATE (ovec[0].elements.Flt, opihi_flt, ovec[0].Nelements + ivec[0].Nelements);
+    for (j = ovec[0].Nelements, i = 0; i < ivec[0].Nelements; i++, j++) {
+      ovec[0].elements.Flt[j] = ivec[0].elements.Flt[i];
+    }
+  }
+  if ((ovec[0].type == OPIHI_FLT) && (ivec[0].type == OPIHI_INT)) {
+    REALLOCATE (ovec[0].elements.Flt, opihi_flt, ovec[0].Nelements + ivec[0].Nelements);
+    for (j = ovec[0].Nelements, i = 0; i < ivec[0].Nelements; i++, j++) {
+      ovec[0].elements.Flt[j] = ivec[0].elements.Int[i];
+    }
+  }
+  if ((ovec[0].type == OPIHI_INT) && (ivec[0].type == OPIHI_INT)) {
+    REALLOCATE (ovec[0].elements.Int, opihi_int, ovec[0].Nelements + ivec[0].Nelements);
+    for (j = ovec[0].Nelements, i = 0; i < ivec[0].Nelements; i++, j++) {
+      ovec[0].elements.Int[j] = ivec[0].elements.Int[i];
+    }
+  }
+
+  // this case forces ovec to be raised to FLT
+  if ((ovec[0].type == OPIHI_INT) && (ivec[0].type == OPIHI_FLT)) {
+    ALLOCATE (temp, opihi_flt, ovec[0].Nelements + ivec[0].Nelements);
+    for (i = 0; i < ovec[0].Nelements; i++) {
+      temp[i] = ovec[0].elements.Int[i];
+    }
+    ovec[0].type = OPIHI_FLT;
+    free (ovec[0].elements.Int);
+    ovec[0].elements.Flt = temp;
+    for (j = ovec[0].Nelements, i = 0; i < ivec[0].Nelements; i++, j++) {
+      ovec[0].elements.Flt[j] = ivec[0].elements.Flt[i];
+    }
+  }
+
+  ovec[0].Nelements += ivec[0].Nelements;
+  return (TRUE);
+}
Index: /branches/sc_branches/pantasks_condor/cmd.data/contour.c
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.data/contour.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.data/contour.c	(revision 34783)
@@ -0,0 +1,261 @@
+# include "data.h"
+# define LL { \
+ dx =  d00 / (*v01 - *v00); \
+ dy = -d00 / (*v10 - *v00); \
+ x = i - 0.5; \
+ y = j - dy - 0.5; }
+
+# define UL { \
+ tmp = d00 / (*v10 - *v00); \
+ dy = 1 - tmp; \
+ dx =  d10 / (*v11 - *v10); \
+ x = i - 0.5; \
+ y = j + tmp - 0.5; }
+      
+# define LR { \
+ tmp = d00 / (*v01 - *v00); \
+ dx = 1 - tmp; \
+ dy = d01 / (*v11 - *v01); \
+ x = i + tmp - 0.5; \
+ y = j - 0.5; }
+
+# define UR { \
+ tmp = d10 / (*v11 - *v10); \
+ dx = 1 - tmp; \
+ dy = -d11 / (*v01 - *v11); \
+ x = i + tmp - 0.5; \
+ y = j + 1 - 0.5; }
+      
+# define HZ { \
+ tmp = d00 / (*v10 - *v00); \
+ dy = d01 / (*v11 - *v01) - tmp; \
+ dx = 1; \
+ x = i - 0.5; \
+ y = j + tmp - 0.5; }
+
+# define VT { \
+ tmp = d00 / (*v01 - *v00); \
+ dx = d10 / (*v11 - *v10) - tmp; \
+ x = i + tmp - 0.5; \
+ dy = 1; \
+ y = j - 0.5; }
+
+Vector *xv, *yv;
+int N, NVEC;
+
+void DUMP (opihi_flt x, opihi_flt y, opihi_flt dx, opihi_flt dy) {
+  
+  xv[0].elements.Flt[N]   = x;
+  xv[0].elements.Flt[N+1] = x+dx;
+  yv[0].elements.Flt[N]   = y;
+  yv[0].elements.Flt[N+1] = y+dy;
+  
+  N+=2;
+
+  if (N >= NVEC - 2) {
+    NVEC += 100;
+    REALLOCATE (xv[0].elements.Flt, opihi_flt, NVEC);
+    REALLOCATE (yv[0].elements.Flt, opihi_flt, NVEC);
+  }
+}
+
+int contour (int argc, char **argv) {
+
+  int i, j, Nx, Ny;
+  opihi_flt x, y, dx, dy;
+  float level, d00, d01, d10, d11, tmp;
+  float *v00, *v01, *v10, *v11;
+  float *matrix;
+  Buffer *buf;
+  
+  if (argc != 5) {
+    gprint (GP_ERR, "USAGE: contour <buffer> X Y level\n");
+    return (FALSE);
+  }
+
+  if ((buf = SelectBuffer (argv[1], OLDBUFFER, TRUE)) == NULL) return (FALSE);
+  if ((xv = SelectVector (argv[2], ANYVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((yv = SelectVector (argv[3], ANYVECTOR, TRUE)) == NULL) return (FALSE);
+
+  level = atof (argv[4]);
+  matrix = (float *)(buf[0].matrix.buffer);
+  Nx = buf[0].matrix.Naxis[0];
+  Ny = buf[0].matrix.Naxis[1];
+
+  v00 = matrix;
+  v01 = matrix + 1;
+  v10 = matrix + Nx;
+  v11 = matrix + Nx + 1;
+  d01 = (level - *v00);
+  d11 = (level - *v10);
+
+  N = 0;
+  NVEC = 100;
+  ResetVector (xv, OPIHI_FLT, NVEC);
+  ResetVector (yv, OPIHI_FLT, NVEC);
+
+  for (j = 1; j < Ny; j++) {
+    if (!(j%10)) gprint (GP_ERR, ".");
+    for (i = 1; i < Nx; i++, v00++, v01++, v10++, v11++) {
+
+      d00 = d01;
+      d10 = d11;
+      d01 = (level - *v01);
+      d11 = (level - *v11);
+
+      if (((d00 > 0) && (d01 > 0) && (d10 > 0) && (d11 > 0)) ||
+	  ((d00 < 0) && (d01 < 0) && (d10 < 0) && (d11 < 0)))
+	continue;
+
+      if ((d00 > 0) && (d10 <= 0)) { 
+	if ((d01 <= 0) && (d11 <= 0)) { /* \  */
+	  LL;
+	  DUMP (x,y,dx,dy);
+	  continue;
+	}
+	if ((d01 > 0) && (d11 <= 0)) { /* -  */
+	  HZ;
+	  DUMP (x,y,dx,dy);
+	  continue;
+	}
+	if ((d01 > 0) && (d11 > 0)) { /* /  */
+	  UL;
+	  DUMP (x,y,dx,dy);
+	  continue;
+	}
+	if ((d01 <= 0) && (d11 > 0)) { /* \\  */
+	  LL;
+	  DUMP (x,y,dx,dy);
+	  UR;
+	  DUMP (x,y,dx,dy);
+	  continue;
+	}
+      }
+
+      if ((d00 <= 0) && (d10 > 0)) {
+	if ((d01 > 0) && (d11 > 0)) { /* \  */
+	  LL;
+	  DUMP (x,y,dx,dy);
+	  continue;
+	}
+	if ((d01 <= 0) && (d11 > 0)) { /* -  */
+	  HZ;
+	  DUMP (x,y,dx,dy);
+	  continue;
+	}
+	if ((d01 <= 0) && (d11 <= 0)) { /* /  */
+	  UL;
+	  DUMP (x,y,dx,dy);
+	  continue;
+	}
+	if ((d01 > 0) && (d11 <= 0)) { /* //  */
+	  UL;
+	  DUMP (x,y,dx,dy);
+	  LR;
+	  DUMP (x,y,dx,dy);
+	  continue;
+	}
+      }
+      
+
+      if ((d00 <= 0) && (d10 <= 0)) { 
+	if ((d01 > 0) && (d11 <= 0)) {
+	  LR;
+	  DUMP (x,y,dx,dy);
+	  continue;
+	}
+	if ((d01 <= 0) && (d11 > 0)) {
+	  UR;
+	  DUMP (x,y,dx,dy);
+	  continue;
+	}
+	if ((d01 > 0) && (d11 > 0)) {
+	  VT;
+	  DUMP (x,y,dx,dy);
+	  continue;
+	}
+      }
+
+      if ((d00 > 0) && (d10 > 0)) { 
+	if ((d01 <= 0) && (d11 > 0)) {
+	  LR;
+	  DUMP (x,y,dx,dy);
+	  continue;
+	}
+	if ((d01 > 0) && (d11 <= 0)) {
+	  UR;
+	  DUMP (x,y,dx,dy);
+	  continue;
+	}
+	if ((d01 <= 0) && (d11 <= 0)) {
+	  VT;
+	  DUMP (x,y,dx,dy);
+	  continue;
+	}
+      }
+
+    }
+    /* skip left-hand edge */
+    v00++; v01++; v10++; v11++;
+    d01 = (level - *v00);
+    d11 = (level - *v10);
+  }
+
+  /****** bottom line *******/
+  v00 = matrix;  
+  v01 = matrix + 1;  
+  y = 0;
+  dx = 0;
+  dy = -0.5;
+  for (i = 1; i < Nx; i++, v00++, v01++) { /* do the edges */
+    if (((*v00 > level) && (*v01 <= level)) || ((*v00 <= level) && (*v01 > level))) {
+      x = i + (level - *v01)/(*v01 - *v00);
+      DUMP (x,y,dx,dy);
+    }
+  }
+
+  /********** top line *******/
+  v00 = matrix + Nx*(Ny - 1);  
+  v01 = v00 + 1;
+  y = Ny - 1;
+  dx = 0;
+  dy = 0.5;
+  for (i = 1; i < Nx; i++, v00++, v01++) { /* do the edges */
+    if (((*v00 > level) && (*v01 <= level)) || ((*v00 <= level) && (*v01 > level))) {
+      x = i + (level - *v01)/(*v01 - *v00);
+      DUMP (x,y,dx,dy);
+    }
+  }
+
+  /******** left line *********/
+  v00 = matrix; 
+  v01 = matrix + Nx;
+  x = 0;
+  dx = -0.5;
+  dy = 0;
+  for (j = 1; j < Ny; j++, v00+=Nx, v01+=Nx) { /* do the edges */
+    if (((*v00 > level) && (*v01 <= level)) || ((*v00 <= level) && (*v01 > level))) {
+      y = j + (level - *v01)/(*v01 - *v00);
+      DUMP (x,y,dx,dy);
+    }
+  }
+
+  /******** right line *********/
+  v00 = matrix + Nx - 1; 
+  v01 = v00 + Nx;
+  x = Nx - 1;
+  dx = 0.5;
+  dy = 0;
+  for (j = 1; j < Ny; j++, v00+=Nx, v01+=Nx) { /* do the edges */
+    if (((*v00 > level) && (*v01 <= level)) || ((*v00 <= level) && (*v01 > level))) {
+      y = j + (level - *v01)/(*v01 - *v00);
+      DUMP (x,y,dx,dy);
+    }
+  }
+  
+/* free anything? */
+
+  xv[0].Nelements = N;
+  yv[0].Nelements = N;
+  return (TRUE);
+}
Index: /branches/sc_branches/pantasks_condor/cmd.data/create.c
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.data/create.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.data/create.c	(revision 34783)
@@ -0,0 +1,59 @@
+# include "data.h"
+
+int create (int argc, char **argv) {
+  
+  int i, N, INT;
+  opihi_flt start, end, delta;
+  Vector *vec;
+  
+  INT = FALSE;
+  if ((N = get_argument (argc, argv, "-int"))) {
+    INT = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+
+  if ((argc != 5) && (argc != 4)) {
+    gprint (GP_ERR, "USAGE: create vector start end [delta] [-int]\n");
+    gprint (GP_ERR, " -int : resulting vector is integer type (delta must be integer)\n");
+    return (FALSE);
+  }
+
+  if ((vec = SelectVector (argv[1], ANYVECTOR, TRUE)) == NULL) return (FALSE);
+
+  delta = 1;
+  start = atof (argv[2]);
+  end   = atof (argv[3]);
+  if (argc == 5) delta = atof (argv[4]);
+
+  if ((start == end) || (delta == 0)) {
+    gprint (GP_ERR, "error in value: %f to %f, %f\n", start, end, delta);
+    return (FALSE);
+  }
+  delta = fabs (delta);
+  if (end - start < 0) {
+    delta = -1.0 * delta;
+  }
+
+  if (INT && (delta != (int)delta)) {
+    gprint (GP_ERR, "integer vector requested but fractional step-size specified\n");
+    return (FALSE);
+  }
+
+  vec[0].Nelements = (end - start) / delta;
+
+  if (INT) {
+    vec[0].type = OPIHI_INT;
+    REALLOCATE (vec[0].elements.Int, opihi_int, vec[0].Nelements);
+    for (i = 0; i < vec[0].Nelements; i++) {
+      vec[0].elements.Int[i] = start + i*delta;
+    }
+  } else {
+    vec[0].type = OPIHI_FLT;
+    REALLOCATE (vec[0].elements.Flt, opihi_flt, vec[0].Nelements);
+    for (i = 0; i < vec[0].Nelements; i++) {
+      vec[0].elements.Flt[i] = start + i*delta;
+    }
+  }
+
+  return (TRUE);
+}
Index: /branches/sc_branches/pantasks_condor/cmd.data/cumulative.c
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.data/cumulative.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.data/cumulative.c	(revision 34783)
@@ -0,0 +1,40 @@
+# include "data.h"
+
+int cumulative (int argc, char **argv) {
+  
+  int i;
+  opihi_flt *Vo;
+  Vector *ivec, *ovec;
+
+  if (argc != 3) {
+    gprint (GP_ERR, "USAGE: cumulative invec outvec\n");
+    return (FALSE);
+  }
+
+  if ((ivec = SelectVector (argv[1], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((ovec = SelectVector (argv[2], ANYVECTOR, TRUE)) == NULL) return (FALSE);
+
+  ResetVector (ovec, OPIHI_FLT, ivec[0].Nelements);
+  bzero (ovec[0].elements.Flt, sizeof(opihi_flt)*ovec[0].Nelements);
+
+  Vo = ovec[0].elements.Flt;
+
+  if (ivec[0].type == OPIHI_FLT) {
+    opihi_flt *Vi = ivec[0].elements.Flt;
+    *Vo = *Vi;
+    Vi++; 
+    Vo++;
+    for (i = 1; i < ivec[0].Nelements; i++, Vi++, Vo++) {
+      *Vo = Vo[-1] + *Vi;
+    }      
+  } else {
+    opihi_int *Vi = ivec[0].elements.Int;
+    *Vo = *Vi;
+    Vi++;
+    Vo++;
+    for (i = 1; i < ivec[0].Nelements; i++, Vi++, Vo++) {
+      *Vo = Vo[-1] + *Vi;
+    }      
+  }
+  return (TRUE);
+}
Index: /branches/sc_branches/pantasks_condor/cmd.data/cursor.c
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.data/cursor.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.data/cursor.c	(revision 34783)
@@ -0,0 +1,68 @@
+# include "data.h"
+
+int cursor (int argc, char **argv) {
+
+  char string[20], key[20], *name;
+  int i, N, kapa, VERBOSE;
+  double X, Y, R, D, Z;
+  void *oldsignal;
+
+  // XXX need to be able to specify graph vs image coords
+  // currently, if only one exists, that frame will be used
+  // if both exist, defaults to ??
+  // if ((N = get_argument (argc, argv, "-g"))) {
+  // if ((N = get_argument (argc, argv, "-i"))) {
+
+  VERBOSE = TRUE;
+  if ((N = get_argument (argc, argv, "-a"))) {
+    VERBOSE = FALSE;
+  }
+
+  name = NULL;
+  if ((N = get_argument (argc, argv, "-n"))) {
+    remove_argument (N, &argc, argv);
+    name = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+  if (!GetGraphdata (NULL, &kapa, name)) return (FALSE);
+  FREE (name);
+
+  N = 0;
+  if (argc == 2) N = atof (argv[1]);
+
+  if ((argc != 1) && (argc != 2)) {
+    gprint (GP_ERR, "USAGE: cursor [Npts] [-n window] [-g | -i]\n");
+    return (FALSE);
+  }
+  
+  KiiCursorOn (kapa);
+  
+  oldsignal = signal (SIGINT, handle_interrupt);
+  interrupt = FALSE;
+
+  for (i = 0; ((i < N) || (N == 0)) && !interrupt; i++) {
+
+    KiiCursorRead (kapa, &X, &Y, &Z, &R, &D, key);
+
+    sprintf (string, "X%s", key);
+    set_variable (string, X);
+    sprintf (string, "Y%s", key);
+    set_variable (string, Y);
+    sprintf (string, "Z%s", key);
+    set_variable (string, Z);
+    sprintf (string, "R%s", key);
+    set_variable (string, R);
+    sprintf (string, "D%s", key);
+    set_variable (string, D);
+
+    set_str_variable ("KEY", key);
+    
+    if (VERBOSE) gprint (GP_LOG, "%s %f %f %f %f %f\n", key, X, Y, Z, R, D);
+
+    if (!strcasecmp (key, "Q")) break;
+  }
+
+  signal (SIGINT, oldsignal);
+  KiiCursorOff (kapa);
+  return (TRUE);
+}
Index: /branches/sc_branches/pantasks_condor/cmd.data/cut.c
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.data/cut.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.data/cut.c	(revision 34783)
@@ -0,0 +1,120 @@
+# include "data.h"
+
+enum {SUM, MEAN, MEDIAN};
+
+int cut (int argc, char **argv) {
+  
+  int i, j, N, Nx, Ny, Mode;
+  float *Vin, *Vbuf, value;
+  int sx, sy, nx, ny;
+  Vector *xvec, *yvec;
+  Buffer *buf;
+
+  Mode = SUM;
+  if ((N = get_argument (argc, argv, "-median"))) {
+    remove_argument (N, &argc, argv);
+    Mode = MEDIAN;
+  }
+  if ((N = get_argument (argc, argv, "-mean"))) {
+    remove_argument (N, &argc, argv);
+    Mode = MEAN;
+  }
+
+  if (argc != 9) {
+    gprint (GP_ERR, "USAGE: cut <buffer> <X vector> <Y vector> <X|Y> sx sy nx ny\n");
+    return (FALSE);
+  }
+
+  if ((buf = SelectBuffer (argv[1], OLDBUFFER, TRUE)) == NULL) return (FALSE);
+ 
+  sx = atof (argv[5]);
+  sy = atof (argv[6]);
+  nx = atof (argv[7]);
+  ny = atof (argv[8]);
+
+  Nx = buf[0].matrix.Naxis[0];
+  Ny = buf[0].matrix.Naxis[1];
+
+  if ((sx < 0) || (sy < 0) || (sx+nx > Nx) || (sy+ny > Ny)) {
+    gprint (GP_ERR, "region out of range\n");
+    return (FALSE);
+  }
+
+  if ((xvec = SelectVector (argv[2], ANYVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((yvec = SelectVector (argv[3], ANYVECTOR, TRUE)) == NULL) return (FALSE);
+
+  switch (argv[4][0]) {
+  case 'x':
+  case 'X':
+    /* create output vectors */
+    ResetVector (xvec, OPIHI_FLT, nx);
+    ResetVector (yvec, OPIHI_FLT, nx);
+    bzero (yvec[0].elements.Flt, nx*sizeof(opihi_flt));
+    for (i = 0; i < nx; i++) {
+      xvec[0].elements.Flt[i] = i + sx; 
+    }
+    ALLOCATE (Vbuf, float, MAX (ny, 1));
+
+    for (i = 0; i < nx; i++) {
+      /* accumulate values */
+      Vin = (float *)(buf[0].matrix.buffer) + sy*Nx + sx + i; 
+      for (j = 0; j < ny; j++, Vin += Nx) {
+	Vbuf[j] = *Vin;
+      }
+      /* apply stat of choice */
+      if (Mode == MEDIAN) {
+	fsort (Vbuf, ny);
+	value = Vbuf[(int)(0.5*ny)];
+      } else {
+	value = 0;
+	for (j = 0; j < ny; j++) {
+	  value += Vbuf[j];
+	}
+	if (Mode == MEAN) { value /= ny; }
+      }
+      yvec[0].elements.Flt[i] = value;
+    }
+    free (Vbuf);
+    break;
+    
+  case 'y':
+  case 'Y':
+    ResetVector (xvec, OPIHI_FLT, ny);
+    ResetVector (yvec, OPIHI_FLT, ny);
+    bzero (yvec[0].elements.Flt, ny*sizeof(opihi_flt));
+    for (i = 0; i < ny; i++) {
+      xvec[0].elements.Flt[i] = i + sy; 
+    }
+    ALLOCATE (Vbuf, float, MAX (nx, 1));
+
+    for (i = 0; i < ny; i++) {
+      /* accumulate values */
+      Vin = (float *)(buf[0].matrix.buffer) + (sy + i)*Nx + sx; 
+      for (j = 0; j < nx; j++, Vin ++) {
+	Vbuf[j] = *Vin;
+      }
+      /* apply stat of choice */
+      if (Mode == MEDIAN) {
+	fsort (Vbuf, nx);
+	value = Vbuf[(int)(0.5*nx)];
+      } else {
+	value = 0;
+	for (j = 0; j < nx; j++) {
+	  value += Vbuf[j];
+	}
+	if (Mode == MEAN) { value /= nx; }
+      }
+      yvec[0].elements.Flt[i] = value;
+    }
+    free (Vbuf);
+    break;
+
+  default:
+    gprint (GP_ERR, "<dir> can be X or Y\n");
+    return (FALSE);
+    break;
+  }
+
+  return (TRUE);
+}
+
Index: /branches/sc_branches/pantasks_condor/cmd.data/dbconnect.c
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.data/dbconnect.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.data/dbconnect.c	(revision 34783)
@@ -0,0 +1,115 @@
+# include "data.h"
+# if (HAVE_MYSQL_H)
+# include "mysql.h"
+
+MYSQL mysql;
+MYSQL *connection = NULL;
+
+int dbconnect (int argc, char **argv) {
+  
+  char query[256];
+  char password[1024];
+  MYSQL_RES *result;
+
+  if (argc != 4) {
+    gprint (GP_ERR, "USAGE: dbconnect (hostname) (username) (database)\n");
+    return FALSE;
+  }
+
+  fprintf (stdout, "Enter password: ");
+  scan_line (stdin, password);
+
+# if (0)
+  int i;
+  char c;
+
+  initscr();
+  noecho();
+  i = 0;
+  while (((c = getch()) != EOF) && (c != '\n')) {
+    password[i] = c;
+    i++;
+  }
+  password[i] = 0;
+# endif
+
+  // XXX do I need to call mysql_library_init()?
+
+  mysql_init (&mysql);
+  connection = mysql_real_connect (&mysql, argv[1], argv[2], password, argv[3], 0, 0, 0);
+
+  if (connection == NULL) {
+    gprint (GP_ERR, "failed to connect to database\n");
+    gprint (GP_ERR, "%s\n", mysql_error (&mysql));
+    return (FALSE);
+  }
+    
+  sprintf (query, "set @@interactive_timeout = 30000");
+  if (mysql_query (connection, query)) {
+    gprint (GP_ERR, "failed to set interactive timout\n");
+    gprint (GP_ERR, "%s\n", mysql_error (connection));
+    return (FALSE);
+  }
+  result = mysql_store_result (connection);
+  mysql_free_result (result);
+    
+  sprintf (query, "set @@wait_timeout = 30000");
+  if (mysql_query (connection, query)) {
+    gprint (GP_ERR, "failed to set wait timout\n");
+    gprint (GP_ERR, "%s\n", mysql_error (connection));
+    return (FALSE);
+  }
+  result = mysql_store_result (connection);
+  mysql_free_result (result);
+    
+# if (0)
+  int Nrows;
+  MYSQL_ROW row;
+
+  sprintf (query, "select @@interactive_timeout");
+  if (mysql_query (connection, query)) {
+    gprint (GP_ERR, "failed to get timout\n");
+    gprint (GP_ERR, "%s\n", mysql_error (connection));
+    return (FALSE);
+  }
+  result = mysql_store_result (connection);
+  Nrows = mysql_num_rows(result);
+  row = mysql_fetch_row(result);
+  fprintf (stderr, "interactive timeout: %s\n", row[0]);
+  mysql_free_result (result);
+
+  sprintf (query, "select @@wait_timeout");
+  if (mysql_query (connection, query)) {
+    gprint (GP_ERR, "failed to get timout\n");
+    gprint (GP_ERR, "%s\n", mysql_error (connection));
+    return (FALSE);
+  }
+  result = mysql_store_result (connection);
+  Nrows = mysql_num_rows(result);
+  row = mysql_fetch_row(result);
+  fprintf (stderr, "wait timeout: %s\n", row[0]);
+  mysql_free_result (result);
+# endif
+    
+  return (TRUE);
+}
+
+// XXX do I need to close the connection before opening a new one? 
+
+void *db_getConnection () {
+  return connection;
+}
+
+# else 
+
+int dbconnect (int argc, char **argv) {
+
+  gprint (GP_ERR, "mysql library is not available\n");
+  return FALSE;
+}
+
+void *db_getConnection () {
+  return NULL;
+}
+
+# endif
Index: /branches/sc_branches/pantasks_condor/cmd.data/dbselect.c
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.data/dbselect.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.data/dbselect.c	(revision 34783)
@@ -0,0 +1,143 @@
+# include "data.h"
+# if (HAVE_MYSQL_H) 
+# include "mysql.h"
+
+int dbselect (int argc, char **argv) {
+  
+  time_t seconds;
+  int i, j, Nbytes, Ncols, Nrows;
+  char *query;
+  Vector **vec;
+
+  MYSQL_RES *result;
+  MYSQL_ROW row;
+  MYSQL_FIELD *fields;
+  MYSQL *connection = NULL;
+
+  if (argc < 4) {
+    gprint (GP_ERR, "USAGE: dbselect (fields) from (table) [where]\n");
+    return FALSE;
+  }
+
+  connection = db_getConnection ();
+  if (connection == NULL) {
+    gprint (GP_ERR, "database not defined; use dbconnect\n");
+    return (FALSE);
+  }
+
+  // generate the query line (concat the argv[i] entries)
+  Nbytes = 0;
+  for (i = 1; i < argc; i++) {
+    Nbytes += strlen(argv[i]) + 1;
+  }
+  Nbytes += 10;
+
+  ALLOCATE (query, char, Nbytes);
+  bzero (query, Nbytes);
+  strcat (query, "select ");
+  for (i = 1; i < argc; i++) {
+    strcat (query, argv[i]);
+    strcat (query, " ");
+  }
+  // strcat (query, ";");
+  // fprintf (stderr, "query: %s\n", query);
+
+  if (mysql_query (connection, query)) {
+    gprint (GP_ERR, "problem with query\n");
+    gprint (GP_ERR, "%s\n", mysql_error (connection));
+    free (query);
+    return (FALSE);
+  }
+    
+  result = mysql_store_result (connection);
+
+  Nrows = mysql_num_rows(result);
+  Ncols = mysql_num_fields(result);
+  fields = mysql_fetch_fields (result);
+
+  // fprintf (stderr, "Nrows: %d\n", Nrows);
+  // fprintf (stderr, "Ncols: %d\n", Ncols);
+
+  ALLOCATE (vec, Vector *, Ncols);
+  for (i = 0; i < Ncols; i++) {
+    char *name = fields[i].name ? fields[i].name : fields[i].org_name;
+    if ((vec[i] = SelectVector (name, ANYVECTOR, TRUE)) == NULL) {
+      gprint (GP_ERR, "trouble creating vector named %s\n", name);
+      free (query);
+      free (vec);
+      return (FALSE);
+    }
+    switch (fields[i].type) {
+      case FIELD_TYPE_TINY:
+      case FIELD_TYPE_SHORT:
+      case FIELD_TYPE_LONG:
+      case FIELD_TYPE_INT24:
+      case FIELD_TYPE_LONGLONG:
+	ResetVector (vec[i], OPIHI_INT, Nrows);
+	break;
+      case FIELD_TYPE_DECIMAL:
+      case FIELD_TYPE_FLOAT:
+      case FIELD_TYPE_DOUBLE:
+      case FIELD_TYPE_TIME:
+      case FIELD_TYPE_DATE:
+      case FIELD_TYPE_DATETIME:
+	ResetVector (vec[i], OPIHI_FLT, Nrows);
+	break;
+      default:
+	ResetVector (vec[i], OPIHI_FLT, Nrows);
+	break;
+    }
+  }
+
+  for (j = 0; j < Nrows; j++) {
+    row = mysql_fetch_row(result);
+    if (row == NULL) {
+      gprint (GP_ERR, "inconsistent row count: expected %d, got %d\n", Nrows, j);
+      free (query);
+      free (vec);
+      mysql_free_result (result);
+      return (FALSE);
+    }
+    for (i = 0; i < Ncols; i++) {
+      if (row[i]) {
+	switch (fields[i].type) {
+	  case FIELD_TYPE_TINY:
+	  case FIELD_TYPE_SHORT:
+	  case FIELD_TYPE_LONG:
+	  case FIELD_TYPE_INT24:
+	  case FIELD_TYPE_LONGLONG:
+	    vec[i][0].elements.Int[j] = atol (row[i]);
+	    break;
+	  case FIELD_TYPE_DECIMAL:
+	  case FIELD_TYPE_FLOAT:
+	  case FIELD_TYPE_DOUBLE:
+	    vec[i][0].elements.Flt[j] = atof (row[i]);
+	    break;
+	  case FIELD_TYPE_TIME:
+	  case FIELD_TYPE_DATE:
+	  case FIELD_TYPE_DATETIME:
+	    seconds = ohana_date_to_sec (row[i]);
+	    vec[i][0].elements.Flt[j] = ohana_sec_to_mjd (seconds);
+	    break;
+	  default:
+	    vec[i][0].elements.Flt[j] = NAN;
+	}
+      } else {
+	vec[i][0].elements.Flt[j] = NAN;
+      }
+    }
+  }
+  free (query);
+  free (vec);
+  mysql_free_result (result);
+  return (TRUE);
+}
+# else 
+
+int dbselect (int argc, char **argv) {
+
+  gprint (GP_ERR, "mysql library is not available\n");
+  return FALSE;
+}
+
+# endif
Index: /branches/sc_branches/pantasks_condor/cmd.data/delete.c
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.data/delete.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.data/delete.c	(revision 34783)
@@ -0,0 +1,31 @@
+# include "data.h"
+
+int delete (int argc, char **argv) {
+  
+  int i, N, Quiet;
+
+  Quiet = FALSE;
+  if ((N = get_argument (argc, argv, "-q"))) {
+    Quiet = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+  if ((N = get_argument (argc, argv, "-quiet"))) {
+    Quiet = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+
+  if (argc < 2) {
+    gprint (GP_ERR, "USAGE: delete <obiect> [<object> ..]\n");
+    return (FALSE);
+  }
+
+  for (i = 1; i < argc; i++) {
+    if (DeleteNamedBuffer (argv[i])) continue; 
+    if (DeleteNamedVector (argv[i])) continue;
+    if (DeleteNamedScalar (argv[i])) continue; 
+    if (!Quiet) gprint (GP_ERR, "can't find object %s\n", argv[i]);
+  }
+
+  return (TRUE);
+}
+
Index: /branches/sc_branches/pantasks_condor/cmd.data/densify.c
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.data/densify.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.data/densify.c	(revision 34783)
@@ -0,0 +1,92 @@
+# include "data.h"
+
+# define CHECKVAL(ARG) if (!isfinite(ARG)) { gprint (GP_ERR, "illegal value for %s: %f\n", #ARG, ARG); return (FALSE); }
+
+int densify (int argc, char **argv) {
+
+  int i, Nx, Ny, Xb, Yb, N, Xpix, Ypix, good, UseGraph;
+  double Xmin, Xmax, dX, Ymin, Ymax, dY;
+  float *val;
+  Buffer *bf;
+  Vector *vx, *vy;
+  opihi_flt *x, *y;
+
+  // int Normalize = TRUE;
+  // if ((N = get_argument (argc, argv, "-raw"))) {
+  //   remove_argument (N, &argc, argv);
+  //   Normalize = FALSE;
+  // }
+
+  UseGraph = FALSE;
+  if ((N = get_argument (argc, argv, "-graph"))) {
+    remove_argument (N, &argc, argv);
+    UseGraph = TRUE;
+  }
+
+  good = UseGraph ? (argc == 4) : (argc == 10);
+  if (!good) {
+    gprint (GP_ERR, "USAGE: densify buffer x y Xmin Xmax dX Ymin Ymax dY\n");
+    gprint (GP_ERR, "   OR: densify buffer x y -graph\n");
+    return (FALSE);
+  }
+  
+  if ((bf = SelectBuffer (argv[1], ANYBUFFER, TRUE)) == NULL) return (FALSE);
+  if ((vx = SelectVector (argv[2], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((vy = SelectVector (argv[3], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+
+  if (vx[0].Nelements != vy[0].Nelements) return (FALSE);
+
+  REQUIRE_VECTOR_FLT (vx, FALSE); 
+  REQUIRE_VECTOR_FLT (vy, FALSE); 
+
+  if (UseGraph) {
+    int kapa;
+    Graphdata graphmode;
+    if (!GetGraph (&graphmode, &kapa, NULL)) return (FALSE);
+    KapaGetImageRange (kapa, &Xmin, &Xmax, &Ymax, &Ymin, &Xpix, &Ypix);
+    Xmax = graphmode.xmax;
+    Xmin = graphmode.xmin;
+    Ymax = graphmode.ymax;
+    Ymin = graphmode.ymin;
+    dX = (Xmax - Xmin) / (Xpix - 1);
+    dY = (Ymax - Ymin) / (Ypix - 1);
+  } else {
+    Xmin = atof (argv[4]);
+    Xmax = atof (argv[5]);
+    dX   = atof (argv[6]);
+
+    Ymin = atof (argv[7]);
+    Ymax = atof (argv[8]);
+    dY   = atof (argv[9]);
+  }
+
+  CHECKVAL(Xmin);
+  CHECKVAL(Xmax);
+  CHECKVAL(dX);
+
+  CHECKVAL(Ymin);
+  CHECKVAL(Ymax);
+  CHECKVAL(dY);
+
+  Nx = (Xmax - Xmin) / dX + 1;
+  Ny = (Ymax - Ymin) / dY + 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)");
+
+  x = vx[0].elements.Flt;
+  y = vy[0].elements.Flt;
+  val = (float *)bf[0].matrix.buffer;
+  for (i = 0; i < vx[0].Nelements; i++, x++, y++) {
+    Xb = (*x - Xmin) / dX;
+    Yb = (*y - Ymin) / dY;
+    if (Xb >= Nx) continue;
+    if (Yb >= Ny) continue;
+    if (Xb < 0) continue;
+    if (Yb < 0) continue;
+    val[Xb + Yb*Nx] ++;
+  }
+  return (TRUE);
+}
Index: /branches/sc_branches/pantasks_condor/cmd.data/device.c
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.data/device.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.data/device.c	(revision 34783)
@@ -0,0 +1,28 @@
+# include "data.h"
+
+int device (int argc, char **argv) {
+
+  int N, kapa;
+  char *name;;
+  /* set / get current graphics device */
+
+  name = NULL;
+  if ((N = get_argument (argc, argv, "-n"))) {
+    remove_argument (N, &argc, argv);
+    name = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  if (name == NULL) {
+    name = GetKapaName ();
+    if (name == NULL) {
+      gprint (GP_ERR, "no device defined\n");
+      return (FALSE);
+    }
+  } else {
+    if (!GetGraph (NULL, &kapa, name)) return (FALSE);
+  }
+  gprint (GP_ERR, "kapa %s\n", name); 
+
+  return (TRUE);
+}
Index: /branches/sc_branches/pantasks_condor/cmd.data/dimendown.c
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.data/dimendown.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.data/dimendown.c	(revision 34783)
@@ -0,0 +1,64 @@
+# include "data.h"
+
+enum {VALUE, XCOORD, YCOORD};
+
+int dimendown (int argc, char **argv) {
+  
+  int i, Nx, Ny, Npix, N, mode;
+  float *in;
+  opihi_flt *out;
+  Vector *vec;
+  Buffer *buf;
+
+  mode = VALUE;
+  if ((N = get_argument (argc, argv, "-x"))) {
+    remove_argument (N, &argc, argv);
+    mode = XCOORD;
+  }
+  if ((N = get_argument (argc, argv, "-y"))) {
+    remove_argument (N, &argc, argv);
+    mode = YCOORD;
+  }
+
+  if (argc != 3) goto usage;
+
+  if ((buf = SelectBuffer (argv[1], OLDBUFFER, TRUE)) == NULL) return (FALSE);
+  if ((vec = SelectVector (argv[2], ANYVECTOR, TRUE)) == NULL) return (FALSE);
+
+  Nx = buf[0].matrix.Naxis[0];
+  Ny = buf[0].matrix.Naxis[1];
+  Npix = Nx * Ny;
+
+  ResetVector (vec, OPIHI_FLT, Npix);
+
+  in = (float *) buf[0].matrix.buffer;
+  out = vec[0].elements.Flt;
+
+  switch (mode) {
+    case VALUE:
+      for (i = 0; i < Npix; i++, in++, out++) {
+	*out = *in;
+      }
+      break;
+
+    case XCOORD:
+      for (i = 0; i < Npix; i++, out++) {
+	*out = i % Nx;
+      }
+      break;
+
+    case YCOORD:
+      for (i = 0; i < Npix; i++, out++) {
+	*out = i / Nx;
+      }
+      break;
+  }
+      
+  return (TRUE);
+
+ usage:
+    gprint (GP_ERR, "USAGE: dimendown <buffer> <vector>\n");
+    gprint (GP_ERR, "  -x : fill vector with buffer x-coords\n");
+    gprint (GP_ERR, "  -y : fill vector with buffer y-coords\n");
+    return (FALSE);
+}
Index: /branches/sc_branches/pantasks_condor/cmd.data/dimenup.c
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.data/dimenup.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.data/dimenup.c	(revision 34783)
@@ -0,0 +1,46 @@
+# include "data.h"
+
+int dimenup (int argc, char **argv) {
+  
+  int i, Nx, Ny, Npix;
+  float *out;
+  Vector *vec;
+  Buffer *buf;
+
+  if (argc != 5) {
+    gprint (GP_ERR, "USAGE: dimenup <vector> <buffer> Nx Ny\n");
+    return (FALSE);
+  }
+
+  if ((vec = SelectVector (argv[1], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((buf = SelectBuffer (argv[2], ANYBUFFER, TRUE)) == NULL) return (FALSE);
+
+  Npix = vec[0].Nelements;
+  Nx = atof (argv[3]);
+  Ny = atof (argv[4]);
+  if (Npix != Nx * Ny) {
+    gprint (GP_ERR, "dimensions don't match\n");
+    return (FALSE);
+  }
+
+  gfits_free_matrix (&buf[0].matrix);
+  gfits_free_header (&buf[0].header);
+  CreateBuffer (buf, Nx, Ny, -32, 0.0, 1.0);
+
+  out = (float *) buf[0].matrix.buffer;
+
+  if (vec[0].type == OPIHI_FLT) {
+    opihi_flt *in = vec[0].elements.Flt;
+    for (i = 0; i < Npix; i++, in++, out++) {
+      *out = *in;
+    }
+  } else {
+    opihi_int *in = vec[0].elements.Int;
+    for (i = 0; i < Npix; i++, in++, out++) {
+      *out = *in;
+    }
+  }
+
+  return (TRUE);
+}
+
Index: /branches/sc_branches/pantasks_condor/cmd.data/dot.c
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.data/dot.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.data/dot.c	(revision 34783)
@@ -0,0 +1,39 @@
+# include "data.h"
+
+int dot (int argc, char **argv) {
+  
+  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) {
+    gprint (GP_ERR, "USAGE: dot <x> <y>\n");
+    return (FALSE);
+  }
+  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) */
+  graphmode.style = 2;
+  graphmode.etype = 0;
+
+  if (!KapaPrepPlot (kapa, 1, &graphmode)) return (FALSE);
+  KapaPlotVector (kapa, 1, &x, "x");
+  KapaPlotVector (kapa, 1, &y, "y");
+  
+  return (TRUE);
+}
Index: /branches/sc_branches/pantasks_condor/cmd.data/erase.c
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.data/erase.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.data/erase.c	(revision 34783)
@@ -0,0 +1,39 @@
+# include "data.h"
+
+int erase (int argc, char **argv) {
+  
+  int i, N;
+  int kapa;
+  char *name;
+  
+  name = NULL;
+  if ((N = get_argument (argc, argv, "-n"))) {
+    remove_argument (N, &argc, argv);
+    name = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+  if (!GetImage (NULL, &kapa, name)) return (FALSE);
+  FREE (name);
+
+  if (argc < 2) {
+    gprint (GP_ERR, "USAGE: erase (overlay) [overlay, overlay, ..] \n");
+    gprint (GP_ERR, " (overlay) may be: red (0), green (1), blue (2), yellow (3) or all\n");
+    return (FALSE);
+  }
+
+  for (i = 1; i < argc; i++) {
+    if (!(strcasecmp (argv[i], "all"))) {
+      KiiEraseOverlay (kapa, "red");
+      KiiEraseOverlay (kapa, "green");
+      KiiEraseOverlay (kapa, "blue");
+      KiiEraseOverlay (kapa, "yellow");
+      continue;
+    }
+    if (!KiiSelectOverlay (argv[i], &N)) {
+      gprint (GP_ERR, "%s is not a valid overlay\n", argv[i]);
+      return (FALSE);
+    }
+    KiiEraseOverlay (kapa, argv[i]);
+  }
+  return (TRUE);
+}
Index: /branches/sc_branches/pantasks_condor/cmd.data/extract.c
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.data/extract.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.data/extract.c	(revision 34783)
@@ -0,0 +1,93 @@
+# include "data.h"
+
+int extract (int argc, char **argv) {
+  
+  int i, j;
+  float *Vin, *Vout;
+  int sx, sy, nx, ny, NX, NY;
+  int Sx, Sy, Nx, Ny;
+  Buffer *in, *out;
+
+  if (argc != 11) {
+    gprint (GP_ERR, "USAGE: extract <from> <to> sx sy nx ny Sx Sy Nx Ny\n");
+    return (FALSE);
+  }
+
+  if ((in = SelectBuffer (argv[1], OLDBUFFER, TRUE)) == NULL) return (FALSE);
+  NX = in[0].matrix.Naxis[0];
+  NY = in[0].matrix.Naxis[1];
+
+  sx = atof (argv[3]);
+  sy = atof (argv[4]);
+  nx = atof (argv[5]);
+  ny = atof (argv[6]);
+
+  Sx = atof (argv[7]);
+  Sy = atof (argv[8]);
+  Nx = atof (argv[9]);
+  Ny = atof (argv[10]);
+
+  if ((Sy + ny > Ny) || (Sx + nx > Nx)) {
+    gprint (GP_ERR, "mismatch between source and dest regions\n");
+    gprint (GP_ERR, "%d + %d > %d or %d + %d > %d\n", Sy, ny, Ny, Sx, nx, Nx);
+    return (FALSE);
+  }
+
+  /* region is not on first image */
+  if ((sx + nx < 0) || (sy + ny < 0) || 
+      (sx > in[0].matrix.Naxis[0]) || 
+      (sy > in[0].matrix.Naxis[1])) {
+    gprint (GP_ERR, "region outside of source image\n");
+    return (FALSE);
+  }
+
+  if ((Sx + nx > Nx) || (Sy + ny > Ny)) {
+    gprint (GP_ERR, "source region larger than dest region\n");
+    return (FALSE);
+  }
+  if ((Sx < 0) || (Sy < 0)) {
+    gprint (GP_ERR, "dest region out of range\n");
+    return (FALSE);
+  }
+
+  if ((out = SelectBuffer (argv[2], OLDBUFFER, FALSE)) == NULL) {
+    if ((out = SelectBuffer (argv[2], ANYBUFFER, TRUE)) == NULL) return (FALSE);
+    gfits_free_matrix (&out[0].matrix);
+    gfits_free_header (&out[0].header);
+
+    out[0].bitpix = in[0].bitpix;
+    out[0].unsign = in[0].unsign;
+    out[0].bscale = in[0].bscale;
+    out[0].bzero  = in[0].bzero;
+    strcpy (out[0].file, in[0].file);
+    gfits_copy_header (&in[0].header, &out[0].header);
+    gfits_modify (&out[0].header, "NAXIS1", "%d", 1, Nx);
+    gfits_modify (&out[0].header, "NAXIS2", "%d", 1, Ny);
+    out[0].header.Naxis[0] = Nx;
+    out[0].header.Naxis[1] = Ny;
+    gfits_create_matrix (&out[0].header, &out[0].matrix);
+  } else {
+    if ((out[0].header.Naxis[1] != Ny) || (out[0].header.Naxis[0] != Nx)) {
+      gprint (GP_ERR, "matrix sizes mis-matched\n");
+      gprint (GP_ERR, "%d x %d  vs  "OFF_T_FMT" x "OFF_T_FMT"\n", Nx, Ny, 
+	       out[0].header.Naxis[0],  out[0].header.Naxis[1]);
+      return (FALSE);
+    }
+  }
+
+  for (j = 0; j < ny; j++) {
+    if (j + sy < 0) continue;
+    if (j + sy >= NY) continue;
+    Vin = (float *)(in[0].matrix.buffer) + (j + sy)*in[0].matrix.Naxis[0] + sx;  
+    Vout = (float *)(out[0].matrix.buffer) + (j + Sy)*out[0].matrix.Naxis[0] + Sx;   
+    for (i = 0; i < nx; i++, Vin++, Vout++) {
+      if (i + sx < 0) continue;
+      if (i + sx >= NX) continue;
+      *Vout = *Vin;
+    }
+  }
+
+  return (TRUE);
+
+}
+
Index: /branches/sc_branches/pantasks_condor/cmd.data/fft1d.c
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.data/fft1d.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.data/fft1d.c	(revision 34783)
@@ -0,0 +1,62 @@
+# include "data.h"
+
+int fft1d (int argc, char **argv) {
+  
+  int N, Npix, Nbit, ZeroImaginary, forward;
+  Vector *Ire, *Iim, *Ore, *Oim;
+
+  forward = TRUE;
+  if ((N = get_argument (argc, argv, "-inverse"))) {
+    remove_argument (N, &argc, argv);
+    forward = FALSE;
+  }
+
+  if ((argc != 6) || (strcmp (argv[3], "to"))) {
+    gprint (GP_ERR, "USAGE: fft1d (real) (imag) to (real) (imag)\n");
+    return (FALSE);
+  }
+
+  Iim = NULL;
+  ZeroImaginary = TRUE;
+  if (strcmp (argv[2], "0")) {
+    ZeroImaginary = FALSE;
+    if ((Iim = SelectVector (argv[2], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+  }    
+  if ((Ire = SelectVector (argv[1], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+  
+  REQUIRE_VECTOR_FLT (Ire, FALSE); 
+  if (Iim) { REQUIRE_VECTOR_FLT (Iim, FALSE); }
+
+  // check the input data (match lengths? binary length?)
+  Npix = Ire[0].Nelements;
+  if (!ZeroImaginary && (Npix != Iim[0].Nelements)) {
+    gprint (GP_ERR, "vector size mismatch\n");
+    return (FALSE);
+  }
+  if (!IsBinary (Npix, &Nbit)) {
+    gprint (GP_ERR, "Npix is not a binary number!\n");
+    return (FALSE);
+  }
+
+  // select or create the output vectors
+  if ((Ore = SelectVector (argv[4], ANYVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((Oim = SelectVector (argv[5], ANYVECTOR, TRUE)) == NULL) return (FALSE);
+
+  // allocate sufficient output space, force output to be FLT
+  ResetVector (Ore, OPIHI_FLT, Npix);
+  ResetVector (Oim, OPIHI_FLT, Npix);
+ 
+  // copy data to output vectors (fft is done in place)
+  memcpy (Ore[0].elements.Flt, Ire[0].elements.Flt, Npix*sizeof(opihi_flt));
+
+  // copy imaginary vector or create a zero vector
+  if (ZeroImaginary) {
+    memset (Oim[0].elements.Flt, 0, Npix*sizeof(opihi_flt));
+  } else {
+    memcpy (Oim[0].elements.Flt, Iim[0].elements.Flt, Npix*sizeof(opihi_flt));
+  }    
+
+  dfft1D (Ore[0].elements.Flt, Oim[0].elements.Flt, Npix, Nbit, forward); 
+  
+  return (TRUE);
+}
Index: /branches/sc_branches/pantasks_condor/cmd.data/fft1d.old.c
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.data/fft1d.old.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.data/fft1d.old.c	(revision 34783)
@@ -0,0 +1,70 @@
+# include "data.h"
+
+int fft1dold (int argc, char **argv) {
+  
+  int i, Npix, ZeroImaginary;
+  float *t1, *t2, *temp;
+  Vector *Ire, *Iim, *Ore, *Oim;
+
+  if ((argc != 6) || (strcmp (argv[3], "to"))) {
+    gprint (GP_ERR, "USAGE: fft1d (real) (imag) to (real) (imag)\n");
+    return (FALSE);
+  }
+
+  Iim = NULL;
+  ZeroImaginary = TRUE;
+  if (strcmp (argv[2], "0")) {
+    ZeroImaginary = FALSE;
+    if ((Iim = SelectVector (argv[2], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+  }    
+  if ((Ire = SelectVector (argv[1], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((Ore = SelectVector (argv[4], ANYVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((Oim = SelectVector (argv[5], ANYVECTOR, TRUE)) == NULL) return (FALSE);
+
+  Npix = Ire[0].Nelements;
+  if (!ZeroImaginary && (Npix != Iim[0].Nelements)) {
+    gprint (GP_ERR, "vector mismatch in size\n");
+    return (FALSE);
+  }
+
+  if (!IsBinaryOld (Npix)) {
+    gprint (GP_ERR, "Npix is not a binary number!\n");
+    return (FALSE);
+  }
+  
+  ALLOCATE (temp, float, 2*Npix);
+  if (ZeroImaginary) {
+    t1 = Ire[0].elements;
+    for (i = 0; i < Npix; i++, t1++) {
+      temp[2*i  ] = *t1;
+      temp[2*i+1] = 0;
+    }
+  } else {
+    t1 = Ire[0].elements;
+    t2 = Iim[0].elements;
+    for (i = 0; i < Npix; i++, t1++, t2++) {
+      temp[2*i  ] = *t1;
+      temp[2*i+1] = *t2;
+    }
+  }    
+    
+  fftold (temp, Npix, 1); 
+
+  Ore[0].Nelements = Npix;
+  Oim[0].Nelements = Npix;
+  REALLOCATE (Ore[0].elements, float, Npix);
+  REALLOCATE (Oim[0].elements, float, Npix);
+ 
+  t1 = Ore[0].elements;
+  t2 = Oim[0].elements;
+  for (i = 0; i < Npix; i++, t1++, t2++) {
+    *t1 = temp[2*i  ] / Npix;
+    *t2 = temp[2*i+1] / Npix;
+  }    
+  
+  free (temp);
+  
+  return (TRUE);
+}
+
+  
Index: /branches/sc_branches/pantasks_condor/cmd.data/fft2d.c
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.data/fft2d.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.data/fft2d.c	(revision 34783)
@@ -0,0 +1,79 @@
+# include "data.h"
+
+int fft2d (int argc, char **argv) {
+  
+  int ZeroImaginary, forward;
+  int N, Nx, Ny;
+  Buffer *Ire, *Iim, *Ore, *Oim;;
+
+  forward = TRUE;
+  if ((N = get_argument (argc, argv, "-inverse"))) {
+    remove_argument (N, &argc, argv);
+    forward = FALSE;
+  }
+
+  if ((argc != 6) || (strcmp (argv[3], "to"))) {
+    gprint (GP_ERR, "USAGE: fft2d (real) (imag) to (real) (imag)\n");
+    return (FALSE);
+  }
+
+  /* select input / output buffers */
+  Iim = NULL;
+  ZeroImaginary = TRUE; /* Input(imaginary) may be 0, in which case we create a 0 filled image */
+  if (!strcmp (argv[2], "0")) { 
+  } else {
+    ZeroImaginary = FALSE;
+    if ((Iim = SelectBuffer (argv[2], OLDBUFFER, TRUE)) == NULL) return (FALSE);
+  }    
+  if ((Ire = SelectBuffer (argv[1], OLDBUFFER, TRUE)) == NULL) return (FALSE);
+
+  Nx = Ire[0].header.Naxis[0];
+  Ny = Ire[0].header.Naxis[1];
+
+  // check input image dimensions (match lengths? binary lengths?)
+  if (!ZeroImaginary) {
+    if ((Nx != Iim[0].header.Naxis[0]) || 
+	(Ny != Iim[0].header.Naxis[1])) {
+      gprint (GP_ERR, "image size mismatch\n");
+      return (FALSE);
+    }
+  }
+  if (!IsBinary (Nx, NULL)) {
+    gprint (GP_ERR, "Nx is not a binary number!\n");
+    return (FALSE);
+  }
+  if (!IsBinary (Ny, NULL)) {
+    gprint (GP_ERR, "Ny is not a binary number!\n");
+    return (FALSE);
+  }
+
+  if ((Ore = SelectBuffer (argv[4], ANYBUFFER, TRUE)) == NULL) return (FALSE);
+  if ((Oim = SelectBuffer (argv[5], ANYBUFFER, TRUE)) == NULL) return (FALSE);
+
+  /* free up output space */
+  gfits_free_matrix (&Ore[0].matrix);
+  gfits_free_header (&Ore[0].header);
+  gfits_free_matrix (&Oim[0].matrix);
+  gfits_free_header (&Oim[0].header);
+  
+  /* fix up output headers (real) & allocate data buffer */
+  CreateBuffer (Ore, Nx, Ny, -32, 0.0, 1.0);
+  CreateBuffer (Oim, Nx, Ny, -32, 0.0, 1.0);
+
+  gfits_copy_header (&Ire[0].header, &Ore[0].header);
+  gfits_copy_header (&Ire[0].header, &Oim[0].header);
+
+  // copy data to output buffers (fft is done in place)
+  memcpy (Ore[0].matrix.buffer, Ire[0].matrix.buffer, Nx*Ny*sizeof(float));
+
+  if (ZeroImaginary) {
+    memset (Oim[0].matrix.buffer, 0, Nx*Ny*sizeof(float));
+  } else {
+    memcpy (Oim[0].matrix.buffer, Iim[0].matrix.buffer, Nx*Ny*sizeof(float));
+  }
+
+  /* run the fft */
+  fftND ((float *)Ore[0].matrix.buffer, (float *)Oim[0].matrix.buffer, 2, Ire[0].header.Naxis, forward);
+
+  return (TRUE);
+}
Index: /branches/sc_branches/pantasks_condor/cmd.data/fft2d.old.c
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.data/fft2d.old.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.data/fft2d.old.c	(revision 34783)
@@ -0,0 +1,113 @@
+# include "data.h"
+
+int fft2dold (int argc, char **argv) {
+  
+  int i, N, Nx, Ny, Naxis[2];
+  int Npix, ZeroImaginary, isign;
+  float *t1, *t2, *out, *temp;
+  Buffer *Ire, *Iim, *Ore, *Oim;;
+
+  isign = 1;
+  if ((N = get_argument (argc, argv, "-inverse"))) {
+    remove_argument (N, &argc, argv);
+    isign = -1;
+  }
+
+  if ((argc != 6) || (strcmp (argv[3], "to"))) {
+    gprint (GP_ERR, "USAGE: fft2d (real) (imag) to (real) (imag)\n");
+    return (FALSE);
+  }
+
+  /* select input / output buffers */
+  Iim = NULL;
+  ZeroImaginary = TRUE; /* Input(imaginary) may be 0, in which case we create a 0 filled image */
+  if (!strcmp (argv[2], "0")) { 
+  } else {
+    ZeroImaginary = FALSE;
+    if ((Iim = SelectBuffer (argv[2], OLDBUFFER, TRUE)) == NULL) return (FALSE);
+  }    
+  if ((Ire = SelectBuffer (argv[1], OLDBUFFER, TRUE)) == NULL) return (FALSE);
+  if ((Ore = SelectBuffer (argv[4], ANYBUFFER, TRUE)) == NULL) return (FALSE);
+  if ((Oim = SelectBuffer (argv[5], ANYBUFFER, TRUE)) == NULL) return (FALSE);
+
+  /* free up output space */
+  gfits_free_matrix (&Ore[0].matrix);
+  gfits_free_header (&Ore[0].header);
+  gfits_free_matrix (&Oim[0].matrix);
+  gfits_free_header (&Oim[0].header);
+
+  /* get image dimensions, check value */
+  Npix = Ire[0].header.Naxis[0]*Ire[0].header.Naxis[1];
+  Nx = Ire[0].header.Naxis[0];
+  Ny = Ire[0].header.Naxis[1];
+  Naxis[0] = Ny; Naxis[1] = Nx;
+  if (!IsBinaryOld (Npix)) {
+    gprint (GP_ERR, "dimensions are not binary!\n");
+    return (FALSE);
+  }
+  
+  /* create working space */
+  t1 = (float *) Ire[0].matrix.buffer;
+  ALLOCATE (temp, float, 2*Npix);
+  out = temp;
+
+  /* copy input to working space */
+  if (ZeroImaginary) {
+    for (i = 0; i < Npix; i++, t1++) {
+      *out = *t1;
+      out++;
+      *out = 0;
+      out++;
+    }
+  } else {
+    t2 = (float *) Iim[0].matrix.buffer;
+    for (i = 0; i < Npix; i++, t1++, t2++) {
+      *out = *t1;
+      out++;
+      *out = *t2;
+      out++;
+    }
+  } 
+    
+  /* run the fft */
+  fftNold (temp, Naxis, 2, isign);
+
+  /* fix up output headers (real) */
+  gfits_copy_header (&Ire[0].header, &Ore[0].header);
+  gfits_modify (&Ore[0].header, "NAXIS1", "%d", 1, Nx);
+  gfits_modify (&Ore[0].header, "NAXIS2", "%d", 1, Ny);
+  Ore[0].header.Naxis[0] = Nx;
+  Ore[0].header.Naxis[1] = Ny;
+  Ore[0].bitpix = Ire[0].bitpix;
+  Ore[0].unsign = Ire[0].unsign;
+  Ore[0].bscale = Ire[0].bscale;
+  Ore[0].bzero  = Ire[0].bzero;
+  gfits_create_matrix (&Ore[0].header, &Ore[0].matrix);
+
+  /* fix up output headers (imaginary) */
+  gfits_copy_header (&Ire[0].header, &Oim[0].header);
+  gfits_modify (&Oim[0].header, "NAXIS1", "%d", 1, Nx);
+  gfits_modify (&Oim[0].header, "NAXIS2", "%d", 1, Ny);
+  Oim[0].header.Naxis[0] = Nx;
+  Oim[0].header.Naxis[1] = Ny;
+  Oim[0].bitpix = Ire[0].bitpix;
+  Oim[0].unsign = Ire[0].unsign;
+  Oim[0].bscale = Ire[0].bscale;
+  Oim[0].bzero  = Ire[0].bzero;
+  gfits_create_matrix (&Oim[0].header, &Oim[0].matrix);
+
+  /* move data from working space to output buffers */
+  out = temp;
+  t1 = (float *) Ore[0].matrix.buffer;
+  t2 = (float *) Oim[0].matrix.buffer;
+  for (i = 0; i < Npix; i++, t1++, t2++) {
+    *t1 = *out / Npix;
+    out ++;
+    *t2 = *out / Npix;
+    out ++;
+  }    
+
+  free (temp);
+
+  return (TRUE);
+}
Index: /branches/sc_branches/pantasks_condor/cmd.data/fit1d.c
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.data/fit1d.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.data/fit1d.c	(revision 34783)
@@ -0,0 +1,237 @@
+# include "data.h"
+
+int fit1d (int argc, char **argv) {
+  
+  double **c, **b, *s, X, Y, dY, dY2;
+  double ClipNSigma, mean, sigma, maxsigma;
+  int i, j, nterm, mterm, order, Npt, Nmask;
+  int N, Weight, Quiet, ClipNiter;
+  Vector *xvec, *yvec, *dyvec;
+  opihi_flt *x, *y, *dy, *yf, *yfit;
+  char name[64], *mask;
+
+  Quiet = FALSE;
+  if ((N = get_argument (argc, argv, "-q"))) {
+    Quiet = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+  if ((N = get_argument (argc, argv, "-quiet"))) {
+    Quiet = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+
+  ClipNSigma = 0;
+  ClipNiter  = 1;
+  if ((N = get_argument (argc, argv, "-clip"))) {
+    remove_argument (N, &argc, argv);
+    ClipNSigma = atof(argv[N]);
+    remove_argument (N, &argc, argv);
+    ClipNiter  = atof(argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  dy = NULL;
+  dyvec = NULL;
+  Weight = FALSE;
+  if ((N = get_argument (argc, argv, "-dy"))) {
+    remove_argument (N, &argc, argv);
+    if ((dyvec = SelectVector (argv[N], OLDVECTOR, TRUE)) == NULL) return (FALSE);    
+    remove_argument (N, &argc, argv);
+    Weight = TRUE;
+  }
+
+  if (argc != 4) {
+    gprint (GP_ERR, "USAGE: fit1d x y order [-dy wt] [-quiet/-q] [-clip Nsigma Niter]\n");
+    return (FALSE);
+  }
+
+  if ((xvec = SelectVector (argv[1], OLDVECTOR, TRUE)) == NULL) return (FALSE);    
+  if ((yvec = SelectVector (argv[2], OLDVECTOR, TRUE)) == NULL) return (FALSE);    
+
+  if (xvec[0].Nelements != yvec[0].Nelements) {
+    gprint (GP_ERR, "vectors must have same length\n");
+    return (FALSE);
+  }
+  CastVector (xvec, OPIHI_FLT);
+  CastVector (yvec, OPIHI_FLT);
+
+  if (Weight) {
+    CastVector (dyvec, OPIHI_FLT);
+    if (xvec[0].Nelements != dyvec[0].Nelements) {
+      gprint (GP_ERR, "vectors must have same length\n");
+      return (FALSE);
+    }
+  }
+ 
+
+  /* nterm is number of polynomial terms, starting at x^0 */
+  order = atof (argv[3]);
+  nterm = order + 1;
+  mterm = 2*nterm;
+
+  ALLOCATE (yfit, opihi_flt, xvec[0].Nelements);
+  ALLOCATE (mask, char, xvec[0].Nelements);
+  memset (mask, 0, xvec[0].Nelements);
+
+  ALLOCATE (s, double, mterm);
+  ALLOCATE (b, double *, nterm);
+  ALLOCATE (c, double *, nterm);
+  for (i = 0; i < nterm; i++) {
+    ALLOCATE (c[i], double, nterm);
+    ALLOCATE (b[i], double, 1);
+  }
+
+  Nmask = 0;
+  sigma = 0.0;
+
+  for (N = 0; N < ClipNiter; N++) {
+
+    /* init registers for current pass */
+    memset (s, 0, mterm*sizeof(double));
+    for (i = 0; i < nterm; i++) {
+      memset (c[i], 0, nterm*sizeof(double));
+      memset (b[i], 0, sizeof(double));
+    }
+
+    /* perform linear fit */
+    x = xvec[0].elements.Flt;
+    y = yvec[0].elements.Flt;
+    if (Weight) dy = dyvec[0].elements.Flt;
+
+    for (i = 0; i < xvec[0].Nelements; i++, x++, y++) {
+      if (mask[i]) continue;
+      if (!(finite(*x) && finite(*y))) continue;
+      dY = 1.0;
+      if (Weight) { 
+	dY = 1.0 / SQ(*dy);
+	dy ++;
+      }
+      X = 1*dY;
+      Y = *y*dY;
+      for (j = 0; j < nterm; j++) {
+	s[j] += X;
+	b[j][0] += Y;
+	X = X * (*x);
+	Y = Y * (*x);
+      }
+      for (j = nterm; j < mterm; j++) {
+	s[j] += X;
+	X = X * (*x);
+      }
+    }
+    for (i = 0; i < nterm; i++) {
+      for (j = 0; j < nterm; j++) {
+	c[i][j] = s[i + j];
+      }
+    }
+    if (!dgaussjordan (c, b, nterm, 1)) {
+	gprint (GP_ERR, "failed to fit data : ill-conditioned matrix\n");
+	goto escape;
+    }
+
+    /* generate fitted values */
+    x = xvec[0].elements.Flt;
+    yf = yfit;
+    for (i = 0; i < xvec[0].Nelements; i++, x++, yf++) {
+      if (!finite(*x)) continue;
+      *yf = 0;
+      X = 1;
+      for (j = 0; j < order + 1; j++) {
+	*yf += b[j][0]*X;
+	X = X * (*x);
+      }
+    }
+
+    /* measure fit residual scatter */
+    x  = xvec[0].elements.Flt;
+    y  = yvec[0].elements.Flt;
+    yf = yfit;
+    dY = dY2 = 0;
+    for (i = Npt = 0; i < xvec[0].Nelements; i++, x++, y++, yf++) {
+      if (mask[i]) continue;
+      if (!finite(*x)) continue;
+      dY  += (*y - *yf);
+      dY2 += SQ(*y - *yf);
+      Npt ++;
+    }
+    mean  = dY / Npt;
+    sigma = sqrt (fabs(dY2/Npt - SQ(mean)));
+    maxsigma = ClipNSigma * sigma;
+
+    /* mask outlier points */
+    x  = xvec[0].elements.Flt;
+    y  = yvec[0].elements.Flt;
+    yf = yfit;
+    Nmask = 0;
+    for (i = 0; ClipNSigma && (i < xvec[0].Nelements); i++, x++, y++, yf++) {
+      dY = (*y - *yf);
+      if (fabs(dY) > maxsigma) {
+	mask[i] = TRUE;
+	Nmask ++;
+      } else {
+	mask[i] = FALSE;
+      }	
+    }
+  }
+      
+  /* print & save basic fit parameters */
+  if (!Quiet) gprint (GP_ERR, "y = ");
+  for (i = 0; i < nterm; i++) {
+    sprintf (name, "C%d", i);
+    set_variable (name, b[i][0]);
+    if (!Quiet) gprint (GP_ERR, "%f x^%d ", b[i][0], i);
+  }
+  sprintf (name, "Cn");
+  set_variable (name, (double) order);
+  
+  /* print & save basic fit parameters */
+  if (!Quiet) gprint (GP_ERR, "\n");
+  if (!Quiet) gprint (GP_ERR, "    ");
+  for (i = 0; i < nterm; i++) {
+    sprintf (name, "dC%d", i);
+    set_variable (name, sqrt(c[i][i]));
+    if (!Quiet) gprint (GP_ERR, "%f     ", sqrt(c[i][i]));
+  }
+  if (!Quiet) gprint (GP_ERR, "\n");
+
+  set_variable ("dC", sigma);
+  set_variable ("Cnv", (xvec[0].Nelements - Nmask));
+
+  /* save mask and yfit for testing? */
+  if (1) { 
+    Vector *fvec, *mvec;
+    if ((fvec = SelectVector ("yfit", ANYVECTOR, TRUE)) == NULL) return (FALSE);    
+    if ((mvec = SelectVector ("mask", ANYVECTOR, TRUE)) == NULL) return (FALSE);    
+    free (fvec[0].elements.Flt);
+    fvec[0].elements.Flt = yfit;
+    fvec[0].Nelements = xvec[0].Nelements;
+    mvec[0].Nelements = xvec[0].Nelements;
+
+    REALLOCATE (mvec[0].elements.Flt, opihi_flt, xvec[0].Nelements);
+    for (i = 0; i < xvec[0].Nelements; i++) {
+      mvec[0].elements.Flt[i] = mask[i];
+    }
+  } else {
+    free (yfit);
+  }
+  free (mask);
+
+  for (i = 0; i < nterm; i++) {
+    free (b[i]);
+    free (c[i]);
+  }
+  free (b);
+  free (c);
+  free (s);
+  return (TRUE);
+
+escape:
+  for (i = 0; i < nterm; i++) {
+    free (b[i]);
+    free (c[i]);
+  }
+  free (b);
+  free (c);
+  free (s);
+  return (FALSE);
+}
Index: /branches/sc_branches/pantasks_condor/cmd.data/fit2d.c
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.data/fit2d.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.data/fit2d.c	(revision 34783)
@@ -0,0 +1,293 @@
+# include "data.h"
+
+int fit2d (int argc, char **argv) {
+  
+  double **c, **b, **s, X, Y, dZ, dZ2;
+  double ClipNSigma, mean, sigma, maxsigma;
+  int k, K, i, j, n, Npt, Nmask, nx, ny, nterm, mterm, wterm, order;
+  int N, Weight, Quiet, ClipNiter, VERBOSE;
+  opihi_flt *x, *y, *z, *dz, *zfit, *zf; 
+  char name[64], *mask;
+  Vector *xvec, *yvec, *zvec, *dzvec;
+
+  VERBOSE = FALSE;
+  if ((N = get_argument (argc, argv, "-v"))) {
+    VERBOSE = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+
+  Quiet = FALSE;
+  if ((N = get_argument (argc, argv, "-q"))) {
+    Quiet = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+  if ((N = get_argument (argc, argv, "-quiet"))) {
+    Quiet = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+
+  ClipNSigma = 0;
+  ClipNiter  = 1;
+  if ((N = get_argument (argc, argv, "-clip"))) {
+    remove_argument (N, &argc, argv);
+    ClipNSigma = atof(argv[N]);
+    remove_argument (N, &argc, argv);
+    ClipNiter  = atof(argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  dz = NULL;
+  dzvec = NULL;
+  Weight = FALSE;
+  if ((N = get_argument (argc, argv, "-dz"))) {
+    remove_argument (N, &argc, argv);
+    if ((dzvec = SelectVector (argv[N], OLDVECTOR, TRUE)) == NULL) return (FALSE);    
+    remove_argument (N, &argc, argv);
+    Weight = TRUE;
+  }
+
+  if (argc != 5) {
+    gprint (GP_ERR, "USAGE: fit2d x y z order [-dz wt] [-quiet/-q] [-clip Nsigma Niter]\n");
+    return (FALSE);
+  }
+
+  if ((xvec = SelectVector (argv[1], OLDVECTOR, TRUE)) == NULL) return (FALSE);    
+  if ((yvec = SelectVector (argv[2], OLDVECTOR, TRUE)) == NULL) return (FALSE);    
+  if ((zvec = SelectVector (argv[3], OLDVECTOR, TRUE)) == NULL) return (FALSE);    
+
+  if (xvec[0].Nelements != yvec[0].Nelements) {
+    gprint (GP_ERR, "vectors must have same length\n");
+    return (FALSE);
+  }
+  if (xvec[0].Nelements != zvec[0].Nelements) {
+    gprint (GP_ERR, "vectors must have same length\n");
+    return (FALSE);
+  }
+  CastVector (xvec, OPIHI_FLT);
+  CastVector (yvec, OPIHI_FLT);
+  CastVector (zvec, OPIHI_FLT);
+
+  if (Weight) {
+    CastVector (dzvec, OPIHI_FLT);
+    if (xvec[0].Nelements != dzvec[0].Nelements) {
+      gprint (GP_ERR, "vectors must have same length\n");
+      return (FALSE);
+    }
+  }
+  
+  order = atof (argv[4]);
+  nterm = order + 1;
+  wterm = nterm*(nterm + 1)/2;
+  mterm = 2*order + 1;
+
+  ALLOCATE (zfit, opihi_flt, xvec[0].Nelements);
+  ALLOCATE (mask, char, xvec[0].Nelements);
+  memset (mask, 0, xvec[0].Nelements);
+
+  /* allocate the summation matrices */
+  ALLOCATE (s, double *, mterm);
+  ALLOCATE (b, double *, wterm);
+  ALLOCATE (c, double *, wterm);
+  for (i = 0; i < wterm; i++) {
+    ALLOCATE (c[i], double, wterm);
+    ALLOCATE (b[i], double, 1);
+  }
+  for (i = 0; i < mterm; i++) {
+    ALLOCATE (s[i], double, mterm);
+  }
+
+  for (N = 0; N < ClipNiter; N++) {
+
+    /* init registers for current pass */
+    // XXX this was incorrectly using nterm (missing 1 row)
+    for (i = 0; i < wterm; i++) {
+      memset (c[i], 0, wterm*sizeof(double));
+      memset (b[i], 0, sizeof(double));
+    }
+    for (i = 0; i < mterm; i++) {
+      memset (s[i], 0, mterm*sizeof(double));
+    }
+
+    x = xvec[0].elements.Flt;
+    y = yvec[0].elements.Flt;
+    z = zvec[0].elements.Flt;
+    if (Weight) dz = dzvec[0].elements.Flt;
+
+    /* add up the x,y values */
+    for (i = 0; i < xvec[0].Nelements; i++, x++, y++) {
+      if (mask[i]) continue;
+      if (!finite(*x) || !finite(*y)) continue;
+      dZ = 1.0;
+      if (Weight) { 
+	dZ = 1.0 / SQ(*dz);
+	dz ++;
+      }
+      Y = X = 1*dZ;
+      for (ny = 0; ny < mterm; ny++) {
+	X = Y;
+	for (nx = 0; nx < mterm - ny; nx++) {
+	  s[nx][ny] += X;
+	  X = X * (*x);
+	}
+	Y = Y * (*y);
+      }
+    }
+
+    /* add up the z values */
+    x = xvec[0].elements.Flt;
+    y = yvec[0].elements.Flt;
+    z = zvec[0].elements.Flt;
+    for (i = 0; i < xvec[0].Nelements; i++, x++, y++, z++) {
+      if (mask[i]) continue;
+      if (!finite(*x) || !finite(*y)) continue;
+      dZ = 1.0;
+      if (Weight) { 
+	dZ = 1.0 / SQ(*dz);
+	dz ++;
+      }
+      Y = X = *z*dZ;
+      for (j = 0, ny = 0; ny < nterm; ny++) {
+	X = Y;
+	for (nx = 0; nx < nterm - ny; nx++, j++) {
+	  b[j][0] += X;
+	  X = X * (*x);
+	}
+	Y = Y * (*y);
+      }
+    }
+
+    /* re-sort mterm x mterm matrix to wterm matrix */
+    for (k = j = 0; j < nterm; j++) {
+      for (i = 0; i < nterm - j; i++, k++) {
+	for (K = ny = 0; ny < nterm; ny++) {
+	  for (nx = 0; nx < nterm - ny; nx++, K++) {
+	    c[K][k] = s[nx+i][ny+j];
+	  }
+	}
+      }
+    }
+
+    /** test print **/
+    if (VERBOSE) {
+      for (i = 0; i < wterm; i++) {
+	for (j = 0; j < wterm; j++) {
+	  gprint (GP_ERR, "%g  ", c[i][j]);
+	}
+	gprint (GP_ERR, "\n");
+      }
+      gprint (GP_ERR, "-----\n");
+    }
+
+    dgaussjordan (c, b, wterm, 1);
+
+    /** test print **/
+    if (VERBOSE) {
+      for (i = ny = 0; ny < nterm; ny++) {
+	for (nx = 0; nx < nterm - ny; nx++, i++) {
+	  gprint (GP_ERR, "x^%d y^%d: %g\n", nx, ny, b[i][0]);
+	}
+      }
+    }
+
+    /** test print **/
+    if (VERBOSE) {
+      for (i = 0; i < wterm; i++) {
+	for (j = 0; j < wterm; j++) {
+	  gprint (GP_ERR, "%g  ", c[i][j]);
+	}
+	gprint (GP_ERR, "\n");
+      }
+    }
+
+    /* the b[][0] terms are in the following order:
+       y^0 x^0, y^0 x^1, ... y^0 x^N
+       y^1 x^0, y^1 x^1, ... y^1 x^N
+       ...
+       y^N x^0, y^N x^1, ... y^N x^N
+    */
+    /* generate fitted values */
+    x = xvec[0].elements.Flt;
+    y = yvec[0].elements.Flt;
+    zf = zfit;
+    for (n = 0; n < xvec[0].Nelements; n++, x++, y++, zf++) {
+      if (!finite(*x) || !finite(*y)) continue;
+      *zf = 0;
+      Y = X = 1;
+      for (i = ny = 0; ny < nterm; ny++) {
+	Y = X;
+	for (nx = 0; nx < nterm - ny; nx++, i++) {
+	  *zf += b[i][0]*Y;
+	  Y = Y * (*y);
+	}
+	X = X * (*x);
+      }
+    }
+
+    /* measure fit residual scatter */
+    x  = xvec[0].elements.Flt;
+    y  = yvec[0].elements.Flt;
+    z  = zvec[0].elements.Flt;
+    zf = zfit;
+    dZ = dZ2 = 0;
+    for (i = Npt = 0; i < xvec[0].Nelements; i++, x++, y++, z++, zf++) {
+      if (mask[i]) continue;
+      if (!finite(*x) || !finite(*y)) continue;
+      dZ  += (*z - *zf);
+      dZ2 += SQ(*z - *zf);
+      Npt ++;
+    }
+    mean  = dZ / Npt;
+    sigma = sqrt (fabs(dZ2/Npt - SQ(mean)));
+    maxsigma = ClipNSigma * sigma;
+
+    if (VERBOSE) gprint (GP_ERR, "mean: %g, sigma: %g, maxsigma: %g\n", mean, sigma, maxsigma);
+
+    /* mask outlier points */
+    x  = xvec[0].elements.Flt;
+    y  = yvec[0].elements.Flt;
+    z  = zvec[0].elements.Flt;
+    zf = zfit;
+    Nmask = 0;
+    for (i = 0; ClipNSigma && (i < xvec[0].Nelements); i++, x++, y++, z++, zf++) {
+      dZ = (*z - *zf);
+      if (fabs(dZ) > maxsigma) {
+	mask[i] = TRUE;
+	Nmask ++;
+      } else {
+	mask[i] = FALSE;
+      }	
+    }
+    if (VERBOSE) gprint (GP_ERR, "pass: %d, Nmask: %d\n", N, Nmask);
+  }
+
+  if (!Quiet) gprint (GP_ERR, "z = ");
+  for (N = ny = 0; ny < nterm; ny++) {
+    for (nx = 0; nx < nterm - ny; nx++, N++) {
+      sprintf (name, "CX%dY%d", nx, ny);
+      set_variable (name, b[N][0]);
+      if (!Quiet) gprint (GP_ERR, "%f x^%d y^%d  ", b[N][0], nx, ny);
+    }
+  }
+  sprintf (name, "Cnn");
+  set_variable (name, (double) order);
+  
+  if (!Quiet) gprint (GP_ERR, "\n");
+
+  /* free internal data */
+  free (zfit);
+  free (mask);
+
+  for (i = 0; i < wterm; i++) {
+    free (c[i]);
+    free (b[i]);
+  }
+  free (b);
+  free (c);
+
+  for (i = 0; i < mterm; i++) {
+    free (s[i]);
+  }
+  free (s);
+
+  return (TRUE);
+}
Index: /branches/sc_branches/pantasks_condor/cmd.data/fit3d.c
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.data/fit3d.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.data/fit3d.c	(revision 34783)
@@ -0,0 +1,352 @@
+# include "data.h"
+
+int fit3d (int argc, char **argv) {
+  
+  double **c, **b, ***s, X, Y, Z, dF1, dF2;
+  double ClipNSigma, mean, sigma, maxsigma;
+  int ix, iy, i, j, k, n, Npt, Nmask, nx, ny, nz, nterm, mterm, wterm, order;
+  int N, Weight, Quiet, ClipNiter, VERBOSE;
+  opihi_flt *x, *y, *z, *F, *dFv, *Ffit, *Ff; 
+  char name[64], *mask;
+  Vector *xvec, *yvec, *zvec, *Fvec, *dFvec;
+
+  VERBOSE = FALSE;
+  if ((N = get_argument (argc, argv, "-v"))) {
+    VERBOSE = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+
+  Quiet = FALSE;
+  if ((N = get_argument (argc, argv, "-q"))) {
+    Quiet = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+  if ((N = get_argument (argc, argv, "-quiet"))) {
+    Quiet = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+
+  ClipNSigma = 0;
+  ClipNiter  = 1;
+  if ((N = get_argument (argc, argv, "-clip"))) {
+    remove_argument (N, &argc, argv);
+    ClipNSigma = atof(argv[N]);
+    remove_argument (N, &argc, argv);
+    ClipNiter  = atof(argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  dFv = NULL;
+  dFvec = NULL;
+  Weight = FALSE;
+  if ((N = get_argument (argc, argv, "-dF"))) {
+    remove_argument (N, &argc, argv);
+    if ((dFvec = SelectVector (argv[N], OLDVECTOR, TRUE)) == NULL) return (FALSE);    
+    remove_argument (N, &argc, argv);
+    Weight = TRUE;
+  }
+
+  if (argc != 6) {
+    gprint (GP_ERR, "USAGE: fit x y z F order [-dF wt]\n");
+    return (FALSE);
+  }
+
+  if ((xvec = SelectVector (argv[1], OLDVECTOR, TRUE)) == NULL) return (FALSE);    
+  if ((yvec = SelectVector (argv[2], OLDVECTOR, TRUE)) == NULL) return (FALSE);    
+  if ((zvec = SelectVector (argv[3], OLDVECTOR, TRUE)) == NULL) return (FALSE);    
+  if ((Fvec = SelectVector (argv[4], OLDVECTOR, TRUE)) == NULL) return (FALSE);    
+
+  if (xvec[0].Nelements != yvec[0].Nelements) {
+    gprint (GP_ERR, "vectors must have same length\n");
+    return (FALSE);
+  }
+  if (xvec[0].Nelements != zvec[0].Nelements) {
+    gprint (GP_ERR, "vectors must have same length\n");
+    return (FALSE);
+  }
+  if (xvec[0].Nelements != Fvec[0].Nelements) {
+    gprint (GP_ERR, "vectors must have same length\n");
+    return (FALSE);
+  }
+  CastVector (xvec, OPIHI_FLT);
+  CastVector (yvec, OPIHI_FLT);
+  CastVector (zvec, OPIHI_FLT);
+  CastVector (Fvec, OPIHI_FLT);
+
+  if (Weight) {
+    if (xvec[0].Nelements != dFvec[0].Nelements) {
+      gprint (GP_ERR, "vectors must have same length\n");
+      return (FALSE);
+    }
+    CastVector (dFvec, OPIHI_FLT);
+  }
+  
+  order = atof (argv[5]);
+  nterm = order + 1;
+  wterm = (order + 3)*(order + 2)*(order + 1)/6;
+  mterm = (order + 2)*(order + 1)/2;
+
+  ALLOCATE (Ffit, opihi_flt, xvec[0].Nelements);
+  ALLOCATE (mask, char, xvec[0].Nelements);
+  memset (mask, 0, xvec[0].Nelements);
+
+  /* allocate the summation matrices */
+  ALLOCATE (b, double *, wterm);
+  ALLOCATE (c, double *, wterm);
+  for (i = 0; i < wterm; i++) {
+    ALLOCATE (c[i], double, wterm);
+    ALLOCATE (b[i], double, 1);
+  }
+
+  /* we initially sum the values in a cube */
+  ALLOCATE (s, double **, mterm);
+  for (i = 0; i < mterm; i++) {
+    ALLOCATE (s[i], double *, mterm);
+    for (j = 0; j < mterm; j++) {
+      ALLOCATE (s[i][j], double, mterm);
+    }
+  }
+
+  for (N = 0; N < ClipNiter; N++) {
+
+    /* init registers for current pass */
+    for (i = 0; i < wterm; i++) {
+      memset (c[i], 0, wterm*sizeof(double));
+      memset (b[i], 0, sizeof(double));
+    }
+    for (i = 0; i < mterm; i++) {
+      for (j = 0; j < mterm; j++) {
+	memset (s[i][j], 0, mterm*sizeof(double));
+      }
+    }
+
+    x = xvec[0].elements.Flt;
+    y = yvec[0].elements.Flt;
+    z = zvec[0].elements.Flt;
+    F = Fvec[0].elements.Flt;
+    if (Weight) dFv = dFvec[0].elements.Flt;
+
+    /* add up the x,y,z values */
+    for (i = 0; i < xvec[0].Nelements; i++, x++, y++, z++, F++) {
+      if (mask[i]) continue;
+      if (!finite(*x)) continue;
+      if (!finite(*y)) continue;
+      if (!finite(*z)) continue;
+      if (!finite(*F)) continue;
+      dF1 = 1.0;
+      if (Weight) { 
+	dF1 = 1.0 / SQ(*dFv);
+	dFv ++;
+      }
+      X = Y = Z = dF1;
+      for (nz = 0; nz < mterm; nz++) {
+	Y = Z;
+	for (ny = 0; ny < mterm - nz; ny++) {
+	  X = Y;
+	  for (nx = 0; nx < mterm - nz - ny; nx++) {
+	    s[nx][ny][nz] += X;
+	    X = X * (*x);
+	  }
+	  Y = Y * (*y);
+	}
+	Z = Z * (*z);
+      }
+    }
+
+    /* add up the F values */
+    x = xvec[0].elements.Flt;
+    y = yvec[0].elements.Flt;
+    z = zvec[0].elements.Flt;
+    F = Fvec[0].elements.Flt;
+    for (i = 0; i < xvec[0].Nelements; i++, x++, y++, z++, F++) {
+      if (mask[i]) continue;
+      if (!finite(*x)) continue;
+      if (!finite(*y)) continue;
+      if (!finite(*z)) continue;
+      if (!finite(*F)) continue;
+      dF1 = 1.0;
+      if (Weight) { 
+	dF1 = 1.0 / SQ(*dFv);
+	dFv ++;
+      }
+      X = Y = Z = *F*dF1;
+      j = 0;
+      for (nz = 0; nz < nterm; nz++) {
+	Y = Z;
+	for (ny = 0; ny < nterm - nz; ny++) {
+	  X = Y;
+	  for (nx = 0; nx < nterm - ny - nz; nx++, j++) {
+	    b[j][0] += X;
+	    X = X * (*x);
+	  }
+	  Y = Y * (*y);
+	}
+	Z = Z * (*z);
+      }
+    }
+
+    /* re-sort mterm x mterm matrix to wterm matrix */
+    ix = 0;
+    for (k = 0; k < nterm; k++) {
+      for (j = 0; j < nterm - k; j++) {
+	for (i = 0; i < nterm - k - j; i++, ix++) {
+	  iy = 0;
+	  for (nz = 0; nz < nterm; nz++) {
+	    for (ny = 0; ny < nterm - nz; ny++) {
+	      for (nx = 0; nx < nterm - ny - nz; nx++, iy++) {
+		c[ix][iy] = s[nx+i][ny+j][nz+k];
+	      }
+	    }
+	  }
+	}
+      }
+    }
+
+    /** test print **/
+    if (VERBOSE) {
+      for (i = 0; i < wterm; i++) {
+	for (j = 0; j < wterm; j++) {
+	  gprint (GP_ERR, "%g  ", c[i][j]);
+	}
+	gprint (GP_ERR, "\n");
+      }
+      gprint (GP_ERR, "-----\n");
+    }
+
+    dgaussjordan (c, b, wterm, 1);
+
+    /** test print **/
+    if (VERBOSE) {
+      i = 0;
+      for (nz = 0; nz < nterm; nz++) {
+	for (ny = 0; ny < nterm - nz; ny++) {
+	  for (nx = 0; nx < nterm - nz - ny; nx++, i++) {
+	    gprint (GP_ERR, "x^%d y^%d z^%d: %g\n", nx, ny, nz, b[i][0]);
+	  }
+	}
+      }
+    }
+
+    /** test print **/
+    if (VERBOSE) {
+      for (i = 0; i < wterm; i++) {
+	for (j = 0; j < wterm; j++) {
+	  gprint (GP_ERR, "%g  ", c[i][j]);
+	}
+	gprint (GP_ERR, "\n");
+      }
+    }
+
+    /* the b[][0] terms are in the following order:
+       y^0 x^0, y^0 x^1, ... y^0 x^N
+       y^1 x^0, y^1 x^1, ... y^1 x^N
+       ...
+       y^N x^0, y^N x^1, ... y^N x^N
+    */
+    /* generate fitted values */
+    x = xvec[0].elements.Flt;
+    y = yvec[0].elements.Flt;
+    z = zvec[0].elements.Flt;
+    Ff = Ffit;
+    for (n = 0; n < xvec[0].Nelements; n++, x++, y++, z++, Ff++) {
+      if (!finite(*x)) continue;
+      if (!finite(*y)) continue;
+      if (!finite(*z)) continue;
+      if (!finite(*F)) continue;
+      *Ff = 0;
+      Z = Y = X = 1;
+      i = 0;
+      for (nz = 0; nz < nterm; nz++) {
+	Y = Z;
+	for (ny = 0; ny < nterm - nz; ny++) {
+	  X = Y;
+	  for (nx = 0; nx < nterm - nz - ny; nx++, i++) {
+	    *Ff += b[i][0]*X;
+	    X = X * (*x);
+	  }
+	  Y = Y * (*y);
+	}
+	Z = Z * (*z);
+      }
+    }
+
+    /* measure fit residual scatter */
+    x  = xvec[0].elements.Flt;
+    y  = yvec[0].elements.Flt;
+    z  = zvec[0].elements.Flt;
+    F  = Fvec[0].elements.Flt;
+    Ff = Ffit;
+    dF1 = dF2 = 0;
+    for (i = Npt = 0; i < xvec[0].Nelements; i++, x++, y++, z++, F++, Ff++) {
+      if (mask[i]) continue;
+      if (!finite(*x)) continue;
+      if (!finite(*y)) continue;
+      if (!finite(*z)) continue;
+      if (!finite(*F)) continue;
+      dF1 += (*F - *Ff);
+      dF2 += SQ(*F - *Ff);
+      Npt ++;
+    }
+    mean  = dF1 / Npt;
+    sigma = sqrt (fabs(dF2/Npt - SQ(mean)));
+    maxsigma = ClipNSigma * sigma;
+
+    if (VERBOSE) gprint (GP_ERR, "mean: %g, sigma: %g, maxsigma: %g\n", mean, sigma, maxsigma);
+
+    /* mask outlier points */
+    x  = xvec[0].elements.Flt;
+    y  = yvec[0].elements.Flt;
+    z  = zvec[0].elements.Flt;
+    F  = Fvec[0].elements.Flt;
+    Ff = Ffit;
+    Nmask = 0;
+    for (i = 0; ClipNSigma && (i < xvec[0].Nelements); i++, x++, y++, z++, Ff++) {
+      dF1 = (*F - *Ff);
+      if (fabs(dF1) > maxsigma) {
+	mask[i] = TRUE;
+	Nmask ++;
+      } else {
+	mask[i] = FALSE;
+      }	
+    }
+    if (VERBOSE) gprint (GP_ERR, "pass: %d, Nmask: %d\n", N, Nmask);
+  }
+
+  if (!Quiet) gprint (GP_ERR, "F = ");
+
+  N = 0;
+  for (nz = 0; nz < nterm; nz++) {
+    for (ny = 0; ny < nterm - nz; ny++) {
+      for (nx = 0; nx < nterm - nz - ny; nx++, N++) {
+	sprintf (name, "CX%dY%dZ%d", nx, ny, nz);
+	set_variable (name, b[N][0]);
+	if (!Quiet) gprint (GP_ERR, "%f x^%d y^%d z^%d ", b[N][0], nx, ny, nz);
+      }
+    }
+  }
+  sprintf (name, "Cnnn");
+  set_variable (name, (double) order);
+  
+  if (!Quiet) gprint (GP_ERR, "\n");
+
+  /* free internal data */
+  free (Ffit);
+  free (mask);
+
+  for (i = 0; i < wterm; i++) {
+    free (c[i]);
+    free (b[i]);
+  }
+  free (b);
+  free (c);
+
+  for (i = 0; i < mterm; i++) {
+    for (j = 0; j < mterm; j++) {
+      free (s[i][j]);
+    }
+    free (s[i]);
+  }
+  free (s);
+
+  return (TRUE);
+}
Index: /branches/sc_branches/pantasks_condor/cmd.data/gaussdeviate.c
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.data/gaussdeviate.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.data/gaussdeviate.c	(revision 34783)
@@ -0,0 +1,58 @@
+# include "data.h"
+
+int gaussdeviate (int argc, char **argv) {
+  
+  int i, Npts;
+  double mean, sigma;
+  Vector *vec;
+
+  if (argc != 5) goto usage;
+
+  if ((vec = SelectVector (argv[1], ANYVECTOR, TRUE)) == NULL) return (FALSE);    
+
+  Npts = atoi (argv[2]);
+  mean = atof (argv[3]);
+  sigma = atof (argv[4]);
+
+  ResetVector (vec, OPIHI_FLT, Npts);
+
+  gauss_init (2048);
+  for (i = 0; i < Npts; i++) {
+    vec[0].elements.Flt[i] = rnd_gauss (mean, sigma);
+  }
+  return (TRUE);
+
+ usage:
+  gprint (GP_ERR, "USAGE: gaussdeviate (vector) Npts mean sigma\n");
+  return (FALSE);
+    
+}
+
+double int_gauss (int i);
+
+int gaussintegral (int argc, char **argv) {
+  
+  int i, Npts;
+  Vector *vec;
+
+  if (argc != 5) goto usage;
+
+  if ((vec = SelectVector (argv[1], ANYVECTOR, TRUE)) == NULL) return (FALSE);    
+
+  Npts = atoi (argv[2]);
+  // mean = atof (argv[3]);
+  // sigma = atof (argv[4]);
+
+  ResetVector (vec, OPIHI_FLT, Npts);
+
+  gauss_init (Npts);
+  for (i = 0; i < Npts; i++) {
+    vec[0].elements.Flt[i] = int_gauss (i);
+  }
+  return (TRUE);
+
+ usage:
+  gprint (GP_ERR, "USAGE: gaussintegral Npts mean sigma\n");
+  return (FALSE);
+    
+}
Index: /branches/sc_branches/pantasks_condor/cmd.data/gaussj.c
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.data/gaussj.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.data/gaussj.c	(revision 34783)
@@ -0,0 +1,81 @@
+# include "data.h"
+
+int gaussjordan (int argc, char **argv) {
+
+  float *m;
+  opihi_flt *vf;
+  opihi_int *vi;
+  double **a, **b;
+  int i, j, N, status, QUIET, isFloat;
+  Vector *B;
+  Buffer *A;
+
+  QUIET = FALSE;
+  if ((N = get_argument (argc, argv, "-q"))) {
+    QUIET = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+
+  if (argc != 3) goto usage;
+
+  if ((A = SelectBuffer (argv[1], OLDBUFFER, TRUE)) == NULL) return (FALSE);    
+  if ((B = SelectVector (argv[2], OLDVECTOR, TRUE)) == NULL) return (FALSE);    
+
+  N = B[0].Nelements;
+  if (A[0].matrix.Naxis[0] != N) goto usage;
+  if (A[0].matrix.Naxis[1] != N) goto usage;
+  
+  ALLOCATE (a, double *, N);
+  ALLOCATE (b, double *, N);
+  for (i = 0; i < N; i++) {
+    ALLOCATE (a[i], double, N);
+    ALLOCATE (b[i], double, 1);
+  }
+
+  isFloat = (B[0].type == OPIHI_FLT);
+  vf = B[0].elements.Flt;
+  vi = B[0].elements.Int;
+
+  m = (float *) A[0].matrix.buffer;
+  for (i = 0; i < N; i++) {
+    for (j = 0; j < N; j++) {
+      a[i][j] = m[i+j*N];
+    }
+    b[i][0] = isFloat ? vf[i] : vi[i]; 
+  }
+
+  status = dgaussjordan (a, b, N, 1);
+
+  // if dgaussjordan succeeds, replace the input values with the results
+  if (status) {
+    // output vector needs to be float, so re-cast it
+    ResetVector (B, OPIHI_FLT, N);
+    vf = B[0].elements.Flt;
+
+    for (i = 0; i < N; i++) {
+      for (j = 0; j < N; j++) {
+	m[i+j*N] = a[i][j];
+      }
+      vf[i] = b[i][0]; 
+    }
+  }
+
+  for (i = 0; i < N; i++) {
+    free (a[i]);
+    free (b[i]);
+  }
+  free (a);
+  free (b);
+
+  if (!status && !QUIET) {
+      gprint (GP_ERR, "gaussjordan: ill-conditioned matrix; input values are retained\n");
+  }
+  return (status);
+
+ usage:
+  gprint (GP_ERR, "USAGE: gaussj A B\n");
+  gprint (GP_ERR, "  solves Ax = B, returns 1/A in A and x in B\n");
+  gprint (GP_ERR, "  A must be square, B same dimensions\n");
+  return (FALSE);
+    
+}
Index: /branches/sc_branches/pantasks_condor/cmd.data/grid.c
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.data/grid.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.data/grid.c	(revision 34783)
@@ -0,0 +1,188 @@
+# include "data.h"
+
+int SetGridScales (double *major, double *minor, double range) {
+
+  double lrange, factor, mantis, fmantis, power;
+
+  lrange = log10(MAX(fabs(range), 1e-30));
+  factor = (int) (lrange);
+  if (lrange < 0) { factor -= 1; }
+  mantis = lrange - factor;
+  power = pow(10.0, factor);
+  fmantis = pow(10.0, mantis);
+  if ((fmantis >= 1.0) && (fmantis <=  2.0)) {
+    *major = 0.5 * power;
+    *minor = 0.1 * power;
+  }
+  if ((fmantis > 2.0) && (fmantis <=  4.0)) {
+    *major = 1.0 * power;
+    *minor = 0.2 * power;
+  }
+  if ((fmantis > 4.0) && (fmantis <=  6.0)) {
+    *major = 1.0 * power;
+    *minor = 0.5 * power;
+  }
+  if ((fmantis > 6.0) && (fmantis <=  10.0)) {
+    *major = 2.0 * power;
+    *minor = 0.5 * power;
+  }
+  return TRUE;
+}
+
+int grid (int argc, char **argv) {
+  
+  double range, major, minor, first, next;
+  int j, kapa, N, MinorTick, MajorTick;
+  Vector Xvec, Yvec;
+  Graphdata graphmode;
+
+  if (!style_args (&graphmode, &argc, argv, &kapa)) return (FALSE);
+
+  MajorTick = TRUE;
+  MinorTick = FALSE;
+  if ((N = get_argument (argc, argv, "-all"))) {
+    remove_argument (N, &argc, argv);
+    MinorTick = TRUE;
+  }
+
+  if (argc > 1) {
+    gprint (GP_ERR, "USAGE: grid [-n graph]\n");
+    return (FALSE);
+  }
+
+  N = 0;
+  SetVector (&Xvec, OPIHI_FLT, 200);
+  SetVector (&Yvec, OPIHI_FLT, 200);
+
+  major = minor = 1;
+  range = graphmode.xmax - graphmode.xmin;
+  SetGridScales (&major, &minor, range);
+  if (graphmode.xmin > 0)
+    first = minor + minor*((int)(graphmode.xmin/minor));
+  else 
+    first = -minor + minor*((int)(graphmode.xmin/minor));
+  if (minor*((int)(graphmode.xmin/minor)) == graphmode.xmin) {
+    first = graphmode.xmin;
+  }
+  
+  for (j = 0, next = first; next <= graphmode.xmax; j++) {
+    if ((fabs((int)(next/major) - (next/major)) < 0.5*(minor/major)) || (fabs ((int)((next + 0.5*minor)/major) - (next/major)) < 0.5*(minor/major))) {
+      if (MajorTick) {
+	/* major tick */
+	Xvec.elements.Flt[N] = next;
+	Yvec.elements.Flt[N] = graphmode.ymin;
+	N++;
+	if (N == Xvec.Nelements) {
+	  Xvec.Nelements += 200;
+	  Yvec.Nelements += 200;
+	  REALLOCATE (Xvec.elements.Flt, opihi_flt, Xvec.Nelements);
+	  REALLOCATE (Yvec.elements.Flt, opihi_flt, Yvec.Nelements);
+	}
+	Xvec.elements.Flt[N] = next;
+	Yvec.elements.Flt[N] = graphmode.ymax;
+	N++;
+	if (N == Xvec.Nelements) {
+	  Xvec.Nelements += 200;
+	  Yvec.Nelements += 200;
+	  REALLOCATE (Xvec.elements.Flt, opihi_flt, Xvec.Nelements);
+	  REALLOCATE (Yvec.elements.Flt, opihi_flt, Yvec.Nelements);
+	}
+      }
+    } else {
+      if (MinorTick) {
+	/* minor tick */
+	Xvec.elements.Flt[N] = next;
+	Yvec.elements.Flt[N] = graphmode.ymin;
+	N++;
+	if (N == Xvec.Nelements) {
+	  Xvec.Nelements += 200;
+	  Yvec.Nelements += 200;
+	  REALLOCATE (Xvec.elements.Flt, opihi_flt, Xvec.Nelements);
+	  REALLOCATE (Yvec.elements.Flt, opihi_flt, Yvec.Nelements);
+	}
+	Xvec.elements.Flt[N] = next;
+	Yvec.elements.Flt[N] = graphmode.ymax;
+	N++;
+	if (N == Xvec.Nelements) {
+	  Xvec.Nelements += 200;
+	  Yvec.Nelements += 200;
+	  REALLOCATE (Xvec.elements.Flt, opihi_flt, Xvec.Nelements);
+	  REALLOCATE (Yvec.elements.Flt, opihi_flt, Yvec.Nelements);
+	}
+      }
+    }
+    next += minor;
+  }
+
+  range = graphmode.ymax - graphmode.ymin;
+  SetGridScales (&major, &minor, range);
+  if (graphmode.ymin > 0)
+    first = minor + minor*((int)(graphmode.ymin/minor));
+  else 
+    first = -minor + minor*((int)(graphmode.ymin/minor));
+  if (minor*((int)(graphmode.ymin/minor)) == graphmode.ymin) {
+    first = graphmode.ymin;
+  }
+  
+  for (j = 0, next = first; next <= graphmode.ymax; j++) {
+    if ((fabs((int)(next/major) - (next/major)) < 0.5*(minor/major)) || (fabs ((int)((next + 0.5*minor)/major) - (next/major)) < 0.5*(minor/major))) {
+      if (MajorTick) {
+	/* major tick */
+	Xvec.elements.Flt[N] = graphmode.xmin;
+	Yvec.elements.Flt[N] = next;
+	N++;
+	if (N == Xvec.Nelements) {
+	  Xvec.Nelements += 200;
+	  Yvec.Nelements += 200;
+	  REALLOCATE (Xvec.elements.Flt, opihi_flt, Xvec.Nelements);
+	  REALLOCATE (Yvec.elements.Flt, opihi_flt, Yvec.Nelements);
+	}
+	Xvec.elements.Flt[N] = graphmode.xmax;
+	Yvec.elements.Flt[N] = next;
+	N++;
+	if (N == Xvec.Nelements) {
+	  Xvec.Nelements += 200;
+	  Yvec.Nelements += 200;
+	  REALLOCATE (Xvec.elements.Flt, opihi_flt, Xvec.Nelements);
+	  REALLOCATE (Yvec.elements.Flt, opihi_flt, Yvec.Nelements);
+	}
+      }
+    } else {
+      if (MinorTick) {
+	/* minor tick */
+	Xvec.elements.Flt[N] = graphmode.xmin;
+	Yvec.elements.Flt[N] = next;
+	N++;
+	if (N == Xvec.Nelements) {
+	  Xvec.Nelements += 200;
+	  Yvec.Nelements += 200;
+	  REALLOCATE (Xvec.elements.Flt, opihi_flt, Xvec.Nelements);
+	  REALLOCATE (Yvec.elements.Flt, opihi_flt, Yvec.Nelements);
+	}
+	Xvec.elements.Flt[N] = graphmode.xmax;
+	Yvec.elements.Flt[N] = next;
+	N++;
+	if (N == Xvec.Nelements) {
+	  Xvec.Nelements += 200;
+	  Yvec.Nelements += 200;
+	  REALLOCATE (Xvec.elements.Flt, opihi_flt, Xvec.Nelements);
+	  REALLOCATE (Yvec.elements.Flt, opihi_flt, Yvec.Nelements);
+	}
+      }
+    }
+    next += minor;
+  }
+
+  Xvec.Nelements = Yvec.Nelements = N;
+  graphmode.style = 2; /* points */
+  graphmode.ptype = 100; /* connect a pair */
+  graphmode.etype = 0;
+  PlotVectorPair (kapa, &Xvec, &Yvec, NULL, &graphmode);
+
+  free (Xvec.elements.Flt);
+  free (Yvec.elements.Flt);
+
+  return (TRUE);
+
+}
+
Index: /branches/sc_branches/pantasks_condor/cmd.data/gridify.c
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.data/gridify.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.data/gridify.c	(revision 34783)
@@ -0,0 +1,140 @@
+# include "data.h"
+
+int gridify (int argc, char **argv) {
+
+  int i, Nx, Ny, Xb, Yb, Normalize, N;
+  float Xmin, Xmax, dX, Ymin, Ymax, dY, initValue;
+  float *buf, *val, *cnt;
+  int *Nval;
+  Vector *vx, *vy, *vz;
+  opihi_flt *x, *y, *z;
+
+  Buffer *bf = NULL;
+  Buffer *ct = NULL;
+
+  Normalize = TRUE;
+  if ((N = get_argument (argc, argv, "-raw"))) {
+    remove_argument (N, &argc, argv);
+    Normalize = FALSE;
+    if ((ct = SelectBuffer (argv[N], ANYBUFFER, TRUE)) == NULL) return (FALSE);
+    remove_argument (N, &argc, argv);
+  }
+
+  initValue = 0.0;
+  if ((N = get_argument (argc, argv, "-init-value"))) {
+    remove_argument (N, &argc, argv);
+    initValue = atof(argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  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);
+  }
+  
+  if ((vx = SelectVector (argv[1], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((vy = SelectVector (argv[2], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((vz = SelectVector (argv[3], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((bf = SelectBuffer (argv[4], ANYBUFFER, TRUE)) == NULL) return (FALSE);
+
+  if (vx[0].Nelements != vy[0].Nelements) return (FALSE);
+  if (vx[0].Nelements != vz[0].Nelements) return (FALSE);
+
+  REQUIRE_VECTOR_FLT (vx, FALSE); 
+  REQUIRE_VECTOR_FLT (vy, FALSE); 
+  REQUIRE_VECTOR_FLT (vz, FALSE); 
+
+  if (isnan(dX)) {
+    Xmin = 0;
+    Xmax = bf[0].matrix.Naxis[0];
+    dX = 1;
+  }
+
+  if (isnan(dY)) {
+    Ymin = 0;
+    Ymax = bf[0].matrix.Naxis[1];
+    dY = 1;
+  }
+
+  Nx = (Xmax - Xmin) / dX;
+  Ny = (Ymax - Ymin) / dY;
+  
+  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);
+  bzero (val, Nx*Ny*sizeof(float));
+  ALLOCATE (Nval, int, Nx*Ny);
+  bzero (Nval, Nx*Ny*sizeof(int));
+
+  x = vx[0].elements.Flt;
+  y = vy[0].elements.Flt;
+  z = vz[0].elements.Flt;
+  for (i = 0; i < vx[0].Nelements; i++, x++, y++, z++) {
+    Xb = (*x - Xmin) / dX;
+    Yb = (*y - Ymin) / dY;
+    if (Xb < 0) continue;
+    if (Yb < 0) continue;
+    if (Xb >= Nx) continue;
+    if (Yb >= Ny) continue;
+    val[Xb + Yb*Nx] += *z;
+    Nval[Xb + Yb*Nx]++;
+  }
+
+  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 (Nval[i] == 0) continue;
+    buf[i] = val[i] / Nval[i];
+  }
+
+  free (val);
+  free (Nval);
+
+  return (TRUE);
+
+}
Index: /branches/sc_branches/pantasks_condor/cmd.data/grow.c
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.data/grow.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.data/grow.c	(revision 34783)
@@ -0,0 +1,50 @@
+# include "data.h"
+
+int grow (int argc, char **argv) {
+  
+  int i, j, nx, ny, npix, nsum, Nx, Ny;
+  float *input, *output;
+  Buffer *in;
+
+  if (argc != 3) {
+    gprint (GP_ERR, "USAGE: grow (input) (npix)\n");
+    return (FALSE);
+  }
+  
+  if ((in  = SelectBuffer (argv[1], OLDBUFFER, TRUE)) == NULL) return (FALSE);
+  npix = atoi (argv[2]);
+
+  Nx = in[0].matrix.Naxis[0];
+  Ny = in[0].matrix.Naxis[1];
+  ALLOCATE (output, float, Nx*Ny);
+  memset (output, 0, Nx*Ny*sizeof(float));
+
+  input = (float *) in[0].matrix.buffer;
+
+  for (j = 0; j < Ny; j++) {
+    for (i = 0; i < Nx; i++) {
+      nsum = 0;
+      for (ny = -1; ny <= +1; ny++) {
+	if (j + ny < 0) continue;
+	if (j + ny >= Ny) continue;
+	for (nx = -1; nx <= +1; nx++) {
+	  if (i + nx < 0) continue;
+	  if (i + nx >= Nx) continue;
+	  if (!nx && !ny) continue;
+	  if (input[i + nx + Nx*(j + ny)] == 0) continue;
+	  nsum ++;
+	}
+      }
+      if (nsum >= npix) {
+	output[i + j*Nx] = 1;
+      } else {
+	output[i + j*Nx] = input[i + j*Nx];
+      }
+    }
+  }
+  
+  free (in[0].matrix.buffer);
+  in[0].matrix.buffer = (char *) output;
+  return (TRUE);
+}
+
Index: /branches/sc_branches/pantasks_condor/cmd.data/help/Graphics
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.data/help/Graphics	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.data/help/Graphics	(revision 34783)
@@ -0,0 +1,20 @@
+
+  Kapa, the graphics window.
+
+  Kapa is the program which Status uses to display plots on an X
+terminal.  There are actually 5 graphics windows available to Status,
+though several functions write to specific windows by default.  All
+plots of sky coordinates are displayed on window 0, while 
+functions which plot other types of data use window 1.  Most functions
+have an option -n which allows the user to specify which Kapa window.
+If it is not specified, the last window used will receive the action.
+
+  Below are the available Resources.  
+
+  kii*Foreground:			white
+  kii*Background:			maroon
+  kii*geometry:				540x540+10+10
+
+  See Also: box, clear, limits, plot
+
+
Index: /branches/sc_branches/pantasks_condor/cmd.data/help/Kii
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.data/help/Kii	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.data/help/Kii	(revision 34783)
@@ -0,0 +1,57 @@
+
+
+  Kii (= "picture" in Hawaiian) is the program which Mana uses to
+display images on an X terminal.  The window consists of a main region
+where the image is displayed, a small "zoom box" where a magnified
+view of the region around the cursor is shown, a colorbar showing the
+current color map across the top of the window, a status box where the
+coordinates and pixel values are displayed, and several buttons.
+Clicking with the mounse in the different regions produces different
+effects.
+
+  Image Window:  Clicking with the left mouse button (mouse-1)
+recenters the image at the specified location.  Clicking with the
+right button (mouse-3) also recenters, but increases the zoom factor
+by one.  Clicking with the middle button centers and decreases the
+zoom factor.  
+
+  Colorbar: Clicking and draging with the left button (mouse-1) alters
+the color mapping.  Moving left-to-right slides the center of the
+color scale with the mouse.  Moving up-and-down squeezes or expands
+the color scale.  The middle button resets the color mapping.
+Clicking and dragging side-to-side with the right button expands or
+squeezes the color mapping.  **Note: the dynamic colormap cannot be
+used with a 24 or 32 bit visual.  The standard Linux XFree86 server
+does not support 8 bit visuals in 24 or 32 bit/pixel mode.  In this
+case, the Kii window must resort to a static colormap with the
+colorbar disabled.  In Linux, it is possible to run startx with the
+following command-line options to force the 8 bit/pixel mode:
+# startx -- -bpp 8 
+This assumes that you have an 8bpp Display mode set in the XF86Config
+file.  If not, you can just duplicate the 24 or 32 bpp Display modes
+which will enable this option.
+
+  Buttons:
+    PS: this button creates a PostScript version of the image.
+    Grey: this button makes the colormap a greyscale.
+    Rainbow: this button makes the colormap a rainbow.
+    Puns: this button make the colormap a blue and yellow mapping.
+    Recenter: this button recenters the image and resets the zoom.
+
+  Kii also recognizes several X Resource values:
+
+  Below are the available Resources (with Gene's default values).  Unlike
+most standard X programs, the geometry resource specifies the size of
+the Image Window, not the borders of the entire window.  Valid values
+for the colormap are: greyscale, grayscale (photonegative), -greyscale,
+-grayscale (photopositive), Puns, GoBears (buff 'n blue colormap),
+Rainbow (a rainbow map).  
+
+  kii*Foreground:			white
+  kii*Background:			maroon
+  kii*Colormap:				greyscale
+  kii*geometry:				540x540+10+10
+  
+
+  See Also: tv, cursor
+
Index: /branches/sc_branches/pantasks_condor/cmd.data/help/Math
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.data/help/Math	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.data/help/Math	(revision 34783)
@@ -0,0 +1,58 @@
+
+  Opihi shell math operations:
+
+  Mana will evaluate scalar (1D) arithmetic expresions in two
+situations.  To set a variable equal to a math operation, use the
+syntax is:
+
+  $foo = expression
+
+where "expression" is an arithmetic expression on numbers and
+variables, including a variety of functions (see below).  After
+performing the arithmetic and setting the variable to the resulting
+value, mana returns the prompts without performing any further
+command.  Mana will also evaluate any arithmetic expression enclosed
+in curly brackets before attempting to perform the given command.  For
+example, if the variables $MEAN and $SIGMA contain the mean and
+standard deviation of an image, it would be possible to display the
+image with a zero and range based on $MEAN and $SIGMA like this:
+
+  tv image {$MEAN - 1.5*$SIGMA} {5*$SIGMA}
+
+
+The two expressions in curly brackets are evaluated and the resulting
+numbers passed as arguments to the "tv" command.
+
+  See Also:  Variables, tv
+
+
+list of valid math operations:
+
+  binary operators:
+  * (times)
+  / (divided by)
+  + (plus)
+  - (minus)
+  ^ (to the power of: 2^3 = 8)
+
+  unary operators:
+  exp (e to the power of)
+  ten (10 to the power of)
+  ln  (natural logarithm)
+  log (log base 10)
+
+  angles in radians:
+  sin (sine)
+  cos (cosine)
+  tan (tangent)
+  asin (arcsin)
+  acos (arccos)
+  atan (arctan)
+
+  angles in degrees:
+  dsin (sine)
+  dcos (cosine)
+  dtan (tangent)
+  dasin (arcsin)
+  dacos (arccos)
+  datan (arctan)
Index: /branches/sc_branches/pantasks_condor/cmd.data/help/applyfit
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.data/help/applyfit	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.data/help/applyfit	(revision 34783)
@@ -0,0 +1,10 @@
+
+  applyfit x y
+
+  apply the results of a polynomial fit (stored in variables 
+  $C0, $C1, etc, with $Cn representing the order of the fit).
+  The second vector will contain the function value at the 
+  corresponding x coordinates of the first vector.
+
+  See also: fit
+
Index: /branches/sc_branches/pantasks_condor/cmd.data/help/box
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.data/help/box	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.data/help/box	(revision 34783)
@@ -0,0 +1,11 @@
+
+  box [-n Nwindow]
+
+  draw a coordinate box on the current kapa window, or on the window
+specified by -n (0 - 4).
+
+  See also: Kapa
+
+
+
+  
Index: /branches/sc_branches/pantasks_condor/cmd.data/help/buffers
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.data/help/buffers	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.data/help/buffers	(revision 34783)
@@ -0,0 +1,9 @@
+
+  buffers
+
+  buffers gives information about the currently allocated buffers.  
+A * next to the filename means the data has been altered from the
+originally read in image.
+
+  See also: memory
+
Index: /branches/sc_branches/pantasks_condor/cmd.data/help/center
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.data/help/center	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.data/help/center	(revision 34783)
@@ -0,0 +1,10 @@
+
+  center (xpix) (ypix) [magnification]
+
+  "center" centers the Kii window at the specified pixel coordinates.
+Specifying a magnification will change the image scale to the
+specified magnification.  If you do not specify a magnification
+(default), the image scale will remain the same.
+
+  See also: Kii, tv
+
Index: /branches/sc_branches/pantasks_condor/cmd.data/help/clear
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.data/help/clear	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.data/help/clear	(revision 34783)
@@ -0,0 +1,5 @@
+
+   clear
+
+   clear the Kapa window
+
Index: /branches/sc_branches/pantasks_condor/cmd.data/help/clip
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.data/help/clip	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.data/help/clip	(revision 34783)
@@ -0,0 +1,8 @@
+
+  clip (buffer) [min Vmin max Vmax] [-inf val] [-nan val]
+
+  clip values in an image.  A min/max clip can be applied, 
+  in which case all values above max are set to Vmax and all
+  below min are set to Vmin.  -nan and -inf flags will set all 
+  instances of NaN or any non-finite values to the given value.
+
Index: /branches/sc_branches/pantasks_condor/cmd.data/help/close
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.data/help/close	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.data/help/close	(revision 34783)
@@ -0,0 +1,14 @@
+
+
+   close [-n win] [-g Kapa win] [-i Kii win]
+
+   Closes the specified graphics window
+
+   Default: the most recently activated window (only works
+    for the first call)
+
+   [win]: the index of the window to close
+
+   -n: any graphics or plotting window
+   -g: the Kapa plotting window
+   -i: the Kii image window
Index: /branches/sc_branches/pantasks_condor/cmd.data/help/concat
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.data/help/concat	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.data/help/concat	(revision 34783)
@@ -0,0 +1,7 @@
+
+   concat v1 v2
+
+   Concatenates the values of vector v1 to the end of vector
+    v2, increasing the length of v2.
+
+   See also: subset, set
Index: /branches/sc_branches/pantasks_condor/cmd.data/help/contour
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.data/help/contour	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.data/help/contour	(revision 34783)
@@ -0,0 +1,13 @@
+
+  contour <buffer> (overlay) level [Npix]
+
+  "contour" makes a contour plot from the given <buffer> at the
+specified level.  If Npix is specified, the image is rebinned by a
+factor of Npix in each direction.  The contour is drawn as a series of
+lines on the specified overlay.  The new lines are added to any
+existing shapes.  (Use "erase" first to erase all objects if desired).
+
+Valid overlays may be: 0, red, 1, green, 2, blue, 3, yellow.
+
+  See also: load, save, erase
+
Index: /branches/sc_branches/pantasks_condor/cmd.data/help/create
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.data/help/create	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.data/help/create	(revision 34783)
@@ -0,0 +1,9 @@
+
+   create (vector) (start) (end) [delta]
+
+   Create a vector of uniformly spaced values, starting at
+   (start) and going to (end).  By default the spacing is 1, but
+   may be chosen with the [delta] option.
+
+   Note: A vector cannot be created if an image buffer already
+    exists with the same name
Index: /branches/sc_branches/pantasks_condor/cmd.data/help/cumulative
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.data/help/cumulative	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.data/help/cumulative	(revision 34783)
@@ -0,0 +1,8 @@
+
+
+   cumulative (invec) (outvec)
+
+   Creates a vector (outvec) where the element N is the sum of the elements
+    0-N of (invec)
+
+   Note: The final element of (outvec) is always 0
Index: /branches/sc_branches/pantasks_condor/cmd.data/help/cursor
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.data/help/cursor	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.data/help/cursor	(revision 34783)
@@ -0,0 +1,13 @@
+
+  cursor
+
+  place cursor coordinates (and values ??) into Mana variables.  to do
+this, type cursor, then place the cursor on the desired spot in the
+Ki'i window and type a digit (0 - 9).  The coordinates of the cursor
+are then placed in the variables $Xn and $Yn (where n is the digit you
+typed).  To exit the cursor mode, type "q" (or "Q") in the Ki'i
+window.  
+
+  See Also: Kii, tv, center
+
+ 
Index: /branches/sc_branches/pantasks_condor/cmd.data/help/cut
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.data/help/cut	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.data/help/cut	(revision 34783)
@@ -0,0 +1,9 @@
+
+   cut buffer <X vector> <Y vector> <X|Y> sx sy nx ny
+
+   take a cut from an image and place it in a pair of vectors.
+   the <X vector> gets the pixel coordinate in the given direction,
+   the <Y vector> gets the pixel values.  <X|Y> specifies the 
+   direction of the cut.  the region sx, sy, nx, ny specifies the
+   region for the cut, with summation in the cross-direction.
+
Index: /branches/sc_branches/pantasks_condor/cmd.data/help/datafile
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.data/help/datafile	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.data/help/datafile	(revision 34783)
@@ -0,0 +1,6 @@
+
+   datafile (filename)
+
+   define a data file for subsequent vector reads.
+
+   See also: read
Index: /branches/sc_branches/pantasks_condor/cmd.data/help/delete
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.data/help/delete	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.data/help/delete	(revision 34783)
@@ -0,0 +1,10 @@
+
+  delete (buffer/vector/variable)
+
+  Delete the named buffer/vector/variable.
+
+  Warning: no second chances are given!
+
+  Note: Delete will remove the buffer/vector of a
+   given name first, then delete the variable of
+   the same name upon the second call.
Index: /branches/sc_branches/pantasks_condor/cmd.data/help/dimendown
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.data/help/dimendown	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.data/help/dimendown	(revision 34783)
@@ -0,0 +1,7 @@
+
+
+   dimendown (buffer) (vector) [-x/-y]
+
+   Compresses an image into a vector
+
+   The vector can be given either the x or y coordinates of the image
Index: /branches/sc_branches/pantasks_condor/cmd.data/help/dimenup
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.data/help/dimenup	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.data/help/dimenup	(revision 34783)
@@ -0,0 +1,5 @@
+
+
+   dimenup (vector) (buffer) (Nx) (Ny)
+
+   Expands a vector into an image with dimensions Nx,Ny
Index: /branches/sc_branches/pantasks_condor/cmd.data/help/dot
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.data/help/dot	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.data/help/dot	(revision 34783)
@@ -0,0 +1,7 @@
+
+
+   dot (x) (y)
+
+   Plots a single point
+
+   Uses the same style options as plot
Index: /branches/sc_branches/pantasks_condor/cmd.data/help/erase
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.data/help/erase	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.data/help/erase	(revision 34783)
@@ -0,0 +1,8 @@
+
+  erase (overlay)
+
+  "erase" erases all objects on the specified overlay.  
+  Valid overlays may be: all, red, green, blue, yellow.
+
+  See also: load, save, contour
+
Index: /branches/sc_branches/pantasks_condor/cmd.data/help/extract
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.data/help/extract	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.data/help/extract	(revision 34783)
@@ -0,0 +1,12 @@
+
+  extract <from> <to> sx sy nx ny sx sy nx ny
+
+  extract takes a portion of an image (buffer <from>) and creates a
+new image (buffer <to>).  The source region is defined by the first
+set of (sx sy nx ny), the resulting image and the location of the
+extracted image are defined by the second (sx sy nx ny).  This allows
+a portion of an image to be overlayed at a particular location in a
+larger image.
+
+  See also: Kii, tv
+
Index: /branches/sc_branches/pantasks_condor/cmd.data/help/fit
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.data/help/fit	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.data/help/fit	(revision 34783)
@@ -0,0 +1,15 @@
+
+   fit (-q) x y (order) (-dy wt) (-clip sig N)
+
+   perform a lease-square polynomial fit to the data defined 
+   by vectors x and y.  the coefficients are placed in the variables
+   $C0, $C1, ..., and the order is placed in $Cn.
+
+   order: order of fit
+   -q: quiets the fit (no output to window)
+   -dy: take into account the error vector wt
+   -clip: remove outliers beyond sig*sigma from the fit;
+           perform N iterations of this
+
+   See also: applyfit
+
Index: /branches/sc_branches/pantasks_condor/cmd.data/help/grid
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.data/help/grid	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.data/help/grid	(revision 34783)
@@ -0,0 +1,6 @@
+
+   grid (overlay) (buffer)
+
+   draw a coordinate grid for the given buffer in the 
+   given overlay in Kii.
+
Index: /branches/sc_branches/pantasks_condor/cmd.data/help/header
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.data/help/header	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.data/help/header	(revision 34783)
@@ -0,0 +1,8 @@
+
+   header (buffer)
+
+   print the header information for the given buffer.  The header
+   information is Meta-information associated with an image.  It is
+   well-defined for the FITS images, but may not exist for some of the
+   other possible data types.  
+
Index: /branches/sc_branches/pantasks_condor/cmd.data/help/histogram
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.data/help/histogram	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.data/help/histogram	(revision 34783)
@@ -0,0 +1,19 @@
+
+   histogram <invec> <outvec> <start> <end> [<delta>] [-range <dx_outvec>]
+
+   calculate a histogram of the <invec> values and store the
+   occurrences count in the <outvec> buffer. Optionally constrained to
+   the given <start>-<end> region with <delta> step value (default
+   step is 1). The optional '-range <dx_outvec>' parameter allows storing
+   the range <start>-<end> values with <delta> increment
+
+   Sample code usage:
+   
+   # create a vector ('x') containing arbitrary values  [0.:1.] range
+   create y 0 100 1; set x = sin(y)
+
+   # build histogram from x from 0. to 1. with 0.1 delta step
+   histogram x xhist 0. 1. .1 -range dx
+
+   # plot corresponding histogram
+   limits dx xhist; clear; box; plot dx xhist -x 1
Index: /branches/sc_branches/pantasks_condor/cmd.data/help/imhist
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.data/help/imhist	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.data/help/imhist	(revision 34783)
@@ -0,0 +1,8 @@
+
+   imhist <buffer> <x> <y> [-region sx sy nx ny] [-range min max]
+
+   calculate a histogram of the image pixel values in the given
+   buffer, optionally constrained to the given region, with optional
+   max and min values.  the results are placed in the vectors x and y,
+   which contain the pixel values and the number of occurences.
+
Index: /branches/sc_branches/pantasks_condor/cmd.data/help/integrate
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.data/help/integrate	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.data/help/integrate	(revision 34783)
@@ -0,0 +1,10 @@
+
+   integrate (x) (y) (start) (end)
+
+   Perform an integration of (y) as a function of (x) from (start) to (end)
+
+   The result is placed into the variable: $sum
+
+   Caution!: Program uses the front end of each x bin to approximate the integral.
+    Very coarse bins (delta x = 1) over small regions (x=1->5) can introduce
+    errors as large as 20%. Fine bins are reccommended.
Index: /branches/sc_branches/pantasks_condor/cmd.data/help/interpolate
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.data/help/interpolate	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.data/help/interpolate	(revision 34783)
@@ -0,0 +1,5 @@
+
+   interpolate (xin) (yin) (xout) (yout)
+
+   Performs an interpolation of (xin) and (yin) onto the new grid (xout),
+    producing the corresponding y values in (yout)
Index: /branches/sc_branches/pantasks_condor/cmd.data/help/kern
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.data/help/kern	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.data/help/kern	(revision 34783)
@@ -0,0 +1,4 @@
+
+   kern buffer (kernel file or -)
+
+   apply a 3x3 kernel to the image.  
Index: /branches/sc_branches/pantasks_condor/cmd.data/help/keyword
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.data/help/keyword	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.data/help/keyword	(revision 34783)
@@ -0,0 +1,11 @@
+
+  keyword <buffer> (KEYWORD) [variable] [-w value]
+
+  "keyword" extracts the specified keyword from the header of the
+   specified buffer.  If a third word is listed, the value of the
+   keyword is stored in a variable with the given name.  If the -w
+   option is given, the value is written to the header keyword.
+
+   The header Meta-data is well-defined for FITS, but not necessarily
+   for other data types.
+
Index: /branches/sc_branches/pantasks_condor/cmd.data/help/labels
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.data/help/labels	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.data/help/labels	(revision 34783)
@@ -0,0 +1,23 @@
+
+   labels 
+
+   write a label on the Kapa window.  there are many options:
+
+   -fn font size -- define the font (may be times, helvetica, courier)
+
+   -x "a long line" -- label on the bottom x-axis
+
+   +x "a long line" -- label on the top x-axis
+
+   -y "a long line" -- label on the left y-axis
+
+   +y "a long line" -- label on the right y-axis
+
+   -ul "a long line" -- label on the upper left of plot
+
+   -ll "a long line" -- label on the lower left of plot
+
+   -ur "a long line" -- label on the upper right of plot
+
+   -lr "a long line" -- label on the lower right of plot
+
Index: /branches/sc_branches/pantasks_condor/cmd.data/help/limits
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.data/help/limits	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.data/help/limits	(revision 34783)
@@ -0,0 +1,12 @@
+
+   limits min max min max
+   limits x y
+   limits x min max
+   limits min max y
+   
+   set Kapa plot limits.  The x and y axis limits may be explicitly
+   set (first example), or they may be assigned based on the range of
+   values in a pair of vectors (second examples), or one range may be
+   explicit and the other range assigned from a vector.
+
+
Index: /branches/sc_branches/pantasks_condor/cmd.data/help/load
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.data/help/load	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.data/help/load	(revision 34783)
@@ -0,0 +1,20 @@
+
+   load (overlay) (filename)
+
+  "load" reads a file with objects in SAOimage style into the
+  specified overlay.  If the specified overlay is already used, the
+  new objects are added to the old.  If you do not want this, use
+  "erase" to erase all objects in the overlay first.  Valid overlays
+  may be: 0, red, 1, green, 2, blue, 3, yellow.
+
+Examples of SAOimage style objects:
+BOX   300  200  100 50  : draws a box centered at pixel 300,200 that
+			  is 100 pixels wide, and 50 pixels high
+
+CIRCLE 500 400 200 : draws a circle centered at pixel 500,400 with a
+                     radius of 200 pixels
+
+LINE 100 100 200 200 : draws a line from (100,100) with length (200,200)
+
+  See also: save, erase, contour
+
Index: /branches/sc_branches/pantasks_condor/cmd.data/help/peak
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.data/help/peak	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.data/help/peak	(revision 34783)
@@ -0,0 +1,12 @@
+
+   peak [-q] (x) (y) [startx endx]
+
+   Determines the peak of the y vector between starx and endx
+    or between the extrema of the x vector (default)
+
+   -q: quiets the script (no output to the window)
+
+   Outputs:
+    $peakval: Value of the peak
+    $peakpos: X value corresponding to the peak value
+    $peaknum: Index of the peak value
Index: /branches/sc_branches/pantasks_condor/cmd.data/help/periodogram
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.data/help/periodogram	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.data/help/periodogram	(revision 34783)
@@ -0,0 +1,6 @@
+
+   periodogram (time) (flux) (minP) (maxP) (period) (power)
+
+   creates a periodogram from the vectors (time) and (flux)
+    between (minP) and (maxP) and outputs the results to
+    (period) and (power)
Index: /branches/sc_branches/pantasks_condor/cmd.data/help/plot
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.data/help/plot	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.data/help/plot	(revision 34783)
@@ -0,0 +1,13 @@
+
+   plot <x> <y> [-dx dx] [-dy dy] [+dx dx] [+dy dy]
+
+   plot a pair of vectors.  the options allow for errorbars.  If only
+   one of -dy or +dy is given, the given vector is used for the
+   errorbar.  If both are given, -dy defines the lower errorbar, while
+   +dy defines the upper errorbar.  This allows for assymetric errors
+   in a trivial fashion.  The same applies to the -dx, +dx values.  
+
+   The vectors are plotted with the current plot style.  See style for
+   all of the options.
+
+
Index: /branches/sc_branches/pantasks_condor/cmd.data/help/ps
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.data/help/ps	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.data/help/ps	(revision 34783)
@@ -0,0 +1,16 @@
+
+   ps [-name file.ps] [-g | -i] [-n device] [-raw] [-noscale] [-newpage]
+
+   create a PostScript file from the current graphics window (Kii or
+   Kapa).  The target graphics window may be selected with the -g or
+   -i options (to choose between Kii or Kapa) in combination the -n
+   option (to specify which instance).
+
+   With no additional arguments, Kii produces an encapsulated
+   postscript file called Ximage.ps, while Kapa produces a file called
+   Xgraph.ps.  The output name may be set with the -name (file.ps)
+   option. 
+
+   By default, the output is scaled to fit on a letter page 
+
+   
Index: /branches/sc_branches/pantasks_condor/cmd.data/help/queuedelete
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.data/help/queuedelete	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.data/help/queuedelete	(revision 34783)
@@ -0,0 +1,4 @@
+
+   queuedelete (name)
+
+   Deletes the entire queue (name)
Index: /branches/sc_branches/pantasks_condor/cmd.data/help/queueinit
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.data/help/queueinit	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.data/help/queueinit	(revision 34783)
@@ -0,0 +1,4 @@
+
+   queueinit (name)
+
+   Creates a queue (name) with an initial size of 0
Index: /branches/sc_branches/pantasks_condor/cmd.data/help/queuepush
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.data/help/queuepush	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.data/help/queuepush	(revision 34783)
@@ -0,0 +1,8 @@
+
+   queuepush (queue) (value) [-key N] [-uniq] [-replace]
+
+   Pushes (value) onto the back of (queue)
+
+   -key N:
+   -uniq:
+   -replace:
Index: /branches/sc_branches/pantasks_condor/cmd.data/help/queuesize
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.data/help/queuesize	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.data/help/queuesize	(revision 34783)
@@ -0,0 +1,5 @@
+
+   queuesize (name) [-var variable]
+
+   Returns the size of the queue (name), either to variable
+    or to the window (default)
Index: /branches/sc_branches/pantasks_condor/cmd.data/help/rd
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.data/help/rd	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.data/help/rd	(revision 34783)
@@ -0,0 +1,10 @@
+
+  rd (buffer) (filename)
+
+  "rd" reads a file into the specified buffer, creating the buffer if
+  none exists.  A buffer name may consist of any letters, numbers, and
+  some limited other characters.  However, the name may not start with
+  a digit.
+
+  See also: wd, tv
+
Index: /branches/sc_branches/pantasks_condor/cmd.data/help/read
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.data/help/read	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.data/help/read	(revision 34783)
@@ -0,0 +1,10 @@
+
+   read vect col [vect col, ...]
+
+   read vector values from a file.  An arbitrary number of vectors can
+   be specified, and the (whitespace-separated) field number given for
+   each vector.  Data is read from the file defined by "datafile".
+
+   See also: datafile
+
+
Index: /branches/sc_branches/pantasks_condor/cmd.data/help/rebin
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.data/help/rebin	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.data/help/rebin	(revision 34783)
@@ -0,0 +1,9 @@
+
+  rebin <from> <to> scale
+
+  "rebin" rebins the specified buffer by the given scale factor and
+  places the result in the <to> buffer.  Negative integer values imply
+  expansion, positive numbers imply compression.
+
+  See also: extract
+
Index: /branches/sc_branches/pantasks_condor/cmd.data/help/resize
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.data/help/resize	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.data/help/resize	(revision 34783)
@@ -0,0 +1,6 @@
+
+   resize Nx Ny
+
+   change the size of the Kii window to have image dimensions Nx x
+   Ny.  
+
Index: /branches/sc_branches/pantasks_condor/cmd.data/help/rotate
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.data/help/rotate	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.data/help/rotate	(revision 34783)
@@ -0,0 +1,10 @@
+
+   rotate (buffer) (angle) [-center x y]
+
+   rotate the buffer by the given angle.  The angle may also be one of
+   the following special words:
+
+   -flipx - flip in the x direction
+   -flipy - flip in the y direction
+
+   if the optional center is given the rotation is about this position.
Index: /branches/sc_branches/pantasks_condor/cmd.data/help/save
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.data/help/save	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.data/help/save	(revision 34783)
@@ -0,0 +1,16 @@
+  save (overlay) (filename)
+
+  "save" stores the objects from the specified overlay into the named
+file.  Valid overlays may be: 0, red, 1, green, 2, blue, 3, yellow.
+
+Examples of SAOimage style objects:
+BOX   300  200  100 50  : draws a box centered at pixel 300,200 that
+			  is 100 pixels wide, and 50 pixels high
+
+CIRCLE 500 400 200 : draws a circle centered at pixel 500,400 with a
+                     radius of 200 pixels
+
+LINE 100 100 200 200 : draws a line from (100,100) with length (200,200)
+
+  See also: load, erase, contour
+
Index: /branches/sc_branches/pantasks_condor/cmd.data/help/set
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.data/help/set	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.data/help/set	(revision 34783)
@@ -0,0 +1,41 @@
+
+  set (buffer) = expression..
+
+  perform math operations on images.  there are several allowed
+  operators.  the standard math functions are + - * /.  Exponentiation
+  is performed with ^ (set c = a ^ b).  There are several "logic"
+  operators which need some explanation.
+
+  set c = a < b  -- c is 1 if a < b, 0 otherwise
+  set c = a > b  -- c is 1 if a > b, 0 otherwise
+  set c = m1 & m1 - c is 1 if m1 AND m2 true (non-zero is true)
+  set c = m1 | m1 - c is 1 if m1 OR m2 true (non-zero is true)
+  set c = a << b -- c is the minimum of a and b
+  set c = a >> b -- c is maximum of a and b
+
+  complex operations may be performed:
+
+  set c = a*(a < b) + c*((a < c) | (c < d))
+
+  unary operators also exist:
+
+  exp(a) - e to the power of a
+  ten(a) - 10 to the power of a
+  ln(a) - log base e of a
+  log(a) - log base 10 of a
+  sqrt(a) - square root of a
+  sin(a) - sin of a
+  cos(a) - sin of a
+  tan(a) - sin of a
+  not(a) - logical inverse of a
+  abs(a) - absolute value of a
+  int(a) - integer value of a
+
+  Examples:
+  
+  set a = b + 10  (add 10 to every pixel in b and put the answer in a)
+  set a = a / b   (divide every pixel in a by the corresponding pixel
+                     b and put the answer in a)
+  set b = 10 / a  (divide 10 by every pixel in a and put the answer
+                     in b)
+
Index: /branches/sc_branches/pantasks_condor/cmd.data/help/shift
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.data/help/shift	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.data/help/shift	(revision 34783)
@@ -0,0 +1,6 @@
+
+   shift buffer dx dy
+
+   shift image by dx,dy pixels (may be fractional values).
+
+   
Index: /branches/sc_branches/pantasks_condor/cmd.data/help/stats
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.data/help/stats	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.data/help/stats	(revision 34783)
@@ -0,0 +1,10 @@
+
+  stats (buffer) (x) (y) (nx) (ny)
+
+  report the statistics on a portion of an image.  (x) and (y) specify
+the starting corner of the region, (nx) and (ny) specify the width and
+height of the region.  stats reports the mean, sigma, and number of
+pixels in the region.
+
+  See Also: ??
+
Index: /branches/sc_branches/pantasks_condor/cmd.data/help/style
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.data/help/style	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.data/help/style	(revision 34783)
@@ -0,0 +1,36 @@
+
+   style -- many options
+
+   define or check the Kapa plotting style.  without any command-line arguments,
+   style prints the current style.  
+
+   option definitions:
+
+   -n	window number (0-4), can also be used to change active window.
+   -pt	point style: 
+		0 = filled box
+		1 = open box
+		2 = +
+		3 = X
+		4 = filled triangle
+		5 = blank
+		6 = open triangle 
+		7 = open circle
+		100 = connect pair of points
+   -x	plotting method:
+		0 = connect
+		1 = histogram
+		2 = points
+   -lt	line type:
+		0 = solid
+		1 = dashed
+		2 = dotted??
+   -lw	line weight (0-10)
+   -sz	point size
+   -c   color:
+	black, white, red, orange, 
+	yellow, green, blue, indigo, violet
+   -eb  errorbar with no top
+   +eb  errorbar with top
+
+   See also: plot, box
Index: /branches/sc_branches/pantasks_condor/cmd.data/help/subset
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.data/help/subset	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.data/help/subset	(revision 34783)
@@ -0,0 +1,12 @@
+
+   subset vec = vec if (logic expression)
+
+   reduce the length of a vector based on logical expression.  The
+   logic expression is some function of vectors of equal length to the
+   main vector.
+
+   for example, we have vectors x and y of the same length:
+   
+   subset X = x if ((y > 100) | (x < 10))
+
+    
Index: /branches/sc_branches/pantasks_condor/cmd.data/help/textline
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.data/help/textline	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.data/help/textline	(revision 34783)
@@ -0,0 +1,7 @@
+
+   textline x y (line) [-fn (font) size] [-rot angle]
+
+   write the string at the given coordinates and rotation,
+   in the given font.
+
+   (enclose long strings with spaces in quotes)
Index: /branches/sc_branches/pantasks_condor/cmd.data/help/tv
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.data/help/tv	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.data/help/tv	(revision 34783)
@@ -0,0 +1,13 @@
+
+  tv [-log] (buffer) [zero range]
+
+  display an image in the Ki'i window (X display program).  zero and
+  range specify the data values corresponding to the color mapping.
+  If they are not specified, the old values are used (default is 0,
+  1024).
+
+  If -log is specified the color mapping scale will be logarithmic.  
+
+
+  See Also: Kii, rd, cursor
+
Index: /branches/sc_branches/pantasks_condor/cmd.data/help/unsign
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.data/help/unsign	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.data/help/unsign	(revision 34783)
@@ -0,0 +1,20 @@
+  unsign
+
+  "unsign" toggles the status of the UNSIGN mode.  Warning: This is a
+non-FITS standard implementation.  The FITS standard does not allow
+for unsigned integer data values, but many sites write CCD images with
+16 bit UNSIGNED data.  Mana looks for a FITS keyword "UNSIGN" to
+determine if an image which is read in has signed or unsigned
+integers.  However, since this is a non-standard concept, many sites
+do not use the UNSIGN flag, yet still write unsigned data.  If the
+user knows the data in the file is unsigned (ie, the user must have
+apriori knowledge -- a FITS no-no!), then the user should set the
+UNSIGN mode to true.  In the event that the UNSIGN mode is set, if a
+FITS file is encountered which contains integers, but which does not
+contain the UNSIGN keyword, Mana will make the default assumption that
+the data is unsigned.  If the SIGNED mode is set, Mana will assume all
+integer FITS files contain signed integers.  
+
+((( should be able to say "unsign <buffer>" to convert the status of a
+buffer to UNSIGN or to toggle the buffer's status, but not yet
+implemented)))
Index: /branches/sc_branches/pantasks_condor/cmd.data/help/vectors
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.data/help/vectors	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.data/help/vectors	(revision 34783)
@@ -0,0 +1,5 @@
+
+   vectors
+
+   list the currently defined vectors and lengths
+
Index: /branches/sc_branches/pantasks_condor/cmd.data/help/wd
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.data/help/wd	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.data/help/wd	(revision 34783)
@@ -0,0 +1,29 @@
+
+  wd (buffer) (filename) [BITPIX]
+
+  wd writes a buffer to the specified file in FITS format.  (You may
+need quotes around the filename if there are any /'s in the path).
+The FITS keyword BITPIX may be specified.  Valid values are 8, 16, 32
+(integer formats) and -32, -64 (floating point formats).  If BITPIX is
+unspecified, the original value of the image is used.  The image is
+converted blindly to the format and written out.  This may mean that
+values are rounded (integers), or wrapped by the range of the number
+of bits.  Also beware of the issue of signed vs unsigned images.
+
+  See Also: rd
+
+bitpix  bzero  bscale  file range     data range
+16      0      1       -32k : 32k     -32k : 32k
+16	32k    1       -32k : 32k        0 : 64k
+16	0      2       -32k : 32k     -64k : 64k
+16	0      0.001   -32k : 32k     -32. : 32.
+32	0      1       -2e9 : 2e9     -2e9 : 2e9
+8	0      1       -128 : 128     -128 : 128
+
+so for example, a file with a data range of -128k to +128k written
+with bitpix 16, bzero 0, bscale 1 will have all data outside -32k :
++32k wrapped back in that range.
+
+note: on write, bzero is subtracted from the data before the data is
+      divided by bscale.
+
Index: /branches/sc_branches/pantasks_condor/cmd.data/help/zplot
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.data/help/zplot	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.data/help/zplot	(revision 34783)
@@ -0,0 +1,7 @@
+
+   zplot x y z min max
+
+   plot the vector pair x and y as a series of points with size scaled
+   by the values in the vector z, with the smallest point having
+   values <= min and the largest having values >= max.
+
Index: /branches/sc_branches/pantasks_condor/cmd.data/hermitian1d.c
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.data/hermitian1d.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.data/hermitian1d.c	(revision 34783)
@@ -0,0 +1,61 @@
+# include "data.h"
+
+int hermitian1d (int argc, char **argv) {
+  
+  int i, N, order, Nvec, Poly;
+  opihi_int *inI;
+  opihi_flt *out, *inF;
+  opihi_flt mean, sigma, x;
+  double nf, norm;
+  Vector *xvec, *yvec;
+
+  Poly = FALSE;
+  if ((N = get_argument (argc, argv, "-poly"))) {
+    Poly = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+
+  if (argc != 6) goto usage;
+
+  /* select input / output buffers */
+  if ((xvec = SelectVector (argv[1], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((yvec = SelectVector (argv[2], ANYVECTOR, TRUE)) == NULL) return (FALSE);
+  Nvec = xvec[0].Nelements;
+  ResetVector (yvec, OPIHI_FLT, Nvec);
+
+  /* gaussian parameters */
+  mean  = atof (argv[3]);
+  sigma = atof (argv[4]);
+  order = atoi (argv[5]);
+  if (order < 0) goto usage;
+  if (order > 10) goto usage;
+
+  out = yvec[0].elements.Flt;
+  inF = xvec[0].elements.Flt;
+  inI = xvec[0].elements.Int;
+
+  nf = exp(lgamma(order + 1));
+  norm = 1.0 / sqrt(nf*sqrt(2*M_PI)*sigma);
+
+  // a little sub-optimal : split this up with macros?
+  for (i = 0; i < Nvec; i++, out++) {
+    if (xvec[0].type == OPIHI_FLT) {
+      x = (inF[i] - mean) / sigma;
+    } else {
+      x = (inI[i] - mean) / sigma;
+    }
+    *out = hermitian_polynomial (x, order);
+    if (!Poly) {
+      *out *= norm*exp (-0.25*x*x);
+    }
+  }
+  return (TRUE);
+
+usage:
+  gprint (GP_ERR, "USAGE: hermitian1d (x) (y) (mean) (sigma) (order)\n");
+  gprint (GP_ERR, "  Note : only orders up to 10 are supported\n");
+  return (FALSE);
+
+}
+
+// {\psi}_n(x) = \frac{1}{\sqrt{n! \, 2^n\sqrt{\pi}}}\, \mathrm{e}^{-x^2/2}H_n(x).\,\! 
Index: /branches/sc_branches/pantasks_condor/cmd.data/hermitian2d.c
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.data/hermitian2d.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.data/hermitian2d.c	(revision 34783)
@@ -0,0 +1,79 @@
+# include "astro.h"
+
+int hermitian2d (int argc, char **argv) {
+  
+  int i, j, N, Nx, Ny, Xorder, Yorder, Poly;
+  float *in;
+  double Xo, Yo, sigma;
+  double x, y, xf, yf, value, norm, weight;
+  double nf, mf;
+  Buffer *buf;
+
+  // optionally return the Hermitian Polynomial or the Hermitian Function
+  Poly = FALSE; 
+  if ((N = get_argument (argc, argv, "-poly"))) {
+    Poly = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+
+  Xo = Yo = NAN;
+  if ((N = get_argument (argc, argv, "-c"))) {
+    remove_argument (N, &argc, argv);
+    Xo = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+    Yo = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  if (argc != 5) goto usage;
+
+  /* select input / output buffers */
+  if ((buf = SelectBuffer (argv[1], OLDBUFFER, TRUE)) == NULL) return (FALSE);
+  Nx = buf[0].header.Naxis[0];
+  Ny = buf[0].header.Naxis[1];
+  
+  if (isnan(Xo)) Xo = Nx / 2;
+  if (isnan(Yo)) Yo = Ny / 2;
+
+  /* gaussian parameters */
+  sigma = atof (argv[2]);
+  Xorder = atof (argv[3]);
+  Yorder = atof (argv[4]);
+  if (Xorder < 0) goto usage;
+  if (Yorder < 0) goto usage;
+  if (Xorder > 10) goto usage;
+  if (Yorder > 10) goto usage;
+
+  nf = exp(lgamma(Xorder + 1));
+  mf = exp(lgamma(Yorder + 1));
+  norm = 1.0 / sqrt(nf*mf*2*M_PI) / sigma;
+
+  in = (float *) buf[0].matrix.buffer;
+  for (j = 0; j < Ny; j++) {
+    for (i = 0; i < Nx; i++, in++) {
+
+      x = (i - Xo) / sigma;
+      y = (j - Yo) / sigma;
+
+      // not sure what a 2D hermitian looks like
+      // is it H_i(x) * H_j(y)?
+
+      xf = hermitian_polynomial (x, Xorder);
+      yf = hermitian_polynomial (y, Yorder);
+      value = xf*yf;
+      if (!Poly) {
+	weight = norm*exp(-0.25*(x*x + y*y));
+	value *= weight;
+      }
+
+      *in += value;
+    }
+  }
+
+  return (TRUE);
+
+usage:
+  gprint (GP_ERR, "USAGE: hermitian2d (buffer) (sigma) Xorder Yorder\n");
+  gprint (GP_ERR, "  Note : only orders up to 10 are supported\n");
+  return (FALSE);
+}
Index: /branches/sc_branches/pantasks_condor/cmd.data/histogram.c
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.data/histogram.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.data/histogram.c	(revision 34783)
@@ -0,0 +1,70 @@
+# include "data.h"
+
+int histogram (int argc, char **argv) {
+  
+  int i, N, bin, Nbins;
+  opihi_int *OUT;
+  opihi_flt start, end, delta;
+  Vector *xvec, *yvec, *range;
+
+  range = NULL;
+  if ((N = get_argument (argc, argv, "-range"))) {
+    remove_argument (N, &argc, argv);
+    if ((range = SelectVector (argv[N], ANYVECTOR, TRUE)) == NULL) return (FALSE);    
+    remove_argument (N, &argc, argv);
+  }
+
+  if ((argc != 6) && (argc != 5)) {
+    gprint (GP_ERR, "USAGE: hist invec outvec start end [delta] [-range range]\n");
+    return (FALSE);
+  }
+
+  delta = 1;
+  start = atof (argv[3]);
+  end   = atof (argv[4]);
+  if (argc == 6) delta = atof (argv[5]);
+ 
+  if ((start == end) || (delta == 0)) {
+    gprint (GP_ERR, "error in value: %f to %f, %f\n", start, end, delta);
+    return (FALSE);
+  }
+  delta = fabs (delta);
+  if (end - start < 0) {
+    delta = -1.0 * delta;
+  }
+  Nbins = (end - start) / delta;
+  /* number here should match number in create.c */
+
+  if ((xvec = SelectVector (argv[1], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((yvec = SelectVector (argv[2], ANYVECTOR, TRUE)) == NULL) return (FALSE);
+
+  if (range) {
+    ResetVector (range, OPIHI_FLT, Nbins);
+    for (i = 0; i < range[0].Nelements; i++) {
+      range[0].elements.Flt[i] = start + i*delta;
+    }
+  }
+
+  ResetVector (yvec, OPIHI_INT, Nbins);
+  bzero (yvec[0].elements.Int, sizeof(opihi_int)*yvec[0].Nelements);
+  if (Nbins < 1) return (TRUE);
+  OUT = yvec[0].elements.Int;
+
+  if (xvec[0].type == OPIHI_FLT) {
+    opihi_flt *V = xvec[0].elements.Flt;
+    for (i = 0; i < xvec[0].Nelements; i++, V++) {
+      if (isnan(*V)) continue;
+      bin = MIN (MAX (0, (*V - start) / delta), Nbins - 1);
+      OUT[bin]++;
+    }      
+  } else {
+    opihi_int *V = xvec[0].elements.Int;
+    for (i = 0; i < xvec[0].Nelements; i++, V++) {
+      bin = MIN (MAX (0, (*V - start) / delta), Nbins - 1);
+      OUT[bin]++;
+    }      
+  }
+
+  return (TRUE);
+
+}
Index: /branches/sc_branches/pantasks_condor/cmd.data/imclip.c
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.data/imclip.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.data/imclip.c	(revision 34783)
@@ -0,0 +1,73 @@
+# include "data.h"
+
+int imclip (int argc, char **argv) {
+
+  int i, Npix, DO_NAN, DO_INF, N;
+  double min, Vmin, max, Vmax, nan_val, inf_val;
+  float *in;
+  Buffer *buf;
+
+  inf_val = nan_val = min = Vmin = max = Vmax = 0;
+
+  DO_NAN = FALSE;
+  if ((N = get_argument (argc, argv, "-nan"))) {
+    remove_argument (N, &argc, argv);
+    nan_val  = atof(argv[N]);
+    remove_argument (N, &argc, argv);
+    DO_NAN = TRUE;
+  }
+
+  DO_INF = FALSE;
+  if ((N = get_argument (argc, argv, "-inf"))) {
+    remove_argument (N, &argc, argv);
+    inf_val  = atof(argv[N]);
+    remove_argument (N, &argc, argv);
+    DO_INF = TRUE;
+  }
+
+  if ((argc != 6) && (!(DO_INF || DO_NAN))) {
+    gprint (GP_ERR, "USAGE: clip (buffer) [min Vmin max Vmax] [-inf val] [-nan val]\n");
+    return (FALSE);
+  }
+
+  if ((buf = SelectBuffer (argv[1], OLDBUFFER, TRUE)) == NULL) return (FALSE);
+
+  if (argc == 6) {
+    min = atof (argv[2]);
+    Vmin = atof (argv[3]);
+    max = atof (argv[4]);
+    Vmax = atof (argv[5]);
+  }
+
+  Npix = buf[0].matrix.Naxis[0]*buf[0].matrix.Naxis[1];
+  in = (float *) buf[0].matrix.buffer;
+
+  if (argc == 6) {
+    for (i = 0; i < Npix; i++, in++) {
+      if (*in < min) 
+	*in = Vmin;
+      if (*in > max)
+	*in = Vmax;
+    }
+  }
+  in = (float *) buf[0].matrix.buffer;
+  if (DO_NAN) {
+    for (i = 0; i < Npix; i++, in++) {
+      if (isnan (*in)) {
+	*in = nan_val;
+      }
+    }
+  }
+  in = (float *) buf[0].matrix.buffer;
+  if (DO_INF) {
+    for (i = 0; i < Npix; i++, in++) {
+      if (!finite (*in)) {
+	*in = inf_val;
+      }
+    }
+  }
+  return (TRUE);
+
+}
+
+
Index: /branches/sc_branches/pantasks_condor/cmd.data/imcut.c
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.data/imcut.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.data/imcut.c	(revision 34783)
@@ -0,0 +1,64 @@
+# include "data.h"
+
+int imcut (int argc, char **argv) {
+  
+  int i, Nx, Ny, xi, yi, L;
+  double xs, ys, xe, ye, dX, dY;
+  Vector *xvec, *yvec;
+  Buffer *buf;
+  float *V;
+
+  if (argc != 8) {
+    gprint (GP_ERR, "USAGE: cut <buffer> <X vector> <Y vector> xs ys xe ye\n");
+    return (FALSE);
+  }
+
+  if ((buf  = SelectBuffer (argv[1], OLDBUFFER, TRUE)) == NULL) goto missed;
+  if ((xvec = SelectVector (argv[2], ANYVECTOR, TRUE)) == NULL) goto usage;
+  if ((yvec = SelectVector (argv[3], ANYVECTOR, TRUE)) == NULL) goto usage;
+ 
+  xs = atof (argv[4]);
+  ys = atof (argv[5]);
+  xe = atof (argv[6]);
+  ye = atof (argv[7]);
+
+  Nx = buf[0].matrix.Naxis[0];
+  Ny = buf[0].matrix.Naxis[1];
+
+  if ((xs < 0) || (xs > Nx)) goto range;
+  if ((ys < 0) || (ys > Ny)) goto range;
+  if ((xe < 0) || (xe > Nx)) goto range;
+  if ((ye < 0) || (ye > Ny)) goto range;
+
+  dX = xe - xs;
+  dY = ye - ys;
+  L = hypot (dX, dY);
+  dX = dX / L;
+  dY = dY / L;
+
+  ResetVector (xvec, OPIHI_FLT, L);
+  ResetVector (yvec, OPIHI_FLT, L);
+
+  V = (float *)buf[0].matrix.buffer;
+  for (i = 0; i < L; i++) {
+    xi = xs + i*dX - 0.5;
+    yi = ys + i*dY - 0.5;
+    xvec[0].elements.Flt[i] = i;
+    yvec[0].elements.Flt[i] = V[xi + Nx*yi];
+  }
+
+  return (TRUE);
+
+ usage: 
+  gprint (GP_ERR, "USAGE: circstats <buffer> x y radius\n");
+  return (FALSE);
+
+ range:
+  gprint (GP_ERR, "ERROR: coordinates out of range\n");
+  return (FALSE);
+
+ missed:
+  gprint (GP_ERR, "ERROR: buffer not found\n");
+  return (FALSE);
+}
+
Index: /branches/sc_branches/pantasks_condor/cmd.data/imhist.c
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.data/imhist.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.data/imhist.c	(revision 34783)
@@ -0,0 +1,125 @@
+# include "data.h"
+
+int imhist (int argc, char **argv) {
+  
+  int i, j, N, Nbins, Quiet;
+  int sx, sy, nx, ny, bin;
+  float *V, delta;
+  double max, min, dx;
+  Vector *vec1, *vec2;
+  Buffer *buf;
+
+  Quiet = FALSE;
+  if ((N = get_argument (argc, argv, "-q"))) {
+    Quiet = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+  if ((N = get_argument (argc, argv, "-quiet"))) {
+    Quiet = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+
+  delta = 0;
+  if ((N = get_argument (argc, argv, "-delta"))) {
+    remove_argument (N, &argc, argv);
+    delta = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  min = max = 0.0;
+  if ((N = get_argument (argc, argv, "-range"))) {
+    remove_argument (N, &argc, argv);
+    min = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+    max = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  sx = sy = 0.0;
+  nx = ny = 0.0;
+  if ((N = get_argument (argc, argv, "-region"))) {
+    remove_argument (N, &argc, argv);
+    sx = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+    sy = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+    nx = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+    ny = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  if (argc != 4) {
+    gprint (GP_ERR, "USAGE: histogram <buffer> <x> <y> [-region sx sy nx ny] [-range min max] [-delta binsize]\n");
+    return (FALSE);
+  }
+
+  if ((buf = SelectBuffer (argv[1], OLDBUFFER, TRUE)) == NULL) return (FALSE);
+  
+  /* if either range is set to zero, use the rest of the chip */
+  if (nx == 0)
+    nx = buf[0].matrix.Naxis[0] - sx;
+  if (ny == 0)
+    ny = buf[0].matrix.Naxis[1] - sy;
+
+  if ((sx < 0) || (sy < 0) || 
+      (sx+nx > buf[0].matrix.Naxis[0]) || 
+      (sy+ny > buf[0].matrix.Naxis[1])) {
+    gprint (GP_ERR, "region out of range\n");
+    return (FALSE);
+  }
+
+  if ((vec1 = SelectVector (argv[2], ANYVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((vec2 = SelectVector (argv[3], ANYVECTOR, TRUE)) == NULL) return (FALSE);
+
+  /* unfortunately, we must do this in two passes:
+     first pass finds the max and min and defines the bin size
+     second pass counts the pixes in each bin 
+     */
+
+  if ((max == 0) && (min == 0)) {
+    max = min = *((float *)(buf[0].matrix.buffer) + sy*buf[0].matrix.Naxis[0] + sx);
+    gprint (GP_ERR, "sx: %d, sy: %d, first: %f\n", sx, sy, max);
+    for (j = sy; j < sy + ny; j++) {
+      V = (float *)(buf[0].matrix.buffer) + j*buf[0].matrix.Naxis[0] + sx; 
+      for (i = 0; i < nx; i++, V++) {
+	max = MAX (max, *V);
+	min = MIN (min, *V);
+      }
+    }
+  }
+
+  if (delta == 0) {
+    Nbins = 1024;
+    dx = (max - min) / Nbins;
+  } else {
+    dx = delta;
+    Nbins = (max - min) / dx;
+  }
+  if (Quiet) {
+    set_variable ("MIN", min);
+    set_variable ("MAX", max);
+    set_variable ("DX",  dx);
+  } else {
+    gprint (GP_LOG, "max %f, min %f, dx %f\n", max, min, dx);
+  }  
+
+  ResetVector (vec1, OPIHI_FLT, Nbins + 1);
+  ResetVector (vec2, OPIHI_FLT, Nbins + 1);
+  bzero (vec1[0].elements.Flt, vec1[0].Nelements*sizeof(opihi_flt));
+  bzero (vec2[0].elements.Flt, vec2[0].Nelements*sizeof(opihi_flt));
+  
+  for (j = sy; j < sy + ny; j++) {
+    V = (float *)(buf[0].matrix.buffer) + j*buf[0].matrix.Naxis[0] + sx; 
+    for (i = 0; i < nx; i++, V++) {
+      bin = MAX (MIN (Nbins, (*V - min) / dx), 0);
+      vec2[0].elements.Flt[bin] += 1.0;
+    }
+  }
+  for (i = 0; i < Nbins + 1; i++, V++) {
+    vec1[0].elements.Flt[i] = i*dx + min;
+  }
+  
+  return (TRUE);
+}
+
Index: /branches/sc_branches/pantasks_condor/cmd.data/imsmooth.c
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.data/imsmooth.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.data/imsmooth.c	(revision 34783)
@@ -0,0 +1,75 @@
+# include "data.h"
+
+int imsmooth (int argc, char **argv) {
+  
+  int i, j, n, N, Nx, Ny, Ns, Ngauss;
+  float *vi, *vo, *gauss, *gaussnorm;
+  float g, s, sigma, Nsigma;
+  Buffer *in;
+  float *temp;
+
+  Nsigma = 3;
+  if ((N = get_argument (argc, argv, "-Nsigma"))) {
+    remove_argument (N, &argc, argv);
+    Nsigma = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  if (argc != 3) {
+    gprint (GP_ERR, "USAGE: imsmooth (input) sigma\n");
+    return (FALSE);
+  }
+  
+  if ((in  = SelectBuffer (argv[1], OLDBUFFER, TRUE)) == NULL) return (FALSE);
+  sigma = atof (argv[2]);
+
+  Nx = in[0].matrix.Naxis[0];
+  Ny = in[0].matrix.Naxis[1];
+  ALLOCATE (temp, float, Nx*Ny);
+
+  /* build a 1D gaussian */
+  Ns = (int) (Nsigma*sigma + 0.5);
+  Ngauss = 2*Ns + 1;
+  ALLOCATE (gaussnorm, float, Ngauss);
+  gauss = &gaussnorm[Ns];
+  for (i = -Ns; i < Ns + 1; i++) {
+    gauss[i] = exp ((i*i)/(-2*sigma*sigma));
+  }
+
+  /* smooth in X direction */
+  for (j = 0; j < Ny; j++) {
+    vi = (float *) in[0].matrix.buffer + j*Nx;
+    vo = &temp[j*Nx];
+    for (i = 0; i < Nx; i++) {
+      g = s = 0;
+      for (n = -Ns; n < Ns + 1; n++) {
+	if (i+n < 0) continue;
+	if (i+n >= Nx) continue;
+	s += gauss[n]*vi[i+n];
+	g += gauss[n];
+      }
+      vo[i] = s / g;
+    }
+  }
+
+  /* smooth in Y direction */
+  for (i = 0; i < Nx; i++) {
+    vi = &temp[i];
+    vo = (float *)in[0].matrix.buffer + i;
+    for (j = 0; j < Ny; j++) {
+      g = s = 0;
+      for (n = -Ns; n < Ns + 1; n++) {
+	if (j+n < 0) continue;
+	if (j+n >= Ny) continue;
+	s += gauss[n]*vi[(n+j)*Nx];
+	g += gauss[n];
+      }
+      vo[j*Nx] = s / g;
+    }
+  }
+
+  free (temp);
+  free (gaussnorm);
+  return (TRUE);
+}
+
Index: /branches/sc_branches/pantasks_condor/cmd.data/imspline_apply.c
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.data/imspline_apply.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.data/imspline_apply.c	(revision 34783)
@@ -0,0 +1,98 @@
+# include "data.h"
+
+// need to rename this as an image function
+int imspline_apply (int argc, char **argv) {
+  
+  int i, j, I, J;
+  int nx, ny, Nx, Ny;
+  float rx, ry, x, y;
+  float *Tx1, *Tx2, *Txc, *Ty1, *Ty2, *Tyc, *V, *V1, *V2;
+  Buffer *out, *y1, *y2;
+
+  if (argc != 7) {
+    gprint (GP_ERR, "USAGE: imspline_apply <Y> <Y2> <out> (x/y) Nx Ny\n");
+    return (FALSE);
+  }
+
+  if ((y1  = SelectBuffer (argv[1], OLDBUFFER, TRUE)) == NULL) return (FALSE);
+  if ((y2  = SelectBuffer (argv[2], OLDBUFFER, TRUE)) == NULL) return (FALSE);
+  if ((out = SelectBuffer (argv[3], ANYBUFFER, TRUE)) == NULL) return (FALSE);
+
+  // xdir = FALSE;
+  // if (!strcmp (argv[4], "x")) xdir = TRUE; 
+
+  nx = atoi (argv[5]);
+  ny = atoi (argv[6]);
+
+  Nx = y1[0].matrix.Naxis[0];
+  Ny = y1[0].matrix.Naxis[1];
+
+  rx = Nx / (float) nx;
+  ry = Ny / (float) ny;
+
+  /* create an output matrix buffer with desired nx, ny */
+  gfits_free_matrix (&out[0].matrix);
+  gfits_free_header (&out[0].header);
+
+  out[0].bitpix = y1[0].bitpix;
+  out[0].unsign = y1[0].unsign;
+  out[0].bscale = y1[0].bscale;
+  out[0].bzero  = y1[0].bzero;
+  gfits_copy_header (&y1[0].header, &out[0].header);
+  gfits_modify (&out[0].header, "NAXIS1", "%d", 1, nx);
+  gfits_modify (&out[0].header, "NAXIS2", "%d", 1, ny);
+
+  out[0].header.Naxis[0] = nx;
+  out[0].header.Naxis[1] = ny;
+  gfits_create_matrix (&out[0].header, &out[0].matrix);
+  if ((y1[0].file[0] != '*') && (y1[0].file[0] != '(')) {
+    sprintf (out[0].file, "*%s", y1[0].file);
+  } else {
+    sprintf (out[0].file, "%s", y1[0].file);
+  }
+
+  ALLOCATE (Ty2, float, Ny);
+  ALLOCATE (Ty1, float, Ny);
+  ALLOCATE (Tyc, float, Ny);
+  for (i = 0; i < Ny; i++) { Tyc[i] = i; }
+
+  ALLOCATE (Tx1, float, Nx);
+  ALLOCATE (Tx2, float, Nx);
+  ALLOCATE (Txc, float, Nx);
+  for (i = 0; i < Nx; i++) { Txc[i] = i; }
+
+  V = (float *)(out[0].matrix.buffer);
+
+  for (J = 0; J < ny; J++) {
+    y = J * ry;
+
+    /* construct spline for each element in this row */
+    for (i = 0; i < Nx; i++) {
+      V1 = (float *)(y1[0].matrix.buffer) + i;
+      V2 = (float *)(y2[0].matrix.buffer) + i;
+      for (j = 0; j < Ny; j++, V1+=Nx, V2+=Nx) {
+	Ty1[j] = *V1;
+	Ty2[j] = *V2;
+      }
+      Tx1[i] = spline_apply_flt (Tyc, Ty1, Ty2, Ny, y);
+    }
+    spline_construct_flt (Txc, Tx1, Nx, Tx2);
+
+    /* apply x-dir spline to new image */
+    for (I = 0; I < nx; I++, V++) {
+      x = I * rx;
+      *V = spline_apply_flt (Txc, Tx1, Tx2, Nx, x);
+    }
+  }
+
+  free (Ty1);
+  free (Ty2);
+  free (Tyc);
+  
+  free (Tx1);
+  free (Tx2);
+  free (Txc);
+  
+  return (TRUE);
+
+}
Index: /branches/sc_branches/pantasks_condor/cmd.data/imspline_construct.c
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.data/imspline_construct.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.data/imspline_construct.c	(revision 34783)
@@ -0,0 +1,72 @@
+# include "data.h"
+
+// need to rename this as an image function
+int imspline_construct (int argc, char **argv) {
+  
+  int i, j, Nx, Ny;
+  float *Tx, *Ty, *Ty2, *V;
+  Buffer *in, *out;
+
+  if (argc != 4) {
+    gprint (GP_ERR, "USAGE: imspline_construct <in> <out> (x/y)\n");
+    return (FALSE);
+  }
+
+  if ((in  = SelectBuffer (argv[1], OLDBUFFER, TRUE)) == NULL) return (FALSE);
+  if ((out = SelectBuffer (argv[2], ANYBUFFER, TRUE)) == NULL) return (FALSE);
+
+  // XXX move this to gfits_create_matrix
+  free (out[0].matrix.buffer);
+  if ((in[0].file[0] != '*') && (in[0].file[0] != '(')) {
+    sprintf (out[0].file, "*%s", in[0].file);
+  } else {
+    sprintf (out[0].file, "%s", in[0].file);
+  }
+  out[0].bitpix = in[0].bitpix;
+  out[0].unsign = in[0].unsign;
+  out[0].bscale = in[0].bscale;
+  out[0].bzero  = in[0].bzero;
+  gfits_copy_matrix_info (&in[0].matrix, &out[0].matrix);
+  gfits_copy_header (&in[0].header, &out[0].header);
+  gfits_create_matrix (&out[0].header, &out[0].matrix);
+
+  // int xdir = FALSE;
+  // if (!strcmp (argv[3], "x")) xdir = TRUE; 
+  /* ideally, the resulting image should carry this info (in header?) */
+
+  Nx = in[0].matrix.Naxis[0];
+  Ny = in[0].matrix.Naxis[1];
+
+  ALLOCATE (Ty2, float, Ny);
+  ALLOCATE (Ty, float, Ny);
+  ALLOCATE (Tx, float, Ny);
+
+  /** for now only perform the operation for the ydir splines */
+
+  /* construct coordinate vector */
+  for (j = 0; j < Ny; j++) { Tx[j] = j; }
+  
+  for (i = 0; i < Nx; i++) {
+    
+    /* construct temp vector with values to spline */
+    V = (float *)(in[0].matrix.buffer) + i;
+    for (j = 0; j < Ny; j++, V+=Nx) {
+      Ty[j] = *V;
+    }
+  
+    spline_construct_flt (Tx, Ty, Ny, Ty2);
+  
+    /* copy derivatives to output buffer */
+    V = (float *)(out[0].matrix.buffer) + i;
+    for (j = 0; j < Ny; j++, V+=Nx) {
+      *V = Ty2[j];
+    }
+  }
+
+  free (Tx);
+  free (Ty);
+  free (Ty2);
+  
+  return (TRUE);
+
+}
Index: /branches/sc_branches/pantasks_condor/cmd.data/imstats.c
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.data/imstats.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.data/imstats.c	(revision 34783)
@@ -0,0 +1,125 @@
+# include "data.h"
+
+int imstats (int argc, char **argv) {
+  
+  int i, j, Nmode, Imode;
+  double Npix, N1, N2, max, min, range, median, mode, IgnoreValue;
+  float *V;
+  int sx, sy, nx, ny, *hist, Nhist, bin;
+  int Ignore, Quiet, N;
+  Buffer *buf;
+
+  IgnoreValue = 0;
+  Ignore = FALSE;
+  if ((N = get_argument (argc, argv, "-ignore"))) {
+    Ignore = TRUE;
+    remove_argument (N, &argc, argv);
+    IgnoreValue = atof(argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  Quiet = FALSE;
+  if ((N = get_argument (argc, argv, "-q"))) {
+    Quiet = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+  if ((N = get_argument (argc, argv, "-quiet"))) {
+    Quiet = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+
+  if ((argc != 2) && (argc != 6)) {
+    gprint (GP_ERR, "USAGE: stats <buffer> sx sy nx ny\n");
+    gprint (GP_ERR, "OR:    stats <buffer>\n");
+    return (FALSE);
+  }
+
+  if ((buf = SelectBuffer (argv[1], OLDBUFFER, TRUE)) == NULL) return (FALSE);
+
+  if (argc == 6) {
+    sx = strcmp (argv[2], "-") ? atof (argv[2]) : 0;
+    sy = strcmp (argv[3], "-") ? atof (argv[3]) : 0;
+    nx = strcmp (argv[4], "-") ? atof (argv[4]) : buf[0].matrix.Naxis[0];
+    ny = strcmp (argv[5], "-") ? atof (argv[5]) : buf[0].matrix.Naxis[1];
+  } else {
+    sx = 0;
+    sy = 0;
+    nx = buf[0].matrix.Naxis[0];
+    ny = buf[0].matrix.Naxis[1];
+  }
+
+  Npix = N1 = N2 = 0;
+  if ((sx < 0) || (sy < 0) || 
+      (sx+nx > buf[0].matrix.Naxis[0]) || 
+      (sy+ny > buf[0].matrix.Naxis[1])) {
+    gprint (GP_ERR, "region out of range\n");
+    return (FALSE);
+  }
+
+  max = -1e32;
+  min = +1e32;
+  for (j = sy; j < sy + ny; j++) {
+    V = (float *)(buf[0].matrix.buffer) + j*buf[0].matrix.Naxis[0] + sx; 
+    for (i = 0; i < nx; i++, V++) {
+      if (Ignore && (fabs (*V - IgnoreValue) < 1e-8)) continue;
+      if (isnan(*V)) continue;
+      if (isinf(*V)) continue;
+      N1 += *V;
+      N2 += (*V)*(*V);
+      Npix += 1.0;
+      max = MAX (max, *V);
+      min = MIN (min, *V);
+    }
+  }
+  N1 = N1 / Npix;
+
+/* calculate mode, median */
+  median = mode = 0.5*(max + min);
+  if ((max - min) != 0) {
+    range = 0xffff / (max - min);
+    ALLOCATE (hist, int, 0x10000);
+    bzero (hist, 0x10000*sizeof(int));
+    for (j = sy; j < sy + ny; j++) {
+      V = (float *)(buf[0].matrix.buffer) + j*buf[0].matrix.Naxis[0] + sx; 
+      for (i = 0; i < nx; i++, V++) {
+	if (Ignore && (fabs (*V - IgnoreValue) < 1e-8)) continue;
+	if (isnan(*V)) continue;
+	if (isinf(*V)) continue;
+	bin = MIN (MAX (0, (*V - min) * range), 0xffff);
+	hist[bin] ++;
+      }
+    }
+    Nhist = 0;
+    for (i = 0; (i < 0xffff) && (Nhist < 0.5*Npix); i++) 
+      Nhist += hist[i];
+    median = i / range + min;
+    Nmode = hist[0];
+    Imode = 0;
+    for (i = 1; i < 0x10000; i++) {
+      if (hist[i] > Nmode) {
+	Nmode = hist[i];
+	Imode = i;
+      }
+    }
+    mode = Imode / range + min;
+    free (hist);
+  }  
+  
+  if (!Quiet) {
+    gprint (GP_LOG, "  mean    stdev    min     max   median   Npix   Total\n");
+    gprint (GP_LOG, "%7.4g %7.4g %7.4g %7.4g %7.4g %7.0f %7.4g\n", N1, sqrt (N2/Npix - N1*N1), 
+	    min, max, median, Npix, Npix*N1);
+  }
+
+  set_variable ("MIN",    min);
+  set_variable ("MAX",    max);
+  set_variable ("MEDIAN", median);
+  set_variable ("MEAN",   N1);
+  set_variable ("MODE",   mode);
+  set_variable ("TOTAL",  N1*Npix);
+  set_variable ("NPIX",   Npix);
+  set_variable ("SIGMA",  sqrt (N2/Npix - N1*N1));
+
+  return (TRUE);
+}
+
Index: /branches/sc_branches/pantasks_condor/cmd.data/init.c
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.data/init.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.data/init.c	(revision 34783)
@@ -0,0 +1,318 @@
+# include "data.h"
+
+int accum            PROTO((int, char **));
+int applyfit         PROTO((int, char **));
+int applyfit1d       PROTO((int, char **));
+int applyfit2d       PROTO((int, char **));
+int applyfit3d       PROTO((int, char **));
+int box              PROTO((int, char **));
+int book_command     PROTO((int, char **));
+int center           PROTO((int, char **));
+int parity           PROTO((int, char **));
+int circstats        PROTO((int, char **));
+int clear            PROTO((int, char **));
+int clip             PROTO((int, char **));
+int imclip           PROTO((int, char **));
+int close_device     PROTO((int, char **));
+int concat           PROTO((int, char **));
+int contour          PROTO((int, char **));
+int create           PROTO((int, char **));
+int cumulative       PROTO((int, char **));
+int cursor           PROTO((int, char **));
+int cut              PROTO((int, char **));
+int datafile         PROTO((int, char **));
+int dbconnect        PROTO((int, char **));
+int dbselect         PROTO((int, char **));
+int delete           PROTO((int, char **));
+int densify          PROTO((int, char **));
+int device           PROTO((int, char **));
+int dimendown        PROTO((int, char **));
+int dimenup          PROTO((int, char **));
+int erase            PROTO((int, char **));
+int extract          PROTO((int, char **));
+int fft1d            PROTO((int, char **));
+int fft2d            PROTO((int, char **));
+int fit              PROTO((int, char **));
+int fit1d            PROTO((int, char **));
+int fit2d            PROTO((int, char **));
+int fit3d            PROTO((int, char **));
+int gaussjordan      PROTO((int, char **));
+int gaussdeviate     PROTO((int, char **));
+int gaussintegral    PROTO((int, char **));
+int grid             PROTO((int, char **));
+int gridify          PROTO((int, char **));
+int grow             PROTO((int, char **));
+int ungridify        PROTO((int, char **));
+int histogram        PROTO((int, char **));
+int hermitian1d      PROTO((int, char **));
+int hermitian2d      PROTO((int, char **));
+int imcut            PROTO((int, char **));
+int imhist           PROTO((int, char **));
+int imsmooth         PROTO((int, char **));
+int integrate        PROTO((int, char **));
+int interpolate      PROTO((int, char **));
+int jpeg             PROTO((int, char **));
+int kern             PROTO((int, char **));
+int keyword          PROTO((int, char **));
+int labels           PROTO((int, char **));
+int limits           PROTO((int, char **));
+int line             PROTO((int, char **));
+int list_buffers     PROTO((int, char **));
+int header           PROTO((int, char **));
+int list_vectors     PROTO((int, char **));
+int vtype            PROTO((int, char **));
+int load             PROTO((int, char **));
+int lookup           PROTO((int, char **));
+int matrix           PROTO((int, char **));
+int match2d          PROTO((int, char **));
+int mkrgb            PROTO((int, char **));
+int mcreate          PROTO((int, char **));
+int medacc           PROTO((int, char **));
+int mget             PROTO((int, char **));
+int minterp          PROTO((int, char **));
+int mset             PROTO((int, char **));
+int peak             PROTO((int, char **));
+int periodogram      PROTO((int, char **));
+int plot             PROTO((int, char **));
+int dot              PROTO((int, char **));
+int point            PROTO((int, char **));
+int ps               PROTO((int, char **));
+int queuelist        PROTO((int, char **));
+int queueload        PROTO((int, char **));
+int queueinit        PROTO((int, char **));
+int queuedelete      PROTO((int, char **));
+int queuedrop        PROTO((int, char **));
+int queuepop         PROTO((int, char **));
+int queueprint       PROTO((int, char **));
+int queuepush        PROTO((int, char **));
+int queuesubstr      PROTO((int, char **));
+int queuesize        PROTO((int, char **));
+int rd               PROTO((int, char **));
+int rdseg            PROTO((int, char **));
+int read_vectors     PROTO((int, char **));
+int rebin            PROTO((int, char **));
+int resize           PROTO((int, char **));
+int reindex          PROTO((int, char **));
+int relocate         PROTO((int, char **));
+int roll             PROTO((int, char **));
+int rotate           PROTO((int, char **));
+int save             PROTO((int, char **));
+int section          PROTO((int, char **));
+int set              PROTO((int, char **));
+int shift            PROTO((int, char **));
+int sort_vectors     PROTO((int, char **));
+int spline_command   PROTO((int, char **));
+int imspline_apply   PROTO((int, char **));
+int imspline_construct PROTO((int, char **));
+int stats            PROTO((int, char **));
+int imstats          PROTO((int, char **));
+int style            PROTO((int, char **));
+int subraster        PROTO((int, char **));
+int subset           PROTO((int, char **));
+int svd              PROTO((int, char **));
+int swapbytes        PROTO((int, char **));
+int textline         PROTO((int, char **));
+int threshold        PROTO((int, char **));
+int tv               PROTO((int, char **));
+int tvchannel        PROTO((int, char **));
+int tvcolors         PROTO((int, char **));
+int tvcontour        PROTO((int, char **));
+int tvgrid           PROTO((int, char **));
+int uniq             PROTO((int, char **));
+int unsign           PROTO((int, char **));
+int vbin             PROTO((int, char **));
+int vgroup           PROTO((int, char **));
+int vclip            PROTO((int, char **));
+int vect_select      PROTO((int, char **));
+int vgrid            PROTO((int, char **));
+int vgauss           PROTO((int, char **));
+int vellipse         PROTO((int, char **));
+int vmaxwell         PROTO((int, char **));
+int vload            PROTO((int, char **));
+int vzload           PROTO((int, char **));
+int vstats           PROTO((int, char **));
+int vroll            PROTO((int, char **));
+int vshift           PROTO((int, char **));
+int vpop             PROTO((int, char **));
+int vsmooth          PROTO((int, char **));
+int wd               PROTO((int, char **));
+int write_vectors    PROTO((int, char **));
+int zap              PROTO((int, char **));
+int zplot            PROTO((int, char **));
+int zcplot            PROTO((int, char **));
+
+// ???
+// int mtype            PROTO((int, char **));
+//  {1, "mtype",        mtype,            "return the type of the defined buffer"},
+
+static Command cmds[] = {  
+  {1, "accum",        accum,            "accumulate vector values in another vector"},
+  {1, "applyfit",     applyfit1d,       "apply 1-d fit to new vector"},
+  {1, "applyfit1d",   applyfit1d,       "apply 1-d fit to new vector"},
+  {1, "applyfit2d",   applyfit2d,       "apply 2-d fit to new vector"},
+  {1, "applyfit3d",   applyfit3d,       "apply 3-d fit to new vector"},
+  {1, "book",         book_command,     "commands to manipulate book/page/word data"},
+  {1, "box",          box,              "draw a box on the plot"},
+  {1, "buffers",      list_buffers,     "list the currently allocated buffers (images)"},
+  {1, "center",       center,           "center image on coords"},
+  {1, "circstats",    circstats,        "circular statistics"},
+  {1, "clear",        clear,            "erase plot"},
+  {1, "clip",         imclip,           "clip values in an image to be within a range"},
+  {1, "close",        close_device,     "close the current display device"},
+  {1, "concat",       concat,           "append values to the end of a vector"},
+  {1, "contour",      contour,          "create contour from image"},
+  {1, "create",       create,           "create a new vector"},
+  {1, "vcreate",      create,           "create a new vector"},
+  {1, "cumulative",   cumulative,       "build a cumulative histogram from a specific histogram"},
+  {1, "cursor",       cursor,           "get coords from cursor"},
+  {1, "cut",          cut,              "extract a cut across an image"},
+  {1, "datafile",     datafile,         "define file to read vectors"},
+  {1, "dbconnect",    dbconnect,        "setup mysql db connection"},
+  {1, "dbselect",     dbselect,         "extract vectors from mysql database table"},
+  {1, "delete",       delete,           "delete vectors or images"},
+  {1, "densify",      densify,          "create an image histogram from a set of vectors"},
+  {1, "device",       device,           "set / get current graphics device"},
+  {1, "dimendown",    dimendown,        "convert image to vector"},
+  {1, "dimenup",      dimenup,          "convert vector to image"},
+  {1, "dot",          dot,              "plot a single point"},
+  {1, "erase",        erase,            "erase objects on an image overlay"},
+  {1, "extract",      extract,          "extract a portion of a image into another image"},
+  {1, "fft1d",        fft1d,            "fft on a vector"},
+  {1, "fft2d",        fft2d,            "fft on an image"},
+  {1, "fit",          fit1d,            "fit polynomial to vector pair"},
+  {1, "fit1d",        fit1d,            "fit polynomial to vector pair"},
+  {1, "fit2d",        fit2d,            "fit 2-d polynomial to vector triplet"},
+  {1, "fit3d",        fit3d,            "fit 3-d polynomial to vector quad"},
+  {1, "gaussdev",     gaussdeviate,     "generate a gaussian deviate vector"},
+  {1, "gaussint",     gaussintegral,    "return the integrated gaussian vector"},
+  {1, "gaussj",       gaussjordan,      "solve Ax = B (N-Dimensional)"},
+  {1, "grid",         grid,             "plot cartesian grid on graph"},
+  {1, "gridify",      gridify,          "convert vector triplet to image (same as vgrid?)"},
+  {1, "grow",         grow,             "grow a mask"},
+  {1, "header",       header,           "print image header"},
+  {1, "histogram",    histogram,        "generate histogram from vector"},
+  {1, "hermitian1d",  hermitian1d,      "generate 1-D Hermitian Polynomial"},
+  {1, "hermitian2d",  hermitian2d,      "generate 2-D Hermitian Polynomial"},
+  {1, "imbin",        rebin,            "rebin image data by factor of N"},
+  {1, "imclip",       imclip,           "clip values in an image to be within a range"},
+  {1, "imcut",        imcut,            "linear image cut between arbitrary coords"},
+  {1, "imhistogram",  imhist,           "histogram of an image region"},
+  {1, "imsmooth",     imsmooth,         "circular gaussian smoothing"},
+  {1, "imstats",      imstats,          "statistics on a portion of an image"},
+  {1, "integrate",    integrate,        "integrate a vector"},
+  {1, "interpolate",  interpolate,      "interpolate between vector pairs"},
+  {1, "jpeg",         jpeg,             "convert display image to JPEG"},
+  {1, "kern",         kern,             "convolve with 3x3 kernel"},
+  {1, "keyword",      keyword,          "extract a FITS keyword from image header"},
+  {1, "labels",       labels,           "define labels for plot"},
+  {1, "limits",       limits,           "define plot limits"},
+  {1, "line",         line,             "plot a line"},
+  {1, "load",         load,             "load an SAOimage/DS9 style overlay"},
+  {1, "lookup",       lookup,           "convert vector via lookup table (vector pair)"},
+  {1, "mcreate",      mcreate,          "create an image"},
+  {1, "imcreate",     mcreate,          "create an image"},
+  {1, "medacc",       medacc,           "accumulate vector values in another vector"},
+  {1, "mget",         mget,             "extract a vector from an image"},
+  {1, "imget",        mget,             "extract a vector from an image"},
+  {1, "minterp",      minterp,          "interpolate image pixels"},
+  {1, "iminterp",     minterp,          "interpolate image pixels"},
+  {1, "matrix",       matrix,           "matrix math operations"},
+  {1, "match2d",      match2d,          "match 2 pairs of X,Y vectors and return matched indexes"},
+  {1, "mkrgb",        mkrgb,            "convert 3 images to rgb jpeg (use Kapa for better control)"},
+  {1, "mset",         mset,             "insert a vector in an image"},
+  {1, "imset",        mset,             "insert a vector in an image"},
+  {1, "parity",       parity,           "set image parity"},
+  {1, "peak",         peak,             "find vector peak in range"},
+  {1, "periodogram",  periodogram,      "measure periods in unevenly sampled data"},
+  {1, "plot",         plot,             "plot a pair of vectors"},
+  {1, "png",          jpeg,             "convert display graphic to PNG"},
+  {1, "point",        point,            "load image overlay with a single point"},
+  {1, "ppm",          jpeg,             "convert display graphic to PPM"},
+  {1, "ps",           ps,               "convert display to PostScript"},
+  {1, "queuedelete",  queuedelete,      "delete a queue"},
+  {1, "queuedrop",    queuedrop,        "drop values from queue matching a key"},
+  {1, "queueinit",    queueinit,        "create an empty queue"},
+  {1, "queuelist",    queuelist,        "list defined queues"},
+  {1, "queueload",    queueload,        "load queue from command"},
+  {1, "queuepop",     queuepop,         "pop value from queue to variable"},
+  {1, "queueprint",   queueprint,       "print the contents of a queue"},
+  {1, "queuepush",    queuepush,        "push value onto queue"},
+  {1, "queuesize",    queuesize,        "show queue size"},
+  {1, "queuesubstr",  queuesubstr,      "bulk replace strings in queue"},
+  {1, "rd",           rd,               "load fits image"},
+  {1, "rdseg",        rdseg,            "read a segment of an image from a file"},
+  {1, "read",         read_vectors,     "read vectors from datafile"},
+  {1, "rebin",        rebin,            "rebin image data by factor of N"},
+  {1, "reindex",      reindex,          "create new vector from old vector based on index vector"},
+  {1, "resize",       resize,           "set graphics/image window size"},
+  {1, "relocate",     relocate,         "set graphics/image window position"},
+  {1, "roll",         roll,             "roll image to new start point"},
+  {1, "rotate",       rotate,           "rotate image"},
+  {1, "save",         save,             "save an SAOimage style image overlay"},
+  {1, "section",      section,          "define section of graph"},
+  {1, "select",       vect_select,      "selective vector assignment"},
+  {1, "set",          set,              "image and vector math"},
+  {1, "shift",        shift,            "shift data in an image"},
+  {1, "sort",         sort_vectors,     "sort list of vectors"},
+  {1, "spline",       spline_command,   "shift data in an image"},
+  {1, "imspline.apply", imspline_apply, "apply spline fit to generate an image"},
+  {1, "imspline.const", imspline_construct, "create spline 2nd deriv. terms"},
+  {1, "stats",        imstats,          "statistics on a portion of an image"},
+  {1, "style",        style,            "set the style for graph plots"},
+  {1, "subraster",    subraster,        "subraster of fits image"},
+  {1, "subset",       subset,           "generate a vector from a portion of another vector"},
+  {1, "svd",          svd,              "singular value decomposition of a matrix (image)"},
+  {1, "swapbytes",    swapbytes,        "byte swap thing"},
+  {1, "textline",     textline,         "write text line on graph"},
+  {1, "threshold",    threshold,        "find (interpolate) location of transition"},
+  {1, "tv",           tv,               "display an image on the Kii window"},
+  {1, "tvchannel",    tvchannel,        "set the current tv channel"},
+  {1, "tvcolors",     tvcolors,         "set the tv colormap"},
+  {1, "tvcontour",    tvcontour,        "send contour to image overlay"},
+  {1, "tvgrid",       tvgrid,           "RA/DEC grid on an image"},
+  {1, "ungridify",    ungridify,        "convert image region to vector triplet"},
+  {1, "uniq",         uniq,             "create a uniq vector subset from a vector"},
+  {1, "unsign",       unsign,           "toggle the UNSIGN status"},
+  {1, "vbin",         vbin,             "rebin vector data by a factor of N"},
+  {1, "vgroup",       vgroup,           "group y vector into bins defined by x vector values"},
+  {1, "vclip",        vclip,            "clip values in a vector to be within a range"},
+  {1, "vectors",      list_vectors,     "list vectors"},
+  {1, "vtype",        vtype,            "return the vector type (FLT or INT)"},
+  {1, "vgauss",       vgauss,           "fit a Gaussian to a vector"},
+  {1, "vellipse",     vellipse,         "fit a Ellipse to a vector pair"},
+  {1, "vgrid",        vgrid,            "generate an image from a triplet of vectors"},
+  {1, "vhistogram",   histogram,        "generate histogram from vector"},
+  {1, "vload",        vload,            "load vectors as overlay on image display"},
+  {1, "vmaxwell",     vmaxwell,         "fit a Maxwellian to a vector"},
+  {1, "vpop",         vpop,             "remove first element of a vector"},
+  {1, "vroll",        vroll,            "roll vector elements by 1 entry"},
+  {1, "vshift",       vshift,           "shift vector elements by arbitrary amount"},
+  {1, "vsmooth",      vsmooth,          "Gaussian smooth of a vector"},
+  {1, "vstats",       vstats,           "statistics on a vector"},
+  {1, "vzload",       vzload,           "load vectors as overlay on image display (scaled points)"},
+  {1, "wd",           wd,               "write an image to a file"},
+  {1, "write",        write_vectors,    "write vectors to datafile"},
+  {1, "zap",          zap,              "assign values to pixel regions"},
+  {1, "zplot",        zplot,            "plot x y with size scaled by z"},
+  {1, "zcplot",       zcplot,           "plot x y with color scaled by z"},
+}; 
+
+void InitData () {
+  
+  int i;
+
+  InitKapa ();
+  InitQueues ();
+  InitBooks ();
+
+  for (i = 0; i < sizeof (cmds) / sizeof (Command); i++) {
+    AddCommand (&cmds[i]);
+  }
+}
+
+void FreeData () {
+
+  FreeKapa ();
+  FreeQueues ();
+  FreeBooks ();
+}
Index: /branches/sc_branches/pantasks_condor/cmd.data/integrate.c
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.data/integrate.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.data/integrate.c	(revision 34783)
@@ -0,0 +1,60 @@
+# include "data.h"
+
+int integrate (int argc, char **argv) {
+  
+  int i, N, VERBOSE, isFloat;
+  opihi_flt *Yf;
+  opihi_int *Yi;
+  double start, end, value, range, dvalue;
+  Vector *vecx, *vecy;
+
+  VERBOSE = FALSE;
+  if ((N = get_argument (argc, argv, "-v"))) {
+    VERBOSE = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+
+  if (argc != 5) {
+    gprint (GP_ERR, "USAGE: integrate <x> <y> start end\n");
+    return (FALSE);
+  }
+
+  if ((vecx = SelectVector (argv[1], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((vecy = SelectVector (argv[2], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+
+  start = atof (argv[3]);
+  end   = atof (argv[4]);
+
+  isFloat = vecy[0].type == OPIHI_FLT;
+  Yf = vecy[0].elements.Flt;
+  Yi = vecy[0].elements.Int;
+
+  value = 0;
+  range = 0;
+
+  if (vecx[0].type == OPIHI_FLT) {
+    opihi_flt *X = vecx[0].elements.Flt;
+    for (i = 0; i < vecx[0].Nelements-1; i++, X++, Yi++, Yf++) {
+      if ((*X >= start) && (*X <= end)) {
+	dvalue = (isFloat) ? *Yf : *Yi;
+	value += dvalue * (X[1] - X[0]);
+	range += (X[1] - X[0]);
+      }
+    }      
+  } else {
+    opihi_int *X = vecx[0].elements.Int;
+    for (i = 0; i < vecx[0].Nelements-1; i++, X++, Yi++, Yf++) {
+      if ((*X >= start) && (*X <= end)) {
+	dvalue = (isFloat) ? *Yf : *Yi;
+	value += dvalue * (X[1] - X[0]);
+	range += (X[1] - X[0]);
+      }
+    }      
+  }
+
+  set_variable ("sum", value); 
+  set_variable ("range", range);
+  if (VERBOSE) gprint (GP_LOG, "sum: %f\n", value);
+  return (TRUE);
+}
+
Index: /branches/sc_branches/pantasks_condor/cmd.data/interpolate.c
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.data/interpolate.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.data/interpolate.c	(revision 34783)
@@ -0,0 +1,55 @@
+# include "data.h"
+
+int interpolate (int argc, char **argv) {
+
+  int  i, j;
+  double x0, x1, dx, dy, y0;
+  Vector *xout, *yout, *xin, *yin;
+
+  /** check basic syntax **/
+  if (argc != 5) {
+    gprint (GP_ERR, "USAGE: interpolate Xi Yi Xo Yo\n");
+    gprint (GP_ERR, "  Xi Yi - sorted reference vectors\n");
+    gprint (GP_ERR, "  Xo    - output positions\n");
+    gprint (GP_ERR, "  Yo    - output values\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);
+
+  REQUIRE_VECTOR_FLT (xin, FALSE); 
+  REQUIRE_VECTOR_FLT (yin, FALSE); 
+  REQUIRE_VECTOR_FLT (xout, FALSE); 
+  ResetVector (yout, OPIHI_FLT, xout[0].Nelements);
+
+  dx = xin[0].elements.Flt[1] - xin[0].elements.Flt[0];
+  dy = yin[0].elements.Flt[1] - yin[0].elements.Flt[0];
+  x0 = xin[0].elements.Flt[0];
+  y0 = yin[0].elements.Flt[0];
+  
+  /* in vectors are sorted, out vectors are not */
+  for (j = 0; j < xin[0].Nelements - 1; j++) {
+    dx = xin[0].elements.Flt[j+1] - xin[0].elements.Flt[j];
+    dy = yin[0].elements.Flt[j+1] - yin[0].elements.Flt[j];
+    x0 = xin[0].elements.Flt[j];
+    y0 = yin[0].elements.Flt[j];
+    x1 = xin[0].elements.Flt[j+1];
+    for (i = 0; i < xout[0].Nelements; i++) {
+      if ((xout[0].elements.Flt[i] >= x0) && (xout[0].elements.Flt[i] < x1)) {
+	yout[0].elements.Flt[i] = (dy/dx)*(xout[0].elements.Flt[i] - x0) + y0;
+      }
+      if ((j == 0) && (xout[0].elements.Flt[i] < x0)) {
+	yout[0].elements.Flt[i] = (dy/dx)*(xout[0].elements.Flt[i] - x0) + y0;
+      }
+      if ((j == xin[0].Nelements - 2) && (xout[0].elements.Flt[i] >= x1)) {
+	yout[0].elements.Flt[i] = (dy/dx)*(xout[0].elements.Flt[i] - x0) + y0;
+      }
+    }    
+  }
+
+  return (TRUE);
+    
+}
Index: /branches/sc_branches/pantasks_condor/cmd.data/jpeg.c
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.data/jpeg.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.data/jpeg.c	(revision 34783)
@@ -0,0 +1,67 @@
+# include "data.h"
+
+int jpeg (int argc, char **argv) {
+
+  char filename[1024];
+  int N, kapa, IsPNG, IsPPM;
+  char *name;
+  
+  if ((N = get_argument (argc, argv, "--help"))) {
+    gprint (GP_ERR, "USAGE: jpeg [-name file] [-g | -i] [-n device] [-ppm]\n");
+    return (FALSE);
+  }
+
+  /* image type */
+  IsPPM = IsPNG = FALSE;
+  if ((N = get_argument (argc, argv, "-ppm"))) {
+    remove_argument (N, &argc, argv);
+    IsPPM = TRUE;
+  }
+  if ((N = get_argument (argc, argv, "-png"))) {
+    remove_argument (N, &argc, argv);
+    IsPNG = TRUE;
+  }
+  if (!strcmp (argv[0], "png")) IsPNG = TRUE;
+  if (!strcmp (argv[0], "ppm")) IsPPM = TRUE;
+
+  /* file name */
+  filename[0] = 0;
+  if ((N = get_argument (argc, argv, "-name"))) {
+    remove_argument (N, &argc, argv);
+    strcpy (filename, argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  /* display source */
+  name = NULL;
+  if ((N = get_argument (argc, argv, "-n"))) {
+    remove_argument (N, &argc, argv);
+    name = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  /* XXX output png / jpeg needs to include both graph and image
+     if available.  this is a poor mix of data representations 
+     (png for image / jpeg for plots)
+  if ((N = get_argument (argc, argv, "-g"))) {
+  if ((N = get_argument (argc, argv, "-i"))) {
+  */
+
+  if (!GetGraph (NULL, &kapa, name)) return (FALSE);
+  if (!IsPNG && !IsPPM) {
+    if (!filename[0]) strcpy (filename, "kapa.jpg");
+    KiiJPEG (kapa, filename);
+  }
+  if (IsPNG) {
+    if (!filename[0]) strcpy (filename, "kapa.png");
+    KapaPNG (kapa, filename);
+  } 
+  if (IsPPM) {
+    if (!filename[0]) strcpy (filename, "kapa.ppm");
+    KapaPPM (kapa, filename);
+  }
+  return (TRUE);
+}
+
+/* jpeg converts graph to png or ppm
+   jpeg converts image to jpeg */
Index: /branches/sc_branches/pantasks_condor/cmd.data/kern.c
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.data/kern.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.data/kern.c	(revision 34783)
@@ -0,0 +1,123 @@
+# include "data.h"
+
+/** need to allow larger kernels (5x5, 7x7, etc) **/
+int kern (int argc, char **argv) {
+
+  int i, n, m;
+  int NX, NY, status;
+  FILE *f;
+  float *in_buff, *out_buff, *ib, *ob;
+  char line[256], *list;
+  double kernel[3][3], val;
+  Buffer *buf;
+
+  if (argc != 3) {
+    gprint (GP_ERR, "USAGE: kern buffer (file)\n");
+    gprint (GP_ERR, "USAGE: kern buffer (list)\n");
+    gprint (GP_ERR, "USAGE: kern buffer -\n");
+    gprint (GP_ERR, "kernel file contains a 3x3 matrix for convolution\n");
+    return (FALSE);
+  }
+
+  if ((buf = SelectBuffer (argv[1], OLDBUFFER, TRUE)) == NULL) return (FALSE);
+
+  /* open file to read in kernel */
+  if (!strcmp (argv[2], "-")) {
+    for (i = 0; i < 3; i++) {
+      status = scan_line (stdin, line);
+      if (status == EOF) {
+	gprint (GP_ERR, "kernel should be a 3x3 matrix...\n");
+	return (FALSE);
+      }
+      dparse (&kernel[0][i], 1, line);
+      dparse (&kernel[1][i], 2, line);
+      dparse (&kernel[2][i], 3, line);
+    }
+    goto have_kernel;
+  }
+  /* test list */
+  sprintf (line, "%s:n", argv[2]);
+  if ((list = get_variable (line)) == (char *) NULL) {
+    /* file */
+    f = fopen (argv[2], "r");
+    if (f == NULL) {
+      gprint (GP_ERR, "file not found: %s\n", argv[2]);
+      return (FALSE);
+    }
+    for (i = 0; i < 3; i++) {
+      status = scan_line (f, line);
+      if (status == EOF) {
+	gprint (GP_ERR, "kernel should be a 3x3 matrix...\n");
+	fclose (f);
+      }
+      return (FALSE);
+      dparse (&kernel[0][i], 1, line);
+      dparse (&kernel[1][i], 2, line);
+      dparse (&kernel[2][i], 3, line);
+    }
+    fclose (f);
+    goto have_kernel;
+  }
+  if (atoi (list) != 9) {
+    gprint (GP_ERR, "kernel should be a 3x3 matrix...\n");
+    return (FALSE);
+  }
+  free (list);
+  for (i = 0; i < 9; i++) {
+    sprintf (line, "%s:%d", argv[2], i);
+    list = get_variable (line);
+    if (list == (char *) NULL) {
+      gprint (GP_ERR, "kernel should be a 3x3 matrix...\n");
+      return (FALSE);
+    }
+    kernel[(int)(i/3)][i%3] = atof (list);
+    free (list);
+  }
+  goto have_kernel;
+
+ have_kernel:
+  /* normalize kernel */
+  val = 0;
+  for (n = 0; n < 3; n++) {
+    for (m = 0; m < 3; m++) {
+      val += kernel[n][m];
+    }
+  }
+  if (val == 0) {
+    gprint (GP_ERR, "kernel has zero power, not renormalizing...");
+  } else {
+    for (n = 0; n < 3; n++) {
+      for (m = 0; m < 3; m++) {
+	kernel[n][m] /= val;
+      }
+    }
+  }
+
+  gprint (GP_ERR, "working...");
+  
+  /* create output buffer */
+  NX = buf[0].header.Naxis[0];
+  NY = buf[0].header.Naxis[1];
+  in_buff = (float *)buf[0].matrix.buffer;
+  ALLOCATE (buf[0].matrix.buffer, char, sizeof(float)*NX*NY);
+  out_buff = (float *)buf[0].matrix.buffer;
+  
+  /* do the convolution (on all but outer rows) */
+  
+  for (n = 0; n < 3; n++) {
+    for (m = 0; m < 3; m++) {
+      gprint (GP_ERR, "%d", n*3 + m + 1);
+      val = kernel[n][m];
+      ob = out_buff + NX + 1;
+      ib = in_buff + m*NX + n;
+      for (i = 0; i < (NX-2)*(NY-2); i++, ob++, ib++) {
+	*ob += *ib*val;
+      }
+    }
+  }
+  gprint (GP_ERR, "(done)\n");
+
+  free (in_buff);
+  return (TRUE);
+
+}
Index: /branches/sc_branches/pantasks_condor/cmd.data/keyword.c
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.data/keyword.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.data/keyword.c	(revision 34783)
@@ -0,0 +1,158 @@
+# include "data.h"
+
+enum {NONE, STRING, FLOAT, INT, BOOLEAN, KEYCOMMENT, COMMENT};
+
+/** WARNING: no error checking on variable validity **/
+int keyword (int argc, char **argv) {
+
+  int ivalue, status, N, ascomment, asfloat, delete, soft, Wmode;
+  char line[80];
+  double value;
+  Buffer *buf;
+
+  asfloat = FALSE;
+  if ((N = get_argument (argc, argv, "-f"))) {
+    remove_argument (N, &argc, argv);
+    asfloat = TRUE;
+  }
+  
+  ascomment = FALSE;
+  if ((N = get_argument (argc, argv, "-c"))) {
+    remove_argument (N, &argc, argv);
+    ascomment = TRUE;
+  }
+  
+  delete = FALSE;
+  if ((N = get_argument (argc, argv, "-d"))) {
+    remove_argument (N, &argc, argv);
+    delete = TRUE;
+  }
+  
+  /* return TRUE for missing keyword if soft */
+  soft = FALSE;
+  if ((N = get_argument (argc, argv, "-soft"))) {
+    remove_argument (N, &argc, argv);
+    soft = TRUE;
+  }
+  
+  /* identify write modes */
+  Wmode = NONE;
+  if ((N = get_argument (argc, argv, "-w"))) {
+    remove_argument (N, &argc, argv);
+    strcpy (line, argv[N]);
+    remove_argument (N, &argc, argv);
+    Wmode = STRING;
+  }
+  if ((N = get_argument (argc, argv, "-wf"))) {
+    remove_argument (N, &argc, argv);
+    strcpy (line, argv[N]);
+    remove_argument (N, &argc, argv);
+    Wmode = FLOAT;
+  }
+  if ((N = get_argument (argc, argv, "-wd"))) {
+    remove_argument (N, &argc, argv);
+    strcpy (line, argv[N]);
+    remove_argument (N, &argc, argv);
+    Wmode = INT;
+  }
+  if ((N = get_argument (argc, argv, "-wc"))) {
+    remove_argument (N, &argc, argv);
+    strcpy (line, argv[N]);
+    remove_argument (N, &argc, argv);
+    Wmode = KEYCOMMENT;
+  }
+  if ((N = get_argument (argc, argv, "-ws"))) {
+    remove_argument (N, &argc, argv);
+    strcpy (line, argv[N]);
+    remove_argument (N, &argc, argv);
+    Wmode = COMMENT;
+  }
+  if ((N = get_argument (argc, argv, "-wb"))) {
+    remove_argument (N, &argc, argv);
+    strcpy (line, argv[N]);
+    remove_argument (N, &argc, argv);
+    Wmode = BOOLEAN;
+  }
+
+  if (!((argc == 3) || (!N && argc == 4))) {
+    gprint (GP_ERR, "USAGE: keyword <buffer> (KEYWORD) [variable] [-d] [-w(mode) value]\n");
+    gprint (GP_ERR, " -w modes: \n");
+    gprint (GP_ERR, "  -w  - string\n");
+    gprint (GP_ERR, "  -wf - float\n");
+    gprint (GP_ERR, "  -wd - int\n");
+    gprint (GP_ERR, "  -wb - boolean\n");
+    gprint (GP_ERR, "  -wc - comment\n");
+    gprint (GP_ERR, "  -ws - full string comment\n");
+    return (FALSE);
+  }
+
+  if ((buf = SelectBuffer (argv[1], OLDBUFFER, TRUE)) == NULL) return (FALSE);
+
+  if (Wmode != NONE) {
+    switch (Wmode) {
+    case STRING:
+      gfits_modify (&buf[0].header, argv[2], "%s", 1, line);
+      return (TRUE);
+    case FLOAT:
+      value = atof(line);
+      gfits_modify (&buf[0].header, argv[2], "%lf", 1, value);
+      return (TRUE);
+    case INT:
+      gfits_modify (&buf[0].header, argv[2], "%d", 1, atoi(line));
+      return (TRUE);
+    case BOOLEAN:
+      if (strcasecmp (line, "T") && strcasecmp (line, "TRUE") && strcasecmp (line, "F") && strcasecmp (line, "FALSE")) {
+	gprint (GP_ERR, "syntax error in boolean value\n");
+	return (FALSE);
+      }
+      ivalue = !strcasecmp (line, "T");
+      gfits_modify_alt (&buf[0].header, argv[2], "%t", 1, ivalue);
+      return (TRUE);
+    case KEYCOMMENT:
+      gfits_modify_alt (&buf[0].header, argv[2], "%C", 1, line);
+      return (TRUE);
+    case COMMENT:
+      gfits_modify_alt (&buf[0].header, argv[2], "%S", 0, line);
+      return (TRUE);
+    }
+  }
+  
+  if (delete) {
+    gfits_delete (&buf[0].header, argv[2], -1);
+    return (TRUE);
+  }
+  
+  /* grab the value in the given format, either a string or a digit */
+  if (asfloat) {
+    status = gfits_scan (&buf[0].header, argv[2], "%lf", 1, &value);
+    if (!status) goto failure;
+    if (argc == 4) 
+      set_variable (argv[3], value);
+    else 
+      gprint (GP_LOG, "%s: %f\n", argv[2], value);
+    return (TRUE);
+  } 
+
+  if (ascomment) {
+    status = gfits_scan_alt (&buf[0].header, argv[2], "%C", 1, line);
+    if (!status) goto failure;
+    if (argc == 4) 
+      set_str_variable (argv[3], line);
+    else 
+      gprint (GP_LOG, "%s: %s\n", argv[2], line);
+    return (TRUE);
+  }    
+
+  /* not-specified */
+  status = gfits_scan (&buf[0].header, argv[2], "%s", 1, line);
+  if (!status) goto failure;
+  if (argc == 4) 
+    set_str_variable (argv[3], line);
+  else 
+    gprint (GP_LOG, "%s: %s\n", argv[2], line);
+  return (TRUE);
+
+ failure: 
+  if (!soft) gprint (GP_ERR, "keyword %s not found\n", argv[2]);
+  return (soft);
+}
Index: /branches/sc_branches/pantasks_condor/cmd.data/labels.c
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.data/labels.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.data/labels.c	(revision 34783)
@@ -0,0 +1,95 @@
+# include "data.h"
+
+int labels (int argc, char **argv) {
+  
+  char name[64];
+  int N, size, kapa;
+
+  if (!GetGraph (NULL, &kapa, NULL)) return (FALSE);
+
+  if (get_argument (argc, argv, "-h")) {
+    gprint (GP_ERR, "label options: \n");
+    gprint (GP_ERR, " -x : bottom-center\n");
+    gprint (GP_ERR, " +x : top-center\n");
+    gprint (GP_ERR, " -y : right-side\n");
+    gprint (GP_ERR, " +y : left-side\n\n");
+
+    gprint (GP_ERR, " -ul : upper-left corner\n");
+    gprint (GP_ERR, " -ll : lower-left corner\n");
+    gprint (GP_ERR, " -ur : upper-right corner\n");
+    gprint (GP_ERR, " -lr : lower-right corner\n\n");
+
+    gprint (GP_ERR, " -fn (font) (size) : set font and size\n");
+    gprint (GP_ERR, "   (font) : courier, helvetica, times, symbol\n\n");
+    gprint (GP_ERR, " label special characters:\n");
+    gprint (GP_ERR, " ^ : superscript\n");
+    gprint (GP_ERR, " _ : subscript\n");
+    gprint (GP_ERR, " | : default script \n");
+    gprint (GP_ERR, " &c, &h, &t, &s : set font\n\n");
+    return (FALSE);
+  }
+
+  if ((N = get_argument (argc, argv, "-fn"))) {
+    remove_argument (N, &argc, argv);
+    strcpy (name, argv[N]);
+    remove_argument (N, &argc, argv);
+    size = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+    KapaSetFont (kapa, name, size);
+  } 
+
+  if ((N = get_argument (argc, argv, "-x"))) {
+    remove_argument (N, &argc, argv);
+    KapaSendLabel (kapa, argv[N], 0);
+    remove_argument (N, &argc, argv);
+  }
+
+  if ((N = get_argument (argc, argv, "-y"))) {
+    remove_argument (N, &argc, argv);
+    KapaSendLabel (kapa, argv[N], 1);
+    remove_argument (N, &argc, argv);
+  }
+
+  if ((N = get_argument (argc, argv, "+x"))) {
+    remove_argument (N, &argc, argv);
+    KapaSendLabel (kapa, argv[N], 2);
+    remove_argument (N, &argc, argv);
+  }
+
+  if ((N = get_argument (argc, argv, "+y"))) {
+    remove_argument (N, &argc, argv);
+    KapaSendLabel (kapa, argv[N], 3);
+    remove_argument (N, &argc, argv);
+  }
+
+  if ((N = get_argument (argc, argv, "-ul"))) {
+    remove_argument (N, &argc, argv);
+    KapaSendLabel (kapa, argv[N], 4);
+    remove_argument (N, &argc, argv);
+  }
+
+  if ((N = get_argument (argc, argv, "-ur"))) {
+    remove_argument (N, &argc, argv);
+    KapaSendLabel (kapa, argv[N], 5);
+    remove_argument (N, &argc, argv);
+  }
+
+  if ((N = get_argument (argc, argv, "-ll"))) {
+    remove_argument (N, &argc, argv);
+    KapaSendLabel (kapa, argv[N], 6);
+    remove_argument (N, &argc, argv);
+  }
+
+  if ((N = get_argument (argc, argv, "-lr"))) {
+    remove_argument (N, &argc, argv);
+    KapaSendLabel (kapa, argv[N], 7);
+    remove_argument (N, &argc, argv);
+  }
+
+  if (argc != 1) {
+    gprint (GP_ERR, "USAGE: labels [-x] [-y] [+x] [+y] [-ul] [-ur] [-ll] [-lr]\n");
+    return (FALSE);
+  }
+
+  return (TRUE);
+}
Index: /branches/sc_branches/pantasks_condor/cmd.data/limits.c
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.data/limits.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.data/limits.c	(revision 34783)
@@ -0,0 +1,101 @@
+# include "data.h"
+
+int limits (int argc, char **argv) {
+
+  int N, APPLY, dX, dY;
+  int kapa;
+  char *name;
+  Graphdata graphmode;
+  Vector *xvec, *yvec;
+
+  xvec = yvec = NULL;
+
+  APPLY = FALSE;
+  if ((N = get_argument (argc, argv, "-a"))) {
+    remove_argument (N, &argc, argv);
+    APPLY = TRUE;
+  }
+  name = NULL;
+  if ((N = get_argument (argc, argv, "-n"))) {
+    remove_argument (N, &argc, argv);
+    name = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+  if (!GetGraph (&graphmode, &kapa, name)) return (FALSE);
+  FREE (name);
+
+  // XXX need an option to set the limits based on the current image bounds
+  if ((N = get_argument (argc, argv, "-image"))) {
+    remove_argument (N, &argc, argv);
+    KapaGetImageRange (kapa, &graphmode.xmin, &graphmode.xmax, &graphmode.ymax, &graphmode.ymin, &dX, &dY);
+
+    set_variable ("XMIN", graphmode.xmin);
+    set_variable ("XMAX", graphmode.xmax);
+    set_variable ("YMIN", graphmode.ymin);
+    set_variable ("YMAX", graphmode.ymax);
+
+    set_variable ("KAPA_XMIN", graphmode.xmin);
+    set_variable ("KAPA_XMAX", graphmode.xmax);
+    set_variable ("KAPA_YMIN", graphmode.ymin);
+    set_variable ("KAPA_YMAX", graphmode.ymax);
+
+    set_variable ("KAPA_XPIX", dX);
+    set_variable ("KAPA_YPIX", dY);
+
+    // if (!NoClear) KapaClearSections (kapa);
+    SetGraph (&graphmode);
+    KapaSetLimits (kapa, &graphmode);
+    return (TRUE);
+    // Set Region based on image
+  }
+
+  if (argc == 1) {
+    gprint (GP_ERR, "limits: %f %f %f %f [-a] [-n device]\n",
+	     graphmode.xmin, graphmode.xmax,
+	     graphmode.ymin, graphmode.ymax);
+    goto success;
+  }
+
+  if (argc == 3) { /* expect to see: limits x y */
+    if ((xvec = SelectVector (argv[1], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+    if ((yvec = SelectVector (argv[2], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+    goto success;
+  }
+    
+  if (argc == 4) { /* expect to see: limits x num num or limits num num y */
+    if (ISNUM(argv[1][0]) && ISNUM(argv[2][0])) {
+      if ((yvec = SelectVector (argv[3], OLDVECTOR, FALSE)) == NULL) goto error;
+      graphmode.xmin = atof (argv[1]);
+      graphmode.xmax = atof (argv[2]);
+      goto success;
+    }
+    if (ISNUM(argv[2][0]) && ISNUM(argv[3][0])) {
+      if ((xvec = SelectVector (argv[1], OLDVECTOR, FALSE)) == NULL) goto error;
+      graphmode.ymin = atof (argv[2]);
+      graphmode.ymax = atof (argv[3]);
+      goto success;
+    }
+    goto error;
+  }
+  
+  if (argc == 5) {
+    graphmode.xmin = atof (argv[1]);
+    graphmode.xmax = atof (argv[2]);
+    graphmode.ymin = atof (argv[3]);
+    graphmode.ymax = atof (argv[4]);
+    goto success;
+  }
+
+  gprint (GP_ERR, "USAGE: limits [xrange] [yrange]\n");
+  gprint (GP_ERR, " [range] may be either [min max] or a vector\n");
+  return (FALSE);
+
+ error:
+  gprint (GP_ERR, "error in vectors\n");
+  return (FALSE);
+
+ success:
+  SetLimits (xvec, yvec, &graphmode);
+  if (APPLY) KapaSetLimits (kapa, &graphmode);
+  return (TRUE);
+}
Index: /branches/sc_branches/pantasks_condor/cmd.data/line.c
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.data/line.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.data/line.c	(revision 34783)
@@ -0,0 +1,43 @@
+# include "data.h"
+
+int line (int argc, char **argv) {
+  
+  int kapa, N;
+  Graphdata graphmode;
+  float x[2], y[2];
+
+  /* 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 (!style_args (&graphmode, &argc, argv, &kapa)) return FALSE;
+
+  if (argc != 6) {
+    gprint (GP_ERR, "USAGE: line <x> <y> to <x> <y>\n");
+    return (FALSE);
+  }
+  x[0] = atof(argv[1]);
+  y[0] = atof(argv[2]);
+  x[1] = atof(argv[4]);
+  y[1] = atof(argv[5]);
+
+  if (FracPositions) {
+    x[0] =  x[0] * (graphmode.xmax - graphmode.xmin) + graphmode.xmin;
+    y[0] =  y[0] * (graphmode.ymax - graphmode.ymin) + graphmode.ymin;
+    x[1] =  x[1] * (graphmode.xmax - graphmode.xmin) + graphmode.xmin;
+    y[1] =  y[1] * (graphmode.ymax - graphmode.ymin) + graphmode.ymin;
+  }    
+
+  /* set point style and errorbar mode (these are NOT sticky) */
+  graphmode.style = 0;
+  graphmode.etype = 0;
+
+  if (!KapaPrepPlot (kapa, 2, &graphmode)) return (FALSE);
+  KapaPlotVector (kapa, 2, x, "x");
+  KapaPlotVector (kapa, 2, y, "y");
+  
+  return (TRUE);
+}
Index: /branches/sc_branches/pantasks_condor/cmd.data/list_buffers.c
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.data/list_buffers.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.data/list_buffers.c	(revision 34783)
@@ -0,0 +1,51 @@
+# include "data.h"
+
+int list_buffers (int argc, char **argv) {
+
+  if (argc == 3) PrintBuffers (TRUE);
+  else PrintBuffers (FALSE);
+
+  return (TRUE);
+
+}
+
+# if (0) 
+int mtype (int argc, char **argv) {
+
+  Variable = (char *) NULL;
+  if ((N = get_argument (argc, argv, "-var"))) {
+    remove_argument (N, &argc, argv);
+    Variable = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  if (argc != 2) {
+    gprint (GP_ERR, "USAGE: mtype (buffer) [-var out]\n");
+    return (FALSE);
+  }
+
+  Buffer *buf = SelectBuffer (argv[1], OLDBUFFER, FALSE);
+  if (!buf) {
+    gprint (GP_ERR, "unknown buffer %s\n", argv[1]);
+    free (Variable);
+    return FALSE;
+  }
+
+  if (buf->type == OPIHI_FLT) {
+    if (Variable) {
+      set_str_variable (Variable, "FLT");
+    } else {
+      gprint (GP_LOG, "%s : FLT\n", argv[1]);
+    }
+  } else {
+    if (Variable) {
+      set_str_variable (Variable, "INT");
+    } else {
+      gprint (GP_LOG, "%s : INT\n", argv[1]);
+    }
+  }
+  if (Variable) free (Variable);
+
+  return (TRUE);
+}
+# endif
Index: /branches/sc_branches/pantasks_condor/cmd.data/list_header.c
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.data/list_header.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.data/list_header.c	(revision 34783)
@@ -0,0 +1,86 @@
+# include "data.h"
+
+int header (int argc, char **argv) {
+  
+  int j, N, nlines, nbytes, Nbytes, LOADHEAD, status, bitpix, unsign;
+  char *p, filename[128];
+  FILE *f;
+  double bscale, bzero;
+  Buffer *buf;
+
+  LOADHEAD = FALSE;
+  if ((N = get_argument (argc, argv, "-w"))) {
+    remove_argument (N, &argc, argv);
+    strcpy (filename, argv[N]);
+    remove_argument (N, &argc, argv);
+    LOADHEAD = TRUE;
+  }
+
+  if (argc != 2) {
+    gprint (GP_ERR, "USAGE: header <buffer> [-w filename]\n");
+    return (FALSE);
+  }
+
+  if ((buf = SelectBuffer (argv[1], OLDBUFFER, TRUE)) == NULL) return (FALSE);
+
+  if (LOADHEAD) {
+    f = fopen (filename, "r");
+    if (f == (FILE *) NULL) {
+      gprint (GP_ERR, "file %s not found\n", filename);
+      return (FALSE);
+    }
+    fclose (f);
+    
+    bitpix = buf[0].header.bitpix;
+    bzero  = buf[0].header.bzero;
+    bscale = buf[0].header.bscale;
+    unsign = buf[0].header.unsign;
+    gfits_free_header (&buf[0].header);
+    
+    strcpy (filename, buf[0].file);
+    strcpy (buf[0].file, "*");
+    strcat (buf[0].file, filename);
+
+    status = gfits_read_header (argv[2], &buf[0].header);
+    if (!status) {
+      gprint (GP_ERR, "failed to read header for %s\n", argv[2]);
+      return FALSE;
+    }
+    buf[0].header.bitpix = bitpix;     
+    buf[0].header.bzero  = bzero;      
+    buf[0].header.bscale = bscale;     
+    buf[0].header.unsign = unsign;     
+    gfits_modify (&buf[0].header, "BITPIX", "%d",  1, bitpix);
+    gfits_modify (&buf[0].header, "BSCALE", "%lf", 1, bscale);
+    gfits_modify (&buf[0].header, "BZERO",  "%lf", 1, bzero);
+    gfits_modify_alt (&buf[0].header, "UNSIGN", "%t",  1, unsign);
+    
+  } else {
+
+    f = popen ("more", "w");
+    
+    p = gfits_header_field (&buf[0].header, "END", 1);
+    nlines = (p - buf[0].header.buffer) / 80;
+    nbytes = 81*nlines;
+
+    /* duplicate the header, add in the <return> chars, send to more */
+    ALLOCATE (p, char, nbytes);
+    for (j = 0; j < nlines; j++) {
+      memcpy (&p[81*j], &buf[0].header.buffer[80*j], 80);
+      p[81*j+80] = 10;
+    }
+    Nbytes = fwrite (p, sizeof(char), nbytes, f);
+    if (Nbytes != nbytes) {
+      gprint (GP_ERR, "warning: not all printed...\n");
+    }
+    free (p);
+
+    pclose (f);
+
+  }
+
+  return (TRUE);
+
+}
+
+/* XXX this function is not written in the context of the output file/buffer */
Index: /branches/sc_branches/pantasks_condor/cmd.data/list_vectors.c
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.data/list_vectors.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.data/list_vectors.c	(revision 34783)
@@ -0,0 +1,49 @@
+# include "data.h"
+
+int list_vectors (int argc, char **argv) {
+
+  ListVectors ();
+  return (TRUE);
+}
+
+int vtype (int argc, char **argv) {
+
+  int N;
+
+  char *Variable = (char *) NULL;
+  if ((N = get_argument (argc, argv, "-var"))) {
+    remove_argument (N, &argc, argv);
+    Variable = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  if (argc != 2) {
+    gprint (GP_ERR, "USAGE: vtype (vector) [-var out]\n");
+    return (FALSE);
+  }
+
+  Vector *vec = SelectVector (argv[1], OLDVECTOR, FALSE);
+  if (!vec) {
+    gprint (GP_ERR, "unknown vector %s\n", argv[1]);
+    free (Variable);
+    return FALSE;
+  }
+
+  if (vec->type == OPIHI_FLT) {
+    if (Variable) {
+      set_str_variable (Variable, "FLT");
+    } else {
+      gprint (GP_LOG, "%s : FLT\n", argv[1]);
+    }
+  } else {
+    if (Variable) {
+      set_str_variable (Variable, "INT");
+    } else {
+      gprint (GP_LOG, "%s : INT\n", argv[1]);
+    }
+  }
+  if (Variable) free (Variable);
+
+  return (TRUE);
+}
+
Index: /branches/sc_branches/pantasks_condor/cmd.data/load.c
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.data/load.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.data/load.c	(revision 34783)
@@ -0,0 +1,144 @@
+# include "data.h"
+
+int load (int argc, char **argv) {
+  
+  int i, N, ISCEL;
+  int kapa, Noverlay, NOVERLAY;
+  char *c, type[10], string[128], line[1024];
+  double x, y, dx, dy, x1, y1;
+  double dra, ddec, ra1, dec1, ra, dec;
+  FILE *f;
+  char *buffer, *name;
+  Coords coords;
+  Buffer *buf;
+  KiiOverlay *overlay;
+  KapaImageData data;
+  
+  name = NULL;
+  if ((N = get_argument (argc, argv, "-n"))) {
+    remove_argument (N, &argc, argv);
+    name = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+  if (!GetImageData (&data, &kapa, name)) return (FALSE);
+  FREE (name);
+
+  ISCEL = FALSE;
+  if ((N = get_argument (argc, argv, "-c"))) {
+    remove_argument (N, &argc, argv);
+    ISCEL = TRUE;
+  }
+
+  if (argc != 3) {
+    gprint (GP_ERR, "USAGE: load (overlay) <filename>\n [-c] [-n]");
+    gprint (GP_ERR, "  -c: read overlay in celestial coords\n");
+    return (FALSE);
+  }
+  
+  if (!strcmp (argv[2], "-")) {
+    f = stdin;
+  } else {
+    f = fopen (argv[2], "r");
+  }
+  if (f == NULL) {
+    gprint (GP_ERR, "can't find file %s\n", argv[2]);
+    return (FALSE);
+  }
+
+  if (ISCEL) {
+    if ((buf = SelectBuffer (data.name, OLDBUFFER, TRUE)) == NULL) return (FALSE);
+    GetCoords (&coords, &buf[0].header);
+  }
+
+  Noverlay = 0;
+  NOVERLAY = 1000;
+  ALLOCATE (overlay, KiiOverlay, NOVERLAY);
+
+  ALLOCATE (buffer, char, 65536);  /* space for 512 lines of 128 bytes */
+  bzero (buffer, 65536);
+
+  dx = dy = 0;
+  while (scan_line (f, line) != EOF) {
+    c = strchr (line, '#');
+    if (c != (char *) NULL) 
+      *c = 0;  /* force end of line at comment! */
+    while ((c = strchr (line, '(')) != (char *) NULL) 
+      *c = ' ';
+    while ((c = strchr (line, ')')) != (char *) NULL) 
+      *c = ' ';
+    while ((c = strchr (line, ',')) != (char *) NULL) 
+      *c = ' ';
+    /* we could use some syntactial checks here */
+    /* have to get all three for this to be any valid object, if the line is commented out,
+     we should get none, so check that N == 3 before continuing: */
+    N = sscanf (line, "%s %lf %lf %lf %lf", type, &ra, &dec, &dra, &ddec);
+    switch (N) {
+    case 0:
+    case -1:
+      continue;
+    case 1:
+    case 2:
+    case 3:
+      if (strcmp (type, "TEXT")) {
+	gprint (GP_ERR, "syntax error in line:\n   %s\n", line);
+	continue;
+      }
+      sscanf (line, "%s %lf %lf %127s", type, &ra, &dec, string);
+    case 4:
+      ddec = dra;
+    case 5:
+      x = ra;
+      y = dec;
+      dx = dra;
+      dy = ddec;
+      if (ISCEL) {
+	if (!strcmp (type, "LINE")) {
+	  RD_to_XY (&x, &y, ra, dec, &coords);
+	  ra1 = ra + dra;
+	  dec1 = dec + ddec;
+	  RD_to_XY (&x1, &y1, ra1, dec1, &coords);
+	  dy = (y1 - y);
+	  dx = (x1 - x);
+	} else {
+	  RD_to_XY (&x, &y, ra, dec, &coords);
+	  ra1 = ra;
+	  dec1 = dec + ddec;
+	  RD_to_XY (&x1, &y1, ra1, dec1, &coords);
+	  dy = (fabs(x1 - x) + fabs(y1 - y));
+	  ra1 = ra + dra/cos(dec*RAD_DEG);;
+	  dec1 = dec;
+	  RD_to_XY (&x1, &y1, ra1, dec1, &coords);
+	  dx = (fabs(x1 - x) + fabs(y1 - y));
+	}
+      }
+    }
+    overlay[Noverlay].type = KiiOverlayTypeByName (type);
+    if (overlay[Noverlay].type == KII_OVERLAY_TEXT) {
+      overlay[Noverlay].text = strcreate (string);
+    } else {
+      overlay[Noverlay].text = NULL;
+    }
+    overlay[Noverlay].x = x;
+    overlay[Noverlay].y = y;
+    overlay[Noverlay].dx = dx;
+    overlay[Noverlay].dy = dy;
+    Noverlay++;
+    CHECK_REALLOCATE (overlay, KiiOverlay, NOVERLAY, Noverlay, 1000);
+  }
+
+  KiiLoadOverlay (kapa, overlay, Noverlay, argv[1]);
+
+  for (i = 0; i < Noverlay; i++) {
+    if (overlay[i].text == NULL) continue;
+    free (overlay[i].text);
+  }
+  free (overlay);
+
+  gprint (GP_ERR, "loaded %d objects\n", Noverlay);
+
+  if (f != stdin) {
+    fclose (f);
+  }
+  return (TRUE);
+}
+
Index: /branches/sc_branches/pantasks_condor/cmd.data/lookup.c
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.data/lookup.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.data/lookup.c	(revision 34783)
@@ -0,0 +1,43 @@
+# include "data.h"
+
+int lookup (int argc, char **argv) {
+  
+  int i, j;
+  opihi_flt *ip, *op, *xp, *yp;
+  Vector *in, *out, *xv, *yv;
+
+  if (argc != 5) {
+    gprint (GP_ERR, "USAGE: lookup (input) (output) (x) (y)\n");
+    return (FALSE);
+  }
+
+  if ((in  = SelectVector (argv[1], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((out = SelectVector (argv[2], ANYVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((xv  = SelectVector (argv[3], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((yv  = SelectVector (argv[4], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+
+  REQUIRE_VECTOR_FLT (in, FALSE); 
+  REQUIRE_VECTOR_FLT (xv, FALSE); 
+  REQUIRE_VECTOR_FLT (yv, FALSE); 
+
+  if (xv[0].Nelements != yv[0].Nelements) {
+      gprint (GP_ERR, "unmatched lookup table lengths\n");
+      return (FALSE);
+  }
+
+  ResetVector (out, OPIHI_FLT, in[0].Nelements);
+
+  ip = in[0].elements.Flt;
+  op = out[0].elements.Flt;
+
+  for (i = 0; i < in[0].Nelements; i++, ip++, op++) {
+    // re-write this using bisection
+    xp = xv[0].elements.Flt;
+    yp = yv[0].elements.Flt;
+
+    for (j = 0; (*ip < *xp) && (j < yv[0].Nelements); j++);
+    *op = *yp;
+  }      
+
+  return (TRUE);
+}
Index: /branches/sc_branches/pantasks_condor/cmd.data/match2d.c
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.data/match2d.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.data/match2d.c	(revision 34783)
@@ -0,0 +1,228 @@
+# include "data.h"
+
+int find_matches2d (Vector *X1, Vector *Y1, Vector *X2, Vector *Y2, double Radius, Vector *index1, Vector *index2);
+int find_matches2d_closest (Vector *X1, Vector *Y1, Vector *X2, Vector *Y2, double Radius, Vector *index);
+
+// match2d (X1) (Y1) (X2) (Y2) (Radius) [-index1 (index1)] [-index2 (index2)] [-nomatch1 nomatch1] [-nomatch2 nomatch2]
+// X1[Index1] <=> X2[Index2] (etc)
+int match2d (int argc, char **argv) {
+  
+  int N, CLOSEST;
+  double Radius;
+  char *endptr;
+  Vector *X1vec, *Y1vec, *X2vec, *Y2vec;
+  Vector *index1, *index2;
+
+  CLOSEST = FALSE;
+  if ((N = get_argument (argc, argv, "-closest"))) {
+    remove_argument (N, &argc, argv);
+    CLOSEST = TRUE;
+  }
+
+  if ((N = get_argument (argc, argv, "-index1"))) {
+    remove_argument (N, &argc, argv);
+    if ((index1 = SelectVector (argv[N], ANYVECTOR, TRUE)) == NULL) return (FALSE);    
+    remove_argument (N, &argc, argv);
+  } else {
+    if ((index1 = SelectVector ("index1", ANYVECTOR, TRUE)) == NULL) return (FALSE);    
+  }
+
+  if ((N = get_argument (argc, argv, "-index2"))) {
+    remove_argument (N, &argc, argv);
+    if ((index2 = SelectVector (argv[N], ANYVECTOR, TRUE)) == NULL) return (FALSE);    
+    remove_argument (N, &argc, argv);
+  } else {
+    if ((index2 = SelectVector ("index2", ANYVECTOR, TRUE)) == NULL) return (FALSE);    
+  }
+
+  if (argc != 6) {
+    gprint (GP_ERR, "USAGE: match2d X1 Y1 X2 Y2 Radius [-index1 (index1)] [-index2 (index2)] [-closest]\n");
+    gprint (GP_ERR, "  if -closest is provided, index1 & index2 will have the same length as X1 and X2 (respectively)\n");
+    gprint (GP_ERR, "    with either the index of the match or a value of -1 for non-matches\n");
+    return (FALSE);
+  }
+
+  /*
+    we have two modes of operation:  
+
+    without -closest, we are finding all matched pairs within the match radius.  in this
+    case, the two index vectors have the same length, one entry per matched pair.
+    x1[index1],y1[index1] matches to x2[index2],y2[index2].
+
+    with -closest selected, we are finding the closest element of set 1 to each of set 2
+    and vice versa.  in this case, index1 is always the same length as x1,y1, while index2
+    is the same lengths as x2,y2.  x2[index1],y2[index1] matches x1,y1 while
+    x1[index2],y1[index2] matches x2,y2
+
+   */
+
+  if ((X1vec = SelectVector (argv[1], OLDVECTOR, TRUE)) == NULL) return (FALSE);    
+  if ((Y1vec = SelectVector (argv[2], OLDVECTOR, TRUE)) == NULL) return (FALSE);    
+  if ((X2vec = SelectVector (argv[3], OLDVECTOR, TRUE)) == NULL) return (FALSE);    
+  if ((Y2vec = SelectVector (argv[4], OLDVECTOR, TRUE)) == NULL) return (FALSE);    
+
+  if (X1vec[0].Nelements != Y1vec[0].Nelements) {
+    gprint (GP_ERR, "X1 & Y1 vectors must have same length\n");
+    return (FALSE);
+  }
+  if (X2vec[0].Nelements != Y2vec[0].Nelements) {
+    gprint (GP_ERR, "X2 & Y2 vectors must have same length\n");
+    return (FALSE);
+  }
+
+  REQUIRE_VECTOR_FLT (X1vec, FALSE); 
+  REQUIRE_VECTOR_FLT (Y1vec, FALSE); 
+  REQUIRE_VECTOR_FLT (X2vec, FALSE); 
+  REQUIRE_VECTOR_FLT (Y2vec, FALSE); 
+
+  Radius = strtod (argv[5], &endptr);
+  if (*endptr) {
+    gprint (GP_ERR, "Radius must be numerical (%s)\n", argv[5]);
+    return (FALSE);
+  }
+
+  if (CLOSEST) {
+      find_matches2d_closest (X1vec, Y1vec, X2vec, Y2vec, Radius, index1);
+      find_matches2d_closest (X2vec, Y2vec, X1vec, Y1vec, Radius, index2);
+  } else {
+      find_matches2d (X1vec, Y1vec, X2vec, Y2vec, Radius, index1, index2);
+  }
+
+  return (TRUE);
+}
+
+// we are not defining a relative offset DX,DY for now
+int find_matches2d (Vector *X1, Vector *Y1, Vector *X2, Vector *Y2, double Radius, Vector *index1, Vector *index2) {
+  
+  off_t i, j, first_j, I, J, *N1, *N2, Nmatch, NMATCH, DMATCH;
+  double dX, dY, dR, Radius2;
+
+  NMATCH = MAX(MAX(0.01*X1->Nelements, 0.01*X2->Nelements), 100);
+  DMATCH = NMATCH;
+
+  ResetVector (index1, OPIHI_INT, NMATCH);
+  ResetVector (index2, OPIHI_INT, NMATCH);
+
+  ALLOCATE (N1, off_t, X1->Nelements);
+  ALLOCATE (N2, off_t, X2->Nelements);
+
+  for (i = 0; i < X1->Nelements; i++) { N1[i] = i; }
+  for (i = 0; i < X2->Nelements; i++) { N2[i] = i; }
+
+  sort_coords_indexonly (X1->elements.Flt, Y1->elements.Flt, N1, X1->Nelements);
+  sort_coords_indexonly (X2->elements.Flt, Y2->elements.Flt, N2, X2->Nelements);
+
+  Radius2 = Radius*Radius;
+
+  Nmatch = 0;
+  for (i = j = 0; (i < X1->Nelements) && (j < X2->Nelements);) {
+    I = N1[i];
+    J = N2[j];
+
+    dX = X1->elements.Flt[I] - X2->elements.Flt[J];
+
+    if (dX <= -1.02*Radius) { i++; continue; }
+    if (dX >= +1.02*Radius) { j++; continue; }
+
+    // look for all matches of list2() to list1(i)
+    first_j = j;
+    for (j = first_j; (dX > -1.02*Radius) && (j < X2->Nelements); j++) {
+      J = N2[j];
+      dX = X1->elements.Flt[I] - X2->elements.Flt[J];
+      dY = Y1->elements.Flt[I] - Y2->elements.Flt[J];
+      dR = dX*dX + dY*dY;
+      if (dR < Radius2) {
+	index1->elements.Int[Nmatch] = I;
+	index2->elements.Int[Nmatch] = J;
+
+	// XXX track matches 1 and 2 with internal vector, save new nomatch index vectors
+	// after this loop
+
+	Nmatch ++;
+	if (Nmatch >= NMATCH) {
+	  NMATCH += DMATCH;
+	  REALLOCATE (index1->elements.Int, opihi_int, NMATCH);
+	  REALLOCATE (index2->elements.Int, opihi_int, NMATCH);
+	}
+      }
+    }
+    j = first_j;
+    i++;
+  }
+  index1->Nelements = Nmatch;
+  index2->Nelements = Nmatch;
+
+  free (N1);
+  free (N2);
+
+  return (TRUE);
+}
+
+// find the elements of X2,Y2 which are closest to each element of X1,Y1 (-1 if no match)
+int find_matches2d_closest (Vector *X1, Vector *Y1, Vector *X2, Vector *Y2, double Radius, Vector *index) {
+  
+  off_t i, j, Jmin, Ji, I, J, *N1, *N2, NMATCH;
+  double dX, dY, dR, Radius2, Rmin;
+
+  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);
+  ALLOCATE (N2, off_t, X2->Nelements);
+
+  for (i = 0; i < X1->Nelements; i++) { N1[i] = i; }
+  for (i = 0; i < X2->Nelements; i++) { N2[i] = i; }
+
+  sort_coords_indexonly (X1->elements.Flt, Y1->elements.Flt, N1, X1->Nelements);
+  sort_coords_indexonly (X2->elements.Flt, Y2->elements.Flt, N2, X2->Nelements);
+
+  Radius2 = Radius*Radius;
+
+  // find the closest entry in list 2 to the current entry in list 1:
+  for (i = j = 0; (i < X1->Nelements) && (j < X2->Nelements);) {
+    I = N1[i];
+    J = N2[j];
+
+    dX = X1->elements.Flt[I] - X2->elements.Flt[J];
+
+    if (dX <= -1.02*Radius) { 
+      // no match in list 2 to this entry
+      index->elements.Int[I] = -1;
+      i++; 
+      continue; 
+    }
+    if (dX >= +1.02*Radius) { j++; continue; }
+
+    // look for closest matches of list2() to list1(i)
+    Jmin = -1;
+    Rmin = Radius2;
+    for (Ji = j; (dX > -1.02*Radius) && (Ji < X2->Nelements); Ji++) {
+      J = N2[Ji];
+      dX = X1->elements.Flt[I] - X2->elements.Flt[J];
+      dY = Y1->elements.Flt[I] - Y2->elements.Flt[J];
+      dR = dX*dX + dY*dY;
+      if (dR > Radius2) continue;
+      if (dR < Rmin) {
+	Rmin = dR;
+	Jmin  = J;
+      }
+    }
+
+    // no match in list 2 to this entry
+    if (Jmin == -1) {
+      index->elements.Int[I] = -1;
+      i++;
+      continue;
+    }
+    index->elements.Int[I] = Jmin;
+    i++;
+  }
+  index->Nelements = NMATCH;
+
+  free (N1);
+  free (N2);
+
+  return (TRUE);
+}
Index: /branches/sc_branches/pantasks_condor/cmd.data/matrix.c
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.data/matrix.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.data/matrix.c	(revision 34783)
@@ -0,0 +1,106 @@
+# include "data.h"
+
+int matrix (int argc, char **argv) {
+  
+  int ix, iy, ik, Nx, Ny, Nk;
+  float *Va, *Vb, *Vc, sum;
+  Buffer *bufA, *bufB, *bufC;
+
+  if ((argc != 5) && (argc != 7)) {
+    gprint (GP_ERR, "USAGE: matrix set A = B * C : matrix multiplication\n");
+    gprint (GP_ERR, "USAGE: matrix transpose A to B\n");
+    return (FALSE);
+  }
+
+  if (!strcasecmp (argv[1], "set")) {
+
+    if ((argc != 7) || strcasecmp (argv[3], "=") || strcasecmp (argv[5], "*")) {
+      gprint (GP_ERR, "USAGE: matrix set A = B * C : matrix multiplication\n");
+      return (FALSE);
+    }
+    if ((bufB = SelectBuffer (argv[4], OLDBUFFER, TRUE)) == NULL) return (FALSE);
+    if ((bufC = SelectBuffer (argv[6], OLDBUFFER, TRUE)) == NULL) return (FALSE);
+
+    if (bufB[0].matrix.Naxis[0] != bufC[0].matrix.Naxis[1]) {
+      gprint (GP_ERR, "size mis-match in matrices: ("OFF_T_FMT" x "OFF_T_FMT") * ("OFF_T_FMT" x "OFF_T_FMT")\n", 
+	       bufB[0].matrix.Naxis[0],  bufB[0].matrix.Naxis[1], 
+	       bufC[0].matrix.Naxis[0],  bufC[0].matrix.Naxis[1]);
+      return (FALSE);
+    }
+    if ((bufA = SelectBuffer (argv[2], ANYBUFFER, TRUE)) == NULL) return (FALSE);
+
+    Nx = bufC[0].matrix.Naxis[0];
+    Ny = bufB[0].matrix.Naxis[1];
+    Nk = bufB[0].matrix.Naxis[0];
+
+    gfits_free_matrix (&bufA[0].matrix);
+    gfits_free_header (&bufA[0].header);
+
+    bufA[0].bitpix = bufB[0].bitpix;
+    bufA[0].unsign = bufB[0].unsign;
+    bufA[0].bscale = bufB[0].bscale;
+    bufA[0].bzero  = bufB[0].bzero;
+    gfits_copy_header (&bufB[0].header, &bufA[0].header);
+    gfits_modify (&bufA[0].header, "NAXIS1", "%d", 1, Nx);
+    gfits_modify (&bufA[0].header, "NAXIS2", "%d", 1, Ny);
+    bufA[0].header.Naxis[0] = Nx;
+    bufA[0].header.Naxis[1] = Ny;
+    gfits_create_matrix (&bufA[0].header, &bufA[0].matrix);
+
+    Va = (float *)bufA[0].matrix.buffer;
+    Vb = (float *)bufB[0].matrix.buffer;
+    Vc = (float *)bufC[0].matrix.buffer;
+
+    for (iy = 0; iy < Ny; iy++) {
+      for (ix = 0; ix < Nx; ix++) {
+	sum = 0.0;
+	for (ik = 0; ik < Nk; ik++) {
+	  sum += Vb[iy*Nk + ik] * Vc[ik*Nx + ix];
+	}
+	Va[iy*Nx + ix] = sum;
+      }
+    }
+    return (TRUE);
+  }
+
+  if (!strcasecmp (argv[1], "transpose")) {
+    if ((argc != 5) || strcasecmp (argv[3], "to")) {
+      gprint (GP_ERR, "USAGE: matrix transpose A to B\n");
+      return (FALSE);
+    }
+    if ((bufA = SelectBuffer (argv[2], OLDBUFFER, TRUE)) == NULL) return (FALSE);
+    if ((bufB = SelectBuffer (argv[4], ANYBUFFER, TRUE)) == NULL) return (FALSE);
+
+    Nx = bufA[0].matrix.Naxis[0];
+    Ny = bufA[0].matrix.Naxis[1];
+
+    gfits_free_matrix (&bufB[0].matrix);
+    gfits_free_header (&bufB[0].header);
+
+    bufB[0].bitpix = bufA[0].bitpix;
+    bufB[0].unsign = bufA[0].unsign;
+    bufB[0].bscale = bufA[0].bscale;
+    bufB[0].bzero  = bufA[0].bzero;
+    gfits_copy_header (&bufA[0].header, &bufB[0].header);
+    gfits_modify (&bufB[0].header, "NAXIS1", "%d", 1, Ny);
+    gfits_modify (&bufB[0].header, "NAXIS2", "%d", 1, Nx);
+    bufB[0].header.Naxis[0] = Ny;
+    bufB[0].header.Naxis[1] = Nx;
+    gfits_create_matrix (&bufB[0].header, &bufB[0].matrix);
+
+    Va = (float *)bufA[0].matrix.buffer;
+    Vb = (float *)bufB[0].matrix.buffer;
+
+    for (iy = 0; iy < Ny; iy++) {
+      for (ix = 0; ix < Nx; ix++) {
+	Vb[ix*Ny + iy] = Va[iy*Nx + ix];
+      }
+    }
+
+    return (TRUE);
+  }
+
+    gprint (GP_ERR, "USAGE: matrix set A = B * C : matrix and/or vector multiplication\n");
+    gprint (GP_ERR, "USAGE: matrix transpose A to B\n");
+    return (FALSE);
+}
Index: /branches/sc_branches/pantasks_condor/cmd.data/mcreate.c
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.data/mcreate.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.data/mcreate.c	(revision 34783)
@@ -0,0 +1,22 @@
+# include "data.h"
+
+int mcreate (int argc, char **argv) {
+  
+  int Nx, Ny;
+  Buffer *buf;
+
+  if (argc != 4) {
+    gprint (GP_ERR, "USAGE: mcreate <buffer> Nx Ny\n");
+    return (FALSE);
+  }
+
+  if ((buf = SelectBuffer (argv[1], ANYBUFFER, TRUE)) == NULL) return (FALSE);
+  Nx = atof (argv[2]);
+  Ny = atof (argv[3]);
+
+  /* I should encapsulate this in a create_default_buffer */
+  gfits_free_matrix (&buf[0].matrix);
+  gfits_free_header (&buf[0].header);
+  CreateBuffer (buf, Nx, Ny, -32, 1.0, 0.0);
+  return (TRUE);
+}
Index: /branches/sc_branches/pantasks_condor/cmd.data/medacc.c
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.data/medacc.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.data/medacc.c	(revision 34783)
@@ -0,0 +1,85 @@
+# include "data.h"
+
+int medacc (int argc, char **argv) {
+  
+  int i, j, Nbins, Nvalues, N, N0, N1;
+  double start, end, delta, k0, k1, fn;
+  opihi_flt *V, *K, *V1, *K1, *O, *tmpvec, *tmpkey;
+  Vector *val, *key, *out;
+
+  if ((argc != 6) && (argc != 7)) {
+    gprint (GP_ERR, "USAGE: medacc <value> <vector> <key> start end [delta]\n");
+    return (FALSE);
+  }
+
+  if ((val = SelectVector (argv[1], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((key = SelectVector (argv[3], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+  if (val[0].Nelements != key[0].Nelements) {
+    gprint (GP_ERR, "key and value don't match\n");
+    return (FALSE);
+  }
+  if ((out = SelectVector (argv[2], ANYVECTOR, TRUE)) == NULL) return (FALSE);
+
+  REQUIRE_VECTOR_FLT (val, FALSE); 
+  REQUIRE_VECTOR_FLT (key, FALSE); 
+
+  start = atof (argv[4]);
+  end   = atof (argv[5]);
+  if (argc == 7) 
+    delta = atof (argv[6]);
+  else 
+    delta = 1;
+
+  delta = fabs (delta);
+  if (end - start < 0) {
+    delta = -1.0 * delta;
+  }
+  Nbins = 1 + (int)((end - start) / delta);
+
+  ResetVector (out, OPIHI_FLT, Nbins);
+  bzero (out[0].elements.Flt, sizeof(opihi_flt)*out[0].Nelements);
+
+  /* copy vec and key to temp vectors */
+  ALLOCATE (tmpvec, opihi_flt, val[0].Nelements);
+  ALLOCATE (tmpkey, opihi_flt, val[0].Nelements);
+
+  V = val[0].elements.Flt;
+  K = key[0].elements.Flt;
+  V1 = tmpvec;
+  K1 = tmpkey;
+  Nvalues = val[0].Nelements;
+  for (i = 0; i < Nvalues; i++, V++, K++, V1++, K1++) {
+    *V1 = *V;
+    *K1 = *K;
+  }      
+
+  /* sort vec and key by key */
+  dsortpair (tmpkey, tmpvec, Nvalues);
+
+  O = out[0].elements.Flt;
+  /* find the start and end key for each range */
+  N0 = 0;
+  N1 = 0;
+  for (i = 0; i < Nbins; i++) {
+    k0 = i*delta + start;
+    k1 = (i+1)*delta + start;
+    for (j = N1; (j < Nvalues) && (tmpkey[j] < k0); j++);
+    N0 = j;
+    for (j = N0; (j < Nvalues) && (tmpkey[j] < k1); j++);
+    N1 = j;
+    N = N1 - N0;
+    dsort (&tmpvec[N0], N);
+    fn = O[i] = 0;
+    for (j = N0 + 0.25*N; j < N0 + 0.75*N; j++) {
+      O[i] += tmpvec[j];
+      fn += 1.0;
+    }
+    if (fn > 0) O[i] /= fn;
+  }      
+  
+  free (tmpvec);
+  free (tmpkey);
+
+  return (TRUE);
+}
+
Index: /branches/sc_branches/pantasks_condor/cmd.data/mget.c
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.data/mget.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.data/mget.c	(revision 34783)
@@ -0,0 +1,60 @@
+# include "data.h"
+
+int mget (int argc, char **argv) {
+  
+  int i, Nx, Ny, Npix, xdir, Nset;
+  float *in;
+  opihi_flt *out;
+  Buffer *buf;
+  Vector *vec;
+
+  if (argc != 5) {
+    gprint (GP_ERR, "USAGE: mget <buffer> <vector> <-x/-y> <N>\n");
+    return (FALSE);
+  }
+
+  if ((buf = SelectBuffer (argv[1], OLDBUFFER, TRUE)) == NULL) return (FALSE);
+  if ((vec = SelectVector (argv[2], ANYVECTOR, TRUE)) == NULL) return (FALSE);
+
+  if (strcasecmp (argv[3], "-x") && strcasecmp (argv[3], "-y")) {
+    gprint (GP_ERR, "USAGE: mget <buffer> <vector> <-x/-y> <N>\n");
+    return (FALSE);
+  }
+  xdir = TRUE;
+  if (!strcasecmp (argv[3], "-y")) xdir = FALSE;
+
+  Nx = buf[0].matrix.Naxis[0];
+  Ny = buf[0].matrix.Naxis[1];
+  Nset = atof (argv[4]);
+  if (Nset < 0) {
+    gprint (GP_ERR, "selection out of range\n");
+    return (FALSE);
+  }
+
+  if (xdir) {
+    Npix = Nx;
+    ResetVector (vec, OPIHI_FLT, Nx);
+    if (Nset >= Ny) {
+      gprint (GP_ERR, "row out of range\n");
+      return (FALSE);
+    }
+    in  = (float *) buf[0].matrix.buffer + Nx*Nset;
+    out = vec[0].elements.Flt;
+    for (i = 0; i < Npix; i++, in++, out++) {
+      *out = *in;
+    }
+  } else {
+    Npix = Ny;
+    ResetVector (vec, OPIHI_FLT, Ny);
+    if (Nset >= Nx) {
+      gprint (GP_ERR, "column out of range\n");
+      return (FALSE);
+    }
+    in  = (float *) buf[0].matrix.buffer + Nset;
+    out = vec[0].elements.Flt;
+    for (i = 0; i < Npix; i++, in+=Nx, out++) {
+      *out = *in;
+    }
+  }
+  return (TRUE);
+}
Index: /branches/sc_branches/pantasks_condor/cmd.data/minterpolate.c
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.data/minterpolate.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.data/minterpolate.c	(revision 34783)
@@ -0,0 +1,159 @@
+# include "data.h"
+
+int minterp (int argc, char **argv) {
+  
+  int i, j, status, nx, ny, Nx, Ny, N, Extrapolate;
+  int ic, jc, dx, dy, Npix;
+  char temp[1024];
+  double scale, scale2, dX, dY;
+  float *V00, *V01, *V10, *V11, *Vout, dV1, dV2, dV3;
+  float *buf, I, J, x, y, xs, xe, ys, ye;
+  Buffer *in, *out;
+
+  /* choose the appropriate graphing window */
+  Extrapolate = FALSE;
+  if ((N = get_argument (argc, argv, "-extrapolate"))) {
+    remove_argument (N, &argc, argv);
+    Extrapolate = TRUE;
+  }
+
+  if (argc != 4) {
+    gprint (GP_ERR, "USAGE: minterpolate <from> <to> scale [-extrapolate]\n");
+    return (FALSE);
+  }
+
+  if ((in  = SelectBuffer (argv[1], OLDBUFFER, TRUE)) == NULL) return (FALSE);
+  if ((out = SelectBuffer (argv[2], ANYBUFFER, TRUE)) == NULL) return (FALSE);
+
+  gfits_free_matrix (&out[0].matrix);
+  gfits_free_header (&out[0].header);
+
+  scale  = atof (argv[3]);
+  scale2 = scale*scale;
+  Nx = in[0].header.Naxis[0];
+  Ny = in[0].header.Naxis[1];
+  nx = Nx * scale;
+  ny = Ny * scale;
+
+  /* create new matrix */
+  out[0].bitpix = in[0].bitpix;
+  out[0].unsign = in[0].unsign;
+  out[0].bscale = in[0].bscale;
+  out[0].bzero  = in[0].bzero;
+  gfits_copy_header (&in[0].header, &out[0].header);
+
+  gfits_modify (&out[0].header, "NAXIS1", "%d", 1, nx);
+  gfits_modify (&out[0].header, "NAXIS2", "%d", 1, ny);
+  out[0].header.Naxis[0] = nx;
+  out[0].header.Naxis[1] = ny;
+  gfits_create_matrix (&out[0].header, &out[0].matrix);
+
+  /* fix astrometric terms */
+  status =  gfits_scan (&out[0].header, "CDELT1", "%lf", 1, &dX);
+  status &= gfits_scan (&out[0].header, "CDELT2", "%lf", 1, &dY);
+  dX /= scale;
+  dY /= scale;
+  if (status) {
+    gfits_modify (&out[0].header, "CDELT1", "%lf", 1, dX);
+    gfits_modify (&out[0].header, "CDELT2", "%lf", 1, dY);
+  }
+  status =  gfits_scan (&out[0].header, "CRPIX1", "%lf", 1, &dX);
+  status &= gfits_scan (&out[0].header, "CRPIX2", "%lf", 1, &dY);
+  dX *= scale;
+  dY *= scale;
+  if (status) {
+    gfits_modify (&out[0].header, "CRPIX1", "%lf", 1, dX);
+    gfits_modify (&out[0].header, "CRPIX2", "%lf", 1, dY);
+  }
+
+  /* adjust filename */
+  temp[0] = 0;
+  if ((in[0].file[0] != '*') && (in[0].file[0] != '(')) {
+    strcpy (temp, "*");
+  }
+  strcat (temp, in[0].file);
+  strcpy (out[0].file, temp);
+
+  dX = dY = scale;
+
+  buf = (float *)in[0].matrix.buffer;
+  Npix = 0;
+
+  if (Extrapolate) {
+    for (j = 0; j < Ny - 1; j++) {
+      for (i = 0; i < Nx - 1; i++) {
+	V00 = buf + i + j*Nx;
+	V10 = V00 + 1;
+	V01 = V00 + Nx;
+	V11 = V01 + 1;
+	dV1 = (*V11 + *V00 - *V01 - *V10) / scale2;
+	dV2 = (*V01 - *V00) / scale;
+	dV3 = (*V10 - *V00) / scale;
+
+	x = (i + 0.5) * scale;
+	y = (j + 0.5) * scale;
+
+	xs = ys = 0;
+	xe = ye = scale;
+
+	if (i == 0)      { xs = -0.5*scale; }
+	if (i == Nx - 2) { xe =  1.5*scale; }
+
+	if (j == 0)      { ys = -0.5*scale; }
+	if (j == Ny - 2) { ye =  1.5*scale; }
+
+	for (J = ys; J < ye; J += 1.0) {
+	  dx = (x + xs);
+	  dy = (y + J);
+	  Vout = (float *)(out[0].matrix.buffer) + dy*nx + dx;
+	  for (I = xs; I < xe; I += 1.0, Vout++) {
+	    *Vout = dV1 * (I*J) + dV2 * J + dV3 * I + *V00;
+	    Npix ++;
+	  }
+	}
+      }
+    }
+  } else {
+     for (j = -1; j < Ny; j++) {
+      for (i = -1; i < Nx; i++) {
+	ic = MIN (MAX (i, 0), Nx-1);  /* we never actually reach Nx, Ny */
+	jc = MIN (MAX (j, 0), Ny-1);
+	V00 = buf + ic + jc*Nx;
+	V10 = V00 + 1;
+	V01 = V00 + Nx;
+	V11 = V01 + 1;
+
+	if ((i == -1) || (i == Nx - 1)) { V10 = V00; } else { V10 = V00 + 1; }
+	if ((j == -1) || (j == Ny - 1)) { V01 = V00; } else { V01 = V00 + Nx; }
+	if ((i == -1) || (i == Nx - 1)) { V11 = V01; } else { V11 = V01 + 1; }
+
+	dV1 = (*V11 + *V00 - *V01 - *V10) / scale2;
+	dV2 = (*V01 - *V00) / scale;
+	dV3 = (*V10 - *V00) / scale;
+
+	x = (i + 0.5) * scale;
+	y = (j + 0.5) * scale;
+
+	xs = ys = 0;
+	xe = ye = scale;
+
+	if (i == -1)     { xs = 0.5*scale; }
+	if (i == Nx - 1) { xe = 0.5*scale; }
+
+	if (j == -1)     { ys = 0.5*scale; }
+	if (j == Ny - 1) { ye = 0.5*scale; }
+
+	for (J = ys; J < ye; J += 1.0) {
+	  dx = (x + xs);
+	  dy = (y + J);
+	  Vout = (float *)(out[0].matrix.buffer) + dy*nx + dx;
+	  for (I = xs; I < xe; I += 1.0, Vout++) {
+	    *Vout = dV1 * (I*J) + dV2 * J + dV3 * I + *V00;
+	    Npix ++;
+	  }
+	}
+      }
+    }
+  }
+  return (TRUE);
+}
Index: /branches/sc_branches/pantasks_condor/cmd.data/mkrgb.c
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.data/mkrgb.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.data/mkrgb.c	(revision 34783)
@@ -0,0 +1,78 @@
+# include "data.h"
+# include "jpeglib.h"
+
+int mkrgb (int argc, char **argv) {
+ 
+  int i, j, Nx, Ny;
+  FILE *f;
+  Buffer *red, *green, *blue;
+  float *Vr, *Vg, *Vb;
+
+  struct jpeg_compress_struct cinfo;
+  struct jpeg_error_mgr jerr;
+  JSAMPROW row_pointer[1];	/* pointer to JSAMPLE row[s] */
+  JSAMPLE *image_buffer;	/* Points to data for current line */
+
+  if (argc != 5) {
+    gprint (GP_ERR, "USAGE: mkrgb (red) (green) (blue) (output)\n");
+    return (FALSE);
+  }
+
+  // define the input buffer and examine the shift
+  if ((red    = SelectBuffer (argv[1], OLDBUFFER, TRUE)) == NULL) return (FALSE);
+  if ((green  = SelectBuffer (argv[2], OLDBUFFER, TRUE)) == NULL) return (FALSE);
+  if ((blue   = SelectBuffer (argv[3], OLDBUFFER, TRUE)) == NULL) return (FALSE);
+
+  Nx = red[0].matrix.Naxis[0];
+  Ny = red[0].matrix.Naxis[1];
+  if (Nx != blue[0].matrix.Naxis[0]) return (FALSE);
+  if (Ny != blue[0].matrix.Naxis[1]) return (FALSE);
+  if (Nx != green[0].matrix.Naxis[0]) return (FALSE);
+  if (Ny != green[0].matrix.Naxis[1]) return (FALSE);
+
+  f = fopen (argv[4], "w");
+  if (f == (FILE *) NULL) {
+    fprintf (stderr, "failed to open %s for output\n", argv[4]);
+    return (FALSE);
+  }
+
+  /* set up the error handler , initialize the JPEG compression object. */
+  cinfo.err = jpeg_std_error (&jerr);
+  jpeg_create_compress (&cinfo);
+  jpeg_stdio_dest(&cinfo, f);
+  
+  // set up the basic jpeg output file
+  cinfo.image_width = Nx; 	/* image width and height, in pixels */
+  cinfo.image_height = Ny;
+  cinfo.input_components = 3;		        
+  cinfo.in_color_space = JCS_RGB; 	
+  jpeg_set_defaults (&cinfo);
+  jpeg_set_quality (&cinfo, 75, TRUE       /* limit to baseline-JPEG values */);
+  jpeg_start_compress (&cinfo, TRUE);
+
+  ALLOCATE (image_buffer, JSAMPLE, 3*Nx);
+
+  // ??
+  // && (cinfo.next_scanline < cinfo.image_height)
+
+  Vr = (float *) red[0].matrix.buffer;
+  Vg = (float *) green[0].matrix.buffer;
+  Vb = (float *) blue[0].matrix.buffer;
+
+  for (j = 0; j < Ny; j++) {
+    for (i = 0; i < Nx; i++, Vr++, Vg++, Vb++) {
+      image_buffer[3*i+0] = MAX (0.0, MIN (255.0, *Vr));
+      image_buffer[3*i+1] = MAX (0.0, MIN (255.0, *Vg));
+      image_buffer[3*i+2] = MAX (0.0, MIN (255.0, *Vb));
+    }
+    row_pointer[0] = image_buffer;
+    (void) jpeg_write_scanlines (&cinfo, row_pointer, 1);
+  }
+
+  jpeg_finish_compress (&cinfo);
+  fclose (f);
+
+  jpeg_destroy_compress (&cinfo);
+
+  return (TRUE);
+}
Index: /branches/sc_branches/pantasks_condor/cmd.data/mset.c
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.data/mset.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.data/mset.c	(revision 34783)
@@ -0,0 +1,83 @@
+# include "data.h"
+
+int mset (int argc, char **argv) {
+  
+  int i, Nx, Ny, Npix, xdir, Nset;
+  float *out;
+  Buffer *buf;
+  Vector *vec;
+
+  if (argc != 5) {
+    gprint (GP_ERR, "USAGE: mset <buffer> <vector> <-x/-y> <N>\n");
+    return (FALSE);
+  }
+
+  if ((buf = SelectBuffer (argv[1], OLDBUFFER, TRUE)) == NULL) return (FALSE);
+  if ((vec = SelectVector (argv[2], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+
+  if (strcasecmp (argv[3], "-x") && strcasecmp (argv[3], "-y")) {
+    gprint (GP_ERR, "USAGE: mset <buffer> <vector> <-x/-y> <N>\n");
+    return (FALSE);
+  }
+  xdir = TRUE;
+  if (!strcasecmp (argv[3], "-y")) xdir = FALSE;
+
+  Npix = vec[0].Nelements;
+  Nx = buf[0].matrix.Naxis[0];
+  Ny = buf[0].matrix.Naxis[1];
+  Nset = atof (argv[4]);
+  if (Nset < 0) {
+    gprint (GP_ERR, "selection out of range\n");
+    return (FALSE);
+  }
+
+  if (xdir) {
+    if (Nx != Npix) {
+      gprint (GP_ERR, "dimensions don't match\n");
+      return (FALSE);
+    }
+    if (Nset >= Ny) {
+      gprint (GP_ERR, "row out of range\n");
+      return (FALSE);
+    }
+    out = (float *) buf[0].matrix.buffer + Nx*Nset;
+
+    if (vec[0].type == OPIHI_FLT) {
+      opihi_flt *in = vec[0].elements.Flt;
+      for (i = 0; i < Npix; i++, in++, out++) {
+	*out = *in;
+      }
+    } else {
+      opihi_int *in = vec[0].elements.Int;
+      for (i = 0; i < Npix; i++, in++, out++) {
+	*out = *in;
+      }
+    }
+    return (TRUE);
+
+  } else {
+    if (Ny != Npix) {
+      gprint (GP_ERR, "dimensions don't match\n");
+      return (FALSE);
+    }
+    if (Nset >= Nx) {
+      gprint (GP_ERR, "column out of range\n");
+      return (FALSE);
+    }
+    out = (float *) buf[0].matrix.buffer + Nset;
+
+    if (vec[0].type == OPIHI_FLT) {
+      opihi_flt *in = vec[0].elements.Flt;
+      for (i = 0; i < Npix; i++, in++, out+=Nx) {
+	*out = *in;
+      }
+    } else {
+      opihi_int *in = vec[0].elements.Int;
+      for (i = 0; i < Npix; i++, in++, out+=Nx) {
+	*out = *in;
+      }
+    }
+    return (TRUE);
+  }    
+}
+
Index: /branches/sc_branches/pantasks_condor/cmd.data/peak.c
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.data/peak.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.data/peak.c	(revision 34783)
@@ -0,0 +1,91 @@
+# include "data.h"
+
+int peak (int argc, char **argv) {
+  
+  int i, N, imax, QUIET;
+  double start, end, xmax, ymax;
+  Vector *vecx, *vecy;
+
+  QUIET = FALSE;
+  if ((N = get_argument (argc, argv, "-q"))) {
+    QUIET = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+
+  if ((argc != 5) && (argc != 3)) {
+    gprint (GP_ERR, "USAGE: peak <x> <y> [start end]\n");
+    return (FALSE);
+  }
+
+  if ((vecx = SelectVector (argv[1], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((vecy = SelectVector (argv[2], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+
+  if (argc == 5) {
+    start = atof (argv[3]);
+    end   = atof (argv[4]);
+  } else {
+    start = (vecx[0].type == OPIHI_FLT) ? vecx[0].elements.Flt[0] : vecx[0].elements.Int[0];
+    end   = (vecx[0].type == OPIHI_FLT) ? vecx[0].elements.Flt[vecx[0].Nelements - 1] : vecx[0].elements.Int[vecx[0].Nelements - 1];
+  }
+
+  imax = -1;
+  xmax = -HUGE_VAL;
+  ymax = -HUGE_VAL;
+
+  if ((vecx[0].type == OPIHI_FLT) && (vecy[0].type == OPIHI_FLT)) {
+    opihi_flt *X = vecx[0].elements.Flt;
+    opihi_flt *Y = vecy[0].elements.Flt;
+    for (i = 0; i < vecx[0].Nelements; i++, X++, Y++) {
+      if (*X < start) continue;
+      if (*X > end) continue;
+      if (*Y < ymax) continue;
+      xmax = *X;
+      ymax = *Y;
+      imax = i;
+    }      
+  }
+  if ((vecx[0].type == OPIHI_FLT) && (vecy[0].type == OPIHI_INT)) {
+    opihi_flt *X = vecx[0].elements.Flt;
+    opihi_int *Y = vecy[0].elements.Int;
+    for (i = 0; i < vecx[0].Nelements; i++, X++, Y++) {
+      if (*X < start) continue;
+      if (*X > end) continue;
+      if (*Y < ymax) continue;
+      xmax = *X;
+      ymax = *Y;
+      imax = i;
+    }      
+  }
+  if ((vecx[0].type == OPIHI_INT) && (vecy[0].type == OPIHI_FLT)) {
+    opihi_int *X = vecx[0].elements.Int;
+    opihi_flt *Y = vecy[0].elements.Flt;
+    for (i = 0; i < vecx[0].Nelements; i++, X++, Y++) {
+      if (*X < start) continue;
+      if (*X > end) continue;
+      if (*Y < ymax) continue;
+      xmax = *X;
+      ymax = *Y;
+      imax = i;
+    }      
+  }
+  if ((vecx[0].type == OPIHI_INT) && (vecy[0].type == OPIHI_INT)) {
+    opihi_int *X = vecx[0].elements.Int;
+    opihi_int *Y = vecy[0].elements.Int;
+    for (i = 0; i < vecx[0].Nelements; i++, X++, Y++) {
+      if (*X < start) continue;
+      if (*X > end) continue;
+      if (*Y < ymax) continue;
+      xmax = *X;
+      ymax = *Y;
+      imax = i;
+    }      
+  }
+
+  set_variable ("peakval", ymax);
+  set_variable ("peakpos", xmax);
+  set_variable ("peaknum", imax);
+
+  if (!QUIET) gprint (GP_LOG, "peak %f @ %f (%d)\n", ymax, xmax, imax);
+
+  return (TRUE);
+}
Index: /branches/sc_branches/pantasks_condor/cmd.data/periodogram.c
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.data/periodogram.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.data/periodogram.c	(revision 34783)
@@ -0,0 +1,115 @@
+# include "data.h"
+
+int periodogram (int argc, char **argv) {
+  
+  int i, N, Npt, Np, NP, VERBOSE;
+  opihi_flt *tv, *fv;
+  float minP, maxP, minT, maxT, dTime;
+  float mean, var, w, tau, P, Pc, Ps, Po;
+  float C, S, cs, sn, cs2, sn2, ratio;
+  Vector *time, *flux, *power, *period;
+
+  VERBOSE = FALSE;
+  if ((N = get_argument (argc, argv, "-v"))) {
+    VERBOSE = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+
+  if (argc != 7) {
+    gprint (GP_ERR, "USAGE: periodogram (time) (flux) (minP) (maxP) (period) (power)\n");
+    return (FALSE);
+  }
+  
+  if ((time = SelectVector (argv[1], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((flux = SelectVector (argv[2], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+  minP = atof(argv[3]);
+  maxP = atof(argv[4]);
+  if ((period = SelectVector (argv[5], ANYVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((power = SelectVector (argv[6], ANYVECTOR, TRUE)) == NULL) return (FALSE);
+
+  REQUIRE_VECTOR_FLT (time, FALSE); 
+  REQUIRE_VECTOR_FLT (flux, FALSE); 
+
+  /* find the max baseline, mean, and variance */
+  minT = maxT = time[0].elements.Flt[0];
+  Npt = time[0].Nelements;
+  tv = time[0].elements.Flt;
+  fv = flux[0].elements.Flt;
+  mean = var = 0;
+  for (i = 0; i < Npt; i++, tv++, fv++) {
+    minT = MIN (minT, *tv);
+    maxT = MAX (maxT, *tv);
+    mean += *fv;
+  }
+  mean = mean / Npt;
+  fv = flux[0].elements.Flt;
+  for (i = 0; i < Npt; i++, fv++) {
+    var += SQ(*fv - mean);
+  }
+  var = var / (Npt - 1);
+
+  if (VERBOSE) gprint (GP_ERR, "mean: %f, var: %f, minT: %f, maxT: %f\n", mean, var, minT, maxT);
+
+  dTime = maxT - minT;
+  if (dTime == 0) {
+    gprint (GP_ERR, "ERROR: time range is zero\n");
+    return (FALSE);
+  }
+
+  Np = 0;
+  NP = 100;
+  ResetVector (power,  OPIHI_FLT, NP);
+  ResetVector (period, OPIHI_FLT, NP);
+
+  P = minP;
+  while (P < maxP) {
+    w = 2*M_PI/P;
+    
+    /* find the period offset tau  */
+    tv = time[0].elements.Flt;
+    cs = sn = 0;
+    for (i = 0; i < Npt; i++, tv++) {
+      cs += cos (*tv*w*2);
+      sn += sin (*tv*2*2);
+    }
+    tau = 0.5*atan2 (sn, cs) / w;
+      
+    /* find the power at this period */
+    tv = time[0].elements.Flt;
+    fv = flux[0].elements.Flt;
+    cs = sn = cs2 = sn2 = 0;
+    for (i = 0; i < Npt; i++, tv++, fv++) {
+      C = cos (w*(*tv-tau));
+      S = sin (w*(*tv-tau));
+      // C = cos (w**tv);
+      // S = sin (w**tv);
+      cs += (*fv - mean) * C;
+      sn += (*fv - mean) * S;
+      cs2 += SQ(C);
+      sn2 += SQ(S);
+    }
+    Pc = SQ(cs) / cs2;
+    Ps = SQ(sn) / sn2;
+    Po = (Pc + Ps) / (2*var);
+
+    power[0].elements.Flt[Np] = Po;
+    period[0].elements.Flt[Np] = P;
+    Np ++;
+    if (Np >= NP) {
+      NP += 100;
+      REALLOCATE (power[0].elements.Flt, opihi_flt, NP);
+      REALLOCATE (period[0].elements.Flt, opihi_flt, NP);
+    }
+
+    ratio = 1 + 0.1*P/dTime;
+
+    if (VERBOSE) gprint (GP_ERR, "tau: %f, P: %f, ratio: %f, dTime: %f, nextP: %f\n", tau, P, ratio, dTime, P*ratio);
+
+    P *= ratio;
+  }
+
+  ResetVector (power,  OPIHI_FLT, Np);
+  ResetVector (period, OPIHI_FLT, Np);
+ 
+  return (TRUE);
+}
Index: /branches/sc_branches/pantasks_condor/cmd.data/plot.c
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.data/plot.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.data/plot.c	(revision 34783)
@@ -0,0 +1,127 @@
+# include "data.h"
+
+int plot (int argc, char **argv) {
+  
+  char *out;
+  int kapa, N, Npts, valid, size, i;
+  Graphdata graphmode;
+  Vector *xvec, *yvec, *dxmvec, *dxpvec, *dymvec, *dypvec;
+
+  if (!style_args (&graphmode, &argc, argv, &kapa)) return FALSE;
+
+  /* decide on error bars */
+  dxmvec = dxpvec = dymvec = dypvec = NULL;
+  if ((N = get_argument (argc, argv, "-dx"))) {
+    remove_argument (N, &argc, argv);
+    if ((dxmvec = SelectVector (argv[N], OLDVECTOR, TRUE)) == NULL) return (FALSE);    
+    remove_argument (N, &argc, argv);
+  }
+  if ((N = get_argument (argc, argv, "+dx"))) {
+    remove_argument (N, &argc, argv);
+    if ((dxpvec = SelectVector (argv[N], OLDVECTOR, TRUE)) == NULL) return (FALSE);    
+    remove_argument (N, &argc, argv);
+  }
+  if ((N = get_argument (argc, argv, "-dy"))) {
+    remove_argument (N, &argc, argv);
+    if ((dymvec = SelectVector (argv[N], OLDVECTOR, TRUE)) == NULL) return (FALSE);    
+    remove_argument (N, &argc, argv);
+  }
+  if ((N = get_argument (argc, argv, "+dy"))) {
+    remove_argument (N, &argc, argv);
+    if ((dypvec = SelectVector (argv[N], OLDVECTOR, TRUE)) == NULL) return (FALSE);    
+    remove_argument (N, &argc, argv);
+  }
+
+  valid  = (argc == 3);
+  valid |= (argc > 4) && !strcmp (argv[3], "where");
+  if (!valid) {
+    gprint (GP_ERR, "USAGE: plot <x> <y> [style]\n");
+    gprint (GP_ERR, "   OR: plot <x> <y> [style] where (condition)\n");
+    return (FALSE);
+  }
+
+  // tvec is used for logical test (truth vector)
+  Vector *tvec = NULL;
+  char *mask = NULL;
+  if (argc > 4) {
+    out = dvomath (argc - 4, &argv[4], &size, 1);
+    if (out == NULL) {
+      print_error ();
+      return FALSE;
+    }
+    if ((tvec = SelectVector (out, OLDVECTOR, TRUE)) == NULL) {
+      gprint (GP_ERR, " invalid logic result\n");
+      DeleteNamedVector (out);
+      free (out);
+      return (FALSE);
+    }
+  }
+
+  /* set errorbar mode (these are NOT sticky) */
+  graphmode.etype = 0;
+  if ((dymvec != NULL) && (dypvec == NULL)) dypvec = dymvec;
+  if ((dypvec != NULL) && (dymvec == NULL)) dymvec = dypvec;
+  if ((dypvec != NULL) || (dymvec != NULL)) graphmode.etype |= 0x01;
+  if ((dxmvec != NULL) && (dxpvec == NULL)) dxpvec = dxmvec;
+  if ((dxpvec != NULL) && (dxmvec == NULL)) dxmvec = dxpvec;
+  if ((dxpvec != NULL) || (dxmvec != NULL)) graphmode.etype |= 0x02;
+  
+  /* find vectors */
+  if ((xvec = SelectVector (argv[1], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((yvec = SelectVector (argv[2], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+  if (xvec[0].Nelements != yvec[0].Nelements) {
+    gprint (GP_ERR, "vectors %s and %s not the same length\n", argv[1], argv[2]);
+    return (FALSE);
+  }
+  if (tvec && tvec[0].Nelements != yvec[0].Nelements) {
+    gprint (GP_ERR, "logic test vector not the same length as data vectors\n");
+    return (FALSE);
+  }
+  if (dypvec && (dypvec->Nelements != xvec->Nelements)) goto mismatch;
+  if (dymvec && (dymvec->Nelements != xvec->Nelements)) goto mismatch;
+  if (dxpvec && (dxpvec->Nelements != xvec->Nelements)) goto mismatch;
+  if (dxmvec && (dxmvec->Nelements != xvec->Nelements)) goto mismatch;
+
+  Npts = xvec[0].Nelements;
+  if (Npts == 0) {
+    if (tvec) DeleteVector (tvec);
+    return (TRUE);
+  }
+
+  if (tvec) {
+    Npts = 0;
+    ALLOCATE (mask, char, tvec->Nelements);
+    for (i = 0; i < tvec->Nelements; i++) {
+      mask[i] = (tvec->type == OPIHI_FLT) ? (tvec->elements.Flt[i] == 0.0) : (tvec->elements.Int[i] == 0.0);
+      if (!mask[i]) Npts ++;
+    }
+    if (Npts == 0) {
+      DeleteVector (tvec);
+      free (mask);
+      return TRUE;
+    }
+  }
+
+  if (!KapaPrepPlot (kapa, Npts, &graphmode)) return (FALSE);
+  
+  PlotVectorSingle (kapa, xvec, mask, "x");
+  PlotVectorSingle (kapa, yvec, mask, "y");
+  if (graphmode.etype & 0x01) {
+    PlotVectorSingle (kapa, dymvec, mask, "dym");
+    PlotVectorSingle (kapa, dypvec, mask, "dyp");
+  }
+  if (graphmode.etype & 0x02) {
+    PlotVectorSingle (kapa, dxmvec, mask, "dxm");
+    PlotVectorSingle (kapa, dxpvec, mask, "dxp");
+  }
+
+  if (tvec) {
+    free (mask);
+    DeleteVector (tvec);
+  }
+  return (TRUE);
+
+mismatch:
+  gprint (GP_ERR, "error and data vector lengths are mismatched\n");
+  return (FALSE);
+}
Index: /branches/sc_branches/pantasks_condor/cmd.data/point.c
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.data/point.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.data/point.c	(revision 34783)
@@ -0,0 +1,81 @@
+# include "data.h"
+
+int point (int argc, char **argv) {
+  
+  int N, celestial, pixscale;
+  int kapa;
+  double ra, dec, dra, ddec, angle;
+  double x1, y1, ra1, dec1;
+  char *name;
+  Coords coords;
+  Buffer *buf;
+  KiiOverlay overlay;
+  KapaImageData data;
+
+  name = NULL;
+  if ((N = get_argument (argc, argv, "-n"))) {
+    remove_argument (N, &argc, argv);
+    name = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+  if (!GetImageData (&data, &kapa, name)) return (FALSE);
+  FREE (name);
+
+  celestial = FALSE;
+  if ((N = get_argument (argc, argv, "-c"))) {
+    remove_argument (N, &argc, argv);
+    celestial = TRUE;
+  }
+  
+  pixscale = FALSE;
+  if ((N = get_argument (argc, argv, "-pixscale"))) {
+    remove_argument (N, &argc, argv);
+    pixscale = TRUE;
+  }
+
+  angle = 0.0;
+  if ((N = get_argument (argc, argv, "-angle"))) {
+    remove_argument (N, &argc, argv);
+    angle = atof(argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  if (argc != 7) {
+    gprint (GP_ERR, "USAGE: point (overlay) TYPE x y dx dy [-c]\n");
+    return (FALSE);
+  }
+  
+  if (celestial) {
+    if ((buf = SelectBuffer (data.name, OLDBUFFER, TRUE)) == NULL) return (FALSE);
+    GetCoords (&coords, &buf[0].header);
+  }
+
+  if (celestial) {
+    ra   = atof(argv[3]);
+    dec  = atof(argv[4]);
+    dra  = atof(argv[5]);
+    ddec = atof(argv[6]);
+
+    fRD_to_XY (&overlay.x, &overlay.y, ra, dec, &coords);
+    if (pixscale) {
+      overlay.dx = atof(argv[5]);
+      overlay.dy = atof(argv[6]);
+    } else {
+      ra1 = ra + dra;
+      dec1 = dec + ddec;
+      RD_to_XY (&x1, &y1, ra1, dec1, &coords);
+      overlay.dx = x1 - overlay.x;
+      overlay.dy = y1 - overlay.y;
+    }
+  }
+  else {
+    overlay.x  = atof(argv[3]);
+    overlay.y  = atof(argv[4]);
+    overlay.dx = atof(argv[5]);
+    overlay.dy = atof(argv[6]);
+  }
+  overlay.angle = angle;
+  overlay.type = KiiOverlayTypeByName (argv[2]);
+  KiiLoadOverlay (kapa, &overlay, 1, argv[1]);
+  return (TRUE);
+}
Index: /branches/sc_branches/pantasks_condor/cmd.data/ps.c
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.data/ps.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.data/ps.c	(revision 34783)
@@ -0,0 +1,71 @@
+# include "data.h"
+
+int ps (int argc, char **argv) {
+
+  char filename[1024], pagename[1024], *name;
+  int N, kapa, scaleMode, pageMode;
+  
+  if ((N = get_argument (argc, argv, "--help"))) goto help;
+  if ((N = get_argument (argc, argv, "-h"))) goto help;
+
+  pageMode = KAPA_PS_NEWPLOT;
+
+  /* new page? */
+  if ((N = get_argument (argc, argv, "-newpage"))) {
+    remove_argument (N, &argc, argv);
+    pageMode = KAPA_PS_NEWPAGE;
+  }
+  if ((N = get_argument (argc, argv, "-raw"))) {
+    remove_argument (N, &argc, argv);
+    pageMode = KAPA_PS_RAWPAGE;
+  }
+
+  /* scale image? */
+  scaleMode = TRUE;
+  if ((N = get_argument (argc, argv, "-noscale"))) {
+    remove_argument (N, &argc, argv);
+    scaleMode = TRUE;
+  }
+
+  /* what file? */
+  filename[0] = 0;
+  if ((N = get_argument (argc, argv, "-name"))) {
+    remove_argument (N, &argc, argv);
+    strcpy (filename, argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+  /* pagename ? */
+  strcpy (pagename, "default");
+  if ((N = get_argument (argc, argv, "-pagename"))) {
+    remove_argument (N, &argc, argv);
+    strcpy (pagename, argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  /* which tool */
+  name = NULL;
+  if ((N = get_argument (argc, argv, "-n"))) {
+    remove_argument (N, &argc, argv);
+    name = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  if ((argc > 1) && filename[0]) goto help;
+
+  if (argc > 1) strcpy (filename, argv[1]);
+
+  // get the connection to kapa, false if none available
+  if (!GetGraphdata (NULL, &kapa, name)) return (FALSE);
+  FREE (name);
+
+  if (!filename[0]) strcpy (filename, "kapa.ps");
+  
+  /* tell Ximage/Xgraph to ps the image */
+  KiiPS (kapa, filename, scaleMode, pageMode, pagename);
+  return (TRUE);
+
+help:
+  gprint (GP_ERR, "USAGE: ps [-name file.ps] [-g | -i] [-n device] [-raw] [-noscale] [-newpage] [-pagename (name]\n");
+  return (FALSE);
+}
+
Index: /branches/sc_branches/pantasks_condor/cmd.data/queuedelete.c
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.data/queuedelete.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.data/queuedelete.c	(revision 34783)
@@ -0,0 +1,21 @@
+# include "data.h"
+
+int queuedelete (int argc, char **argv) {
+  
+  Queue *queue;
+
+  if (argc != 2) {
+    gprint (GP_ERR, "USAGE: queuedelete (name)\n");
+    return (FALSE);
+  }
+
+  queue = FindQueue (argv[1]);
+  if (queue == NULL) {
+    gprint (GP_ERR, "ERROR: queue %s not found\n", argv[1]);
+    return (FALSE);
+  }
+
+  DeleteQueue (queue);
+  return (TRUE);
+}
+
Index: /branches/sc_branches/pantasks_condor/cmd.data/queuedrop.c
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.data/queuedrop.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.data/queuedrop.c	(revision 34783)
@@ -0,0 +1,38 @@
+# include "data.h"
+
+int queuedrop (int argc, char **argv) {
+  
+  int N;
+  char *Key = NULL;
+  char *line = NULL;
+  Queue *queue = NULL;
+  char *Value = NULL;
+
+  Key = NULL;
+  if ((N = get_argument (argc, argv, "-key"))) {
+    remove_argument (N, &argc, argv);
+    Key = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+    Value = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  if ((argc != 2) || (Key == NULL)) {
+    gprint (GP_ERR, "USAGE: queuedrop (queue) [-key N value]\n");
+    return (FALSE);
+  }
+
+  /* will create a queue if none exists */
+  queue = FindQueue (argv[1]);
+  if (queue == NULL) {
+    gprint (GP_ERR, "ERROR: queue %s not found\n", argv[1]);
+    return (FALSE);
+  }
+
+  /* drop all matching entries, if any exist */
+  while ((line = PopQueueMatch (queue, Key, Value)) != NULL) {
+    free (line);
+  }
+  return (TRUE);
+}
+
Index: /branches/sc_branches/pantasks_condor/cmd.data/queueinit.c
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.data/queueinit.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.data/queueinit.c	(revision 34783)
@@ -0,0 +1,15 @@
+# include "data.h"
+
+int queueinit (int argc, char **argv) {
+
+  Queue *queue;
+
+  if (argc != 2) {
+    gprint (GP_ERR, "USAGE: queueinit (name)\n");
+    return (FALSE);
+  }
+
+  queue = CreateQueue (argv[1]);
+  InitQueue (queue);
+  return (TRUE);
+}
Index: /branches/sc_branches/pantasks_condor/cmd.data/queuelist.c
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.data/queuelist.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.data/queuelist.c	(revision 34783)
@@ -0,0 +1,12 @@
+# include "data.h"
+
+int queuelist (int argc, char **argv) {
+  
+  if (argc != 1) {
+    gprint (GP_ERR, "USAGE: queuelist\n");
+    return (FALSE);
+  }
+
+  ListQueues ();
+  return (TRUE);
+}
Index: /branches/sc_branches/pantasks_condor/cmd.data/queueload.c
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.data/queueload.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.data/queueload.c	(revision 34783)
@@ -0,0 +1,69 @@
+# include "data.h"
+
+int queueload (int argc, char **argv) {
+  
+  char *A, *B, *val;
+  int i, status;
+  int Nread, Nbytes, NBYTES;
+  FILE *f;
+  Queue *queue;
+
+  if (argc != 4) goto usage;
+  if (strcmp(argv[2], "-x")) goto usage;
+  
+  /* will create a queue if none exists */
+  queue = CreateQueue (argv[1]);
+
+  /* val will hold the result of the command */
+  NBYTES = 1024;
+  ALLOCATE (val, char, NBYTES);
+    
+  /* loop until command produces no more output,  REALLOCATE as needed. */
+  f = popen (argv[3], "r");
+  Nbytes = 0;
+  Nread = 1;
+  while (Nread > 0) {
+    Nread = fread (&val[Nbytes], 1, 1023, f);
+    if (Nread < 0) { 
+      gprint (GP_ERR, "error reading from command\n");
+    }
+    if (Nread > 0) {
+      Nbytes += Nread;
+      NBYTES = 1024 + Nbytes;
+      REALLOCATE (val, char, NBYTES);
+    }
+  }
+  val[Nbytes] = 0;
+  status = pclose (f);
+    
+  if (status) {
+    gprint (GP_ERR, "warning: exit status of command %d\n", status);
+  }
+      
+  A = B = val;
+  for (i = 0; B != (char *) NULL;) {
+    while (isspace (*A) && (*A != 0)) A++;
+    B = strchr (A, '\n');
+    if (B != (char *) NULL) { *B = 0; }
+    if (*A != 0) {
+      PushQueue (queue, A);
+      A = B + 1;
+      i++;
+    }
+  }      
+  free (val);
+    
+  return (TRUE);
+
+usage:
+  gprint (GP_ERR, "USAGE: queueload (queue) -x (command)\n");
+  return (FALSE);
+}
+
+
+/* 
+ * -key only needed for replace or unique : give an error otherwise
+ * -uniq searched for a match and does NOT replace if matched
+ * -replace searches for a match and replaces if matched
+ * should trigger an error if -uniq and -replace...
+ */
Index: /branches/sc_branches/pantasks_condor/cmd.data/queuepop.c
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.data/queuepop.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.data/queuepop.c	(revision 34783)
@@ -0,0 +1,71 @@
+# include "data.h"
+
+int queuepop (int argc, char **argv) {
+  
+  int N;
+  char *Key;
+  char *var;
+  char *line;
+  char *Value;
+  Queue *queue;
+
+  var = (char *) NULL;
+  if ((N = get_argument (argc, argv, "-var"))) {
+    remove_argument (N, &argc, argv);
+    var = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  Key = NULL;
+  Value = NULL;
+  if ((N = get_argument (argc, argv, "-key"))) {
+    remove_argument (N, &argc, argv);
+    Key = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+    Value = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  if (argc != 2) {
+    gprint (GP_ERR, "USAGE: queuepop (queue) [-var variable] [-key N value]\n");
+    return (FALSE);
+  }
+
+  /* will create a queue if none exists */
+  queue = FindQueue (argv[1]);
+  if (queue == NULL) {
+    gprint (GP_ERR, "ERROR: queue %s not found\n", argv[1]);
+    return (FALSE);
+  }
+
+  if (Key == NULL) {
+    line = PopQueue (queue);
+  } else {
+    line = PopQueueMatch (queue, Key, Value);
+  }
+
+  if (var == NULL) {
+    if (line == NULL) {
+      gprint (GP_ERR, "queue %s is empty or match not found\n", argv[1]);
+      return (FALSE);
+    } else {
+      gprint (GP_LOG, "%s\n", line);
+      free (line);
+      return (TRUE);
+    }
+  }
+
+  if (line == NULL) {
+    set_str_variable (var, "NULL");
+  } else {
+    set_str_variable (var, line);
+    free (line);
+  }
+
+  free (var);
+  if (Key != NULL) free (Key);
+  if (Value != NULL) free (Value);
+
+  return (TRUE);
+}
+
Index: /branches/sc_branches/pantasks_condor/cmd.data/queueprint.c
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.data/queueprint.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.data/queueprint.c	(revision 34783)
@@ -0,0 +1,21 @@
+# include "data.h"
+
+int queueprint (int argc, char **argv) {
+  
+  Queue *queue;
+
+  if (argc != 2) {
+    gprint (GP_ERR, "USAGE: queueprint (name)\n");
+    return (FALSE);
+  }
+
+  queue = FindQueue (argv[1]);
+  if (queue == NULL) {
+    gprint (GP_ERR, "ERROR: queue %s not found\n", argv[1]);
+    return (FALSE);
+  }
+
+  PrintQueue (queue);
+  return (TRUE);
+}
+
Index: /branches/sc_branches/pantasks_condor/cmd.data/queuepush.c
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.data/queuepush.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.data/queuepush.c	(revision 34783)
@@ -0,0 +1,56 @@
+# include "data.h"
+
+int queuepush (int argc, char **argv) {
+  
+  char *Key;
+  int N, Unique, Replace;
+  Queue *queue;
+
+  Unique = FALSE;
+  if ((N = get_argument (argc, argv, "-uniq"))) {
+    remove_argument (N, &argc, argv);
+    Unique = TRUE;
+  }
+
+  Replace = FALSE;
+  if ((N = get_argument (argc, argv, "-replace"))) {
+    remove_argument (N, &argc, argv);
+    Replace = TRUE;
+  }
+
+  Key = NULL;
+  if ((N = get_argument (argc, argv, "-key"))) {
+    remove_argument (N, &argc, argv);
+    Key = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  if (argc != 3) {
+    gprint (GP_ERR, "USAGE: queuepush (queue) (value) [-key N] [-uniq] [-replace]\n");
+    return (FALSE);
+  }
+
+  /* will create a queue if none exists */
+  queue = CreateQueue (argv[1]);
+
+  if (Unique) {
+    PushQueueUnique (queue, argv[2], Key);
+  }
+  if (Replace) {
+    PushQueueReplace (queue, argv[2], Key);
+  }
+  if (!Unique && !Replace) {
+    PushQueue (queue, argv[2]);
+  }
+
+  if (Key != NULL) free (Key);
+  return (TRUE);
+}
+
+
+/* 
+ * -key only needed for replace or unique : give an error otherwise
+ * -uniq searched for a match and does NOT replace if matched
+ * -replace searches for a match and replaces if matched
+ * should trigger an error if -uniq and -replace...
+ */
Index: /branches/sc_branches/pantasks_condor/cmd.data/queuesize.c
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.data/queuesize.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.data/queuesize.c	(revision 34783)
@@ -0,0 +1,36 @@
+# include "data.h"
+
+int queuesize (int argc, char **argv) {
+  
+  int N;
+  char *var;
+  Queue *queue;
+
+  var = (char *) NULL;
+  if ((N = get_argument (argc, argv, "-var"))) {
+    remove_argument (N, &argc, argv);
+    var = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  if (argc != 2) {
+    gprint (GP_ERR, "USAGE: queuesize (name) [-var variable]\n");
+    return (FALSE);
+  }
+
+  queue = FindQueue (argv[1]);
+  if (queue == NULL) {
+    gprint (GP_ERR, "ERROR: queue %s not found\n", argv[1]);
+    return (FALSE);
+  }
+
+  if (var == (char *) NULL) {
+    gprint (GP_ERR, "Nlines: %d\n", queue[0].Nlines);
+    return (TRUE);
+  }
+
+  set_int_variable (var, queue[0].Nlines);
+  free (var);
+  return (TRUE);
+}
+
Index: /branches/sc_branches/pantasks_condor/cmd.data/queuesubstr.c
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.data/queuesubstr.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.data/queuesubstr.c	(revision 34783)
@@ -0,0 +1,42 @@
+# include "data.h"
+
+int queuesubstr (int argc, char **argv) {
+  
+  int i;
+  char *p, *q;
+  char *match, *replace;
+  Queue *queue;
+
+  if (argc != 4) {
+    gprint (GP_ERR, "USAGE: queueprint (queue) (match) (replace)\n");
+    return (FALSE);
+  }
+
+  match = argv[2];
+  replace = argv[3];
+
+  assert (match);
+  assert (replace);
+
+  queue = FindQueue (argv[1]);
+  if (queue == NULL) {
+    gprint (GP_ERR, "ERROR: queue %s not found\n", argv[1]);
+    return (FALSE);
+  }
+
+  for (i = 0; i < queue[0].Nlines; i++) {
+
+    p = queue[0].lines[i];
+    if (p == NULL) continue;
+    
+    while (strstr (p, match) != NULL) {
+      q = strsubs (p, match, replace);
+      free (p);
+      p = q;
+    }
+    queue[0].lines[i] = p;
+  }
+
+  return (TRUE);
+}
+
Index: /branches/sc_branches/pantasks_condor/cmd.data/radial.c
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.data/radial.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.data/radial.c	(revision 34783)
@@ -0,0 +1,97 @@
+# include "data.h"
+
+int circstats (int argc, char **argv) {
+  
+  int i, j;
+  double Npix, S1, S2, max, min, Sum, Mean, Stdev, IgnoreValue; 
+  double xc, yc, radius, R2, r;
+  float *V;
+  int xs, ys, xe, ye;
+  int Ignore, Quiet, N, Nx, Ny;
+  Buffer *buf;
+
+  Ignore = FALSE;
+  IgnoreValue = 0;
+  if ((N = get_argument (argc, argv, "-ignore"))) {
+    Ignore = TRUE;
+    remove_argument (N, &argc, argv);
+    IgnoreValue = atof(argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  Quiet = FALSE;
+  if ((N = get_argument (argc, argv, "-q"))) {
+    Quiet = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+  if ((N = get_argument (argc, argv, "-quiet"))) {
+    Quiet = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+
+  if (argc != 5) goto usage;
+  if ((buf = SelectBuffer (argv[1], OLDBUFFER, TRUE)) == NULL) goto missed;
+
+  xc = atof (argv[2]);
+  yc = atof (argv[3]);
+  radius = atof (argv[4]);
+  Nx = buf[0].matrix.Naxis[0];
+  Ny = buf[0].matrix.Naxis[1];
+
+  if (xc < 0) goto range;
+  if (yc < 0) goto range;
+  if (xc >= Nx) goto range;
+  if (yc >= Ny) goto range;
+
+  xs = MAX (0, xc - radius);
+  ys = MAX (0, yc - radius);
+  xe = MIN (Nx, xc + radius + 1);
+  ye = MAX (Ny, yc + radius + 1);
+  R2 = radius*radius;
+
+  S1 = S2 = Npix = 0;
+  min = max = *(float *)(buf[0].matrix.buffer) + (int)(yc)*buf[0].matrix.Naxis[0] + (int)(xc); 
+  for (j = ys; j < ye; j++) {
+    V = (float *)(buf[0].matrix.buffer) + j*buf[0].matrix.Naxis[0] + xs; 
+    for (i = xs; i < xe; i++, V++) {
+      r = SQ(i - xc) + SQ(j - yc);
+      if (r > R2) continue;
+      if (Ignore && (fabs (*V - IgnoreValue) < 1e-8)) continue;
+      S1 += *V;
+      S2 += (*V)*(*V);
+      Npix += 1.0;
+      max = MAX (max, *V);
+      min = MIN (min, *V);
+    }
+  }
+  Mean = S1 / Npix;
+  Sum  = Mean * M_PI * R2;
+  Stdev = sqrt (S2/Npix - Mean*Mean);
+
+  if (!Quiet) {
+    gprint (GP_LOG, "     mean     stdev       min       max    Npix     Total\n");
+    gprint (GP_LOG, "%9.4g %9.4g %9.4g %9.4g %7.0f %9.4g\n", Mean, Stdev, min, max, Npix, Sum);
+  }
+
+  set_variable ("MIN",    min);
+  set_variable ("MAX",    max);
+  set_variable ("MEAN",   Mean);
+  set_variable ("SUM",    Sum);
+  set_variable ("NPIX",   Npix);
+  set_variable ("SIGMA",  Stdev);
+
+  return (TRUE);
+
+ usage: 
+  gprint (GP_ERR, "USAGE: circstats <buffer> x y radius\n");
+  return (FALSE);
+
+ range:
+  gprint (GP_ERR, "ERROR: coordinates out of range\n");
+  return (FALSE);
+
+ missed:
+  gprint (GP_ERR, "ERROR: buffer not found\n");
+  return (FALSE);
+}
+
Index: /branches/sc_branches/pantasks_condor/cmd.data/rd.c
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.data/rd.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.data/rd.c	(revision 34783)
@@ -0,0 +1,234 @@
+# include "data.h"
+
+/* there is some confusion in this function with the several possible options */
+int rd (int argc, char **argv) {
+  
+  int i, N, status, plane, Nplane, extend, Nextend, Nskip, JustHead, blank;
+  int ccdsel, done, Nword, IsCompressed;
+  char region[512], *ccdid, *filename;
+  FILE *f;
+  Buffer *buf;
+
+  JustHead = FALSE;
+  if ((N = get_argument (argc, argv, "-head"))) {
+    remove_argument (N, &argc, argv);
+    JustHead = TRUE;
+  }
+
+  plane = 1;
+  if ((N = get_argument (argc, argv, "-plane"))) {
+    remove_argument (N, &argc, argv);
+    plane  = atof(argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  extend = FALSE;
+  Nextend = -1;
+  if ((N = get_argument (argc, argv, "-x"))) {
+    remove_argument (N, &argc, argv);
+    Nextend  = atof(argv[N]);
+    remove_argument (N, &argc, argv);
+    extend = TRUE;
+  }
+
+  ccdsel = FALSE;
+  ccdid = (char *) NULL;
+  if ((N = get_argument (argc, argv, "-n"))) {
+    remove_argument (N, &argc, argv);
+    ccdid  = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+    ccdsel = TRUE;
+  }
+
+  if (extend && ccdsel) {
+    gprint (GP_ERR, "only specify one of -n and -x\n");
+    return (FALSE);
+  }
+
+  if (argc != 3) {
+    gprint (GP_ERR, "USAGE: rd <buffer> <filename> [-head] [-plane N] [-n ccdid] [-x extnum]\n");
+    return (FALSE);
+  }
+
+  /* test if file exists */
+  f = fopen (argv[2], "r");
+  if (f == (FILE *) NULL) {
+    gprint (GP_ERR, "file %s not found\n", argv[2]);
+    return (FALSE);
+  }
+
+  /* find matrix, free old data */
+  if ((buf = SelectBuffer (argv[1], ANYBUFFER, TRUE)) == NULL) {
+      fclose (f);
+      return (FALSE);
+  }
+  gfits_free_matrix (&buf[0].matrix);
+  gfits_free_header (&buf[0].header);
+
+  /* save file name */
+  filename = filebasename (argv[2]);
+  strcpy (buf[0].file, filename);
+  free (filename);
+
+  status = FALSE;
+  IsCompressed = FALSE;
+
+  /*** advance to the correct FITS extension ***/
+
+  /* FITS extension by number */
+  if (extend) {
+    /* load in appropriate header */
+    Nskip = gfits_fread_Xheader (f, &buf[0].header, Nextend);
+    if (!Nskip) {
+      gprint (GP_ERR, "entry in %s not found\n", argv[2]);
+      DeleteBuffer (buf);
+      fclose (f);
+      return (FALSE);
+    }
+    if (gfits_extension_is_compressed (&buf[0].header)) {
+	IsCompressed = TRUE;
+    }
+  } 
+
+  /* FITS extension by name */
+  if (ccdsel) {
+    char *CCDKeyword, ID[64];
+
+    CCDKeyword = get_variable ("CCDKEYWORD");
+    if (CCDKeyword == (char *) NULL) {
+      // gprint (GP_ERR, "CCDKEYWORD variable is not set; ");
+      // gprint (GP_ERR, "using EXTNAME as default\n");
+      CCDKeyword = strcreate ("EXTNAME");
+    }
+    done = FALSE;
+    for (i = 0; !done; i++) {
+      Nskip = gfits_fread_Xheader (f, &buf[0].header, i);
+      if (!Nskip) {
+	gprint (GP_ERR, "extension %s in %s not found\n", ccdid, argv[2]);
+	DeleteBuffer (buf);
+	free (CCDKeyword);
+	fclose (f);
+	return (FALSE);
+      }
+
+      // for compressed data tables, EXTNAME may be duplicated, with the first one containing the
+      // word 'COMPRESSED_IMAGE'.  in this case, check the second EXTNAME, if CCDKeyword == EXTNAME 
+      // this may have to be a more obscure test specifically for 'imcopy' data...
+      // need to check each header, since file may contain a mix
+      
+      Nword = 1;
+      IsCompressed = FALSE;
+      if (gfits_extension_is_compressed (&buf[0].header)) {
+	if (!strcmp (CCDKeyword, "EXTNAME")) Nword = 1;
+	IsCompressed = TRUE;
+      }
+      if (!gfits_scan (&buf[0].header, CCDKeyword, "%s", Nword, ID)) {
+	gprint (GP_ERR, "%s not in header\n", CCDKeyword);
+	DeleteBuffer (buf);
+	free (CCDKeyword);
+	fclose (f);
+	return (FALSE);
+      }
+
+      /* compare as numbers if both are pure numbers, else as strings */
+      done = strnumcmp (ccdid, ID);
+      if (!done) gfits_free_header (&buf[0].header);
+    }
+    free (CCDKeyword);
+  }
+
+  /* fix up header, if needed */
+  if (extend || ccdsel) {
+    if (!IsCompressed) {
+      gfits_extended_to_primary (&buf[0].header, TRUE, "Standard FITS");
+    }
+  } else {
+      gfits_fread_header (f, &buf[0].header);
+  }
+
+  /* for JustHead, we skip reading the data segment */ 
+  // XXX for compressed data, we need to convert the header to the equivalent uncompressed version
+  if (JustHead) {
+    // XXX what are we doing here exactly?
+    buf[0].header.Naxes = 0;
+    ALLOCATE (buf[0].matrix.buffer, char, 1);
+    buf[0].matrix.datasize = 0;
+    buf[0].bitpix = 16;
+    buf[0].bzero = 0;
+    buf[0].bscale = 1;
+    buf[0].matrix.bitpix = 16;
+    buf[0].matrix.bzero = 0;
+    buf[0].matrix.bscale = 1;
+    buf[0].header.bitpix = 16;
+    buf[0].header.bzero = 0;
+    buf[0].header.bscale = 1;
+    fclose (f);
+    return (TRUE);
+  }
+
+  /* check for valid plane */
+  Nplane = buf[0].header.Naxis[2];
+  if (Nplane == 0) Nplane = 1;
+  if (plane > Nplane) {
+    gprint (GP_ERR, "-plane is too large: %d total planes\n", Nplane);
+    DeleteBuffer (buf);
+    fclose (f);
+    return (FALSE);
+  }
+
+  /* load matrix data */
+  if (IsCompressed) {
+    FTable ftable;
+    Header theader;
+    ftable.header = &theader;
+    ftable.header[0].buffer = NULL;
+    gfits_copy_header (&buf[0].header, ftable.header);
+    status = gfits_fread_ftable_data (f, &ftable, FALSE);  // this just reads the bytes (not even a SWAP)
+    status = gfits_uncompress_image (&buf[0].header, &buf[0].matrix, &ftable);
+    // uncompressing the image leaves the format as an extension
+    gfits_extended_to_primary (&buf[0].header, TRUE, "Standard FITS");
+    gfits_free_table (&ftable);
+    // XXX this currently does not work for a cube (we get a cube back, not a specific plane)
+  } else {
+    sprintf (region, "-1 -1 -1 -1 %d %d", (plane - 1), plane);
+    status = gfits_fread_matrix_segment (f, &buf[0].matrix, &buf[0].header, region);
+  }
+  fclose (f);
+
+  if (!status) {
+    gprint (GP_ERR, "problem reading file, buffer not opened\n");
+    DeleteBuffer (buf);
+    return (FALSE);
+  }
+
+  /* adjust buffer to represent 2D data */
+  if (Nplane > 1) {
+    buf[0].header.Naxis[2] = 0;
+    buf[0].header.Naxes = 2;
+    gfits_modify (&buf[0].header, "NAXIS", "%d", 1, 2);
+    gfits_delete (&buf[0].header, "NAXIS3", 1);
+  }
+
+  /* we need to return a 2D array, convert 1D images to 2D (Naxis[1] = 1) */
+  if (buf[0].header.Naxes == 1) {
+    buf[0].header.Naxes = 2;
+    buf[0].header.Naxis[1] = 1;
+    buf[0].matrix.Naxis[1] = 1;
+  }    
+
+  buf[0].bitpix = buf[0].header.bitpix;    /* store the original values */
+  buf[0].bscale = buf[0].header.bscale;    /* store the original values */
+  buf[0].bzero  = buf[0].header.bzero;     /* store the original values */
+  buf[0].unsign = buf[0].header.unsign;
+
+  gprint (GP_LOG, "read "OFF_T_FMT" bytes from %s into buffer %s\n", 
+	   buf[0].header.datasize + buf[0].matrix.datasize, argv[2], argv[1]);
+
+  blank = 0xffff;
+  gfits_scan (&buf[0].header, "BLANK", "%d", 1, &blank);
+
+  /** now - convert the matrix values to floats for internal use **/
+  gfits_convert_format (&buf[0].header, &buf[0].matrix, -32, 1.0, 0.0, blank, gfits_get_unsign_mode());
+
+  return (TRUE);
+}
Index: /branches/sc_branches/pantasks_condor/cmd.data/rdseg.c
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.data/rdseg.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.data/rdseg.c	(revision 34783)
@@ -0,0 +1,73 @@
+# include "data.h"
+
+/* there is some confusion in this function with the several possible options */
+int rdseg (int argc, char **argv) {
+  
+  int x, y, nx, ny, status, blank;
+  char region[512], *filename;
+  FILE *f;
+  Buffer *buf;
+
+  if (argc != 7) {
+    gprint (GP_ERR, "USAGE: rdseg <buffer> <filename> x y nx ny\n");
+    return (FALSE);
+  }
+  x = atoi (argv[3]);
+  y = atoi (argv[4]);
+  nx = atoi (argv[5]);
+  ny = atoi (argv[6]);
+
+  /* test if file exists */
+  f = fopen (argv[2], "r");
+  if (f == (FILE *) NULL) {
+    gprint (GP_ERR, "file %s not found\n", argv[2]);
+    return (FALSE);
+  }
+  fclose (f);
+
+  /* find matrix, free old data */
+  if ((buf = SelectBuffer (argv[1], ANYBUFFER, TRUE)) == NULL) return (FALSE);
+  gfits_free_matrix (&buf[0].matrix);
+  gfits_free_header (&buf[0].header);
+
+  /* save file name */
+  filename = filebasename (argv[2]);
+  strcpy (buf[0].file, filename);
+  free (filename);
+
+  status = gfits_read_header (argv[2], &buf[0].header);
+  sprintf (region, "%d %d %d %d 0 1", x, nx + x, y, ny + y);
+  status = gfits_read_matrix_segment (argv[2], &buf[0].matrix, region);
+  gfits_modify (&buf[0].header, "NAXIS1", "%d", 1, nx);
+  gfits_modify (&buf[0].header, "NAXIS2", "%d", 1, ny);
+  buf[0].header.Naxis[0] = nx;
+  buf[0].header.Naxis[1] = ny;
+  buf[0].matrix.Naxis[0] = nx;
+  buf[0].matrix.Naxis[1] = ny;
+
+  if (!status) {
+    gprint (GP_ERR, "problem reading file, buffer not opened\n");
+    DeleteBuffer (buf);
+    return (FALSE);
+  }
+  if (buf[0].header.Naxes == 1) {
+    /* we need to return an array, so make Naxis[1] = 1 */
+    buf[0].header.Naxes = 2;
+    buf[0].header.Naxis[1] = 1;
+    buf[0].matrix.Naxis[1] = 1;
+  }    
+
+  buf[0].bitpix = buf[0].header.bitpix;    /* store the original values */
+  buf[0].bscale = buf[0].header.bscale;    /* store the original values */
+  buf[0].bzero  = buf[0].header.bzero;     /* store the original values */
+  buf[0].unsign = buf[0].header.unsign;
+  gprint (GP_LOG, "read "OFF_T_FMT" bytes from %s into buffer %s\n",  buf[0].header.datasize + buf[0].matrix.datasize, argv[2], argv[1]);
+
+  gfits_scan (&buf[0].header, "BLANK", "%d", 1, &blank);
+
+  /** now - convert the matrix values to floats for internal use **/
+  gfits_convert_format (&buf[0].header, &buf[0].matrix, -32, 1.0, 0.0, blank, gfits_get_unsign_mode());
+  
+  return (TRUE);
+
+}
Index: /branches/sc_branches/pantasks_condor/cmd.data/read_vectors.c
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.data/read_vectors.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.data/read_vectors.c	(revision 34783)
@@ -0,0 +1,468 @@
+# include "data.h"
+
+FILE *f = (FILE *) NULL;
+char filename[2048];
+
+int datafile (int argc, char **argv) {
+
+  if (argc < 2) {
+    gprint (GP_ERR, "USAGE: datafile (filename)\n");
+    return (FALSE);
+  }
+  
+  strcpy (filename, argv[1]);
+  if (f != (FILE *) NULL) { fclose (f); }
+  f = fopen (filename, "r");
+  if (f == (FILE *) NULL) {
+    gprint (GP_ERR, "failed to open file %s\n", argv[1]);
+    return (FALSE);
+  }
+  return (TRUE);
+}
+
+// vector types
+enum {COLTYPE_NONE, COLTYPE_FLT, COLTYPE_INT, COLTYPE_TIME, COLTYPE_CHAR};
+
+int read_vectors (int argc, char **argv) {
+  
+  int TimeFormat;
+  time_t TimeReference;
+  int i, j, Nskip, Narg, Nvec, *col, IsCSV, VERBOSE;
+  int Nbytes, Nstart, NELEM, Nelem, nread, *coltype;
+  char *colstr, *c0, *c1, *buffer, *extname;
+  Vector **vec;
+
+  /* auto-sense table type */
+  if ((Narg = get_argument (argc, argv, "-fits"))) {
+    remove_argument (Narg, &argc, argv);
+    extname = strcreate (argv[Narg]);
+    if (extname == (char *) NULL) return (FALSE);
+    remove_argument (Narg, &argc, argv);
+    int status = read_table_vectors (argc, argv, extname);
+    free (extname);
+    return (status);
+  }
+
+  Nskip = 0;
+  if ((Narg = get_argument (argc, argv, "-skip"))) {
+    remove_argument (Narg, &argc, argv);
+    Nskip = atof (argv[Narg]);
+    remove_argument (Narg, &argc, argv);
+  }
+
+  IsCSV = FALSE;
+  if ((Narg = get_argument (argc, argv, "-csv"))) {
+    remove_argument (Narg, &argc, argv);
+    IsCSV = TRUE;
+  }
+
+  VERBOSE = FALSE;
+  if ((Narg = get_argument (argc, argv, "-v"))) {
+    remove_argument (Narg, &argc, argv);
+    VERBOSE = TRUE;
+  }
+
+  if ((argc < 3) || !(argc % 2)) {
+    gprint (GP_ERR, "USAGE: read name N name N ...\n");
+    return (FALSE);
+  }
+  /* read name N name N  */
+
+  // do this only optionally?
+  GetTimeFormat (&TimeReference, &TimeFormat);
+
+  if (f == (FILE *) NULL) {
+    gprint (GP_ERR, "no open file for read\n");
+    return (FALSE);
+  }
+  fseeko (f, 0LL, SEEK_SET);
+
+  Nvec = (argc - 1) / 2;
+  ALLOCATE (vec, Vector *, Nvec);
+  ALLOCATE (col, int, Nvec);
+  ALLOCATE (coltype, int, Nvec);
+
+  for (i = 0; i < Nvec; i++) {
+
+    // interpret the column names including type flags
+    // XXX review the grammar before releasing this : is foo:type best, or is something else needed?
+    // Note the conflict wrt list entries
+    // the name may be of the form foo:type, where type may be one of : int, float, time
+    
+    coltype[i] = COLTYPE_FLT;
+    char *colname = argv[2*i + 1];
+    char *ptr = strchr (colname, ':');
+    if (ptr) {
+      // split out colname and type
+      *ptr = 0;
+      ptr ++;
+      if (!ptr) goto bad_colname;
+      coltype[i] = COLTYPE_NONE;
+      if (!strcasecmp(ptr, "float")) { coltype[i] = COLTYPE_FLT; }
+      if (!strcasecmp(ptr, "int"))   { coltype[i] = COLTYPE_INT; }
+      if (!strcasecmp(ptr, "char"))  { coltype[i] = COLTYPE_CHAR; }
+      if (!strcasecmp(ptr, "time"))  { coltype[i] = COLTYPE_TIME; }
+      if (!coltype[i]) goto bad_colname;
+    }
+
+    if ((vec[i] = SelectVector (argv[2*i + 1], ANYVECTOR, TRUE)) == NULL) {
+      gprint (GP_ERR, "USAGE: read name N name N ...\n");
+      free (vec);
+      free (col);
+      return (FALSE);    
+    }
+
+    colstr = argv[2*i+2];
+    for (j = 0; j < strlen (colstr); j++) {
+      if (isdigit(colstr[j])) {
+	col[i] = atof (colstr);
+	continue;
+      }
+      // allow 'excel' columns names of the form A-Z, AA-AZ, BA-BZ, .. ZA-ZZ
+      if (strlen(colstr) >= 3) goto bad_colname;
+      if (colstr[0] < 'A') goto bad_colname;
+      if (colstr[0] > 'Z') goto bad_colname;
+      if (colstr[1] && colstr[1] < 'A') goto bad_colname;
+      if (colstr[1] && colstr[1] > 'Z') goto bad_colname;
+
+      col[i] = colstr[0] - 'A' + 1;
+      if (colstr[1]) {
+	col[i] *= 26;
+	col[i] += colstr[1] - 'A' + 1;
+      }
+      continue;
+
+    bad_colname:
+      gprint (GP_ERR, "USAGE: read name N name N ...\n");
+      free (vec);
+      free (col);
+      return (FALSE);    
+    }
+  }
+
+  // currently, all read vectors are forced to be type FLT
+  NELEM = 1000;
+  for (i = 0; i < Nvec; i++) {
+    if ((coltype[i] == COLTYPE_INT) || (coltype[i] == COLTYPE_CHAR)) {
+      ResetVector (vec[i], OPIHI_INT, NELEM);
+    } else {
+      // note that COLTYPE_TIME is a type of float
+      ResetVector (vec[i], OPIHI_FLT, NELEM);
+    }
+  }
+  
+  ALLOCATE (buffer, char, 0x10001);
+  bzero (buffer, 0x10001);
+  for (i = 0; i < Nskip; i++) {
+    scan_line (f, buffer);
+  }
+
+  // we have a working buffer read from the file. we parse the lines in the working buffer
+  // until we reach the last chunk without an EOL char.  at that point, we shift the start
+  // of the last (partial) line to the start of the buffer and re-fill.
+
+  Nstart = 0; // location of the last valid byte in the buffer (start filling here)
+  Nelem = 0; // number of valid rows read (vector elements)
+  while (TRUE) {
+    Nbytes = 0x10000 - Nstart;
+    bzero (&buffer[Nstart], Nbytes);
+    nread = fread (&buffer[Nstart], 1, Nbytes, f);
+    if (ferror (f)) {
+      perror ("error reading data file");
+      break;
+    }
+    if (nread == 0) break; // end of the file
+    // nbytes = nread + Nstart;
+    
+    int bufferStatus = TRUE; 
+    c0 = buffer; // c0 always marks the start of a line
+    while (bufferStatus) {
+      c1 = strchr (c0, '\n'); // find the end of this current line
+      if (c1 == (char *) NULL) {
+	Nstart = strlen (c0);
+	memmove (buffer, c0, Nstart);
+	bufferStatus = FALSE;
+	continue;
+      }
+      *c1 = 0; // mark the end of the line 
+
+      if (*c0 == '#') {	c0 = c1 + 1; continue; }
+      if (*c0 == '!') {	c0 = c1 + 1; continue; }
+
+      // parse the vectors in this line.  this code is a bit inefficient: each column
+      // requires a separate pass through the line.
+
+      int lineStatus = TRUE;
+      for (i = 0; i < Nvec; i++) {
+	int ivalue;
+	char cvalue;
+	double dvalue;
+	time_t tvalue;
+	int readStatus = FALSE;
+	// need to make the if cases for coltype[i]
+	switch (coltype[i]) {
+	  case COLTYPE_INT:
+	    readStatus = IsCSV ? iparse_csv (&ivalue, col[i], c0) : iparse (&ivalue, col[i], c0);
+	    vec[i][0].elements.Int[Nelem] = readStatus ? ivalue : 0;
+	    break;
+	  case COLTYPE_CHAR:
+	    readStatus = IsCSV ? charparse_csv (&cvalue, col[i], c0) : charparse (&cvalue, col[i], c0);
+	    vec[i][0].elements.Int[Nelem] = readStatus ? cvalue : 0;
+	    break;
+	  case COLTYPE_FLT:
+	    readStatus = IsCSV ? dparse_csv (&dvalue, col[i], c0) : dparse (&dvalue, col[i], c0);
+	    vec[i][0].elements.Flt[Nelem] = readStatus ? dvalue : NAN;
+	    break;
+	  case COLTYPE_TIME:
+	    readStatus = IsCSV ? tparse_csv (&tvalue, col[i], c0) : tparse (&tvalue, col[i], c0);
+	    dvalue = TimeValue (tvalue, TimeReference, TimeFormat);
+	    vec[i][0].elements.Flt[Nelem] = readStatus ? dvalue : NAN;
+	    break;
+	}
+	if (!readStatus && VERBOSE) {
+	  if (IsCSV) {
+	    gprint (GP_ERR, "suspect field: %d (%s) in %s\n", col[i], argv[2*i+2], c0);
+	  } else {
+	    gprint (GP_ERR, "suspect field: %d in %s\n", col[i], c0);
+	  }
+	}
+	lineStatus &= readStatus;
+      }
+      if (!lineStatus && VERBOSE) {
+	char temp[32];
+	strncpy (temp, c0, 32);
+	temp[31] = 0;
+	gprint (GP_ERR, "skip line %s\n\n", temp);
+      }
+      Nelem ++;
+      if (Nelem == NELEM) {
+	NELEM += 1000;
+	for (i = 0; i < Nvec; i++) {
+	  if (coltype[i] == COLTYPE_INT) {
+	    REALLOCATE (vec[i][0].elements.Int, opihi_int, NELEM);
+	  } else {
+	    REALLOCATE (vec[i][0].elements.Flt, opihi_flt, NELEM);
+	  }
+	}
+      }
+      c0 = c1 + 1;
+    }
+  }
+  for (i = 0; i < Nvec; i++) {
+    if (coltype[i] == COLTYPE_INT) {
+      REALLOCATE (vec[i][0].elements.Int, opihi_int, MAX (Nelem,1));
+    } else {
+      REALLOCATE (vec[i][0].elements.Flt, opihi_flt, MAX (Nelem,1));
+    }
+    vec[i][0].Nelements = Nelem;
+  }
+  
+  free (vec);
+  free (col);
+  free (buffer);
+  return (TRUE);
+
+}
+
+# define ESCAPE(MSG) { \
+  gprint (GP_ERR, "%s\n", MSG); \
+  if (CCDKeyword != NULL) free (CCDKeyword); \
+  gfits_free_table  (&table); \
+  gfits_free_header (&header); \
+  return (FALSE);  }
+
+int read_table_vectors (int argc, char **argv, char *extname) {
+
+  off_t Nbytes;
+  int i, j, k, N, Nextend, Ny, Binary, vecType, padIfShort;
+  char type[16], ID[80], *CCDKeyword;
+  FTable table;
+  Header header;
+  Vector **vec;
+
+  table.buffer = NULL;
+  header.buffer = NULL;
+
+  CCDKeyword = NULL;
+  if ((N = get_argument (argc, argv, "-keyword"))) {
+    remove_argument (N, &argc, argv);
+    CCDKeyword = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  padIfShort = FALSE;
+  if ((N = get_argument (argc, argv, "-pad-if-short"))) {
+    remove_argument (N, &argc, argv);
+    padIfShort = TRUE;
+  }
+
+  Nextend = -1;
+  if ((N = get_argument (argc, argv, "-extnum"))) {
+    remove_argument (N, &argc, argv);
+    Nextend = atoi (extname);
+  }
+
+  // XXX ReadAll needs: deal with Extnum vs Extname, save vectors, etc
+  // ReadAll = FALSE;
+  // if ((N = get_argument (argc, argv, "-all"))) {
+  //   remove_argument (N, &argc, argv);
+  //   ReadAll = atoi (extname);
+  //   if (argc != 1) ESCAPE ("-all option cannot be mixed with selected field");
+  // }
+
+  if (argc < 2) ESCAPE ("USAGE: read -fits extension [-extnum] [-keyword key] name name ...");
+
+  if (f == NULL) ESCAPE ("file not found");
+  fseeko (f, 0LL, SEEK_SET);
+  table.header = &header;
+
+  /* load appropriate extension (if extname is a number, use count) */
+  if (Nextend > -1) {
+    // first extension is PHU, cannot be a table. 
+    // Nextend counts from 0 for first extension
+    if (!gfits_load_header (f, &header)) ESCAPE ("error reading primary header for file");
+    Nbytes = gfits_data_size (&header);
+    fseeko (f, Nbytes, SEEK_CUR);
+    gfits_free_header (&header);
+
+    for (i = 0; i < Nextend; i++) {
+      if (!gfits_load_header (f, &header)) ESCAPE ("extension not found");
+      Nbytes = gfits_data_size (&header);
+      /* skip the prior data buffers */
+      fseeko (f, Nbytes, SEEK_CUR);
+      gfits_free_header (&header);
+    }
+    if (!gfits_load_header (f, &header)) ESCAPE ("error reading header for extension");
+    if (!gfits_fread_ftable_data (f, &table, padIfShort)) ESCAPE ("error reading table for extension");
+
+  } else {
+    if (CCDKeyword == NULL) {
+      CCDKeyword = get_variable ("CCDKEYWORD");
+    }
+    if (CCDKeyword == NULL) {
+      CCDKeyword = strcreate ("EXTNAME");
+    }
+
+    while (1) {
+      if (!gfits_load_header (f, &header)) {
+	gprint (GP_ERR, "extension %s not found in file\n", extname);
+	if (CCDKeyword != NULL) free (CCDKeyword); 
+	gfits_free_table  (&table); 
+	gfits_free_header (&header); 
+	return (TRUE);  
+      }
+
+      Nbytes = gfits_data_size (&header);
+
+      if (!gfits_scan (&header, CCDKeyword, "%s", 1, ID)) {
+	fseeko (f, Nbytes, SEEK_CUR);
+	gfits_free_header (&header);
+	continue;
+      }
+      if (strcmp (ID, extname)) {
+	fseeko (f, Nbytes, SEEK_CUR);
+	gfits_free_header (&header);
+	continue;
+      }
+      if (!gfits_fread_ftable_data (f, &table, padIfShort)) ESCAPE ("error reading table for extension");
+      break;
+    }
+  }
+
+  /* identify table type (ascii / binary) */
+  Binary = FALSE;
+  gfits_scan (&header, "XTENSION", "%s", 1, type);
+  if (strcmp (type, "BINTABLE") && strcmp (type, "TABLE")) {
+    ESCAPE ("specified extension is not a table\n");
+  }
+  Binary = !strcmp (type, "BINTABLE");
+  Ny = header.Naxis[1];
+
+  /* find columns which match requested vectors */
+  for (i = 1; i < argc; i++) {
+    void   *data;
+    int Nval;
+    char name[80];
+      
+    Nval = 0;
+    if (Binary) {
+      if (!gfits_get_bintable_column_type (&header, argv[i], type, &Nval)) ESCAPE ("requested field not found");
+      if (!gfits_get_bintable_column (&header, &table, argv[i], &data)) ESCAPE ("error reading data from specified field");
+    } else {
+      if (!gfits_get_table_column_type (&header, argv[i], type)) ESCAPE ("requested field not found");
+      if (!gfits_get_table_column (&header, &table, argv[i], &data)) ESCAPE ("error reading data from specified field");
+    }
+    if (Nval == 0) ESCAPE ("no data for field in table");
+    
+    vecType = OPIHI_INT;
+    if (!strcmp (type, "double") || !strcmp (type, "float")) {
+      vecType = OPIHI_FLT;
+    }
+	
+    // define the multifield vector names
+    ALLOCATE (vec, Vector *, Nval);
+    for (j = 0; j < Nval; j++) {
+      if (Nval == 1) 
+	sprintf (name, "%s", argv[i]);
+      else
+	sprintf (name, "%s:%d", argv[i], j);
+      if ((vec[j] = SelectVector (name, ANYVECTOR, TRUE)) == NULL) ESCAPE ("bad vector name");
+      ResetVector (vec[j], vecType, Ny);
+    }
+
+    if (!strcmp (type, "char")) {
+      char *Ptr = data;
+      for (j = 0; j < Ny; j++) {
+	for (k = 0; k < Nval; k++, Ptr++) {
+	  vec[k][0].elements.Int[j] = *Ptr;
+	}
+      }
+    }
+    if (!strcmp (type, "short")) {
+      short *Ptr = data;
+      for (j = 0; j < Ny; j++) {
+	for (k = 0; k < Nval; k++, Ptr++) {
+	  vec[k][0].elements.Int[j] = *Ptr;
+	}
+      }
+    }
+    if (!strcmp (type, "int")) {
+      int *Ptr = data;
+      for (j = 0; j < Ny; j++) {
+	for (k = 0; k < Nval; k++, Ptr++) {
+	  vec[k][0].elements.Int[j] = *Ptr;
+	}
+      }
+    }
+    if (!strcmp (type, "int64_t")) {
+      int64_t *Ptr = data;
+      for (j = 0; j < Ny; j++) {
+	for (k = 0; k < Nval; k++, Ptr++) {
+	  vec[k][0].elements.Int[j] = *Ptr;
+	}
+      }
+    }
+    if (!strcmp (type, "float")) {
+      float *Ptr = data;
+      for (j = 0; j < Ny; j++) {
+	for (k = 0; k < Nval; k++, Ptr++) {
+	  vec[k][0].elements.Flt[j] = *Ptr;
+	}
+      }
+    }
+    if (!strcmp (type, "double")) {
+      double *Ptr = data;
+      for (j = 0; j < Ny; j++) {
+	for (k = 0; k < Nval; k++, Ptr++) {
+	  vec[k][0].elements.Flt[j] = *Ptr;
+	}
+      }
+    }
+    free (data);
+    free (vec);
+  }
+  if (CCDKeyword != NULL) free (CCDKeyword);
+  gfits_free_table (&table);
+  gfits_free_header (&header);
+  return (TRUE);
+}
Index: /branches/sc_branches/pantasks_condor/cmd.data/rebin.c
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.data/rebin.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.data/rebin.c	(revision 34783)
@@ -0,0 +1,230 @@
+# include "data.h"
+
+int rebin (int argc, char **argv) {
+  
+  int i, j, status, n, nx, ny, Nx, Ny, x, y, N, *Npix, *Vn;
+  int Ignore, IgnoreValue, VERBOSE, Normalize, ExactScale;
+  char temp[1024];
+  float *Vout, *Vin, *Out, *In;
+  double scale, scale2, fx, fy, dX, dY;
+  Buffer *in, *out;
+
+  Vn = Npix = NULL;
+  Normalize = FALSE;
+  if ((N = get_argument (argc, argv, "-norm"))) {
+    remove_argument (N, &argc, argv);
+    Normalize = TRUE;
+  }
+
+  VERBOSE = FALSE;
+  if ((N = get_argument (argc, argv, "-v"))) {
+    remove_argument (N, &argc, argv);
+    VERBOSE = TRUE;
+  }
+
+  Ignore = FALSE;
+  IgnoreValue = 0.0;
+  if ((N = get_argument (argc, argv, "-ignore"))) {
+    Ignore = TRUE;
+    remove_argument (N, &argc, argv);
+    IgnoreValue = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  if (argc != 4) {
+    gprint (GP_ERR, "USAGE: rebin <from> <to> scale \n");
+    gprint (GP_ERR, "  negative integer scale expands image\n");
+    return (FALSE);
+  }
+
+  if ((in  = SelectBuffer (argv[1], OLDBUFFER, TRUE)) == NULL) return (FALSE);
+  if ((out = SelectBuffer (argv[2], ANYBUFFER, TRUE)) == NULL) return (FALSE);
+  gfits_free_matrix (&out[0].matrix);
+  gfits_free_header (&out[0].header);
+
+  scale  = atof (argv[3]);
+  if ((scale == (int) scale) || ((1.0/scale) == (int)(1.0/scale))) {
+    ExactScale = TRUE;
+    if (scale > 0) {
+      nx = in[0].header.Naxis[0] / scale;
+      ny = in[0].header.Naxis[1] / scale;
+    } else {
+      nx = in[0].header.Naxis[0] * fabs(scale);
+      ny = in[0].header.Naxis[1] * fabs(scale);
+    }    
+  } else {
+    ExactScale = FALSE;
+    if (scale > 0) {
+      nx = (int) (in[0].header.Naxis[0] / scale) + 1;
+      ny = (int) (in[0].header.Naxis[1] / scale) + 1;
+    } else {
+      nx = (int) (in[0].header.Naxis[0] * fabs(scale)) + 1;
+      ny = (int) (in[0].header.Naxis[1] * fabs(scale)) + 1;
+    }      
+  }
+  if (VERBOSE) gprint (GP_LOG, "rebin %s to %s ("OFF_T_FMT","OFF_T_FMT" to %d,%d)\n", argv[1], argv[2],  in[0].header.Naxis[0],  in[0].header.Naxis[1], nx, ny);
+
+  Nx = in[0].header.Naxis[0];
+  Ny = in[0].header.Naxis[1];
+  out[0].bitpix = in[0].bitpix;
+  out[0].unsign = in[0].unsign;
+  out[0].bscale = in[0].bscale;
+  out[0].bzero  = in[0].bzero;
+  gfits_copy_header (&in[0].header, &out[0].header);
+  gfits_modify (&out[0].header, "NAXIS1", "%d", 1, nx);
+  gfits_modify (&out[0].header, "NAXIS2", "%d", 1, ny);
+
+  status =  gfits_scan (&out[0].header, "CDELT1", "%lf", 1, &dX);
+  status &= gfits_scan (&out[0].header, "CDELT2", "%lf", 1, &dY);
+  if (scale > 0) {
+    dX *= scale;
+    dY *= scale;
+  } else {
+    dX /= fabs(scale);
+    dY /= fabs(scale);
+  }    
+  if (status) {
+    gfits_modify (&out[0].header, "CDELT1", "%lf", 1, dX);
+    gfits_modify (&out[0].header, "CDELT2", "%lf", 1, dY);
+  }
+
+  status =  gfits_scan (&out[0].header, "CRPIX1", "%lf", 1, &dX);
+  status &= gfits_scan (&out[0].header, "CRPIX2", "%lf", 1, &dY);
+  if (scale > 0) {
+    dX /= scale;
+    dY /= scale;
+  } else {
+    dX *= fabs(scale);
+    dY *= fabs(scale);
+  }    
+  if (status) {
+    gfits_modify (&out[0].header, "CRPIX1", "%lf", 1, dX);
+    gfits_modify (&out[0].header, "CRPIX2", "%lf", 1, dY);
+  }
+
+  out[0].header.Naxis[0] = nx;
+  out[0].header.Naxis[1] = ny;
+  gfits_create_matrix (&out[0].header, &out[0].matrix);
+  temp[0] = 0;
+  if ((in[0].file[0] != '*') && (in[0].file[0] != '(')) {
+    strcpy (temp, "*");
+  }
+  strcat (temp, in[0].file);
+  strcpy (out[0].file, temp);
+
+  if (Normalize) {
+    ALLOCATE (Npix, int, nx*ny);
+    bzero (Npix, nx*ny*sizeof(int));
+  }
+
+  if (ExactScale) {
+    n = scale;
+    if (n > 0) {
+      for (j = 0; j < ny; j++) {
+	for (y = 0; y < n; y++) {
+	  Vout = (float *)(out[0].matrix.buffer) + j*nx;
+	  Vin  = (float *)(in[0].matrix.buffer)  + (j*n + y)*in[0].header.Naxis[0];
+	  if (Normalize) { Vn = Npix + j*nx; }
+	  for (i = 0; i < nx; i++, Vout++) {
+	    for (x = 0; x < n; x++, Vin++) {
+	      if (isnan(*Vin)) continue;
+	      if (isinf(*Vin)) continue;
+	      if (Ignore && (*Vin == IgnoreValue)) continue;
+	      *Vout += *Vin;
+	      if (Normalize) {(*Vn) ++;}
+	    }
+	    if (Normalize) {Vn ++;}
+	  }
+	}
+      }
+    } else {
+      n = fabs (n);
+      for (j = 0; j < in[0].header.Naxis[1]; j++) {
+	for (y = 0; y < n; y++) {
+	  Vout = (float *)(out[0].matrix.buffer) + (j*n + y)*nx;
+	  Vin  = (float *)(in[0].matrix.buffer)  + j*in[0].header.Naxis[0];
+	  if (Normalize) { Vn = Npix + j*nx; }
+	  for (i = 0; i < in[0].header.Naxis[0]; i++, Vin++) {
+	    if (isnan(*Vin) || isinf(*Vin) || (Ignore && (*Vin == IgnoreValue))) { 
+	      Vout += n; 
+	      if (Normalize) Vn += n; 
+	      continue; 
+	    }
+	    for (x = 0; x < n; x++, Vout++) {
+	      *Vout = *Vin;
+	      if (Normalize) {(*Vn) ++; Vn ++;}
+	    }
+	  }
+	}
+      }
+    }
+    if (Normalize) {
+      Vn = Npix;
+      Vout = (float *)out[0].matrix.buffer;
+      for (i = 0; i < nx*ny; i++, Vout++, Vn++) {
+	if (*Vn) { 
+	  *Vout /= *Vn; 
+	} else {
+	  *Vout = 0;
+	}
+      }
+    }
+  } else {
+    
+    /* normalization is broken.  repair please */
+    if (Normalize) { gprint (GP_ERR, "normalize not enabled for fractional scaling\n"); }
+
+    if (scale < 0) scale = 1.0 / fabs(scale);
+    In = (float *)in[0].matrix.buffer;
+    Out = (float *)out[0].matrix.buffer;
+    scale2 = scale*scale;
+    if (scale > 1) {
+      for (i = 0; i < Ny; i++) {
+	y = 0.5 + (i - 0.5) / scale;
+	fy = 0.5 + MIN (0.5, (y + 0.5) * scale - i);
+	for (j = 0; j < Nx; j++, In++) {
+	  x = 0.5 + (j - 0.5) / scale;
+	  fx = 0.5 + MIN (0.5, (x + 0.5) * scale - j);
+	  Vout = Out + y*nx + x;
+	  *Vout += fx*fy*(*In);
+	  if (fx < 1) {
+	    *(Vout+1)    += (1-fx)*fy*(*In);
+	  }
+	  if (fy < 1) {
+	    *(Vout+nx) += fx*(1-fy)*(*In);
+	  }
+	  if ((fx < 1) && (fy < 1)) {
+	    *(Vout+1+nx) += (1-fx)*(1-fy)*(*In);
+	  }
+	}
+      }
+    } else {
+      for (i = 0; i < ny; i++) {
+	y = 0.5 + (i - 0.5) * scale;
+	fy = 0.5 + MIN (0.5, (y + 0.5) / scale - i);
+	for (j = 0; j < nx; j++, Out++) {
+	  x = 0.5 + (j - 0.5) * scale;
+	  fx = 0.5 + MIN (0.5, (x + 0.5) / scale - j);
+	  Vin = In + y*Nx + x;
+	  *Out += *Vin*fx*fy;
+	  if (fx < 1) {
+	    *Out += *(Vin+1)*(1-fx)*fy;
+	  }
+	  if (fy < 1) {
+	    *Out += *(Vin+Nx)*fx*(1-fy);
+	  }
+	  if ((fx < 1) && (fy < 1)) {
+	    *Out += *(Vin+1+Nx)*(1-fx)*(1-fy);
+	  }
+	  *Out = *Out * scale2;
+	}
+      }
+    }
+  }
+
+  if (Normalize) free (Npix);
+
+  return (TRUE);
+
+}
+
Index: /branches/sc_branches/pantasks_condor/cmd.data/reindex.c
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.data/reindex.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.data/reindex.c	(revision 34783)
@@ -0,0 +1,95 @@
+# include "data.h"
+
+# define ESCAPE(MSG,...){ gprint (GP_ERR, MSG, __VA_ARGS__); goto error; } 
+
+int reindex (int argc, char **argv) {
+  
+  int  i, Nmax, N;
+  Vector *ivec, *ovec, *xvec;
+
+  ivec = ovec = xvec = NULL;
+  int Npts = 0;
+
+  int KEEP_UNMATCH = FALSE;
+  if ((N = get_argument (argc, argv, "-keep-unmatched"))) {
+    remove_argument (N, &argc, argv);
+    KEEP_UNMATCH = TRUE;
+  }
+
+  if (argc != 6) goto usage;
+  if (strcmp(argv[2], "=")) goto usage;
+  if (strcmp(argv[4], "using")) goto usage;
+
+  if ((ovec = SelectVector (argv[1], ANYVECTOR, TRUE)) == NULL) goto error;
+  if ((ivec = SelectVector (argv[3], OLDVECTOR, TRUE)) == NULL) goto error;
+  if ((xvec = SelectVector (argv[5], OLDVECTOR, TRUE)) == NULL) goto error;
+
+  if (xvec->type != OPIHI_INT) ESCAPE("%s\n", "index is not an integer?");
+
+  // ovec matches ivec in type and xvec in size (xvec need not have all ivec elements, and may have duplicates
+  int NPTS = xvec[0].Nelements;
+  ResetVector (ovec, ivec->type, xvec[0].Nelements);
+
+  Nmax = ivec[0].Nelements - 1;
+
+  // we have two cases: (ivec == flt or int)
+  if (ivec->type == OPIHI_FLT) {
+    opihi_flt *vi = ivec[0].elements.Flt;
+    opihi_int *vx = xvec[0].elements.Int;
+    for (Npts = i = 0; i < xvec[0].Nelements; i++, vx++) {
+      if (Npts >= NPTS) {
+	NPTS += 2000;
+	REALLOCATE (ovec[0].elements.Flt, opihi_flt, NPTS);
+      }
+      if (*vx < 0) {
+	if (KEEP_UNMATCH) {
+	  ovec[0].elements.Flt[Npts] = NAN;
+	  Npts++;
+	} 
+	continue;
+      }
+      if (*vx > Nmax) ESCAPE("unexpected value in index: %d (%d)\n", *vx, i);
+      ovec[0].elements.Flt[Npts] = vi[*vx];
+      Npts++;
+    }
+  }
+  if (ivec->type == OPIHI_INT) {
+    opihi_int *vi = ivec[0].elements.Int;
+    opihi_int *vx = xvec[0].elements.Int;
+    for (Npts = i = 0; i < xvec[0].Nelements; i++, vx++) {
+      if (Npts >= NPTS) {
+	NPTS += 2000;
+	REALLOCATE (ovec[0].elements.Int, opihi_int, NPTS);
+      }
+      if (*vx < 0) {
+	if (KEEP_UNMATCH) {
+	  ovec[0].elements.Flt[Npts] = -1;
+	  Npts++;
+	} 
+	continue;
+      }
+      if (*vx > Nmax) ESCAPE("unexpected value in index: %d (%d)\n", *vx, i);
+      ovec[0].elements.Int[Npts] = vi[*vx];
+      Npts++;
+    }
+  }
+
+  // free up unused memory
+  ResetVector (ovec, ivec->type, Npts);
+  return (TRUE);
+
+error:
+  DeleteVector (ovec);
+  return (FALSE);
+
+usage:
+    gprint (GP_ERR, "USAGE: reindex (out) = (in) using (index)\n");
+    gprint (GP_ERR, "  Creates a new vector (out) from (in) based on sequence in (index)\n");
+    gprint (GP_ERR, "  output[i] = input[index[i]]\n");
+    gprint (GP_ERR, "  If -keep-unmatched is provided, elements of index with negative values will be set to NaN / -1,\n");
+    gprint (GP_ERR, "    otherwise they will be skipped in the output.\n");
+    gprint (GP_ERR, "  The output vector has the type of the input vector and the length of the index (if -keep-unmatched).\n");
+    gprint (GP_ERR, "  The index vector may have duplicates\n");
+    return (FALSE);
+}
+
Index: /branches/sc_branches/pantasks_condor/cmd.data/relocate.c
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.data/relocate.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.data/relocate.c	(revision 34783)
@@ -0,0 +1,32 @@
+# include "data.h"
+
+int relocate (int argc, char **argv) {
+
+  int x, y;
+  int N, kapa;
+  char *name;
+  
+  /* display source */
+  name = NULL;
+  if ((N = get_argument (argc, argv, "-n"))) {
+    remove_argument (N, &argc, argv);
+    name = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+  if (!GetImage (NULL, &kapa, name)) return (FALSE);
+  FREE (name);
+
+  if (argc != 3) {
+    gprint (GP_ERR, "USAGE: relocate x y [-n]\n");
+    return (FALSE);
+  }
+
+  /* x & y are pixels for the screen -- this command has no meaning for non-X */
+
+  x = atoi (argv[1]);
+  y = atoi (argv[2]);
+
+  KiiRelocate (kapa, x, y);
+  return (TRUE);
+}
+
Index: /branches/sc_branches/pantasks_condor/cmd.data/resize.c
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.data/resize.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.data/resize.c	(revision 34783)
@@ -0,0 +1,49 @@
+# include "data.h"
+
+int resize (int argc, char **argv) {
+
+  char *end;
+  double NX, NY;
+  int N, kapa;
+  char *name;
+  
+  /* display source */
+  name = NULL;
+  if ((N = get_argument (argc, argv, "-n"))) {
+    remove_argument (N, &argc, argv);
+    name = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+  if (!GetImage (NULL, &kapa, name)) return (FALSE);
+  FREE (name);
+
+  if ((N = get_argument (argc, argv, "-by-image"))) {
+    remove_argument (N, &argc, argv);
+    KiiResizeByImage (kapa);
+    return (TRUE);
+  }
+
+  if (argc != 3) {
+    gprint (GP_ERR, "USAGE: resize NX NY [-n] [-g | -i]\n");
+    return (FALSE);
+  }
+
+  /* NX & NY are pixels for the screen & points for PS 
+     convert units to points (1in = 72pt, 1cm = 28pt) */
+
+  /* have kapa convert physical units to screen units 
+     for now, fixed at 96 pix / in == 38 pix / cm
+  */
+
+  NX = strtod (argv[1], &end);
+  if (!strcmp (end, "in")) { NX *= 96; }
+  if (!strcmp (end, "cm")) { NX *= 38; }
+
+  NY = strtod (argv[2], &end);
+  if (!strcmp (end, "in")) { NY *= 96; }
+  if (!strcmp (end, "cm")) { NY *= 38; }
+
+  KiiResize (kapa, NX, NY);
+  return (TRUE);
+}
+
Index: /branches/sc_branches/pantasks_condor/cmd.data/roll.c
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.data/roll.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.data/roll.c	(revision 34783)
@@ -0,0 +1,45 @@
+# include "data.h"
+
+int roll (int argc, char **argv) {
+  
+  int Nbytes, Nextra;
+  int dX, dx, dy, nx, ny;
+  Buffer *buf;
+
+  if (argc != 4) {
+    gprint (GP_ERR, "USAGE: roll <buffer> dx dy\n");
+    return (FALSE);
+  }
+
+  if ((buf = SelectBuffer (argv[1], OLDBUFFER, TRUE)) == NULL) return (FALSE);
+
+  dx = atof (argv[2]);
+  dy = atof (argv[3]);
+  if (dy != 0) {
+    gprint (GP_ERR, "only x rolls implemented for now\n");
+  }
+
+  /* if (dx < 0), we are moving the start position back by dx pixels,
+     if (dx > 0), we are moving the start position forward by dx pixels */
+  
+  nx = buf[0].matrix.Naxis[0];
+  ny = buf[0].matrix.Naxis[1];
+
+  dX = abs(dx);
+  Nbytes = nx * ny * sizeof (float);
+  Nextra = (nx * ny + dX) * sizeof (float);
+
+  REALLOCATE (buf[0].matrix.buffer, char, Nextra);
+
+  if (dx < 0) {
+    memmove (buf[0].matrix.buffer, &buf[0].matrix.buffer[dX*sizeof(float)], Nbytes);
+  } else {
+    memmove (&buf[0].matrix.buffer[dX*sizeof(float)], buf[0].matrix.buffer, Nbytes);
+  }
+
+  REALLOCATE (buf[0].matrix.buffer, char, Nbytes);
+
+  return (TRUE);
+
+}
+
Index: /branches/sc_branches/pantasks_condor/cmd.data/rotate.c
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.data/rotate.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.data/rotate.c	(revision 34783)
@@ -0,0 +1,295 @@
+# include "data.h"
+
+int rotate (int argc, char **argv) {
+  
+  int i, j, NX, NY, X, Y, Lx, Ly, N;
+  float *in_buff, *out_buff, *c;
+  double angle, CosAngle, SinAngle, Xo, Yo, dX, dY, fx, fy, x, y, X1, Y1;
+  double pc11, pc12, pc21, pc22, PC11, PC12, PC21, PC22;
+  Buffer *buf;
+
+  Xo = 0;
+  Yo = 0;
+  if ((N = get_argument (argc, argv, "-center"))) {
+    remove_argument (N, &argc, argv);
+    Xo  = atof(argv[N]);
+    remove_argument (N, &argc, argv);
+    Yo  = atof(argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  if (argc != 3) {
+    gprint (GP_ERR, "USAGE: rotate <buffer> <angle>\n");
+    return (FALSE);
+  }
+  if ((buf = SelectBuffer (argv[1], OLDBUFFER, TRUE)) == NULL) return (FALSE);
+
+  if ((atof (argv[1]) < -180) || (atof (argv[1]) > 180)) {
+    gprint (GP_ERR, "valid rotate angle between -180 and +180 degrees\n");
+    return (FALSE);
+  }
+
+  /* save starting values */
+  NX = buf[0].header.Naxis[0];
+  NY = buf[0].header.Naxis[1];
+  in_buff = (float *) buf[0].matrix.buffer;  /* don't lose reference */
+
+  if (!strcasecmp (argv[2], "LEFT") || (atof (argv[2]) == -90)) {
+    buf[0].header.Naxis[0] = NY;
+    buf[0].header.Naxis[1] = NX;
+    gfits_modify (&buf[0].header, "NAXIS1", "%d", 1, NY);
+    gfits_modify (&buf[0].header, "NAXIS2", "%d", 1, NX);
+    gfits_print_alt (&buf[0].header, "HISTORY", "%S", 1, "WARNING: rotated image!");
+    gfits_create_matrix (&buf[0].header, &buf[0].matrix);
+    out_buff = (float *)buf[0].matrix.buffer;
+    for (i = NX - 1; i > -1; i--) {
+      for (j = 0; j < NY; j++, out_buff++) {
+	*out_buff = in_buff[i + j*NX];
+      }
+    }
+    /* fix reference pixel */
+    gfits_scan (&buf[0].header, "CRPIX1", "%lf", 1, &Xo);
+    gfits_scan (&buf[0].header, "CRPIX2", "%lf", 1, &Yo);
+    X1 = Yo;
+    Y1 = NX - Xo;
+    gfits_modify (&buf[0].header, "CRPIX1", "%lf", 1, X1);
+    gfits_modify (&buf[0].header, "CRPIX2", "%lf", 1, Y1);
+    
+    /* fix rotate matrix */
+    gfits_scan (&buf[0].header, "PC001001", "%lf", 1, &pc11);
+    gfits_scan (&buf[0].header, "PC001002", "%lf", 1, &pc12);
+    gfits_scan (&buf[0].header, "PC002001", "%lf", 1, &pc21);
+    gfits_scan (&buf[0].header, "PC002002", "%lf", 1, &pc22);
+    PC11 = pc21;
+    PC12 = pc22;
+    PC21 = -pc11;
+    PC22 = -pc12;
+    gfits_modify (&buf[0].header, "PC001001", "%le", 1, PC11);
+    gfits_modify (&buf[0].header, "PC001002", "%le", 1, PC12);
+    gfits_modify (&buf[0].header, "PC002001", "%le", 1, PC21);
+    gfits_modify (&buf[0].header, "PC002002", "%le", 1, PC22);
+
+    free (in_buff);
+    return (TRUE);
+  }
+
+  if (!strcasecmp (argv[2], "RIGHT") || (atof (argv[2]) == 90)) {
+    buf[0].header.Naxis[0] = NY;
+    buf[0].header.Naxis[1] = NX;
+    gfits_modify (&buf[0].header, "NAXIS1", "%d", 1, NY);
+    gfits_modify (&buf[0].header, "NAXIS2", "%d", 1, NX);
+    gfits_print_alt (&buf[0].header, "HISTORY", "%S", 1, "WARNING: rotated image!");
+    gfits_create_matrix (&buf[0].header, &buf[0].matrix);
+    out_buff = (float *)buf[0].matrix.buffer;
+    for (i = 0; i < NX; i++) {
+      for (j = NY - 1; j > -1; j--, out_buff++) {
+	*out_buff = in_buff[i + j*NX];
+      }
+    }
+    /* fix reference pixel */
+    gfits_scan (&buf[0].header, "CRPIX1", "%lf", 1, &Xo);
+    gfits_scan (&buf[0].header, "CRPIX2", "%lf", 1, &Yo);
+    X1 = NY - Yo;
+    Y1 = Xo;
+    gprint (GP_ERR, "%f %f -> %f %f\n", Xo, Yo, X1, Y1);
+    gfits_modify (&buf[0].header, "CRPIX1", "%lf", 1, X1);
+    gfits_modify (&buf[0].header, "CRPIX2", "%lf", 1, Y1);
+    
+    /* fix rotate matrix */
+    gfits_scan (&buf[0].header, "PC001001", "%lf", 1, &pc11);
+    gfits_scan (&buf[0].header, "PC001002", "%lf", 1, &pc12);
+    gfits_scan (&buf[0].header, "PC002001", "%lf", 1, &pc21);
+    gfits_scan (&buf[0].header, "PC002002", "%lf", 1, &pc22);
+    PC11 = -pc21;
+    PC12 = -pc22;
+    PC21 = pc11;
+    PC22 = pc12;
+    gprint (GP_ERR, "%f %f  ->  %f %f\n", pc11, pc12, PC11, PC12);
+    gprint (GP_ERR, "%f %f  ->  %f %f\n", pc21, pc22, PC21, PC22);
+    gfits_modify (&buf[0].header, "PC001001", "%le", 1, PC11);
+    gfits_modify (&buf[0].header, "PC001002", "%le", 1, PC12);
+    gfits_modify (&buf[0].header, "PC002001", "%le", 1, PC21);
+    gfits_modify (&buf[0].header, "PC002002", "%le", 1, PC22);
+
+    free (in_buff);
+    return (TRUE);
+  }
+
+  if (!strcasecmp (argv[2], "UPSIDE") || (atof (argv[2]) == -180) || (atof (argv[2]) == 180)) {
+    gfits_print_alt (&buf[0].header, "HISTORY", "%S", 1, "WARNING: rotated image!");
+    gfits_create_matrix (&buf[0].header, &buf[0].matrix);
+    out_buff = (float *)buf[0].matrix.buffer;
+    for (j = NY - 1; j > -1; j--) {
+      for (i = NX - 1; i > -1; i--, out_buff++) {
+	*out_buff = in_buff[i + j*NX];
+      }
+    }
+    /* fix reference pixel */
+    gfits_scan (&buf[0].header, "CRPIX1", "%lf", 1, &Xo);
+    gfits_scan (&buf[0].header, "CRPIX2", "%lf", 1, &Yo);
+    X1 = NX - Xo;
+    Y1 = NY - Yo;
+    gfits_modify (&buf[0].header, "CRPIX1", "%lf", 1, X1);
+    gfits_modify (&buf[0].header, "CRPIX2", "%lf", 1, Y1);
+    
+    /* fix rotate matrix */
+    gfits_scan (&buf[0].header, "PC001001", "%lf", 1, &pc11);
+    gfits_scan (&buf[0].header, "PC001002", "%lf", 1, &pc12);
+    gfits_scan (&buf[0].header, "PC002001", "%lf", 1, &pc21);
+    gfits_scan (&buf[0].header, "PC002002", "%lf", 1, &pc22);
+    PC11 = -pc11;
+    PC12 = -pc12;
+    PC21 = -pc21;
+    PC22 = -pc22;
+    gfits_modify (&buf[0].header, "PC001001", "%le", 1, PC11);
+    gfits_modify (&buf[0].header, "PC001002", "%le", 1, PC12);
+    gfits_modify (&buf[0].header, "PC002001", "%le", 1, PC21);
+    gfits_modify (&buf[0].header, "PC002002", "%le", 1, PC22);
+
+    free (in_buff);
+    return (TRUE);
+  }
+
+  if (!strcasecmp (argv[2], "FLIPY")) {
+    gfits_print_alt (&buf[0].header, "HISTORY", "%S", 1, "WARNING: rotated image!");
+    gfits_create_matrix (&buf[0].header, &buf[0].matrix);
+    out_buff = (float *)buf[0].matrix.buffer;
+    for (j = NY - 1; j > -1; j--) {
+      for (i = 0; i < NX; i++, out_buff++) {
+	*out_buff = in_buff[i + j*NX];
+      }
+    }
+    /* fix reference pixel */
+    gfits_scan (&buf[0].header, "CRPIX1", "%lf", 1, &Xo);
+    gfits_scan (&buf[0].header, "CRPIX2", "%lf", 1, &Yo);
+    X1 = Xo;
+    Y1 = NY - Yo;
+    gfits_modify (&buf[0].header, "CRPIX1", "%lf", 1, X1);
+    gfits_modify (&buf[0].header, "CRPIX2", "%lf", 1, Y1);
+    
+    /* fix rotate matrix */
+    gfits_scan (&buf[0].header, "PC001001", "%lf", 1, &pc11);
+    gfits_scan (&buf[0].header, "PC001002", "%lf", 1, &pc12);
+    gfits_scan (&buf[0].header, "PC002001", "%lf", 1, &pc21);
+    gfits_scan (&buf[0].header, "PC002002", "%lf", 1, &pc22);
+    PC11 = pc11;
+    PC12 = -pc12;
+    PC21 = pc21;
+    PC22 = -pc22;
+    gfits_modify (&buf[0].header, "PC001001", "%le", 1, PC11);
+    gfits_modify (&buf[0].header, "PC001002", "%le", 1, PC12);
+    gfits_modify (&buf[0].header, "PC002001", "%le", 1, PC21);
+    gfits_modify (&buf[0].header, "PC002002", "%le", 1, PC22);
+
+    free (in_buff);
+    return (TRUE);
+  }
+
+  if (!strcasecmp (argv[2], "FLIPX")) {
+    gfits_print_alt (&buf[0].header, "HISTORY", "%S", 1, "WARNING: rotated image!");
+    gfits_create_matrix (&buf[0].header, &buf[0].matrix);
+    out_buff = (float *)buf[0].matrix.buffer;
+    for (j = 0; j < NY; j++) {
+      for (i = NX - 1; i > -1; i--, out_buff++) {
+	*out_buff = in_buff[i + j*NX];
+      }
+    }
+    /* fix reference pixel */
+    gfits_scan (&buf[0].header, "CRPIX1", "%lf", 1, &Xo);
+    gfits_scan (&buf[0].header, "CRPIX2", "%lf", 1, &Yo);
+    X1 = NX - Xo;
+    Y1 = Yo;
+    gfits_modify (&buf[0].header, "CRPIX1", "%lf", 1, X1);
+    gfits_modify (&buf[0].header, "CRPIX2", "%lf", 1, Y1);
+    
+    /* fix rotate matrix */
+    gfits_scan (&buf[0].header, "PC001001", "%lf", 1, &pc11);
+    gfits_scan (&buf[0].header, "PC001002", "%lf", 1, &pc12);
+    gfits_scan (&buf[0].header, "PC002001", "%lf", 1, &pc21);
+    gfits_scan (&buf[0].header, "PC002002", "%lf", 1, &pc22);
+    PC11 = -pc11;
+    PC12 = pc12;
+    PC21 = -pc21;
+    PC22 = pc22;
+    gfits_modify (&buf[0].header, "PC001001", "%le", 1, PC11);
+    gfits_modify (&buf[0].header, "PC001002", "%le", 1, PC12);
+    gfits_modify (&buf[0].header, "PC002001", "%le", 1, PC21);
+    gfits_modify (&buf[0].header, "PC002002", "%le", 1, PC22);
+
+    free (in_buff);
+    return (TRUE);
+  }
+
+  angle = atof (argv[2]);
+  CosAngle = cos (angle*RAD_DEG);
+  SinAngle = sin (angle*RAD_DEG);
+  
+  gprint (GP_ERR, "rotating: %f %f %f\n", angle, CosAngle, SinAngle);
+
+  Lx = NX*fabs(CosAngle) + NY*fabs(SinAngle);
+  Ly = NX*fabs(SinAngle) + NY*fabs(CosAngle);
+  dX = MAX(0,NY*SinAngle);
+  dY = MAX(0,-NX*SinAngle);
+  /*
+  gprint (GP_ERR, "%f %f  -->  ", Xo, Yo);
+  X1 = Xo*CosAngle - Yo*SinAngle + dX;
+  Y1 =  Xo*SinAngle + Yo*CosAngle + dY;
+  gprint (GP_ERR, "%f %f\n", X1, Y1);
+  */
+
+  /* fix reference pixel */
+  gfits_scan (&buf[0].header, "CRPIX1", "%lf", 1, &Xo);
+  gfits_scan (&buf[0].header, "CRPIX2", "%lf", 1, &Yo);
+  /*
+  X1 = (Xo - dX)*CosAngle - (Yo - dY)*SinAngle;
+  Y1 = (dX - Xo)*SinAngle + (Yo - dY)*CosAngle;
+  */
+  X1 = Xo*CosAngle - Yo*SinAngle + dX;
+  Y1 =  Xo*SinAngle + Yo*CosAngle + dY;
+  gfits_modify (&buf[0].header, "CRPIX1", "%lf", 1, X1);
+  gfits_modify (&buf[0].header, "CRPIX2", "%lf", 1, Y1);
+
+  /* fix rotate matrix */
+  gfits_scan (&buf[0].header, "PC001001", "%lf", 1, &pc11);
+  gfits_scan (&buf[0].header, "PC001002", "%lf", 1, &pc12);
+  gfits_scan (&buf[0].header, "PC002001", "%lf", 1, &pc21);
+  gfits_scan (&buf[0].header, "PC002002", "%lf", 1, &pc22);
+  PC11 = pc11*CosAngle - pc21*SinAngle;
+  PC12 = pc12*CosAngle - pc22*SinAngle;
+  PC21 = pc21*CosAngle + pc11*SinAngle;
+  PC22 = pc22*CosAngle + pc12*SinAngle;
+  gfits_modify (&buf[0].header, "PC001001", "%le", 1, PC11);
+  gfits_modify (&buf[0].header, "PC001002", "%le", 1, PC12);
+  gfits_modify (&buf[0].header, "PC002001", "%le", 1, PC21);
+  gfits_modify (&buf[0].header, "PC002002", "%le", 1, PC22);
+
+  buf[0].header.Naxis[0] = Lx;
+  buf[0].header.Naxis[1] = Ly;
+  gfits_modify (&buf[0].header, "NAXIS1", "%d", 1, Lx);
+  gfits_modify (&buf[0].header, "NAXIS2", "%d", 1, Ly);
+  gfits_create_matrix (&buf[0].header, &buf[0].matrix);
+  gfits_print_alt (&buf[0].header, "HISTORY", "%S", 1, "WARNING: rotated image!");
+  out_buff = (float *)buf[0].matrix.buffer;
+  for (j = 0; j < Ly; j++) {
+    for (i = 0; i < Lx; i++, out_buff++) {
+
+      x = (i - dX)*CosAngle + (j - dY)*SinAngle;
+      y = (dX - i)*SinAngle + (j - dY)*CosAngle;
+      X = (int) x;
+      Y = (int) y;
+
+      if (X < 0) continue;
+      if (X >= NX - 1) continue;
+      if (Y < 0) continue;
+      if (Y >= NY - 1) continue;
+
+      c = &in_buff[X + NX*Y];
+      fx = x - X;
+      fy = y - Y;
+      *out_buff = (c[0]*(1-fx) + c[1]*fx)*(1-fy) + (c[NX+1]*fx + c[NX]*(1-fx))*fy;
+    }
+  }
+  free (in_buff);
+  return (TRUE);
+
+}
Index: /branches/sc_branches/pantasks_condor/cmd.data/save.c
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.data/save.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.data/save.c	(revision 34783)
@@ -0,0 +1,32 @@
+# include "data.h"
+
+int save (int argc, char **argv) {
+  
+  int N, celestial;
+  int kapa;
+  char *name;
+  
+  name = NULL;
+  if ((N = get_argument (argc, argv, "-n"))) {
+    remove_argument (N, &argc, argv);
+    name = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+  if (!GetImage (NULL, &kapa, name)) return (FALSE);
+  FREE (name);
+
+  celestial = FALSE;
+  if ((N = get_argument (argc, argv, "-c"))) {
+    remove_argument (N, &argc, argv);
+    celestial = TRUE;
+  }
+
+  if (argc != 3) {
+    gprint (GP_ERR, "USAGE: save (overlay) <filename> [-c]\n");
+    gprint (GP_ERR, "  -c: write contour in celestial coords\n");
+    return (FALSE);
+  }
+
+  KiiSaveOverlay (kapa, celestial, argv[1], argv[2]);
+  return (TRUE);
+}
Index: /branches/sc_branches/pantasks_condor/cmd.data/section.c
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.data/section.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.data/section.c	(revision 34783)
@@ -0,0 +1,175 @@
+# include "data.h"
+
+enum {NONE, LIST, UP, DOWN, TOP, BOTTOM, TOOL, BG, IMAGE};
+
+int section (int argc, char **argv) {
+  
+  int N, action, kapa, background;
+  Graphdata graphmode;
+  KapaSection section;
+  char *name = NULL;
+  char *location = NULL;
+
+  action = NONE;
+  if ((N = get_argument (argc, argv, "-list"))) {
+    action = LIST;
+    remove_argument (N, &argc, argv);
+  }
+  if ((N = get_argument (argc, argv, "-up"))) {
+    action = UP;
+    remove_argument (N, &argc, argv);
+  }
+  if ((N = get_argument (argc, argv, "-down"))) {
+    action = DOWN;
+    remove_argument (N, &argc, argv);
+  }
+  if ((N = get_argument (argc, argv, "-top"))) {
+    action = TOP;
+    remove_argument (N, &argc, argv);
+  }
+  if ((N = get_argument (argc, argv, "-bottom"))) {
+    action = BOTTOM;
+    remove_argument (N, &argc, argv);
+  }
+  if ((N = get_argument (argc, argv, "-imtool"))) {
+    action = TOOL;
+    remove_argument (N, &argc, argv);
+    location = argv[N];
+    remove_argument (N, &argc, argv);
+  }
+
+  background = -1; // invisible
+  if ((N = get_argument (argc, argv, "-bg"))) {
+    remove_argument (N, &argc, argv);
+    if (!strcasecmp (argv[N], "NONE")) {
+      background = -1;
+    } else {
+      background = KapaColorByName(argv[N]);
+    }
+    remove_argument (N, &argc, argv);
+    action = BG;
+  }
+
+  if ((N = get_argument (argc, argv, "-image"))) {
+    remove_argument (N, &argc, argv);
+    action = IMAGE;
+  }
+
+  name = NULL;
+  if ((N = get_argument (argc, argv, "-n"))) {
+    remove_argument (N, &argc, argv);
+    name = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+  if (!GetGraph (&graphmode, &kapa, name)) return (FALSE);
+  FREE (name);
+
+  /* list sections */
+  if ((argc == 1) && (action == NONE)) {
+    KapaGetSection (kapa, "*");
+    gprint (GP_ERR, "USAGE: section name [x y dx dy] or [options]\n");
+    gprint (GP_ERR, "OPTIONS: -list   : show properties of all sections\n");
+    gprint (GP_ERR, "         -up     : move section up in display stack\n");
+    gprint (GP_ERR, "         -down   : move section down in display stack\n");
+    gprint (GP_ERR, "         -top    : move section to top of display stack\n");
+    gprint (GP_ERR, "         -bottom : move section to bottom of display stack\n");
+    gprint (GP_ERR, "         -imtool (position) : set location of image zoom / status box\n");
+    gprint (GP_ERR, "                 (position may be: -x, +x, -y, +y, none)\n");
+    gprint (GP_ERR, "         -image  : define section upper-right corner so image fills the section\n");
+    gprint (GP_ERR, "         -bg (color) : set background color (see style -c help for color choices)\n");
+    
+    return (TRUE);
+  } 
+  
+  if ((argc != 2) && (action != NONE)) {
+      gprint (GP_ERR, "can only use dash options without numbers\n");
+      return (FALSE);
+  }
+
+  if (argc == 2) {
+    /* select / show section */
+    switch (action) {
+      case NONE:
+	KapaSelectSection (kapa, argv[1]);
+	break;
+
+      case UP:
+	KapaMoveSection (kapa, argv[1], "up");
+	break;
+      case DOWN:
+	KapaMoveSection (kapa, argv[1], "down");
+	break;
+      case TOP:
+	KapaMoveSection (kapa, argv[1], "top");
+	break;
+      case BOTTOM:
+	KapaMoveSection (kapa, argv[1], "bottom");
+	break;
+
+      case BG:
+	KapaSectionBG (kapa, argv[1], background);
+	break;
+
+      case TOOL:
+	if (!strcmp(location, "-x")) {
+	  KapaSetToolbox (kapa, 1);
+	  break;
+	}
+	if (!strcmp(location, "+x")) {
+	  KapaSetToolbox (kapa, 3);
+	  break;
+	}
+	if (!strcmp(location, "-y")) {
+	  KapaSetToolbox (kapa, 2);
+	  break;
+	}
+	if (!strcmp(location, "+y")) {
+	  KapaSetToolbox (kapa, 4);
+	  break;
+	}
+	if (!strcmp(location, "none")) {
+	  KapaSetToolbox (kapa, 0);
+	  break;
+	}
+	gprint (GP_ERR, "unknown toolbox location %s\n", location);
+	gprint (GP_ERR, "valid values: -x, +x, -y, +y, none\n");
+	return (FALSE);
+
+      case LIST:
+	KapaGetSection (kapa, argv[1]);
+	break;
+    }
+    return (TRUE);
+  } 
+  
+  if (argc == 4) {
+    /* set section */
+    section.name = argv[1];
+    section.x = atof (argv[2]);
+    section.y = atof (argv[3]);
+    section.bg = background;
+    KapaSetSectionByImage (kapa, &section);
+    return (TRUE);
+  }
+
+  if (argc == 6) {
+    /* set section */
+    section.name = argv[1];
+    section.x = atof (argv[2]);
+    section.y = atof (argv[3]);
+    section.dx = atof (argv[4]);
+    section.dy = atof (argv[5]);
+    section.bg = background;
+    KapaSetSection (kapa, &section);
+    return (TRUE);
+  }
+  gprint (GP_ERR, "USAGE: section name [x y dx dy]\n");
+  gprint (GP_ERR, "USAGE: section name [-image x y] : width based on current image\n");
+  gprint (GP_ERR, "USAGE: section name [-list] [-up] [-down] [-top] [-bottom]\n");
+  return (FALSE);
+}
+
+/* should do some range checking on x y dx dy
+   should be between 0.0 and 1.0, precision of 0.001
+   is sufficient
+*/
Index: /branches/sc_branches/pantasks_condor/cmd.data/select.c
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.data/select.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.data/select.c	(revision 34783)
@@ -0,0 +1,87 @@
+# include "data.h"
+
+int vect_select (int argc, char **argv) {
+  
+  int  i, size;
+  char *out;
+  Vector *in1, *in2, *tvec, *ovec;
+
+  out = NULL;
+  in1 = in2 = ovec = tvec = NULL;
+
+  /** check basic syntax **/
+  if ((argc != 8) || strcmp(argv[2], "=") || strcmp (argv[4], "if") || strcmp (argv[6], "else")) {
+    gprint (GP_ERR, "SYNTAX: select vec = vec if (logic expression) else vec\n");
+    return (FALSE);
+  }
+  if ((in1  = SelectVector (argv[3], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((in2  = SelectVector (argv[7], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((ovec = SelectVector (argv[1], ANYVECTOR, TRUE)) == NULL) return (FALSE);
+
+  out = dvomath (argc - 5, &argv[5], &size, 1);
+  if (out == NULL) {
+    print_error ();
+    goto error;
+  }
+  if ((tvec = SelectVector (out, OLDVECTOR, TRUE)) == NULL) goto error;
+  /* check size of in1, in2, tvec: must match */
+
+  if ((in1->type == OPIHI_INT) && (in2->type == OPIHI_INT)) {
+    ResetVector (ovec, OPIHI_INT, tvec[0].Nelements);
+  } else {
+    ResetVector (ovec, OPIHI_FLT, tvec[0].Nelements);
+  }
+
+  // (in1 and in2) == (flt or int) and tvec == (flt or int)
+  if ((in1->type == OPIHI_FLT) && (in2->type == OPIHI_FLT) && (tvec->type == OPIHI_FLT)) {
+    for (i = 0; i < tvec[0].Nelements; i++) {
+      ovec[0].elements.Flt[i] = tvec[0].elements.Flt[i] ? in1[0].elements.Flt[i] : in2[0].elements.Flt[i];
+    }
+  }
+  if ((in1->type == OPIHI_FLT) && (in2->type == OPIHI_FLT) && (tvec->type == OPIHI_INT)) {
+    for (i = 0; i < tvec[0].Nelements; i++) {
+      ovec[0].elements.Flt[i] = tvec[0].elements.Int[i] ? in1[0].elements.Flt[i] : in2[0].elements.Flt[i];
+    }
+  }
+  if ((in1->type == OPIHI_INT) && (in2->type == OPIHI_FLT) && (tvec->type == OPIHI_FLT)) {
+    for (i = 0; i < tvec[0].Nelements; i++) {
+      ovec[0].elements.Flt[i] = tvec[0].elements.Flt[i] ? in1[0].elements.Int[i] : in2[0].elements.Flt[i];
+    }
+  }
+  if ((in1->type == OPIHI_INT) && (in2->type == OPIHI_FLT) && (tvec->type == OPIHI_INT)) {
+    for (i = 0; i < tvec[0].Nelements; i++) {
+      ovec[0].elements.Flt[i] = tvec[0].elements.Int[i] ? in1[0].elements.Int[i] : in2[0].elements.Flt[i];
+    }
+  }
+  if ((in1->type == OPIHI_FLT) && (in2->type == OPIHI_INT) && (tvec->type == OPIHI_FLT)) {
+    for (i = 0; i < tvec[0].Nelements; i++) {
+      ovec[0].elements.Flt[i] = tvec[0].elements.Flt[i] ? in1[0].elements.Flt[i] : in2[0].elements.Int[i];
+    }
+  }
+  if ((in1->type == OPIHI_FLT) && (in2->type == OPIHI_INT) && (tvec->type == OPIHI_INT)) {
+    for (i = 0; i < tvec[0].Nelements; i++) {
+      ovec[0].elements.Flt[i] = tvec[0].elements.Int[i] ? in1[0].elements.Flt[i] : in2[0].elements.Int[i];
+    }
+  }
+  if ((in1->type == OPIHI_INT) && (in2->type == OPIHI_INT) && (tvec->type == OPIHI_FLT)) {
+    for (i = 0; i < tvec[0].Nelements; i++) {
+      ovec[0].elements.Int[i] = tvec[0].elements.Flt[i] ? in1[0].elements.Int[i] : in2[0].elements.Int[i];
+    }
+  }
+  if ((in1->type == OPIHI_INT) && (in2->type == OPIHI_INT) && (tvec->type == OPIHI_INT)) {
+    for (i = 0; i < tvec[0].Nelements; i++) {
+      ovec[0].elements.Int[i] = tvec[0].elements.Int[i] ? in1[0].elements.Int[i] : in2[0].elements.Int[i];
+    }
+  }
+  
+  DeleteVector (tvec);
+  free (out);
+  return (TRUE);
+
+ error:
+  DeleteVector (tvec);
+  DeleteVector (ovec);
+  DeleteNamedVector (out);
+  free (out);
+  return (FALSE);
+}
Index: /branches/sc_branches/pantasks_condor/cmd.data/set.c
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.data/set.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.data/set.c	(revision 34783)
@@ -0,0 +1,47 @@
+# include "data.h"
+
+int set (int argc, char **argv) {
+  
+  int size;
+  char *out;
+
+  /** check basic form for line **/
+  if ((argc < 3) || strcmp(argv[2], "=")) {
+    gprint (GP_ERR, "%s = (matrix expression)\n", argv[0]);
+    return (FALSE);
+  }
+
+  out = dvomath (argc - 3, &argv[3], &size, -1);
+  if (out == NULL) {
+    print_error ();
+    return (FALSE);
+  }
+  
+  switch (size) {
+    case 0:
+      set_str_variable (argv[1], out);
+      free (out);
+      break;
+
+    case 1:
+      if (!MoveNamedVector (argv[1], out)) {
+	DeleteNamedVector (out);
+	free (out);
+	gprint (GP_ERR, "invalid output vector name\n");
+	return (FALSE);
+      }
+      free (out);
+      break;
+  
+    case 2:
+      if (!MoveNamedBuffer (argv[1], out)) {
+	DeleteNamedBuffer (out);
+	free (out);
+	gprint (GP_ERR, "invalid output matrix name\n");
+	return (FALSE);
+      }
+      free (out);
+      break;
+  }
+  return (TRUE);
+}
Index: /branches/sc_branches/pantasks_condor/cmd.data/shift.c
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.data/shift.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.data/shift.c	(revision 34783)
@@ -0,0 +1,110 @@
+# include "data.h"
+
+int shift (int argc, char **argv) {
+ 
+  int i, j;
+  int nx, ny, dx, dy, DXin, DXot, DYin, DYot;
+  float *Vin, *Vot;
+  double Dx, Dy, fdx, fdy;
+  Buffer *in, *out;
+
+  // int ROLL = FALSE;
+  // if ((N = get_argument (argc, argv, "-roll"))) {
+  //   remove_argument (N, &argc, argv);
+  //   ROLL = TRUE;
+  // }
+
+  if (argc != 5) {
+    gprint (GP_ERR, "USAGE: shift (input) (output) dx dy\n");
+    return (FALSE);
+  }
+
+  // define the input buffer and examine the shift
+  if ((in  = SelectBuffer (argv[1], OLDBUFFER, TRUE)) == NULL) return (FALSE);
+
+  Dx = atof (argv[3]);
+  Dy = atof (argv[4]);
+
+  dx = Dx;
+  dy = Dy;
+  fdx = Dx - dx;
+  fdy = Dy - dy;
+  if (fdx < -0.000001) {dx -= 1; fdx += 1;}
+  if (fdy < -0.000001) {dy -= 1; fdy += 1;}
+  // we always specify a positive fractional shift
+  // the above choice defines a minimum fractional shift of 1e-5
+
+  nx = in[0].matrix.Naxis[0];
+  ny = in[0].matrix.Naxis[1];
+
+  if ((dx > nx) || (dy > ny)) {
+    gprint (GP_ERR, "shifting data out of image\n");
+    return (FALSE);
+  }
+  
+  // define the output buffer
+  if ((out = SelectBuffer (argv[2], ANYBUFFER, TRUE)) == NULL) return (FALSE);
+
+  gfits_free_matrix (&out[0].matrix);
+  gfits_free_header (&out[0].header);
+  CreateBuffer (out, nx, ny, -32, 0.0, 1.0);
+
+  DXin = (dx < 0) ? -dx : 0;
+  DXot = (dx < 0) ?   0 : dx;
+  DYin = (dy < 0) ? -dy : 0;
+  DYot = (dy < 0) ?   0 : dy;
+  
+  for (j = 0; j < ny - abs(dy); j++) {
+    Vin = (float *)(in[0].matrix.buffer)  + (j + DYin)*nx + DXin;  
+    Vot = (float *)(out[0].matrix.buffer) + (j + DYot)*nx + DXot; 
+    for (i = 0; i < nx - abs(dx); i++, Vin++, Vot++) {
+      *Vot = *Vin;
+    }
+    // fill in the exposed x-border with 0.0
+    Vot = (dx > 0) ? 
+      (float *)(out[0].matrix.buffer) + (j + DYot)*nx : 
+      (float *)(out[0].matrix.buffer) + (j + DYot)*nx + nx - abs(dx);
+    for (i = 0; i < abs(dx); i++, Vot++) {
+      *Vot = 0.0;
+    }	
+  }
+
+  // fill in the exposed y-border with 0.0
+  Vot = (dy > 0) ? 
+    (float *)(out[0].matrix.buffer) :
+    (float *)(out[0].matrix.buffer) + (ny - abs(dy))*nx;
+
+  for (j = 0; j < nx * abs(dy); j++, Vot++) {
+    *Vot = 0.0;
+  }   
+
+  // apply the fractional shift 
+  gprint (GP_ERR, "%f %f\n", fdx, fdy);
+  if ((fdx > 0) || (fdy > 0)) {
+    double f00, f01, f10, f11;
+    float value;
+
+    f00 = (1-fdx)*(1-fdy);
+    f01 =    fdx *(1-fdy);
+    f10 = (1-fdx)*   fdy;
+    f11 =    fdx *   fdy;
+
+    Vin = (float *)out[0].matrix.buffer;
+    Vot = (float *)out[0].matrix.buffer;
+    for (j = 0; j < ny - 1; j++) {
+      for (i = 0; i < nx - 1; i++, Vin++, Vot++) {
+	value  = Vin[   0] * f00;
+	value += Vin[   1] * f01;
+	value += Vin[nx  ] * f10;
+	value += Vin[nx+1] * f11;
+	*Vot = value;
+      }
+      Vin ++;
+      Vot ++;
+    }
+  }   
+
+  return (TRUE);
+
+}
+
Index: /branches/sc_branches/pantasks_condor/cmd.data/sort.c
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.data/sort.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.data/sort.c	(revision 34783)
@@ -0,0 +1,114 @@
+# include "data.h"
+
+void fsortindex (opihi_flt *X, int *IDX, int N) {
+
+# define SWAPFUNC(A,B){ opihi_flt tmp; int itmp; 	\
+  tmp = X[A]; X[A] = X[B]; X[B] = tmp; \
+  itmp = IDX[A]; IDX[A] = IDX[B]; IDX[B] = itmp; \
+}
+# define COMPARE(A,B)(X[A] < X[B])
+
+  OHANA_SORT (N, COMPARE, SWAPFUNC);
+
+# undef SWAPFUNC
+# undef COMPARE
+
+}
+
+void isortindex (opihi_int *X, int *IDX, int N) {
+
+# define SWAPFUNC(A,B){ opihi_int tmp; int itmp; 	\
+  tmp = X[A]; X[A] = X[B]; X[B] = tmp; \
+  itmp = IDX[A]; IDX[A] = IDX[B]; IDX[B] = itmp; \
+}
+# define COMPARE(A,B)(X[A] < X[B])
+
+  OHANA_SORT (N, COMPARE, SWAPFUNC);
+
+# undef SWAPFUNC
+# undef COMPARE
+
+}
+
+// XXX add an option to NOT sort, but return an index instead?
+int sort_vectors (int argc, char **argv) {
+  
+  int i, j, Nvec, Nval;
+  opihi_flt *ftemp;
+  opihi_int *itemp;
+  int *index, *I;
+  Vector **vec;
+
+  if (argc < 2) {
+    gprint (GP_ERR, "USAGE: sort (vector) [vectors ...] \n");
+    gprint (GP_ERR, "  first vector is sort key for others\n");
+    return (FALSE);
+  }
+
+  Nvec = (argc - 1);
+  ALLOCATE (vec, Vector *, Nvec);
+
+  Nval = 0;
+  /* find vectors and check sizes */
+  for (i = 0; i < Nvec; i++) {
+    if ((vec[i] = SelectVector (argv[i + 1], OLDVECTOR, FALSE)) == NULL) {
+      gprint (GP_ERR, "USAGE: sort vector vector ...\n");
+      free (vec);
+      return (FALSE);    
+    }
+    if (i == 0) {
+      Nval = vec[i][0].Nelements;
+    } else {
+      if (Nval != vec[i][0].Nelements) {
+	free (vec);
+	gprint (GP_ERR, "vectors must all be same length\n");
+	return (FALSE);
+      }
+    }
+  }
+  
+  /* create index */
+  ALLOCATE (index, int, Nval);
+  for (i = 0; i < Nval; i++) index[i] = i;
+
+  /* sort key & index */
+  if (vec[0][0].type == OPIHI_FLT) {
+    fsortindex (vec[0][0].elements.Flt, index, Nval);
+  } else {
+    isortindex (vec[0][0].elements.Int, index, Nval);
+  }
+
+  ALLOCATE (ftemp, opihi_flt, Nval);
+  ALLOCATE (itemp, opihi_int, Nval);
+
+  for (i = 1; i < Nvec; i++) {
+    if (vec[i][0].type == OPIHI_FLT) {
+      opihi_flt *T = ftemp;
+      opihi_flt *V = vec[i][0].elements.Flt;
+      I = index;
+      for (j = 0; j < Nval; j++, T++, I++) {
+	*T = V[*I];
+      }
+      /* swap .elements.Flt (== V) and ftemp */ 
+      vec[i][0].elements.Flt = ftemp;
+      ftemp = V;
+    } else {
+      opihi_int *T = itemp;
+      opihi_int *V = vec[i][0].elements.Int;
+      I = index;
+      for (j = 0; j < Nval; j++, T++, I++) {
+	*T = V[*I];
+      }
+      /* swap .elements.Flt (== V) and itemp */ 
+      vec[i][0].elements.Int = itemp;
+      itemp = V;
+    }
+  }
+  free (itemp);
+  free (ftemp);
+  free (vec);
+  free (index);
+
+  return (TRUE);
+
+}
Index: /branches/sc_branches/pantasks_condor/cmd.data/spline.c
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.data/spline.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.data/spline.c	(revision 34783)
@@ -0,0 +1,54 @@
+# include "data.h"
+
+int spline_list (int argc, char **argv);
+int spline_create (int argc, char **argv);
+int spline_apply (int argc, char **argv);
+int spline_load (int argc, char **argv);
+int spline_save (int argc, char **argv);
+int spline_delete (int argc, char **argv);
+int spline_getspline (int argc, char **argv);
+
+static Command spline_commands[] = {
+  {1, "list",       spline_list,       "list splines"},
+  {1, "create",     spline_create,     "create a spline"},
+  {1, "apply",      spline_apply,      "apply a spline"},
+  {1, "load",       spline_load,       "write a spline to a FITS file"},
+  {1, "save",       spline_save,       "read a spline from a FITS file"},
+  {1, "delete",     spline_delete,     "delete a spline"},
+};
+
+int spline_command (int argc, char **argv) {
+
+  int i, N, status;
+
+  if (argc < 2) {
+    gprint (GP_ERR, "USAGE: spline (command)\n");
+    gprint (GP_ERR, "    spline list                                    : list splines\n");
+    gprint (GP_ERR, "    spline create   (spline)                       : create a spline\n");
+    gprint (GP_ERR, "    spline delete   (spline)                       : delete a spline\n");
+    return (FALSE);
+  }
+
+  N = sizeof (spline_commands) / sizeof (Command);
+
+  /* find the spline sub-command which matches */
+  for (i = 0; i < N; i++) {
+    if (!strcmp (spline_commands[i].name, argv[1])) {
+      status = (*spline_commands[i].func) (argc - 1, argv + 1);
+      return (status);
+    }
+  }
+
+  gprint (GP_ERR, "unknown spline command %s\n", argv[1]);
+  return (FALSE);
+}
+
+/* spline is called with the command "spline".  
+   the command line word "spline" is meant to be followed the one of several 
+   possible options:
+   
+   spline create
+   spline delete
+   spline list
+
+*/
Index: /branches/sc_branches/pantasks_condor/cmd.data/spline_commands.c
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.data/spline_commands.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.data/spline_commands.c	(revision 34783)
@@ -0,0 +1,148 @@
+# include "data.h"
+
+int spline_list (int argc, char **argv) {
+  if (argc != 1) {
+    gprint (GP_ERR, "USAGE: spline list\n");
+    return FALSE;
+  }
+
+  ListSplines();
+  return TRUE;
+}
+
+int spline_create (int argc, char **argv) {
+
+  int i;
+  Vector *xvec, *yvec;
+
+  if (argc != 4) {
+    gprint (GP_ERR, "USAGE: spline create (name) (Xknots) (Yknots)\n");
+    return FALSE;
+  }
+
+  if ((xvec = SelectVector (argv[2], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((yvec = SelectVector (argv[3], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+
+  if (xvec->Nelements != yvec->Nelements) {
+    gprint (GP_ERR, "x and y vectors must be the same length\n");
+    return FALSE;
+  }
+
+  Spline *myspline = CreateSpline (argv[1], xvec->Nelements);
+
+  for (i = 0; i < xvec->Nelements; i++) {
+    myspline->xk[i] = xvec->elements.Flt[i];
+    myspline->yk[i] = yvec->elements.Flt[i];
+  }    
+
+  spline_construct_dbl (myspline->xk, myspline->yk, myspline->Nknots, myspline->y2);
+  return TRUE;
+}
+
+int spline_apply (int argc, char **argv) {
+
+  int i;
+  Vector *xvec, *yvec;
+
+  if (argc != 4) {
+    gprint (GP_ERR, "USAGE: spline apply (name) (Xpoints) (Ypoints)\n");
+    return FALSE;
+  }
+
+  Spline *myspline = FindSpline (argv[1]);
+  if (!myspline) {
+    gprint (GP_ERR, "spline %s not found\n", argv[1]);
+    return (FALSE);
+  }
+
+  if ((xvec = SelectVector (argv[2], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((yvec = SelectVector (argv[3], ANYVECTOR, TRUE)) == NULL) return (FALSE);
+
+  ResetVector (yvec, OPIHI_FLT, xvec->Nelements);
+
+  for (i = 0; i < xvec->Nelements; i++) {
+    opihi_flt value = spline_apply_dbl (myspline->xk, myspline->yk, myspline->y2, myspline->Nknots, xvec->elements.Flt[i]);
+    yvec->elements.Flt[i] = value;
+  }    
+
+  return TRUE;
+}
+
+int spline_save (int argc, char **argv) {
+
+  int N;
+
+  int APPEND = FALSE;
+  if ((N = get_argument (argc, argv, "-append"))) {
+    APPEND = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+
+  if (argc != 3) {
+    gprint (GP_ERR, "USAGE: spline save (name) (filename) [-append]\n");
+    return FALSE;
+  }
+
+  if (!SaveSpline(argv[2], argv[1], APPEND)) {
+    gprint (GP_ERR, "failed to save spline %s\n", argv[1]);
+    return (FALSE);
+  }
+  return TRUE;
+}
+
+int spline_load (int argc, char **argv) {
+
+  if (argc != 3) {
+    gprint (GP_ERR, "USAGE: spline load (name) (filename)\n");
+    return FALSE;
+  }
+
+  if (!LoadSpline(argv[2], argv[1])) {
+    gprint (GP_ERR, "failed to load spline %s\n", argv[1]);
+    return (FALSE);
+  }
+  return TRUE;
+}
+
+int spline_delete (int argc, char **argv) {
+
+  int status;
+  Spline *spline;
+
+  if (argc != 2) {
+    gprint (GP_ERR, "USAGE: spline delete (spline)\n");
+    return FALSE;
+  }
+
+  spline = FindSpline (argv[1]);
+  if (spline == NULL) {
+    gprint (GP_ERR, "spline %s not found\n", argv[1]);
+    return FALSE;
+  }
+
+  status = DeleteSpline (spline);
+  if (!status) abort ();
+  return TRUE;
+}
+
+/* 
+int spline_listspline (int argc, char **argv) {
+
+  Spline *spline;
+
+  if (argc != 2) {
+    gprint (GP_ERR, "USAGE: spline listspline (spline)\n");
+    return FALSE;
+  }
+
+  spline = FindSpline (argv[1]);
+  if (spline == NULL) {
+    gprint (GP_ERR, "spline %s not found\n", argv[1]);
+    return FALSE;
+  }
+
+  ListPages (spline);
+  return TRUE;
+}
+*/
+
Index: /branches/sc_branches/pantasks_condor/cmd.data/stats-new.c
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.data/stats-new.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.data/stats-new.c	(revision 34783)
@@ -0,0 +1,259 @@
+# include "data.h"
+
+double interpolateValue (int *hist, int Nbins, float min, float binsize, int bin, float value);
+double sampleMedian (Matrix *matrix, int Nsubset);
+Stats *robustMedian (Matrix *matrix, Region *region, float minValue, float maxValue, float sigma);
+
+typedef struct {
+  int sx;
+  int sy;
+  int nx;
+  int ny;
+} Region;
+
+typedef struct {
+  float median;
+  float mean;
+  float mode;
+  float sigma;
+} Stats;
+
+int stats (int argc, char **argv) {
+  
+  int i, j, Nmode, Imode;
+  double Npix, N1, N2, max, min, range, median, mode, IgnoreValue;
+  float *V;
+  int sx, sy, nx, ny, *hist, Nhist, bin;
+  int Ignore, Quiet, N;
+  Buffer *buf;
+
+  IgnoreValue = 0;
+  Ignore = FALSE;
+  if ((N = get_argument (argc, argv, "-ignore"))) {
+    Ignore = TRUE;
+    remove_argument (N, &argc, argv);
+    IgnoreValue = atof(argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  Quiet = FALSE;
+  if ((N = get_argument (argc, argv, "-q"))) {
+    Quiet = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+  if ((N = get_argument (argc, argv, "-quiet"))) {
+    Quiet = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+
+  if ((argc != 2) && (argc != 6)) {
+    gprint (GP_ERR, "USAGE: stats <buffer> sx sy nx ny\n");
+    gprint (GP_ERR, "OR:    stats <buffer>\n");
+    return (FALSE);
+  }
+
+  if ((buf = SelectBuffer (argv[1], OLDBUFFER, TRUE)) == NULL) return (FALSE);
+
+  if (argc == 6) {
+    sx = strcmp (argv[2], "-") ? atof (argv[2]) : 0;
+    sy = strcmp (argv[3], "-") ? atof (argv[3]) : 0;
+    nx = strcmp (argv[4], "-") ? atof (argv[4]) : buf[0].matrix.Naxis[0];
+    ny = strcmp (argv[5], "-") ? atof (argv[5]) : buf[0].matrix.Naxis[1];
+  } else {
+    sx = 0;
+    sy = 0;
+    nx = buf[0].matrix.Naxis[0];
+    ny = buf[0].matrix.Naxis[1];
+  }
+
+  Npix = N1 = N2 = 0;
+  if ((sx < 0) || (sy < 0) || 
+      (sx+nx > buf[0].matrix.Naxis[0]) || 
+      (sy+ny > buf[0].matrix.Naxis[1])) {
+    gprint (GP_ERR, "region out of range\n");
+    return (FALSE);
+  }
+
+  max = min = *((float *)(buf[0].matrix.buffer) + sy*buf[0].matrix.Naxis[0] + sx);
+  for (j = sy; j < sy + ny; j++) {
+    V = (float *)(buf[0].matrix.buffer) + j*buf[0].matrix.Naxis[0] + sx; 
+    for (i = 0; i < nx; i++, V++) {
+      if (Ignore && (fabs (*V - IgnoreValue) < 1e-8)) continue;
+      N1 += *V;
+      N2 += (*V)*(*V);
+      Npix += 1.0;
+      max = MAX (max, *V);
+      min = MIN (min, *V);
+    }
+  }
+  N1 = N1 / Npix;
+
+  sigma = sqrt (N2/Npix - N1*N1);
+
+  if (ROBUST) {
+    stats = robustMedian (matrix, min, max, sigma);
+    if (stats->sigma 
+  } 
+  
+  if (!Quiet) {
+    gprint (GP_LOG, "  mean    stdev    min     max   median   Npix   Total\n");
+    gprint (GP_LOG, "%7.4g %7.4g %7.4g %7.4g %7.4g %7.0f %7.4g\n", N1, sqrt (N2/Npix - N1*N1), 
+	    min, max, median, Npix, Npix*N1);
+  }
+
+  set_variable ("MIN",    min);
+  set_variable ("MAX",    max);
+  set_variable ("MEDIAN", median);
+  set_variable ("MEAN",   N1);
+  set_variable ("MODE",   mode);
+  set_variable ("TOTAL",  N1*Npix);
+  set_variable ("NPIX",   Npix);
+  set_variable ("SIGMA",  sqrt (N2/Npix - N1*N1));
+
+  return (TRUE);
+}
+
+double sampleMedian (Matrix *matrix, int Nsubset) {
+
+  int i, Nsample, Npix;
+  long A, B;
+  float *values, *buffer, median;
+
+  /* Generate a vector of Nsample elements */
+  Npix = matrix[0].Naxis[0]*matrix[0].Naxis[1]
+    Nsample = Nsubset;
+  if (Nsubset == 0) {
+    Nsample = Npix;
+  }
+
+  ALLOCATE (values, float, Nsample);
+    
+  A = time(NULL);
+  for (B = 0; A == time(NULL); B++);
+  srand48(B);
+ 
+  *buffer = (float *) matrix[0].buffer;
+
+  for (i = 0; i < Nsample; i++) {
+    if (Nsubset) {
+      j = MIN(Npix - 1, MAX(0, Npix*drand48()));
+    } else {
+      j = i;
+    }
+    values[i] = buffer[j];
+  }
+    
+  fsort (values, Nsample);
+
+  if (Nsample == 0) return 0.0;
+
+  // these are all covered by the logic below
+  // if (Nsample == 1) return values[0];
+  // if (Nsample == 2) return (0.5*(values[0] + values[1]));
+  // if (Nsample == 3) return (values[1]);
+
+  if (Nsample % 2) {
+    median = 0.5*(values[(int)(0.5*Nsample)] + values[(int)(0.5*Nsample) - 1]);
+  } else {
+    median = values[(int)(0.5*Nsample)];
+  }
+  return (median);
+}
+
+Stats *robustMedian (Matrix *matrix, Region *region, float minValue, float maxValue, float sigma) {
+
+  Stats *stats;
+
+  ALLOCATE (stats, Stats, 1);
+
+  // no data range:
+  if (maxValue - minValue < FLT_EPSILON) {
+    stats->median = minValue;
+    stats->mean   = minValue;
+    stats->mode   = minValue;
+    stats->sigma  = 0.0;
+    return stats;
+  }
+
+  // generate a histogram ranging from min to max with step size of sigma / 4
+  // no more than 0x1000 bins, no less than 0x10
+
+  Nbins = 4.0 * (maxValue - minValue) / sigma;
+  Nbins = MIN (0x1000, MAX (0x10, Nbins));
+
+  ALLOCATE (hist, int, Nbins);
+  memset (hist, 0, Nbins*sizeof(int));
+  
+  delta = Nbins / (max - min);
+  binsize = 1.0 / delta;
+
+  Ntotal = 0;
+  for (j = region->sy; j < region->sy + region->ny; j++) {
+    V = (float *)(buf[0].matrix.buffer) + j*buf[0].matrix.Naxis[0] + region->sx; 
+    for (i = 0; i < region->nx; i++, V++) {
+      if (Ignore && (fabs (*V - IgnoreValue) < 1e-8)) continue;
+      bin = MIN (MAX (0, (*V - min) * delta), Nbins - 1);
+      hist[bin] ++;
+      Ntotal ++;
+    }
+  }
+
+  // generate the cumulative histogram & find mode
+  ALLOCATE (cumu, int, Nbins);
+  memset (cumu, 0, Nbins*sizeof(int));
+
+  Nhist = 0;
+  Imode = -1;
+  Vmode = 0;
+  for (i = 0; i < Nbins; i++) {
+    Nhist += hist[i];
+    cumu[i] = Nhist;
+    if (hist[i] > Vmode) {
+      Imode = i;
+      Vmode = hist[i];
+    }
+  }
+
+  Ioo = ibracket (cumu, Nbins, 0.500000*Nhist, 1);
+  Im1 = ibracket (cumu, Nbins, 0.308538*Nhist, 1);
+  Ip1 = ibracket (cumu, Nbins, 0.691462*Nhist, 1);
+  Im2 = ibracket (cumu, Nbins, 0.022481*Nhist, 1);
+  Ip2 = ibracket (cumu, Nbins, 0.977519*Nhist, 1);
+
+  Voo = interpolateValue (cumu, Nbins, min, delta, Ioo, 0.500000*Nhist);
+  Vm1 = interpolateValue (cumu, Nbins, min, delta, Im1, 0.308538*Nhist);
+  Vp1 = interpolateValue (cumu, Nbins, min, delta, Ip1, 0.691462*Nhist);
+  Vm2 = interpolateValue (cumu, Nbins, min, delta, Im2, 0.022481*Nhist);
+  Vp2 = interpolateValue (cumu, Nbins, min, delta, Ip2, 0.977519*Nhist);
+
+  sigma1 = (Vp1 - Vm1);
+  sigma2 = (Vp2 - Vm2) / 4.0;
+
+  Vs = Vn = 0;
+  for (i = Im1; i < Ip1 + 1; i++) {
+    Vn += hist[i];
+    Vs += hist[i] * (i * binsize + min);
+  }
+
+  stats->median = Voo;
+  stats->mean = Vs / Vn;
+  stats->mode = Imode * delta + min;
+  stats->sigma = MIN (sigma1, sigma2);
+
+  free (hist);
+  free (cumu);
+
+  return (stats);
+}
+
+// what is the fractional bin position for the given value
+double interpolateValue (int *hist, int Nbins, float min, float binsize, int bin, float value) {
+
+  V0 = bin * binsize + min;
+  V1 = (bin + 1) * binsize + min;
+
+  // fbin = (value - V0) / (V1 - V0) + bin;
+  fbin = (value - V0) / binsize + bin;
+
+  return fbin;
+}
Index: /branches/sc_branches/pantasks_condor/cmd.data/style.c
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.data/style.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.data/style.c	(revision 34783)
@@ -0,0 +1,17 @@
+# include "data.h"
+
+int style (int argc, char **argv) {
+  
+  int kapa;
+  Graphdata data;
+
+  if (!style_args (&data, &argc, argv, &kapa)) return FALSE;
+
+  if (argc > 1) {
+    gprint (GP_ERR, "USAGE: style [-n Ngraph] [-x plot style] [-c color] [-pt point type] [-lt line type] [-lw line width] [-sz size]\n");
+    return (FALSE);
+  }
+  KapaSetGraphData (kapa, &data);
+
+  return (TRUE);
+}
Index: /branches/sc_branches/pantasks_condor/cmd.data/subraster.c
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.data/subraster.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.data/subraster.c	(revision 34783)
@@ -0,0 +1,86 @@
+# include "data.h"
+
+int subraster (int argc, char **argv) {
+  
+  int i, j;
+  float *Vin, *Vout;
+  int sx, sy, nx, ny;
+  int Sx, Sy, Nx, Ny;
+  int NX, NY;
+  Buffer *ibuf, *obuf;
+
+  if (argc != 11) {
+    gprint (GP_ERR, "USAGE: extract <from> <to> sx sy nx ny Sx Sy Nx Ny\n");
+    return (FALSE);
+  }
+
+  if ((ibuf = SelectBuffer (argv[1], OLDBUFFER, TRUE)) == NULL) return (FALSE);
+  NX = ibuf[0].matrix.Naxis[0];
+  NY = ibuf[0].matrix.Naxis[1];
+
+  sx = atof (argv[3]);
+  sy = atof (argv[4]);
+  nx = atof (argv[5]);
+  ny = atof (argv[6]);
+
+  Sx = atof (argv[7]);
+  Sy = atof (argv[8]);
+  Nx = atof (argv[9]);
+  Ny = atof (argv[10]);
+
+  if ((Sy + ny > Ny) || (Sx + nx > Nx)) {
+    gprint (GP_ERR, "mismatch between source and dest regions\n");
+    gprint (GP_ERR, "%d + %d > %d or %d + %d > %d\n", Sy, ny, Ny, Sx, nx, Nx);
+    return (FALSE);
+  }
+
+  /* region is not on first image */
+  if ((sx + nx < 0) || (sy + ny < 0) || 
+      (sx > ibuf[0].matrix.Naxis[0]) || 
+      (sy > ibuf[0].matrix.Naxis[1])) {
+    gprint (GP_ERR, "region outside of source image\n");
+    return (FALSE);
+  }
+
+  if ((Sx + nx > Nx) || (Sy + ny > Ny)) {
+    gprint (GP_ERR, "source region larger than dest region\n");
+    return (FALSE);
+  }
+  if ((Sx < 0) || (Sy < 0)) {
+    gprint (GP_ERR, "dest region out of range\n");
+    return (FALSE);
+  }
+
+  if ((obuf = SelectBuffer (argv[2], ANYBUFFER, TRUE)) == NULL) return (FALSE);
+  gfits_free_matrix (&obuf[0].matrix);
+  gfits_free_header (&obuf[0].header);
+
+  obuf[0].bitpix = ibuf[0].bitpix;
+  obuf[0].unsign = ibuf[0].unsign;
+  obuf[0].bscale = ibuf[0].bscale;
+  obuf[0].bzero  = ibuf[0].bzero;
+  /* strcpy (obuf[0].name, ibuf[0].name); */
+  strcpy (obuf[0].file, ibuf[0].file);
+  gfits_copy_header (&ibuf[0].header, &obuf[0].header);
+  gfits_modify (&obuf[0].header, "NAXIS1", "%d", 1, Nx);
+  gfits_modify (&obuf[0].header, "NAXIS2", "%d", 1, Ny);
+  obuf[0].header.Naxis[0] = Nx;
+  obuf[0].header.Naxis[1] = Ny;
+  gfits_create_matrix (&obuf[0].header, &obuf[0].matrix);
+
+  for (j = 0; j < ny; j++) {
+    if (j + sy < 0) continue;
+    if (j + sy >= NY) continue;
+    Vin = (float *)(ibuf[0].matrix.buffer) + (j + sy)*ibuf[0].matrix.Naxis[0] + sx;  
+    Vout = (float *)(obuf[0].matrix.buffer) + (j + Sy)*obuf[0].matrix.Naxis[0] + Sx;   
+    for (i = 0; i < nx; i++, Vin++, Vout++) {
+      if (i + sx < 0) continue;
+      if (i + sx >= NX) continue;
+      *Vout = *Vin;
+    }
+  }
+
+  return (TRUE);
+
+}
+
Index: /branches/sc_branches/pantasks_condor/cmd.data/subset.c
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.data/subset.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.data/subset.c	(revision 34783)
@@ -0,0 +1,94 @@
+# include "data.h"
+
+/* need to check dimensions of vectors */
+
+int subset (int argc, char **argv) {
+  
+  char *out;
+  int  i, Npts, size, valid;
+  Vector *ivec, *ovec, *tvec;
+
+  out = NULL;
+  ivec = ovec = tvec = NULL;
+  Npts = 0;
+
+  if (argc < 6) {
+    gprint (GP_ERR, "SYNTAX: subset vec = vec [if/where] (logic expression)\n");
+    return (FALSE);
+  }
+
+  valid = TRUE;
+  valid &= !strcmp(argv[2], "=");
+  valid &= !strcmp(argv[4], "if") || !strcmp (argv[4], "where");
+  if (!valid) {
+    gprint (GP_ERR, "SYNTAX: subset vec = vec [if/where] (logic expression)\n");
+    return (FALSE);
+  }
+
+  if ((ovec = SelectVector (argv[1], ANYVECTOR, TRUE)) == NULL) goto error;
+  if ((ivec = SelectVector (argv[3], OLDVECTOR, TRUE)) == NULL) goto error;
+
+  out = dvomath (argc - 5, &argv[5], &size, 1);
+  if (out == NULL) {
+    print_error ();
+    goto error;
+  }
+  if ((tvec = SelectVector (out, OLDVECTOR, TRUE)) == NULL) goto error;
+  /* check size of ivec, tvec: must match */
+
+  // ovec matches ivec in type
+  ResetVector (ovec, ivec->type, tvec[0].Nelements);
+
+  // we have four cases: (ivec == flt or int) and (tvec == flt or int)
+  if ((ivec->type == OPIHI_FLT) && (tvec->type == OPIHI_FLT)) {
+    opihi_flt *vi = ivec[0].elements.Flt;
+    opihi_flt *vt = tvec[0].elements.Flt;
+    for (Npts = i = 0; i < tvec[0].Nelements; i++, vi++, vt++) {
+      if (!*vt) continue;
+      ovec[0].elements.Flt[Npts] = *vi;
+      Npts++;
+    }
+  }
+  if ((ivec->type == OPIHI_FLT) && (tvec->type == OPIHI_INT)) {
+    opihi_flt *vi = ivec[0].elements.Flt;
+    opihi_int *vt = tvec[0].elements.Int;
+    for (Npts = i = 0; i < tvec[0].Nelements; i++, vi++, vt++) {
+      if (!*vt) continue;
+      ovec[0].elements.Flt[Npts] = *vi;
+      Npts++;
+    }
+  }
+  if ((ivec->type == OPIHI_INT) && (tvec->type == OPIHI_FLT)) {
+    opihi_int *vi = ivec[0].elements.Int;
+    opihi_flt *vt = tvec[0].elements.Flt;
+    for (Npts = i = 0; i < tvec[0].Nelements; i++, vi++, vt++) {
+      if (!*vt) continue;
+      ovec[0].elements.Int[Npts] = *vi;
+      Npts++;
+    }
+  }
+  if ((ivec->type == OPIHI_INT) && (tvec->type == OPIHI_INT)) {
+    opihi_int *vi = ivec[0].elements.Int;
+    opihi_int *vt = tvec[0].elements.Int;
+    for (Npts = i = 0; i < tvec[0].Nelements; i++, vi++, vt++) {
+      if (!*vt) continue;
+      ovec[0].elements.Int[Npts] = *vi;
+      Npts++;
+    }
+  }
+
+  // free up unused memory
+  ResetVector (ovec, ivec->type, Npts);
+
+  DeleteVector (tvec);
+  free (out);
+  return (TRUE);
+
+error:
+  DeleteVector (tvec);
+  DeleteVector (ovec);
+  DeleteNamedVector (out);
+  free (out);
+  return (FALSE);
+}
+
Index: /branches/sc_branches/pantasks_condor/cmd.data/svd.c
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.data/svd.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.data/svd.c	(revision 34783)
@@ -0,0 +1,128 @@
+# include "data.h"
+
+int svd (int argc, char **argv) {
+  
+  int i, Nx, Ny, status;
+  float *in, *out, *A, *U, *V;
+  Buffer *Ma, *Mu, *Mv;
+  Vector *Vw;
+  opihi_flt *W;
+
+  if (argc != 6) goto usage;
+  if (strcmp (argv[2], "=")) goto usage;
+
+  if ((Ma = SelectBuffer (argv[1], OLDBUFFER, TRUE)) == NULL) return (FALSE);
+  if ((Mu = SelectBuffer (argv[3], ANYBUFFER, TRUE)) == NULL) return (FALSE);
+  if ((Vw = SelectVector (argv[4], ANYVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((Mv = SelectBuffer (argv[5], ANYBUFFER, TRUE)) == NULL) return (FALSE);
+
+  Nx = Ma[0].header.Naxis[0];
+  Ny = Ma[0].header.Naxis[1];
+
+  /* U is Nx, Ny */
+  gfits_free_matrix (&Mu[0].matrix);
+  gfits_free_header (&Mu[0].header);
+  Mu[0].bitpix = Ma[0].bitpix;
+  Mu[0].unsign = Ma[0].unsign;
+  Mu[0].bscale = Ma[0].bscale;
+  Mu[0].bzero  = Ma[0].bzero;
+  gfits_copy_header (&Ma[0].header, &Mu[0].header);
+  gfits_create_matrix (&Mu[0].header, &Mu[0].matrix);
+  
+  /* V is Nx, Nx */
+  gfits_free_matrix (&Mv[0].matrix);
+  gfits_free_header (&Mv[0].header);
+  Mv[0].bitpix = Ma[0].bitpix;
+  Mv[0].unsign = Ma[0].unsign;
+  Mv[0].bscale = Ma[0].bscale;
+  Mv[0].bzero  = Ma[0].bzero;
+  gfits_copy_header (&Ma[0].header, &Mv[0].header);
+  gfits_modify (&Mv[0].header, "NAXIS2", "%d", 1, Nx);
+  Mv[0].header.Naxis[1] = Nx;
+  gfits_create_matrix (&Mv[0].header, &Mv[0].matrix);
+
+  /* w is Nx */
+  ResetVector (Vw, OPIHI_FLT, Nx);
+
+  /* pointers to the various arrays */
+  A = (float *) Ma[0].matrix.buffer;
+  U = (float *) Mu[0].matrix.buffer;
+  V = (float *) Mv[0].matrix.buffer;
+  W = Vw[0].elements.Flt;
+
+  /* copy A to U (svdcmp replaces A with U) */
+  in  = A;
+  out = U;
+  for (i = 0; i < Nx*Ny; i++, in++, out++) *out = *in;
+  /* use a bcopy instead? */
+
+  // try C.R. Bond's version -- requires matrices in the form A[row][col] not A[row*Ncol + col]
+  if (1) { 
+      int j;
+      double **a, **u, **v, *q;
+      ALLOCATE (a, double *, Ny);
+      ALLOCATE (u, double *, Ny);
+      ALLOCATE (v, double *, Ny);
+      for (i = 0; i < Ny; i++) {
+	  ALLOCATE (a[i], double, Nx);
+	  ALLOCATE (u[i], double, Nx);
+	  ALLOCATE (v[i], double, Nx);
+      }	  
+      ALLOCATE (q, double, Nx);
+
+      for (j = 0; j < Ny; j++) {
+	  for (i = 0; i < Nx; i++) {
+	      a[j][i] = A[j*Nx + i];
+	      u[j][i] = 0;
+	      v[j][i] = 0;
+	  }
+      }
+      for (i = 0; i < Nx; i++) {
+	  q[i] = 0;
+      }
+
+# if 1     
+      status = svdcmp_bond_new (Ny, Nx, 1, 1, FLT_EPSILON, 1e-6, a, q, u, v);
+      fprintf (stderr, "status: %d\n", status);
+# else
+      fprintf (stderr, "fix bond svdcmp\n");
+      return FALSE;
+# endif
+
+      // copy u q v back to U W V:
+      for (j = 0; j < Ny; j++) {
+	  for (i = 0; i < Nx; i++) {
+	      U[j*Nx + i] = u[j][i];
+	      V[j*Nx + i] = v[j][i];
+	  }
+      }
+      for (i = 0; i < Nx; i++) {
+	  W[i] = q[i];
+      }
+
+      for (j = 0; j < Ny; j++) {
+	  free(a[j]);
+	  free(u[j]);
+	  free(v[j]);
+      }
+      free (a);
+      free (u);
+      free (v);
+      free (q);
+
+      return TRUE;
+  }
+
+  status = svdcmp (U, W, V, Nx, Ny);
+  if (!status) {
+    gprint (GP_ERR, "error running svdcmp\n");
+    return (FALSE);
+  }
+  return (TRUE);
+
+ usage:
+  gprint (GP_ERR, "USAGE: svd A = U w Vt\n");
+  return (FALSE);
+  
+}
+
Index: /branches/sc_branches/pantasks_condor/cmd.data/swapbytes.c
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.data/swapbytes.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.data/swapbytes.c	(revision 34783)
@@ -0,0 +1,29 @@
+# include "data.h"
+
+int swapbytes (int argc, char **argv) {
+  
+  int i, nx, ny;
+  char *V, tmp;
+  Buffer *buf;
+
+  if (argc != 2) {
+    gprint (GP_ERR, "USAGE: swapbytes <buffer>\n");
+    return (FALSE);
+  }
+
+  if ((buf = SelectBuffer (argv[1], OLDBUFFER, TRUE)) == NULL) return (FALSE);
+
+  nx = buf[0].matrix.Naxis[0];
+  ny = buf[0].matrix.Naxis[1];
+
+  gprint (GP_ERR, "npix: %d\n", nx*ny);
+
+  V = buf[0].matrix.buffer;
+  for (i = 0; i < nx*ny; i++, V+=4) {
+    tmp = V[0]; V[0] = V[3]; V[3] = tmp;
+    tmp = V[1]; V[1] = V[2]; V[2] = tmp;
+  }
+
+  return (TRUE);
+}
+
Index: /branches/sc_branches/pantasks_condor/cmd.data/test/applyfit.sh
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.data/test/applyfit.sh	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.data/test/applyfit.sh	(revision 34783)
@@ -0,0 +1,51 @@
+
+list tests
+ test1
+ memtest1
+end
+
+# Test if applyfit works
+macro test1
+
+ $PASS = 1
+
+ $Cn = 2
+ $C0 = 4
+ $C1 = -2
+ $C2 = 1
+
+ create x 0 10
+
+ applyfit x y
+
+ if (y[5] != 19)
+  $PASS = 0
+  echo "Value mismatch: y[5]"
+ end
+
+end
+
+
+# Memory test
+macro memtest1
+
+ local i
+
+ list word -x "ps -p $PID -o rss"
+ $startmem = $word:1
+
+ for i 0 1000
+  applyfit x y
+ end
+  
+ list word -x "ps -p $PID -o rss"
+ $endmem = $word:1
+
+ $PASS = 1
+
+ if ($endmem - $startmem > 10)
+   $PASS = 0
+   echo "growth: {$endmem-$startmem}"
+   echo "kB/loop: {($endmem-$startmem)/1000}"
+ end
+end
Index: /branches/sc_branches/pantasks_condor/cmd.data/test/applyfit2d.sh
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.data/test/applyfit2d.sh	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.data/test/applyfit2d.sh	(revision 34783)
@@ -0,0 +1,56 @@
+
+list tests
+ test1
+ memtest1
+end
+
+# Test if applyfit2d works
+macro test1
+
+ $PASS = 1
+
+ $Cnn = 2
+ $CX0Y0 = 1
+ $CX1Y0 = -4
+ $CX2Y0 = 2
+ $CX1Y1 = -3
+ $CX0Y1 = 1.5
+ $CX0Y2 = -2.5
+
+ create x 0 5 0.01
+ set y = 3*cos(2*3.14159*x/2.25)
+
+ applyfit2d x y z
+
+ if (abs(z[300]-12.625) > 0.001)
+  $PASS = 0
+  echo "Value mismatch: z[300]"
+ end
+
+end
+
+
+# Memory test
+# NOTE: requires test1 to be run first
+macro memtest1
+
+ local i
+
+ list word -x "ps -p $PID -o rss"
+ $startmem = $word:1
+
+ for i 0 1000
+  applyfit2d x y z
+ end
+  
+ list word -x "ps -p $PID -o rss"
+ $endmem = $word:1
+
+ $PASS = 1
+
+ if (($endmem - $startmem)/1000 > 1.0)
+   $PASS = 0
+   echo "growth: {$endmem-$startmem}"
+   echo "kB/loop: {($endmem-$startmem)/1000}"
+ end
+end
Index: /branches/sc_branches/pantasks_condor/cmd.data/test/book.sh
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.data/test/book.sh	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.data/test/book.sh	(revision 34783)
@@ -0,0 +1,109 @@
+
+list tests
+ test1
+ memtest1
+end
+
+# Test book commands
+macro test1
+
+ $PASS = 1
+
+ local bname01 bname02 pcheck pname01 pname02 tword01 tword02 wcheck
+
+ book create testb01
+ book create testb02
+ book getbook 0 -var bname01
+ book getbook 1 -var bname02
+
+ if (("$bname01" != "testb01") || ("$bname02" != "testb02"))
+  $PASS = 0
+  echo "Books not created/listed correctly!"
+ end
+
+ book newpage testb01 tpage01
+ book newpage testb01 tpage02
+ book npages testb01 -var pcheck
+ 
+ if ($pcheck != 2)
+  $PASS = 0
+  echo "Book pages not added/recorded correctly!"
+ end
+
+ book getpage testb01 0 -var pname01
+ book getpage testb01 1 -var pname02
+
+ if (("$pname01" != "tpage01") || ("$pname02" != "tpage02"))
+  $PASS = 0
+  echo "Pages not created/listed correctly!"
+ end
+
+ book setword testb01 tpage01 w01 tword01
+ book setword testb01 tpage01 w02 tword02
+ book getword testb01 tpage01 w01 -var wcheck
+
+ if ("$wcheck" != "tword01")
+  $PASS = 0
+  echo "Words not created/listed correctly!"
+ end
+
+ book delpage testb01 tpage02
+ book getpage testb01 1 -var pcheck
+
+ if ("$pcheck" != "NULL")
+  $PASS = 0
+  echo "Book pages not deleted correctly (by delpage)!"
+ end
+
+ book init testb01
+ book getpage testb01 0 -var pcheck
+
+ if ("$pcheck" != "NULL")
+  $PASS = 0
+  echo "Book pages not deleted correctly (by init)!"
+ end
+
+ book delete testb01
+ book delete testb02
+
+# delete bname01 bname02 pcheck pname01 pname02 tword01 tword02 wcheck
+
+end
+
+
+# Memory test
+macro memtest1
+
+ local i bcheck pcheck wcheck
+
+ list word -x "ps -p $PID -o rss"
+ $startmem = $word:1
+
+ for i 0 1000
+  book create testb
+  book getbook 0 -var bcheck
+  book newpage testb testp1
+  book newpage testb testp2
+  book npages testb -var pcheck
+  book getpage testb 0 -var pcheck
+  book setword testb testp1 testw tada
+  book getword testb testp1 testw -var wcheck
+  book delpage testb testp2
+  book init testb
+  book delete testb
+ end
+  
+ list word -x "ps -p $PID -o rss"
+ $endmem = $word:1
+
+ $PASS = 1
+
+ if ($endmem - $startmem > 10)
+   $PASS = 0
+   echo "growth: {$endmem-$startmem}"
+   echo "kB/loop: {($endmem-$startmem)/1000}"
+ end
+
+#delete i bcheck pcheck wcheck
+
+end
Index: /branches/sc_branches/pantasks_condor/cmd.data/test/concat.sh
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.data/test/concat.sh	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.data/test/concat.sh	(revision 34783)
@@ -0,0 +1,48 @@
+
+list tests
+ test1
+ memtest1
+end
+
+# Does concat work?
+macro test1
+
+ $PASS = 1
+
+ create a 0 10
+ set b = a
+
+ concat a b
+
+ if ((b[] != 20) || (b[10] != 0))
+  $PASS = 0
+  echo "Concat failed!: nelements: b[] b(10)= b[10]"
+ end
+end
+
+
+# Memory Test
+macro memtest1
+
+ local i
+
+ list word -x "ps -p $PID -o rss"
+ $startmem = $word:1
+
+ for i 0 1000
+  create a 0 10
+  set b = a
+  concat a b
+ end
+  
+ list word -x "ps -p $PID -o rss"
+ $endmem = $word:1
+
+ $PASS = 1
+
+ if ($endmem - $startmem > 10)
+   $PASS = 0
+   echo "growth: {$endmem-$startmem}"
+   echo "kB/loop: {($endmem-$startmem)/1000}"
+ end
+end
Index: /branches/sc_branches/pantasks_condor/cmd.data/test/create.sh
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.data/test/create.sh	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.data/test/create.sh	(revision 34783)
@@ -0,0 +1,44 @@
+
+list tests
+ test1
+ memtest1
+end
+
+# Test create function
+macro test1
+ 
+ $PASS = 1
+
+ create x 0 10 0.5
+
+ if ((x[1] != 0.5) || (x[9] != 4.5))
+  $PASS = 0
+ end
+
+end
+
+
+# Memory test
+macro memtest1
+
+ local i
+
+ list word -x "ps -p $PID -o rss"
+ $startmem = $word:1
+
+ for i 0 1000
+  create y 0 10 0.1
+ end
+  
+ list word -x "ps -p $PID -o rss"
+ $endmem = $word:1
+
+ $PASS = 1
+
+ if ($endmem - $startmem > 10)
+   $PASS = 0
+   echo "growth: {$endmem-$startmem}"
+   echo "kB/loop: {($endmem-$startmem)/1000}"
+ end
+
+end
Index: /branches/sc_branches/pantasks_condor/cmd.data/test/csv.dat
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.data/test/csv.dat	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.data/test/csv.dat	(revision 34783)
@@ -0,0 +1,3 @@
+word,1,5,10
+foo,2,4,8
+sam,3,3,6
Index: /branches/sc_branches/pantasks_condor/cmd.data/test/csv1.dat
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.data/test/csv1.dat	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.data/test/csv1.dat	(revision 34783)
@@ -0,0 +1,3 @@
+word,1,5,10
+foo,,4,8
+sam,3,,6
Index: /branches/sc_branches/pantasks_condor/cmd.data/test/cumulative.sh
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.data/test/cumulative.sh	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.data/test/cumulative.sh	(revision 34783)
@@ -0,0 +1,46 @@
+
+list tests
+ test1
+ memtest1
+end
+
+# Does cumulative work?
+macro test1
+
+ $PASS = 1
+
+ create a 5 15
+
+ cumulative a acum
+
+ if (acum[5] != 45)
+  $PASS = 0
+  echo "Cumulative failed!: nelements: acum[] acum(5)= acum[5]"
+ end
+end
+
+
+# Memory Test
+macro memtest1
+
+ local i
+
+ list word -x "ps -p $PID -o rss"
+ $startmem = $word:1
+
+ for i 0 1000
+  create a 5 15
+  cumulative a acum
+ end
+  
+ list word -x "ps -p $PID -o rss"
+ $endmem = $word:1
+
+ $PASS = 1
+
+ if ($endmem - $startmem > 10)
+   $PASS = 0
+   echo "growth: {$endmem-$startmem}"
+   echo "kB/loop: {($endmem-$startmem)/1000}"
+ end
+end
Index: /branches/sc_branches/pantasks_condor/cmd.data/test/cut.sh
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.data/test/cut.sh	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.data/test/cut.sh	(revision 34783)
@@ -0,0 +1,38 @@
+
+list tests
+ test1
+ memtest1
+end
+
+# Test if cut works
+macro test1
+
+ $PASS = 1
+
+ mcreate tim 100 10
+ zap tim 0 0 100 10 -v 10
+
+ cut tim xdir imx X 40 4 60 6
+ cut tim ydir imy Y 40 4 60 6
+
+ if (xdir[] != 60)
+  $PASS = 0
+ end
+ if (imx[] != 60)
+  $PASS = 0
+ end
+
+ if (ydir[] != 6)
+  $PASS = 0
+ end
+ if (imy[] != 6)
+  $PASS = 0
+ end
+
+ if (imx[0] != 10*6)
+  $PASS = 0
+ end
+ if (imy[0] != 10*60)
+  $PASS = 0
+ end
+end
Index: /branches/sc_branches/pantasks_condor/cmd.data/test/delete.sh
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.data/test/delete.sh	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.data/test/delete.sh	(revision 34783)
@@ -0,0 +1,50 @@
+
+list tests
+ test1
+ memtest1
+end
+
+# Test delete
+macro test1
+
+ $PASS = 1
+
+ $v = 7
+
+# create v 0 10
+
+ delete v
+
+ if ($?v != 0)
+  $PASS = 0
+  echo "Variable not deleted!"
+ end
+
+end
+
+
+# Memory test
+macro memtest1
+
+ local i
+
+ list word -x "ps -p $PID -o rss"
+ $startmem = $word:1
+
+ for i 0 1000
+  $u = testing
+  delete u
+ end
+  
+ list word -x "ps -p $PID -o rss"
+ $endmem = $word:1
+
+ $PASS = 1
+
+ if ($endmem - $startmem > 10)
+   $PASS = 0
+   echo "growth: {$endmem-$startmem}"
+   echo "kB/loop: {($endmem-$startmem)/1000}"
+ end
+
+end
Index: /branches/sc_branches/pantasks_condor/cmd.data/test/dimendown.sh
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.data/test/dimendown.sh	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.data/test/dimendown.sh	(revision 34783)
@@ -0,0 +1,60 @@
+
+list tests
+ test1
+ memtest1
+end
+
+# Test if dimendown works
+macro test1
+
+ $PASS = 1
+
+ mcreate timg 100 10
+ zap timg 0 0 100 10 -v 10
+ dimendown timg val
+ dimendown timg xc -x
+ dimendown timg yc -y
+
+ if (val[777] != 10)
+  $PASS = 0
+  echo "Value mismatch: val[777] (should be 10)"
+ end
+
+ if (xc[777] != 77)
+  $PASS = 0
+  echo "X Coord mismatch: xc[777] (should be 77)"
+ end
+
+ if (yc[777] != 7)
+  $PASS = 0
+  echo "Y Coord mismatch: yc[77] (should be 7)"
+ end
+
+end
+
+
+# Memory test
+macro memtest1
+
+ local i
+
+ list word -x "ps -p $PID -o rss"
+ $startmem = $word:1
+
+ for i 0 10000
+  dimendown timg val
+  dimendown timg xc -x
+  dimendown timg yc -y
+ end
+  
+ list word -x "ps -p $PID -o rss"
+ $endmem = $word:1
+
+ $PASS = 1
+
+ if ($endmem - $startmem > 128)
+   $PASS = 0
+   echo "growth: {$endmem-$startmem}"
+   echo "kB/loop: {($endmem-$startmem)/1000}"
+ end
+end
Index: /branches/sc_branches/pantasks_condor/cmd.data/test/dimenup.sh
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.data/test/dimenup.sh	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.data/test/dimenup.sh	(revision 34783)
@@ -0,0 +1,48 @@
+
+list tests
+ test1
+ memtest1
+end
+
+# Test if dimendown works
+macro test1
+
+ $PASS = 1
+
+ create tvec 0 1000
+
+ dimenup tvec timg 10 100
+
+ stats -q timg 6 34 1 1
+
+ if ($MEAN != 346)
+  $PASS = 0
+  echo "Value mismatch: $MEAN (should be 346)"
+ end
+
+end
+
+
+# Memory test
+macro memtest1
+
+ local i
+
+ list word -x "ps -p $PID -o rss"
+ $startmem = $word:1
+
+ for i 0 1000
+  dimenup tvec timg 10 100
+ end
+  
+ list word -x "ps -p $PID -o rss"
+ $endmem = $word:1
+
+ $PASS = 1
+
+ if ($endmem - $startmem > 10)
+   $PASS = 0
+   echo "growth: {$endmem-$startmem}"
+   echo "kB/loop: {($endmem-$startmem)/1000}"
+ end
+end
Index: /branches/sc_branches/pantasks_condor/cmd.data/test/fft1d.sh
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.data/test/fft1d.sh	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.data/test/fft1d.sh	(revision 34783)
@@ -0,0 +1,17 @@
+
+list tests
+end
+
+macro test1
+ $PASS = 1
+ break -auto off
+
+ create t 0 4096 1.0
+ set f = dsin(20*t)
+
+ fft1d f 0 to Frn Fin
+
+ clear
+ section a 0.0 0.0 1.0 0.5
+ lim t Fro; box; plot -pt 7 -c blue t Fro; plot -pt 2 -c red t Frn
+end
Index: /branches/sc_branches/pantasks_condor/cmd.data/test/fft2d.sh
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.data/test/fft2d.sh	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.data/test/fft2d.sh	(revision 34783)
@@ -0,0 +1,27 @@
+
+list tests
+# test1
+end
+
+macro test1
+ $PASS = 1
+ break -auto off
+
+ delete x y t f Frn Fin Fro Fio dfi dfr
+ mcreate t 2048 2048
+ set x = xramp(t)
+ set y = yramp(t)
+
+ # set f = dsin(3*x)*dcos(5*y)
+ set f = exp(-0.5*((x-1024)^2 + (y-1024)^2)*0.01)
+
+ date; fft2d f 0 to Frn Fin; date
+
+ date; fft2dold f 0 to Fro Fio; date
+
+ set dfr = Frn - Fro
+ set dfi = Fin - Fio
+
+ stats dfr
+ stats dfi
+end
Index: /branches/sc_branches/pantasks_condor/cmd.data/test/fit.sh
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.data/test/fit.sh	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.data/test/fit.sh	(revision 34783)
@@ -0,0 +1,226 @@
+
+list tests
+ test1
+ test2
+ test3
+ test4
+ test5
+ test6
+ test7
+ test8
+end
+
+# fit a line without errors
+macro test1
+ $PASS = 1
+ break -auto off
+
+ delete -q x y
+
+ create x 0 100
+ set y = 3 + 5*x
+ fit -q x y 1
+
+ if ($Cn != 1)
+   $PASS = 0
+ end
+ if (abs($C0 - 3) > 1e-5)
+   $PASS = 0
+ end
+ if (abs($C1 - 5) > 1e-5)
+   $PASS = 0
+ end
+end
+
+# fit a line with errors
+macro test2
+ $PASS = 1
+ break -auto off
+
+ delete -q x y dy
+
+ create x 0 100
+ set dy = 0.1*rnd(x) - 0.05
+ set y = 3 + 5*x + dy
+ fit -q x y 1
+
+ if ($Cn != 1)
+   $PASS = 0
+   echo "wrong number of elements : $Cn"
+ end
+ if (abs($C0 - 3) > 0.06)
+   $PASS = 0
+   echo "wrong value for C0 : $C0"
+ end
+ if (abs($C1 - 5) > 0.06)
+   $PASS = 0
+   echo "wrong value for C1 : $C1"
+ end
+end
+
+# fit a line with errors and weights
+macro test3
+ $PASS = 1
+ break -auto off
+
+ delete -q x y dy
+
+ create x 0 100
+ set dy = 0.1*rnd(x) - 0.05
+ set y = 3 + 5*x + dy
+ set dy = 0.1 + zero(x)
+ fit -q x y 1 -dy dy
+
+ if ($Cn != 1)
+   $PASS = 0
+ end
+ if (abs($C0 - 3) > 0.06)
+   $PASS = 0
+ end
+ if (abs($C1 - 5) > 0.06)
+   $PASS = 0
+ end
+end
+
+# fit a line with errors, weights, and outliers 
+macro test4
+ $PASS = 1
+ break -auto off
+
+ delete -q x y dy
+
+ create x 0 100
+ set dy = 0.1*rnd(x) - 0.05
+ set y = 3 + 5*x + dy
+ set dy = 0.1 + zero(x)
+ y[5] = 23
+ y[20] = -10
+ y[50] = 0.0
+ fit -q x y 1 -dy dy -clip 3 3
+
+ if ($Cn != 1)
+   $PASS = 0
+ end
+ if ($Cnv != 97)
+   $PASS = 0
+ end
+ if (abs($C0 - 3) > 0.06)
+   $PASS = 0
+ end
+ if (abs($C1 - 5) > 0.06)
+   $PASS = 0
+ end
+end
+
+# fit a quadratic without errors
+macro test5
+ $PASS = 1
+ break -auto off
+
+ delete -q x y
+
+ create x 0 100
+ set y = 3 + 5*x - 4*x^2
+ fit -q x y 2
+
+ if ($Cn != 2)
+   $PASS = 0
+ end
+ if (abs($C0 - 3) > 1e-5)
+   $PASS = 0
+ end
+ if (abs($C1 - 5) > 1e-5)
+   $PASS = 0
+ end
+ if (abs($C2 + 4) > 1e-5)
+   $PASS = 0
+ end
+end
+
+# fit a quadratic with errors
+macro test6
+ $PASS = 1
+ break -auto off
+
+ delete -q x y dy
+
+ create x 0 100
+ set dy = 0.1*rnd(x) - 0.05
+ set y = 3 + 5*x - 4*x^2 + dy
+ fit -q x y 2
+
+ if ($Cn != 2)
+   $PASS = 0
+ end
+ if (abs($C0 - 3) > 0.06)
+   $PASS = 0
+ end
+ if (abs($C1 - 5) > 0.06)
+   $PASS = 0
+ end
+ if (abs($C2 + 4) > 0.06)
+   $PASS = 0
+ end
+end
+
+# fit a quadratic with errors and weights
+macro test7
+ $PASS = 1
+ break -auto off
+
+ delete -q x y dy
+
+ create x 0 100
+ set dy = 0.1*rnd(x) - 0.05
+ set y = 3 + 5*x - 4*x^2 + dy
+ set dy = 0.1 + zero(x)
+ fit -q x y 2 -dy dy
+
+ if ($Cn != 2)
+   $PASS = 0
+ end
+ if (abs($C0 - 3) > 0.06)
+   $PASS = 0
+ end
+ if (abs($C1 - 5) > 0.06)
+   $PASS = 0
+ end
+ if (abs($C2 + 4) > 0.06)
+   $PASS = 0
+ end
+end
+
+# fit a quadratic with errors, weights, and outliers 
+macro test8
+ $PASS = 1
+ break -auto off
+
+ delete -q x y dy
+
+ create x 0 100
+ set dy = 0.1*rnd(x) - 0.05
+ set y = 3 + 5*x - 4*x^2 + dy
+ set dy = 0.1 + zero(x)
+ y[5] = 23
+ y[20] = -10
+ y[50] = 0.0
+
+ # it takes 4 iterations to successfully reject the outliers above...
+ fit -q x y 2 -dy dy -clip 3 4
+
+ if ($Cn != 2)
+   $PASS = 0
+ end
+ if ($Cnv != 97)
+   $PASS = 0
+ end
+ if (abs($C0 - 3) > 0.06)
+   $PASS = 0
+ end
+ if (abs($C1 - 5) > 0.06)
+   $PASS = 0
+ end
+ if (abs($C2 + 4) > 0.06)
+   $PASS = 0
+ end
+end
Index: /branches/sc_branches/pantasks_condor/cmd.data/test/fit2d.sh
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.data/test/fit2d.sh	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.data/test/fit2d.sh	(revision 34783)
@@ -0,0 +1,118 @@
+
+list tests
+ test1
+ test2
+ memtest1
+end
+
+# fit a function without errors
+macro test1
+ $PASS = 1
+ break -auto off
+
+ create x 0 5 0.01
+ set y = sin((2*3.14159*x)/2)
+ set z = 5-4*x+x^2-3*y+6*x*y-2*y^2
+
+ fit2d -q x y z 2
+
+ if ($Cnn != 2)
+   $PASS = 0
+   echo "Function Order Incorrect!"
+ end
+ if (abs($CX0Y0 - 5) > 1e-5)
+   $PASS = 0
+   echo "Term CX0Y0 Incorrect!"
+ end
+ if (abs($CX1Y0 + 4) > 1e-5)
+   $PASS = 0
+   echo "Term CX1Y0 Incorrect!"
+ end
+ if (abs($CX2Y0 - 1) > 1e-5)
+   $PASS = 0
+   echo "Term CX2Y0 Incorrect!"
+ end
+ if (abs($CX0Y1 + 3) > 1e-5)
+   $PASS = 0
+   echo "Term CX0Y1 Incorrect!"
+ end
+ if (abs($CX1Y1 - 6) > 1e-5)
+   $PASS = 0
+   echo "Term CX1Y1 Incorrect!"
+ end
+ if (abs($CX0Y2 + 2) > 1e-5)
+   $PASS = 0
+   echo "Term CX0Y2 Incorrect!"
+ end
+end
+
+# fit a function with errors
+macro test2
+ $PASS = 1
+ break -auto off
+
+ create x 0 5 0.01
+ set y = sin((2*3.14159*x)/2)
+ set dz = 0.1*rnd(x) - 0.05
+ set z = 5-4*x+x^2-3*y+6*x*y-2*y^2+dz
+
+ fit2d -q x y z 2
+
+ if ($Cnn != 2)
+   $PASS = 0
+   echo "Function Order Incorrect!"
+ end
+ if (abs($CX0Y0 - 5) > 0.01)
+   $PASS = 0
+   echo "Term CX0Y0 Incorrect!"
+ end
+ if (abs($CX1Y0 + 4) > 0.01)
+   $PASS = 0
+   echo "Term CX1Y0 Incorrect!"
+ end
+ if (abs($CX2Y0 - 1) > 0.01)
+   $PASS = 0
+   echo "Term CX2Y0 Incorrect!"
+ end
+ if (abs($CX0Y1 + 3) > 0.01)
+   $PASS = 0
+   echo "Term CX0Y1 Incorrect!"
+ end
+ if (abs($CX1Y1 - 6) > 0.01)
+   $PASS = 0
+   echo "Term CX1Y1 Incorrect!"
+ end
+ if (abs($CX0Y2 + 2) > 0.01)
+   $PASS = 0
+   echo "Term CX0Y2 Incorrect!"
+ end
+end
+
+# Memory Test
+macro memtest1
+
+ local i
+
+ list word -x "ps -p $PID -o rss"
+ $startmem = $word:1
+
+ create x 0 5 0.01
+ set y = sin((2*3.14159*x)/2)
+ set dz = 0.1*rnd(x) - 0.05
+ set z = 5-4*x+x^2-3*y+6*x*y-2*y^2+dz
+
+ for i 0 1000
+  fit2d -q x y z 2
+ end
+  
+ list word -x "ps -p $PID -o rss"
+ $endmem = $word:1
+
+ $PASS = 1
+
+ if ($endmem - $startmem > 10)
+   $PASS = 0
+   echo "growth: {$endmem-$startmem}"
+   echo "kB/loop: {($endmem-$startmem)/1000}"
+ end
+end
Index: /branches/sc_branches/pantasks_condor/cmd.data/test/gaussj.sh
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.data/test/gaussj.sh	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.data/test/gaussj.sh	(revision 34783)
@@ -0,0 +1,297 @@
+
+list tests
+ test1
+ test2
+ test3
+ test4
+ test5
+end
+
+# a very simple diagonal matrix equation
+macro test1
+ $PASS = 1
+ break -auto off
+
+ mcreate A 3 3
+ create B 0 3
+
+ A[0][0] = 2
+ A[1][1] = 2
+ A[2][2] = 2
+
+ A[0][1] = -1
+ A[1][2] = -1
+ A[1][0] = -1
+ A[2][1] = -1
+
+ gaussj A B
+ if (not($STATUS))
+   $PASS = 0
+ end
+
+ if (abs(A[0][0] - 0.75) > 0.01)
+  $PASS = 0
+ end
+ if (abs(A[0][1] - 0.50) > 0.01)
+  $PASS = 0
+ end
+ if (abs(A[0][2] - 0.25) > 0.01)
+  $PASS = 0
+ end
+
+ if (abs(A[1][0] - 0.50) > 0.01)
+  $PASS = 0
+ end
+ if (abs(A[1][1] - 1.00) > 0.01)
+  $PASS = 0
+ end
+ if (abs(A[1][2] - 0.50) > 0.01)
+  $PASS = 0
+ end
+
+ if (abs(A[2][0] - 0.25) > 0.01)
+  $PASS = 0
+ end
+ if (abs(A[2][1] - 0.50) > 0.01)
+  $PASS = 0
+ end
+ if (abs(A[2][2] - 0.75) > 0.01)
+  $PASS = 0
+ end
+
+ if (abs(B[0] - 1.00) > 0.01)
+  $PASS = 0
+ end
+ if (abs(B[1] - 2.00) > 0.01)
+  $PASS = 0
+ end
+ if (abs(B[2] - 2.00) > 0.01)
+  $PASS = 0
+ end
+end
+
+# a very simple off-diagonal matrix equation
+macro test2
+ $PASS = 1
+ break -auto off
+
+ mcreate A 3 3
+ create B 0 3
+
+ A[0][1] = 2
+ A[1][0] = 2
+ A[2][2] = 2
+
+ A[0][0] = -1
+ A[1][2] = -1
+ A[1][1] = -1
+ A[2][0] = -1
+
+ gaussj A B
+ if (not($STATUS))
+   $PASS = 0
+ end
+
+ # echo A[0][0] A[0][1] A[0][2]
+ # echo A[1][0] A[1][1] A[1][2]
+ # echo A[2][0] A[2][1] A[2][2]
+
+ if (abs(A[1][0] - 0.75) > 0.01)
+  $PASS = 0
+ end
+ if (abs(A[1][1] - 0.50) > 0.01)
+  $PASS = 0
+ end
+ if (abs(A[1][2] - 0.25) > 0.01)
+  $PASS = 0
+ end
+
+ if (abs(A[0][0] - 0.50) > 0.01)
+  $PASS = 0
+ end
+ if (abs(A[0][1] - 1.00) > 0.01)
+  $PASS = 0
+ end
+ if (abs(A[0][2] - 0.50) > 0.01)
+  $PASS = 0
+ end
+
+ if (abs(A[2][0] - 0.25) > 0.01)
+  $PASS = 0
+ end
+ if (abs(A[2][1] - 0.50) > 0.01)
+  $PASS = 0
+ end
+ if (abs(A[2][2] - 0.75) > 0.01)
+  $PASS = 0
+ end
+
+ if (abs(B[1] - 1.00) > 0.01)
+  $PASS = 0
+ end
+ if (abs(B[0] - 2.00) > 0.01)
+  $PASS = 0
+ end
+ if (abs(B[2] - 2.00) > 0.01)
+  $PASS = 0
+ end
+end
+
+# a singular matrix equation
+macro test3
+ $PASS = 1
+ break -auto off
+
+ mcreate A 3 3
+ create B 0 3
+
+ A[0][0] = 2
+ A[1][0] = 2
+ A[2][2] = 2
+
+ A[0][1] = -1
+ A[1][1] = -1
+ A[2][1] = -1
+
+ gaussj -q A B
+ if ($STATUS)
+   $PASS = 0
+ end
+end
+
+# a very large matrix equation
+macro test4
+ $PASS = 1
+ break -auto off
+
+ $Ndim = 50
+ mcreate A $Ndim $Ndim
+ create B 0 $Ndim
+
+ # generate the diagonal + off-diagonal elements
+ for i 0 $Ndim
+   A[$i][$i] = 2.0
+   if ($i > 0)
+     A[$i][$i-1] = -1.0
+   end
+   if ($i < $Ndim - 1)
+     A[$i][$i+1] = -1.0
+   end
+ end
+
+ set inB = B
+ set inA = A
+
+ gaussj A B
+ if (not($STATUS))
+   $PASS = 0
+ end
+
+ set meas = zero(inB)
+ for i 0 B[]
+  for j 0 B[]
+   meas[$i] = meas[$i] + inA[$i][$j] * B[$j]
+  end
+ end
+
+ for i 0 inB[]
+  if (abs(inB[$i]-meas[$i]) > 1e-3)
+    $PASS = 0
+    echo inB[$i] meas[$i] {inB[$i]-meas[$i]}
+  end
+ end
+end
+
+# a nearly singular matrix equation
+macro test5
+ $PASS = 1
+ break -auto off
+
+ delete A B inA inB meas
+
+ mcreate A 3 3
+ create B 0 3
+
+ A[0][0] = 2
+ A[1][0] = 2.00001
+ A[2][2] = 2
+
+ A[0][1] = -1
+ A[1][1] = -1
+ A[2][1] = -1
+
+ set inB = B
+ set inA = A
+
+ gaussj A B
+ if (not($STATUS))
+   $PASS = 0
+ end
+
+ set meas = zero(inB)
+ for i 0 B[]
+  for j 0 B[]
+   # echo $i $j meas[$i] inA[$i][$j] B[$j] {inA[$i][$j] * B[$j]} {meas[$i] + inA[$i][$j] * B[$j]}
+   meas[$i] = meas[$i] + inA[$i][$j] * B[$j]
+   # echo "-> meas[$i]"
+  end
+ end
+
+ for i 0 inB[]
+  if (abs(inB[$i]-meas[$i]) > 1e-5)
+    echo inB[$i] meas[$i] {abs(inB[$i]-meas[$i])}
+    $PASS = 0
+  end
+ end
+end
+
+# a very large matrix equation
+# timing on my laptop: 100 : ~1sec; 300 : ~7sec; 1000 : ~88sec
+# note: at Ndim = 1000, it failed (Ax - B was in the range -2..+2)
+macro test6
+ if ($0 != 2)
+  echo "USAGE: test6 (Ndim)"
+  break
+ end
+
+ $PASS = 1
+ break -auto off
+
+ $Ndim = $1
+ mcreate A $Ndim $Ndim
+ create B 0 $Ndim
+
+ # generate the diagonal + off-diagonal elements
+ for i 0 $Ndim
+   A[$i][$i] = 2.0
+   if ($i > 0)
+     A[$i][$i-1] = -1.0
+   end
+   if ($i < $Ndim - 1)
+     A[$i][$i+1] = -1.0
+   end
+ end
+
+ set inB = B
+ set inA = A
+
+ gaussj A B
+ if (not($STATUS))
+   $PASS = 0
+ end
+
+ set meas = zero(inB)
+ for i 0 B[]
+  for j 0 B[]
+   meas[$i] = meas[$i] + inA[$i][$j] * B[$j]
+  end
+ end
+
+ for i 0 inB[]
+  if (abs(inB[$i]-meas[$i]) > 1e-3)
+    $PASS = 0
+    echo inB[$i] meas[$i] {inB[$i]-meas[$i]}
+  end
+ end
+end
+
Index: /branches/sc_branches/pantasks_condor/cmd.data/test/histogram.sh
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.data/test/histogram.sh	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.data/test/histogram.sh	(revision 34783)
@@ -0,0 +1,54 @@
+
+list tests
+ test1
+ memtest1
+end
+
+# Test if histogram works
+macro test1
+
+ $PASS = 1
+
+ local i
+
+ # set data values at the i*0.1 + 0.05 values so round-off does not causs miscounts
+ create x 0.05 10.05 0.1
+
+ for i 45 55
+  x[$i] = 4.5
+ end
+
+ # histogram bins are 0-0.1, 0.1-0.2, etc
+ histogram x xhis 0 10 0.1
+
+ if ((xhis[10] != 1) || (xhis[45] != 10))
+  $PASS = 0
+  echo "Value mismatch: xhis[10] xhis[45] (should be 1,10)"
+ end
+
+end
+
+
+# Memory test
+macro memtest1
+
+ local i
+
+ list word -x "ps -p $PID -o rss"
+ $startmem = $word:1
+
+ for i 0 1000
+  histogram x xhis 0 10 0.1
+ end
+  
+ list word -x "ps -p $PID -o rss"
+ $endmem = $word:1
+
+ $PASS = 1
+
+ if ($endmem - $startmem > 10)
+   $PASS = 0
+   echo "growth: {$endmem-$startmem}"
+   echo "kB/loop: {($endmem-$startmem)/1000}"
+ end
+end
Index: /branches/sc_branches/pantasks_condor/cmd.data/test/imhist.sh
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.data/test/imhist.sh	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.data/test/imhist.sh	(revision 34783)
@@ -0,0 +1,58 @@
+
+list tests
+ test1
+ memtest1
+end
+
+# Test if imhist works
+macro test1
+
+ $PASS = 1
+
+ local i
+
+ mcreate buff 100 10
+
+ zap buff 40 0 10 10 -v 17
+ zap buff 60 5 5 2 -v 6
+
+ imhist buff xvec yvec
+
+ if ((xvec[1024] != 17) || (yvec[1024] != 100))
+  $PASS = 0
+  echo "Value mismatch: xvec[1024] yvec[1024] (should be 17,100)"
+ end
+
+ imhist -q buff xvec yvec -region 40 0 25 10 -range 0 10
+
+ if ((xvec[1024] != 10) || (yvec[1024] != 100))
+  $PASS = 0
+  echo "Value mismatch: xvec[1024] yvec[1024] (should be 10,100)"
+ end
+
+end
+
+
+# Memory test
+macro memtest1
+
+ local i
+
+ list word -x "ps -p $PID -o rss"
+ $startmem = $word:1
+
+ for i 0 1000
+  imhist -q buff xvec yvec -region 40 0 25 10 -range 0 10
+ end
+
+ list word -x "ps -p $PID -o rss"
+ $endmem = $word:1
+
+ $PASS = 1
+
+ if ($endmem - $startmem > 10)
+   $PASS = 0
+   echo "growth: {$endmem-$startmem}"
+   echo "kB/loop: {($endmem-$startmem)/1000}"
+ end
+end
Index: /branches/sc_branches/pantasks_condor/cmd.data/test/imsmooth.sh
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.data/test/imsmooth.sh	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.data/test/imsmooth.sh	(revision 34783)
@@ -0,0 +1,59 @@
+
+list tests
+ test1
+ memtest1
+end
+
+# Test if imsmooth works
+macro test1
+
+ $PASS = 1
+
+ local i j
+
+ mcreate buff 100 100
+
+ for i 0 99
+  for j 0 99
+   zap buff $i $j 1 1 -v {100*rnd($i)}
+  end
+ end
+
+ stats -q buff
+ $s1 = $SIGMA
+
+ imsmooth buff 10
+ stats -q buff
+ $s2 = $SIGMA
+
+ if ($s2/$s1 >= 0.1)
+  $PASS = 0
+  echo "Inadequate noise reduction: {$s2/$s1*100}\% (should be less than 10%)"
+ end
+
+end
+
+
+# Memory test
+macro memtest1
+
+ local i
+
+ list word -x "ps -p $PID -o rss"
+ $startmem = $word:1
+
+ for i 0 100
+  imsmooth buff 10
+ end
+
+ list word -x "ps -p $PID -o rss"
+ $endmem = $word:1
+
+ $PASS = 1
+
+ if ($endmem - $startmem > 10)
+   $PASS = 0
+   echo "growth: {$endmem-$startmem}"
+   echo "kB/loop: {($endmem-$startmem)/100}"
+ end
+end
Index: /branches/sc_branches/pantasks_condor/cmd.data/test/integrate.sh
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.data/test/integrate.sh	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.data/test/integrate.sh	(revision 34783)
@@ -0,0 +1,47 @@
+
+list tests
+ test1
+ memtest1
+end
+
+# Test if integrate works
+macro test1
+
+ $PASS = 1
+
+ create x 0 10 0.01
+ set y = 1+2*x+3*x^2
+
+ integrate x y 1 5
+
+ if (abs ($sum-152) > 0.5)
+  $PASS = 0
+  echo "Inaccurate result (should be 152): $sum"
+ end
+
+end
+
+
+# Memory test
+macro memtest1
+
+ local i
+
+ list word -x "ps -p $PID -o rss"
+ $startmem = $word:1
+
+ for i 0 1000
+  integrate x y 1 5
+ end
+  
+ list word -x "ps -p $PID -o rss"
+ $endmem = $word:1
+
+ $PASS = 1
+
+ if ($endmem - $startmem > 128)
+   $PASS = 0
+   echo "growth: {$endmem-$startmem}"
+   echo "kB/loop: {($endmem-$startmem)/1000}"
+ end
+end
Index: /branches/sc_branches/pantasks_condor/cmd.data/test/interpolate.sh
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.data/test/interpolate.sh	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.data/test/interpolate.sh	(revision 34783)
@@ -0,0 +1,48 @@
+
+list tests
+ test1
+ memtest1
+end
+
+# Test if interpolate works
+macro test1
+
+ $PASS = 1
+
+ create x0 0 10 0.1
+ set y0 = 1+2*x0+3*x0^2
+ create x1 0 10 0.001
+ interpolate x0 y0 x1 y1
+ integrate x1 y1 1 5
+
+ if (abs ($sum-152) > 0.08)
+  $PASS = 0
+  echo "Inaccurate result (should be 152): $sum"
+ end
+
+end
+
+
+# Memory test
+macro memtest1
+
+ local i
+
+ list word -x "ps -p $PID -o rss"
+ $startmem = $word:1
+
+ for i 0 1000
+  interpolate x0 y0 x1 y1
+ end
+  
+ list word -x "ps -p $PID -o rss"
+ $endmem = $word:1
+
+ $PASS = 1
+
+ if ($endmem - $startmem > 128)
+   $PASS = 0
+   echo "growth: {$endmem-$startmem}"
+   echo "kB/loop: {($endmem-$startmem)/1000}"
+ end
+end
Index: /branches/sc_branches/pantasks_condor/cmd.data/test/matrix.sh
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.data/test/matrix.sh	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.data/test/matrix.sh	(revision 34783)
@@ -0,0 +1,78 @@
+
+list tests
+ test1
+ test2
+end
+
+# a very simple diagonal matrix equation
+macro test1
+ $PASS = 1
+ break -auto off
+
+ delete -q A B C
+ mcreate B 2 2
+ mcreate C 2 2
+
+ B[0][0] = 2
+ B[1][0] = -1
+ B[0][1] = -1
+ B[1][1] = 4
+
+ C[0][0] = 2
+ C[1][0] = 1
+ C[0][1] = 1
+ C[1][1] = 1
+
+ matrix set A = B * C
+
+ # A[0][0] = B[0][0]*C[0][0] + B[1][0]*C[0][1] =  4 - 1 = 3
+ # A[1][0] = B[0][0]*C[1][0] + B[1][0]*C[1][1] =  2 - 1 = 1
+ # A[0][1] = B[0][1]*C[0][0] + B[1][1]*C[0][1] = -2 + 4 = 2
+ # A[1][1] = B[0][1]*C[1][0] + B[1][1]*C[1][1] = -1 + 4 = 3
+
+ if (abs(A[0][0] - 3.0) > 0.0001)
+  $PASS = 0
+ end
+ if (abs(A[0][1] - 2.0) > 0.0001)
+  $PASS = 0
+ end
+ if (abs(A[1][0] - 1.0) > 0.0001)
+  $PASS = 0
+ end
+ if (abs(A[1][1] - 3.0) > 0.0001)
+  $PASS = 0
+ end
+end
+
+# a very simple off-diagonal matrix equation
+macro test2
+ $PASS = 1
+ break -auto off
+
+ delete -q A B
+
+ mcreate A 2 2
+
+ A[0][0] =  2
+ A[1][0] = +1
+ A[0][1] = -1
+ A[1][1] =  4
+
+ matrix transpose A to B
+ if (not($STATUS))
+   $PASS = 0
+ end
+
+ if (abs(B[0][0] - 2.0) > 0.0001)
+  $PASS = 0
+ end
+ if (abs(B[0][1] - 1.0) > 0.0001)
+  $PASS = 0
+ end
+ if (abs(B[1][0] + 1.0) > 0.0001)
+  $PASS = 0
+ end
+ if (abs(B[1][1] - 4.0) > 0.0001)
+  $PASS = 0
+ end
+end
Index: /branches/sc_branches/pantasks_condor/cmd.data/test/peak.sh
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.data/test/peak.sh	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.data/test/peak.sh	(revision 34783)
@@ -0,0 +1,103 @@
+
+list tests
+ test1
+ test2
+ test3
+ testmem1
+end
+
+# test using full range
+macro test1
+ $PASS = 1
+ break -auto off
+
+ create x 0 100
+ set y = zero (x)
+ y[50] = 1
+
+ peak -q x y 0 100
+
+ if ($peakpos != 50)
+   $PASS = 0
+ end
+ if ($peaknum != 50)
+   $PASS = 0
+ end
+ if ($peakval != 1)
+   $PASS = 0
+ end
+end
+
+# test using auto range
+macro test2
+ $PASS = 1
+ break -auto off
+
+ create x 0 100
+ set y = zero (x)
+ y[50] = 1
+
+ peak -q x y 
+
+ if ($peakpos != 50)
+   $PASS = 0
+ end
+ if ($peaknum != 50)
+   $PASS = 0
+ end
+ if ($peakval != 1)
+   $PASS = 0
+ end
+end
+
+# test using constrained range
+macro test3
+ $PASS = 1
+ break -auto off
+
+ create x 0 100
+ set y = zero (x)
+ y[60] = 2
+ y[50] = 1
+ y[40] = 2
+
+ peak -q x y 45 55
+
+ if ($peakpos != 50)
+   $PASS = 0
+ end
+ if ($peaknum != 50)
+   $PASS = 0
+ end
+ if ($peakval != 1)
+   $PASS = 0
+ end
+end
+
+# test memory usage
+macro testmem1
+ break -auto off
+
+ create x 0 1000
+ set y = zero (x)
+ y[500] = 100
+
+ list word -x "ps -p $PID -o rss"
+ $startmem = $word:1
+
+ for i 0 10000
+   peak -q x y 400 600
+ end
+ 
+ list word -x "ps -p $PID -o rss"
+ $endmem = $word:1
+
+ if ({$endmem - $startmem} < 10)
+   $PASS = 1
+ else
+   $PASS = 0
+   echo "growth: {$endmem-$startmem}"
+   echo "kB/loop: {($endmem-$startmem)/10000}"
+ end
+
+end
Index: /branches/sc_branches/pantasks_condor/cmd.data/test/periodogram.sh
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.data/test/periodogram.sh	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.data/test/periodogram.sh	(revision 34783)
@@ -0,0 +1,143 @@
+
+list tests
+ test1
+ test2
+ test3
+ memtest1
+end
+
+# test using even samples
+macro test1
+ $PASS = 1
+ break -auto off
+
+ local P PI
+ $PI = 3.14159265359
+ $P  = 15.0
+
+ delete -q t f period power
+
+ create t 0 100
+ set f = sin(2*$PI*t/$P)
+ periodogram t f 5 50 period power
+
+ peak -q period power
+
+ if (abs ($peakpos - $P) > 0.5)
+   $PASS = 0
+   echo "OFFSET: {$peakpos - $P}"
+ end
+end
+
+# test using random samples
+macro test2
+ $PASS = 1
+ break -auto off
+
+ local P PI
+ $PI = 3.14159265359
+ $P  = 15.0
+
+ delete -q x t f period power
+
+ create x 0 100
+ set t = 100 * rnd(x) 
+ set f = sin(2*$PI*t/$P)
+ periodogram t f 5 50 period power
+
+ # lim -n 0 t f; clear; box; plot -x 2 -pt 2 t f
+ # lim -n 1 period power; clear; box; plot period power
+
+ peak -q period power
+
+ if (abs ($peakpos - $P) > 0.5)
+   $PASS = 0
+ end
+end
+
+# test using random samples, higher frequency
+macro test3
+ $PASS = 1
+ break -auto off
+
+ local P PI
+ $PI = 3.14159265359
+ $P  = 2.0
+
+ delete -q x t f period power
+
+ create x 0 100
+ set t = 100 * rnd(x) 
+ set f = sin(2*$PI*t/$P)
+
+ periodogram t f 1 10 period power
+
+ # lim -n 0 t f; clear; box; plot -x 2 -pt 2 t f
+ # lim -n 1 period power; clear; box; plot period power
+
+ peak -q period power
+
+ if (abs ($peakpos - $P) > 0.05)
+   $PASS = 0
+ end
+end
+
+# test using random samples, offset start
+macro test4
+ $PASS = 1
+ break -auto off
+
+ local P PI
+ $PI = 3.14159265359
+ $P  = 15.0
+
+ delete -q x t f period power
+
+ create x 500 800
+ set t = 300 * rnd(x) + 500
+ set f = sin(2*$PI*t/$P)
+
+ periodogram t f 2 30 period power
+
+#  lim -n 0 t f; clear; box; plot -x 2 -pt 2 t f
+#  lim -n 1 period power; clear; box; plot period power
+
+ peak -q period power
+
+ if (abs ($peakpos - $P) > 0.05)
+   $PASS = 0
+ end
+end
+
+# Memory test
+macro memtest1
+
+ local i
+ local P PI
+ $PI = 3.14159265359
+ $P  = 15.0
+
+ delete -q x t f period power
+
+ create x 500 800
+ set t = 300 * rnd(x) + 500
+ set f = sin(2*$PI*t/$P)
+
+ list word -x "ps -p $PID -o rss"
+ $startmem = $word:1
+
+ for i 0 100
+  periodogram t f 2 30 period power
+ end
+  
+ list word -x "ps -p $PID -o rss"
+ $endmem = $word:1
+
+ $PASS = 1
+
+ if ($endmem - $startmem > 180)
+   $PASS = 0
+   echo "growth: {$endmem-$startmem}"
+   echo "kB/loop: {($endmem-$startmem)/100}"
+ end
+end
Index: /branches/sc_branches/pantasks_condor/cmd.data/test/queues.sh
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.data/test/queues.sh	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.data/test/queues.sh	(revision 34783)
@@ -0,0 +1,347 @@
+
+list tests
+ test1
+ test2
+ test3
+ test4
+ test4.1
+ test5
+ test5.1
+ testmem1
+ testmem2
+ testmem3
+ testmem4.0
+ testmem4.1
+ testmem4.2
+ testmem4.3
+ testmem5.0
+ testmem5.1
+end
+
+# test queueinit
+macro test1
+ $PASS = 1
+ queueinit dummy
+ queuesize dummy -var N
+
+ if ($N != 0)
+   $PASS = 0
+ end
+end
+
+# test queueinit memory
+macro testmem1
+ $PASS = 1
+ list word -x "ps -p $PID -o rss"
+ $startmem = $word:1
+
+ output /dev/null
+ for i 0 10000
+   queueinit dummy
+ end
+ output stdout
+
+ list word -x "ps -p $PID -o rss"
+ $endmem = $word:1
+
+ if ({$endmem - $startmem} > 10)
+   $PASS = 0
+   echo "growth: {$endmem-$startmem}"
+   echo "kB/loop: {($endmem-$startmem)/10000}"
+ end
+end
+
+# test queuesize
+macro test2
+ $PASS = 1
+ queueinit dummy
+ queuepush dummy foobar
+ queuesize dummy -var N
+
+ if ($N != 1)
+   $PASS = 0
+ end
+end
+
+# test queuesize memory
+macro testmem2
+ $PASS = 1
+ queueinit dummy
+ queuepush dummy foobar
+
+ list word -x "ps -p $PID -o rss"
+ $startmem = $word:1
+
+ output /dev/null
+ for i 0 10000
+   queuesize dummy -var N
+ end
+ output stdout
+
+ list word -x "ps -p $PID -o rss"
+ $endmem = $word:1
+
+ if ({$endmem - $startmem} > 10)
+   $PASS = 0
+   echo "growth: {$endmem-$startmem}"
+   echo "kB/loop: {($endmem-$startmem)/10000}"
+ end
+end
+
+# test queuedelete
+macro test3
+ $PASS = 1
+ queueinit dummy
+ queuepush dummy foobar
+ queuepush dummy foobar
+ queuepush dummy foobar
+
+ queuedelete dummy
+ queuepush dummy foobar
+ queuesize dummy -var N
+
+ if ($N != 1)
+   $PASS = 0
+ end
+end
+
+# test queuedelete memory
+macro testmem3
+ $PASS = 1
+ list word -x "ps -p $PID -o rss"
+ $startmem = $word:1
+
+ output /dev/null
+ for i 0 10000
+   queueinit dummy
+   queuepush dummy foobar
+   queuedelete dummy
+ end
+ output stdout
+
+ list word -x "ps -p $PID -o rss"
+ $endmem = $word:1
+
+ if ({$endmem - $startmem} > 10)
+   $PASS = 0
+   echo "growth: {$endmem-$startmem}"
+   echo "kB/loop: {($endmem-$startmem)/10000}"
+ end
+end
+
+# test queuepush / queuepop
+macro test4
+ $PASS = 1
+ queueinit dummy
+ queuepush dummy foobar
+ queuepop  dummy -var N
+
+ if ("$N" != "foobar")
+   $PASS = 0
+ end
+end
+
+# test queuepush / queuepop
+macro test4.1
+ $PASS = 1
+ queueinit dummy
+ queuepush dummy foo1
+ queuepush dummy foo2
+ queuepush dummy foo3
+
+ queuepop  dummy -var N
+ if ("$N" != "foo1")
+   $PASS = 0
+ end
+ queuepop  dummy -var N
+ if ("$N" != "foo2")
+   $PASS = 0
+ end
+ queuepop  dummy -var N
+ if ("$N" != "foo3")
+   $PASS = 0
+ end
+ queuesize dummy -var N
+ if ($N != 0)
+   $PASS = 0
+ end
+end
+
+# test queuepush / queuepop memory
+macro testmem4.0
+ $PASS = 1
+ list word -x "ps -p $PID -o rss"
+ $startmem = $word:1
+
+ output /dev/null
+ for i 0 10000
+   queueinit dummy
+   queuepush dummy foobar
+   queuepop dummy -var N
+ end
+ output stdout
+
+ list word -x "ps -p $PID -o rss"
+ $endmem = $word:1
+
+ if ({$endmem - $startmem} > 10)
+   $PASS = 0
+   echo "growth: {$endmem-$startmem}"
+   echo "kB/loop: {($endmem-$startmem)/10000}"
+ end
+end
+
+# test queuepush / queuepop memory
+macro testmem4.1
+ $PASS = 1
+ list word -x "ps -p $PID -o rss"
+ $startmem = $word:1
+
+ output /dev/null
+ for i 0 10000
+   queueinit dummy
+   queuepush dummy foobar
+   queuepop dummy
+ end
+ output stdout
+
+ list word -x "ps -p $PID -o rss"
+ $endmem = $word:1
+
+ if ({$endmem - $startmem} > 10)
+   $PASS = 0
+   echo "growth: {$endmem-$startmem}"
+   echo "kB/loop: {($endmem-$startmem)/10000}"
+ end
+end
+
+# test queuepush / queuepop memory
+macro testmem4.2
+ $PASS = 1
+ list word -x "ps -p $PID -o rss"
+ $startmem = $word:1
+
+ output /dev/null
+ for i 0 10000
+   queueinit dummy
+   queuepush dummy foobar
+ end
+ output stdout
+
+ list word -x "ps -p $PID -o rss"
+ $endmem = $word:1
+
+ if ({$endmem - $startmem} > 10)
+   $PASS = 0
+   echo "growth: {$endmem-$startmem}"
+   echo "kB/loop: {($endmem-$startmem)/10000}"
+ end
+end
+
+# test queuepush / queuepop memory
+macro testmem4.3
+ $PASS = 1
+ list word -x "ps -p $PID -o rss"
+ $startmem = $word:1
+
+ queueinit dummy
+
+ output /dev/null
+ for i 0 10000
+   queuepush dummy foobar
+   queuepop dummy
+ end
+ output stdout
+
+ list word -x "ps -p $PID -o rss"
+ $endmem = $word:1
+
+ if ({$endmem - $startmem} > 10)
+   $PASS = 0
+   echo "growth: {$endmem-$startmem}"
+   echo "kB/loop: {($endmem-$startmem)/10000}"
+ end
+end
+
+# test queuepush / queuepop with keys
+macro test5
+ $PASS = 1
+ queueinit dummy
+ queuepush dummy "test 1 word"
+ queuepush dummy "test 2 word"
+ queuepush dummy "test 3 word"
+ queuepop  dummy -var N -key 1 2
+
+ if ("$N" != "test 2 word")
+   $PASS = 0
+ end
+end
+
+# test queuepush / queuepop with keys
+macro test5.1
+ $PASS = 1
+ queueinit dummy
+ queuepush dummy "test 1 word"
+ queuepush dummy "test 2 word"
+ queuepush dummy "bird 2 word"
+ queuepop  dummy -var N -key 0:1 test:2
+
+ if ("$N" != "test 2 word")
+   $PASS = 0
+ end
+end
+
+# memory test for queuepush / queuepop with keys
+macro testmem5.0
+ $PASS = 1
+ queueinit dummy
+
+ list word -x "ps -p $PID -o rss"
+ $startmem = $word:1
+
+ output /dev/null
+ for i 0 10000
+   queuepush dummy "test 1 word"
+   queuepush dummy "test 2 word"
+   queuepush dummy "test 3 word"
+   queuepop dummy -var N -key 1 2
+   queuepop dummy -var N -key 1 1
+   queuepop dummy -var N -key 1 3
+ end
+ output stdout
+
+ list word -x "ps -p $PID -o rss"
+ $endmem = $word:1
+
+ if ({$endmem - $startmem} > 10)
+   $PASS = 0
+   echo "growth: {$endmem-$startmem}"
+   echo "kB/loop: {($endmem-$startmem)/10000}"
+   queuelist
+ end
+end
+
+# memory test for queuepush / queuepop with keys
+macro testmem5.1
+ $PASS = 1
+ queueinit dummy
+
+ list word -x "ps -p $PID -o rss"
+ $startmem = $word:1
+
+ output /dev/null
+ for i 0 10000
+   queuepush dummy "test 1 word"
+   queuepop dummy -var N -key 1 1
+ end
+ output stdout
+
+ list word -x "ps -p $PID -o rss"
+ $endmem = $word:1
+
+ if ({$endmem - $startmem} > 10)
+   $PASS = 0
+   echo "growth: {$endmem-$startmem}"
+   echo "kB/loop: {($endmem-$startmem)/10000}"
+ end
+end
Index: /branches/sc_branches/pantasks_condor/cmd.data/test/read.sh
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.data/test/read.sh	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.data/test/read.sh	(revision 34783)
@@ -0,0 +1,145 @@
+
+list tests
+ #test1
+ #test2
+ #test3
+ #test4
+ test5
+ test6
+end
+
+# basic read
+macro test1
+ 
+ $PASS = 1
+
+ data read.t1.dat
+ read t1 1 t2 2 t3 3
+
+ if (t1[0] != 1) set PASS = 0
+ if (t1[1] != 2) set PASS = 0
+ if (t1[2] != 4) set PASS = 0
+
+ if (t2[0] != 5) set PASS = 0
+ if (t2[1] != 3) set PASS = 0
+ if (t2[2] != 5) set PASS = 0
+
+ if (t3[0] != 8) set PASS = 0
+ if (t3[1] != 9) set PASS = 0
+ if (t3[2] != 7) set PASS = 0
+end
+
+# basic read with int types
+macro test2
+ 
+ $PASS = 1
+
+ data read.t1.dat
+ read t1:int 1 t2:int 2 t3:int 3
+
+ # note: no way to verify the mode of a vector
+ vectors
+
+ if (t1[0] != 1) set PASS = 0
+ if (t1[1] != 2) set PASS = 0
+ if (t1[2] != 4) set PASS = 0
+
+ if (t2[0] != 5) set PASS = 0
+ if (t2[1] != 3) set PASS = 0
+ if (t2[2] != 5) set PASS = 0
+
+ if (t3[0] != 8) set PASS = 0
+ if (t3[1] != 9) set PASS = 0
+ if (t3[2] != 7) set PASS = 0
+end
+
+# basic read with comments
+macro test3
+ 
+ $PASS = 1
+
+ data read.t2.dat
+ read t1 1 t2 2 t3 3
+
+ if (t1[0] != 1) set PASS = 0
+ if (t1[1] != 2) set PASS = 0
+ if (t1[2] != 4) set PASS = 0
+
+ if (t2[0] != 5) set PASS = 0
+ if (t2[1] != 3) set PASS = 0
+ if (t2[2] != 5) set PASS = 0
+
+ if (t3[0] != 8) set PASS = 0
+ if (t3[1] != 9) set PASS = 0
+ if (t3[2] != 7) set PASS = 0
+end
+
+# basic read with comments & int types
+macro test4
+ 
+ $PASS = 1
+
+ data read.t2.dat
+ read t1:int 1 t2:int 2 t3:int 3
+
+ # note: no way to verify the mode of a vector
+ vectors
+
+ if (t1[0] != 1) set PASS = 0
+ if (t1[1] != 2) set PASS = 0
+ if (t1[2] != 4) set PASS = 0
+
+ if (t2[0] != 5) set PASS = 0
+ if (t2[1] != 3) set PASS = 0
+ if (t2[2] != 5) set PASS = 0
+
+ if (t3[0] != 8) set PASS = 0
+ if (t3[1] != 9) set PASS = 0
+ if (t3[2] != 7) set PASS = 0
+end
+
+# read with invalid entries
+macro test5
+ 
+ $PASS = 1
+
+ data read.t3.dat
+ read t1 1 t2 2 t3 3
+
+ if (t1[0] != 1) set PASS = 0
+ if (t1[1] != 2) set PASS = 0
+ if (t1[2] != 4) set PASS = 0
+
+ if (t2[0] != 5) set PASS = 0
+ if (not(isnan(t2[1]))) set PASS = 0
+ if (t2[2] != 5) set PASS = 0
+
+ if (t3[0] != 8) set PASS = 0
+ if (t3[1] != 9) set PASS = 0
+ if (not(isnan(t3[2]))) set PASS = 0
+end
+
+# read with invalid entries & int types
+macro test6
+ 
+ $PASS = 1
+
+ data read.t3.dat
+ read t1:int 1 t2:int 2 t3:int 3
+
+ # note: no way to verify the mode of a vector
+ vectors
+
+ if (t1[0] != 1) set PASS = 0
+ if (t1[1] != 2) set PASS = 0
+ if (t1[2] != 4) set PASS = 0
+
+ if (t2[0] != 5) set PASS = 0
+ if (t2[1] != 0) set PASS = 0
+ if (t2[2] != 5) set PASS = 0
+
+ if (t3[0] != 8) set PASS = 0
+ if (t3[1] != 9) set PASS = 0
+ if (t3[2] != 0) set PASS = 0
+end
+
Index: /branches/sc_branches/pantasks_condor/cmd.data/test/read.t1.dat
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.data/test/read.t1.dat	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.data/test/read.t1.dat	(revision 34783)
@@ -0,0 +1,3 @@
+1 5 8
+2 3 9
+4 5 7
Index: /branches/sc_branches/pantasks_condor/cmd.data/test/read.t2.dat
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.data/test/read.t2.dat	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.data/test/read.t2.dat	(revision 34783)
@@ -0,0 +1,5 @@
+# comment line
+1 5 8
+2 3 9
+! comment line 2
+4 5 7
Index: /branches/sc_branches/pantasks_condor/cmd.data/test/read.t3.dat
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.data/test/read.t3.dat	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.data/test/read.t3.dat	(revision 34783)
@@ -0,0 +1,3 @@
+1 5 8
+2 X 9
+4 5 Y
Index: /branches/sc_branches/pantasks_condor/cmd.data/test/read.t4.dat
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.data/test/read.t4.dat	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.data/test/read.t4.dat	(revision 34783)
@@ -0,0 +1,7 @@
+1 5 8 2000/01/01,00:00:00
+2 3 9 2000/01/01,01:00:00
+4 5 7 2000/01/01,02:00:00
+4 5 7 now
+4 5 7 today
+4 5 7 2451544.750000j
+4 5 7 51544.500000J
Index: /branches/sc_branches/pantasks_condor/cmd.data/test/stats.sh
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.data/test/stats.sh	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.data/test/stats.sh	(revision 34783)
@@ -0,0 +1,156 @@
+
+$skip = 0
+
+list tests
+ test1
+ test2
+ test3
+end
+
+# test using full range
+macro test1
+ $PASS = 1
+ break -auto off
+
+ gaussdev dev {100*100} 0.0 1.0
+ dimenup dev buf 100 100
+ stats -q buf 
+ $npix = 100*100
+
+ if ($STATUS == 0)
+   echo "failed command"
+   $PASS = 0
+ end
+ if ($NPIX != $npix) 
+   echo "failed npix"
+   $PASS = 0
+ end
+ if (abs($MEAN - 0.0) > (2.0/sqrt($npix))) 
+   echo "failed mean"
+   $PASS = 0
+ end
+ if (abs($MEDIAN - 0.0) > (2.0/sqrt($npix))) 
+   echo "failed median"
+   $PASS = 0
+ end
+ if (abs($SIGMA - 1.0) > (2.0/sqrt($npix))) 
+   echo "failed sigma"
+   $PASS = 0
+ end
+ if ($MIN < -4.0) 
+   echo "failed min"
+   $PASS = 0
+ end
+ if ($MAX > +4.0)
+   echo "failed max"
+   $PASS = 0
+ end
+# I don't this MODE is being correctly calculated.
+ if ($skip && (abs($MODE - 0.0) > (2.0/sqrt($npix))))
+   echo "MODE fails: known problem with mode"
+   $PASS = 0
+ end
+ if (abs($TOTAL - $MEAN*$npix) > (2.0/sqrt($npix))) 
+   echo "failed total"
+   $PASS = 0
+ end
+end
+
+# test using full range
+macro test2
+ $PASS = 1
+ break -auto off
+
+ gaussdev dev {100*100} 0.0 1.0
+ dimenup dev buf 100 100
+ stats -q buf  - - - -
+ $npix = 100*100
+
+ if ($STATUS == 0)
+   echo "failed command"
+   $PASS = 0
+ end
+ if ($NPIX != $npix) 
+   echo "failed npix"
+   $PASS = 0
+ end
+ if (abs($MEAN - 0.0) > (2.0/sqrt($npix))) 
+   echo "failed mean"
+   $PASS = 0
+ end
+ if (abs($MEDIAN - 0.0) > (2.0/sqrt($npix))) 
+   echo "failed median"
+   $PASS = 0
+ end
+ if (abs($SIGMA - 1.0) > (2.0/sqrt($npix))) 
+   echo "failed sigma"
+   $PASS = 0
+ end
+ if ($MIN < -4.0) 
+   echo "failed min"
+   $PASS = 0
+ end
+ if ($MAX > +4.0)
+   echo "failed max"
+   $PASS = 0
+ end
+# I don't this MODE is being correctly calculated.
+ if ($skip && (abs($MODE - 0.0) > (2.0/sqrt($npix))))
+   echo "MODE fails: known problem with mode"
+   $PASS = 0
+ end
+ if (abs($TOTAL - $MEAN*$npix) > (2.0/sqrt($npix))) 
+   echo $TOTAL {$MEAN*$npix} {abs($TOTAL - $MEAN*$npix) > (2.0/sqrt($npix))) 
+   echo "failed total"
+   $PASS = 0
+ end
+end
+
+# test using full range
+macro test3
+ $PASS = 1
+ break -auto off
+
+ gaussdev dev {100*100} 0.0 1.0
+ dimenup dev buf 100 100
+ stats -q buf 10 10 10 10
+ $npix = 10*10
+
+ if ($STATUS == 0)
+   echo "failed command"
+   $PASS = 0
+ end
+ if ($NPIX != $npix) 
+   echo "failed npix"
+   $PASS = 0
+ end
+ if (abs($MEAN - 0.0) > (2.0/sqrt($npix))) 
+   echo "failed mean"
+   $PASS = 0
+ end
+ if (abs($MEDIAN - 0.0) > (2.0/sqrt($npix))) 
+   echo "failed median"
+   $PASS = 0
+ end
+ if (abs($SIGMA - 1.0) > (2.0/sqrt($npix))) 
+   echo "failed sigma"
+   $PASS = 0
+ end
+ if ($MIN < -4.0) 
+   echo "failed min"
+   $PASS = 0
+ end
+ if ($MAX > +4.0)
+   echo "failed max"
+   $PASS = 0
+ end
+# I don't this MODE is being correctly calculated.
+ if ($skip && (abs($MODE - 0.0) > (2.0/sqrt($npix))))
+   echo "MODE fails: known problem with mode"
+   $PASS = 0
+ end
+ if (abs($TOTAL - $MEAN*$npix) > (2.0/sqrt($npix))) 
+   echo "failed total"
+   $PASS = 0
+ end
+end
Index: /branches/sc_branches/pantasks_condor/cmd.data/test/svd.sh
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.data/test/svd.sh	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.data/test/svd.sh	(revision 34783)
@@ -0,0 +1,91 @@
+
+list tests
+ test1
+ test2
+end
+
+# a very simple diagonal matrix equation
+macro test1
+ $PASS = 1
+ break -auto off
+
+ delete -q A U w Vt W A1
+
+ mcreate A 2 2
+
+ A[0][0] = 1.0
+ A[1][0] = 0.5
+
+ A[0][1] = 0.5
+ A[1][1] = 2.0
+
+ # svd give A = U w Vt, but this function returns V, not Vt:
+ svd A = U w V
+ if (not($STATUS))
+   $PASS = 0
+ end
+
+ # can we recreate A?
+ mcreate W w[] w[]
+ for i 0 w[]
+   W[$i][$i] = w[$i]
+ end
+
+ matrix transpose V to Vt
+ matrix set t1 = W * Vt
+ matrix set A1 = U * t1
+ set dA = A1 - A
+
+ stats -q dA
+ if ($MIN < -0.0001)
+   $PASS = 0
+ end
+ if ($MAX >  0.0001)
+   $PASS = 0
+ end
+end
+
+# a very simple diagonal matrix equation
+macro test2
+ $PASS = 1
+ break -auto off
+
+ delete -q A U w Vt W A1
+
+ mcreate A 2 3
+
+ A[0][0] = 1.0
+ A[1][0] = 0.0
+
+ A[0][1] = 0.0
+ A[1][1] = 2.0
+
+ A[0][2] = 0.0
+ A[1][2] = 1.0
+
+ # svd give A = U w Vt, but this function returns V, not Vt:
+ svd A = U w V
+ if (not($STATUS))
+   $PASS = 0
+ end
+
+ # can we recreate A?
+ mcreate W w[] w[]
+ for i 0 w[]
+   W[$i][$i] = w[$i]
+ end
+
+ matrix transpose V to Vt
+ matrix set t1 = W * Vt
+ matrix set A1 = U * t1
+ set dA = A1 - A
+
+ stats -q dA
+ if ($MIN < -0.0001)
+   $PASS = 0
+ end
+ if ($MAX >  0.0001)
+   $PASS = 0
+ end
+end
+
Index: /branches/sc_branches/pantasks_condor/cmd.data/test/vgauss.sh
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.data/test/vgauss.sh	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.data/test/vgauss.sh	(revision 34783)
@@ -0,0 +1,112 @@
+
+list tests
+ test1
+ test2
+ test3
+end
+
+macro test1
+ $PASS = 1
+ break -auto off
+
+ create x -10 10 0.1
+ set y = 5 * exp(-0.5*x^2/3^2)
+
+ $C0 = 0.5
+ $C1 = 2
+ $C2 = 10
+ $C3 = 1
+ set dy = sqrt(y)
+
+ vgauss -q x y dy yfit
+ # lim x y; clear; box; plot -x 1 -c black x y; plot -c red x yfit
+ # cursor
+
+ if (abs($C0 - 0.0) > 0.01)
+   $PASS = 0
+ end
+ if (abs($C1 - 3.0) > 0.01)
+   $PASS = 0
+ end
+ if (abs($C2 - 5.0) > 0.01)
+   $PASS = 0
+ end
+ if (abs($C3 - 0.0) > 0.01)
+   $PASS = 0
+ end
+end
+
+# noise of 0.1
+macro test2
+ $PASS = 1
+ break -auto off
+
+ create x -10 10 0.1
+ set y = 1000 * exp(-0.5*x^2/3^2)
+ set dy = (rnd(y) - 0.5)/0.5
+ set y = y + dy
+
+ $C0 = 0.5
+ $C1 = 2
+ $C2 = 900
+ $C3 = 1
+
+ vgauss -q x y dy yfit
+ # lim x y; clear; box; plot -x 1 -c black x y; plot -c red x yfit
+ # cursor
+
+ if (abs($C0 - 0.0) > 0.01)
+   $PASS = 0
+ end
+ if (abs($C1 - 3.0) > 0.01)
+   $PASS = 0
+ end
+ if (abs($C2 - 1000.0) > 0.1)
+   $PASS = 0
+ end
+ if (abs($C3 - 0.0) > 0.1)
+   $PASS = 0
+ end
+end
+
+# poisson-distributed noise
+macro test3
+ $PASS = 1
+ break -auto off
+
+ $C0o = 0.5
+ $C1o = 2
+ $C2o = 900
+ $C3o = 1
+
+ create x -10 10 0.1
+ set y = $C3o + $C2o * exp(-0.5*(x - $C0o)^2/$C1o^2)
+
+ gaussdev dY y[] 0.0 1.0
+ set dy = dY * sqrt(y)
+ set y = y + dy
+
+ $C0 = $C0o + 2
+ $C1 = $C1o + 2
+ $C2 = $C2o + 50
+ $C3 = $C3o + 10
+
+ vgauss -q x y dy yfit
+ # lim x y; clear; box; plot -x 1 -c black x y; plot -c red x yfit
+ # cursor
+
+ $dS = 3.0 / sqrt(y[])
+
+ if (abs($C0 - $C0o) > $dS)
+   $PASS = 0
+ end
+ if (abs($C1 - $C1o)/$C1o > $dS)
+   $PASS = 0
+ end
+ if (abs($C2 - $C2o)/$C2o > $dS)
+   $PASS = 0
+ end
+ if (abs($C3 - $C3o) > $dS)
+   $PASS = 0
+ end
+end
Index: /branches/sc_branches/pantasks_condor/cmd.data/test/vmaxwell.sh
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.data/test/vmaxwell.sh	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.data/test/vmaxwell.sh	(revision 34783)
@@ -0,0 +1,113 @@
+
+list tests
+# test1
+ test2
+# test3
+end
+
+macro test1
+ $PASS = 1
+ break -auto off
+
+ create x -10 10 0.1
+ set y = 5 * exp(-0.5*x^2/3^2)
+
+ $C0 = 0.5
+ $C1 = 2
+ $C2 = 10
+ $C3 = 1
+ set dy = sqrt(y)
+
+ vgauss -q x y dy yfit
+
+ if (abs($C0 - 0.0) > 0.01)
+   $PASS = 0
+ end
+ if (abs($C1 - 3.0) > 0.01)
+   $PASS = 0
+ end
+ if (abs($C2 - 5.0) > 0.01)
+   $PASS = 0
+ end
+ if (abs($C3 - 0.0) > 0.01)
+   $PASS = 0
+ end
+end
+
+# noise of 0.1
+macro test2
+ $PASS = 1
+ break -auto off
+
+ $C0o = 250
+ $C1o = 25
+ $C2o = 100
+ $C3o = 1
+ $C4o = 10
+
+ create x 0 1000 1
+ set y = $C2o * (x-$C4o)^2 * exp(-0.5*(x-$C0o)^2/$C1o^2) + $C3o
+ gaussdev dY y[] 0.0 1.0
+ set dy = dY * sqrt(y)
+ set y = y + dy
+
+ $C0 = $C0o + 20
+ $C1 = $C1o + 2
+ $C2 = $C2o + 100
+ $C3 = $C3o + 10
+ $C4 = $C4o + 20
+
+ vmaxwell -q x y dy yfit
+ lim x y; clear; box; plot -x 1 -c black x y; plot x yfit -c red
+
+ $dS = 6.0 / sqrt(y[])
+
+ if (abs($C0 - $C0o) > $dS)
+   $PASS = 0
+ end
+ if (abs($C1 - $C1o)/$C1o > $dS)
+   $PASS = 0
+ end
+ if (abs($C2 - $C2o)/$C2o > $dS)
+   $PASS = 0
+ end
+ if (abs($C3 - $C3o) > $dS)
+   $PASS = 0
+ end
+ if (abs($C4 - $C4o)/$C4o > $dS)
+   $PASS = 0
+ end
+end
+
+# poisson-distributed noise
+macro test3
+ $PASS = 1
+ break -auto off
+
+ create x -10 10 0.1
+ set y = 1000 * exp(-0.5*x^2/3^2)
+
+ gaussdev dY y[] 0.0 1.0
+ set dy = dY * sqrt(y)
+ set y = y + dy
+
+ $C0 = 0.5
+ $C1 = 2
+ $C2 = 900
+ $C3 = 1
+
+ vgauss -q x y dy yfit
+
+ if (abs($C0 - 0.0) > 0.01)
+   $PASS = 0
+ end
+ if (abs($C1 - 3.0) > 0.01)
+   $PASS = 0
+ end
+ if (abs($C2 - 1000.0) > 1)
+   $PASS = 0
+ end
+ if (abs($C3 - 0.0) > 0.2)
+   $PASS = 0
+ end
+end
Index: /branches/sc_branches/pantasks_condor/cmd.data/teststats.c
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.data/teststats.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.data/teststats.c	(revision 34783)
@@ -0,0 +1,117 @@
+# include "data.h"
+
+int teststats (int argc, char **argv) {
+  
+  int i, N;
+  double max, min, sum, var, dvar, mean, stdev;
+  float *X, IgnoreValue;
+  int Ignore, Quiet;
+
+  int *Nval, bin, Nmode, Nmed;
+  double dx, mode, median;
+  Vector *vec;
+
+  Ignore = FALSE;
+  if (N = get_argument (argc, argv, "-ignore")) {
+    Ignore = TRUE;
+    remove_argument (N, &argc, argv);
+    IgnoreValue = atof(argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  Quiet = FALSE;
+  if (N = get_argument (argc, argv, "-q")) {
+    Quiet = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+  if (N = get_argument (argc, argv, "-quiet")) {
+    Quiet = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+
+  if (argc != 2) {
+    gprint (GP_ERR, "USAGE: teststats (vector)\n");
+    return (FALSE);
+  }
+
+  if ((vec = SelectVector (argv[1], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+
+  Mean = Stdev = 0; 
+  /* first pass: measure the sample statistics */
+  for (j = 0; j < 3; j++) {
+    /* calculate mean, npix */
+    X = vec[0].elements;
+    max = min = *X;
+    sum = N = 0;
+    for (i = 0; i < vec[0].Nelements; i++, X++) {
+      if (!finite (*X)) continue;
+      if (Ignore && (*X == IgnoreValue)) continue;
+      if ((j > 0) && (fabs (*X - Mean) > 3*stdev)) continue;
+      sum += *X;
+      N++;
+    }      
+    mean = sum / N;
+    /* calculate stdev */
+    X = vec[0].elements;
+    var = 0;
+    for (i = 0; i < vec[0].Nelements; i++, X++) {
+      if (!finite (*X)) continue;
+      if (Ignore && (*X == IgnoreValue)) continue;
+      if ((j > 0) && (fabs (*X - mean) > 3*stdev)) continue;
+      dvar = (*X - mean);
+      var += dvar*dvar;
+    }      
+    stdev = sqrt (var / N);
+    Mean = mean;
+  }
+  mean = Mean;
+  set_variable ("MEAN_C",     mean);
+  set_variable ("SIGMA_C",    stdev);
+
+  /* construct histogram with resolution of 0.1*stdev and range -1000*dx : 1000*dx centered on mean */
+  dx = 0.1*stdev;
+  min = mean - 1000*dx;
+  max = mean + 1000*dx;
+  NVAL = 1 + (int)((max - min) / dx);
+  ALLOCATE (Nval, int, NVAL);
+  bzero (Nval, NVAL*sizeof(int));
+  X = vec[0].elements;
+  for (i = 0; i < vec[0].Nelements; i++, X++) {
+    if (!finite (*X)) continue;
+    if (Ignore && (*X == IgnoreValue)) continue;
+    bin = MAX (0, MIN (NVAL, (*X - min) / dx));
+    Nval[bin] ++;
+  }      
+
+  /* find mode */
+  Nmode = 0;
+  mode = Nval[Nmode];
+  for (i = 0; i < NVAL; i++) {
+    if (mode < Nval[i]) {
+      Nmode = i;
+      mode = Nval[Nmode];
+    }
+  }
+  mode = Nmode * dx + min;
+
+  
+
+  if (!Quiet) {
+    gprint (GP_ERR, "mean: %f, stdev: %f, min: %f, max: %f, median: %f, mode: %f, Npts: %d\n", 
+	     mean, stdev, min, max, median, mode, N);
+  }
+
+  set_variable ("MIN",      min);
+  set_variable ("MAX",      max);
+  set_variable ("MEDIAN",   median);
+  set_variable ("MODE",     mode);
+  set_variable ("TOTAL",    sum);
+  set_variable ("MEAN",     mean);
+  set_variable ("SIGMA",    stdev);
+
+  set_int_variable ("NPIX", N);
+
+  free (Nval);
+  return (TRUE);
+}
+
Index: /branches/sc_branches/pantasks_condor/cmd.data/textline.c
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.data/textline.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.data/textline.c	(revision 34783)
@@ -0,0 +1,56 @@
+# include "data.h"
+
+int textline (int argc, char **argv) {
+
+  int N, size, FracPositions;
+  char name[64];
+  double x, y, angle;
+  int kapa;
+  Graphdata graphmode;
+
+  if (!style_args (&graphmode, &argc, argv, &kapa)) return (FALSE);
+
+  if ((N = get_argument (argc, argv, "-fn"))) {
+    remove_argument (N, &argc, argv);
+    strcpy (name, argv[N]);
+    remove_argument (N, &argc, argv);
+    size = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+    KapaSetFont (kapa, name, size);
+  } 
+
+  /* FracPositions uses coordinates of 0-1 relative to axis range */
+  FracPositions = FALSE;
+  if ((N = get_argument (argc, argv, "-frac"))) {
+    remove_argument (N, &argc, argv);
+    FracPositions = TRUE;
+  } 
+
+  angle = 0.0;
+  if ((N = get_argument (argc, argv, "-rot"))) {
+    remove_argument (N, &argc, argv);
+    angle = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+  } 
+
+  if (argc != 4) {
+    gprint (GP_ERR, "USAGE: text x y (line) [-fn (font) size] [-rot angle]\n");
+    return (FALSE);
+  }
+
+  if (strlen (argv[3]) > 127) {
+    gprint (GP_ERR, "labels currently limited to 127 chars\n");
+    return (FALSE);
+  }
+
+  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;
+  }    
+
+  KapaSendTextline (kapa, argv[3], x, y, angle);
+  return (TRUE);
+}
Index: /branches/sc_branches/pantasks_condor/cmd.data/threshold.c
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.data/threshold.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.data/threshold.c	(revision 34783)
@@ -0,0 +1,96 @@
+# include "data.h"
+
+int threshold (int argc, char **argv) {
+  
+  int N, QUIET;
+  double value;
+  Vector *vecx, *vecy;
+
+  QUIET = FALSE;
+  if ((N = get_argument (argc, argv, "-q"))) {
+    QUIET = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+
+  if (argc != 4) {
+    gprint (GP_ERR, "USAGE: threshold <x> <y> (value)\n");
+    gprint (GP_ERR, "  find the x coordinate at which we pass the specified value\n");
+    gprint (GP_ERR, "  by default, y must be monotonically increasing\n");
+    return (FALSE);
+  }
+  
+  if ((vecx = SelectVector (argv[1], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((vecy = SelectVector (argv[2], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+  
+  value = atof (argv[3]);
+  
+  // if (argc == 6) {
+  //   start = atof (argv[4]);
+  //   end   = atof (argv[5]);
+  // } else {
+  //   start = (vecx[0].type == OPIHI_FLT) ? vecx[0].elements.Flt[0] : vecx[0].elements.Int[0];
+  //   end   = (vecx[0].type == OPIHI_FLT) ? vecx[0].elements.Flt[vecx[0].Nelements - 1] : vecx[0].elements.Int[vecx[0].Nelements - 1];
+  // }
+
+  int isFltX = (vecx[0].type == OPIHI_FLT);
+  int isFltY = (vecy[0].type == OPIHI_FLT);
+
+  // use bisection to find the value
+  int Nlo, Nhi;
+  int Nelements = vecx[0].Nelements;
+
+  // find the last entry before start
+  Nlo = 0;
+  Nhi = Nelements - 1;
+  while (Nhi - Nlo > 10) {
+    N = 0.5*(Nlo + Nhi);
+    double testval = isFltY ? vecy[0].elements.Flt[N] : vecy[0].elements.Int[N];
+    if (testval < value) {
+      Nlo = MAX(N, 0);
+    } else {
+      Nhi = MIN(N, Nelements - 1);
+    }
+  }
+  // v[Nlo] < value <= v[Nhi]
+  for (N = Nlo; N <= Nhi; N++) {
+    double testval = isFltY ? vecy[0].elements.Flt[N] : vecy[0].elements.Int[N];
+    if (testval > value) {
+      Nhi = N;
+      break;
+    }
+  }
+  // v[Nhi] is transition bin
+  
+  double x0, x1, y0, y1, Xvalue;
+  if (Nhi == 0) {
+    // interpolate to value:
+    y0 = isFltY ? vecy[0].elements.Flt[Nhi] : vecy[0].elements.Int[Nhi];
+    y1 = isFltY ? vecy[0].elements.Flt[Nhi+1]   : vecy[0].elements.Int[Nhi+1];
+    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];
+    if (y0 == y1) {
+      Xvalue = 0.5*(x0 + x1);
+    } else {
+      Xvalue = (value - y0) * (x1 - x0) / (y1 - y0) + x0;
+    }    
+  } else {
+    // interpolate to value:
+    y0 = isFltY ? vecy[0].elements.Flt[Nhi-1] : vecy[0].elements.Int[Nhi-1];
+    y1 = isFltY ? vecy[0].elements.Flt[Nhi]   : vecy[0].elements.Int[Nhi];
+    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];
+    if (y0 == y1) {
+      Xvalue = 0.5*(x0 + x1);
+    } else {
+      Xvalue = (value - y0) * (x1 - x0) / (y1 - y0) + x0;
+    }
+  }
+
+  set_variable ("threshval", y1);
+  set_int_variable ("threshbin", Nhi);
+  set_variable ("threshold", Xvalue);
+
+  if (!QUIET) gprint (GP_LOG, "theshold %f (bin %d is %f)\n", Xvalue, Nhi, y1);
+
+  return (TRUE);
+}
Index: /branches/sc_branches/pantasks_condor/cmd.data/tv.c
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.data/tv.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.data/tv.c	(revision 34783)
@@ -0,0 +1,63 @@
+# include "data.h"
+
+int tv (int argc, char **argv) {
+  
+  int N, kapa;
+  char *name, *file;
+  Coords coords;
+  Buffer *buf;
+  KiiImage image;
+  KapaImageData data;
+
+  name = NULL;
+  if ((N = get_argument (argc, argv, "-n"))) {
+    remove_argument (N, &argc, argv);
+    name = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+  if (!GetImage (&data, &kapa, name)) return (FALSE);
+  FREE (name);
+
+  /* shell exits on pipe close, FIX */
+  if ((N = get_argument (argc, argv, "-kill"))) {
+    KiiClose (kapa);
+    return (TRUE);
+  }
+
+  data.logflux = FALSE;
+  if ((N = get_argument (argc, argv, "-log"))) {
+    remove_argument (N, &argc, argv);
+    data.logflux = TRUE;
+  }
+
+  // use the currently-set zero,range values if not supplied
+  if ((argc != 2) && (argc != 4)) {
+    gprint (GP_ERR, "USAGE: tv <buffer> [zero range] [-n Nimage] [-log] [-kill]\n");
+    return (FALSE);
+  }
+
+  if (argc == 4) {
+    data.zero = atof (argv[2]);
+    data.range = atof (argv[3]);
+    if (data.range == 0.0) data.range = 0.001;
+  }
+
+  if ((buf = SelectBuffer (argv[1], OLDBUFFER, TRUE)) == NULL) return (FALSE);
+  GetCoords (&coords, &buf[0].header);
+  
+  image.data1d = (float *) buf[0].matrix.buffer;
+  image.Nx = buf[0].matrix.Naxis[0];
+  image.Ny = buf[0].matrix.Naxis[1];
+
+  // send only the root of the file, not the full path
+  file = filerootname (buf[0].file);
+  strcpy (data.file, file);
+  free (file);
+
+  strcpy (data.name, argv[1]);
+  
+  KiiNewPicture1D (kapa, &image, &data, &coords);
+
+  set_str_variable ("TV", argv[1]);
+  return (TRUE);
+}
Index: /branches/sc_branches/pantasks_condor/cmd.data/tvchannel.c
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.data/tvchannel.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.data/tvchannel.c	(revision 34783)
@@ -0,0 +1,44 @@
+# include "data.h"
+
+int tvchannel (int argc, char **argv) {
+  
+  int N, kapa, Nchannel;
+  char *name;
+  KapaImageData data;
+
+  name = NULL;
+  if ((N = get_argument (argc, argv, "-n"))) {
+    remove_argument (N, &argc, argv);
+    name = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+  if (!GetImage (&data, &kapa, name)) return (FALSE);
+  FREE (name);
+
+  // use the currently-set zero,range values if not supplied
+  if (argc != 2) {
+    gprint (GP_ERR, "USAGE: tvchannel (channel)\n");
+    return (FALSE);
+  }
+
+  Nchannel = atoi (argv[1]);
+  if (Nchannel == 0) {
+    // try the string values R/Red, G/Green, B/Blue
+    if (!strcasecmp (argv[1], "R") || !strcasecmp (argv[1], "RED")) {
+      Nchannel = 1;
+    }
+    if (!strcasecmp (argv[1], "G") || !strcasecmp (argv[1], "GREEN")) {
+      Nchannel = 2;
+    }
+    if (!strcasecmp (argv[1], "B") || !strcasecmp (argv[1], "BLUE")) {
+      Nchannel = 3;
+    }
+  }
+  if ((Nchannel < 1) || (Nchannel > 3)) {
+    gprint (GP_ERR, "invalid channel : use 1 - 3 or (R)ed, (G)reen, (B)lue\n");
+    return (FALSE);
+  }
+    
+  KiiSetChannel (kapa, Nchannel - 1);
+  return (TRUE);
+}
Index: /branches/sc_branches/pantasks_condor/cmd.data/tvcolors.c
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.data/tvcolors.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.data/tvcolors.c	(revision 34783)
@@ -0,0 +1,27 @@
+# include "data.h"
+
+int tvcolors (int argc, char **argv) {
+  
+  int N, kapa;
+  char *name;
+  // KapaImageData data;
+
+  name = NULL;
+  if ((N = get_argument (argc, argv, "-n"))) {
+    remove_argument (N, &argc, argv);
+    name = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+  if (!GetGraph (NULL, &kapa, name)) return (FALSE);
+  FREE (name);
+
+  // use the currently-set zero,range values if not supplied
+  if (argc != 2) {
+    gprint (GP_ERR, "USAGE: tvcolors (colormap)\n");
+    gprint (GP_ERR, " colormap options : greyscale, -greyscale, rainbow, heat, fullcolor, ruffcolor (also grayscale, -grayscale)\n");
+    return (FALSE);
+  }
+
+  KiiSetColormap (kapa, argv[1]);
+  return (TRUE);
+}
Index: /branches/sc_branches/pantasks_condor/cmd.data/tvcontour.c
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.data/tvcontour.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.data/tvcontour.c	(revision 34783)
@@ -0,0 +1,287 @@
+# include "data.h"
+# define LL { \
+ dx =  d00 / (*v01 - *v00); \
+ dy = -d00 / (*v10 - *v00); \
+ x = i - 0.5; \
+ y = j - dy - 0.5; }
+
+# define UL { \
+ tmp = d00 / (*v10 - *v00); \
+ dy = 1 - tmp; \
+ dx =  d10 / (*v11 - *v10); \
+ x = i - 0.5; \
+ y = j + tmp - 0.5; }
+      
+# define LR { \
+ tmp = d00 / (*v01 - *v00); \
+ dx = 1 - tmp; \
+ dy = d01 / (*v11 - *v01); \
+ x = i + tmp - 0.5; \
+ y = j - 0.5; }
+
+# define UR { \
+ tmp = d10 / (*v11 - *v10); \
+ dx = 1 - tmp; \
+ dy = -d11 / (*v01 - *v11); \
+ x = i + tmp - 0.5; \
+ y = j + 1 - 0.5; }
+      
+# define HZ { \
+ tmp = d00 / (*v10 - *v00); \
+ dy = d01 / (*v11 - *v01) - tmp; \
+ dx = 1; \
+ x = i - 0.5; \
+ y = j + tmp - 0.5; }
+
+# define VT { \
+ tmp = d00 / (*v01 - *v00); \
+ dx = d10 / (*v11 - *v10) - tmp; \
+ x = i + tmp - 0.5; \
+ dy = 1; \
+ y = j - 0.5; }
+
+# define DUMP { \
+overlay[Noverlay].type = KII_OVERLAY_LINE; \
+overlay[Noverlay].x = Npix*x; \
+overlay[Noverlay].y = Npix*y; \
+overlay[Noverlay].dx = Npix*dx; \
+overlay[Noverlay].dy = Npix*dy; \
+Noverlay ++; \
+CHECK_REALLOCATE (overlay, KiiOverlay, NOVERLAY, Noverlay, 1000); \
+}
+
+int tvcontour (int argc, char **argv) {
+
+  int i, j, ii, jj, Npix, Nx, Ny;
+  float level, d00, d01, d10, d11, tmp;
+  float x, y, dx, dy;
+  float *v00, *v01, *v10, *v11;
+  float *Vout, *Vin, *matrix;
+  char *name;
+  int kapa, N, Noverlay, NOVERLAY;
+  Buffer *buf;
+  KiiOverlay *overlay;
+  
+  name = NULL;
+  if ((N = get_argument (argc, argv, "-n"))) {
+    remove_argument (N, &argc, argv);
+    name = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+  if (!GetImage (NULL, &kapa, name)) return (FALSE);
+  FREE (name);
+
+  if ((argc != 4) && (argc != 5)) {
+    gprint (GP_ERR, "USAGE: contour <buffer> (overlay) level [Npix]\n");
+    return (FALSE);
+  }
+
+  if ((buf = SelectBuffer (argv[1], OLDBUFFER, TRUE)) == NULL) return (FALSE);
+
+  level = atof (argv[3]);
+  if (argc == 5) 
+    Npix = (int) MAX (atof (argv[4]), 1); 
+  else 
+    Npix = 1;
+  level *= Npix*Npix;
+
+  /*** make rebinned image ***/
+  Nx = buf[0].header.Naxis[0]/Npix;
+  Ny = buf[0].header.Naxis[1]/Npix;
+  if (Npix != 1) {
+    gprint (GP_LOG, "rebin by a factor of %d (%d,%d)\n", Npix, Nx, Ny);
+    ALLOCATE (matrix, float, Nx*Ny);
+    bzero (matrix, Nx*Ny*sizeof(float));
+	
+    for (j = 0; j < Ny; j++) {
+      for (jj = 0; jj < Npix; jj++) {
+	Vout = matrix + j*Nx;
+	Vin  = (float *)(buf[0].matrix.buffer) + (j*Npix + jj)*buf[0].header.Naxis[0];
+	for (i = 0; i < Nx; i++, Vout++) {
+	  for (ii = 0; ii < Npix; ii++, Vin++) {
+	    *Vout += *Vin;
+	  }
+	}
+      }
+    }
+  } else {
+    gprint (GP_ERR, "using scale of 1\n");
+    matrix = (float *)(buf[0].matrix.buffer);
+  }
+
+  Noverlay = 0;
+  NOVERLAY = 1000;
+  ALLOCATE (overlay, KiiOverlay, NOVERLAY);
+
+  v00 = matrix;
+  v01 = matrix + 1;
+  v10 = matrix + Nx;
+  v11 = matrix + Nx + 1;
+  d01 = (level - *v00);
+  d11 = (level - *v10);
+  for (j = 1; j < Ny; j++) {
+    if (!(j%10)) gprint (GP_ERR, ".");
+    for (i = 1; i < Nx; i++, v00++, v01++, v10++, v11++) {
+
+      d00 = d01;
+      d10 = d11;
+      d01 = (level - *v01);
+      d11 = (level - *v11);
+
+      if (((d00 > 0) && (d01 > 0) && (d10 > 0) && (d11 > 0)) ||
+	  ((d00 < 0) && (d01 < 0) && (d10 < 0) && (d11 < 0)))
+	continue;
+
+      if ((d00 > 0) && (d10 <= 0)) { 
+	if ((d01 <= 0) && (d11 <= 0)) { /* \  */
+	  LL;
+	  DUMP;
+	  continue;
+	}
+	if ((d01 > 0) && (d11 <= 0)) { /* -  */
+	  HZ;
+	  DUMP;
+	  continue;
+	}
+	if ((d01 > 0) && (d11 > 0)) { /* /  */
+	  UL;
+	  DUMP;
+	  continue;
+	}
+	if ((d01 <= 0) && (d11 > 0)) { /* \\  */
+	  LL;
+	  DUMP;
+	  UR;
+	  DUMP;
+	  continue;
+	}
+      }
+
+      if ((d00 <= 0) && (d10 > 0)) {
+	if ((d01 > 0) && (d11 > 0)) { /* \  */
+	  LL;
+	  DUMP;
+	  continue;
+	}
+	if ((d01 <= 0) && (d11 > 0)) { /* -  */
+	  HZ;
+	  DUMP;
+	  continue;
+	}
+	if ((d01 <= 0) && (d11 <= 0)) { /* /  */
+	  UL;
+	  DUMP;
+	  continue;
+	}
+	if ((d01 > 0) && (d11 <= 0)) { /* //  */
+	  UL;
+	  DUMP;
+	  LR;
+	  DUMP;
+	  continue;
+	}
+      }
+      
+
+      if ((d00 <= 0) && (d10 <= 0)) { 
+	if ((d01 > 0) && (d11 <= 0)) {
+	  LR;
+	  DUMP;
+	  continue;
+	}
+	if ((d01 <= 0) && (d11 > 0)) {
+	  UR;
+	  DUMP;
+	  continue;
+	}
+	if ((d01 > 0) && (d11 > 0)) {
+	  VT;
+	  DUMP;
+	  continue;
+	}
+      }
+
+      if ((d00 > 0) && (d10 > 0)) { 
+	if ((d01 <= 0) && (d11 > 0)) {
+	  LR;
+	  DUMP;
+	  continue;
+	}
+	if ((d01 > 0) && (d11 <= 0)) {
+	  UR;
+	  DUMP;
+	  continue;
+	}
+	if ((d01 <= 0) && (d11 <= 0)) {
+	  VT;
+	  DUMP;
+	  continue;
+	}
+      }
+
+    }
+    /* skip left-hand edge */
+    v00++; v01++; v10++; v11++;
+    d01 = (level - *v00);
+    d11 = (level - *v10);
+  }
+
+  /****** bottom line *******/
+  v00 = matrix;  
+  v01 = matrix + 1;  
+  y = 0;
+  dx = 0;
+  dy = -0.5;
+  for (i = 1; i < Nx; i++, v00++, v01++) { /* do the edges */
+    if (((*v00 > level) && (*v01 <= level)) || ((*v00 <= level) && (*v01 > level))) {
+      x = i + (level - *v01)/(*v01 - *v00);
+      DUMP;
+    }
+  }
+
+  /********** top line *******/
+  v00 = matrix + Nx*(Ny - 1);  
+  v01 = v00 + 1;
+  y = Ny - 1;
+  dx = 0;
+  dy = 0.5;
+  for (i = 1; i < Nx; i++, v00++, v01++) { /* do the edges */
+    if (((*v00 > level) && (*v01 <= level)) || ((*v00 <= level) && (*v01 > level))) {
+      x = i + (level - *v01)/(*v01 - *v00);
+      DUMP;
+    }
+  }
+
+  /******** left line *********/
+  v00 = matrix; 
+  v01 = matrix + Nx;
+  x = 0;
+  dx = -0.5;
+  dy = 0;
+  for (j = 1; j < Ny; j++, v00+=Nx, v01+=Nx) { /* do the edges */
+    if (((*v00 > level) && (*v01 <= level)) || ((*v00 <= level) && (*v01 > level))) {
+      y = j + (level - *v01)/(*v01 - *v00);
+      DUMP;
+    }
+  }
+
+  /******** right line *********/
+  v00 = matrix + Nx - 1; 
+  v01 = v00 + Nx;
+  x = Nx - 1;
+  dx = 0.5;
+  dy = 0;
+  for (j = 1; j < Ny; j++, v00+=Nx, v01+=Nx) { /* do the edges */
+    if (((*v00 > level) && (*v01 <= level)) || ((*v00 <= level) && (*v01 > level))) {
+      y = j + (level - *v01)/(*v01 - *v00);
+      DUMP;
+    }
+  }
+  
+  KiiLoadOverlay (kapa, overlay, Noverlay, argv[2]);
+  free (overlay);
+
+  if (Npix != 1) free (matrix);
+  gprint (GP_ERR, "\n");
+  return (TRUE);
+}
Index: /branches/sc_branches/pantasks_condor/cmd.data/tvgrid.c
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.data/tvgrid.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.data/tvgrid.c	(revision 34783)
@@ -0,0 +1,152 @@
+# include "data.h"
+# define TEN(X) (pow(10.0, (double)(X)))
+
+int tvgrid (int argc, char **argv) {
+  
+  int ndig1, ndig2, NX, NY, connect;
+  int tDEC, tRA;
+  double ra, dec, ra0, dec0, ra1, dec1;
+  double x0, y0, x1, y1;
+  double dDEC, fDEC, dRA, fRA;
+  char format[16];
+  Coords coords;
+  int kapa, N, Noverlay, NOVERLAY;
+  char *name;
+  Buffer *buf;
+  KiiOverlay *overlay;
+
+  name = NULL;
+  if ((N = get_argument (argc, argv, "-n"))) {
+    remove_argument (N, &argc, argv);
+    name = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+  if (!GetImage (NULL, &kapa, name)) return (FALSE);
+  FREE (name);
+
+  if (argc != 3) {
+    gprint (GP_ERR, "USAGE: tvgrid (overlay) (buffer)\n");
+    gprint (GP_ERR, " (overlay) may be: red, green, blue, yellow\n");
+    return (FALSE);
+  }
+
+  if ((buf = SelectBuffer (argv[2], OLDBUFFER, TRUE)) == NULL) return (FALSE);
+  GetCoords (&coords, &buf[0].header);
+
+  XY_to_RD (&ra0, &dec0, 0.0, 0.0, &coords);
+  XY_to_RD (&ra1, &dec1, (double)buf[0].header.Naxis[0], (double)buf[0].header.Naxis[1], &coords);
+  gprint (GP_ERR, "%f %f  %f %f\n", ra0, dec0, ra1, dec1);
+  
+  Noverlay = 0;
+  NOVERLAY = 1000;
+  ALLOCATE (overlay, KiiOverlay, NOVERLAY);
+
+  dDEC = fabs(dec1 - dec0);
+  tDEC = log10(dDEC) - log10(5.0);
+  fDEC = log10(dDEC) - tDEC;
+  if ((fDEC > log10(0.5)) && (fDEC < log10(1.0))) {
+    dDEC = TEN (tDEC + log10(0.1));
+  }
+  if ((fDEC > log10(1.0)) && (fDEC < log10(2.0))) {
+    dDEC = TEN (tDEC + log10(0.2));
+  }
+  if ((fDEC > log10(2.0)) && (fDEC < log10(5.0))) {
+    dDEC = TEN (tDEC + log10(0.5));
+  }
+  if ((fDEC > log10(5.0)) && (fDEC < log10(10.0))) {
+    dDEC = TEN (tDEC + log10(1.0));
+  }
+  if ((fDEC > log10(10.0)) && (fDEC < log10(20.0))) {
+    dDEC = TEN (tDEC + log10(2.0));
+  }
+  if ((fDEC > log10(20.0)) && (fDEC < log10(50.0))) {
+    dDEC = TEN (tDEC + log10(5.0));
+  }
+  ndig2 = ((log10(dDEC) < 0) ? fabs(log10(dDEC)) : 0);
+  ndig1 = 3 + log10(MAX(dec0, dec1)) + ndig2;
+  sprintf (format, "%%%d.%df", ndig1, ndig2);
+  gprint (GP_ERR, "format: %s..\n", format);
+
+  NX = buf[0].header.Naxis[0];
+  NY = buf[0].header.Naxis[1];
+
+  x0 = y0 = 0;
+  dRA = MAX (fabs(ra1 - ra0) / 100.0, 0.1);
+  connect = FALSE;
+  for (dec = dDEC * ((int)(MIN(dec0,dec1)/dDEC) + 1); dec < MAX(dec0,dec1); dec += dDEC) {
+    for (ra = 0; ra < 361; ra += dRA) {
+      RD_to_XY (&x1, &y1, ra, dec, &coords);
+      if ((x1 >= 0) && (x1 < NX) && (y1 >= 0) && (y1 < NY)) {
+	if (connect) {
+	  overlay[Noverlay].type = KII_OVERLAY_LINE;
+	  overlay[Noverlay].x = x0;
+	  overlay[Noverlay].y = y0;
+	  overlay[Noverlay].dx = x1 - x0;
+	  overlay[Noverlay].dy = y1 - y0;
+	  Noverlay ++;
+	  CHECK_REALLOCATE (overlay, KiiOverlay, NOVERLAY, Noverlay, 1000);
+	}
+	x0 = x1;
+	y0 = y1;
+	connect = TRUE;
+      } else {
+	connect = FALSE;
+      }
+    }
+  }
+
+  dRA = fabs(ra1 - ra0);
+  tRA = log10(dRA) - log10(5.0);
+  fRA = log10(dRA) - tRA;
+  if ((fRA > log10(0.5)) && (fRA < log10(1.0))) {
+    dRA = TEN (tRA + log10(0.1));
+  }
+  if ((fRA > log10(1.0)) && (fRA < log10(2.0))) {
+    dRA = TEN (tRA + log10(0.2));
+  }
+  if ((fRA > log10(2.0)) && (fRA < log10(5.0))) {
+    dRA = TEN (tRA + log10(0.5));
+  }
+  if ((fRA > log10(5.0)) && (fRA < log10(10.0))) {
+    dRA = TEN (tRA + log10(1.0));
+  }
+  if ((fRA > log10(10.0)) && (fRA < log10(20.0))) {
+    dRA = TEN (tRA + log10(2.0));
+  }
+  if ((fRA > log10(20.0)) && (fRA < log10(50.0))) {
+    dRA = TEN (tRA + log10(5.0));
+  }
+  ndig2 = ((log10(dRA) < 0) ? fabs(log10(dRA)) : 0);
+  ndig1 = 3 + log10(MAX(ra0, ra1)) + ndig2;
+  sprintf (format, "%%%d.%df", ndig1, ndig2);
+  gprint (GP_ERR, "format: %s..\n", format);
+
+  dDEC = MAX (fabs(dec1 - dec0) / 100.0, 0.1);
+  connect = FALSE;
+  for (ra = dRA * ((int)(MIN(ra0,ra1)/dRA) + 1); ra < MAX(ra0,ra1); ra += dRA) {
+    for (dec = -90; dec < 90; dec += dDEC) {
+      RD_to_XY (&x1, &y1, ra, dec, &coords);
+      if ((x1 >= 0) && (x1 < NX) && (y1 >= 0) && (y1 < NY)) {
+	if (connect) {
+	  overlay[Noverlay].type = KII_OVERLAY_LINE;
+	  overlay[Noverlay].x = x0;
+	  overlay[Noverlay].y = y0;
+	  overlay[Noverlay].dx = x1 - x0;
+	  overlay[Noverlay].dy = y1 - y0;
+	  Noverlay ++;
+	  CHECK_REALLOCATE (overlay, KiiOverlay, NOVERLAY, Noverlay, 1000);
+	}
+	x0 = x1;
+	y0 = y1;
+	connect = TRUE;
+      } else {
+	connect = FALSE;
+      }
+    }
+  }
+
+  KiiLoadOverlay (kapa, overlay, Noverlay, argv[1]);
+  free (overlay);
+
+  return (TRUE);
+}
Index: /branches/sc_branches/pantasks_condor/cmd.data/ungridify.c
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.data/ungridify.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.data/ungridify.c	(revision 34783)
@@ -0,0 +1,61 @@
+# include "data.h"
+
+// XXX adapt this to accept an optional region to limit the areas, otherwise use the whole image
+int ungridify (int argc, char **argv) {
+
+  int i, j, n;
+  int Nx, Ny, NX, NY;
+  int Xmin, Xmax, Ymin, Ymax;
+  Buffer *bf;
+  float *v;
+  Vector *vx, *vy, *vz;
+  opihi_flt *x, *y, *z;
+
+  if (argc != 9) {
+    gprint (GP_ERR, "USAGE: ungridify buffer Xmin Xmax Ymin Ymax x y z\n");
+    gprint (GP_ERR, "  convert a portion of an image to a collection a triplet of vectors\n");
+    return (FALSE);
+  }
+  
+  if ((bf = SelectBuffer (argv[1], OLDBUFFER, TRUE)) == NULL) return (FALSE);
+  Xmin = atof (argv[2]);
+  Xmax = atof (argv[3]);
+  Ymin = atof (argv[4]);
+  Ymax = atof (argv[5]);
+  Nx = Xmax - Xmin;
+  Ny = Ymax - Ymin;
+  
+  NX = bf[0].matrix.Naxis[0];
+  NY = bf[0].matrix.Naxis[1];
+
+  if ((vx = SelectVector (argv[6], ANYVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((vy = SelectVector (argv[7], ANYVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((vz = SelectVector (argv[8], ANYVECTOR, TRUE)) == NULL) return (FALSE);
+
+  ResetVector (vx, OPIHI_FLT, Nx*Ny);
+  ResetVector (vy, OPIHI_FLT, Nx*Ny);
+  ResetVector (vz, OPIHI_FLT, Nx*Ny);
+
+  x = vx[0].elements.Flt;
+  y = vy[0].elements.Flt;
+  z = vz[0].elements.Flt;
+  n = 0;
+  v = (float *)bf[0].matrix.buffer;
+  for (j = Ymin; j < Ymax; j++) {
+    for (i = Xmin; i < Xmax; i++, x++, y++, z++, n++) {
+      vx[0].elements.Flt[n] = i;
+      vy[0].elements.Flt[n] = j;
+      if (i < 0) continue;
+      if (i >= NX) continue;
+      if (j < 0) continue;
+      if (j >= NY) continue;
+      vz[0].elements.Flt[n] = v[i+j*NX];
+    }
+  }
+  if (n != Nx*Ny) {
+    gprint (GP_ERR, "error in ungrid: %d vs %d (%d x %d)\n", n, Nx*Ny, Nx, Ny);
+    gprint (GP_ERR, "(this is probably a programming error in ungridify)\n");
+  }
+  vx[0].Nelements = vy[0].Nelements = vz[0].Nelements = n;
+  return (TRUE);
+}
Index: /branches/sc_branches/pantasks_condor/cmd.data/uniq.c
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.data/uniq.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.data/uniq.c	(revision 34783)
@@ -0,0 +1,54 @@
+# include "data.h"
+
+int uniq (int argc, char **argv) {
+  
+  int Nnew, i, j, found;
+  Vector *ivec, *ovec;
+
+  if (argc != 3) {
+    gprint (GP_ERR, "USAGE: uniq (in) (out)\n");
+    return (FALSE);
+  }
+
+  if ((ivec = SelectVector (argv[1], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((ovec = SelectVector (argv[2], ANYVECTOR, TRUE)) == NULL) return (FALSE);
+
+  /* allocate the maximum possible needed */
+  ResetVector (ovec, ivec->type, ivec->Nelements);
+
+  Nnew = 0;
+
+  if (ivec->type == OPIHI_FLT) {
+    opihi_flt *v1 = ivec[0].elements.Flt;
+    for (i = 0; i < ivec[0].Nelements; i++, v1++) {
+      opihi_flt *v2 = ovec[0].elements.Flt;
+      found = FALSE;
+      for (j = 0; !found && (j < Nnew); j++, v2++) {
+	if (*v1 == *v2) found = TRUE;
+      }
+      if (!found) {
+	ovec[0].elements.Flt[Nnew] = *v1;
+	Nnew ++;
+      }
+    }
+  } else {
+    opihi_int *v1 = ivec[0].elements.Int;
+    for (i = 0; i < ivec[0].Nelements; i++, v1++) {
+      opihi_int *v2 = ovec[0].elements.Int;
+      found = FALSE;
+      for (j = 0; !found && (j < Nnew); j++, v2++) {
+	if (*v1 == *v2) found = TRUE;
+      }
+      if (!found) {
+	ovec[0].elements.Int[Nnew] = *v1;
+	Nnew ++;
+      }
+    }
+  }
+
+  // free up extra memory
+  ResetVector (ovec, ivec->type, Nnew);
+
+  return (TRUE);
+}
+
Index: /branches/sc_branches/pantasks_condor/cmd.data/unsign.c
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.data/unsign.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.data/unsign.c	(revision 34783)
@@ -0,0 +1,28 @@
+# include "data.h"
+
+int unsign (int argc, char **argv) {
+  
+  if (argc == 1) {
+    if (gfits_get_unsign_mode()) 
+      gprint (GP_ERR, "mode is now UNSIGNED int \n");
+    else
+      gprint (GP_ERR, "mode is now SIGNED int \n");
+    return (TRUE);
+  }
+
+  if (argc == 2) {
+    if (!strcmp (argv[1], "1")) {
+      gfits_set_unsign_mode (TRUE);
+      set_int_variable ("UNSIGN", 1);
+      return (TRUE);
+    }
+    if (!strcmp (argv[1], "0")) {
+      gfits_set_unsign_mode (FALSE);
+      set_int_variable ("UNSIGN", 0);
+      return (TRUE);
+    }
+  }
+
+  gprint (GP_ERR, "USAGE: unsign [0/1]\n");
+  return (FALSE);
+}
Index: /branches/sc_branches/pantasks_condor/cmd.data/vbin.c
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.data/vbin.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.data/vbin.c	(revision 34783)
@@ -0,0 +1,87 @@
+# include "data.h"
+
+int vbin (int argc, char **argv) {
+  
+  int i, j, n, N, Nin, Nout;
+  int Normalize, Ignore;
+  opihi_flt *Vout, IgnoreValue;
+  double scale;
+  Vector *in, *out;
+
+  Normalize = FALSE;
+  if ((N = get_argument (argc, argv, "-norm"))) {
+    remove_argument (N, &argc, argv);
+    Normalize = TRUE;
+  }
+
+  Ignore = FALSE;
+  IgnoreValue = 0.0;
+  if ((N = get_argument (argc, argv, "-ignore"))) {
+    Ignore = TRUE;
+    remove_argument (N, &argc, argv);
+    IgnoreValue = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  if (argc != 4) {
+    gprint (GP_ERR, "USAGE: vbin <input> <output> scale \n");
+    gprint (GP_ERR, "  (use interpolate to expand)\n");
+    return (FALSE);
+  }
+
+  if ((in  = SelectVector (argv[1], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((out = SelectVector (argv[2], ANYVECTOR, TRUE)) == NULL) return (FALSE);
+
+  scale  = atof (argv[3]);
+  if ((int)(scale) != scale) {
+    gprint (GP_ERR, "integer binning only, please\n");
+    return (FALSE);
+  }
+
+  Nin  = in[0].Nelements;
+  Nout = Nin / scale;
+
+  // re-binning creates a float vector
+  ResetVector (out, OPIHI_FLT, in[0].Nelements);
+
+  Vout = out[0].elements.Flt;
+
+  if (in[0].type == OPIHI_FLT) {
+    opihi_flt *Vin  = in[0].elements.Flt;
+    for (n = j = 0; j < Nout; j++, Vout++) {
+      *Vout = 0;
+      for (N = i = 0; (i < scale) && (n < Nin); n++, i++, Vin++) {
+	if (!finite (*Vin)) continue;
+	if (Ignore && (*Vin == IgnoreValue)) continue;
+	*Vout += *Vin;
+	N ++;
+      } 
+      if (Normalize) {
+	if (N > 0) { 
+	  *Vout /= (opihi_flt) N; 
+	} else {
+	  *Vout = 0;
+	}
+      }
+    }
+  } else {
+    opihi_int *Vin  = in[0].elements.Int;
+    for (n = j = 0; j < Nout; j++, Vout++) {
+      *Vout = 0;
+      for (N = i = 0; (i < scale) && (n < Nin); n++, i++, Vin++) {
+	if (!finite (*Vin)) continue;
+	if (Ignore && (*Vin == IgnoreValue)) continue;
+	*Vout += *Vin;
+	N ++;
+      } 
+      if (Normalize) {
+	if (N > 0) { 
+	  *Vout /= (opihi_flt) N; 
+	} else {
+	  *Vout = 0;
+	}
+      }
+    }
+  }
+  return (TRUE);
+}
Index: /branches/sc_branches/pantasks_condor/cmd.data/vclip.c
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.data/vclip.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.data/vclip.c	(revision 34783)
@@ -0,0 +1,83 @@
+# include "data.h"
+
+int vclip (int argc, char **argv) {
+
+  int i, Npix, DO_NAN, DO_INF, DO_CLIP, N;
+  double min, Vmin, max, Vmax, nan_val, inf_val;
+  Vector *vec;
+
+  inf_val = nan_val = min = Vmin = max = Vmax = 0;
+
+  DO_NAN = FALSE;
+  if ((N = get_argument (argc, argv, "-nan"))) {
+    remove_argument (N, &argc, argv);
+    nan_val  = atof(argv[N]);
+    remove_argument (N, &argc, argv);
+    DO_NAN = TRUE;
+  }
+
+  DO_INF = FALSE;
+  if ((N = get_argument (argc, argv, "-inf"))) {
+    remove_argument (N, &argc, argv);
+    inf_val  = atof(argv[N]);
+    remove_argument (N, &argc, argv);
+    DO_INF = TRUE;
+  }
+
+  if ((argc != 6) && (!(DO_INF || DO_NAN))) {
+    gprint (GP_ERR, "USAGE: vclip (vector) [min Vmin max Vmax] [-inf val] [-nan val]\n");
+    return (FALSE);
+  }
+
+  if ((vec = SelectVector (argv[1], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+
+  DO_CLIP = FALSE;
+  if (argc == 6) {
+    min = atof (argv[2]);
+    Vmin = atof (argv[3]);
+    max = atof (argv[4]);
+    Vmax = atof (argv[5]);
+    DO_CLIP = TRUE;
+  }
+
+  if (DO_NAN && vec[0].type == OPIHI_INT) {
+    gprint (GP_ERR, "  warning : clipping on NAN is invalid for INT vectors\n");
+    return (FALSE);
+  }
+    
+  if (DO_INF && vec[0].type == OPIHI_INT) {
+    gprint (GP_ERR, "  warning : clipping on INF is invalid for INT vectors\n");
+    return (FALSE);
+  }
+    
+  Npix = vec[0].Nelements;
+
+  if (vec[0].type == OPIHI_FLT) {
+    opihi_flt *in =  vec[0].elements.Flt;
+    for (i = 0; i < Npix; i++, in++) {
+      if (DO_CLIP && (*in < min)) {
+	*in = Vmin;
+      }
+      if (DO_CLIP && (*in > max)) {
+	*in = Vmax;
+      }
+      if (!finite (*in) && DO_INF) {
+	*in = inf_val;
+      }
+      if (isnan (*in) && DO_NAN) {
+	*in = nan_val;
+      }
+    }
+  } else {
+    opihi_int *in =  vec[0].elements.Int;
+    for (i = 0; i < Npix; i++, in++) {
+      if (DO_CLIP && (*in < min)) {
+	*in = Vmin;
+      }
+      if (DO_CLIP && (*in > max)) {
+	*in = Vmax;
+      }
+    }
+  }
+  return (TRUE);
+}
Index: /branches/sc_branches/pantasks_condor/cmd.data/vcontour.c
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.data/vcontour.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.data/vcontour.c	(revision 34783)
@@ -0,0 +1,278 @@
+# include "data.h"
+# define LL { \
+ dx =  d00 / (*v01 - *v00); \
+ dy = -d00 / (*v10 - *v00); \
+ x = i - 0.5; \
+ y = j - dy - 0.5; }
+
+# define UL { \
+ tmp = d00 / (*v10 - *v00); \
+ dy = 1 - tmp; \
+ dx =  d10 / (*v11 - *v10); \
+ x = i - 0.5; \
+ y = j + tmp - 0.5; }
+      
+# define LR { \
+ tmp = d00 / (*v01 - *v00); \
+ dx = 1 - tmp; \
+ dy = d01 / (*v11 - *v01); \
+ x = i + tmp - 0.5; \
+ y = j - 0.5; }
+
+# define UR { \
+ tmp = d10 / (*v11 - *v10); \
+ dx = 1 - tmp; \
+ dy = -d11 / (*v01 - *v11); \
+ x = i + tmp - 0.5; \
+ y = j + 1 - 0.5; }
+      
+# define HZ { \
+ tmp = d00 / (*v10 - *v00); \
+ dy = d01 / (*v11 - *v01) - tmp; \
+ dx = 1; \
+ x = i - 0.5; \
+ y = j + tmp - 0.5; }
+
+# define VT { \
+ tmp = d00 / (*v01 - *v00); \
+ dx = d10 / (*v11 - *v10) - tmp; \
+ x = i + tmp - 0.5; \
+ dy = 1; \
+ y = j - 0.5; }
+
+# define DUMP { \
+overlay[Noverlay].type = KII_OVERLAY_LINE; \
+overlay[Noverlay].x = Npix*x; \
+overlay[Noverlay].y = Npix*y; \
+overlay[Noverlay].dx = Npix*dx; \
+overlay[Noverlay].dy = Npix*dy; \
+Noverlay ++; \
+CHECK_REALLOCATE (overlay, KiiOverlay, NOVERLAY, Noverlay, 1000); \
+}
+
+int vcontour (int argc, char **argv) {
+
+  int i, j, ii, jj, n, Nbuf, Npix, Nx, Ny, Nline;
+  float level, d00, d01, d10, d11, tmp;
+  float x, y, dx, dy;
+  float *v00, *v01, *v10, *v11;
+  float *Vout, *Vin, *matrix;
+  char *buffer, line[17];
+  int Ximage, Nimage, N;
+  Vector *xvec, *yvec, *zvec;
+
+  gprint (GP_ERR, "vcontour not working yet\n");
+  return (FALSE);
+
+  if (argc != 7) {
+    gprint (GP_ERR, "USAGE: vcontour x y z Xc Yc (level)\n");
+    return (FALSE);
+  }
+  
+  if ((xvec = SelectVector (argv[1], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((yvec = SelectVector (argv[2], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((zvec = SelectVector (argv[3], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((foo = SelectVector (argv[4], ANYVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((foo = SelectVector (argv[5], ANYVECTOR, TRUE)) == NULL) return (FALSE);
+
+  level = atof (argv[6]);
+
+  /* convert the x,y,z vectors to a matrix */
+  *V = xvec[0].elements;
+  max = min = *V;
+  for (i = 0; i < xvec[0].Nelements; i++, V++) {
+    if (!finite (*V)) continue;
+    max = MAX (*V, max);
+    min = MIN (*V, min);
+  }      
+  Xmax = max; Xmin = min;
+
+  *V = yvec[0].elements;
+  max = min = *V;
+  for (i = 0; i < yvec[0].Nelements; i++, V++) {
+    if (!finite (*V)) continue;
+    max = MAX (*V, max);
+    min = MIN (*V, min);
+  }      
+  Ymax = max; Ymin = min;
+
+  /* not really finished */
+
+  Noverlay = 0;
+  NOVERLAY = 1000;
+  ALLOCATE (overlay, KiiOverlay, NOVERLAY);
+
+  v01 = matrix + 1;
+  v10 = matrix + Nx;
+  v11 = matrix + Nx + 1;
+  d01 = (level - *v00);
+  d11 = (level - *v10);
+  for (j = 1; j < Ny; j++) {
+    if (!(j%10)) gprint (GP_ERR, ".");
+    for (i = 1; i < Nx; i++, v00++, v01++, v10++, v11++) {
+
+      d00 = d01;
+      d10 = d11;
+      d01 = (level - *v01);
+      d11 = (level - *v11);
+
+      if (((d00 > 0) && (d01 > 0) && (d10 > 0) && (d11 > 0)) ||
+	  ((d00 < 0) && (d01 < 0) && (d10 < 0) && (d11 < 0)))
+	continue;
+
+      if ((d00 > 0) && (d10 <= 0)) { 
+	if ((d01 <= 0) && (d11 <= 0)) { /* \  */
+	  LL;
+	  DUMP;
+	  continue;
+	}
+	if ((d01 > 0) && (d11 <= 0)) { /* -  */
+	  HZ;
+	  DUMP;
+	  continue;
+	}
+	if ((d01 > 0) && (d11 > 0)) { /* /  */
+	  UL;
+	  DUMP;
+	  continue;
+	}
+	if ((d01 <= 0) && (d11 > 0)) { /* \\  */
+	  LL;
+	  DUMP;
+	  UR;
+	  DUMP;
+	  continue;
+	}
+      }
+
+      if ((d00 <= 0) && (d10 > 0)) {
+	if ((d01 > 0) && (d11 > 0)) { /* \  */
+	  LL;
+	  DUMP;
+	  continue;
+	}
+	if ((d01 <= 0) && (d11 > 0)) { /* -  */
+	  HZ;
+	  DUMP;
+	  continue;
+	}
+	if ((d01 <= 0) && (d11 <= 0)) { /* /  */
+	  UL;
+	  DUMP;
+	  continue;
+	}
+	if ((d01 > 0) && (d11 <= 0)) { /* //  */
+	  UL;
+	  DUMP;
+	  LR;
+	  DUMP;
+	  continue;
+	}
+      }
+      
+
+      if ((d00 <= 0) && (d10 <= 0)) { 
+	if ((d01 > 0) && (d11 <= 0)) {
+	  LR;
+	  DUMP;
+	  continue;
+	}
+	if ((d01 <= 0) && (d11 > 0)) {
+	  UR;
+	  DUMP;
+	  continue;
+	}
+	if ((d01 > 0) && (d11 > 0)) {
+	  VT;
+	  DUMP;
+	  continue;
+	}
+      }
+
+      if ((d00 > 0) && (d10 > 0)) { 
+	if ((d01 <= 0) && (d11 > 0)) {
+	  LR;
+	  DUMP;
+	  continue;
+	}
+	if ((d01 > 0) && (d11 <= 0)) {
+	  UR;
+	  DUMP;
+	  continue;
+	}
+	if ((d01 <= 0) && (d11 <= 0)) {
+	  VT;
+	  DUMP;
+	  continue;
+	}
+      }
+
+    }
+    /* skip left-hand edge */
+    v00++; v01++; v10++; v11++;
+    d01 = (level - *v00);
+    d11 = (level - *v10);
+  }
+
+  /****** bottom line *******/
+  v00 = matrix;  
+  v01 = matrix + 1;  
+  y = 0;
+  dx = 0;
+  dy = -0.5;
+  for (i = 1; i < Nx; i++, v00++, v01++) { /* do the edges */
+    if (((*v00 > level) && (*v01 <= level)) || ((*v00 <= level) && (*v01 > level))) {
+      x = i + (level - *v01)/(*v01 - *v00);
+      DUMP;
+      continue;
+    }
+  }
+
+  /********** top line *******/
+  v00 = matrix + Nx*(Ny - 1);  
+  v01 = v00 + 1;
+  y = Ny - 1;
+  dx = 0;
+  dy = 0.5;
+  for (i = 1; i < Nx; i++, v00++, v01++) { /* do the edges */
+    if (((*v00 > level) && (*v01 <= level)) || ((*v00 <= level) && (*v01 > level))) {
+      x = i + (level - *v01)/(*v01 - *v00);
+      DUMP;
+      continue;
+    }
+  }
+
+  /******** left line *********/
+  v00 = matrix; 
+  v01 = matrix + Nx;
+  x = 0;
+  dx = -0.5;
+  dy = 0;
+  for (j = 1; j < Ny; j++, v00+=Nx, v01+=Nx) { /* do the edges */
+    if (((*v00 > level) && (*v01 <= level)) || ((*v00 <= level) && (*v01 > level))) {
+      y = j + (level - *v01)/(*v01 - *v00);
+      DUMP;
+      continue;
+    }
+  }
+
+  /******** right line *********/
+  v00 = matrix + Nx - 1; 
+  v01 = v00 + Nx;
+  x = Nx - 1;
+  dx = 0.5;
+  dy = 0;
+  for (j = 1; j < Ny; j++, v00+=Nx, v01+=Nx) { /* do the edges */
+    if (((*v00 > level) && (*v01 <= level)) || ((*v00 <= level) && (*v01 > level))) {
+      y = j + (level - *v01)/(*v01 - *v00);
+      DUMP;
+      continue;
+    }
+  }
+  
+  KiiLoadOverlay (Ximage, overlay, Noverlay, argv[2]);
+  free (overlay);
+
+  if (Npix != 1) free (matrix);
+  gprint (GP_ERR, "\n");
+}
Index: /branches/sc_branches/pantasks_condor/cmd.data/vellipse.c
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.data/vellipse.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.data/vellipse.c	(revision 34783)
@@ -0,0 +1,272 @@
+# include "data.h"
+
+/* local private functions */
+opihi_flt fellipseOD (opihi_flt theta, opihi_flt *par, int Npar, opihi_flt *dpar);
+
+// enum {PAR_X0, PAR_Y0, PAR_RX, PAR_RY, PAR_T0, PAR_P0};
+enum {PAR_RMIN, PAR_EPSILON, PAR_PHI};
+
+# define GET_VAR(V,A) { \
+  char *c; \
+  c = get_variable (A); \
+  if (c == NULL) { \
+    gprint (GP_ERR, "missing fit parameter A\n"); \
+    return (FALSE); \
+  } \
+  V = atof (c); \
+  free (c); }
+
+int vellipse (int argc, char **argv) {
+
+  int i, N, Npts, Npar, Quiet;
+  opihi_flt par[6], *pos, *dpos, *theta, **covar;
+  opihi_flt chisq, ochisq, dchisq;
+  // opihi_flt Rmaj;
+  opihi_flt Xmin, Xmax, Ymin, Ymax;
+  Vector *xvec, *yvec, *Xvec, *Yvec, *tvec;
+
+  Quiet = FALSE;
+  if ((N = get_argument (argc, argv, "-q"))) {
+    Quiet = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+  if ((N = get_argument (argc, argv, "-quiet"))) {
+    Quiet = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+
+  if (argc != 6) {
+    gprint (GP_ERR, "USAGE: vellipse <xobs> <yobs> <xfit> <yfit> (theta)\n");
+    // gprint (GP_ERR, " uses guesses: E_X0, E_Y0, E_RMAJ, E_RMIN, E_THETA\n");
+    gprint (GP_ERR, " uses guesses: E_RMAJ, E_RMIN, E_PHI\n");
+    return (FALSE);
+  }
+  
+  if ((xvec = SelectVector (argv[1], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((yvec = SelectVector (argv[2], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((Xvec = SelectVector (argv[3], ANYVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((Yvec = SelectVector (argv[4], ANYVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((tvec = SelectVector (argv[5], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+
+  REQUIRE_VECTOR_FLT (xvec, FALSE); 
+  REQUIRE_VECTOR_FLT (yvec, FALSE); 
+
+  // GET_VAR (par[PAR_X0], "E_X0");
+  // GET_VAR (par[PAR_Y0], "E_Y0");
+  // GET_VAR (par[PAR_RX], "E_RMAJ");
+  // GET_VAR (par[PAR_RY], "E_RMIN");
+  // GET_VAR (par[PAR_T0], "E_THETA");
+  // GET_VAR (par[PAR_P0], "E_PHI");
+  // Npar = 6;
+
+  // we need to generate a vector of alternating x,y values and the independent variable theta:
+  Xmax = Ymax = Xmin = Ymin = 0.0;
+  ALLOCATE (pos,   opihi_flt, 2*xvec[0].Nelements); 
+  ALLOCATE (dpos,  opihi_flt, 2*xvec[0].Nelements); 
+  ALLOCATE (theta, opihi_flt, 2*xvec[0].Nelements); 
+  for (i = 0; i < xvec[0].Nelements; i++) {
+    pos[2*i+0]   = xvec[0].elements.Flt[i];
+    pos[2*i+1]   = yvec[0].elements.Flt[i];
+    Xmin = MIN (Xmin, xvec[0].elements.Flt[i]);
+    Xmax = MAX (Xmax, xvec[0].elements.Flt[i]);
+    Ymin = MIN (Ymin, yvec[0].elements.Flt[i]);
+    Ymax = MAX (Ymax, yvec[0].elements.Flt[i]);
+    dpos[2*i+0]  = 1.0;
+    dpos[2*i+1]  = 1.0;
+    theta[2*i+0] = atan2(yvec[0].elements.Flt[i], xvec[0].elements.Flt[i]);
+    theta[2*i+1] = theta[2*i+0];
+    // fprintf (stderr, "x,y: %f, %f -> %f, %f\n", yvec[0].elements.Flt[i], xvec[0].elements.Flt[i], yvec[0].elements.Flt[i] - par[PAR_Y0], xvec[0].elements.Flt[i] - par[PAR_X0]);
+    // fprintf (stderr, "x,y: %f, %f -> %f, %f\n", yvec[0].elements.Flt[i], xvec[0].elements.Flt[i], yvec[0].elements.Flt[i] - par[PAR_Y0], xvec[0].elements.Flt[i] - par[PAR_X0]);
+    // fprintf (stderr, "theta: %f - %f : %f\n", DEG_RAD*theta[2*i+0], tvec[0].elements.Flt[i], DEG_RAD*theta[2*i+0] - tvec[0].elements.Flt[i]);
+  }
+  Npts = 2*xvec[0].Nelements;
+
+  // basic guess from range, PHI in degrees, converted to radians
+  // GET_VAR (Rmaj,          "E_RMAJ");
+  // GET_VAR (par[PAR_RMIN], "E_RMIN");
+  // GET_VAR (par[PAR_PHI],  "E_PHI");
+  par[PAR_PHI] = 0.0;
+  par[PAR_EPSILON] = 0.5;
+  par[PAR_RMIN] = 0.25*((Xmax - Xmin) + (Ymax - Ymin));
+  Npar = 3;
+
+  ochisq = mrqinit (theta, pos, dpos, Npts, par, Npar, fellipseOD, !Quiet);
+  dchisq = ochisq + 2*Npts;
+  chisq  = ochisq + dchisq;
+
+  for (i = 0; (i < 20) && (chisq > 1e-3) && ((dchisq > 0.001*(Npts - Npar)) || (dchisq <= 0.0)); i++) {
+    chisq = mrqmin (theta, pos, dpos, Npts, par, Npar, fellipseOD, !Quiet);
+    dchisq = ochisq - chisq;
+    ochisq = chisq;
+    if (!Quiet) gprint (GP_ERR, "dchisq: %f, Ndof: %d\n", dchisq, Npts - Npar);
+  }  
+  if (!Quiet) gprint (GP_ERR, "%d iterations\n", i); 
+
+  ResetVector (Xvec, OPIHI_FLT, xvec[0].Nelements);
+  ResetVector (Yvec, OPIHI_FLT, xvec[0].Nelements);
+  for (i = 0; i < xvec[0].Nelements; i++) {
+    Xvec[0].elements.Flt[i] = fellipseOD (theta[2*i+0], par, Npar, dpos);
+    Yvec[0].elements.Flt[i] = fellipseOD (theta[2*i+1], par, Npar, dpos);
+  }
+  Xvec[0].Nelements = xvec[0].Nelements;
+  Yvec[0].Nelements = xvec[0].Nelements;
+
+  covar = mrqcovar (Npar);
+  //set_variable ("E_X0",   par[PAR_X0]);
+  //set_variable ("E_Y0",   par[PAR_Y0]);
+  //set_variable ("E_RMAJ", par[PAR_RX]);
+  //set_variable ("E_RMIN", par[PAR_RY]);
+  //set_variable ("E_T0",   par[PAR_T0]);
+  //set_variable ("E_P0",   par[PAR_P0]);
+
+  set_variable ("E_RMAJ", par[PAR_RMIN] / par[PAR_EPSILON]);
+  set_variable ("E_RMIN", par[PAR_RMIN]);
+  set_variable ("E_PHI",  DEG_RAD*par[PAR_PHI]);
+
+  // set_variable ("dE_X0",   sqrt(covar[PAR_X0][PAR_X0]));
+  // set_variable ("dE_Y0",   sqrt(covar[PAR_Y0][PAR_Y0]));
+  // set_variable ("dE_RMAJ", sqrt(covar[PAR_RX][PAR_RX]));
+  // set_variable ("dE_RMIN", sqrt(covar[PAR_RY][PAR_RY]));
+  // set_variable ("dE_T0",   sqrt(covar[PAR_T0][PAR_T0]));
+  // set_variable ("dE_P0",   sqrt(covar[PAR_P0][PAR_P0]));
+
+  set_variable ("dE_RMAJ", sqrt(covar[PAR_EPSILON][PAR_EPSILON]*covar[PAR_RMIN][PAR_RMIN]));
+  set_variable ("dE_RMIN", sqrt(covar[PAR_RMIN][PAR_RMIN]));
+  set_variable ("dE_PHI",  DEG_RAD*sqrt(covar[PAR_PHI][PAR_PHI]));
+
+  // if (!Quiet) gprint (GP_ERR, "Xo : %f +/- %f\n", par[PAR_X0], sqrt(covar[PAR_X0][PAR_X0]));
+  // if (!Quiet) gprint (GP_ERR, "Yo : %f +/- %f\n", par[PAR_Y0], sqrt(covar[PAR_Y0][PAR_Y0]));
+  // if (!Quiet) gprint (GP_ERR, "RX : %f +/- %f\n", par[PAR_RX], sqrt(covar[PAR_RX][PAR_RX]));
+  // if (!Quiet) gprint (GP_ERR, "RY : %f +/- %f\n", par[PAR_RY], sqrt(covar[PAR_RY][PAR_RY]));
+  // if (!Quiet) gprint (GP_ERR, "To : %f +/- %f\n", par[PAR_T0], sqrt(covar[PAR_T0][PAR_T0]));
+  // if (!Quiet) gprint (GP_ERR, "Po : %f +/- %f\n", par[PAR_P0], sqrt(covar[PAR_P0][PAR_P0]));
+
+  if (!Quiet) gprint (GP_ERR, "RMAJ : %f +/- %f\n", par[PAR_RMIN] / par[PAR_EPSILON], 0.0);
+  if (!Quiet) gprint (GP_ERR, "RMIN : %f +/- %f\n", par[PAR_RMIN], sqrt(covar[PAR_RMIN][PAR_RMIN]));
+  if (!Quiet) gprint (GP_ERR, "PHI  : %f +/- %f\n", DEG_RAD*par[PAR_PHI],  sqrt(covar[PAR_PHI][PAR_PHI]));
+
+  free (pos);
+  free (dpos);
+  free (theta);
+
+  mrqfree (Npar);
+  return (TRUE);
+}
+
+# if (0)
+/**
+ * the full chisq is built of two associated sums over coordinates:
+ * chisq = sum ((X_obs - X_fit(t))^2 + (Y_obs - Y_fit(t))^2)
+ * 
+ * the independent variable is Theta:
+ * X_fit = X0 + RX*cos(theta)*cos(phi) - RY*sin(theta)*sin(phi)
+ * Y_fit = Y0 + RX*cos(theta)*sin(phi) + RY*sin(theta)*cos(phi)
+ *
+ * alternating calls to fellipseOD refer alternatively to X or Y
+ *
+ * parameters: Xo, Yo (center), RX, RY (axes), To (angle), Po (phase)
+ */
+
+opihi_flt fellipseOD (opihi_flt theta, opihi_flt *par, int Npar, opihi_flt *dpar) {
+  
+  static int pass = 0;
+
+  opihi_flt csphi = cos(par[PAR_P0]);
+  opihi_flt snphi = sin(par[PAR_P0]);
+
+  opihi_flt dtheta = theta - par[PAR_T0];
+  opihi_flt cstht = cos(dtheta);
+  opihi_flt sntht = sin(dtheta);
+
+  // value is X
+  if (pass == 0) {
+    pass = 1;
+
+    opihi_flt value = par[PAR_X0] + par[PAR_RX]*cstht*csphi - par[PAR_RY]*sntht*snphi;
+
+    dpar[PAR_X0] = 1.0;
+    dpar[PAR_Y0] = 0.0;
+    dpar[PAR_RX] = +cstht*csphi;
+    dpar[PAR_RY] = -sntht*snphi;
+    dpar[PAR_P0] = -par[PAR_RX]*cstht*snphi - par[PAR_RY]*sntht*csphi;
+    dpar[PAR_T0] = -par[PAR_RX]*sntht*csphi - par[PAR_RY]*cstht*snphi;
+
+    return (value);
+  }  
+
+  // value is Y
+  if (pass == 1) {
+    pass = 0;
+
+    opihi_flt value = par[PAR_Y0] + par[PAR_RX]*cstht*snphi + par[PAR_RY]*sntht*csphi;
+
+    dpar[PAR_X0]  = 0.0;
+    dpar[PAR_Y0]  = 1.0;
+    dpar[PAR_RX]  = +cstht*snphi;
+    dpar[PAR_RY]  = +sntht*csphi;
+    dpar[PAR_P0]  = +par[PAR_RX]*cstht*csphi - par[PAR_RY]*sntht*snphi;
+    dpar[PAR_T0]  = -par[PAR_RX]*sntht*snphi + par[PAR_RY]*cstht*csphi;
+    return (value);
+  }  
+  abort();
+}
+
+# endif
+
+/**
+ * the full chisq is built of two associated sums over coordinates:
+ * chisq = sum ((X_obs - X_fit(t))^2 + (Y_obs - Y_fit(t))^2)
+ * 
+ * the independent variable is theta (angle from 0,0 to point):
+ * X_fit = RMIN*R*cos(theta)
+ * Y_fit = RMIN*R*sin(theta)
+ * R = 1 / sqrt(sin^2(theta-phi) + epsilon^2 * cos^2(theta-phi))
+ *
+ * alternating calls to fellipseOD refer alternatively to X or Y
+ *
+ * RMIN, EPSILON, PHI
+ */
+
+opihi_flt fellipseOD (opihi_flt alpha, opihi_flt *par, int Npar, opihi_flt *dpar) {
+  
+  static int pass = 0;
+
+  opihi_flt cs_alpha = cos(alpha);
+  opihi_flt sn_alpha = sin(alpha);
+
+  opihi_flt cs_phi = cos(alpha - par[PAR_PHI]);
+  opihi_flt sn_phi = sin(alpha - par[PAR_PHI]);
+
+  opihi_flt r     = 1.0 / sqrt(SQ(sn_phi) + SQ(par[PAR_EPSILON]*cs_phi));
+  opihi_flt r3    = pow(r, 3.0);
+  opihi_flt drdE  = -0.5 * r3 * SQ(cs_phi) * 2.0 * par[PAR_EPSILON];
+  opihi_flt drdP  = -0.5 * r3 * (SQ(par[PAR_EPSILON]) - 1) * 2.0 * cs_phi * sn_phi;
+
+  // value is X
+  if (pass == 0) {
+    pass = 1;
+
+    opihi_flt value = par[PAR_RMIN]*cs_alpha*r;
+
+    dpar[PAR_RMIN]    = r*cs_alpha;
+    dpar[PAR_EPSILON] = par[PAR_RMIN]*cs_alpha*drdE;
+    dpar[PAR_PHI]     = 4.0*par[PAR_RMIN]*cs_alpha*drdP;
+
+    return (value);
+  }  
+
+  // value is Y
+  if (pass == 1) {
+    pass = 0;
+
+    opihi_flt value = par[PAR_RMIN]*sn_alpha*r;
+
+    dpar[PAR_RMIN]    = r*sn_alpha;
+    dpar[PAR_EPSILON] = par[PAR_RMIN]*sn_alpha*drdE;
+    dpar[PAR_PHI]     = 4.0*par[PAR_RMIN]*sn_alpha*drdP;
+
+    return (value);
+  }  
+  abort();
+}
+
Index: /branches/sc_branches/pantasks_condor/cmd.data/vgauss.c
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.data/vgauss.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.data/vgauss.c	(revision 34783)
@@ -0,0 +1,114 @@
+# include "data.h"
+
+/* local private functions */
+opihi_flt fgaussOD (opihi_flt, opihi_flt *, int, opihi_flt *);
+
+# define GET_VAR(V,A) \
+  c = get_variable (A); \
+  if (c == NULL) { \
+    gprint (GP_ERR, "missing fit parameter A\n"); \
+    return (FALSE); \
+  } \
+  V = atof (c); \
+  free (c);
+
+int vgauss (int argc, char **argv) {
+
+  int i, N, Npts, Npar, Quiet;
+  opihi_flt par[4], *v1, *v2, *dy, **covar;
+  opihi_flt chisq, ochisq, dchisq;
+  Vector *xvec, *yvec, *svec, *ovec;
+  char *c, name[16];
+
+  Quiet = FALSE;
+  if ((N = get_argument (argc, argv, "-q"))) {
+    Quiet = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+  if ((N = get_argument (argc, argv, "-quiet"))) {
+    Quiet = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+
+  if (argc != 5) {
+    gprint (GP_ERR, "USAGE: vgauss <x> <y> <dy> (out)\n");
+    gprint (GP_ERR, " uses guesses: C0 (mean), C1 (sigma), C2 (norm), C3 (sky)\n");
+    return (FALSE);
+  }
+  
+  if ((xvec = SelectVector (argv[1], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((yvec = SelectVector (argv[2], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((svec = SelectVector (argv[3], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((ovec = SelectVector (argv[4], ANYVECTOR, TRUE)) == NULL) return (FALSE);
+
+  CastVector (xvec, OPIHI_FLT);
+  CastVector (yvec, OPIHI_FLT);
+  CastVector (svec, OPIHI_FLT);
+  // XXX Cast is failing.
+    
+
+  Npts = xvec[0].Nelements;
+  ResetVector (ovec, OPIHI_FLT, Npts);
+
+  ALLOCATE (dy, opihi_flt, Npts);
+
+  GET_VAR (par[0], "C0");
+  GET_VAR (par[1], "C1");
+  GET_VAR (par[2], "C2");
+  GET_VAR (par[3], "C3");
+  Npar = 4;
+
+  // mrqmin takes the inverse variance (do not generate NANs)
+  v1 = svec[0].elements.Flt;
+  v2 = dy;
+  for (i = 0; i < Npts; i++, v1++, v2++) {
+      *v2 = (*v1 == 0.0) ? 0.0 : 1.0 / (*v1 * *v1);
+  } 
+  
+  ochisq = mrqinit (xvec[0].elements.Flt, yvec[0].elements.Flt, dy, Npts, par, Npar, fgaussOD, !Quiet);
+  dchisq = ochisq + 2*Npts;
+
+  for (i = 0; (i < 20) && ((dchisq > 0.1*(Npts - Npar)) || (dchisq <= 0.0)); i++) {
+    chisq = mrqmin (xvec[0].elements.Flt, yvec[0].elements.Flt, dy, Npts, par, Npar, fgaussOD, !Quiet);
+    dchisq = ochisq - chisq;
+    ochisq = chisq;
+    if (!Quiet) gprint (GP_ERR, "dchisq: %f, Ndof: %d\n", dchisq, Npts - Npar);
+  }  
+  if (!Quiet) gprint (GP_ERR, "%d iterations\n", i); 
+
+  for (i = 0; i < Npts; i++) {
+    ovec[0].elements.Flt[i] = fgaussOD (xvec[0].elements.Flt[i], par, Npar, dy);
+  }
+  ovec[0].Nelements = Npts;
+  /* set output *before* variable renomalization */
+
+  covar = mrqcovar (Npar);
+  for (i = 0; i < Npar; i++) {
+    sprintf (name, "C%d", i);
+    set_variable (name, par[i]);
+    if (!Quiet) gprint (GP_ERR, "%d  %f  %f\n", i, par[i], sqrt(covar[i][i]));
+    sprintf (name, "dC%d", i);
+    set_variable (name, sqrt(covar[i][i]));
+  }
+
+  free (dy);
+  mrqfree (Npar);
+  return (TRUE);
+}
+
+/* pars: x_o, sigma, I, back */
+opihi_flt fgaussOD (opihi_flt x, opihi_flt *par, int Npar, opihi_flt *dpar) {
+
+  opihi_flt z, r, f;
+
+  z = (x - par[0])/par[1];
+  r = exp (-0.5*SQ(z));
+  f = par[2]*r + par[3];
+
+  dpar[0] = par[2]*r*z/par[1];
+  dpar[1] = par[2]*r*z*z/par[1];
+  dpar[2] = r;
+  dpar[3] = 1;
+  
+  return (f);
+}
Index: /branches/sc_branches/pantasks_condor/cmd.data/vgrid.c
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.data/vgrid.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.data/vgrid.c	(revision 34783)
@@ -0,0 +1,79 @@
+# include "data.h"
+
+int vgrid (int argc, char **argv) {
+
+  int i, Nx, Ny, Xb, Yb;
+  float Xmin, Xmax, dX, Ymin, Ymax, dY;
+  float *buf;
+  opihi_flt *val, *x, *y, *z;
+  int *Nval;
+  Buffer *bf;
+  Vector *vx, *vy, *vz;
+
+  if (argc != 11) {
+    gprint (GP_ERR, "USAGE: vgrid x y z buffer Xmin Xmax dX Ymin Ymax dY\n");
+    gprint (GP_ERR, "  re-grid values from a triplet of vectors (x,y,z) into an image\n");
+    gprint (GP_ERR, "  the vectors must be floating-point type\n");
+    return (FALSE);
+  }
+  
+  if ((vx = SelectVector (argv[1], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((vy = SelectVector (argv[2], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((vz = SelectVector (argv[3], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((bf = SelectBuffer (argv[4], ANYBUFFER, TRUE)) == NULL) return (FALSE);
+
+  if (vx[0].Nelements != vy[0].Nelements) return (FALSE);
+  if (vx[0].Nelements != vz[0].Nelements) return (FALSE);
+
+  REQUIRE_VECTOR_FLT (vx, FALSE); 
+  REQUIRE_VECTOR_FLT (vy, FALSE); 
+  REQUIRE_VECTOR_FLT (vz, FALSE); 
+
+  Xmin = atof (argv[5]);
+  Xmax = atof (argv[6]);
+  dX   = atof (argv[7]);
+
+  Ymin = atof (argv[8]);
+  Ymax = atof (argv[9]);
+  dY   = atof (argv[10]);
+
+  Nx = (Xmax - Xmin) / dX + 1;
+  Ny = (Ymax - Ymin) / dY + 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, opihi_flt, Nx*Ny);
+  bzero (val, Nx*Ny*sizeof(opihi_flt));
+  ALLOCATE (Nval, int, Nx*Ny);
+  bzero (Nval, Nx*Ny*sizeof(int));
+
+  x = vx[0].elements.Flt;
+  y = vy[0].elements.Flt;
+  z = vz[0].elements.Flt;
+  for (i = 0; i < vx[0].Nelements; i++, x++, y++, z++) {
+    Xb = (*x - Xmin) / dX;
+    Yb = (*y - Ymin) / dY;
+    if (Xb >= Nx) continue;
+    if (Yb >= Ny) continue;
+    val[Xb + Yb*Nx] = *z;
+    Nval[Xb + Yb*Nx]++;
+  }
+
+  buf = (float *) bf[0].matrix.buffer;
+  for (i = 0; i < Nx*Ny; i++) {
+    if (Nval[i] == 0) {
+      buf[i] = 0;
+      continue;
+    }
+    buf[i] = val[i] / Nval[i];
+  }
+
+  free (val);
+  free (Nval);
+
+  return (TRUE);
+
+}
Index: /branches/sc_branches/pantasks_condor/cmd.data/vgroup.c
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.data/vgroup.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.data/vgroup.c	(revision 34783)
@@ -0,0 +1,125 @@
+# include "data.h"
+
+enum {USE_MEDIAN, USE_COUNT, USE_SUM, USE_MEAN};
+
+int vgroup (int argc, char **argv) {
+  
+  int i, j, N;
+  Vector *xin, *yin, *xout, *yout;
+  opihi_flt xmin, xmax, sum;
+  double *values;
+
+  // Normalize = FALSE;
+  // if ((N = get_argument (argc, argv, "-norm"))) {
+  //   remove_argument (N, &argc, argv);
+  //   Normalize = TRUE;
+  // }
+  // 
+  // Ignore = FALSE;
+  // IgnoreValue = 0.0;
+  // if ((N = get_argument (argc, argv, "-ignore"))) {
+  //   Ignore = TRUE;
+  //   remove_argument (N, &argc, argv);
+  //   IgnoreValue = atof (argv[N]);
+  //   remove_argument (N, &argc, argv);
+  // }
+
+  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: 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 ((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
+  ResetVector (yout, OPIHI_FLT, xout[0].Nelements);
+
+  // storage vector
+  ALLOCATE (values, double, xin[0].Nelements);
+
+  for (i = 0; i < xout[0].Nelements - 1; i++) {
+    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;
+    for (j = 0; j < xin[0].Nelements; j++) {
+      if (xin[0].elements.Flt[j] < xmin) continue;
+      if (xin[0].elements.Flt[j] > xmax) continue;
+      if (yin) {
+	values[N] = yin[0].elements.Flt[j];
+      }
+      N++;
+    }
+    
+    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;
+    }
+    yout[0].elements.Flt[i] = sum;
+  }
+  free (values);
+  return (TRUE);
+}
Index: /branches/sc_branches/pantasks_condor/cmd.data/vload.c
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.data/vload.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.data/vload.c	(revision 34783)
@@ -0,0 +1,97 @@
+# include "data.h"
+
+int vload (int argc, char **argv) {
+  
+  int i, N, Noverlay;
+  int kapa, type;
+  char *name;
+  double dx, dy, angle;
+  KiiOverlay *overlay;
+  Vector *vecx, *vecy;
+  
+  name = NULL;
+  if ((N = get_argument (argc, argv, "-n"))) {
+    remove_argument (N, &argc, argv);
+    name = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+  if (!GetImage (NULL, &kapa, name)) return (FALSE);
+  FREE (name);
+
+  type = KII_OVERLAY_BOX;
+  if ((N = get_argument (argc, argv, "-type"))) {
+    remove_argument (N, &argc, argv);
+    type = KiiOverlayTypeByName (argv[N]);
+    remove_argument (N, &argc, argv);
+    if (!type) {
+      gprint (GP_ERR, "unknown Kii point type %s\n", argv[N]);
+      return (FALSE);
+    }
+  }
+
+  if (get_argument (argc, argv, "-size") && get_argument (argc, argv, "-dx")) {
+      gprint (GP_ERR, "only specify one of -size, -dx, -dy\n");
+      return (FALSE);
+  }
+
+  dx = dy = 1.0;
+  if ((N = get_argument (argc, argv, "-size"))) {
+    remove_argument (N, &argc, argv);
+    dx = dy = fabs(atof (argv[N]));
+    remove_argument (N, &argc, argv);
+  }
+  if ((N = get_argument (argc, argv, "-dx"))) {
+    remove_argument (N, &argc, argv);
+    dx = fabs(atof (argv[N]));
+    remove_argument (N, &argc, argv);
+  }
+  if ((N = get_argument (argc, argv, "-dy"))) {
+    remove_argument (N, &argc, argv);
+    dy = fabs(atof (argv[N]));
+    remove_argument (N, &argc, argv);
+  }
+
+  angle = 0.0;
+  if ((N = get_argument (argc, argv, "-angle"))) {
+    remove_argument (N, &argc, argv);
+    angle = fabs(atof (argv[N]));
+    remove_argument (N, &argc, argv);
+  }
+
+  if (argc != 4) {
+    gprint (GP_ERR, "USAGE: vload (overlay) (xvec) (yvec) [-n] [-type] [-size] [-dx] [-dy]\n");
+    return (FALSE);
+  }
+  
+  if (type == KII_OVERLAY_CIRCLE) {
+      dx /= 2.0;
+      dy /= 2.0;
+  }    
+
+  if ((vecx = SelectVector (argv[2], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((vecy = SelectVector (argv[3], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+  if (vecx[0].Nelements != vecy[0].Nelements) {
+    gprint (GP_ERR, "mismatched vector lengths\n");
+    return (FALSE);
+  }
+
+  Noverlay = vecx[0].Nelements;
+  ALLOCATE (overlay, KiiOverlay, Noverlay);
+
+  for (i = 0; i < Noverlay; i++) {
+    overlay[i].type = type;
+    overlay[i].text = NULL;
+    overlay[i].x = (vecx[0].type == OPIHI_FLT) ? vecx[0].elements.Flt[i] : vecx[0].elements.Int[i];
+    overlay[i].y = (vecy[0].type == OPIHI_FLT) ? vecy[0].elements.Flt[i] : vecy[0].elements.Int[i];
+    overlay[i].x += 0.5;
+    overlay[i].y += 0.5;
+    overlay[i].dx = dx;
+    overlay[i].dy = dy;
+    overlay[i].angle = angle;
+  }
+
+  KiiLoadOverlay (kapa, overlay, Noverlay, argv[1]);
+  free (overlay);
+  return (TRUE);
+}
+
Index: /branches/sc_branches/pantasks_condor/cmd.data/vmaxwell.c
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.data/vmaxwell.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.data/vmaxwell.c	(revision 34783)
@@ -0,0 +1,113 @@
+# include "data.h"
+
+/* local private functions */
+opihi_flt fmaxwellOD (opihi_flt, opihi_flt *, int, opihi_flt *);
+
+# define GET_VAR(V,A) \
+  c = get_variable (A); \
+  if (c == NULL) { \
+    gprint (GP_ERR, "missing fit parameter A\n"); \
+    return (FALSE); \
+  } \
+  V = atof (c); \
+  free (c);
+
+int vmaxwell (int argc, char **argv) {
+
+  int i, N, Npts, Npar, Quiet;
+  opihi_flt par[5], *v1, *v2, *dy, **covar;
+  opihi_flt chisq, ochisq, dchisq;
+  Vector *xvec, *yvec, *svec, *ovec;
+  char *c, name[16];
+
+  Quiet = FALSE;
+  if ((N = get_argument (argc, argv, "-q"))) {
+    Quiet = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+  if ((N = get_argument (argc, argv, "-quiet"))) {
+    Quiet = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+
+  if (argc != 5) {
+    gprint (GP_ERR, "USAGE: vmaxwell <x> <y> <dy> (out)\n");
+    gprint (GP_ERR, " uses guesses: C0 (mean), C1 (sigma), C2 (norm), C3 (sky), C4 (ref)\n");
+    return (FALSE);
+  }
+  
+  if ((xvec = SelectVector (argv[1], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((yvec = SelectVector (argv[2], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((svec = SelectVector (argv[3], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((ovec = SelectVector (argv[4], ANYVECTOR, TRUE)) == NULL) return (FALSE);
+
+  REQUIRE_VECTOR_FLT (xvec, FALSE); 
+  REQUIRE_VECTOR_FLT (yvec, FALSE); 
+  REQUIRE_VECTOR_FLT (svec, FALSE); 
+
+  Npts = xvec[0].Nelements;
+  ALLOCATE (dy, opihi_flt, Npts);
+  ResetVector (ovec, OPIHI_FLT, Npts);
+
+  GET_VAR (par[0], "C0");
+  GET_VAR (par[1], "C1");
+  GET_VAR (par[2], "C2");
+  GET_VAR (par[3], "C3");
+  GET_VAR (par[4], "C4");
+  Npar = 5;
+  /* careful of variable renomalization */
+
+  v1 = svec[0].elements.Flt;
+  v2 = dy;
+  for (i = 0; i < Npts; i++, v1++, v2++) *v2 = 1.0 / (*v1 * *v1);
+  
+  ochisq = mrqinit (xvec[0].elements.Flt, yvec[0].elements.Flt, dy, Npts, par, Npar, fmaxwellOD, !Quiet);
+  dchisq = ochisq + 2*Npts;
+
+  for (i = 0; (i < 30) && ((dchisq > 0.1*(Npts - Npar)) || (dchisq <= 0.0)); i++) {
+    chisq = mrqmin (xvec[0].elements.Flt, yvec[0].elements.Flt, dy, Npts, par, Npar, fmaxwellOD, !Quiet);
+    dchisq = ochisq - chisq;
+    ochisq = chisq;
+    if (!Quiet) gprint (GP_ERR, "dchisq: %f, Ndof: %d\n", dchisq, Npts - Npar);
+  }  
+  if (!Quiet) gprint (GP_ERR, "%d iterations\n", i); 
+
+  for (i = 0; i < Npts; i++) {
+    ovec[0].elements.Flt[i] = fmaxwellOD (xvec[0].elements.Flt[i], par, Npar, dy);
+  }
+  ovec[0].Nelements = Npts;
+  /* set output *before* variable renomalization */
+
+  covar = mrqcovar (Npar);
+  for (i = 0; i < Npar; i++) {
+    sprintf (name, "C%d", i);
+    set_variable (name, par[i]);
+    if (!Quiet) gprint (GP_ERR, "%d  %f  %f\n", i, par[i], sqrt(covar[i][i]));
+    sprintf (name, "dC%d", i);
+    set_variable (name, sqrt(covar[i][i]));
+  }
+
+  free (dy);
+  mrqfree (Npar);
+  return (TRUE);
+}
+
+/* pars: x_o, -0.5/sigma^2, I, back, ref */
+// f = C3 + C2*(x - C4)^2 * exp(-0.5*(x - C0)^2 / C1^2)
+opihi_flt fmaxwellOD (opihi_flt x, opihi_flt *par, int Npar, opihi_flt *dpar) {
+
+  opihi_flt z, r, f;
+
+  z = (x - par[0])/par[1];
+  r = SQ(x - par[4])*exp (-0.5*SQ(z));
+  f = par[2]*r + par[3];
+
+  dpar[0] = par[2]*r*z/par[1];
+  dpar[1] = par[2]*r*z*z/par[1];
+  dpar[2] = r;
+  dpar[3] = 1;
+  dpar[4] = -par[2]*(x - par[4])*exp(-0.5*SQ(z));
+  
+  return (f);
+
+}
Index: /branches/sc_branches/pantasks_condor/cmd.data/vpop.c
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.data/vpop.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.data/vpop.c	(revision 34783)
@@ -0,0 +1,27 @@
+# include "data.h"
+
+int vpop (int argc, char **argv) {
+
+  int Npix;
+  Vector *vec;
+
+  if (argc != 2) {
+    gprint (GP_ERR, "USAGE: vpop (vector)\n");
+    gprint (GP_ERR, "  remove first element of vector\n");
+    return (FALSE);
+  }
+
+  if ((vec = SelectVector (argv[1], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+  Npix = vec[0].Nelements;
+  if (Npix < 1) return (TRUE);
+
+  if (Npix > 1) {
+    if (vec[0].type == OPIHI_FLT) {
+      memmove (&vec[0].elements.Flt[0], &vec[0].elements.Flt[1], Npix*sizeof(opihi_flt));
+    } else {
+      memmove (&vec[0].elements.Int[0], &vec[0].elements.Int[1], Npix*sizeof(opihi_int));
+    }
+  }
+  vec[0].Nelements = Npix - 1;
+  return (TRUE);
+}
Index: /branches/sc_branches/pantasks_condor/cmd.data/vroll.c
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.data/vroll.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.data/vroll.c	(revision 34783)
@@ -0,0 +1,29 @@
+# include "data.h"
+
+int vroll (int argc, char **argv) {
+
+  int Npix;
+  opihi_flt first;
+  Vector *vec;
+
+  if (argc != 2) {
+    gprint (GP_ERR, "USAGE: vroll (vector)\n");
+    gprint (GP_ERR, "  roll vector elements (first goes to end)\n");
+    return (FALSE);
+  }
+
+  if ((vec = SelectVector (argv[1], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+  Npix = vec[0].Nelements;
+  if (Npix < 2) return (TRUE);
+
+  if (vec[0].type == OPIHI_FLT) {
+    first = vec[0].elements.Flt[0];
+    memmove (&vec[0].elements.Flt[0], &vec[0].elements.Flt[1], Npix*sizeof(opihi_flt));
+    vec[0].elements.Flt[Npix-1] = first;
+  } else {
+    first = vec[0].elements.Int[0];
+    memmove (&vec[0].elements.Int[0], &vec[0].elements.Int[1], Npix*sizeof(opihi_int));
+    vec[0].elements.Int[Npix-1] = first;
+  }
+  return (TRUE);
+}
Index: /branches/sc_branches/pantasks_condor/cmd.data/vshift.c
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.data/vshift.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.data/vshift.c	(revision 34783)
@@ -0,0 +1,73 @@
+# include "data.h"
+
+int vshift (int argc, char **argv) {
+
+  int isPos, N, Npix, Nshift, delta, ROLL;
+  Vector *ivec, *ovec;
+
+  ROLL = FALSE;
+  if ((N = get_argument (argc, argv, "-roll"))) {
+    remove_argument (N, &argc, argv);
+    ROLL = TRUE;
+  }
+
+  if (argc != 4) {
+    gprint (GP_ERR, "USAGE: vshift (input) (output) (delta) [-roll]\n");
+    gprint (GP_ERR, "  shift vector by (delta) elements\n");
+    gprint (GP_ERR, "  a positive value move element (i) to (i+delta)\n");
+    gprint (GP_ERR, "  -roll : move dropped values to the other size (no elements are lost)\n");
+    return (FALSE);
+  }
+
+  if ((ivec = SelectVector (argv[1], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((ovec = SelectVector (argv[2], ANYVECTOR, TRUE)) == NULL) return (FALSE);
+  delta = atoi(argv[3]);
+  isPos = delta > 0;
+  delta = abs(delta);
+    
+  ResetVector (ovec, ivec->type, ivec->Nelements);
+
+  Npix = ivec[0].Nelements;
+  if (delta > Npix) {
+    if (!ROLL) {
+      if (ivec[0].type == OPIHI_FLT) {
+	memset (ovec[0].elements.Flt, 0, Npix*sizeof(opihi_flt));
+      } else {
+	memset (ovec[0].elements.Flt, 0, Npix*sizeof(opihi_flt));
+      }
+      return TRUE;
+    }
+    delta = delta % Npix;
+  }
+
+  if (ivec[0].type == OPIHI_FLT) {
+    memset (ovec[0].elements.Flt, 0, Npix*sizeof(opihi_flt));
+    Nshift = Npix - delta;
+    if (isPos) {
+      memcpy (&ovec[0].elements.Flt[delta], &ivec[0].elements.Flt[0], Nshift*sizeof(opihi_flt));
+      if (ROLL) {
+	memcpy (&ovec[0].elements.Flt[0], &ivec[0].elements.Flt[Nshift], delta*sizeof(opihi_flt));
+      } 
+    } else {
+      memcpy (&ovec[0].elements.Flt[0], &ivec[0].elements.Flt[delta], Nshift*sizeof(opihi_flt));
+      if (ROLL) {
+	memcpy (&ovec[0].elements.Flt[Nshift], &ivec[0].elements.Flt[0], delta*sizeof(opihi_flt));
+      }
+    }
+  } else {
+    memset (ovec[0].elements.Int, 0, Npix*sizeof(opihi_int));
+    Nshift = Npix - delta;
+    if (isPos) {
+      memcpy (&ovec[0].elements.Int[delta], &ivec[0].elements.Int[0], Nshift*sizeof(opihi_int));
+      if (ROLL) {
+	memcpy (&ovec[0].elements.Int[0], &ivec[0].elements.Int[Nshift], delta*sizeof(opihi_int));
+      } 
+    } else {
+      memcpy (&ovec[0].elements.Int[0], &ivec[0].elements.Int[delta], Nshift*sizeof(opihi_int));
+      if (ROLL) {
+	memcpy (&ovec[0].elements.Int[Nshift], &ivec[0].elements.Int[0], delta*sizeof(opihi_int));
+      }
+    }
+  }
+  return (TRUE);
+}
Index: /branches/sc_branches/pantasks_condor/cmd.data/vsmooth.c
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.data/vsmooth.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.data/vsmooth.c	(revision 34783)
@@ -0,0 +1,67 @@
+# include "data.h"
+
+int vsmooth (int argc, char **argv) {
+  
+  int i, n, N, Nx, Ns, Ngauss, isFloat;
+  opihi_flt *vf, *vo, *gauss, *gaussnorm;
+  opihi_int *vi;
+  float g, s, sigma, Nsigma, value;
+  Vector *in;
+
+  Nsigma = 3;
+  if ((N = get_argument (argc, argv, "-Nsigma"))) {
+    remove_argument (N, &argc, argv);
+    Nsigma = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  if (argc != 3) {
+    gprint (GP_ERR, "USAGE: vsmooth (input) sigma\n");
+    return (FALSE);
+  }
+  
+  if ((in  = SelectVector (argv[1], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+  sigma = atof (argv[2]);
+  Nx = in[0].Nelements;
+
+  /* build a 1D gaussian */
+  Ns = (int) (Nsigma*sigma + 0.5);
+  Ngauss = 2*Ns + 1;
+  ALLOCATE (gaussnorm, opihi_flt, Ngauss);
+  gauss = &gaussnorm[Ns];
+  for (i = -Ns; i < Ns + 1; i++) {
+    gauss[i] = exp ((i*i)/(-2*sigma*sigma));
+  }
+
+  ALLOCATE (vo, opihi_flt, Nx);
+
+  isFloat = (in[0].type == OPIHI_FLT);
+  vf = in[0].elements.Flt;
+  vi = in[0].elements.Int;
+
+  for (i = 0; i < Nx; i++) {
+    g = s = 0;
+    for (n = -Ns; n < Ns + 1; n++) {
+      if (i+n < 0) continue;
+      if (i+n >= Nx) continue;
+      value = isFloat ? vf[i+n] : vi[i+n];
+      s += gauss[n]*value;
+      g += gauss[n];
+    }
+    vo[i] = s / g;
+  }
+
+  free (gaussnorm);
+
+  if (isFloat) {
+    free (in[0].elements.Flt);
+  } else {
+    free (in[0].elements.Int);
+  }
+
+  // smoothing an int vector results in a float vector
+  in[0].type = OPIHI_FLT;
+  in[0].elements.Flt = vo;
+  return (TRUE);
+}
+
Index: /branches/sc_branches/pantasks_condor/cmd.data/vstats.c
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.data/vstats.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.data/vstats.c	(revision 34783)
@@ -0,0 +1,231 @@
+# include "data.h"
+
+int vstats (int argc, char **argv) {
+  
+  int i, iter, N, Nbin, Niter;
+  double max, min, pmin, pmax, sum, var, dvar, mean, stdev;
+  float IgnoreValue, Nsigma;
+  int Ignore, Quiet;
+
+  int *Nval, bin, Nmode, Nmed, Nused;
+  double dx, mode, median, threshold;
+  Vector *vec;
+  char *mask = NULL;
+
+  IgnoreValue = 0;
+  Ignore = FALSE;
+  if ((N = get_argument (argc, argv, "-ignore"))) {
+    Ignore = TRUE;
+    remove_argument (N, &argc, argv);
+    IgnoreValue = atof(argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  Niter = 1;
+  Nsigma = 3.0;
+
+  if ((N = get_argument (argc, argv, "-sigma-clip"))) {
+    remove_argument (N, &argc, argv);
+    Nsigma = atof(argv[N]);
+    Niter = 3;
+    remove_argument (N, &argc, argv);
+  }
+
+  if ((N = get_argument (argc, argv, "-iter"))) {
+    remove_argument (N, &argc, argv);
+    Niter = atoi(argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  Quiet = FALSE;
+  if ((N = get_argument (argc, argv, "-q"))) {
+    Quiet = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+  if ((N = get_argument (argc, argv, "-quiet"))) {
+    Quiet = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+
+  if (argc != 2) {
+    gprint (GP_ERR, "USAGE: vstat (vector) [-ignore value] [-q] [-quiet] [-iter Niter] [-sigma-clip Nsigma]\n");
+    gprint (GP_ERR, "  default is 1 iteration without sigma clipping or 3 with sigma clipping\n");
+    return (FALSE);
+  }
+
+  if ((vec = SelectVector (argv[1], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+
+  /* we need two passes, one for max, min, mean, sum, one for median, stdev, etc */
+
+  // set a good / bad mask
+  ALLOCATE (mask, char, vec[0].Nelements);
+  if (vec[0].type == OPIHI_FLT) {
+    opihi_flt *X = vec[0].elements.Flt;
+    for (i = 0; i < vec[0].Nelements; i++, X++) {
+      mask[i] = 1;
+      if (!finite (*X)) continue;
+      if (Ignore && (*X == IgnoreValue)) continue;
+      mask[i] = 0;
+    }      
+  } else {
+    opihi_int *X = vec[0].elements.Int;
+    for (i = 0; i < vec[0].Nelements; i++, X++) {
+      mask[i] = 1;
+      if (!finite (*X)) continue;
+      if (Ignore && (*X == IgnoreValue)) continue;
+      mask[i] = 0;
+    }      
+  }
+
+  /* calculate max, min, mean, sum, npix */
+  max = -HUGE_VAL;
+  min = HUGE_VAL;
+  sum = N = 0;
+  if (vec[0].type == OPIHI_FLT) {
+    opihi_flt *X = vec[0].elements.Flt;
+    for (i = 0; i < vec[0].Nelements; i++, X++) {
+      if (mask[i]) continue;
+      max = MAX (*X, max);
+      min = MIN (*X, min);
+      sum += *X;
+      N++;
+    }      
+  } else {
+    opihi_int *X = vec[0].elements.Int;
+    for (i = 0; i < vec[0].Nelements; i++, X++) {
+      if (mask[i]) continue;
+      max = MAX (*X, max);
+      min = MIN (*X, min);
+      sum += *X;
+      N++;
+    }      
+  }
+  mean = sum / N;
+
+  // we do Niter passes; on each pass, we exclude entries > Nsigma from the median
+  pmin = min;
+  pmax = max;
+
+  stdev = mode = median = Nused = 0.0;
+
+  for (iter = 0; iter < Niter; iter ++) {
+
+    // reduce Nbin after the first iteration?
+    Nbin = 1000;
+
+    /* calculate median and mode with resolution of (max - min) / 1000 */ 
+    dx = (pmax - pmin) / Nbin;
+    if (dx == 0) {
+      median = mode = min;
+      stdev = 0.0;
+      goto skip;
+    }
+
+    ALLOCATE (Nval, int, Nbin + 2);
+    bzero (Nval, Nbin*sizeof(int));
+    var = 0;
+    if (vec[0].type == OPIHI_FLT) {
+      opihi_flt *X = vec[0].elements.Flt;
+      for (i = 0; i < vec[0].Nelements; i++, X++) {
+	if (mask[i]) continue;
+	bin = MAX (0, MIN (Nbin, (*X - pmin) / dx));
+	Nval[bin] ++;
+	dvar = (*X - mean);
+	var += dvar*dvar;
+      }      
+    } else {
+      opihi_int *X = vec[0].elements.Int;
+      for (i = 0; i < vec[0].Nelements; i++, X++) {
+	if (mask[i]) continue;
+	bin = MAX (0, MIN (Nbin, (*X - pmin) / dx));
+	Nval[bin] ++;
+	dvar = (*X - mean);
+	var += dvar*dvar;
+      }      
+    }
+    stdev = sqrt (var / (N - 1));
+
+    Nmode = 0;
+    mode = Nval[Nmode];
+    median = 0;
+    Nmed = -1;
+    for (i = 0; i < Nbin + 1; i++) {
+      if (Nmed == -1) {
+	median += Nval[i];
+	if (median >= N / 2.0) {
+	  Nmed = i;
+	  median = i * dx + pmin;
+	}
+      }
+      if (mode < Nval[i]) {
+	Nmode = i;
+	mode = Nval[Nmode];
+      }
+    }
+    mode = Nmode * dx + pmin;
+    free (Nval);
+
+    threshold = Nsigma * stdev;
+
+    // we are going to do another pass: mark the entries to skip
+    pmin = min;
+    pmax = max;
+    if (iter < Niter - 1) {
+      if (vec[0].type == OPIHI_FLT) {
+	opihi_flt *X = vec[0].elements.Flt;
+	for (i = 0; i < vec[0].Nelements; i++, X++) {
+	  if (mask[i]) continue;
+	  if (fabs(*X - median) > threshold) {
+	    mask[i] = 1;
+	  } else {
+	    pmin = MIN (*X, pmin);
+	    pmax = MAX (*X, pmax);
+	  }      
+	}
+      } else {
+	opihi_int *X = vec[0].elements.Int;
+	for (i = 0; i < vec[0].Nelements; i++, X++) {
+	  if (mask[i]) continue;
+	  if (fabs(*X - median) > threshold) {
+	    mask[i] = 1;
+	  } else {
+	    pmin = MIN (*X, pmin);
+	    pmax = MAX (*X, pmax);
+	  }
+	}      
+      }
+    }
+    // gprint (GP_ERR, "iter %d, mean: %g, stdev: %g, min: %g, max: %g, median: %g, mode: %g, Npts: %d\n", 
+    // iter, mean, stdev, min, max, median, mode, N);
+  }
+
+  Nused = 0;
+  for (i = 0; i < vec[0].Nelements; i++) {
+    if (mask[i]) continue;
+    Nused ++;
+  }
+
+skip:
+  FREE(mask);
+  if (mask) mask = NULL;
+
+  if (!Quiet) {
+    gprint (GP_ERR, "mean: %g, stdev: %g, min: %g, max: %g, median: %g, mode: %g, Npts: %d\n", 
+	    mean, stdev, min, max, median, mode, N);
+  }
+
+  set_variable ("PMIN",      pmin);
+  set_variable ("PMAX",      pmax);
+  set_variable ("MIN",      min);
+  set_variable ("MAX",      max);
+  set_variable ("MEDIAN",   median);
+  set_variable ("MEAN",     mean);
+  set_variable ("MODE",     mode);
+  set_variable ("TOTAL",    sum);
+  set_int_variable ("NPIX", N);
+  set_int_variable ("NPTS", N);
+  set_int_variable ("NUSED", Nused);
+  set_variable ("SIGMA",    stdev);
+  return (TRUE);
+}
+
Index: /branches/sc_branches/pantasks_condor/cmd.data/vzload.c
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.data/vzload.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.data/vzload.c	(revision 34783)
@@ -0,0 +1,88 @@
+# include "data.h"
+
+int vzload (int argc, char **argv) {
+  
+  int i, N, Noverlay;
+  int kapa, type;
+  char *name;
+  double size, min, range, MAX_OUTPUT_SIZE;
+  KiiOverlay *overlay;
+  Vector *vecx, *vecy, *vecz;
+  
+  name = NULL;
+  if ((N = get_argument (argc, argv, "-n"))) {
+    remove_argument (N, &argc, argv);
+    name = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+  if (!GetImage (NULL, &kapa, name)) return (FALSE);
+  FREE (name);
+
+  MAX_OUTPUT_SIZE = 10.0;
+  if ((N = get_argument (argc, argv, "-max"))) {
+    remove_argument (N, &argc, argv);
+    MAX_OUTPUT_SIZE = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  type = KII_OVERLAY_BOX;
+  if ((N = get_argument (argc, argv, "-type"))) {
+    remove_argument (N, &argc, argv);
+    type = KiiOverlayTypeByName (argv[N]);
+    remove_argument (N, &argc, argv);
+    if (!type) {
+      gprint (GP_ERR, "unknown Kii point type %s\n", argv[N]);
+      return (FALSE);
+    }
+  }
+
+  if (argc != 7) {
+    gprint (GP_ERR, "USAGE: vzload (overlay) (xvec) (yvec) (zvec) (min) (max) [-n] [-type]\n");
+    return (FALSE);
+  }
+  
+  if ((vecx = SelectVector (argv[2], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((vecy = SelectVector (argv[3], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((vecz = SelectVector (argv[4], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+  if (vecx[0].Nelements != vecy[0].Nelements) {
+    gprint (GP_ERR, "mismatched vector lengths\n");
+    return (FALSE);
+  }
+
+  min = atof(argv[5]);
+  range = (atof(argv[6]) - min) / MAX_OUTPUT_SIZE;
+  // renormalize to the max output size (output range is 0.1 - MAX_OUTPUT_SIZE)
+
+  Noverlay = vecx[0].Nelements;
+  ALLOCATE (overlay, KiiOverlay, Noverlay);
+
+  for (i = N = 0; i < Noverlay; i++) {
+    float z = (vecz[0].type == OPIHI_FLT) ? vecz[0].elements.Flt[i] : vecz[0].elements.Int[i];
+    size = MIN (MAX_OUTPUT_SIZE, (z - min) / range);
+    if (size < 0.1) continue;
+
+    overlay[N].type = type;
+    overlay[N].text = NULL;
+    overlay[N].x = (vecx[0].type == OPIHI_FLT) ? vecx[0].elements.Flt[i] : vecx[0].elements.Int[i];
+    overlay[N].y = (vecy[0].type == OPIHI_FLT) ? vecy[0].elements.Flt[i] : vecy[0].elements.Int[i];
+
+    overlay[N].x += 0.5;
+    overlay[N].y += 0.5;
+
+    overlay[N].angle = 0.0;
+
+    if (type == KII_OVERLAY_CIRCLE) {
+      overlay[N].dx = size / 2.0;
+      overlay[N].dy = size / 2.0;
+    } else {
+      overlay[N].dx = size;
+      overlay[N].dy = size;
+    }    
+    N++;
+  }
+
+  KiiLoadOverlay (kapa, overlay, N, argv[1]);
+  free (overlay);
+  return (TRUE);
+}
+
Index: /branches/sc_branches/pantasks_condor/cmd.data/wd.c
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.data/wd.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.data/wd.c	(revision 34783)
@@ -0,0 +1,188 @@
+# include "data.h"
+
+int wd (int argc, char **argv) {
+  
+  int N, Extend;
+  int newUnsign, newBitpix, newScale, newZero;
+  int outUnsign, outBitpix;
+  double outScale, outZero;
+  Header temp_header;
+  Matrix temp_matrix;
+  Buffer *buf;
+
+  /* XXX I must have dropped the old 'newplane' option */
+  Extend  = FALSE;
+  if ((N = get_argument (argc, argv, "-extend"))) {
+    remove_argument (N, &argc, argv);
+    Extend  = TRUE;
+  }
+
+  outZero = 0;
+  newZero = FALSE;
+  if ((N = get_argument (argc, argv, "-bzero"))) {
+    remove_argument (N, &argc, argv);
+    outZero  = atof(argv[N]);
+    newZero  = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+
+  outScale = 1;
+  newScale = FALSE;
+  if ((N = get_argument (argc, argv, "-bscale"))) {
+    remove_argument (N, &argc, argv);
+    outScale = atof(argv[N]);
+    newScale = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+
+  outBitpix = 16;
+  newBitpix = FALSE;
+  if ((N = get_argument (argc, argv, "-bitpix"))) {
+    remove_argument (N, &argc, argv);
+    outBitpix = atof(argv[N]);
+    newBitpix = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+
+  outUnsign = FALSE;
+  newUnsign = FALSE;
+  if ((N = get_argument (argc, argv, "-unsign"))) {
+    remove_argument (N, &argc, argv);
+    outUnsign = -1;
+    if (!strcasecmp (argv[N], "t") || !strcasecmp (argv[N], "true")) outUnsign = TRUE;
+    if (!strcasecmp (argv[N], "f") || !strcasecmp (argv[N], "false")) outUnsign = FALSE;
+    if (outUnsign == -1) {
+      gprint (GP_ERR, "-unsign options: t, f, true, false\n");
+      return (FALSE);
+    }
+    newUnsign = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+
+  if (argc != 3) {
+    gprint (GP_ERR, "USAGE: wd <buffer> <filename> [-bitpix N] [-bscale X] [-bzero X] [-extend] [-newplane]\n");
+    return (FALSE);
+  }
+
+  if ((buf = SelectBuffer (argv[1], OLDBUFFER, TRUE)) == NULL) return (FALSE);
+
+  if (!newBitpix) outBitpix = buf[0].bitpix;
+  if (!newScale) outScale = buf[0].bscale;
+  if (!newZero) outZero = buf[0].bzero;
+  if (!newUnsign) outUnsign = buf[0].unsign;
+
+  /* Convert the buffer from (float) to correct format */
+  /* save the (float) version, write out a temporary buffer */
+  temp_matrix = buf[0].matrix;
+  ALLOCATE (temp_matrix.buffer, char, MAX(1, temp_matrix.datasize));
+  memcpy (temp_matrix.buffer, buf[0].matrix.buffer, temp_matrix.datasize);
+  temp_header = buf[0].header;
+  ALLOCATE (temp_header.buffer, char, MAX(1, temp_header.datasize));
+  memcpy (temp_header.buffer, buf[0].header.buffer, temp_header.datasize);
+
+  if (temp_header.Naxes) {
+    // the inBlank value probably does not matter: temp_matrix is float, so nan is used
+    gfits_convert_format (&temp_header, &temp_matrix, outBitpix, outScale, outZero, 0xffff, outUnsign);
+  } else {
+    gfits_modify (&temp_header, "BITPIX", "%d", 1, outBitpix);
+    gfits_modify (&temp_header, "BSCALE", "%lf", 1, outScale);
+    gfits_modify (&temp_header, "BZERO",  "%lf", 1, outZero);
+    gfits_modify_alt (&temp_header, "UNSIGN", "%t", 1, outUnsign);
+  }
+
+  if (Extend) {
+    Header Xhead;
+    FILE *f;
+    off_t nbytes;
+    int status, Nextend;
+
+    /* assume failure means non-existent file */
+    if (!gfits_read_header (argv[2], &Xhead)) {
+
+      gfits_init_header (&Xhead);
+      Xhead.bitpix = 16;
+      Xhead.extend = TRUE;
+      gfits_create_header (&Xhead);
+
+      gfits_modify (&Xhead, "NEXTEND", "%d", 1, 0);
+      f = fopen (argv[2], "w");
+      fclose (f);
+    }
+
+    gfits_modify_alt (&Xhead, "EXTEND", "%t", 1, TRUE);
+
+    Nextend = 0;
+    gfits_scan (&Xhead, "NEXTEND", "%d", 1, &Nextend);
+    Nextend ++;
+    gfits_modify (&Xhead, "NEXTEND", "%d", 1, Nextend);
+
+    /* write the main header to the start of the file */
+    f = fopen (argv[2], "r+");
+    if (f == NULL) {
+      gprint (GP_ERR, "failed to write file\n");
+      status = FALSE;
+      goto done1;
+    }
+    
+    /* position to begining of file to write header */
+    fseeko (f, 0LL, SEEK_SET);
+    nbytes = fwrite (Xhead.buffer, 1, Xhead.datasize, f);
+    if (nbytes != Xhead.datasize) {
+      gprint (GP_ERR, "ERROR: failed writing data to image header\n");
+      status = FALSE;
+      goto done1;
+    }
+    
+    /* fix up header */
+    {
+      static char simple[] = "XTENSION= 'IMAGE  '            / Image extension";
+      int Ns, No;
+      Ns = strlen (simple);
+      No = 80 - Ns;
+      strncpy (temp_header.buffer, simple, Ns);
+      memset (&temp_header.buffer[Ns], ' ', No);
+    }
+
+    /* position to end of file to write new extend */
+    fseeko (f, 0LL, SEEK_END);
+    nbytes = fwrite (temp_header.buffer, 1, temp_header.datasize, f);
+    fclose (f);
+    if (nbytes != temp_header.datasize) {
+      gprint (GP_ERR, "failed to write file\n");
+      status = FALSE;
+      goto done1;
+    }
+    /* write the matrix buffer (automatically goes to end of file */
+    if (!gfits_write_matrix (argv[2], &temp_matrix)) {
+      gprint (GP_ERR, "failed to write file\n");
+      status = FALSE;
+      goto done1;
+    }
+    status = TRUE;
+  done1:
+    gfits_free_header (&Xhead);
+    gfits_free_header (&temp_header);
+    gfits_free_matrix (&temp_matrix);
+    return (status);
+  }
+  
+  /* the actual write-to-disk goes here */
+  if (!gfits_write_header (argv[2], &temp_header)) {
+    gprint (GP_ERR, "failed to write header\n");
+    gfits_free_header (&temp_header);
+    gfits_free_matrix (&temp_matrix);
+    return (FALSE);
+  }
+  
+  if (!gfits_write_matrix (argv[2], &temp_matrix)) {
+    gprint (GP_ERR, "failed to write matrix\n");
+    gfits_free_header (&temp_header);
+    gfits_free_matrix (&temp_matrix);
+    return (FALSE);
+  }
+
+  gfits_free_header (&temp_header);
+  gfits_free_matrix (&temp_matrix);
+  
+  return (TRUE);
+}
Index: /branches/sc_branches/pantasks_condor/cmd.data/write_vectors.c
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.data/write_vectors.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.data/write_vectors.c	(revision 34783)
@@ -0,0 +1,226 @@
+# include "data.h"
+
+int write_vectors (int argc, char **argv) {
+  
+  int append;
+  int i, j, Nvec, Ne, N;
+  FILE *f;
+  char **fmtlist, *fmttype;
+  char *p0, *p1, *p2, *format, *FITS;
+  Vector **vec;
+
+  /* look for format option */
+  format = (char *) NULL;
+  if ((N = get_argument (argc, argv, "-f"))) {
+    remove_argument (N, &argc, argv);
+    format = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  /* option generate a FITS output table */
+  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;
+  }
+
+  append = FALSE;
+  if ((N = get_argument (argc, argv, "-append"))) {
+    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;
+  }
+
+  if (argc < 3) {
+    gprint (GP_ERR, "USAGE: write [-append] [-f \"format\"] file vector vector ...\n");
+    return (FALSE);
+  }
+
+  /* find number of output vectors */
+  Nvec = (argc - 2);
+  if (Nvec < 1) {
+    gprint (GP_ERR, "USAGE: write (file) vector vector ...\n");
+    return (FALSE);
+  }
+  ALLOCATE (vec, Vector *, Nvec);
+
+  /* select/check vectors from list */
+  for (i = 0; i < Nvec; i++) {
+    if ((vec[i] = SelectVector (argv[i + 2], OLDVECTOR, FALSE)) == NULL) {
+      gprint (GP_ERR, "unknown vector %s\n", argv[i+2]);
+      gprint (GP_ERR, "USAGE: write (file) vector vector ...\n");
+      free (vec);
+      return (FALSE);    
+    }
+  }
+  
+  /* select vector lengths */
+  Ne = vec[0][0].Nelements;
+  for (i = 0; i < Nvec; i++) {
+    if (vec[0][0].Nelements != Ne) {
+      gprint (GP_ERR, "error: vectors must all be the same size\n");
+      free (vec);
+      return (FALSE);    
+    }
+  }
+
+  if (FITS) {
+    int status = WriteVectorTableFITS (argv[1], FITS, vec, Nvec, append, format);
+    free (vec);
+    return status;
+  }
+
+  /* open file for output */
+  if (append) {
+    f = fopen (argv[1], "a");
+  } else {
+    f = fopen (argv[1], "w");
+  }
+  if (f == (FILE *) NULL) {
+    gprint (GP_ERR, "can't open file for write\n");
+    return (FALSE);
+  }
+
+  /* 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) {
+	  if (CSV) {
+	    fprintf (f, "%.12g,", vec[j][0].elements.Flt[i]);
+	  } else {
+	    fprintf (f, "%.12g ", vec[j][0].elements.Flt[i]);
+	  }
+	} else {
+	  if (CSV) {
+	    fprintf (f, "%d,", vec[j][0].elements.Int[i]);
+	  } else {
+	    fprintf (f, "%d ", vec[j][0].elements.Int[i]);
+	  }
+	}
+      }
+      fprintf (f, "\n");
+    } 
+    fclose (f);
+    free (vec);
+    fflush (f);
+    return (TRUE);
+  }
+
+  /* construct an array of format strings */
+  ALLOCATE (fmttype, char, Nvec);
+  ALLOCATE (fmtlist, char *, Nvec);
+  for (i = 0; i < Nvec; i++) {
+    ALLOCATE (fmtlist[i], char, 1024);
+    bzero (fmtlist[i], 1024);
+  }
+
+  p0 = format;
+  for (j = 0; j < Nvec; j++) {
+    /* find this format character */
+    p1 = strchr (p0, '%');
+    if (p1 == (char *) NULL) {
+      gprint (GP_ERR, "mismatch between format and values\n");
+      free (fmttype);
+      for (i = 0; i < Nvec; i++) free (fmtlist[i]);
+      free (fmtlist);
+      free (format);
+      fclose (f);
+      fflush (f);
+      return (FALSE);
+    }
+    
+    /* identify type (%NNNNd %NNNNf) */
+    for (p2 = p1 + 1; (*p2 == '.') || (*p2 == '-') || (*p2 == '+') || (*p2 == ' ') || isdigit(*p2); p2++);
+    strncpy (fmtlist[j], p0, p2 - p0 + 1);
+    switch (*p2) {
+      case 'e':
+      case 'f':
+	fmttype[j] = 'f';
+	break;
+      case 'd':
+      case 'c':
+      case 'x':
+	fmttype[j] = 'd';
+	break;
+      default:
+	gprint (GP_ERR, "syntax error in format (only e,f,d,c,x allowed)\n");
+	return (FALSE);
+    }
+    p0 = p2 + 1;
+  }
+  strcat (fmtlist[Nvec-1], p0);
+  
+  for (i = 0; i < vec[0][0].Nelements; i++) {
+    for (j = 0; j < Nvec; j++) {
+      if (fmttype[j] == 'd') {
+	if (vec[j][0].type == OPIHI_FLT) {
+	  fprintf (f, fmtlist[j], (opihi_int)(vec[j][0].elements.Flt[i]));
+	} else {
+	  fprintf (f, fmtlist[j], (opihi_int)(vec[j][0].elements.Int[i]));
+	}
+      } 
+      if (fmttype[j] == 'f') {
+	if (vec[j][0].type == OPIHI_FLT) {
+	  fprintf (f, fmtlist[j], (opihi_flt)(vec[j][0].elements.Flt[i]));
+	} else {
+	  fprintf (f, fmtlist[j], (opihi_flt)(vec[j][0].elements.Int[i]));
+	}
+      } 
+    }
+    fprintf (f, "\n");
+  }
+  fclose (f);
+  fflush (f);
+
+  free (fmttype);
+  for (i = 0; i < Nvec; i++) free (fmtlist[i]);
+  free (fmtlist);
+  free (format);
+  
+  return (TRUE);
+
+}
+
+
Index: /branches/sc_branches/pantasks_condor/cmd.data/zap.c
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.data/zap.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.data/zap.c	(revision 34783)
@@ -0,0 +1,43 @@
+# include "data.h"
+
+int zap (int argc, char **argv) {
+
+  int i, j, N;
+  int sx, sy, nx, ny;
+  float *V, value;
+  Buffer *buf;
+
+  value = 0;
+  if ((N = get_argument (argc, argv, "-v"))) {
+    remove_argument (N, &argc, argv);
+    value  = atof(argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  if (argc != 6) {
+    gprint (GP_ERR, "USAGE: zap <buffer> sx sy nx ny [-v value]\n");
+    return (FALSE);
+  }
+  if ((buf = SelectBuffer (argv[1], OLDBUFFER, TRUE)) == NULL) return (FALSE);
+
+  sx = atof (argv[2]);
+  sy = atof (argv[3]);
+  nx = atof (argv[4]);
+  ny = atof (argv[5]);
+
+  if (sx < 0) goto error;
+  if (sy < 0) goto error;
+  if (sx + nx > buf[0].matrix.Naxis[0]) goto error;
+  if (sy + ny > buf[0].matrix.Naxis[1]) goto error;
+
+  for (j = sy; j < sy + ny; j++) {
+    V = (float *)(buf[0].matrix.buffer) + j*buf[0].matrix.Naxis[0] + sx; 
+    for (i = 0; i < nx; i++, V++) *V = value;
+  }
+  return (TRUE);
+
+ error:
+  gprint (GP_ERR, "region out of range\n");
+  return (FALSE);
+}
+
Index: /branches/sc_branches/pantasks_condor/cmd.data/zplot.c
===================================================================
--- /branches/sc_branches/pantasks_condor/cmd.data/zplot.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/cmd.data/zplot.c	(revision 34783)
@@ -0,0 +1,185 @@
+# include "data.h"
+
+int zplot (int argc, char **argv) {
+  
+  char *outname = NULL;
+  int i, kapa, valid, size;
+  opihi_flt *out;
+  double min, range;
+  Graphdata graphmode;
+  Vector *xvec, *yvec, *zvec, Zvec;
+
+  if (!style_args (&graphmode, &argc, argv, &kapa)) return (FALSE);
+
+  valid  = (argc == 6);
+  valid |= (argc > 7) && !strcmp (argv[6], "where");
+  if (!valid) {
+    gprint (GP_ERR, "USAGE: zplot <x> <y> <z> min max\n");
+    gprint (GP_ERR, "   OR: zplot <x> <y> <z> min max where (condition)\n");
+    return (FALSE);
+  }
+
+  min = atof(argv[4]);
+  range = atof(argv[5]) - min;
+
+  // tvec is used for logical test (truth vector)
+  Vector *tvec = NULL;
+  char *mask = NULL;
+  if (argc > 7) {
+    outname = dvomath (argc - 7, &argv[7], &size, 1);
+    if (outname == NULL) {
+      print_error ();
+      return FALSE;
+    }
+    if ((tvec = SelectVector (outname, OLDVECTOR, TRUE)) == NULL) {
+      gprint (GP_ERR, " invalid logic result\n");
+      DeleteNamedVector (outname);
+      free (outname);
+      return (FALSE);
+    }
+  }
+
+  /* find vectors */
+  if ((xvec = SelectVector (argv[1], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((yvec = SelectVector (argv[2], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((zvec = SelectVector (argv[3], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+  if (xvec[0].Nelements != yvec[0].Nelements) {
+    gprint (GP_ERR, "vectors %s and %s not the same length\n", argv[1], argv[2]);
+    return (FALSE);
+  }
+  if (xvec[0].Nelements != zvec[0].Nelements) {
+    gprint (GP_ERR, "vectors %s and %s not the same length\n", argv[1], argv[3]);
+    return (FALSE);
+  }
+  if (tvec && tvec[0].Nelements != yvec[0].Nelements) {
+    gprint (GP_ERR, "logic test vector not the same length as data vectors\n");
+    return (FALSE);
+  }
+  SetVector (&Zvec, OPIHI_FLT, zvec[0].Nelements);
+  out = Zvec.elements.Flt;
+ 
+  if (zvec[0].type == OPIHI_FLT) {
+    opihi_flt *in = zvec[0].elements.Flt;
+    for (i = 0; i < Zvec.Nelements; i++, in++, out++) {
+      *out = MIN (1.0, MAX (0.01, (*in - min) / range));
+    }
+  } else {
+    opihi_int *in = zvec[0].elements.Int;
+    for (i = 0; i < Zvec.Nelements; i++, in++, out++) {
+      *out = MIN (1.0, MAX (0.01, (*in - min) / range));
+    }
+  }
+
+  if (tvec) {
+    ALLOCATE (mask, char, tvec->Nelements);
+    for (i = 0; i < tvec->Nelements; i++) {
+      mask[i] = (tvec->type == OPIHI_FLT) ? (tvec->elements.Flt[i] == 0.0) : (tvec->elements.Int[i] == 0.0);
+    }
+  }
+
+  /* point size determined by Zvec */
+  graphmode.style = 2; /* plot points */
+  graphmode.size = -1; /* point size determined by Zvec */
+  graphmode.etype = 0; /* no errorbars */
+  PlotVectorTriplet (kapa, xvec, yvec, &Zvec, mask, &graphmode);
+
+  free (Zvec.elements.Ptr);
+  if (mask) free (mask);
+  DeleteNamedVector (outname);
+
+  return (TRUE);
+
+}
+
+int zcplot (int argc, char **argv) {
+  
+  char *outname = NULL;
+  int i, kapa, valid, size;
+  opihi_flt *out;
+  double min, range;
+  Graphdata graphmode;
+  Vector *xvec, *yvec, *zvec, Zvec;
+
+  if (!style_args (&graphmode, &argc, argv, &kapa)) return (FALSE);
+
+  valid  = (argc == 6);
+  valid |= (argc > 7) && !strcmp (argv[6], "where");
+  if (!valid) {
+    gprint (GP_ERR, "USAGE: zcplot <x> <y> <z> min max\n");
+    gprint (GP_ERR, "   OR: zcplot <x> <y> <z> min max where (condition)\n");
+    return (FALSE);
+  }
+
+  min = atof(argv[4]);
+  range = atof(argv[5]) - min;
+
+  // tvec is used for logical test (truth vector)
+  Vector *tvec = NULL;
+  char *mask = NULL;
+  if (argc > 7) {
+    outname = dvomath (argc - 7, &argv[7], &size, 1);
+    if (outname == NULL) {
+      print_error ();
+      return FALSE;
+    }
+    if ((tvec = SelectVector (outname, OLDVECTOR, TRUE)) == NULL) {
+      gprint (GP_ERR, " invalid logic result\n");
+      DeleteNamedVector (outname);
+      free (outname);
+      return (FALSE);
+    }
+  }
+
+  /* find vectors */
+  if ((xvec = SelectVector (argv[1], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((yvec = SelectVector (argv[2], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((zvec = SelectVector (argv[3], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+  if (xvec[0].Nelements != yvec[0].Nelements) {
+    gprint (GP_ERR, "vectors %s and %s not the same length\n", argv[1], argv[2]);
+    return (FALSE);
+  }
+  if (xvec[0].Nelements != zvec[0].Nelements) {
+    gprint (GP_ERR, "vectors %s and %s not the same length\n", argv[1], argv[3]);
+    return (FALSE);
+  }
+  if (tvec && tvec[0].Nelements != yvec[0].Nelements) {
+    gprint (GP_ERR, "logic test vector not the same length as data vectors\n");
+    return (FALSE);
+  }
+  SetVector (&Zvec, OPIHI_FLT, zvec[0].Nelements);
+  out = Zvec.elements.Flt;
+ 
+  if (zvec[0].type == OPIHI_FLT) {
+    opihi_flt *in = zvec[0].elements.Flt;
+    for (i = 0; i < Zvec.Nelements; i++, in++, out++) {
+      *out = MIN (1.0, MAX (0.01, (*in - min) / range));
+    }
+  } else {
+    opihi_int *in = zvec[0].elements.Int;
+    for (i = 0; i < Zvec.Nelements; i++, in++, out++) {
+      *out = MIN (1.0, MAX (0.01, (*in - min) / range));
+    }
+  }
+
+  if (tvec) {
+    ALLOCATE (mask, char, tvec->Nelements);
+    for (i = 0; i < tvec->Nelements; i++) {
+      mask[i] = (tvec->type == OPIHI_FLT) ? (tvec->elements.Flt[i] == 0.0) : (tvec->elements.Int[i] == 0.0);
+    }
+  }
+
+  /* point size determined by Zvec */
+  graphmode.style = 2; /* plot points */
+  graphmode.color = -1; /* point color determined by Zvec */
+  graphmode.etype = 0; /* no errorbars */
+  PlotVectorTriplet (kapa, xvec, yvec, &Zvec, mask, &graphmode);
+
+  free (Zvec.elements.Ptr);
+  if (mask) free (mask);
+  DeleteNamedVector (outname);
+
+  return (TRUE);
+
+}
+
+
Index: /branches/sc_branches/pantasks_condor/dimm/Analysis.c
===================================================================
--- /branches/sc_branches/pantasks_condor/dimm/Analysis.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/dimm/Analysis.c	(revision 34783)
@@ -0,0 +1,191 @@
+# include "dimm.h"
+
+/* should this all be wrapped within an opihi implementation? */
+
+int subtractImage (Image *a, Image *b) {
+
+  if (a[0].Nx != b[0].Nx) return (FALSE);
+  if (a[0].Ny != b[0].Ny) return (FALSE);
+
+  Npix = a[0].Nx*a[0].Ny;
+  ap = (float *) a[0].buffer;
+  bp = (float *) b[0].buffer;
+  for (i = 0; i < Npix; i++, ap++, bp++) {
+    *ap -= *bp;
+  }
+  return (FALSE);
+}
+
+void statsImage (Image *image, Stats *stats) {
+
+  val = (float *)image[0].buffer;
+  max = min = val[0];
+  Npix = image[0].Nx*image[0].Ny;
+  for (i = 0; i < Npix; i++, val++) {
+    N1 += *val;
+    N2 += (*val)*(*val);
+    max = MAX (max, *val);
+    min = MIN (min, *val);
+  }
+  stats[0].mean  = N1 / Npix;
+  stats[0].sigma = sqrt (N2 / Npix - SQ(stats[0].mean));
+  stats[0].min = min;
+  stats[0].max = max;
+
+  stats[0].median = stats[0].mean;
+  range = MAX (0.5, 0xffff / (max - min));
+  if (range == 0) return;
+
+  ALLOCATE (hist, int, 0x10000);
+  bzero (hist, 0x10000*sizeof(int));
+
+  val = (float *)image[0].buffer;
+  for (i = 0; i < Npix; i++) {
+    bin = MIN (MAX (0, (*val - min) * range), 0xffff);
+    hist[bin] ++;
+  }
+
+  Nhist = 0;
+  for (i = 0; (i < 0xffff) && (Nhist < 0.5*Npix); i++) 
+    Nhist += hist[i];
+  stats[0].median = i / range + min;
+  free (hist);
+
+  return;
+}
+
+# if (0)
+void findStars (Image *image, Stars **stars, int *Nstars, double threshold) {
+
+  /* binarize @ threshold */
+
+  binimage = createImage (image[0].Nx, image[0].Ny);
+
+  Npix = image[0].Nx*image[0].Ny;
+  ap = image[0].buffer;
+  bp = binimage[0].buffer;
+  bzero (bp, Npix*sizeof (short));
+
+  for (i = 0; i < Npix; i++, ap++, bp++) {
+    if (*ap > threshold) * bp = 1;
+  }
+
+  clearpix ();
+
+  for (i = 0; i < Ny; i++) {
+    for (j = 0; j < Nx; j++) {
+      pix = j + i*Ny;
+      if (binimage.buffer[pix]) {
+	addpix (pix);
+	binimage.buffer[pix] = 0;
+	/* continue in row to end */
+	for (k = j + 1; k < Nx; k++) {
+	  pix = k + i*Nx;
+	  if (!binimage.buffer[pix]) { 
+	  }
+	}
+
+/* find contiguous trigger pixels in row from starting point */
+
+int fillrow (float *buffer, int Nx, int offset, int sx, int *xs, int *xe) {
+
+  trigger = FALSE;
+  for (i = sx, pix = offset + i; buffer[pix] && (i < Nx); i++, pix++) {
+    addpix (pix);
+    buffer[pix] = 0;
+    trigger = TRUE;
+    *xe = i;
+  }
+  for (i = sx - 1, pix = offset + i; (i >= 0) && buffer[pix]; i--, pix--) {
+    addpix (pix);
+    buffer[pix] = 0;
+    trigger = TRUE;
+    *xs = i;
+  }
+  return (trigger);
+}
+
+static int Npix = 0;
+static int *Pix = (int *) NULL;
+
+addpix (int pix) {
+  Npix ++;
+  if (Pix == (int *) NULL) {
+    ALLOCATE (Pix, int, MAX (1, Npix));
+  } else {
+    REALLOCATE (Pix, int, MAX (1, Npix));
+  }    
+  Pix[Npix - 1] = pix;
+}
+
+clearpix () {
+  Npix = 0;
+  REALLOCATE (Pix, int, 1);
+}
+
+statpix (double *x, double *y, float *buffer, int Nx) {
+
+  int X, Y;
+  double Sx, Sy, So;
+
+  So = Sx = Sy = 0;
+  for (i = 0; i < Npix; i++) {
+    Y = pix / Nx;
+    X = pix % Nx;
+    So += buffer[pix];
+    Sx += X * buffer[pix];
+    Sy += Y * buffer[pix];
+  }
+  *x = Sx / So;
+  *y = Sy / So;
+}
+
+/* find stars: 
+   - binarize @ threshold
+   - find all contiguous blobs
+   - find geom center of each blob
+*/
+
+
+
+/*
+
+.....................
+....x................
+...xxx...............
+..........xxx..........
+.........xx..........
+.....................
+.....................
+.....................
+.....................
+.....................
+
+
+ */
+
+# endif
+
+/* find stars: 
+   - binarize @ threshold
+   - find all contiguous blobs
+   - find geom center of each blob
+*/
+
+
+
+/*
+
+.....................
+....x................
+...xxx...............
+....x...xxx..........
+.........xx..........
+.....................
+.....................
+.....................
+.....................
+.....................
+
+
+ */
Index: /branches/sc_branches/pantasks_condor/dimm/Camera.c
===================================================================
--- /branches/sc_branches/pantasks_condor/dimm/Camera.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/dimm/Camera.c	(revision 34783)
@@ -0,0 +1,223 @@
+# include "dimm.h"
+
+static struct sbig_init info;
+
+int InitCamera (int port) {
+  
+  int i, state;
+
+  for (i = 0; i < 10; i++) {
+    state = sbig_init (port, SBIG_IMAGING_CCD, &info);
+    if (state == -6) state = 0;
+    if (state ==  0) {
+      DumpCameraInfo ();
+      return (TRUE);
+    }
+    gprint (GP_ERR, "retry...\n");
+  }
+  
+  gprint (GP_ERR, "failed to init sbig camera on %d\n", port);
+  gprint (GP_ERR, "%s\n", sbig_show_error (state));
+  return (FALSE);
+}
+
+void DumpCameraInfo () {
+
+      gprint (GP_ERR, "opened sbig camera:\n");
+      gprint (GP_ERR, "linux_version: %f\n",      info.linux_version);
+      gprint (GP_ERR, "nmbr_bad_columns: %d\n",   info.nmbr_bad_columns);
+      gprint (GP_ERR, "imaging_abg_type: %d\n",   info.imaging_abg_type);
+      gprint (GP_ERR, "serial_number: %s\n",      info.serial_number);
+      gprint (GP_ERR, "firmware_version: %d\n",   info.firmware_version);
+      gprint (GP_ERR, "camera_name: %s\n",        info.camera_name);
+      gprint (GP_ERR, "nmbr_readout_modes: %d\n", info.camera_info[0].nmbr_readout_modes);
+      gprint (GP_ERR, "mode: %d\n",               info.camera_info[0].readout_mode[0].mode);
+      gprint (GP_ERR, "width: %d\n",              info.camera_info[0].readout_mode[0].width);
+      gprint (GP_ERR, "height: %d\n",             info.camera_info[0].readout_mode[0].height);
+      gprint (GP_ERR, "gain: %d\n",               info.camera_info[0].readout_mode[0].gain);
+      gprint (GP_ERR, "pixel_width: %d\n",        info.camera_info[0].readout_mode[0].pixel_width);
+      gprint (GP_ERR, "pixel_height: %d\n",       info.camera_info[0].readout_mode[0].pixel_height);
+
+      gprint (GP_ERR, "ST5_AD_size: %d\n", info.ST5_AD_size);
+      gprint (GP_ERR, "ST5_filter_type: %d\n", info.ST5_filter_type);
+}      
+
+void CameraFullSize (int *x, int *y) {
+  *x = info.camera_info[0].readout_mode[0].width;
+  *y = info.camera_info[0].readout_mode[0].height;
+}
+
+int SetTemperature (double temp) {
+
+  int state;
+  struct sbig_cool cool;
+
+  if (temp < -50) return (FALSE);
+  if (temp > +20) return (FALSE);
+
+  cool.regulation = SBIG_TEMP_REGULATION_ON;
+  cool.temperature = (int) (10.0*temp + 0.5);
+  cool.direct_drive = 0;
+
+  state = sbig_set_cooling (&cool);
+  if (state < 0) {
+    gprint (GP_ERR, "sbig error: %s\n", sbig_show_error (state));
+    return (FALSE);
+  }
+  return (TRUE);
+}
+
+double GetTemperature () {
+
+  int state;
+  double temp;
+  struct sbig_status status;
+
+  state = sbig_get_status (&status);
+  if (state < 0) {
+    gprint (GP_ERR, "sbig error: %s\n", sbig_show_error (state));
+    return (-200.0);
+  }
+  temp = (status.ccd_temperature - 0.5) / 10.0;
+  return (temp);
+}
+
+int DumpCameraStatus () {
+
+  int state;
+  double temp;
+  struct sbig_status status;
+
+  state = sbig_get_status (&status);
+  if (state < 0) {
+    gprint (GP_ERR, "sbig error: %s\n", sbig_show_error (state));
+    return (FALSE);
+  }
+
+  gprint (GP_ERR, "imaging_ccd_status: %d\n", status.imaging_ccd_status);
+  gprint (GP_ERR, "tracking_ccd_status: %d\n", status.tracking_ccd_status);
+  gprint (GP_ERR, "fan_on: %d\n", status.fan_on);
+  gprint (GP_ERR, "shutter_state: %d\n", status.shutter_state);
+  gprint (GP_ERR, "led_state: %d\n", status.led_state);
+  gprint (GP_ERR, "shutter_edge: %d\n", status.shutter_edge);
+  gprint (GP_ERR, "plus_x_relay: %d\n", status.plus_x_relay);
+  gprint (GP_ERR, "minus_x_relay: %d\n", status.minus_x_relay);
+  gprint (GP_ERR, "plus_y_relay: %d\n", status.plus_y_relay);
+  gprint (GP_ERR, "minus_y_relay: %d\n", status.minus_y_relay);
+  gprint (GP_ERR, "pulse_active: %d\n", status.pulse_active);
+  gprint (GP_ERR, "temperature_regulation: %d\n", status.temperature_regulation);
+  gprint (GP_ERR, "temperature_setpoint: %d\n", status.temperature_setpoint);
+  gprint (GP_ERR, "cooling_power: %d\n", status.cooling_power);
+  gprint (GP_ERR, "air_temperature: %d\n", status.air_temperature);
+  gprint (GP_ERR, "ccd_temperature: %d\n", status.ccd_temperature);
+
+  return (TRUE);
+}
+
+/* block until exposure is complete */
+int Exposure (double exptime) {
+
+  int i, state;
+  struct sbig_expose expose;
+  struct sbig_status status;
+
+  expose.ccd = SBIG_IMAGING_CCD;
+  expose.exposure_time = (int)(100.0*exptime);
+  expose.abg_state = SBIG_ABG_OFF;
+  expose.shutter = SBIG_EXPOSE_SHUTTER_NORMAL;  /* shuttermode = ? */
+
+  /* drop this ? */
+  /* usleep ((int)(exptime*1000000)); */
+  state = sbig_expose (&expose);
+  if (state < 0) {
+    gprint (GP_ERR, "exposure error\n");
+    gprint (GP_ERR, "%s\n", sbig_show_error (state));
+    return (FALSE);
+  }
+  
+  for (i = 0; i < expose.exposure_time + 10; ++i) {
+    state = sbig_get_status (&status);
+    /* gprint (GP_ERR, "%d\n", state); */
+    /* gprint (GP_ERR, "%d  %d\n", status.imaging_ccd_status, status.shutter_state); */
+    /*    if (state == 0) return (TRUE); */
+    if (status.imaging_ccd_status == -SBIG_NO_EXPOSURE_IN_PROGRESS) return (TRUE);
+    if (status.imaging_ccd_status == -SBIG_EXPOSURE_IN_PROGRESS) {
+      usleep (10000);
+      continue;
+    }
+    gprint (GP_ERR, "exposure error\n");
+    gprint (GP_ERR, "%s\n", sbig_show_error (state));
+    return (FALSE);
+  }
+  gprint (GP_ERR, "exposure timeout\n");
+  return (FALSE);
+}
+
+int   readout_abort;
+float readout_percent;
+static int readout_callback (float percent) {
+  /* return 1 to continue, 0 to abort */
+  if (((int)(percent) % 10) == 0) { gprint (GP_ERR, "."); }
+  readout_percent = percent;
+  if (readout_abort) return 0;
+  return 1;
+}
+
+int ReadOut (int x, int y, int dx, int dy, int binning, unsigned short *buffer) {
+
+  int state, Nbytes;
+  static struct sbig_readout readout;
+
+  readout.x = x;
+  readout.y = y;
+  readout.width  = dx;
+  readout.height = dy;
+
+  Nbytes = readout.width*readout.height*sizeof(short);
+
+  /* for bin 2x2 or 3x3, need to adjust dx, dy above */
+  readout.ccd = SBIG_IMAGING_CCD;
+  readout.binning = SBIG_BIN_1X1;
+  readout.data = buffer;
+  readout.data_size_in_bytes = Nbytes;
+  readout.callback = readout_callback;
+    
+  gprint (GP_ERR, "%d, %d : %d x %d\n", readout.x, readout.y, readout.width, readout.height);
+  sync (); 
+  readout_abort = FALSE;
+  state = sbig_readout (&readout);
+  gprint (GP_ERR, "\n");
+  if (state < 0) {
+    gprint (GP_ERR, "sbig error: %s\n", sbig_show_error (state));
+    return (FALSE);
+  }
+  return (TRUE);
+}
+
+int OpenShutter () {
+
+  int state;
+  struct sbig_control control;
+
+  control.shutter = SBIG_OPEN_SHUTTER;
+  state = sbig_control (&control);
+  if (state < 0) {
+    gprint (GP_ERR, "sbig error: %s\n", sbig_show_error (state));
+    return (FALSE);
+  }
+  return (TRUE);
+}
+
+int CloseShutter () {
+
+  int state;
+  struct sbig_control control;
+
+  control.shutter = SBIG_CLOSE_SHUTTER;
+  state = sbig_control (&control);
+  if (state < 0) {
+    gprint (GP_ERR, "sbig error: %s\n", sbig_show_error (state));
+    return (FALSE);
+  }
+  return (TRUE);
+}
Index: /branches/sc_branches/pantasks_condor/dimm/Image.c
===================================================================
--- /branches/sc_branches/pantasks_condor/dimm/Image.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/dimm/Image.c	(revision 34783)
@@ -0,0 +1,85 @@
+# include "dimm.h"
+
+/*** this uses an Image structure from DIMM which 
+     is different from the Image structure in DVO ***/
+
+static Image *images = (Image *) NULL;
+static int   Nimages = 0;
+
+Image *createImage (int Nx, int Ny) {
+
+  int N;
+
+  if (Nx*Ny <= 0) return ((Image *) NULL);
+
+  N = Nimages;
+  Nimages ++;
+  if (images == (Image *) NULL) {
+    ALLOCATE (images, Image, MAX (1, Nimages));
+  } else {
+    REALLOCATE (images, Image, MAX (1, Nimages));
+  }
+
+  images[N].Nx = Nx;
+  images[N].Ny = Ny;
+  images[N].Nbytes = Nx*Ny*sizeof (short);
+  ALLOCATE (images[N].buffer, char, images[N].Nbytes);
+
+  return (&images[N]);
+}
+
+int freeImage (Image *entry) {
+
+  int i, j, N;
+
+  N = -1;
+  for (i = 0; (i < Nimages) && (N == -1) ; i++) {
+    if (&images[i] == entry) N = i;
+  }
+  if (N == -1) return (FALSE);
+
+  free (images[N].buffer);
+
+  for (j = N; j < Nimages - 1; j++) {
+    images[j] = images[j+1];
+  }
+
+  Nimages --;
+  REALLOCATE (images, Image, MAX (1, Nimages));
+  return (TRUE);
+}
+
+int writeImage (char *filename, Image *image) {
+
+  Header header;
+  Matrix matrix;
+
+  gfits_init_header (&header);
+
+  header.Naxes = 2;
+  header.Naxis[0] = image[0].Nx;
+  header.Naxis[1] = image[0].Ny;
+  header.bitpix = 16;
+
+  gfits_create_header (&header);
+  gfits_create_matrix (&header, &matrix);
+  free (matrix.buffer);
+
+  matrix.buffer = image[0].buffer;
+  
+  /* write meta-data to header */
+  gfits_print (&header, "RA", "%lf", 1, image[0].ra);
+  gfits_print (&header, "DEC", "%lf", 1, image[0].dec);
+  gfits_print (&header, "EQUINOX", "%lf", 1, 2000.0);
+
+  gfits_print (&header, "AIRMASS", "%lf", 1, image[0].airmass);
+  gfits_print (&header, "CCDTEMP", "%lf", 1, image[0].ccdtemp);
+  gfits_print (&header, "AIRTEMP", "%lf", 1, image[0].airtemp);
+  gfits_print (&header, "EXPTIME", "%lf", 1, image[0].exptime);
+
+  gfits_write_header (filename, &header);
+  gfits_write_matrix (filename, &matrix);
+  
+  return (TRUE);
+}
+
Index: /branches/sc_branches/pantasks_condor/dimm/Makefile
===================================================================
--- /branches/sc_branches/pantasks_condor/dimm/Makefile	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/dimm/Makefile	(revision 34783)
@@ -0,0 +1,68 @@
+default: dimm
+
+include ../../../Makefile.System
+HOME    =       $(ROOT)/src/opihi
+BIN     =       $(HOME)/bin
+LIB     =       $(HOME)/lib
+INC     =       $(HOME)/include
+SRC     =       $(HOME)/dimm
+DATA    =       $(DESTDATA)/dimm
+include ../Makefile.Common
+
+# programs may add their own internal requirements here
+LIBS1   =       -lbasiccmd -ldatacmd -lastrocmd -lshell -ldata -lsbig
+LIBS2   =       -ldvo -lkapa -lFITS -lohana
+FULL_CFLAGS   = $(BASE_CFLAGS)
+FULL_CPPFLAGS = $(BASE_CPPFLAGS)
+FULL_LDFLAGS  = $(LIBS1) $(LIBS2) $(BASE_LDFLAGS)
+
+# dimm user commands and support functions #####################
+funcs = \
+$(SRC)/Camera.$(ARCH).o		\
+$(SRC)/Serial.$(ARCH).o		\
+$(SRC)/Telescope.$(ARCH).o             
+
+cmds = \
+$(SRC)/init.$(ARCH).o		  	\
+$(SRC)/dimm.$(ARCH).o		  	\
+$(SRC)/camera_cmds.$(ARCH).o	  	\
+$(SRC)/findstars.$(ARCH).o	  	\
+$(SRC)/telescope_cmds.$(ARCH).o   \
+$(SRC)/version.$(ARCH).o
+
+libs = \
+$(DESTLIB)/libshell.a \
+$(DESTLIB)/libdata.a \
+$(DESTLIB)/libbasiccmd.a \
+$(DESTLIB)/libastrocmd.a \
+$(DESTLIB)/libdatacmd.a \
+$(DESTLIB)/libsbig.a
+
+dimm: sbig $(BIN)/dimm.$(ARCH)
+$(SRC)/dimm.$(ARCH).o : $(libs)
+$(BIN)/dimm.$(ARCH)   : $(funcs) $(cmds)
+
+install: $(DESTBIN)/dimm help modules
+
+help: cmd.basic.help cmd.data.help cmd.astro.help dimm.help
+
+modules: dimm.modules
+
+# SBIG install functions
+sbig: $(DESTINC)/sbig.h $(DESTLIB)/libsbig.a
+	@echo sbig code installed
+
+$(DESTINC)/sbig.h:	$(SRC)/sbig/sbig.h
+	cp $(SRC)/sbig/sbig.h $(DESTINC)/sbig.h
+
+$(DESTLIB)/libsbig.a:	$(SRC)/sbig/sbig.a
+	cp $(SRC)/sbig/sbig.a $(DESTLIB)/libsbig.a
+
+$(SRC)/sbig/sbig.a: $(SRC)/sbig/sbig.a.src
+	cp $(SRC)/sbig/sbig.a.src $(SRC)/sbig/sbig.a
+
+.PHONY: dimm
+
+# these have not been finished: should be used for analysis of the extracted images
+# $(SRC)/Analysis.$(ARCH).o            \
+# $(SRC)/Image.$(ARCH).o		\
Index: /branches/sc_branches/pantasks_condor/dimm/Serial.c
===================================================================
--- /branches/sc_branches/pantasks_condor/dimm/Serial.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/dimm/Serial.c	(revision 34783)
@@ -0,0 +1,388 @@
+# include "dimm.h"
+# include <termios.h>
+
+# define CR   0x0D
+# define LF   0x0A
+# define BEEP 0x07
+# define OPENERR    -1   /* Port could not be opened */
+# define PORTERR    -2   /* Opened port is not a serial (tty) port */
+# define BADCMDERR  -3   /* Command sent to camera is not understood */
+# define TIMEOUTERR -4   /* No response */
+ 
+# define SER_VERBOSE 0
+# define SER_DEBUG   0
+
+# ifndef SER_VERBOSE
+# define SER_VERBOSE 1           /* Be verbose? */
+# endif
+# ifndef SER_DEBUG 
+# define SER_DEBUG 1
+# endif
+
+/* Defines for Serial Port  */
+typedef struct {
+  int f;
+  int rate;
+  int parity;
+  int bits;
+  int stpbit;
+  char port[64];
+} Serial;
+
+static Serial serial = {0, 0, 0, 0, 0};
+static int SER_ECHO = 0;
+
+int SerialOpen (char *);
+
+int SerialVerbose (int mode) {
+
+  SER_ECHO = mode;
+  return (TRUE);
+
+}
+
+int SerialInit (char *port) {
+  
+  strcpy (serial.port, port);
+  serial.rate = 2400;
+  serial.rate = 9600;
+  serial.parity = 0;
+  serial.bits = 8;
+  serial.stpbit = 1;
+  
+  serial.f = SerialOpen (serial.port);
+  if (serial.f <= 0) {
+    gprint (GP_ERR, "Error opening serial port %s - error %d.\n", serial.port, serial.f);
+    return (FALSE);
+  }
+  if (SerialBaudRate (serial.f, serial.rate))   return (FALSE);
+  if (SerialParity   (serial.f, serial.parity)) return (FALSE);
+  if (SerialDataBits (serial.f, serial.bits))   return (FALSE);
+  if (SerialStopBit  (serial.f, serial.stpbit)) return (FALSE);
+  return (TRUE);
+}
+
+/********************************** Open *********************************/
+int SerialOpen (char *port) {
+  
+  int err = 0;
+  struct termios term;
+  char prefix[100];
+  int fdesc = -1;
+  int locked;
+  struct flock lock;
+   
+  /* open serial line */
+   if (SER_VERBOSE) printf ("Opening the serial port %s for read/write.\n", port);
+   fdesc = open (port, O_RDWR);
+   if (fdesc == -1) {
+     if (SER_VERBOSE) gprint (GP_ERR, "Cannot open %s for read/write.\n", port);
+     return OPENERR;
+   }
+   /* lock serial line */
+   lock.l_type = F_WRLCK;
+   lock.l_len = 0;
+   lock.l_start = 0;
+   lock.l_whence = 0;
+   locked = fcntl (fdesc, F_SETLK, &lock);
+   if (locked == -1) {
+     gprint (GP_ERR, "can't lock serial line\n");
+     close (fdesc);
+     return OPENERR;
+   }
+
+   /* Get the serial port's attributes... */
+   if (tcgetattr (fdesc, &term) == -1) {
+      if (SER_VERBOSE) gprint (GP_ERR, "Port is not a tty\n");
+      return PORTERR;
+   }
+
+   /* cfmakeraw (&term); */
+   term.c_iflag &= ~(IGNBRK|BRKINT|PARMRK|ISTRIP|INLCR|IGNCR|ICRNL|IXON);
+   term.c_oflag &= ~OPOST;
+   term.c_lflag &= ~(ECHO|ECHONL|ICANON|ISIG|IEXTEN);
+   term.c_cflag &= ~(CSIZE|PARENB);
+   term.c_cflag |= CS8;
+   term.c_cc[VMIN] = 0;     /* MIN setting...if 0, wait only for timeout */
+   term.c_cc[VTIME] = 2;    /* TIME setting...wait at most 1 sec for response 
+		              (ignored if c_cc[VMIN]>0) */
+   tcsetattr (fdesc, TCSAFLUSH, &term);
+
+# if (0)   
+   term.c_lflag &= ~ICANON; /* Turn OFF canonical input 
+ 	                      (so it does it character by character) */
+   term.c_cc[VMIN] = 0;     /* MIN setting...if 0, wait only for timeout */
+   term.c_cc[VTIME] = 10;    /* TIME setting...wait at most 1 sec for response 
+		              (ignored if c_cc[VMIN]>0) */
+   term.c_lflag &= ~ECHO;   /* Turn OFF echoing... */
+   term.c_iflag &= ~ICRNL;   /* Don't map CR to NL on input */
+
+   /* Set port (terminal) to reflect the change...(flush first) */
+   tcsetattr (fdesc, TCSAFLUSH, &term);
+# endif
+
+   return fdesc;
+}
+
+/******************************* Baud Rate *******************************/
+int SerialBaudRate (int fdesc, int rate) {
+   int err = 0;
+   struct termios term;
+
+  /* Get the serial port's attributes... */
+   if (SER_VERBOSE) printf("Setting the serial port's baud rate.\n");
+   if (tcgetattr (fdesc, &term) == -1) {
+      if (SER_VERBOSE) gprint (GP_ERR, "Port is not a tty\n");
+      return PORTERR;
+   }
+ 
+   /* Set the input and output baud rates to 'rate'... */
+   switch (rate) { 
+   case 1200:         /* Set speed to 1200 */ 
+      if (cfgetospeed(&term) != B1200) cfsetospeed(&term, B1200);
+      if (cfgetispeed(&term) != B1200) cfsetispeed(&term, B1200);
+      break;
+   case 2400:         /* Set speed to 2400 */ 
+      if (cfgetospeed(&term) != B2400) cfsetospeed(&term, B2400);
+      if (cfgetispeed(&term) != B2400) cfsetispeed(&term, B2400);
+      break;
+   case 9600:         /* Set speed to 9600 */
+      if (cfgetospeed(&term) != B9600) cfsetospeed(&term, B9600);
+      if (cfgetispeed(&term) != B9600) cfsetispeed(&term, B9600);
+      break;
+   case 19200:        /* Set speed to 19200 */ 
+      if (cfgetospeed(&term) != B19200) cfsetospeed(&term, B19200);
+      if (cfgetispeed(&term) != B19200) cfsetispeed(&term, B19200);
+      break;
+   default:
+      printf ("ERROR:  Unknown Baud Rate\n"); 
+      break;
+   }	
+
+   /* Set port (terminal) to reflect the change...(flush first) */
+   tcsetattr(fdesc, TCSAFLUSH, &term);
+   return (FALSE);
+}
+
+/****************************** Parity ********************************/
+int SerialParity (int fdesc, int parity) { 
+   int err=0;
+   struct termios term;
+   char *prefix = "/dev/term/";
+
+   /* Get the serial port's attributes... */
+   if (SER_VERBOSE) printf("Setting the serial port's parity.\n");
+   if (tcgetattr(fdesc, &term) == -1) {
+      if (SER_VERBOSE) gprint (GP_ERR, "Port is not a tty\n");
+      return PORTERR;
+   }
+
+   if (parity == 0)              /* Turn off parity generation... */
+      term.c_cflag &= ~PARENB;     
+   else if (parity == 1) {            
+      term.c_cflag |= PARENB;     /* Turn on parity generation... */
+      term.c_cflag |= PARODD;     /* Sets parity to odd */ 
+   }	 
+   else if (parity == 2)         /* Turn on parity generation... */
+      term.c_cflag |= PARENB;     /* Defaults parity to even */ 
+   else 
+      printf ("ERROR:  Unknown parity specification\n");
+ 
+   /* Set port (terminal) to reflect the change...(flush first) */
+   tcsetattr(fdesc, TCSAFLUSH, &term);
+   return (FALSE);
+}
+
+/****************************Data Bit Size ***************************/
+int SerialDataBits(int fdesc, int bits) { 
+   int err=0;
+   struct termios term;
+
+   /* Get the serial port's attributes... */
+   if (SER_VERBOSE) printf("Setting the serial port's data bit size.\n");
+   if (tcgetattr(fdesc, &term) == -1) {
+      if (SER_VERBOSE) gprint (GP_ERR, "Port is not a tty\n");
+      return PORTERR;
+   }
+
+   switch (bits) {
+   case 5:  			/* Sets data bits to 5 */
+      term.c_cflag &= ~CSIZE; 
+      term.c_cflag |= CS5;
+      break; 
+   case 6:   			/* Sets data bits to 6 */ 
+      term.c_cflag &= ~CSIZE; 
+      term.c_cflag |= CS6;
+      break; 
+   case 7:     		/* Sets data bits to 7 */ 
+      term.c_cflag &= ~CSIZE; 
+      term.c_cflag |= CS7;
+      break; 
+   case 8:  			/* Sets data bits to 8 */
+      term.c_cflag &= ~CSIZE; 
+      term.c_cflag |= CS8;
+      break; 
+   default:
+      printf ("ERROR:  Illegal data bit size\n");
+      break; 
+   }
+
+   /* Set port (terminal) to reflect the change...(flush first) */
+   tcsetattr(fdesc, TCSAFLUSH, &term);
+   return (FALSE);
+}
+
+/****************************** Stop Bit ****************************/
+int SerialStopBit(int fdesc, int stpbit) { 
+   int err=0;
+   struct termios term;
+
+   /* Get the serial port's attributes... */
+   if (SER_VERBOSE) printf("Setting the serial port's stop bit.\n");
+   if (tcgetattr(fdesc, &term) == -1) {
+      if (SER_VERBOSE) gprint (GP_ERR, "Port is not a tty\n");
+      return PORTERR;
+   }
+
+   if (stpbit == 1) {     		/* Sets stop bit to 1 */  
+      if (term.c_cflag & CSTOPB) term.c_cflag |= CSTOPB;
+   } else { 				/* Else stop bit to 2 */ 
+      term.c_cflag & CSTOPB;
+   }
+
+   /* Set port (terminal) to reflect the change...(flush first) */
+   tcsetattr(fdesc, TCSAFLUSH, &term);
+   return (FALSE);
+}
+
+/**************************** Stop ***********************************/
+void SerialStop (int fdesc) {
+   if (SER_VERBOSE) printf("Closing the serial port.\n");
+   close(fdesc); /* Close up shop... */
+}
+
+# define D_NREAD 1024
+
+/* send a string to the serial port, wait for an answer */
+/* answer is returned on the pointer provided */
+
+/**************************** Command ***********************************/
+int SerialCommand (char *in, char **out, int wait) {
+  
+  int i, j;
+  char *line;
+  int done, Nread, Nin, NREAD;
+  
+  if (SER_ECHO) gprint (GP_ERR, "command: %s\n", in); 
+
+  if (serial.f <= 0) {
+    gprint (GP_ERR, "serial line closed\n"); 
+    if (out != (char **) NULL) {
+      *out = strcreate ("SERIAL OFF");
+      /* return (char *) NULL instead? */
+    }
+    return (FALSE);
+  }
+
+  /* flush out the line */
+  tcflush (serial.f, TCIOFLUSH);
+
+# if (0)
+  for (i = 0; i < strlen(in); i++) {
+    gprint (GP_ERR, "%d %x\n", i, in[i]);
+  }
+# endif
+
+  /* send command to serial line */
+  Nin = write (serial.f, in, strlen(in));
+  if (Nin != strlen(in)) {
+    gprint (GP_ERR, "Serial Command not sent\n");
+    return (FALSE);
+   }
+  usleep (20000);
+  /* LX200 GPS requires some lead time (10msec) to check ready state */
+  
+  /* create space to store answer */
+  NREAD = D_NREAD;
+  ALLOCATE (line, char, NREAD);
+  bzero (line, NREAD);
+  Nread = Nin = 0;
+
+  /* read data back from serial line until no response (Nin == 0) 
+     or timeout (Nin == -1) && (i == wait) */
+
+  done = FALSE;
+  for (i = 0; !done && (i < wait); i++) {
+    Nin = read (serial.f, &line[Nread], 256);
+# if (SER_DEBUG)
+    gprint (GP_ERR, "%d ", Nin);
+# endif
+    if (Nin < 0) { /* error, check value */
+      gprint (GP_ERR, "error?");
+      continue;
+    }
+    if (Nin > 0) {
+      Nread += Nin;
+      line[Nread] = 0;
+      i = 0;
+    }
+    if (Nread > D_NREAD - 257) {
+      NREAD += D_NREAD;
+      REALLOCATE (line, char, NREAD);
+    }
+    if ((i > 0) && (Nin == 0)) done = TRUE;
+    usleep (2000);
+  }
+
+  if (SER_ECHO) gprint (GP_ERR, "answer: %s\n", line);
+
+  if (out == (char **) NULL) {
+    free (line);
+  } else {
+    *out = line;
+  }
+  return (TRUE);
+}
+
+
+/* raw:
+   cfmakeraw sets the terminal attributes as follows:
+   termios_p->c_iflag &= ~(IGNBRK|BRKINT|PARMRK|ISTRIP|INLCR|IGNCR|ICRNL|IXON);
+   termios_p->c_oflag &= ~OPOST;
+   termios_p->c_lflag &= ~(ECHO|ECHONL|ICANON|ISIG|IEXTEN);
+   termios_p->c_cflag &= ~(CSIZE|PARENB);
+   termios_p->c_cflag |= CS8;
+
+   there conditions are turned off:
+   c_iflag:
+       IGNBRK ignore BREAK condition on input
+       BRKINT If IGNBRK is not set, generate SIGINT on BREAK condition, else read BREAK as
+              character \0.
+       PARMRK if  IGNPAR  is  not  set,  prefix a character with a parity error or framing
+              error with \377 \0.  If neither IGNPAR nor PARMRK is set, read  a  character
+              with a parity error or framing error as \0.
+       (IGNPAR ignore framing errors and parity errors.)
+
+       ISTRIP strip off eighth bit
+       INLCR  translate NL to CR on input
+       IGNCR  ignore carriage return on input
+       ICRNL  translate carriage return to newline on input (unless IGNCR is set)
+       IXON   enable XON/XOFF flow control on output
+
+   c_oflag:
+       OPOST  enable implementation-defined output processing
+
+   c_lflag:
+       ECHO   echo input characters.
+       ECHONL if ICANON is also set, echo the NL character even if ECHO is not set.
+       ICANON enable canonical mode.  This enables the special characters EOF, EOL,  EOL2,
+              ERASE, KILL, REPRINT, STATUS, and WERASE, and buffers by lines.
+       ISIG   when any of the characters INTR, QUIT, SUSP, or DSUSP are received, generate
+              the corresponding signal.
+       IEXTEN enable implementation-defined input processing.
+
+   c_cflag:
+       CSIZE  character size mask.  Values are CS5, CS6, CS7, or CS8. (CS8 set).
+       PARENB enable parity generation on output and parity checking for input.
+
+*/
Index: /branches/sc_branches/pantasks_condor/dimm/Telescope.c
===================================================================
--- /branches/sc_branches/pantasks_condor/dimm/Telescope.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/dimm/Telescope.c	(revision 34783)
@@ -0,0 +1,361 @@
+# include "dimm.h"
+
+# define SER_TIMEOUT 10
+# define SLEW_TIMEOUT 30
+# define dCOS(A)   ((double) cos ((double)RAD_DEG*A))
+# define dSIN(A)   ((double) sin ((double)RAD_DEG*A))
+
+double distSky (double r1, double r2, double d1, double d2) {
+
+  double x1, y1, z1;
+  double x2, y2, z2;
+  double cosT, dist;
+
+  x1 = dCOS (r1) * dCOS (d1);
+  y1 = dSIN (r1) * dCOS (d1);
+  z1 = dSIN (d1);
+
+  x2 = dCOS (r2) * dCOS (d2);
+  y2 = dSIN (r2) * dCOS (d2);
+  z2 = dSIN (d2);
+
+  cosT = x1*x2 + y1*y2 + z1*z2;
+  dist = DEG_RAD * acos (cosT);
+
+  return (dist);
+}
+
+int getRD (double *r, double *d) { 
+
+  int status;
+  char *rastr, *decstr;
+
+  status = SerialCommand (":GR#", &rastr, SER_TIMEOUT);
+  if (!status) return (FALSE);
+
+  status = SerialCommand (":GD#", &decstr, SER_TIMEOUT); 
+  if (!status) return (FALSE);
+
+  status = ohana_str_to_radec (r, d, rastr, decstr);
+  if (!status) return (FALSE);
+
+  free (rastr);
+  free (decstr);
+
+  return (TRUE);
+}
+
+int gotoRD (double r, double d) {
+
+  double R, D, dist;
+  int Ntry, status;
+  char *str, *answer, cmd[64];
+
+  /* error on ra, dec means coords out of range */
+
+  /* set telescope coords, send */
+  str = meade_ra_to_str (r);
+  sprintf (cmd, ":Sr%s#", str);   free (str);
+  status = SerialCommand (cmd, &answer, SER_TIMEOUT);
+  if (!status) return (FALSE); 
+  if (answer == (char *) NULL) return (FALSE); 
+  if (strcmp (answer, "1")) return (FALSE); 
+  free (answer);
+
+  str = meade_dec_to_str (d);
+  sprintf (cmd, ":Sd%s#", str);  free (str);
+  status = SerialCommand (cmd, &answer, SER_TIMEOUT);   
+  if (!status) return (FALSE); 
+  if (answer == (char *) NULL) return (FALSE); 
+  if (strcmp (answer, "1")) return (FALSE); 
+  free (answer);
+
+  Ntry = 0;
+  status = SerialCommand (":MS#", &answer, SER_TIMEOUT);   
+  if (!status) return (FALSE); 
+  if (answer == (char *) NULL) return (FALSE); 
+  if (strcmp (answer, "0")) {
+    gprint (GP_ERR, "error: %s\n", answer);
+    return (FALSE); 
+  }
+  free (answer);
+
+  /* watch for response? */
+  status = FALSE;
+  while (!status) {
+    getRD (&R, &D);
+    dist = distSky (R, r, D, d);
+    if (dist < 0.1) return (TRUE);
+    usleep (100000);
+    Ntry ++;
+    if (Ntry > SLEW_TIMEOUT) return (FALSE);
+  }
+  return (status);
+}
+
+/* actual offsets are x,y, convert to arcmin */
+int offset (char *direction, double distance) {
+
+  /* Four rate choices: 
+     slew   (RS) -  8 degree / sec : rate 1
+     find   (RM) - 30 arcmin / sec : rate 2
+     center (RC) -  4 arcmin / sec : rate 3
+     guide  (RG) - 15 arcsec / sec : rate 4
+
+     communication requires ~1.0 sec:
+     offset should use rate which gives shortest time > 2.0 sec 
+  */
+
+# define NRATE 4
+  static double delay[NRATE]  = {0.1, 0.1, 0.1, 0.1};
+  static double rate[NRATE]   = {480.0, 30.0, 4.0, 0.25};
+  static char rcmd[NRATE][16] = {"RS", "RM", "RC", "RG"};
+
+  int i, status, rsel;
+  char dir, cmd[32];
+  double tsel, dt;
+
+  dir = 0;
+  if (!strcasecmp (direction, "y")) dir = (distance > 0) ? 'n' : 's';
+  if (!strcasecmp (direction, "x")) dir = (distance > 0) ? 'w' : 'e';
+  if (!dir) return (FALSE);
+
+  /* distance is in arcmin */
+  distance = fabs (distance);
+
+  /* logic is bad -- does not catch too small distances */  
+  rsel = -1;
+  tsel = SLEW_TIMEOUT;
+  for (i = 0; i < NRATE; i++) {
+    dt = distance / rate[i] - delay[i];
+    if ((dt > 0) && (dt < tsel)) {
+      rsel = i;
+      tsel = dt;
+    }
+  }
+  if (tsel < 0) {
+    gprint (GP_ERR, "offset %f arcmin below minimum\n", distance);
+    return (FALSE);
+  }
+  if (tsel > SLEW_TIMEOUT) {
+    gprint (GP_ERR, "offset %f arcmin above maximum\n", distance);
+    return (FALSE);
+  }
+  gprint (GP_ERR, "offsetting %c for %f seconds\n", dir, tsel);
+  
+  sprintf (cmd, ":%s#", rcmd[rsel]);
+  status = SerialCommand (cmd, (char **) NULL, SER_TIMEOUT);
+  if (!status) return (FALSE);
+
+  sprintf (cmd, ":M%c#", dir);
+  status = SerialCommand (cmd, (char **) NULL, SER_TIMEOUT);
+  if (!status) return (FALSE);
+
+  usleep ((int)(tsel*1000000));
+
+  sprintf (cmd, ":Q%c#", dir);
+  status = SerialCommand (cmd, (char **) NULL, SER_TIMEOUT);
+  if (!status) return (FALSE);
+
+  return (TRUE);
+}  
+
+/* actual offsets are x,y, convert to arcmin */
+int toffset (char *direction, char *rate, double duration) {
+
+# define NRATE 6
+  /* static char rcmd[NRATE][16] = {"RS", "RM", "RC", "RG"};*/
+  static char rcmd[NRATE][64] = {"RS", "RM", "RC", "RG", "RA0.0085", "RE0.0085"};
+
+  int i, status, rsel;
+  char dir, cmd[32];
+  double tsel, dt;
+
+  dir = 0;
+  if (!strcasecmp (direction, "x")) dir = (duration > 0) ? 'w' : 'e';
+  if (!strcasecmp (direction, "y")) dir = (duration > 0) ? 'n' : 's';
+  if (!dir) return (FALSE);
+  duration = fabs (duration);
+  
+  status = FALSE;
+  for (i = 0; i < NRATE; i++) if (!strcmp (rcmd[i], rate)) status = TRUE;
+  if (!status) {
+    gprint (GP_ERR, "bad rate: %s\n", rate);
+    return (FALSE);
+  }
+
+  sprintf (cmd, ":%s#", rate);
+  status = SerialCommand (cmd, (char **) NULL, SER_TIMEOUT);
+  if (!status) return (FALSE);
+
+  sprintf (cmd, ":M%c#", dir);
+  status = SerialCommand (cmd, (char **) NULL, SER_TIMEOUT);
+  if (!status) return (FALSE);
+
+  usleep ((int)(duration*1000000));
+
+  sprintf (cmd, ":Q%c#", dir);
+  status = SerialCommand (cmd, (char **) NULL, SER_TIMEOUT);
+  if (!status) return (FALSE);
+
+  return (TRUE);
+}  
+
+int getXY (double *x, double *y) {
+
+  char *answer;
+
+  SerialCommand (":GA#", &answer, SER_TIMEOUT);
+  ohana_dms_to_ddd (x, answer);
+  free (answer);
+
+  SerialCommand (":GZ#", &answer, SER_TIMEOUT);
+  ohana_dms_to_ddd (y, answer);
+  free (answer);
+
+  return (TRUE);
+}
+
+/* need error checking on these */
+int setRD (double r, double d) {
+
+  char *str, *answer, cmd[64];
+
+  /* set telescope coords, send */
+  str = meade_ra_to_str (r);
+  sprintf (cmd, ":Sr%s#", str);
+  SerialCommand (cmd, (char **) NULL, SER_TIMEOUT);
+  free (str);
+
+  str = meade_dec_to_str (d);
+  sprintf (cmd, ":Sd%s#", str);
+  SerialCommand (cmd, (char **) NULL, SER_TIMEOUT);
+  free (str);
+
+  SerialCommand (":CM#", &answer, SER_TIMEOUT);
+  gprint (GP_ERR, "result: %s\n", answer);
+  free (answer);
+  return (TRUE);
+}
+
+int setSite (char *sitename, double lon, double lat) {
+
+  struct tm *gmt;
+  struct timeval now;
+  char *str, line[32];
+
+  gprint (GP_ERR, "careful, this causes problems\n");
+  return (FALSE);
+
+  SerialCommand (":W1#", (char **) NULL, SER_TIMEOUT);
+
+  /* Set site name 1 */
+  sprintf (line, ":SM %s#", sitename); 
+  SerialCommand (line, (char **) NULL, SER_TIMEOUT);
+
+  /* Set site long */
+  str = meade_deg_to_str (lon);
+  str[6] = 0;
+  sprintf (line, ":Sg%s#", str); 
+  SerialCommand (line, (char **) NULL, SER_TIMEOUT);
+  free (str);
+
+  /* Set site lat */
+  str = meade_dec_to_str (lat);
+  sprintf (line, ":St%s#", str); 
+  SerialCommand (line, (char **) NULL, SER_TIMEOUT);
+  free (str);
+
+  /* set UTC offset to 0.0: offset + local = gmt */
+  sprintf (line, ":SG+00#");
+  SerialCommand (line, (char **) NULL, SER_TIMEOUT);
+
+  /* Set local */
+  gettimeofday (&now, (struct timezone *) NULL);
+  gmt = gmtime (&now.tv_sec);
+  sprintf (line, ":SL%02d:%02d:%02d#", gmt[0].tm_hour, gmt[0].tm_min, gmt[0].tm_sec); 
+  SerialCommand (line, (char **) NULL, SER_TIMEOUT);
+
+  return (TRUE);
+}
+
+int setTime (char *lst) {
+
+  struct tm *gmt;
+  struct timeval now;
+  char line[32], *answer;
+
+  /* set UTC offset to 0.0: offset + local = gmt */
+  sprintf (line, ":SG+10#");
+  SerialCommand (line, (char **) NULL, SER_TIMEOUT);
+
+  /* Set local */
+  gettimeofday (&now, (struct timezone *) NULL);
+  gmt = localtime (&now.tv_sec);
+  sprintf (line, ":SL%02d:%02d:%02d#", gmt[0].tm_hour, gmt[0].tm_min, gmt[0].tm_sec); 
+  SerialCommand (line, (char **) NULL, SER_TIMEOUT);
+
+  /*
+  sprintf (line, ":SS%s#", lst);
+  SerialCommand (line, (char **) NULL, SER_TIMEOUT);
+  */
+
+  return (TRUE);
+}
+
+int getSite (double *lon, double *lat, double *lst) {
+
+  struct tm *gmt;
+  struct timeval now;
+  char *str, *answer, line[32];
+
+  /* : get latitude */
+  SerialCommand (":Gt#", &answer, SER_TIMEOUT);
+  ohana_dms_to_ddd (lat, answer);
+  free (answer);
+
+  /* : get longitude */
+  SerialCommand (":Gg#", &answer, SER_TIMEOUT);
+  ohana_dms_to_ddd (lon, answer);
+  free (answer);
+
+  /* : get LST */
+  SerialCommand (":GS#", &answer, SER_TIMEOUT);
+  ohana_dms_to_ddd (lst, answer);
+  free (answer);
+
+  return (TRUE);
+}
+
+int ParkScope() {
+
+  char *str, *answer, line[32];
+
+  SerialCommand (":hP#", &answer, SER_TIMEOUT);
+  free (answer);
+
+  return (TRUE);
+
+}
+
+int SleepScope() {
+
+  char *str, *answer, line[32];
+  
+  SerialCommand (":hN#", &answer, SER_TIMEOUT);
+  free (answer);
+  
+  return (TRUE);
+
+}
+
+int WakeScope() {
+
+  char *str, *answer, line[32];
+
+  SerialCommand (":hW#", &answer, SER_TIMEOUT);
+  free (answer);
+
+  return (TRUE);
+
+}
Index: /branches/sc_branches/pantasks_condor/dimm/altaz.c
===================================================================
--- /branches/sc_branches/pantasks_condor/dimm/altaz.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/dimm/altaz.c	(revision 34783)
@@ -0,0 +1,88 @@
+# include "dimm.h"
+
+# define dCOS(A)   ((double) cos ((double)RAD_DEG*A))
+# define dSIN(A)   ((double) sin ((double)RAD_DEG*A))
+
+double atan2 (double y, double x);
+
+int altaz (int argc, char **argv) {
+  
+  double alt, az, lat, rot;
+  double ha, dec;
+  double sind, sinh, cosh;
+  char *latstr;
+
+  if (argc != 6) goto usage;
+
+  if (!strcmp (argv[1], "-h")) goto radec;
+  if (!strcmp (argv[1], "-c")) goto altaz;
+
+ radec:
+  /* ha/dec -> alt/az */
+  ha  = atof (argv[2]);
+  dec = atof (argv[3]);
+
+  latstr = get_variable ("LATITUDE");
+  if (latstr == (char *) NULL) {
+    gprint (GP_ERR, "please define $LATITUDE\n");
+    return (FALSE);
+  }
+  lat = atof (latstr);
+  free (latstr);
+ 
+  sind = dSIN (dec) * dSIN (lat) + dCOS (dec) * dCOS (ha) * dCOS (lat);
+  alt  = DEG_RAD * asin (sind);
+
+  sinh = - dCOS (dec) * dSIN (ha);
+  cosh =   dSIN (dec) * dCOS (lat) - dCOS (dec) * dCOS (ha) * dSIN (lat);
+
+  az = DEG_RAD * atan2 (sinh, cosh);
+  set_variable (argv[4], alt);
+  set_variable (argv[5], az);
+
+  sinh = -dCOS(az) * dSIN(alt) * dSIN(ha) * dSIN(lat) + dSIN(az) * dSIN(alt) * dCOS(ha) - dSIN(ha) * dCOS(alt) * dCOS(lat);
+  cosh = -dSIN(az) * dSIN(ha) * dSIN(lat) - dCOS(az) * dCOS(ha);
+  rot = -DEG_RAD * atan2 (sinh, cosh);
+  set_variable ("ROT", rot);
+
+  return (TRUE);
+  
+ altaz:
+  /* alt/az -> ha/dec */
+  alt = atof (argv[4]);
+  az  = atof (argv[5]);
+
+  latstr = get_variable ("LATITUDE");
+  if (latstr == (char *) NULL) {
+    gprint (GP_ERR, "please define $LATITUDE\n");
+    return (FALSE);
+  }
+  lat = atof (latstr);
+  free (latstr);
+
+  sind = dSIN (alt) * dSIN (lat) + dCOS (alt) * dCOS (az) * dCOS (lat);
+  dec  = DEG_RAD * asin (sind);
+
+  sinh = -dCOS (alt) * dSIN (az);
+  cosh =  dSIN (alt) * dCOS (lat) - dCOS (alt) * dCOS (az) * dSIN (lat);
+
+  ha = DEG_RAD * atan2 (sinh, cosh);
+  set_variable (argv[2], ha);
+  set_variable (argv[3], dec);
+
+  sinh = -dCOS(az) * dSIN(alt) * dSIN(ha) * dSIN(lat) + dSIN(az) * dSIN(alt) * dCOS(ha) - dSIN(ha) * dCOS(alt) * dCOS(lat);
+  cosh = -dSIN(az) * dSIN(ha) * dSIN(lat) - dCOS(az) * dCOS(ha);
+  rot = -DEG_RAD * atan2 (sinh, cosh);
+  set_variable ("ROT", rot);
+
+  return (TRUE);
+  
+ usage:
+  gprint (GP_ERR, "USAGE: altaz -h (ha) (dec) (alt) (az)\n");
+  gprint (GP_ERR, "USAGE: altaz -c (ha) (dec) (alt) (az)\n");
+  gprint (GP_ERR, "       -h alt/az to ha/dec, -c ha/dec to alt/az\n");
+  gprint (GP_ERR, "       returned values in variables provided\n");
+  return (FALSE);
+
+}
+
Index: /branches/sc_branches/pantasks_condor/dimm/camera_cmds.c
===================================================================
--- /branches/sc_branches/pantasks_condor/dimm/camera_cmds.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/dimm/camera_cmds.c	(revision 34783)
@@ -0,0 +1,130 @@
+# include "dimm.h"
+# define EXIT_STATUS(S) { seteuid (UID); return (S); }
+
+static uid_t UID, EUID;
+
+SetEUID () {
+
+  /* save the UID (ID of calling process) and EUID (should be root) */
+  UID = getuid ();
+  EUID = geteuid ();
+  seteuid (UID);
+}
+
+int camera (int argc, char **argv) {
+  
+  /* USAGE: 
+     camera init port
+     camera expose exptime
+     camera readout x y dx dy
+     camera temp set value
+     camera temp get var
+  */
+
+  if (argc < 2) goto usage;
+
+  seteuid (EUID);
+  
+  if (!strcasecmp (argv[1], "init")) {
+    int port, status;
+
+    if (argc != 3) {
+      gprint (GP_ERR, "USAGE: camera init (port)\n");
+      EXIT_STATUS (FALSE);
+    }
+    sscanf (argv[2], "%x", &port);
+    status = InitCamera (port);
+    EXIT_STATUS (status);
+  }
+
+  if (!strcasecmp (argv[1], "expose")) {
+
+    int status;
+    double exptime;
+
+    if (argc != 3) {
+      gprint (GP_ERR, "USAGE: camera expose (exptime)\n");
+      EXIT_STATUS (FALSE);
+    }
+    exptime = atof (argv[2]);
+    status = Exposure (exptime);
+    EXIT_STATUS (status);
+  }
+
+  if (!strcasecmp (argv[1], "temp")) {
+
+    int status;
+    double temp;
+
+    if (argc != 3) {
+      gprint (GP_ERR, "USAGE: camera temp (temperature)\n");
+      EXIT_STATUS (FALSE);
+    }
+    temp = atof (argv[2]);
+    status = SetTemperature (temp);
+    EXIT_STATUS (status);
+  }
+
+  if (!strcasecmp (argv[1], "status")) {
+
+    int status;
+    double temp;
+
+    if (argc != 2) {
+      gprint (GP_ERR, "USAGE: camera status\n");
+      EXIT_STATUS (FALSE);
+    }
+    DumpCameraStatus ();
+    EXIT_STATUS (TRUE);
+  }
+
+  if (!strcasecmp (argv[1], "readout")) {
+
+    int Nbuf, status;
+    double temp;
+    int x, y, dx, dy, NX, NY;
+    Buffer *buf;
+
+    if ((argc != 7) && (argc != 3)) {
+      gprint (GP_ERR, "USAGE: camera readout (buffer) x y dx dy\n");
+      EXIT_STATUS (FALSE);
+    }
+
+    if ((buf = SelectBuffer (argv[2], OLDBUFFER, TRUE)) == NULL) EXIT_STATUS (FALSE);
+
+    CameraFullSize (&NX, &NY);
+    x = y = 0;
+    dx = NX;
+    dy = NY;
+    if (argc == 7) {
+      x  = atof (argv[3]);
+      y  = atof (argv[4]);
+      dx = atof (argv[5]);
+      dy = atof (argv[6]);
+    } 
+
+    /* generate a buffer to store the image */
+    gfits_free_matrix (&buf[0].matrix);
+    gfits_free_header (&buf[0].header);
+    CreateBuffer (buf, dx, dy, -32, 0.0, 1.0);
+    strcpy (buf[0].file, "(empty)");
+
+    ReadOut (x, y, dx, dy, 1, buf[0].matrix.buffer);
+
+    gfits_convert_format (&buf[0].header, &buf[0].matrix, -32, 1.0, 0.0, 0xffff, gfits_get_unsign_mode());
+
+    EXIT_STATUS (TRUE);
+  }
+
+usage:
+  gprint (GP_ERR, "camera init port\n");
+  gprint (GP_ERR, "camera expose exptime\n");
+  gprint (GP_ERR, "camera readout x y dx dy\n");
+  gprint (GP_ERR, "camera temp set value\n");
+  gprint (GP_ERR, "camera temp get var\n");
+  seteuid (UID);
+  return (FALSE);
+
+}
+
+
Index: /branches/sc_branches/pantasks_condor/dimm/dimm.c.in
===================================================================
--- /branches/sc_branches/pantasks_condor/dimm/dimm.c.in	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/dimm/dimm.c.in	(revision 34783)
@@ -0,0 +1,68 @@
+# include "opihi.h"
+
+# define opihi_name "DIMM"
+# define opihi_prompt "dimm: "
+# define opihi_description "DIMM telescope controller\n"
+# define opihi_history ".dimm"
+# define opihi_rcfile ".dimmrc"
+
+/* program-dependent initialization */
+void program_init (int *argc, char **argv) {
+  
+  auto_break = TRUE;
+
+  /* load the commands used by this implementation */
+  InitBasic ();
+  InitData ();
+  InitAstro ();
+  InitDIMM ();
+
+  rl_readline_name = opihi_name;
+  rl_attempted_completion_function = command_completer;
+
+  set_str_variable ("HISTORY", opihi_history);
+  set_str_variable ("PROMPT", opihi_prompt);
+  set_str_variable ("RCFILE", opihi_rcfile);
+
+  { 
+    char *helpdir;
+    char *modules;
+    static char *datadir = "@DATADIR@";
+    ALLOCATE (helpdir, char, strlen(datadir) + strlen("/help") + 2);
+    sprintf (helpdir, "%s/help", datadir);
+    set_str_variable ("HELPDIR", helpdir);
+    free (helpdir);
+    ALLOCATE (modules, char, strlen(datadir) + strlen("/modules") + 2);
+    sprintf (modules, "%s/modules", datadir);
+    set_str_variable ("MODULES:0", modules);
+    set_int_variable ("MODULES:n", 1);
+    free (modules);
+  }
+  return;
+}
+
+/* standard welcome message */
+void welcome () {
+  gprint (GP_ERR, "\n");
+  gprint (GP_ERR, "Welcome to %s - %s\n\n", opihi_name, opihi_description);
+}
+
+/* add program-dependent exit functions here */
+void cleanup () {
+  QuitKapa ();
+  ConfigFree ();
+
+  FreeBasic ();
+  FreeData ();
+  FreeAstro ();
+  FreeDIMM ();
+
+  return;
+}
+
+/* call to opihi shell */
+int main (int argc, char **argv) {
+  int status;
+  status = opihi (argc, argv);
+  exit (status);
+}
Index: /branches/sc_branches/pantasks_condor/dimm/findstars.c
===================================================================
--- /branches/sc_branches/pantasks_condor/dimm/findstars.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/dimm/findstars.c	(revision 34783)
@@ -0,0 +1,152 @@
+# include "dimm.h"
+
+int fillrow (char *buffer, int Nx, int offset, int sx, int *xs, int *xe);
+int addpix (int pix);
+int clearpix ();
+int freepix ();
+int statpix (float *x, float *y, float *buffer, int Nx);
+int addstar (double x, double y);
+
+Vector *vecx, *vecy, *vecf, *vecn;
+
+int findstars (int argc, char **argv) {
+
+  int i, j, I, J, Npix, Nbuf, status;
+  int xs, xe, Nx, Ny;
+  char *binary, *bp;
+  float *ap, threshold, x, y;
+  Buffer *buf;
+
+  if (argc != 3) {
+    gprint (GP_ERR, "USAGE: findstars (buffer) (threshold)\n");
+    return (FALSE);
+  }
+
+  if ((buf = SelectBuffer (argv[1], OLDBUFFER, TRUE)) == NULL) return (FALSE);
+  threshold = atof (argv[2]);
+
+  if ((vecx = SelectVector ("star_x", ANYVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((vecy = SelectVector ("star_y", ANYVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((vecf = SelectVector ("star_f", ANYVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((vecn = SelectVector ("star_n", ANYVECTOR, TRUE)) == NULL) return (FALSE);
+
+  vecx[0].Nelements = vecy[0].Nelements = 0;
+  vecf[0].Nelements = vecn[0].Nelements = 0;
+
+  REALLOCATE (vecx[0].elements, float, 1);
+  REALLOCATE (vecy[0].elements, float, 1);
+  REALLOCATE (vecf[0].elements, float, 1);
+  REALLOCATE (vecn[0].elements, float, 1);
+
+  /* binarize @ threshold */
+  Nx = buf[0].header.Naxis[0];
+  Ny = buf[0].header.Naxis[1];
+  Npix = Nx*Ny;
+  ALLOCATE (binary, char, Npix);
+
+  ap = (float *) buf[0].matrix.buffer;
+  bp = binary;
+  bzero (bp, Npix);
+
+  for (i = 0; i < Npix; i++, ap++, bp++) {
+    if (*ap > threshold) *bp = 1;
+  }
+
+  for (j = 0; j < Ny; j++) {
+    for (i = 0; i < Nx; i++) {
+      if (binary[j*Nx + i]) {
+	clearpix ();
+	status = fillrow (binary, Nx, j*Nx, i, &xs, &xe);
+	for (J = j + 1; (J < Ny) && status; J++) {
+	  for (I = xs; !binary[J*Nx + I] && (I <= xe); I++);
+	  status = fillrow (binary, Nx, J*Nx, I, &xs, &xe);
+	}  
+	/* we now have a stack of pixels, find geometric center */
+	statpix (&x, &y, (float *)buf[0].matrix.buffer, Nx);
+      }
+    }
+  }
+  freepix ();
+  return (TRUE);
+}
+
+/* find contiguous trigger pixels in row from starting point */
+int fillrow (char *buffer, int Nx, int offset, int sx, int *xs, int *xe) {
+
+  int i, pix, trigger;
+
+  *xe = *xs = sx;
+  trigger = FALSE;
+  for (i = sx, pix = offset + i; buffer[pix] && (i < Nx); i++, pix++) {
+    addpix (pix);
+    buffer[pix] = 0;
+    trigger = TRUE;
+    *xe = i;
+  }
+  for (i = sx - 1, pix = offset + i; (i >= 0) && buffer[pix]; i--, pix--) {
+    addpix (pix);
+    buffer[pix] = 0;
+    trigger = TRUE;
+    *xs = i;
+  }
+  return (trigger);
+}
+
+static int Npixlist = 0;
+static int *pixlist = (int *) NULL;
+
+addpix (int pix) {
+  Npixlist ++;
+  if (pixlist == (int *) NULL) {
+    ALLOCATE (pixlist, int, MAX (1, Npixlist));
+  } else {
+    REALLOCATE (pixlist, int, MAX (1, Npixlist));
+  }    
+  pixlist[Npixlist - 1] = pix;
+}
+
+clearpix () {
+  Npixlist = 0;
+  REALLOCATE (pixlist, int, 1);
+}
+
+freepix () {
+  Npixlist = 0;
+  free (pixlist);
+  pixlist = (int *) NULL;
+}
+
+statpix (float *x, float *y, float *buffer, int Nx) {
+
+  int i, X, Y, pix, Nv, No;
+  double Sx, Sy, So;
+
+  So = Sx = Sy = 0;
+  for (i = 0; i < Npixlist; i++) {
+    pix = pixlist[i];
+    Y = pix / Nx;
+    X = pix % Nx;
+    So += buffer[pix];
+    Sx += X * buffer[pix];
+    Sy += Y * buffer[pix];
+  }
+  *x = Sx / So;
+  *y = Sy / So;
+  gprint (GP_ERR, "%f %f  %f %d\n", *x, *y, So, Npixlist);
+
+  No = vecx[0].Nelements;
+  Nv = No + 1;
+  vecx[0].Nelements = vecy[0].Nelements = Nv;
+  vecf[0].Nelements = vecn[0].Nelements = Nv;
+
+  REALLOCATE (vecx[0].elements, float, MAX (Nv, 1));
+  REALLOCATE (vecy[0].elements, float, MAX (Nv, 1));
+  REALLOCATE (vecf[0].elements, float, MAX (Nv, 1));
+  REALLOCATE (vecn[0].elements, float, MAX (Nv, 1));
+
+  vecx[0].elements[No] = *x;
+  vecy[0].elements[No] = *y;
+  vecf[0].elements[No] = So;
+  vecn[0].elements[No] = Npixlist;
+
+}
Index: /branches/sc_branches/pantasks_condor/dimm/init.c
===================================================================
--- /branches/sc_branches/pantasks_condor/dimm/init.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/dimm/init.c	(revision 34783)
@@ -0,0 +1,26 @@
+# include "dimm.h"
+
+int camera          PROTO((int, char **));
+int findstars       PROTO((int, char **));
+int telescope       PROTO((int, char **));
+int version         PROTO((int, char **));
+
+static Command cmds[] = {  
+  {1, "camera",    camera,    "camera functions"},
+  {1, "findstars", findstars, "find objects on image"},
+  {1, "telescope", telescope, "telescope communications"},
+  {1, "version",   version,   "show version information"},
+}; 
+
+void InitDIMM () {
+  
+  int i;
+
+  for (i = 0; i < sizeof (cmds) / sizeof (Command); i++) {
+    AddCommand (&cmds[i]);
+  }
+
+}
+
+void FreeDIMM () {
+}
Index: /branches/sc_branches/pantasks_condor/dimm/sbig/sbig.h
===================================================================
--- /branches/sc_branches/pantasks_condor/dimm/sbig/sbig.h	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/dimm/sbig/sbig.h	(revision 34783)
@@ -0,0 +1,222 @@
+#ifndef _SBIG_H
+#define _SBIG_H
+
+#define VERSION  2.0
+
+struct sbig_init {
+    float linux_version;
+    int	 nmbr_bad_columns;	/* bad columns in imaging CCD */
+    int  bad_columns[4];
+    int  imaging_abg_type;	/* 0 no ABG, 1 ABG present */
+    char serial_number[10];
+    unsigned firmware_version;
+    char  camera_name[64];
+    struct camera_info {
+	int  nmbr_readout_modes;
+	struct readout_mode {
+	    int  mode;
+	    int  width;			/* pixels */
+	    int  height;		/* height */
+	    int  gain;			/* in 0.01 e-/ADU */
+	    unsigned pixel_width;	/* in nanometers */
+	    unsigned pixel_height;	/* in nanometers */
+	} readout_mode[12];
+    } camera_info[2];
+    int ST5_AD_size;
+    int ST5_filter_type;
+};
+extern int  sbig_init(int port, int options, struct sbig_init *);
+
+#define SBIG_IMAGING_CCD 0
+#define SBIG_TRACKING_CCD 1
+
+#define SBIG_CCD_STATUS_IDLE  0
+#define SBIG_CCD_STATUS_IN_PROGRESS  2
+#define SBIG_CCD_STATUS_COMPLETE  3
+
+#define SBIG_CCD_SHUTTER_OPENED  0
+#define SBIG_CCD_SHUTTER_CLOSED  1
+#define SBIG_CCD_SHUTTER_OPENING  2
+#define SBIG_CCD_SHUTTER_CLOSING  3
+
+struct sbig_status {
+    int  imaging_ccd_status;
+    int  tracking_ccd_status;
+    int  fan_on;
+    int  shutter_state;
+    int  led_state;
+    int  shutter_edge;
+    int  plus_x_relay;
+    int  minus_x_relay;
+    int  plus_y_relay;
+    int  minus_y_relay;
+    int  pulse_active;
+    int  temperature_regulation;
+    int  temperature_setpoint;
+    int  cooling_power;
+    int  air_temperature;
+    int  ccd_temperature;
+};
+extern int  sbig_get_status(struct sbig_status *);
+
+
+#define SBIG_LEAVE_SHUTTER  0
+#define SBIG_OPEN_SHUTTER  1
+#define SBIG_CLOSE_SHUTTER  2
+#define SBIG_INITIALIZE_SHUTTER  3
+
+#define SBIG_LED_OFF  0
+#define SBIG_LED_ON  1
+#define SBIG_LED_BLINK_SLOW 2
+#define SBIG_LED_BLINK_FAST 3
+
+struct sbig_control {
+    int  fan_on;
+    int  shutter;
+    int  led;
+};
+extern int  sbig_control(struct sbig_control *);
+
+struct sbig_pulse {
+    int nmbr_pulses;		/* 0 to 255 */
+    int pulse_width;		/* microsec, min 9 */
+    int pulse_interval;		/* microsec, min 27+pulse_width */
+};
+extern int  sbig_pulse(struct sbig_pulse *);
+
+struct sbig_relay {
+    int x_plus_time;
+    int x_minus_time;
+    int y_plus_time;
+    int y_minus_time;
+};
+extern int  sbig_activate_relay(struct sbig_relay *);
+
+
+/*
+ * Shutter control.  0 for no change in shutter (i.e. control of shutter
+ * using SBigControl class), 1 normal shutter (open during exposure,
+ * closed otherwise), 2 shutter closed (for taking dark frames).
+ */
+#define SBIG_EXPOSE_SHUTTER_UNCHANGED  0
+#define SBIG_EXPOSE_SHUTTER_NORMAL  1
+#define SBIG_EXPOSE_SHUTTER_CLOSED  2
+
+struct sbig_expose {
+    int ccd;
+    int exposure_time;
+    int abg_state;
+    int shutter;
+};
+extern int  sbig_expose(struct sbig_expose *);
+
+
+/*
+ *  Terminate the image and read it out.
+ *  Use width == 0 and height == 0 to abort an image with no readout (all
+ *   params other than ccd ignored)
+ */
+#define SBIG_BIN_1X1  0
+#define SBIG_BIN_2X2  1
+#define SBIG_BIN_3X3  2
+
+struct sbig_readout {
+    int ccd;
+    int binning;
+    int x, y;
+    int width, height;
+    unsigned short *data;
+    int data_size_in_bytes;
+    int (*callback)(float percent_complete);
+};
+extern int  sbig_readout(struct sbig_readout *);
+
+#define SBIG_NO_SHUTTER_DELAY  0x0001	/* don't wait for shutter to close */
+struct sbig_readout2 {
+    int flags;				/* see immediately above */
+    int ccd;
+    int binning;
+    int x, y;
+    int width, height;
+    unsigned short *data;
+    int data_size_in_bytes;
+    int (*callback)(float percent_complete);
+};
+extern int  sbig_readout2(struct sbig_readout2 *);
+
+
+#define SBIG_TEMP_REGULATION_OFF  0
+#define SBIG_TEMP_REGULATION_ON  1
+#define SBIG_TEMP_REGULATION_DIRECT_DRIVE  2
+
+/*
+ * Antiblooming gate control values
+ */
+#define SBIG_ABG_OFF  0
+#define SBIG_ABG_LOW  1
+#define SBIG_ABG_MEDIUM  2
+#define SBIG_ABG_HIGH  3
+
+struct sbig_cool {
+    int regulation;		/* 0 off, 1 on, 2 direct_drive */
+    int temperature;		/* in 0.1 deg C, if 'on' */
+    int direct_drive;		/* power [0..255], direct_drive */
+};
+extern int  sbig_set_cooling(struct sbig_cool *);
+
+extern int  sbig_set_ao7_deflection(int x_deflection, int y_deflection);
+
+extern int  sbig_set_ao7_focus(int type);
+
+#define SBIG_AO7_FOCUS_SOFT_CENTER 4
+#define SBIG_AO7_FOCUS_HARD_CENTER  3
+#define SBIG_AO7_FOCUS_STEP_TOWARD_SCOPE 2
+#define SBIG_AO7_FOCUS_STEP_FROM_SCOPE 1
+
+/*
+ *	Return Error Codes
+ *
+ *	These are the error codes returned by the driver
+ *	function.  They are prefixed with CE_ to designate
+ *	them as camera errors.
+ *
+ *      The return codes from the sbig_xxx() routines will
+ *      be the NEGATIVE of these on an error.
+ *
+ */
+#define SBIG_CAMERA_NOT_FOUND	-1
+#define SBIG_EXPOSURE_IN_PROGRESS -2
+#define SBIG_NO_EXPOSURE_IN_PROGRESS -3
+#define SBIG_UNKNOWN_COMMAND	-4
+#define SBIG_BAD_CAMERA_COMMAND	-5
+#define SBIG_BAD_PARAMETER	-6
+#define SBIG_TX_TIMEOUT		-7
+#define SBIG_RX_TIMEOUT		-8
+#define SBIG_NAK_RESBIGIVED	-9
+#define SBIG_CAN_RESBIGIVED	-10
+#define SBIG_UNKNOWN_RESPONSE	-11
+#define SBIG_BAD_LENGTH		-12
+#define SBIG_AD_TIMEOUT		-13
+#define SBIG_CHECKSUM_ERROR	-14
+#define SBIG_EEPROM_ERROR	-15
+#define SBIG_SHUTTER_ERROR	-16
+#define SBIG_UNKNOWN_CAMERA	-17
+#define SBIG_DRIVER_NOT_FOUND	-18
+#define SBIG_DRIVER_NOT_OPEN	-19
+#define SBIG_DRIVER_NOT_CLOSED	-20
+#define SBIG_SHARE_ERROR	-21
+#define SBIG_TSBIG_NOT_FOUND	-22
+#define SBIG_NEXT_ERROR		-23
+#define SBIG_NOT_ROOT		-24
+char *sbig_show_error(int);
+
+/*
+ * IO strategy flags
+ */
+#define SBIG_DISABLE_INTERRUPTS 0x001
+#define SBIG_LOCK_ALL	0x002
+#define SBIG_SHORT_DELAYS 0x004
+#define SBIG_DEFAULT_STRATEGY (SBIG_DISABLE_INTERRUPTS|SBIG_LOCK_ALL)
+void sbig_set_linux_strategy(int);
+
+#endif /* S_BIG_H */
Index: /branches/sc_branches/pantasks_condor/dimm/sbig/sp_ccdcontrol.c
===================================================================
--- /branches/sc_branches/pantasks_condor/dimm/sbig/sp_ccdcontrol.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/dimm/sbig/sp_ccdcontrol.c	(revision 34783)
@@ -0,0 +1,472 @@
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <math.h>
+#include <time.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include "sbig.h"
+#include "fh/fh.h" /* CFHT FITS Handling library */
+
+// XXX RHL claims this is not needed
+// #include <values.h>
+
+#define N_FITS_ENTRIES 33
+
+/*
+ * The following hack is needed if you want to use sbig.a w/ libc5
+ */
+#ifndef __bzero
+void __bzero(void* s, int n)
+{
+  memset(s, 0, n);
+}
+#endif
+
+static struct sbig_init info;
+
+static int write_fits(int width, int height, double etime, int binmode, unsigned short *data);
+
+static struct sbig_readout readout;
+
+/*
+ * This is the 'progress' callback
+ * passed to sbig_readout()
+ */
+static int readout_callback(float percent)
+{
+  gprint (GP_ERR, "progress: Reading %d x %d pixels (%d%%)\r",
+	  readout.width, readout.height, (int)percent);
+  /* return 1 to continue, 0 to abort */
+  return 1;
+}
+
+/*
+ *  Start an exposure, wait for it, then download from the camera
+ *  in bands.  This is somewhat complex, since it handles any size
+ *  subimage of the CCD, and bands too.
+ */
+static void take_picture(double etime, int binmode, int shuttermode, int x, int y, int w, int h)
+{
+  int  ret, i, needed;
+  static unsigned short  *buffer;
+  static int  buffer_size;
+  struct sbig_expose  expose;
+  struct sbig_status  status;
+  double  f;
+  
+  expose.ccd = 0; /* Always in imaging mode, not tracking 
+		    since CFHT PF does the tracking*/
+
+  if(binmode<0 || binmode>3){
+    gprint (GP_ERR, "Imaging binmode options: [0=1x1, *1=2x2, 2=3x3]\n");
+    exit(EXIT_FAILURE);
+  }
+  readout.binning=binmode;
+  
+  readout.x=x;
+  readout.y=y;
+  readout.width=w;
+  readout.height=h;
+  if (readout.width == 0) {
+    readout.width =
+      info.camera_info[expose.ccd].readout_mode[readout.binning].width - x;
+  } else
+    if (readout.width + x >
+	info.camera_info[expose.ccd].readout_mode[readout.binning].width) {
+    gprint (GP_ERR, "Raster X or Width parameter out of range.\n");
+    exit(EXIT_FAILURE);
+  }
+
+  if (readout.height == 0) {
+    readout.height =
+      info.camera_info[expose.ccd].readout_mode[readout.binning].height - y;
+  } else
+    if (readout.height + y >
+	info.camera_info[expose.ccd].readout_mode[readout.binning].height) {
+    gprint (GP_ERR, "Raster Y or Height parameter out of range.\n");
+    exit(EXIT_FAILURE);
+  }
+
+  f = etime;
+  expose.exposure_time = (int)(100.0*f);
+  expose.abg_state = SBIG_ABG_OFF; /* SBIG_ABG_MEDIUM */
+
+  expose.shutter = shuttermode;  /* No shutter present */
+
+  sync();
+  ret = sbig_expose(&expose);
+  if (ret < 0)
+    gprint (GP_ERR, "sbig error: %s\n", sbig_show_error(ret));
+  
+  gprint (GP_ERR, "progress: Sleeping for %g seconds\r", 0.01*expose.exposure_time);
+
+  if (expose.exposure_time > 100) {
+    for (i = 0; i < expose.exposure_time; i += 100) {
+      sbig_get_status(&status);
+      gprint (GP_ERR, "progress: Exposure in progress; ccd_status=%d, shutter=%d (%d%%)\r",
+	      status.imaging_ccd_status, status.shutter_state,
+	      i * 100 / expose.exposure_time);
+      sleep(1);
+    }
+  } else
+    usleep(expose.exposure_time*10000);
+  
+  /* wait for exposure to complete */
+  gprint (GP_ERR, "progress: Waiting for controller                             (100%%)\r");
+
+  for (i = 0; i < 1000; ++i) {
+    ret = sbig_get_status(&status);
+    if (ret < 0) {
+      gprint (GP_ERR, "sbig error: %s\n", sbig_show_error(ret));
+      break;
+    }
+    if (status.imaging_ccd_status != 3)
+      gprint (GP_ERR, "logonly: ccd_status=%d\n", status.imaging_ccd_status);
+
+    if (readout.ccd == 0) {
+      if (status.imaging_ccd_status != 2)
+	break;
+    } else {
+      if (status.tracking_ccd_status != 2)
+	break;
+    }
+    usleep(50000);
+  }
+  if (i)
+    gprint (GP_ERR, "warning: Exposure took an extra %g seconds\n", 0.050*i);
+
+  if (i == 1000)
+    gprint (GP_ERR, "error: EXPOSURE DIDN'T FINISH!\n");
+  else
+    gprint (GP_ERR, "progress: Exposure complete                                  (100%%)\n");
+
+  readout.ccd = expose.ccd;
+  readout.binning = readout.binning;
+  needed = readout.height*readout.width*sizeof(short);
+  if (buffer_size < needed) {
+    if (buffer != NULL)
+      free(buffer);
+    buffer = malloc(buffer_size = needed);
+  }
+  readout.data = buffer;
+  readout.data_size_in_bytes = buffer_size;
+  readout.callback = readout_callback;
+    
+  sync();
+  if ((ret = sbig_readout(&readout)) < 0) {
+    gprint (GP_ERR, "sbig error: %s\n", sbig_show_error(ret));
+    return;
+  }
+
+  gprint (GP_ERR, "progress: Writing FITS data to stdout\n");
+
+  if(write_fits(readout.width, readout.height, etime, binmode, buffer)==-1)
+    gprint (GP_ERR, "error: Could not write FITS file to stdout.\n");
+  gprint (GP_ERR, "progress: sp_ccdcontrol done.\n");
+}
+
+static int write_fits(int w, int h, double etime, int binmode, unsigned short *data)
+{
+  HeaderUnit hu;
+  register int i;
+  double datamin = DBL_MAX, datamax = DBL_MIN;
+  unsigned short u;
+  char str[80]; /* For building string FITS headers */
+  time_t date;
+
+  int ret; /* Return value for sbig_status */
+  struct sbig_status sbstat; /* Status structure */
+  
+  /* Get status to add to FITS header */
+  sync();
+  ret = sbig_get_status(&sbstat);
+
+  if (ret < 0) {
+    gprint (GP_ERR, "error: sbig_get_status failed: %s\n", sbig_show_error(ret));
+    exit(EXIT_FAILURE);
+  }
+
+  /* shouldn't we scale this to fit, first? */
+  for (i = w*h; --i >= 0; )
+    {
+      u = data[i];
+      if (u > datamax) datamax = u;
+      if (u < datamin) datamin = u;
+      data[i] = ((u<<8)|(u>>8)) ^ 0x0080; /* -32768 */
+    }
+
+  time(&date);
+
+  hu = fh_create();
+  fh_reserve(hu, 50); /* Reserve space for 50 cards (TCS, Elixir?, etc.) */
+  fh_set_bool(hu, 0., "SIMPLE", 1, "Standard FITS");
+  fh_set_int(hu, 1.0, "BITPIX", 16,"16-bit data");
+  fh_set_int(hu, 2.0, "NAXIS", 2,  "Number of axes");
+  fh_set_int(hu, 2.1, "NAXIS1", w, "Number of pixel columns");
+  fh_set_int(hu, 2.2, "NAXIS2", h, "Number of pixel rows");
+  fh_set_int(hu, 5.0, "PCOUNT",	0, "No 'random' parameters");
+  fh_set_int(hu, 6.0, "GCOUNT",	1, "Only one group");
+
+  strftime(str, sizeof(str)-1, "%Y-%m-%dT%T", gmtime(&date));
+  fh_set_str(hu, 104, "DATE", str, "UTC Date of file creation");
+  strftime(str, sizeof(str)-1, "%a %b %d %H:%M:%S %Z %Y", localtime(&date));
+  fh_set_str(hu, 104.1, "HSTTIME", str, "Local time in Hawaii");
+
+  fh_set_str(hu, 105, "ORIGIN", "CFHT", "Canada-France-Hawaii Telescope");
+
+  fh_set_flt(hu, 141.,"BZERO",	32768.0, 6,	"Zero factor");
+  fh_set_flt(hu, 142.,"BSCALE",	1.0, 2, "Scale factor");
+  fh_set_flt(hu, 150, "DATAMIN", datamin, 6, "Minimum value of the data");
+  fh_set_flt(hu, 151, "DATAMAX", datamax, 6, "Maximum value of the data");
+  fh_set_flt(hu, 160, "SATURATE", 4016.0, 6, "Saturation value");
+
+  fh_set_flt(hu, 220, "EXPTIME", etime, 5, "Integration time (seconds)");
+  sprintf(str, "%d %d", binmode + 1, binmode + 1);
+  fh_set_str(hu, 230, "CCDSUM", str, "Binning factors");
+  fh_set_int(hu, 231, "CCDBIN1", binmode + 1, "Binning factor along first axis");
+  fh_set_int(hu, 232, "CCDBIN2", binmode + 1, "Binning factor along second axis");
+
+  fh_set_com(hu, 1400.0, "COMMENT", "");
+  fh_set_com(hu, 1400.1, "COMMENT", " SBIG status record:");
+  fh_set_com(hu, 1400.2, "COMMENT", "");
+  if (sbstat.imaging_ccd_status==0)
+    fh_set_str(hu, 1500, "DETSTAT", "ok", "(SBIG imaging_ccd_status is 0)");
+  else
+    fh_set_int(hu, 1500, "DETSTAT", sbstat.imaging_ccd_status, "SBIG error!");
+  fh_set_flt(hu, 1500.1, "DETTEM", sbstat.ccd_temperature/10., 3, "Detector temperature");
+  fh_set_int(hu, 1500.2, "DETTEMRG", sbstat.temperature_regulation, "1=set temp 0=set power");
+  fh_set_int(hu, 1500.3, "DETTEMSP", sbstat.temperature_setpoint, "Temperature setpoint");
+  fh_set_int(hu, 1500.4, "DETTEMCP", sbstat.cooling_power, "Cooling power");
+  fh_set_flt(hu, 1510,  "CAMTEM", sbstat.air_temperature/10., 3, "Outside air temperature");
+  fh_set_com(hu, 1600.0, "COMMENT", "");
+  fh_set_com(hu, 1600.1, "COMMENT", " SBIG info record:");
+  fh_set_com(hu, 1600.2, "COMMENT", "");
+  sprintf(str, "%u", info.firmware_version);
+  fh_set_str(hu, 1600.9, "CONSWV", str, "SBIG firmware version");
+  fh_set_str(hu, 1601,"DETECTOR", info.camera_name, info.serial_number);
+  fh_set_bool(hu, 1700, "ABGON", info.imaging_abg_type, "Antiblooming gate present");
+  fh_set_int(hu, 1701, "ABGMODE", info.imaging_abg_type, "Antiblooming gate control value");
+  fh_set_int(hu, 1800, "BADCOL", info.nmbr_bad_columns, "Number of bad columns");
+  for (i = 0; i < info.nmbr_bad_columns; ++i){
+    sprintf(str, "BADCOL%d", i);
+    fh_set_int(hu, 1800 + 0.1 + 0.1 * i, str, info.bad_columns[i], "Bad column");
+  }
+  fh_set_int(hu, 1901, "ST5ADSIZ", info.ST5_AD_size, "");
+  fh_set_int(hu, 1902, "ST5FLTYP", info.ST5_filter_type, "");
+
+  if (fh_write(hu, STDOUT_FILENO) != FH_SUCCESS)
+    exit(EXIT_FAILURE);
+
+  if (fh_write_padded_image(hu, STDOUT_FILENO, data, w*h*sizeof(unsigned short), FH_TYPESIZE_RAW) != FH_SUCCESS)
+    exit(EXIT_FAILURE);
+
+  gprint (GP_ERR, "logonly: Wrote %d pixels, min=%g, max=%g\n", w*h, datamin, datamax);
+  sync();
+  return 0;
+}
+
+static void show_format(void){
+  gprint (GP_ERR, "Format for sp_ccdcontrol, SkyProbe CCD controller:\n"
+	  "Writes FITS file to stdout, status messages to stderr.\n"
+	  "NOTE: -port {hex} is an option for all forms of sp_ccdcontrol,\n"
+	  "  to specify a hex adress for the parallel port.\n"
+	  "sp_ccdcontrol expose -etime {Exp time}\n"
+          "   [-binmode {Binning mode}]\n"
+          "   [-shuttermode {Shutter mode}]\n"
+          "   [-raster X Y W H]\n"
+	  "Exp time: Exposure time given in seconds.\n"
+	  "Binning mode: 0=1x1, 1=2x2, 2=3x3.  Note, 3x3 not allowed in -track mode.\n"
+	  "   OR \n"
+	  "sp_ccdcontrol cool {coolmode} [temp|power]\n"
+	  "coolmode: 0=OFF, 1=ON, 2=Direct Drive\n"
+	  "For coolmode=0: Do not specify temp or power\n"
+	  "For coolmode=1: Specify temp in deg C\n"
+	  "For coolmode=2: Specify power (0-255)\n");
+  exit(EXIT_FAILURE);
+}
+
+
+int main(int argc, char *argv[])
+{
+  /*    char buffer[80], last_command[80]; */
+  int  ret = -1, i, port;
+  int  is_small_camera;
+    
+  /* Simon Kras--command line variables */
+  char cmd[80];
+  double etime;
+  int binmode;
+  int shuttermode;
+  int coolmode;
+  int raster_x, raster_y, raster_h, raster_w;
+  double cooltemp, coolpwr;
+
+  if(argc>1) strcpy(cmd, argv[1]);
+  else show_format();
+
+  port = 1; /* 0x378 */
+  for (i = 1; i < argc; ++i) {
+    if (!strcmp(argv[i], "-port")) {
+      sscanf(argv[++i], "%x", &port);
+      gprint (GP_ERR, "port override to 0x%x\n", port);
+    }
+  }
+
+  for (i = 0; i < 4; ++i) {
+    sync();
+    /* set our verbosity to 0 (range is 0 to 5) */
+    ret = sbig_init(port, 0, &info);
+    if (ret == -6) ret = 0; /* %%% It always returns this error, but actually it worked.
+			     * Since the library is binary only, we can't fix it.
+			     */
+    if (ret == 0) break;
+    gprint (GP_ERR,"logonly: sbig_init() failed: %s\n", sbig_show_error(ret));
+    usleep(100000);
+  }
+  if (ret < 0) {
+    gprint (GP_ERR, "error: cannot open camera, giving up\n");
+    exit(EXIT_FAILURE);
+  }
+
+  is_small_camera = (strstr(info.camera_name, "237") != NULL ||
+		     index(info.camera_name, '5') != NULL);
+  for (i = 0; i < info.camera_info[0].nmbr_readout_modes; ++i) {
+    struct readout_mode  *rmp;
+    rmp = &info.camera_info[0].readout_mode[i];
+#ifdef DEBUG
+    gprint (GP_ERR, "debug: ImagingCCD mode %d: w=%d h=%d "
+	    "gain=%f "
+	    "pw=%g ph=%g\n", rmp->mode, rmp->width,
+	    rmp->height,  0.01*rmp->gain, 0.001*rmp->pixel_width, 
+	    0.001*rmp->pixel_height);
+#endif
+  }
+
+  for (i = 0; i < info.camera_info[1].nmbr_readout_modes; ++i) {
+    struct readout_mode  *rmp;
+
+    rmp = &info.camera_info[1].readout_mode[i];
+#ifdef DEBUG
+    gprint (GP_ERR, "debug: TrackingCCD mode %d: w=%d h=%d "
+	    "gain=%f "
+	    "pw=%g ph=%g\n"
+	    , rmp->mode, rmp->width, rmp->height,
+	    0.01*rmp->gain,
+	    0.001*rmp->pixel_width, 0.001*rmp->pixel_height);
+#endif
+  }
+  if(!strcmp(cmd, "expose")){
+    etime=-999.;
+    for(i=1;i<argc;++i){
+      if(!strcmp(argv[i], "-etime")){
+	if(argc>(i+1)) etime = atof(argv[++i]);
+	else
+	  gprint (GP_ERR, "No argument given to -etime\n");
+      }
+    }
+    if(etime==-999.){
+	gprint (GP_ERR, "Must specify exposure time.\n");
+	exit(EXIT_FAILURE);
+      }
+      
+      binmode=-999;
+      for(i=1;i<argc;++i){
+	if(!strcmp(argv[i], "-binmode")){
+	  if(argc>(i+1)) binmode = atoi(argv[++i]);
+	  else{
+	    gprint (GP_ERR, "No argument given for -binmode\n");
+	    exit(EXIT_FAILURE);
+	  }
+	}
+      }
+      if(binmode==-999) binmode=0;
+
+      shuttermode=0;
+      for (i=1; i<argc;++i){
+	if (!strcmp(argv[i], "-shuttermode")){
+	  if (argc>(i+1)) shuttermode = atoi(argv[++i]);
+	  else{
+	    gprint (GP_ERR, "No argument given for -shuttermode\n");
+	    exit(EXIT_FAILURE);
+	  }
+	}
+      }
+
+      raster_x=0;
+      raster_y=0;
+      raster_w=0;
+      raster_h=0;
+      for (i=1; i<argc;++i){
+	if (!strcmp(argv[i], "-raster")){
+	  if (argc>(i+4)) {
+	    raster_x = atoi(argv[++i]);
+	    raster_y = atoi(argv[++i]);
+	    raster_w = atoi(argv[++i]);
+	    raster_h = atoi(argv[++i]);
+	  }
+	  else{
+	    gprint (GP_ERR, "-raster requires 4 arguments: x y w h\n");
+	    exit(EXIT_FAILURE);
+	  }
+	}
+      }
+      
+      take_picture(etime, binmode, shuttermode, raster_x, raster_y, raster_w, raster_h);
+
+  } else if(!strcmp(cmd, "cool")){
+    double  f;
+    struct sbig_cool  cool;
+    
+    if(argc<3){
+      gprint (GP_ERR, "Too few arguments for cool mode\n");
+      exit(EXIT_FAILURE);
+    } 
+    coolmode = atoi(argv[2]);
+    
+    if(coolmode<0 || coolmode>2){
+      gprint (GP_ERR, "Cooling mode values allowed: [0 Off, 1 *On, 2 DirectDrive]\n");
+      exit(EXIT_FAILURE);
+    }
+    cool.regulation = coolmode;
+    cool.direct_drive = 0;
+    if (cool.regulation == 2){
+      if(argc<4){
+	gprint (GP_ERR, "Must specify cooling power (0-255)\n");
+	exit(EXIT_FAILURE);
+      }
+      coolpwr=atof(argv[3]);
+      if(coolpwr<0 || coolpwr>255){
+	gprint (GP_ERR, "Cooling power must be 0-255.\n");
+	exit(EXIT_FAILURE);
+      }
+      cool.direct_drive = coolpwr;
+    }
+    else if (cool.regulation == 1) {
+      if(argc<4){
+	gprint (GP_ERR, "Must specify temperature in deg C\n");
+	exit(EXIT_FAILURE);
+      }
+      cooltemp=atof(argv[3]);
+      if(cooltemp<-50 || cooltemp>20){
+	gprint (GP_ERR, "Cooling temperature must be -50 to 20 deg C.\n");
+	exit(EXIT_FAILURE);
+      }
+      f = cooltemp;
+      i = 10.0*f + 0.5;
+      cool.temperature = i;
+    }
+    sync();
+    ret = sbig_set_cooling(&cool);
+    if (ret < 0){
+      gprint (GP_ERR, "sbig error: %s\n", sbig_show_error(ret));
+    }
+  }else{
+    gprint (GP_ERR, "Illegal command: '%s'\n", cmd);
+    show_format();
+  }
+  exit(EXIT_SUCCESS);
+}
+ 
Index: /branches/sc_branches/pantasks_condor/dimm/telescope_cmds.c
===================================================================
--- /branches/sc_branches/pantasks_condor/dimm/telescope_cmds.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/dimm/telescope_cmds.c	(revision 34783)
@@ -0,0 +1,234 @@
+# include "dimm.h"
+
+double distSky (double r1, double r2, double d1, double d2);
+
+int telescope (int argc, char **argv) {
+  
+  if (argc < 2) goto usage;
+
+  if (!strcasecmp (argv[1], "init")) {
+    if (argc != 3) {
+      gprint (GP_ERR, "USAGE: telescope init (port)\n");
+      return (FALSE);
+    }
+    if (!SerialInit (argv[2])) return (FALSE);
+    gprint (GP_ERR, "telescope on port %s\n", argv[2]);
+    return (TRUE);
+  }
+
+  if (!strcasecmp (argv[1], "cmd")) {
+
+    int status;
+    char *answer = (char *) NULL;
+
+    if (argc != 3) {
+      gprint (GP_ERR, "USAGE: telescope cmd (string)\n");
+      return (FALSE);
+    }
+    status = SerialCommand (argv[2], &answer, 10);
+    gprint (GP_ERR, "status: %d\n", status);
+    if (answer != (char *) NULL) {
+      gprint (GP_ERR, "answer: ..%s..\n", answer);
+    }
+    return (TRUE);
+  }
+
+  if (!strcasecmp (argv[1], "ack")) {
+
+    int status;
+    char line[32], *answer;
+
+    if (argc != 3) {
+      gprint (GP_ERR, "USAGE: telescope cmd (string)\n");
+      return (FALSE);
+    }
+    line[0] = 0x06;
+    line[1] = 0;
+    status = SerialCommand (line, &answer, 10);
+    gprint (GP_ERR, "status: %d\n", status);
+    if (answer != (char *) NULL) {
+      gprint (GP_ERR, "answer: ..%s..\n", answer);
+    }
+    return (TRUE);
+  }
+
+  if (!strcasecmp (argv[1], "slew")) {
+
+    double ra, dec;
+    int status;
+
+    if (argc != 4) {
+      gprint (GP_ERR, "USAGE: telescope slew (ra) (dec)\n");
+      return (FALSE);
+    }
+
+    ra  = ohana_normalize_angle(atof (argv[2]));
+    dec = atof (argv[3]);
+
+    status = gotoRD (ra, dec);
+
+    if (!status) return (FALSE);
+    return (TRUE);
+  }
+
+  if (!strcasecmp (argv[1], "coords")) {
+
+    int status;
+    double ra, dec;
+    char line[64];
+
+    if (argc != 2) {
+      gprint (GP_ERR, "USAGE: telescope coords\n");
+      return (FALSE);
+    }
+
+    if (!getRD (&ra, &dec)) return (FALSE);
+    gprint (GP_ERR, "%f %f\n", ra, dec);
+    set_variable ("RA", ra);
+    set_variable ("DEC", dec);
+    dms_format (line, (ra/15.0));
+    set_str_variable ("Rs", line);
+    dms_format (line, dec);
+    set_str_variable ("Ds", line);
+
+    return (TRUE);
+  }
+
+  if (!strcasecmp (argv[1], "altaz")) {
+
+    int status;
+    double x, y;
+
+    if (argc != 2) {
+      gprint (GP_ERR, "USAGE: telescope altaz\n");
+      return (FALSE);
+    }
+
+    if (!getXY (&x, &y)) return (FALSE);
+    gprint (GP_ERR, "%f %f\n", x, y);
+    set_variable ("ALT", x);
+    set_variable ("AZ", y);
+    return (TRUE);
+  }
+
+  if (!strcasecmp (argv[1], "site")) {
+
+    int status;
+    double lon, lat, LST;
+
+    if (argc != 2) {
+      gprint (GP_ERR, "USAGE: telescope site\n");
+      return (FALSE);
+    }
+
+    if (!getSite (&lon, &lat, &LST)) return (FALSE);
+    gprint (GP_ERR, "%f %f  %f\n", lon, lat, LST);
+    set_variable ("LON", lon);
+    set_variable ("LAT", lat);
+    set_variable ("LST", LST);
+    return (TRUE);
+  }
+
+  if (!strcasecmp (argv[1], "setsite")) {
+
+    double lon, lat;
+
+    if (argc != 5) {
+      gprint (GP_ERR, "USAGE: telescope setsite (name) (longitude) (latitude)\n");
+      return (FALSE);
+    }
+
+    lon = atof (argv[3]);
+    lat = atof (argv[4]);
+    if (!setSite (argv[2], lon, lat)) return (FALSE);
+    set_variable ("LON", lon);
+    set_variable ("LAT", lat);
+    return (TRUE);
+  }
+
+  if (!strcasecmp (argv[1], "settime")) {
+
+    if (argc != 3) {
+      gprint (GP_ERR, "USAGE: telescope settime (lst)\n");
+      return (FALSE);
+    }
+
+    if (!setTime (argv[2])) return (FALSE);
+    return (TRUE);
+  }
+
+  if (!strcasecmp (argv[1], "verbose")) {
+
+    int mode;
+
+    if (argc != 3) {
+      gprint (GP_ERR, "USAGE: telescope verbose (mode)\n");
+      return (FALSE);
+    }
+
+    mode = atoi (argv[2]);
+    SerialVerbose (mode);
+    return (TRUE);
+  }
+
+  if (!strcasecmp (argv[1], "setcoords")) {
+
+    int status;
+    double ra, dec;
+
+    if (argc != 4) {
+      gprint (GP_ERR, "USAGE: telescope setcoords (ra) (dec)\n");
+      return (FALSE);
+    }
+
+    ra  = atof (argv[2]);
+    dec = atof (argv[3]);
+
+    if (!setRD (ra, dec)) return (FALSE);
+    return (TRUE);
+  }
+
+  if (!strcasecmp (argv[1], "offset")) {
+
+    int status;
+
+    if (argc != 4) {
+      gprint (GP_ERR, "USAGE: telescope offset (direction) (distance)\n");
+      gprint (GP_ERR, "  direction : x or y\n");
+      gprint (GP_ERR, "  distance  : +arcmin or -arcmin\n");
+      return (FALSE);
+    }
+    status = offset (argv[2], atof (argv[3]));
+
+    if (!status) return (FALSE);
+    return (TRUE);
+  }
+
+  if (!strcasecmp (argv[1], "toffset")) {
+
+    int status;
+
+    if (argc != 5) {
+      gprint (GP_ERR, "USAGE: telescope toffset (direction) (rate) (duration)\n");
+      gprint (GP_ERR, "example: telescope toffset x RC 1.5\n");
+      return (FALSE);
+    }
+    status = toffset (argv[2], argv[3], atof(argv[4]));
+
+    if (!status) return (FALSE);
+    return (TRUE);
+  }
+
+ usage:
+  gprint (GP_ERR, "telescope init port - set serial port (eg, /dev/ttyS0)\n");
+  gprint (GP_ERR, "telescope site - get site information\n");
+  gprint (GP_ERR, "telescope altaz - g\n");
+  gprint (GP_ERR, "telescope setcoords (ra) (dec)\n");
+  gprint (GP_ERR, "telescope setsite (name) (longitute) (latitude)\n");
+  gprint (GP_ERR, "telescope coords\n");
+  gprint (GP_ERR, "telescope slew (ra) (dec)\n");
+  gprint (GP_ERR, "telescope offset (direction) (duration)\n");
+  gprint (GP_ERR, "telescope toffset (direction) (rate) (duration)\n");
+  return (FALSE);
+}
+
Index: /branches/sc_branches/pantasks_condor/dimm/version.c
===================================================================
--- /branches/sc_branches/pantasks_condor/dimm/version.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/dimm/version.c	(revision 34783)
@@ -0,0 +1,17 @@
+# include "dimm.h"
+static char *name = "$Name: not supported by cvs2svn $";
+
+int version (int argc, char **argv) {
+
+  char *tmp;
+
+  gprint (GP_LOG, "\n");
+  gprint (GP_LOG, "pclient version: %s\n", (tmp = strip_version (name))); free (tmp);
+
+  gprint (GP_LOG, "opihi version: %s\n", (tmp = strip_version (opihi_version()))); free (tmp);
+  gprint (GP_LOG, "ohana version: %s\n", (tmp = strip_version (ohana_version()))); free (tmp);
+  gprint (GP_LOG, "gfits version: %s\n", (tmp = strip_version (gfits_version()))); free (tmp);
+
+  gprint (GP_LOG, "compiled on %s %s\n", __DATE__, __TIME__);
+  return (TRUE);
+}
Index: /branches/sc_branches/pantasks_condor/doc/ChangeLog.txt
===================================================================
--- /branches/sc_branches/pantasks_condor/doc/ChangeLog.txt	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/doc/ChangeLog.txt	(revision 34783)
@@ -0,0 +1,207 @@
+
+2006.10.04:
+  opihi-2-8    added output names, gprintGetName, '-current' to 'output'
+	       removed prototypes from old 'output' code
+	       fixed memory leak in evaluate_stack
+	       added global PID variable
+	       added quiet mode to coords
+	       added various memleak tests, other tests
+	       fixed error in buffer/vector selection for dimenup
+	       fixed counting error in fit2d (missing 1 row of matrix!)
+	       added normalization to kern
+	       cleaned CreateBuffer function, usage
+	       
+  mana-1-6     removed prototypes from old 'output' code
+	       cleaned CreateBuffer function, usage
+
+  pantasks-0-6 added TASK_STDOUT, TASK_STDERR to pantasks.h
+	       added stdout, stderr output targets for tasks/jobs
+	       cleaned CreateBuffer function, usage
+
+  dvo-0-6      cleaned CreateBuffer function, usage
+
+2006.08.23:
+  opihi-2-7    converted to gfits APIs (forces libfits 1.6)
+	       consolidated style argument parsing
+	       added style arguments to astro plotting
+	       added support for pantasks_client, pantasks_server
+	       added more complex keys to queue operations
+	       added gprint to allow buffered printing in threads
+	       updates to test framework for more flexibility
+	       added time conversions sec_to_hms, sec_to_day
+	       added SetLimitsRaw
+	       added isort_pair
+	       added VarConfigEntry
+	       added strip_version to clean version output
+	       moved gaussj to libohana
+	       moved main out of opihi to mana.c, etc
+
+  mana-1-5     converted to gfits APIs (forces libfits 1.6)
+	       added option for threaded shell/backend (not used)
+
+  dvo-0-5      converted to gfits APIs (forces libfits 1.6)
+      	       consolidated style argument parsing
+	       converted to new DVO APIs (dvo_catalog_xxx)
+	       added fitsed
+	       improvements to fitcolors
+	      
+  pantasks-0-5 converted to gfits APIs (forces libfits 1.6)
+	       added 'option' task command
+	       added limits on Njobs for time ranges
+	       fixed time ranges
+	       created client/server version
+	       added controller run/stop commands
+	       added controller help
+	       added server help
+
+  pcontrol-0-5 converted to gfits APIs (forces libfits 1.6)
+	       code re-org to make thread safe
+	       added multithread option (user/client threads)
+	       liberal use of ASSERTs
+
+  pclient-0-5  converted to gfits APIs (forces libfits 1.6)
+  dimm-0-4     converted to gfits APIs (forces libfits 1.6)
+
+2006.03.26:
+  opihi-2-6 : added 'close' function for devices
+	      added test framework  
+	      added tests to cmd.basic, cmd.data
+	      added $?var syntax (variable existence test)
+	      fixed broken a[3] = 5 assignment
+	      added PopQueueMatch function
+	      added periodogram.c
+	      improvements to dimendown, peak
+	      added fixwrap for roll-over on saturation
+
+  dvo-0-4     added proper-motion, parallax
+	      dropped _PS from Stars.R,D
+	      fixed configuration flags for readline/curses
+
+  mana-1-4     fixed configuration flags for readline/curses
+  pcontrol-0-4 fixed configuration flags for readline/curses
+  pclient-0-4  fixed configuration flags for readline/curses
+
+  pantasks-0-4 fixed configuration flags for readline/curses
+	       changed name from psched
+
+
+
+- opihi-2-5
+  * major work to switch to libkapa
+  * revised init sequence (general_init, program_init, startup)
+
+- mana 1.3
+  * changes to support new init sequence
+
+- dvo 0.3
+  * major changes to support SkyRegions
+  * changes to support libkapa
+  * changes to support new init sequence
+
+- psched 0.3
+  * changes to support new init sequence
+  * changes to support libkapa
+
+- pcontrol 0.3
+  * changes to support new init sequence
+  * changes to support libkapa
+
+- pclient 0.3
+  * changes to support new init sequence
+  * changes to support libkapa
+
+- dimm 0.2
+  * changes to support new init sequence
+  * changes to support libkapa
+
+dimm-0-1: release 2005.10.20
+  first tagged dimm release
+
+pclient-0-2: release 2005.10.20
+  minor cleanups
+  minor changes to use new libohana (v1.5) / libfits (v1.4)
+
+pcontrol-0-2: release 2005.10.20
+  added run/stop commands
+  minor changes to use new libohana (v1.5) / libfits (v1.4)
+
+psched-0-2: release 2005.10.20
+  work to improve / test throughput
+  minor cleanups
+  minor changes to use new libohana (v1.5) / libfits (v1.4)
+
+dvo-0-2:  release 2005.10.20
+  substantial changes to work with new dvo load functions
+  support for mode/format changes
+  minor changes to use new libohana (v1.5) / libfits (v1.4)
+
+mana-1-2:  release 2005.10.20
+  minor changes to use new libohana (v1.5) / libfits (v1.4)
+
+opihi-2-4: release 2005.10.20
+  minor changes to use new libohana (v1.5) / libfits (v1.4)
+  kapa/kii cursor interaction modified (needs kapa v1.3, kii v1.3)
+
+opihi-2-3: release 2005.08.15
+  first tagged release tarball
+  fixed up versions commands
+  added list types for psched 
+  moved to Makefile.Common method.
+
+opihi-2-2:
+  fixed up inclusion of dvo.h (conflicted names)
+  added Graph commands functions
+  added mosaic astrometry support
+  a variety of memory alloc bugfixes
+  added support for ohana_allocate functions
+  added real multicommand support (correct parsing of variables)
+  added some object modelling functions
+  added clipping to fits and stats
+  added gaussdeviate
+  added extrapolation to minterp
+  default of EXTNAME for extension keyword
+  added -cpt to various dvo functions
+  better help in dvo functions
+  fixed up lockfile APIs
+  added fitcolors
+  added lightcurve
+  some cleanups in dvo/photometry.c
+  added typefrac tests is photometry.c
+  added gain test for mrqmin
+  fixed up precedence in math
+  macro-ized binary/unary math ops
+  added modulo, erf
+
+opihi-2-1:
+  major reorg of imfit / added imfit functions
+  cleaned up get_argument calls so -Wall does not give errors
+  moved the 'elixir' command to dvo
+  tag added only to elixir implementations (mana + dvo)
+  dimm - need to coordinate with Olivier and Bernt
+  dvo2, pcontrol, pclient, sched - all under development
+
+2004.12.23
+
+  Moved typename/modename funcs to libohana.  this was motivated by
+  imregister, which would not compile cleanly using the old method.
+
+  Added library dependencies to the makefiles. previously, mana (etc)
+  would not re-build if the library was modified.
+
+  Fixed incorrect exit on non-tty.  this kept perl scripts with the
+  call "|mana" from running.
+
+  Fixed dvomath for strings.  since we moved to dvomath everywhere,
+  this broke complex string conditionals: (($a == hi) && ($b == lo)).
+  I added string types to the math functions, and tests to see if the
+  stack has strings which cannot be resolved as a vector or matrix.  
+
+2004.12.10:
+
+  fixed problem in lib.data/PlotVectors.c and lib.data/graphtools.c
+  that broke plotting for data with very large or small exponents (|x|
+  < 1e-12, |x| > 1e12).  also fixed cmd.data/vstats.c with same
+  problem.
+	
+opihi-2-0:
+  import to CVS 2004.12.03
Index: /branches/sc_branches/pantasks_condor/doc/VectorTypes.txt
===================================================================
--- /branches/sc_branches/pantasks_condor/doc/VectorTypes.txt	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/doc/VectorTypes.txt	(revision 34783)
@@ -0,0 +1,88 @@
+starcontour      : outputs are FLT
+rawstars         : inputs may be FLT or INT; outputs are FLT
+fitcontour       : input must be FLT
+findpeaks        : outputs are FLT
+skycat           : internal vectors only
+simage           : internal vectors only
+showtile         : internal vectors only
+procks           : internal vectors only
+mmextract        : output vector types are determined by selected db field
+mextract         : output vector types are determined by selected db field
+lightcurve       : outputs are FLT
+lcurve           : outputs are FLT
+imstats          : internal vectors only
+imrough          : outputs are FLT
+imextract        : outputs are FLT
+imdense          : internal vectors only
+imdata           : outputs are FLT
+imbox            : internal vectors only
+images           : internal vectors only
+gstar            : outputs are FLT
+fitcolors        : outputs are FLT
+detrend          : outputs are FLT
+compare          : function's outputs are FLT
+cmpread          : outputs are FLT
+cmd              : outputs are FLT
+ccd              : outputs are FLT
+calmextract      : outputs are FLT
+avextract        : output vector types are determined by selected db field
+zplot            : inputs may be FLT or INT
+write_vectors    : inputs may be FLT or INT
+vzload           : inputs may be FLT or INT
+vstat            : inputs may be FLT or INT
+vsmooth          : inputs may be FLT or INT; outputs are FLT
+vroll            : inputs may be FLT or INT
+vpop             : inputs may be FLT or INT
+vmaxwell         : input must be FLT; outputs are FLT
+vload            : inputs may be FLT or INT
+vgrid            : input must be FLT
+vgauss           : input must be FLT; outputs are FLT
+vclip            : inputs may be FLT or INT
+vbin             : inputs may be FLT or INT; outputs are FLT
+uniq             : inputs may be FLT or INT; outputs are set based on input
+ungridify        : outputs are FLT
+svd              : outputs are FLT
+subset           : inputs may be FLT or INT; outputs are set based on input
+sort             : inputs may be FLT or INT
+select           : inputs may be FLT or INT
+read_vectors     : outputs are FLT (someday add a format option to the read statement?)
+periodogram      : input must be FLT; outputs are FLT
+peak             : inputs may be FLT or INT
+mset             : inputs may be FLT or INT
+mget             : outputs are FLT
+medacc           : input must be FLT; outputs are FLT
+lookup           : input must be FLT; outputs are FLT
+interpolate      : input must be FLT; outputs are FLT
+integrate        : inputs may be FLT or INT
+imhist           : outputs are FLT
+imcut            : outputs are FLT
+histogram        : inputs may be FLT or INT, outputs are INT
+gridify          : input must be FLT
+grid             : internal vectors only
+gaussj           : inputs may be FLT or INT; outputs are FLT
+gaussdeviate     : outputs are FLT
+gaussintegral    : outputs are FLT
+fit              : input must be FLT
+fit2d            : input must be FLT
+fft1d            : input must be FLT
+dimenup          : inputs may be FLT or INT
+dimendown        : outputs are FLT
+dbselect         : output vector types are determined by selected db field
+cut              : outputs are FLT
+cumulative       : inputs may be FLT or INT; outputs are FLT
+create           : output vector type defaults to FLT, but may be optionally set to INT with the -int flag
+contour          : outputs are FLT
+concat           : inputs may be FLT or INT; outputs are set based on input
+applyfit         : inputs may be FLT or INT; outputs are FLT
+applyfit2d       : inputs may be FLT or INT; outputs are FLT
+accum            : input must be FLT; outputs are FLT
+spec             : outputs are FLT
+profile          : outputs are FLT
+precess          : inputs may be FLT or INT; outputs are FLT
+multifit         : input must be FLT; outputs are FLT
+czplot           : input must be FLT
+csystem          : inputs may be FLT or INT
+cplot            : input must be FLT
+coords           : input must be FLT
+cgrid            : internal vectors only
+biassub          : outputs are FLT
Index: /branches/sc_branches/pantasks_condor/doc/changes.txt
===================================================================
--- /branches/sc_branches/pantasks_condor/doc/changes.txt	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/doc/changes.txt	(revision 34783)
@@ -0,0 +1,54 @@
+Oct 19  2003 misc/startup.c              . minor changes, DONE
+Oct 19  2003 misc/conversions.c		 . minor changes, DONE
+Oct 19  2003 misc/match_image.c		 . copied to dvo
+Apr 19 17:41 misc/ConfigInit.c		 . minor changes, kept dvo version
+May  6 16:13 misc/ImageOps.c		 . minor changes, DONE
+May 12 09:00 misc/photometry.c           . major changes : DONE
+
+Oct 15  2003 user/badimages.c            . no change
+Oct 15  2003 user/imlist.c		 . no change
+Oct 15  2003 user/precess.c		 . dvo version ok
+Oct 15  2003 user/delete.c		 . dvo version ok
+
+May  6 15:25 user/extract.c		 . mostly deprecated (transfer USNO/HST/etc versions)
+May 10 21:13 user/mextract.c		 . used status version + vectors
+May 11 12:06 user/avextract.c		 . used status version + vectors
+Oct 27  2003 user/imextract.c		 . used status version + vectors
+
+May 12 09:06 user/dmags.c		 . used status version + vectors
+May 12 09:12 user/dmagmeas.c		 . used status version + vectors
+May 12 09:13 user/dmagaves.c		 . used status version + vectors
+May 11 16:18 user/ddmags.c		 . used status version + vectors
+
+May  2 11:14 user/calextract.c		 . used status version + vectors 
+May  3 20:08 user/calmextract.c		 . used status version + vectors
+Oct 19  2003 user/cmd.c			 . fixed (photcodes vs vectors)
+Oct 19  2003 user/ccd.c			 . used status version + vectors
+
+Apr 19 19:48 user/gimages.c		 . minor changes, fixed
+Nov 14  2003 user/gstar.c		 . minor changes, fixed
+May  3 20:49 user/gtypes.c		 . dvo version ok
+May  6 09:37 user/images.c		 . copied to dvo
+May  3 20:49 user/imdata.c		 . dvo version ok
+May  3 20:50 user/imphot.c		 . dvo version ok
+May  3 20:51 user/imrough.c		 . dvo version ok
+May  3 20:51 user/imsearch.c		 . dvo version ok
+Nov  5  2003 user/photcodes.c		 . copied to dvo
+Nov  1  2003 user/pmeasure.c		 . dvo version ok
+Mar  3 11:25 user/showtile.c		 . copied to dvo
+Nov  1  2003 user/subpix.c		 . copied to dvo
+Oct 30  2003 user/dmt.c			 . minor changes & photcodes
+Nov  1  2003 user/lcurve.c		 . minor changes & photcodes
+
+May 18 09:50 user/cgrid.c		 . copied to dvo
+
+Nov  1  2003 user/abszero.c		 . deprecated
+May  3 20:51 user/zeropts.c		 . deprecated
+Nov 13  2003 user/resid.c		 . deprecated
+Nov 13  2003 user/photresid.c		 . deprecated
+
+May  2 11:20 user/cals.c		 . deprecated?
+May  3 20:37 user/ddmagextract.c	 . deprecated?
+May  3 20:11 user/dmagextract.c		 . deprecated?
+Oct 19  2003 user/ccdextract.c		 . deprecated?
+Oct 19  2003 user/cmdextract.c		 . deprecated?
Index: /branches/sc_branches/pantasks_condor/doc/command.txt
===================================================================
--- /branches/sc_branches/pantasks_condor/doc/command.txt	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/doc/command.txt	(revision 34783)
@@ -0,0 +1,24 @@
+
+readline: allocates a line and returns it; empty lines return NULL
+
+expand_vars: (recursively) expand expressions of the form $X
+	     frees the input line, allocates a new one.
+	     returns NULL on error (including NULL input)
+
+expand_vectors: expand expressions of the form $X
+	     frees the input line, allocates a new one.
+	     returns NULL on error (including NULL input)
+
+parse performs math expansion on the line, returning a new line (old one is freed)
+
+
+in newmath:
+
+isolate_elements converts argc,argv to cstack,Ncstack (argc,argv NOT freed)
+convert_to_RPN takes cstack,Nstack and converts to stack,Nstack; input cstack,Ncstack are freed
+check_stack puts pointers to data types on stack, no data allocated
+evaluate_stack 
+
+
+command parses a single input line (line) and expands
+
Index: /branches/sc_branches/pantasks_condor/doc/coords.list
===================================================================
--- /branches/sc_branches/pantasks_condor/doc/coords.list	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/doc/coords.list	(revision 34783)
@@ -0,0 +1,47 @@
+./cmd.astro/cgrid.c - OK (graph only)
+./cmd.astro/cplot.c  - OK (graph only)
+./cmd.astro/czplot.c - OK (graph only)
+./cmd.data/cursor.c - OK (graph only)
+./dvo/catalog.c - OK (graph only)
+./dvo/pcat.c - OK (graph)
+./dvo/pmeasure.c - OK (graph)
+./dvo/procks.c - OK (graph)
+./dvo/showtile.c - OK (graph)
+./cmd.astro/coords.c - fixed
+./dvo/gimages.c - fixed
+./dvo/ImageOps.c - fixed
+./dvo/images.c - fixed
+./dvo/imdense.c - fixed
+./dvo/imextract.c - fixed
+./dvo/imlist.c - fixed
+./dvo/imstats.c - fixed
+./dvo/photometry.c - fixed
+./dvo/subpix.c - fixed
+./dvo/get_regions.c - fixed
+
+./cmd.astro/drizzle.c - needs work
+./cmd.astro/transform.c - needs work
+./cmd.data/load.c - needs work
+./cmd.data/point.c - needs work
+./cmd.data/tvgrid.c - needs work
+./dvo/cmpread.c - needs work (single image)
+./dvo/imbox.c - needs work (single image)
+./dvo/simage.c - needs work (single image)
+
+dvo commands / functions affected by WRP / DIS astrometry
+
+LoadImage - no
+badimages.c - yes : also, use LoadImages
+get_regions.c - ? : OK if we register DIS image
+gimages.c
+ImageOps.c        : need to make association with DIS
+images.c
+imbox.c
+imdense.c
+imextract.c
+imlist.c
+imstats.c
+photometry.c
+showtile.c
+simage.c
+subpix.c
Index: /branches/sc_branches/pantasks_condor/doc/dmagoptions.txt
===================================================================
--- /branches/sc_branches/pantasks_condor/doc/dmagoptions.txt	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/doc/dmagoptions.txt	(revision 34783)
@@ -0,0 +1,143 @@
+
+mextract from value [options]
+avextract from value [options]
+
+imextract value [options]
+
+dmags F1 - F2 : F3 [options]
+dmagmeas F1 - F2 : value [options]
+dmagaves F1 - F2 : value [options]
+
+cmd F1 - F2 : F3 [options]
+ccd F1 - F2 : F3 - F4 [options]
+
+calextract F1 - F2 [options]
+calmextract F1 - F2 [options]
+
+options:
+
+ -photcode value
+  for avextract and dmagaves, -photcode defines the which code is used
+  to calculate certain values (mag, dmag, etc).  objects which lack a 
+  photcode return a logical NaN for that value.
+
+ -magrange min max
+
+this can apply to any photcode
+
+ -imaglim min 
+ -flag value
+ -time start stop
+ -fwhm (value)
+ -type (value)
+
+these naturally apply to the REF and DEP photcodes, but cannot apply to 
+to PRI and SEC.  (note that -type is interpretted a bit differently here
+from below. 
+
+ -chisq max
+ -errorlim max
+ -type (value)
+ -typefrac (type) (frac)
+ -nphot (value)
+ -ncode (value)
+ -fwhmfrac (value) (frac)
+
+these naturally apply to PRI and SEC photcodes, but cannot apply to
+REF.  For DEP, they could apply to the equivalent photcode.  eg
+
+  ccd I - 2MASS_J : 2MASS_J - 2MASS_K -chisq 2.0
+
+  this will exclude on chisq for the I value, and ignore the limit
+  for the 2MASS values
+
+  ccd I - CFH12K.R.00 : 2MASS_J - 2MASS_K -chisq 2.0
+
+  this will exclude on chisq for the I value, and ignore the limit
+  for the 2MASS values, and exclude on R (equiv to CFH12K.R.00).
+
+  ccd I - CFH12K.R.00 : 2MASS_J - 2MASS_K -time start stop
+
+  by default, this will filter the CFH12K.R.00 and 2MASS measurements 
+  based on
+
+we need to be able to restrict the selections to a subset of the
+extraction values.  We can use a flag like: -apply YYNN:
+
+  ccd I - CFH12K.R.00 : 2MASS_J - 2MASS_K -time start stop -apply NYNN
+
+to restrict the application to only the CFH12K.R.00 entry.
+
+measure values:
+  ra
+  dec
+  mag
+  dmag
+  airmass
+  exptime
+  photcode
+  time
+  dR
+  dD
+  fwhm
+  dophot
+  FLAGS
+  XCCD
+  YCCD
+  XMOSAIC
+  YMOSAIC
+
+average values:
+  ra
+  dec
+  dmag
+  Nmeas
+  Nmiss
+  Xp
+  Xm
+  flag
+  type
+  typefrac
+  Nphot
+  Ncode
+
+
+
+- ExtractDMag
+  - ExtractMeasuresDMag
+    - TestAverage
+  - ExtractMagnitudes
+    - ExtractAverages
+    - ExtractMeasures
+
+mextract
+ - ExtractMeasures
+avextract
+ - ExtractAverages
+dmags
+ - ExtractDMag
+ - ExtractMagnitudes
+ddmags
+ - ExtractDMag
+ - ExtractDMag
+dmagmeas
+ - ExtractDMag
+ - ExtractMeasures
+dmagaves
+ - ExtractDMag
+ - ExtractAverages
+cmd
+ - ExtractDMag
+ - ExtractMagnitudes
+ccd
+ - ExtractDMag
+calextract
+ - ExtractAverages
+calmextract
+ - ExtractMeasures
+
+
+cmd B - V : B
+dmagmeas B - V : ra
+dmagmeas B - V : time
+
Index: /branches/sc_branches/pantasks_condor/doc/dvo-2.txt
===================================================================
--- /branches/sc_branches/pantasks_condor/doc/dvo-2.txt	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/doc/dvo-2.txt	(revision 34783)
@@ -0,0 +1,28 @@
+
+Region APIs
+
+Region table is a FITS table
+
+- load region data from FITS table
+- save region data to FITS table
+
+/* find region which overlaps c at given depth (-1 : max depth) */
+SkyRegion *SkyFindPoint (SkyRegion *db, SkyCoord c, int depth);
+
+/* find regions at all levels which overlap c */
+SkyRegion **SkyFindLevels (SkyRegion *db, SkyCoord c, int *Nregion);
+
+/* find regions contained within rectangular region  c1 - c2 */
+SkyRegion **SkyFindArea (SkyRegion *db, SkyCoord c1, SkyCoord c2, *nlist);
+
+- I have created table generation functions which construct a
+  collection of table entries starting with the full sky and iterating
+  down N levels.  The function also fills out the names at the
+  appropriate levels.  This is currently ok up to level 6, but runs
+  out of memory at level 7.  This should not be the case, so I suspect
+  I'm not free'ing some memory.  
+
+- I need to create a function to take the GSC table list and construct
+  a set of SkyRegions that match the layout, starting with the full
+  sky and iterating down to and past the GSC tables.  
+
Index: /branches/sc_branches/pantasks_condor/doc/dvo-apis.txt
===================================================================
--- /branches/sc_branches/pantasks_condor/doc/dvo-apis.txt	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/doc/dvo-apis.txt	(revision 34783)
@@ -0,0 +1,89 @@
+
+skyregion APIs:
+
+// a single patch on the sky
+typedef struct {
+  float            Rmin;                 // 
+  float            Rmax;                 // 
+  float            Dmin;                 // 
+  float            Dmax;                 // 
+  int              childS;               // sequence number in full table of first child
+  int              childE;               // sequence number in full table of last child + 1
+  int              parent;               // sequence number in full table of parent
+  int              index;                // sequence number in full table of this entry
+  char             depth;                // depth of this entry
+  char             child;                // does this entry have children?
+  char             table;                // does this entry have a table?
+  char             name[21];             // name / filename
+} SkyRegion;
+
+/* SkyRegion : better implementation than GSCRegion */
+typedef struct {
+  int Nregions;
+  char **filename;
+  SkyRegion *regions;
+} SkyTable;
+
+typedef struct {
+  int Nregions;
+  char **filename;
+  SkyRegion **regions;
+} SkyList;
+
+SkyList *SkyRegionByPoint (SkyTable *table, int depth, double ra, double dec);
+SkyList *SkyListByName (SkyTable *table, char *name);
+SkyList *SkyListByPoint (SkyTable *table, double ra, double dec);
+SkyList *SkyListByRadius (SkyTable *table, int depth, double RA, double DEC, double radius);
+SkyList *SkyListByPatch (SkyTable *table, int depth, SkyRegion *patch);
+SkyList *SkyListByImage (SkyTable *table, int depth, Image *image);
+SkyList *SkyListByBounds (SkyTable *table, int depth, double Rmin, double Rmax, double Dmin, double Dmax);
+SkyList *SkyListChildrenByBounds (SkyTable *table, int No, int depth, double Rmin, double Rmax, double Dmin, double Dmax);
+void SetImageCorners (double *X, double *Y, Image *image);
+int SkyTableSetDepth (SkyTable *sky, int depth);
+int SkyListFree (SkyList *list, int ELEMENTS);
+int SkyTableFree (SkyTable *table);
+int SkyListMerge (SkyList **outlist, SkyList *newlist);
+
+--- db query sky region ---
+
+// options for selecting the ra,dec limits of the db selections
+typedef struct {
+  char *name;
+  char *list;
+  int useDisplay;
+  int useSkyregion;
+} SkyRegionSelection;
+
+int get_skyregion (double *Rs, double *Re, double *Ds, double *De);
+int set_skyregion (double Rs, double Re, double Ds, double De);
+
+void FreeSkyRegionSelection (SkyRegionSelection *selection);
+SkyRegionSelection *SetRegionSelection (int *argc, char **argv);
+
+
+--- photometry.c api ---
+
+* int GetTimeSelection (time_t *tz, time_t *te) {
+int GetPhotcodeInfo (char *string, PhotCode **Code, int *Mode) {
+int SetSelectionParam (int param) {
+int GetSelectionParam () {
+int GetMeasureParam (char *parname) {
+int GetAverageParam (char *parname) {
+int TestPhotSelections (PhotCode **code, int *mode, int param) {
+void GetAverageParamHelp () {
+int InitPhotcodes () {
+int ListPhotSelections () {
+int SetPhotSelections (int *argc, char **argv, int Nparams) {
+double *ExtractMeasures (PhotCode *code, int mode, Average *average, SecFilt *secfilt, Measure *measure, int *nlist, int param) {
+double ExtractAverages (PhotCode *code, int mode, Average *average, SecFilt *secfilt, Measure *measure, int param) {
+double DetermineTypefrac (Average *average, Measure *measure, PhotCode *code) {
+int DetermineTypeCode (Average *average, Measure *measure, int code) {
+int GetMeasureTypeCode (Measure *measure) {
+int Quality (Measure *measure, int IsDophot) {
+int TestAverage (PhotCode *code, Average *average, SecFilt *secfilt, Measure *measure) {
+double *ExtractMagnitudes (PhotCode *code, int mode, Average *average, SecFilt *secfilt, Measure *measure, int *n) {
+double *ExtractDMag (PhotCode **code, int *mode, Average *average, SecFilt *secfilt, Measure *measure, int *nlist) {
+double *ExtractMeasuresDMag (PhotCode **code, int *mode, Average *average, SecFilt *secfilt, Measure *measure, int *nlist) {
+double *ExtractByDMag (PhotCode **code, int *mode, Average *average, SecFilt *secfilt, Measure *measure, int *nlist, int param) {
+double *ExtractMeasuresByDMag (PhotCode **code, int *mode, int use_first, Average *average, SecFilt *secfilt, Measure *measure, int *nlist, int param) {
+double GetMeasure (int param, Average *average, Measure *measure, double mag) {
Index: /branches/sc_branches/pantasks_condor/doc/dvo.html
===================================================================
--- /branches/sc_branches/pantasks_condor/doc/dvo.html	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/doc/dvo.html	(revision 34783)
@@ -0,0 +1,288 @@
+
+<h2> PA data requirements </h2>
+
+Counting the total size of the PA database for the PA survey, with
+roughly 20 pointings per location and 30 second exposures.  The depth
+for PA survey in P2 @ 20 \sigma is 21.3 (r).  Each detection uses 100
+bytes (<b>we need to flesh this out with more realistic numbers from
+our table of parameters</b>).  The access speed to a RAID disk is 100
+MB/sec.  The number density of stars in i' in the plane may be higher,
+but we reach saturation at ~1.4e6 detections per deg^2 (one object per
+100 pixels).  This is a factor of 5 higher than the raw numbers.
+
+<h3> stellar counts for PS-1 </h3>
+latitude                 90          30         0
+density (deg^{-2})      5e3         3e4       3e5
+N_det (FPA^{-1})        4e4         2e5       2e6
+Sum N_det (FPA^{-1})    8e5         4e6       4e7
+Sum Nbyte (FPA^{-1})  80 MB      400 MB      4 GB
+Nsec for 1 channel      0.8           4        40
+Nchannel for 2 sec        1           2        20
+
+The total number of bytes for the PA survey for P2 detections is ~12
+TB (30000 x 400MB).  The density of detections per FPA from P4 delta
+is roughly the same as P2 at 0 deg (2e5 vs 3e5 det deg^{-2}).  The
+total number of bytes needed to store the P4 Delta detections from all
+of PS-1 is 
+
+Fields in the plane will take longer to process
+
+<h2> addstar interactions for a distributed db </h2>
+
+addstar.client <---> addstar.daemon
+
+The sky is divided into hierarchical regions, each broken into smaller
+subregions.  Both the image and object tables are divided into
+subtables by region on the sky.  The density of image tables is
+smaller than the density of object tables.  A top-level table defines
+the distribution of the lower-level tables by defining the hierarchy
+of regions and subregions.  Each entry in this table contains:
+
+region ID  - an identifier for the subregion
+RAs, RAe   - RA range of region
+DECs, DECe - DEC range of region
+parent ID  - ID of the region which contains this region (NULL for allsky)
+Nchild     - number of children
+offset     - starting entry of first child
+images     - is this region used for images? (if FALSE, down one layer)
+objects    - is this region used for objects? (if FALSE, down one layer)
+
+use the machine name / file name as the indicator in the images and
+objects entries?
+
+The table defines the relationship between the subregions and provides
+a mechanism to find the subregions appropriate to a given sky
+location.  It also specified for a given depth if that depth is used
+for the image and/or the object table.  The image tables contain
+images whose reference coordinate is located in the given region.
+Other regions which the image overlaps contain entries in the image
+reference table which specify the primary image table in which they
+are stored.  
+
+The details of the sky region definitions do not matter for the
+structure of the region table.  The data structures can handle any
+arrangement of tables which meets the basic requirements that the
+boundaries be lines of constant RA & DEC and that each level defines
+all regions which cover the entire sky.  One implementation is as
+follows and is easy to generate:  
+
+Start with the complete sky as a single region (RAs = 0, RAe = 360;
+DECs = -90, DECe = 90).  To create a new region, always subdivide the
+region with the largest area (roughly (RAe - RAs)*(DECe -
+DECs)*cos((DECs + DECe)/2)).  To subdivide a region, define two
+possible subdivisions: RA = 0.5*(RAs + RAe) (which becomes RAs and RAe
+for the two new regions) or DEC = 0.5*(DECs + DECe).  Determine the
+length of these dividing lines (S1 = (DECe - DECs); S2 = (RAe - RAs) /
+cos (DEC)).  Choose the shorter of these two lines, and subdivide the
+region on that basis.  
+
+The number of subregions increases by a factor of 8 for
+each new level.  Approximate table size: Need at least 200,000
+lowest-level regions, leads to 2^18 regions (256k) in lowest-level.
+Total table size is roughly 300k rows.  Each row is roughly 32 bytes,
+for a table size of 10MB.
+
+There is one addstar.daemon per DVO server.  The addstar.daemons are
+responsible for serving the objects and images from the tables they
+contain.  
+
+addstar.client steps to load a new set of objects:
+- load region table (defined in config db)
+- find overlapping object regions
+- find overlapping image regions
+- identify primary image region
+- send image data to node for primary image region 
+- send image reference to nodes for secondary image regions
+- send detection data (with image ID) to corresponding object region nodes
+
+addstar.daemon responsibilities:
+- receive image data -> add to primary image table
+- receive detection data -> add to local region file
+- request objects in overlap regions from neighbor daemons
+- send detections to neighbor daemons if associated with object over
+  border
+- construct / update objects on basis of detections
+
+in DVO 1.0, addstar updates objects on every upload of the
+detections.  In fact, the construction of the objects need not be
+performed every time detections are added.  Do we need to construct
+objects for all single detections?  The current process is:
+
+- get list of new detections (from incoming image)
+- match each detection with each object
+- update object parameters
+
+another option:
+
+step 1:
+ - get list of new detections
+ - add to new.detection table
+
+step 2:
+ - match detections to object table
+ - update object parameters
+
+third option
+step 1:
+ - add new detections to new table
+
+step 2:
+ - compare detections to orphans 
+ - if found, promote to object
+ - if not, test against objects
+ - if found update object
+ - if not add to orphans
+
+<h2> DVO image organization </h2>
+
+DVO 1.0 uses a single image table to store all image data.  In this
+table, each image is a chip; that is, each entry represents a single
+astrometric system.  In principal, there is no reason this could not
+be an entire mosaic or (in the case of Pan-STARRS) an individual
+Cell. It is usually necessary to be able to define the relationship
+between the different detector / focal plane / etc coordinate
+systems.  If the data entity is a full mosaic, then it will be
+necessary to look up the chip (and cell) transformations.  If the data
+entity is the cell, the reverse conversions will be needed.  
+
+The image table should be distributed to multiple files to speed up
+the access.  One option is to do this by coordinate region.  In the
+plan for DVO 2.0, there will be a table of the region hierarchy for
+the object tables, and the image tables could be distibuted similarly,
+though with a different density.  In PS-1, for example, there will be
+around 250,000 images (FPAs).  If we store OTAs (chips) as the data
+entity in the image table, then we will have in the vicinity of 16M
+rows.  If each contains 256 bytes, the total data volume in the image
+table will be about 4 GB.  If we want to have typical access times to
+any image of 1 second, and we need to scan through the entire table to
+get to the image, then we will need to distribute the data across 40
+tables (note that they need not be distributed by machine).  If each
+table represents a region on the sky, this translates to 1000 - 500
+square degrees per table.  
+
+Some additional aspects are interesting.  First, the
+spatially-distributed tables correspond to specific regions on the
+sky, likely to be bounded by lines of constant RA and DEC, or
+something equivalent.  However, an image cannot be guaranteed to land
+in only one of these regions.  To mitigate this, each image (chip or
+FPA or whatever the data unit is) should have a single defined
+position with which the general location is identified and the choice
+of region is made.  In addition, there should be a table associated
+with each region which defines images in other tables (regions) which
+overlap the given region.  In general, these overlap tables will
+contain only a small fraction of the image entries (regions are much
+large than images) and they need only identify the image ID and the
+corresponding table.
+
+An additional accelerator table would include all images, their
+reference time and their reference coordinate, sorted by time, with an
+index for the name.
+
+The end result is three types of tables: images.db, overlaps.db,
+imagetimes.db.  images.db contains the bulk of the information (256
+byte / row).  overlaps.db contains only the image ID and the the RA
+and DEC of the reference position (not the actual table because that
+may change). imagetimes.db contains only image ID (8 byte? 16 byte?),
+time, RA, DEC, and and index.  This is a total of about 40 bytes per
+image, for a total of roughly 700 MB at the end of PS-1.
+
+<h3> Examples queries </h3>
+
+<h4> find a single, specific image by ID </h4>
+<ul>
+<li> open imagetimes.db
+<li> load block marker (ID,block; every 10000 blocks)
+<li> find appropriate block
+<li> load block
+<li> find appropriate image entry
+<li> determine appropriate image.db table (region)
+<li> open image.db 
+<li> load block marker (ra,block; every 10000 blocks)
+<li> find appropriate block
+<li> load block
+<li> find appropriate image entry
+</ul>
+
+<h4> find a single, specific image by ID </h4>
+<ul>
+<li> open imagetimes.db
+<li> load block marker (ID,block; every 10000 blocks)
+<li> find appropriate block
+<li> load block
+<li> find appropriate image entry
+<li> determine appropriate image.db table (region)
+<li> open image.db 
+<li> load block marker (ra,block; every 10000 blocks)
+<li> find appropriate block
+<li> load block
+<li> find appropriate image entry
+</ul>
+
+<h3> dvo / object catalog scaling to massive collections </h3>
+
+DVO divides the sky into tables which represent specific areas on the
+sky.  Currently, these are pre-defined to match the HST GSC regions,
+roughly 1 - 4 square degree patches with fixed RA and DEC boundaries.
+A future extension will provide a mechanism to increaese or decrease
+the table density on the fly.
+
+The likely data rate under Pan-STARRS PS-1 is in the vicinity of 5 x
+10<sup>5</sup> stars per square degree.  The density is likely to vary
+by a factor of 30 for the bulk of the sky.  The Pan-STARRS camera
+consists of 1.44 x 10<sup>8</sup> pixels.  The confusion limit will
+likely be reached when each object encompases 25 pixels, resulting in
+a saturation of 6 x 10<sup>6</sup> stars per square degree.  The
+Pan-STARRS camera covers a total of 7.45 square degrees, and the
+expected exposure rate is roughly 1 per minute on average.  This
+amounts to a total of 3000 square degrees covered per night of
+observation, or a total of 1.5 x 10<sup>9</sup>detections per night,
+or a total of 5 x 10<sup>11</sup> over the course of one year.  Given
+this total number of detections, and the total sky coverage of 30,000
+square degrees, the average number of detections per square degree
+will be in the vicinity of 1.7 x 10<sup>7</sup>
+
+There are two main limitations to the DVO object storage model.
+First, the large data volume translates to a finite time to read the
+data from the disk.  Second, the large number of objects limits the
+rate at which new detections may be associated with the existing
+objects.  The first of these has generally proven to be the more
+significant limitation in applications to date.  We can easily
+calculate the time needed to load the data relevant to a random image
+pointing, as well as the maximum time based on the range of data
+volumes.  A necessary assumption in this calculation is the number of
+bytes used per detection.  We generously assume 100 bytes per
+detection, 3 times the existing data structures used by DVO.  
+
+Based on the numbers above, the total data volume represented by a
+year of observations is 50 TB, assuming the detection component
+dominates the total.  The average camera footprint translates to
+roughly 13 GB worth of detections.  Since each footprint is performed
+once per minute, it will be necessary to perform the read and write in
+each 10 seconds.  This in turn corresponds to 1.3 GB per second for
+the read and write portions.  With typical local hard-drive access
+speeds for RAIDs of 100 MB/sec, the data will need to be distributed
+across more than the machines in order to achieve these average
+rates.  
+
+The existing DVO system expects only one object table to be open at a
+time.  In addition, access to the tables is not controlled through
+separate machines.  Rather, a single collection of tables is expected
+and all queries are performed independently.  In order to handle the
+above load with the DVO software, the following changes will be
+needed:
+
+<ul>
+<li> tables must be distributed across multiple machines.  This is
+mandatory regardless of the underlying database engine based on the
+data I/O analysis above.
+
+<li> the 'addstar' front end needs to send the data for each table to
+the database backends on each of the corresponding machines.  
+
+<li> the addstar update for a single image can be done in series as is
+currently done.
+
+<li> write locking should be done on the tables rather than on the
+database as a whole (currently using the image table). 
+</ul>
+
Index: /branches/sc_branches/pantasks_condor/doc/dvo.parallel.txt
===================================================================
--- /branches/sc_branches/pantasks_condor/doc/dvo.parallel.txt	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/doc/dvo.parallel.txt	(revision 34783)
@@ -0,0 +1,12 @@
+
+2012.02.17
+
+ To parallelize dvo queries, like mextract, I need a version of dvo
+ that takes acts as the remote client.  It should take a single dvo
+ command on the command line and operate on it on the remote host.
+ eg:
+
+dvo_client mextract (reset of line)
+
+it would dump out a result table somewhere (what is loaded?)
+
Index: /branches/sc_branches/pantasks_condor/doc/dvo.todo.20081204.txt
===================================================================
--- /branches/sc_branches/pantasks_condor/doc/dvo.todo.20081204.txt	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/doc/dvo.todo.20081204.txt	(revision 34783)
@@ -0,0 +1,54 @@
+
+DVO : Outstanding Issues (20081204) TBR before survey start
+
+* fundamentals : 
+  * Commit to and Require C99?  
+  * Integer scalars and vectors to s64?
+  * force float for / for scalars (and vectors?)
+
+* Indices / IDs
+
+  Do we have all of the necessary IDs defined?
+
+  * Image IDs : we need to be able to link back to the IPP tables, but
+    also guarantee an internal unique ID even if images from multiple
+    databases / other sources are injects.  Note that the full 3pi
+    survey will consist of ~ 6000 * 60 * 60 chips = 1.3e7 images.
+
+    * imageID  : Internal image ID (32bit -> 4G rows)
+    * externID : chip_id, warp_id, stack_id, diff_id
+    * sourceID : database + table
+
+  * Object IDs : Our internal ID needs to be unique across all
+    objects.  We enforce this by generating the object ID when the
+    object is created with the objID seq number from the table in
+    which is was created + catID for the table.  We also need to
+    generate and track the external (PSPS) ID (64bit).
+
+    * objID : ID for object in a table
+    * catID : ID of the source table 
+    * extIDlo : high bits for external ID
+    * extIDhi : low bits for external ID
+      (or commit to a 64bit version)
+
+  * Detection IDs : internal only; these are generated as the seq
+    number on the image + the IPP image ID.
+
+* Add PSPS obj ID code
+
+* Orphans / Objects: PSPS considers detections to be orphans until
+  they are correlated with enough (2?) other detections.  IPP needs to
+  send the detections with their IPP objID, but only supply a list of
+  the new PSPS 'objects' when the object is no longer considered an
+  orphan.  The detection-to-object correlation is performed during the
+  addstar process.  IPP needs to generate a table of new non-orphaned
+  objects at that time for distribution.  How do we complete the
+  hand-shake?  
+
+* Schema updates
+
+* External Image IDs :  
+
+  * IMAGEID in header is the ID from chip_imfile, warp_skyfile, etc.
+  * SOURCEID in header maps to the database + stages (chip, warp, etc)
+
Index: /branches/sc_branches/pantasks_condor/doc/image-graph-overlay.txt
===================================================================
--- /branches/sc_branches/pantasks_condor/doc/image-graph-overlay.txt	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/doc/image-graph-overlay.txt	(revision 34783)
@@ -0,0 +1,103 @@
+
+the kapa display tool has the ability to show both images (2D grid
+with grey or color scale) and graphs (plot of points and lines).
+
+normally, the kapa graph and image displays are used independently
+(either in different windows or in different sections of the same
+window).  
+
+there are situations in which it is useful to combine these two tools
+and show graphs overlayed on images.
+
+there are several mana / dvo functions which enable graph/image
+overlays, and have use in different contexts.
+
+* basic points
+
+  here are some aspects of image/graph overlays to keep in mind.
+
+  * the image and graph are not dynamically linked.  althought it is
+    possible to set the graph to have limits that correspond to the
+    image in some way (see below), this relationship is not maintained
+    dynamically.  if you change the view of the image (eg, by
+    clicking, zooming, or recentering), the graph does not change
+    without re-issuing some of the commands given below.
+
+  * the boundaries of the image and the graph are dependent on whether
+    or not they are both present and on whether or not the zoom box
+    tools are displayed.  the status of the zoom tools is modifed with
+    the 'section' command as follows:
+
+    section (name) -imtool (option)
+
+    where (option) may be one of -x, +x, -y, +y or none
+
+    for example, use: section default -imtool none to remove the zoom
+    tool from the default section of the current display.
+
+* set graph limits based on the current image
+
+  the simplest way to have the image and the graph interact is to
+  display an image and graph in the same window, and to set the graph
+  limits based on the view of the displayed image:
+
+  # create a contour and overlay in pixel coordinates
+  contour image xc yc 100
+  tv image -50 200
+  box
+  limits -image
+  plot xc yc -pt 100 -c red
+
+* image with WCS overlay on display 'region'
+
+  if you have an image with WCS elements, then it is possible to
+  define the sky region to correspond to the displayed portion of the
+  image.
+
+  # create a contour and convert to celestial coords based on the image:
+  contour image xc yc 100
+  set rc = xc
+  set dc = yc
+  coords image -p rc dc
+
+  # display the image, set the sky region, plot the contour
+  tv image -50 200
+  box -ticks 0000 -labels 0000
+  region -image
+  cplot rc dc -pt 100 -c red
+
+* setting the display size based on the image size
+
+  sometimes it is convenient to ensure the whole image will fit on a
+  graph.  it is possible to use the 'resize -by-image' and section
+  -image options to control this behavior:
+
+  * resize -by-image adjusts the display size so that the image in the
+    currently active section completely fills that section (not that
+    this command assumes the image is centered, but does not center
+    it.
+
+    for example, the following commands display a sample 1000x1000
+    image in a section:
+
+    section view 0.2 0.2 0.6 0.6
+    section view -imtool none
+    box -ticks 0000 -labels 0000
+    tv image -50 200
+    center 500 500 -2
+    resize -by-image
+    
+  * section -image gives something like the opposite behavior.  It
+    defines the size of the section to completely contain the image,
+    with the lower-left corner defined.  Note: this may result in a
+    section which extends beyond the edge of the window.
+
+    resize 800 800
+    section view -imtool none
+    tv image -50 200
+    center 500 500 -2
+    section view -image 0.1 0.1
+
+NOTE: in the examples above, the 'box' command is currently necessary
+to ensure a graph box is defined.  Otherwise, the coordinate systems
+may be inconsistent.
Index: /branches/sc_branches/pantasks_condor/doc/ippc_commandserver.c
===================================================================
--- /branches/sc_branches/pantasks_condor/doc/ippc_commandserver.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/doc/ippc_commandserver.c	(revision 34783)
@@ -0,0 +1,166 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/file.h>
+#include <unistd.h>
+#include <string.h>
+#include <libxml/xmlmemory.h>
+#include <libxml/parser.h>
+#include "ippc.h"
+#include "ippc_messages.h"
+#include <arpa/inet.h>
+#include <netinet/in.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/wait.h>
+#include <signal.h>
+#include "ippc_command.h"
+
+#define MAX_KIDS    256
+#define WAIT_FOR_KIDS    5
+
+/** \file ippc_commandserver.c
+ * \brief This is basically a big fat security problem.
+ */
+
+/* global variables */
+int    kids = 0;
+
+void  decrement_kids(int sig_num);
+
+int main(int argc, char *argv[])
+{
+  int    server_sockfd, client_sockfd, server_len, client_len;
+  int    result;
+  char    buffer[STRLEN], *portString;
+  uint16_t  netSafe;
+  uint32_t  auth_token;
+  unsigned short  status;
+  struct  sockaddr_in server_address;
+  struct  sockaddr_in client_address;
+
+  if (IPPC_DEFAULT_VERBOSITY > 1) {
+    gprint (GP_ERR, "%s : THIS PROGRAM _IS_ A REMOTE EXPLOIT!!!\n", argv[0]);
+  }
+
+  if (!(portString = getenv("IPPC_COMMANDSERVER_PORT"))) {
+    portString = "1035";
+  }
+
+  server_sockfd = socket(AF_INET, SOCK_STREAM, 0);
+        if (server_sockfd == 0) {
+                perror("socket");
+                exit(EXIT_FAILURE);
+        }
+
+  server_address.sin_family = AF_INET;
+  server_address.sin_addr.s_addr = htonl(INADDR_ANY);
+  server_address.sin_port = htons(atoi(portString));
+  server_len = sizeof(server_address);
+
+  result = bind(server_sockfd, (struct sockaddr *)&server_address, server_len); 
+        if(result == -1) {
+                perror("bind");
+                exit(EXIT_FAILURE);
+        }
+
+  result = listen(server_sockfd, 10);
+        if(result == -1) {
+                perror("listen");
+                exit(EXIT_FAILURE);
+        }
+  
+  /* decrement the kiddie count after a child exits */
+  signal(SIGCHLD, decrement_kids);
+
+  /* flush dem pesky chars outa my buffer */
+  memset(buffer, 0, sizeof(buffer));
+
+  while(1) {
+    if (IPPC_DEFAULT_VERBOSITY > 1) {
+      gprint (GP_ERR, "%s : pid: %d - waiting for connection...\n", argv[0], getpid());
+    }
+
+    if (kids < MAX_KIDS) {
+      client_len = sizeof(client_address);
+      client_sockfd = accept(server_sockfd, (struct sockaddr *)&client_address, &client_len);
+    } else {
+      if (IPPC_DEFAULT_VERBOSITY > 1) {
+        gprint (GP_ERR, "%s : pid: %d - kids: %d - child limit already reached, sleeping %ds...\n",
+          argv[0], getpid(), kids, WAIT_FOR_KIDS);
+      }
+      sleep(WAIT_FOR_KIDS);
+      continue;
+    }
+
+    /* increment kiddie count before forking */
+    kids++;
+
+    if (fork() == 0) {
+      if (IPPC_DEFAULT_VERBOSITY > 1) {
+        gprint (GP_ERR, "%s : pid: %d - new kid number: %d\n", argv[0], getpid(), kids);
+      }
+
+      /* a non-zero result doesn't always mean an error occured
+       * I need to find out more about how to handle error checking here
+       */
+
+      /* receive and check authentication key */
+      recv(client_sockfd, &auth_token, sizeof(auth_token), 0);
+
+      if (ntohl(auth_token) != COMMAND_KEY) {
+        if (IPPC_DEFAULT_VERBOSITY > 1) {
+          gprint (GP_ERR, "%s : pid: %d - invalid authentication key\n", argv[0], getpid());
+        }
+
+        netSafe = htons(-1);
+        send(client_sockfd, &netSafe, sizeof(status), 0);
+
+        close(client_sockfd);
+        exit(EXIT_FAILURE);
+      }
+
+      /* receive command */
+      recv(client_sockfd, buffer, STRLEN, 0);
+  
+      if (IPPC_DEFAULT_VERBOSITY > 1) {
+        gprint (GP_ERR, "%s : pid: %d - recieved: %s\n", argv[0], getpid(), buffer);
+      }
+  
+      /* oh man is this dangerous */
+      status = system(buffer);
+      if (status != 0) {
+        if (IPPC_DEFAULT_VERBOSITY > 1) {
+          gprint (GP_ERR, "%s : pid: %d - command failed - code: %d\n", argv[0], getpid(), status);
+        }
+        netSafe = htons(status);
+        send(client_sockfd, &netSafe, sizeof(netSafe), 0);
+      } else {
+        if (IPPC_DEFAULT_VERBOSITY > 1) {
+          gprint (GP_ERR, "%s : pid: %d - command successful\n", argv[0], getpid());
+        }
+        netSafe = htons(status);
+        send(client_sockfd, &netSafe, sizeof(status), 0);
+      }
+
+      status = shutdown(client_sockfd, SHUT_RDWR);
+      if (status) {
+        perror("shutdown");
+        exit(EXIT_FAILURE);
+      }
+  
+      close(client_sockfd);
+      exit(EXIT_SUCCESS);
+    } else {
+      close(client_sockfd);
+    }
+  }
+}
+
+void  decrement_kids(int sig_num)
+{
+  /* sysv requires the handler to be reinstalled */
+  signal(SIGCHLD, decrement_kids);
+
+  /* reap child exit value */
+  while (waitpid(-1, NULL, WNOHANG) > 0) { kids--; }
+}
Index: /branches/sc_branches/pantasks_condor/doc/libs.txt
===================================================================
--- /branches/sc_branches/pantasks_condor/doc/libs.txt	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/doc/libs.txt	(revision 34783)
@@ -0,0 +1,17 @@
+
+I have divided the (non-user) functions into classes. 
+
+- conversions can be placed in a single library
+
+- graph and image (tv) functions can be placed in a library
+
+- the opihi shell functions can go into a third library
+
+- the dvo-related functions can go into a fourth
+
+- i need to clean up the mana and dimm misc functions, and put them in libraries 
+
+- the user functions also need to go into some libraries.
+
+- how do I have the library add its functions to the command list?
+
Index: /branches/sc_branches/pantasks_condor/doc/mana.updates.txt
===================================================================
--- /branches/sc_branches/pantasks_condor/doc/mana.updates.txt	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/doc/mana.updates.txt	(revision 34783)
@@ -0,0 +1,135 @@
+
+mana functions which existed in DVO from status:
+
+applyfit2d.c         : OK - vector changes only
+applyfit.c	     : OK - vector changes only
+box.c		     : OK - no diff
+center.c	     : OK - no diff
+clear.c		     : OK - no diff
+concat.c	     : OK - vector changes only
+contour.c	     : mana version writes to Ximage, dvo version writes to vectors.  mana/contour -> tvcontour.c
+create.c	     : OK - buffer changes only
+cursor.c	     : OK - buffer & minor changes only
+delete.c	     : OK - buffer & minor changes only
+device.c	     : OK - no diff
+extract.c	     : mana extract operates on buffers / status extract is db operation
+file.c		     : OK - no diff
+fit2d.c		     : OK - vector changes only
+fit.c		     : OK - added weights and parameter errors from mana version
+gaussj.c	     : OK - buffer changes only
+grid.c		     : mana version write to Ximage, dvo version writes to Xgraph. mana/grid -> tvgrid
+histogram.c	     : OK - vector changes only
+interpolate.c	     : OK - vector changes only
+jpeg.c		     : OK - no diff
+labels.c	     : OK - no diff
+limits.c	     : OK - vector & minor changes only
+list_buffers.c	     : OK - buffer changes only
+list_vectors.c	     : OK - vector & minor changes only
+mcreate.c	     : OK - buffer changes only
+mget.c		     : OK - buffer changes only
+mset.c		     : OK - buffer changes only
+ps.c		     : OK - no diff
+rd.c		     : OK - buffer changes only
+read_vectors.c	     : OK - included reading vectors from fits tables
+region.c	     : OK - no diff
+resize.c	     : OK - no diff
+section.c	     : OK - no diff
+set.c		     : OK - mana version out of date
+sprintf.c	     : OK - no diff
+stats.c		     : OK - included threshold comparison for IgnoreValue and min == max test
+style.c		     : OK - mana version out of date
+subset.c	     : OK - mana version out of date
+textline.c	     : OK - no diff
+tv.c		     : OK - buffer changes only
+uniq.c		     : OK - vector changes only
+vcontour.c	     : mana/vcontour equiv to dvo/contour
+vstat.c		     : OK - vector changes only
+wd.c		     : OK - buffer changes only
+write_vectors.c	     : OK - vector changes only
+zap.c		     : OK - buffer changes only
+zplot.c		     : OK - vector changes only
+
+mana functions not previously in DVO from status:
+
+accum.c			: fix vectors
+adc.c			: keep - specialized
+biassub.c		: fix buffers and vectors
+clip.c			: fix buffers and vectors
+cmpload.c		: keep
+coords.c		: fix buffers and vectors
+cut.c			: fix buffers and vectors
+cval.c			: ? superceeded by 'star'?
+demux.c			: keep - specialized
+dimendown.c		: fix buffers and vectors
+dimenup.c		: fix buffers and vectors
+drawline.c		: keep - unused
+drizzle.c		: fix buffers and vectors
+erase.c			: keep
+fft1d.c			: fix buffers and vectors
+fft2d.c			: fix buffers and vectors
+flux.c			: fix buffers and vectors
+gauss.c			: fix buffers and vectors
+gaussfit.c		: ? to correct?
+getchr.c		: keep
+getvel.c		: 
+imfit.c			: 
+imhist.c		: 
+integrate.c		: 
+kern.c			: 
+keyword.c		: 
+line.c			: 
+list_header.c		: 
+load.c			: 
+medacc.c		: 
+medianmap.c		: 
+memory.c		: 
+minterpolate.c		: 
+mkgauss.c		: 
+multifit.c		: 
+nextract.c		: 
+objload.c		: 
+outline2.c		: ??
+outline.c		: ??
+pause.c			: 
+peak.c			: 
+point.c			: 
+polar.c			: 
+profile.c		: 
+rdseg.c			: 
+rebin.c			: 
+roll.c			: 
+rotate.c		: 
+rotcurve.c		: 
+save.c			: 
+scale.c			: 
+select.c		: 
+sexigesimal.c		: 
+shape.c			: 
+shift.c			: 
+simsignal.c		: 
+sort_vectors.c		: 
+spec.c			: 
+spline_apply.c		: 
+spline_construct.c	: 
+star.c			: 
+strchr.c		: 
+strlen.c		: 
+substr.c		: 
+svd.c			: 
+swapbytes.c		: 
+testfit.c		: 
+transform.c		: 
+unsign.c		: 
+usleep.c		: 
+vbin.c			: 
+vclip.c			: 
+
+outdated or unneeded functions:
+
+dummy.c
+downdimen.c
+buftovec.c
+kernel.c
+updimen.c		: 
+stupidtest.c		: 
+plot2.c			: fixed GetColor in dvo/plot.c
Index: /branches/sc_branches/pantasks_condor/doc/notes.dvo-data.html
===================================================================
--- /branches/sc_branches/pantasks_condor/doc/notes.dvo-data.html	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/doc/notes.dvo-data.html	(revision 34783)
@@ -0,0 +1,166 @@
+
+<h2> data structures in DVO </h2>
+
+DVO uses several data structures, defined in <tt>loneos.h</tt> to
+represent data in the database tables.  This document attempts to
+describe the actual usage, some of which may disagree with the comment
+entries in <tt>loneos.h</tt>.
+
+<b> Image </b>
+<table> 
+<tr><th> name               </th><th> type           </th><th> value                     </th><th> units              </th>
+<tr><td> coords             </td><td> Coords         </td><td> astrometry                </td><td>                    </td>
+<tr><td> tzero              </td><td> unsigned int   </td><td> readout time row 0        </td><td> unix time          </td>
+<tr><td> nstar              </td><td> unsigned int   </td><td> number of stars on image  </td><td>                    </td>
+<tr><td> secz               </td><td> short int      </td><td> airmass                   </td><td> 1000*airmass       </td>
+<tr><td> NX                 </td><td> short int      </td><td> image size                </td><td> pixels             </td>
+<tr><td> NY                 </td><td> short int      </td><td> image size                </td><td> pixels             </td>
+<tr><td> apmifit            </td><td> short int      </td><td> aperture correction       </td><td> 1000*mag           </td>
+<tr><td> dapmifit           </td><td> short int      </td><td> error                     </td><td> 1000*mag           </td>
+<tr><td> source             </td><td> short int      </td><td> photcode                  </td><td>                    </td>
+<tr><td> Mcal               </td><td> short int      </td><td> relphot correciton        </td><td> 1000*mag           </td>
+<tr><td> dMcal              </td><td> short int      </td><td> error                     </td><td> 1000*mag           </td>
+<tr><td> Xm                 </td><td> short int      </td><td> image chi-square          </td><td> 100*log(chisq)     </td>
+<tr><td> name[32]           </td><td> char           </td><td> image name                </td><td>                    </td>
+<tr><td> detection_limit    </td><td> unsigned char  </td><td>                           </td><td> 10*mag             </td>
+<tr><td> saturation_limit   </td><td> unsigned char  </td><td>                           </td><td> 10*mag             </td>
+<tr><td> cerror             </td><td> unsigned char  </td><td> astrometric error         </td><td> 50*arcsec          </td>
+<tr><td> fwhm_x             </td><td> unsigned char  </td><td> average FWHM X            </td><td> 25*arcsec          </td>
+<tr><td> fwhm_y             </td><td> unsigned char  </td><td> average FWHM Y            </td><td> 25*arcsec          </td>
+<tr><td> trate              </td><td> unsigned char  </td><td> drift rate                </td><td> 10000*sec/pix      </td>
+<tr><td> exptime            </td><td> float          </td><td> exposure time             </td><td> seconds            </td>
+<tr><td> code               </td><td> char           </td><td> data flags                </td><td>                    </td>
+<tr><td> ccdnum             </td><td> unsigned char  </td><td> mosaic CCD ID number      </td><td>                    </td>
+<tr><td> dummy[20]          </td><td> char           </td><td> extra                     </td><td>                    </td>
+<tr><td> order              </td><td> short int      </td><td> order of Mcal function    </td><td>                    </td>
+<tr><td> Mx                 </td><td> short int      </td><td> Mcal polynomial term      </td><td>                    </td>
+<tr><td> My                 </td><td> short int      </td><td> Mcal polynomial term      </td><td>                    </td>
+<tr><td> Mxx                </td><td> short int      </td><td> Mcal polynomial term      </td><td>                    </td>
+<tr><td> Mxy                </td><td> short int      </td><td> Mcal polynomial term      </td><td>                    </td>
+<tr><td> Myy                </td><td> short int      </td><td> Mcal polynomial term      </td><td>                    </td>
+<tr><td> Mxxx               </td><td> short int      </td><td> Mcal polynomial term      </td><td>                    </td>
+<tr><td> Mxxy               </td><td> short int      </td><td> Mcal polynomial term      </td><td>                    </td>
+<tr><td> Mxyy               </td><td> short int      </td><td> Mcal polynomial term      </td><td>                    </td>
+<tr><td> Myyy               </td><td> short int      </td><td> Mcal polynomial term      </td><td>                    </td>
+<tr><td> Mxxxx              </td><td> short int      </td><td> Mcal polynomial term      </td><td>                    </td>
+<tr><td> Mxxxy              </td><td> short int      </td><td> Mcal polynomial term      </td><td>                    </td>
+<tr><td> Mxxyy              </td><td> short int      </td><td> Mcal polynomial term      </td><td>                    </td>
+<tr><td> Mxyyy              </td><td> short int      </td><td> Mcal polynomial term      </td><td>                    </td>
+<tr><td> Myyyy              </td><td> short int      </td><td> Mcal polynomial term      </td><td>                    </td>
+</table>
+
+<em> apmifit, dapmifit are needed for dophot data, but not sextractor data.  are these correctly handled? </em>
+<em> skyprobe used Mxxxx for Ncal and Myyyy for sky - can this be reconciled? </em>
+
+<h3> codes in Image</h3>
+<ul>
+<li> ID_IMAGE_NOCAL = 0x04 - internal to relphot 
+<li> ID_IMAGE_POOR  = 0x02 - set by relphot
+<li> ID_IMAGE_SKIP  = 0x04 - use by relphot (!!!)
+</ul>
+
+<b> Average </b>
+<table> 
+<tr><th> name       </th><th> type           </th><th> value               </th><th> units                 </th></tr>
+<tr><td> R          </td><td> float          </td><td> RA                  </td><td> decimal degrees J2000 </td></tr>
+<tr><td> D          </td><td> float          </td><td> DEC                 </td><td> decimal degrees J2000 </td></tr>
+<tr><td> M          </td><td> short int      </td><td>                     </td><td> 1000*mag		    </td></tr>
+<tr><td> Nm         </td><td> unsigned short </td><td> N measure	    </td><td> 			    </td></tr>
+<tr><td> Nn         </td><td> unsigned short </td><td> N missing	    </td><td> 			    </td></tr>
+<tr><td> Xp         </td><td> short int      </td><td> position scatter    </td><td> 100*arcsec	    </td></tr>
+<tr><td> Xm         </td><td> short int      </td><td> mag chisq           </td><td> 100*log(chisq)	    </td></tr>
+<tr><td> code       </td><td> unsigned short </td><td> data flags	    </td><td> 			    </td></tr>
+<tr><td> offset     </td><td> signed int     </td><td> measure offset	    </td><td> 			    </td></tr>
+<tr><td> missing    </td><td> signed int     </td><td> missing offset	    </td><td> 			    </td></tr>
+<tr><td> dM         </td><td> short int      </td><td> error on M          </td><td> 1000*log(value)	    </td></tr>
+<tr><td> Xg         </td><td> short int      </td><td> best chisq value    </td><td> ??		    </td></tr>
+</table>		  
+
+<h3> codes in Average</h3>
+<li> set to 0 by addstar, etc
+<li> ID_STAR_NOCAL        = 0x0001 - set by relphot
+<li> ID_PROPER            = 0x0400 - set by addusno, used by markrock
+<li> ID_BAD_DATE          = 0x0800 - deprecate (not currently set)
+<li> ID_TRANSIENT         = 0x1000 - not currently set? 
+<li> ID_VARIABLE          = 0x2000 - not currently set? 
+<li> ID_ROCK              = 0xa000 - set by markrock
+<li> ID_GHOST             = 0xc001 - set by fixcat, markstar
+<li> ID_TRAIL             = 0xc002 - set by fixcat, markstar
+<li> ID_BLEED             = 0xc003 - set by fixcat, markstar, markrock
+<li> ID_COSMIC            = 0xc004 - set by markrock
+</ul>
+
+<b> Measure </b>
+<table> 
+<tr><th> name       </th><th> type           </th><th> value               </th><th> units             </th>
+<tr><td> dR         </td><td> short int      </td><td> RA offset           </td><td> 100*arcsec        </td>
+<tr><td> dD         </td><td> short int      </td><td> DEC offset          </td><td> 100*arcsec        </td>
+<tr><td> M          </td><td> short int      </td><td> catalog mag         </td><td> 1000*mag          </td>
+<tr><td> Mcal       </td><td> short int      </td><td> image cal mag       </td><td> 1000*mag          </td>
+<tr><td> Mgal       </td><td> short int      </td><td> 'galaxy' mag,       </td><td> 1000*mag          </td>
+<tr><td> airmass    </td><td> short int      </td><td> (airmass - 1),      </td><td> 1000*airmass      </td>
+<tr><td> FWx        </td><td> short int      </td><td> fwhm major axis     </td><td> 100*arcsec        </td>
+<tr><td> dM         </td><td> unsigned char  </td><td> mag error           </td><td> 1000*mag          </td>
+<tr><td> fwy        </td><td> unsigned char  </td><td> minor/major ratio   </td><td>                   </td>
+<tr><td> theta      </td><td> unsigned char  </td><td> angle wrt ccd X dir </td><td> degree*(256/360)  </td>
+<tr><td> dophot     </td><td> char           </td><td> dophot type         </td><td>                   </td>
+<tr><td> source     </td><td> unsigned short </td><td> photcode            </td><td>                   </td>
+<tr><td> t          </td><td> unsigned int   </td><td> time                </td><td> unix time         </td>
+<tr><td> averef     </td><td> unsigned int   </td><td> average entry       </td><td>                   </td>
+<tr><td> dt         </td><td> short int      </td><td> exposure time       </td><td> 2500*log(exptime) </td>
+<tr><td> flags      </td><td> unsigned short </td><td> data flags          </td><td>                   </td>
+</table>
+
+<h3> flags in Measure</h3>
+<ul>
+<li> ID_MEAS_POOR         = 0x0001 - set by relphot, unset?
+<li> ID_MEAS_NOCAL        = 0x0002 - set by relphot, unset?
+<li> BLEND_IMAGE          = 0x0100 - set by addstar, etc
+<li> BLEND_CATALOG        = 0x0200 - set by addstar, etc
+<li> BLEND_IMAGE_NEIGHBOR = 0x1000 - set by addstar, etc
+<li> PART_OF_TRAIL        = 0x2000 - set by markstar, fixcat, used by markrock
+<li> GHOST_DATA           = 0x4000 - set by markstar, fixcat, used by markrock
+</ul>
+
+<em> relphot flag meanings: </em>
+<b> ID_IMAGE_NOCAL </b> : completely ignore image (internal only)
+<b> ID_IMAGE_SKIP  </b> : externally marked, don't include in Mrel, calculate Mcal (?)
+<b> ID_IMAGE_POOR  </b> : internally marked, don't include in Mrel, calculate Mcal (?)
+
+<b> ID_STAR_NOCAL  </b> : poor star, don't include in Mcal, calculate Mrel
+STAR_BAD == (average.Xm > STAR_CHISQ) || (average.dM > STAR_SCATTER) || (NVALID < MEAS_TOOFEW)
+
+<b> ID_MEAS_NOCAL  </b> : skip this measurement (temporary, internal)
+<b> ID_MEAS_POOR   </b> : poor star, don't include in Mcal, calculate Mrel
+MEAS_POOR == (3 sigma outlier) || (out of detector area)
+<em> currently NOT sticky!! </em>
+
+--
+
+uniphot.h:
+# define MEAS_POOR     0x0001
+# define MEAS_NOCAL    0x0002
+# define ID_IMAGE_SKIP 0x0004
+
+relphot.h:
+# define ID_VARIABLE   0x2000
+# define ID_TRANSIENT  0x1000 /* is this mutually exclusive with USNO?  */
+# define ID_BAD_DATA   0x0800 /* stationary object with some bad data points */
+# define ID_PROPER     0x0400 /* star with large proper motion */
+
+# define MEAS_POOR     0x0001
+# define MEAS_NOCAL    0x0002
+# define ID_IMAGE_SKIP 0x0004
+
+<em> things to update, cleanup </em>
+<d> finish fix of photcode in imregister-3.0, etc </d>
+<d> move flag constants from relphot/uniphot/photdbc include -> loneos.h </d>
+<d> watch for use of Xp, Xg, code in photdbc & status.extract, status.gstar</d>
+<b> status.extract.NVALID : count !(measure.flag & MEAS_POOR) </b>
+<d> fix error with ID_IMAGE_NOCAL == ID_IMAGE_SKIP </d>
+<d> make sure ID_IMAGE_POOR is saved and ID_IMAGE_SKIP is kept, including with -mosaic </d>
+<d> should STAR_BAD be saved or not?  probably not: there is not one per photcode </d>
+<d> MEAS_POOR should be saved, MEAS_NOCAL should not </d>
+<b> recompile all dvo programs, test </b>
+
+<b> photdbc </b> <em> check on the use of flags, etc; consistent with relphot, uniphot? </em>
Index: /branches/sc_branches/pantasks_condor/doc/notes.photcode.html
===================================================================
--- /branches/sc_branches/pantasks_condor/doc/notes.photcode.html	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/doc/notes.photcode.html	(revision 34783)
@@ -0,0 +1,221 @@
+
+<h2> A Primer on DVO Photcodes and Magnitude Representation </h2>
+
+<p>
+There are two types of DVO table which store magnitude data.  The
+Average table stores magnitudes which represent averages of multiple
+measurements.  The Measure table stores magnitudes which represent
+single measurements.  
+
+<p>
+Each type of entry is defined by a Photcode.  The individual Measure
+entries have their photcodes stored as part of the structure.  The
+Average entries have photcodes which are determined externally by
+their sequence.  For historical reasons, the Average photcode are
+divided into the Primary photcode (of which there may only be one) and
+Secondary (of which there may be several).  The average magnitude
+associated with the Primary photcode is stored with the Average
+structure while the Secondary photcodes are stored in a separate,
+associated table called Secfilt.  The layout of the Average and
+Secfilt tables, and the photcodes assigned to them, are defined when
+the database is created based on the layout of the photcode table.
+This table is defined with a single Primary and a number of Secondary
+photcodes.  When the database tables are created, primary photcode is
+automatically associated with the Average entry while the sequence of
+Secondary photcodes defines the sequence of the entries in the Secfilt
+table.  This is very fragile, but very fast for making the
+association.  Thus, changing the number or sequence of secondary
+photcodes would cause an existing database table to be
+mis-interpretted.  
+
+<p>
+The Measure entries are broken into two photcode types as well:
+internal and external measurements.  Internal measurements are those
+for which the data source is well understood; the instrumental
+magnitudes are available and the instrumental parameters which are
+required to determine a calibrated magnitude.  External measurements
+are provided by external sources: published catalogs, reference
+sources, etc.  For these measurements, there is no control over the
+instrumental parameters which determined the measurement, and in many
+cases the measurement represents an average of an ensemble.
+
+<p>
+The photcode table defines relationships between photcodes,
+representing different photometry systems.  Each photcode entry
+defines the target of the calibration (the equivalence), and it may
+potentially define the zero-point (Co), the airmass slope (Ko), the
+appropriate color terms (C1, C2), the reference color term (Color),
+and the polynomial coefficients of the color equation (starting with
+the 1st order term).  
+
+<p>
+I need to include operations to handle varying color terms between
+CCDs.  I have measured color terms between CCDs in the range +/- 0.1
+mag/mag.  This is an appropriate time to clean up some of the code
+that handles the photcodes, etc.  In particular, the photcode table
+should provide the relationship beween relate between filter systems.
+
+For all of the functions below, a REF photcode simply returns the
+stored magnitude without transformation.  
+
+PhotInst (measure)
+  returns the instrumental magnitude of the given Measure
+  Minst = measure[0].M - measure[0].dt - iZERO_POINT;
+
+PhotCat (measure)
+  returns the 'catalog' magnitude of the given Measure (best guess
+  given no other information like color):
+  Mcat = measure[0].M - iZERO_POINT + Klam*(measure[0].airmass - 1000) + photcodes[0].code[Np].C;
+
+PhotSys (measure, average, secfilt)
+  returns the 'system' magnitude of the given Measure (best guess
+  using existing color information, but without application of
+  relative photometry offsets):
+  Msys = Mcat + f(color)
+  color is derived from measure.photcode.c1,c2  (measure.source.c1,c2)
+
+PhotRel (measure, average, secfilt)
+  returns the 'relative' magnitude of the given Measure (best guess
+  using existing color information and current relative photometry
+  measurment for this image).  This is the same photometry system as
+  the Average (PRI/SEC) measurement equivalent to this measurement
+  photcode:
+  Mrel = Msys + measure[0].Mcal;
+  color is derived from measure.photcode.c1,c2  (measure.source.c1,c2)
+
+PhotCal (measure, average, secfilt, allmeasures, code)
+  returns the 'calibrated' magnitude of the given Measure (best guess
+  using existing color information and current relative photometry
+  measurment for this image transformed to the appropriate reference
+  photometry system):
+  Mcal = Mref + f(color)
+  color is derived from code.c1,c2
+  the provided code must be either the measure.photcode.equiv or an
+  alternate for that code.
+
+<p>
+consider two filters, B & V, and two CCDs 00 & 01.  also, reference
+data in B_L92, V_L92
+
+<pre>
+    N  photcode     type  Co      Ko       X    C1    C2    Ao     Color  Equivalent
+  100  CFH12K.B.00  dep   26.000 -0.15     -    B     V     0.1    -      B
+  101  CFH12K.B.01  dep   26.000 -0.15     -    B     V     0.0    -      B
+
+  200  CFH12K.V.00  dep   26.100 -0.15     -    B     V     0.0    -      V
+  201  CFH12K.V.01  dep   26.100 -0.15     -    B     V     0.1    -      V
+
+    1  B            pri    0.000  -        -    B_L92 V_L92 0.016  -      B_L92
+    2  V            sec    0.000  -        -    B_L92 V_L92 0.008  -      V_L92
+
+  1003 B_L92        ref    -      -        -    -     -     -      -      -
+  1004 V_L92        ref    -      -        -    -     -     -      -      -
+  
+  B = m + Co + Ko*(secz - 1) + Ao*(B - V)
+  B_L92 = B + Co + color*A0 + color^2*A1 + color^3*A2
+</pre>
+
+<b>changes I'd like to make related to photcodes</b>
+
+<li> use structure value 'photcode', not 'source' everywhere
+<li> better abstraction / conceptualization of PhotMode values (Inst, Rel, Abs, Sys)
+<li> N-order polynomial for color-fits
+
+<b> relevant programs </b>
+
+<table>
+<tr><td>* addrefs    </td><td> minor fixes, no conversions are used </td></tr> 
+<tr><td>* addspphot  </td><td> minor fixes, no conversions are used </td></tr> 
+<tr><td>* addstar    </td><td> minor fixes, no conversions are used </td></tr> 
+<tr><td>* delstar    </td><td> minor fixes, no conversions are used </td></tr> 
+<tr><td>* photcode   </td><td> probably ok, check is consistent with new (?) photcode structs </td></tr> 
+<tr><td>* photreg    </td><td> check consistency </td></tr> 
+<tr><td>* photsearch </td><td> check consistency, use photcode table to get Equivalent </td></tr> 
+<tr><td>* relphot    </td><td> signficant changes to handle new structures </td></tr>
+<tr><td>libohana     </td><td> update LoadPhotcodes funcs, check consistency, use photcode table to get Equivalent </td></tr> 
+<tr><td>photdbc      </td><td> signficant changes to handle new structures </td></tr>
+<tr><td>status       </td><td> signficant changes to handle new structures </td></tr>
+<tr><td>dvo          </td><td> repeat fixes in status </td></tr> 
+
+<tr><td>lightcurve </td><td> unused? </td></tr>
+</table>
+
+<b> basic photcode APIs </b>
+
+int LoadPhotcodes (char *filename);
+void SetZeroPoint (double ZP);
+
+PhotCode *GetPhotcodebyName (name);
+int       GetPhotcodeCodebyName (name);
+PhotCode *GetPhotEquivbyName (name);
+int       GetPhotEquivCodebyName (name);
+
+PhotCode *GetPhotcodebyCode (code);
+char     *GetPhotcodeNamebyCode (code);
+
+PhotCode *GetPhotEquivbyCode (code);
+int       GetPhotEquivCodebyCode (code);
+
+double PhotInst (Measure *measure);
+double PhotAbs (Measure *measure);
+double PhotCat (Measure *measure);
+double PhotSys (Measure *measure, Average *average, SecFilt *secfilt);
+double PhotRel (Measure *measure, Average *average, SecFilt *secfilt);
+double PhotAve (PhotCode *code, Average *average, SecFilt *secfilt);
+double PhotRef (PhotCode *code, Average *average, SecFilt *secfilt, Measure *measure);
+
+<b> examples </b>
+find measures which are of photcode equivalent to X
+if (GetPhotEquivCodebyCode (measure[0].source) == N1) { } 
+
+<b> code, entry, hashcode, hashNsec </b>
+entry = 1;
+photcode[entry].code = 100 
+photcode[entry].name = B
+hashcode[100] = 1;
+
+hashNsec is only valid for type PHOT_SEC:
+hashNsec[code] = Nsec entry in SecFilt table
+
+<b> note recursion relationships </b>
+hashcode[photcode[entry].code] = entry;
+photcode[hashcode[entry]].code = entry
+
+<b> outstanding questions / issues </b>
+
+<ul>
+<li> PhotSys needs to handle missing color values
+<li> define valid range for color?
+<li> double PhotFoo functions return what on NO_MAG?
+<li> PhotFoo functions need to check for valid input types:
+  <ul> 
+  <li> PhotInst - PHOT_DEP
+  <li> PhotAbs  - PHOT_DEP, PHOT_REF
+  <li> PhotCat  - PHOT_DEP, PHOT_REF
+  <li> PhotSys  - PHOT_DEP, PHOT_REF
+  <li> PhotRel  - PHOT_DEP, PHOT_REF
+  <li> PhotAve  - PHOT_PRI, PHOT_SEC
+  <li> PhotRef  - PHOT_PRI, PHOT_SEC
+  </ul>
+</ul>
+
+<b> phot definitions </b>
+PhotInst = Measure.M - dt - ZP 
+PhotCat  = Measure.M + K*(airmass-1) + C - ZP
+PhotSys  = Measure.M + K*(airmass-1) + C - ZP + X*color
+PhotRel  = Measure.M + K*(airmass-1) + C - ZP + X*color - Mcal  -> average.m
+PhotRef  = average.M + C + X*color 
+
+C: def ~ 26.0, pri ~ 0.0
+
+PhotAbs  = deprecate (measure.M + K*(airmass-1) + C - ZP
+
+<b> alt photcodes </b>
+
+photcodes of type ALT provide alternate calibration terms for a given
+photcode.  these codes are equivalent to primary/secondary codes, but
+no data is ever saved with this type.  There are only lookups between
+the photcode name and the photcode structure, never the photcode.code
+value, since that defines the photcode for which the given structure
+is an alternate relationship.
+
Index: /branches/sc_branches/pantasks_condor/doc/notes.txt
===================================================================
--- /branches/sc_branches/pantasks_condor/doc/notes.txt	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/doc/notes.txt	(revision 34783)
@@ -0,0 +1,103 @@
+
+opihi improvements / fixes:
+
+* single math processing system : inline shell math used one set of
+  functions (shell/math.c), while the set = () used another set, and
+  logic used part of a third set.  This has been unified into a single
+  math handling function (newmath).  The new math expression handling
+  also returns temporary in-line vectors and matrix expressions.
+
+* global user variables (Buffers, Vectors) and other global variables
+  (outfile, tv parameters, graphic and image tool parameters) have
+  been encapsulated in fuctions to avoid the global data constructs.  
+
+* improved command exit status handling : in the past, a function
+  returning FALSE would halt all script executing, returning to the
+  top level of the command stack.  we now set the variable $STATUS
+  with the exit status and allow the the user to test the exit
+  status to choose an operation.  loops, macros, and input files can
+  use the 'break' and 'continue' functions to escape from the current
+  location.  calling break returns with an exit status of FALSE,
+  calling break returns immediately with an exit status of TRUE.
+
+* more extensive use of 'outfile' 
+
+* support for opihi scripts: there is now better interpretation of
+  command-line arguments to allow for embedded opihi scripts, ie
+  scripts with the dvo or mana interpretter named on the first line
+  with the prefix #! like a shell script.  The first command-line
+  argument is interpreted as a file to input while successive ones are
+  converted to variables $argv:0 - $argv:n  WARNING: in the past,
+  multiple input files could be specified on the command-line.  this
+  now must be specified with --load file arguments.
+
+* local variables: there is now support for local (vs global) opihi
+  variables.  any macro may have a variable declared as local with the
+  command 'local (var)', which may take a list of variables.  these
+  variables are stored internally in association with the macro name.
+  the command may include the option -static, in which case the
+  variable will retain its value on successive calls to the same
+  macro.
+
+- merge of all old opihi-type programs into a single code base 
+
+- group related functions into libraries
+
+- function names, etc, should come from a single function, now a
+  global include file.
+
+- update help files
+
+- update web pages
+
+--
+
+some notes: 
+
+- new vector interpretation is now somewhat slower, due to the multiple
+  assignments.
+
+- BinaryOp does not leak memory (tested)
+
+- 55 seconds for 1000 floating-pt operations on a 4Mpix (3 GHz) -> 0.2
+  seconds per FP OP on a 4k square image
+
+- loop / list functions:
+
+  opihi.c
+  run_loop.c
+  run_if.c
+  input.c
+  for_loop.c
+  macro_exec.c
+
+  I have added continue / break functionality to all loop operations:
+
+  * the variable $STATUS is set based on the exit status of each command
+  * each loop tests for break and continue
+  * in the case of break, the loop returns a status of FALSE
+  * in the case of continue, the loop returns a status of TRUE
+  * if break -auto is set to 'on', then any exit status of FALSE escapes to  
+    the top level.
+  * interrupts are carried up to the top level.
+
+- local variables
+
+  I have added local variables.  They have names of the form
+  MacroName.VarName, where MacroName is the macro in which they are
+  local.  They are declared local with the command 'local', which
+  takes a list of variables to declare local, and the optional -static
+  flag, which requires the shell to test for the existence of the
+  variable before creating it, and only create it if it does not
+  exist.  otherwise the existing value of the variable is maintained.
+
+- math
+
+  I have removed the last instances of math interpretation being
+  performed by math, not newmath, in expand_vectors (interpreting the
+  contents of the vector square brackets: x[5]), and in the two
+  locations where logic was used in the evaluation: run_in and
+  run_while.  there are still some error in the precedence of the
+  operators: logical operators >,<,==,!=, etc need to be lower
+  priority than other binary operators.
+
Index: /branches/sc_branches/pantasks_condor/doc/opihi-vectors.txt
===================================================================
--- /branches/sc_branches/pantasks_condor/doc/opihi-vectors.txt	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/doc/opihi-vectors.txt	(revision 34783)
@@ -0,0 +1,21 @@
+
+2008.04.20
+
+  I am trying to update the opihi vector code for two important upgrades:
+
+  1) I want to convert the float vector to double vectors
+  2) I want to allow the option of int (unsigned int) vectors, especially for flags
+
+  I've define opihi_float and opihi_int types.  A major portion of the
+  upgrade is to convert all of the places where a vector is allocated
+  or manipulated as a float to use 'opihi_float' which can then be
+  re-defined.  The other trick here is to identify the functions which
+  require a vector to be a float and set assserts or error reporting.
+
+  The other major change is to modify the dvo math code.  for the
+  moment, all functions should result in opihi_float vectors except
+  for the explicit cases of the db extractions which return flags.
+
+  Some math operations on the vectors will force conversion to floats,
+  but some are sensible as functions from int to int, and those
+  results can be kept in their native format.
Index: /branches/sc_branches/pantasks_condor/doc/pantasks.controller.txt
===================================================================
--- /branches/sc_branches/pantasks_condor/doc/pantasks.controller.txt	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/doc/pantasks.controller.txt	(revision 34783)
@@ -0,0 +1,273 @@
+
+Pantasks interacts with a parallel job manager.  The current version
+is 'pcontrol', but we would like to allow interactions with condor as
+well.  This file documents the pantasks / controller interactions and
+the requirements for implementing a condor (or other) interface.
+
+Some terminology:
+
+* controller : the abstract concept of the software or system which
+  manages parallel jobs for pantasks.
+
+* pcontrol : the default (Ohana-native) controller implementation
+
+Pantasks Controller Commands.  
+
+The following commands are available within the pantasks shell to send
+commands to the controller.  Within the pantasks shell, these are
+invoked with "controller (command) [options]".  When running the
+pcontrol shell on its own, these commands are called directly.
+
+* check job (jobID)
+* check host (hostID)
+
+check on the status of a single job or host.  the jobID or hostID is
+an integer value [1].  The return is a block of information giving the
+status and some other infomation.  
+
+For 'host', the return is of the form:
+
+host (state)
+STATUS 1
+
+where (state) is one of IDLE, BUSY, RESP, DONE, DOWN, OFF.
+
+For 'job', the return is of the form:
+
+STATUS (status)
+EXITST (Nexit)
+STDOUT (Nbytes)
+STDERR (Nbytes)
+DTIME  (elapsed time)
+
+where:
+
+(status) is one of PENDING, BUSY, DONE, EXIT, CRASH
+(Nexit) is the exit status of the command (ie, as if it were run on
+	the UNIX command line).
+(Nbytes) is the size of the standard out and standard error buffers
+	 from the job
+(elapsed time) is the number of seconds it took to run the command;
+	 this is only set on exit.
+
+* exit
+
+Tell the controller to exit.  In pantasks, it is necessary to give this command in
+the form 'controller exit TRUE'.
+
+* host [options] : manipulate hosts managed by the controller.  The
+following commands may be given:
+
+  * host add (hostname) [-threads N] : add a new connection to the host
+    (hostname).  The optional -threads N argument specifies the default
+    value for this machine for the @MAX_THREADS@ directive.  A job sent
+    to this machine with @MAX_THREADS@ in the command line will have
+    that value replaced by "-threads N" for this machine, were N is the
+    value specified in this host add command.
+  
+  * host on (hostname) : tell the controller to activate the specified
+    host
+  
+  * host off (hostname) : tell the contoller to de-activate the
+    specified host
+  
+  * host check (hostname) : check the status of the given host by
+    hostname (returns 'host (hostname) is (status)' where (status) is
+    one of the list given above for check host
+  
+  * host retry (hostname) : tell the controller to re-attempt a
+    connection to the specified machine NOW (if the connection failed,
+    pcontrol attempts to connect with an increasingly long timeout.  If
+    the timeout is long, but the user knows the machine is now alive,
+    they may desire to force a connection attempt sooner rather than
+    waiting for the timeout to complete).
+  
+  * host delete (hostname) : remove the named host from the list of
+    managed hosts.
+
+Note that the controller manages connections to host machines by
+name. Multiple connections are not generally tied together -- as far
+as pantasks normally is aware, they are not related.  Thus, if a given
+session has N connections to a given machine (host add was called N
+times), then manipulation of the state of the machine may require N
+calls of the same function.  (Note that commands which change a
+machine state, such as 'on', 'off', 'delete' only affect machines in
+the appropriate state.  eg, 'host on ipp050' is invalid if ipp050 is not
+currently off.
+
+* hoststack (stack) : list the hosts in the given 'stack'.  A
+  collection of hosts in a given state is called a 'stack' of hosts.
+  This command lists all of the hosts in one of the stacks.  Stack
+  names are case-insensitive and may be one of the following:
+
+  * IDLE : machines which are currently unoccupied with processing
+  * BUSY : machines which are currently active
+  * RESP : machines which are currently active and responding to
+           another command
+  * DONE : machines which have completed a job, but are not yet ready
+    	   to accept a new job.
+  * DOWN : machines which are currently unresponsive (pcontrol will
+    	   try to reconnect after an interval)
+  * OFF  : machines which are currently off (pcontrol will not try to reconnect)
+
+The response to this command is a list of the machines (a series of
+lines each with ID NAME on a single line).
+
+* jobstack (stack) : list the jobs in the given 'stack'.  Like the
+  hosts, a collection of jobs in a given state is called a 'stack' of
+  jobs.  This command lists all of the jobs in one of the stacks.
+  Stack names are case-insensitive and may be one of the following
+
+  * PENDING : job is waiting for a host
+  * BUSY    : job is running on a host
+  * RESP    : job is running on a host, and responding to another command
+  * DONE    : job has finished, but its completion state has not yet
+    	      been assessed
+  * EXIT    : job finished with a valid exit status (ie, no abort or segfault)
+  * CRASH   : job aborted or segfaulted
+  * KILL    : kill has been requested for the job
+
+* machines : list the status of the hosts by unique machine name.
+  This command is one of the few which work with the multiple
+  connections to a single named machine as the same connection.  The
+  command lists the number of connections to the give machine, the
+  number of jobs currently running on that host, and the number of
+  jobs running on another host which requested the given host. 
+
+* parameters : set several pcontrol internal parameters.  this
+  function lets the user interact with the pcontrol shell and set some
+  internal state variables. The 3 currently allowed options are:
+
+  * parameters connect_time (time) : set the maximum time a pclient is
+    kept alive before pcontrol attempts to reset the connection (time
+    in seconds?)
+  * parameters wanthost_wait (time) : set the amount of time pcontrol
+    will wait before sending a job to a host other than the desired
+    host (time in seconds)
+  * parameters unwanted_host_jobs (Njobs) : set the number of jobs
+    allowed to run which desire a specific host.  this parameters
+    prevents pcontrol from overloading some specific machine due to
+    I/O operations when the process operation is elsewhere.
+
+ * output : print the bufers which carry the controller output.  when
+   pcontrol starts up, the output can be redirected to a file.  if it
+   is not redirected, it is stored by pantasks.  the buffer is not
+   normally dumped in a regular fashion, and can fill the pantasks
+   memory usage.  this command dumps the output to pantasks, and can
+   also flush the buffer (if the "flush" option is given).
+
+ * run : set the run level for the controller.  the command is of the
+   form "run (level)".  pcontrol may be in one of 4 run levels: 
+   * all : all normal pcontrol ops (this is aslo set with no optional
+     	   argument to the run command).
+   * reap : keep the machines running (maintain comms and turn on/off
+     	    as needed) and harvest results from jobs, but do not spawn
+   	    new jobs.
+   * hosts : manage the machines, but do not manage jobs (spawn or
+     	     harvest).
+   * none  : stop all pcontrol processing
+
+ * status : report the current status of the controller: list all
+   known jobs and all known hosts, giving their status.
+
+ * stop : stop all pcontrol processing (equivalent to "run none")
+
+ * verbose : turn on verbose mode for pcontrol (output to pcontrol.log
+   or use "controller output").
+
+ * version : print version info for pcontrol
+
+ * pulse : in non-threaded pcontrol mode (deprecated), set the
+   readline timeout.
+  
+[1] : note that the command 'controller status' includes the job and
+host ID values in the form X.X.X.X where X is a hexidecimal number.
+Unfortunately, pcontrol does not understand this format for the IDs
+for the 'check' command.   
+
+--
+
+Pantasks / Controller Interactions
+
+Pantasks Threads & Controller Interactions
+
+Pantasks has a server/client mode and a stand-alone mode.  These share
+the bulk of code, but there are some minor difference.  In stand-alone
+mode, there are 3 active threads.  The main thread interprets the
+commands (accepted by readline); a second thread manages the tasks and
+jobs known to pantasks; the third thread manages interactions with the
+parallel controller.   In server/client mode, a fourth thread manages
+the communcations with the remote clients.  
+
+The task/job thread is responsible for monitoring the task rules and
+constructing the commands which corresponds to jobs when appropriate.
+Those jobs which are defined to be local are executed on the local
+machine, while remote jobs are sent to the thread which interacts with
+the parallel controller.
+
+The primary job of the controller thread is to monitor the status of
+jobs submitted to the controller and to harvest jobs which have
+finished.  A secondary job is to flush the stdout and stderr buffers
+of the pantasks / pcontrol connection.  
+
+Jobs are submitted to the controller directly by the Task/Job thread.
+Other operations, check as manual checks of the job status are
+performed by the main thread.
+
+Commands send to pcontrol:
+
+In addition to the user-level commands discussed above, the following
+messages are sent to the controller:
+
+* jobstack exit : the controller thread checks for the set of completed
+  jobs (which did not crash) by sending this command.  The response is
+  a list of jobs ready for harvest.
+
+* jobstack crash : the controller thread checks for the set of
+  complete jobs which crashed by sending this command.  The response
+  is a list of jobs ready for harvest.
+
+* delete : jobs which have completed and for which the stderr/stdout
+  have been received can be deleted from the controller.  For
+  'pcontrol', this is necessary to free up resources managing the
+  specific job.
+
+* check job : once the controller gets a list of jobs which have
+  exited or crashed, pantasks loops over those jobs, harvesting their
+  results.  this command is used to get the needed stats (size of
+  stderr buffer, size of stdout buffer, exit status, etc).  
+
+* job : Submit a job to the controller.  this command is used by the
+  function SubmitControllerJob to send a new job to the controller.
+  the function appends options such as +host, -nice as needed.  the
+  function expects to receive a job ID from the controller for future
+  interactions.
+
+* quit : shutdown the controller
+
+---
+
+pantasks & condor
+
+There are three classes of pcontrol operations which pantasks
+currently perform:
+
+* submit jobs to pcontrol
+* detect completion and harvest job output
+* manage the hosts used by pcontrol
+
+The communication between pantasks and pcontrol uses a pipe; pcontrol
+is run as a forked child of pantasks.  The communication is done via
+text blocks with fairly minimal hand-shaking.  
+
+If pantasks is integrated with condor, it is critical that it be able
+to perform the first two of these functions.  It is not required that
+pantasks manage the condor hosts.
+
+Condor client / server interaction could be performed via forked
+commands or we could use the SOAP interface.  I suspect that SOAP will
+be more effective for the high-rate job harvesting process.
+
+However jobs are submitted to condor, it is critical that condor be
+able to respect the limit on the number of mistargeted jobs currently
+active.
Index: /branches/sc_branches/pantasks_condor/doc/pantasks.txt
===================================================================
--- /branches/sc_branches/pantasks_condor/doc/pantasks.txt	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/doc/pantasks.txt	(revision 34783)
@@ -0,0 +1,52 @@
+
+- task spawning speed
+
+  I have been examining things which affect the speed of the pantasks
+  processing. I have learned some interesting things:
+
+  * pcontrol was being slammed with requests for status by pantasks.
+    this may account for Paul's controller hang-ups.  I have added a
+    long (500ms) sleep to the controller thread to limit the rate at
+    which controller checks are run
+
+  * adding even a small usleep to the task_thread or job_thread puts
+    them to sleep for a long time (>> 10ms).  it seems longer than the
+    linux time slicer.  I have removed sleeps from the task and job
+    threads.
+
+  * the job submit rate is apparently limited by two things:
+
+    * when the job is submitted (SubmitJob) the interaction with
+      the controller seems to take ~30ms or more.
+
+    * some thread (controller thread? main readline thread?) seems to
+      introduce timeouts which are very long (up to 100ms).  These
+      introduce bit delays if when they happen during the task_thread
+      loop. 
+
+- updates for queues:
+
+  -key 1:2:4 (key is string with possibly multiple columns joined)
+  string function to drop first word
+
+
+- todo:
+
+  - create the processing threads:
+    - check tasks
+    - check jobs
+    - check controller  
+    - load inputs
+
+- additional issues:
+  - server input needs to place files for input on a stack which is 
+    actually loaded by the RunScheduler loop
+  - have the client run a special multicommand function which passes 
+    any unfound commands along to the server.  the server should not 
+    be running multicommand, but it probably does not hurt.
+    (the client is not allowed to send ';' to the server)
+
+  - pantasks input: this passes an 'input' command to the server, which
+    performs the input in a different thread.  is this the same thread
+    as the scheduler loop?  another option within the scheduler loop?
+
Index: /branches/sc_branches/pantasks_condor/doc/pcontrol.txt
===================================================================
--- /branches/sc_branches/pantasks_condor/doc/pcontrol.txt	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/doc/pcontrol.txt	(revision 34783)
@@ -0,0 +1,276 @@
+
+2006.08.18
+
+ Outstanding issues related to pcontrol:
+
+ * disposition of HUNG jobs?
+ * probably should not save the history for pcontrol or pclient
+   (these will be many lines long very quickly...)
+ * need to add options to run/stop for hosts and jobs independently
+
+2006.08.11
+
+I have nearly finished the conversion of pcontrol to use a background
+thread for monitoring the remote machines.  A few questions are still
+outstanding:  
+
+- currently, we are thread-safe for interactions with the stacks.  As
+  long as an operation is only working with a single job/host, and all
+  jobs/hosts are selected by pulling them from the stacks, there will
+  never be a contention between threads for the same job/host.
+  However, are there problems for commands which require a specific
+  job or host but are unable to find it because the job/host may be in
+  flight from one stack to another.  
+
+- The CheckIdleHost command needs to join a job and a host.  In this
+  case, it is necessary to lock the job PENDING stack while searching
+  for a job to give to a host.  The host is pulled off of the IDLE
+  stack before being past to CheckIdleHost.
+
+o CheckIdleHost currently does not have a way to send a WANTHOST job
+  to any host other than the WANTHOST.  What should the rule be by
+  which a job is run on an alternative machine? (partial fix)
+
+- we are not starting any of the job or host timers?
+
+2006.08.09
+
+working on pcontrol CheckSystem background thread.  One thread runs
+the readline interaction and performs all of the user commands.  The
+second thread runs the CheckSystem loop and tests the hosts and jobs.
+We need to be sure these two do not interfere with one another.  Here
+is a list of all of the user commands and the ways in which they
+interact with the Job / Host queues:
+
+
+
+2006.08.04
+
+pcontrol gets a large delay every time it tries to connect to a host.
+this is because the readline interrupt has to wait for the connection
+to complete.  I probably need to fix this by using a threaded model
+and running CheckSystem in a background thread.
+
+-----
+
+typedef struct {
+  char *buffer;
+  int   Nalloc;
+  int   Nmaxread;
+  int   Nextra;
+  int   Nlast;
+  int   Nbuffer;
+} Fifo;
+
+typedef struct {
+  int argc; char **argv; /* a list of words that define this object */
+  struct timeval start, accum, timer;
+  int   status;
+  char *logfile;
+  char *lastproc;
+} Object;
+
+typedef struct {
+  Object **object;
+  int    Nobject;
+  int    NOBJECT;
+} Queue;
+
+typedef struct {
+  char   *hostname;
+  int     rsock, wsock;
+  int     status; /* idle, busy, etc... */
+  struct  timeval start, accum, timer;
+  Fifo    fifo;
+  int     code;
+  Object *object;
+} Machine;
+
+
+currently, the transport is /usr/bin/rsh, defined in InitMachines.c 
+
+the shell on the remote machines is /bin/tcsh, defined by rconnect.c
+
+---
+
+pcontrol.client:
+
+ - remote process initiated by pcontrol
+
+ - accepts jobs, returns status, stdout and stderr
+
+ - valid commands:
+
+   - job (argv)
+     returns PID or -1 (0?) on failure
+
+   - status
+     returns current job status:
+     BUSY
+     EXIT n 
+     CRASH n
+     
+   - stderr
+     returns the current stderr buffer:
+     NBYTES n
+     (DATA)
+
+   - stdout
+     returns the current stdout buffer:
+     NBYTES n
+     (DATA)
+
+   
+---
+
+the client needs to accept commands from the server (via
+stdin/stdout), but it also needs to monitor its process.  I can use
+the opihi structure to implement the command-line interpretation with
+readline.  I can use the readline function rl_event_hook to set the
+background functions to check and rl_set_keyboard_input_timeout to set
+the polling period.
+
+this same method can be used with the scheduler:  the command 'run'
+can set the CheckTask function to this hook (& unset it).
+
+---
+
+rl_event_hook -> CheckChild
+
+  - needs to handle the case when no child process yet exists
+  - needs to examine the child status,
+  - needs to read from child stderr and store
+  - needs to read from child stdout and store
+
+  * no warnings (will not do anything clever if buffers get too large)
+
+---
+
+ pcontrol commands:
+
+ job [options] argv0 argv1 argv2 ...
+  -host name : run job on specified host, or any other if not available
+  +host name : run job on specified host, error if not available (error when attempted, not when submitted)
+  -timeout N : seconds before controller gives up on job (once started)
+  -stdout name : redirect job stdout to file directly
+  -stderr name : redirect job stderr to file directly
+
+  * priority information?
+  * returns JobID
+  * adds job to pending queue
+
+ host (hostname) [-delete]
+ (may have multiple entries to the same machine, these are not distinguished)
+
+ stdout ID [-file name]
+ stderr ID [-file name]
+ delete ID
+
+ status -job ID
+ status -machine hostname
+ status -queues
+
+pcontrol may be given a timeout for each job.  pcontrol will monitor a
+job and kill/crash it if the timeout expires.  the timeout only
+governs how long it is allowed to execute, not how long it can sit in
+the queue.  (the scheduler / operator should decide if a job has been
+on pcontrol for too long -- this probably means there are no
+appropriate machines ).
+
+pcontrol currently does not distinguish between multiple instances of
+a single host.  all have the same name.  if you want to bring down a
+host, you need to issue N host -down commands.  perhaps this is
+silly.  a simple alternative would be for the host [-on -off -start
+-stop] commands to apply to all defined entries which match the
+hostname.  In this case, a command like 'host foo -off' would find and
+halt all connections to the machine 'foo', while 'host foo -on' would
+restart them all (or rather, given the functionality of pcontrol,
+would allow pcontrol to attempt to bring them on).
+
+It is not clear why a user should be able to execute 'start' (down ->
+idle) or 'stop' (idle -> down).  The transition down -> idle is
+automatically performed by pcontrol for any machines which are
+currently down, while the transition idle -> down is immediately
+followed by an attempt by pcontrol to move the host from down -> idle.
+This functionality can be used with non-automatic calling of
+CheckSystem to test the pcontrol host interface operations.
+
+does it makes sense to kill all jobs on a host?  this would only have
+the effect of clearing the host for a moment until pcontrol decided to
+start another job on that host.  the desired effect is gained putting
+the host to 'off'.
+ 
+currently the command 'host (hostname)' puts the host in 'down'
+state.  pcontrol then immediately tries to connect to the host, moving
+it to 'idle' state (and then 'busy' if any jobs are available).  it
+might be useful to be able to add a host in 'off' state as a starting
+point.  
+
+it is not obvious that the user should be able to run 'CheckHost',
+unless this gets expanded to return state information on the host.
+
+---
+
+Job States:
+
+PENDING
+BUSY
+EXIT
+CRASH
+NEW *
+DEL *
+
+* - invisible states 
+
+Job State Transitions:
+
+NEW     -> PENDING : AddJob
+PENDING -> BUSY    : StartJob
+PENDING -> DEL     : DelJob
+BUSY    -> DONE    : CheckBusyJob | KillJob
+DONE    -> EXIT    : CheckDoneJob
+DONE    -> CRASH   : CheckDoneJob
+BUSY    -> PENDING : CheckJob | CheckHost
+EXIT    -> DEL     : DelJob
+CRASH   -> DEL     : DelJob
+
+Host States:
+
+IDLE
+BUSY
+DOWN
+OFF
+NEW *
+DEL *
+
+* - invisible states 
+
+Host State Transitions:
+
+NEW      -> OFF      : AddHost
+OFF      -> DEL      : DelHost
+OFF      -> DOWN     : OnHost
+DOWN     -> OFF      : OffHost
+IDLE     -> OFF      : OffHost
+DOWN     -> IDLE     : StartHost
+IDLE     -> BUSY     : StartJob
+BUSY     -> IDLE     : CheckJob | KillJob
+BUSY     -> BUSY-OFF : OffHost
+BUSY     -> DOWN     : CheckJob | CheckHost
+BUSY-OFF -> OFF      : CheckJob
+
+AddJob    - U
+DelJob    - U
+StartJob  - P
+CheckJob  - P
+KillJob   - U
+
+AddHost   - U
+DelHost   - U
+OnHost    - U
+OffHost   - U
+StartHost - P
+CheckHost - P
+
+U - operation performed by the user
+P - operation performed by the program
Index: /branches/sc_branches/pantasks_condor/doc/psched.htm
===================================================================
--- /branches/sc_branches/pantasks_condor/doc/psched.htm	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/doc/psched.htm	(revision 34783)
@@ -0,0 +1,161 @@
+
+This article describes the concept, design, and operation of
+<tt>psched</tt>, the Pan-STARRS IPP task scheduler.  
+
+<h2> Basic Concept </h2>
+
+  <p>
+  The purpose of <tt>psched</tt> is to manage the automatic construction
+  and execution of inter-related (often repetative) operations.
+  <tt>psched</tt> uses a set of rules to define UNIX commands, and
+  their corresponding command-line arguments, to be performed on some
+  regular, repeated basis.  The utility of <tt>psched</tt> is that it
+  can easily define an analysis system which is completely
+  state-based, as opposed to an event-driven system.  
+
+  <p>
+  Consider, for example, a telescope which obtains a collection of
+  images over the course of a night.  Every minute or two, it takes an
+  image and writes the image to some disk.  An event-driven analysis
+  system would involve having the telescope initiate a process at the
+  end of the exposure.  This process would perform an analysis, write
+  some output, then send trigger another process.  This type of
+  operation works very well for a simple set up with reliable
+  hardware.  Such a system becomes more difficult to maintain when
+  hardware failures occur or when multiple systems need to interact
+  with each other.  When failures occur, the triggering information
+  (the events) is easily lost, thus some mechanisms are needed to
+  detect these failures and either re-send the trigger or send an
+  alternative failure-mode trigger.  Or, if two systems need to
+  interact, one or the other system must block for results from the
+  first.  Stopping and restarting such an analysis system is very
+  delicate since the appropriate triggers must be set up some how, eg
+  by noticing which images have not succeeded and restarting them at
+  the appropriate stage.  All of these types of methods of handling
+  complexity and failures are essentially state-based rules.
+  <tt>psched</tt> allows the easy definition of a totally state-based
+  analysis system.
+
+  <p>
+  In a state-based system, some mechanism examines the state of the
+  system and decides which actions to perform based on the current
+  state.  In the illustration above, the mechanism could examine the
+  images available (either by examining the disk or by examining the
+  state of a data table) and decide to perform an operation based on
+  what images are available.  This makes it very easy to handle
+  complexity and errors.  If an analysis fails, the state either is
+  not successfully updated or the error state is recorded, both
+  situations being easy to detect and easy to handle.  Restarting the
+  system simply involves starting the state-monitoring mechanism.
+  Combining results from multiple input sources simply involves
+  watching for the multiple inputs to be available.  <tt>psched</tt>
+  provides a mechanism to define state monitors, and to define the
+  actions which are performed when those states occur.
+  <tt>psched</tt> action consist of initiating UNIX commands, where
+  the arguments of those commands may depend on the results of the
+  state tests.
+
+  <h3> Tasks vs Jobs </h3>
+
+  <p>
+  The primary function of <tt>psched</tt> is to repeatedly perform
+  <b>tasks</b>, and execute <b>jobs</b> on the basis of those tasks.
+  A task consists of a set of rules which describe system state tests
+  to perform on a regular time scale.  Based on the results of those
+  state tests, the task will then choose whether or not to construct a
+  job.  The task also defines actions to perform upon the completion
+  of a job, based upon the output and exit status of the job.  A task
+  thus defines the repeat period.  It may optionally define valid or
+  invalid time ranges (eg, Mon-Fri or 10:00-17:00, etc).  The task may
+  also specify that the job be run locally (ie, in the background on
+  the same computer as psched) or remotely by the parallel process
+  controller (<tt>pcontrol</tt>).  A job may even be restricted to a
+  specific computer managed by <tt>pcontrol</tt>.
+
+  An example of a simple tasks is given below.  
+
+<pre>
+  task datalist
+    command ls /data/foo
+    periods -exec 5.0
+    periods -timeout 50.0
+    periods -poll 1.0
+
+    task.exit 0
+      queueprint stdout
+      queuedelete stdout
+    end
+ 
+    task.exit 1
+      queuepush failure "task failed"
+    end
+  end
+</pre>
+
+  <p>
+  This task does not perform any system state tests; it is simply
+  constructs a new job every 5.0 seconds.  The job in this case is
+  always the same: <tt> ls /data/foo </tt>.  When the job finished,
+  if the job exit status is 0 (normal UNIX success status), the
+  resulting output is printed to the screen.  If the job returns an
+  exit status of 1 (a failure), the failure queue receives a single
+  entry.  Although they are not defined in this case, it is also
+  possible to specify the action to be taken if the job crashes (does
+  not exit normally) or if it times out (runs beyond the specified
+  timeout period).
+
+  A slightly more complex task which performs a state test and
+  constructs a command based on that test is shown below
+
+<pre>
+  task datalist
+    periods -exec 5.0
+    periods -timeout 50.0
+    periods -poll 1.0
+
+    task.exec 
+      $file = `next.file`
+      if ($file == "none")
+        break
+      end
+      command cp /data/foo/$file /data/bar
+    end
+
+    task.exit 0
+      queueprint stdout
+      queuedelete stdout
+      queuepush copied $file
+    end
+ 
+    task.exit 1
+      queuepush failure $file
+    end
+  end
+</pre>
+
+  The <tt>task.exec</tt> macro is executed by psched every 5.0
+  seconds.  This macro executes a (hypothetical user-defined) UNIX
+  command (<tt>next.file</tt>) which examines the system state, return
+  either a filename or the word "none".  If the result of this test is
+  "none", the task does nothing: no job is constructed.  Otherwise, a
+  job is constructed using the name of the file returned by the state
+  test.  Successful jobs have the filename added to the 'copied'
+  queue, while failed jobs add the filename to the 'failure' queue.
+
+  <h3> Parallel vs Local Job Processing </h3>
+
+  <h3> Task Restrictions </h3>
+
+  <h3> Inter-Task and Inter-Job Communications </h3>
+
+<h2> psched Design </h2>
+
+  <h3> The Opihi Shell </h3>
+
+  <h3> Task List </h3>
+
+  <h3> Job List </h3>
+
+  <h3> pcontrol Interface </h3>
+
+  <h3> 
Index: /branches/sc_branches/pantasks_condor/doc/rd-upgrades.txt
===================================================================
--- /branches/sc_branches/pantasks_condor/doc/rd-upgrades.txt	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/doc/rd-upgrades.txt	(revision 34783)
@@ -0,0 +1,10 @@
+
+rd a file.fits : read from PHU
+rd a file.fits -x N : read from extension N (0 - N)
+rd a file.fits -n name : read from extension name
+
+-plane : read only slice N
+
+* this function needs to check for compressed data:
+
+  * if PHU has been compressed, 
Index: /branches/sc_branches/pantasks_condor/doc/region-image.txt
===================================================================
--- /branches/sc_branches/pantasks_condor/doc/region-image.txt	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/doc/region-image.txt	(revision 34783)
@@ -0,0 +1,13 @@
+
+I have done some work on getting region to use the image projection
+for the dvo display.  The pieces are almost there, except:
+
+* there is apparently a small offset between the two systems?
+
+* SelectRegions relies on the values of xmax and ymax being degrees
+  from a 0,0 coordinate.  this is a little tricky: this should not
+  fail for large AITOFF projections.
+
+* kapa does not do non-linear projections
+* kapa does not do multi-level projections
+
Index: /branches/sc_branches/pantasks_condor/doc/sample.c
===================================================================
--- /branches/sc_branches/pantasks_condor/doc/sample.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/doc/sample.c	(revision 34783)
@@ -0,0 +1,18 @@
+# include <stdio.h>
+
+main (int argc, char **argv) {
+
+  int i;
+  char line[1024];
+
+  for (i = 0; i <= argc; i++) {
+    gprint (GP_ERR, "arg %2d: %s\n", i, argv[i]);
+  }
+  while (argv[i] != NULL) {
+    gprint (GP_ERR, "env %2d: %s\n", i, argv[i]);
+    i++;
+  } 
+  while (fscanf (stdin, "%s", line) != EOF) {
+    gprint (GP_ERR, "line: ...%s...\n", line);
+  }
+}
Index: /branches/sc_branches/pantasks_condor/doc/scheduler.txt
===================================================================
--- /branches/sc_branches/pantasks_condor/doc/scheduler.txt	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/doc/scheduler.txt	(revision 34783)
@@ -0,0 +1,206 @@
+
+2005.07.15
+
+The controller sends messages to both stdout and stderr.  I can easily
+require the messages which are immediate responses to external
+commands (status, check, etc) go back on stdout.  other messages
+should go to stderr, or be suppressed.  I suppose i can regularly
+harvest the stderr messages?
+
+2005.07.14
+
+I am still exploring the scheduler / controller interactions.  the
+automatic interactions seem to work pretty well now.  The area of
+confusion is in the user interface, both in terms of checking on the
+status of things (both controller and scheduler) and in terms of
+having user control over aspects of the controller.
+
+I have defined user functions which execute the controller commands
+'status' and 'check'.  These are straightforeward since they simply
+send a command to the controller and echo the output (or give an error
+condition message).
+
+Should the user have the ability to define a job, independent of a
+task?  This could be implemented purely as a controller action: the
+controller commands 'job', 'kill', 'delete', 'stderr', 'stdout' would
+be available from the scheduler, and the commands simply passed
+along.  This adds a bit to the complexity: if the 'delete' command is
+passed along, nothing prevents the user from deleting a job scheduled
+by the scheduler from a task.  the scheduler may then get confused
+when it tries to interact with that job in the future from the
+automatic loop.
+
+Another option is to simply have these commands interact with the
+scheduler's job stack.  this has the advantage of limiting the
+scheduler / controller responsibility errors (scheduler, not user, is
+always responsible to sending/harvesting jobs to/from the
+controller, though we still need to handle the cases if a job is lost
+or dropped by the controller).  the diffficulty here is deciding how
+to handle the job completion.  we would need a way to define a set of
+exit macros, which could then do something useful with the output.  
+
+Another possibility is to define limits on how many times a task may
+spawn a job.  There would then be no 'job' function.  If we define
+this limitation, we will still need a way of killing and deleting a
+specific job.  Thus a 'kill' and 'delete' function would examine and
+modify the scheduler's job stack.  The stderr and stdout functions are
+then already part of the task commands.  
+
+Other task options might include: 
+
+- a list of allow / exclude time periods (which should be time-of-day
+  ranges and day-of-week ranges).
+
+- a function to delete an existing task (which would have to stop the
+  spawning of new jobs, at least until no more jobs for that task
+  remain).
+
+- allow the 'periods' command to define defaults when outside of a
+  task
+
+2005.07.05
+
+At this point, scheduler / pcontrol / pclient all work in a basic way.
+pclient is the most robust of the three, having the simplest
+responsibility.  pcontrol is generally pretty good, though I need to
+flesh out the user interface a bit and clean up the output warning / info
+messages.  scheduler will need the most attention, though it is
+already fairly reasonable.  I need to flesh out the user commands to
+check on the controller status (basically, these need to replicate the
+status commands available to the controller).  
+
+I also need to handle the case of timeout on the controller,
+independently of timeout for a local job on the scheduler.  currently,
+if a local job exceeds the timeout value, scheduler flags it.  but, it
+does not make sense to use the same timeout value for a controller
+job. I could pass the timeout to the controller when the job executes,
+in which case it has the same meaning, essentially, for the controller
+jobs as it does for the local jobs: once you start the function, it
+needs to complete within NN seconds.  However, I think I still need to
+have a scheduler concept of a job which the controller is unable to
+complete.  It should be possible to prevent a job from sitting pending
+on the controller forever.  What exactly you do if the controller is
+unwilling to execute a job is another story (possible reasons:
+controller overload, missing required host, missing any hosts,
+something hung somewhere?).  
+
+The scheduler does not do a good job of shutting down the controller
+when it (the scheduler) exits.  This works well for the
+pcontrol/pclient interface, so the solution lies there.  
+
+I need to decide how to behave if the scheduler asks for a job with a
+required host which the controller knows is currently down or
+non-existent.  Several options could be used.  The controller could
+simply hold the task until the scheduler notices it is not being
+executed (after all, the controller does not know if the machine is
+being serviced for a short time or a long time, but the scheduler
+could know).  The controller could immediately return a failure noting
+the current state of the machine (this would put the burden of
+deciding that the machine should be available on the scheduler).  The
+controller could try to execute the job a certain number of times, and
+then it could report the failure to the scheduler.  This is not so
+different from having a pending-timeout which the scheduler tracks
+(moves the timeout check to the controller, essentially).  
+
+There was some odd behavior with 'exec echo $stdout >> foo'.  This
+resulted in empty files 'foo'.  The following work fine, so something
+is just weird:
+exec echo foobar >> foo
+output foo
+echo $stdout
+output stdout
+
+Various error conditions should be checked
+
+What do we do if a task requests a host which is not available to the
+controller (ie, not defined)?  this is similar to the problem of
+requesting a host which is down.  I think the controller should either
+immediately refuse or accept in anticipation that such a host may
+eventually be defined.
+
+I need to be careful about jobs sent to the controller and not
+harvested before stopping the scheduler execution.
+
+---
+
+sched / pcontrol todo:
+
+- sched: validate task hosts with controller
+
+---
+
+scheduler commands:
+
+task (taskname)
+ - define a new task
+ - loads task-related commands from list / readline
+ - commands are parsed on load
+ - end with end (like if / for)
+
+task.exit (value)
+ - define a new task macro for this exit condition
+   (value) may be an exit status (number)
+   (value) may be 'timeout'
+   (value) may be 'crash' ?
+ - commands are parsed on execution (not on definition)
+
+task.exec
+ - define a task macro for exec condition
+ - commands are parsed on execution (not on definition)
+
+command (args) (args)
+ - defines command associated with task
+ - may be in task or in task.macro (exit/exec)
+   (in task, command line is static; in task.macro, command line is expanded for each instance)
+
+host (machine) [-required]
+ - defines preferred host
+ - may be in task or in task.macro (exit/exec)
+   (in task, value is static; in task.macro, value is defined for each instance)
+ - value of LOCAL runs job as local job (not on controller)
+ - value of NONE runs job on controller without specifying host
+
+stderr (file / variable)
+ - defines destination for stderr capture from task
+ - written to destination at end of execution?
+
+stdout (file / variable)
+ - defines destination for stdout capture from task
+ - written to destination at end of execution?
+
+periods -poll 1
+periods -exec 30
+periods -timeout 2
+ - defines relevant time-scale for the task
+
+run
+stop
+ - start or stop the scheduler loop, executing the various tasks
+
+---
+
+local jobs vs controller jobs
+
+a local job is run as background fork (ie, not on controller)
+a controller job is sent to the controller to run when it can
+
+---
+
+possible errors which the scheduler may encounter when executing a
+job:
+
+  - controller is not responding
+  - controller says machine is DOWN
+  - controller says command not found
+  - controller has too many processes
+  - controller takes to long to start job (pending timeout)
+  - controller says job timed out
+  - controller says job crashed
+  - controller says job exited with status
+
+---
+
+notes:
+
+ - watch for NFS lags / blocking.  if NFS has file visibility lags, we
+   may need to add blocking as an option to the job (-block filename)
Index: /branches/sc_branches/pantasks_condor/doc/stds.txt
===================================================================
--- /branches/sc_branches/pantasks_condor/doc/stds.txt	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/doc/stds.txt	(revision 34783)
@@ -0,0 +1,40 @@
+
+notation:
+
+object : an astronomical source, may be observed multiple times
+measurement : a single observation of an object (one per object per exposure) 
+m_inst : instrumental magnitude measurement
+m_cat  : catalog magnitude measurement (m_inst + 25.0)
+m_sys  : nominal internal system magnitude measurement (m_inst + ZP(CCD,FILTER)
+m_rel  : transparency adjusted magnitude measurement (m_sys + offset)
+
+M_ave  : average (object) magnitude in internal system
+M_ref  : average (object) magnitude in reference system
+
+test for photometric consistency:
+
+for the output from dither.corr:
+
+M_ave - m_rel(ccd) vs x_ccd, y_ccd
+M_ave - m_rel vs x_mosaic, y_mosaic
+M_ave - m_rel vs airmass?
+
+
+
+  MEGACAM.g.00 Minst - MEGACAM.g.01 Mcal : time
+
+  MEGACAM.g.00 - g : time
+
+  MEGACAM.g.00,Mrel - MEGACAM.g.01,Mcal : MEGACAM.g.00 - MEGACAM.r.00
+
+- use relphot to flag stars in poor locations, etc
+
+- we can choose for entry if it is Minst, Mcat, Msys, Mvrel, Mcal, Mave, or Mref
+
+int GetMagnitudeType (char *name, PhotCode *code, int *MagMode);
+
+- should return photcode & value MEAS_MREL, AVE_MREF, or what ever
+- should provide an appropriate default for measures, etc
+- should check for consistency with photcode type:
+  PRI/SEC : AVE_MREF, AVE_MAVE
+  DEP     : MEAS_ETC
Index: /branches/sc_branches/pantasks_condor/doc/svdcmp_eispack.f
===================================================================
--- /branches/sc_branches/pantasks_condor/doc/svdcmp_eispack.f	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/doc/svdcmp_eispack.f	(revision 34783)
@@ -0,0 +1,358 @@
+      subroutine svd(nm,m,n,a,w,matu,u,matv,v,ierr,rv1)
+c
+      integer i,j,k,l,m,n,ii,i1,kk,k1,ll,l1,mn,nm,its,ierr
+      double precision a(nm,n),w(n),u(nm,n),v(nm,n),rv1(n)
+      double precision c,f,g,h,s,x,y,z,tst1,tst2,scale,pythag
+      logical matu,matv
+c
+c     this subroutine is a translation of the algol procedure svd,
+c     num. math. 14, 403-420(1970) by golub and reinsch.
+c     handbook for auto. comp., vol ii-linear algebra, 134-151(1971).
+c
+c     this subroutine determines the singular value decomposition
+c          t
+c     a=usv  of a real m by n rectangular matrix.  householder
+c     bidiagonalization and a variant of the qr algorithm are used.
+c
+c     on input
+c
+c        nm must be set to the row dimension of two-dimensional
+c          array parameters as declared in the calling program
+c          dimension statement.  note that nm must be at least
+c          as large as the maximum of m and n.
+c
+c        m is the number of rows of a (and u).
+c
+c        n is the number of columns of a (and u) and the order of v.
+c
+c        a contains the rectangular input matrix to be decomposed.
+c
+c        matu should be set to .true. if the u matrix in the
+c          decomposition is desired, and to .false. otherwise.
+c
+c        matv should be set to .true. if the v matrix in the
+c          decomposition is desired, and to .false. otherwise.
+c
+c     on output
+c
+c        a is unaltered (unless overwritten by u or v).
+c
+c        w contains the n (non-negative) singular values of a (the
+c          diagonal elements of s).  they are unordered.  if an
+c          error exit is made, the singular values should be correct
+c          for indices ierr+1,ierr+2,...,n.
+c
+c        u contains the matrix u (orthogonal column vectors) of the
+c          decomposition if matu has been set to .true.  otherwise
+c          u is used as a temporary array.  u may coincide with a.
+c          if an error exit is made, the columns of u corresponding
+c          to indices of correct singular values should be correct.
+c
+c        v contains the matrix v (orthogonal) of the decomposition if
+c          matv has been set to .true.  otherwise v is not referenced.
+c          v may also coincide with a if u is not needed.  if an error
+c          exit is made, the columns of v corresponding to indices of
+c          correct singular values should be correct.
+c
+c        ierr is set to
+c          zero       for normal return,
+c          k          if the k-th singular value has not been
+c                     determined after 30 iterations.
+c
+c        rv1 is a temporary storage array.
+c
+c     calls pythag for  dsqrt(a*a + b*b) .
+c
+c     questions and comments should be directed to burton s. garbow,
+c     mathematics and computer science div, argonne national laboratory
+c
+c     this version dated august 1983.
+c
+c     ------------------------------------------------------------------
+c
+      ierr = 0
+c
+      do 100 i = 1, m
+c
+         do 100 j = 1, n
+            u(i,j) = a(i,j)
+  100 continue
+c     .......... householder reduction to bidiagonal form ..........
+      g = 0.0d0
+      scale = 0.0d0
+      x = 0.0d0
+c
+      do 300 i = 1, n
+         l = i + 1
+         rv1(i) = scale * g
+         g = 0.0d0
+         s = 0.0d0
+         scale = 0.0d0
+         if (i .gt. m) go to 210
+c
+         do 120 k = i, m
+  120    scale = scale + dabs(u(k,i))
+c
+         if (scale .eq. 0.0d0) go to 210
+c
+         do 130 k = i, m
+            u(k,i) = u(k,i) / scale
+            s = s + u(k,i)**2
+  130    continue
+c
+         f = u(i,i)
+         g = -dsign(dsqrt(s),f)
+         h = f * g - s
+         u(i,i) = f - g
+         if (i .eq. n) go to 190
+c
+         do 150 j = l, n
+            s = 0.0d0
+c
+            do 140 k = i, m
+  140       s = s + u(k,i) * u(k,j)
+c
+            f = s / h
+c
+            do 150 k = i, m
+               u(k,j) = u(k,j) + f * u(k,i)
+  150    continue
+c
+  190    do 200 k = i, m
+  200    u(k,i) = scale * u(k,i)
+c
+  210    w(i) = scale * g
+         g = 0.0d0
+         s = 0.0d0
+         scale = 0.0d0
+         if (i .gt. m .or. i .eq. n) go to 290
+c
+         do 220 k = l, n
+  220    scale = scale + dabs(u(i,k))
+c
+         if (scale .eq. 0.0d0) go to 290
+c
+         do 230 k = l, n
+            u(i,k) = u(i,k) / scale
+            s = s + u(i,k)**2
+  230    continue
+c
+         f = u(i,l)
+         g = -dsign(dsqrt(s),f)
+         h = f * g - s
+         u(i,l) = f - g
+c
+         do 240 k = l, n
+  240    rv1(k) = u(i,k) / h
+c
+         if (i .eq. m) go to 270
+c
+         do 260 j = l, m
+            s = 0.0d0
+c
+            do 250 k = l, n
+  250       s = s + u(j,k) * u(i,k)
+c
+            do 260 k = l, n
+               u(j,k) = u(j,k) + s * rv1(k)
+  260    continue
+c
+  270    do 280 k = l, n
+  280    u(i,k) = scale * u(i,k)
+c
+  290    x = dmax1(x,dabs(w(i))+dabs(rv1(i)))
+  300 continue
+c     .......... accumulation of right-hand transformations ..........
+      if (.not. matv) go to 410
+c     .......... for i=n step -1 until 1 do -- ..........
+      do 400 ii = 1, n
+         i = n + 1 - ii
+         if (i .eq. n) go to 390
+         if (g .eq. 0.0d0) go to 360
+c
+         do 320 j = l, n
+c     .......... double division avoids possible underflow ..........
+  320    v(j,i) = (u(i,j) / u(i,l)) / g
+c
+         do 350 j = l, n
+            s = 0.0d0
+c
+            do 340 k = l, n
+  340       s = s + u(i,k) * v(k,j)
+c
+            do 350 k = l, n
+               v(k,j) = v(k,j) + s * v(k,i)
+  350    continue
+c
+  360    do 380 j = l, n
+            v(i,j) = 0.0d0
+            v(j,i) = 0.0d0
+  380    continue
+c
+  390    v(i,i) = 1.0d0
+         g = rv1(i)
+         l = i
+  400 continue
+c     .......... accumulation of left-hand transformations ..........
+  410 if (.not. matu) go to 510
+c     ..........for i=min(m,n) step -1 until 1 do -- ..........
+      mn = n
+      if (m .lt. n) mn = m
+c
+      do 500 ii = 1, mn
+         i = mn + 1 - ii
+         l = i + 1
+         g = w(i)
+         if (i .eq. n) go to 430
+c
+         do 420 j = l, n
+  420    u(i,j) = 0.0d0
+c
+  430    if (g .eq. 0.0d0) go to 475
+         if (i .eq. mn) go to 460
+c
+         do 450 j = l, n
+            s = 0.0d0
+c
+            do 440 k = l, m
+  440       s = s + u(k,i) * u(k,j)
+c     .......... double division avoids possible underflow ..........
+            f = (s / u(i,i)) / g
+c
+            do 450 k = i, m
+               u(k,j) = u(k,j) + f * u(k,i)
+  450    continue
+c
+  460    do 470 j = i, m
+  470    u(j,i) = u(j,i) / g
+c
+         go to 490
+c
+  475    do 480 j = i, m
+  480    u(j,i) = 0.0d0
+c
+  490    u(i,i) = u(i,i) + 1.0d0
+  500 continue
+c     .......... diagonalization of the bidiagonal form ..........
+  510 tst1 = x
+c     .......... for k=n step -1 until 1 do -- ..........
+      do 700 kk = 1, n
+         k1 = n - kk
+         k = k1 + 1
+         its = 0
+c     .......... test for splitting.
+c                for l=k step -1 until 1 do -- ..........
+  520    do 530 ll = 1, k
+            l1 = k - ll
+            l = l1 + 1
+            tst2 = tst1 + dabs(rv1(l))
+            if (tst2 .eq. tst1) go to 565
+c     .......... rv1(1) is always zero, so there is no exit
+c                through the bottom of the loop ..........
+            tst2 = tst1 + dabs(w(l1))
+            if (tst2 .eq. tst1) go to 540
+  530    continue
+c     .......... cancellation of rv1(l) if l greater than 1 ..........
+  540    c = 0.0d0
+         s = 1.0d0
+c
+         do 560 i = l, k
+            f = s * rv1(i)
+            rv1(i) = c * rv1(i)
+            tst2 = tst1 + dabs(f)
+            if (tst2 .eq. tst1) go to 565
+            g = w(i)
+            h = pythag(f,g)
+            w(i) = h
+            c = g / h
+            s = -f / h
+            if (.not. matu) go to 560
+c
+            do 550 j = 1, m
+               y = u(j,l1)
+               z = u(j,i)
+               u(j,l1) = y * c + z * s
+               u(j,i) = -y * s + z * c
+  550       continue
+c
+  560    continue
+c     .......... test for convergence ..........
+  565    z = w(k)
+         if (l .eq. k) go to 650
+c     .......... shift from bottom 2 by 2 minor ..........
+         if (its .eq. 30) go to 1000
+         its = its + 1
+         x = w(l)
+         y = w(k1)
+         g = rv1(k1)
+         h = rv1(k)
+         f = 0.5d0 * (((g + z) / h) * ((g - z) / y) + y / h - h / y)
+         g = pythag(f,1.0d0)
+         f = x - (z / x) * z + (h / x) * (y / (f + dsign(g,f)) - h)
+c     .......... next qr transformation ..........
+         c = 1.0d0
+         s = 1.0d0
+c
+         do 600 i1 = l, k1
+            i = i1 + 1
+            g = rv1(i)
+            y = w(i)
+            h = s * g
+            g = c * g
+            z = pythag(f,h)
+            rv1(i1) = z
+            c = f / z
+            s = h / z
+            f = x * c + g * s
+            g = -x * s + g * c
+            h = y * s
+            y = y * c
+            if (.not. matv) go to 575
+c
+            do 570 j = 1, n
+               x = v(j,i1)
+               z = v(j,i)
+               v(j,i1) = x * c + z * s
+               v(j,i) = -x * s + z * c
+  570       continue
+c
+  575       z = pythag(f,h)
+            w(i1) = z
+c     .......... rotation can be arbitrary if z is zero ..........
+            if (z .eq. 0.0d0) go to 580
+            c = f / z
+            s = h / z
+  580       f = c * g + s * y
+            x = -s * g + c * y
+            if (.not. matu) go to 600
+c
+            do 590 j = 1, m
+               y = u(j,i1)
+               z = u(j,i)
+               u(j,i1) = y * c + z * s
+               u(j,i) = -y * s + z * c
+  590       continue
+c
+  600    continue
+c
+         rv1(l) = 0.0d0
+         rv1(k) = f
+         w(k) = x
+         go to 520
+c     .......... convergence ..........
+  650    if (z .ge. 0.0d0) go to 700
+c     .......... w(k) is made non-negative ..........
+         w(k) = -z
+         if (.not. matv) go to 700
+c
+         do 690 j = 1, n
+  690    v(j,k) = -v(j,k)
+c
+  700 continue
+c
+      go to 1001
+c     .......... set error -- no convergence to a
+c                singular value after 30 iterations ..........
+ 1000 ierr = k
+ 1001 return
+      end
Index: /branches/sc_branches/pantasks_condor/doc/todo.txt
===================================================================
--- /branches/sc_branches/pantasks_condor/doc/todo.txt	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/doc/todo.txt	(revision 34783)
@@ -0,0 +1,4 @@
+
+- convert and include all mana/dimm/status derived functions
+- local scalar variables
+- break / continue in: for, macro, 
Index: /branches/sc_branches/pantasks_condor/dvo/Makefile
===================================================================
--- /branches/sc_branches/pantasks_condor/dvo/Makefile	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/dvo/Makefile	(revision 34783)
@@ -0,0 +1,136 @@
+default: dvo
+
+include ../../../Makefile.System
+HOME    =       $(ROOT)/src/opihi
+BIN     =       $(HOME)/bin
+LIB     =       $(HOME)/lib
+INC     =       $(HOME)/include
+SRC     =       $(HOME)/dvo
+DATA    =       $(DESTDATA)/dvo
+include ../Makefile.Common
+
+# programs may add their own internal requirements here
+LIBS1   =       -lbasiccmd -ldatacmd -lastrocmd -lshell -ldata 
+LIBS2   =       -ldvo -lkapa -lFITS -lohana
+FULL_CFLAGS   = $(BASE_CFLAGS)
+FULL_CPPFLAGS = $(BASE_CPPFLAGS)
+FULL_LDFLAGS  = $(LIBS1) $(LIBS2) $(BASE_LDFLAGS)
+
+# dvo user commands and support functions ########################
+funcs = \
+$(SRC)/init.$(ARCH).o            	\
+$(SRC)/cmpReadFile.$(ARCH).o		\
+$(SRC)/compare.$(ARCH).o                \
+$(SRC)/dvomisc.$(ARCH).o		\
+$(SRC)/dvo_host_utils.$(ARCH).o		\
+$(SRC)/region_list.$(ARCH).o		\
+$(SRC)/find_matches.$(ARCH).o		\
+$(SRC)/photometry.$(ARCH).o             
+
+cmds = \
+$(SRC)/avextract.$(ARCH).o	  	\
+$(SRC)/avmatch.$(ARCH).o	  	\
+$(SRC)/badimages.$(ARCH).o	  	\
+$(SRC)/calextract.$(ARCH).o      	\
+$(SRC)/calmextract.$(ARCH).o     	\
+$(SRC)/catdir.$(ARCH).o             	\
+$(SRC)/ccd.$(ARCH).o             	\
+$(SRC)/cmatch.$(ARCH).o	  	\
+$(SRC)/cmd.$(ARCH).o             	\
+$(SRC)/cmpload.$(ARCH).o	  	\
+$(SRC)/cmpread.$(ARCH).o	  	\
+$(SRC)/ddmags.$(ARCH).o	  	\
+$(SRC)/detrend.$(ARCH).o	  	\
+$(SRC)/dmagaves.$(ARCH).o	  	\
+$(SRC)/dmagmeas.$(ARCH).o	  	\
+$(SRC)/dmags.$(ARCH).o		  	\
+$(SRC)/dmt.$(ARCH).o		  	\
+$(SRC)/elixir.$(ARCH).o                \
+$(SRC)/fitcolors.$(ARCH).o             \
+$(SRC)/fitsed.$(ARCH).o                \
+$(SRC)/gcat.$(ARCH).o		  	\
+$(SRC)/catlist.$(ARCH).o		  	\
+$(SRC)/gimages.$(ARCH).o	  	\
+$(SRC)/gstar.$(ARCH).o		  	\
+$(SRC)/hosts.$(ARCH).o	  	\
+$(SRC)/images.$(ARCH).o	  	\
+$(SRC)/imbox.$(ARCH).o		  	\
+$(SRC)/imdata.$(ARCH).o	  	\
+$(SRC)/imdense.$(ARCH).o	  	\
+$(SRC)/imextract.$(ARCH).o	  	\
+$(SRC)/imlist.$(ARCH).o	  	\
+$(SRC)/imphot.$(ARCH).o	  	\
+$(SRC)/imrough.$(ARCH).o	  	\
+$(SRC)/imsearch.$(ARCH).o	  	\
+$(SRC)/lcat.$(ARCH).o		  	\
+$(SRC)/lcurve.$(ARCH).o	  	\
+$(SRC)/lightcurve.$(ARCH).o	  	\
+$(SRC)/mextract.$(ARCH).o	  	\
+$(SRC)/mmatch.$(ARCH).o	  	\
+$(SRC)/mmextract.$(ARCH).o	  	\
+$(SRC)/objectcoverage.$(ARCH).o	  	\
+$(SRC)/photcodes.$(ARCH).o	  	\
+$(SRC)/pmeasure.$(ARCH).o	  	\
+$(SRC)/paverage.$(ARCH).o	  	\
+$(SRC)/procks.$(ARCH).o	  	\
+$(SRC)/remote.$(ARCH).o	  	\
+$(SRC)/skycat.$(ARCH).o	  	\
+$(SRC)/skycoverage.$(ARCH).o	  	\
+$(SRC)/skyregion.$(ARCH).o	  	\
+$(SRC)/showtile.$(ARCH).o	  	\
+$(SRC)/simage.$(ARCH).o	  	\
+$(SRC)/subpix.$(ARCH).o  \
+$(SRC)/version.$(ARCH).o
+
+libs = \
+$(DESTLIB)/libshell.a \
+$(DESTLIB)/libdata.a \
+$(DESTLIB)/libbasiccmd.a \
+$(DESTLIB)/libastrocmd.a \
+$(DESTLIB)/libdatacmd.a
+
+dvo: $(BIN)/dvo.$(ARCH)
+$(SRC)/dvo.$(ARCH).o : $(libs)
+$(BIN)/dvo.$(ARCH)   : $(funcs) $(cmds) $(SRC)/dvo.$(ARCH).o
+
+dvo_client: $(BIN)/dvo_client.$(ARCH)
+$(SRC)/dvo_client.$(ARCH).o : $(libs)
+$(BIN)/dvo_client.$(ARCH)   : $(funcs) $(cmds) $(SRC)/dvo_client.$(ARCH).o
+
+dvo.install: $(DESTBIN)/dvo
+dvo_client.install: $(DESTBIN)/dvo_client
+
+install: $(DESTBIN)/dvo help modules
+
+help: clean-help cmd.basic.help cmd.data.help cmd.astro.help dvo.help
+
+$(funcs) $(cmds) $(libs) : $(INC)/dvoshell.h
+
+modules: dvo.modules
+
+.PHONY: dvo dvo_client
+
+# deprecated functions: verify & delete
+#$(SRC)/abszero.$(ARCH).o \
+#$(SRC)/cals.$(ARCH).o \
+#$(SRC)/dumpmags.$(ARCH).o \
+#$(SRC)/extract.$(ARCH).o \
+#$(SRC)/gtypes.$(ARCH).o \
+#$(SRC)/photresid.$(ARCH).o \
+#$(SRC)/resid.$(ARCH).o \
+#$(SRC)/zeropts.$(ARCH).o
+#$(SRC)/objload.$(ARCH).o \ - make sure we have vect to tv
+#$(SRC)/ccdextract.$(ARCH).o \
+#$(SRC)/cmdextract.$(ARCH).o \
+#$(SRC)/dmagextract.$(ARCH).o \
+#$(SRC)/ddmagextract.$(ARCH).o \
+#$(SRC)/pcat.$(ARCH).o		  	\
+
+# future functions, not fully implemented
+#$(SRC)/detrend.$(ARCH).o \
+#$(SRC)/getxtra.$(ARCH).o \
+#$(SRC)/addxtra.$(ARCH).o \
+
+# functions that need to be updated
+#$(SRC)/gregions.$(ARCH).o \
+
Index: /branches/sc_branches/pantasks_condor/dvo/addxtra.c
===================================================================
--- /branches/sc_branches/pantasks_condor/dvo/addxtra.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/dvo/addxtra.c	(revision 34783)
@@ -0,0 +1,60 @@
+# include "dvoshell.h"
+
+typedef struct {
+  char type[64];
+  char name[64];
+  char source[256];
+  char mode[64];
+  char value[64];
+  char range[64];
+  double R, D;
+  int averef;
+} Xtras;
+
+int addxtra (int argc, char **argv) {
+  
+  Source = (char *) NULL;
+  if (N = get_argument (argc, argv, "-source")) {
+    remove_argument (N, &argc, argv);
+    Source = strcreate (atof[N]);
+    remove_argument (N, &argc, argv);
+  }
+  Name = (char *) NULL;
+  if (N = get_argument (argc, argv, "-name")) {
+    remove_argument (N, &argc, argv);
+    Name = strcreate (atof[N]);
+    remove_argument (N, &argc, argv);
+  }
+  Mode = (char *) NULL;
+  if (N = get_argument (argc, argv, "-mode")) {
+    remove_argument (N, &argc, argv);
+    Mode = strcreate (atof[N]);
+    remove_argument (N, &argc, argv);
+  }
+  Range = (char *) NULL;
+  if (N = get_argument (argc, argv, "-range")) {
+    remove_argument (N, &argc, argv);
+    Range = strcreate (atof[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  if (argc != 6) {
+    gprint (GP_ERR, "USAGE: addxtra R D radius (type) (value)\n");
+    return (FALSE);
+  }
+  
+  /*
+
+  validate the input values (type, defines the needed options)
+  find catalog (based on r,d)
+  load catalog (need to load measures?)
+  find the object
+  find the xtra entry (sorted by ra/dec? sorted by averef?)
+  add new entry
+  save catalog 
+  */
+}
+
+/* 
+  addxtra R D dR type value -name (name) -source (source) -mode (mode) -range (range) 
+*/
Index: /branches/sc_branches/pantasks_condor/dvo/aregion.c
===================================================================
--- /branches/sc_branches/pantasks_condor/dvo/aregion.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/dvo/aregion.c	(revision 34783)
@@ -0,0 +1,80 @@
+# include "dvoshell.h"
+# include "hstgsc.h"
+
+/* find region file which contains ra, dec */
+void aregion (GSCRegion *region, FILE *f, double ra, double dec, char *path) {
+  
+  char buffer[28800], temp[50], file[50];
+  double RA0, RA1, DEC0, DEC1;
+  int i, NBigDec, NLINES, done;
+  
+  ra = ohana_normalize_angle (ra);
+
+  if (dec >= 86.25) {
+    sprintf (file, "%s/n8230/pole.cpt", path);
+    region[0].DEC[0] = 86.25;
+    region[0].DEC[1] = 93.75;
+    region[0].RA[0] =  0.0;
+    region[0].RA[1] =  360.0;
+    strcpy (region[0].filename, file);
+    return;
+  }
+    
+  NBigDec = -1;
+  for (i = 0; i < 12; i++) {
+    if ((dec >= BigDecBounds[i]) && (dec < BigDecBounds[i+1])) {
+      NBigDec = i;
+      break;
+    }
+  }
+  if (NBigDec < 0) {
+    for (i = 13; i < 24; i++) {
+      if ((dec < BigDecBounds[i]) && (dec >= BigDecBounds[i+1])) {
+	NBigDec = i;
+	break;
+      }
+    }
+  }
+  if (NBigDec < 0) {
+    gprint (GP_ERR, "dec out of range: %f\n", dec);
+  }
+    
+  NLINES = 0;
+  for (i = 0; i < NBigDec; i++) {
+    NLINES += NDecLines[i];
+  }
+  fseeko (f, 5*2880 + 48*NLINES, SEEK_SET);
+      
+  done = FALSE;
+  fread (buffer, 1, 48*NDecLines[NBigDec], f);
+  for (i = 0; !done && (i < NDecLines[NBigDec]); i++) {
+    strncpy (temp, &buffer[i*48], 48);
+    temp[49] = 0;
+    hstgsc_hms_to_deg (&RA0, &RA1, &DEC0, &DEC1, &temp[7]);
+    if (RA1 < RA0) RA1 += 360.0;
+    if ((dec >= 0) && (dec >= DEC0) && (dec < DEC1) && (ra >= RA0) && (ra < RA1)) {
+      done = TRUE;
+    }
+    if ((dec < 0) && (dec < DEC0) && (dec >= DEC1) && (ra >= RA0) && (ra < RA1)) {
+      done = TRUE;
+    }
+  }
+
+  if (!done) {
+    gprint (GP_ERR, "error in search: %f %f\n", ra, dec);
+    exit (0);
+  }
+  temp[5] = 0;
+  sprintf (file, "%s/%s/%s.cpt", path, Dec2Sections[NBigDec],&temp[1]);
+  if (DEC0 < DEC1) {
+    region[0].DEC[0] = DEC0;
+    region[0].DEC[1] = DEC1;
+  } else {
+    region[0].DEC[0] = DEC1;
+    region[0].DEC[1] = DEC0;
+  }     
+  region[0].RA[0] = RA0;
+  region[0].RA[1] = RA1;
+  strcpy (region[0].filename, file);
+  return;
+}
Index: /branches/sc_branches/pantasks_condor/dvo/avextract.c
===================================================================
--- /branches/sc_branches/pantasks_condor/dvo/avextract.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/dvo/avextract.c	(revision 34783)
@@ -0,0 +1,333 @@
+# include "dvoshell.h"
+
+int avextract (int argc, char **argv) {
+  
+  off_t i, j, n, m;
+  int N, Npts, NPTS, last, next, state, Nfields, Nreturn, Ncstack, Nstack;
+  int Nsecfilt, VERBOSE, needMeasures;
+  char **cstack, name[1024];
+  void *Signal;
+
+  Catalog catalog;
+
+  Vector **vec;
+  dbStack *stack;
+  dbField *fields;
+  dbValue *values;
+  SkyList *skylist;
+  SkyRegionSelection *selection;
+
+  /* defaults */
+  vec = NULL;
+  stack = NULL;
+  fields = NULL;
+  values = NULL;
+  skylist = NULL;
+  selection = NULL;
+
+  if ((N = get_argument (argc, argv, "-h"))) goto help;
+  if ((N = get_argument (argc, argv, "--help"))) goto help;
+
+  VERBOSE = FALSE;
+  if ((N = get_argument (argc, argv, "-v"))) {
+    remove_argument (N, &argc, argv);
+    VERBOSE = TRUE;
+  }
+
+  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"))) {
+    remove_argument (N, &argc, argv);
+    PARALLEL = TRUE;
+  }
+
+  // use remote tables, but not dvo_client..
+  int PARALLEL_LOCAL = FALSE;
+  HostTable *table = NULL;
+  if ((N = get_argument (argc, argv, "-parallel-local"))) {
+    remove_argument (N, &argc, argv);
+    PARALLEL_LOCAL = TRUE;
+
+    char *CATDIR = GetCATDIR();
+    if (!CATDIR) {
+      gprint (GP_ERR, "CATDIR is not set\n");
+      return FALSE;
+    }
+    SkyTable *sky = GetSkyTable();
+    if (!sky) {
+      gprint (GP_ERR, "failed to load sky table for database\n");
+      return FALSE;
+    }
+    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;
+    }    
+  }
+
+  // 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;
+  }
+
+  dvo_catalog_init (&catalog, TRUE);
+
+  /* load photcode information */
+  if (!InitPhotcodes ()) goto escape;
+  Nsecfilt = GetPhotcodeNsecfilt ();
+
+  // parse skyregion options.  NOTE: this is stripped off in parallel operation and always
+  // defined for the client via the -skyregion option.  The dvo_client parses this
+  // argument in the main program, before it is passed to the command (like mextract)
+  if ((selection = SetRegionSelection (&argc, argv)) == NULL) {
+    print_error(); 
+    goto escape; 
+  }
+
+  // init locally static variables (time refs)
+  dbExtractAveragesInit (); 
+
+  // command-line is of the form: avextract field,field, field [where (field op value)...]
+
+  // parse the fields to be extracted and returned
+  fields = dbCmdlineFields (argc, argv, DVO_TABLE_AVERAGE, &last, &Nfields);
+  if (fields == NULL) goto escape;
+  if (Nfields == 0) {
+    FreeSkyRegionSelection (selection);
+    dbFreeFields (fields, Nfields);
+    dvo_catalog_free (&catalog);
+    goto help;
+  }
+
+  // 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-next, &argv[next], &Ncstack);
+  
+  // construct the db Boolean math stack (frees cstack)
+  stack = dbRPN (Ncstack, cstack, &Nstack);
+  if (Ncstack && !Nstack) {
+    print_error(); 
+    goto escape; 
+  }
+
+  // add the skyregion limits to the where statement (or create)
+  dbAstroRegionLimits (&stack, &Nstack, selection, DVO_TABLE_AVERAGE);
+
+  // parse stack elements into fields and scalars as needed
+  Nreturn = Nfields; 
+  if (!dbCheckStack (stack, Nstack, DVO_TABLE_AVERAGE, &fields, &Nfields)) goto escape;
+
+  /* load region corresponding to selection above */
+  if ((skylist = SelectRegions (selection)) == NULL) goto escape;
+
+  // this does all the work of re-packaging the command, calling it on the remote machines, then loading in the results
+  if (PARALLEL && !HOST_ID) {
+    int status = HostTableParallelOps (argc, argv, RESULT_FILE, 0, VERBOSE);
+
+    dbFreeFields (fields, Nfields);
+    dbFreeStack (stack, Nstack);
+    free (stack);
+    FreeSkyRegionSelection (selection);
+    dvo_catalog_free (&catalog);
+
+    return status;
+  }
+
+  /* create output storage vectors */
+  Npts = 0;
+  NPTS = 100;
+  ALLOCATE (values, dbValue, Nfields);
+  ALLOCATE (vec, Vector *, Nreturn);
+  for (i = 0; i < Nreturn; i++) {
+    if (ISNUM(fields[i].name[0])) {
+      sprintf (name, "v_%s", fields[i].name);
+    } else {
+      sprintf (name, "%s", fields[i].name);
+    }
+    if ((vec[i] = SelectVector (name, ANYVECTOR, TRUE)) == NULL) goto escape;
+    ResetVector (vec[i], fields[i].type, NPTS);
+  }
+
+  // check the requested fields : are all average/secfilt entries, or do we need measures?
+  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;
+  }
+
+  // grab data from all selected sky regions
+  Signal = signal (SIGINT, handle_interrupt);
+  interrupt = FALSE;
+  for (i = 0; (i < skylist[0].Nregions) && !interrupt; i++) {
+
+    // does this host ID match the desired location for the table?
+    if (!HostTableTestHost(skylist[0].regions[i], HOST_ID)) continue;
+
+    /* lock, load, unlock catalog */
+    char hostfile[1024];
+    if (PARALLEL_LOCAL) {
+      int hostID = (skylist[0].regions[i]->hostFlags & DATA_USE_BCK) ? skylist[0].regions[i]->backupID : skylist[0].regions[i]->hostID;
+      int seq = table->index[hostID];
+      HOSTDIR = table->hosts[seq].pathname;
+    }
+    snprintf (hostfile, 1024, "%s/%s.cpt", HOSTDIR, skylist[0].regions[i]->name);
+    catalog.filename = (HOST_ID || PARALLEL_LOCAL) ? hostfile : skylist[0].filename[i];
+    catalog.catflags = LOAD_AVES | LOAD_SECF;
+    catalog.catflags |= needMeasures ? LOAD_MEAS : SKIP_MEAS;
+    catalog.Nsecfilt = 0;
+
+    if (VERBOSE) gprint (GP_ERR, "trying %s ("OFF_T_FMT" of "OFF_T_FMT")\n", catalog.filename,  i,  skylist[0].Nregions);
+      
+    // an error exit status here is a significant error
+    if (!dvo_catalog_open (&catalog, NULL, VERBOSE2, "r")) {
+      gprint (GP_ERR, "ERROR: failure to open catalog file %s\n", catalog.filename);
+      exit (2);
+    }
+    dvo_catalog_unlock (&catalog);
+
+    for (j = 0; (j < catalog.Naverage) && !interrupt; j++) {
+      // extract the relevant values
+      // XXX for measure values, this could be optimized for one loop over measures...
+
+      m = catalog.average[j].measureOffset;
+
+      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]);
+      }
+
+      // test the conditional statement
+      if (!dbBooleanCond (stack, Nstack, values)) continue;
+      for (n = 0; n < Nreturn; n++) {
+	if (vec[n][0].type == OPIHI_FLT) {
+	  vec[n][0].elements.Flt[Npts] = values[n].Flt;
+	} else {
+	  vec[n][0].elements.Int[Npts] = values[n].Int;
+	}
+      }
+      Npts++;
+      if (Npts >= NPTS) {
+	NPTS += 2000;
+	for (n = 0; n < Nreturn; n++) {
+	  if (vec[n][0].type == OPIHI_FLT) {
+	    REALLOCATE (vec[n][0].elements.Flt, opihi_flt, NPTS);
+	  } else {
+	    REALLOCATE (vec[n][0].elements.Int, opihi_int, NPTS);
+	  }
+	}
+      }
+    }
+    dvo_catalog_free (&catalog);
+  }
+  signal (SIGINT, Signal);
+  interrupt = FALSE;
+  for (n = 0; n < Nreturn; n++) {
+    ResetVector (vec[n], fields[n].type, Npts);
+  }
+
+  // write vectors to a table (this is used by parallel dvo operations, but can be used elsewhere)
+  if (RESULT_FILE && !SKIP_RESULTS) {
+    int status = WriteVectorTableFITS (RESULT_FILE, "RESULT", vec, Nreturn, FALSE, NULL);
+    if (!status) {
+      goto escape;
+    }
+  }
+
+  if (table) free (table);
+  if (vec) free (vec);
+  if (values) free (values);
+  dbFreeFields (fields, Nfields);
+  dbFreeStack (stack, Nstack);
+  if (stack) free (stack);
+  SkyListFree (skylist);
+  FreeSkyRegionSelection (selection);
+  return (TRUE);
+
+ escape:
+  if (table) free (table);
+  if (vec) free (vec);
+  if (values) free (values);
+  dbFreeFields (fields, Nfields);
+  dbFreeStack (stack, Nstack);
+  if (stack) free (stack);
+  SkyListFree (skylist);
+  FreeSkyRegionSelection (selection);
+  dvo_catalog_free (&catalog);
+  return (FALSE);
+
+ help:
+  gprint (GP_ERR, "USAGE: avextract field[,field,field...] where (expression)\n");
+
+  if ((argc > N + 1) && !strcasecmp (argv[N+1], "fields")) {
+    gprint (GP_ERR, "  RA : right ascension (J2000) [degrees]\n");
+    gprint (GP_ERR, "  DEC : declination [degrees]\n");
+    gprint (GP_ERR, "  GLON : galactic longitude [degrees]\n");
+    gprint (GP_ERR, "  GLAT : galactic latitude [degrees]\n");
+    gprint (GP_ERR, "  ELON : ecliptic longitude [degrees]\n");
+    gprint (GP_ERR, "  ELAT : ecliptic latitude [degrees]\n");
+    gprint (GP_ERR, "  dRA : ra scatter [degrees]\n");
+    gprint (GP_ERR, "  dDEC : dec scatter [degrees]\n");
+    gprint (GP_ERR, "  uRA : proper motion in ra [arcseconds]\n");
+    gprint (GP_ERR, "  uDEC : proper motion in dec [arcseconds]\n");
+    gprint (GP_ERR, "  duRA : proper motion error in ra [arcseconds]\n");
+    gprint (GP_ERR, "  duDEC : proper motion error in dec [arcseconds]\n");
+    gprint (GP_ERR, "  PAR : parallax\n");
+    gprint (GP_ERR, "  dPAR : parallax error \n");
+
+    gprint (GP_ERR, "  ChiSqPos : chi square of position fit \n");
+    gprint (GP_ERR, "  ChiSqPM  : chi square of proper-motion fit \n");
+    gprint (GP_ERR, "  ChiSqPar : chi square of parallax fit \n");
+
+    gprint (GP_ERR, "  Tmean : mean epoch (reference for proper motion)\n");
+    gprint (GP_ERR, "  Trange : range of times used for proper motion/parallax fit\n");
+
+    gprint (GP_ERR, "  Nmeas : number of measurements\n");
+    gprint (GP_ERR, "  Nmiss : number of non-detections\n");
+    gprint (GP_ERR, "  Npos  : number of measurments used for astrometry\n");
+    gprint (GP_ERR, "  Nastrom  : number of measurments used for astrometry (= Npos)\n");
+
+    gprint (GP_ERR, "  flags     : object flags\n");
+    gprint (GP_ERR, "  objflags  : object flags\n");
+    gprint (GP_ERR, "  obj_flags : object flags\n");
+
+    gprint (GP_ERR, "  objID : object ID (32 bit, unique in catalog)\n");
+    gprint (GP_ERR, "  catID : catalog ID (32 bit)\n");
+    gprint (GP_ERR, "  extID_hi : external ID (upper 32 of 64 bit) -- eg, PSPS ID\n");
+    gprint (GP_ERR, "  extID_lo : external ID (lower 32 of 64 bit) -- eg, PSPS ID\n");
+
+    gprint (GP_ERR, "  <photcode>:ave : average magnitude for <photcode>\n");
+    gprint (GP_ERR, "  <photcode>:ref : reference magnitude system for <photcode>\n");
+    gprint (GP_ERR, "  <photcode>:inst : first instrumental magnitude for <photcode>\n");
+    gprint (GP_ERR, "  <photcode>:cat : first catalog magnitude for <photcode>\n");
+    gprint (GP_ERR, "  <photcode>:sys : first system magnitude for <photcode>\n");
+    gprint (GP_ERR, "  <photcode>:rel : first relative magnitude for <photcode>\n");
+    gprint (GP_ERR, "  <photcode>:cal : first calibrated magnitude for <photcode> \n");
+    gprint (GP_ERR, "  <photcode>:err : magnitude error for photcode\n");
+    gprint (GP_ERR, "  <photcode>:chisq : raw chi-square of magnitude fit\n");
+    gprint (GP_ERR, "  <photcode>:ncode : number of measurements matching photcode \n");
+    gprint (GP_ERR, "  <photcode>:nphot : number of measurements used for average magnitude in this photcode\n");
+
+    // gprint (GP_ERR, "  type : dophot type (unused)\n");
+    // gprint (GP_ERR, "  typefrac : dophot type fraction (unused)\n");
+    return (FALSE);
+  }
+  gprint (GP_ERR, " avextract --help fields : for a complete listing of allowed fields\n");
+  return (FALSE);
+}
Index: /branches/sc_branches/pantasks_condor/dvo/avmatch.c
===================================================================
--- /branches/sc_branches/pantasks_condor/dvo/avmatch.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/dvo/avmatch.c	(revision 34783)
@@ -0,0 +1,359 @@
+# include "dvoshell.h"
+
+/* This function uses the 'find_match' algorithm to select the objects of interest.
+   Each entry in the match vectors (RA, DEC, RADIUS) yields a result value -- if no
+   source matches, the resulting fields are all NAN.
+
+   * choose the sky regions based on the provided RA,DEC points
+   * loop over the catalogs
+   * within a catalog, use the find_match code to find the matching coordinates
+   * use dbExtractAverages to get the fields for the matched entry 
+ */
+
+int avmatch (int argc, char **argv) {
+  
+  off_t i, j, n, m, *index;
+  int N, Ncat, Npts, NPTS, last, Nfields, Nsecfilt, Ninvec;
+  int VERBOSE;
+  char name[1024], *found;
+  void *Signal;
+  float RADIUS;
+
+  Catalog catalog;
+
+  Vector **vec, **invec, *RAvec, *DECvec;
+  dbField *fields;
+  dbValue *values;
+  SkyList *skylist;
+
+  /* defaults */
+  vec = NULL;
+  invec = NULL;
+  fields = NULL;
+  values = NULL;
+  skylist = NULL;
+  Ninvec = 0;
+
+  if ((N = get_argument (argc, argv, "-h"))) goto help;
+  if ((N = get_argument (argc, argv, "--help"))) goto help;
+
+  VERBOSE = FALSE;
+  if ((N = get_argument (argc, argv, "-v"))) {
+    remove_argument (N, &argc, argv);
+    VERBOSE = TRUE;
+  }
+
+  int PARALLEL = FALSE;
+  if ((N = get_argument (argc, argv, "-parallel"))) {
+    remove_argument (N, &argc, argv);
+    PARALLEL = TRUE;
+  }
+
+  // dump results directly to fits file (esp for parallel dvo)
+  char *CoordsFile = NULL;
+  if ((N = get_argument (argc, argv, "-coords"))) {
+    remove_argument (N, &argc, argv);
+    CoordsFile = strcreate(argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  if (!CoordsFile && (argc < 5)) goto help;
+  if ( CoordsFile && (argc < 3)) goto help;
+
+  dvo_catalog_init (&catalog, TRUE);
+
+  /* load photcode information */
+  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
+  RAvec  = NULL;
+  DECvec = NULL;
+  if (PARALLEL && !HOST_ID) {
+    if (!CoordsFile) {
+      // get vectors corresponding to coordinates of interest
+      if ((RAvec  = SelectVector (argv[1], ANYVECTOR, TRUE)) == NULL) goto help;
+      if ((DECvec = SelectVector (argv[2], ANYVECTOR, TRUE)) == NULL) goto help;
+      
+      ALLOCATE (vec, Vector *, 2);
+      vec[0] = RAvec;
+      vec[1] = DECvec;
+
+      CoordsFile = abspath("coords.fits", 1024);
+      int status = WriteVectorTableFITS (CoordsFile, "COORDS", vec, 2, FALSE, NULL);
+      if (!status) goto escape;
+    }
+
+    char *targv1 = argv[1];
+    char *targv2 = argv[2];
+    argv[1] = strcreate ("-coords");
+    argv[2] = strcreate (CoordsFile);
+    free (CoordsFile);
+
+    // I need to pass the RA & DEC vectors to the remote clients...
+    int status = HostTableParallelOps (argc, argv, RESULT_FILE, RAvec->Nelements, VERBOSE);
+    if (vec) free (vec);
+    
+    free (argv[1]);
+    free (argv[2]);
+    argv[1] = targv1;
+    argv[2] = targv2;
+
+    return status;
+  }
+
+  // get vectors corresponding to coordinates of interest
+  if (CoordsFile) {
+    // read RAvec, DECvec from coords file (1st 2 fields?)
+    Ninvec = 0;
+    invec = ReadVectorTableFITS (CoordsFile, "COORDS", &Ninvec);
+    RAvec = invec[0];
+    DECvec = invec[1];
+  } else {
+    if ((RAvec  = SelectVector (argv[1], ANYVECTOR, TRUE)) == NULL) goto help;
+    if ((DECvec = SelectVector (argv[2], ANYVECTOR, TRUE)) == NULL) goto help;
+    // strip off RA & DEC
+    remove_argument (1, &argc, argv);
+    remove_argument (1, &argc, argv);
+  }
+  RADIUS = atof (argv[1]);
+  remove_argument (1, &argc, argv);
+
+  // parse the fields to be extracted and returned
+  fields = dbCmdlineFields (argc, argv, DVO_TABLE_AVERAGE, &last, &Nfields);
+  if (fields == NULL) goto help;
+  if ((Nfields == 0) || (last != argc)) {
+    dbFreeFields (fields, Nfields);
+    dvo_catalog_free (&catalog);
+    goto help;
+  }
+
+  // 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;
+
+  /* create output storage vectors */
+  NPTS = RAvec->Nelements;
+  ALLOCATE (values, dbValue, Nfields);
+  ALLOCATE (vec, Vector *, Nfields);
+  for (i = 0; i < Nfields; i++) {
+    if (ISNUM(fields[i].name[0])) {
+      sprintf (name, "v_%s", fields[i].name);
+    } else {
+      sprintf (name, "%s", fields[i].name);
+    }
+    if ((vec[i] = SelectVector (name, ANYVECTOR, TRUE)) == NULL) goto escape;
+    ResetVector (vec[i], fields[i].type, NPTS);
+    for (n = 0; n < vec[i][0].Nelements; n++) {
+      if (vec[i][0].type == OPIHI_FLT) {
+	vec[i][0].elements.Flt[n] = NAN;
+      } else {
+	vec[i][0].elements.Int[n] = 0; // or NAN_INT?
+      }
+    }
+  }
+  ALLOCATE (index, off_t, NPTS);
+  ALLOCATE (found, char, NPTS);
+  memset (found, 0, NPTS*sizeof(char));
+
+  // grab data from all selected sky regions
+  Signal = signal (SIGINT, handle_interrupt);
+  interrupt = FALSE;
+  for (i = 0; (i < skylist[0].Nregions) && !interrupt; i++) {
+
+    // does this host ID match the desired location for the table?
+    if (!HostTableTestHost(skylist[0].regions[i], HOST_ID)) continue;
+
+    /* lock, load, unlock catalog */
+    char hostfile[1024];
+    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_SECF;
+    catalog.catflags |= needMeasures ? LOAD_MEAS : SKIP_MEAS;
+    catalog.Nsecfilt = 0;
+
+    if (VERBOSE) gprint (GP_ERR, "trying %s ("OFF_T_FMT" of "OFF_T_FMT")\n", catalog.filename,  i,  skylist[0].Nregions);
+      
+    // an error exit status here is a significant error
+    if (!dvo_catalog_open (&catalog, NULL, FALSE, "r")) {
+      gprint (GP_ERR, "ERROR: failure to open catalog file %s\n", catalog.filename);
+      exit (2);
+    }
+    dvo_catalog_unlock (&catalog);  /// we can unlock here since this is read-only (do not block other access)
+    if (catalog.Naverage == 0) {
+      dvo_catalog_free (&catalog);
+      continue;
+    }
+
+    find_matches_by_vectors (skylist[0].regions[i], &catalog, RAvec, DECvec, RADIUS, index);
+
+    for (j = 0; (j < NPTS) && !interrupt; j++) {
+      Ncat = index[j];
+      Npts = j;
+
+      if (Ncat == -1) continue; // this point is not in this catalog file
+      if (Ncat == -2) continue; // no matches to this point
+
+      m = catalog.average[Ncat].measureOffset;
+
+      // reset counters for saved fields, extract fields
+      dbExtractAveragesInitAve (); 
+      for (n = 0; n < Nfields; n++) {
+	values[n] = dbExtractAverages (&catalog.average[Ncat], &catalog.secfilt[Ncat*Nsecfilt], &catalog.measure[m], &fields[n]);
+      }
+
+      // XXX if we are allowed to return more rows than the supplied RA,DEC we will need to create an output RA,DEC
+      for (n = 0; n < Nfields; n++) {
+	if (vec[n][0].type == OPIHI_FLT) {
+	  vec[n][0].elements.Flt[Npts] = values[n].Flt;
+	} else {
+	  vec[n][0].elements.Int[Npts] = values[n].Int;
+	}
+      }
+      found[Npts] = TRUE;
+    }
+    dvo_catalog_free (&catalog);
+  }
+  signal (SIGINT, Signal);
+  interrupt = FALSE;
+
+  // write vectors to a table (this is used by parallel dvo operations, but can be used elsewhere)
+  // only write the fields which were in a valid catalog
+  if (RESULT_FILE) {
+    // extend the array by one to hold index array
+    REALLOCATE (vec, Vector *, Nfields + 1);
+    vec[Nfields] = InitVector();
+    strcpy (vec[Nfields]->name, "index");
+    ResetVector (vec[Nfields], OPIHI_INT, NPTS);
+    Vector *idxVec = vec[Nfields];
+
+    // only write out the rows which were found
+    Npts = 0;
+    for (i = 0; i < NPTS; i++) {
+      if (!found[i]) continue;
+      idxVec->elements.Int[Npts] = i;
+      Npts ++;
+    }
+    int Nfound = Npts;
+    idxVec->Nelements = Nfound;
+
+    fprintf (stderr, "found %d of %d pts\n", Nfound, NPTS);
+
+    for (i = 0; i < Nfields; i++) {
+      if (vec[i][0].type == OPIHI_FLT) {
+	opihi_flt *tmp = NULL;
+	ALLOCATE (tmp, opihi_flt, Nfound);
+	Npts = 0;
+	for (j = 0; j < NPTS; j++) {
+	  if (!found[j]) continue;
+	  tmp[Npts] = vec[i][0].elements.Flt[j];
+	  Npts++;
+	}
+	free (vec[i][0].elements.Flt);
+	vec[i][0].elements.Flt = tmp;
+      } else {
+	opihi_int *tmp = NULL;
+	ALLOCATE (tmp, opihi_int, Nfound);
+	Npts = 0;
+	for (j = 0; j < NPTS; j++) {
+	  if (!found[j]) continue;
+	  tmp[Npts] = vec[i][0].elements.Int[j];
+	  Npts++;
+	}
+	free (vec[i][0].elements.Int);
+	vec[i][0].elements.Int = tmp;
+      }
+      vec[i][0].Nelements = Nfound;
+    }
+    int status = WriteVectorTableFITS (RESULT_FILE, "RESULT", vec, Nfields + 1, FALSE, NULL);
+    free (vec[Nfields]->elements.Int);
+    free (vec[Nfields]);
+    if (!status) goto escape;
+  }
+
+  if (vec) free (vec);
+  if (values) free (values);
+  if (invec) FreeVectorArray (invec, Ninvec);
+  dbFreeFields (fields, Nfields);
+  SkyListFree (skylist);
+  return (TRUE);
+
+ escape:
+  if (vec) free (vec);
+  if (values) free (values);
+  if (invec) FreeVectorArray (invec, Ninvec);
+  dbFreeFields (fields, Nfields);
+  SkyListFree (skylist);
+  return (FALSE);
+
+ help:
+  gprint (GP_ERR, "USAGE: avmatch (RA) (DEC) (RADIUS) field[,field,field...]\n");
+  gprint (GP_ERR, "   OR: avmatch -coords (filename.fits) (RADIUS) field[,field,field...]\n");
+
+  if ((argc > N + 1) && !strcasecmp (argv[N+1], "fields")) {
+    gprint (GP_ERR, "  RA : right ascension (J2000) [degrees]\n");
+    gprint (GP_ERR, "  DEC : declination [degrees]\n");
+    gprint (GP_ERR, "  GLON : galactic longitude [degrees]\n");
+    gprint (GP_ERR, "  GLAT : galactic latitude [degrees]\n");
+    gprint (GP_ERR, "  ELON : ecliptic longitude [degrees]\n");
+    gprint (GP_ERR, "  ELAT : ecliptic latitude [degrees]\n");
+    gprint (GP_ERR, "  dRA : ra scatter [degrees]\n");
+    gprint (GP_ERR, "  dDEC : dec scatter [degrees]\n");
+    gprint (GP_ERR, "  uRA : proper motion in ra [arcseconds]\n");
+    gprint (GP_ERR, "  uDEC : proper motion in dec [arcseconds]\n");
+    gprint (GP_ERR, "  duRA : proper motion error in ra [arcseconds]\n");
+    gprint (GP_ERR, "  duDEC : proper motion error in dec [arcseconds]\n");
+    gprint (GP_ERR, "  PAR : parallax\n");
+    gprint (GP_ERR, "  dPAR : parallax error \n");
+
+    gprint (GP_ERR, "  ChiSqPos : chi square of position fit \n");
+    gprint (GP_ERR, "  ChiSqPM  : chi square of proper-motion fit \n");
+    gprint (GP_ERR, "  ChiSqPar : chi square of parallax fit \n");
+
+    gprint (GP_ERR, "  Tmean : mean epoch (reference for proper motion)\n");
+    gprint (GP_ERR, "  Trange : range of times used for proper motion/parallax fit\n");
+
+    gprint (GP_ERR, "  Nmeas : number of measurements\n");
+    gprint (GP_ERR, "  Nmiss : number of non-detections\n");
+    gprint (GP_ERR, "  Npos  : number of measurments used for astrometry\n");
+    gprint (GP_ERR, "  Nastrom  : number of measurments used for astrometry (= Npos)\n");
+
+    gprint (GP_ERR, "  flags     : object flags\n");
+    gprint (GP_ERR, "  objflags  : object flags\n");
+    gprint (GP_ERR, "  obj_flags : object flags\n");
+
+    gprint (GP_ERR, "  objID : object ID (32 bit, unique in catalog)\n");
+    gprint (GP_ERR, "  catID : catalog ID (32 bit)\n");
+    gprint (GP_ERR, "  extID_hi : external ID (upper 32 of 64 bit) -- eg, PSPS ID\n");
+    gprint (GP_ERR, "  extID_lo : external ID (lower 32 of 64 bit) -- eg, PSPS ID\n");
+
+    gprint (GP_ERR, "  <photcode>:ave : average magnitude for <photcode>\n");
+    gprint (GP_ERR, "  <photcode>:ref : reference magnitude system for <photcode>\n");
+    gprint (GP_ERR, "  <photcode>:inst : first instrumental magnitude for <photcode>\n");
+    gprint (GP_ERR, "  <photcode>:cat : first catalog magnitude for <photcode>\n");
+    gprint (GP_ERR, "  <photcode>:sys : first system magnitude for <photcode>\n");
+    gprint (GP_ERR, "  <photcode>:rel : first relative magnitude for <photcode>\n");
+    gprint (GP_ERR, "  <photcode>:cal : first calibrated magnitude for <photcode> \n");
+    gprint (GP_ERR, "  <photcode>:err : magnitude error for photcode\n");
+    gprint (GP_ERR, "  <photcode>:chisq : raw chi-square of magnitude fit\n");
+    gprint (GP_ERR, "  <photcode>:ncode : number of measurements matching photcode \n");
+    gprint (GP_ERR, "  <photcode>:nphot : number of measurements used for average magnitude in this photcode\n");
+
+    // gprint (GP_ERR, "  type : dophot type (unused)\n");
+    // gprint (GP_ERR, "  typefrac : dophot type fraction (unused)\n");
+    return (FALSE);
+  }
+  gprint (GP_ERR, " avextract --help fields : for a complete listing of allowed fields\n");
+  return (FALSE);
+}
Index: /branches/sc_branches/pantasks_condor/dvo/badimages.c
===================================================================
--- /branches/sc_branches/pantasks_condor/dvo/badimages.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/dvo/badimages.c	(revision 34783)
@@ -0,0 +1,74 @@
+# include "dvoshell.h"
+
+static int badim_int = FALSE;
+void badim_escape () {
+  badim_int = TRUE;
+}
+
+int badimages (int argc, char **argv) {
+  
+  off_t i, Nimage;
+  int entry, First, Cross;
+  float *ptr;
+  double nominal, big, small, value;
+  Image *image;
+
+  if (argc != 3) {
+    gprint (GP_ERR, "USAGE: badimages entry value\n");
+    gprint (GP_ERR, "   OR: badimages -image N\n");
+    return (FALSE);
+  }
+  
+  image = LoadImagesDVO (&Nimage);
+
+  Cross = FALSE;
+  First = FALSE;
+  nominal = 1;
+  if (!strcmp (argv[1], "-image")) {
+    First = TRUE;
+    entry = atof(argv[2]);
+  } else {
+    entry = atof(argv[1]);
+    nominal = atof(argv[2]);
+    if (!strcasecmp (argv[1], "x")) {
+      Cross = TRUE;
+    }
+  }
+  
+  if (First) {
+    ptr = &image[entry].coords.crpix1;
+    for (i = 0; i < 22; i++) {
+      gprint (GP_LOG, "%2lld: %g\n", (long long) i, ptr[i]);
+    }
+    value = image[entry].coords.pc1_1*image[entry].coords.pc2_2 + image[entry].coords.pc1_2*image[entry].coords.pc2_1;
+    gprint (GP_LOG, " x: %g\n", value);
+    return (TRUE);
+  }
+  
+  big = nominal * 1.05;
+  small = nominal / 1.05;
+  if (big < small) {
+    double tmp;
+    tmp = big; big = small; small = tmp;
+  }
+  
+  badim_int = FALSE;
+  if (Cross) {
+    for (i = 0; (i < Nimage) && !badim_int; i++) {
+      value = image[i].coords.pc1_1*image[i].coords.pc2_2 + image[i].coords.pc1_2*image[i].coords.pc2_1;
+      if ((value > big) || (value < small)) {
+	gprint (GP_LOG, "%5lld %s: %d %g\n", (long long) i, image[i].name, image[i].tzero, value);
+      }
+    }
+  } else {
+    for (i = 0; (i < Nimage) && !badim_int; i++) {
+      ptr = &image[i].coords.crpix1;
+      if ((ptr[entry] > big) || (ptr[entry] < small)) {
+	gprint (GP_LOG, "%5lld %s: %d %g\n", (long long) i, image[i].name, image[i].tzero, ptr[entry]);
+      }
+    }
+  }
+  
+  FreeImagesDVO(image);
+  return (TRUE);
+}
Index: /branches/sc_branches/pantasks_condor/dvo/calextract.c
===================================================================
--- /branches/sc_branches/pantasks_condor/dvo/calextract.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/dvo/calextract.c	(revision 34783)
@@ -0,0 +1,141 @@
+# include "dvoshell.h"
+
+enum {Nd, Nm, NC, NR, ND, Np, Nc, Nt, Nx, Nd1, Nd2, NVEC};
+
+int calextract (int argc, char **argv) {
+  
+  off_t i, Nr;
+  int N, mode[2];
+
+  PhotCode *code[2];
+  Catalog catalog;
+  Vector **vec;
+  SkyList *skylist;
+  SkyRegionSelection *selection;
+
+  /* these need to be freed in the end */
+  catalog.average = NULL; 
+  catalog.secfilt = NULL;
+  catalog.measure = NULL;
+  skylist = NULL;
+  selection = NULL;
+  vec = NULL;
+
+  /* load photcode information */
+  if (!InitPhotcodes ()) return (FALSE);
+
+  /* command line arguments */
+  SetSelectionParam (0);
+
+  // parse skyregion options
+  if ((selection = SetRegionSelection (&argc, argv)) == NULL) goto escape;
+  if (!SetPhotSelections (&argc, argv, 2)) goto usage;
+
+  if (argc != 4) goto usage;
+  if (strcmp (argv[2], "-")) goto usage;
+  if (!GetPhotcodeInfo (argv[1], &code[0], &mode[0])) goto usage;
+  if (!GetPhotcodeInfo (argv[3], &code[1], &mode[1])) goto usage;
+  /* code.type must be PHOT_REF */
+
+  /* one unique value per star */
+  N = 0;
+  ALLOCATE (vec, Vector *, NVEC);
+  if ((vec[Nd] 	= SelectVector ("cal:dmag",     ANYVECTOR, TRUE)) == NULL) goto escape;
+  if ((vec[Nm] 	= SelectVector ("cal:mag",      ANYVECTOR, TRUE)) == NULL) goto escape;
+  if ((vec[NC] 	= SelectVector ("cal:color",    ANYVECTOR, TRUE)) == NULL) goto escape;
+  if ((vec[NR] 	= SelectVector ("cal:ra",       ANYVECTOR, TRUE)) == NULL) goto escape;
+  if ((vec[ND] 	= SelectVector ("cal:dec",      ANYVECTOR, TRUE)) == NULL) goto escape;
+  if ((vec[Np] 	= SelectVector ("cal:nphot",    ANYVECTOR, TRUE)) == NULL) goto escape;
+  if ((vec[Nc] 	= SelectVector ("cal:ncode",    ANYVECTOR, TRUE)) == NULL) goto escape;
+  // if ((vec[Nt] 	= SelectVector ("cal:ncrit",    ANYVECTOR, TRUE)) == NULL) goto escape;
+  if ((vec[Nx] 	= SelectVector ("cal:chisq",    ANYVECTOR, TRUE)) == NULL) goto escape;
+  if ((vec[Nd1] = SelectVector ("cal:dm1",      ANYVECTOR, TRUE)) == NULL) goto escape;
+  if ((vec[Nd2] = SelectVector ("cal:dm2",      ANYVECTOR, TRUE)) == NULL) goto escape;
+
+  /* load region corresponding to selection above */
+  if ((skylist = SelectRegions (selection)) == NULL) goto escape;
+
+  for (Nr = 0; Nr < skylist[0].Nregions; Nr++) {
+    if (Nr && !(Nr % 500)) { gprint (GP_ERR, "."); }
+
+    /* lock, load, unlock catalog */
+    catalog.filename = skylist[0].filename[Nr];
+    catalog.catflags = LOAD_AVES | LOAD_MEAS | LOAD_SECF;
+    catalog.Nsecfilt = 0;
+
+    // an error exit status here is a significant error
+    if (!dvo_catalog_open (&catalog, NULL, FALSE, "r")) {
+      fprintf (stderr, "ERROR: failure to open catalog file %s\n", catalog.filename);
+      exit (2);
+    }
+    dvo_catalog_unlock (&catalog);
+
+    # if (0)
+    int NSTAR = 1;
+    int Nsecfilt = GetPhotcodeNsecfilt ();
+    /* extract values, assign to vectors */
+    for (i = 0; i < catalog.Naverage; i++) {
+      if (i && !(i % 10000)) { gprint (GP_ERR, ","); }
+      m = catalog.average[i].offset;
+
+      if (code[0][0].c1 && code[0][0].c2 && !PhotColor (&catalog.average[i], &catalog.secfilt[i*Nsecfilt], &catalog.measure[m], code[0][0].c1, code[0][0].c2, &color)) continue;
+
+      /* find data for filter 2 (PHOT_REF) */
+      M2 = NAN;
+      dM2 = NAN;
+      for (j = 0; j < catalog.average[i].Nm; j++) {
+	if (catalog.measure[m+j].photcode != code[1][0].code) continue;
+	M2 = PhotCat  (&catalog.measure[m+j]);
+	dM2 = catalog.measure[m+j].dM;
+      }	
+      if (isnan(M2)) continue;
+
+      /* find data for filter 1 */
+      M1 = ExtractAverages (code[0], mode[0], &catalog.average[i], &catalog.secfilt[i*Nsecfilt], &catalog.measure[m], AVE_MAG);
+      if (isnan(M1)) continue;
+
+      vec[Nd ][0].elements[N] = M1 - M2;
+      vec[Nm ][0].elements[N] = M2;
+      vec[NC ][0].elements[N] = color;
+      vec[NR ][0].elements[N] = catalog.average[i].R;
+      vec[ND ][0].elements[N] = catalog.average[i].D;
+      vec[Nd1][0].elements[N] = ExtractAverages (code[0], mode[0], &catalog.average[i], &catalog.secfilt[i*Nsecfilt], &catalog.measure[m], AVE_dMAG);
+      vec[Nd2][0].elements[N] = dM2;
+      vec[Nx ][0].elements[N] = ExtractAverages (code[0], mode[0], &catalog.average[i], &catalog.secfilt[i*Nsecfilt], &catalog.measure[m], AVE_Xm);
+      vec[Nc ][0].elements[N] = ExtractAverages (code[0], mode[0], &catalog.average[i], &catalog.secfilt[i*Nsecfilt], &catalog.measure[m], AVE_NCODE);
+      vec[Np ][0].elements[N] = ExtractAverages (code[0], mode[0], &catalog.average[i], &catalog.secfilt[i*Nsecfilt], &catalog.measure[m], AVE_NPHOT);
+      // vec[Nt ][0].elements[N] = ExtractAverages (code[0], mode[0], &catalog.average[i], &catalog.secfilt[i*Nsecfilt], &catalog.measure[m], AVE_NCRIT);
+      N ++;
+      if (N == NSTAR) {
+	NSTAR += 100;
+	for (j = 0; j < NVEC; j++) {
+	  REALLOCATE (vec[j][0].elements, float, NSTAR);
+	}
+      }
+    }
+    # endif
+    dvo_catalog_free (&catalog);
+  }
+
+  for (i = 0; i < NVEC; i++) {
+    vec[i][0].Nelements = N;
+  }
+
+  SkyListFree (skylist);
+  FreeSkyRegionSelection (selection);
+  return (TRUE);
+  
+usage:
+  gprint (GP_ERR, "USAGE: calextract F - F\n");
+  return (FALSE);
+
+escape:
+  SkyListFree (skylist);
+  FreeSkyRegionSelection (selection);
+  for (i = 0; i < NVEC; i++) {
+    DeleteVector (vec[i]);
+  }
+  free (vec);
+
+  return (FALSE);
+}
Index: /branches/sc_branches/pantasks_condor/dvo/calmextract.c
===================================================================
--- /branches/sc_branches/pantasks_condor/dvo/calmextract.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/dvo/calmextract.c	(revision 34783)
@@ -0,0 +1,190 @@
+# include "dvoshell.h"
+
+enum {Nd, Nm1, Nm2, Nc, Ns, Nt, Nz, NR, ND, Nxc, Nyc, Nxm, Nym, NT, NP, Nd1, Nd2, NVEC};
+int ConcatMeasures (Vector *vec, PhotCode *code, int mode, Average *average, SecFilt *secfilt, Measure *measure, int param, int Nin);
+
+int calmextract (int argc, char **argv) {
+  
+  off_t i, k, Nr;
+  int NSTAR, N, mode[2];
+
+  Catalog catalog;
+  PhotCode *code[2];
+  SkyList *skylist;
+  SkyRegionSelection *selection;
+  Vector **vec;
+
+  /* these need to be freed in the end */
+  catalog.average = NULL; 
+  catalog.secfilt = NULL;
+  catalog.measure = NULL;
+  skylist = NULL;
+  selection = NULL;
+  vec = NULL;
+
+  /* load photcode information */
+  if (!InitPhotcodes ()) goto escape;
+
+  /* command line arguments */
+  if ((selection = SetRegionSelection (&argc, argv)) == NULL) goto escape;
+  if (!SetPhotSelections (&argc, argv, 2)) goto usage;
+
+  /* interpret required command-line arguments: calmextract F1 - F2 */
+  if (argc != 4) goto usage;
+  if (strcmp (argv[2], "-")) goto usage;
+  if (!GetPhotcodeInfo (argv[1], &code[0], &mode[0])) goto usage;
+  if (!GetPhotcodeInfo (argv[3], &code[1], &mode[1])) goto usage;
+  if (!TestPhotSelections (&code[0], &mode[0], MEAS_ZERO)) goto escape;
+
+  gprint (GP_ERR, "warning: this function may be deprecated in the future -- use avextract for better control\n");
+
+  /* returned vectors are dmag, mag, color, time, airmass, ra, dec, x, y, exptime */
+  ALLOCATE (vec, Vector *, NVEC);
+  if ((vec[Nd ] = SelectVector ("cal:dmag",     ANYVECTOR, TRUE)) == NULL) goto escape;
+  if ((vec[Nm1] = SelectVector ("cal:mag1",     ANYVECTOR, TRUE)) == NULL) goto escape;
+  if ((vec[Nm2] = SelectVector ("cal:mag2",     ANYVECTOR, TRUE)) == NULL) goto escape;
+  if ((vec[Nc ] = SelectVector ("cal:color",    ANYVECTOR, TRUE)) == NULL) goto escape;
+  if ((vec[Ns ] = SelectVector ("cal:star",     ANYVECTOR, TRUE)) == NULL) goto escape;
+  if ((vec[Nt ] = SelectVector ("cal:time",     ANYVECTOR, TRUE)) == NULL) goto escape;
+  if ((vec[Nz ] = SelectVector ("cal:airmass",  ANYVECTOR, TRUE)) == NULL) goto escape;
+  if ((vec[NR ] = SelectVector ("cal:ra",       ANYVECTOR, TRUE)) == NULL) goto escape;
+  if ((vec[ND ] = SelectVector ("cal:dec",      ANYVECTOR, TRUE)) == NULL) goto escape;
+  if ((vec[Nxc] = SelectVector ("cal:xccd",     ANYVECTOR, TRUE)) == NULL) goto escape;
+  if ((vec[Nyc] = SelectVector ("cal:yccd",     ANYVECTOR, TRUE)) == NULL) goto escape;
+  if ((vec[Nxm] = SelectVector ("cal:xmosaic",  ANYVECTOR, TRUE)) == NULL) goto escape;
+  if ((vec[Nym] = SelectVector ("cal:ymosaic",  ANYVECTOR, TRUE)) == NULL) goto escape;
+  if ((vec[NT ] = SelectVector ("cal:exptime",  ANYVECTOR, TRUE)) == NULL) goto escape;
+  if ((vec[NP ] = SelectVector ("cal:photcode", ANYVECTOR, TRUE)) == NULL) goto escape;
+  if ((vec[Nd1] = SelectVector ("cal:dm1",      ANYVECTOR, TRUE)) == NULL) goto escape;
+  if ((vec[Nd2] = SelectVector ("cal:dm2",      ANYVECTOR, TRUE)) == NULL) goto escape;
+
+  N = 0;
+  NSTAR = 100;
+  for (k = 0; k < NVEC; k++) {
+    ResetVector (vec[k], OPIHI_FLT, NSTAR);
+  }
+
+  /* load region corresponding to selection above */
+  if ((skylist = SelectRegions (selection)) == NULL) goto escape;
+  if (!SetImageSelection (TRUE, selection)) goto escape;
+
+  for (Nr = 0; Nr < skylist[0].Nregions; Nr++) {
+    if (Nr && !(Nr % 500)) { gprint (GP_ERR, "."); }
+
+    /* lock, load, unlock catalog */
+    catalog.filename = skylist[0].filename[Nr];
+    catalog.catflags = LOAD_AVES | LOAD_MEAS | LOAD_SECF;
+    catalog.Nsecfilt = 0;
+
+    // an error exit status here is a significant error
+    if (!dvo_catalog_open (&catalog, NULL, FALSE, "r")) {
+      fprintf (stderr, "ERROR: failure to open catalog file %s\n", catalog.filename);
+      exit (2);
+    }
+    dvo_catalog_unlock (&catalog);
+
+    # if (0)
+    int Nstar = 0;
+    int Nsecfilt = GetPhotcodeNsecfilt ();
+
+    /* extract values, assign to vectors */
+    for (i = 0; i < catalog.Naverage; i++) {
+      m = catalog.average[i].offset;
+
+      /* PRI/SEC must have data for color term */
+      if (code[0][0].c1 && code[0][0].c2 && !PhotColor (&catalog.average[i], &catalog.secfilt[i*Nsecfilt], &catalog.measure[m], code[0][0].c1, code[0][0].c2, &color)) continue;
+
+      /* find data for filter 2 (REF) */
+      M2 = NAN;
+      dM2 = NAN;
+      for (j = 0; j < catalog.average[i].Nm; j++) {
+	if (catalog.measure[m+j].photcode != code[1][0].code) continue;
+	M2 = PhotCat  (&catalog.measure[m+j]); 
+	dM2 = catalog.measure[m+j].dM;
+      }	
+      if (isnan(M2)) continue;
+      
+      /* find data for filter 1 */
+      M1 = ExtractMeasures (code[0], mode[0], &catalog.average[i], &catalog.secfilt[i*Nsecfilt], &catalog.measure[m], &N1, MEAS_MAG);
+      if (N1 == 0) goto skip;
+
+      /* extend storage vectors to take new data, if needed */
+      if (N + N1 >= NSTAR) {
+	NSTAR += N1 + 100;
+	for (k = 0; k < NVEC; k++) {
+	  REALLOCATE (vec[k][0].elements.Flt, opihi_flt, NSTAR);
+	}
+      }
+
+      ConcatMeasures (vec[Nt ], code[0], mode[0], &catalog.average[i], &catalog.secfilt[i*Nsecfilt], &catalog.measure[m], N1, MEAS_TIME); 
+      ConcatMeasures (vec[Nz ], code[0], mode[0], &catalog.average[i], &catalog.secfilt[i*Nsecfilt], &catalog.measure[m], N1, MEAS_AIRMASS); 
+      ConcatMeasures (vec[NT ], code[0], mode[0], &catalog.average[i], &catalog.secfilt[i*Nsecfilt], &catalog.measure[m], N1, MEAS_EXPTIME); 
+      ConcatMeasures (vec[NP ], code[0], mode[0], &catalog.average[i], &catalog.secfilt[i*Nsecfilt], &catalog.measure[m], N1, MEAS_PHOTCODE); 
+      // ConcatMeasures (vec[Nd1], code[0], mode[0], &catalog.average[i], &catalog.secfilt[i*Nsecfilt], &catalog.measure[m], N1, MEAS_dMAG); 
+      ConcatMeasures (vec[Nxc], code[0], mode[0], &catalog.average[i], &catalog.secfilt[i*Nsecfilt], &catalog.measure[m], N1, MEAS_XCCD); 
+      ConcatMeasures (vec[Nyc], code[0], mode[0], &catalog.average[i], &catalog.secfilt[i*Nsecfilt], &catalog.measure[m], N1, MEAS_YCCD); 
+      ConcatMeasures (vec[Nxm], code[0], mode[0], &catalog.average[i], &catalog.secfilt[i*Nsecfilt], &catalog.measure[m], N1, MEAS_XMOSAIC); 
+      ConcatMeasures (vec[Nym], code[0], mode[0], &catalog.average[i], &catalog.secfilt[i*Nsecfilt], &catalog.measure[m], N1, MEAS_YMOSAIC); 
+
+      for (j = 0; j < N1; j++, N++) {
+	vec[Nd ][0].elements.Flt[N] = M1[j] - M2;
+	vec[Nm1][0].elements.Flt[N] = M1[j];
+	vec[Nm2][0].elements.Flt[N] = M2;
+	vec[Nd2][0].elements.Flt[N] = dM2;
+	vec[Nc ][0].elements.Flt[N] = color;
+	vec[Ns ][0].elements.Flt[N] = Nstar;
+	vec[NR ][0].elements.Flt[N] = catalog.average[i].R;
+	vec[ND ][0].elements.Flt[N] = catalog.average[i].D;
+      }
+      Nstar ++; 
+    skip:
+      if (M1 != NULL) free (M1);
+    }
+    # endif
+    dvo_catalog_free (&catalog);
+  }
+
+  for (i = 0; i < NVEC; i++) {
+    vec[i][0].Nelements = N;
+  }
+  SkyListFree (skylist);
+  FreeSkyRegionSelection (selection);
+  FreeImageSelection ();
+  return (TRUE);
+  
+usage:
+  gprint (GP_ERR, "USAGE: dmags F - F : measure.param\n");
+  return (FALSE);
+
+escape:
+  SkyListFree (skylist);
+  FreeSkyRegionSelection (selection);
+  FreeImageSelection ();
+  for (i = 0; i < NVEC; i++) {
+    DeleteVector (vec[i]);
+  }
+  free (vec);
+  return (FALSE);
+}
+
+int ConcatMeasures (Vector *vec, PhotCode *code, int mode, Average *average, SecFilt *secfilt, Measure *measure, int Nin, int param) {
+
+  off_t i, N;
+  int Ns;
+  double *value;
+
+  value = ExtractMeasures (code, mode, average, secfilt, measure, &N, param); 
+  if (N != Nin) {
+    gprint (GP_ERR, "error!\n");
+    return (FALSE);
+  }
+
+  Ns = vec[0].Nelements;
+  for (i = 0; i < N; i++) {
+    vec[0].elements.Flt[Ns+i] = value[i];
+  }
+  vec[0].Nelements = Ns + N;
+
+  free (value);
+  return (TRUE);
+}
Index: /branches/sc_branches/pantasks_condor/dvo/catalog.c
===================================================================
--- /branches/sc_branches/pantasks_condor/dvo/catalog.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/dvo/catalog.c	(revision 34783)
@@ -0,0 +1,410 @@
+# include "dvoshell.h"
+# define NBYTES 160000
+# define BYTES_STAR 23
+# define BLOCK 1000
+# define DNSTARS 1000
+
+# define MAGSCALE 0
+# define NUMSCALE 1
+# define MISSCALE 2
+
+// XXX EAM : should this function be dropped? 
+int catlog (int argc, char **argv) {
+  
+  FILE *f;
+  Catalog catalog;
+  Vector Xvec, Yvec, Zvec;
+  int i, N, Nm, Nn, NN, Nbytes, nbytes, Bytes_Star;
+  int Ar, Ad, Am, InRegion, GSC, ASCII, DVO, FIXED;
+  char filename[128];
+  double Mz, Mr, Nz, Nr;
+  int clip, mode, IDclip, IDchoice, LimExclude;
+  RegionFile *regions;
+  int j, Nregions;
+  double Radius, Rmin, Rmax;
+  Graphdata graphmode;
+  double epoch, current_epoch;
+  char gscdir[256], catdir[256];
+  int Ngraph;
+
+  if (!GetGraph (&graphmode, NULL, NULL)) return (FALSE);
+
+  VarConfig ("GSCDIR", "%s", gscdir);
+  VarConfig ("CATDIR", "%s", catdir);
+
+  Mz = 17.0;
+  Mr = -5.0;
+  mode = MAGSCALE;
+  clip = FALSE;
+  Rmin = graphmode.coords.crval1 - 182.0;
+  Rmax = graphmode.coords.crval1 + 182.0;
+
+  regions = (RegionFile *) NULL;
+  f = (FILE *) NULL;
+  Nz = Nr = Am = Ar = Ad = 0;
+  /* either MagScale or NumScale, whichever is first is scale */
+  Nm = get_argument (argc, argv, "-m");
+  Nn = get_argument (argc, argv, "+n");
+  NN = get_argument (argc, argv, "-n");
+  if (NN && Nn) {
+    gprint (GP_ERR, "can't mix meas and miss scaling\n");
+    return (FALSE);
+  }
+ 
+  if (Nm)
+    mode = MAGSCALE;
+  if (Nn)
+    mode = NUMSCALE;
+  if (NN)
+    mode = MISSCALE;
+    
+  if (Nm && Nn) {
+    clip = TRUE;
+    if (Nm < Nn) 
+      mode = MAGSCALE;
+    else 
+      mode = NUMSCALE;
+  }
+  if (Nm && NN) {
+    clip = TRUE;
+    if (Nm < NN) 
+      mode = MAGSCALE;
+    else 
+      mode = MISSCALE;
+  }
+   
+  current_epoch = 2000.0;
+  epoch = 2000.0;
+  if ((N = get_argument (argc, argv, "-e"))) {
+    remove_argument (N, &argc, argv);
+    epoch  = atof(argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  IDchoice = 0;
+  IDclip = FALSE;
+  if ((N = get_argument (argc, argv, "-ID"))) {
+    remove_argument (N, &argc, argv);
+    IDchoice  = atof(argv[N]);
+    remove_argument (N, &argc, argv);
+    IDclip = TRUE;
+  }
+
+  LimExclude = FALSE;
+  if ((N = get_argument (argc, argv, "-x"))) {
+    remove_argument (N, &argc, argv);
+    LimExclude = TRUE;
+  }
+
+  if ((Nm = get_argument (argc, argv, "-m"))) {
+    remove_argument (Nm, &argc, argv);
+    Mr  = atof(argv[Nm]);
+    remove_argument (Nm, &argc, argv);
+    Mz = atof(argv[Nm]);
+    Mr = Mr - Mz;
+    remove_argument (Nm, &argc, argv);
+  }
+
+  if ((Nn = get_argument (argc, argv, "+n"))) {
+    remove_argument (Nn, &argc, argv);
+    Nz  = atof(argv[Nn]);
+    remove_argument (Nn, &argc, argv);
+    Nr = atof(argv[Nn]) - Nz;
+    remove_argument (Nn, &argc, argv);
+  }
+
+  if ((Nn = get_argument (argc, argv, "-n"))) {
+    remove_argument (Nn, &argc, argv);
+    Nz  = atof(argv[Nn]);
+    remove_argument (Nn, &argc, argv);
+    Nr = atof(argv[Nn]) - Nz;
+    remove_argument (Nn, &argc, argv);
+  }
+
+  InRegion = FALSE;
+  if ((N = get_argument (argc, argv, "-all"))) {
+    remove_argument (N, &argc, argv);
+    InRegion = TRUE;
+  }
+
+  Bytes_Star = 0;
+  ASCII = FALSE;
+  DVO = TRUE;
+  GSC = FALSE;
+  FIXED = FALSE;
+  if ((N = get_argument (argc, argv, "-g"))) {
+    remove_argument (N, &argc, argv);
+    GSC = TRUE;
+    ASCII = FALSE;
+    DVO = FALSE;
+  }
+
+  if ((N = get_argument (argc, argv, "-a"))) {
+    remove_argument (N, &argc, argv);
+    ASCII = TRUE;
+    GSC = FALSE;
+    DVO = FALSE;
+    Ar = atof(argv[N]);
+    remove_argument (N, &argc, argv);
+    Ad = atof(argv[N]);
+    remove_argument (N, &argc, argv);
+    Am = atof(argv[N]);
+    remove_argument (N, &argc, argv);
+    if ((N = get_argument (argc, argv, "-f"))) {
+      remove_argument (N, &argc, argv);
+      FIXED = TRUE;
+      ASCII = FALSE;
+      Bytes_Star = atof(argv[N]);
+      remove_argument (N, &argc, argv);
+    }
+  }
+
+  
+  if ((InRegion || (argc != 2)) && (!InRegion || (argc != 1))) {
+    gprint (GP_ERR, "USAGE: catalog (filename / -all) [-m M M] [-n N N] [-g] [-a RA DEC MAG] \n");
+    return (FALSE);
+  }
+  
+  if (InRegion) {
+    Radius = MAX (fabs(graphmode.xmax), fabs(graphmode.ymax));
+    regions = find_regions (graphmode.coords.crval1, graphmode.coords.crval2, Radius, &Nregions);
+  } else {
+    Nregions = 1;
+  }
+  
+  for (j = 0; j < Nregions; j++) {
+    catalog.average = 0;
+    
+    /* Load in data from an ASCII file list of ra, dec, mag */
+    if (ASCII) {
+      char *tbuffer;
+      int nstar, NSTARS;
+      double R, D, M;
+      
+      f = fopen (argv[1], "r");
+      if (f == (FILE *) NULL) {
+	gprint (GP_ERR, "ERROR: can't open catalog file: %s\n", argv[1]);
+	return (FALSE);
+      }
+      
+      nstar = 0;
+      NSTARS = DNSTARS;
+      ALLOCATE (tbuffer, char, 1024);
+      ALLOCATE (catalog.average, Average, NSTARS);
+      while (scan_line (f, tbuffer) != EOF) {
+	dparse (&R, Ar, tbuffer);
+	dparse (&D, Ad, tbuffer);
+	dparse (&M, Am, tbuffer);
+	catalog.average[nstar].R = R;
+	catalog.average[nstar].D = D;
+	catalog.average[nstar].M = M;
+	nstar++;
+	if (nstar == NSTARS - 1) {
+	  NSTARS += DNSTARS;
+	  REALLOCATE (catalog.average, Average, NSTARS);
+	}
+      }
+      fclose (f);
+      free (tbuffer);
+      REALLOCATE (catalog.average, Average, nstar);
+      catalog.Naverage = nstar;
+
+      if (epoch != current_epoch) {
+	cprecess (catalog.average, catalog.Naverage, epoch, current_epoch);
+      }
+
+    }
+    
+    /* Load in data from an ASCII file list of ra, dec, mag */
+    if (FIXED) {
+      char *tbuffer;
+      int nstar, NSTARS;
+      double R, D, M;
+      
+      f = fopen (argv[1], "r");
+      if (f == (FILE *) NULL) {
+	gprint (GP_ERR, "ERROR: can't open catalog file: %s\n", argv[1]);
+	return (FALSE);
+      }
+      
+      nstar = 0;
+      NSTARS = DNSTARS;
+      ALLOCATE (tbuffer, char, (BLOCK*Bytes_Star));
+      ALLOCATE (catalog.average, Average, NSTARS);
+      Nbytes = BLOCK*Bytes_Star;
+      while ((nbytes = fread (tbuffer, 1, Nbytes, f)) > 0) {
+	for (i = 0; i < nbytes / Bytes_Star; i++) {
+	  dparse (&R, Ar, &tbuffer[i*Bytes_Star]);
+	  dparse (&D, Ad, &tbuffer[i*Bytes_Star]);
+	  dparse (&M, Am, &tbuffer[i*Bytes_Star]);
+	  catalog.average[nstar].R = R;
+	  catalog.average[nstar].D = D;
+	  catalog.average[nstar].M = M;
+	  nstar++;
+	  if (nstar == NSTARS - 1) {
+	    NSTARS += DNSTARS;
+	    REALLOCATE (catalog.average, Average, NSTARS);
+	  }
+	}
+      }
+      fclose (f);
+      free (tbuffer);
+      REALLOCATE (catalog.average, Average, nstar);
+      catalog.Naverage = nstar;
+
+      if (epoch != current_epoch) {
+	cprecess (catalog.average, catalog.Naverage, epoch, current_epoch);
+      }
+
+    }
+    
+    /* load data from the GSC files */
+    if (GSC) {
+      char *tbuffer;
+      int nstar, NSTARS;
+      double R, D, M;
+      
+      if (InRegion) {
+	sprintf (filename, "%s/%s", gscdir, regions[j].name);
+      } else {
+	sprintf (filename, "%s/%s", gscdir, argv[1]);
+      }
+      
+      f = fopen (filename, "r");
+      if (f == (FILE *) NULL) {
+	gprint (GP_ERR, "no stars in %s, skipping\n", filename);
+	continue;
+	/* return (FALSE); */
+      }
+      
+      nstar = 0;
+      NSTARS = DNSTARS;
+      ALLOCATE (tbuffer, char, (BLOCK*BYTES_STAR));
+      ALLOCATE (catalog.average, Average, NSTARS);
+      Nbytes = BLOCK*BYTES_STAR;
+      while ((nbytes = fread (tbuffer, 1, Nbytes, f)) > 0) {
+	for (i = 0; i < nbytes / BYTES_STAR; i++) {
+	  dparse (&R, 1, &tbuffer[i*BYTES_STAR]);
+	  dparse (&D, 2, &tbuffer[i*BYTES_STAR]);
+	  dparse (&M, 3, &tbuffer[i*BYTES_STAR]);
+	  catalog.average[nstar].R = R;
+	  catalog.average[nstar].D = D;
+	  catalog.average[nstar].M = M;
+	  nstar++;
+	  if (nstar == NSTARS - 1) {
+	    NSTARS += DNSTARS;
+	    REALLOCATE (catalog.average, Average, NSTARS);
+	  }
+	}
+      }
+      fclose (f);
+      free (tbuffer);
+      REALLOCATE (catalog.average, Average, nstar);
+      catalog.Naverage = nstar;
+    }
+  
+    /* load data from the photometry database files */
+    if (DVO) {
+      
+      if (InRegion) {
+	sprintf (filename, "%s/%s", catdir, regions[j].name);
+      } else {
+	sprintf (filename, "%s/%s", catdir, argv[1]);
+      }
+      
+      /* lock, load, unlock catalog */
+      catalog.filename = filename;
+      catalog.catflags = LOAD_AVES;
+
+      // an error exit status here is a significant error
+      if (!dvo_catalog_open (&catalog, NULL, FALSE, "r")) {
+	  fprintf (stderr, "ERROR: failure to open catalog file %s\n", catalog.filename);
+	  exit (2);
+      }
+      dvo_catalog_unlock (&catalog);
+    }
+    
+    /* data has been loaded, get ready to plot it */
+    Xvec.Nelements = catalog.Naverage;
+    Yvec.Nelements = catalog.Naverage;
+    Zvec.Nelements = catalog.Naverage;
+    ALLOCATE (Xvec.elements, float, Xvec.Nelements);
+    ALLOCATE (Yvec.elements, float, Yvec.Nelements);
+    ALLOCATE (Zvec.elements, float, Zvec.Nelements);
+    /* project stars to screen display coords */
+    Xvec.Nelements = 0;
+    switch (mode) {
+    case (MAGSCALE):
+      for (N = i = 0; i < catalog.Naverage; i++) {
+	if (clip && ((catalog.average[i].Nm < Nz) || (catalog.average[i].Nm > Nr+Nz))) 
+	  continue;
+	if (IDclip && (catalog.average[i].code != IDchoice))
+	  continue;
+	Zvec.elements[N] = MIN (1.0, MAX (0.01, (catalog.average[i].M - Mz) / Mr));
+	if (LimExclude && (Zvec.elements[N] > 0.99)) continue;
+	if (Zvec.elements[N] < 0.011) continue;
+	catalog.average[i].R = ohana_normalize_angle (catalog.average[i].R);
+	while (catalog.average[i].R < Rmin) catalog.average[i].R += 360.0;
+	while (catalog.average[i].R > Rmax) catalog.average[i].R -= 360.0;
+	if (fRD_to_XY (&Xvec.elements[N], &Yvec.elements[N], catalog.average[i].R, catalog.average[i].D, &graphmode.coords)) N ++;
+      }
+      break;
+    case (NUMSCALE):
+      for (N = i = 0; i < catalog.Naverage; i++) {
+	if (clip && ((catalog.average[i].M > Mz) || (catalog.average[i].M < Mr+Mz))) 
+	  continue;
+	if (IDclip && (catalog.average[i].code != IDchoice))
+	  continue;
+	Zvec.elements[N] = MIN (1.0, MAX (0.01, (catalog.average[i].Nm - Nz) / Nr));
+	if (LimExclude && (Zvec.elements[N] == 1.0)) continue;
+	if (Zvec.elements[N] == 0.01) 
+	  continue;
+	catalog.average[i].R = ohana_normalize_angle (catalog.average[i].R);
+	while (catalog.average[i].R < Rmin) catalog.average[i].R += 360.0;
+	while (catalog.average[i].R > Rmax) catalog.average[i].R -= 360.0;
+	if (fRD_to_XY (&Xvec.elements[N], &Yvec.elements[N], catalog.average[i].R, catalog.average[i].D, &graphmode.coords)) N++;
+      }
+      break;
+    case (MISSCALE):
+      for (N = i = 0; i < catalog.Naverage; i++) {
+	if (clip && ((catalog.average[i].M > Mz) || (catalog.average[i].M < Mr+Mz))) 
+	  continue;
+	if (IDclip && (catalog.average[i].code != IDchoice))
+	  continue;
+	Zvec.elements[N] = MIN (1.0, MAX (0.01, (catalog.average[i].Nn - Nz) / Nr));
+	if (LimExclude && (Zvec.elements[N] == 1.0)) continue;
+	if (Zvec.elements[N] == 0.01) 
+	  continue;
+	catalog.average[i].R = ohana_normalize_angle (catalog.average[i].R);
+	while (catalog.average[i].R < Rmin) catalog.average[i].R += 360.0;
+	while (catalog.average[i].R > Rmax) catalog.average[i].R -= 360.0;
+	if (fRD_to_XY (&Xvec.elements[N], &Yvec.elements[N], catalog.average[i].R, catalog.average[i].D, &graphmode.coords)) N++;
+      }
+      break;
+    }
+
+    Zvec.Nelements = Yvec.Nelements = Xvec.Nelements = N;
+    REALLOCATE (Xvec.elements, float, MAX (Xvec.Nelements, 1));
+    REALLOCATE (Yvec.elements, float, MAX (Yvec.Nelements, 1));
+    REALLOCATE (Zvec.elements, float, MAX (Zvec.Nelements, 1));
+    
+    graphmode.style = 2; /* set style to points */
+    graphmode.size = -1; /* point size determined by Zvec */
+    graphmode.etype = 0; /* no errorbars */
+    PrepPlotting (N, &graphmode);
+    
+    PlotVector (N, Xvec.elements, "x");
+    PlotVector (N, Yvec.elements, "y");
+    PlotVector (N, Zvec.elements, "z");
+    
+    free (Xvec.elements);
+    free (Yvec.elements);
+    free (Zvec.elements);
+
+    if (catalog.average != 0) free (catalog.average);
+
+  }
+  return (TRUE);
+
+}
+  
Index: /branches/sc_branches/pantasks_condor/dvo/catdir.c
===================================================================
--- /branches/sc_branches/pantasks_condor/dvo/catdir.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/dvo/catdir.c	(revision 34783)
@@ -0,0 +1,38 @@
+# include "dvoshell.h"
+
+int catdir_define (int argc, char **argv) {
+  
+  char *current;
+  int status, N, VERBOSE;
+
+  VERBOSE = FALSE;
+  if ((N = get_argument (argc, argv, "-v"))) {
+    VERBOSE = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+
+  if (argc != 2) {
+    gprint (GP_ERR, "USAGE: catdir (name)\n");
+    gprint (GP_ERR, "       (name) may be a path or 'default'\n");
+    current = GetCATDIR ();
+    if (current == NULL) {
+      gprint (GP_ERR, "catdir not defined\n");
+    } else {
+      gprint (GP_ERR, "current: %s\n", current);
+    }
+    return (FALSE);
+  }
+
+  if (!strcasecmp (argv[1], "default")) {
+    status = SetCATDIR (NULL, VERBOSE);
+  } else {
+    status = SetCATDIR (argv[1], VERBOSE);
+  }
+
+  if (!status) {
+    gprint (GP_ERR, "invalid / undefined CATDIR\n");
+    return (FALSE);
+  }
+
+  return (TRUE);
+}
Index: /branches/sc_branches/pantasks_condor/dvo/catlist.c
===================================================================
--- /branches/sc_branches/pantasks_condor/dvo/catlist.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/dvo/catlist.c	(revision 34783)
@@ -0,0 +1,105 @@
+# include "dvoshell.h"
+
+// find the catalog files which are in a given region, possibly only by host
+int catlist (int argc, char **argv) {
+  
+  int i, N;
+  struct stat filestat;
+
+  int VERBOSE = FALSE;
+  if ((N = get_argument (argc, argv, "-v"))) {
+    remove_argument (N, &argc, argv);
+    VERBOSE = TRUE;
+  }
+  int ShowAll = FALSE;
+  if ((N = get_argument (argc, argv, "-all"))) {
+    remove_argument (N, &argc, argv);
+    ShowAll = TRUE;
+  }
+  int Depth = -1;
+  if ((N = get_argument (argc, argv, "-depth"))) {
+    remove_argument (N, &argc, argv);
+    Depth = atoi (argv[N]);
+    remove_argument (N, &argc, argv);    
+  }
+  int ThisHost = FALSE;
+  if ((N = get_argument (argc, argv, "-this-host"))) {
+    remove_argument (N, &argc, argv);
+    ThisHost = TRUE;
+  }
+
+  // use remote tables, but not dvo_client..
+  int PARALLEL_LOCAL = FALSE;
+  HostTable *table = NULL;
+  if ((N = get_argument (argc, argv, "-parallel-local"))) {
+    remove_argument (N, &argc, argv);
+    PARALLEL_LOCAL = TRUE;
+  }
+
+  if (argc != 5) {
+    gprint (GP_ERR, "USAGE: catlist Rmin Rmax Dmin Dmax\n");
+    return (FALSE);
+  }
+
+  float Rmin = atof(argv[1]);
+  float Rmax = atof(argv[2]);
+  float Dmin = atof(argv[3]);
+  float Dmax = atof(argv[4]);
+
+  SkyTable *sky = GetSkyTable ();
+  SkyList *skylist = SkyListByBounds (sky, Depth, Rmin, Rmax, Dmin, Dmax);
+  
+  SkyRegion **regions = skylist[0].regions;
+
+
+  if (PARALLEL_LOCAL) {
+    char *CATDIR = GetCATDIR();
+    if (!CATDIR) {
+      gprint (GP_ERR, "CATDIR is not set\n");
+      return FALSE;
+    }
+    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;
+    }    
+  }
+
+  int Nregion = 0;
+  for (i = 0; i < skylist[0].Nregions; i++) {
+
+    // skip tables that are not on this host (if -this-host supplied)
+    if (ThisHost && !HostTableTestHost (regions[i], HOST_ID)) continue;
+
+    if (PARALLEL_LOCAL) {
+      int hostID = (skylist[0].regions[i]->hostFlags & DATA_USE_BCK) ? skylist[0].regions[i]->backupID : skylist[0].regions[i]->hostID;
+      int seq = table->index[hostID];
+      HOSTDIR = table->hosts[seq].pathname;
+    }
+    char hostfile[1024];
+    snprintf (hostfile, 1024, "%s/%s.cpt", HOSTDIR, skylist[0].regions[i]->name);
+    char *filename = (HOST_ID || PARALLEL_LOCAL) ? hostfile : skylist[0].filename[i];
+
+    if (ShowAll || (stat (filename, &filestat) != -1)) {
+      if (VERBOSE) gprint (GP_ERR, "%3d %s %6.2f - %6.2f, %6.2f - %6.2f\n", i, regions[i][0].name, 
+			   regions[i][0].Rmin, regions[i][0].Rmax, regions[i][0].Dmin, regions[i][0].Dmax);
+      
+      char name[64], nameRmin[64], nameRmax[64], nameDmin[64], nameDmax[64];
+      snprintf (name, 64, "region:%d", Nregion);
+      snprintf (nameRmin, 64, "region_Rmin:%d", Nregion);
+      snprintf (nameRmax, 64, "region_Rmax:%d", Nregion);
+      snprintf (nameDmin, 64, "region_Dmin:%d", Nregion);
+      snprintf (nameDmax, 64, "region_Dmax:%d", Nregion);
+      set_str_variable (name,     regions[i][0].name);
+      set_variable (nameRmin, regions[i][0].Rmin);
+      set_variable (nameRmax, regions[i][0].Rmax);
+      set_variable (nameDmin, regions[i][0].Dmin);
+      set_variable (nameDmax, regions[i][0].Dmax);
+      Nregion ++;
+    }
+  }
+  set_int_variable ("region:n", Nregion);
+
+  if (table) free (table);
+  return (TRUE);
+}
Index: /branches/sc_branches/pantasks_condor/dvo/catstats.c
===================================================================
--- /branches/sc_branches/pantasks_condor/dvo/catstats.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/dvo/catstats.c	(revision 34783)
@@ -0,0 +1,91 @@
+# include "dvoshell.h"
+
+// return some stats on the specified catalog files
+int catstats (int argc, char **argv) {
+  
+  int i, N;
+  struct stat filestat;
+
+  int VERBOSE = FALSE;
+  if ((N = get_argument (argc, argv, "-v"))) {
+    remove_argument (N, &argc, argv);
+    VERBOSE = TRUE;
+  }
+  int ShowAll = FALSE;
+  if ((N = get_argument (argc, argv, "-all"))) {
+    remove_argument (N, &argc, argv);
+    ShowAll = TRUE;
+  }
+  int Depth = -1;
+  if ((N = get_argument (argc, argv, "-depth"))) {
+    remove_argument (N, &argc, argv);
+    Depth = atoi (argv[N]);
+    remove_argument (N, &argc, argv);    
+  }
+  int ThisHost = FALSE;
+  if ((N = get_argument (argc, argv, "-this-host"))) {
+    remove_argument (N, &argc, argv);
+    ThisHost = TRUE;
+  }
+
+  // use remote tables, but not dvo_client..
+  int PARALLEL_LOCAL = FALSE;
+  HostTable *table = NULL;
+  if ((N = get_argument (argc, argv, "-parallel-local"))) {
+    remove_argument (N, &argc, argv);
+    PARALLEL_LOCAL = TRUE;
+  }
+
+  if (argc != 5) {
+    gprint (GP_ERR, "USAGE: catstats (name)\n");
+    return (FALSE);
+  }
+
+  SkyTable *sky = GetSkyTable ();
+  SkyList *skylist = SkyListByBounds (sky, Depth, Rmin, Rmax, Dmin, Dmax);
+  SkyRegion **regions = skylist[0].regions;
+
+
+  if (PARALLEL_LOCAL) {
+    char *CATDIR = GetCATDIR();
+    if (!CATDIR) {
+      gprint (GP_ERR, "CATDIR is not set\n");
+      return FALSE;
+    }
+    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;
+    }    
+  }
+
+  int Nregion = 0;
+  for (i = 0; i < skylist[0].Nregions; i++) {
+
+    // skip tables that are not on this host (if -this-host supplied)
+    if (ThisHost && !HostTableTestHost (regions[i], HOST_ID)) continue;
+
+    if (PARALLEL_LOCAL) {
+      int hostID = (skylist[0].regions[i]->hostFlags & DATA_USE_BCK) ? skylist[0].regions[i]->backupID : skylist[0].regions[i]->hostID;
+      int seq = table->index[hostID];
+      HOSTDIR = table->hosts[seq].pathname;
+    }
+    char hostfile[1024];
+    snprintf (hostfile, 1024, "%s/%s.cpt", HOSTDIR, skylist[0].regions[i]->name);
+    char *filename = (HOST_ID || PARALLEL_LOCAL) ? hostfile : skylist[0].filename[i];
+
+    if (ShowAll || (stat (filename, &filestat) != -1)) {
+      if (VERBOSE) gprint (GP_ERR, "%3d %s %6.2f - %6.2f, %6.2f - %6.2f\n", i, regions[i][0].name, 
+			   regions[i][0].Rmin, regions[i][0].Rmax, regions[i][0].Dmin, regions[i][0].Dmax);
+      
+      char name[64];
+      snprintf (name, 64, "region:%d", Nregion);
+      set_str_variable (name, regions[i][0].name);
+      Nregion ++;
+    }
+  }
+  set_int_variable ("region:n", Nregion);
+
+  if (table) free (table);
+  return (TRUE);
+}
Index: /branches/sc_branches/pantasks_condor/dvo/ccd.c
===================================================================
--- /branches/sc_branches/pantasks_condor/dvo/ccd.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/dvo/ccd.c	(revision 34783)
@@ -0,0 +1,136 @@
+# include "dvoshell.h"
+
+int ccd (int argc, char **argv) {
+  
+  double *M1, *M2;
+  off_t i, m, k, N1, N2, i1, i2;
+  int Npts, NPTS, N;
+  int mode[4];
+  int Nsecfilt, KeepNulls;
+
+  Catalog catalog;
+  PhotCode *code[4];
+  SkyList *skylist;
+  SkyRegionSelection *selection;
+  Vector *xvec, *yvec;
+
+  /* defaults */
+  catalog.average = NULL; 
+  catalog.secfilt = NULL;
+  catalog.measure = NULL;
+  skylist = NULL;
+  selection = NULL;
+
+  /* load photcode information */
+  if (!InitPhotcodes ()) goto escape;
+  Nsecfilt = GetPhotcodeNsecfilt ();
+
+  /* interpret command-line options */
+  if ((selection = SetRegionSelection (&argc, argv)) == NULL) goto escape;
+  if (!SetPhotSelections (&argc, argv, 4)) goto usage;
+
+  KeepNulls = FALSE;
+  if ((N = get_argument (argc, argv, "-nulls"))) {
+    KeepNulls = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+
+  /* interpret command-line options */
+  if (argc != 8) goto usage;
+  if (strcmp (argv[2], "-")) goto usage;
+  if (strcmp (argv[4], ":")) goto usage;
+  if (strcmp (argv[6], "-")) goto usage;
+  if (!GetPhotcodeInfo (argv[1], &code[0], &mode[0])) return (FALSE);
+  if (!GetPhotcodeInfo (argv[3], &code[1], &mode[1])) return (FALSE);
+  if (!GetPhotcodeInfo (argv[5], &code[2], &mode[2])) return (FALSE);
+  if (!GetPhotcodeInfo (argv[7], &code[3], &mode[3])) return (FALSE);
+  if (!TestPhotSelections (&code[0], &mode[0], MEAS_ZERO)) goto escape;
+
+  /* load region corresponding to selection above */
+  if ((skylist = SelectRegions (selection)) == NULL) goto escape;
+
+  /* init vectors to save data */
+  if ((xvec = SelectVector ("xv", ANYVECTOR, TRUE)) == NULL) goto escape;
+  if ((yvec = SelectVector ("yv", ANYVECTOR, TRUE)) == NULL) goto escape;
+
+  gprint (GP_ERR, "warning: this function may be deprecated in the future -- use avextract for better control\n");
+
+  Npts = 0;
+  NPTS = 100;
+  ResetVector (xvec, OPIHI_FLT, NPTS);
+  ResetVector (yvec, OPIHI_FLT, NPTS);
+
+  // grab data from all selected sky regions
+  signal (SIGINT, handle_interrupt);
+  interrupt = FALSE;
+
+  /* loop over regions, extract data for each region */
+  for (k = 0; (k < skylist[0].Nregions) && !interrupt; k++) {
+    /* lock, load, unlock catalog */
+    catalog.filename = skylist[0].filename[k];
+    catalog.catflags = LOAD_AVES | LOAD_MEAS | LOAD_SECF;
+    catalog.Nsecfilt = 0;
+
+    // an error exit status here is a significant error
+    if (!dvo_catalog_open (&catalog, NULL, FALSE, "r")) {
+      fprintf (stderr, "ERROR: failure to open catalog file %s\n", catalog.filename);
+      exit (2);
+    }
+    dvo_catalog_unlock (&catalog);
+
+    /* get correct mags, convert to X,Y */
+    for (i = 0; (i < catalog.Naverage) && !interrupt; i++) {
+      M1 = M2 = NULL;
+      m = catalog.average[i].measureOffset;
+
+      SetSelectionParam (0);
+      M1 = ExtractDMag (&code[0], &mode[0], &catalog.average[i], &catalog.secfilt[i*Nsecfilt], &catalog.measure[m], &N1);
+      if (N1 == 0) goto skip;
+
+      SetSelectionParam (2);
+      M2 = ExtractDMag (&code[2], &mode[2], &catalog.average[i], &catalog.secfilt[i*Nsecfilt], &catalog.measure[m], &N2);
+      if (N2 == 0) {
+	if (KeepNulls) {
+	  ALLOCATE (M2, double, 1);
+	  N2 = 1;
+	  M2[0] = NAN;
+	} else {
+	  goto skip;
+	}
+      }
+
+      for (i1 = 0; i1 < N1; i1++) {
+	for (i2 = 0; i2 < N2; i2++) {
+	  xvec[0].elements.Flt[Npts] = M1[i1];
+	  yvec[0].elements.Flt[Npts] = M2[i2];
+	  Npts++;
+	  if (Npts >= NPTS) {
+	    NPTS += 2000;
+	    REALLOCATE (xvec[0].elements.Flt, opihi_flt, NPTS);
+	    REALLOCATE (yvec[0].elements.Flt, opihi_flt, NPTS);
+	  }
+	}
+      }
+    skip:
+      if (M1 != NULL) free (M1);
+      if (M2 != NULL) free (M2);
+    }
+    dvo_catalog_free (&catalog);
+  }
+
+  xvec[0].Nelements = yvec[0].Nelements = Npts;
+  SkyListFree (skylist);
+  FreeSkyRegionSelection (selection);
+  return (TRUE);
+
+usage:
+  gprint (GP_ERR, "USAGE: ccd F - F : F - F\n");
+  return (FALSE);
+
+escape:
+  SkyListFree (skylist);
+  FreeSkyRegionSelection (selection);
+  dvo_catalog_free (&catalog);
+  return (FALSE);
+}
+
Index: /branches/sc_branches/pantasks_condor/dvo/cmatch.c
===================================================================
--- /branches/sc_branches/pantasks_condor/dvo/cmatch.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/dvo/cmatch.c	(revision 34783)
@@ -0,0 +1,82 @@
+# include "dvoshell.h"
+
+int cmatch (int argc, char **argv) {
+  
+  Catalog catalog1, catalog2;
+  char filename[128];
+  double radius;
+  Vector *rvec, *dvec, *mvec, *drvec, *ddvec, *dmvec;
+
+  if (argc != 9) {
+    gprint (GP_ERR, "USAGE: cmatch file radius (RA) (DEC) (Mag) (dRA) (dDEC) (dMag)\n");
+    gprint (GP_ERR, "       match a set of object coordinates with a DVO db table\n");
+    return (FALSE);
+  }
+
+  /*** this function is not well-defined.  re-assess it and re-code it ***/
+  gprint (GP_ERR, "disabled for now\n");
+  return (FALSE);
+
+  radius = atof (argv[2]);
+
+  if ((rvec  = SelectVector (argv[3], ANYVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((dvec  = SelectVector (argv[4], ANYVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((mvec  = SelectVector (argv[5], ANYVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((drvec = SelectVector (argv[6], ANYVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((ddvec = SelectVector (argv[7], ANYVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((dmvec = SelectVector (argv[8], ANYVECTOR, TRUE)) == NULL) return (FALSE);
+
+  /* load data from the photometry database file */
+  catalog1.filename = filename;
+  catalog1.catflags = LOAD_AVES | LOAD_MEAS | LOAD_SECF;
+
+  // an error exit status here is a significant error
+  if (!dvo_catalog_open (&catalog1, NULL, FALSE, "r")) {
+      fprintf (stderr, "ERROR: failure to open catalog file %s\n", catalog1.filename);
+      exit (2);
+  }
+  dvo_catalog_unlock (&catalog1);
+  gprint (GP_ERR, "read "OFF_T_FMT" stars from phot catalog file %s\n",  catalog1.Naverage, filename);
+
+  /* this is for loading from a text file, presumably hstgsc or usno
+     replace this with references to the ra and dec vectors?
+  nstar = 0;
+  NSTARS = DNSTARS;
+  ALLOCATE (tbuffer, char, (BLOCK*BYTES_STAR));
+  ALLOCATE (catalog2.average, Average, NSTARS);
+  Nbytes = BLOCK*BYTES_STAR;
+  while ((nbytes = fread (tbuffer, 1, Nbytes, f)) > 0) {
+    for (i = 0; i < nbytes / BYTES_STAR; i++) {
+      dparse (&R, 1, &tbuffer[i*BYTES_STAR]);
+      dparse (&D, 2, &tbuffer[i*BYTES_STAR]);
+      dparse (&M, 3, &tbuffer[i*BYTES_STAR]);
+      catalog2.average[nstar].R = R;
+      catalog2.average[nstar].D = D;
+      catalog2.average[nstar].M = M * 1000.0;
+      nstar++;
+      if (nstar == NSTARS - 1) {
+	NSTARS += DNSTARS;
+	REALLOCATE (catalog2.average, Average, NSTARS);
+      }
+    }
+  }
+  free (tbuffer);
+  REALLOCATE (catalog2.average, Average, MAX (nstar, 1));
+  catalog2.Naverage = nstar;
+  fclose (f);
+  */
+
+  /* sort data in order of RA */
+  sortave (catalog1.average, catalog1.Naverage);
+  sortave (catalog2.average, catalog2.Naverage);
+
+  /* data has been loaded, use gcompare algorithm to match */
+  compare (&catalog1, &catalog2, rvec, dvec, mvec, drvec, ddvec, dmvec, radius);
+
+  dvo_catalog_free (&catalog1);
+  free (catalog2.average);
+
+  return (TRUE);
+
+}
+  
Index: /branches/sc_branches/pantasks_condor/dvo/cmd.c
===================================================================
--- /branches/sc_branches/pantasks_condor/dvo/cmd.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/dvo/cmd.c	(revision 34783)
@@ -0,0 +1,135 @@
+# include "dvoshell.h"
+
+int cmd (int argc, char **argv) { /* really need to think about upper limits & how to represent them */
+  
+  double *M1, *M3;
+  off_t i, j, m, N1, N3, i1, i3;
+  int N;
+  int Npts, NPTS, mode[3];
+  int Nsecfilt, KeepNulls;
+
+  PhotCode *code[3];
+  Catalog catalog;
+  SkyList *skylist;
+  SkyRegionSelection *selection;
+  Vector *xvec, *yvec;
+
+  /* defaults */
+  catalog.average = NULL; 
+  catalog.secfilt = NULL;
+  catalog.measure = NULL;
+  skylist = NULL;
+  selection = NULL;
+
+  /* load photcode information */
+  if (!InitPhotcodes ()) goto escape;
+  Nsecfilt = GetPhotcodeNsecfilt ();
+
+  /* interpret command-line options */
+  if ((selection = SetRegionSelection (&argc, argv)) == NULL) goto escape;
+  if (!SetPhotSelections (&argc, argv, 3)) goto usage;
+
+  KeepNulls = FALSE;
+  if ((N = get_argument (argc, argv, "-nulls"))) {
+    KeepNulls = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+
+  /* interpret command-line options */
+  if (argc != 6) { goto usage; }
+  if (strcmp (argv[2], "-")) goto usage;
+  if (strcmp (argv[4], ":")) goto usage;
+  if (!GetPhotcodeInfo (argv[1], &code[0], &mode[0])) return (FALSE);
+  if (!GetPhotcodeInfo (argv[3], &code[1], &mode[1])) return (FALSE);
+  if (!GetPhotcodeInfo (argv[5], &code[2], &mode[2])) return (FALSE);
+  if (!TestPhotSelections (&code[0], &mode[0], MEAS_ZERO)) goto escape;
+
+  /* load region corresponding to selection above */
+  if ((skylist = SelectRegions (selection)) == NULL) goto escape;
+
+  /* init vectors to save data */
+  if ((xvec = SelectVector ("xv", ANYVECTOR, TRUE)) == NULL) goto escape;
+  if ((yvec = SelectVector ("yv", ANYVECTOR, TRUE)) == NULL) goto escape;
+
+  gprint (GP_ERR, "warning: this function may be deprecated in the future -- use avextract for better control\n");
+
+  Npts = 0;
+  NPTS = 100;
+  ResetVector (xvec, OPIHI_FLT, NPTS);
+  ResetVector (yvec, OPIHI_FLT, NPTS);
+
+  // grab data from all selected sky regions
+  // void *Signal = signal (SIGINT, handle_interrupt);
+  signal (SIGINT, handle_interrupt);
+  interrupt = FALSE;
+
+  /* loop over regions, extract data for each region */
+  for (j = 0; (j < skylist[0].Nregions) && !interrupt; j++) {
+    /* lock, load, unlock catalog */
+    catalog.filename = skylist[0].filename[j];
+    catalog.catflags = LOAD_AVES | LOAD_MEAS | LOAD_SECF;
+    catalog.Nsecfilt = 0;
+
+    // an error exit status here is a significant error
+    if (!dvo_catalog_open (&catalog, NULL, FALSE, "r")) {
+      fprintf (stderr, "ERROR: failure to open catalog file %s\n", catalog.filename);
+      exit (2);
+    }
+    dvo_catalog_unlock (&catalog);
+    
+    /* get correct mags, convert to X,Y */
+    for (i = 0; (i < catalog.Naverage) && !interrupt; i++) {
+      M1 = M3 = NULL;
+      m = catalog.average[i].measureOffset;
+
+      SetSelectionParam (0);
+      M1 = ExtractDMag (code, mode, &catalog.average[i], &catalog.secfilt[i*Nsecfilt], &catalog.measure[m], &N1);
+      if (N1 == 0) goto skip;
+
+      SetSelectionParam (2);
+      M3 = ExtractMagnitudes (code[2], mode[2], &catalog.average[i], &catalog.secfilt[i*Nsecfilt], &catalog.measure[m], &N3);
+      if (N3 == 0) {
+	if (KeepNulls) {
+	  ALLOCATE (M3, double, 1);
+	  N3 = 1;
+	  M3[0] = NAN;
+	} else {
+	  goto skip;
+	}
+      }
+
+      for (i1 = 0; i1 < N1; i1++) {
+	for (i3 = 0; i3 < N3; i3++) {
+	  xvec[0].elements.Flt[Npts] = M1[i1];
+	  yvec[0].elements.Flt[Npts] = M3[i3];
+	  Npts++;
+	  if (Npts >= NPTS) {
+	    NPTS += 2000;
+	    REALLOCATE (xvec[0].elements.Flt, opihi_flt, NPTS);
+	    REALLOCATE (yvec[0].elements.Flt, opihi_flt, NPTS);
+	  }
+	}
+      }
+    skip:
+      if (M1 != NULL) free (M1);
+      if (M3 != NULL) free (M3);
+    }
+    dvo_catalog_free (&catalog);
+  }
+
+  xvec[0].Nelements = yvec[0].Nelements = Npts;
+  SkyListFree (skylist);
+  FreeSkyRegionSelection (selection);
+  return (TRUE);
+
+usage:
+  gprint (GP_ERR, "USAGE: cmd F - F : F\n");
+  return (FALSE);
+
+escape:
+  dvo_catalog_free (&catalog);
+  SkyListFree (skylist);
+  FreeSkyRegionSelection (selection);
+  return (FALSE);
+}
+    
Index: /branches/sc_branches/pantasks_condor/dvo/cmpReadFile.c
===================================================================
--- /branches/sc_branches/pantasks_condor/dvo/cmpReadFile.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/dvo/cmpReadFile.c	(revision 34783)
@@ -0,0 +1,130 @@
+# include "dvoshell.h"
+# define D_NSTARS 1000
+# define BYTES_STAR 66
+# define BLOCK 1000
+
+CMPstars *cmpReadFits (FILE *f, off_t *nstars) {
+
+  off_t i, Nstars;
+  Header theader;
+  FTable table;
+  CMPstars *stars;
+  SMPData *smpdata;
+
+  /* if no stars, no table */
+  if (*nstars == 0) return (NULL);
+
+  /* init & load in table data */
+  table.header   = &theader;
+  if (!gfits_fread_ftable (f, &table, "SMPFILE")) goto escape;
+
+  smpdata = gfits_table_get_SMPData (&table, &Nstars, NULL);
+  if (!smpdata) {
+    fprintf (stderr, "ERROR: failed to read stars\n");
+    exit (2);
+  }
+
+  ALLOCATE (stars, CMPstars, Nstars);
+  for (i = 0; i < Nstars; i++) {
+    stars[i].X      = smpdata[i].X;
+    stars[i].Y      = smpdata[i].Y;
+    stars[i].M      = smpdata[i].M;
+    stars[i].dM     = smpdata[i].dM;
+    stars[i].dophot = smpdata[i].dophot;
+
+    stars[i].Mgal   = smpdata[i].M;
+    stars[i].Map    = smpdata[i].dM;
+    stars[i].fx     = smpdata[i].fx;
+    stars[i].fy     = smpdata[i].fy;
+    stars[i].df     = smpdata[i].df;
+  }    
+  *nstars = Nstars;
+  return (stars);
+
+escape:
+  gprint (GP_ERR, "error reading file\n");
+  *nstars = 0;
+  return (NULL);
+}
+
+CMPstars *cmpReadText (FILE *f, off_t *nstars) {
+
+  off_t N;
+  int j, Nextra, Ninstar, Nskip, Nbytes, nbytes;
+  int done;
+  char *buffer, *c, *c2;
+  double tmp;
+  CMPstars *stars;
+  
+  /* load in stars by blocks of 1000 */
+  N = 0;
+  ALLOCATE (buffer, char, (BLOCK*BYTES_STAR) + 1);
+  buffer[BLOCK*BYTES_STAR] = 0;
+  Nextra = 0;
+
+  ALLOCATE (stars, CMPstars, *nstars);
+
+  while (N < *nstars) {
+    /* load next data block */
+    Nbytes = BYTES_STAR * BLOCK - Nextra;
+    nbytes = fread (&buffer[Nextra], 1, Nbytes, f);
+    if (nbytes == 0) {
+      *nstars = N;
+      return (stars);
+    }
+    nbytes += Nextra;
+
+    /* check line-by-line integrity */
+    c = buffer;
+    done = FALSE;
+    while ((c < buffer + nbytes) && (!done)) { 
+      for (c2 = c; *c2 == '\n'; c2++);
+      if (c2 > c) { /* extra return chars */
+	memmove (c, c2, (int)(buffer + nbytes - c2));
+	Nskip = c2 - c;
+	nbytes -= Nskip;
+	bzero (buffer + nbytes, Nskip);
+	/* if (VERBOSE) gprint (GP_ERR, "deleted %d extra return chars\n", Nskip); */
+      }
+      c2 = strchr (c, '\n');
+      if (c2 == (char *) NULL) {
+	done = TRUE;	
+	continue;
+      }
+      c2++;
+      if ((c2 - c) != BYTES_STAR) { /* bad line, delete it */
+	memmove (c, c2, (int)(buffer + nbytes - c2));
+	Nskip = c2 - c;
+	nbytes -= Nskip;
+	bzero (buffer + nbytes, Nskip);
+	/* if (VERBOSE) gprint (GP_ERR, "deleted line, %d extra chars\n", Nskip); */
+      } else {
+	c = c2;
+      }
+    }
+
+    /* extract data for stars */
+    Ninstar = nbytes / BYTES_STAR;
+    Nextra = nbytes % BYTES_STAR;
+    for (j = 0; (j < Ninstar) && (N < *nstars); j++, N++) {
+      dparse (&stars[N].X,  1, &buffer[j*BYTES_STAR]);
+      dparse (&stars[N].Y,  2, &buffer[j*BYTES_STAR]);
+      dparse (&stars[N].M,  3, &buffer[j*BYTES_STAR]);
+
+      /* cmp files carry dM in millimags */
+      dparse (&tmp, 4, &buffer[j*BYTES_STAR]);
+      stars[N].dM = 0.001*tmp;
+
+      dparse (&tmp,         5, &buffer[j*BYTES_STAR]);
+      stars[N].dophot = tmp;
+
+      dparse (&stars[N].Mgal, 7, &buffer[j*BYTES_STAR]);
+      dparse (&stars[N].Map,  8, &buffer[j*BYTES_STAR]);
+      dparse (&stars[N].fx,   9, &buffer[j*BYTES_STAR]);
+      dparse (&stars[N].fy,  10, &buffer[j*BYTES_STAR]);
+      dparse (&stars[N].df,  11, &buffer[j*BYTES_STAR]);
+    }
+  }
+  *nstars = N;
+  return (stars);
+}
Index: /branches/sc_branches/pantasks_condor/dvo/cmpload.c
===================================================================
--- /branches/sc_branches/pantasks_condor/dvo/cmpload.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/dvo/cmpload.c	(revision 34783)
@@ -0,0 +1,142 @@
+# include "dvoshell.h"
+# define D_NSTARS 1000
+# define BYTES_STAR 66
+# define BLOCK 1000
+
+int cmpload (int argc, char **argv) {
+  
+  int i, Noverlay, NOVERLAY, Nstar, N, Nextra, Objtype, type;
+  int doneread, done, Nskip, Nbytes, nbytes, Ninstar;
+  char *c, *c2, *name;
+  double dtmp;
+  FILE *f;
+  char *buffer;
+  int kapa;
+  Header header;
+  KiiOverlay *overlay;
+  
+  name = NULL;
+  if ((N = get_argument (argc, argv, "-n"))) {
+    remove_argument (N, &argc, argv);
+    name = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+  if (!GetImage (NULL, &kapa, name)) return (FALSE);
+  FREE (name);
+
+  Objtype = 0;
+  if ((N = get_argument (argc, argv, "-t"))) {
+    remove_argument (N, &argc, argv);
+    Objtype = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  if (argc != 3) {
+    gprint (GP_ERR, "USAGE: cmpload (overlay) <filename>\n");
+    return (FALSE);
+  }
+
+  if (!gfits_read_header (argv[2], &header)) {
+    gprint (GP_ERR, "ERROR: can't read header for %s\n", argv[2]);
+    return (FALSE);
+  }
+
+  /* find expected number of stars */
+  gfits_scan (&header, "NSTARS", "%d", 1, &Nstar);
+  if (Nstar == 0) {
+    gprint (GP_ERR, "ERROR: can't get NSTARS from header\n");
+    gfits_free_header (&header);
+    return (FALSE);
+  }
+
+  f = fopen (argv[2], "r");
+  if (f == NULL) {
+    gprint (GP_ERR, "ERROR: can't read data from %s\n", argv[2]);
+    gfits_free_header (&header);
+    return (FALSE);
+  }
+  fseeko (f, header.datasize, SEEK_SET); 
+
+  Noverlay = 0;
+  NOVERLAY = 1000;
+  ALLOCATE (overlay, KiiOverlay, Noverlay);
+  
+  /* load in stars by blocks of 1000 */
+  ALLOCATE (buffer, char, (BLOCK*BYTES_STAR) + 1);
+  buffer[BLOCK*BYTES_STAR] = 0;
+  Nextra = 0;
+  doneread = FALSE;
+  while (!doneread) {
+    Nbytes = BYTES_STAR * BLOCK - Nextra;
+    nbytes = fread (&buffer[Nextra], 1, Nbytes, f);
+    if (nbytes == 0) {
+      doneread = TRUE;
+      continue;
+    }
+    nbytes += Nextra;
+    /* check line-by-line integrity */
+    c = buffer;
+    done = FALSE;
+    while ((c < buffer + nbytes) && (!done)) { 
+      for (c2 = c; *c2 == '\n'; c2++);
+      if (c2 > c) { /* extra return chars */
+	memmove (c, c2, (int)(buffer + nbytes - c2));
+	Nskip = c2 - c;
+	nbytes -= Nskip;
+	bzero (buffer + nbytes, Nskip);
+      }
+      c2 = strchr (c, '\n');
+      if (c2 == (char *) NULL) {
+	done = TRUE;	
+	continue;
+      }
+      c2++;
+      if ((c2 - c) != BYTES_STAR) { /* bad line, delete it */
+	memmove (c, c2, (int)(buffer + nbytes - c2));
+	Nskip = c2 - c;
+	nbytes -= Nskip;
+	bzero (buffer + nbytes, Nskip);
+      } else {
+	c = c2;
+      }
+    }
+    Ninstar = nbytes / BYTES_STAR;
+    Nextra = nbytes % BYTES_STAR;
+    for (i = 0; i < Ninstar; i++) {
+      if (Objtype) {
+	dparse (&dtmp, 5, &buffer[i*BYTES_STAR]);
+	type = dtmp;
+	if (type != Objtype) continue;
+      }
+      # if (0)
+      if (scale) {
+	dparse (&mag,  3, &buffer[i*BYTES_STAR]);
+	overlay[Noverlay].dx = mzero + mscale * mag;
+	overlay[Noverlay].dy = mzero + mscale * mag;
+      } else {
+	overlay[Noverlay].dx = 5.0;
+	overlay[Noverlay].dy = 5.0;
+      }      
+      # endif 
+
+      fparse (&overlay[Noverlay].x,  1, &buffer[i*BYTES_STAR]);
+      fparse (&overlay[Noverlay].y,  2, &buffer[i*BYTES_STAR]);
+      overlay[Noverlay].type = KII_OVERLAY_BOX;
+      overlay[Noverlay].dx = 5.0;
+      overlay[Noverlay].dy = 5.0;
+      Noverlay ++;
+      CHECK_REALLOCATE (overlay, KiiOverlay, NOVERLAY, Noverlay, 1000);
+    }
+  }
+  fclose (f);
+
+  KiiLoadOverlay (kapa, overlay, Noverlay, argv[1]);
+  free (overlay);
+
+  gfits_free_header (&header);
+  free (buffer);
+
+  gprint (GP_ERR, "loaded %d objects\n", Noverlay);
+  return (TRUE);
+}
+
Index: /branches/sc_branches/pantasks_condor/dvo/cmpread.c
===================================================================
--- /branches/sc_branches/pantasks_condor/dvo/cmpread.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/dvo/cmpread.c	(revision 34783)
@@ -0,0 +1,143 @@
+# include "dvoshell.h"
+
+/* add others as needed */
+enum {F_NONE, F_RA, F_DEC, F_X, F_Y, F_MAG, F_DMAG, F_TYPE, F_SKY, F_FX, F_FY, F_APMAG, F_GALMAG};
+
+int cmpread (int argc, char **argv) {
+  
+  off_t i, Nbytes, Nstars;
+  int field, Naxis;
+  double tR, tD;
+  float value;
+  FILE *f;
+  Vector *vec;
+  Header header;
+  Coords coords;
+  CMPstars *stars;
+
+  if (argc != 3) {
+    gprint (GP_ERR, "USAGE: cmpread field <filename>\n");
+    return (FALSE);
+  }
+
+  field = F_NONE;
+  if (!strcasecmp (argv[1], "ra"))   field = F_RA;
+  if (!strcasecmp (argv[1], "dec"))  field = F_DEC;
+  if (!strcasecmp (argv[1], "mag"))  field = F_MAG;
+  if (!strcasecmp (argv[1], "dmag")) field = F_DMAG;
+  if (!strcasecmp (argv[1], "x"))    field = F_X;
+  if (!strcasecmp (argv[1], "y"))    field = F_Y;
+  if (!strcasecmp (argv[1], "type")) field = F_TYPE;
+  if (!strcasecmp (argv[1], "sky"))  field = F_SKY;
+  if (!strcasecmp (argv[1], "fx"))   field = F_FX;
+  if (!strcasecmp (argv[1], "fy"))   field = F_FY;
+  if (!strcasecmp (argv[1], "apmag"))  field = F_APMAG;
+  if (!strcasecmp (argv[1], "galmag"))  field = F_GALMAG;
+  if (field == F_NONE) {
+    gprint (GP_ERR, "invalid cmp field: %s\n", argv[1]);
+    return (FALSE);
+  }
+
+  if ((vec = SelectVector (argv[1],  ANYVECTOR, TRUE)) == NULL) return (FALSE);
+
+  /* load FITS header */
+  if (!gfits_read_header (argv[2], &header)) {
+    gprint (GP_ERR, "ERROR: can't read header for %s\n", argv[2]);
+    return (FALSE);
+  }
+
+  if ((field == F_RA) || (field == F_DEC)) {
+    if (!GetCoords (&coords, &header)) {
+      gprint (GP_ERR, "can't get WCS info from header\n");
+      gfits_free_header (&header);
+      return (FALSE);
+    }
+  }
+
+  /* re-open file to load data */
+  f = fopen (argv[2], "r");
+  if (f == NULL) {
+    gprint (GP_ERR, "ERROR: can't read data from %s\n", argv[2]);
+    gfits_free_header (&header);
+    return (FALSE);
+  }
+  fseeko (f, header.datasize, SEEK_SET); 
+
+  /* find expected number of stars */
+  if (!gfits_scan (&header, "NSTARS", OFF_T_FMT, 1,  &Nstars)) {
+    gprint (GP_ERR, "ERROR: can't get NSTARS from header\n");
+    gfits_free_header (&header);
+    return (FALSE);
+  }
+
+  /* read from FITS table or from text table */
+  Naxis = 0;
+  gfits_scan (&header, "NAXIS",  "%d", 1, &Naxis);
+  if (Naxis == 2) {
+    /* allocate space for stars */
+    gprint (GP_ERR, "reading from TEXT cmp file %s\n", argv[2]);
+    if (!gfits_scan (&header, "NSTARS", OFF_T_FMT, 1,  &Nstars)) {
+      gprint (GP_ERR, "ERROR: failed to find NSTARS\n");
+      exit (1);
+    }
+    stars = cmpReadText (f, &Nstars);
+  } else {
+    gprint (GP_ERR, "reading from FITS cmp file %s\n", argv[2]);
+    Nbytes = gfits_data_size (&header);
+    fseeko (f, Nbytes, SEEK_CUR); 
+    stars = cmpReadFits (f, &Nstars);
+  }
+  fclose (f);
+
+  ResetVector (vec, OPIHI_FLT, Nstars);
+  bzero (vec[0].elements.Flt, Nstars*sizeof(opihi_flt));
+
+  value = 0;
+  for (i = 0; i < Nstars; i++) {
+    switch (field) {
+      case F_RA:
+	XY_to_RD (&tR, &tD, stars[i].X, stars[i].Y, &coords);
+	value = tR;
+	break;
+      case F_DEC:
+	XY_to_RD (&tR, &tD, stars[i].X, stars[i].Y, &coords);
+	value = tD;
+	break;
+      case F_X:
+	value = stars[i].X;
+	break;
+      case F_Y:
+	value = stars[i].Y;
+	break;
+      case F_MAG:
+	value = stars[i].M;
+	break;
+      case F_APMAG:
+	value = stars[i].Map;
+	break;
+      case F_GALMAG:
+	value = stars[i].Mgal;
+	break;
+      case F_DMAG:
+	value = stars[i].dM;
+	break;
+      case F_TYPE:
+	value = stars[i].dophot;
+	break;
+      case F_SKY:
+	value = stars[i].sky;
+	break;
+      case F_FX:
+	value = stars[i].fx;
+	break;
+      case F_FY:
+	value = stars[i].fy;
+	break;
+    }
+    vec[0].elements.Flt[i] = value;
+  }      
+  free (stars);
+  gfits_free_header (&header);
+  gprint (GP_ERR, "loaded "OFF_T_FMT" objects\n",  Nstars);
+  return (TRUE);
+}
Index: /branches/sc_branches/pantasks_condor/dvo/compare.c
===================================================================
--- /branches/sc_branches/pantasks_condor/dvo/compare.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/dvo/compare.c	(revision 34783)
@@ -0,0 +1,68 @@
+# include "dvoshell.h"
+# define D_NMATCH 500;
+
+void compare (Catalog *catlog1, Catalog *catlog2, 
+	      Vector *rvec,  Vector *dvec,  Vector *mvec, Vector *drvec, Vector *ddvec, Vector *dmvec, double radius) {
+
+  off_t i, j, first_j, Nmatch, NMATCH;
+  double dX, dY, dR;
+
+  Nmatch = 0;
+  NMATCH = D_NMATCH;
+  ResetVector ( rvec, OPIHI_FLT, NMATCH);
+  ResetVector ( dvec, OPIHI_FLT, NMATCH);
+  ResetVector ( mvec, OPIHI_FLT, NMATCH);
+  ResetVector (drvec, OPIHI_FLT, NMATCH);
+  ResetVector (ddvec, OPIHI_FLT, NMATCH);
+  ResetVector (dmvec, OPIHI_FLT, NMATCH);
+
+  for (i = j = 0; (i < catlog1[0].Naverage) && (j < catlog2[0].Naverage);) {
+    
+    dX = catlog1[0].average[i].R - catlog2[0].average[j].R;
+
+    if (!(i % 100))
+      gprint (GP_ERR, ".");
+    
+    if (dX <= -radius)
+      i++;
+    if (dX >= radius)
+      j++;
+
+    if (fabs (dX) < radius) {
+      first_j = j;
+      for (j = first_j; (fabs (dX) < radius) && (j < catlog2[0].Naverage); j++) {
+	dX = catlog1[0].average[i].R - catlog2[0].average[j].R;
+	dY = catlog1[0].average[i].D - catlog2[0].average[j].D;
+	dR = hypot (dX, dY);
+	if (dR < radius) {
+	  rvec[0].elements.Flt[Nmatch] = catlog1[0].average[i].R;
+	  dvec[0].elements.Flt[Nmatch] = catlog1[0].average[i].D;
+	  // mvec[0].elements.Flt[Nmatch] = catlog1[0].average[i].M;
+	  drvec[0].elements.Flt[Nmatch] = dX;
+	  ddvec[0].elements.Flt[Nmatch] = dY;
+	  // dmvec[0].elements.Flt[Nmatch] = catlog1[0].average[i].M - catlog2[0].average[j].M;
+	  Nmatch ++;
+	  if (Nmatch == NMATCH - 1) {
+	    NMATCH += D_NMATCH;
+	    REALLOCATE ( rvec[0].elements.Flt, opihi_flt, NMATCH);
+	    REALLOCATE ( dvec[0].elements.Flt, opihi_flt, NMATCH);
+	    REALLOCATE ( mvec[0].elements.Flt, opihi_flt, NMATCH);
+	    REALLOCATE (drvec[0].elements.Flt, opihi_flt, NMATCH);
+	    REALLOCATE (ddvec[0].elements.Flt, opihi_flt, NMATCH);
+	    REALLOCATE (dmvec[0].elements.Flt, opihi_flt, NMATCH);
+	  }
+	}
+      }
+      j = first_j;
+      i++;
+    }
+  }
+
+  ResetVector ( rvec, OPIHI_FLT, Nmatch);
+  ResetVector ( dvec, OPIHI_FLT, Nmatch);
+  ResetVector ( mvec, OPIHI_FLT, Nmatch);
+  ResetVector (drvec, OPIHI_FLT, Nmatch);
+  ResetVector (ddvec, OPIHI_FLT, Nmatch);
+  ResetVector (dmvec, OPIHI_FLT, Nmatch);
+}
+
Index: /branches/sc_branches/pantasks_condor/dvo/ddmags.c
===================================================================
--- /branches/sc_branches/pantasks_condor/dvo/ddmags.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/dvo/ddmags.c	(revision 34783)
@@ -0,0 +1,136 @@
+# include "dvoshell.h"
+
+int ddmags (int argc, char **argv) {
+  
+  gprint (GP_ERR, "this function is deprecated\n");
+  return (FALSE);
+
+} 
+
+# if (0) 
+  char *RegionName, *RegionList;
+  double *M1, *M2;
+  int i, m, k, N, Npts, NPTS;
+  int N1, N2, i1, i2, mode[4];
+  int Nsecfilt, KeepNulls;
+
+  Catalog catalog;
+  PhotCode *code[4];
+  SkyList *skylist;
+  Vector *xvec, *yvec;
+
+  /* defaults */
+  catalog.average = NULL; 
+  catalog.secfilt = NULL;
+  catalog.measure = NULL;
+  RegionName = NULL;
+  RegionList = NULL;
+  skylist = NULL;
+
+  /* load photcode information */
+  if (!InitPhotcodes ()) goto escape;
+  Nsecfilt = GetPhotcodeNsecfilt ();
+
+  /* interpret command-line options */
+  if (!SetRegionSelection (&argc, argv, &RegionName, &RegionList)) goto escape;
+  if (!SetPhotSelections (&argc, argv, 4)) goto usage;
+
+  KeepNulls = FALSE;
+  if ((N = get_argument (argc, argv, "-nulls"))) {
+    KeepNulls = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+
+  /* interpret command-line options */
+  if (argc != 8) goto usage;
+  if (strcmp (argv[2], "-")) goto usage;
+  if (strcmp (argv[4], ":")) goto usage;
+  if (strcmp (argv[6], "-")) goto usage;
+  if (!GetPhotcodeInfo (argv[1], &code[0], &mode[0])) return (FALSE);
+  if (!GetPhotcodeInfo (argv[3], &code[1], &mode[1])) return (FALSE);
+  if (!GetPhotcodeInfo (argv[5], &code[2], &mode[2])) return (FALSE);
+  if (!GetPhotcodeInfo (argv[7], &code[3], &mode[3])) return (FALSE);
+  if (!TestPhotSelections (&code[0], &mode[0], MEAS_ZERO)) goto escape;
+
+  /* load region corresponding to selection above */
+  if ((skylist = SelectRegions (RegionName, RegionList)) == NULL) goto escape;
+
+  /* init vectors to save data */
+  Npts = 0;
+  NPTS = 1;
+  if ((xvec = SelectVector ("xv", ANYVECTOR, TRUE)) == NULL) goto escape;
+  if ((yvec = SelectVector ("yv", ANYVECTOR, TRUE)) == NULL) goto escape;
+
+  /* loop over regions, extract data for each region */
+  for (k = 0; k < skylist[0].Nregions; k++) {
+    /* lock, load, unlock catalog */
+    catalog.filename = skylist[0].filename[k];
+    catalog.catflags = LOAD_AVES | LOAD_MEAS | LOAD_SECF;
+    catalog.Nsecfilt = 0;
+
+    // an error exit status here is a significant error
+    if (!dvo_catalog_open (&catalog, NULL, FALSE, "r")) {
+      fprintf (stderr, "ERROR: failure to open catalog file %s\n", catalog.filename);
+      exit (2);
+    }
+    dvo_catalog_unlock (&catalog);
+
+    // gprint (GP_ERR, "seaching %s with %d stars\n", catalog.filename, catalog.Naverage);
+    // ListPhotSelections ();
+
+    /* get correct mags, convert to X,Y */
+    for (i = 0; i < catalog.Naverage; i++) {
+      M1 = M2 = NULL;
+      m = catalog.average[i].offset;
+
+      SetSelectionParam (0);
+      M1 = ExtractDMag (&code[0], &mode[0], &catalog.average[i], &catalog.secfilt[i*Nsecfilt], &catalog.measure[m], &N1);
+      if (N1 == 0) goto skip;
+
+      SetSelectionParam (2);
+      M2 = ExtractDMag (&code[2], &mode[2], &catalog.average[i], &catalog.secfilt[i*Nsecfilt], &catalog.measure[m], &N2);
+      if (N2 == 0) {
+	if (KeepNulls) {
+	  ALLOCATE (M2, double, 1);
+	  N2 = 1;
+	  M2[0] = NAN;
+	} else {
+	  goto skip;
+	}
+      }
+
+      for (i1 = 0; i1 < N1; i1++) {
+	for (i2 = 0; i2 < N2; i2++) {
+	  xvec[0].elements[Npts] = M1[i1];
+	  yvec[0].elements[Npts] = M2[i2];
+	  Npts++;
+	  if (Npts >= NPTS) {
+	    NPTS += 2000;
+	    REALLOCATE (xvec[0].elements, float, NPTS);
+	    REALLOCATE (yvec[0].elements, float, NPTS);
+	  }
+	}
+      }
+    skip:
+      if (M1 != NULL) free (M1);
+      if (M2 != NULL) free (M2);
+    }
+    // gprint (GP_ERR, "selected %d stars\n", Npts);
+    dvo_catalog_free (&catalog);
+  }
+  SkyListFree (skylist);
+  xvec[0].Nelements = yvec[0].Nelements = Npts;
+  return (TRUE);
+
+usage:
+  gprint (GP_ERR, "USAGE: ddmags F - F : measure.param\n");
+
+escape:
+  SkyListFree (skylist);
+  dvo_catalog_free (&catalog);
+  if (RegionName != NULL) free (RegionName);
+  if (RegionList != NULL) free (RegionList);
+  return (FALSE);
+}
+
+# endif
Index: /branches/sc_branches/pantasks_condor/dvo/detrend.c
===================================================================
--- /branches/sc_branches/pantasks_condor/dvo/detrend.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/dvo/detrend.c	(revision 34783)
@@ -0,0 +1,208 @@
+# include "dvoshell.h"
+
+/* qualities to be extracted */
+enum {ZERO, DTIME, SKY, BIAS, FWHM, AIRM, TIME, TEMP};
+
+int detrend (int argc, char **argv) {
+ 
+  FILE *f;
+  off_t i, Nimage, status;
+  int N, TimeSelect;
+  char DataBase[256];
+  time_t tzero, tend;
+  double trange;
+  int TypeSelect, CCDSelect, FilterSelect;
+  char *Filter;
+  int Type, mode, CCD;
+  int NVALUE;
+  opihi_flt *value;
+  time_t TimeReference;
+  int TimeFormat;
+  Header header;
+  RegImage *pimage;
+  Vector *vec;
+
+  VarConfig ("REGISTRATION_DATABASE", "%s", DataBase);
+
+  TimeSelect = FALSE;
+  if ((N = get_argument (argc, argv, "-time"))) {
+    remove_argument (N, &argc, argv);
+    if (!ohana_str_to_time (argv[N], &tzero)) { 
+      gprint (GP_ERR, "syntax error\n");
+      return (FALSE);
+    }
+    remove_argument (N, &argc, argv);
+    if (!ohana_str_to_dtime (argv[N], &trange)) { 
+      gprint (GP_ERR, "syntax error\n");
+      return (FALSE);
+    }
+    remove_argument (N, &argc, argv);
+    if (trange < 0) {
+      trange = fabs (trange);
+      tzero -= trange;
+    }
+    TimeSelect = TRUE;
+  }
+  if ((N = get_argument (argc, argv, "-trange"))) {
+    remove_argument (N, &argc, argv);
+    if (!ohana_str_to_time (argv[N], &tzero)) { 
+      gprint (GP_ERR, "syntax error\n");
+      return (FALSE);
+    }
+    remove_argument (N, &argc, argv);
+    if (!ohana_str_to_time (argv[N], &tend)) { 
+      gprint (GP_ERR, "syntax error\n");
+      return (FALSE);
+    }
+    remove_argument (N, &argc, argv);
+    trange = tend - tzero;
+    if (trange < 0) {
+      trange = fabs (trange);
+      tzero -= trange;
+    }
+    TimeSelect = TRUE;
+  }
+ 
+  Type = 0;
+  TypeSelect = FALSE;
+  if ((N = get_argument (argc, argv, "-type"))) {
+    remove_argument (N, &argc, argv);
+    Type = get_image_type (argv[N]);
+    if (Type == T_UNDEF) {
+      gprint (GP_ERR, "ERROR: invalid image type %s\n", argv[N]);
+      return (FALSE);
+    }
+    remove_argument (N, &argc, argv);
+    TypeSelect = TRUE;
+  }
+
+  CCD = 0;
+  CCDSelect = FALSE;
+  if ((N = get_argument (argc, argv, "-ccd"))) {
+    remove_argument (N, &argc, argv);
+    CCD = atoi (argv[N]);
+    remove_argument (N, &argc, argv);
+    CCDSelect = TRUE;
+  }
+ 
+  Filter = NULL;
+  FilterSelect = FALSE;
+  if ((N = get_argument (argc, argv, "-filter"))) {
+    remove_argument (N, &argc, argv);
+    Filter = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+    FilterSelect = TRUE;
+    if (!strcasecmp (Filter, "X")) {
+      FilterSelect = FALSE;
+    }
+  }
+
+  if (argc != 2) {
+    gprint (GP_ERR, "USAGE: imrough (value)\n");
+    return (FALSE);
+  }
+  gprint (GP_ERR, "  this function is not well-defined.  re-work and re-code\n");
+  return (FALSE);
+  
+  /* identify selection */
+  mode = ZERO;
+  if (!strcasecmp (argv[1], "exptime")) mode = DTIME;
+  if (!strcasecmp (argv[1], "sky")) mode = SKY;
+  if (!strcasecmp (argv[1], "bias")) mode = BIAS;
+  if (!strcasecmp (argv[1], "fwhm")) mode = FWHM;
+  if (!strcasecmp (argv[1], "airmass")) mode = AIRM;
+  if (!strcasecmp (argv[1], "time")) mode = TIME;
+  if (!strcasecmp (argv[1], "temp")) mode = TEMP;
+  if (mode == ZERO) {
+    gprint (GP_ERR, "value may be one of the following:\n");
+    gprint (GP_ERR, " exptime, sky, bias, fwhm, airmass, time\n");
+    return (FALSE);
+  }
+
+  if ((vec = SelectVector (argv[1], ANYVECTOR, TRUE)) == NULL) return (FALSE);
+
+  /* load in database header */
+  if (!gfits_read_header (DataBase, &header)) {
+    gprint (GP_ERR, "ERROR: trouble reading database header\n");
+    return (FALSE);
+  }
+
+  /* open database */
+  f = fopen (DataBase, "r");
+  if (f == (FILE *) NULL) {
+    gprint (GP_ERR, "ERROR: can't open Registration Database\n");
+    return (FALSE);
+  }
+  fseeko (f, header.datasize, SEEK_SET);
+
+  /* load existing data from database */
+  gfits_scan (&header, "NIMAGES", OFF_T_FMT, 1,  &Nimage);
+  ALLOCATE (pimage, RegImage, Nimage);
+  status = fread (pimage, sizeof(RegImage), Nimage, f);
+  fclose (f);
+
+  if (status != Nimage) {
+    gprint (GP_ERR, "ERROR: header and data in dB don't match ("OFF_T_FMT" vs "OFF_T_FMT")\n",  Nimage,  status);
+    gfits_free_header (&header);
+    free (pimage);
+    return (FALSE);
+  }
+  gfits_convert_RegImage (pimage, sizeof (RegImage), Nimage);
+
+  N = 0;
+  NVALUE = 1000;
+  ResetVector (vec, OPIHI_FLT, NVALUE);
+  value = vec[0].elements.Flt;
+
+  GetTimeFormat (&TimeReference, &TimeFormat);
+  gprint (GP_ERR, "%ld %d\n", TimeReference, TimeFormat);
+
+  /* get data */
+  for (i = 0; i < Nimage; i++) {
+    /* skip unmatched selections */
+    if (TimeSelect && ((pimage[i].obstime < tzero) || (pimage[i].obstime > tzero + trange))) continue;
+    if (TimeSelect && ((pimage[i].obstime < tzero) || (pimage[i].obstime > tzero + trange))) continue;
+    if (FilterSelect && (strcasecmp (pimage[i].filter, Filter))) continue;
+    if (CCDSelect && (pimage[i].ccd != CCD)) continue;
+    if (TypeSelect && (pimage[i].type != Type)) continue;
+
+    /* assign correct value */
+    switch (mode) {
+    case (DTIME):
+      value[N] = pimage[i].exptime;
+      break;
+    case (TIME):
+      value[N] = TimeValue (pimage[i].obstime, TimeReference, TimeFormat);
+      break;
+    case (SKY):
+      value[N] = pimage[i].sky;
+      break;
+    case (BIAS):
+      value[N] = pimage[i].bias;
+      break;
+    case (FWHM):
+      value[N] = pimage[i].fwhm;
+      break;
+    case (AIRM):
+      value[N] = pimage[i].airmass;
+      break;
+    case (TEMP):
+      value[N] = pimage[i].teltemp_0;
+      break;
+    }
+    N++;
+    if (N >= NVALUE - 1) {
+      NVALUE += 1000;
+      REALLOCATE (vec[0].elements.Flt, opihi_flt, NVALUE);
+      value = vec[0].elements.Flt;
+    }
+  }
+
+  REALLOCATE (vec[0].elements.Flt, opihi_flt, MAX (1,N));
+  vec[0].Nelements = N;
+
+  free (pimage);
+  gfits_free_header (&header);
+  return (TRUE);
+
+}
Index: /branches/sc_branches/pantasks_condor/dvo/dmagaves.c
===================================================================
--- /branches/sc_branches/pantasks_condor/dvo/dmagaves.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/dvo/dmagaves.c	(revision 34783)
@@ -0,0 +1,113 @@
+# include "dvoshell.h"
+
+int dmagaves (int argc, char **argv) {
+  
+  gprint (GP_ERR, "this function is deprecated\n");
+  return (FALSE);
+
+} 
+
+# if (0) 
+  char *RegionName, *RegionList;
+  double *M1, M2;
+  int i, j, k, m, N1;
+  int Npts, NPTS, param, mode[3];
+  int Nsecfilt;
+
+  PhotCode *code[3];
+  Catalog catalog;
+  SkyList *skylist;
+  Vector *xvec, *yvec;
+
+  /* defaults */
+  catalog.average = NULL; 
+  catalog.secfilt = NULL;
+  catalog.measure = NULL;
+  RegionName = NULL;
+  RegionList = NULL;
+  skylist = NULL;
+  code[2] = NULL;
+
+  /* load photcode information */
+  if (!InitPhotcodes ()) goto escape;
+  Nsecfilt = GetPhotcodeNsecfilt ();
+
+  /* interpret command-line options */
+  if (!SetRegionSelection (&argc, argv, &RegionName, &RegionList)) goto escape;
+  if (!SetPhotSelections (&argc, argv, 3)) goto usage;
+
+  /* interpret command-line options: dmagaves F1 - F2 : (value) */
+  if (argc != 6) { goto usage; }
+  if (strcmp (argv[2], "-")) goto usage;
+  if (strcmp (argv[4], ":")) goto usage;
+  if (!GetPhotcodeInfo (argv[1], &code[0], &mode[0])) goto usage;
+  if (!GetPhotcodeInfo (argv[3], &code[1], &mode[1])) goto usage;
+  if ((param = GetAverageParam (argv[5])) == AVE_ZERO) goto usage;
+  if (!TestPhotSelections (&code[2], &mode[2], param)) goto escape;
+
+  /* load region corresponding to selection above */
+  if ((skylist = SelectRegions (RegionName, RegionList)) == NULL) goto escape;
+
+  /* init vectors to save data */
+  Npts = 0;
+  NPTS = 1;
+  if ((xvec = SelectVector ("xv", ANYVECTOR, TRUE)) == NULL) goto escape;
+  if ((yvec = SelectVector ("yv", ANYVECTOR, TRUE)) == NULL) goto escape;
+
+  for (j = 0; j < skylist[0].Nregions; j++) {
+    /* lock, load, unlock catalog */
+    catalog.filename = skylist[0].filename[j];
+    catalog.catflags = LOAD_AVES | LOAD_MEAS | LOAD_SECF;
+    catalog.Nsecfilt = 0;
+
+    // an error exit status here is a significant error
+    if (!dvo_catalog_open (&catalog, NULL, FALSE, "r")) {
+      fprintf (stderr, "ERROR: failure to open catalog file %s\n", catalog.filename);
+      exit (2);
+    }
+    dvo_catalog_unlock (&catalog);
+
+    /* get correct mags, convert to X,Y */
+    for (i = 0; i < catalog.Naverage; i++) {
+      M1 = NULL;
+      m = catalog.average[i].offset;
+
+      SetSelectionParam (0);
+      M1 = ExtractDMag (code, mode, &catalog.average[i], &catalog.secfilt[i*Nsecfilt], &catalog.measure[m], &N1);
+      if (N1 == 0) goto skip;
+
+      SetSelectionParam (2);
+      M2 = ExtractAverages (code[2], mode[2], &catalog.average[i], &catalog.secfilt[i*Nsecfilt], &catalog.measure[m], param);
+
+      for (k = 0; k < N1; k++) {
+	xvec[0].elements[Npts] = M1[k];
+	yvec[0].elements[Npts] = M2;
+	Npts++;
+	if (Npts >= NPTS) {
+	  NPTS += 2000;
+	  REALLOCATE (xvec[0].elements, float, NPTS);
+	  REALLOCATE (yvec[0].elements, float, NPTS);
+	}
+      }
+    skip:
+      if (M1 != NULL) free (M1);
+    }
+    dvo_catalog_free (&catalog);
+  }
+  SkyListFree (skylist);
+  xvec[0].Nelements = yvec[0].Nelements = Npts;
+  return (TRUE);
+
+usage:
+  gprint (GP_ERR, "USAGE: dmagaves F - F : average.param\n");
+  return (FALSE);
+
+escape:
+  SkyListFree (skylist);
+  dvo_catalog_free (&catalog);
+  if (RegionName != NULL) free (RegionName);
+  if (RegionList != NULL) free (RegionList);
+  return (FALSE);
+}
+
+# endif
Index: /branches/sc_branches/pantasks_condor/dvo/dmagmeas.c
===================================================================
--- /branches/sc_branches/pantasks_condor/dvo/dmagmeas.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/dvo/dmagmeas.c	(revision 34783)
@@ -0,0 +1,135 @@
+# include "dvoshell.h"
+
+int dmagmeas (int argc, char **argv) {
+  
+  gprint (GP_ERR, "this function is deprecated\n");
+  return (FALSE);
+
+} 
+
+# if (0) 
+  char *RegionName, *RegionList;
+  double *M1, *M3;
+  int i, j, m, i1, i3, N1, N3, N;
+  int Npts, NPTS, param, mode[3];
+  int Nsecfilt, KeepNulls;
+
+  Catalog catalog;
+  PhotCode *code[3];
+  SkyList *skylist;
+  Vector *xvec, *yvec;
+
+  /* defaults */
+  catalog.average = NULL; 
+  catalog.secfilt = NULL;
+  catalog.measure = NULL;
+  RegionName = NULL;
+  RegionList = NULL;
+  skylist = NULL;
+  code[2] = NULL;
+
+  /* load photcode information */
+  if (!InitPhotcodes ()) goto escape;
+  Nsecfilt = GetPhotcodeNsecfilt ();
+
+  /* interpret command-line options */
+  if (!SetRegionSelection (&argc, argv, &RegionName, &RegionList)) goto escape;
+  if (!SetPhotSelections (&argc, argv, 3)) goto usage;
+
+  KeepNulls = FALSE;
+  if ((N = get_argument (argc, argv, "-nulls"))) {
+    KeepNulls = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+
+  /* interpret command-line arguments: dmagmeas F1 - F2 : (value) */
+  if (argc != 6) { goto usage; }
+  if (strcmp (argv[2], "-")) goto usage;
+  if (strcmp (argv[4], ":")) goto usage;
+  if (!GetPhotcodeInfo (argv[1], &code[0], &mode[0])) goto usage;
+  if (!GetPhotcodeInfo (argv[3], &code[1], &mode[1])) goto usage;
+  if ((param = GetMeasureParam (argv[5])) == MEAS_ZERO) goto usage;
+  if (!TestPhotSelections (&code[2], &mode[2], MEAS_ZERO)) goto escape;
+
+  /* load region corresponding to selection above */
+  if ((skylist = SelectRegions (RegionName, RegionList)) == NULL) goto escape;
+  if (!SetImageSelection (((param == MEAS_XMOSAIC) || (param == MEAS_YMOSAIC)), ((RegionName == NULL) && (RegionList == NULL)))) goto escape;
+
+  /* init vectors to save data */
+  Npts = 0;
+  NPTS = 1;
+  if ((xvec = SelectVector ("xv", ANYVECTOR, TRUE)) == NULL) goto escape;
+  if ((yvec = SelectVector ("yv", ANYVECTOR, TRUE)) == NULL) goto escape;
+
+  /* loop over regions, extract data for each region */
+  for (j = 0; j < skylist[0].Nregions; j++) {
+    /* lock, load, unlock catalog */
+    catalog.filename = skylist[0].filename[j];
+    catalog.catflags = LOAD_AVES | LOAD_MEAS | LOAD_SECF;
+    catalog.Nsecfilt = 0;
+
+    // an error exit status here is a significant error
+    if (!dvo_catalog_open (&catalog, NULL, FALSE, "r")) {
+      fprintf (stderr, "ERROR: failure to open catalog file %s\n", catalog.filename);
+      exit (2);
+    }
+    dvo_catalog_unlock (&catalog);
+
+    /* get correct mags, convert to X,Y */
+    for (i = 0; i < catalog.Naverage; i++) {
+      M1 = M3 = NULL;
+      m = catalog.average[i].offset;
+
+      SetSelectionParam (0);
+      M1 = ExtractDMag (code, mode, &catalog.average[i], &catalog.secfilt[i*Nsecfilt], &catalog.measure[m], &N1);
+      if (N1 == 0) goto skip;
+
+      SetSelectionParam (2);
+      M3 = ExtractMeasures (code[2], mode[2], &catalog.average[i], &catalog.secfilt[i*Nsecfilt], &catalog.measure[m], &N3, param);
+      if (N3 == 0) {
+	if (KeepNulls) {
+	  ALLOCATE (M3, double, 1);
+	  N3 = 1;
+	  M3[0] = NAN;
+	} else {
+	  goto skip;
+	}
+      }
+
+      for (i1 = 0; i1 < N1; i1++) {
+	for (i3 = 0; i3 < N3; i3++) {
+	  xvec[0].elements[Npts] = M1[i1];
+	  yvec[0].elements[Npts] = M3[i3];
+	  Npts++;
+	  if (Npts >= NPTS) {
+	    NPTS += 2000;
+	    REALLOCATE (xvec[0].elements, float, NPTS);
+	    REALLOCATE (yvec[0].elements, float, NPTS);
+	  }
+	}
+      }
+    skip:
+      if (M1 != NULL) free (M1);
+      if (M3 != NULL) free (M3);
+    }
+    dvo_catalog_free (&catalog);
+  }
+  FreeImageSelection ();
+  SkyListFree (skylist);
+  xvec[0].Nelements = yvec[0].Nelements = Npts;
+  return (TRUE);
+
+usage:
+  gprint (GP_ERR, "USAGE: dmagmeas F - F : measure.param\n");
+  return (FALSE);
+
+escape:
+  FreeImageSelection ();
+  SkyListFree (skylist);
+  dvo_catalog_free (&catalog);
+  if (RegionName != NULL) free (RegionName);
+  if (RegionList != NULL) free (RegionList);
+  return (FALSE);
+}
+
+# endif
Index: /branches/sc_branches/pantasks_condor/dvo/dmags.c
===================================================================
--- /branches/sc_branches/pantasks_condor/dvo/dmags.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/dvo/dmags.c	(revision 34783)
@@ -0,0 +1,136 @@
+# include "dvoshell.h"
+
+int dmags (int argc, char **argv) {
+  
+  gprint (GP_ERR, "this function is deprecated\n");
+  return (FALSE);
+
+} 
+
+# if (0) 
+  char *RegionName, *RegionList;
+  double *M1, *M3;
+  int i, j, m, i1, i3, N1, N3, N;
+  int Npts, NPTS, mode[3];
+  int Nsecfilt, KeepNulls;
+
+  PhotCode *code[3];
+  Catalog catalog;
+  SkyList *skylist;
+  Vector *xvec, *yvec;
+
+  /* defaults */
+  catalog.average = NULL; 
+  catalog.secfilt = NULL;
+  catalog.measure = NULL;
+  RegionName = NULL;
+  RegionList = NULL;
+  skylist = NULL;
+
+  /* load photcode information */
+  if (!InitPhotcodes ()) goto escape;
+  Nsecfilt = GetPhotcodeNsecfilt ();
+
+  /* interpret command-line options */
+  if (!SetRegionSelection (&argc, argv, &RegionName, &RegionList)) goto escape;
+  if (!SetPhotSelections (&argc, argv, 3)) goto usage;
+
+  KeepNulls = FALSE;
+  if ((N = get_argument (argc, argv, "-nulls"))) {
+    KeepNulls = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+
+  /* interpret required command-line arguments: dmags F1 - F2 : F3 */
+  if (argc != 6) { goto usage; }
+  if (strcmp (argv[2], "-")) goto usage;
+  if (strcmp (argv[4], ":")) goto usage;
+  if (!GetPhotcodeInfo (argv[1], &code[0], &mode[0])) goto usage;
+  if (!GetPhotcodeInfo (argv[3], &code[1], &mode[1])) goto usage;
+  if (!GetPhotcodeInfo (argv[5], &code[2], &mode[2])) goto usage;
+  if (!TestPhotSelections (&code[0], &mode[0], MEAS_ZERO)) goto escape;
+
+  /* load region corresponding to selection above */
+  if ((skylist = SelectRegions (RegionName, RegionList)) == NULL) goto escape;
+
+  /* init vectors to save data */
+  Npts = 0;
+  NPTS = 1;
+  if ((xvec = SelectVector ("xv", ANYVECTOR, TRUE)) == NULL) goto escape;
+  if ((yvec = SelectVector ("yv", ANYVECTOR, TRUE)) == NULL) goto escape;
+
+  /* loop over regions, extract data for each region */
+  for (j = 0; j < skylist[0].Nregions; j++) {
+    /* lock, load, unlock catalog */
+    catalog.filename = skylist[0].filename[j];
+    catalog.catflags = LOAD_AVES | LOAD_MEAS | LOAD_SECF;
+    catalog.Nsecfilt = 0;
+
+    // an error exit status here is a significant error
+    if (!dvo_catalog_open (&catalog, NULL, FALSE, "r")) {
+      fprintf (stderr, "ERROR: failure to open catalog file %s\n", catalog.filename);
+      exit (2);
+    }
+    dvo_catalog_unlock (&catalog);
+
+    /* get correct mags, convert to X,Y */
+    for (i = 0; i < catalog.Naverage; i++) {
+      M1 = M3 = NULL;
+      m = catalog.average[i].offset;
+
+      SetSelectionParam (0);
+      M1 = ExtractDMag (code, mode, &catalog.average[i], &catalog.secfilt[i*Nsecfilt], &catalog.measure[m], &N1);
+      if (N1 == 0) goto skip;
+
+      SetSelectionParam (2);
+      M3 = ExtractMagnitudes (code[2], mode[2], &catalog.average[i], &catalog.secfilt[i*Nsecfilt], &catalog.measure[m], &N3);
+      if (N3 == 0) {
+	if (KeepNulls) {
+	  ALLOCATE (M3, double, 1);
+	  N3 = 1;
+	  M3[0] = NAN;
+	} else {
+	  goto skip;
+	}
+      }
+
+      for (i1 = 0; i1 < N1; i1++) {
+	for (i3 = 0; i3 < N3; i3++) {
+	  xvec[0].elements[Npts] = M1[i1];
+	  yvec[0].elements[Npts] = M3[i3];
+	  Npts++;
+	  if (Npts >= NPTS) {
+	    NPTS += 2000;
+	    REALLOCATE (xvec[0].elements, float, NPTS);
+	    REALLOCATE (yvec[0].elements, float, NPTS);
+	  }
+	}
+      }
+    skip:
+      if (M1 != NULL) free (M1);
+      if (M3 != NULL) free (M3);
+    }
+    dvo_catalog_free (&catalog);
+  }
+
+  /* need to free SkyList / or free all regions as well */
+  SkyListFree (skylist);
+  xvec[0].Nelements = yvec[0].Nelements = Npts;
+  return (TRUE);
+
+usage:
+  gprint (GP_ERR, "USAGE: dmags F - F : F\n");
+  gprint (GP_ERR, "    F : any photcodes with matched qualifiers:\n");
+  gprint (GP_ERR, "    pri: F:inst, F:cat, F:sys, F:rel, F:cal, F:ave, F:ref\n");
+  gprint (GP_ERR, "    sec: F:inst, F:cat, F:sys, F:rel, F:cal, F:ave, F:ref\n");
+  gprint (GP_ERR, "    dep: F:inst, F:cat, F:sys, F:rel, F:cal\n");
+  gprint (GP_ERR, "    ref: F:cat\n");
+
+escape:
+  SkyListFree (skylist);
+  dvo_catalog_free (&catalog);
+  if (RegionName != NULL) free (RegionName);
+  if (RegionList != NULL) free (RegionList);
+  return (FALSE);
+}
+# endif
Index: /branches/sc_branches/pantasks_condor/dvo/dmt.c
===================================================================
--- /branches/sc_branches/pantasks_condor/dvo/dmt.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/dvo/dmt.c	(revision 34783)
@@ -0,0 +1,158 @@
+# include "dvoshell.h"
+
+/* extract vectors giving delta mags for multiple measurements */ 
+int dmt (int argc, char **argv) {
+  
+  // XXX this needs to be fixed: how to access different graphs at once?
+  gprint (GP_ERR, "ERROR: this function is currently disabled\n");
+  return (FALSE);
+
+# if (0)
+
+  int kapa, SaveVectors;
+  int Nsec, Nsecfilt;
+  off_t i, m, k, N, NPTS;
+  double Radius;
+  float dt1, dt2, dmt1, dmt2;
+  float M0, M1, M2, M3;
+  PhotCode *code;
+  Catalog catalog;
+  Graphdata graphmode, graphsky;
+  SkyTable *sky;
+  SkyList *skylist;
+  Vector Xvec, Yvec, Zvec, Rvec, Dvec;
+  Vector *vec1, *vec2, *vec3, *vec4, *vec5;
+
+  Dvec.elements = Rvec.elements = Zvec.elements = NULL;
+
+  if (!InitPhotcodes ()) return (FALSE);
+
+  vec1 = vec2 = vec3 = vec4 = vec5 = NULL;
+  SaveVectors = FALSE;
+  if ((N = get_argument (argc, argv, "-vect"))) {
+    remove_argument (N, &argc, argv);
+    if ((vec1 = SelectVector ("dmtdmt", ANYVECTOR, TRUE)) == NULL) return (FALSE);
+    if ((vec2 = SelectVector ("dmtvar", ANYVECTOR, TRUE)) == NULL) return (FALSE);
+    if ((vec3 = SelectVector ("dmtmag", ANYVECTOR, TRUE)) == NULL) return (FALSE);
+    if ((vec4 = SelectVector ("dmtra",  ANYVECTOR, TRUE)) == NULL) return (FALSE);
+    if ((vec5 = SelectVector ("dmtdec", ANYVECTOR, TRUE)) == NULL) return (FALSE);
+    SaveVectors = TRUE;
+  }
+
+  if (argc != 3) {
+    gprint (GP_ERR, "USAGE: dmags filter\n");
+    return (FALSE);
+  }
+
+  if (!GetGraphdata (&graphsky, &kapa, NULL)) return (FALSE);
+  if (!GetGraph (&graphmode, NULL, NULL)) return (FALSE);
+
+  if ((code = GetPhotcodebyName (argv[1])) == NULL) {
+    gprint (GP_ERR, "ERROR: photcode not found in photcode table\n");
+    return (FALSE);
+  }
+  if (code[0].type != PHOT_SEC) {
+    gprint (GP_ERR, "first filter must be a average photometry type\n");
+    return (FALSE);
+  }
+  Nsecfilt = GetPhotcodeNsecfilt();
+  Nsec = GetPhotcodeNsec (code[0].code);
+
+  Radius = MAX (fabs(graphsky.xmax), fabs(graphsky.ymax));
+
+  sky = GetSkyTable ();
+  skylist = SkyListByRadius (sky, -1, graphsky.coords.crval1, graphsky.coords.crval2, Radius);
+  
+  N = 0;
+  NPTS = catalog.Nmeasure;
+  ALLOCATE (Xvec.elements, float, NPTS);
+  ALLOCATE (Yvec.elements, float, NPTS);
+  if (SaveVectors) {
+    ALLOCATE (Zvec.elements, float, NPTS);
+    ALLOCATE (Rvec.elements, float, NPTS);
+    ALLOCATE (Dvec.elements, float, NPTS);
+  }
+
+  for (k = 0; k < skylist[0].Nregions; k++) {
+    /* lock, load, unlock catalog */
+    catalog.filename = skylist[0].filename[k];
+    catalog.catflags = LOAD_AVES | LOAD_MEAS | LOAD_SECF;
+    catalog.Nsecfilt = 0;
+
+    // an error exit status here is a significant error
+    if (!dvo_catalog_open (&catalog, NULL, FALSE, "r")) {
+      fprintf (stderr, "ERROR: failure to open catalog file %s\n", catalog.filename);
+      exit (2);
+    }
+    dvo_catalog_unlock (&catalog);
+
+    for (i = 0; i < catalog.Naverage; i++) {
+      if (catalog.average[i].Nm != 3) continue;
+      m = catalog.average[i].offset;
+      M0 = catalog.secfilt[i*Nsecfilt+Nsec].M;
+      M1 = PhotCat (&catalog.measure[m+0]);
+      M2 = PhotCat (&catalog.measure[m+1]);
+      M3 = PhotCat (&catalog.measure[m+2]);
+
+      dt1 = (catalog.measure[m+0].t < catalog.measure[m+1].t) ? catalog.measure[m+1].t - catalog.measure[m+0].t : -1 * ((float)(catalog.measure[m+1].t - catalog.measure[m+0].t));
+      dt2 = (catalog.measure[m+1].t < catalog.measure[m+2].t) ? catalog.measure[m+2].t - catalog.measure[m+1].t : -1 * ((float)(catalog.measure[m+2].t - catalog.measure[m+1].t));
+      dmt1 = (M2 - M1) / dt1;
+      dmt2 = (M3 - M2) / dt2;
+      Xvec.elements[N] = (dmt1 - dmt2) / (dmt1 + dmt2);
+      Yvec.elements[N] = (dmt1 + dmt2) / 2.0;
+      if (SaveVectors) {
+	Rvec.elements[N] = catalog.average[i].R;
+	Dvec.elements[N] = catalog.average[i].D;
+	Zvec.elements[N] = M0;
+      }
+      N++;
+      if (N == NPTS - 1) {
+	NPTS += 2000;
+	REALLOCATE (Xvec.elements, float, NPTS);
+	REALLOCATE (Yvec.elements, float, NPTS);
+	if (SaveVectors) {
+	  REALLOCATE (Zvec.elements, float, NPTS);
+	  REALLOCATE (Rvec.elements, float, NPTS);
+	  REALLOCATE (Dvec.elements, float, NPTS);
+	}
+      }
+    }
+    dvo_catalog_free (&catalog);
+  }
+  Yvec.Nelements = Xvec.Nelements = N;
+  REALLOCATE (Xvec.elements, float, MAX (1, N));
+  REALLOCATE (Yvec.elements, float, MAX (1, N));
+  if (SaveVectors) {
+    Rvec.Nelements = Dvec.Nelements = Zvec.Nelements = N;
+    REALLOCATE (Zvec.elements, float, MAX (1, N));
+    REALLOCATE (Rvec.elements, float, MAX (1, N));
+    REALLOCATE (Dvec.elements, float, MAX (1, N));
+  }
+
+  if (SaveVectors) {
+    free (vec1[0].elements);
+    vec1[0].elements = Yvec.elements;
+    vec1[0].Nelements = Yvec.Nelements;
+    free (vec2[0].elements);
+    vec2[0].elements = Xvec.elements;
+    vec2[0].Nelements = Xvec.Nelements;
+    free (vec3[0].elements);
+    vec3[0].elements = Zvec.elements;
+    vec3[0].Nelements = Zvec.Nelements;
+    free (vec4[0].elements);
+    vec4[0].elements = Rvec.elements;
+    vec4[0].Nelements = Rvec.Nelements;
+    free (vec5[0].elements);
+    vec5[0].elements = Dvec.elements;
+    vec5[0].Nelements = Dvec.Nelements;
+  } else {
+    graphmode.style = 2; /* set style to points */
+    PlotVector (kapa, N, Xvec.elements, Yvec.elements, &graphmode);
+
+    free (Xvec.elements);
+    free (Yvec.elements);
+    free (Zvec.elements);
+  }
+  return (TRUE);
+# endif 
+}
Index: /branches/sc_branches/pantasks_condor/dvo/dvo.c.in
===================================================================
--- /branches/sc_branches/pantasks_condor/dvo/dvo.c.in	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/dvo/dvo.c.in	(revision 34783)
@@ -0,0 +1,79 @@
+# include "dvoshell.h"
+
+# define opihi_name "DVO"
+# define opihi_prompt "dvo: "
+# define opihi_description "desktop virtual observatory\n"
+# define opihi_history ".dvo"
+# define opihi_rcfile ".dvorc"
+
+/* program-dependent initialization */
+void program_init (int *argc, char **argv) {
+  
+  auto_break = TRUE;
+
+  /* load the commands used by this implementation */
+  InitBasic ();
+  InitData ();
+  InitAstro ();
+  InitDVO ();
+
+  // XXX don't open the catdir until needed
+  // if (!SetCATDIR (NULL, FALSE)) {
+  //  gprint (GP_ERR, "CATDIR is not defined\n");
+  // }
+
+  rl_readline_name = opihi_name;
+  rl_attempted_completion_function = command_completer;
+
+  set_str_variable ("HISTORY", opihi_history);
+  set_str_variable ("PROMPT", opihi_prompt);
+  set_str_variable ("RCFILE", opihi_rcfile);
+  set_int_variable ("DVO_CLIENT", FALSE);
+
+  {
+    char *helpdir;
+    char *modules;
+    static char *datadir = "@DATADIR@";
+    ALLOCATE (helpdir, char, strlen(datadir) + strlen("/help") + 2);
+    sprintf (helpdir, "%s/help", datadir);
+    set_str_variable ("HELPDIR", helpdir);
+    free (helpdir);
+    ALLOCATE (modules, char, strlen(datadir) + strlen("/modules") + 2);
+    sprintf (modules, "%s/modules", datadir);
+    set_str_variable ("MODULES:0", modules);
+    set_int_variable ("MODULES:n", 1);
+    free (modules);
+  }
+
+  // dvo_client uses the following; they should be NULL for dvo
+  HOST_ID = 0;
+  HOSTDIR = NULL;
+  RESULT_FILE = NULL;
+
+  return;
+}
+
+/* standard welcome message */
+void welcome () {
+  gprint (GP_ERR, "\n");
+  gprint (GP_ERR, "Welcome to %s - %s\n\n", opihi_name, opihi_description);
+}
+
+/* add program-dependent exit functions here */
+void cleanup () {
+  QuitKapa ();
+  ConfigFree ();
+
+  FreeBasic ();
+  FreeData ();
+  FreeAstro ();
+  FreeDVO ();
+  return;
+}
+
+/* call to opihi shell */
+int main (int argc, char **argv) {
+  int status;
+  status = opihi (argc, argv);
+  exit (status);
+}
Index: /branches/sc_branches/pantasks_condor/dvo/dvoBooleanElements.c
===================================================================
--- /branches/sc_branches/pantasks_condor/dvo/dvoBooleanElements.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/dvo/dvoBooleanElements.c	(revision 34783)
@@ -0,0 +1,158 @@
+# include "opihi.h"
+
+/* local private functions */
+void InsertValue (char c);
+void EndOfString (void);
+int IsAnOp (char *c);
+int IsTwoOp (char *c);
+
+/* local private static variables */
+int Nchar, Nout, NOUT;
+char **out;
+
+// split up the input arguments into appropriate blocks
+char **dvoBooleanElements (int Nin, char **in, int *nout) {
+  
+  int i, j, minus, negate, plus, posate, OpStat, SciNotation;
+
+  NOUT = Nin;
+  Nchar = Nout = 0;
+  ALLOCATE (out, char *, NOUT);
+  ALLOCATE (out[Nout], char, NCHARS);
+
+  for (i = 0; i < Nin; i++) {
+    for (j = 0; j < strlen(in[i]); j++) {
+      SciNotation = FALSE;
+      /* identify 'negate' or 'minus' ops */
+      negate = minus = FALSE;
+      if (in[i][j] == '-') { 
+	minus = TRUE;  
+	/* if - is first thing on line, must be a negator */
+	if ((Nout == 0) && (Nchar == 0)) {  
+	  minus = FALSE;
+	  negate = TRUE;
+	  goto skip1;
+	}
+	/* check previous entry on line */
+	if (Nchar) {
+	  OpStat = IsAnOp (out[Nout]);
+	  if (out[Nout][0] == ')') OpStat = FALSE;
+	} else {
+	  OpStat = IsAnOp (out[Nout-1]);
+	  if (out[Nout-1][0] == ')') OpStat = FALSE;
+	}
+	/* if - follows an operator, must be negator */
+	if (OpStat) {
+	  minus = FALSE;
+	  negate = TRUE;
+	  goto skip1;
+	}
+	/* if - follows 'e' is part of 1e-5 */
+	if (j == 0) goto skip1;
+	if ((in[i][j-1] == 'e') || (in[i][j-1] == 'E')) {
+	  SciNotation = TRUE;
+	  negate = minus = FALSE;
+	}
+      }
+    skip1:
+      /* idenfity 'posate' or 'plus' ops */
+      posate = plus = FALSE;
+      if (in[i][j] == '+') { 
+	plus = TRUE;  
+	/* if + is first thing on line, must be a posator */
+	if ((Nout == 0) && (Nchar == 0)) {  
+	  plus = FALSE;
+	  posate = TRUE;
+	  goto skip2;
+	}
+	/* check previous entry on line */
+	if (Nchar) {
+	  OpStat = IsAnOp (out[Nout]);
+	  if (out[Nout][0] == ')') OpStat = FALSE;
+	} else {
+	  OpStat = IsAnOp (out[Nout-1]);
+	  if (out[Nout-1][0] == ')') OpStat = FALSE;
+	}
+	/* if + follows an operator, must be posator */
+	if (OpStat) {
+	  plus = FALSE;
+	  posate = TRUE;
+	  goto skip2;
+	}
+	/* if + follows 'e' is part of 1e+5 */
+	if (j == 0) goto skip2;
+	if ((in[i][j-1] == 'e') || (in[i][j-1] == 'E')) {
+	  SciNotation = TRUE;
+	  posate = plus = FALSE;
+	}
+      }
+    skip2:
+      /* operators */
+      if (negate || minus || posate || plus || (IsAnOp (&in[i][j]) && !SciNotation)) {
+	if (posate) continue;
+	EndOfString ();
+	/* copy operator to out[Nout] */
+	InsertValue (in[i][j]);
+	if (negate) InsertValue ('-');
+
+	if (IsTwoOp (&in[i][j])) {
+	  InsertValue (in[i][j+1]);
+	  j++;
+	} 
+	EndOfString ();
+	continue;
+      }
+      /* quoted string */
+      if (in[i][j] == '"') {
+	InsertValue (in[i][j]);
+	j++;
+	while ((j < strlen(in[i])) && (in[i][j] != '"')) {
+	  InsertValue (in[i][j]);
+	  j++;
+	}
+	if (in[i][j] != '"') continue;
+	/* 
+	  gprint (GP_ERR, "mismatched quotes\n");
+	  return (FALSE);
+	}
+	*/
+	InsertValue (in[i][j]);
+	EndOfString ();
+	continue;
+      }
+      /* not an operator, not a quoted string */
+      if (!OHANA_WHITESPACE (in[i][j])) {
+	InsertValue (in[i][j]);
+      } else {
+	EndOfString ();
+      }
+    }
+    EndOfString ();
+  }
+
+  /* one extra entry is allocated, free here */
+  free (out[Nout]);
+  *nout = Nout;
+  return (out);
+
+}
+
+void InsertValue (char c) {
+  out[Nout][Nchar] = c;
+  Nchar ++;
+  out[Nout][Nchar] = 0;
+}
+
+void EndOfString () {
+  if (Nchar > 0) {
+    out[Nout][Nchar] = 0;
+    Nout ++;
+    Nchar = 0;
+    
+    if (Nout >= NOUT - 1) {
+      NOUT += 10; 
+      REALLOCATE (out, char *, NOUT); 
+    } 
+    ALLOCATE (out[Nout], char, NCHARS); 
+  }
+}
Index: /branches/sc_branches/pantasks_condor/dvo/dvoEvaluateStack.c
===================================================================
--- /branches/sc_branches/pantasks_condor/dvo/dvoEvaluateStack.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/dvo/dvoEvaluateStack.c	(revision 34783)
@@ -0,0 +1,65 @@
+# include "opihi.h"
+
+int CheckBooleanCondition (dvoStack dbStack, int NdbStack, float *values, dvoFields *fields, int Nfields) {
+  
+  int i, j, Nstack;
+  dvoStack **stack, *output;
+
+  Nstack = NdbStack;
+  ALLOCATE (stack, dvoStack *, NdbStack);
+  for (i = 0; i < NdbStack; i++) {
+    stack[i] = &dbStack[i];
+  }
+
+  for (i = 0; i < Nstack; i++) {
+
+    /***** binary operators *****/
+    if ((stack[i].type >= 3) && (stack[i].type <= 8)) {
+
+      // pre-test that op and entries match
+      output = db_binary (stack[i-2], stack[i-1], stack[i].name, fields, Nfields); 
+
+      // free temporary stack items, drop external items
+      clear_stack (stack[i-2]);
+      clear_stack (stack[i-1]);
+
+      stack[i-2] = output;
+      for (j = i + 1; j < Nstack; j++) {
+	stack[j-2] = stack[j];
+      }
+
+      Nstack -= 2;
+      i -= 2;
+      continue;
+    }
+
+    /***** unary operators **/
+    if (stack[i].type == 9) {
+
+      // pre-test that op and entries match
+      output = db_unary (&stack[i-1], stack[i].name, fields, Nfields); 
+
+      // free temporary stack items, drop external items
+      clear_stack (stack[i-2]);
+      clear_stack (stack[i-1]);
+
+      for (j = i + 1; j < Nstack; j++) {
+	stack[j-1] = stack[j];
+      }
+
+      Nstack -= 1;
+      i -= 1;
+      continue;
+    } 
+  }
+
+  // pre-test that op and entries match
+  return (TRUE);
+}
+
+/* delete name and data */
+void clear_stack (dvoStack *stack) {
+
+  if (stack->type != 'T') return;
+  free (stack);
+}
Index: /branches/sc_branches/pantasks_condor/dvo/dvo_client.c
===================================================================
--- /branches/sc_branches/pantasks_condor/dvo/dvo_client.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/dvo/dvo_client.c	(revision 34783)
@@ -0,0 +1,137 @@
+# include "dvoshell.h"
+
+int input PROTO((int, char **));
+
+/* program-dependent initialization */
+void program_init (int *argc, char **argv) {
+  
+  int N;
+
+  /* load the commands used by this implementation */
+  InitBasic ();
+  InitData ();
+  InitAstro ();
+  InitDVO ();
+
+  {
+    char *helpdir;
+    char *modules;
+    static char *datadir = "@DATADIR@";
+    ALLOCATE (helpdir, char, strlen(datadir) + strlen("/help") + 2);
+    sprintf (helpdir, "%s/help", datadir);
+    set_str_variable ("HELPDIR", helpdir);
+    free (helpdir);
+    ALLOCATE (modules, char, strlen(datadir) + strlen("/modules") + 2);
+    sprintf (modules, "%s/modules", datadir);
+    set_str_variable ("MODULES:0", modules);
+    set_int_variable ("MODULES:n", 1);
+    free (modules);
+  }
+
+  set_int_variable ("DVO_CLIENT", TRUE);
+  set_int_variable ("SCRIPT", FALSE);
+
+  // dvo_client should have 2 standard arguments: -hostID and -hostdir
+  HOST_ID = 0;
+  if ((N = get_argument (*argc, argv, "-hostID"))) {
+    remove_argument (N, argc, argv);
+    HOST_ID = atoi (argv[N]);;
+    remove_argument (N, argc, argv);
+  }
+
+  HOSTDIR = NULL;
+  if ((N = get_argument (*argc, argv, "-hostdir"))) {
+    remove_argument (N, argc, argv);
+    HOSTDIR = strcreate (argv[N]);;
+    remove_argument (N, argc, argv);
+  }
+
+  RESULT_FILE = NULL;
+  if ((N = get_argument (*argc, argv, "-result"))) {
+    remove_argument (N, argc, argv);
+    RESULT_FILE = strcreate (argv[N]);;
+    remove_argument (N, argc, argv);
+  }
+
+  // parse -skyregion option (used by most commands)
+  set_skyregion (0.0, 360.0, -90.0, +90.0);
+  if ((N = get_argument (*argc, argv, "-skyregion"))) {
+    if (N + 4 >= *argc) {
+      gprint (GP_ERR, "USAGE: -skyregion (RA) (RA) (DEC) (DEC)\n");
+      exit (1);
+    }
+    remove_argument (N, argc, argv);
+    set_skyregion (atof(argv[N]), atof(argv[N+1]), atof(argv[N+2]), atof(argv[N+3]));
+    remove_argument (N, argc, argv);
+    remove_argument (N, argc, argv);
+    remove_argument (N, argc, argv);
+    remove_argument (N, argc, argv);
+  }    
+
+  // parse -time option
+  if ((N = get_argument (*argc, argv, "-time"))) {
+    if (N + 2 >= *argc) {
+      gprint (GP_ERR, "USAGE: -time (TimeRef) (TimeFormat)\n");
+      exit (1);
+    }
+    remove_argument (N, argc, argv);
+    set_str_variable ("TIMEREF", argv[N]);
+    remove_argument (N, argc, argv);
+    set_str_variable ("TIMEFORMAT", argv[N]);
+    remove_argument (N, argc, argv);
+  }
+
+  return;
+}
+
+// dvo_client should be called like a dvo command;
+int main (int argc, char **argv) {
+  
+  // parse out whatever might be needed up front
+  general_init (&argc, argv);
+  program_init (&argc, argv);
+
+  if (argc < 2) {
+    fprintf (stderr, "USAGE: dvo_client (command) (options)\n");
+    exit (3);
+  }
+
+  int status = input (2, &argv[0]);
+  if (!status) {
+    fprintf (stderr, "WARNING: exit status 2 %d (%s)\n", HOST_ID, HOSTDIR);
+    exit (2);
+  }
+  exit (0);
+
+# if (0)
+  // identiy the comm
+  Command *cmd = MatchCommand (argv[1], TRUE, TRUE);
+  if (cmd == NULL) {
+    fprintf (stderr, "unknown command %s\n", argv[1]);
+    exit (1);
+  }
+
+  // argv[0] is dvo_client; we want to pass in argv[1].. as argv[0]
+  int status = (*cmd[0].func) (argc - 1, argv + 1);
+  if (!status) exit (2);
+  exit (0);
+# endif
+}
+
+/* standard welcome message */
+void welcome () {
+  gprint (GP_ERR, "starting dvo_client...\n");
+}
+
+/* add program-dependent exit functions here */
+void cleanup () {
+  QuitKapa ();
+  ConfigFree ();
+
+  FreeBasic ();
+  FreeData ();
+  FreeAstro ();
+  FreeDVO ();
+  return;
+}
+
Index: /branches/sc_branches/pantasks_condor/dvo/dvo_host_utils.c
===================================================================
--- /branches/sc_branches/pantasks_condor/dvo/dvo_host_utils.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/dvo/dvo_host_utils.c	(revision 34783)
@@ -0,0 +1,268 @@
+# include "dvoshell.h"
+
+# define DEBUG 0
+# define PARALLEL_MANUAL 0
+# define PARALLEL_SERIAL 0
+# 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];
+  int TIME = time(NULL);
+  int PID = getpid();
+  snprintf (uniquer, 12, "%05d.%05d", PID, TIME % 100000);
+
+  // write the dvo comment (host independent)
+  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);
+  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);
+
+  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, 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);
+
+    char command[1024];
+    snprintf (command, 1024, "dvo_client %s -result %s %s -hostID %d -hostdir %s", commandFile, table->hosts[i].results, options, table->hosts[i].hostID, table->hosts[i].pathname);
+
+    if (VERBOSE) gprint (GP_ERR, "command: %s\n", command);
+
+    if (PARALLEL_MANUAL) {
+      continue;
+    }
+
+    if (PARALLEL_SERIAL) {
+      int status = system (command);
+      if (status) {
+	gprint (GP_ERR, "ERROR running relphot_client\n");
+	top_status = FALSE;
+      }
+    } else {
+      // launch the job on the remote machine (no handshake)
+      int errorInfo = 0;
+      int pid = rconnect ("ssh", table->hosts[i].hostname, command, table->hosts[i].stdio, &errorInfo, FALSE);
+      if (!pid) {
+	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
+    }
+  }
+  free (commandFile);
+  return top_status;
+}
+
+// bundle the arguments into a command and pass to dvo_client.  this implementation
+// expects there to be a result file from the clients, and to load this into vectors in
+// the main shell.  'Nelements' is a temp hack : for most commands, the result vectors are
+// concatenated, but for avmatch, the vectors are merged by index into a pre-known
+// length.  this is probably not a solution to a general problem..
+int HostTableParallelOps (int argc, char **argv, char *ResultFile, int Nelements, 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 *tmppath = GetCATDIR ();
+  if (!tmppath) {
+    gprint (GP_ERR, "failed to get CATDIR for database\n");
+    return FALSE;
+  }
+
+  char *CATDIR = abspath (tmppath, DVO_MAX_PATH);
+  if (!CATDIR) {
+    gprint (GP_ERR, "failed to make an absolute path from %s (too long)\n", tmppath);
+    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;
+  }    
+
+  // other things I need to append?
+  char *basecmd = paste_args (argc, argv);
+
+  // determine the sky region 
+  double Rmin, Rmax, Dmin, Dmax;
+  get_skyregion (&Rmin, &Rmax, &Dmin, &Dmax);
+
+  // determine time reference and format
+  char *TimeRef = get_variable ("TIMEREF");
+  if (!TimeRef) {
+    gprint (GP_ERR, "failed to find TIMEREF variable\n");
+    return FALSE;
+  }
+
+  char *TimeFormat = get_variable ("TIMEFORMAT");
+  if (!TimeFormat) {
+    gprint (GP_ERR, "failed to find TIMEFORMAT variable\n");
+    return FALSE;
+  }
+
+  char tmp;
+  char *options = NULL;
+  int length = snprintf (&tmp, 0, "-D CATDIR %s -time %s %s -skyregion %f %f %f %f", CATDIR, TimeRef, TimeFormat, Rmin, Rmax, Dmin, Dmax);
+
+  ALLOCATE (options, char, length);
+  snprintf (options, length, "-D CATDIR %s -time %s %s -skyregion %f %f %f %f", CATDIR, TimeRef, TimeFormat, Rmin, Rmax, Dmin, Dmax);
+
+  // launch this command remotely
+  HostTableLaunchJobs (table, basecmd, options, VERBOSE);
+  free (options);
+  free (basecmd);
+
+  if (PARALLEL_MANUAL) {
+    gprint (GP_ERR, "run the relphot_client commands above.  when these are done, hit return\n");
+    getchar();
+  }
+  if (!PARALLEL_MANUAL && !PARALLEL_SERIAL) {
+    HostTableWaitJobsGetIO (table, __FILE__, __LINE__, VERBOSE);
+  }
+
+  // load fields from file
+  int    Nvec = 0;
+  Vector **vec = NULL;
+  for (i = 0; i < table->Nhosts; i++) {
+    if (table->hosts[i].status) continue; 
+
+    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;
+
+    if (Nelements == 0) {
+      vec = MergeVectors (vec, &Nvec, invec, Ninvec);
+      if (vec != invec) {
+	FreeVectorArray (invec, Ninvec);
+      }
+    } else {
+      vec = MergeVectorsByIndex (vec, &Nvec, invec, Ninvec, Nelements);
+      FreeVectorArray (invec, Ninvec);
+    }
+  }
+
+  // write vectors to a table (this is used by parallel dvo operations, but can be used elsewhere)
+  if (ResultFile) {
+    int status = WriteVectorTableFITS (ResultFile, "RESULT", vec, Nvec, FALSE, NULL);
+    if (!status) {
+      gprint (GP_ERR, "failed to write result file %s\n", ResultFile);
+      return FALSE;
+    }
+  }
+
+  for (i = 0; i < Nvec; i++) {
+    AssignVector (vec[i], vec[i]->name, ANYVECTOR, TRUE);
+  }
+  free (vec);
+
+  free (table);
+  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: /branches/sc_branches/pantasks_condor/dvo/dvomisc.c
===================================================================
--- /branches/sc_branches/pantasks_condor/dvo/dvomisc.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/dvo/dvomisc.c	(revision 34783)
@@ -0,0 +1,34 @@
+# include "dvoshell.h"
+
+void cprecess (Average *average, off_t Naverage, double in_epoch, double out_epoch) {
+
+  off_t i;
+  double T;
+  double A, D, RA, DEC, zeta, z, theta;
+  double SA, CA, SD, CD;
+  
+  T = (out_epoch - in_epoch) / 100.0;
+  
+  zeta  = RAD_DEG*(0.6406161*T + 0.0000839*T*T + 0.0000050*T*T*T);
+  theta = RAD_DEG*(0.5567530*T - 0.0001185*T*T - 0.0000116*T*T*T);
+  z     =          0.6406161*T + 0.0003041*T*T + 0.0000051*T*T*T;
+  
+  for (i = 0; i < Naverage; i++) {
+    A = average[i].R;
+    D = average[i].D;
+    SD =  cos(RAD_DEG*A + zeta)*sin(theta)*cos(RAD_DEG*D) + cos(theta)*sin(RAD_DEG*D);
+    CD = sqrt (1 - SD*SD);
+    SA =  sin(RAD_DEG*A + zeta)*cos(RAD_DEG*D)/CD;
+    CA = (cos(RAD_DEG*A + zeta)*cos(theta)*cos(RAD_DEG*D) - sin(theta)*sin(RAD_DEG*D))/CD;
+    
+    DEC = DEG_RAD*asin(SD);
+    RA  = DEG_RAD*atan2(SA, CA) + z;
+    
+    if (RA < 0)
+      RA += 360;
+    
+    average[i].R = RA;
+    average[i].D = DEC; 
+  }
+
+}
Index: /branches/sc_branches/pantasks_condor/dvo/elixir.c
===================================================================
--- /branches/sc_branches/pantasks_condor/dvo/elixir.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/dvo/elixir.c	(revision 34783)
@@ -0,0 +1,148 @@
+# include "dvoshell.h"
+
+int WriteMsg (char *fifo, char *message);
+int ReadMsg (char *fifo, char **message);
+
+int elixir (int argc, char **argv) {
+  
+  char message[256], cmd[256], ElixirBase[256];
+  char fifo[256], fifodir[256], msgfile[256];
+  char *answer;
+  int N;
+
+  sprintf (cmd, "STATUS");
+  if ((N = get_argument (argc, argv, "-time"))) {
+    remove_argument (N, &argc, argv);
+    sprintf (cmd, "TIMES");
+  }
+  if ((N = get_argument (argc, argv, "-live"))) {
+    remove_argument (N, &argc, argv);
+    sprintf (cmd, "ALIVE");
+  }
+  if ((N = get_argument (argc, argv, "-stop"))) {
+    remove_argument (N, &argc, argv);
+    sprintf (cmd, "STOP");
+  }
+  if ((N = get_argument (argc, argv, "-kill"))) {
+    remove_argument (N, &argc, argv);
+    sprintf (cmd, "ABORT");
+  }
+ 
+  if (argc != 2) {
+    gprint (GP_ERR, "USAGE: elixir (elixir) [-time] [-live]\n");
+    return (FALSE);
+  }
+
+  if (!VarConfig (argv[1], "%s", ElixirBase)) {
+    gprint (GP_ERR, "elixir %s not in config file\n", argv[1]);
+    return (FALSE);
+  }
+  sprintf (fifo, "%s.msg", ElixirBase);
+  if (!VarConfig ("FIFOS", "%s", fifodir)) {
+    gprint (GP_ERR, "FIFOS not in config, using local /tmp\n");
+    strcpy (fifodir, "/tmp");
+  }
+  sprintf (fifo, "%s.msg", ElixirBase);
+
+  sprintf (msgfile, "%s/EMsg.XXXXXX", fifodir);
+  if (mkstemp (msgfile) == -1) {
+    gprint (GP_ERR, "can't create fifo\n");
+    return (FALSE);
+  }
+  sprintf (message, "%s %s", cmd, msgfile);
+  unlink (msgfile);
+
+  if (!WriteMsg (fifo, message)) {
+    gprint (GP_ERR, "can't access fifo %s\n", fifo);
+    return (FALSE);
+  }
+
+  if (ReadMsg (msgfile, &answer)) {
+    gprint (GP_ERR, "%s\n", answer);
+  } 
+  unlink (msgfile);
+  return (TRUE);
+  
+}
+
+int WriteMsg (char *fifo, char *message) {
+
+  int state, mode;
+  FILE *f;
+
+  /* check lockfile */
+  f = fsetlockfile (fifo, 2.0, LCK_XCLD, &state);
+  if (f == NULL) return (0);
+
+  /* write message to end of file */
+  fseeko (f, 0LL, SEEK_END);
+  fprintf (f, "%s\n", message);
+
+  mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
+  chmod (fifo, mode);
+
+  fclearlockfile (fifo, f, LCK_XCLD, &state);
+  return (1);
+}
+
+int ReadMsg (char *fifo, char **message) {
+
+  int i, nbytes, Nbytes, NBYTES;
+  char *buffer;
+  int state, mode;
+  FILE *f;
+  struct stat filestat;
+
+  /* wait (2 sec) for file to exist, then try to read it */
+  for (i = 0; (stat (fifo, &filestat) == -1) && (i < 20); i++) {
+    usleep (100000);
+  }
+  if (i >= 20) {
+    gprint (GP_ERR, "no response\n");
+    return (0);
+  }
+
+  /* check lockfile */
+  f = fsetlockfile (fifo, 2.0, LCK_XCLD, &state);
+  if (f == NULL) {
+    gprint (GP_ERR, "message locked (%d)\n", state);
+    return (0);
+  }
+
+  /* if file is empty, return 0 */
+  if (state == LCK_EMPTY) {
+    mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
+    chmod (fifo, mode);
+    fclearlockfile (fifo, f, LCK_XCLD, &state);
+    return (0);
+  }  
+
+  Nbytes = 0;
+  NBYTES = 0x1000;
+  ALLOCATE (buffer, char, NBYTES);
+  while (TRUE) {
+    nbytes = fread (&buffer[Nbytes], 1, 0x1000, f);
+    if (nbytes < 0) { 
+      gprint (GP_ERR, "error in ReadMsg -- got -1 bytes\n");
+      return (0);
+    }
+    if (nbytes == 0) break;
+    Nbytes += nbytes;
+    NBYTES += 0x1000;
+    REALLOCATE (buffer, char, NBYTES);
+  }
+  buffer[Nbytes] = 0;
+
+  mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
+  chmod (fifo, mode);
+  fclearlockfile (fifo, f, LCK_XCLD, &state);
+
+  if (Nbytes == 0) {
+    free (buffer);
+    return (0);
+  }
+
+  *message = buffer;
+  return (1);
+
+}
Index: /branches/sc_branches/pantasks_condor/dvo/extract.c
===================================================================
--- /branches/sc_branches/pantasks_condor/dvo/extract.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/dvo/extract.c	(revision 34783)
@@ -0,0 +1,450 @@
+# include "dvoshell.h"
+// XXX EAM : this function is deprecated
+
+enum {ZERO, RA, DEC, MAG, dMAG, Xm, Xp, NMEAS, NMISS, REF, TYPE, NPHOT, NCODE, FLAG};
+
+# define NBYTES 160000
+# define BYTES_STAR 23
+# define BLOCK 1000
+# define DNSTARS 1000
+
+int extract (int argc, char **argv) {
+  
+  FILE *f;
+  int i, Col, N, Nbytes, nbytes, NPTS;
+  int InRegion, GSC, ASCII, LONEOS, mode, loadmode;
+  int j, k, m, Nregions;
+  float M0, m0;
+  char filename[128];
+  float Radius;
+  char catdir[256], gscdir[256];
+  int PhotcodeSelect;
+  char PhotCodeFile[256], code[64];
+  double ZERO_POINT;
+  int Ns, N1, n1, Nsec;
+  int Ngraph;
+  int value;
+  Vector *vec;
+  PhotCodeData photcodes;
+  Graphdata graphmode;
+  Catalog catalog;
+  RegionFile *regions;
+
+  if (!GetGraphdata (&graphmode, NULL, NULL)) return (FALSE);
+  if (!InitPhotcodes ()) return (FALSE);
+
+  VarConfig ("GSCDIR", "%s", gscdir);
+  VarConfig ("CATDIR", "%s", catdir);
+
+  regions = (RegionFile *) NULL;
+  ASCII = FALSE;
+  LONEOS = TRUE;
+  GSC = FALSE;
+  if (N = get_argument (argc, argv, "-g")) {
+    remove_argument (N, &argc, argv);
+    GSC = TRUE;
+    ASCII = FALSE;
+    LONEOS = FALSE;
+  }
+
+  Col = 1;
+  if (N = get_argument (argc, argv, "-a")) {
+    remove_argument (N, &argc, argv);
+    ASCII = TRUE;
+    GSC = FALSE;
+    LONEOS = FALSE;
+    Col = atof(argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  /* check for region-based selection */
+  code = NULL;
+  PhotcodeSelect = FALSE;
+  if (N = get_argument (argc, argv, "-photcode")) {
+    PhotcodeSelect = True;
+    remove_argument (N, &argc, argv);
+    if ((code = GetPhotcodebyName (argv[N])) == NULL) {
+      gprint (GP_ERR, "ERROR: photcode %s not found in photcode table\n", argv[N]);
+      return (FALSE);
+    }
+    remove_argument (N, &argc, argv);
+  }
+
+  if (argc != 3) {
+    gprint (GP_ERR, "USAGE: extract (filename) (value) [-g / -a Ncol] \n");
+    return (FALSE);
+  }
+  
+  InRegion = FALSE;
+  if (!strcmp (argv[1], "-all")) {
+    InRegion = TRUE;
+  }
+
+  /* identify selection */
+  if (LONEOS) {
+    mode = ZERO;
+    if (!strcasecmp (argv[2], "ra")) mode = RA;
+    if (!strcasecmp (argv[2], "dec")) mode = DEC;
+    if (!strcasecmp (argv[2], "mag")) mode = MAG;
+    if (!strcasecmp (argv[2], "Nmeas")) mode = NMEAS;
+    if (!strcasecmp (argv[2], "Nmiss")) mode = NMISS;
+    if (!strcasecmp (argv[2], "Xp")) mode = Xp;
+    if (!strcasecmp (argv[2], "Xm")) mode = Xm;
+    if (!strcasecmp (argv[2], "dM")) mode = dMAG;
+    if (!strcasecmp (argv[2], "flag")) mode = FLAG;
+    if (!strcasecmp (argv[2], "ref")) mode = REF;
+    if (!strcasecmp (argv[2], "type")) mode = TYPE;
+    if (!strcasecmp (argv[2], "Nphot")) mode = NPHOT;
+    if (!strcasecmp (argv[2], "Ncode")) mode = NCODE;
+    if (mode == ZERO) {
+      gprint (GP_ERR, "value may be one of the following:\n");
+      gprint (GP_ERR, " ra dec mag Nmeas Nmiss Xp Xm ID\n");
+      return (FALSE);
+    }
+  }
+
+  if ((mode == REF) && !PhotcodeSelect) {
+    gprint (GP_ERR, "must specify photcode for Reference\n");
+    return (FALSE);
+  }
+  if ((mode == TYPE) && !PhotcodeSelect) {
+    gprint (GP_ERR, "must specify photcode for Type\n");
+    return (FALSE);
+  }
+
+  /* check photcode data / selection validity */
+  Ns = -1;
+  Nsec = GetPhotcodeNsecfilt ();
+  if (PhotcodeSelect) {
+    Ns = GetPhotcodeNsec (code[0].code);
+    if ((mode != REF) && (code[0].type != PHOT_SEC)) {
+      gprint (GP_ERR, "filter must be a average photometry type\n");
+      return (FALSE);
+    }
+    if ((mode == REF) && (code[0].type != PHOT_REF)) {
+      gprint (GP_ERR, "filter must be a REFERENCE photometry type\n");
+      return (FALSE);
+    }
+  }
+
+  if (GSC) {
+    mode = ZERO;
+    if (!strcasecmp (argv[2], "ra")) 
+      mode = RA;
+    if (!strcasecmp (argv[2], "dec")) 
+      mode = DEC;
+    if (!strcasecmp (argv[2], "mag")) 
+      mode = MAG;
+    if (mode == ZERO) {
+      gprint (GP_ERR, "for GSC, value may be one of the following:\n");
+      gprint (GP_ERR, " ra dec mag\n");
+      return (FALSE);
+    }
+  }
+
+  if ((vec = SelectVector (argv[2], ANYVECTOR, TRUE)) == NULL) return (FALSE);
+
+  if (InRegion) {
+    Radius = MAX (fabs(graphmode.xmax), fabs(graphmode.ymax));
+    regions = find_regions (graphmode.coords.crval1, graphmode.coords.crval2, Radius, &Nregions);
+  } else {
+    Nregions = 1;
+  }
+  
+  /* create storage vector */
+  NPTS = 1000;
+  REALLOCATE (vec[0].elements, float, NPTS);
+  vec[0].Nelements = N = 0;
+
+  /* we loop over Nregions, but for ASCII Nregions = 1 */
+  for (j = 0; j < Nregions; j++) {
+    
+    /* Load in data from an ASCII file list of ra, dec, mag */
+    if (ASCII) {
+      char *tbuffer;
+      double tmp;
+      float *V;
+      
+      f = fopen (argv[1], "r");
+      if (f == (FILE *) NULL) {
+	gprint (GP_ERR, "ERROR: can't open data file: %s\n", argv[1]);
+	/* delete new vector! */
+	return (FALSE);
+      }
+      ALLOCATE (tbuffer, char, 1024);
+      
+      V = vec[0].elements;
+      while (scan_line (f, tbuffer) != EOF) {
+	dparse (&tmp, Col, tbuffer);
+	*V = tmp;
+	V++;
+	N++;
+	if (N == NPTS - 1) {
+	  NPTS += 1000;
+	  REALLOCATE (vec[0].elements, float, NPTS);
+	  V = &vec[0].elements[N];
+	}
+      }
+      free (tbuffer);
+      vec[0].Nelements = N;
+      REALLOCATE (vec[0].elements, float, MAX(1,N));
+      fclose (f);
+      return (TRUE);
+    }
+    
+    /* load data from the GSC files */
+    if (GSC) {
+      char *tbuffer;
+      double tmp;
+      float *V;
+      
+      if (InRegion) {
+	sprintf (filename, "%s/%s\0", gscdir, regions[j].name);
+      } else {
+	sprintf (filename, "%s/%s\0", gscdir, argv[1]);
+      }
+      
+      f = fopen (filename, "r");
+      if (f == (FILE *) NULL) {
+	gprint (GP_ERR, "no stars in %s, skipping\n", filename);
+	continue;
+      }
+      ALLOCATE (tbuffer, char, (BLOCK*BYTES_STAR));
+      
+      V = &vec[0].elements[N];
+      Nbytes = BLOCK*BYTES_STAR;
+      while ((nbytes = fread (tbuffer, 1, Nbytes, f)) > 0) {
+	for (i = 0; i < nbytes / BYTES_STAR; i++) {
+	  if (mode == RA) {
+	    dparse (&tmp, 1, &tbuffer[i*BYTES_STAR]);
+	    *V = tmp;
+	  }
+	  if (mode == DEC) {
+	    dparse (&tmp, 2, &tbuffer[i*BYTES_STAR]);
+	    *V = tmp;
+	  }
+	  if (mode == MAG) {
+	    dparse (&tmp, 3, &tbuffer[i*BYTES_STAR]);
+	    *V = tmp;
+	  }
+	  V++;
+	  N++;
+	  if (N == NPTS - 1) {
+	    NPTS += 1000;
+	    REALLOCATE (vec[0].elements, float, NPTS);
+	    V = &vec[0].elements[N];
+	  }
+	}
+      }
+      free (tbuffer);
+      fclose (f);
+    }
+  
+    /* load data from the photometry database files */
+    if (LONEOS) {
+      /* find and open correct file */
+      if (InRegion) {
+	sprintf (filename, "%s/%s\0", catdir, regions[j].name);
+      } else {
+	sprintf (filename, "%s/%s\0", catdir, argv[1]);
+      }
+      catalog.average = (Average *) NULL;
+      catalog.measure = (Measure *) NULL;
+      catalog.secfilt = (SecFilt *) NULL;
+      loadmode = LOAD_AVES | LOAD_SECF;
+      if ((mode == REF) || (mode == TYPE) || (mode == NPHOT) || (mode == NCODE)) 
+	loadmode = loadmode | LOAD_MEAS;
+
+      /* lock, load, unlock catalog */
+      catalog.filename = filename;
+      switch (lock_catalog (&catalog, LCK_SOFT)) {
+      case 2:
+	unlock_catalog (&catalog);
+      case 0:
+	continue;
+      }
+      catalog.catflags = loadmode;
+      if (!load_catalog (&catalog, TRUE)) {
+	unlock_catalog (&catalog);
+	continue;
+      }
+      unlock_catalog (&catalog);
+
+      /* assign vector values */
+      switch (mode) {
+      case (RA):
+	for (i = 0; i < catalog.Naverage; i++) {
+	  vec[0].elements[N] = catalog.average[i].R;
+	  N++;
+	  if (N == NPTS - 1) {
+	    NPTS += 1000;
+	    REALLOCATE (vec[0].elements, float, NPTS);
+	  }
+	}
+	break;
+      case (DEC):
+	for (i = 0; i < catalog.Naverage; i++) {
+	  vec[0].elements[N] = catalog.average[i].D;
+	  N++;
+	  if (N == NPTS - 1) {
+	    NPTS += 1000;
+	    REALLOCATE (vec[0].elements, float, NPTS);
+	  }
+	}
+	break;
+      case (MAG):
+	for (i = 0; i < catalog.Naverage; i++) {
+	  M0 = (Ns == -1) ? catalog.average[i].M : catalog.secfilt[i*Nsec+Ns].M;
+	  vec[0].elements[N] = M0 / 1000.0;
+	  N++;
+	  if (N == NPTS - 1) {
+	    NPTS += 1000;
+	    REALLOCATE (vec[0].elements, float, NPTS);
+	  }
+	}
+	break;
+      case (NMEAS):
+	for (i = 0; i < catalog.Naverage; i++) {
+	  vec[0].elements[N] = catalog.average[i].Nm;
+	  N++;
+	  if (N == NPTS - 1) {
+	    NPTS += 1000;
+	    REALLOCATE (vec[0].elements, float, NPTS);
+	  }
+	}
+	break;
+      case (NMISS):
+	for (i = 0; i < catalog.Naverage; i++) {
+	  vec[0].elements[N] = catalog.average[i].Nn;
+	  N++;
+	  if (N == NPTS - 1) {
+	    NPTS += 1000;
+	    REALLOCATE (vec[0].elements, float, NPTS);
+	  }
+	}
+	break;
+      case (Xp):
+	for (i = 0; i < catalog.Naverage; i++) {
+	  /* Xp is scatter in 1/100 arcsec */
+	  vec[0].elements[N] = 0.01*catalog.average[i].Xp;
+	  N++;
+	  if (N == NPTS - 1) {
+	    NPTS += 1000;
+	    REALLOCATE (vec[0].elements, float, NPTS);
+	  }
+	}
+	break;
+      case (NCODE):
+	for (i = 0; i < catalog.Naverage; i++) {
+	  m = catalog.average[i].offset;
+	  Ncode = 0;
+	  for (k = 0; k < catalog.average[i].Nm; k++, m++) {
+	    if (code[0].code != GetPhotcodeEquivCodebyCode (catalog.measure[m].photcode)) continue;
+	    Ncode ++;
+	  }
+	  vec[0].elements[N] = Ncode;
+	  N++;
+	  if (N == NPTS - 1) {
+	    NPTS += 1000;
+	    REALLOCATE (vec[0].elements, float, NPTS);
+	  }
+	}
+	break;
+      case (NPHOT):
+	for (i = 0; i < catalog.Naverage; i++) {
+	  m = catalog.average[i].offset;
+	  Ncode = 0;
+	  for (k = 0; k < catalog.average[i].Nm; k++, m++) {
+	    if (code[0].code != GetPhotcodeEquivCodebyCode (catalog.measure[m].photcode)) continue;
+	    if (catalog.measure[m].photcode & (ID_MEAS_POOR_PHOTOM | ID_MEAS_SKIP_PHOTOM)) continue;
+	    Ncode ++;
+	  }
+	  vec[0].elements[N] = Ncode;
+	  N++;
+	  if (N == NPTS - 1) {
+	    NPTS += 1000;
+	    REALLOCATE (vec[0].elements, float, NPTS);
+	  }
+	}
+	break;
+      case (Xm):
+	for (i = 0; i < catalog.Naverage; i++) {
+	  M0 = (Ns == -1) ? catalog.average[i].Xm : catalog.secfilt[i*Nsec+Ns].Xm;
+	  vec[0].elements[N] = (M0 == NO_MAG) ? -1.0 : pow (10.0, 0.01*M0);
+	  N++;
+	  if (N == NPTS - 1) {
+	    NPTS += 1000;
+	    REALLOCATE (vec[0].elements, float, NPTS);
+	  }
+	}
+	break;
+      case (dMAG):
+	for (i = 0; i < catalog.Naverage; i++) {
+	  /* dM is 1000.0 * error */
+	  M0 = (Ns == -1) ? catalog.average[i].dM : catalog.secfilt[i*Nsec+Ns].dM;
+	  vec[0].elements[N] = (M0 == NO_MAG) ? -1.0 : 0.001*M0;
+	  N++;
+	  if (N == NPTS - 1) {
+	    NPTS += 1000;
+	    REALLOCATE (vec[0].elements, float, NPTS);
+	  }
+	}
+	break;
+      case (FLAG):
+	for (i = 0; i < catalog.Naverage; i++) {
+	  vec[0].elements[N] = catalog.average[i].code;
+	  N++;
+	  if (N == NPTS - 1) {
+	    NPTS += 1000;
+	    REALLOCATE (vec[0].elements, float, NPTS);
+	  }
+	}
+	break;
+      case (REF):
+	for (i = 0; i < catalog.Naverage; i++) {
+	  m = catalog.average[i].offset;
+	  vec[0].elements[N] = -32;
+	  for (k = 0; k < catalog.average[i].Nm; k++) {
+	    if (catalog.measure[m+k].photcode == N1) {
+	      vec[0].elements[N] = PhotCat (&catalog.measure[m+k]);
+	      k = catalog.average[i].Nm;
+	    }
+	  }
+	  N++;
+	  if (N == NPTS - 1) {
+	    NPTS += 1000;
+	    REALLOCATE (vec[0].elements, float, NPTS);
+	  }
+	}
+	break;
+      case (TYPE):
+	for (i = 0; i < catalog.Naverage; i++) {
+	  m = catalog.average[i].offset;
+	  vec[0].elements[N] = DetermineTypeCode (&catalog.average[i], &catalog.measure[m], &photcodes, N1);
+	  N++;
+	  if (N == NPTS - 1) {
+	    NPTS += 1000;
+	    REALLOCATE (vec[0].elements, float, NPTS);
+	  }
+	}
+	break;
+      }
+      if (catalog.average != 0) free (catalog.average);
+      if (catalog.measure != 0) free (catalog.measure);
+      if (catalog.secfilt != 0) free (catalog.secfilt);
+    }
+  }
+
+  vec[0].Nelements = N;
+  REALLOCATE (vec[0].elements, float, MAX(1,N));
+  return (TRUE);
+
+}
+  
+  /* USAGE: extract (what) (where) (from) [option] */
+  /* examples:
+     extract n0000/n0001 mag m 
+     extract -g n0000/n0020 ra r 
+     extract -all Xm xm
+     extract fred dec d -a 2 */
+  
Index: /branches/sc_branches/pantasks_condor/dvo/find_matches.c
===================================================================
--- /branches/sc_branches/pantasks_condor/dvo/find_matches.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/dvo/find_matches.c	(revision 34783)
@@ -0,0 +1,154 @@
+# include "dvoshell.h"
+
+// SkyRegion *region, Stars *stars, unsigned int NstarsIn, Catalog *catalog, AddstarClientOptions options)
+
+// attempt to match every RA,DEC entry with an entry in the catalog.  the result is stored in 'index'
+// index >= 0 : valid match; index == -2 : no valid match; index == -1 : not contained by this catalog
+int find_matches_by_vectors (SkyRegion *region, Catalog *catalog, Vector *RAvec, Vector *DECvec, float RADIUS, off_t *index) {
+
+  off_t i, j, J, Jmin, Npoints, Nmatch;
+  off_t *N1, *N2;
+  double *X1, *Y1, *X2, *Y2;
+  double dX, dY, dR, Doff, Dmin, Dmax, Rmin, Rmax;
+  int status;
+  double RADIUS2, RadMin;
+  off_t Nave;
+  Coords tcoords;
+
+  // XXX dvoconvert does not correctly maintain 'sorted' 
+  // assert(catalog[0].sorted);
+
+  Npoints = RAvec->Nelements;
+  Nmatch = 0;
+
+  /** allocate local arrays (points) **/
+  ALLOCATE (X1, double, Npoints);
+  ALLOCATE (Y1, double, Npoints);
+  ALLOCATE (N1, off_t,  Npoints);
+
+  /** allocate local arrays (catalog) **/
+  Nave = catalog[0].Naverage;
+  ALLOCATE (X2, double, Nave);
+  ALLOCATE (Y2, double, Nave);
+  ALLOCATE (N2, off_t,  Nave);
+
+  /* project onto rectilinear grid with 1 arcsec pixels. the choice of ARC projection has
+   * the advantage that every point in R,D has a mapping to a unique X,Y.  However, note
+   * that not all possible X,Y points map back to R,D and the local plate scale changes
+   * far from the projection pole. We use the center of the region (catalog) for crval1,2.
+   */
+  tcoords.crval1 = 0.5*(region[0].Rmin + region[0].Rmax);
+  if (region[0].Dmax < 90) {
+    tcoords.crval2 = 0.5*(region[0].Dmin + region[0].Dmax);
+  } else {
+    tcoords.crval2 = 90.0;
+  }
+  tcoords.crpix1 = 0;
+  tcoords.crpix2 = 0;
+  tcoords.cdelt1 = tcoords.cdelt2 = 1.0 / 3600.0;
+  tcoords.pc1_1 = tcoords.pc2_2 = 1.0;
+  tcoords.pc1_2 = tcoords.pc2_1 = 0.0;
+  tcoords.Npolyterms = 1;
+  strcpy (tcoords.ctype, "RA---ARC");
+
+  // this region includes a boundary layer of size RADIUS
+  if (fabs(region[0].Dmin) < fabs(region[0].Dmax)) {
+    Doff = RAD_DEG*region[0].Dmax;
+  } else {
+    Doff = RAD_DEG*region[0].Dmin;
+  }    
+  if (Doff < 80) {
+    Rmin = region[0].Rmin - RADIUS / 3600.0 / cos(Doff);
+    Rmax = region[0].Rmax + RADIUS / 3600.0 / cos(Doff);
+  } else {
+    Rmin = 0.0;
+    Rmax = 360.0;
+  }
+  Dmin = region[0].Dmin - RADIUS / 3600.0;
+  Dmax = region[0].Dmax + RADIUS / 3600.0;
+
+  // identify the entries contained by this catalog & init index
+  for (i = 0; i < Npoints; i++) {
+    index[i] = -1;
+    if (RAvec->elements.Flt[i] < Rmin) continue;
+    if (RAvec->elements.Flt[i] > Rmax) continue;
+    if (DECvec->elements.Flt[i] < Dmin) continue;
+    if (DECvec->elements.Flt[i] > Dmax) continue;
+    index[i] = -2;
+  }
+
+  /* build spatial index (RA sort) referencing input array sequence */
+  for (i = 0; i < Npoints; i++) {
+    // we need to have a finite number for the sort below; index == -1 entries are skipped
+    // in the matching process
+    X1[i] = Y1[i] = 0.0; 
+    N1[i] = i;
+    if (index[i] == -1) continue;
+    status = RD_to_XY (&X1[i], &Y1[i], RAvec->elements.Flt[i], DECvec->elements.Flt[i], &tcoords);
+    assert (status);
+  }
+  if (Npoints > 1) sort_coords_index (X1, Y1, N1, Npoints);
+
+  /* build spatial index (RA sort) */
+  for (i = 0; i < Nave; i++) {
+    RD_to_XY (&X2[i], &Y2[i], catalog[0].average[i].R, catalog[0].average[i].D, &tcoords);
+    N2[i] = i;
+  }
+  if (Nave > 1) sort_coords_index (X2, Y2, N2, Nave);
+
+  /* choose a radius for matches */
+  RADIUS2 = RADIUS*RADIUS;
+
+# define NEXTi { i++; continue; }
+# define NEXTj { j++; continue; }
+                                                                                                                                                                                 
+  /** find matched stars **/
+  for (i = j = 0; (i < Npoints) && (j < Nave); ) {
+    if (index[N1[i]] == -1) NEXTi;
+    if (!finite(X1[i]) || !finite(Y1[i])) NEXTi;
+    if (!finite(X2[j]) || !finite(Y2[j])) NEXTj;
+    
+    /* negative dX: j is too large */
+    dX = X1[i] - X2[j];
+    if (dX <= -1.02*RADIUS) NEXTi;
+
+    /* positive dX, i is too large */
+    if (dX >= 1.02*RADIUS) NEXTj;
+
+    /* within match range; look for matches */
+    Jmin = -1;
+    RadMin = RADIUS2;
+    for (J = j; (dX > -1.02*RADIUS) && (J < Nave); J++) {
+      /* find closest match for this detection */
+      dX = X1[i] - X2[J];
+      dY = Y1[i] - Y2[J];
+      dR = dX*dX + dY*dY;
+      if (dR > RADIUS2) continue;
+      if (dR < RadMin) {
+	RadMin = dR;
+	Jmin  = J;
+      }
+    }
+
+    /* no match, try next detection */ 
+    if (Jmin == -1) {
+      NEXTi;
+    }
+
+    /*** a match is found, set the index for this entry ***/
+    index[N1[i]] = N2[Jmin];
+
+    Nmatch ++;
+
+    NEXTi;
+  }
+
+  free (X1);
+  free (Y1);
+  free (N1);
+  free (N2);
+  free (X2);
+  free (Y2);
+
+  return (TRUE);
+}
Index: /branches/sc_branches/pantasks_condor/dvo/find_regions.c
===================================================================
--- /branches/sc_branches/pantasks_condor/dvo/find_regions.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/dvo/find_regions.c	(revision 34783)
@@ -0,0 +1,156 @@
+# include "dvoshell.h"
+# include "hstgsc.h"
+
+/* returns a list of region files within the desired RA, DEC region */
+RegionFile *find_regions (double Ra, double Dec, double radius, int *Nregions) {
+  
+  char filename[256];
+  char buffer[28800], temp[50];
+  RegionFile *regions;
+  FILE *f;
+  double minRa, maxRa, minDec, maxDec, rad;
+
+  double RA0, RA1, DEC0, DEC1;
+  int i, j, NBigDec;
+  int done, NREGIONS, nregion;
+  off_t NLINES;
+  
+  VarConfig ("GSCFILE", "%s", filename);
+  f = fopen (filename, "r");
+  if (f == NULL) {
+    gprint (GP_ERR, "ERROR: can't find regions file %s\n", filename);
+    *Nregions = 0;
+    return ((RegionFile *) NULL);
+  }
+  
+  NREGIONS = 50;
+  ALLOCATE (regions, RegionFile, NREGIONS);
+  nregion = 0;
+
+  Ra = ohana_normalize_angle (Ra);
+
+  minDec = Dec - radius;
+  maxDec = Dec + radius;
+
+  if ((minDec <= -90) || (maxDec >= 90)) {
+    minRa = 0;
+    maxRa = 360;
+  } else {
+    rad = MAX (radius / (cos(minDec*RAD_DEG)), radius / (cos(maxDec*RAD_DEG)));
+    minRa = Ra - rad;
+    maxRa = Ra + rad;
+  }
+  
+  /* use the pole regions, if near pole */
+  if (maxDec > 86.25) {
+    sprintf (regions[nregion].name, "n8230/pole.cpt");
+    regions[nregion].RA0 = 0;
+    regions[nregion].RA1 = 360;
+    regions[nregion].DEC0 = 86.25;
+    regions[nregion].DEC1 = 90.0;
+    nregion ++;
+    if (nregion == NREGIONS) {
+      NREGIONS += 50;
+      REALLOCATE (regions, RegionFile, NREGIONS);
+    }
+  }
+
+  if (minDec > 86.25) {
+    return (regions);
+  }
+    
+  if ((minDec < 0) && (maxDec > 0)) {
+    /* Search Both Sides */
+    NBigDec = 0;
+  } else {
+    /* find large DEC region (directory) */
+    NBigDec = -1;
+    for (i = 0; i < 12; i++) {
+      if ((minDec >= BigDecBounds[i]) && (minDec < BigDecBounds[i+1])) {
+	NBigDec = i;
+	break;
+      }
+    }
+    if (NBigDec < 0) {
+      for (i = 13; i < 24; i++) {
+	if ((maxDec < BigDecBounds[i]) && (maxDec >= BigDecBounds[i+1])) {
+	  NBigDec = i;
+	  break;
+	}
+      }
+    }
+  }
+  if (NBigDec < 0) {
+    gprint (GP_ERR, "ERROR: Dec out of range: %f\n", minDec);
+    *Nregions = 0;
+    return ((RegionFile *) NULL);
+  }
+  
+  /* count lines before section */
+  NLINES = 0;
+  for (i = 0; i < NBigDec; i++) {
+    NLINES += NDecLines[i];
+  }
+  fseeko (f, 5*2880 + 48*NLINES, SEEK_SET);
+  
+  /* should be in this section.  if not, there is a problem counting... */
+  /* careful with the 0,360.0 boundary **/
+  done = FALSE;
+  for (j = 0; !done && (NBigDec + j < 25); j++) {
+    fread (buffer, 48*NDecLines[NBigDec + j], 1, f);
+    for (i = 0; (i < NDecLines[NBigDec + j]); i++) {
+      strncpy (temp, &buffer[i*48], 48);
+      temp[49] = 0;
+      hstgsc_hms_to_deg (&RA0, &RA1, &DEC0, &DEC1, &temp[7]);
+      if (RA1 < RA0) RA1 += 360.0;
+      if ((DEC1 > 0) && (minDec < DEC1) && (maxDec > DEC0) && (minRa < RA1) && (maxRa > RA0)) {
+	temp[5] = 0;
+	sprintf (regions[nregion].name, "%s/%s.cpt", Dec2Sections[NBigDec + j], &temp[1]);
+	regions[nregion].RA0 = RA0;
+	regions[nregion].RA1 = RA1;
+	regions[nregion].DEC0 = DEC0;
+	regions[nregion].DEC1 = DEC1;
+	nregion ++;
+	if (nregion == NREGIONS) {
+	  NREGIONS += 50;
+	  REALLOCATE (regions, RegionFile, NREGIONS);
+	}
+      }
+      if ((DEC1 < 0) && (minDec < DEC0) && (maxDec > DEC1) && (minRa < RA1) && (maxRa > RA0)) {
+	temp[5] = 0;
+	sprintf (regions[nregion].name, "%s/%s.cpt", Dec2Sections[NBigDec + j], &temp[1]);
+	regions[nregion].RA0 = RA0;
+	regions[nregion].RA1 = RA1;
+	regions[nregion].DEC0 = DEC0;
+	regions[nregion].DEC1 = DEC1;
+	nregion ++;
+	if (nregion == NREGIONS) {
+	  NREGIONS += 50;
+	  REALLOCATE (regions, RegionFile, NREGIONS);
+	}
+      }
+      if (((DEC1 > 0) && (maxDec <= DEC1)) || ((DEC1 < 0) && (minDec >= DEC1))) {
+	done = TRUE;
+      }
+    }
+    if (done && (minDec < 0) && (maxDec > 0) && (BigDecBounds[NBigDec + j + 1] > 0)) {
+      /* skip remaining north sections, try south sections */
+      /* count lines before section */
+      NLINES = 0;
+      for (i = 0; i < 13; i++) { 
+	NLINES += NDecLines[i];
+      }
+      fseeko (f, 5*2880 + 48*NLINES, SEEK_SET);
+      done = FALSE;
+      j = 12;
+    }
+  }
+
+  REALLOCATE (regions, RegionFile, MAX(1,nregion));
+  *Nregions = nregion;
+  
+  fclose (f);
+  return (regions);
+  
+}
+
Index: /branches/sc_branches/pantasks_condor/dvo/fitcolors.c
===================================================================
--- /branches/sc_branches/pantasks_condor/dvo/fitcolors.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/dvo/fitcolors.c	(revision 34783)
@@ -0,0 +1,395 @@
+# include "dvoshell.h"
+# define NMIN_PTS 100
+
+static void free_catalog (Catalog *catalog, int Ncatalog);
+
+/* this function takes a photcode (and camera name?) and measures the  *
+ * chip-to-chip slopes for all DEP photcodes equiv to the PRI/SEC code */
+
+int fitcolors (int argc, char **argv) {
+  
+  int *list, Nlist;
+  off_t i, k, m, N1, N2, i1, i2;
+  int N, NP1, NP2, NP, Np, Npts, NPTS;
+  int mode[4];
+  int Nsecfilt, status;
+  void *oldsignal;
+  char *cmd, *outcmd, *camera;
+  char name[64], filename[64], plotname[64], label[64];
+  double *M1, *M2;
+  float *out;
+  float *colorFit, *deltaFit, dColor, C0, C1;
+  opihi_flt minDelta, maxDelta, minColor, maxColor;
+  int kapa, Npx, Npy, NPX, NPY, Nplot, PLOT;
+  Graphdata graphdata;
+  KapaSection section;
+
+  Catalog *catalog;
+  PhotCode **codelist, *tcode, *code[4];
+  SkyList *skylist;
+  SkyRegionSelection *selection;
+  Vector *xvec, *yvec;
+  Buffer *buf;
+
+  /* defaults */
+  catalog  = NULL;
+  skylist  = NULL;
+  selection = NULL;
+  codelist = NULL;
+  xvec = yvec = NULL;
+  colorFit = NULL;
+  deltaFit = NULL;
+
+  oldsignal = signal (SIGINT, handle_interrupt);
+  interrupt = FALSE;
+
+  /* load photcode information */
+  if (!InitPhotcodes ()) goto escape;
+  Nsecfilt = GetPhotcodeNsecfilt ();
+
+  /* interpret command-line options */
+  if ((selection = SetRegionSelection (&argc, argv)) == NULL) goto escape;
+  if (!SetPhotSelections (&argc, argv, 4)) goto usage;
+
+  // range for valid data points (exclude extreme outliers)
+  minDelta = -0.2;
+  maxDelta = +0.2;
+  minColor = -1.0;
+  maxColor = +3.0;
+  if ((N = get_argument (argc, argv, "-color-range"))) {
+    remove_argument (N, &argc, argv);
+    minColor = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+    maxColor = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+  if ((N = get_argument (argc, argv, "-delta-range"))) {
+    remove_argument (N, &argc, argv);
+    minDelta = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+    maxDelta = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  PLOT = FALSE;
+  NPX = NPY = 0;
+  if ((N = get_argument (argc, argv, "-plot"))) {
+    remove_argument (N, &argc, argv);
+    strcpy (plotname, argv[N]);
+    remove_argument (N, &argc, argv);
+    NPX = atof(argv[N]);
+    remove_argument (N, &argc, argv);
+    NPY = atof(argv[N]);
+    remove_argument (N, &argc, argv);
+    PLOT = TRUE;
+  }
+
+  /* interpret command-line options */
+  if (argc != 4) goto usage;
+
+  if (PLOT) {
+    if (!GetGraph (&graphdata, &kapa, NULL)) return (FALSE);
+    Nplot = 0;
+    Npx = Npy = 0;
+    graphdata.xmin = minColor;
+    graphdata.xmax = maxColor;
+    graphdata.ymin = minDelta;
+    graphdata.ymax = maxDelta;
+    graphdata.style = 2;
+    graphdata.ptype = 2;
+    KapaClearSections (kapa);
+    KapaSetFont (kapa, "helvetica", 14);
+
+    ALLOCATE (colorFit, float, 11);
+    ALLOCATE (deltaFit, float, 11);
+    dColor = (maxColor - minColor) / 10.0;
+    for (i = 0; i < 11; i++) {
+      colorFit[i] = minColor + i*dColor;
+    }
+  }
+
+  /* determine relevant photcodes, colors */
+  if (!(Np = GetPhotcodeCodebyName (argv[2]))) {
+    gprint (GP_ERR, "ERROR: photcode not found in photcode table\n");
+    goto usage;
+  }
+  camera = argv[3];
+
+  /* reduce the list of codes */
+  list = GetPhotcodeEquivList (Np, &Nlist);
+  ALLOCATE (codelist, PhotCode *, Nlist);
+  for (i = NP = 0; i < Nlist; i++) {
+    tcode = GetPhotcodebyCode (list[i]);
+    if (strncmp (tcode[0].name, camera, strlen(camera))) continue;
+    codelist[NP] = tcode;
+    NP++;
+  }
+  mode[0] = mode[1] = MAG_REL;  /* we should be applying any relative photometry corrections here */
+  mode[2] = mode[3] = MAG_AVE;
+
+  /* set the reference colors */
+  code[2] = GetPhotcodebyCode (codelist[0][0].c1);
+  code[3] = GetPhotcodebyCode (codelist[0][0].c2);
+  if ((code[2] == NULL) || (code[3] == NULL)) goto color_missing;
+
+  /* all codes must have the same colors (validate) */
+  for (i = 0; i < NP; i++) {
+    if (codelist[i][0].c1 != codelist[0][0].c1) goto color_mismatch;
+    if (codelist[i][0].c2 != codelist[0][0].c2) goto color_mismatch;
+  }
+  gprint (GP_ERR, "using %d photcodes\n", NP);
+
+  /* output is a named buffer */
+  if ((buf = SelectBuffer (argv[1], ANYVECTOR, TRUE)) == NULL) goto usage;
+
+  gfits_free_matrix (&buf[0].matrix);
+  gfits_free_header (&buf[0].header);
+  CreateBuffer (buf, NP, NP, -32, 0.0, 1.0);
+  strcpy (buf[0].file, "(empty)");
+
+  out = (float *) buf[0].matrix.buffer;
+  /* we set a default flag value of -1 */
+  for (i = 0; i < NP*NP; i++) {
+    out[i] = -1;
+  }
+
+  /* load region corresponding to selection above */
+  if ((skylist = SelectRegions (selection)) == NULL) goto escape;
+
+  /* loop over regions, extract data for each region */
+  ALLOCATE (catalog, Catalog, skylist[0].Nregions);
+  for (k = 0; k < skylist[0].Nregions; k++) {
+    /* lock, load, unlock catalog */
+    catalog[k].filename = skylist[0].filename[k];
+    catalog[k].catflags = LOAD_AVES | LOAD_MEAS | LOAD_SECF;
+    catalog[k].Nsecfilt = 0;
+
+    // an error exit status here is a significant error
+    if (!dvo_catalog_open (&catalog[k], NULL, FALSE, "r")) {
+      fprintf (stderr, "ERROR: failure to open catalog file %s\n", catalog[k].filename);
+      exit (2);
+    }
+    dvo_catalog_unlock (&catalog[k]);
+    // XXX make a subset catalog consisting of only Average and Measure values which meet
+    // the selection criteria
+  }
+  gprint (GP_ERR, "using "OFF_T_FMT" possible regions\n",  skylist[0].Nregions);
+
+  /* vectors to save data */
+  Npts = 0;
+  NPTS = 64;
+  if ((xvec = SelectVector ("tmp_x", ANYVECTOR, TRUE)) == NULL) goto escape;
+  if ((yvec = SelectVector ("tmp_y", ANYVECTOR, TRUE)) == NULL) goto escape;
+
+  ResetVector (xvec, OPIHI_FLT, NPTS);
+  ResetVector (yvec, OPIHI_FLT, NPTS);
+
+  // set up basic windows
+  if (PLOT) {
+    Nplot = 0;
+    Npx = Npy = 0;
+    NPX = NPY = 6;
+    KapaInitGraph (&graphdata);
+    graphdata.xmin = minColor;
+    graphdata.xmax = maxColor;
+    graphdata.ymin = minDelta;
+    graphdata.ymax = maxDelta;
+    graphdata.style = 2;
+    graphdata.ptype = 2;
+  }
+
+  /*** generate the color-color vectors for the pairs ***/
+  // XXXX this function also needs to check for interrupts
+  // XXX exclude obvious outliers (eg, fabs(dM) > 0.2)
+  /* loop over chip photcode pairs */
+  for (NP1 = 0; NP1 < NP; NP1++) {
+    for (NP2 = NP1 + 1; NP2 < NP; NP2++) {
+      code[0] = codelist[NP1];
+      code[1] = codelist[NP2];
+      
+      /* extract all magnitude pairs from catalog tables */
+      Npts = 0;
+      for (k = 0; k < skylist[0].Nregions; k++) {
+	if (catalog[k].Naverage == 0) continue;
+
+	// gprint (GP_ERR, "seaching %s with %d stars\n", catalog[k].filename, catalog[k].Naverage);
+	// ListPhotSelections ();
+
+	/* get correct mags, convert to X,Y */
+	for (i = 0; i < catalog[k].Naverage; i++) {
+	  if (interrupt) goto escape;
+
+	  M1 = M2 = NULL;
+	  m = catalog[k].average[i].measureOffset;
+
+	  SetSelectionParam (0);
+	  M1 = ExtractDMag (&code[0], &mode[0], &catalog[k].average[i], &catalog[k].secfilt[i*Nsecfilt], &catalog[k].measure[m], &N1);
+	  if (N1 == 0) goto skip_star;
+
+	  SetSelectionParam (2);
+	  M2 = ExtractDMag (&code[2], &mode[2], &catalog[k].average[i], &catalog[k].secfilt[i*Nsecfilt], &catalog[k].measure[m], &N2);
+	  if (N2 == 0) goto skip_star;
+
+	  for (i1 = 0; i1 < N1; i1++) {
+	    for (i2 = 0; i2 < N2; i2++) {
+	      if (M1[i1] < minDelta) continue;
+	      if (M1[i1] > maxDelta) continue;
+	      if (M2[i2] < minColor) continue;
+	      if (M2[i2] > maxColor) continue;
+	      yvec[0].elements.Flt[Npts] = M1[i1];
+	      xvec[0].elements.Flt[Npts] = M2[i2];
+	      Npts++;
+	      if (Npts >= NPTS) {
+		NPTS += 2000;
+		REALLOCATE (xvec[0].elements.Flt, opihi_flt, NPTS);
+		REALLOCATE (yvec[0].elements.Flt, opihi_flt, NPTS);
+	      }
+	    }
+	  }
+	skip_star:
+	  if (M1 != NULL) free (M1);
+	  if (M2 != NULL) free (M2);
+	}
+	// gprint (GP_ERR, "selected %d stars\n", Npts);
+      }
+
+      if (Npts < NMIN_PTS) continue;
+      xvec[0].Nelements = Npts;
+      yvec[0].Nelements = Npts;
+
+      /* perform robust fit on dmag vs color */
+      cmd = strcreate ("fit tmp_x tmp_y 1 -clip 3 3 -quiet");
+      status = command (cmd, &outcmd, TRUE);
+      if (outcmd != NULL) free (outcmd);
+      
+      C0 = get_double_variable ("C0", &status);
+      C1 = get_double_variable ("C1", &status);
+      
+      /* do something useful with the results (stored in Cn, C0, C1, etc) */
+      gprint (GP_LOG, "%s - %s : ", code[0][0].name, code[1][0].name);
+      gprint (GP_LOG, "%7.4f %7.4f   %7.4f   ", 
+	       C0, C1, get_double_variable ("dC", &status));
+      gprint (GP_LOG, "%5s of %5d\n", get_variable ("Cnv"), Npts);
+      out[NP1 + NP2*NP] = C1;
+
+      // make an illustrating plot of each chip vs each other chip
+      // each page should have, say, a 6x6 grid of plots. each one should show a single chip pair
+      // as the page fills up, it gets written and a new one created.  
+      if (PLOT) {
+	sprintf (name, "s%02d.%02d", Npx, Npy);
+	section.name = strcreate (name);
+	if (Npx || Npy) {
+	  section.dx = 0.9 / NPX;
+	  section.dy = 0.9 / NPY;
+	  section.x = 0.1 + Npx * section.dx;
+	  section.y = 0.1 + Npy * section.dy;
+	  strcpy (graphdata.labels, "0000");
+	} 
+	if (Npx == 0) {
+	  section.dx = 0.9 / NPX + 0.1;
+	  section.dy = 0.9 / NPY;
+	  section.x = 0.0;
+	  section.y = 0.1 + Npy * section.dy;
+	  strcpy (graphdata.labels, "0100");
+	}
+	if (Npy == 0) {
+	  section.dx = 0.9 / NPX;
+	  section.dy = 0.9 / NPY + 0.1;
+	  section.x = 0.1 + Npx * section.dx;
+	  section.y = 0.0;
+	  strcpy (graphdata.labels, "1000");
+	}
+	if (!Npx && !Npy) {
+	  section.dx = 0.9 / NPX + 0.1;
+	  section.dy = 0.9 / NPY + 0.1;
+	  section.x = 0.0;
+	  section.y = 0.0;
+	  strcpy (graphdata.labels, "1100");
+	}
+	KapaSetSection (kapa, &section);
+	KapaSetLimits (kapa, &graphdata);
+	KapaBox (kapa, &graphdata);
+
+	PlotVectorPair (kapa, xvec, yvec, NULL, &graphdata);
+
+	for (i = 0; i < 11; i++) {
+	  deltaFit[i] = C0 + C1*colorFit[i];
+	}
+	graphdata.style = 0;
+	graphdata.color = KapaColorByName ("red");
+
+	KapaPrepPlot (kapa, 11, &graphdata);
+	KapaPlotVector (kapa, 11, colorFit, "x");
+	KapaPlotVector (kapa, 11, deltaFit, "y");
+
+	KapaSetFont (kapa, "helvetica", 8);
+	sprintf (label, "%s", code[0][0].name);
+	KapaSendTextline (kapa, label, 0.2*maxColor + 0.8*minColor, 0.8*maxDelta + 0.2*minDelta, 0.0);
+	sprintf (label, "%s", code[1][0].name);
+	KapaSendTextline (kapa, label, 0.2*maxColor + 0.8*minColor, 0.2*maxDelta + 0.8*minDelta, 0.0);
+	KapaSetFont (kapa, "helvetica", 14);
+
+	graphdata.style = 2;
+	graphdata.color = KapaColorByName ("black");
+
+	free (section.name);
+
+	Npx++;
+	if (Npx == NPX) {
+	  Npx = 0;
+	  Npy ++;
+	  if (Npy == NPY) {
+	    Npy = 0;
+	    sprintf (filename, "%s.%02d.png", plotname, Nplot);
+	    KapaPNG (kapa, filename);
+	    KapaClearSections (kapa);
+	    Nplot++;
+	  }
+	}
+      }	
+    }
+  }
+  if (skylist != NULL) free_catalog (catalog, skylist[0].Nregions);
+  SkyListFree (skylist);
+  FreeSkyRegionSelection (selection);
+  if (codelist != NULL) free (codelist);
+  if (colorFit != NULL) free (colorFit);
+  if (deltaFit != NULL) free (deltaFit);
+  signal (SIGINT, oldsignal);
+  return (TRUE);
+
+usage:
+  gprint (GP_ERR, "USAGE: chipcolors (output) (photcode) (camera)\n");
+  goto escape;
+
+color_missing:
+  gprint (GP_ERR, "error: chips are missing a color reference\n");
+  goto escape;
+
+color_mismatch:
+  gprint (GP_ERR, "error: all chips must have the same colors\n");
+  goto escape;
+
+escape:
+  if (skylist != NULL) free_catalog (catalog, skylist[0].Nregions);
+  SkyListFree (skylist);
+  FreeSkyRegionSelection (selection);
+  if (codelist != NULL) free (codelist);
+  if (colorFit != NULL) free (colorFit);
+  if (deltaFit != NULL) free (deltaFit);
+  DeleteVector (xvec);
+  DeleteVector (yvec);
+  signal (SIGINT, oldsignal);
+  return (FALSE);
+}
+
+static void free_catalog (Catalog *catalog, int Ncatalog) {
+
+  int i;
+
+  if (catalog == NULL) return;
+  for (i = 0; i < Ncatalog; i++) {
+    dvo_catalog_free (&catalog[i]);
+  }
+  free (catalog);
+}
Index: /branches/sc_branches/pantasks_condor/dvo/fitsed.c
===================================================================
--- /branches/sc_branches/pantasks_condor/dvo/fitsed.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/dvo/fitsed.c	(revision 34783)
@@ -0,0 +1,458 @@
+# include "dvoshell.h"
+
+typedef struct {
+  float *mags;
+  float color;
+  float Temp;
+  float Av;
+} SEDtableRow;
+
+typedef struct {
+  float chisq;
+  float Md;
+  int row;
+} SEDfit;
+
+SEDtableRow **sort_SEDtable (SEDtableRow *raw, int N);
+int SEDcolorBracket (SEDtableRow **table, int Ntable, float color);
+SEDfit SEDchisq (SEDtableRow *ref, SEDtableRow *data, SEDtableRow *error, int Nfilter);
+
+/* this function takes a photcode (and camera name?) and measures the  *
+ * chip-to-chip slopes for all DEP photcodes equiv to the PRI/SEC code */
+
+int fitsed (int argc, char **argv) {
+  
+  int *hashcode;
+  off_t i, j, k, m;
+  int N, done, Nfit;
+  int status;
+  void *oldsignal;
+  char name[64], line[1024], key[20];
+  float *fitmags, *fiterrs, *wavecode, *vegaToAB;
+  float color;
+  double X, Y, ZP, RA, DEC;
+  int kapa, PLOT;
+  int Nrow, NROW, idx, Nfilter, start, row;
+  unsigned short colorP, colorM, code, USNOred, USNOblu;
+  int codeP, codeM;
+  FILE *f;
+
+  Graphdata graphdata;
+  KapaSection magSection, resSection;
+
+  Catalog catalog;
+  SkyList *skylist;
+  SkyRegionSelection *selection;
+
+  SEDtableRow *SEDtableRaw, **SEDtable;
+  SEDtableRow sourceValue, sourceError;
+  SEDfit minFit, testFit;
+
+  /* defaults */
+  skylist  = NULL;
+  selection = NULL;
+
+  catalog.average = NULL;
+  catalog.measure = NULL;
+  catalog.secfilt = NULL;
+
+  // outcat.average = NULL;
+  // outcat.measure = NULL;
+  // outcat.secfilt = NULL;
+
+  SEDtable = NULL;
+  SEDtableRaw = NULL;
+  sourceValue.mags = NULL;
+  sourceError.mags = NULL;
+  wavecode = NULL;
+  hashcode = NULL;
+  magSection.name = NULL;
+  resSection.name = NULL;
+
+  Nrow = 0;
+
+  fiterrs = NULL;
+  fitmags = NULL;
+
+  oldsignal = signal (SIGINT, handle_interrupt);
+  interrupt = FALSE;
+
+  /* load photcode information */
+  if (!InitPhotcodes ()) goto escape;
+
+  /* interpret command-line options */
+  if ((selection = SetRegionSelection (&argc, argv)) == NULL) goto escape;
+  if (!SetPhotSelections (&argc, argv, 4)) goto usage;
+
+  PLOT = FALSE;
+  if ((N = get_argument (argc, argv, "-plot"))) {
+    remove_argument (N, &argc, argv);
+    PLOT = TRUE;
+  }
+
+  /* interpret command-line options */
+  if (argc != 6) goto usage;
+
+  Nfit = 0;
+  colorP = GetPhotcodeCodebyName (argv[3]);
+  colorM = GetPhotcodeCodebyName (argv[5]);
+  if (!colorP || !colorM) goto color_undefined;
+
+  // artificially set USNOred and blu errors to 0.3
+  USNOred = GetPhotcodeCodebyName ("USNO_RED");
+  USNOblu = GetPhotcodeCodebyName ("USNO_BLUE");
+
+  // load SED table
+  f = fopen (argv[1], "r");
+  if (f == NULL) goto table_missing;
+
+  // XXX add error checks for header data
+  scan_line (f, line);
+  sscanf (line, "%*s %*s %d", &Nfilter);
+
+  // load SED table photcodes, generate the photcode hashtable
+  ALLOCATE (hashcode, int, 0x10000);
+  ALLOCATE (wavecode, float, Nfilter);
+  ALLOCATE (vegaToAB, float, Nfilter);
+
+  for (i = 0; i < 0x10000; i++) hashcode[i] = -1;
+  for (i = 0; i < Nfilter; i++) {
+    scan_line (f, line);
+    sscanf (line, "%*s %s %f %f", name, &wavecode[i], &vegaToAB[i]);
+    code = GetPhotcodeCodebyName (name);
+    if (code == 0) goto code_missing;
+    hashcode[code] = i;
+  }
+  codeP = hashcode[colorP];
+  codeM = hashcode[colorM];
+  if ((codeP == -1) || (codeM == -1)) goto color_missing;
+    
+  // skip remaining header lines
+  scan_line (f, line);
+  scan_line (f, line);
+  scan_line (f, line);
+  scan_line (f, line);
+  
+  // load the SED table data
+  Nrow = 0;
+  NROW = 100;
+  ALLOCATE (SEDtableRaw, SEDtableRow, NROW);
+  while (scan_line(f, line) != EOF) {
+    fparse (&SEDtableRaw[Nrow].Temp, 1, line);
+    fparse (&SEDtableRaw[Nrow].Av, 2, line);
+    ALLOCATE (SEDtableRaw[Nrow].mags, float, Nfilter);
+    for (i = 0; i < Nfilter; i++) {
+      fparse (&SEDtableRaw[Nrow].mags[i], i + 3, line);
+    }
+    SEDtableRaw[Nrow].color = SEDtableRaw[Nrow].mags[codeP] - SEDtableRaw[Nrow].mags[codeM];
+    Nrow ++;
+    CHECK_REALLOCATE (SEDtableRaw, SEDtableRow, NROW, Nrow, 100);
+  }      
+
+  // sort the SEDtable by the reference colors
+  SEDtable = sort_SEDtable (SEDtableRaw, Nrow);
+
+  // create holder for the source data
+  ALLOCATE (sourceValue.mags, float, Nfilter);
+  ALLOCATE (sourceError.mags, float, Nfilter);
+
+  if (PLOT) {
+    if (!GetGraph (&graphdata, &kapa, NULL)) return (FALSE);
+    SetLimitsRaw (wavecode, NULL, Nfilter, &graphdata);
+    graphdata.style = 2;
+    graphdata.ptype = 2;
+    KapaClearSections (kapa);
+    magSection.name = strcreate ("mag");
+    magSection.x  = 0;
+    magSection.dx = 1;
+    magSection.y  = 0.5;
+    magSection.dy = 0.5;
+    resSection.name = strcreate ("res");
+    resSection.x  = 0.0;
+    resSection.dx = 1.0;
+    resSection.y  = 0.0;
+    resSection.dy = 0.5;
+    
+    KapaSetFont (kapa, "helvetica", 14);
+    ALLOCATE (fitmags, float, Nfilter);
+    ALLOCATE (fiterrs, float, Nfilter);
+  }
+
+  /* load region corresponding to selection above */
+  if ((skylist = SelectRegions (selection)) == NULL) goto escape;
+
+  /* loop over regions, extract data for each region */
+  // XXX add interrupt checks
+  gprint (GP_ERR, "using "OFF_T_FMT" possible regions\n",  skylist[0].Nregions);
+  for (k = 0; k < skylist[0].Nregions; k++) {
+    /* lock, load, unlock catalog */
+    catalog.filename = skylist[0].filename[k];
+    catalog.catflags = LOAD_AVES | LOAD_MEAS | LOAD_SECF;
+    catalog.Nsecfilt = 0;
+
+    // an error exit status here is a significant error
+    if (!dvo_catalog_open (&catalog, NULL, FALSE, "r")) {
+      fprintf (stderr, "ERROR: failure to open catalog file %s\n", catalog.filename);
+      exit (2);
+    }
+    dvo_catalog_unlock (&catalog);
+
+    // perform the fit to all sources
+    for (i = 0; i < catalog.Naverage; i++) {
+
+      // blank out the source array
+      for (j = 0; j < Nfilter; j++) {
+	sourceValue.mags[j] = 100;
+      }	
+
+      // load the measurements for this source
+      m = catalog.average[i].measureOffset;
+      for (j = 0; j < catalog.average[i].Nmeasure; j++) {
+	idx = hashcode[catalog.measure[m+j].photcode];
+	if (idx == -1) continue;
+	// XXX do something more clever if more than one value exists per photcode
+	sourceValue.mags[idx] = catalog.measure[m+j].M + vegaToAB[idx];
+	sourceError.mags[idx] = catalog.measure[m+j].dM;
+	if ((catalog.measure[m+j].photcode == USNOred) || (catalog.measure[m+j].photcode == USNOblu)) {
+	  sourceError.mags[idx] = 0.3;
+	}
+      }
+
+      // XXX for the moment, skip sources without ref color
+      if (sourceValue.mags[codeP] > 50) continue;
+      if (sourceValue.mags[codeM] > 50) continue;
+      color = sourceValue.mags[codeP] - sourceValue.mags[codeM];
+
+      // XXX find tableRow within 0.1 mag of color
+      start = SEDcolorBracket (SEDtable, Nrow, color);
+      minFit = SEDchisq (SEDtable[start], &sourceValue, &sourceError, Nfilter);
+      minFit.row = start;
+
+      // search for min chisq backwards
+      done = FALSE;
+      row = start - 1;
+      while (!done && (row > 0)) {
+	testFit = SEDchisq (SEDtable[row], &sourceValue, &sourceError, Nfilter);
+	if (testFit.chisq < minFit.chisq) {
+	  minFit = testFit;
+	  minFit.row = row;
+	}
+	if (fabs(SEDtable[row][0].color - color) > 0.5) done = TRUE;
+	row --;
+      }
+
+      // search for min chisq forwards
+      done = FALSE;
+      row = start + 1;
+      while (!done && (row < Nrow)) {
+	testFit = SEDchisq (SEDtable[row], &sourceValue, &sourceError, Nfilter);
+	if (testFit.chisq < minFit.chisq) {
+	  minFit = testFit;
+	  minFit.row = row;
+	}
+	if (fabs(SEDtable[row][0].color - color) > 0.5) done = TRUE;
+	row ++;
+      }
+
+      Nfit ++;
+      // create the vectors for the example plots
+      if (PLOT) {
+	// find plot range
+	SetLimitsRaw (NULL, SEDtable[minFit.row][0].mags, Nfilter, &graphdata);
+	SWAP (graphdata.ymin, graphdata.ymax);
+
+	KapaClearSections (kapa);
+	KapaSetSection (kapa, &magSection);
+    	KapaSetLimits (kapa, &graphdata);
+	KapaBox (kapa, &graphdata);
+	graphdata.color = KapaColorByName ("blue");
+	graphdata.etype = 0;
+	KapaPrepPlot (kapa, Nfilter, &graphdata);
+	KapaPlotVector (kapa, Nfilter, wavecode, "x");
+	KapaPlotVector (kapa, Nfilter, SEDtable[minFit.row][0].mags, "y");
+	graphdata.color = KapaColorByName ("red");
+	graphdata.etype = 1;
+	for (j = 0; j < Nfilter; j++) {
+	  fitmags[j] = 100;
+	  fiterrs[j] = 0;
+	  if (sourceValue.mags[j] > 50) continue;
+	  fitmags[j] = sourceValue.mags[j] - minFit.Md;
+	  fiterrs[j] = sourceError.mags[j];
+	}
+	KapaPrepPlot (kapa, Nfilter, &graphdata);
+	KapaPlotVector (kapa, Nfilter, wavecode, "x");
+	KapaPlotVector (kapa, Nfilter, fitmags, "y");
+	KapaPlotVector (kapa, Nfilter, fiterrs, "dym");
+	KapaPlotVector (kapa, Nfilter, fiterrs, "dyp");
+	KapaSendLabel (kapa, "model,fit (mags)", 1);
+
+	sprintf (line, "star: %10.6f %10.6f  T: %5.0fK  A_V|: %4.2f  M_D|: %5.2f  &sc&h^2|: %5.2f", 
+		 catalog.average[i].R, catalog.average[i].D, 
+		 SEDtable[minFit.row][0].Temp, SEDtable[minFit.row][0].Av, minFit.Md, minFit.chisq);
+	KapaSendLabel (kapa, line, 2);
+	KapaSendLabel (kapa, "model,fit (mags)", 1);
+
+	KapaSetSection (kapa, &resSection);
+	graphdata.ymin = -1.0;
+	graphdata.ymax = +1.0;
+    	KapaSetLimits (kapa, &graphdata);
+	KapaBox (kapa, &graphdata);
+	graphdata.color = KapaColorByName ("red");
+	graphdata.etype = 1;
+
+	for (j = 0; j < Nfilter; j++) {
+	  fitmags[j] = 100;
+	  fiterrs[j] = 0;
+	  if (sourceValue.mags[j] > 50) continue;
+	  fitmags[j] = sourceValue.mags[j] - minFit.Md - SEDtable[minFit.row][0].mags[j];
+	  fiterrs[j] = sourceError.mags[j];
+	}
+	KapaPrepPlot (kapa, Nfilter, &graphdata);
+	KapaPlotVector (kapa, Nfilter, wavecode, "x");
+	KapaPlotVector (kapa, Nfilter, fitmags, "y");
+	KapaPlotVector (kapa, Nfilter, fiterrs, "dym");
+	KapaPlotVector (kapa, Nfilter, fiterrs, "dyp");
+	KapaSendLabel (kapa, "wavelength (nm)", 0);
+	KapaSendLabel (kapa, "resid (mags)", 1);
+
+	KiiCursorOn (kapa);
+	while (KiiCursorRead (kapa, &X, &Y, &ZP, &RA, &DEC, key)) {
+	  gprint (GP_ERR, "window: %f %f (%s)\n", X, Y, key);
+	  if (!strcasecmp (key, "Q")) {
+	    KiiCursorOff (kapa);
+	    break;
+	  }
+	  if (!strcasecmp (key, "ESCAPE")) {
+	    KiiCursorOff (kapa);
+	    goto escape;
+	  }
+	}
+      }
+      // we now have the min chisq row. use this to supply the other filter values....
+    }
+    dvo_catalog_free (&catalog);
+  }
+  gprint (GP_ERR, "fitted %d stars\n", Nfit);
+  status = TRUE;
+  goto finish;
+  
+usage:
+  gprint (GP_ERR, "USAGE: fitset (sedtable) : (F) - (F)\n");
+  goto escape;
+
+table_missing:
+  gprint (GP_ERR, "ERROR: can't open the SED table\n");
+  goto escape;
+
+color_missing:
+  gprint (GP_ERR, "ERROR: reference color not in SED table\n");
+  goto escape;
+
+color_undefined:
+  gprint (GP_ERR, "ERROR: undefined photcode in reference color\n");
+  goto escape;
+
+code_missing:
+  gprint (GP_ERR, "ERROR: undefined photcode in SED table\n");
+  goto escape;
+
+escape:
+  status = FALSE;
+  goto finish;
+
+finish:
+  SkyListFree (skylist);
+  FreeSkyRegionSelection (selection);
+  if (wavecode != NULL) free (wavecode);
+  if (hashcode != NULL) free (hashcode);
+  if (SEDtableRaw != NULL) {
+    for (i = 0; i < Nrow; i++) {
+      free (SEDtableRaw[i].mags);
+    }
+    free (SEDtableRaw);
+  }
+  if (SEDtable != NULL) free (SEDtable);
+  if (sourceValue.mags != NULL) free (sourceValue.mags);
+  if (sourceError.mags != NULL) free (sourceError.mags);
+
+  signal (SIGINT, oldsignal);
+  return (status);
+}
+
+// fit the data (with errors) to the given table row
+SEDfit SEDchisq (SEDtableRow *ref, SEDtableRow *data, SEDtableRow *error, int Nfilter) {
+
+  int i;
+  double Sm, Sd, S2, wt, dM;
+  SEDfit fit;
+
+  Sm = Sd = S2 = 0.0;
+
+  for (i = 0; i < Nfilter; i++) {
+    if (data[0].mags[i] > 50.0) continue;
+
+    if (error[0].mags[i] == 0.0) {
+      wt = 1.0;
+    } else {
+      wt = 1.0 / SQ(error[0].mags[i]);
+    }
+
+    dM = data[0].mags[i] - ref[0].mags[i];
+    S2 += SQ(dM) * wt;
+    Sm += dM * wt;
+    Sd += wt;
+  }
+    
+  // row is assigned after fit
+  fit.row = -1;
+  fit.Md = Sm / Sd;
+  fit.chisq = S2 + SQ(fit.Md) * Sd - 2*fit.Md*Sm;
+
+  return (fit);
+}
+
+// find the first table row within 0.1 mag of the requested color (or within 10)
+int SEDcolorBracket (SEDtableRow **table, int Ntable, float color) {
+
+  int Nlo, Nhi, N;
+  float tcolor;
+
+  N = Nlo = 0; Nhi = Ntable;
+  tcolor = table[Nlo][0].color;
+  while ((Nhi - Nlo > 10) && (fabs(tcolor-color) > 0.1)) {
+    N = 0.5*(Nlo + Nhi);
+    N = MAX (N, 0);
+    N = MIN (N, Ntable - 1);
+    tcolor = table[N][0].color;
+    if (tcolor < color) {
+      Nlo = N;
+    } else {
+      Nhi = N + 1;
+    }
+  }
+  return (N);
+}
+
+SEDtableRow **sort_SEDtable (SEDtableRow *raw, int N) {
+
+  int i;
+  SEDtableRow **value;
+  
+  if (N <= 0) return (NULL);
+
+  ALLOCATE (value, SEDtableRow *, N);
+  for (i = 0; i < N; i++) {
+    value[i] = &raw[i];
+  }
+
+# define SWAPFUNC(A,B){ SEDtableRow *temp = value[A]; value[A] = value[B]; value[B] = temp; }
+# define COMPARE(A,B)(value[A][0].color < value[B][0].color)
+
+  OHANA_SORT (N, COMPARE, SWAPFUNC);
+
+# undef SWAPFUNC
+# undef COMPARE
+
+  return (value);
+}
+
Index: /branches/sc_branches/pantasks_condor/dvo/gcat.c
===================================================================
--- /branches/sc_branches/pantasks_condor/dvo/gcat.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/dvo/gcat.c	(revision 34783)
@@ -0,0 +1,75 @@
+# include "dvoshell.h"
+
+int gcat (int argc, char **argv) {
+  
+  int i, N;
+  struct stat filestat;
+  double Ra, Dec, Radius;
+  SkyTable *sky;
+  SkyList *skylist;
+
+  int ShowHost = FALSE;
+  if ((N = get_argument (argc, argv, "-host"))) {
+    remove_argument (N, &argc, argv);
+    ShowHost = TRUE;
+  }
+  int ShowBackup = FALSE;
+  if ((N = get_argument (argc, argv, "-backup"))) {
+    remove_argument (N, &argc, argv);
+    ShowBackup = TRUE;
+  }
+  int ShowFlags = FALSE;
+  if ((N = get_argument (argc, argv, "-flags"))) {
+    remove_argument (N, &argc, argv);
+    ShowFlags = TRUE;
+  }
+
+  if ((argc != 3) && (argc != 4)) {
+    gprint (GP_ERR, "USAGE: gcat RA DEC [Radius] [-host] [-backup] [-flags]\n");
+    return (FALSE);
+  }
+
+  /* load sky from correct table */
+  Ra = atof (argv[1]);
+  Dec = atof (argv[2]);
+  if (argc == 4) 
+    Radius = atof(argv[3]);
+  else 
+    Radius = 0.0001;
+
+  sky = GetSkyTable ();
+  skylist = SkyListByRadius (sky, -1, Ra, Dec, Radius);
+
+  // prepare to handle interrupt signals
+  signal (SIGINT, handle_interrupt);
+  interrupt = FALSE;
+
+  for (i = 0; (i < skylist[0].Nregions) && !interrupt; i++) {
+    gprint (GP_ERR, "%3d %s", i, skylist[0].regions[i][0].name);
+    if (stat (skylist[0].filename[i], &filestat) != -1) {
+      gprint (GP_ERR, " *");
+    } else {
+      gprint (GP_ERR, "  ");
+    } 
+    if (ShowHost) {
+      gprint (GP_ERR, "  %3d", skylist[0].regions[i][0].hostID);
+    } else {
+      gprint (GP_ERR, "     ");
+    }
+    if (ShowBackup) {
+      gprint (GP_ERR, "  %3d", skylist[0].regions[i][0].backupID);
+    } else {
+      gprint (GP_ERR, "     ");
+    }
+    if (ShowFlags) {
+      gprint (GP_ERR, "  0x%04x", skylist[0].regions[i][0].hostFlags);
+    } else {
+      gprint (GP_ERR, "        ");
+    }
+    gprint (GP_ERR, "\n");
+    set_str_variable ("CATNAME", skylist[0].filename[i]);
+  }
+
+  return (TRUE);
+  SkyListFree (skylist);
+}
Index: /branches/sc_branches/pantasks_condor/dvo/get_regions.c
===================================================================
--- /branches/sc_branches/pantasks_condor/dvo/get_regions.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/dvo/get_regions.c	(revision 34783)
@@ -0,0 +1,50 @@
+# include "dvoshell.h"
+
+/* return region files containing given image */
+GSCRegion *get_regions (Image *image, int *Nregions) {
+  
+  GSCRegion *region;
+  FILE *f;
+  double x, y;
+  double dec, ra;
+  int j, done, nregion, NREGION;
+  char filename[256], path[256];
+  
+  VarConfig ("CATDIR", "%s", path);
+  VarConfig ("GSCFILE", "%s", filename);
+  f = fopen (filename, "r");
+  if (f == NULL) {
+    gprint (GP_ERR, "ERROR: can't find GSC region file %s\n", filename);
+    exit (0);
+  }
+  
+  /* find regions at image corners */
+  NREGION = 10;
+  ALLOCATE (region, GSCRegion, NREGION);
+  nregion = 0;
+
+  /* look for new regions on grid across image */ 
+  for (x = 0.0; x <= 1.0; x+=0.25) {
+    for (y = 0.0; y <= 1.0; y+=0.25) {
+      XY_to_RD (&ra, &dec, image[0].NX*(1.1*x - 0.05), image[0].NY*(1.1*y - 0.05), &image[0].coords);
+      aregion (&region[nregion], f, ra, dec, path);
+      done = FALSE;
+      for (j = 0; (j < nregion) && !done; j++) {
+	if (!strcmp (region[nregion].filename, region[j].filename)) {
+	  nregion --;
+	  done = TRUE;
+	}
+      }
+      nregion ++;
+      if (nregion == NREGION) {
+	NREGION += 10;
+	REALLOCATE (region, GSCRegion, NREGION);
+      }
+    }
+  }
+  *Nregions = nregion;
+  
+  fclose (f);
+  return (region);
+  
+}
Index: /branches/sc_branches/pantasks_condor/dvo/getxtra.c
===================================================================
--- /branches/sc_branches/pantasks_condor/dvo/getxtra.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/dvo/getxtra.c	(revision 34783)
@@ -0,0 +1,63 @@
+# include "dvoshell.h"
+
+int getxtra (int argc, char **argv) {
+  
+  SelectXtra = (char *) NULL;
+  if (N = get_argument (argc, argv, "-xtra")) {
+    SelectXtra = TRUE;
+    remove_argument (N, &argc, argv);
+    XtraType = strcreate (atof[N]);
+    remove_argument (N, &argc, argv);
+    XtraValue = strcreate (atof[N]);
+    remove_argument (N, &argc, argv);
+  }
+  SelectRegion = (char *) NULL;
+  if (N = get_argument (argc, argv, "-region")) {
+    SelectRegion = TRUE;
+    remove_argument (N, &argc, argv);
+    RA = atof (atof[N]);
+    remove_argument (N, &argc, argv);
+    Dec = atof (atof[N]);
+    remove_argument (N, &argc, argv);
+    Radius = atof (atof[N]);
+    remove_argument (N, &argc, argv);
+  }
+  Mode = (char *) NULL;
+  if (N = get_argument (argc, argv, "-mode")) {
+    remove_argument (N, &argc, argv);
+    Mode = strcreate (atof[N]);
+    remove_argument (N, &argc, argv);
+  }
+  Range = (char *) NULL;
+  if (N = get_argument (argc, argv, "-range")) {
+    remove_argument (N, &argc, argv);
+    Range = strcreate (atof[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  if (argc != 6) {
+    gprint (GP_ERR, "USAGE: addxtra R D radius (type) (value)\n");
+    return (FALSE);
+  }
+  
+  
+
+  /*
+
+  validate the input values (type, defines the needed options)
+  find catalog (based on r,d)
+  load catalog (need to load measures?)
+  find the object
+  find the xtra entry (sorted by ra/dec? sorted by averef?)
+  add new entry
+  save catalog 
+  */
+}
+
+/* 
+   get specified values for specified objects 
+
+   getxtra type -xtra name K01.01 
+   getxtra type -region R D radius 
+
+*/
Index: /branches/sc_branches/pantasks_condor/dvo/gimages.c
===================================================================
--- /branches/sc_branches/pantasks_condor/dvo/gimages.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/dvo/gimages.c	(revision 34783)
@@ -0,0 +1,252 @@
+# include "dvoshell.h"
+
+// XXX this needs some help: it should define the region for image_subset or otherwise
+// limit the input selection
+int gimages (int argc, char **argv) {
+  
+  off_t i, j, Nimage, *subset, Nsubset;
+  int N, Nfound, status;
+  double ra, dec, Ra, Dec, X, Y, Yo;
+  double trange, t;
+  int TimeSelect, PixelCoords, TimeFormat, PhotCodeSelect;
+  time_t tzero, TimeReference;
+  char name[64], *date;
+  int typehash;
+  SkyRegionSelection *selection;
+
+  PhotCode *code;
+  Image *image;
+
+  if (!InitPhotcodes ()) return (FALSE);
+
+  GetTimeFormat (&TimeReference, &TimeFormat);
+
+  // parse skyregion options
+  if ((selection = SetRegionSelection (&argc, argv)) == NULL) {
+    gprint (GP_ERR, "invalid sky region selection\n");
+    return FALSE;
+  }
+
+  TimeSelect = FALSE;
+  if ((N = get_argument (argc, argv, "-time"))) {
+    remove_argument (N, &argc, argv);
+    if (!ohana_str_to_time (argv[N], &tzero)) { 
+      gprint (GP_ERR, "syntax error\n");
+      return (FALSE);
+    }
+    remove_argument (N, &argc, argv);
+    if (!ohana_str_to_dtime (argv[N], &trange)) { 
+      gprint (GP_ERR, "syntax error\n");
+      return (FALSE);
+    }
+    remove_argument (N, &argc, argv);
+    TimeSelect = TRUE;
+  }
+  if ((N = get_argument (argc, argv, "-tref"))) {
+    remove_argument (N, &argc, argv);
+    TimeSelect = TRUE;
+
+    t = atof (argv[N]);
+    tzero = TimeRef (t, TimeReference, TimeFormat);
+    remove_argument (N, &argc, argv);
+
+    if (!ohana_str_to_dtime (argv[N], &trange)) { 
+      gprint (GP_ERR, "syntax error\n");
+      return (FALSE);
+    }
+    remove_argument (N, &argc, argv);
+  }
+
+  code = NULL;
+  PhotCodeSelect = FALSE;
+  if ((N = get_argument (argc, argv, "-photcode"))) {
+    remove_argument (N, &argc, argv);
+    if ((code = GetPhotcodebyName (argv[N])) == NULL) {
+      gprint (GP_ERR, "ERROR: photcode not found in photcode table\n");
+      return (FALSE);
+    }
+    PhotCodeSelect = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+
+  PixelCoords = FALSE;
+  if ((N = get_argument (argc, argv, "-pix"))) {
+    remove_argument (N, &argc, argv);
+    PixelCoords = TRUE;
+  }
+
+  if (argc != 3) {
+    gprint (GP_ERR, "USAGE: gimages RA DEC [-time t dt] [-pix]\n");
+    return (FALSE);
+  }
+
+  if (!ohana_str_to_radec (&Ra, &Dec, argv[1], argv[2])) return (FALSE);
+
+  if ((image = LoadImagesDVO (&Nimage)) == NULL) return (FALSE);
+  image_subset (image, Nimage, &subset, &Nsubset, selection, tzero, trange, TimeSelect);
+  BuildChipMatch (image, Nimage);
+
+  int DistortImage = wordhash ("-DIS");
+  int TriangleUp   = wordhash ("TRP-");
+  int TriangleDn   = wordhash ("TRM-");
+
+  Nfound = 0;
+  for (j = 0; j < Nsubset; j++) {
+    i = subset[j];
+    if (PhotCodeSelect) {
+      if ((code[0].type == PHOT_REF) || (code[0].type == PHOT_DEP)) {
+	if (code[0].code != image[i].photcode) continue;
+      } 
+      if (code[0].type == PHOT_SEC) {
+	if (code[0].code != GetPhotcodeEquivCodebyCode (image[i].photcode)) continue;
+      } 
+    }      
+    if (!FindMosaicForImage (image, Nimage, i)) continue;
+
+    typehash = wordhash (&image[i].coords.ctype[4]);
+
+    // for non-linear astrometry solutions, a point which is far from the image may
+    // potentially appear to land in the image: at large field-position, the high order
+    // terms which are supposed to be perturbations may dominate the solution
+
+    // XXX this code checks if the image is even in the ballpark.  this is not a great
+    // solution.  the better way to do this would be to use the linear portions of the fit
+    // only for RD_to_XY, then see if the result is close.
+    { 
+      Coords local;
+      double Ro, Do, Xo, Yo, Xs, Ys, Radius;
+      
+      if (typehash == DistortImage) {
+	Xo = 0.0;
+	Yo = 0.0;
+      } else {
+	Xo = 0.5*image[i].NX;
+	Yo = 0.5*image[i].NY;
+      }
+
+      // find coordinates of image center
+      XY_to_RD (&Ro, &Do, Xo, Yo, &image[i].coords);
+      if (fabs(Ro - Ra) > 120.0) continue;
+
+      local.crval1 = Ro;
+      local.crval2 = Do;
+      local.crpix1 = local.crpix2 = 0.0;
+      local.cdelt1 = local.cdelt2 = 1.0/3600.0;
+      local.pc1_1 = local.pc2_2 = 1.0;
+      local.pc2_1 = local.pc1_2 = 0.0;
+      local.Npolyterms = 1;
+      strcpy (local.ctype, "RA---TAN");
+
+      if (typehash == DistortImage) {
+	Xs = -0.5*image[i].NX;
+	Ys = -0.5*image[i].NY;
+      } else {
+	Xs = 0.0;
+	Ys = 0.0;
+      }
+      
+      // find coordinates of an image corner
+      XY_to_RD (&Ro, &Do, Xs, Ys, &image[i].coords);
+
+      // find radius of image in arcsec
+      RD_to_XY (&Xo, &Yo, Ro, Do, &local);
+      Radius = hypot (Xo, Yo);
+      // fprintf (stderr, "%s: %f %f    %f ", image[i].name, local.crval1, local.crval2, Radius);
+
+      // check for distances to coordinates in arcsec
+      RD_to_XY (&Xo, &Yo, Ra, Dec, &local);
+      // fprintf (stderr, " : %f\n", hypot(Xo,Yo));
+
+      // skip images with center too far from coordinaes
+      if (hypot(Xo,Yo) > 1.5*Radius) continue;
+      // fprintf (stderr, " ** try me **\n");
+    }
+
+    status = RD_to_XY (&X, &Y, Ra, Dec, &image[i].coords);
+    if (!finite(X)) continue;
+    if (!finite(Y)) continue;
+    if (!status) continue;
+
+    if (typehash == DistortImage) {
+      if (X < -0.5*image[i].NX) continue;
+      if (Y < -0.5*image[i].NY) continue;
+      if (X > +0.5*image[i].NX) continue;
+      if (Y > +0.5*image[i].NY) continue;
+      goto got_spot;
+    } 
+
+    typehash = wordhash (image[i].coords.ctype);
+    if (typehash == TriangleUp) {
+      if (Y < -0.5*image[i].NY) continue;
+      Yo = +0.5*image[i].NY + 2.0*(image[i].NY/image[i].NX)*X;
+      if (Y > Yo) continue;
+      Yo = +0.5*image[i].NY - 2.0*(image[i].NY/image[i].NX)*X;
+      if (Y > Yo) continue;
+      goto got_spot;
+    }
+    if (typehash == TriangleDn) {
+      if (Y > +0.5*image[i].NY) continue;
+      Yo = -0.5*image[i].NY + 2.0*(image[i].NY/image[i].NX)*X;
+      if (Y < Yo) continue;
+      Yo = -0.5*image[i].NY - 2.0*(image[i].NY/image[i].NX)*X;
+      if (Y < Yo) continue;
+      goto got_spot;
+    }
+
+    {
+      if (X < 0) continue;
+      if (Y < 0) continue;
+      if (X > image[i].NX) continue;
+      if (Y > image[i].NY) continue;
+    }
+
+    // XXX Mcal = applyMcal (&image[i], 2048.0, 2048.0);
+
+  got_spot:
+    date = ohana_sec_to_date (image[i].tzero);
+
+    // double-check coorsd:
+# if 0
+    {
+	double Rout, Dout;
+	status = XY_to_RD (&Rout, &Dout, X, Y, &image[i].coords);
+	fprintf (stderr, "r,d = %f,%f\n", Rout, Dout);
+    }
+# endif
+
+    if (PixelCoords) {
+      gprint (GP_LOG, "%3d %s %6.1f %6.1f %20s %5d %2d %4.2f %6.3f %5.3f %5.3f %4x %7d\n", 
+	      Nfound, image[i].name, X, Y, date, image[i].nstar, image[i].photcode, image[i].secz, image[i].Mcal, image[i].dMcal, image[i].exptime, image[i].flags, image[i].imageID);
+    } else {
+      XY_to_RD (&ra, &dec, 0.5*image[i].NX, 0.5*image[i].NY, &image[i].coords);
+      gprint (GP_LOG, "%3d %s %8.4f %8.4f %20s %5d %2d %4.2f %6.3f %5.3f %5.3f %4x %7d\n", 
+	       Nfound, image[i].name, ra, dec, date, image[i].nstar, image[i].photcode, image[i].secz, image[i].Mcal, image[i].dMcal, image[i].exptime, image[i].flags, image[i].imageID);
+    }
+    sprintf (name, "IMAGEx:%d", Nfound);
+    set_variable     (name, X);
+    sprintf (name, "IMAGEy:%d", Nfound);
+    set_variable     (name, Y);
+    sprintf (name, "IMAGEt:%d", Nfound);
+    set_str_variable (name, date);
+    sprintf (name, "IMAGEccd:%d", Nfound);
+    set_int_variable (name, image[i].ccdnum);
+    sprintf (name, "IMAGEname:%d", Nfound);
+    set_str_variable (name, image[i].name);
+    sprintf (name, "IMAGEphotcode:%d", Nfound);
+    set_int_variable (name, image[i].photcode);
+    Nfound ++;
+    free (date);
+  }
+  set_int_variable ("IMAGEx:n", Nfound);
+  set_int_variable ("IMAGEy:n", Nfound);
+  set_int_variable ("IMAGEt:n", Nfound);
+  set_int_variable ("IMAGEccd:n", Nfound);
+  set_int_variable ("IMAGEname:n", Nfound);
+  set_int_variable ("IMAGEphotcode:n", Nfound);
+
+  FreeImagesDVO (image);
+  free (subset);
+
+  return (TRUE);
+
+}
Index: /branches/sc_branches/pantasks_condor/dvo/gstar.c
===================================================================
--- /branches/sc_branches/pantasks_condor/dvo/gstar.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/dvo/gstar.c	(revision 34783)
@@ -0,0 +1,459 @@
+# include "dvoshell.h"
+
+void initPhotcodeSequence (int Nsecfilt);
+void freePhotcodeSequence ();
+void printPhotcodeSequence (Average *average, SecFilt *secfilt, int entry, int type);
+
+int gstar (int argc, char **argv) {
+  
+  char *date;
+  double Ra, Dec, Radius, Radius2, r, dec0, dec1;
+  double Mcat, Mrel;
+  double *RA, *DEC;
+  off_t i, Nstars, *N1;
+  off_t j, k, m, N, Nlo, Nhi;
+  int Nsecfilt, NPTS, QUIET, FULL_OUTPUT, INST;
+  int found, GetMeasures;
+  int SaveVectors;
+  Vector *vec1, *vec2, *vec3, *vec4, *vec5, *vec6;
+  SkyTable *sky;
+  SkyList *skylist;
+  Catalog catalog;
+  int TimeFormat;
+  time_t TimeReference;
+
+  if (!InitPhotcodes ()) return (FALSE);
+  Nsecfilt = GetPhotcodeNsecfilt ();
+
+  QUIET = FALSE;
+  if ((N = get_argument (argc, argv, "-q"))) {
+    QUIET = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+
+  INST = FALSE;
+  if ((N = get_argument (argc, argv, "-inst"))) {
+    INST = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+
+  NPTS = 0;
+  vec1 = vec2 = vec3 = vec4 = vec5 = vec6 = NULL;
+  SaveVectors = FALSE;
+  if ((N = get_argument (argc, argv, "-save"))) {
+    remove_argument (N, &argc, argv);
+    SaveVectors = TRUE;
+    if ((vec1 = SelectVector ("gs:m", ANYVECTOR, TRUE)) == NULL) return (FALSE);
+    if ((vec2 = SelectVector ("gs:t", ANYVECTOR, TRUE)) == NULL) return (FALSE);
+    if ((vec3 = SelectVector ("gs:z", ANYVECTOR, TRUE)) == NULL) return (FALSE);
+    if ((vec4 = SelectVector ("gs:f", ANYVECTOR, TRUE)) == NULL) return (FALSE);
+    if ((vec5 = SelectVector ("gs:dr", ANYVECTOR, TRUE)) == NULL) return (FALSE);
+    if ((vec6 = SelectVector ("gs:dd", ANYVECTOR, TRUE)) == NULL) return (FALSE);
+  }
+
+  FULL_OUTPUT = FALSE;
+  if ((N = get_argument (argc, argv, "-full"))) {
+    FULL_OUTPUT = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+
+  GetMeasures = FALSE;
+  if ((N = get_argument (argc, argv, "-m"))) {
+    GetMeasures = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+  if ((N = get_argument (argc, argv, "-meas"))) {
+    GetMeasures = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+
+  int PARALLEL = FALSE;
+  if ((N = get_argument (argc, argv, "-parallel"))) {
+    remove_argument (N, &argc, argv);
+    PARALLEL = TRUE;
+  }
+
+  if (argc != 4) {
+    gprint (GP_ERR, "USAGE: gstar RA DEC Radius [-m]\n");
+    return (FALSE);
+  }
+  
+  GetTimeFormat (&TimeReference, &TimeFormat);
+
+  Ra = atof (argv[1]);
+  Dec = atof (argv[2]);
+  Radius = atof (argv[3]);
+
+  Ra = ohana_normalize_angle (Ra);
+  
+  /* load sky from correct table */
+  sky = GetSkyTable ();
+  skylist = SkyListByRadius (sky, -1, Ra, Dec, Radius);
+
+  if (skylist[0].Nregions > 1) {
+    gprint (GP_ERR, "warning, radius overlaps region boundary, not yet implemented\n");
+  }
+
+  HostTable *table = NULL;  
+  if (PARALLEL) {
+    char *CATDIR = GetCATDIR();
+    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;
+    }    
+
+    SkyRegion *region = skylist[0].regions[0];
+    int hostID = (region->hostFlags & DATA_USE_BCK) ? region->backupID : region->hostID;
+    int index = table->index[hostID];
+    
+    char hostfile[1024];
+    snprintf (hostfile, 1024, "%s/%s.cpt", table->hosts[index].pathname, region->name);
+    catalog.filename = hostfile;
+  } else {
+    catalog.filename = skylist[0].filename[0];
+  }
+
+  /* lock, load, unlock catalog */
+  catalog.catflags = LOAD_AVES | LOAD_SECF;
+  catalog.catflags |= GetMeasures ? LOAD_MEAS : SKIP_MEAS;
+  catalog.Nsecfilt = 0;
+
+  // an error exit status here is a significant error
+  if (!dvo_catalog_open (&catalog, NULL, FALSE, "r")) {
+      fprintf (stderr, "ERROR: failure to open catalog file %s\n", catalog.filename);
+      exit (2);
+  }
+  dvo_catalog_unlock (&catalog);
+
+  Nstars = catalog.Naverage;
+  ALLOCATE (RA, double, Nstars);
+  ALLOCATE (DEC, double, Nstars);
+  ALLOCATE (N1, off_t, Nstars);
+
+  /* find star(s) in RA, DEC list -- use a dumb algorithm for now, improve later */
+  /* stars are not guaranteed to be sorted in RA or in DEC, so first sort the list */
+  for (i = 0; i < Nstars; i++) {
+    RA[i] = catalog.average[i].R;
+    DEC[i] = catalog.average[i].D;
+    N1[i] = i;
+  }
+  /* sort list by DEC */
+  if (Nstars > 1) sort_coords_index (DEC, RA, N1, Nstars);
+  /* at this point, RA, DEC, and N1 are sorted by DEC.  
+     catalog.average[N1[i]].R = RA[i] */
+
+  /* bracket the RA range of interest */
+  dec0 = Dec - Radius;
+  dec1 = Dec + Radius;
+
+  Nlo = 0; Nhi = catalog.Naverage;
+  while (Nhi - Nlo > 10) {
+    N = 0.5*(Nlo + Nhi);
+    if (DEC[N] < dec0) {
+      Nlo = N;
+    } else {
+      Nhi = N + 1;
+    }
+  }
+  /* DEC[Nlo] is guaranteed to be just lower than dec0 */
+
+  Radius2 = Radius*Radius;
+  found = FALSE;
+
+  /* data has been loaded, get ready to plot it */
+  if (SaveVectors) {
+    N = 0;
+    NPTS = 1000;
+    ResetVector (vec1, OPIHI_FLT, NPTS);
+    ResetVector (vec2, OPIHI_FLT, NPTS);
+    ResetVector (vec3, OPIHI_FLT, NPTS);
+    ResetVector (vec4, OPIHI_FLT, NPTS);
+    ResetVector (vec5, OPIHI_FLT, NPTS);
+    ResetVector (vec6, OPIHI_FLT, NPTS);
+  }
+
+  initPhotcodeSequence (Nsecfilt);
+
+  for (i = Nlo; (i < catalog.Naverage) && !found; i++) {
+
+    if (dec0 > DEC[i]) continue;
+    if (dec1 < DEC[i]) found = TRUE;
+    
+    r = SQ(Dec - DEC[i]) + SQ((Ra - RA[i])*cos(Dec*RAD_DEG));
+    if (r < Radius2) {
+      k = N1[i];
+      if (!QUIET) {
+	gprint (GP_LOG, "star: "OFF_T_FMT"\n",  i);
+	gprint (GP_LOG, "%11.7f ", catalog.average[k].R);
+	gprint (GP_LOG, "%11.7f ", catalog.average[k].D);
+	gprint (GP_LOG, "%5.2f ",   3600.0*sqrt(r));
+	gprint (GP_LOG, "%3d   ",  catalog.average[k].Nmeasure);
+	gprint (GP_LOG, "%4.1f ",  catalog.average[k].ChiSqAve);
+	gprint (GP_LOG, "%5x ",    catalog.average[k].flags);
+	gprint (GP_LOG, "%x ",     catalog.average[k].objID);
+	gprint (GP_LOG, "%x ",     catalog.average[k].catID);
+	
+	if (FULL_OUTPUT) {
+	    gprint (GP_LOG, "%f ",     catalog.average[k].dR);
+	    gprint (GP_LOG, "%f ",     catalog.average[k].dD);
+	    gprint (GP_LOG, "%f ",     catalog.average[k].uR);
+	    gprint (GP_LOG, "%f ",     catalog.average[k].uD);
+	    gprint (GP_LOG, "%f ",     catalog.average[k].duR);
+	    gprint (GP_LOG, "%f ",     catalog.average[k].duD);
+	    gprint (GP_LOG, "%f ",     catalog.average[k].P);
+	    gprint (GP_LOG, "%f   ",     catalog.average[k].dP);
+
+	    gprint (GP_LOG, "%f   ",     catalog.average[k].ChiSqPM);
+	    gprint (GP_LOG, "%f   ",     catalog.average[k].ChiSqPar);
+
+	    date = ohana_sec_to_date (catalog.average[k].Tmean);
+	    gprint (GP_LOG, "%20s ",     date);
+	    gprint (GP_LOG, "%f   ",     catalog.average[k].Trange / 86400.0);
+	}
+
+	gprint (GP_LOG, "\n");
+      
+	/* filter names */
+	for (j = 0; j < Nsecfilt; j++) printPhotcodeSequence (&catalog.average[k], &catalog.secfilt[Nsecfilt*k], j, 3);
+	gprint (GP_LOG, "\n");
+
+	/* average mags */
+	for (j = 0; j < Nsecfilt; j++) printPhotcodeSequence (&catalog.average[k], &catalog.secfilt[Nsecfilt*k], j, 0);
+	gprint (GP_LOG, "\n");
+
+	/* average mag errors */
+	for (j = 0; j < Nsecfilt; j++) printPhotcodeSequence (&catalog.average[k], &catalog.secfilt[Nsecfilt*k], j, 1);
+	gprint (GP_LOG, "\n");
+
+	/* average mag chisq */
+	for (j = 0; j < Nsecfilt; j++) printPhotcodeSequence (&catalog.average[k], &catalog.secfilt[Nsecfilt*k], j, 2);
+	gprint (GP_LOG, "\n");
+
+	if (FULL_OUTPUT) {
+	  /* Map */
+	  for (j = 0; j < Nsecfilt; j++) printPhotcodeSequence (&catalog.average[k], &catalog.secfilt[Nsecfilt*k], j, 5);
+	  gprint (GP_LOG, "\n");
+
+	  /* M_20 */
+	  for (j = 0; j < Nsecfilt; j++) printPhotcodeSequence (&catalog.average[k], &catalog.secfilt[Nsecfilt*k], j, 6);
+	  gprint (GP_LOG, "\n");
+
+	  /* M_80 */
+	  for (j = 0; j < Nsecfilt; j++) printPhotcodeSequence (&catalog.average[k], &catalog.secfilt[Nsecfilt*k], j, 7);
+	  gprint (GP_LOG, "\n");
+
+	  /* secfilt flags */
+	  for (j = 0; j < Nsecfilt; j++) printPhotcodeSequence (&catalog.average[k], &catalog.secfilt[Nsecfilt*k], j, 4);
+	  gprint (GP_LOG, "\n");
+
+	}
+      }
+      if (GetMeasures || SaveVectors) {
+	m = catalog.average[k].measureOffset;
+	for (j = 0; j < catalog.average[k].Nmeasure; j++, m++) {
+
+	  Mcat = PhotCat (&catalog.measure[m]);
+	  if (INST) {
+	    Mrel = PhotInst (&catalog.measure[m]);
+	  } else {
+	    Mrel = PhotRel (&catalog.measure[m], &catalog.average[k], &catalog.secfilt[k*Nsecfilt]);
+	  }
+
+	  if (GetMeasures && !QUIET) {
+	    date = ohana_sec_to_date (catalog.measure[m].t);
+	    gprint (GP_LOG, "%6.3f ",  Mcat);
+	    gprint (GP_LOG, "%6.3f ",  Mrel);
+	    gprint (GP_LOG, "%5.3f  ", catalog.measure[m].dM);
+	    gprint (GP_LOG, "%20s  ",  date);
+	    gprint (GP_LOG, "%7.4f ",  catalog.measure[m].dR);
+	    gprint (GP_LOG, "%7.4f ",  catalog.measure[m].dD);
+	    gprint (GP_LOG, "0x%08x ", catalog.measure[m].photFlags);
+	    gprint (GP_LOG, "0x%08x ", catalog.measure[m].dbFlags);
+	    gprint (GP_LOG, "%5d ",    catalog.measure[m].photcode);
+	    gprint (GP_LOG, "%-20s ",  GetPhotcodeNamebyCode (catalog.measure[m].photcode));
+ 	    gprint (GP_LOG, "%5.2f ",  FromShortPixels(catalog.measure[m].FWx));
+	    gprint (GP_LOG, "%5.2f ",  FromShortPixels(catalog.measure[m].FWy));
+
+	    if (FULL_OUTPUT) {
+		gprint (GP_LOG, "%6.3f ", catalog.measure[m].Mcal);
+		gprint (GP_LOG, "%6.3f ", catalog.measure[m].Map);
+		gprint (GP_LOG, "%5.1f ", pow(10.0, 0.4*catalog.measure[m].dt));
+		gprint (GP_LOG, "%5.3f ", catalog.measure[m].airmass);
+		gprint (GP_LOG, "%6.1f ", catalog.measure[m].az);
+		gprint (GP_LOG, "%6.1f ", catalog.measure[m].Xccd);
+		gprint (GP_LOG, "%6.1f ", catalog.measure[m].Yccd);
+		gprint (GP_LOG, "%3.1f ", FromShortPixels(catalog.measure[m].dXccd));
+		gprint (GP_LOG, "%3.1f ", FromShortPixels(catalog.measure[m].dYccd));
+		gprint (GP_LOG, "%6.1f ", catalog.measure[m].Sky);
+		gprint (GP_LOG, "%5.1f ", catalog.measure[m].dSky);
+		gprint (GP_LOG, "%8d ", catalog.measure[m].averef);
+		gprint (GP_LOG, "0x%08x ", catalog.measure[m].detID);
+		gprint (GP_LOG, "0x%08x ", catalog.measure[m].imageID);
+		gprint (GP_LOG, "%.3f ", catalog.measure[m].psfQF);
+		gprint (GP_LOG, "%.1f ", catalog.measure[m].psfChisq);
+		gprint (GP_LOG, "%.1f ", catalog.measure[m].crNsigma);
+		gprint (GP_LOG, "%.1f ", catalog.measure[m].extNsigma);
+		gprint (GP_LOG, "%.1f ", FromShortDegrees(catalog.measure[m].theta));
+	    }
+	    gprint (GP_LOG, "\n");
+
+	    free (date);
+	  }
+	  
+	  if (SaveVectors) {
+	    vec1[0].elements.Flt[N] = Mcat;
+	    vec2[0].elements.Flt[N] = TimeValue (catalog.measure[m].t, TimeReference, TimeFormat);
+	    vec3[0].elements.Flt[N] = catalog.measure[m].airmass;
+	    vec4[0].elements.Flt[N] = catalog.measure[m].photcode;
+	    vec5[0].elements.Flt[N] = catalog.measure[m].dR;
+	    vec6[0].elements.Flt[N] = catalog.measure[m].dD;
+	    N ++;
+	    if (N == NPTS - 1) {
+	      NPTS += 2000;
+	      REALLOCATE (vec1[0].elements.Flt, opihi_flt, NPTS);
+	      REALLOCATE (vec2[0].elements.Flt, opihi_flt, NPTS);
+	      REALLOCATE (vec3[0].elements.Flt, opihi_flt, NPTS);
+	      REALLOCATE (vec4[0].elements.Flt, opihi_flt, NPTS);
+	      REALLOCATE (vec5[0].elements.Flt, opihi_flt, NPTS);
+	      REALLOCATE (vec6[0].elements.Flt, opihi_flt, NPTS);
+	    }
+	  }
+	}
+      }
+    }
+  }
+
+  if (SaveVectors) {
+    vec1[0].Nelements = N;
+    vec2[0].Nelements = N;
+    vec3[0].Nelements = N;
+    vec4[0].Nelements = N;
+    vec5[0].Nelements = N;
+    vec6[0].Nelements = N;
+  }
+
+  free (RA);
+  free (DEC);
+  free (N1);
+  dvo_catalog_free (&catalog);
+
+  freePhotcodeSequence ();
+  return (TRUE);
+
+}
+
+void print_double (double value) {
+  if (isnan(value)) 
+    gprint (GP_LOG, "  NaN    ");
+  else 
+    gprint (GP_LOG, "  %6.3f ", value);
+}
+
+void print_short (double value, short int ival) {
+  if (ival == NAN_S_SHORT) 
+    gprint (GP_LOG, "  NaN    ");
+  else 
+    gprint (GP_LOG, "  %6.3f ", value);
+}
+
+// XXX fix printing to be in photcode numerical order for PRI/SEC data
+static int *sequence = NULL;
+
+void initPhotcodeSequence (int Nsecfilt) {
+
+  int j;
+  int *codeNumber;
+  PhotCode *code;
+
+  // sequence contains, in desired order, secfilt number (0 == pri)
+  ALLOCATE (sequence, int, Nsecfilt);
+  ALLOCATE (codeNumber, int, Nsecfilt);
+  
+  /* filter names -- primary code is 0 in this function */
+  for (j = 0; j < Nsecfilt; j++) {
+    code = GetPhotcodebyNsec (j);
+    codeNumber[j] = code[0].code;
+    sequence[j] = j;
+  }
+
+  isortpair (codeNumber, sequence, Nsecfilt);
+  free (codeNumber);
+}
+
+void freePhotcodeSequence () {
+  free (sequence);
+}
+
+
+void printPhotcodeSequence (Average *average, SecFilt *secfilt, int entry, int type) {
+
+  int seq;
+  PhotCode *code;
+
+  seq = sequence[entry];
+
+  switch (type) {
+    case 0: /* average mags */
+      if (seq == -1) {
+	print_double (NAN);
+      } else {
+	print_double (secfilt[seq].M);
+      }
+      break;
+
+    case 1: /* average mags errors */
+      if (seq == -1) {
+	print_double (NAN);
+      } else {
+	print_double (secfilt[seq].dM);
+      }
+      break;
+
+    case 2: /* average mag chisq */
+      if (seq == -1) {
+	print_short (NAN_S_SHORT, NAN_S_SHORT);
+      } else {
+	print_short (pow (10.0, 0.01*secfilt[seq].Xm), secfilt[seq].Xm);
+      }
+      break;
+
+    case 3: /* filter names */
+      code = GetPhotcodebyNsec (seq);
+      gprint (GP_LOG, "  %-6s ", code[0].name);
+      break;
+
+    case 4: /* secfilt flags */
+      if (seq == -1) {
+	gprint (GP_LOG, "%08x ", 0);
+      } else {
+	gprint (GP_LOG, "%08x ", secfilt[seq].flags);
+      }
+      break;
+
+    case 5: /* average ap mags */
+      if (seq == -1) {
+	print_double (NAN);
+      } else {
+	print_double (secfilt[seq].Map);
+      }
+      break;
+
+    case 6: /* average ap mags */
+      if (seq == -1) {
+	print_double (NAN);
+      } else {
+	print_short (0.001*secfilt[seq].M_20, secfilt[seq].M_20);
+      }
+      break;
+
+    case 7: /* average ap mags */
+      if (seq == -1) {
+	print_double (NAN);
+      } else {
+	print_short (0.001*secfilt[seq].M_20, secfilt[seq].M_80);
+      }
+      break;
+
+  }
+}
Index: /branches/sc_branches/pantasks_condor/dvo/help/Photometry
===================================================================
--- /branches/sc_branches/pantasks_condor/dvo/help/Photometry	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/dvo/help/Photometry	(revision 34783)
@@ -0,0 +1,26 @@
+
+cals: Plot residuals for reference stars. 
+
+ This function takes the currently displayed region and looks for all
+ stars which have 'reference' photometry and plots the difference
+ between the reference magnitude (in the given filter) and the
+ observed magnitude (same filter) versus one of several options:  the
+ star color (based on the 'reference' photometry), the star magnitude,
+ the airmass, time, secqunce, etc.
+
+ These plots can use one of two options for the 'observed' magnitude.
+ Either, the magnitude for individual measurement, using the currently
+ set values for the CCD zeropoints (from the photometry database), or
+ using the relative photometry determined for the particular star.  
+
+
+dmags: plot difference between two measured magnitudes
+
+resid: 
+
+abszero
+
+zeropts
+
+cmd
+
Index: /branches/sc_branches/pantasks_condor/dvo/help/catalog
===================================================================
--- /branches/sc_branches/pantasks_condor/dvo/help/catalog	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/dvo/help/catalog	(revision 34783)
@@ -0,0 +1,74 @@
+
+  catalog (filename/-all) [many options!]
+
+  plot the star positions from any of several possible catalogs.  By
+default, the photometry database (see Database) is used, but the HST
+Guide Star Catalog and a simple ASCII file may also be used.  The
+plotting is done on window 0, with the current point type.  The point
+size can be scaled by the magnitude of the star or by the number
+of observations (-n).  
+
+There are many options to this function.  Here they are all:
+
+-e epoch: given ASCII catalog has this epoch, plot in J2000
+
+-ID ID: plot only measurements with this ID number
+ ID may be one of:
+	0 - star
+	4 - variable
+	5 - transient
+      100 - ghost image
+      101 - trail (satellite)
+      102 - bright star garbage	
+      note that numbers < 50 are static objects, 
+	and numbers > 100 are bad data.
+
+-all:  plot all data for the current display region
+
+-g: use the HST GSC instead of the photometry database
+
+-a RA DEC MAG:  the given file is an ASCII file with 
+	RA, DEC, and MAG in columns given by the three numbers
+
+-v ra dec scale: save the values in vectors with the three names given
+	the ra and dec are always used, but the third is the size of 
+	the points plotted, based on whatever the scaling is.
+
+-m m1 m2: scale points by magnitude, with smallest points having
+	magnitude m2.
+
++n n1 n2: scale points by number of measurements, with smallest points
+	having number n1.
+
+-n n1 n2: scale points by number of missing values, with smallest points
+	having number n1.
+
+Note that if two of -m, -n, +n are given, the first determines the
+ scaling of the points.  the second provides an allowed range, so
+ points are only plotted if the second parameter has a value in the
+ given range.
+
+Examples: 
+
+  catalog -all -m 12 18  
+    plot all stars in current region with magnitude 12 having the
+    biggest points and magnitude 18 having the smallest.
+   
+  catalog -all -m 12 18 +n 2 4
+    plot all stars in current region with magnitude 12 having the
+    biggest points and magnitude 18 having the smallest, but only
+    if the stars have 2-4 measurements each
+   
+  catalog -all -m 12 18 +n 0 1 -ID 101
+    plot all stars in current region with magnitude 12 having the
+    biggest points and magnitude 18 having the smallest, but only
+    if the stars have 1 measurements each, and if the object is 
+    found to be a trail.
+   
+  catalog n2230/1819.cpt -n 2 8  
+    plot all stars in catalog n2230/1819.cpt with the biggest points
+    having 8 measurements and the smallest having only 2.
+
+  See Also: lcat, pcat, style, region
+
+   
Index: /branches/sc_branches/pantasks_condor/dvo/help/images
===================================================================
--- /branches/sc_branches/pantasks_condor/dvo/help/images	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/dvo/help/images	(revision 34783)
@@ -0,0 +1,9 @@
+
+  images 
+
+  plot the outlines of images in the photometry database and list
+  along with relevant information.  An example of relevant data is the
+  image time, which can be used in other functions to define a specific
+  image or a range of images relevant for the function.
+
+  See also: Database, lcat, pcat
Index: /branches/sc_branches/pantasks_condor/dvo/help/imstats
===================================================================
--- /branches/sc_branches/pantasks_condor/dvo/help/imstats	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/dvo/help/imstats	(revision 34783)
@@ -0,0 +1,5 @@
+
+  imstats 
+
+  list interesting statistics for a set of images.
+
Index: /branches/sc_branches/pantasks_condor/dvo/help/lcat
===================================================================
--- /branches/sc_branches/pantasks_condor/dvo/help/lcat	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/dvo/help/lcat	(revision 34783)
@@ -0,0 +1,10 @@
+
+  lcat [-all]
+
+  list photometry database files in the current region.  if the option
+  -all is given, all database in the region are listed, along with a
+  comment to show if there is data in the file or not.  if the option
+  is not given, only those files with data are listed.
+
+  See also: region, pcat
+
Index: /branches/sc_branches/pantasks_condor/dvo/help/lcurve
===================================================================
--- /branches/sc_branches/pantasks_condor/dvo/help/lcurve	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/dvo/help/lcurve	(revision 34783)
@@ -0,0 +1,8 @@
+
+  lcurve RA DEC Radius [-l]
+
+  plot light curves for all stars within Radius of the given
+  coordinates.  The units for all three are decimal degrees.  
+  The option -l autoscales the limits of the plot.  
+
+  See also: limits
Index: /branches/sc_branches/pantasks_condor/dvo/help/mextract
===================================================================
--- /branches/sc_branches/pantasks_condor/dvo/help/mextract	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/dvo/help/mextract	(revision 34783)
@@ -0,0 +1,12 @@
+
+ mextract (filename) (value) [-g / -a Ncol]
+
+ extracts a vector from the photometry database.  The name also
+ specifies the type of data extracted (and is case insensitive).  Only
+ certain names may be used.
+
+ The possible values are:
+ ra, dec, mag, dmag, Mcal, Mrel, source, time, dR, dD
+
+
+ Note: this routine extracts the individual measurements.
Index: /branches/sc_branches/pantasks_condor/dvo/help/pcat
===================================================================
--- /branches/sc_branches/pantasks_condor/dvo/help/pcat	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/dvo/help/pcat	(revision 34783)
@@ -0,0 +1,9 @@
+
+  pcat [-all]
+
+  plot outlines of photometry database files in the current region.
+  if the option -all is given, all database in the region are listed.
+  if the option is not given, only those files with data are listed.
+
+  See also: region, lcat
+
Index: /branches/sc_branches/pantasks_condor/dvo/help/resid
===================================================================
--- /branches/sc_branches/pantasks_condor/dvo/help/resid	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/dvo/help/resid	(revision 34783)
@@ -0,0 +1,12 @@
+
+  resid [-im time] [-X] [-l] (photometry file)
+
+  plot the photometry residuals for a photometry database file. 
+  For every star in the database, the average magnitude is plotted 
+  vs the difference between the average magnitude and each existing 
+  measurement for that star.  
+
+  -l		automatically set the plot limits.
+  -X		plot residual divided by photometric error
+  -im time	only plot the residuals for one of the images 
+		in the database, specified by the obseravation time.
Index: /branches/sc_branches/pantasks_condor/dvo/help/simage
===================================================================
--- /branches/sc_branches/pantasks_condor/dvo/help/simage	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/dvo/help/simage	(revision 34783)
@@ -0,0 +1,7 @@
+
+  simage (filename)
+
+  plot the positions of stars in a CMP file.  This function is a
+  useful test for image comparisons, and astrometry must be determined
+  before the function is run.
+
Index: /branches/sc_branches/pantasks_condor/dvo/hosts.c
===================================================================
--- /branches/sc_branches/pantasks_condor/dvo/hosts.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/dvo/hosts.c	(revision 34783)
@@ -0,0 +1,147 @@
+# include "dvoshell.h"
+# include <glob.h>
+# define DVO_MAX_PATH 1024
+
+enum {TEMP_NONE, TEMP_DVO_RESULTS, TEMP_DVO_LOG, TEMP_RELPHOT_RESULTS, TEMP_RELPHOT_LOG};
+
+// functions to manage the remote hosts
+int hosts (int argc, char **argv) {
+  
+  if (argc < 2) {
+    gprint (GP_ERR, "USAGE: hosts (command) [options]\n");
+    gprint (GP_ERR, "  commands:\n");
+    gprint (GP_ERR, "    purge-temp : delete all tempfiles for this shell\n");
+    gprint (GP_ERR, "               : [-old-pid] [-all-pid] [-v] [-verbose] [-commit] [-type type]\n");
+    gprint (GP_ERR, "    get-results : determine name of RESULTS file\n");
+    return FALSE;
+  }
+
+  if (!strncmp(argv[1], "purge-temp", MAX(strlen(argv[1]), 3))) {
+    glob_t pglob;
+    int ALL_PID = FALSE;
+    int PID = getpid();
+    int N;
+    if ((N = get_argument (argc, argv, "-old-pid"))) {
+      remove_argument (N, &argc, argv);
+      PID = atoi(argv[N]);
+      remove_argument (N, &argc, argv);
+    }
+
+    if ((N = get_argument (argc, argv, "-all-pid"))) {
+      remove_argument (N, &argc, argv);
+      ALL_PID = TRUE;
+    }
+    
+    int VERBOSE = FALSE;
+    if ((N = get_argument (argc, argv, "-v"))) {
+      remove_argument (N, &argc, argv);
+      VERBOSE = TRUE;
+    }
+    if ((N = get_argument (argc, argv, "-verbose"))) {
+      remove_argument (N, &argc, argv);
+      VERBOSE = TRUE;
+    }
+    
+    int DRYRUN = TRUE;
+    if ((N = get_argument (argc, argv, "-commit"))) {
+      remove_argument (N, &argc, argv);
+      DRYRUN = FALSE;
+    }
+    
+    // we have the following types of temp files:
+    // dvo.results.*.fits
+    // dvo.results.*.fits.log
+    // log.rlpc.* (relphot logs)
+    // relphot.catalog.subset.dat [no glob needed]
+    int TEMP_TYPE = TEMP_DVO_RESULTS;
+    if ((N = get_argument (argc, argv, "-type"))) {
+      TEMP_TYPE = TEMP_NONE;
+      remove_argument (N, &argc, argv);
+      if (!strcasecmp(argv[N], "dvo.results"))     TEMP_TYPE = TEMP_DVO_RESULTS;
+      if (!strcasecmp(argv[N], "dvo.log"))         TEMP_TYPE = TEMP_DVO_LOG;
+      if (!strcasecmp(argv[N], "relphot.results")) TEMP_TYPE = TEMP_RELPHOT_RESULTS;
+      if (!strcasecmp(argv[N], "relphot.log"))     TEMP_TYPE = TEMP_RELPHOT_LOG;
+      if (TEMP_TYPE == TEMP_NONE) {
+	gprint (GP_ERR, "USAGE: hosts purge-temp [-type (type)]\n");
+	gprint (GP_ERR, "  allowed types dvo.results, dvo.log, relphot.results, relphot.log]\n");
+	return FALSE;
+      }
+      remove_argument (N, &argc, argv);
+    }
+
+    // XXX wrap this up in a function:
+    char *CATDIR = GetCATDIR();
+    if (!CATDIR) {
+      gprint (GP_ERR, "CATDIR is not set\n");
+      return FALSE;
+    }
+    SkyTable *sky = GetSkyTable();
+    if (!sky) {
+      gprint (GP_ERR, "failed to load sky table 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;
+    }    
+    
+    if (argc != 2) {
+      gprint (GP_ERR, "USAGE: hosts purge-temp [-all-pid] [-old-pid PID] [-verbose] [-commit]\n");
+      return FALSE;
+    }
+
+    int i;
+    for (i = 0; i < table->Nhosts; i++) {
+      pglob.gl_offs = 0;
+      char name[DVO_MAX_PATH];
+      if (ALL_PID) {
+	if (TEMP_TYPE == TEMP_DVO_RESULTS)     snprintf (name, DVO_MAX_PATH, "%s/dvo.results.*.fits", table->hosts[i].pathname);
+	if (TEMP_TYPE == TEMP_DVO_LOG)         snprintf (name, DVO_MAX_PATH, "%s/dvo.results.*.fits.log", table->hosts[i].pathname);
+	if (TEMP_TYPE == TEMP_RELPHOT_RESULTS) snprintf (name, DVO_MAX_PATH, "%s/log.rlpc.*", table->hosts[i].pathname);
+	if (TEMP_TYPE == TEMP_RELPHOT_LOG)     snprintf (name, DVO_MAX_PATH, "%s/relphot.catalog.subset.dat", table->hosts[i].pathname);
+      } else {
+	if (TEMP_TYPE == TEMP_DVO_RESULTS)     snprintf (name, DVO_MAX_PATH, "%s/dvo.results.%05d.*.fits", table->hosts[i].pathname, PID);
+	if (TEMP_TYPE == TEMP_DVO_LOG)         snprintf (name, DVO_MAX_PATH, "%s/dvo.results.%05d.*.fits.log", table->hosts[i].pathname, PID);
+	if (TEMP_TYPE == TEMP_RELPHOT_RESULTS) snprintf (name, DVO_MAX_PATH, "%s/log.rlpc.*", table->hosts[i].pathname);
+	if (TEMP_TYPE == TEMP_RELPHOT_LOG)     snprintf (name, DVO_MAX_PATH, "%s/relphot.catalog.subset.dat", table->hosts[i].pathname);
+      }
+      if (VERBOSE) gprint (GP_ERR, "checking %s\n", name);
+      glob (name, 0, NULL, &pglob);
+      int j;
+      for (j = 0; j < pglob.gl_pathc; j++) {
+	if (VERBOSE) gprint (GP_ERR, "unlink %s\n", pglob.gl_pathv[j]);
+	if (!DRYRUN) unlink (pglob.gl_pathv[j]);
+      }
+      globfree (&pglob);
+    }
+    return TRUE;
+  }
+
+  if (!strncmp(argv[1], "get.results", MAX(strlen(argv[1]), 3))) {
+    int N;
+
+    char *varname = NULL;
+    if ((N = get_argument (argc, argv, "-var"))) {
+      remove_argument (N, &argc, argv);
+      varname = strcreate(argv[N]);
+      remove_argument (N, &argc, argv);
+    }
+
+    if (argc < 2) {
+      gprint (GP_ERR, "USAGE: hosts get.results [-var var]\n");
+      return FALSE;
+    }
+
+    if (varname) {
+      set_str_variable (varname, RESULT_FILE);
+      free (varname);
+    } else {
+      gprint (GP_LOG, "results: %s\n", RESULT_FILE);
+    }
+    return TRUE; 
+  }
+
+  gprint (GP_ERR, "error: unknown hosts command %s\n", argv[1]);
+  return FALSE;
+}
Index: /branches/sc_branches/pantasks_condor/dvo/images.c
===================================================================
--- /branches/sc_branches/pantasks_condor/dvo/images.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/dvo/images.c	(revision 34783)
@@ -0,0 +1,374 @@
+# include "dvoshell.h"
+# define BETA 0.41421
+
+int wordhash (char *word) {
+  int value;
+
+  value = *(int *)word;
+  return value;
+}
+
+int images (int argc, char **argv) {
+
+  off_t i, Nimage, Nmosaic;
+  int j, status, InPic, leftside, *plist, TimeSelect, ByName;
+  int WITH_MOSAIC, SOLO_MOSAIC;
+  time_t tzero, tend;
+  int N, NPTS, n, npts, Npts, kapa, *foundMosaic;
+  Vector Xvec, Yvec;
+  double r[8], d[8], x[8], y[8], Rmin, Rmax, Rmid, trange, Radius;
+  Image *image;
+  Graphdata graphmode;
+  char name[256];
+  int typehash;
+  PhotCode *photcode;
+  int Nsec, photcodeEquiv;
+
+  if (!InitPhotcodes ()) return (FALSE);
+  if (!style_args (&graphmode, &argc, argv, &kapa)) goto usage;
+
+  WITH_MOSAIC = FALSE;
+  if ((N = get_argument (argc, argv, "+mosaic"))) {
+    remove_argument (N, &argc, argv);
+    WITH_MOSAIC = TRUE;
+  }
+
+  SOLO_MOSAIC = FALSE;
+  foundMosaic = NULL;
+  if ((N = get_argument (argc, argv, "-mosaic"))) {
+    remove_argument (N, &argc, argv);
+    SOLO_MOSAIC = TRUE;
+    WITH_MOSAIC = TRUE;
+  }
+
+  // int HIDDEN = FALSE;
+  // if ((N = get_argument (argc, argv, "-hidden"))) {
+  //   remove_argument (N, &argc, argv);
+  //   HIDDEN = TRUE;
+  // }
+
+  photcode = NULL;
+  photcodeEquiv = FALSE;
+  if ((N = get_argument (argc, argv, "-p"))) {
+    remove_argument (N, &argc, argv);
+    photcode = GetPhotcodebyName (argv[N]);
+    if (photcode == NULL) {
+      gprint (GP_ERR, "photcode %s not found\n", argv[N]);
+      return FALSE;
+    }
+    remove_argument (N, &argc, argv);
+    Nsec = GetPhotcodeNsec (photcode->code);
+    if (Nsec != -1) photcodeEquiv = TRUE;
+  }
+  if ((N = get_argument (argc, argv, "-photcode"))) {
+    remove_argument (N, &argc, argv);
+    photcode = GetPhotcodebyName (argv[N]);
+    if (photcode == NULL) {
+      gprint (GP_ERR, "photcode %s not found\n", argv[N]);
+      return FALSE;
+    }
+    remove_argument (N, &argc, argv);
+    Nsec = GetPhotcodeNsec (photcode->code);
+    if (Nsec != -1) photcodeEquiv = TRUE;
+  }
+
+  ByName = FALSE;
+  if ((N = get_argument (argc, argv, "-name"))) {
+    remove_argument (N, &argc, argv);
+    strcpy (name, argv[N]);
+    remove_argument (N, &argc, argv);
+    ByName = TRUE;
+  }
+
+  Radius = 45;
+  if ((N = get_argument (argc, argv, "-radius"))) {
+    remove_argument (N, &argc, argv);
+    Radius = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  TimeSelect = FALSE;
+  if ((N = get_argument (argc, argv, "-time"))) {
+    remove_argument (N, &argc, argv);
+    if (!ohana_str_to_time (argv[N], &tzero)) {
+      gprint (GP_ERR, "syntax error\n");
+      goto usage;
+    }
+    remove_argument (N, &argc, argv);
+    if (!ohana_str_to_dtime (argv[N], &trange)) { 
+      gprint (GP_ERR, "syntax error\n");
+      goto usage;
+    }
+    remove_argument (N, &argc, argv);
+    if (trange < 0) {
+      trange = fabs (trange);
+      tzero -= trange;
+    }
+    TimeSelect = TRUE;
+  }
+  if ((N = get_argument (argc, argv, "-trange"))) {
+    remove_argument (N, &argc, argv);
+    if (!ohana_str_to_time (argv[N], &tzero)) { 
+      gprint (GP_ERR, "syntax error\n");
+      goto usage;
+    }
+    remove_argument (N, &argc, argv);
+    if (!ohana_str_to_time (argv[N], &tend)) { 
+      gprint (GP_ERR, "syntax error\n");
+      goto usage;
+    }
+    remove_argument (N, &argc, argv);
+    trange = tend - tzero;
+    if (trange < 0) {
+      trange = fabs (trange);
+      tzero -= trange;
+    }
+    TimeSelect = TRUE;
+  }
+ 
+  if (argc != 1) goto usage;
+  
+  /* it is not an error for the database not to have any images */
+  if ((image = LoadImagesDVO (&Nimage)) == NULL) return (TRUE);
+  BuildChipMatch (image, Nimage);
+
+  if (SOLO_MOSAIC && photcode) {
+    ALLOCATE(foundMosaic, int, Nimage);
+    memset(foundMosaic, 0, Nimage*sizeof(int));
+  }
+
+  Rmin = graphmode.coords.crval1 - 180.0;
+  Rmax = graphmode.coords.crval1 + 180.0;
+  Rmid = 0.5*(Rmin + Rmax);
+  
+  int DistortImage = wordhash ("-DIS");
+  int ChipImage    = wordhash ("-WRP");
+  int TriangleUp   = wordhash ("TRP-");
+  int TriangleDn   = wordhash ("TRM-");
+  int TrianglePts  = wordhash ("TRI-");
+
+  npts = NPTS = 200;
+  SetVector (&Xvec, OPIHI_FLT, NPTS);
+  SetVector (&Yvec, OPIHI_FLT, NPTS);
+
+  ALLOCATE (plist, int, NPTS);
+  n = N = 0;
+  for (i = 0; i < Nimage; i++) {
+    if (ByName && strncmp (image[i].name, name, strlen(name))) continue;
+    if (TimeSelect && ((image[i].tzero < tzero) || (image[i].tzero+image[i].trate*image[i].NY > tzero + trange))) continue;
+    if (!(Nmosaic = FindMosaicForImage (image, Nimage, i))) continue;
+    Nmosaic --; // XXX kind of a hack: FindMosaicForImage returns 0 or the mosaic seq number + 1
+    if (photcode) {
+      if ( photcodeEquiv && (photcode[0].code != GetPhotcodeEquivCodebyCode(image[i].photcode))) continue;
+      if (!photcodeEquiv && (photcode[0].code != image[i].photcode)) continue;
+    }
+
+    Npts = 4;
+    status = TRUE;
+    leftside = FALSE;
+
+    typehash = wordhash (&image[i].coords.ctype[4]);
+
+    if (photcode && SOLO_MOSAIC) {
+      // mosaic (DIS) images are not currently given a photcode : plot these via the WRP entries
+      /* DIS images represent a field, not a chip */
+      if (typehash != ChipImage) continue;
+      if (foundMosaic[Nmosaic]) continue;
+      x[0] = -0.5*image[Nmosaic].NX; y[0] = -0.5*image[Nmosaic].NY;
+      x[1] = +0.5*image[Nmosaic].NX; y[1] = -0.5*image[Nmosaic].NY;
+      x[2] = +0.5*image[Nmosaic].NX; y[2] = +0.5*image[Nmosaic].NY;
+      x[3] = -0.5*image[Nmosaic].NX; y[3] = +0.5*image[Nmosaic].NY;
+      for (j = 0; j < Npts; j++) {
+	status = XY_to_RD (&r[j], &d[j], x[j], y[j], &image[Nmosaic].coords);
+	if (!status) break;
+	r[j] = ohana_normalize_angle (r[j]);
+	while (r[j] < Rmin) { r[j] += 360.0; }
+	while (r[j] > Rmax) { r[j] -= 360.0; }
+	if (j == 0) {
+	  leftside = (r[j] < Rmid);
+	} 
+	if (j > 0) { 
+	  if ( leftside && (r[j] > Rmid + 90)) { r[j] -= 360.0; }
+	  if (!leftside && (r[j] < Rmid - 90)) { r[j] += 360.0; }
+	}
+      }
+      foundMosaic[Nmosaic] = TRUE;
+      goto plot_points;
+    }
+
+    /* DIS images represent a field, not a chip */
+    if ((typehash == DistortImage) && !WITH_MOSAIC) continue;
+    if ((typehash != DistortImage) &&  SOLO_MOSAIC) continue;
+    if (typehash == DistortImage) {
+      x[0] = -0.5*image[i].NX; y[0] = -0.5*image[i].NY;
+      x[1] = +0.5*image[i].NX; y[1] = -0.5*image[i].NY;
+      x[2] = +0.5*image[i].NX; y[2] = +0.5*image[i].NY;
+      x[3] = -0.5*image[i].NX; y[3] = +0.5*image[i].NY;
+      goto got_type;
+    }
+
+    typehash = wordhash (image[i].coords.ctype);
+    if (typehash == TriangleUp) {
+      Npts = 3;
+      x[0] =                0; y[0] = +0.5*image[i].NY;
+      x[1] = +0.5*image[i].NX; y[1] = -0.5*image[i].NY;
+      x[2] = -0.5*image[i].NX; y[2] = -0.5*image[i].NY;
+      goto got_type;
+    }
+    if (typehash == TriangleDn) {
+      Npts = 3;
+      x[0] =                0; y[0] = -0.5*image[i].NY;
+      x[1] = +0.5*image[i].NX; y[1] = +0.5*image[i].NY;
+      x[2] = -0.5*image[i].NX; y[2] = +0.5*image[i].NY;
+      goto got_type;
+    }
+    // For 'TrianglePts' (TRI-), we are absurdly overloaded values otherwise not used
+    if (typehash == TrianglePts) {
+      Npts = 3;
+      x[0] = image[0].dXpixSys;      y[0] = image[0].dYpixSys;
+      x[1] = image[0].dMagSys;       y[1] = image[0].nFitAstrom;
+      x[2] = image[0].photom_map_id; y[2] = image[0].astrom_map_id;
+      goto got_type;
+    }
+
+    // default layout
+    {
+      x[0] = 0;           y[0] = 0;
+      x[1] = image[i].NX; y[1] = 0;
+      x[2] = image[i].NX; y[2] = image[i].NY;
+      x[3] = 0;           y[3] = image[i].NY;
+    }
+
+  got_type:
+
+    /* project this image to screen display coords */
+    // check for boundary overlap?
+    for (j = 0; j < Npts; j++) {
+      status = XY_to_RD (&r[j], &d[j], x[j], y[j], &image[i].coords);
+      if (!status) break;
+      r[j] = ohana_normalize_angle (r[j]);
+      while (r[j] < Rmin) { r[j] += 360.0; }
+      while (r[j] > Rmax) { r[j] -= 360.0; }
+      if (j == 0) {
+	leftside = (r[j] < Rmid);
+      } 
+      if (j > 0) { 
+	if ( leftside && (r[j] > Rmid + 90)) { r[j] -= 360.0; }
+	if (!leftside && (r[j] < Rmid - 90)) { r[j] += 360.0; }
+      }
+    }
+
+    // extremely large-scale images with certain projections will have odd boundaries.
+    // eg, the ASCA images are essentially circles of radius ~60 degrees.  plot these as 
+    // octagons with some dummy size.
+    if (!status) {
+      int jp, xo, yo;
+      double rc, dc;
+      xo = 0.5*image[i].NX;
+      yo = 0.5*image[i].NY;
+      // is the image center on the screen?
+      status = XY_to_RD (&rc, &dc, 0.5*x[2], 0.5*y[2], &image[i].coords);
+      if (status && (image[i].NX * image[i].coords.cdelt2 > 90)) {
+	// draw an octagon with radius 45 degrees
+	double dX = Radius / image[i].coords.cdelt2;
+	x[0] = xo+dX;      y[0] = yo+BETA*dX;
+	x[1] = xo+BETA*dX; y[1] = yo+dX;
+	x[2] = xo-BETA*dX; y[2] = yo+dX;
+	x[3] = xo-dX;      y[3] = yo+BETA*dX;
+	x[4] = xo-dX;      y[4] = yo-BETA*dX;
+	x[5] = xo-BETA*dX; y[5] = yo-dX;
+	x[6] = xo+BETA*dX; y[6] = yo-dX;
+	x[7] = xo+dX;      y[7] = yo-BETA*dX;
+	Npts = 8;
+	j = 0;
+	for (jp = 0; jp < 8; jp++) {
+	  status = XY_to_RD (&r[j], &d[j], x[jp], y[jp], &image[i].coords);
+	  if (!status) continue;
+	  r[j] = ohana_normalize_angle (r[j]);
+	  while (r[j] < Rmin) { r[j] += 360.0; }
+	  while (r[j] > Rmax) { r[j] -= 360.0; }
+	  if (j == 0) {
+	    leftside = (r[j] < Rmid);
+	  } 
+	  if (j > 0) { 
+	    if ( leftside && (r[j] > Rmid + 90)) { r[j] -= 360.0; }
+	    if (!leftside && (r[j] < Rmid - 90)) { r[j] += 360.0; }
+	  }
+	  j++;
+	}
+	Npts = j;
+      } else {
+	continue;
+      }
+    }
+    if (Npts == 0) continue;
+
+  plot_points:
+
+    status = FALSE;
+    for (j = 0; j < Npts; j++) {
+      status |= RD_to_XY (&Xvec.elements.Flt[N+2*j], &Yvec.elements.Flt[N+2*j], r[j], d[j], &graphmode.coords);
+      if (j > 0) {
+	Xvec.elements.Flt[N+2*j - 1] = Xvec.elements.Flt[N+2*j];
+	Yvec.elements.Flt[N+2*j - 1] = Yvec.elements.Flt[N+2*j];
+      }
+    }
+    Xvec.elements.Flt[N+2*Npts-1] = Xvec.elements.Flt[N];
+    Yvec.elements.Flt[N+2*Npts-1] = Yvec.elements.Flt[N];
+    if (!status) continue;
+    // if none of the points are on the visible side of the projection, do not plot the image
+
+    InPic = FALSE;
+    for (j = 0; j < 2*Npts; j+=2) {
+      if ((Xvec.elements.Flt[N+j] >= graphmode.xmin) && 
+	  (Xvec.elements.Flt[N+j] <= graphmode.xmax) && 
+	  (Yvec.elements.Flt[N+j] >= graphmode.ymin) && 
+	  (Yvec.elements.Flt[N+j] <= graphmode.ymax))
+	InPic = TRUE;
+    }
+    // if (!status && HIDDEN) status = TRUE;
+    if (!InPic) continue;
+
+    plist[n] = i;
+    n++;
+    if (n > npts - 1) {
+      npts += 200;
+      REALLOCATE (plist, int, npts);
+    }
+    N+=2*Npts;
+    if (N + 16 >= NPTS) {  /* need to leave room for 8 point image */
+      NPTS += 400;
+      REALLOCATE (Xvec.elements.Flt, opihi_flt, NPTS);
+      REALLOCATE (Yvec.elements.Flt, opihi_flt, NPTS);
+    }
+  }
+
+  gprint (GP_ERR, "plotting %d images\n", n);
+  Xvec.Nelements = Yvec.Nelements = N;
+  if (N > 0) {
+    graphmode.style = 2; /* points */
+    graphmode.ptype = 100; /* connect pairs of points */
+    graphmode.etype = 0;
+    PlotVectorPair (kapa, &Xvec, &Yvec, NULL, &graphmode);
+  }
+
+  free (Xvec.elements.Flt);
+  free (Yvec.elements.Flt);
+  FreeImagesDVO (image);
+  FREE (foundMosaic);
+  return (TRUE);
+
+
+ usage:
+  gprint (GP_ERR, "USAGE: image [options]\n");
+  gprint (GP_ERR, "  +mosaic : show mosaic outline\n");
+  gprint (GP_ERR, "  -mosaic : only mosaic outline\n");
+  gprint (GP_ERR, "  -hidden : (deprecated)\n");
+  gprint (GP_ERR, "  -name   : only names matching (start of name)\n");
+  gprint (GP_ERR, "  -radius : display all-sky images with given radius octagon\n");
+  gprint (GP_ERR, "  -time (start) (range)  : show images for time and interval\n");
+  gprint (GP_ERR, "  -trange (start) (stop) : show images within time range\n");
+  return (FALSE);
+}
+
+
Index: /branches/sc_branches/pantasks_condor/dvo/imbox.c
===================================================================
--- /branches/sc_branches/pantasks_condor/dvo/imbox.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/dvo/imbox.c	(revision 34783)
@@ -0,0 +1,135 @@
+# include "dvoshell.h"
+
+// LARGEFILES: this function is currently limited to images with Nx,Ny each < 2^31
+int imbox (int argc, char **argv) {
+  
+  off_t Nskip;
+  int j, kapa, status, InPic, flipped, N, haveNx, haveNy, Nx, Ny, SOLO_PHU, Npts, NPTS;
+  Vector Xvec, Yvec;
+  double r, d, x[4], y[4], Rmin, Rmax, Rmid;
+  Header header;
+  Coords coords;
+  Coords mosaic;
+  Graphdata graphmode;
+  FILE *f;
+
+  if (!style_args (&graphmode, &argc, argv, &kapa)) return FALSE;
+
+  SOLO_PHU = FALSE;
+  if ((N = get_argument (argc, argv, "-phu"))) {
+    SOLO_PHU = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+
+  if (argc != 2) {
+    gprint (GP_ERR, "USAGE: imbox (filename)\n");
+    return (FALSE);
+  }
+
+  f = fopen (argv[1], "r");
+  if (f == NULL) {
+    gprint (GP_ERR, "file not found\n");
+    return (FALSE);
+  }
+  
+  Rmin = graphmode.coords.crval1 - 182.0;
+  Rmax = graphmode.coords.crval1 + 182.0;
+  Rmid = 0.5*(Rmin + Rmax);
+  
+  /* project this image to screen display coords */
+  Npts = 0;
+  NPTS = 200; 
+  SetVector (&Xvec, OPIHI_FLT, NPTS);
+  SetVector (&Yvec, OPIHI_FLT, NPTS);
+
+  while (gfits_fread_header (f, &header)) {
+    if (!GetCoords (&coords, &header)) goto skip;
+    if (!SOLO_PHU && !strcmp (&coords.ctype[4], "-DIS")) {
+      mosaic = coords;
+      RegisterMosaic (&mosaic);
+      goto skip;
+    }
+
+    // XXX currently, image uses an unsigned short for NX,XY. this is rather restrictive
+    // and needs to be at least checked.
+    haveNx = gfits_scan (&header, "IMNAXIS1",   "%d", 1, &Nx);
+    haveNy = gfits_scan (&header, "IMNAXIS2",   "%d", 1, &Ny);
+
+    if (!haveNx && !haveNy) {
+	haveNx = gfits_scan (&header, "ZNAXIS1",   "%d", 1, &Nx);
+	haveNy = gfits_scan (&header, "ZNAXIS2",   "%d", 1, &Ny);
+    }
+
+    if (!haveNx && !haveNy) {
+	haveNx = gfits_scan (&header, "NAXIS1",   "%d", 1, &Nx);
+	haveNy = gfits_scan (&header, "NAXIS2",   "%d", 1, &Ny);
+    }
+
+    if (!haveNx || !haveNy) {
+	fprintf (stderr, "missing image dimensions in header\n");
+	goto skip;
+    }
+
+    x[0] = 0;  y[0] = 0;
+    x[1] = Nx; y[1] = 0;
+    x[2] = Nx; y[2] = Ny;
+    x[3] = 0;  y[3] = Ny;
+    status = FALSE;
+    flipped = FALSE;
+    for (j = 0; j < 4; j++) {
+      XY_to_RD (&r, &d, x[j], y[j], &coords);
+      r = ohana_normalize_angle (r);
+      while ((j == 0) && (r < Rmin)) { flipped = TRUE; r += 360.0; }
+      while ((j == 0) && (r > Rmax)) { flipped = TRUE; r -= 360.0; }
+      if ((j > 0) && flipped) {
+	while (r < Rmid) r+= 360.0;
+	while (r > Rmid) r-= 360.0;
+      }
+      status |= RD_to_XY (&Xvec.elements.Flt[Npts + 2*j], &Yvec.elements.Flt[Npts + 2*j], r, d, &graphmode.coords);
+      if (j > 0) {
+	Xvec.elements.Flt[Npts + 2*j - 1] = Xvec.elements.Flt[Npts + 2*j];
+	Yvec.elements.Flt[Npts + 2*j - 1] = Yvec.elements.Flt[Npts + 2*j];
+      }
+    }
+    Xvec.elements.Flt[Npts + 7] = Xvec.elements.Flt[Npts + 0];
+    Yvec.elements.Flt[Npts + 7] = Yvec.elements.Flt[Npts + 0];
+
+    InPic = FALSE;
+    for (j = 0; j < 8; j+=2) {
+      if ((Xvec.elements.Flt[Npts + j] >= graphmode.xmin) && 
+    	  (Xvec.elements.Flt[Npts + j] <= graphmode.xmax) && 
+    	  (Yvec.elements.Flt[Npts + j] >= graphmode.ymin) && 
+    	  (Yvec.elements.Flt[Npts + j] <= graphmode.ymax))
+    	InPic = TRUE;
+    }
+    if (!InPic) continue;
+
+    Npts += 8;
+    if (Npts + 8 >= NPTS) {  /* need to leave room for 4 point image */
+      NPTS += 200;
+      REALLOCATE (Xvec.elements.Flt, opihi_flt, NPTS);
+      REALLOCATE (Yvec.elements.Flt, opihi_flt, NPTS);
+    }
+
+  skip:
+    Nskip = gfits_data_size (&header);
+    fseeko (f, Nskip, SEEK_CUR); 
+    gfits_free_header (&header);
+  }
+
+  Xvec.Nelements = Yvec.Nelements = Npts;
+  if (Npts > 0) {
+    graphmode.style = 2; /* points */
+    graphmode.ptype = 100; /* connect pairs of points */
+    graphmode.etype = 0;
+    PlotVectorPair (kapa, &Xvec, &Yvec, NULL, &graphmode);
+  }
+
+  fclose (f);
+  free (Xvec.elements.Flt);
+  free (Yvec.elements.Flt);
+  return (TRUE);
+
+}
+
+
Index: /branches/sc_branches/pantasks_condor/dvo/imdata.c
===================================================================
--- /branches/sc_branches/pantasks_condor/dvo/imdata.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/dvo/imdata.c	(revision 34783)
@@ -0,0 +1,225 @@
+# include "dvoshell.h"
+
+int imdata (int argc, char **argv) {
+  
+  off_t i, j, k, n, I;
+  int N, NPTS, found, mode, TimeSelect, TimeFormat;
+  off_t Nregions, NREGIONS;
+  off_t *subset, Nsubset, Nimage;
+  double trange;
+  time_t tzero, start, stop, TimeReference;
+  Image *image;
+  Catalog catalog;
+  SkyTable *sky;
+  SkyList *skylist, *skyset;
+  Vector *vec;
+  SkyRegionSelection *selection;
+
+  // parse skyregion options
+  if ((selection = SetRegionSelection (&argc, argv)) == NULL) {
+    gprint (GP_ERR, "invalid sky region selection\n");
+    return FALSE;
+  }
+
+  start = stop = 0;
+  TimeSelect = FALSE;
+  if ((N = get_argument (argc, argv, "-time"))) {
+    remove_argument (N, &argc, argv);
+    if (!ohana_str_to_time (argv[N], &tzero)) { 
+      gprint (GP_ERR, "syntax error\n");
+      return (FALSE);
+    }
+    remove_argument (N, &argc, argv);
+    if (!ohana_str_to_dtime (argv[N], &trange)) { 
+      gprint (GP_ERR, "syntax error\n");
+      return (FALSE);
+    }
+    remove_argument (N, &argc, argv);
+    TimeSelect = TRUE;
+    gprint (GP_ERR, "searching in range %ds - %ds (%f seconds)\n", (int)tzero, (int)(tzero + trange), trange);
+    if (trange > 0) {
+      start = tzero;
+      stop  = tzero + trange;
+    } else {
+      stop = tzero;
+      start  = tzero + trange;
+    }      
+  }
+
+  gprint (GP_ERR, "function is poorly defined; disabled and may be removed\n");
+  gprint (GP_ERR, "this function extracts measure data corresponding to a given image?\n");
+  return (FALSE);
+
+  if (argc != 4) {
+    gprint (GP_ERR, "USAGE: imdata (value) [-time t dt]\n");
+    return (FALSE);
+  }
+
+  /* identify selection */
+  mode = 0;
+  if (!strcasecmp (argv[1], "ra")) 
+    mode = 1;
+  if (!strcasecmp (argv[1], "dec")) 
+    mode = 2;
+  if (!strcasecmp (argv[1], "mag")) 
+    mode = 3;
+  if (!strcasecmp (argv[1], "dmag")) 
+    mode = 4;
+  if (!strcasecmp (argv[1], "Mcal")) 
+    mode = 5;
+  if (!strcasecmp (argv[1], "Mrel")) 
+    mode = 6;
+  if (!strcasecmp (argv[1], "source")) 
+    mode = 7;
+  if (!strcasecmp (argv[1], "x")) 
+    mode = 8;
+  if (!strcasecmp (argv[1], "y")) 
+    mode = 9;
+  if (!strcasecmp (argv[1], "time")) 
+    mode = 10;
+  if (mode == 0) {
+    gprint (GP_ERR, "value may be one of the following:\n");
+    gprint (GP_ERR, " ra dR dec dD mag dmag Mrel Mcal source time\n");
+    return (FALSE);
+  }
+  if ((vec = SelectVector (argv[1], ANYVECTOR, TRUE)) == NULL) return (FALSE);
+
+  if ((image = LoadImagesDVO (&Nimage)) == NULL) return (FALSE);
+  image_subset (image, Nimage, &subset, &Nsubset, selection, tzero, trange, TimeSelect);
+  BuildChipMatch (image, Nimage);
+  GetTimeFormat (&TimeReference, &TimeFormat);
+
+  /* load sky from correct table */
+  sky = GetSkyTable ();
+
+  Nregions = 0;
+  NREGIONS = 10;
+  ALLOCATE (skylist, SkyList, 1);
+  ALLOCATE (skylist[0].regions, SkyRegion *, NREGIONS);
+  skylist[0].ownElements = FALSE;
+
+  /* for each image of interest, find the appropriate region files */
+  for (i = 0; i < Nsubset; i++) {
+    I = subset[i];
+
+    if (!FindMosaicForImage (image, Nimage, I)) continue;
+    skyset = SkyListByImage (sky, -1, &image[I]);
+
+    for (j = 0; j < skyset[0].Nregions; j++) {
+      found = FALSE;
+      for (k = 0; (k < skylist[0].Nregions) && !found; k++) {
+	found = !strcmp (skylist[0].regions[k][0].name, skyset[0].regions[j][0].name);
+      }
+      if (found) continue;
+      skylist[0].regions[Nregions] = skyset[0].regions[j];
+      Nregions ++;
+      CHECK_REALLOCATE (skylist[0].regions, SkyRegion *, NREGIONS, Nregions, 10);
+    }
+    SkyListFree (skyset);
+  }	
+  free (subset);
+  for (i = 0; i < skylist[0].Nregions; i++) {
+    gprint (GP_ERR, "try %s\n", skylist[0].regions[i][0].name);
+  } 
+  
+  /* create output vector */
+  N = 0;
+  NPTS = 1000;
+  ResetVector (vec, OPIHI_FLT, NPTS);
+
+  // prepare to handle interrupt signals
+  signal (SIGINT, handle_interrupt);
+  interrupt = FALSE;
+
+  /* for each region file, extract the data of interest in the right time range */
+  for (j = 0; (j < skylist[0].Nregions) && !interrupt; j++) {
+
+    /* get file name and open */
+    catalog.filename = skylist[0].filename[j];
+    catalog.catflags = LOAD_AVES | LOAD_MEAS | LOAD_SECF;
+    catalog.Nsecfilt = 0;
+
+    // an error exit status here is a significant error
+    if (!dvo_catalog_open (&catalog, NULL, FALSE, "r")) {
+      fprintf (stderr, "ERROR: failure to open catalog file %s\n", catalog.filename);
+      exit (2);
+    }
+    dvo_catalog_unlock (&catalog);
+
+    /* assign vector values */
+    switch (mode) {
+      case (1):  /* ra */
+	for (i = 0; i < catalog.Nmeasure; i++) {
+	  if ((catalog.measure[i].t < start) || (catalog.measure[i].t > stop)) continue;
+	  n = catalog.measure[i].averef;
+	  vec[0].elements.Flt[N] = catalog.average[n].R - catalog.measure[i].dR / 3600.0;
+	  N++;
+	  CHECK_REALLOCATE (vec[0].elements.Flt, opihi_flt, NPTS, N, 1000);
+	}
+	break;
+      case (2):  /* dec */
+	for (i = 0; i < catalog.Nmeasure; i++) {
+	  if ((catalog.measure[i].t < start) || (catalog.measure[i].t > stop)) continue;
+	  n = catalog.measure[i].averef;
+	  vec[0].elements.Flt[N] = catalog.average[n].D - catalog.measure[i].dD / 3600.0;
+	  N++;
+	  CHECK_REALLOCATE (vec[0].elements.Flt, opihi_flt, NPTS, N, 1000);
+	}
+	break;
+      case (3):  /* mag */
+	for (i = 0; i < catalog.Nmeasure; i++) {
+	  if ((catalog.measure[i].t < start) || (catalog.measure[i].t > stop)) continue;
+	  vec[0].elements.Flt[N] = catalog.measure[i].M;
+	  N++;
+	  CHECK_REALLOCATE (vec[0].elements.Flt, opihi_flt, NPTS, N, 1000);
+	}
+	break;
+      case (4):  /* dmag */
+	for (i = 0; i < catalog.Nmeasure; i++) {
+	  if ((catalog.measure[i].t < start) || (catalog.measure[i].t > stop)) continue;
+	  vec[0].elements.Flt[N] = catalog.measure[i].dM;
+	  N++;
+	  CHECK_REALLOCATE (vec[0].elements.Flt, opihi_flt, NPTS, N, 1000);
+	}
+	break;
+      case (5):  /* Mcal */
+	for (i = 0; i < catalog.Nmeasure; i++) {
+	  if ((catalog.measure[i].t < start) || (catalog.measure[i].t > stop)) continue;
+	  vec[0].elements.Flt[N] = catalog.measure[i].Mcal;
+	  N++;
+	  CHECK_REALLOCATE (vec[0].elements.Flt, opihi_flt, NPTS, N, 1000);
+	}
+	break;
+      case (6):  /* Mrel */
+	for (i = 0; i < catalog.Nmeasure; i++) {
+	  if ((catalog.measure[i].t < start) || (catalog.measure[i].t > stop)) continue;
+	  n = catalog.measure[i].averef;
+	  // vec[0].elements.Flt[N] = catalog.average[n].M;
+	  N++;
+	}
+	break;
+      case (7):  /* source */
+	for (i = 0; i < catalog.Nmeasure; i++) {
+	  if ((catalog.measure[i].t < start) || (catalog.measure[i].t > stop)) continue;
+	  vec[0].elements.Flt[N] = catalog.measure[i].photcode;
+	  N++;
+	  CHECK_REALLOCATE (vec[0].elements.Flt, opihi_flt, NPTS, N, 1000);
+	}
+	break;
+      case (10):  /* time */
+	for (i = 0; i < catalog.Nmeasure; i++) {
+	  if ((catalog.measure[i].t < start) || (catalog.measure[i].t > stop)) continue;
+	  vec[0].elements.Flt[N] = TimeValue (catalog.measure[i].t, TimeReference, TimeFormat);
+	  N++;
+	  CHECK_REALLOCATE (vec[0].elements.Flt, opihi_flt, NPTS, N, 1000);
+	}
+	break;
+    }
+    dvo_catalog_free (&catalog);
+  }
+  
+  vec[0].Nelements = N;
+  REALLOCATE (vec[0].elements.Flt, opihi_flt, MAX(1,N));
+  FreeImagesDVO(image);
+  return (TRUE);
+}
Index: /branches/sc_branches/pantasks_condor/dvo/imdense.c
===================================================================
--- /branches/sc_branches/pantasks_condor/dvo/imdense.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/dvo/imdense.c	(revision 34783)
@@ -0,0 +1,77 @@
+# include "dvoshell.h"
+extern double drand48();
+
+int imdense (int argc, char **argv) {
+  
+  long A, B;
+  off_t i, Nimage;
+  int kapa, N, status, NPTS;
+  double r, d, x, y, Rmin, Rmax;
+  Vector Xvec, Yvec;
+  Image *image;
+  Graphdata graphmode;
+
+  if (!style_args (&graphmode, &argc, argv, &kapa)) return FALSE;
+
+  /* need options to list only images in region and only images in a time range */
+  /* also, option to list and not plot or plot and not list images */
+  if (argc != 1) {
+    gprint (GP_ERR, "USAGE: image\n");
+    return (FALSE);
+  }
+
+  if ((image = LoadImagesDVO (&Nimage)) == NULL) return (FALSE);
+  BuildChipMatch (image, Nimage);
+
+  Rmin = graphmode.coords.crval1 - 182.0;
+  Rmax = graphmode.coords.crval1 + 182.0;
+  
+  A = time(NULL);
+  for (B = 0; A == time(NULL); B++);
+  srand48(B);
+
+  N = 0;
+  NPTS = 200;
+  SetVector (&Xvec, OPIHI_FLT, NPTS);
+  SetVector (&Yvec, OPIHI_FLT, NPTS);
+
+  for (i = 0; i < Nimage; i++) {
+    /* choose a position for point within image box */
+    x = (0.1 + 0.9*drand48()) * image[i].NX;
+    y = (0.1 + 0.9*drand48()) * image[i].NY;
+    /* project this image to screen display coords */
+    status = FALSE;
+    if (!FindMosaicForImage (image, Nimage, i)) continue;
+    XY_to_RD (&r, &d, x, y, &image[i].coords);
+    r = ohana_normalize_angle (r);
+    while (r < Rmin) r += 360.0; 
+    while (r > Rmax) r -= 360.0; 
+    status |= RD_to_XY (&Xvec.elements.Flt[N], &Yvec.elements.Flt[N], r, d, &graphmode.coords);
+    if ((Xvec.elements.Flt[N] >= graphmode.xmin) && 
+	(Xvec.elements.Flt[N] <= graphmode.xmax) && 
+	(Yvec.elements.Flt[N] >= graphmode.ymin) && 
+	(Yvec.elements.Flt[N] <= graphmode.ymax) && status) {
+      N++;
+      if (N > NPTS - 1) { 
+	NPTS += 200;
+	REALLOCATE (Xvec.elements.Flt, opihi_flt, NPTS);
+	REALLOCATE (Yvec.elements.Flt, opihi_flt, NPTS);
+      }
+    }
+  }
+
+  Xvec.Nelements = Yvec.Nelements = N;
+  if (N > 0) {
+    graphmode.style = 2; /* points */
+    graphmode.etype = 0;
+    PlotVectorPair (kapa, &Xvec, &Yvec, NULL, &graphmode);
+  }
+
+  free (Xvec.elements.Flt);
+  free (Yvec.elements.Flt);
+  FreeImagesDVO (image);
+  return (TRUE);
+
+}
+
+
Index: /branches/sc_branches/pantasks_condor/dvo/imextract.c
===================================================================
--- /branches/sc_branches/pantasks_condor/dvo/imextract.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/dvo/imextract.c	(revision 34783)
@@ -0,0 +1,245 @@
+# include "dvoshell.h"
+
+int imextract (int argc, char **argv) {
+  
+  off_t i, j, Nimage;
+  int n, N, Npts, NPTS, last, next, state, Nfields, Nreturn, Ncstack, Nstack;
+  char **cstack, name[1024];
+  void *Signal;
+
+  Vector **vec;
+  Image *image;
+  dbStack *stack;
+  dbField *fields;
+  dbValue *values;
+  SkyRegionSelection *selection;
+
+  /* defaults */
+  vec = NULL;
+  image = NULL;
+  stack = NULL;
+  fields = NULL;
+  values = NULL;
+  selection = NULL;
+
+  if ((N = get_argument (argc, argv, "-h"))) goto help;
+  if ((N = get_argument (argc, argv, "--help"))) goto help;
+
+  // int VERBOSE = FALSE;
+  // if ((N = get_argument (argc, argv, "-v"))) {
+  //   remove_argument (N, &argc, argv);
+  //   VERBOSE = TRUE;
+  // }
+
+  if (!InitPhotcodes ()) goto escape;
+
+  // parse skyregion options
+  if ((selection = SetRegionSelection (&argc, argv)) == NULL) {
+    gprint (GP_ERR, "invalid sky region selection\n");
+    goto escape;
+  }
+
+  // command-line is of the form: imextract field,field, field [where (field op value)...]
+
+  // parse the fields to be extracted and returned
+  fields = dbCmdlineFields (argc, argv, DVO_TABLE_IMAGE, &last, &Nfields);
+  if (fields == NULL) goto escape;
+  if (Nfields == 0) {
+    FreeSkyRegionSelection (selection);
+    dbFreeFields (fields, Nfields);
+    goto help;
+  }
+
+  // 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-next, &argv[next], &Ncstack);
+  
+  // construct the db Boolean math stack (frees cstack)
+  stack = dbRPN (Ncstack, cstack, &Nstack);
+  if (Ncstack && !Nstack) {
+    print_error(); 
+    goto escape; 
+  }
+
+  // add the skyregion limits to the where statement (or create)
+  // XXX we may want to drop this and use just the image_subset function
+  dbAstroRegionLimits (&stack, &Nstack, selection, DVO_TABLE_IMAGE);
+
+  // parse stack elements into fields and scalars as needed
+  Nreturn = Nfields; 
+  if (!dbCheckStack (stack, Nstack, DVO_TABLE_IMAGE, &fields, &Nfields)) goto escape;
+
+  /* create output storage vectors */
+  Npts = 0;
+  NPTS = 100;
+  ALLOCATE (values, dbValue, Nfields);
+  ALLOCATE (vec, Vector *, Nreturn);
+  for (i = 0; i < Nreturn; i++) {
+    if (ISNUM(fields[i].name[0])) {
+      sprintf (name, "v_%s", fields[i].name);
+    } else {
+      sprintf (name, "%s", fields[i].name);
+    }
+    if ((vec[i] = SelectVector (name, ANYVECTOR, TRUE)) == NULL) goto escape;
+    ResetVector (vec[i], fields[i].type, NPTS);
+  }
+
+  if ((image = LoadImagesDVO (&Nimage)) == NULL) goto escape;
+  BuildChipMatch (image, Nimage);
+  dbExtractImagesInit (); 
+
+  // XXX do I need to use this, or the region portion
+  // image_subset (image, Nimage, &subset, &Nsubset, selection, tzero, trange, TimeSelect);
+
+  // grab data from all selected sky regions
+  Signal = signal (SIGINT, handle_interrupt);
+  interrupt = FALSE;
+  for (j = 0; (j < Nimage) && !interrupt; j++) {
+
+    // reset counters for saved fields, extract fields
+    dbExtractImagesReset (); 
+    for (n = 0; n < Nfields; n++) {
+      values[n] = dbExtractImages (image, Nimage, j, &fields[n]);
+    }
+
+    // test the conditional statement
+    if (!dbBooleanCond (stack, Nstack, values)) continue;
+    for (n = 0; n < Nreturn; n++) {
+      if (vec[n][0].type == OPIHI_FLT) {
+	vec[n][0].elements.Flt[Npts] = values[n].Flt;
+      } else {
+	vec[n][0].elements.Int[Npts] = values[n].Int;
+      }
+    }
+    Npts++;
+    if (Npts >= NPTS) {
+      NPTS += 2000;
+      for (n = 0; n < Nreturn; n++) {
+	if (vec[n][0].type == OPIHI_FLT) {
+	  REALLOCATE (vec[n][0].elements.Flt, opihi_flt, NPTS);
+	} else {
+	  REALLOCATE (vec[n][0].elements.Int, opihi_int, NPTS);
+	}
+      }
+    }
+  }
+  signal (SIGINT, Signal);
+  interrupt = FALSE;
+  for (n = 0; n < Nreturn; n++) {
+    vec[n][0].Nelements = Npts;
+    if (vec[n][0].type == OPIHI_FLT) {
+      REALLOCATE (vec[n][0].elements.Flt, opihi_flt, MAX(1,Npts));
+    } else {
+      REALLOCATE (vec[n][0].elements.Int, opihi_int, MAX(1,Npts));
+    }
+  }
+
+  // free (subset);
+  FreeImagesDVO (image);
+
+  if (vec) free (vec);
+  if (values) free (values);
+  dbFreeFields (fields, Nfields);
+  dbFreeStack (stack, Nstack);
+  if (stack) free (stack);
+  return (TRUE);
+  
+ escape:
+  if (vec) free (vec);
+  if (values) free (values);
+  dbFreeFields (fields, Nfields);
+  dbFreeStack (stack, Nstack);
+  if (stack) free (stack);
+  return (FALSE);
+
+ help:
+  gprint (GP_ERR, "USAGE: imextract field[,field,field...] where (expression)\n");
+
+  if ((argc > N + 1) && !strcasecmp (argv[N+1], "fields")) {
+    gprint (GP_ERR, " USAGE: imextract field[,field,field...] where (expression)\n");
+    gprint (GP_ERR, "  RA : right ascension of field center (J2000)\n");
+    gprint (GP_ERR, "  DEC : declination of field center\n");
+    gprint (GP_ERR, "  GLON : galactic longitude of field center (J2000)\n");
+    gprint (GP_ERR, "  GLAT : galactic latitude of field center (J2000)\n");
+    gprint (GP_ERR, "  ELON : ecliptic longitude of field center (J2000)\n");
+    gprint (GP_ERR, "  ELAT : ecliptic latitude of field center (J2000)\n");
+
+    gprint (GP_ERR, "  theta : position angle of image\n");
+    gprint (GP_ERR, "  skew : distortion from rectangle\n");
+    gprint (GP_ERR, "  scale : pixel scale\n");
+    gprint (GP_ERR, "  dscale : pixel-scale error (or variation?)\n");
+
+    gprint (GP_ERR, "  time : time of exposure\n");
+    gprint (GP_ERR, "  nstar : number of stars detected in exposure\n");
+    gprint (GP_ERR, "  airmass : mean airmass of exposure\n");
+    gprint (GP_ERR, "  NX : image dimensions\n");
+    gprint (GP_ERR, "  NY : image dimensions\n");
+
+    gprint (GP_ERR, "  apresid : aperture - fit magnitude\n");
+    gprint (GP_ERR, "  dapresid : aperture - fit magnitude scatter\n");
+
+    gprint (GP_ERR, "  Mcal : photometry calibration (mags)\n");
+    gprint (GP_ERR, "  dMcal : photometry calibration error (mags)\n");
+    gprint (GP_ERR, "  Xm : chisq of photometry calibration\n");
+    gprint (GP_ERR, "  photcode : numeric photcode value for image\n");
+    gprint (GP_ERR, "  exptime : exposure duration (seconds)\n");
+    gprint (GP_ERR, "  sidtime : sidereal time of exposure\n");
+
+    gprint (GP_ERR, "  latitude : observatory latitude\n");
+
+    gprint (GP_ERR, "  detlimit : detection limit of exposure\n");
+    gprint (GP_ERR, "  satlimit : saturation limit of exposure\n");
+    gprint (GP_ERR, "  cerror : astrometric scatter\n");
+
+    gprint (GP_ERR, "  -- Note: the follow FWHM are from the PSF model --\n");
+    gprint (GP_ERR, "  FWHM : mean fwhm of chip\n");
+    gprint (GP_ERR, "  FWHM_MAJ : fwhm of chip (major axis)\n");
+    gprint (GP_ERR, "  FWHM_MIN : fwhm of chip (minor axis)\n");
+    gprint (GP_ERR, "  FWHM_MAJOR : fwhm of chip (major axis)\n");
+    gprint (GP_ERR, "  FWHM_MININ : fwhm of chip (minor axis)\n");
+
+    gprint (GP_ERR, "  FWHM_MEDIAN : median fwhm of exposure\n");
+    gprint (GP_ERR, "  FWHM_MAJ_MEDIAN : median fwhm of major axis\n");
+    gprint (GP_ERR, "  FWHM_MIN_MEDIAN : median fwhm of minor axis\n");
+    gprint (GP_ERR, "  FWHM_MAJOR_MEDIAN : median fwhm of major axis\n");
+    gprint (GP_ERR, "  FWHM_MININ_MEDIAN : median fwhm of minor axis\n");
+
+    gprint (GP_ERR, "  trate : tracking rate for TDI images\n");
+
+    gprint (GP_ERR, "  ncal : number of stars used for photometry calibration\n");
+    gprint (GP_ERR, "  sky : mean background flux\n");
+
+    gprint (GP_ERR, "  imflags : processing bit flags\n");
+    gprint (GP_ERR, "  flags : processing bit flags\n");
+    gprint (GP_ERR, "  ccdnum : identifier for CCD\n");
+
+    gprint (GP_ERR, "  imageID  : unique image identifier\n");
+    gprint (GP_ERR, "  externID : external image identifier\n");
+    gprint (GP_ERR, "  sourceID : external db reference\n");
+
+    gprint (GP_ERR, "  X_LL_CHIP : chip x-pixel coordinate of lower left corner\n");
+    gprint (GP_ERR, "  X_LR_CHIP : chip x-pixel coordinate of lower right corner\n");
+    gprint (GP_ERR, "  X_UL_CHIP : chip x-pixel coordinate of upper left corner\n");
+    gprint (GP_ERR, "  X_UR_CHIP : chip x-pixel coordinate of upper right corner\n");
+    gprint (GP_ERR, "  Y_LL_CHIP : chip y-pixel coordinate of lower left corner\n");
+    gprint (GP_ERR, "  Y_LR_CHIP : chip y-pixel coordinate of lower right corner\n");
+    gprint (GP_ERR, "  Y_UL_CHIP : chip y-pixel coordinate of upper left corner\n");
+    gprint (GP_ERR, "  Y_UR_CHIP : chip y-pixel coordinate of upper right corner\n");
+    gprint (GP_ERR, "  X_LL_FP   : focal-plane x-pixel coordinate of lower left corner\n");
+    gprint (GP_ERR, "  X_LR_FP   : focal-plane x-pixel coordinate of lower right corner\n");
+    gprint (GP_ERR, "  X_UL_FP   : focal-plane x-pixel coordinate of upper left corner\n");
+    gprint (GP_ERR, "  X_UR_FP   : focal-plane x-pixel coordinate of upper right corner\n");
+    gprint (GP_ERR, "  Y_LL_FP   : focal-plane y-pixel coordinate of lower left corner\n");
+    gprint (GP_ERR, "  Y_LR_FP   : focal-plane y-pixel coordinate of lower right corner\n");
+    gprint (GP_ERR, "  Y_UL_FP   : focal-plane y-pixel coordinate of upper left corner\n");
+    gprint (GP_ERR, "  Y_UR_FP   : focal-plane y-pixel coordinate of upper right corner\n");
+    return (FALSE);
+  }
+  gprint (GP_ERR, " imextract --help fields : for a complete listing of allowed fields\n");
+  return (FALSE);
+}
+  
Index: /branches/sc_branches/pantasks_condor/dvo/imlist.c
===================================================================
--- /branches/sc_branches/pantasks_condor/dvo/imlist.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/dvo/imlist.c	(revision 34783)
@@ -0,0 +1,113 @@
+# include "dvoshell.h"
+
+int imlist (int argc, char **argv) {
+  
+  off_t i, j, Nimage, *subset, Nsubset;
+  int N, TimeSelect, TimeFormat, NameSelect;
+  int PhotcodeSelect;
+  time_t tzero, TimeReference;
+  double r, d, trange, t;
+  char *name;
+  Image *image;
+  PhotCode *PhotcodeValue;
+  SkyRegionSelection *selection;
+
+  if (!InitPhotcodes ()) return (FALSE);
+
+  // parse skyregion options
+  if ((selection = SetRegionSelection (&argc, argv)) == NULL) {
+    gprint (GP_ERR, "invalid sky region selection\n");
+    return FALSE;
+  }
+
+  TimeSelect = FALSE;
+  if ((N = get_argument (argc, argv, "-time"))) {
+    remove_argument (N, &argc, argv);
+    if (!ohana_str_to_time (argv[N], &tzero)) { 
+      gprint (GP_ERR, "syntax error\n");
+      return (FALSE);
+    }
+    remove_argument (N, &argc, argv);
+    if (!ohana_str_to_dtime (argv[N], &trange)) { 
+      gprint (GP_ERR, "syntax error\n");
+      return (FALSE);
+    }
+    remove_argument (N, &argc, argv);
+    TimeSelect = TRUE;
+    gprint (GP_ERR, "plotting in range %ds - %ds (%f seconds)\n", (int)tzero, (int)(tzero + trange), trange);
+  }
+
+  // int RegionSelect = FALSE;
+  // if ((N = get_argument (argc, argv, "-region"))) {
+  //   remove_argument (N, &argc, argv);
+  //   RegionSelect = TRUE;
+  // }
+
+  PhotcodeValue = NULL;
+  PhotcodeSelect = FALSE;
+  if ((N = get_argument (argc, argv, "-photcode"))) {
+    PhotcodeSelect = TRUE;
+    remove_argument (N, &argc, argv);
+    PhotcodeValue = GetPhotcodebyName (argv[N]);
+    if (PhotcodeValue == NULL) {
+      gprint (GP_ERR, "photcode not found in photcode table\n");
+      return (FALSE);
+    }
+    remove_argument (N, &argc, argv);
+  }
+  if ((N = get_argument (argc, argv, "-Nphotcode"))) {
+    PhotcodeSelect = TRUE;
+    remove_argument (N, &argc, argv);
+    PhotcodeValue = GetPhotcodebyCode (atoi(argv[N]));
+    if (PhotcodeValue == NULL) {
+      gprint (GP_ERR, "photcode not found in photcode table\n");
+      return (FALSE);
+    }
+    remove_argument (N, &argc, argv);
+  }
+
+  name = NULL;
+  NameSelect = FALSE;
+  if ((N = get_argument (argc, argv, "-name"))) {
+    remove_argument (N, &argc, argv);
+    name = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+    NameSelect = TRUE;
+  }
+
+  if (argc != 1) {
+    gprint (GP_ERR, "USAGE: image [-time start range] [-region] [-name string]\n");
+    return (FALSE);
+  }
+  
+  if ((image = LoadImagesDVO (&Nimage)) == NULL) return (FALSE);
+  image_subset (image, Nimage, &subset, &Nsubset, selection, tzero, trange, TimeSelect);
+  BuildChipMatch (image, Nimage);
+
+  GetTimeFormat (&TimeReference, &TimeFormat);
+
+  for (j = 0; j < Nsubset; j++) {
+    i = subset[j];
+    if (NameSelect && (strstr (image[i].name, name) == (char *) NULL)) continue;
+    if (PhotcodeSelect) {
+      if (PhotcodeValue[0].type == PHOT_DEP) {
+	if (PhotcodeValue[0].code != image[i].photcode) continue;
+      } else {
+	if (PhotcodeValue[0].code != GetPhotcodeEquivCodebyCode (image[i].photcode)) continue;
+      }
+    }
+    if (!FindMosaicForImage (image, Nimage, i)) continue;
+    t = TimeValue (image[i].tzero, TimeReference, TimeFormat);
+    if (!strcmp(&image[i].coords.ctype[4], "-DIS")) {
+      XY_to_RD (&r, &d, 0.0, 0.0, &image[i].coords);
+    } else {
+      XY_to_RD (&r, &d, 0.5*image[i].NX, 0.5*image[i].NY, &image[i].coords);
+    }
+    gprint (GP_LOG, "%3lld %s %8lld %8.4f %8.4f %f %5d %2d %4.2f %5.3f %5.3f\n", 
+	    (long long) i, image[i].name, (long long) image[i].imageID, r, d, t, image[i].nstar, image[i].photcode, image[i].secz, image[i].Mcal, image[i].dMcal);
+  }
+
+  FreeImagesDVO(image);
+  free (subset);
+  return (TRUE);
+}
Index: /branches/sc_branches/pantasks_condor/dvo/imphot.c
===================================================================
--- /branches/sc_branches/pantasks_condor/dvo/imphot.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/dvo/imphot.c	(revision 34783)
@@ -0,0 +1,104 @@
+# include "dvoshell.h"
+
+int imphot (int argc, char **argv) {
+  
+  time_t tzero;
+  double trange;
+  int N, GreyScale;
+  off_t i, j, Nimage, Nsubset, *subset;
+  char bufname[64];
+  float *p;
+  double x, y;
+  Image *image;
+  Buffer *buf;
+  SkyRegionSelection *selection;
+
+  GreyScale = FALSE;
+  if ((N = get_argument (argc, argv, "-g"))) {
+    remove_argument (N, &argc, argv);
+    strcpy (bufname, argv[N]);
+    remove_argument (N, &argc, argv);
+    GreyScale = TRUE;
+  }
+
+  // parse skyregion options
+  if ((selection = SetRegionSelection (&argc, argv)) == NULL) {
+    gprint (GP_ERR, "invalid sky region selection\n");
+    return FALSE;
+  }
+
+  if (argc != 3) {
+    gprint (GP_ERR, "USAGE: imphot tzero trange [-g buffer]\n");
+    return (FALSE);
+  }
+
+  buf = NULL;
+  if (GreyScale) {
+    if ((buf = SelectBuffer (bufname, ANYBUFFER, TRUE)) == NULL) return (FALSE);
+    CreateBuffer (buf, 100, 200, -32, 0.0, 1.0);
+  }
+
+  /* load image(s) in time range given */
+  if (!ohana_str_to_time (argv[1], &tzero)) { 
+    gprint (GP_ERR, "syntax error\n");
+    return (FALSE);
+  }
+  if (!ohana_str_to_dtime (argv[2], &trange)) { 
+    gprint (GP_ERR, "syntax error\n");
+    return (FALSE);
+  }    
+  gprint (GP_ERR, "searching in range %ds - %ds (%f seconds)\n", (int)tzero, (int)(tzero + trange), trange);
+  
+  if ((image = LoadImagesDVO (&Nimage)) == NULL) return (FALSE);
+  image_subset (image, Nimage, &subset, &Nsubset, selection, tzero, trange, TRUE);
+
+  if ((Nsubset > 1) && GreyScale) {
+    gprint (GP_ERR, "more than one image selected, making GreyScale of first only\n");
+  }
+
+  if (GreyScale && Nsubset) {
+    // double fx = image[subset[0]].NX / 100;
+    // double fy = image[subset[0]].NY / 200;
+    p = (float *) buf[0].matrix.buffer;
+    for (y = 0; y < 200; y+=1.0) {
+      for (x = 0; x < 100; x+=1.0, p++) {
+	// *p = applyMcal (&image[subset[0]], (fx*x), (fy*y));
+	*p = image[subset[0]].Mcal;
+      }
+    }
+  }
+
+  for (j = 0; j < Nsubset; j++) {
+    i = subset[j];
+    gprint (GP_ERR, "%s: %f\n", image[i].name, image[i].Mcal);
+
+// XXX old code when we had the option of a 2D zero point model
+# if (0)      
+    switch (image[i].order) {
+    case 0:
+      gprint (GP_ERR, "%s: %d - %f\n", image[i].name, image[i].order, image[i].Mcal);
+      break;
+    case 1:
+      gprint (GP_ERR, "%s: %d - %f, %d %d\n", image[i].name, image[i].order, image[i].Mcal, image[i].Mx, image[i].My);
+      break;
+    case 2:
+      gprint (GP_ERR, "%s: %d - %f, %d %d, %d %d %d\n", image[i].name, image[i].order, image[i].Mcal, image[i].Mx, image[i].My, image[i].Mxx, image[i].Mxy, image[i].Myy);
+      break;
+    case 3:
+      gprint (GP_ERR, "%s: %d - %f, %d %d, %d %d %d, %d %d %d %d\n", image[i].name, image[i].order, image[i].Mcal, image[i].Mx, image[i].My, 
+	       image[i].Mxx, image[i].Mxy, image[i].Myy, image[i].Mxxx, image[i].Mxxy, image[i].Mxyy, image[i].Myyy);
+      break;
+    case 4:
+      gprint (GP_ERR, "%s: %d - %f, %d %d, %d %d %d, %d %d %d %d, %d %d %d %d %d\n", image[i].name, image[i].order, image[i].Mcal, image[i].Mx, image[i].My, 
+	       image[i].Mxx, image[i].Mxy, image[i].Myy, image[i].Mxxx, image[i].Mxxy, image[i].Mxyy, image[i].Myyy,
+	       image[i].Mxxxx, image[i].Mxxxy, image[i].Mxxyy, image[i].Mxyyy, image[i].Myyyy);
+      break;
+    }
+# endif
+  }
+
+  FreeImagesDVO(image);
+  free (subset);
+  return (TRUE);
+}
+
Index: /branches/sc_branches/pantasks_condor/dvo/imrough.c
===================================================================
--- /branches/sc_branches/pantasks_condor/dvo/imrough.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/dvo/imrough.c	(revision 34783)
@@ -0,0 +1,314 @@
+# include "dvoshell.h"
+RegImage *load_imreg (char *DataBase, off_t *nimage);
+
+# define NVALUE 22
+enum {V_NONE, V_EXPTIME, V_CCDN, V_SKY, V_BIAS, V_FILTER, V_FWHM, V_AIRM, V_TIME, V_TEMP0, V_TEMP1, V_TEMP2, V_TEMP3, V_TELFOCUS, V_XPROBE, V_YPROBE, V_ZPROBE, V_RA, V_DEC, V_DETTEMP, V_ROTANGLE, V_REGTIME};
+static char valuename[NVALUE][32] = {"none", "exptime", "ccd", "sky", "bias", "filter", "fwhm", "airmass", "time", "temp0", "temp1", "temp2", "temp3", "telfocus", "xprobe", "yprobe", "zprobe", "ra", "dec", "dettemp", "rotangle", "regtime"};
+
+int imrough (int argc, char **argv) {
+ 
+  off_t i, Nimage;
+  int N, TimeSelect;
+  int ModeSelect, TypeSelect, CCDSelect, FilterSelect, TimeFormat;
+  int type, value, mode, CCD, NVEC;
+  char DataBase[256], *Filter;
+  double trange;
+  opihi_flt *Vec;
+  time_t tzero, tend, TimeReference;
+  RegImage *image;
+  Vector *vec;
+
+  // XXX this function is only valid for the Elixir db system, not the IPP db system
+  // this function should exit gracefully if the REGISTRATION_DATABASE is missing
+  VarConfig ("REGISTRATION_DATABASE", "%s", DataBase);
+
+  TimeSelect = FALSE;
+  if ((N = get_argument (argc, argv, "-time"))) {
+    remove_argument (N, &argc, argv);
+    if (!ohana_str_to_time (argv[N], &tzero)) { 
+      gprint (GP_ERR, "syntax error\n");
+      return (FALSE);
+    }
+    remove_argument (N, &argc, argv);
+    if (!ohana_str_to_dtime (argv[N], &trange)) { 
+      gprint (GP_ERR, "syntax error\n");
+      return (FALSE);
+    }
+    remove_argument (N, &argc, argv);
+    if (trange < 0) {
+      trange = fabs (trange);
+      tzero -= trange;
+    }
+    TimeSelect = TRUE;
+  }
+  if ((N = get_argument (argc, argv, "-trange"))) {
+    remove_argument (N, &argc, argv);
+    if (!ohana_str_to_time (argv[N], &tzero)) { 
+      gprint (GP_ERR, "syntax error\n");
+      return (FALSE);
+    }
+    remove_argument (N, &argc, argv);
+    if (!ohana_str_to_time (argv[N], &tend)) { 
+      gprint (GP_ERR, "syntax error\n");
+      return (FALSE);
+    }
+    remove_argument (N, &argc, argv);
+    trange = tend - tzero;
+    if (trange < 0) {
+      trange = fabs (trange);
+      tzero -= trange;
+    }
+    TimeSelect = TRUE;
+  }
+ 
+  type = T_UNDEF;
+  TypeSelect = FALSE;
+  if ((N = get_argument (argc, argv, "-type"))) {
+    TypeSelect = TRUE;
+    remove_argument (N, &argc, argv);
+    type = get_image_type (argv[N]);
+    if (type == T_UNDEF) {
+      gprint (GP_ERR, "ERROR: invalid image type %s\n", argv[N]);
+      return (FALSE);
+    }
+    remove_argument (N, &argc, argv);
+  }
+
+  mode = M_NONE;
+  ModeSelect = FALSE;
+  if ((N = get_argument (argc, argv, "-mode"))) {
+    ModeSelect = TRUE;
+    remove_argument (N, &argc, argv);
+    mode = get_image_mode (argv[N]);
+    if (mode == M_UNDEF) {
+      gprint (GP_ERR, "ERROR: invalid image mode %s\n", argv[N]);
+      return (FALSE);
+    }
+    remove_argument (N, &argc, argv);
+  }
+
+  CCD = 0;
+  CCDSelect = FALSE;
+  if ((N = get_argument (argc, argv, "-ccd"))) {
+    remove_argument (N, &argc, argv);
+    CCD = atoi (argv[N]);
+    remove_argument (N, &argc, argv);
+    CCDSelect = TRUE;
+  }
+ 
+  Filter = NULL;
+  FilterSelect = FALSE;
+  if ((N = get_argument (argc, argv, "-filter"))) {
+    remove_argument (N, &argc, argv);
+    Filter = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+    FilterSelect = TRUE;
+    if (!strcasecmp (Filter, "X")) {
+      FilterSelect = FALSE;
+    }
+  }
+
+  if (argc != 2) {
+    gprint (GP_ERR, "USAGE: imrough (value)\n");
+    gprint (GP_ERR, "       value options:\n");
+    for (i = 1; i < NVALUE; i++) {
+      gprint (GP_ERR, "%s\n", valuename[i]);
+    }
+    return (FALSE);
+  }
+ 
+  /* identify selection */
+  value = V_NONE;
+  for (i = 0; (i < NVALUE) && (value == V_NONE); i++) {
+    if (!strncasecmp (argv[1], valuename[i], strlen(argv[1]))) value = i;
+  }
+  if (value == V_NONE) {
+    gprint (GP_ERR, "ERROR: invalid image value %s\n", argv[1]);
+    return (FALSE);
+  }
+
+  if ((vec = SelectVector (argv[1], ANYVECTOR, TRUE)) == NULL) return (FALSE);
+
+  image = load_imreg (DataBase, &Nimage);
+  if (image == (RegImage *) NULL) return (FALSE);
+
+  N = 0;
+  NVEC = 1000;
+  ResetVector (vec, OPIHI_FLT, NVEC);
+  Vec = vec[0].elements.Flt;
+
+  GetTimeFormat (&TimeReference, &TimeFormat);
+
+  /* get data */
+  for (i = 0; i < Nimage; i++) {
+    /* skip unmatched selections */
+    if (TimeSelect && ((image[i].obstime < tzero) || (image[i].obstime > tzero + trange))) continue;
+    if (TimeSelect && ((image[i].obstime < tzero) || (image[i].obstime > tzero + trange))) continue;
+    if (FilterSelect && (strcasecmp (image[i].filter, Filter))) continue;
+    if (CCDSelect && (image[i].ccd != CCD)) continue;
+    if (TypeSelect && (image[i].type != type)) continue;
+    if (ModeSelect && (image[i].mode != mode)) continue;
+
+    /* assign correct value */
+    switch (value) {
+    case (V_EXPTIME):
+      Vec[N] = image[i].exptime;
+      break;
+    case (V_CCDN):
+      Vec[N] = image[i].ccd;
+      break;
+    case (V_SKY):
+      Vec[N] = image[i].sky;
+      break;
+    case (V_BIAS):
+      Vec[N] = image[i].bias;
+      break;
+    case (V_FILTER):
+      Vec[N] = image[i].filter[0];
+      break;
+    case (V_FWHM):
+      Vec[N] = image[i].fwhm;
+      break;
+    case (V_AIRM):
+      Vec[N] = image[i].airmass;
+      break;
+    case (V_TIME):
+      Vec[N] = TimeValue (image[i].obstime, TimeReference, TimeFormat);
+      break;
+    case (V_TEMP0):
+      Vec[N] = image[i].teltemp_0;
+      break;
+    case (V_TEMP1):
+      Vec[N] = image[i].teltemp_1;
+      break;
+    case (V_TEMP2):
+      Vec[N] = image[i].teltemp_2;
+      break;
+    case (V_TEMP3):
+      Vec[N] = image[i].teltemp_3;
+      break;
+    case (V_TELFOCUS):
+      Vec[N] = image[i].telfocus;
+      break;
+    case (V_XPROBE):
+      Vec[N] = image[i].xprobe;
+      break;
+    case (V_YPROBE):
+      Vec[N] = image[i].yprobe;
+      break;
+    case (V_ZPROBE):
+      Vec[N] = image[i].zprobe;
+      break;
+    case (V_RA):
+      Vec[N] = image[i].ra;
+      break;
+    case (V_DEC):
+      Vec[N] = image[i].dec;
+      break;
+    case (V_DETTEMP):
+      Vec[N] = image[i].dettemp;
+      break;
+    case (V_ROTANGLE):
+      Vec[N] = image[i].rotangle;
+      break;
+    case (V_REGTIME):
+      Vec[N] = TimeValue (image[i].regtime, TimeReference, TimeFormat);
+      break;
+    }
+    N++;
+    if (N >= NVEC - 1) {
+      NVEC += 1000;
+      REALLOCATE (vec[0].elements.Flt, opihi_flt, NVEC);
+      Vec = vec[0].elements.Flt;
+    }
+  }
+
+  REALLOCATE (vec[0].elements.Flt, opihi_flt, MAX (1,N));
+  vec[0].Nelements = N;
+
+  free (image);
+  return (TRUE);
+
+}
+
+RegImage *load_imreg (char *DataBase, off_t *nimage) {
+
+  off_t Nimage;
+  int status;
+  char line[80];
+  FILE *f;
+  Header header, theader;
+  Matrix matrix;
+  FTable table;
+  RegImage *image;
+
+  *nimage = 0;
+
+  /* open database */
+  f = fopen (DataBase, "r");
+  if (f == (FILE *) NULL) {
+    gprint (GP_ERR, "ERROR: can't open Registration Database\n");
+    return ((RegImage *) NULL);
+  }
+
+  /* load in database header */
+  if (!gfits_fread_header (f, &header)) {
+    fclose (f);
+    gfits_free_header (&header);
+    gprint (GP_ERR, "ERROR: trouble reading database header\n");
+    return ((RegImage *) NULL);
+  }
+
+  /* check for database v1, v2 */
+  gfits_scan (&header, "ORIGIN", "%s", 1, line);
+  if (!strcmp (line, "MDM Observatory")) {
+
+    fseeko (f, header.datasize, SEEK_SET);
+    
+    /* load existing data from database */
+    gfits_scan (&header, "NIMAGES", OFF_T_FMT, 1,  &Nimage);
+    ALLOCATE (image, RegImage, Nimage);
+    status = fread (image, sizeof(RegImage), Nimage, f);
+    fclose (f);
+    
+    if (status != Nimage) {
+      gprint (GP_ERR, "ERROR: header and data in dB don't match ("OFF_T_FMT" vs %d)\n",  Nimage, status);
+      gfits_free_header (&header);
+      free (image);
+      return ((RegImage *) NULL);
+    }
+    gfits_convert_RegImage (image, sizeof (RegImage), Nimage);
+
+    *nimage = Nimage;
+    return (image);
+  }
+
+  /* we probably have v3 */
+  if (!gfits_fread_matrix (f, &matrix, &header)) {
+    fclose (f);
+    gfits_free_header (&header);
+    gfits_free_matrix (&matrix);
+    gprint (GP_ERR, "ERROR: trouble reading database matrix\n");
+    return ((RegImage *) NULL);
+  }
+
+  table.header = &theader;
+  if (!gfits_fread_ftable  (f, &table, "IMAGE_DATABASE")) {
+    fclose (f);
+    gfits_free_header (&header);
+    gfits_free_matrix (&matrix);
+    gprint (GP_ERR, "ERROR: trouble reading database table\n");
+    return ((RegImage *) NULL);
+  }
+
+  /* convert to internal format */
+  image = (RegImage *) table.buffer;
+  gfits_scan (table.header, "NAXIS2", OFF_T_FMT, 1,  &Nimage);
+  gfits_convert_RegImage (image, sizeof (RegImage), Nimage);
+
+  gfits_free_header (&header);
+  gfits_free_matrix (&matrix);
+
+  *nimage = Nimage;
+  return (image);
+}
Index: /branches/sc_branches/pantasks_condor/dvo/imsearch.c
===================================================================
--- /branches/sc_branches/pantasks_condor/dvo/imsearch.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/dvo/imsearch.c	(revision 34783)
@@ -0,0 +1,155 @@
+# include "dvoshell.h"
+
+int imsearch (int argc, char **argv) {
+ 
+  char DataBase[256], name[64];
+  FILE *f;
+  Header header;
+  RegImage *pimage;
+  off_t i, Nimage, status;
+  int N, TimeSelect, SaveNames;
+  int ModeSelect, TypeSelect, CCDSelect, FilterSelect;
+  char *Filter, *obstime;
+  int Type, Mode, CCD;
+  time_t tzero, obstime_sec;
+  double trange;
+   
+  VarConfig ("REGISTRATION_DATABASE", "%s", DataBase);
+
+  SaveNames = FALSE;
+  if ((N = get_argument (argc, argv, "-save"))) {
+    remove_argument (N, &argc, argv);
+    SaveNames = TRUE;
+  }
+ 
+  TimeSelect = FALSE;
+  if ((N = get_argument (argc, argv, "-time"))) {
+    remove_argument (N, &argc, argv);
+    if (!ohana_str_to_time (argv[N], &tzero)) { 
+      gprint (GP_ERR, "syntax error\n");
+      return (FALSE);
+    }
+    remove_argument (N, &argc, argv);
+    if (!ohana_str_to_dtime (argv[N], &trange)) { 
+      gprint (GP_ERR, "syntax error\n");
+      return (FALSE);
+    }
+    remove_argument (N, &argc, argv);
+    TimeSelect = TRUE;
+  }
+ 
+  Type = 0;
+  TypeSelect = FALSE;
+  if ((N = get_argument (argc, argv, "-type"))) {
+    remove_argument (N, &argc, argv);
+    Type = get_image_type (argv[N]);
+    if (Type == T_UNDEF) {
+      gprint (GP_ERR, "ERROR: invalid image type %s\n", argv[N]);
+      return (FALSE);
+    }
+    remove_argument (N, &argc, argv);
+    TypeSelect = TRUE;
+  }
+ 
+  Mode = 0;
+  ModeSelect = FALSE;
+  if ((N = get_argument (argc, argv, "-mode"))) {
+    remove_argument (N, &argc, argv);
+    Mode = get_image_mode (argv[N]);
+    if (Mode == M_UNDEF) {
+      gprint (GP_ERR, "ERROR: invalid image mode %s\n", argv[N]);
+      exit (1);
+    }
+    remove_argument (N, &argc, argv);
+    ModeSelect = TRUE;
+  }
+ 
+  CCD = 0;
+  CCDSelect = FALSE;
+  if ((N = get_argument (argc, argv, "-ccd"))) {
+    remove_argument (N, &argc, argv);
+    CCD = atoi (argv[N]);
+    remove_argument (N, &argc, argv);
+    CCDSelect = TRUE;
+  }
+ 
+  Filter = NULL;
+  FilterSelect = FALSE;
+  if ((N = get_argument (argc, argv, "-filter"))) {
+    remove_argument (N, &argc, argv);
+    Filter = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+    FilterSelect = TRUE;
+    if (!strcasecmp (Filter, "X")) {
+      FilterSelect = FALSE;
+    }
+  }
+
+  if (argc != 1) {
+    gprint (GP_ERR, "USAGE: imsearch [-time start range] [-type type] [-mode mode] [-ccd N] [-filter name]\n");
+    exit (1);
+  }
+
+  /* load in database header */
+  if (!gfits_read_header (DataBase, &header)) {
+    gprint (GP_ERR, "ERROR: trouble reading database header\n");
+    return (FALSE);
+  }
+
+  /* open database */
+  f = fopen (DataBase, "r");
+  if (f == (FILE *) NULL) {
+    gprint (GP_ERR, "ERROR: can't open Registration Database\n");
+    return (FALSE);
+  }
+  fseeko (f, header.datasize, SEEK_SET);
+
+  /* load existing data from database */
+  gfits_scan (&header, "NIMAGES", OFF_T_FMT, 1,  &Nimage);
+  ALLOCATE (pimage, RegImage, Nimage);
+  status = fread (pimage, sizeof(RegImage), Nimage, f);
+  fclose (f);
+  if (status != Nimage) {
+    gprint (GP_ERR, "ERROR: header and data in dB don't match ("OFF_T_FMT" vs "OFF_T_FMT")\n",  Nimage,  status);
+    gfits_free_header (&header);
+    free (pimage);
+    return (FALSE);
+  }
+  gfits_convert_RegImage (pimage, sizeof (RegImage), Nimage);
+
+  /* print out all data */
+  N = 0;
+  for (i = 0; i < Nimage; i++) {
+    if (TimeSelect && ((pimage[i].obstime < tzero) || (pimage[i].obstime > tzero + trange))) continue;
+    if (FilterSelect && (strcasecmp (pimage[i].filter, Filter))) continue;
+    if (ModeSelect && (pimage[i].mode != Mode)) continue;
+    if (CCDSelect && (pimage[i].ccd != CCD)) continue;
+    if (TypeSelect && (pimage[i].type != Type)) continue;
+
+    obstime_sec = (time_t) pimage[i].obstime;
+    obstime = ctime (&obstime_sec);
+    obstime[strlen(obstime)-1] = 0;
+
+    gprint (GP_LOG, OFF_T_FMT" %6s %6s %2d %2d   ",  i, get_type_name(pimage[i].type), get_mode_name(pimage[i].mode), pimage[i].ccd, pimage[i].type);
+    gprint (GP_LOG, "%s %s  ", pimage[i].pathname, pimage[i].filename);
+    gprint (GP_LOG, "%s %s %f %s\n", pimage[i].filter, pimage[i].instrument, pimage[i].exptime, obstime);
+
+    if (SaveNames) {
+      sprintf (name, "IMAGEpath:%d", N);
+      set_str_variable (name, pimage[i].pathname);
+      sprintf (name, "IMAGEfile:%d", N);
+      set_str_variable (name, pimage[i].filename);
+      sprintf (name, "IMAGEmode:%d", N);
+      set_int_variable (name, pimage[i].mode);
+    }
+    N++;
+  }
+  if (SaveNames) {
+    set_int_variable ("IMAGEpath:n", N);
+    set_int_variable ("IMAGEfile:n", N);
+  }
+
+  free (pimage);
+  gfits_free_header (&header);
+  return (TRUE);
+}
Index: /branches/sc_branches/pantasks_condor/dvo/imstats.c
===================================================================
--- /branches/sc_branches/pantasks_condor/dvo/imstats.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/dvo/imstats.c	(revision 34783)
@@ -0,0 +1,63 @@
+# include "dvoshell.h"
+
+int imstats (int argc, char **argv) {
+  
+  off_t i, Nimage;
+  int kapa, N;
+  int Mcal, AutoLimits;
+  double r, d;
+  Image *image;
+  Vector Xvec, Yvec;  
+  Graphdata graphmode;
+
+  if (!style_args (&graphmode, &argc, argv, &kapa)) return (FALSE);
+
+  Mcal = TRUE;
+  if ((N = get_argument (argc, argv, "-dM"))) {
+    remove_argument (N, &argc, argv);
+    Mcal = FALSE;
+  }
+
+  AutoLimits = FALSE;
+  if ((N = get_argument (argc, argv, "-l"))) {
+    remove_argument (N, &argc, argv);
+    AutoLimits = TRUE;
+  }
+
+  if (argc != 1) {
+    gprint (GP_ERR, "USAGE: imstats [-dM] [-l]\n");
+    return (FALSE);
+  }
+
+  if ((image = LoadImagesDVO (&Nimage)) == NULL) return (FALSE);
+  BuildChipMatch (image, Nimage);
+
+  /* assign vector values */
+  SetVector (&Xvec, OPIHI_FLT, Nimage);
+  SetVector (&Yvec, OPIHI_FLT, Nimage);
+
+  gprint (GP_LOG, "seq  ra (J2000) dec    time (s)   Nstars\n");
+  for (i = 0; i < Nimage; i++) {
+    Xvec.elements.Flt[i] = image[i].secz;
+    if (Mcal) 
+      Yvec.elements.Flt[i] = image[i].Mcal;
+    else 
+      Yvec.elements.Flt[i] = image[i].dMcal;
+    if (!FindMosaicForImage (image, Nimage, i)) continue;
+    XY_to_RD (&r, &d, 0.5*image[i].NX, 0.5*image[i].NY, &image[i].coords);
+    gprint (GP_ERR, "%d %8.4f %8.4f %10d %6d  %5.3f %6.3f %6.3f\n", 
+	     i, r, d, image[i].tzero, image[i].nstar, Xvec.elements.Flt[i], 
+	     image[i].Mcal, image[i].dMcal);
+  } 
+  if (AutoLimits) SetLimits (&Xvec, &Yvec, &graphmode);
+
+  graphmode.style = 2;
+  graphmode.etype = 0;
+  PlotVectorPair (kapa, &Xvec, &Yvec, NULL, &graphmode);
+  
+  free (Xvec.elements.Flt);
+  free (Yvec.elements.Flt);
+  FreeImagesDVO (image);
+  return (TRUE);
+}
+
Index: /branches/sc_branches/pantasks_condor/dvo/init.c
===================================================================
--- /branches/sc_branches/pantasks_condor/dvo/init.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/dvo/init.c	(revision 34783)
@@ -0,0 +1,132 @@
+# include "dvoshell.h"
+
+int avextract       PROTO((int, char **));
+int avmatch         PROTO((int, char **));
+int badimages       PROTO((int, char **));
+int calextract      PROTO((int, char **));
+int calmextract     PROTO((int, char **));
+int catlog          PROTO((int, char **));
+int catdir_define   PROTO((int, char **));
+int ccd             PROTO((int, char **));
+int cmatch          PROTO((int, char **));
+int cmd             PROTO((int, char **));
+int cmpload         PROTO((int, char **));
+int cmpread         PROTO((int, char **));
+int ddmags          PROTO((int, char **));
+int detrend         PROTO((int, char **));
+int dmagaves        PROTO((int, char **));
+int dmagmeas        PROTO((int, char **));
+int dmags           PROTO((int, char **));
+int dmt             PROTO((int, char **));
+int elixir          PROTO((int, char **));
+int fitcolors       PROTO((int, char **));
+int fitsed          PROTO((int, char **));
+int gcat            PROTO((int, char **));
+int catlist         PROTO((int, char **));
+int getxtra         PROTO((int, char **));
+int gimages         PROTO((int, char **));
+int gstar           PROTO((int, char **));
+int gtypes          PROTO((int, char **));
+int hosts           PROTO((int, char **));
+int images          PROTO((int, char **));
+int imbox           PROTO((int, char **));
+int imdata          PROTO((int, char **));
+int imdense         PROTO((int, char **));
+int imextract       PROTO((int, char **));
+int imlist          PROTO((int, char **));
+int imphot          PROTO((int, char **));
+int imrough         PROTO((int, char **));
+int imsearch        PROTO((int, char **));
+int lcat            PROTO((int, char **));
+int lcurve          PROTO((int, char **));
+int lightcurve      PROTO((int, char **));
+int mextract        PROTO((int, char **));
+int mmatch          PROTO((int, char **));
+int mmextract       PROTO((int, char **));
+int objectcoverage  PROTO((int, char **));
+int pcat            PROTO((int, char **));
+int photcodes       PROTO((int, char **));
+int pmeasure        PROTO((int, char **));
+int paverage        PROTO((int, char **));
+int procks          PROTO((int, char **));
+int remote          PROTO((int, char **));
+int showtile        PROTO((int, char **));
+int skycat          PROTO((int, char **));
+int skycoverage     PROTO((int, char **));
+int skyregion       PROTO((int, char **));
+int simage          PROTO((int, char **));
+int subpix          PROTO((int, char **));
+int version         PROTO((int, char **));
+
+static Command cmds[] = {  
+  {1, "avextract",   avextract,    "extract average data values"},
+  {1, "avmatch",     avmatch,      "extract average data values matched to RA,DEC points"},
+  {1, "badimages",   badimages,    "look for images with anomalous astrometry"},
+  {1, "calextract",  calextract,   "extract photometry calibration"},
+  {1, "calmextract", calmextract,  "extract photometry calibration"},
+  {1, "catdir",      catdir_define,"re-define CATDIR"},
+  {1, "ccd",         ccd,          "plot color-color diagram"},
+  {1, "cmatch",      cmatch,       "match two catalogs"},
+  {1, "cmd",         cmd,          "plot cmd of stars in current region"},
+  {1, "cmpload",     cmpload,      "load cmp file into ?"},
+  {1, "cmpread",     cmpread,      "read data from cmp format files"},
+  {1, "ddmags",      ddmags,       "plot magnitude differences"},
+  {1, "detrend",     detrend,      "extract from detrend database?"},
+  {1, "dmagaves",    dmagaves,     "foo"},
+  {1, "dmagmeas",    dmagmeas,     "foo"},
+  {1, "dmags",       dmags,        "plot differential magnitudes between filters"},
+  {1, "dmt",         dmt,          "plot mag scatter"},
+  {1, "elixir",      elixir,       "talk to elixir"},
+  {1, "fitcolors",   fitcolors,    "fit chip-to-chip color terms"},
+  {1, "fitsed",      fitsed,       "fit stellar SEDs to objects"},
+  {1, "gcat",        gcat,         "get catalog at location"},
+  {1, "catlist",     catlist,      "get list of catalogs for region / host"},
+  {1, "gimages",     gimages,      "get images at location"},
+  {1, "gstar",       gstar,        "get star statistics"},
+  {1, "hosts",       hosts,        "remote host support functions"},
+  {1, "images",      images,       "plot image boxes"},
+  {1, "imbox",       imbox,        "plot expected image box"},
+  {1, "imdata",      imdata,       "extract data for specific images"},
+  {1, "imdense",     imdense,      "image density plot"},
+  {1, "imextract",   imextract,    "extract vectors from catalogs"},
+  {1, "imlist",      imlist,       "list image info"},
+  {1, "imphot",      imphot,       "image photometry info"},
+  {1, "imrough",     imrough,      "get info from imruf database"},
+  {1, "imsearch",    imsearch,     "get info from imreg database"},
+  {1, "lcat",        lcat,         "list catalogs in region"},
+  {1, "lcurve",      lcurve,       "plot lightcurve for a star"},
+  {1, "lightcurve",  lightcurve,   "extract lightcurve for a star"},
+  {1, "mextract",    mextract,     "extract measure data values"},
+  {1, "mmatch",      mmatch,       "extract measure data values matched to RA,DEC points"},
+  {1, "mmextract",   mmextract,    "extract joined measurements"},
+  {1, "objectcoverage", objectcoverage, "plot catalog boundaries"},
+  {1, "pcat",        skycat,       "plot catalog boundaries"},
+  {1, "photcodes",   photcodes,    "list photometry codes"},
+  {1, "pmeasure",    pmeasure,     "plot individual measurements"},
+  {1, "paverage",    paverage,     "plot average magnitude"},
+  {1, "procks",      procks,       "plot rocks"},
+  {1, "remote",      remote,       "generic remote dvo client operation"},
+  {1, "showtile",    showtile,     "plot tile pattern"},
+  {1, "skycat",      skycat,       "show sky catalog boundaries"},
+  {1, "skycoverage", skycoverage,  "measure image union on sky"},
+  {1, "skyregion",   skyregion,    "set sky region for db queries"},
+  {1, "simage",      simage,       "plot stars in an image"},
+  {1, "subpix",      subpix,       "get subpixel positions"},
+  {1, "version",     version,      "show version information"},
+//{1, "addxtra",     addxtra,      "add extra data to object"},
+//{1, "getxtra",     getxtra,      "get extra data from object"},
+}; 
+
+/* move to astro */
+
+void InitDVO () {
+  
+  int i;
+
+  for (i = 0; i < sizeof (cmds) / sizeof (Command); i++) {
+    AddCommand (&cmds[i]);
+  }
+}
+
+void FreeDVO () {
+}
Index: /branches/sc_branches/pantasks_condor/dvo/lcat.c
===================================================================
--- /branches/sc_branches/pantasks_condor/dvo/lcat.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/dvo/lcat.c	(revision 34783)
@@ -0,0 +1,46 @@
+# include "dvoshell.h"
+
+int lcat (int argc, char **argv) {
+  
+  double Radius;
+  int i, N, ShowAll;
+  char exists;
+  struct stat filestat;
+  Graphdata graphmode;
+  SkyTable *sky;
+  SkyList *skylist;
+
+  if (!GetGraphdata (&graphmode, NULL, NULL)) return (FALSE);
+
+  ShowAll = FALSE;
+  if ((N = get_argument (argc, argv, "-all"))) {
+    remove_argument (N, &argc, argv);
+    ShowAll = TRUE;
+  }
+  if (argc != 1) {
+    gprint (GP_ERR, "USAGE: lcat [-all]\n");
+    return (FALSE);
+  }
+
+  Radius = MAX (fabs(graphmode.xmax), fabs(graphmode.ymax));
+
+  /* load sky from correct table */
+  sky = GetSkyTable ();
+  skylist = SkyListByRadius (sky, -1, graphmode.coords.crval1, graphmode.coords.crval2, Radius);
+
+  for (i = 0; i < skylist[0].Nregions; i++) {
+    exists = 'Y';
+    if (stat (skylist[0].filename[i], &filestat) == -1) exists = 'N';
+    if (ShowAll) {
+      gprint (GP_ERR, "%3d %s  %c\n", i, skylist[0].regions[i][0].name, exists);
+    } else {
+      if (exists == 'Y') {
+	gprint (GP_ERR, "%3d %s\n", i, skylist[0].regions[i][0].name);
+      }
+    }
+  }
+
+  SkyListFree (skylist);
+  return (TRUE);
+}
+
Index: /branches/sc_branches/pantasks_condor/dvo/lcurve.c
===================================================================
--- /branches/sc_branches/pantasks_condor/dvo/lcurve.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/dvo/lcurve.c	(revision 34783)
@@ -0,0 +1,195 @@
+# include "dvoshell.h"
+
+int lcurve (int argc, char **argv) {
+  
+  char string[128], *p;
+  double Ra, Dec, Radius, Radius2, r;
+  double *RA, *DEC;
+  int kapa, TimeFormat;
+  int found, AutoLimits, ErrorBars, SaveVectors;
+  off_t i, j, m, Nstars, *N1;
+  int N, NPTS;
+  time_t TimeReference;
+  struct tm *timeptr;
+  Vector *xvec, *yvec;
+  Vector Xvec, Yvec, dYvec;
+  Catalog catalog;
+  Graphdata graphmode;
+  SkyTable *sky;
+  SkyList *skylist;
+
+  if (!InitPhotcodes ()) return (FALSE);
+  if (!style_args (&graphmode, &argc, argv, &kapa)) return (FALSE);
+
+  AutoLimits = FALSE;
+  if ((N = get_argument (argc, argv, "-l"))) {
+    remove_argument (N, &argc, argv);
+    AutoLimits = TRUE;
+  }
+
+  xvec = yvec = NULL;
+  SaveVectors = FALSE;
+  if ((N = get_argument (argc, argv, "-v"))) {
+    remove_argument (N, &argc, argv);
+    SaveVectors = TRUE;
+    if ((xvec = SelectVector (argv[N], ANYVECTOR, TRUE)) == NULL) return (FALSE);
+    remove_argument (N, &argc, argv);
+    if ((yvec = SelectVector (argv[N], ANYVECTOR, TRUE)) == NULL) return (FALSE);
+    remove_argument (N, &argc, argv);
+  }
+
+  // int AbsPhot = FALSE;
+  // if ((N = get_argument (argc, argv, "-abs"))) {
+  //   remove_argument (N, &argc, argv);
+  //   AbsPhot = TRUE;
+  // }
+
+  // int GalMag = FALSE;
+  // if ((N = get_argument (argc, argv, "-gal"))) {
+  //   gprint (GP_ERR, "galaxy magnitudes currently disabled\n");
+  //   return (FALSE);
+  // }
+
+  ErrorBars = FALSE;
+  if ((N = get_argument (argc, argv, "-d"))) {
+    remove_argument (N, &argc, argv);
+    ErrorBars = TRUE;
+  }
+
+  if (argc < 4) {
+    gprint (GP_ERR, "USAGE: lcurve RA DEC Radius\n");
+    return (FALSE);
+  }
+  
+  Ra = atof (argv[1]);
+  Dec = atof (argv[2]);
+  Radius = atof (argv[3]);
+
+  /* load sky from correct table */
+  sky = GetSkyTable ();
+  skylist = SkyListByRadius (sky, -1, Ra, Dec, Radius);
+
+  if (skylist[0].Nregions > 1) {
+    gprint (GP_ERR, "warning, radius overlaps region boundary, not yet implemented\n");
+  }
+
+  /* set filename, read in header */
+  catalog.filename = skylist[0].filename[0];
+  catalog.catflags = LOAD_AVES | LOAD_MEAS;
+  catalog.Nsecfilt = 0;
+
+  // an error exit status here is a significant error
+  if (!dvo_catalog_open (&catalog, NULL, FALSE, "r")) {
+      fprintf (stderr, "ERROR: failure to open catalog file %s\n", catalog.filename);
+      exit (2);
+  }
+  dvo_catalog_unlock (&catalog);
+
+  Nstars = catalog.Naverage;
+  ALLOCATE (RA, double, Nstars);
+  ALLOCATE (DEC, double, Nstars);
+  ALLOCATE (N1, off_t, Nstars);
+
+  /* find star(s) in RA, DEC list -- use a dumb algorithm for now, improve later */
+  /* stars are not guaranteed to be sorted in RA or in DEC, so first sort the list */
+  for (i = 0; i < Nstars; i++) {
+    RA[i] = catalog.average[i].R;
+    DEC[i] = catalog.average[i].D;
+    N1[i] = i;
+  }
+  /* sort list by DEC */
+  if (Nstars > 1) sort_coords_index (DEC, RA, N1, Nstars);
+  /* at this point, RA, DEC, and N1 are sorted by DEC.  
+     catalog.average[N1[i]].R = RA[i] */
+
+  N = 0;
+  NPTS = 100;
+  SetVector (&Xvec, OPIHI_FLT, NPTS);
+  SetVector (&Yvec, OPIHI_FLT, NPTS);
+  dYvec.elements.Flt = NULL;
+  if (ErrorBars) {   
+    SetVector (&dYvec, OPIHI_FLT, NPTS);
+  }
+
+  GetTimeFormat (&TimeReference, &TimeFormat);
+
+  Radius2 = Radius*Radius;
+  found = FALSE;
+  for (i = 0; (i < catalog.Naverage) && !found; i++) {
+
+    /* this can be improved by using a couple of jumps to get within range */
+    if (Dec > DEC[i] + Radius)
+      continue;
+    
+    r = SQ(Dec - DEC[i]) + SQ(Ra - RA[i]);
+    if (r < Radius2) {
+      /* found star, extract measurements */
+      m = catalog.average[N1[i]].measureOffset;
+      for (j = 0; j < catalog.average[N1[i]].Nmeasure; j++, m++) {
+	if (ErrorBars) dYvec.elements.Flt[N] = catalog.measure[m].dM;
+	Xvec.elements.Flt[N] = TimeValue (catalog.measure[m].t, TimeReference, TimeFormat);
+	Yvec.elements.Flt[N] = PhotCat (&catalog.measure[m]);
+	/**** need to use PhotRel optionally here ****/
+	N++; 
+	if (N == NPTS) {
+	  NPTS += 100;
+	  REALLOCATE (Xvec.elements.Flt, opihi_flt, NPTS);
+	  REALLOCATE (Yvec.elements.Flt, opihi_flt, NPTS);
+	  if (ErrorBars) { REALLOCATE (dYvec.elements.Flt, opihi_flt, NPTS); }
+	}
+      }      
+    }
+  }
+  Xvec.Nelements = Yvec.Nelements = N;
+  if (ErrorBars) dYvec.Nelements = N;
+  
+  if (ErrorBars)
+    dsortthree (Xvec.elements.Flt, Yvec.elements.Flt, dYvec.elements.Flt, N);
+  else
+    dsortpair (Xvec.elements.Flt, Yvec.elements.Flt, N);
+
+  /* autoscale the plot */
+  if (AutoLimits) SetLimits (&Xvec, &Yvec, &graphmode);
+
+  if (ErrorBars) 
+    graphmode.etype = 1;  /* y errors only in lcurves */
+  else
+    graphmode.etype = 0;  
+
+  if (ErrorBars) {
+    PlotVectorPairErrors (kapa, &Xvec, &Yvec, &dYvec, &graphmode);
+  } else {
+    PlotVectorPair (kapa, &Xvec, &Yvec, NULL, &graphmode);
+  }
+
+  timeptr = gmtime ((time_t *)&TimeReference);
+
+  if ((p = get_variable ("TIMEFORMAT")) == (char *) NULL) p = strcreate ("days");
+  sprintf (string, "%s since %02d/%02d/%02d UT", p,
+	   timeptr[0].tm_year, timeptr[0].tm_mon+1, timeptr[0].tm_mday);
+  free (p);
+  KapaSendLabel (kapa, string, 0);
+
+  free (RA);
+  free (DEC);
+  free (N1);
+
+  if (SaveVectors) {
+    xvec->type = OPIHI_FLT;
+    yvec->type = OPIHI_FLT;
+    free (xvec[0].elements.Flt);
+    free (yvec[0].elements.Flt);
+    xvec[0].elements.Flt = Xvec.elements.Flt;
+    yvec[0].elements.Flt = Yvec.elements.Flt;
+    xvec[0].Nelements = yvec[0].Nelements = Xvec.Nelements;
+  } else {
+    free (Xvec.elements.Flt);
+    free (Yvec.elements.Flt);
+  }
+
+  if (ErrorBars) free (dYvec.elements.Flt);
+  dvo_catalog_free (&catalog);
+
+  SkyListFree (skylist);
+  return (TRUE);
+}
Index: /branches/sc_branches/pantasks_condor/dvo/lightcurve.c
===================================================================
--- /branches/sc_branches/pantasks_condor/dvo/lightcurve.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/dvo/lightcurve.c	(revision 34783)
@@ -0,0 +1,146 @@
+# include "dvoshell.h"
+
+int lightcurve (int argc, char **argv) {
+  
+  double Ra, Dec, Radius, Radius2, r;
+  double *RA, *DEC;
+  off_t i, j, k, m, Nstars, *N1;
+  int found, PhotCodeSelect;
+  int N, NPTS, Nsecfilt, RELPHOT, TimeFormat;
+  time_t TimeReference;
+
+  PhotCode *code;
+  Catalog catalog;
+  SkyTable *sky;
+  SkyList *skylist;
+  Vector *tvec, *mvec, *dmvec;
+
+  if (!InitPhotcodes ()) return (FALSE);
+  Nsecfilt = GetPhotcodeNsecfilt ();
+
+  if ((tvec = SelectVector ("tc", ANYVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((mvec = SelectVector ("mc", ANYVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((dmvec = SelectVector ("dmc", ANYVECTOR, TRUE)) == NULL) return (FALSE);
+
+  RELPHOT = FALSE;
+  if ((N = get_argument (argc, argv, "-rel"))) {
+    remove_argument (N, &argc, argv);
+    RELPHOT = TRUE;
+  }
+
+  code = NULL;
+  PhotCodeSelect = FALSE;
+  if ((N = get_argument (argc, argv, "-photcode"))) {
+    remove_argument (N, &argc, argv);
+    if ((code = GetPhotcodebyName (argv[N])) == NULL) {
+      gprint (GP_ERR, "ERROR: photcode not found in photcode table\n");
+      return (FALSE);
+    }
+    PhotCodeSelect = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+
+  if (argc < 4) {
+    gprint (GP_ERR, "USAGE: lightcurve RA DEC Radius\n");
+    return (FALSE);
+  }
+  
+  Ra = atof (argv[1]);
+  Dec = atof (argv[2]);
+  Radius = atof (argv[3]);
+
+  sky = GetSkyTable ();
+  skylist = SkyListByRadius (sky, -1, Ra, Dec, Radius);
+
+  if (skylist[0].Nregions > 1) {
+    gprint (GP_ERR, "warning, radius overlaps region boundary, not yet implemented\n");
+  }
+
+  /* set filename, read in header */
+  catalog.filename = skylist[0].filename[0];
+  catalog.catflags = LOAD_AVES | LOAD_MEAS | LOAD_SECF;
+  catalog.Nsecfilt = 0;
+
+  // an error exit status here is a significant error
+  if (!dvo_catalog_open (&catalog, NULL, FALSE, "r")) {
+      fprintf (stderr, "ERROR: failure to open catalog file %s\n", catalog.filename);
+      exit (2);
+  }
+  dvo_catalog_unlock (&catalog);
+
+  Nstars = catalog.Naverage;
+  ALLOCATE (RA, double, Nstars);
+  ALLOCATE (DEC, double, Nstars);
+  ALLOCATE (N1, off_t, Nstars);
+
+  /* find star(s) in RA, DEC list -- use a dumb algorithm for now, improve later */
+  /* stars are not guaranteed to be sorted in RA or in DEC, so first sort the list */
+  for (i = 0; i < Nstars; i++) {
+    RA[i] = catalog.average[i].R;
+    DEC[i] = catalog.average[i].D;
+    N1[i] = i;
+  }
+  /* sort list by DEC */
+  if (Nstars > 1) sort_coords_index (DEC, RA, N1, Nstars);
+  /* at this point, RA, DEC, and N1 are sorted by DEC.  
+     catalog.average[N1[i]].R = RA[i] */
+
+  N = 0;
+  NPTS = 100;
+  ResetVector (tvec, OPIHI_FLT, NPTS);
+  ResetVector (mvec, OPIHI_FLT, NPTS);
+  ResetVector (dmvec, OPIHI_FLT, NPTS);
+  
+  GetTimeFormat (&TimeReference, &TimeFormat);
+
+  Radius2 = Radius*Radius;
+  found = FALSE;
+  for (i = 0; (i < catalog.Naverage) && !found; i++) {
+
+    /* this can be improved by using a couple of jumps to get within range */
+    if (Dec > DEC[i] + Radius)
+      continue;
+    
+    r = SQ(Dec - DEC[i]) + SQ(Ra - RA[i]);
+    if (r < Radius2) {
+      k = N1[i];
+      /* found star, extract measurements */
+      m = catalog.average[k].measureOffset;
+      for (j = 0; j < catalog.average[k].Nmeasure; j++, m++) {
+
+	if (PhotCodeSelect) {
+	  if ((code[0].type == PHOT_REF) || (code[0].type == PHOT_DEP)) {
+	    if (code[0].code != catalog.measure[m].photcode) continue;
+	  } 
+	  if (code[0].type == PHOT_SEC) {
+	    if (code[0].code != GetPhotcodeEquivCodebyCode (catalog.measure[m].photcode)) continue;
+	  } 
+	}      
+
+	tvec[0].elements.Flt[N] = TimeValue (catalog.measure[m].t, TimeReference, TimeFormat);
+	dmvec[0].elements.Flt[N] = catalog.measure[m].dM;
+	if (RELPHOT) {
+	  mvec[0].elements.Flt[N] = PhotCat (&catalog.measure[m]);
+	} else {
+	  mvec[0].elements.Flt[N] = PhotRel (&catalog.measure[m], &catalog.average[k], &catalog.secfilt[k*Nsecfilt]);
+	}
+	N++; 
+	if (N == NPTS) {
+	  NPTS += 100;
+	  REALLOCATE (tvec[0].elements.Flt, opihi_flt, NPTS);
+	  REALLOCATE (mvec[0].elements.Flt, opihi_flt, NPTS);
+	  REALLOCATE (dmvec[0].elements.Flt, opihi_flt, NPTS);
+	}
+      }      
+    }
+  }
+  dsortthree (tvec[0].elements.Flt, mvec[0].elements.Flt, dmvec[0].elements.Flt, N);
+  tvec[0].Nelements = mvec[0].Nelements = dmvec[0].Nelements = N;
+
+  free (RA);
+  free (DEC);
+  free (N1);
+  dvo_catalog_free (&catalog);
+  SkyListFree (skylist);
+  return (TRUE);
+}
Index: /branches/sc_branches/pantasks_condor/dvo/mextract.c
===================================================================
--- /branches/sc_branches/pantasks_condor/dvo/mextract.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/dvo/mextract.c	(revision 34783)
@@ -0,0 +1,386 @@
+# include "dvoshell.h"
+
+int mextract (int argc, char **argv) {
+  
+  off_t i, j, k, m; // used for counter averages and measures
+  int n, N, Npts, NPTS, last, next, state, Nfields, Nreturn, Ncstack, Nstack;
+  int Nsecfilt, VERBOSE, loadImages, mosaicMode;
+  char **cstack, name[1024];
+  dbValue *values;
+  void *Signal;
+
+  Catalog catalog;
+  SkyList *skylist;
+  Vector **vec;
+  dbField *fields;
+  dbStack *stack;
+  SkyRegionSelection *selection;
+
+  /* defaults */
+  vec = NULL;
+  stack = NULL;
+  fields = NULL;
+  values = NULL;
+  skylist = NULL;
+  selection = NULL;
+
+  // fprintf (stderr, "start...");
+  if ((N = get_argument (argc, argv, "-h"))) goto help;
+  if ((N = get_argument (argc, argv, "--help"))) goto help;
+
+  VERBOSE = FALSE;
+  if ((N = get_argument (argc, argv, "-v"))) {
+    remove_argument (N, &argc, argv);
+    VERBOSE = TRUE;
+  }
+
+  int PARALLEL = FALSE;
+  if ((N = get_argument (argc, argv, "-parallel"))) {
+    remove_argument (N, &argc, argv);
+    PARALLEL = TRUE;
+  }
+
+  // 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;
+  }
+
+  // init here so free in 'escape' block does not crash
+  dvo_catalog_init (&catalog, TRUE);
+
+  /* load photcode information */
+  if (!InitPhotcodes ()) goto escape;
+  Nsecfilt = GetPhotcodeNsecfilt ();
+  
+  // parse skyregion options.  NOTE: this is stripped off in parallel operation and always
+  // defined for the client via the -skyregion option.  The dvo_client parses this
+  // argument in the main program, before it is passed to the command (like mextract)
+  if ((selection = SetRegionSelection (&argc, argv)) == NULL) {
+    print_error();
+    goto escape;
+  }
+
+  // init locally static variables (time refs)
+  dbExtractMeasuresInit();
+
+  // command-line is of the form: mextract field,field, field [where (field op value)...]
+
+  // parse the fields to be extracted and returned
+  fields = dbCmdlineFields (argc, argv, DVO_TABLE_MEASURE, &last, &Nfields);
+  if (fields == NULL) return (FALSE);
+  if (Nfields == 0) {
+    FreeSkyRegionSelection (selection);
+    dbFreeFields (fields, Nfields);
+    dvo_catalog_free (&catalog);
+    goto help;
+  }
+
+  // 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-next, &argv[next], &Ncstack);
+  
+  // construct the db Boolean math stack (frees cstack)
+  stack = dbRPN (Ncstack, cstack, &Nstack);
+  if (Ncstack && !Nstack) {
+    print_error ();
+    goto escape;
+  }
+
+  // add the skyregion limits to the where statement (or create)
+  dbAstroRegionLimits (&stack, &Nstack, selection, DVO_TABLE_MEASURE);
+
+  // parse stack elements into fields and scalars as needed
+  Nreturn = Nfields; 
+  if (!dbCheckStack (stack, Nstack, DVO_TABLE_MEASURE, &fields, &Nfields)) goto escape;
+  // XXX handle errors
+
+  /* load region corresponding to selection above */
+  if ((skylist = SelectRegions (selection)) == NULL) goto escape;
+
+  // this does all the work of re-packaging the command, calling it on the remote machines, then loading in the results
+  if (PARALLEL && !HOST_ID) {
+    int status = HostTableParallelOps (argc, argv, RESULT_FILE, 0, VERBOSE);
+
+    dbFreeFields (fields, Nfields);
+    dbFreeStack (stack, Nstack);
+    free (stack);
+    FreeSkyRegionSelection (selection);
+    dvo_catalog_free (&catalog);
+
+    return status;
+  }
+
+  // load image data if needed (for fields listed below)
+  loadImages = FALSE;
+  mosaicMode = FALSE;
+  for (i = 0; !loadImages && (i < Nfields); i++) {
+    if (!MEASURE_HAS_XCCD) {
+      // I'm keeping this code because it gives a way of handling dvo dbs that don't have
+      // measure.xccd if we need it
+      if (fields[i].ID == MEAS_XCCD) loadImages = TRUE;
+      if (fields[i].ID == MEAS_YCCD) loadImages = TRUE;
+    }
+    if (fields[i].ID == MEAS_XMOSAIC) 	    loadImages = mosaicMode = TRUE;
+    if (fields[i].ID == MEAS_YMOSAIC) 	    loadImages = mosaicMode = TRUE;
+    if (fields[i].ID == MEAS_EXTERN_ID)     loadImages = mosaicMode = TRUE;
+    if (fields[i].ID == MEAS_FLAT)          loadImages = mosaicMode = TRUE;
+    if (fields[i].ID == MEAS_CENTER_OFFSET) loadImages = mosaicMode = TRUE;
+  }
+  if (loadImages && !SetImageSelection (mosaicMode, selection)) goto escape;
+
+  /* create storage vector */
+  Npts = 0;
+  NPTS = 1000;
+  ALLOCATE (values, dbValue, Nfields);
+  ALLOCATE (vec, Vector *, Nreturn);
+  for (i = 0; i < Nreturn; i++) {
+    if (ISNUM(fields[i].name[0])) {
+      sprintf (name, "v_%s", fields[i].name);
+    } else {
+      sprintf (name, "%s", fields[i].name);
+    }
+    if ((vec[i] = SelectVector (name, ANYVECTOR, TRUE)) == NULL) goto escape;
+    ResetVector (vec[i], fields[i].type, NPTS);
+  }
+
+  // grab data from all selected sky regions
+  Signal = signal (SIGINT, handle_interrupt);
+  interrupt = FALSE;
+
+  // fprintf (stderr, "done setup...");
+
+  for (i = 0; (i < skylist[0].Nregions) && !interrupt; i++) {
+
+    // does this host ID match the desired location for the table?
+    if (!HostTableTestHost(skylist[0].regions[i], HOST_ID)) continue;
+
+    /* lock, load, unlock catalog */
+    char hostfile[1024];
+    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.Nsecfilt = Nsecfilt;
+
+    if (VERBOSE) gprint (GP_ERR, "trying %s ("OFF_T_FMT" of "OFF_T_FMT")\n", catalog.filename,  i,  skylist[0].Nregions);
+      
+    // an error exit status here is a significant error
+    if (!dvo_catalog_open (&catalog, NULL, FALSE, "r")) {
+      gprint (GP_ERR, "ERROR: failure to open catalog file %s\n", catalog.filename);
+      return FALSE;
+    }
+    dvo_catalog_unlock (&catalog);
+
+    /* XXX need to call dvo_catalog_chipcoords here passing the loaded images */
+
+    // fprintf (stderr, "done read...");
+
+    for (j = 0; (j < catalog.Naverage) && !interrupt; j++) {
+      m = catalog.average[j].measureOffset;
+      if (m > catalog.Nmeasure) {
+	gprint (GP_ERR, "ERROR: inconsistent average->measure offset.  Unsorted database?\n");
+	goto escape;
+      }
+
+      dbExtractMeasuresInitAve (); // reset counters for saved fields (costs very little)
+
+      for (k = 0; (k < catalog.average[j].Nmeasure); k++, m++) {
+	if (catalog.measure[m].averef != j) {
+	  gprint (GP_ERR, "ERROR: inconsistent measure->average link.  Unsorted database?\n");
+	  goto escape;
+	}
+
+	// extract the relevant values for this measurement
+	dbExtractMeasuresInitMeas (); // reset counters for saved fields  (costs very little
+	for (n = 0; n < Nfields; n++) {
+	  values[n] = dbExtractMeasures (&catalog.average[j], &catalog.secfilt[j*Nsecfilt], &catalog.measure[m], &fields[n]);
+	}
+	// fprintf (stderr, "object: ave: %f, cat: %f, averef %d\n", fields[n].name, values[2], values[3], catalog.measure[m].averef);
+
+	// test the conditional statement
+	if (!dbBooleanCond (stack, Nstack, values)) continue;
+	for (n = 0; n < Nreturn; n++) {
+	  if (vec[n][0].type == OPIHI_FLT) {
+	    vec[n][0].elements.Flt[Npts] = values[n].Flt;
+	  } else {
+	    vec[n][0].elements.Int[Npts] = values[n].Int;
+	  }
+	  // fprintf (stderr, "keep : field: %s, value: %f\n", fields[n].name, values[n]);
+	}
+	Npts++;
+	if (Npts >= NPTS) {
+	  NPTS += 2000;
+	  for (n = 0; n < Nreturn; n++) {
+	    REALLOCATE (vec[n][0].elements.Flt, opihi_flt, NPTS);
+	  }
+	}
+      }
+    }
+    dvo_catalog_free (&catalog);
+    // dbStackAllocPrint ();
+    // dbStackAllocReset ();
+    // dbStackFreePrint ();
+    // dbStackFreeReset ();
+  }
+  signal (SIGINT, Signal);
+  interrupt = FALSE;
+
+  // fprintf (stderr, "done load...");
+
+  for (n = 0; n < Nreturn; n++) {
+    ResetVector (vec[n], fields[n].type, Npts);
+  }
+
+  // write vectors to a table (this is used by parallel dvo operations, but can be used elsewhere)
+  if (RESULT_FILE && !SKIP_RESULTS) {
+    int status = WriteVectorTableFITS (RESULT_FILE, "RESULT", vec, Nreturn, FALSE, NULL);
+    if (!status) goto escape;
+  }
+
+  if (vec) free (vec);
+  if (values) free (values);
+  dbFreeFields (fields, Nfields);
+  dbFreeStack (stack, Nstack);
+  free (stack);
+  FreeImageSelection ();
+  SkyListFree (skylist);
+  FreeSkyRegionSelection (selection);
+
+  // fprintf (stderr, "done extr...\n");
+  return (TRUE);
+
+escape:
+  if (vec) free (vec);
+  free (values);
+  dbFreeFields (fields, Nfields);
+  dbFreeStack (stack, Nstack);
+  free (stack);
+  FreeImageSelection ();
+  SkyListFree (skylist);
+  FreeSkyRegionSelection (selection);
+  dvo_catalog_free (&catalog);
+  return (FALSE);
+
+ help:
+  gprint (GP_ERR, "USAGE: mextract field[,field,field...] where (expression)\n");
+
+  if ((argc > N + 1) && !strcasecmp (argv[N+1], "fields")) {
+    gprint (GP_ERR, "  RA : right ascension (J2000) for detection [degrees]\n");
+    gprint (GP_ERR, "  DEC : declination for detection [degrees]\n");
+    gprint (GP_ERR, "  RA:ave : average right ascension (J2000) for object [degrees]\n");
+    gprint (GP_ERR, "  DEC:ave : average declination for object [degrees]\n");
+    gprint (GP_ERR, "  RA:err : ra scatter [degrees] \n");
+    gprint (GP_ERR, "  DEC:err : dec scatter [degrees]\n");
+    gprint (GP_ERR, "  GLON : galactic longitude [degrees]\n");
+    gprint (GP_ERR, "  GLAT : galactic latitude [degrees]\n");
+    gprint (GP_ERR, "  GLON:ave : average galactic longitude [degrees]\n");
+    gprint (GP_ERR, "  GLAT:ave : average galactic latitude [degrees]\n");
+    gprint (GP_ERR, "  ELON : ecliptic longitude [degrees]\n");
+    gprint (GP_ERR, "  ELAT : ecliptic latitude [degrees]\n");
+    gprint (GP_ERR, "  ELON:ave : average ecliptic longitude [degrees]\n");
+    gprint (GP_ERR, "  ELAT:ave : average ecliptic latitude [degrees]\n");
+    gprint (GP_ERR, "  uRA : proper motion in ra [mas/yr]\n");
+    gprint (GP_ERR, "  uDEC : proper motion in dec [mas/yr]\n");
+    gprint (GP_ERR, "  duRA : proper motion error in ra [mas/yr]\n");
+    gprint (GP_ERR, "  duDEC : proper motion error in dec [mas/yr]\n");
+    gprint (GP_ERR, "  PAR : parallax\n");
+    gprint (GP_ERR, "  dPAR : parallax error \n");
+
+    gprint (GP_ERR, "  dR : ra offset [arcseconds]\n");
+    gprint (GP_ERR, "  dD : dec offset [arcseconds]\n");
+    gprint (GP_ERR, "  dR:fit : ra offset from fit [arcseconds]\n");
+    gprint (GP_ERR, "  dD:fit : dec offset from fit [arcseconds]\n");
+    gprint (GP_ERR, "  dR:err : ra offset error [arcseconds]\n");
+    gprint (GP_ERR, "  dD:err : dec offset error [arcseconds]\n");
+
+    gprint (GP_ERR, "  ChiSqPos : chi square of position fit \n");
+    gprint (GP_ERR, "  ChiSqPM  : chi square of proper-motion fit \n");
+    gprint (GP_ERR, "  ChiSqPar : chi square of parallax fit \n");
+
+    gprint (GP_ERR, "  Tmean : mean epoch (reference for proper motion)\n");
+    gprint (GP_ERR, "  Trange : range of times used for proper motion/parallax fit\n");
+
+
+    gprint (GP_ERR, "  Nmeas : number of measurements\n");
+    gprint (GP_ERR, "  Nmiss : number of non-detections\n");
+    gprint (GP_ERR, "  Npos  : number of measurments used for astrometry\n");
+
+    gprint (GP_ERR, "  objflags  : object flags\n");
+    gprint (GP_ERR, "  photflags : detection flags from image analysis\n");
+    gprint (GP_ERR, "  dbflags : detection flags from database analysis\n");
+
+    gprint (GP_ERR, "  obj_flags : object flags\n");
+    gprint (GP_ERR, "  phot_flags : detection flags from image analysis\n");
+    gprint (GP_ERR, "  db_flags : detection flags from database analysis\n");
+
+    gprint (GP_ERR, "  <photcode>:ave : average magnitude for photcode (or equivalent)\n");
+    gprint (GP_ERR, "  <photcode>:ref : reference magnitude system for photcode (or equivalent)\n");
+    gprint (GP_ERR, "  <photcode>:inst : instrumental magnitude for photcode\n");
+    gprint (GP_ERR, "  <photcode>:cat :  catalog magnitude for photcode\n");
+    gprint (GP_ERR, "  <photcode>:sys :  system magnitude for photcode\n");
+    gprint (GP_ERR, "  <photcode>:rel :  relative magnitude for photcode\n");
+    gprint (GP_ERR, "  <photcode>:cal :  calibrated magnitude for photcode \n");
+    gprint (GP_ERR, "  <photcode>:err :  magnitude error for measurement\n");
+    gprint (GP_ERR, "  <photcode>:ap :  catalog aperture magnitude for photcode\n");
+    gprint (GP_ERR, "  <photcode>:aper :  catalog aperture magnitude for photcode\n");
+    gprint (GP_ERR, "  <photcode>:aveerr : average error (stdev)\n");
+    gprint (GP_ERR, "  <photcode>:chisq : raw chi-square of magnitude fit\n");
+    gprint (GP_ERR, "  <photcode>:ncode : number of measurements in photcode\n");
+    gprint (GP_ERR, "  <photcode>:nphot : number of measurements used for average magnitude\n");
+    gprint (GP_ERR, "  airmass : airmass of detection\n");
+    gprint (GP_ERR, "  alt : altitude of detection\n");
+    gprint (GP_ERR, "  az  : azimuth of detection\n");
+    gprint (GP_ERR, "  exptime : exposure time [s]\n");
+    gprint (GP_ERR, "  photcode : photcode \n");
+    gprint (GP_ERR, "  photcode:equiv : equivalent average photcode \n");
+    gprint (GP_ERR, "  time : time of exposure [Seconds since Jan 1, 1970/\n");
+    gprint (GP_ERR, "  fwhm : fwhm (average) of fitted PSF [pixels]\n");
+    gprint (GP_ERR, "  fwhm_maj : fwhm (major axis) of fitted PSF [pixels]\n");
+    gprint (GP_ERR, "  fwhm_min : fwhm (minor axis) of fitted PSF [pixels]\n");
+    gprint (GP_ERR, "  theta : position angle of fitted PSF\n");
+    gprint (GP_ERR, "  posangle : position angle of detector at measurement [degrees]\n");
+    gprint (GP_ERR, "  platescale : plate scale of detector at measurement [arcsec/pixel] \n");
+
+    gprint (GP_ERR, "  psf_qf : PSF quality factor (psf-weighted mask fraction)\n");
+    gprint (GP_ERR, "  psf_chisq : PSF fit chi square\n");
+    gprint (GP_ERR, "  psf_ndof : PSF number of degrees of freedom\n");
+    gprint (GP_ERR, "  psf_npix : PSF number of pixels\n");
+
+    gprint (GP_ERR, "  cr_nsigma : Nsigma deviation towards cosmic ray\n");
+    gprint (GP_ERR, "  ext_nsigma : Nsigma deviation towards extended source\n");
+
+    gprint (GP_ERR, "  Mxx : second moment in X [pixels^2]\n");
+    gprint (GP_ERR, "  Mxy : second moment cross term [pixels^2]\n");
+    gprint (GP_ERR, "  Myy : second moment in Y [pixels^2]\n");
+
+    gprint (GP_ERR, "  xccd : ccd x position\n");
+    gprint (GP_ERR, "  yccd : ccd y position\n");
+    gprint (GP_ERR, "  xccd:err : ccd x position error\n");
+    gprint (GP_ERR, "  yccd:err : ccd y position error\n");
+    gprint (GP_ERR, "  xmosaic : mosaic x position\n");
+    gprint (GP_ERR, "  ymosaic : mosaic y position\n");
+    gprint (GP_ERR, "  xchip : chip x position (= ccd position)\n");
+    gprint (GP_ERR, "  ychip : chip y position (= ccd position)\n");
+    gprint (GP_ERR, "  xfpa : fpa x position (= mosaic position)\n");
+    gprint (GP_ERR, "  yfpa : fpa y position (= mosaic position)\n");
+
+    gprint (GP_ERR, "  detID : ID of detection (unique on source image)\n");
+    gprint (GP_ERR, "  objID : object ID (32 bit, unique in catalog)\n");
+    gprint (GP_ERR, "  catID : catalog ID (32 bit)\n");
+    gprint (GP_ERR, "  imageID : ID of source image (32 bit)\n");
+    gprint (GP_ERR, "  externID : externID of source image (32 bit)\n");
+
+    gprint (GP_ERR, "  Mcal_offset : difference wrt nominal zero point (clouds are positive)\n");
+    gprint (GP_ERR, "  flat : flat-field correction (measure.Mcal - image.Mcal)\n");
+    return (FALSE);
+  }
+  gprint (GP_ERR, " mextract --help fields : for a complete listing of allowed fields\n");
+  return (FALSE);
+}
Index: /branches/sc_branches/pantasks_condor/dvo/mmatch.c
===================================================================
--- /branches/sc_branches/pantasks_condor/dvo/mmatch.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/dvo/mmatch.c	(revision 34783)
@@ -0,0 +1,342 @@
+# include "dvoshell.h"
+
+/* This function uses the 'find_match' algorithm to select the objects of interest.
+   All measurements for matched objects result in an element in the output vectors.
+   if -index (index) is supplied, the index of the input vectors are stored to match
+   the output vectors.
+ */
+
+int mmatch (int argc, char **argv) {
+  
+  off_t i, j, k, n, m, *index;
+  int N, Ncat, Npts, NPTS, last, Nfields, Nsecfilt, Ninvec;
+  int VERBOSE;
+  char name[1024];
+  void *Signal;
+  float RADIUS;
+
+  Catalog catalog;
+
+  Vector **vec, **invec, *RAvec, *DECvec, *IDXvec;
+  dbField *fields;
+  dbValue *values;
+  SkyList *skylist;
+
+  /* defaults */
+  vec = NULL;
+  invec = NULL;
+  fields = NULL;
+  values = NULL;
+  skylist = NULL;
+  Ninvec = 0;
+
+  if ((N = get_argument (argc, argv, "-h"))) goto help;
+  if ((N = get_argument (argc, argv, "--help"))) goto help;
+
+  VERBOSE = FALSE;
+  if ((N = get_argument (argc, argv, "-v"))) {
+    remove_argument (N, &argc, argv);
+    VERBOSE = TRUE;
+  }
+
+  IDXvec = NULL;
+  if ((N = get_argument (argc, argv, "-index"))) {
+    remove_argument (N, &argc, argv);
+    IDXvec = SelectVector (argv[N], ANYVECTOR, TRUE);
+    if (IDXvec == NULL) goto help;
+    remove_argument (N, &argc, argv);
+  }
+
+  int PARALLEL = FALSE;
+  if ((N = get_argument (argc, argv, "-parallel"))) {
+    remove_argument (N, &argc, argv);
+    PARALLEL = TRUE;
+  }
+
+  // read RA,DEC coords from a fits file (esp for parallel dvo)
+  char *CoordsFile = NULL;
+  if ((N = get_argument (argc, argv, "-coords"))) {
+    remove_argument (N, &argc, argv);
+    CoordsFile = strcreate(argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  if (!CoordsFile && (argc < 5)) goto help;
+  if ( CoordsFile && (argc < 3)) goto help;
+
+  dvo_catalog_init (&catalog, TRUE);
+
+  /* load photcode information */
+  if (!InitPhotcodes ()) goto escape;
+  Nsecfilt = GetPhotcodeNsecfilt ();
+
+  // init locally static variables (time refs)
+  dbExtractMeasuresInit();
+
+  // this does all the work of re-packaging the command, calling it on the remote machines, then loading in the results
+  RAvec  = NULL;
+  DECvec = NULL;
+  if (PARALLEL && !HOST_ID) {
+
+    // We need to copy the args to a temp array and modify them so that we send the
+    // correct set to the remote client.  The args list looks like this:
+    // if (!CoordsFile) : mmatch (RA) (DEC) (RADIUS) field, ...
+    // if ( CoordsFile) : mmatch (RADIUS) field, ... [because we stripped off the -coords filename elements]
+
+    // allocate the temp array and copy all but (RA) (DEC)
+    int targc = 0;
+    char **targv = NULL;
+    ALLOCATE (targv, char *, argc + 2);
+    for (i = 0; i < argc; i++) {
+      if (!CoordsFile && (i == 1)) continue;
+      if (!CoordsFile && (i == 2)) continue;
+      targv[targc] = strcreate (argv[i]);
+      targc ++;
+    }
+
+    // if not specified, create the coords.fits input file
+    if (!CoordsFile) {
+      // get vectors corresponding to coordinates of interest
+      if ((RAvec  = SelectVector (argv[1], ANYVECTOR, TRUE)) == NULL) goto help;
+      if ((DECvec = SelectVector (argv[2], ANYVECTOR, TRUE)) == NULL) goto help;
+      
+      ALLOCATE (vec, Vector *, 2);
+      vec[0] = RAvec;
+      vec[1] = DECvec;
+
+      CoordsFile = abspath("coords.fits", 1024);
+      int status = WriteVectorTableFITS (CoordsFile, "COORDS", vec, 2, FALSE, NULL);
+      if (!status) goto escape;
+    }
+
+    // add the coords file to the args list
+    targv[targc+0] = strcreate ("-coords");
+    targv[targc+1] = CoordsFile; // this gets freed with targv
+    targc += 2;
+    
+    // if needed, add the index vector to the args list
+    if (IDXvec) {
+      REALLOCATE (targv, char *, targc + 2);
+      targv[targc+0] = strcreate ("-index");
+      targv[targc+1] = strcreate (IDXvec[0].name);
+      targc += 2;
+    }      
+
+    // call the remote client
+    int status = HostTableParallelOps (targc, targv, RESULT_FILE, 0, VERBOSE);
+    if (vec) free (vec);
+    
+    // free up targv
+    for (i = 0; i < targc; i++) {
+      free (targv[i]);
+    }
+    free (targv);
+
+    return status;
+  }
+
+  // get vectors corresponding to coordinates of interest
+  if (CoordsFile) {
+    // read RAvec, DECvec from coords file (1st 2 fields?)
+    Ninvec = 0;
+    invec = ReadVectorTableFITS (CoordsFile, "COORDS", &Ninvec);
+    RAvec = invec[0];
+    DECvec = invec[1];
+  } else {
+    if ((RAvec  = SelectVector (argv[1], ANYVECTOR, TRUE)) == NULL) goto help;
+    if ((DECvec = SelectVector (argv[2], ANYVECTOR, TRUE)) == NULL) goto help;
+    // strip off RA & DEC
+    remove_argument (1, &argc, argv);
+    remove_argument (1, &argc, argv);
+  }
+  RADIUS = atof (argv[1]);
+  remove_argument (1, &argc, argv);
+
+  // parse the fields to be extracted and returned
+  fields = dbCmdlineFields (argc, argv, DVO_TABLE_MEASURE, &last, &Nfields);
+  if (fields == NULL) goto help;
+  if ((Nfields == 0) || (last != argc)) {
+    dbFreeFields (fields, Nfields);
+    dvo_catalog_free (&catalog);
+    goto help;
+  }
+
+  // load image data if needed (for fields listed below)
+  int loadImages = FALSE;
+  int mosaicMode = FALSE;
+  for (i = 0; !loadImages && (i < Nfields); i++) {
+    if (!MEASURE_HAS_XCCD) {
+      // I'm keeping this code because it gives a way of handling dvo dbs that don't have
+      // measure.xccd if we need it
+      if (fields[i].ID == MEAS_XCCD) loadImages = TRUE;
+      if (fields[i].ID == MEAS_YCCD) loadImages = TRUE;
+    }
+    if (fields[i].ID == MEAS_XMOSAIC) 	    loadImages = mosaicMode = TRUE;
+    if (fields[i].ID == MEAS_YMOSAIC) 	    loadImages = mosaicMode = TRUE;
+    if (fields[i].ID == MEAS_EXTERN_ID)     loadImages = mosaicMode = TRUE;
+    if (fields[i].ID == MEAS_FLAT)          loadImages = mosaicMode = TRUE;
+    if (fields[i].ID == MEAS_CENTER_OFFSET) loadImages = mosaicMode = TRUE;
+  }
+
+  // use the whole sky (since we select random points around the sky)
+  SkyRegionSelection selection;
+  selection.useDisplay = FALSE;
+  selection.useSkyregion = FALSE;
+  if (loadImages && !SetImageSelection (mosaicMode, &selection)) goto escape;
+
+  /* load regions which contain all supplied RA,DEC coordinates */
+  if ((skylist = SelectRegionsByCoordVectors (RAvec, DECvec)) == NULL) goto escape;
+
+  /* create output storage vectors */
+  Npts = 0;
+  NPTS = 1000;
+  ALLOCATE (values, dbValue, Nfields);
+  ALLOCATE (vec, Vector *, Nfields);
+  for (i = 0; i < Nfields; i++) {
+    if (ISNUM(fields[i].name[0])) {
+      sprintf (name, "v_%s", fields[i].name);
+    } else {
+      sprintf (name, "%s", fields[i].name);
+    }
+    if ((vec[i] = SelectVector (name, ANYVECTOR, TRUE)) == NULL) goto escape;
+    ResetVector (vec[i], fields[i].type, NPTS);
+  }
+  if (IDXvec) {
+    ResetVector (IDXvec, OPIHI_INT, NPTS);
+  }
+
+  // vectors to track recovered values
+  int Nelem = RAvec->Nelements;
+  ALLOCATE (index, off_t, Nelem);
+
+  // grab data from all selected sky regions
+  Signal = signal (SIGINT, handle_interrupt);
+  interrupt = FALSE;
+  for (i = 0; (i < skylist[0].Nregions) && !interrupt; i++) {
+
+    // does this host ID match the desired location for the table?
+    if (!HostTableTestHost(skylist[0].regions[i], HOST_ID)) continue;
+
+    /* lock, load, unlock catalog */
+    char hostfile[1024];
+    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_SECF | LOAD_MEAS;
+    catalog.Nsecfilt = Nsecfilt;
+
+    if (VERBOSE) gprint (GP_ERR, "trying %s ("OFF_T_FMT" of "OFF_T_FMT")\n", catalog.filename,  i,  skylist[0].Nregions);
+      
+    // an error exit status here is a significant error
+    if (!dvo_catalog_open (&catalog, NULL, FALSE, "r")) {
+      gprint (GP_ERR, "ERROR: failure to open catalog file %s\n", catalog.filename);
+      exit (2);
+    }
+    dvo_catalog_unlock (&catalog);  /// we can unlock here since this is read-only (do not block other access)
+    if (catalog.Naverage == 0) {
+      dvo_catalog_free (&catalog);
+      continue;
+    }
+
+    // returns the matches to AVERAGE objects; what do we do for each MEASURE?
+    find_matches_by_vectors (skylist[0].regions[i], &catalog, RAvec, DECvec, RADIUS, index);
+
+    for (j = 0; (j < Nelem) && !interrupt; j++) {
+      Ncat = index[j];
+
+      if (Ncat == -1) continue;
+      if (Ncat == -2) continue;
+
+      m = catalog.average[Ncat].measureOffset;
+
+      dbExtractMeasuresInitAve (); // reset counters for saved fields (costs very little)
+
+      for (k = 0; (k < catalog.average[Ncat].Nmeasure); k++, m++) {
+	if (catalog.measure[m].averef != Ncat) {
+	  gprint (GP_ERR, "ERROR: inconsistent measure->average link.  Unsorted database?\n");
+	  goto escape;
+	}
+
+	// extract the relevant values for this measurement
+	dbExtractMeasuresInitMeas (); // reset counters for saved fields  (costs very little
+	for (n = 0; n < Nfields; n++) {
+	  values[n] = dbExtractMeasures (&catalog.average[Ncat], &catalog.secfilt[Ncat*Nsecfilt], &catalog.measure[m], &fields[n]);
+	}
+
+	// XXX if we are allowed to return more rows than the supplied RA,DEC we will need to create an output RA,DEC
+	for (n = 0; n < Nfields; n++) {
+	  if (vec[n][0].type == OPIHI_FLT) {
+	    vec[n][0].elements.Flt[Npts] = values[n].Flt;
+	  } else {
+	    vec[n][0].elements.Int[Npts] = values[n].Int;
+	  }
+	}
+	if (IDXvec) {
+	  IDXvec[0].elements.Int[Npts] = j;
+	}
+
+	// extend length of output vectors
+	Npts++;
+	if (Npts >= NPTS) {
+	  NPTS += 2000;
+	  for (n = 0; n < Nfields; n++) {
+	    REALLOCATE (vec[n][0].elements.Flt, opihi_flt, NPTS);
+	  }
+	  if (IDXvec) {
+	    REALLOCATE (IDXvec[0].elements.Int, opihi_int, NPTS);
+	  }
+	}
+      }
+    }
+
+    dvo_catalog_free (&catalog);
+  }
+  signal (SIGINT, Signal);
+  interrupt = FALSE;
+
+  for (n = 0; n < Nfields; n++) {
+    ResetVector (vec[n], fields[n].type, Npts);
+  }
+  if (IDXvec) {
+    ResetVector (IDXvec, IDXvec->type, Npts);
+  }
+
+  // write vectors to a table (this is used by parallel dvo operations, but can be used elsewhere)
+  // only write the fields which were in a valid catalog
+  if (RESULT_FILE) {
+    if (IDXvec) {
+      // extend the array by one to hold index array
+      Nfields ++;
+      REALLOCATE (vec, Vector *, Nfields);
+      vec[Nfields-1] = IDXvec;
+    }
+    int status = WriteVectorTableFITS (RESULT_FILE, "RESULT", vec, Nfields, FALSE, NULL);
+    if (!status) goto escape;
+  }
+
+  if (vec) free (vec);
+  if (values) free (values);
+  if (invec) FreeVectorArray (invec, Ninvec);
+  dbFreeFields (fields, Nfields);
+  SkyListFree (skylist);
+  return (TRUE);
+
+ escape:
+  if (vec) free (vec);
+  if (values) free (values);
+  if (invec) FreeVectorArray (invec, Ninvec);
+  dbFreeFields (fields, Nfields);
+  SkyListFree (skylist);
+  return (FALSE);
+
+ help:
+  gprint (GP_ERR, "USAGE: mmatch (RA) (DEC) (RADIUS) field[,field,field...] [-index index]\n");
+  gprint (GP_ERR, "   OR: mmatch -coords (filename.fits) (RADIUS) field[,field,field...] [-index index]\n");
+
+  if ((argc > N + 1) && !strcasecmp (argv[N+1], "fields")) {
+    gprint (GP_ERR, "  RA : right ascension (J2000) [degrees]\n");
+    gprint (GP_ERR, "  DEC : declination [degrees]\n");
+    return (FALSE);
+  }
+  gprint (GP_ERR, " mmatch --help fields : for a complete listing of allowed fields\n");
+  return (FALSE);
+}
Index: /branches/sc_branches/pantasks_condor/dvo/mmextract.c
===================================================================
--- /branches/sc_branches/pantasks_condor/dvo/mmextract.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/dvo/mmextract.c	(revision 34783)
@@ -0,0 +1,377 @@
+# include "dvoshell.h"
+
+int mmextract (int argc, char **argv) {
+  
+  off_t i, j, k, m;
+  int 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 **cstack1, **cstack2, name1[1024], name2[1024];
+  dbValue *values, **table1, **table2;
+  void *Signal;
+
+  Catalog catalog;
+  SkyList *skylist;
+  Vector **vec;
+  dbField *fields;
+  dbStack *stack1;
+  dbStack *stack2;
+  SkyRegionSelection *selection;
+
+  /* defaults */
+  vec = NULL;
+  stack1 = NULL;
+  stack2 = NULL;
+  fields = NULL;
+  values = NULL;
+  skylist = NULL;
+  selection = NULL;
+
+  if ((N = get_argument (argc, argv, "-h"))) goto help;
+  if ((N = get_argument (argc, argv, "--help"))) goto help;
+
+  VERBOSE = FALSE;
+  if ((N = get_argument (argc, argv, "-v"))) {
+    remove_argument (N, &argc, argv);
+    VERBOSE = TRUE;
+  }
+
+  dvo_catalog_init (&catalog, TRUE);
+
+  /* load photcode information */
+  if (!InitPhotcodes ()) goto escape;
+  Nsecfilt = GetPhotcodeNsecfilt ();
+  
+  // init locally static variables (time refs)
+  dbExtractMeasuresInit();
+
+  // parse skyregion options
+  if ((selection = SetRegionSelection (&argc, argv)) == NULL) goto escape;
+
+  // command-line is of the form: avextract field,field, field [where (field op value)...]
+  // mmextract [-noauto] field,field field where conditions matched to conditions
+  // mmextract ra,dec,mag where photcode equiv g matched to photcode == 2MASS_J
+  // mmextract -noauto ra,dec,mag where photcode equiv g matched to photcode == 2MASS_J
+  // objectID matched is implied
+  // -noauto means imageID is not matched
+
+  // 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) goto escape;
+  if (Nfields == 0) {
+    FreeSkyRegionSelection (selection);
+    dbFreeFields (fields, Nfields);
+    dvo_catalog_free (&catalog);
+    goto help;
+  }
+
+  // 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[Iwhere], &Ncstack1);
+  cstack2 = isolate_elements (Nmatch, &argv[Imatch], &Ncstack2);
+  
+  // construct the db Boolean math stack (frees cstack)
+  stack1 = dbRPN (Ncstack1, cstack1, &Nstack1);
+  if (Ncstack1 && !Nstack1) {
+    print_error ();
+    goto escape;
+  }
+
+  // construct the db Boolean math stack (frees cstack)
+  stack2 = dbRPN (Ncstack2, cstack2, &Nstack2);
+  if (Ncstack2 && !Nstack2) {
+    print_error ();
+    goto escape;
+  }
+
+  // XXX disallow skyregion limits in the matched to expression?
+
+  // add the skyregion limits to the where statement (or create)
+  dbAstroRegionLimits (&stack1, &Nstack1, selection, DVO_TABLE_MEASURE);
+  dbAstroRegionLimits (&stack2, &Nstack2, selection, DVO_TABLE_MEASURE);
+
+  // parse stack elements into fields and scalars as needed
+  Nreturn_base = Nfields;
+  Nreturn = 2*Nfields; // we are returning fieldi_1, fieldi_2 for the selected fields
+
+  if (!dbCheckStack (stack1, Nstack1, DVO_TABLE_MEASURE, &fields, &Nfields)) goto escape;
+  if (!dbCheckStack (stack2, Nstack2, DVO_TABLE_MEASURE, &fields, &Nfields)) goto escape;
+  // XXX handle errors
+
+  /* load region corresponding to selection above */
+  if ((skylist = SelectRegions (selection)) == NULL) goto escape;
+
+  // load image data if needed (for fields listed below)
+  loadImages = FALSE;
+  mosaicMode = FALSE;
+  for (i = 0; !loadImages && (i < Nfields); i++) {
+    if (fields[i].ID == MEAS_XCCD) loadImages = TRUE;
+    if (fields[i].ID == MEAS_YCCD) loadImages = TRUE;
+    if (fields[i].ID == MEAS_XMOSAIC) loadImages = mosaicMode = TRUE;
+    if (fields[i].ID == MEAS_YMOSAIC) loadImages = mosaicMode = TRUE;
+  }
+  if (loadImages && !SetImageSelection (mosaicMode, selection)) goto escape;
+
+  /* create storage vector */
+  ALLOCATE (values, dbValue, Nfields);
+  ALLOCATE (vec, Vector *, Nreturn);
+
+  for (i = 0; i < Nreturn_base; i++) {
+    if (ISNUM(fields[i].name[0])) {
+      sprintf (name1, "v_%s_1", fields[i].name);
+      sprintf (name2, "v_%s_2", fields[i].name);
+    } else {
+      sprintf (name1, "%s_1", fields[i].name);
+      sprintf (name2, "%s_2", fields[i].name);
+    }
+    if ((vec[2*i+0] = SelectVector (name1, ANYVECTOR, TRUE)) == NULL) goto escape;
+    if ((vec[2*i+1] = SelectVector (name2, ANYVECTOR, TRUE)) == NULL) goto escape;
+    ResetVector (vec[2*i+0], fields[i].type, fields[i].type);
+    ResetVector (vec[2*i+1], fields[i].type, fields[i].type);
+  }
+
+  Npts = 0;
+  NPTS = 1;
+
+  // we save the selected measures for each average to temporary tables 1 and 2
+  // XXX need to deal with the INT / DBL difference here...
+  NTABLE = 100;
+  ALLOCATE (table1, dbValue *, Nreturn_base);
+  ALLOCATE (table2, dbValue *, Nreturn_base);
+  for (i = 0; i < Nreturn_base; i++) {
+    ALLOCATE (table1[i], dbValue, NTABLE);
+    ALLOCATE (table2[i], dbValue, NTABLE);
+  }
+
+  // grab data from all selected sky regions
+  Signal = signal (SIGINT, handle_interrupt);
+  interrupt = FALSE;
+  for (i = 0; (i < skylist[0].Nregions) && !interrupt; i++) {
+    /* lock, load, unlock catalog */
+    catalog.filename = skylist[0].filename[i];
+    catalog.catflags = LOAD_AVES | LOAD_MEAS | LOAD_SECF;
+    catalog.Nsecfilt = Nsecfilt;
+
+    if (VERBOSE) gprint (GP_ERR, "trying %s ("OFF_T_FMT" of "OFF_T_FMT")\n", catalog.filename,  i,  skylist[0].Nregions);
+      
+    // an error exit status here is a significant error
+    if (!dvo_catalog_open (&catalog, NULL, FALSE, "r")) {
+      gprint (GP_ERR, "ERROR: failure to open catalog file %s\n", catalog.filename);
+      exit (2);
+    }
+    dvo_catalog_unlock (&catalog);
+
+    /* XXX need to call dvo_catalog_chipcoords here passing the loaded images */
+
+    for (j = 0; (j < catalog.Naverage) && !interrupt; j++) {
+      m = catalog.average[j].measureOffset;
+      dbExtractMeasuresInitAve (); // reset counters for saved fields 
+
+      // XXX check that we have space to keep all Nmeasure
+      if (NTABLE < catalog.average[j].Nmeasure) {
+	NTABLE = catalog.average[j].Nmeasure;
+	for (n = 0; n < Nreturn_base; n++) {
+	  REALLOCATE (table1[n], dbValue, NTABLE);
+	  REALLOCATE (table2[n], dbValue, NTABLE);
+	}
+      }
+
+      // extract the matching measures for this object into the temp tables
+      Nt1 = Nt2 = 0;
+      for (k = 0; (k < catalog.average[j].Nmeasure); k++, m++) {
+
+	// extract the relevant values for this measurement 
+	dbExtractMeasuresInitMeas (); // reset counters for saved fields 
+	for (n = 0; n < Nfields; n++) {
+	  // values needs to be a pointer to a type with FLT and INT (with a union, we would save a bit of memory...)
+	  values[n] = dbExtractMeasures (&catalog.average[j], &catalog.secfilt[j*Nsecfilt], &catalog.measure[m], &fields[n]);
+	}
+	// fprintf (stderr, "object: ave: %f, cat: %f, averef %d\n", fields[n].name, values[2], values[3], catalog.measure[m].averef);
+
+	// test the first conditional statement
+	if (dbBooleanCond (stack1, Nstack1, values)) {
+	  for (n = 0; n < Nreturn_base; n++) {
+	    table1[n][Nt1] = values[n];
+	    // fprintf (stderr, "keep : field: %s, value: %f\n", fields[n].name, values[n]);
+	  }
+	  Nt1 ++;
+	}
+	// test the second conditional statement
+	if (dbBooleanCond (stack2, Nstack2, values)) {
+	  for (n = 0; n < Nreturn_base; n++) {
+	    table2[n][Nt2] = values[n];
+	    // fprintf (stderr, "keep : field: %s, value: %f\n", fields[n].name, values[n]);
+	  }
+	  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++) {
+	    if (vec[2*n+0][0].type == OPIHI_FLT) {
+	      vec[2*n+0][0].elements.Flt[Npts] = table1[n][n1].Flt;
+	      vec[2*n+1][0].elements.Flt[Npts] = table2[n][n2].Flt;
+	    } else {
+	      vec[2*n+0][0].elements.Int[Npts] = table1[n][n1].Int;
+	      vec[2*n+1][0].elements.Int[Npts] = table2[n][n2].Int;
+	    }
+	  }
+	  Npts++;
+	  if (Npts >= NPTS) {
+	    NPTS += 2000;
+	    for (n = 0; n < Nreturn; n++) {
+	      if (vec[n][0].type == OPIHI_FLT) {
+		REALLOCATE (vec[n][0].elements.Flt, opihi_flt, NPTS);
+	      } else {
+		REALLOCATE (vec[n][0].elements.Int, opihi_int, NPTS);
+	      }
+	    }
+	  }
+	}
+      }
+    }
+
+    dvo_catalog_free (&catalog);
+    // dbStackAllocPrint ();
+    // dbStackAllocReset ();
+    // dbStackFreePrint ();
+    // dbStackFreeReset ();
+  }
+  signal (SIGINT, Signal);
+  interrupt = FALSE;
+
+  // free excess memory
+  for (n = 0; n < Nreturn; n++) {
+    vec[n][0].Nelements = Npts;
+    if (vec[n][0].type == OPIHI_FLT) {
+      REALLOCATE (vec[n][0].elements.Flt, opihi_flt, MAX(1,Npts));
+    } else {
+      REALLOCATE (vec[n][0].elements.Int, opihi_int, MAX(1,Npts));
+    }
+  }
+
+  for (n = 0; n < Nreturn_base; n++) {
+    free (table1[n]);
+    free (table2[n]);
+  }
+  free (table1);
+  free (table2);
+
+  free (values);
+
+  dbFreeFields (fields, Nfields);
+  dbFreeStack (stack1, Nstack1);
+  dbFreeStack (stack2, Nstack2);
+  free (stack1);
+  free (stack2);
+  FreeImageSelection ();
+  SkyListFree (skylist);
+  FreeSkyRegionSelection (selection);
+  return (TRUE);
+
+escape:
+  if (vec) free (vec);
+  if (values) free (values);
+  dbFreeFields (fields, Nfields);
+  dbFreeStack (stack1, Nstack1);
+  dbFreeStack (stack2, Nstack2);
+  free (stack1);
+  free (stack2);
+  FreeImageSelection ();
+  SkyListFree (skylist);
+  FreeSkyRegionSelection (selection);
+  dvo_catalog_free (&catalog);
+  return (FALSE);
+
+ help:
+  gprint (GP_ERR, "USAGE: mmextract field[,field,field...] where (expression) match to (expression)\n");
+  gprint (GP_ERR, "  pairs of fields are returned: the first set are restricted by the 'where' expression, the second by the 'match to' expression\n");
+
+  if ((argc > N + 1) && !strcasecmp (argv[N+1], "fields")) {
+    gprint (GP_ERR, " USAGE: avextract field[,field,field...] where (expression)\n");
+    gprint (GP_ERR, "  RA : right ascension (J2000) for detection\n");
+    gprint (GP_ERR, "  DEC : declination for detection\n");
+    gprint (GP_ERR, "  RA:ave : average right ascension (J2000) for object\n");
+    gprint (GP_ERR, "  DEC:ave : average declination for object\n");
+    gprint (GP_ERR, "  RA:err : ra scatter \n");
+    gprint (GP_ERR, "  DEC:err : dec scatter\n");
+    gprint (GP_ERR, "  uRA : proper motion in ra\n");
+    gprint (GP_ERR, "  uDEC : proper motion in dec\n");
+    gprint (GP_ERR, "  duRA : proper motion error in ra\n");
+    gprint (GP_ERR, "  duDEC : proper motion error in dec\n");
+    gprint (GP_ERR, "  PAR : parallax\n");
+    gprint (GP_ERR, "  dPAR : parallax error \n");
+    gprint (GP_ERR, "  nmeas : number of measurements\n");
+    gprint (GP_ERR, "  nmiss : number of non-detections\n");
+    gprint (GP_ERR, "  xp : positional chi-square\n");
+    gprint (GP_ERR, "  objflag : object flags\n");
+    gprint (GP_ERR, "  photcode:ave : average magnitude for photcode (or equivalent)\n");
+    gprint (GP_ERR, "  photcode:ref : reference magnitude system for photcode (or equivalent)\n");
+    gprint (GP_ERR, "  photcode:inst : instrumental magnitude for photcode\n");
+    gprint (GP_ERR, "  photcode:cat :  catalog magnitude for photcode\n");
+    gprint (GP_ERR, "  photcode:sys :  system magnitude for photcode\n");
+    gprint (GP_ERR, "  photcode:rel :  relative magnitude for photcode\n");
+    gprint (GP_ERR, "  photcode:cal :  calibrated magnitude for photcode \n");
+    gprint (GP_ERR, "  photcode:err : magnitude error for photcode\n");
+    gprint (GP_ERR, "  photcode:chisq : raw chi-square of magnitude fit\n");
+    gprint (GP_ERR, "  photcode:ncode : number of measurements in photcode\n");
+    gprint (GP_ERR, "  photcode:nphot : number of measurements used for average magnitude\n");
+    gprint (GP_ERR, "  airmass : airmass of detection\n");
+    gprint (GP_ERR, "  exptime : exposure time\n");
+    gprint (GP_ERR, "  photcode : photcode \n");
+    gprint (GP_ERR, "  time : time of exposure\n");
+    gprint (GP_ERR, "  dR : ra offset\n");
+    gprint (GP_ERR, "  dD : dec offset\n");
+    gprint (GP_ERR, "  fwhm : fwhm (average)\n");
+    gprint (GP_ERR, "  fwhm_maj : fwhm (major axis)\n");
+    gprint (GP_ERR, "  fwhm_min : fwhm (minor axis)\n");
+    gprint (GP_ERR, "  theta : position angle\n");
+    gprint (GP_ERR, "  flags : detection flags\n");
+    gprint (GP_ERR, "  xccd : ccd x position\n");
+    gprint (GP_ERR, "  yccd : ccd y position\n");
+    gprint (GP_ERR, "  xmosaic : mosaic x position\n");
+    gprint (GP_ERR, "  ymosaic : mosaic y position\n");
+    gprint (GP_ERR, "  xchip : chip x position\n");
+    gprint (GP_ERR, "  ychip : chip y position\n");
+    gprint (GP_ERR, "  xfpa : fpa x position\n");
+    gprint (GP_ERR, "  yfpa : fpa y position\n");
+    return (FALSE);
+  }
+  gprint (GP_ERR, " mmextract --help fields : for a complete listing of allowed fields\n");
+  return (FALSE);
+}
Index: /branches/sc_branches/pantasks_condor/dvo/notes.txt
===================================================================
--- /branches/sc_branches/pantasks_condor/dvo/notes.txt	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/dvo/notes.txt	(revision 34783)
@@ -0,0 +1,10 @@
+
+avextract ra,dec,g,r,i where (ra > 5) && (dec < 3)
+
+
+U db_check_stack.c
+U db_convert_to_RPN.c
+U dvoBooleanElements.c
+U dvoEvaluateStack.c
+U dvodb.c
+U dvofields.c
Index: /branches/sc_branches/pantasks_condor/dvo/objectcoverage.c
===================================================================
--- /branches/sc_branches/pantasks_condor/dvo/objectcoverage.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/dvo/objectcoverage.c	(revision 34783)
@@ -0,0 +1,235 @@
+# include "dvoshell.h"
+
+int wordhash (char *word);
+
+int objectcoverage (int argc, char **argv) {
+
+  int ShowDensity;
+  int N, status, xs, ys;
+  double pixscale, r, d, Xs, Ys, RaCenter, DecCenter;
+  char projection[16];
+  float *V;
+  int k, j, invalid, Nx, Ny;
+  Buffer *buf;
+  Coords coords;
+  int Nsecfilt;
+  SkyList *skylist;
+  SkyRegionSelection *selection;
+
+  Catalog catalog; 
+  catalog.average = NULL; 
+  catalog.secfilt = NULL;
+  catalog.measure = NULL;
+  skylist = NULL;
+  selection = NULL;
+  RaCenter = 0.0;
+  DecCenter = 0.0;
+  if ((selection = SetRegionSelection (&argc, argv)) == NULL) goto escape;
+  if ((N = get_argument (argc, argv, "-center"))) {
+    remove_argument (N, &argc, argv);
+    RaCenter = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+    DecCenter = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  Nx = 0;
+  Ny = 0;
+  if ((N = get_argument (argc, argv, "-size"))) {
+    remove_argument (N, &argc, argv);
+    Nx = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+    Ny = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  pixscale = 1.0;
+  if ((N = get_argument (argc, argv, "-scale"))) {
+    remove_argument (N, &argc, argv);
+    pixscale = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  ShowDensity = FALSE;
+  if ((N = get_argument (argc, argv, "-density"))) {
+    remove_argument (N, &argc, argv);
+    ShowDensity = TRUE;
+  }
+
+  strcpy (projection, "DEC--AIT");
+  if ((N = get_argument (argc, argv, "-proj"))) {
+    remove_argument (N, &argc, argv);
+    if (!strcasecmp(argv[N], "TAN")) {
+	strcpy (projection, "DEC--TAN");
+    }	
+    if (!strcasecmp(argv[N], "SIN")) {
+	strcpy (projection, "DEC--SIN");
+    }	
+    if (!strcasecmp(argv[N], "GLS")) {
+	strcpy (projection, "DEC--GLS");
+    }	
+    if (!strcasecmp(argv[N], "PAR")) {
+	strcpy (projection, "DEC--PAR");
+    }	
+    remove_argument (N, &argc, argv);
+  }
+
+  // double trange;
+  // time_t tzero, tend;
+  // int TimeSelect = FALSE;
+  // if ((N = get_argument (argc, argv, "-time"))) {
+  //   remove_argument (N, &argc, argv);
+  //   if (!ohana_str_to_time (argv[N], &tzero)) { 
+  //     gprint (GP_ERR, "syntax error\n");
+  //     return (FALSE);
+  //   }
+  //   remove_argument (N, &argc, argv);
+  //   if (!ohana_str_to_dtime (argv[N], &trange)) { 
+  //     gprint (GP_ERR, "syntax error\n");
+  //     return (FALSE);
+  //   }
+  //   remove_argument (N, &argc, argv);
+  //   if (trange < 0) {
+  //     trange = fabs (trange);
+  //     tzero -= trange;
+  //   }
+  //   TimeSelect = TRUE;
+  // }
+  // if ((N = get_argument (argc, argv, "-trange"))) {
+  //   remove_argument (N, &argc, argv);
+  //   if (!ohana_str_to_time (argv[N], &tzero)) { 
+  //     gprint (GP_ERR, "syntax error\n");
+  //     return (FALSE);
+  //   }
+  //   remove_argument (N, &argc, argv);
+  //   if (!ohana_str_to_time (argv[N], &tend)) { 
+  //     gprint (GP_ERR, "syntax error\n");
+  //     return (FALSE);
+  //   }
+  //   remove_argument (N, &argc, argv);
+  //   trange = tend - tzero;
+  //   if (trange < 0) {
+  //     trange = fabs (trange);
+  //     tzero -= trange;
+  //   }
+  //   TimeSelect = TRUE;
+  // }
+ 
+  if (argc != 3) {
+    gprint (GP_ERR, "USAGE: objectcoverage (buffer) (photcode)\n");
+    gprint (GP_ERR, "  options: [-scale pixscale] [-center ra dec] [-size Nx Nx] [-proj projection] [-time start range] [-trange start stop] [-name name] [+mosaic] [-mosaic] [-density]\n");
+    gprint (GP_ERR, "       (buffer) saves bitmapped image\n");
+    gprint (GP_ERR, "       (photcode) ..........\n");
+    gprint (GP_ERR, "       -scale (pixscale)  : specifies the pixel size in degrees [1.0]\n");
+    gprint (GP_ERR, "       -center (ra) (dec) : specifies the center of the field [0.0, 0.0]\n");
+    gprint (GP_ERR, "       -size (Nx) (Ny)    : specifies the size of the image [360/scale, 180/scale]\n");
+    gprint (GP_ERR, "       -proj (projection) : specifies the projection choice [AIT]\n");
+    gprint (GP_ERR, "       -density           : create image with relative density (else binary on/off)\n");
+    gprint (GP_ERR, "       note: we need 64800 / (pixscale)^2 pixels to represent the sky\n");
+    return (FALSE);
+  }
+  
+  if ((buf = SelectBuffer (argv[1], ANYBUFFER, TRUE)) == NULL) return (FALSE);
+  int myCode = GetPhotcodeCodebyName (argv[2]);
+  if (myCode == 0) {
+      gprint (GP_ERR, "invalid photcode\n");
+      return (FALSE);
+  }
+
+  if (!Nx || !Ny) {
+    Nx = 360/pixscale;
+    Ny = 180/pixscale;
+  }
+
+  gfits_free_matrix (&buf[0].matrix);
+  gfits_free_header (&buf[0].header);
+  CreateBuffer (buf, Nx, Ny, -32, 0.0, 1.0);
+  strcpy (buf[0].file, "(empty)");
+
+  coords.crval1 = RaCenter;
+  coords.crval2 = DecCenter;
+  coords.crpix1 = 0.5*Nx;
+  coords.crpix2 = 0.5*Ny;
+  strcpy (coords.ctype, projection);
+  coords.pc1_1 = -1.0;
+  coords.pc2_2 = +1.0;
+  coords.pc1_2 =  0.0;
+  coords.pc2_1 =  0.0;
+  coords.cdelt1 = coords.cdelt2 = pixscale;
+  coords.Npolyterms = 0;
+
+  PutCoords (&coords, &buf[0].header);
+
+  V = (float *)buf[0].matrix.buffer;
+
+  for (ys = 0; ys < Ny; ys++) {
+    for (xs = 0; xs < Nx; xs++) {
+      status = XY_to_RD (&r, &d, (double)(xs), (double)(ys), &coords);
+      status &= (r >= 0);
+      status &= (r <= 360);
+      if (status) {
+	V[ys*Nx + xs] = ShowDensity ?  0 : 2;
+      } else {
+	V[ys*Nx + xs] = ShowDensity ? -1 : 0;
+      }
+    }
+  }
+
+  Nsecfilt = GetPhotcodeNsecfilt();
+  // grab data from all selected sky regions
+  signal (SIGINT, handle_interrupt);
+  interrupt = FALSE;
+
+  /* load region corresponding to selection above */
+  if ((skylist = SelectRegions (selection)) == NULL) goto escape;
+
+  /* loop over regions, extract data for each region */
+  for (k = 0; (k < skylist[0].Nregions) && !interrupt; k++) {
+    /* lock, load, unlock catalog */
+    catalog.filename = skylist[0].filename[k];
+    catalog.catflags = LOAD_AVES | LOAD_MEAS | LOAD_SECF;
+    catalog.Nsecfilt = 0;
+
+    // an error exit status here is a significant error
+    if (!dvo_catalog_open (&catalog, NULL, FALSE, "r")) {
+      fprintf (stderr, "ERROR: failure to open catalog file %s\n", catalog.filename);
+      exit (2);
+    }
+    dvo_catalog_unlock (&catalog);
+
+    int Nsec = GetPhotcodeNsec(myCode);
+
+    for (j = 0; j <  catalog.Naverage; j++) {
+	if (catalog.average[j].Nmeasure < 2) { continue; }
+
+	
+	if (catalog.secfilt[j*Nsecfilt+Nsec].Ncode < 2) { continue; }
+
+	invalid = ((catalog.secfilt[j*Nsecfilt + Nsec].M < 1.0) || (isnan(catalog.secfilt[j*Nsecfilt + Nsec].M)));
+	if (invalid) continue;
+	
+	double r = catalog.average[j].R;
+	double d = catalog.average[j].D;
+	status = RD_to_XY (&Xs, &Ys, r, d, &coords);
+	if (Xs < 0) continue;
+	if (Ys < 0) continue;
+	if (Xs >= Nx) continue;
+	if (Ys >= Ny) continue;
+	if (status) {
+	    xs = (int)Xs;
+	    ys = (int)Ys;
+	    if (ShowDensity) {
+		V[ys*Nx + xs] += 1;
+	    } else {
+		V[ys*Nx + xs] = 1;
+	    }
+	}
+    }
+  }
+  return (TRUE);
+
+escape:
+  return (FALSE);
+}
+
+
Index: /branches/sc_branches/pantasks_condor/dvo/paverage.c
===================================================================
--- /branches/sc_branches/pantasks_condor/dvo/paverage.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/dvo/paverage.c	(revision 34783)
@@ -0,0 +1,169 @@
+# include "dvoshell.h"
+# define NCHUNK 10000
+
+int paverage (int argc, char **argv) {
+  
+  off_t i, j;
+  int kapa, Narg, Npts, NPTS, status, VERBOSE;
+  int Nsecfilt, Nsec, Nloaded;
+  double Mz, Mr, mag;
+  double Radius, Rmin, Rmax, R, D;
+  unsigned IDclip, IDchoice, LimExclude;
+  float *Xvec, *Yvec, *Zvec;
+
+  PhotCode *photcode;
+  SkyTable *sky;
+  SkyList *skylist;
+  Catalog catalog;
+  Graphdata graphmode;
+  Average *average;
+  SecFilt *secfilt;
+
+  if (!InitPhotcodes ()) return (FALSE);
+  Nsecfilt = GetPhotcodeNsecfilt ();
+
+  if (!style_args (&graphmode, &argc, argv, &kapa)) return FALSE;
+
+  Mz = 17.0;
+  Mr = -5.0;
+  Rmin = graphmode.coords.crval1 - 182.0;
+  Rmax = graphmode.coords.crval1 + 182.0;
+
+  // require a photcode?  default to 0?
+  Nsec = 0;
+  if ((Narg = get_argument (argc, argv, "-p"))) {
+    remove_argument (Narg, &argc, argv);
+    photcode = GetPhotcodebyName (argv[Narg]);
+    if (!photcode) {
+      fprintf (stderr, "unknown photcode %s\n", argv[Narg]);
+      return (FALSE);
+    }
+    remove_argument (Narg, &argc, argv);
+    Nsec = GetPhotcodeNsec (photcode[0].code);
+    if (Nsec == -1) {
+      fprintf (stderr, "photcode %s is not an AVERAGE photcode\n", argv[Narg]);
+      return (FALSE);
+    }
+  }
+
+  IDchoice = 0;
+  IDclip = FALSE;
+  if ((Narg = get_argument (argc, argv, "-ID"))) {
+    remove_argument (Narg, &argc, argv);
+    IDchoice  = atoi(argv[Narg]);
+    remove_argument (Narg, &argc, argv);
+    IDclip = TRUE;
+  }
+
+  LimExclude = FALSE;
+  if ((Narg = get_argument (argc, argv, "-x"))) {
+    remove_argument (Narg, &argc, argv);
+    LimExclude = TRUE;
+  }
+
+  VERBOSE = FALSE;
+  if ((Narg = get_argument (argc, argv, "-v"))) {
+    remove_argument (Narg, &argc, argv);
+    VERBOSE = TRUE;
+  }
+
+  if ((Narg = get_argument (argc, argv, "-m"))) {
+    remove_argument (Narg, &argc, argv);
+    Mr  = atof(argv[Narg]);
+    remove_argument (Narg, &argc, argv);
+    Mz = atof(argv[Narg]);
+    Mr = Mr - Mz;
+    remove_argument (Narg, &argc, argv);
+  }
+
+  if (argc != 2) {
+    gprint (GP_ERR, "USAGE: paverage (-all) [-m M M] [-p photcode] [-ID ID] [-flag value] [-x]\n");
+    return (FALSE);
+  }
+  graphmode.style = 2; /* set style to points */
+  graphmode.size = -1; /* point size determined by Zvec */
+  graphmode.etype = 0; /* no errorbars */
+
+  Radius = MAX (fabs(graphmode.xmax), fabs(graphmode.ymax));
+
+  /* load sky from correct table */
+  sky = GetSkyTable ();
+  skylist = SkyListByRadius (sky, -1, graphmode.coords.crval1, graphmode.coords.crval2, Radius);
+  
+  /* storage for plotting the points */
+  Npts = 0;
+  NPTS = 1000;
+  ALLOCATE (Xvec, float, NPTS);
+  ALLOCATE (Yvec, float, NPTS);
+  ALLOCATE (Zvec, float, NPTS);
+
+  // prepare to handle interrupt signals
+  signal (SIGINT, handle_interrupt);
+  interrupt = FALSE;
+
+  Nloaded = 0;
+  for (j = 0; (j < skylist[0].Nregions) && !interrupt; j++) {
+    catalog.filename = skylist[0].filename[j];
+    catalog.catflags = LOAD_AVES | LOAD_SECF;
+    catalog.Nsecfilt = Nsecfilt;
+
+    // an error exit status here is a significant error
+    if (!dvo_catalog_open (&catalog, NULL, VERBOSE, "r")) {
+      fprintf (stderr, "ERROR: failure to open catalog file %s\n", catalog.filename);
+      exit (2);
+    }
+    dvo_catalog_unlock (&catalog);
+
+    average = catalog.average;
+    secfilt = catalog.secfilt;
+
+    /* project stars to screen display coords */
+    for (i = 0; (i < catalog.Naverage) && !interrupt; i++) {
+      if (IDclip && (average[i].flags != IDchoice)) continue;
+      average[i].R = ohana_normalize_angle (average[i].R);
+      while (average[i].R < Rmin) average[i].R += 360.0;
+      while (average[i].R > Rmax) average[i].R -= 360.0;
+
+      mag = secfilt[i*Nsecfilt+Nsec].M;
+      Zvec[Npts] = MIN (1.0, MAX (0.01, (mag - Mz) / Mr));
+      if (LimExclude && (Zvec[Npts] > 0.99)) continue;
+      if (Zvec[Npts] < 0.011) continue;
+      R = average[i].R;
+      D = average[i].D;
+      status = fRD_to_XY (&Xvec[Npts], &Yvec[Npts], R, D, &graphmode.coords);
+      if (!status) continue;
+      Npts ++;
+
+      if (Npts == NPTS - 1) {
+	NPTS += 1000;
+	REALLOCATE (Xvec, float, NPTS);
+	REALLOCATE (Yvec, float, NPTS);
+	REALLOCATE (Zvec, float, NPTS);
+      }
+      if ((Npts > NCHUNK) || (Nloaded >= 25)) {
+	KapaPrepPlot (kapa, Npts, &graphmode);
+	KapaPlotVector (kapa, Npts, Xvec, "x");
+	KapaPlotVector (kapa, Npts, Yvec, "y");
+	KapaPlotVector (kapa, Npts, Zvec, "z");
+	Npts = 0;
+	Nloaded = 0;
+      }
+    }
+    Nloaded ++;
+    dvo_catalog_free (&catalog);
+  }
+  if (Npts > 0) {
+    KapaPrepPlot (kapa, Npts, &graphmode);
+    KapaPlotVector (kapa, Npts, Xvec, "x");
+    KapaPlotVector (kapa, Npts, Yvec, "y");
+    KapaPlotVector (kapa, Npts, Zvec, "z");
+  }
+
+  free (Xvec);
+  free (Yvec);
+  free (Zvec);
+
+  return (TRUE);
+
+}
+  
Index: /branches/sc_branches/pantasks_condor/dvo/photcodes.c
===================================================================
--- /branches/sc_branches/pantasks_condor/dvo/photcodes.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/dvo/photcodes.c	(revision 34783)
@@ -0,0 +1,63 @@
+# include "dvoshell.h"
+
+/* list or return all photcodes equivalent to the given filter */
+int photcodes (int argc, char **argv) {
+  
+  int i, Np;
+  int *list, Nlist;
+  char name[64];
+  PhotCode *code;
+
+  if (argc != 2) {
+    gprint (GP_ERR, "USAGE: photcodes (photcode)\n");
+    return (FALSE);
+  }
+
+  /* load photcodes, convert name to code */
+  if (!InitPhotcodes ()) return (FALSE);
+
+  if (!(Np = GetPhotcodeCodebyName (argv[1]))) {
+    gprint (GP_ERR, "ERROR: photcode not found in photcode table\n");
+    return (FALSE);
+  }
+
+  list = GetPhotcodeEquivList (Np, &Nlist);
+  
+  for (i = 0; i < Nlist; i++) {
+    code = GetPhotcodebyCode (list[i]);
+
+    sprintf (name, "photcode:name:%d", i);
+    set_str_variable (name, code[0].name);
+
+    sprintf (name, "photcode:C:%d", i);
+    set_variable (name, 0.001*code[0].C);
+
+    sprintf (name, "photcode:K:%d", i);
+    set_variable (name, code[0].K);
+
+    sprintf (name, "photcode:X:%d", i);
+    set_variable (name, code[0].X[0]);
+
+    sprintf (name, "photcode:dX:%d", i);
+    set_variable (name, 0.001*code[0].dX);
+
+    sprintf (name, "photcode:code:%d", i);
+    set_int_variable (name, code[0].code);
+
+    sprintf (name, "photcode:filter:%d", i);
+    set_str_variable (name, GetPhotcodeNamebyCode (code[0].equiv));
+
+    sprintf (name, "photcode:c1:%d", i);
+    set_str_variable (name, GetPhotcodeNamebyCode (code[0].c1));
+
+    sprintf (name, "photcode:c2:%d", i);
+    set_str_variable (name, GetPhotcodeNamebyCode (code[0].c2));
+
+    gprint (GP_ERR, "%5d %s %7.4f %7.4f %7.4f\n", 
+	     code[0].code, code[0].name, 0.001*code[0].C, code[0].K, code[0].X[0]);
+  }
+  set_int_variable ("photcode:n", Nlist);
+  free (list);
+  return (TRUE);
+}
+
Index: /branches/sc_branches/pantasks_condor/dvo/photometry.c
===================================================================
--- /branches/sc_branches/pantasks_condor/dvo/photometry.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/dvo/photometry.c	(revision 34783)
@@ -0,0 +1,1218 @@
+# include "dvoshell.h"
+
+/* match code to measure  */
+# define TESTCODE(C,M)							\
+  if (C != NULL) {							\
+    switch (C[0].type) {						\
+      case PHOT_DEP:							\
+      case PHOT_REF:							\
+	if (C[0].code != M.photcode) continue;				\
+	break;								\
+      case PHOT_SEC:							\
+	if (C[0].code != GetPhotcodeEquivCodebyCode (M.photcode)) continue; \
+	break;								\
+      default:								\
+	break;								\
+    } }
+
+/* exclusions based on measure.params  */
+# define TESTMEASURE(M)							\
+  if (ApplySelections[SelectionParam]) {				\
+    if (TimeSelect && (M.t < tzero)) continue;				\
+    if (TimeSelect && (M.t > tend)) continue;				\
+    if (ErrSelect  && (M.dM > ErrValue)) continue;			\
+    if (iMagSelect && (PhotInst (&M) < iMagMin)) continue;		\
+    if (FlagSelect && (M.dbFlags != FlagValue)) continue;		\
+    if (TypeSelect && (TypeValue != GetMeasureTypeCode (&M))) continue; \
+  }
+
+# define SETMAG(MOUT,MEAS,MODE)						\
+  MOUT = NAN;								\
+  if (MODE == MAG_INST) MOUT = PhotInst (&MEAS);			\
+  if (MODE == MAG_CAT)  MOUT = PhotCat  (&MEAS);			\
+  if (MODE == MAG_SYS)  MOUT = PhotSys  (&MEAS, average, secfilt);	\
+  if (MODE == MAG_REL)  MOUT = PhotRel  (&MEAS, average, secfilt);	\
+  if (MODE == MAG_CAL)  MOUT = PhotCal  (&MEAS, average, secfilt, measure, GetPhotcodeEquivbyCode (MEAS.photcode)); \
+  if (MODE == MAG_AVE)  MOUT = PhotAve  (GetPhotcodeEquivbyCode (MEAS.photcode), average, secfilt); \
+  if (MODE == MAG_REF)  MOUT = PhotRef  (GetPhotcodeEquivbyCode (MEAS.photcode), average, secfilt, measure); \
+  if (ApplySelections[SelectionParam]) {				\
+    if (MagSelect && (MOUT > MagMax)) continue;				\
+    if (MagSelect && (MOUT < MagMin)) continue;				\
+  }
+
+/* selection criteria */
+/* selections based on Measure quantities */
+static int TimeSelect;
+static time_t tzero, tend;
+static int MagSelect;
+static double MagMax, MagMin;
+static int TypeSelect, TypeValue;
+static int ErrSelect;
+static double ErrValue;
+static int iMagSelect;
+static double iMagMin;
+static int FlagSelect, FlagValue;
+static int TypefracSelect, TypefracType, TypefracSign;
+static double TypefracValue;
+
+/* apply selections or not */
+static int ApplySelections[4];
+static int SelectionParam;
+
+/* applied to Average quantities */
+static int PhotcodeSelect;
+static PhotCode *PhotcodeValue;
+static int PhotcodeMode;
+
+/* selections based on Average quantities */
+static int ChiSelect;
+static float ChiLimit;
+
+/* selections based on ensemble quantities */
+static int NphotSelect, NphotSign, NphotValue;
+static int NcodeSelect, NcodeSign, NcodeValue;
+static int FWHMSelect, FWHMsign;
+static double FWHMvalue, FWHMfrac;
+
+/* time concepts */
+static time_t TimeReference;
+static int TimeFormat;
+
+int GetTimeSelection (time_t *tz, time_t *te) {
+  *tz = tzero;
+  *te = tend;
+  return (TimeSelect);
+}
+
+int GetPhotcodeInfo (char *string, PhotCode **Code, int *Mode) {
+
+  PhotCode *code;
+  int mode, status;
+  char *p, *tmpstring;
+
+  /* save local copy */
+  tmpstring = strcreate (string);
+
+  /* check for code:mode in photcode name */
+  mode = MAG_NONE;
+  p = strchr (tmpstring, ':');
+  if (p != NULL) {
+    mode = GetMagMode (p + 1);
+    if (mode == MAG_NONE) {
+      gprint (GP_ERR, "syntax error in magnitude mode\n");
+      free (tmpstring);
+      return (FALSE);
+    }
+    *p = 0;
+  }
+
+  /* how do we handle this elsewhere? */
+  if (!strcasecmp (tmpstring, "mag")) {
+    /* need to validate mode */
+    *Mode = mode;
+    *Code = NULL;
+    free (tmpstring);
+    return (TRUE);
+  }
+
+  code = GetPhotcodebyName (tmpstring);
+  if (code == NULL) {
+    gprint (GP_ERR, "photcode not found in photcode table\n");
+    free (tmpstring);
+    return (FALSE);
+  }
+
+  /* test allowable cases and/or set default values */
+  status = FALSE;
+  if (code[0].type == PHOT_DEP) {
+    if (mode == MAG_NONE) mode = MAG_REL;
+    if (mode == MAG_INST) status = TRUE;
+    if (mode == MAG_CAT)  status = TRUE;
+    if (mode == MAG_SYS)  status = TRUE;
+    if (mode == MAG_REL)  status = TRUE;
+    if (mode == MAG_CAL)  status = TRUE;
+  }  
+  if (code[0].type == PHOT_SEC) {
+    if (mode == MAG_NONE) mode  = MAG_AVE;
+    if (mode == MAG_INST) status = TRUE;
+    if (mode == MAG_CAT)  status = TRUE;
+    if (mode == MAG_SYS)  status = TRUE;
+    if (mode == MAG_REL)  status = TRUE;
+    if (mode == MAG_CAL)  status = TRUE;
+    if (mode == MAG_AVE)  status = TRUE;
+    if (mode == MAG_REF)  status = TRUE;
+  }  
+  if (code[0].type == PHOT_ALT) {
+    if (mode == MAG_NONE) mode  = MAG_AVE;
+    if (mode == MAG_AVE)  status = TRUE;
+    if (mode == MAG_REF)  status = TRUE;
+  }
+
+  if (code[0].type == PHOT_REF) {
+    if (mode == MAG_NONE) mode  = MAG_CAT;
+    if (mode == MAG_CAT)  status = TRUE;
+  }
+
+  if (!status) {
+    gprint (GP_ERR, "mismatch in photcode and magmode\n");
+    free (tmpstring);
+    return (FALSE);
+  }
+  *Code = code;
+  *Mode = mode;
+  free (tmpstring);
+  return (TRUE);
+}
+ 
+int SetSelectionParam (int param) {
+  SelectionParam = param;
+  return (TRUE);
+}
+
+int GetSelectionParam () {
+  return (SelectionParam);
+}
+
+int GetMeasureParam (char *parname) {
+
+  int param;
+
+  param = MEAS_ZERO;
+  if (!strcasecmp (parname, "ra"))   	 param = MEAS_RA;
+  if (!strcasecmp (parname, "dec"))  	 param = MEAS_DEC;
+  if (!strcasecmp (parname, "mag")) 	 param = MEAS_MAG;
+  if (!strcasecmp (parname, "airmass"))  param = MEAS_AIRMASS;
+  if (!strcasecmp (parname, "exptime"))  param = MEAS_EXPTIME;
+  if (!strcasecmp (parname, "photcode")) param = MEAS_PHOTCODE;
+  if (!strcasecmp (parname, "time"))     param = MEAS_TIME;
+  if (!strcasecmp (parname, "dR")) 	 param = MEAS_RA_OFFSET;
+  if (!strcasecmp (parname, "dD")) 	 param = MEAS_DEC_OFFSET;
+  if (!strcasecmp (parname, "fwhm"))   	 param = MEAS_FWHM;
+  if (!strcasecmp (parname, "FLAGS"))    param = MEAS_DB_FLAGS;
+  if (!strcasecmp (parname, "XCCD"))   	 param = MEAS_XCCD;
+  if (!strcasecmp (parname, "YCCD"))   	 param = MEAS_YCCD;
+  if (!strcasecmp (parname, "XMOSAIC"))  param = MEAS_XMOSAIC;
+  if (!strcasecmp (parname, "YMOSAIC"))  param = MEAS_YMOSAIC;
+  if (!strcasecmp (parname, "help")) {
+    gprint (GP_ERR, "value may be one of the following:\n");
+    gprint (GP_ERR, " ra dR dec dD mag dmag Mrel Mcal photcode time fwhm xccd yccd xmosaic ymosaic flags\n");
+    gprint (GP_ERR, "value may also be a valid photcode\n");
+    gprint (GP_ERR, "photcodes or 'mag' may have optional magnitude type: mag,[Minst, Mcat, Msys, Mrel, Mcal]\n");
+  }
+  return (param);
+}
+  
+int GetAverageParam (char *parname) {
+
+  int param;
+
+  param = AVE_ZERO;
+  if (!strcasecmp (parname, "RA"))    param = AVE_RA;
+  if (!strcasecmp (parname, "DEC"))   param = AVE_DEC;
+
+  if (!strcasecmp (parname, "dRA"))   param = AVE_RA_ERR;
+  if (!strcasecmp (parname, "dDEC"))  param = AVE_DEC_ERR;
+
+  if (!strcasecmp (parname, "uRA"))   param = AVE_U_RA;
+  if (!strcasecmp (parname, "uDEC"))  param = AVE_U_DEC;
+  if (!strcasecmp (parname, "duRA"))  param = AVE_U_RA_ERR;
+  if (!strcasecmp (parname, "duDEC")) param = AVE_U_DEC_ERR;
+
+  if (!strcasecmp (parname, "par"))   param = AVE_PAR;
+  if (!strcasecmp (parname, "dpar"))  param = AVE_PAR_ERR;
+
+  if (!strcasecmp (parname, "dmag"))  param = AVE_dMAG;
+  if (!strcasecmp (parname, "mag"))   param = AVE_MAG;
+  if (!strcasecmp (parname, "Nmeas")) param = AVE_NMEAS;
+  if (!strcasecmp (parname, "Nmiss")) param = AVE_NMISS;
+  if (!strcasecmp (parname, "Xm"))    param = AVE_Xm;
+  if (!strcasecmp (parname, "flag"))  param = AVE_OBJ_FLAGS;
+  if (!strcasecmp (parname, "type"))  param = AVE_TYPE;
+  if (!strcasecmp (parname, "typefrac")) {
+    if (!TypefracType) {
+      gprint (GP_ERR, "typefrac needs to specify type to use\n");
+      return (param);
+    }
+    param = AVE_TYPEFRAC;
+  }
+  if (!strcasecmp (parname, "Nphot")) param = AVE_NPHOT;
+  if (!strcasecmp (parname, "Ncode")) param = AVE_NCODE;
+  // if (!strcasecmp (parname, "Ncrit")) param = AVE_NCRIT;
+  return (param);
+}
+
+/* I've set some selections - if these require a photcode, check if I set one */
+int TestPhotSelections (PhotCode **code, int *mode, int param) {
+
+  int NeedPhotcode, Needcode;
+
+  /* if i've supplied a photcode (code != NULL), i'm not allowed to restrict it */
+  if (code[0] != NULL) {
+    if (PhotcodeSelect) {
+      gprint (GP_ERR, "photcode selection rules violated: cannot restrict photcode with a photcode\n");
+      return (FALSE);
+    } else {
+      return (TRUE);
+    }
+  }
+
+  /* for measure tests, supply MEAS_ZERO */
+
+  /* if I have an average or ensemble restriction, I must have a PRI/SEC photcode */
+  NeedPhotcode = FALSE;
+  NeedPhotcode |= ChiSelect;
+  NeedPhotcode |= NphotSelect;
+  NeedPhotcode |= ErrSelect;
+  NeedPhotcode |= TypeSelect;
+  NeedPhotcode |= TypefracSelect;
+  
+  NeedPhotcode |= (param == AVE_Xm);
+  NeedPhotcode |= (param == AVE_MAG);
+  NeedPhotcode |= (param == AVE_dMAG);
+  NeedPhotcode |= (param == AVE_TYPE);
+  NeedPhotcode |= (param == AVE_NPHOT);
+  Needcode = (param == AVE_NCODE);
+
+  if (NeedPhotcode || Needcode || NcodeSelect || PhotcodeSelect) {
+    if (!PhotcodeSelect) {
+      gprint (GP_ERR, "photcode selection problem: value requires photcode\n");
+      return (FALSE);
+    }
+    code[0] = PhotcodeValue;
+    mode[0] = PhotcodeMode;
+  }
+  if (NeedPhotcode) {
+    if (code[0][0].type == PHOT_SEC) return (TRUE);
+    if (code[0][0].type == PHOT_REF) return (TRUE);
+    gprint (GP_ERR, "photcode selection problem: average value requires average photcode\n");
+    return (FALSE);
+  }
+  return (TRUE);
+}
+
+void GetAverageParamHelp () {
+  gprint (GP_ERR, "value may be one of the following:\n");
+  gprint (GP_ERR, " ra dec dmag Nmeas Nmiss Xm Nphot Ncode flag type typefrac\n\n");
+  gprint (GP_ERR, "value may also be a valid photcode\n");
+  gprint (GP_ERR, "photcodes or 'mag' may have optional magnitude mode: mag,[Mave, Mref]\n");
+  return;
+}
+
+/* (re)load photcodes from photcode table */
+int InitPhotcodes () {
+
+  double ZERO_POINT;
+  char MasterPhotcodeFile[256];
+  char CatdirPhotcodeFile[256];
+  char *catdir;
+
+  if (VarConfig ("ZERO_PT", "%lf", &ZERO_POINT) == (char *) NULL) {
+    gprint (GP_ERR, "ZERO_PT undefined in config\n");
+    return (FALSE);
+  }
+  SetZeroPoint (ZERO_POINT);
+
+  catdir = GetCATDIR();
+  if (catdir == NULL) {
+    CatdirPhotcodeFile[0] = 0;
+  } else {
+    sprintf (CatdirPhotcodeFile, "%s/Photcodes.dat", catdir);
+  }
+
+  if (VarConfig ("PHOTCODE_FILE", "%s", MasterPhotcodeFile) == (char *) NULL) {
+    gprint (GP_ERR, "PHOTCODE_FILE undefined in config\n");
+    return (FALSE);
+  }
+
+  // XXX now that DVO does not allow write access, we can drop the MasterPhotcodeFile
+  if (!LoadPhotcodes (CatdirPhotcodeFile, MasterPhotcodeFile, FALSE)) {
+    gprint (GP_ERR, "error loading photcode table %s or master file %s\n", CatdirPhotcodeFile, MasterPhotcodeFile);
+    return (FALSE);
+  }
+  return (TRUE);
+}
+
+int ListPhotSelections () {
+
+  gprint (GP_ERR, "TimeSelect: %d, %s - %s\n",      TimeSelect, ctime(&tzero), ctime(&tend));
+  gprint (GP_ERR, "MagSelect: %d, %f - %f\n",       MagSelect, MagMax, MagMin);
+  gprint (GP_ERR, "TypeSelect: %d, %d\n",           TypeSelect, TypeValue);
+  gprint (GP_ERR, "ErrSelect: %d, %f\n",            ErrSelect, ErrValue);
+  gprint (GP_ERR, "iMagSelect: %d, %f\n",           iMagSelect, iMagMin);
+  gprint (GP_ERR, "FlagSelect: %d, %x\n",           FlagSelect, FlagValue);
+  gprint (GP_ERR, "TypefracSelect: %d, %d %d %f\n", TypefracSelect, TypefracType, TypefracSign, TypefracValue);
+  gprint (GP_ERR, "ApplySelections: %d,%d,%d,%d : %d\n", ApplySelections[0], ApplySelections[1], ApplySelections[2], ApplySelections[3], SelectionParam);
+  if (PhotcodeSelect) {
+    gprint (GP_ERR, "PhotcodeSelect: %d, %s\n",       PhotcodeSelect, PhotcodeValue[0].name);
+  } else {
+    gprint (GP_ERR, "PhotcodeSelect: %d, none\n",       PhotcodeSelect);
+  }
+  gprint (GP_ERR, "ChiSelect: %d, %f\n",            ChiSelect, ChiLimit);
+  gprint (GP_ERR, "NphotSelect: %d, %d - %d\n",     NphotSelect, NphotSign, NphotValue);
+  gprint (GP_ERR, "NcodeSelect: %d, %d - %d\n",     NcodeSelect, NcodeSign, NcodeValue);
+  gprint (GP_ERR, "FWHMSelect: %d, %d %f %f\n",     FWHMSelect, FWHMsign, FWHMvalue, FWHMfrac);
+  return (TRUE);
+}
+
+/* remove standard photometry filtering options, set selections */
+/* not all functions respect all selections... */
+int SetPhotSelections (int *argc, char **argv, int Nparams) {
+
+  int i, N;
+  double trange;
+
+  if ((N = get_argument (*argc, argv, "-phothelp"))) {
+    gprint (GP_ERR, "optional photometry selection criteria:\n");
+    gprint (GP_ERR, " -magrange min max\n");
+    gprint (GP_ERR, " -imaglim min\n");
+    gprint (GP_ERR, " -flag value\n");
+    gprint (GP_ERR, " -chisq value\n");
+    gprint (GP_ERR, " -photcode code\n");
+    gprint (GP_ERR, " -time start range\n");
+    gprint (GP_ERR, " -errorlim value\n");
+    gprint (GP_ERR, " -type type\n");
+    gprint (GP_ERR, " -nmeas [+/-]N\n");
+    gprint (GP_ERR, " -fwhm [+/-]fraction\n");
+    return (FALSE);
+  }
+
+  /* select based on measured mag (MEASURE ONLY) */
+  MagSelect = FALSE;
+  if ((N = get_argument (*argc, argv, "-magrange"))) {
+    MagSelect = TRUE;
+    remove_argument (N, argc, argv);
+    MagMin = atof (argv[N]);
+    remove_argument (N, argc, argv);
+    MagMax = atof (argv[N]);
+    remove_argument (N, argc, argv);
+  }
+
+  /* select based on instrument mag (MEASURE ONLY) */
+  iMagSelect = FALSE;
+  if ((N = get_argument (*argc, argv, "-imaglim"))) {
+    iMagSelect = TRUE;
+    remove_argument (N, argc, argv);
+    iMagMin = atof (argv[N]);
+    remove_argument (N, argc, argv);
+  }
+
+  /* select on value of flag (MEASURE ONLY) */
+  FlagSelect = FALSE;
+  if ((N = get_argument (*argc, argv, "-flag"))) {
+    FlagSelect = TRUE;
+    remove_argument (N, argc, argv);
+    FlagValue = atof (argv[N]);
+    remove_argument (N, argc, argv);
+  }
+
+  /* select on value of Chisq (AVERAGE ONLY) */
+  SelectionParam = 0;
+  for (i = 0; i < 4; i++) ApplySelections[i] = TRUE;
+  if ((N = get_argument (*argc, argv, "-apply"))) {
+    remove_argument (N, argc, argv);
+    if (strlen(argv[N]) != Nparams) {
+      gprint (GP_ERR, "-apply selection must define all parameter choices\n");
+      return (FALSE);
+    }
+    for (i = 0; i < Nparams; i++) {
+      if (toupper(argv[N][i]) == 'Y') {
+	ApplySelections[i] = TRUE;
+      } else {
+	ApplySelections[i] = FALSE;
+      }
+    }
+    remove_argument (N, argc, argv);
+  }
+
+  /* select on value of photcode */
+  PhotcodeSelect = FALSE;
+  if ((N = get_argument (*argc, argv, "-photcode"))) {
+    PhotcodeSelect = TRUE;
+    remove_argument (N, argc, argv);
+    GetPhotcodeInfo (argv[N], &PhotcodeValue, &PhotcodeMode);
+    if (PhotcodeValue == NULL) {
+      gprint (GP_ERR, "photcode not found in photcode table\n");
+      return (FALSE);;
+    }
+    remove_argument (N, argc, argv);
+  }
+
+  /* selection on basis of time range (MEASURE only) */
+  TimeSelect = FALSE;
+  if ((N = get_argument (*argc, argv, "-time"))) {
+    remove_argument (N, argc, argv);
+    if (!ohana_str_to_time (argv[N], &tzero)) { 
+      gprint (GP_ERR, "syntax error\n");
+      return (FALSE);
+    }
+    remove_argument (N, argc, argv);
+    if (!ohana_str_to_dtime (argv[N], &trange)) { 
+      gprint (GP_ERR, "syntax error\n");
+      return (FALSE);
+    }
+    remove_argument (N, argc, argv);
+    TimeSelect = TRUE;
+    if (trange < 0) {
+      trange = fabs (trange);
+      tend = tzero;
+      tzero -= trange;
+    } else {
+      tend = tzero + trange;
+    }
+  }
+
+  /* select by error (on measure or average) */
+  ErrSelect = FALSE;
+  if ((N = get_argument (*argc, argv, "-errorlim"))) {
+    remove_argument (N, argc, argv);
+    ErrValue = atof (argv[N]);
+    remove_argument (N, argc, argv);
+    ErrSelect = TRUE;
+  }
+
+  /* select on value of Chisq (AVERAGE ONLY) */
+  ChiSelect = FALSE;
+  if ((N = get_argument (*argc, argv, "-chisq"))) {
+    ChiSelect = TRUE;
+    remove_argument (N, argc, argv);
+    ChiLimit = atof (argv[N]);
+    remove_argument (N, argc, argv);
+  }
+
+  /* select on measurement type: 1,2,3 (AVERAGE ONLY) */
+  TypeSelect = FALSE;
+  if ((N = get_argument (*argc, argv, "-type"))) {
+    remove_argument (N, argc, argv);
+    TypeValue = atoi (argv[N]);
+    remove_argument (N, argc, argv);
+    TypeSelect = TRUE;
+  }
+
+  /* select on measurement type: 1,2,3 (AVERAGE ONLY) */
+  TypefracType = 0;
+  if ((N = get_argument (*argc, argv, "-usetype"))) {
+    remove_argument (N, argc, argv);
+    TypefracType = atoi (argv[N]);
+    remove_argument (N, argc, argv);
+  }
+
+  /* select on measurement type: 1,2,3 (AVERAGE ONLY) */
+  TypefracSelect = FALSE;
+  if ((N = get_argument (*argc, argv, "-typefrac"))) {
+    remove_argument (N, argc, argv);
+    TypefracType  = atoi (argv[N]);
+    remove_argument (N, argc, argv);
+    TypefracValue = fabs (atof (argv[N]));
+    TypefracSign = 0;
+    if (argv[N][0] == '-') TypefracSign = -1;
+    if (argv[N][0] == '+') TypefracSign = +1;
+    remove_argument (N, argc, argv);
+    TypefracSelect = TRUE;
+  }
+
+  /* select by number of measurements (AVERAGE ONLY) */
+  NphotSelect = FALSE;
+  if ((N = get_argument (*argc, argv, "-nphot"))) {
+    remove_argument (N, argc, argv);
+    NphotValue = abs (atoi (argv[N]));
+    NphotSign = 0;
+    if (argv[N][0] == '-') NphotSign = -1;
+    if (argv[N][0] == '+') NphotSign = +1;
+    remove_argument (N, argc, argv);
+    NphotSelect = TRUE;
+  }
+
+  /* select by number of measurements (AVERAGE ONLY) */
+  NcodeSelect = FALSE;
+  if ((N = get_argument (*argc, argv, "-ncode"))) {
+    remove_argument (N, argc, argv);
+    NcodeValue = abs (atoi (argv[N]));
+    NcodeSign = 0;
+    if (argv[N][0] == '-') NcodeSign = -1;
+    if (argv[N][0] == '+') NcodeSign = +1;
+    remove_argument (N, argc, argv);
+    NcodeSelect = TRUE;
+  }
+
+  /* -fwhm value frac (AVERAGE ONLY) */
+  FWHMSelect = FALSE;
+  if ((N = get_argument (*argc, argv, "-fwhm"))) {
+    remove_argument (N, argc, argv);
+    FWHMvalue = abs (atof (argv[N]));
+    FWHMsign = 0;
+    if (argv[N][0] == '-') FWHMsign = -1;
+    if (argv[N][0] == '+') FWHMsign = +1;
+    remove_argument (N, argc, argv);
+    FWHMSelect = TRUE;
+    remove_argument (N, argc, argv);
+    FWHMfrac = atof (argv[N]);
+  }
+
+  GetTimeFormat (&TimeReference, &TimeFormat);
+  return (TRUE);
+}
+
+/* extract a list of measure parameters from the specified average entry based on the pre-set selections */
+double *ExtractMeasures (PhotCode *code, int mode, Average *average, SecFilt *secfilt, Measure *measure, off_t *nlist, int param) {
+
+  off_t i, Nlist, NLIST;
+  double M, *list;
+  
+  *nlist = 0; 
+  Nlist = 0;
+  NLIST = MAX (1, average[0].Nmeasure);
+  ALLOCATE (list, double, NLIST);
+
+  /* check selections based on averages & ensembles: chisq, Nphot, etc */
+  if (!TestAverage (code, average, secfilt, measure)) return (list); 
+
+  /* look for measures */
+  for (i = 0; i < average[0].Nmeasure; i++) {
+    TESTCODE (code, measure[i]);  /* skip measurements not matching photcode */
+    TESTMEASURE (measure[i]);     /* exclusions based on measure.params  */
+    SETMAG (M, measure[i], mode); /* set appropriate magnitude (also does MagSelect) */ 
+
+    /* assign value */
+    list[Nlist] = GetMeasure (param, &average[0], &measure[i], M);
+    Nlist ++;
+  }
+  *nlist = Nlist;
+  return (list);
+}
+
+/* return average.param based on the selection */
+double ExtractAverages (PhotCode *code, int mode, Average *average, SecFilt *secfilt, Measure *measure, int param) {
+
+  off_t i;
+  double value;
+
+  value = NAN;
+
+  /* this function requires code set for certain value of param.  
+     use TestPhotSelectionsAverage to validate code/param choices */
+
+  /* filter by average quantities (eg, chisq, Nphot, etc) */
+  if (!TestAverage (code, average, secfilt, measure)) return (NAN);
+
+  /* assign vector values */
+  switch (param) {
+    case AVE_RA:
+      value = average[0].R;
+      break;
+    case AVE_DEC:
+      value = average[0].D;
+      break;
+    case AVE_RA_ERR:
+      value = average[0].dR;
+      break;
+    case AVE_DEC_ERR:
+      value = average[0].dD;
+      break;
+
+    case AVE_U_RA:
+      value = average[0].uR;
+      break;
+    case AVE_U_DEC:
+      value = average[0].uD;
+      break;
+    case AVE_U_RA_ERR:
+      value = average[0].duR;
+      break;
+    case AVE_U_DEC_ERR:
+      value = average[0].duD;
+      break;
+
+    case AVE_PAR:
+      value = average[0].P;
+      break;
+    case AVE_PAR_ERR:
+      value = average[0].dP;
+      break;
+
+
+    case AVE_NMEAS:
+      value = average[0].Nmeasure;
+      break;
+    case AVE_NMISS:
+      value = average[0].Nmissing;
+      break;
+    case AVE_OBJ_FLAGS:
+      value = average[0].flags;
+      break;
+    case AVE_MAG:
+      switch (mode) {
+	case MAG_AVE:
+	  value = PhotAve  (code, average, secfilt);
+	  break;
+	case MAG_REF:
+	  value = PhotRef  (code, average, secfilt, measure);
+	  break;
+	case MAG_INST:
+	case MAG_CAT:
+	case MAG_SYS:
+	case MAG_REL:
+	case MAG_CAL:
+	  value = NAN;
+	  for (i = 0; i < average[0].Nmeasure; i++) {
+	    if (code[0].code != measure[i].photcode) continue;
+	    value = measure[i].M;
+	  }
+	  break;
+      }
+      break;
+    case AVE_dMAG:
+      value = PhotdM (code, average, secfilt);
+      break;
+    case AVE_Xm:
+      value = PhotXm (code, average, secfilt);
+      break;
+    case AVE_TYPE:
+      value = DetermineTypeCode (average, measure, code[0].code);
+      break;
+    case AVE_TYPEFRAC:
+      // value = DetermineTypefrac (average, measure, code);
+      value = NAN;
+      break;
+    case AVE_NCODE:
+      value = 0;
+      for (i = 0; i < average[0].Nmeasure; i++) {
+	if (code[0].code != GetPhotcodeEquivCodebyCode (measure[i].photcode)) continue;
+	value ++;
+      }
+      break;
+    case AVE_NPHOT:
+      value = 0;
+      for (i = 0; i < average[0].Nmeasure; i++) {
+	if (code[0].code != GetPhotcodeEquivCodebyCode (measure[i].photcode)) continue;
+	if (measure[i].dbFlags & (ID_MEAS_POOR_PHOTOM | ID_MEAS_SKIP_PHOTOM)) continue;
+	value ++;
+      }
+      break;
+# if (0)
+    case AVE_NCRIT:
+      value = 0;
+      for (i = 0; i < average[0].Nmeasure; i++) {
+	if ((code != NULL) && (code[0].code != GetPhotcodeEquivCodebyCode (measure[i].photcode))) continue;
+	if (ErrSelect && (measure[i].dM > ErrValue)) continue;
+	if (FlagSelect && (measure[i].dbFlags != FlagValue)) continue;
+	if (TypeSelect && (TypeValue != GetMeasureTypeCode (&measure[i]))) continue;
+	if (iMagSelect && (PhotInst (&measure[i]) < iMagMin)) continue;
+	value ++;
+      }
+      break;
+# endif
+  }
+  return (value);
+}  
+
+/* return fraction of measures (matching code) which have requested type */
+double DetermineTypefrac (Average *average, Measure *measure, PhotCode *code) {
+
+  double frac;
+  int Nc, Nt;
+  off_t k;
+  
+  Nt = Nc = 0;
+  for (k = 0; k < average[0].Nmeasure; k++) {
+    if ((code != NULL) && (code[0].code != GetPhotcodeEquivCodebyCode (measure[k].photcode))) continue;
+    Nc ++;
+    if ((measure[k].photFlags >> 16) != TypefracType) continue;
+    Nt ++;
+  }
+  frac = (double) Nt / (double) Nc;
+  return (frac);
+}
+
+/* determine the representative dophot type for this photcode (must be PRI/SEC) */
+int DetermineTypeCode (Average *average, Measure *measure, int code) {
+
+  off_t k;
+  int N, Nt[3];
+  
+  Nt[0] = Nt[1] = Nt[2] = 0;
+  for (k = 0; k < average[0].Nmeasure; k++) {
+    if (code != GetPhotcodeEquivCodebyCode (measure[k].photcode)) continue;
+    N = GetMeasureTypeCode (&measure[k]);
+    Nt[N] ++;
+  }
+  if (Nt[0]) return (0);
+  if (Nt[1]) return (1);
+  if (Nt[2]) return (2);
+  return (3);
+}
+
+int GetMeasureTypeCode (Measure *measure) {
+  switch ((measure[0].photFlags >> 16)) {
+    case 0:
+    case 1:
+    case 2:
+      return (0);
+      break;
+    case 3:
+    case 4:
+    case 5:
+    case 7:
+    case 9:
+      return (1);
+      break;
+    case 10:
+    default:
+      return (2);
+  }
+  return (2);
+}  
+
+int Quality (Measure *measure, int IsDophot) {
+
+  return (TRUE);
+  
+  if (IsDophot) {
+    
+    if ((measure[0].photFlags >> 16) == 4) return (FALSE);
+    return (TRUE);
+  
+  } else {
+    
+    if (FromShortPixels(measure[0].FWx < 3.0)) return (FALSE);
+    if (FromShortPixels(measure[0].FWx > 10.0)) return (FALSE);
+
+    return (TRUE);
+
+  }
+}
+
+/* test if this average object meets the specified selection criteria.
+   for photcode-dependent quantities, only test for PRI/SEC photcodes */
+int TestAverage (PhotCode *code, Average *average, SecFilt *secfilt, Measure *measure) {
+
+  off_t i, Nm;
+  int Type, Select;
+  double fwhm, typefrac, dM, Xm;
+
+  /** temporary special case for Jen Katz: exclude REF with Ncode > 1 */
+  if ((code != NULL) && (code[0].type == PHOT_REF)) {
+    Nm = 0;
+    for (i = 0; i < average[0].Nmeasure; i++) {
+      TESTCODE (code, measure[i]);
+      Nm++;
+    }
+    if (Nm > 1) return (FALSE);
+  }
+
+  if (!ApplySelections[SelectionParam]) return (TRUE);
+
+  /* pass objects with more than FWHMfrac points with FWHM above / below FWHMvalue */ 
+  if (FWHMSelect) {
+    Nm = 0;
+    for (i = 0; i < average[0].Nmeasure; i++) {
+      fwhm = FromShortPixels(measure[i].FWx);
+      switch (FWHMsign) {
+	case 0:
+	  if (fwhm == FWHMvalue) break;
+	  continue;
+	case +1:
+	  if (fwhm >= FWHMvalue) break;
+	  continue;
+	case -1:
+	  if (fwhm <= FWHMvalue) break;
+	  continue;
+      }
+      Nm++;
+    }
+    if (average[0].Nmeasure * FWHMfrac > Nm) return (FALSE);
+  }
+
+  /* all selections below require a valid photcode */
+  Select = ChiSelect || ErrSelect || NcodeSelect || NphotSelect || TypeSelect || TypefracSelect;
+  if (!Select) return (TRUE);
+
+  /* must have a valid code of some kind */
+  if (code == NULL) return (FALSE);
+
+  /* for NcodeSelect, count Nmeas for appropriate photcode */
+  if (NcodeSelect) {
+    Nm = 0;
+    for (i = 0; i < average[0].Nmeasure; i++) {
+      TESTCODE (code, measure[i]);
+      Nm++;
+    }
+    switch (NcodeSign) {
+      case 0:
+	if (Nm == NcodeValue) break;
+	return (FALSE);
+      case 1:
+	if (Nm >= NcodeValue) break;
+	return (FALSE);
+      case -1:
+	if (Nm <= NcodeValue) break;
+	return (FALSE);
+      default:
+	return (FALSE);
+    }
+  }
+
+  /* only PRI/SEC photcodes apply the filter */
+  if (code[0].type == PHOT_DEP) return (TRUE);
+  if (code[0].type == PHOT_REF) return (TRUE);
+
+  /* exclusions based on average.params  */
+  if (ChiSelect) {
+    Xm = PhotXm (code, average, secfilt);
+    if (Xm == -1) return (FALSE);
+    if (Xm > ChiLimit) return (FALSE);
+  }
+  
+  /* for ErrSelect, check average errors */
+  if (ErrSelect) {
+    dM = PhotdM (code, average, secfilt);
+    if (dM > ErrValue) return (FALSE);
+  }
+  
+  /* for NphotSelect, count Nmeas for appropriate photcode */
+  if (NphotSelect) {
+    Nm = 0;
+    for (i = 0; i < average[0].Nmeasure; i++) {
+      TESTCODE (code, measure[i]);
+      if (measure[i].dbFlags && ID_MEAS_SKIP_PHOTOM) continue;
+      Nm++;
+    }
+    switch (NphotSign) {
+      case 0:
+	if (Nm == NphotValue) break;
+	return (FALSE);
+      case 1:
+	if (Nm >= NphotValue) break;
+	return (FALSE);
+      case -1:
+	if (Nm <= NphotValue) break;
+	return (FALSE);
+      default:
+	return (FALSE);
+    }
+  }
+
+  /* for TypeSelect, check on TypeCode for this object */
+  if (TypeSelect) {
+    Type = DetermineTypeCode (average, measure, code[0].code);
+    if (Type != TypeValue) return (FALSE);
+  }
+
+  /* for TypeSelect, check on TypeCode for this object */
+  if (TypefracSelect) {
+    typefrac = DetermineTypefrac (average, measure, code);
+    switch (TypefracSign) {
+      case 0:
+	if (typefrac == TypefracValue) break;
+	return (FALSE);	
+      case +1:
+	if (typefrac >= TypefracValue) break;
+	return (FALSE);	
+      case -1:
+	if (typefrac <= TypefracValue) break;
+	return (FALSE);	
+      default:
+	return (FALSE);
+    }
+  }
+
+  return (TRUE);
+}
+
+/* for this function, we don't need to call PhotRel, etc, but we
+   do need to multiply by 0.001:
+   average[].M is stored as 1000*mag where mag is PhotAbs
+   measure[].M for PHOT_REL is the same 
+   XXX EAM : note that we are transitioning away from millimag internal storage 
+*/ 
+
+/* send in:
+   Nphot - photcode number
+   Tphot - photcode type
+   Ns    - secfilt entry (-1 for PRI)
+   &catalog.average[i], 
+   &catalog.measure[catalog.average[i].measureOffset], 
+   &catalog.secfilt[i*Nsec] 
+*/
+
+
+double *ExtractMagnitudes (PhotCode *code, int mode, Average *average, SecFilt *secfilt, Measure *measure, off_t *n) {
+  
+  double *M, mag;
+  off_t N;
+
+  if ((mode == MAG_AVE) || (mode == MAG_REF)) {
+    ALLOCATE (M, double, 1);
+    mag = ExtractAverages (code, mode, average, secfilt, measure, AVE_MAG);
+    if (isnan(mag)) {
+      N = 0;
+    } else {
+      N = 1;
+      M[0] = mag;
+    }
+  } else {
+    M = ExtractMeasures (code, mode, average, secfilt, measure, &N, MEAS_MAG);
+  }
+  
+  *n = N;
+  return (M);
+}
+
+/* extract delta-mag pairs applying specified selections */
+double *ExtractDMag (PhotCode **code, int *mode, Average *average, SecFilt *secfilt, Measure *measure, off_t *nlist) {
+
+  int A1, A2;
+  off_t i, j, N1, N2, Np, Nlist, NLIST;
+  double *M1, *M2, *list;
+
+  /* check for special case of measure-measure - this is needed to drop self-matches */
+  A1 = ((mode[0] == MAG_AVE) || (mode[0] == MAG_REF));
+  A2 = ((mode[1] == MAG_AVE) || (mode[1] == MAG_REF));
+  if (!A1 && !A2) {
+    list = ExtractMeasuresDMag (code, mode, average, secfilt, measure, nlist);
+    return (list);
+  }
+
+  *nlist = 0; 
+  Nlist = 0;
+  NLIST = MAX (1, average[0].Nmeasure*average[0].Nmeasure);
+  ALLOCATE (list, double, NLIST);
+  M1 = M2 = NULL;
+
+  /* one of the two is an average, must do independently */
+  M1 = ExtractMagnitudes (code[0], mode[0], average, secfilt, measure, &N1);
+  if (N1 == 0) goto skip;
+  
+  Np = GetSelectionParam ();
+  SetSelectionParam (Np + 1);
+  M2 = ExtractMagnitudes (code[1], mode[1], average, secfilt, measure, &N2);
+  if (N2 == 0) goto skip;
+
+  /* magnitudes may be NAN : set delta to NAN */
+  for (i = 0; i < N1; i++) {
+    for (j = 0; j < N2; j++) {
+      if (isnan(M1[i]) || isnan(M2[j])) {
+	list[Nlist] = NAN;
+      } else {
+	list[Nlist] = M1[i] - M2[j];
+      }
+      Nlist ++;
+    }
+  }
+
+skip: 
+  if (M1 != NULL) free (M1);
+  if (M2 != NULL) free (M2);
+  *nlist = Nlist;
+  return (list);
+}
+  
+/* extract a list of delta-measure-mags from the specified average entry based on the 
+   pre-set selections - does not return self-matched measurements */
+double *ExtractMeasuresDMag (PhotCode **code, int *mode, Average *average, SecFilt *secfilt, Measure *measure, off_t *nlist) {
+
+  int Np0, Np1;
+  off_t i, j, Nlist, NLIST;
+  double *list, M1, M2;
+  
+  *nlist = 0; 
+  Nlist = 0;
+  NLIST = MAX (1, average[0].Nmeasure*average[0].Nmeasure);
+  ALLOCATE (list, double, NLIST);
+
+  /* must have two code values - drop this test? this is programming case, not a user case */
+  if (code == NULL) return (list);
+  if (code[0] == NULL) return (list);
+  if (code[1] == NULL) return (list);
+
+  /* exclude based on average parameters for both codes  */
+  if (!TestAverage (code[0], average, secfilt, measure)) return (list);
+  if (!TestAverage (code[1], average, secfilt, measure)) return (list);
+
+  Np0 = GetSelectionParam ();
+  Np1 = Np0 + 1;
+
+  /* loop twice over all measures */
+  for (i = 0; i < average[0].Nmeasure; i++) {
+    SetSelectionParam (Np0);
+    TESTCODE (code[0], measure[i]);
+    TESTMEASURE (measure[i]);
+    SETMAG(M1, measure[i], mode[0]);
+    for (j = 0; j < average[0].Nmeasure; j++) {
+      if (i == j) continue;
+      SetSelectionParam (Np1);
+      TESTCODE (code[1], measure[j]);
+      TESTMEASURE (measure[j]);
+      SETMAG(M2, measure[j], mode[1]);
+      if (isnan(M1) || isnan(M2)) {
+	list[Nlist] = NAN;
+      } else {
+	list[Nlist] = M1 - M2;
+      }
+      Nlist ++;
+    }
+  }
+  *nlist = Nlist;
+  return (list);
+}
+
+/* extract a measurement list matching the number of dmag entries */
+double *ExtractByDMag (PhotCode **code, int *mode, Average *average, SecFilt *secfilt, Measure *measure, off_t *nlist, int param) {
+
+  off_t N1;
+  int A1, A2;
+  double *list;
+
+  /* check for special case of measure-measure */
+  A1 = ((mode[0] == MAG_AVE) || (mode[0] == MAG_REF));
+  A2 = ((mode[1] == MAG_AVE) || (mode[1] == MAG_REF));
+  if (!A1 && !A2) {
+    list = ExtractMeasuresByDMag (code, mode, 1, average, secfilt, measure, nlist, param);
+    return (list);
+  }
+
+  /* one of the two entries results in a single element. extract the other */
+  if (A1) {
+    list = ExtractMeasures (code[1], mode[1], average, secfilt, measure, &N1, param);
+  } else {
+    list = ExtractMeasures (code[0], mode[0], average, secfilt, measure, &N1, param);
+  }
+  *nlist = N1;
+  return (list);
+}
+  
+/* extract a list of delta-measure-mags from the specified average entry based on the pre-set selections */
+double *ExtractMeasuresByDMag (PhotCode **code, int *mode, int use_first, Average *average, SecFilt *secfilt, Measure *measure, off_t *nlist, int param) {
+
+  off_t i, j, n, Nlist, NLIST;
+  double *list, M1, M2;
+  
+  *nlist = 0; 
+  Nlist = 0;
+  NLIST = MAX (1, average[0].Nmeasure*average[0].Nmeasure);
+  ALLOCATE (list, double, NLIST);
+
+  /* must have two code values */
+  if (code == NULL) return (list);
+  if (code[0] == NULL) return (list);
+  if (code[1] == NULL) return (list);
+
+  /* chisq, fwhm, Nphot, Ncode */
+  if (!TestAverage (code[0], average, secfilt, measure)) return (list);
+  if (!TestAverage (code[1], average, secfilt, measure)) return (list);
+
+  /* loop twice over all measures */
+  for (i = 0; i < average[0].Nmeasure; i++) {
+    TESTCODE (code[0], measure[i]);
+    TESTMEASURE (measure[i]);
+    SETMAG(M1, measure[i], mode[0]);
+    for (j = 0; j < average[0].Nmeasure; j++) {
+      if (i == j) continue;
+      TESTCODE (code[1], measure[j]);
+      TESTMEASURE (measure[j]);
+      SETMAG(M2, measure[j], mode[1]);
+      n = (use_first) ? i : j;
+
+      /* assign value */
+      list[Nlist] = GetMeasure (param, &average[0], &measure[n], (use_first ? M1 : M2));
+      Nlist ++;
+    }
+  }
+  *nlist = Nlist;
+  return (list);
+}
+
+double GetMeasure (int param, Average *average, Measure *measure, double mag) {
+
+  double ra, dec, x, y;
+  double value;
+  Image *image;
+  Coords *mosaic;
+
+  value = 0;
+  switch (param) {
+    case MEAS_MAG: /* magnitudes are already determined above */
+      value = mag;
+      break;
+    case MEAS_RA: /* OK */
+      value = average[0].R - measure[0].dR / 3600.0;
+      break;
+    case MEAS_DEC: /* OK */
+      value = average[0].D - measure[0].dD / 3600.0;
+      break;
+    case MEAS_DOPHOT: /* OK */
+      value = (measure[0].photFlags >> 16);
+      break;
+    case MEAS_AIRMASS: /* OK */
+      value = measure[0].airmass;
+      break;
+    case MEAS_EXPTIME: /* OK */
+      value = pow (10.0, measure[0].dt * 0.4);
+      break;
+    case MEAS_PHOTCODE: /* OK */
+      value = measure[0].photcode;
+      break;
+    case MEAS_TIME: /* OK */
+      value = TimeValue (measure[0].t, TimeReference, TimeFormat);
+      break;
+    case MEAS_RA_OFFSET: /* OK */
+      value = measure[0].dR;
+      break;
+    case MEAS_DEC_OFFSET: /* OK */
+      value = measure[0].dD;
+      break;
+    case MEAS_FWHM: /* OK */
+      value = FromShortPixels(measure[0].FWx);
+      break;
+    case MEAS_DB_FLAGS: /* ? */
+      value = measure[0].dbFlags;
+      break;
+    case MEAS_XCCD: /* OK */
+/* I need to perform this conversion for ELIXIR and LONEOS formats on load */      
+# if 0
+      value = measure[0].Xccd;
+# else
+      ra  = average[0].R - measure[0].dR / 3600.0;
+      dec = average[0].D - measure[0].dD / 3600.0;
+      image = MatchImageDVO (measure[0].t, measure[0].photcode, measure[0].imageID);
+      if (image == NULL) break;
+      RD_to_XY (&x, &y, ra, dec, &image[0].coords);
+      value = x;
+# endif
+      break;
+    case MEAS_YCCD: /* OK */
+/* I need to perform this conversion for ELIXIR and LONEOS formats on load */      
+# if 0
+      value = measure[0].Yccd;
+# else
+      ra  = average[0].R - measure[0].dR / 3600.0;
+      dec = average[0].D - measure[0].dD / 3600.0;
+      image = MatchImageDVO (measure[0].t, measure[0].photcode, measure[0].imageID);
+      if (image == NULL) break;
+      RD_to_XY (&x, &y, ra, dec, &image[0].coords);
+      value = y;
+# endif
+      break;
+    case MEAS_XMOSAIC: /* OK */
+      ra  = average[0].R - measure[0].dR / 3600.0;
+      dec = average[0].D - measure[0].dD / 3600.0;
+      mosaic = MatchMosaic (measure[0].t, measure[0].photcode);
+      if (mosaic == NULL) break;
+      RD_to_XY (&x, &y, ra, dec, mosaic);
+      value = x;
+      break;
+    case MEAS_YMOSAIC: /* OK */
+      ra  = average[0].R - measure[0].dR / 3600.0;
+      dec = average[0].D - measure[0].dD / 3600.0;
+      mosaic = MatchMosaic (measure[0].t, measure[0].photcode);
+      if (mosaic == NULL) break;
+      RD_to_XY (&x, &y, ra, dec, mosaic);
+      value = y;
+      break;
+  }
+  return (value);
+}
+
+/** the mosaic entries do not use the registered mosaic found 
+    by MatchImage (via FindMosaicForImage).  Rather, they use
+    a coordinate frame saved by SetImageSelection 
+**/
Index: /branches/sc_branches/pantasks_condor/dvo/pmeasure.c
===================================================================
--- /branches/sc_branches/pantasks_condor/dvo/pmeasure.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/dvo/pmeasure.c	(revision 34783)
@@ -0,0 +1,254 @@
+# include "dvoshell.h"
+# define NCHUNK 10000
+
+enum {FLAG_IGNORE, FLAG_SKIP, FLAG_KEEP};
+
+int pmeasure (int argc, char **argv) {
+  
+  off_t i, j, k, m;
+  int kapa, Narg, Npts, NPTS, status, VERBOSE, TimeSelect, Nloaded;
+  double Mz, Mr, mag;
+  double Radius, Rmin, Rmax, R, D, trange;
+  unsigned IDclip, IDchoice, LimExclude;
+  unsigned dbFlagChoice, dbFlagClip;
+  unsigned photFlagChoice, photFlagClip;
+  int PhotcodeClip;
+  float *Xvec, *Yvec, *Zvec;
+  time_t tzero, tend;
+
+  SkyTable *sky;
+  SkyList *skylist;
+  Catalog catalog;
+  Graphdata graphmode;
+
+  if (!InitPhotcodes ()) return (FALSE);
+  if (!style_args (&graphmode, &argc, argv, &kapa)) return FALSE;
+
+  Mz = 17.0;
+  Mr = -5.0;
+  Rmin = graphmode.coords.crval1 - 182.0;
+  Rmax = graphmode.coords.crval1 + 182.0;
+
+  PhotcodeClip = -1;
+  if ((Narg = get_argument (argc, argv, "-p"))) {
+    remove_argument (Narg, &argc, argv);
+    PhotcodeClip = GetPhotcodeCodebyName (argv[Narg]);
+    remove_argument (Narg, &argc, argv);
+  }
+  if ((Narg = get_argument (argc, argv, "-photcode"))) {
+    remove_argument (Narg, &argc, argv);
+    PhotcodeClip = GetPhotcodeCodebyName (argv[Narg]);
+    remove_argument (Narg, &argc, argv);
+  }
+  IDchoice = 0;
+  IDclip = FALSE;
+  if ((Narg = get_argument (argc, argv, "-ID"))) {
+    remove_argument (Narg, &argc, argv);
+    IDchoice  = strtol(argv[Narg], NULL, 0);
+    remove_argument (Narg, &argc, argv);
+    IDclip = TRUE;
+  }
+  dbFlagChoice = 0;
+  dbFlagClip = FLAG_IGNORE;
+  if ((Narg = get_argument (argc, argv, "-dbflag"))) {
+    remove_argument (Narg, &argc, argv);
+    dbFlagChoice  = strtol(argv[Narg], NULL, 0);
+    remove_argument (Narg, &argc, argv);
+    dbFlagClip = FLAG_SKIP;
+  }
+  if ((Narg = get_argument (argc, argv, "+dbflag"))) {
+    remove_argument (Narg, &argc, argv);
+    dbFlagChoice  = strtol(argv[Narg], NULL, 0);
+    remove_argument (Narg, &argc, argv);
+    dbFlagClip = FLAG_KEEP;
+  }
+  photFlagChoice = 0;
+  photFlagClip = FLAG_IGNORE;
+  if ((Narg = get_argument (argc, argv, "-photflag"))) {
+    remove_argument (Narg, &argc, argv);
+    photFlagChoice  = strtol(argv[Narg], NULL, 0);
+    remove_argument (Narg, &argc, argv);
+    photFlagClip = FLAG_SKIP;
+  }
+  if ((Narg = get_argument (argc, argv, "+photflag"))) {
+    remove_argument (Narg, &argc, argv);
+    photFlagChoice  = strtol(argv[Narg], NULL, 0);
+    remove_argument (Narg, &argc, argv);
+    photFlagClip = FLAG_KEEP;
+  }
+
+  TimeSelect = FALSE;
+  if ((Narg = get_argument (argc, argv, "-time"))) {
+    remove_argument (Narg, &argc, argv);
+    if (!ohana_str_to_time (argv[Narg], &tzero)) {
+      gprint (GP_ERR, "syntax error\n");
+      return FALSE;
+    }
+    remove_argument (Narg, &argc, argv);
+    if (!ohana_str_to_dtime (argv[Narg], &trange)) { 
+      gprint (GP_ERR, "syntax error\n");
+      return FALSE;
+    }
+    remove_argument (Narg, &argc, argv);
+    if (trange < 0) {
+      trange = fabs (trange);
+      tzero -= trange;
+    }
+    TimeSelect = TRUE;
+  }
+  if ((Narg = get_argument (argc, argv, "-trange"))) {
+    remove_argument (Narg, &argc, argv);
+    if (!ohana_str_to_time (argv[Narg], &tzero)) { 
+      gprint (GP_ERR, "syntax error\n");
+      return FALSE;
+    }
+    remove_argument (Narg, &argc, argv);
+    if (!ohana_str_to_time (argv[Narg], &tend)) { 
+      gprint (GP_ERR, "syntax error\n");
+      return FALSE;
+    }
+    remove_argument (Narg, &argc, argv);
+    trange = tend - tzero;
+    if (trange < 0) {
+      trange = fabs (trange);
+      tzero -= trange;
+    }
+    TimeSelect = TRUE;
+  }
+
+  LimExclude = FALSE;
+  if ((Narg = get_argument (argc, argv, "-x"))) {
+    remove_argument (Narg, &argc, argv);
+    LimExclude = TRUE;
+  }
+
+  VERBOSE = FALSE;
+  if ((Narg = get_argument (argc, argv, "-v"))) {
+    remove_argument (Narg, &argc, argv);
+    VERBOSE = TRUE;
+  }
+
+  if ((Narg = get_argument (argc, argv, "-m"))) {
+    remove_argument (Narg, &argc, argv);
+    Mr  = atof(argv[Narg]);
+    remove_argument (Narg, &argc, argv);
+    Mz = atof(argv[Narg]);
+    Mr = Mr - Mz;
+    remove_argument (Narg, &argc, argv);
+  }
+
+  if (argc != 2) {
+    gprint (GP_ERR, "USAGE: pmeasure (-all) [-m M M]\n");
+    gprint (GP_ERR, " options:\n");
+    gprint (GP_ERR, " [-p photcode]\n");
+    gprint (GP_ERR, " [-photcode photcode]\n");
+    gprint (GP_ERR, " [-ID ID]\n");
+    gprint (GP_ERR, " [-dbflag value] : skip matches to these flags\n");
+    gprint (GP_ERR, " [+dbflag value] : keep matches to these flags\n");
+    gprint (GP_ERR, " [-photflag value] : skip matches to these flags\n");
+    gprint (GP_ERR, " [+photflag value] : keep matches to these flags\n");
+    gprint (GP_ERR, " [-time (start) (duration)]\n");
+    gprint (GP_ERR, " [-trange (start) (stop)]\n");
+    gprint (GP_ERR, " [-x] : exclude points larger / smaller than mag limits\n");
+    gprint (GP_ERR, " [-v] : verbose mode\n");
+    return (FALSE);
+  }
+  graphmode.style = 2; /* set style to points */
+  graphmode.size = -1; /* point size determined by Zvec */
+  graphmode.etype = 0; /* no errorbars */
+
+  Radius = MAX (fabs(graphmode.xmax), fabs(graphmode.ymax));
+
+  /* load sky from correct table */
+  sky = GetSkyTable ();
+  skylist = SkyListByRadius (sky, -1, graphmode.coords.crval1, graphmode.coords.crval2, Radius);
+  
+  /* storage for plotting the points */
+  Npts = 0;
+  NPTS = 1000;
+  ALLOCATE (Xvec, float, NPTS);
+  ALLOCATE (Yvec, float, NPTS);
+  ALLOCATE (Zvec, float, NPTS);
+
+  // prepare to handle interrupt signals
+  signal (SIGINT, handle_interrupt);
+  interrupt = FALSE;
+
+  Nloaded = 0;
+  for (j = 0; (j < skylist[0].Nregions) && !interrupt; j++) {
+    catalog.filename = skylist[0].filename[j];
+    catalog.catflags = LOAD_AVES | LOAD_MEAS;
+    catalog.Nsecfilt = 0;
+
+    // an error exit status here is a significant error
+    if (!dvo_catalog_open (&catalog, NULL, VERBOSE, "r")) {
+      fprintf (stderr, "ERROR: failure to open catalog file %s\n", catalog.filename);
+      exit (2);
+    }
+    dvo_catalog_unlock (&catalog);
+
+    /* project stars to screen display coords */
+    for (i = 0; (i < catalog.Naverage) && !interrupt; i++) {
+      if (IDclip && (catalog.average[i].flags != IDchoice)) continue;
+      catalog.average[i].R = ohana_normalize_angle (catalog.average[i].R);
+      while (catalog.average[i].R < Rmin) catalog.average[i].R += 360.0;
+      while (catalog.average[i].R > Rmax) catalog.average[i].R -= 360.0;
+      m = catalog.average[i].measureOffset;
+      for (k = 0; k < catalog.average[i].Nmeasure; k++) {
+	if ((dbFlagClip == FLAG_SKIP) &&  (catalog.measure[m+k].dbFlags & dbFlagChoice)) continue;
+	if ((dbFlagClip == FLAG_KEEP) && !(catalog.measure[m+k].dbFlags & dbFlagChoice)) continue;
+	if ((photFlagClip == FLAG_SKIP) &&  (catalog.measure[m+k].photFlags & photFlagChoice)) continue;
+	if ((photFlagClip == FLAG_KEEP) && !(catalog.measure[m+k].photFlags & photFlagChoice)) continue;
+	if (TimeSelect && (catalog.measure[m+k].t < tzero)) continue;
+	if (TimeSelect && (catalog.measure[m+k].t > tzero + trange)) continue;
+	if ((PhotcodeClip != -1) && (catalog.measure[m+k].photcode != PhotcodeClip)) continue;
+	mag = PhotCat (&catalog.measure[m+k]);
+	Zvec[Npts] = MIN (1.0, MAX (0.01, (mag - Mz) / Mr));
+	if (LimExclude && (Zvec[Npts] > 0.99)) continue;
+	if (Zvec[Npts] < 0.011) continue;
+	R = catalog.average[i].R - catalog.measure[m+k].dR/3600.0;
+	D = catalog.average[i].D - catalog.measure[m+k].dD/3600.0;
+	// XXX drop this check
+	if ((R < Rmin) || (R > Rmax) || (D < -90.0) || (D > 90.0)) {
+	  char *date;
+	  date = ohana_sec_to_date (catalog.measure[m+k].t);
+	  gprint (GP_LOG, "out: %f, %f : %s : (%f, %f) + (%f, %f)\n", R, D, date, catalog.average[i].R, catalog.average[i].D, catalog.measure[m+k].dR/3600.0, catalog.measure[m+k].dD/3600.0);
+	  free (date);
+	}
+	status = fRD_to_XY (&Xvec[Npts], &Yvec[Npts], R, D, &graphmode.coords);
+	if (!status) continue;
+	Npts ++;
+
+	if (Npts == NPTS - 1) {
+	  NPTS += 1000;
+	  REALLOCATE (Xvec, float, NPTS);
+	  REALLOCATE (Yvec, float, NPTS);
+	  REALLOCATE (Zvec, float, NPTS);
+	}
+	if ((Npts > NCHUNK) || (Nloaded >= 25)) {
+	  KapaPrepPlot (kapa, Npts, &graphmode);
+	  KapaPlotVector (kapa, Npts, Xvec, "x");
+	  KapaPlotVector (kapa, Npts, Yvec, "y");
+	  KapaPlotVector (kapa, Npts, Zvec, "z");
+	  Npts = 0;
+	  Nloaded = 0;
+	}
+      }
+    }
+    Nloaded ++;
+    dvo_catalog_free (&catalog);
+  }
+  if (Npts > 0) {
+    KapaPrepPlot (kapa, Npts, &graphmode);
+    KapaPlotVector (kapa, Npts, Xvec, "x");
+    KapaPlotVector (kapa, Npts, Yvec, "y");
+    KapaPlotVector (kapa, Npts, Zvec, "z");
+  }
+  free (Xvec);
+  free (Yvec);
+  free (Zvec);
+
+  return (TRUE);
+
+}
+  
Index: /branches/sc_branches/pantasks_condor/dvo/procks.c
===================================================================
--- /branches/sc_branches/pantasks_condor/dvo/procks.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/dvo/procks.c	(revision 34783)
@@ -0,0 +1,155 @@
+# include "dvoshell.h"
+
+typedef struct {
+  double ra[3];
+  double dec[3];
+  double X[3];
+  double Y[3];
+  unsigned int t[3];
+  double mag[3];
+} Rocks;
+static Rocks *rocks = (Rocks *) NULL;
+static int   Nrocks;
+
+int procks (int argc, char **argv) {
+  
+  FILE *f;
+  Vector Xvec, Yvec;
+  int kapa, i, j, N, NROCKS;
+  int N0, N1, SpeedClip, Reload;
+  unsigned int t0, t1;
+  double Mz, Mr, S0, S1;
+  double Rmin, Rmax;
+  Graphdata graphmode;
+  char rockcat[256];
+
+  VarConfig ("ROCK_CATALOG", "%s", rockcat);
+  if (!GetGraph (&graphmode, &kapa, NULL)) return (FALSE);
+
+  f = (FILE *) NULL;
+  Mz = 17.0;
+  Mr = -5.0;
+  Rmin = graphmode.coords.crval1 - 182.0;
+  Rmax = graphmode.coords.crval1 + 182.0;
+
+  if ((N = get_argument (argc, argv, "-m"))) {
+    remove_argument (N, &argc, argv);
+    Mr  = 1000*atof(argv[N]);
+    remove_argument (N, &argc, argv);
+    Mz = 1000*atof(argv[N]);
+    Mr = Mr - Mz;
+    remove_argument (N, &argc, argv);
+  }
+
+  S0 = S1 = 0;
+  SpeedClip = FALSE;
+  if ((N = get_argument (argc, argv, "-speed"))) {
+    SpeedClip = TRUE;
+    remove_argument (N, &argc, argv);
+    S0 = atof(argv[N]);
+    remove_argument (N, &argc, argv);
+    S1 = atof(argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  Reload = FALSE;
+  if ((N = get_argument (argc, argv, "-reload"))) {
+    Reload = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+
+  if (argc != 1) {
+    gprint (GP_ERR, "USAGE: procks [-m M M] [-speed s s] \n");
+    return (FALSE);
+  }
+  
+  f = fopen (rockcat, "r");
+  if (f == (FILE *) NULL) {
+    gprint (GP_ERR, "can't open rock catalog\n");
+    return (TRUE);
+  }
+
+  if ((rocks == (Rocks *) NULL) || Reload) {
+    if (rocks != (Rocks *) NULL) free (rocks);
+    NROCKS = 100;
+    ALLOCATE (rocks, Rocks, NROCKS);
+    for (i = 0; fscanf (f, "%lf %lf %d%lf%lf%lf %d%lf%lf%lf %d%lf%lf%lf", 
+			&rocks[i].X[0], &rocks[i].Y[0], 
+			&rocks[i].t[0], &rocks[i].ra[0], &rocks[i].dec[0], &rocks[i].mag[0], 
+			&rocks[i].t[1], &rocks[i].ra[1], &rocks[i].dec[1], &rocks[i].mag[1], 
+			&rocks[i].t[2], &rocks[i].ra[2], &rocks[i].dec[2], &rocks[i].mag[2]
+			) != EOF; i++) {
+      if (i == NROCKS - 1) {
+	NROCKS += 100;
+	REALLOCATE (rocks, Rocks, NROCKS);
+      }
+    }
+    Nrocks = i;
+  }
+      
+  if (Nrocks == 0) {
+    free (rocks);
+    gprint (GP_ERR, "no rocks in datafile\n");
+    return (TRUE);
+  }
+
+  /* data has been loaded, get ready to plot it */
+  SetVector (&Xvec, OPIHI_FLT, 3*Nrocks);
+  SetVector (&Yvec, OPIHI_FLT, 3*Nrocks);
+  
+  /* project stars to screen display coords */
+  for (N = i = 0; i < Nrocks; i++) {
+    if (SpeedClip && ((rocks[i].Y[0] < S0) || (rocks[i].Y[0] > S1))) continue;
+    for (j = 0; j < 3; j++) {
+      rocks[i].ra[j]= ohana_normalize_angle (rocks[i].ra[j]);	
+      while (rocks[i].ra[j] < Rmin) rocks[i].ra[j] += 360.0;
+      while (rocks[i].ra[j] > Rmax) rocks[i].ra[j] -= 360.0;
+      RD_to_XY (&Xvec.elements.Flt[N], &Yvec.elements.Flt[N], rocks[i].ra[j], rocks[i].dec[j], &graphmode.coords);
+      N ++;
+    }
+  }
+  Yvec.Nelements = Xvec.Nelements = N;
+  
+  graphmode.style = 2; /* set style to points */
+  graphmode.etype = 0; /* no errorbars */
+  PlotVectorPair (kapa, &Xvec, &Yvec, NULL, &graphmode);
+
+  /* now plot vectors between two extrema */
+  Yvec.Nelements = Xvec.Nelements = 2*Nrocks;
+  REALLOCATE (Xvec.elements.Flt, opihi_flt, Xvec.Nelements);
+  REALLOCATE (Yvec.elements.Flt, opihi_flt, Yvec.Nelements);
+  
+  /* project stars to screen display coords */
+  for (N = i = 0; i < Nrocks; i++) {
+    if (SpeedClip && ((rocks[i].Y[0] < S0) || (rocks[i].Y[0] > S1))) continue;
+    N0 = N1 = 0;
+    t0 = t1 = rocks[i].t[0];
+    for (j = 1; j < 3; j++) {
+      if (rocks[i].t[j] < t0) { N0 = j; t0 = rocks[i].t[j]; }
+      if (rocks[i].t[j] > t1) { N1 = j; t1 = rocks[i].t[j]; }
+    }
+    rocks[i].ra[N0]= ohana_normalize_angle (rocks[i].ra[N0]);	
+    rocks[i].ra[N1]= ohana_normalize_angle (rocks[i].ra[N1]);	
+    while (rocks[i].ra[N0] < Rmin) rocks[i].ra[N0] += 360.0;
+    while (rocks[i].ra[N0] > Rmax) rocks[i].ra[N0] -= 360.0;
+    while (rocks[i].ra[N1] < Rmin) rocks[i].ra[N1] += 360.0;
+    while (rocks[i].ra[N1] > Rmax) rocks[i].ra[N1] -= 360.0;
+    RD_to_XY (&Xvec.elements.Flt[N], &Yvec.elements.Flt[N], rocks[i].ra[N0], rocks[i].dec[N0], &graphmode.coords);
+    N ++;
+    RD_to_XY (&Xvec.elements.Flt[N], &Yvec.elements.Flt[N], rocks[i].ra[N1], rocks[i].dec[N1], &graphmode.coords);
+    N ++;
+  }
+  Yvec.Nelements = Xvec.Nelements = N;
+  
+  graphmode.style = 2; /* set style to points */
+  graphmode.ptype = 100; /* connect pairs */
+  graphmode.etype = 0; /* no errorbars */
+
+  PlotVectorPair (kapa, &Xvec, &Yvec, NULL, &graphmode);
+
+  free (Xvec.elements.Flt);
+  free (Yvec.elements.Flt);
+  return (TRUE);
+
+}
+  
Index: /branches/sc_branches/pantasks_condor/dvo/region_list.c
===================================================================
--- /branches/sc_branches/pantasks_condor/dvo/region_list.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/dvo/region_list.c	(revision 34783)
@@ -0,0 +1,255 @@
+# include "dvoshell.h"
+
+/* XXX note : for RegionName or RegionList, we need to free the skylist
+   elements, but not in the case of radius selection - this implies 
+   information carried back up */
+
+static SkyTable *sky = NULL;
+
+int SetCATDIR (char *path, int verbose) {
+
+  char *CATDIR  = NULL;
+  char *newpath;
+  char catdir_config[256];
+  char gscfile[256];
+  char skyfile[256];
+  int  skydepth;
+
+  /* find CATDIR in config system */
+  if (path == NULL) {
+    if (VarConfig ("CATDIR", "%s", catdir_config) == NULL) return (FALSE);
+    newpath = catdir_config;
+  } else {
+    newpath = path;
+  }
+
+  CATDIR = newpath;
+  // save the new value in libdvo
+  dvo_set_catdir(CATDIR);
+
+  if (VarConfig ("GSCFILE",  "%s", gscfile) == NULL) gscfile[0] = 0;
+  if (VarConfig ("SKYFILE",  "%s", skyfile) == NULL) skyfile[0] = 0;
+  if (VarConfig ("SKYDEPTH", "%d", &skydepth) == NULL) skydepth = 2;
+
+  if (verbose) {
+      gprint (GP_ERR, "CATDIR %s\n", CATDIR);
+      gprint (GP_ERR, "GSCFILE %s\n", gscfile);
+      gprint (GP_ERR, "SKYFILE %s\n", skyfile);
+      gprint (GP_ERR, "SKYDEPTH %d\n", skydepth);
+  }
+
+  /* load the SkyTable at this point */
+  /* set the image path as well */
+
+  if (sky != NULL) SkyTableFree (sky);
+  sky = SkyTableLoadOptimal (CATDIR, skyfile, gscfile, FALSE, skydepth, verbose);
+  if (sky == NULL) return FALSE;
+
+  SkyTableSetFilenames (sky, CATDIR, "cpt");
+
+  return (TRUE);
+}
+
+char *GetCATDIR () {
+  char *CATDIR = dvo_get_catdir();
+
+  if (CATDIR == NULL) {
+    if (SetCATDIR (NULL, FALSE)) {
+        CATDIR = dvo_get_catdir();
+    }
+  }
+  return (CATDIR);
+}
+
+SkyTable *GetSkyTable () {
+  if (sky == NULL) {
+    SetCATDIR (NULL, FALSE);
+  }
+  return (sky);
+}
+
+void FreeSkyRegionSelection (SkyRegionSelection *selection) {
+
+  if (selection == NULL) return;
+  if (selection[0].name != NULL) free (selection[0].name);
+  if (selection[0].list != NULL) free (selection[0].list);
+  free (selection);
+}
+
+SkyRegionSelection *SetRegionSelection (int *argc, char **argv) {
+  
+  int N;
+  SkyRegionSelection *selection;
+
+  ALLOCATE (selection, SkyRegionSelection, 1);
+  selection[0].name = NULL;
+  selection[0].list = NULL;
+  selection[0].useDisplay = FALSE;
+  selection[0].useSkyregion = FALSE;
+
+  /* check for Region selection (named dvo catalog file) */
+  if ((N = get_argument (*argc, argv, "-cpt"))) {
+    remove_argument (N, argc, argv);
+    selection[0].name = strcreate (argv[N]);
+    remove_argument (N, argc, argv);
+    return selection;
+  }    
+
+  /* check for Region list (file containing dvo catalog file list)*/
+  if ((N = get_argument (*argc, argv, "-cptlist"))) {
+    remove_argument (N, argc, argv);
+    selection[0].list = strcreate (argv[N]);
+    remove_argument (N, argc, argv);
+    return selection;
+  } 
+
+  /* check for Region selection from display */
+  if ((N = get_argument (*argc, argv, "-region"))) {
+    remove_argument (N, argc, argv);
+    selection[0].useDisplay = TRUE;
+    return selection;
+  }    
+
+  /* check for Region selection from display */
+  if ((N = get_argument (*argc, argv, "-skyregion"))) {
+    if (N + 4 >= *argc) {
+      gprint (GP_ERR, "USAGE: -skyregion (RA) (RA) (DEC) (DEC)\n");
+      FreeSkyRegionSelection (selection);
+      return NULL;
+    }
+    remove_argument (N, argc, argv);
+    selection[0].useSkyregion = TRUE;
+    set_skyregion (atof(argv[N]), atof(argv[N+1]), atof(argv[N+2]), atof(argv[N+3]));
+    remove_argument (N, argc, argv);
+    remove_argument (N, argc, argv);
+    remove_argument (N, argc, argv);
+    remove_argument (N, argc, argv);
+    return selection;
+  }    
+
+  /* default to pre-defined sky region */
+  selection[0].useSkyregion = TRUE;
+  return selection;
+}
+
+/* given possible options (by name, by list, by graph region), select SkyRegions */
+int SetSkyRegions (SkyRegionSelection *selection) {
+
+  if (selection->name != NULL) {
+    gprint (GP_ERR, "name-based selection not yet implemented (in parallel mode)\n");
+    return FALSE;
+  } 
+
+  if (selection->list != NULL) {
+    gprint (GP_ERR, "list-based selection not yet implemented (in parallel mode)\n");
+    return FALSE;
+  } 
+
+  if (selection->useDisplay) {
+    double Rmin, Rmax, Dmin, Dmax, Radius;
+    Graphdata graphsky;
+
+    if (!GetGraphdata (&graphsky, NULL, NULL)) {
+      gprint (GP_ERR, "region display not available\n");
+      return FALSE;
+    }
+    Radius = MAX (fabs(graphsky.xmax), fabs(graphsky.ymax));
+    Dmin = graphsky.coords.crval2 - Radius;
+    Dmax = graphsky.coords.crval2 + Radius;
+    
+    if ((Dmin <= -89) || (Dmax >= 89)) {
+      Rmin = 0;
+      Rmax = 360;
+    } else {
+      double Rmod = MAX (Radius / (cos(Dmin*RAD_DEG)), Radius / (cos(Dmax*RAD_DEG)));
+      Rmin = graphsky.coords.crval1 - Rmod;
+      Rmax = graphsky.coords.crval1 + Rmod;
+    }
+
+    set_skyregion (Rmin, Rmax, Dmin, Dmax);
+    return TRUE;
+  }
+  if (selection->useSkyregion) {
+    return TRUE;
+  }
+  return FALSE;
+}
+
+/* given possible options (by name, by list, by graph region), select SkyRegions */
+SkyList *SelectRegions (SkyRegionSelection *selection) {
+
+  SkyList *skylist;
+
+  /* determine region-file names */
+  if (selection->name != NULL) {
+    skylist = SkyListByName (sky, selection->name);
+    return (skylist);
+  } 
+
+  if (selection->list != NULL) {
+    skylist = SkyListLoadFile (selection->list);
+    return (skylist);
+  }
+
+  if (selection->useDisplay) {
+    double Radius;
+    Graphdata graphsky;
+
+    if (!GetGraphdata (&graphsky, NULL, NULL)) {
+      gprint (GP_ERR, "region display not available\n");
+      return (NULL);
+    }
+
+    Radius = MAX (fabs(graphsky.xmax), fabs(graphsky.ymax));
+    skylist = SkyListByRadius (sky, -1, graphsky.coords.crval1, graphsky.coords.crval2, Radius);
+    return (skylist);
+  }
+
+  if (selection->useSkyregion) {
+    double Rmin, Rmax, Dmin, Dmax;
+
+    get_skyregion (&Rmin, &Rmax, &Dmin, &Dmax);
+    skylist = SkyListByBounds (sky, -1, Rmin, Rmax, Dmin, Dmax);
+    return (skylist);
+  }    
+
+  return NULL;
+}
+
+/* returns a list of region files names from file */
+SkyList *SkyListLoadFile (char *filename) {
+  
+  FILE *f;
+  int NREGIONS, Nregions;
+  SkyList *skylist;
+
+  ALLOCATE (skylist, SkyList, 1);
+
+  f = fopen (filename, "r");
+  if (f == NULL) {
+    gprint (GP_ERR, "ERROR: can't find region list file %s\n", filename);
+    skylist[0].Nregions = 0;
+    skylist[0].regions = NULL;
+    return (skylist);
+  }
+  
+  Nregions = 0;
+  NREGIONS = 50;
+  ALLOCATE (skylist[0].regions, SkyRegion *, NREGIONS);
+  ALLOCATE (skylist[0].filename, char *, NREGIONS);
+  skylist[0].ownElements = TRUE; // free these elements when freeing the list
+
+  char *CATDIR = dvo_get_catdir();
+
+  while (fscanf (f, "%s", filename) != EOF) {
+    ALLOCATE (skylist[0].regions[Nregions], SkyRegion, 1);
+    strcpy (skylist[0].regions[Nregions][0].name, filename);
+    sprintf (filename, "%s/%s.cpt", CATDIR, skylist[0].regions[Nregions][0].name);
+    skylist[0].filename[Nregions] = strcreate (filename);
+    Nregions ++;
+    CHECK_REALLOCATE (skylist[0].regions, SkyRegion *, NREGIONS, Nregions, 50);
+  }
+  skylist[0].Nregions = Nregions;
+  return (skylist);
+}
+
Index: /branches/sc_branches/pantasks_condor/dvo/remote.c
===================================================================
--- /branches/sc_branches/pantasks_condor/dvo/remote.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/dvo/remote.c	(revision 34783)
@@ -0,0 +1,47 @@
+# include "dvoshell.h"
+# include <glob.h>
+# define DVO_MAX_PATH 1024
+
+// functions to manage the remote hosts
+int remote (int argc, char **argv) {
+  
+  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");
+    gprint (GP_ERR, "  launch (command) on the parallel hosts\n");
+    gprint (GP_ERR, "  options:\n");
+    gprint (GP_ERR, "  -v : verbose mode:\n");
+    gprint (GP_ERR, "OR:    remote -reload (uniquer)\n");
+    return FALSE;
+  }
+
+  // we can call any command remotely, but the collection of macros will
+  // not automatically be passed along.  if we want to run a specific macro,
+  // need to point at the relevant input file and have that get loaded
+
+  // if we specified a remote result file, the function above assumes that this is a FITS table
+  // with a set of vectors to load.
+
+  if ((N = get_argument (argc, argv, "-reload"))) {
+    remove_argument (N, &argc, argv);
+    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;
+  }
+
+  // strip of the 'remote' and send the remaining arguments to the remote machine
+  int status = HostTableParallelOps (argc - 1, &argv[1], NULL, 0, VERBOSE);
+  return status;
+}
Index: /branches/sc_branches/pantasks_condor/dvo/scripts/navigate
===================================================================
--- /branches/sc_branches/pantasks_condor/dvo/scripts/navigate	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/dvo/scripts/navigate	(revision 34783)
@@ -0,0 +1,623 @@
+# -*- perl -*-
+
+macro navigate
+  style -n 0
+  limits
+  $DRAWSTARS  = -1
+  $DRAWIMAGES =  1
+  $DRAWGRID   = -1
+  $ZOOM = 180 / ($YMAX - $YMIN)
+  #this should be changed to a while loop, except 'while' is broken for some reason
+  $KEY = "none"
+  while ("$KEY" != "q")
+    cursor 1
+
+    # help list
+    if ("$KEY" == "h")
+     echo "Arrow Keys - pan in that direction"
+     echo "PgUp,PgDn - zoom in/out a factor of 1.2"
+     echo "Home,End  - zoom in/out a factor of 2"
+     echo "1 - zoom in factor of 2 at the cursor"
+     echo "2 - zoom in factor of 1.2 at the cursor"
+     echo "3 - recenter at cursor"
+     echo "4 - zoom out factor of 1.2 at the cursor"
+     echo "5 - zoom out factor of 2 at the cursor"
+     echo "6 - zoom out factor of 10 at the cursor"
+     echo "z - zoom to radius (requires 2nd keystroke)"
+     echo "f - show full sky"
+     echo ""
+     echo "q - quit"
+     echo "S - toggle auto-plotting of stars"
+     echo "A - toggle auto-plotting of image borders"
+     echo "g - toggle skygrid on/off"
+     echo "c - plot status catalog boundaries"
+     echo "C - list catalog at cursor location"
+     echo "i - list info about images touching cursor location" 
+     echo "I - list info about images, with pixel coords of cursor position"
+     echo "j - adjust mag scale, +0.5"
+     echo "k - adjust mag scale, -0.5"
+     echo "J - adjust dmag scale, /1.25"
+     echo "K - adjust dmag scale, *1.25"
+     echo "r - plot detected asteroids (rocks)"
+     echo "l - plot HST GSC"
+     echo "L - plot Landolt stars"
+     echo "m - list measurements for stars within 1 pixel of cursor"
+     echo "M - list measurements for stars within 1.8 arcsec of cursor"
+     echo "p - ****(don't know what this does)"
+     echo "s - ****(don't know what this does)"
+     echo "t - plot light curve for star within 2 arcsec of cursor position"
+     echo "T - plot 'galaxy' light curve for star within 2 arcsec of cursor position"
+     echo "u - ****(don't know what this does)"
+     echo "x - plot stars scaled by magnitude Chisq"
+     echo "X - plot stars by magnitude scatter"
+     echo "y - ****(don't know what this does)"
+     echo ""
+     echo "@ - execute macro `user_macro`"
+     echo ": - ****input a line and execute (not yet implemented)"
+    end
+
+    # quit from navigate
+    if ("$KEY" == "q") continue
+
+    #pan controls
+    if (("$KEY" == "Left") || ("$KEY" == "Right") || ("$KEY" == "Up") || ("$KEY" == "Down"))
+      $SHIFT = 0.2
+      $R$KEY  = $RMAX-$XMAX  
+      $D$KEY  = $DMAX-$YMAX
+      #assumes standard sky orientation!! (N up, E left)
+      if ("$KEY"=="Left")
+        $R$KEY = $R$KEY + $SHIFT*$XMAX
+      end
+      if ("$KEY"=="Right")
+        $R$KEY = $R$KEY + $SHIFT*$XMIN
+      end
+      if ("$KEY"=="Up")
+        $D$KEY = $D$KEY + $SHIFT*$YMAX
+      end
+      if ("$KEY"=="Down")
+        $D$KEY = $D$KEY + $SHIFT*$YMIN
+      end
+      #pretend like I hit '3' in the place to recenter it
+      nav_zoom 1      
+    end
+
+    # NEW zoom controls
+    if (("$KEY" == "Prior") || ("$KEY" == "Next") || ("$KEY" == "Home") || ("$KEY" == "End") || ("$KEY" == "Button4") || ("$KEY" == "Button5"))    
+      #move where key was hit to center      
+      $R$KEY  = $RMAX-$XMAX  
+      $D$KEY  = $DMAX-$YMAX
+      if ("$KEY" == "Prior")
+        $zfac=1.2
+      end
+      if ("$KEY" == "Next")
+        $zfac={1/1.2}
+      end
+      if ("$KEY" == "Home")
+        $zfac=2
+      end
+      if ("$KEY" == "End")
+        $zfac={1/2.}
+      end
+      if ("$KEY"=="Button4")
+        $zfac=1.6
+      end
+      if ("$KEY"=="Button5")
+        $zfac={1/1.6}
+      end
+      nav_zoom $zfac
+    end
+
+    if ("$KEY"=="Button1")
+      nav_zoom 1
+    end
+    if ("$KEY"=="Button2")
+      nav_zoom {1/2.}
+    end
+    if ("$KEY"=="Button3")
+      nav_zoom 2
+    end
+
+
+
+    # zoom controls
+    if ("$KEY" == "1")
+      nav_zoom 2
+    end
+    if ("$KEY" == "2")
+      nav_zoom 1.2
+    end
+    if ("$KEY" == "3")
+      nav_zoom 1
+    end
+    if ("$KEY" == "4")
+      nav_zoom {1/1.2}
+    end
+    if ("$KEY" == "5")
+      nav_zoom {1/2.}
+    end
+    if ("$KEY" == "6")
+      nav_zoom {1/20.}
+    end
+
+ 
+   # measure distance
+    if ("$KEY" == "d")
+      $r0 = $R$KEY
+      $d0 = $D$KEY
+      $ok = $KEY
+      echo "type 'd' again at endpoint"
+      cursor 1
+      $r1 = $R$KEY
+      $d1 = $D$KEY
+      $dr = 3600*((dcos($d0)*($r0-$r1))^2 + ($d0-$d1)^2)^0.5
+      echo "$dr arcsec"
+    end
+    # show ra, dec
+    if ("$KEY" == "w")
+      $tmp = $R$KEY
+      if ($tmp < 0) 
+        $tmp = $R$KEY + 360.0
+      end
+      echo "$tmp $D$KEY" 
+      exec echo $tmp $D$KEY | radec -hh
+    end
+    # zoom to radius
+    if ("$KEY" == "z")
+      $r0 = $R$KEY
+      $d0 = $D$KEY
+      $ok = $KEY
+      echo "type 'z' again at radius"
+      cursor 1
+      $r1 = $R$KEY
+      $d1 = $D$KEY
+      $dr = (($r0-$r1)^2 + ($d0-$d1)^2)^0.5
+      $ZOOM = $RAD / $dr
+      nav_recenter
+      nav_redraw
+      $KEY = $ok
+      $R$KEY = $r0
+      $D$KEY = $d0
+    end
+
+    # adjust mag scaling
+    if ("$KEY" == "J")
+      $MAG = $MAG - 0.5
+      nav_redraw
+    end
+    if ("$KEY" == "K")
+      $MAG = $MAG + 0.5
+      nav_redraw
+    end
+    if ("$KEY" == "j")
+      $dMAG = $dMAG * 0.8
+      nav_redraw
+    end
+    if ("$KEY" == "k")
+      $dMAG = $dMAG * 1.25
+      nav_redraw
+    end
+    echo "mag, dmag: $MAG, $dMAG"
+
+
+
+    # plot full sky
+    if ("$KEY" == "f") 
+      echo "full"
+      $ZOOM = 1
+      resize 1150 600		      
+      region 0 0 90 ait
+      $RMIN = 0
+      $RMAX = 360
+      $DMIN = -90
+      $DMAX = +90
+      style -c red; cgrid
+      style -c black
+      images
+    end
+
+    # plot rocks
+    if ("$KEY" == "r") 
+#      plot.rocks
+      style -c blue   -pt 1; procks -speed 0.0041 1
+      style -c red    -pt 1; procks -speed 0.00041 0.0041
+      style -c indigo -pt 1; procks -speed 0 0.00041
+      style -c black -lw 0;
+    end
+    # plot HST-GSC
+    if ("$KEY" == "l") 
+      style -c blue -pt 7; cat -all -g -m 9 16
+      style -c black
+    end
+    # plot Landolt
+    if ("$KEY" == "L") 
+#      style -c red  -lw 2 -pt 3; cat -a 1 2 3 /data/elixir/srcdir/refs/stetson/stetsonBn.txt -m 9 18
+#      style -c blue -lw 2 -pt 3; cat -a 25 26 8 /data/elixir/srcdir/refs/landolt/new/Landolt92.fix -m 9 18
+      style -c red -lw 2 -pt 7; cat -a 1 2 3 /data/elixir/srcdir/refs/sdss/g_SDSS.dat -m 9 14
+#      style -c red -lw 2 -pt 3; cat -a 25 26 8 /data/elixir/srcdir/refs/landolt/new/Landolt92.hq -m 9 18
+#      style -c red -lw 2 -pt 3; cat -a 22 23 8 /data/elixir/srcdir/refs/landolt/new/Landolt92.unfix -m 9 18
+#      style -c blue -lw 2 -pt 7; cat -a 1 2 4 /data/elixir/srcdir/refs/landolt/extreme/extreme.match -m 0 20
+#      style -x 2 -c red -pt 7 ; cplot RA DEC
+      style -c black -lw 0
+    end
+
+    # list star measurements
+    if ("$KEY" == "m") 
+        $dR = $RAD/$ZOOM/300
+        if ($dR < 0.0005)
+	 $dR = 0.0005
+        end
+	gstar $R$KEY $D$KEY $dR -m
+    end
+
+    # plot mag residuals
+    if ("$KEY" == "R") 
+      echo "filter: "
+      cursor 1
+      clear -n 1 -s; lim 10 22 -0.2 0.2; clear; box
+      dmags $KEY\:rel - $KEY : $KEY -type 0
+      plot -x 2 -pt 0 -sz 0.3 -c red yv xv
+      dmags $KEY\:rel - $KEY : $KEY -type 0 -flag 0 -nphot +3 -chisq 2.0
+      plot -x 2 -pt 2 -sz 0.5 -c black yv xv
+      $KEY = R
+      style -n 0
+    end
+
+    if ("$KEY" == "M") 
+	gstar $R$KEY $D$KEY 0.0005 -m
+    end
+    # list images
+    if ("$KEY" == "i") 
+	gimages $R$KEY $D$KEY
+    end
+    if ("$KEY" == "I") 
+	gimages $R$KEY $D$KEY -pix
+    end
+
+    #toggle images on / off
+    if ("$KEY" == "A")
+      $DRAWIMAGES = $DRAWIMAGES * -1
+      if ($DRAWIMAGES == 1)
+        images
+      end
+    end
+    # toggle stars on / off
+    if ("$KEY" == "S")
+      $DRAWSTARS = $DRAWSTARS * -1
+      if (($ZOOM > 20) && ($DRAWSTARS == 1))
+       style -pt 7
+       pmeasure -all -m $MAG {$MAG + $dMAG}
+      end
+    end
+    # turn grid on / off
+    if ("$KEY" == "g")
+      $DRAWGRID = $DRAWGRID * -1
+      if (($ZOOM > 20) && ($DRAWGRID==1))
+        style -c black; cgrid
+      end
+      if (($ZOOM > 20) && ($DRAWGRID==-1))
+        nav_redraw
+      end
+    end
+
+    # plot light-curve interactive
+    if ("$KEY" == "t")
+      style -n 1 -pt 2 -x 2
+      clear
+      if ($R$KEY < 0) 
+       $R$KEY = $R$KEY + 360
+      end
+      lcurve -l $R$KEY $D$KEY {30/3600} -d -v time mag
+      box
+      lcv
+      style -n 0
+    end
+    # plot light-curve 
+    if ("$KEY" == "T")
+      style -n 1 -pt 1 -c red -x 2
+      lcurve $R$KEY $D$KEY {30/3600} -d
+      style -c black
+      style -n 0 
+    end
+    # plot catalogs
+    if ("$KEY" == "c")
+      style -c blue; pcat; style -c black
+    end
+    # list catalogs
+    if ("$KEY" == "C")
+      gcat $R$KEY $D$KEY
+    end
+
+    # plot image chisqs
+    if ("$KEY" == "x") 
+       gcat $R$KEY $D$KEY
+       extract $CATNAME Xm -photcode R
+       extract $CATNAME ra
+       extract $CATNAME dec
+       style -x 2 -pt 7 -c blue
+       czplot ra dec Xm 3 30
+       style -c black -pt 1
+    end
+    # plot meas errors
+    if ("$KEY" == "X") 
+       gcat $R$KEY $D$KEY
+       extract $CATNAME dM -photcode R
+       extract $CATNAME ra
+       extract $CATNAME dec
+       style -x 2 -pt 7 -c red
+       czplot ra dec dM 0 30
+       style -c black -pt 1
+    end
+
+
+    # temp plot for skyprobe
+    if ("$KEY" == "u") 
+      imextract -region time
+      imextract -region mcal
+      imextract -region airmass
+      imextract -region nstar
+      vstat time
+      clear -n 1;
+      section a 0 0.00 1 0.33
+      lim {$MEDIAN-0.3} {$MEDIAN+0.3} -0.8 -0.5; box; plot time mcal
+      section b 0 0.33 1 0.33
+      lim {$MEDIAN-0.3} {$MEDIAN+0.3}  0.95 3.0; box; plot time airmass
+      section c 0 0.66 1 0.33
+      lim {$MEDIAN-0.3} {$MEDIAN+0.3} 0 3000; box; plot time nstar
+      style -n 0
+    end
+    if ("$KEY" == "s")
+      $tmp = $R$KEY
+      if ($tmp < 0) 
+        $tmp = $R$KEY + 360.0
+      end
+      $line = `echo $tmp $D$KEY | radec -hh`
+      imextract -region photcode
+      imextract -region time
+     
+      subset t = time if (int(photcode/100) == 1)
+      uniq t T
+      $Bn = t[]
+      $BN = T[]
+      
+      subset t = time if (int(photcode/100) == 2)
+      uniq t T
+      $Vn = t[]
+      $VN = T[]
+      
+      subset t = time if (int(photcode/100) == 3)
+      uniq t T
+      $Rn = t[]
+      $RN = T[]
+      
+      subset t = time if (int(photcode/100) == 4)
+      uniq t T
+      $In = t[]
+      $IN = T[]
+     
+      echo "$line  $Bn $BN  $Vn $VN  $Rn $RN  $In $IN"
+    end
+
+    if ("$KEY" == "p") 
+      echo "P - new coords; p - old coords"
+      cursor 1
+      exec echo $Rp $Dp $RP $DP >> fix.coords
+    end
+
+    if ("$KEY" == "y")
+      ccd I - 2MASS_J : 2MASS_J - 2MASS_K
+      lim -n 1 -1 10 -1 3; clear; box; plot -x 2 -pt 2 -sz 0.5 xv yv
+      dev -n 0 -g
+    end
+
+    #  User-defined macro
+    if ("$KEY" == "at")
+      user_macro
+    end
+
+    if ("$KEY" == "colon")
+      #make this work similar to ':' in vi or iraf
+      #does not work correctly now.
+      scan stdin line
+      $line
+    end
+
+  end
+end
+
+#define this so navigate doesn't crash if you try to call it.
+#If you define a user_macro, be sure to do so AFTER this in .dvorc.
+macro user_macro
+  #echo "success!"
+  $do_nothing=0
+end
+    
+
+macro nav_zoom
+  $ZOOM = $ZOOM * $1
+  nav_recenter
+  nav_redraw
+  $Rnum = $R$KEY		      
+  $Dnum = $D$KEY
+  $KEY = num
+end
+
+macro nav_recenter
+  region $R$KEY $D$KEY {$RAD/$ZOOM} sin
+  #assumes standard sky orientation!! (N up, E left)
+  $RMIN = $R$KEY + $XMIN
+  $RMAX = $R$KEY + $XMAX
+  $DMIN = $D$KEY + $YMIN
+  $DMAX = $D$KEY + $YMAX
+end
+
+macro nav_redraw
+  clear
+  if ($ZOOM <= 20) 
+    style -c red; cgrid
+  end
+  if (($ZOOM > 20) && ($DRAWGRID==1))
+    style -c black; cgrid
+  end
+  if (($ZOOM > 20) && ($DRAWSTARS == 1))
+    pmeasure -all -m $MAG {$MAG + $dMAG}
+  end    
+  style -c black
+  if ($DRAWIMAGES == 1)
+    images
+  end
+end
+
+
+
+#==================================================
+#=================   END BSNAV   ==================
+#==================================================
+
+
+macro sigclip
+  if ("$0" == "1")
+    echo ""
+    echo "sigclip <clipvector> <N_iterations> <N_sigma> [other vectors ..]"
+    echo ""
+  end
+
+  #required parameters
+  $CLIPVECT = $1
+  $NITERATE = $2
+  $NSIGCLIP = $3
+  
+  for i 0 $NITERATE
+    vstat -q $CLIPVECT
+    #clip boundaries
+    $top = $MEAN + ($NSIGCLIP*$SIGMA)
+    $bot = $MEAN - ($NSIGCLIP*$SIGMA)
+    
+    #clip it good.
+    subset temp = $CLIPVECT if (($CLIPVECT < $top) && ($CLIPVECT > $bot))
+    
+    #if you specify other vectors, clip the same elements from them too.
+    #they must all be the same length, of course!!
+    if ($0>4)
+      for j 4 $0
+        subset $$j = $$j if (($CLIPVECT < $top) && ($CLIPVECT > $bot))
+      end
+    end
+    
+    #copy temp back to $CLIPVECT and reiterate!
+    delete $CLIPVECT
+    concat temp $CLIPVECT  
+  end
+end
+
+
+macro binvec
+  if ("$0" == "1")
+    echo ""
+    echo "binvec <vec> <Nbins> [other vectors...]"
+    echo ""
+    echo "Bin the vector 'vec' into Nbin bins.  Listing other vectors will"
+    echo "put the corresponding elements of those into other vectors which"
+    echo "are the subset of the vector in that bin.  (That can probably be"
+    echo "stated better.)  This macro makes lots of new vectors.  Hooray!"
+    echo ""
+    echo "Creates"
+  end
+
+  #REQUIRED PARAMS
+  $binvect = $1
+  $NBINS   = $2
+
+  vstat -q $binvect
+  $step = ($MAX-$MIN)/$NBINS
+  $vmin = $MIN
+  $vmax = $MAX
+  delete -q $binvect\_bins
+  delete -q $binvect\_num
+  for i 1 {$NBINS+1}
+    $top = $vmin + ( $i   *$step)
+    $bot = $vmin + (($i-1)*$step)
+    #      sightly different behavior for last bin    -------v
+    if ($i != $NBINS)
+      subset temp = $binvect if (($binvect>=$bot)&&($binvect< $top))
+    else
+      subset temp = $binvect if (($binvect>=$bot)&&($binvect<=$top))
+    end
+    set $binvect\_bin$i = temp
+    set temp2 = temp
+    delete temp
+    #if you specify other vectors, grab the same elements from them too.
+    #they must all be the same length, of course!!
+
+    if ($0>3)
+      for j 3 $0
+        if ($i != $NBINS)
+          subset temp = $$j if (($binvect>=$bot)&&($binvect< $top))
+        else 
+          subset temp = $$j if (($binvect>=$bot)&&($binvect<=$top))
+        end
+        set $$j\_bin$i = temp
+        delete temp
+      end
+    end
+
+
+    concat {$bot+($step/2)} $binvect\_bins
+    concat temp2[] $binvect\_num
+    #dvo didn't like me saying 'concat $binvect\_bin$i[] $binvect\_num
+    delete temp2
+  end
+end
+
+macro binvec.2
+  if ("$0" == "1")
+    echo ""
+    echo "binvec.2 <vec> <min> <max> <binsize> [other vectors...]"
+    echo ""
+    echo ""
+    echo "see also 'binvec'"
+  end
+
+  #REQUIRED PARAMS
+  $binvect = $1
+  $vmin    = $2
+  $vmax    = $3
+  $step    = $4
+
+  $NBINS = ($MAX-$MIN)/$step
+
+  delete -q $binvect\_bins
+  delete -q $binvect\_num
+  for i 1 {$NBINS+1}
+    $top = $vmin + ( $i   *$step)
+    $bot = $vmin + (($i-1)*$step)
+    #      sightly different behavior for last bin    -------v
+    if ($i != $NBINS)
+      subset temp = $binvect if (($binvect>=$bot)&&($binvect< $top))
+    else
+      subset temp = $binvect if (($binvect>=$bot)&&($binvect<=$top))
+    end
+    set $binvect\_bin$i = temp
+    set temp2 = temp
+    delete temp
+    #if you specify other vectors, grab the same elements from them too.
+    #they must all be the same length, of course!!
+
+    if ($0>5)
+      for j 5 $0
+        if ($i != $NBINS)
+          subset temp = $$j if (($binvect>=$bot)&&($binvect< $top))
+        else 
+          subset temp = $$j if (($binvect>=$bot)&&($binvect<=$top))
+        end
+        set $$j\_bin$i = temp
+        delete temp
+      end
+    end
+
+
+    concat {$bot+($step/2)} $binvect\_bins
+    concat temp2[] $binvect\_num
+    #dvo didn't like me saying 'concat $binvect\_bin$i[] $binvect\_num
+    delete temp2
+  end
+end
Index: /branches/sc_branches/pantasks_condor/dvo/showtile.c
===================================================================
--- /branches/sc_branches/pantasks_condor/dvo/showtile.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/dvo/showtile.c	(revision 34783)
@@ -0,0 +1,95 @@
+# include "dvoshell.h"
+static float dr[] = {0.0, 1.0, 1.0, 0.0};
+static float dd[] = {0.0, 0.0, 1.0, 1.0};
+
+int showtile (int argc, char **argv) {
+
+  int kapa, Nd, N, NPTS, status, i, InPic;
+  Graphdata graphmode;
+  Coords coords;
+  Vector Xvec, Yvec;
+  float r, d, R, D;
+  float Ro[90], Do[90];
+
+  /* show tile pattern in viewed region */
+  if (!GetGraph (&graphmode, &kapa, NULL)) return (FALSE);
+
+  if (argc != 1) {
+    gprint (GP_ERR, "USAGE: showtile [option]\n");
+    return (FALSE);
+  }
+  
+  N = 0;
+  NPTS = 200;
+  SetVector (&Xvec, OPIHI_FLT, NPTS);
+  SetVector (&Yvec, OPIHI_FLT, NPTS);
+
+  /* starting position */
+
+  /* reference for coords is this image */
+  coords.crpix1 = coords.crpix2 = 0.0;
+  coords.crval1 = coords.crval2 = 0.0;
+  coords.cdelt1 = coords.cdelt2 = 1.0;
+  coords.pc1_1  = coords.pc2_2  = 1.0;
+  coords.pc1_2  = coords.pc2_1  = 0.0;
+  coords.Npolyterms = 0;
+  strcpy (coords.ctype, "RA---TAN");
+  
+  /* fill in top-left region */
+  for (r = 0; r < 3; r += 1.0) {
+    gprint (GP_ERR, "r: %f\n", r);
+    for (Nd = d = 0; d < 90; Nd ++, d += 1.0) {
+      if (r == 0) {
+	coords.crval1 = r;
+	coords.crval2 = d;
+      } else {
+	coords.crval1 = Ro[Nd];
+	coords.crval2 = Do[Nd];
+      }
+      for (i = 0; i < 4; i++) {
+	fXY_to_RD (&R, &D, dr[i], dd[i], &coords);
+	status |= RD_to_XY (&Xvec.elements.Flt[N+2*i], &Yvec.elements.Flt[N+2*i], R, D, &graphmode.coords);
+	if (i > 0) {
+	  Xvec.elements.Flt[N+2*i - 1] = Xvec.elements.Flt[N+2*i];
+	  Yvec.elements.Flt[N+2*i - 1] = Yvec.elements.Flt[N+2*i];
+	}
+	if (i == 1) {
+	  Ro[Nd] = R;
+	  Do[Nd] = D;
+	}
+      }
+      Xvec.elements.Flt[N+7] = Xvec.elements.Flt[N];
+      Yvec.elements.Flt[N+7] = Yvec.elements.Flt[N];
+
+      /* check if any corner is in plotting region */
+      InPic = FALSE;
+      for (i = 0; i < 8; i+=2) {
+	if ((Xvec.elements.Flt[N+i] >= graphmode.xmin) && 
+	    (Xvec.elements.Flt[N+i] <= graphmode.xmax) && 
+	    (Yvec.elements.Flt[N+i] >= graphmode.ymin) && 
+	    (Yvec.elements.Flt[N+i] <= graphmode.ymax))
+	  InPic = TRUE;
+      }
+      if (!InPic) continue;
+      N+=8;
+      if (N > NPTS - 1) {  /* this is OK because NPTS is made always a multiple of 8 */
+	NPTS += 200;
+	REALLOCATE (Xvec.elements.Flt, opihi_flt, NPTS);
+	REALLOCATE (Yvec.elements.Flt, opihi_flt, NPTS);
+      }
+    }
+  }
+  
+  Xvec.Nelements = Yvec.Nelements = N;
+  if (N > 0) {
+    graphmode.style = 2; /* points */
+    graphmode.ptype = 100; /* connect pairs of points */
+    graphmode.etype = 0;
+    PlotVectorPair (kapa, &Xvec, &Yvec, NULL, &graphmode);
+  }
+
+  free (Xvec.elements.Flt);
+  free (Yvec.elements.Flt);
+  return (TRUE);
+
+}
Index: /branches/sc_branches/pantasks_condor/dvo/simage.c
===================================================================
--- /branches/sc_branches/pantasks_condor/dvo/simage.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/dvo/simage.c	(revision 34783)
@@ -0,0 +1,164 @@
+# include "dvoshell.h"
+# define D_NSTARS 1000
+# define BYTES_STAR 31
+# define BLOCK 1000
+
+int simage (int argc, char **argv) {
+
+  char *buffer;
+  Vector Xvec, Yvec, Zvec;
+  double R, D, X, Y, M, zero, range;
+  FILE *f;
+  Header header;
+  Coords coords;
+  int i, j, kapa, Nstars, nstars, Nbytes, nbytes, N;
+  Graphdata graphmode;
+
+  if (!GetGraph (&graphmode, &kapa, NULL)) return (FALSE);
+
+  zero = 17.0;
+  range = -5.0;
+  if ((N = get_argument (argc, argv, "-m"))) {
+    remove_argument (N, &argc, argv);
+    range = atof(argv[N]);
+    remove_argument (N, &argc, argv);
+    zero  = atof(argv[N]);
+    range = range - zero;
+    remove_argument (N, &argc, argv);
+  }
+
+  if (argc != 2) {
+    gprint (GP_ERR, "USAGE: image (filename)\n");
+    return (FALSE);
+  }
+
+  gprint (GP_ERR, "not working at the moment (cmp format)\n");
+  return (FALSE);
+  
+  /* read header */
+  if (!gfits_read_header (argv[1], &header)) {
+    gprint (GP_ERR, "ERROR: can't find image file %s\n", argv[1]);
+    return (FALSE);
+  }
+  /* get astrometry information */
+  strcpy (coords.ctype, "NONE");
+  gfits_scan (&header, "CTYPE1",   "%s",  1, coords.ctype);
+  if (strcmp (coords.ctype, "RA---PLY")) {
+    gprint (GP_ERR, "ERROR: wrong astrometric info in header\n");
+    return (FALSE);
+  }
+  gfits_scan (&header, "CDELT1",   "%f", 1, &coords.cdelt1); 
+  gfits_scan (&header, "CDELT2",   "%f", 1, &coords.cdelt2);
+  gfits_scan (&header, "CRVAL1",   "%lf", 1, &coords.crval1);
+  gfits_scan (&header, "CRVAL2",   "%lf", 1, &coords.crval2);  
+  gfits_scan (&header, "CRPIX1",   "%f", 1, &coords.crpix1);
+  gfits_scan (&header, "CRPIX2",   "%f", 1, &coords.crpix2);
+  gfits_scan (&header, "PC001001", "%f", 1, &coords.pc1_1);
+  gfits_scan (&header, "PC001002", "%f", 1, &coords.pc1_2);
+  gfits_scan (&header, "PC002001", "%f", 1, &coords.pc2_1);
+  gfits_scan (&header, "PC002002", "%f", 1, &coords.pc2_2);
+  /* RA Terms */
+  gfits_scan (&header, "PCA1X2Y0", "%f", 1, &coords.polyterms[0][0]);
+  gfits_scan (&header, "PCA1X1Y1", "%f", 1, &coords.polyterms[1][0]);
+  gfits_scan (&header, "PCA1X0Y2", "%f", 1, &coords.polyterms[2][0]);
+  gfits_scan (&header, "PCA1X3Y0", "%f", 1, &coords.polyterms[3][0]);
+  gfits_scan (&header, "PCA1X2Y1", "%f", 1, &coords.polyterms[4][0]);
+  gfits_scan (&header, "PCA1X1Y2", "%f", 1, &coords.polyterms[5][0]);
+  gfits_scan (&header, "PCA1X0Y3", "%f", 1, &coords.polyterms[6][0]);
+  /* Dec Terms */			    
+  gfits_scan (&header, "PCA2X2Y0", "%f", 1, &coords.polyterms[0][1]);
+  gfits_scan (&header, "PCA2X1Y1", "%f", 1, &coords.polyterms[1][1]);
+  gfits_scan (&header, "PCA2X0Y2", "%f", 1, &coords.polyterms[2][1]);
+  gfits_scan (&header, "PCA2X3Y0", "%f", 1, &coords.polyterms[3][1]);
+  gfits_scan (&header, "PCA2X2Y1", "%f", 1, &coords.polyterms[4][1]);
+  gfits_scan (&header, "PCA2X1Y2", "%f", 1, &coords.polyterms[5][1]);
+  gfits_scan (&header, "PCA2X0Y3", "%f", 1, &coords.polyterms[6][1]);
+  coords.Npolyterms = 2; /* how many do we use? */
+
+  /* find number of stars */
+  gfits_scan (&header, "NSTARS", "%d", 1, &Nstars);
+  if (Nstars == 0) {
+    gprint (GP_ERR, "no stars in file\n");
+    return (FALSE);
+  }
+
+  /* open file data */
+  f = fopen (argv[1], "r");
+  if (f == NULL) {
+    gprint (GP_ERR, "can't find data in file %s\n", argv[1]);
+    return (FALSE);
+  }
+  fseeko (f, header.datasize, SEEK_SET); 
+
+  /* set up storage buffers */
+  SetVector (&Xvec, OPIHI_FLT, Nstars);
+  SetVector (&Yvec, OPIHI_FLT, Nstars);
+  SetVector (&Zvec, OPIHI_FLT, Nstars);
+  ALLOCATE (buffer, char, (BLOCK*BYTES_STAR));
+
+  /* load in stars by blocks of 1000 */
+  nstars = 0;
+  Nbytes = Nstars*BYTES_STAR;
+  for (i = 0; i < (int)(Nbytes / (BLOCK*BYTES_STAR)); i++) {
+    nbytes = fread (buffer, 1, (BLOCK*BYTES_STAR), f);
+    if (nbytes != BLOCK*BYTES_STAR) {
+      gprint (GP_ERR, "failed to read in stars (1)\n");
+      free (Xvec.elements.Flt);
+      free (Yvec.elements.Flt);
+      free (Zvec.elements.Flt);
+      free (buffer);
+      return (FALSE);
+    }
+    for (j = 0; j < BLOCK; j++, nstars++) {
+      dparse (&X,  1, &buffer[j*BYTES_STAR]);
+      dparse (&Y,  2, &buffer[j*BYTES_STAR]);
+      dparse (&M,  3, &buffer[j*BYTES_STAR]);
+      XY_to_RD (&R, &D, X, Y, &coords);
+      RD_to_XY (&Xvec.elements.Flt[nstars], &Yvec.elements.Flt[nstars], R, D, &graphmode.coords);
+      Zvec.elements.Flt[nstars] = MIN (1.0, MAX (0.01, (M - zero) / range));
+    }
+  }
+  /* left over fraction of a block */
+  nbytes = fread (buffer, 1, (Nbytes % (BLOCK*BYTES_STAR)), f);
+  if (nbytes != (Nbytes % (BLOCK*BYTES_STAR))) {
+    gprint (GP_ERR, "ERROR: failed to read in stars (2)\n");
+    free (Xvec.elements.Flt);
+    free (Yvec.elements.Flt);
+    free (Zvec.elements.Flt);
+    free (buffer);
+    return (FALSE);
+  }
+  for (j = 0; j < nbytes / BYTES_STAR; j++, nstars++) {
+    dparse (&X,  1, &buffer[j*BYTES_STAR]);
+    dparse (&Y,  2, &buffer[j*BYTES_STAR]);
+    dparse (&M,  3, &buffer[j*BYTES_STAR]);
+    XY_to_RD (&R, &D, X, Y, &coords);
+    RD_to_XY (&Xvec.elements.Flt[nstars], &Yvec.elements.Flt[nstars], R, D, &graphmode.coords);
+    Zvec.elements.Flt[nstars] = MIN (1.0, MAX (0.01, (M - zero) / range));
+  }
+  
+  if (nstars != Nstars) {
+    gprint (GP_ERR, "ERROR: failed to read in all stars (%d of %d)\n", nstars, Nstars);
+    free (Xvec.elements.Flt);
+    free (Yvec.elements.Flt);
+    free (Zvec.elements.Flt);
+    free (buffer);
+    return (FALSE);
+  }
+
+  graphmode.style = 2;
+  graphmode.size = -1;
+  graphmode.etype = 0;
+
+  PlotVectorTriplet (kapa, &Xvec, &Yvec, &Zvec, NULL, &graphmode);
+
+  free (Xvec.elements.Flt);
+  free (Yvec.elements.Flt);
+  free (Zvec.elements.Flt);
+  free (buffer);
+
+  return (TRUE);
+
+}
+
+
Index: /branches/sc_branches/pantasks_condor/dvo/skycat.c
===================================================================
--- /branches/sc_branches/pantasks_condor/dvo/skycat.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/dvo/skycat.c	(revision 34783)
@@ -0,0 +1,130 @@
+# include "dvoshell.h"
+
+int RD_to_XYpic (double *x, double *y, double r, double d, Coords *coords, double Rmin, double Rmax, double Rmid, int *leftside);
+
+int skycat (int argc, char **argv) {
+  
+  double Radius;
+  int i, j, N, Nregions, kapa, ShowAll, NPTS, Npts, leftside, Depth, VERBOSE;
+  struct stat filestat;
+  Vector Xvec, Yvec;
+  Graphdata graphmode;
+  double X[4], Y[4], Rmin, Rmax, Rmid;
+  SkyTable *sky;
+  SkyList *skylist;
+  SkyRegion **regions;
+
+  VERBOSE = FALSE;
+  if ((N = get_argument (argc, argv, "-v"))) {
+    remove_argument (N, &argc, argv);
+    VERBOSE = TRUE;
+  }
+  ShowAll = FALSE;
+  if ((N = get_argument (argc, argv, "-all"))) {
+    remove_argument (N, &argc, argv);
+    ShowAll = TRUE;
+  }
+  Depth = -1;
+  if ((N = get_argument (argc, argv, "-depth"))) {
+    remove_argument (N, &argc, argv);
+    Depth = atoi (argv[N]);
+    remove_argument (N, &argc, argv);    
+  }
+
+  if (!style_args (&graphmode, &argc, argv, &kapa)) return FALSE;
+
+  if (argc != 1) {
+    gprint (GP_ERR, "USAGE: skycat [-all] [-depth depth] [-v]\n");
+    return (FALSE);
+  }
+
+  Radius = MAX (fabs(graphmode.xmax), fabs(graphmode.ymax));
+
+  sky = GetSkyTable ();
+  skylist = SkyListByRadius (sky, Depth, graphmode.coords.crval1, graphmode.coords.crval2, Radius);
+  
+  if (VERBOSE) gprint (GP_ERR, "region: %6.2f - %6.2f, %6.2f - %6.2f\n", 
+			graphmode.coords.crval1 - Radius, graphmode.coords.crval1 + Radius, 
+			graphmode.coords.crval2 - Radius, graphmode.coords.crval2 + Radius);
+
+  Rmin = graphmode.coords.crval1 - 180.0;
+  Rmax = graphmode.coords.crval1 + 180.0;
+  Rmid = 0.5*(Rmin + Rmax);
+
+  Npts = 0;
+  NPTS = 200;
+  SetVector (&Xvec, OPIHI_FLT, NPTS);
+  SetVector (&Yvec, OPIHI_FLT, NPTS);
+   
+  regions = skylist[0].regions;
+  Nregions = skylist[0].Nregions;
+
+  // prepare to handle interrupt signals
+  signal (SIGINT, handle_interrupt);
+  interrupt = FALSE;
+
+  for (i = 0; (i < Nregions) && !interrupt; i++) {
+    if (ShowAll || (stat (skylist[0].filename[i], &filestat) != -1)) {
+      if (VERBOSE) gprint (GP_ERR, "%3d %s %6.2f - %6.2f, %6.2f - %6.2f\n", i, regions[i][0].name, 
+			    regions[i][0].Rmin, regions[i][0].Rmax, regions[i][0].Dmin, regions[i][0].Dmax);
+
+      leftside = -1;
+      RD_to_XYpic (&X[0], &Y[0], regions[i][0].Rmin, regions[i][0].Dmin, &graphmode.coords, Rmin, Rmax, Rmid, &leftside);
+      RD_to_XYpic (&X[1], &Y[1], regions[i][0].Rmin, regions[i][0].Dmax, &graphmode.coords, Rmin, Rmax, Rmid, &leftside);
+      RD_to_XYpic (&X[2], &Y[2], regions[i][0].Rmax, regions[i][0].Dmax, &graphmode.coords, Rmin, Rmax, Rmid, &leftside);
+      RD_to_XYpic (&X[3], &Y[3], regions[i][0].Rmax, regions[i][0].Dmin, &graphmode.coords, Rmin, Rmax, Rmid, &leftside);
+
+      Xvec.elements.Flt[Npts] = X[0];
+      Yvec.elements.Flt[Npts] = Y[0];
+      for (j = 1; j < 4; j++) {
+	Xvec.elements.Flt[Npts + j*2 - 0] = X[j];
+	Yvec.elements.Flt[Npts + j*2 - 0] = Y[j];
+	Xvec.elements.Flt[Npts + j*2 - 1] = X[j];
+	Yvec.elements.Flt[Npts + j*2 - 1] = Y[j];
+      }
+      Xvec.elements.Flt[Npts+7] = Xvec.elements.Flt[Npts];
+      Yvec.elements.Flt[Npts+7] = Yvec.elements.Flt[Npts];
+      Npts += 8;
+      if (Npts > NPTS - 1) {  /* this is OK because NPTS is made always a multiple of 8 */
+	NPTS += 200;
+	REALLOCATE (Xvec.elements.Flt, opihi_flt, NPTS);
+	REALLOCATE (Yvec.elements.Flt, opihi_flt, NPTS);
+      }
+    }
+  }
+
+  gprint (GP_ERR, "plotting %d catalogs\n", Npts/8);
+  Xvec.Nelements = Yvec.Nelements = Npts;
+  if (Npts > 0) {
+    graphmode.style = 2; /* points */
+    graphmode.ptype = 100; /* connect pairs of points */
+    graphmode.etype = 0;
+    PlotVectorPair (kapa, &Xvec, &Yvec, NULL, &graphmode);
+  }
+
+  free (Xvec.elements.Ptr);
+  free (Yvec.elements.Ptr);
+  free (regions);
+
+  return (TRUE);
+
+}
+
+
+int RD_to_XYpic (double *x, double *y, double r, double d, Coords *coords, double Rmin, double Rmax, double Rmid, int *leftside) {
+
+  r = ohana_normalize_angle (r);
+  while (r < Rmin) { r += 360.0; }
+  while (r > Rmax) { r -= 360.0; }
+
+  if (*leftside == -1) {
+    *leftside = (r < Rmid);
+  } else {
+    if (  *leftside && (r > Rmid + 90)) { r -= 360.0; }
+    if (! *leftside && (r < Rmid - 90)) { r += 360.0; }
+  }
+
+  RD_to_XY (x, y, r, d, coords);
+
+  return (TRUE);
+}
Index: /branches/sc_branches/pantasks_condor/dvo/skycoverage.c
===================================================================
--- /branches/sc_branches/pantasks_condor/dvo/skycoverage.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/dvo/skycoverage.c	(revision 34783)
@@ -0,0 +1,359 @@
+# include "dvoshell.h"
+
+// enum to define possible modes
+enum {COVERAGE, DENSITY, MIN_UBERCAL, MIN_DMAG_SYS, MIN_MCAL, MAX_MCAL, MIN_TIME, MAX_TIME};
+
+int wordhash (char *word);
+
+int skycoverage (int argc, char **argv) {
+
+  int WITH_MOSAIC, SOLO_MOSAIC, mode;
+  off_t i, Nimage;
+  int N, status, TimeSelect, ByName, xs, ys;
+  time_t tzero, tend;
+  double pixscale, dX, dY, Npts, r, d, Xi, Yi, Xs, Ys, x[2], y[2], trange, RaCenter, DecCenter;
+  Image *image;
+  char name[256], projection[16];
+  float *V;
+  int Nx, Ny;
+  Buffer *buf;
+  Coords coords;
+  int typehash;
+  int PhotcodeSelect;
+  PhotCode *PhotcodeValue;
+
+  time_t TimeReference;
+  int TimeFormat;
+
+  GetTimeFormat (&TimeReference, &TimeFormat);
+
+  WITH_MOSAIC = FALSE;
+  if ((N = get_argument (argc, argv, "+mosaic"))) {
+    remove_argument (N, &argc, argv);
+    WITH_MOSAIC = TRUE;
+  }
+
+  SOLO_MOSAIC = FALSE;
+  if ((N = get_argument (argc, argv, "-mosaic"))) {
+    remove_argument (N, &argc, argv);
+    SOLO_MOSAIC = TRUE;
+    WITH_MOSAIC = TRUE;
+  }
+
+  RaCenter = 0.0;
+  DecCenter = 0.0;
+  if ((N = get_argument (argc, argv, "-center"))) {
+    remove_argument (N, &argc, argv);
+    RaCenter = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+    DecCenter = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  Nx = 0;
+  Ny = 0;
+  if ((N = get_argument (argc, argv, "-size"))) {
+    remove_argument (N, &argc, argv);
+    Nx = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+    Ny = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  pixscale = 1.0;
+  if ((N = get_argument (argc, argv, "-scale"))) {
+    remove_argument (N, &argc, argv);
+    pixscale = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  mode = COVERAGE;
+  if ((N = get_argument (argc, argv, "-density"))) {
+    remove_argument (N, &argc, argv);
+    mode = DENSITY;
+  }
+  if ((N = get_argument (argc, argv, "-min-ubercal"))) {
+    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"))) {
+    remove_argument (N, &argc, argv);
+    mode = MIN_DMAG_SYS;
+  }
+  if ((N = get_argument (argc, argv, "-min-mcal"))) {
+    remove_argument (N, &argc, argv);
+    mode = MIN_MCAL;
+  }
+  if ((N = get_argument (argc, argv, "-max-mcal"))) {
+    remove_argument (N, &argc, argv);
+    mode = MAX_MCAL;
+  }
+
+  ByName = FALSE;
+  if ((N = get_argument (argc, argv, "-name"))) {
+    remove_argument (N, &argc, argv);
+    strcpy (name, argv[N]);
+    remove_argument (N, &argc, argv);
+    ByName = TRUE;
+  }
+
+  strcpy (projection, "DEC--AIT");
+  if ((N = get_argument (argc, argv, "-proj"))) {
+    remove_argument (N, &argc, argv);
+    if (!strcasecmp(argv[N], "TAN")) {
+	strcpy (projection, "DEC--TAN");
+    }	
+    if (!strcasecmp(argv[N], "SIN")) {
+	strcpy (projection, "DEC--SIN");
+    }	
+    if (!strcasecmp(argv[N], "GLS")) {
+	strcpy (projection, "DEC--GLS");
+    }	
+    if (!strcasecmp(argv[N], "PAR")) {
+	strcpy (projection, "DEC--PAR");
+    }	
+    remove_argument (N, &argc, argv);
+  }
+
+  PhotcodeValue = NULL;
+  PhotcodeSelect = FALSE;
+  if ((N = get_argument (argc, argv, "-photcode"))) {
+    if (!InitPhotcodes ()) return (FALSE);
+    PhotcodeSelect = TRUE;
+    remove_argument (N, &argc, argv);
+    PhotcodeValue = GetPhotcodebyName (argv[N]);
+    if (PhotcodeValue == NULL) {
+      gprint (GP_ERR, "photcode not found in photcode table\n");
+      return (FALSE);
+    }
+    remove_argument (N, &argc, argv);
+  }
+
+  TimeSelect = FALSE;
+  if ((N = get_argument (argc, argv, "-time"))) {
+    remove_argument (N, &argc, argv);
+    if (!ohana_str_to_time (argv[N], &tzero)) { 
+      gprint (GP_ERR, "syntax error\n");
+      return (FALSE);
+    }
+    remove_argument (N, &argc, argv);
+    if (!ohana_str_to_dtime (argv[N], &trange)) { 
+      gprint (GP_ERR, "syntax error\n");
+      return (FALSE);
+    }
+    remove_argument (N, &argc, argv);
+    if (trange < 0) {
+      trange = fabs (trange);
+      tzero -= trange;
+    }
+    TimeSelect = TRUE;
+  }
+  if ((N = get_argument (argc, argv, "-trange"))) {
+    remove_argument (N, &argc, argv);
+    if (!ohana_str_to_time (argv[N], &tzero)) { 
+      gprint (GP_ERR, "syntax error\n");
+      return (FALSE);
+    }
+    remove_argument (N, &argc, argv);
+    if (!ohana_str_to_time (argv[N], &tend)) { 
+      gprint (GP_ERR, "syntax error\n");
+      return (FALSE);
+    }
+    remove_argument (N, &argc, argv);
+    trange = tend - tzero;
+    if (trange < 0) {
+      trange = fabs (trange);
+      tzero -= trange;
+    }
+    TimeSelect = TRUE;
+  }
+ 
+  if (argc != 3) {
+    gprint (GP_ERR, "USAGE: skycoverage (buffer) (Npts)\n");
+    gprint (GP_ERR, "  options: [-scale pixscale] [-center ra dec] [-size Nx Nx] [-proj projection] [-time start range] [-trange start stop] [-name name] [-photcode name] [+mosaic] [-mosaic] [-density]\n");
+    gprint (GP_ERR, "       (buffer) saves bitmapped image\n");
+    gprint (GP_ERR, "       (Npts) gives the number of test points per image in each dimension\n");
+    gprint (GP_ERR, "       -scale (pixscale)  : specifies the pixel size in degrees [1.0]\n");
+    gprint (GP_ERR, "       -center (ra) (dec) : specifies the center of the field [0.0, 0.0]\n");
+    gprint (GP_ERR, "       -size (Nx) (Ny)    : specifies the size of the image [360/scale, 180/scale]\n");
+    gprint (GP_ERR, "       -proj (projection) : specifies the projection choice [AIT]\n");
+    gprint (GP_ERR, "       -density           : create image with relative density (else binary on/off)\n");
+    gprint (GP_ERR, "       note: we need 64800 / (pixscale)^2 pixels to represent the sky\n");
+    return (FALSE);
+  }
+  
+  if ((buf = SelectBuffer (argv[1], ANYBUFFER, TRUE)) == NULL) return (FALSE);
+  Npts = atof(argv[2]);
+
+  if (!Nx || !Ny) {
+    Nx = 360/pixscale;
+    Ny = 180/pixscale;
+  }
+
+  gfits_free_matrix (&buf[0].matrix);
+  gfits_free_header (&buf[0].header);
+  CreateBuffer (buf, Nx, Ny, -32, 0.0, 1.0);
+  strcpy (buf[0].file, "(empty)");
+
+  coords.crval1 = RaCenter;
+  coords.crval2 = DecCenter;
+  coords.crpix1 = 0.5*Nx;
+  coords.crpix2 = 0.5*Ny;
+  strcpy (coords.ctype, projection);
+  coords.pc1_1 = -1.0;
+  coords.pc2_2 = +1.0;
+  coords.pc1_2 =  0.0;
+  coords.pc2_1 =  0.0;
+  coords.cdelt1 = coords.cdelt2 = pixscale;
+  coords.Npolyterms = 0;
+
+  PutCoords (&coords, &buf[0].header);
+
+  image = LoadImagesDVO(&Nimage);
+  if (image == NULL) {
+      return (FALSE);
+  }
+  BuildChipMatch (image, Nimage);
+
+  V = (float *)buf[0].matrix.buffer;
+
+  // init the V array (depends on mode)
+  for (ys = 0; ys < Ny; ys++) {
+    for (xs = 0; xs < Nx; xs++) {
+      status = XY_to_RD (&r, &d, (double)(xs), (double)(ys), &coords);
+      status &= (r >= 0);
+      status &= (r <= 360);
+      // are we in a part of the projection covering the sky or not?
+      if (status) {
+	switch (mode) {
+	  case COVERAGE:
+	    V[ys*Nx + xs] = 2;
+	    break;
+	  case DENSITY:
+	    V[ys*Nx + xs] = 0;
+	    break;
+	  case MIN_UBERCAL:
+	  case MIN_DMAG_SYS:
+	  case MIN_MCAL:
+	  case MIN_TIME:
+	    V[ys*Nx + xs] = 1E9;
+	    break;
+	  case MAX_MCAL:
+	  case MAX_TIME:
+	    V[ys*Nx + xs] = -1E9;
+	    break;
+	}
+      } else {
+	switch (mode) {
+	  case COVERAGE:
+	    V[ys*Nx + xs] = 0;
+	    break;
+	  case DENSITY:
+	  case MIN_UBERCAL:
+	  case MIN_DMAG_SYS:
+	  case MIN_MCAL:
+	  case MAX_MCAL:
+	  case MIN_TIME:
+	  case MAX_TIME:
+	    V[ys*Nx + xs] = NAN;
+	    break;
+	}
+      }
+    }
+  }
+
+  int DistortImage = wordhash ("-DIS");
+
+  for (i = 0; i < Nimage; i++) {
+    if (ByName && strcmp (name, image[i].name)) continue;
+    if (TimeSelect && ((image[i].tzero < tzero) || (image[i].tzero+image[i].trate*image[i].NY > tzero + trange))) continue;
+
+    if (PhotcodeSelect) {
+      if (PhotcodeValue[0].type == PHOT_DEP) {
+	if (PhotcodeValue[0].code != image[i].photcode) continue;
+      } else {
+	if (PhotcodeValue[0].code != GetPhotcodeEquivCodebyCode (image[i].photcode)) continue;
+      }
+    }
+
+    if (!FindMosaicForImage (image, Nimage, i)) continue;
+
+    typehash = wordhash (&image[i].coords.ctype[4]);
+
+    /* DIS images represent a field, not a chip */
+    if ((typehash == DistortImage) && !WITH_MOSAIC) continue;
+    if ((typehash != DistortImage) &&  SOLO_MOSAIC) continue;
+
+    /* project this image to screen display coords */
+    /* DIS images represent a field, not a chip */
+    if (!strcmp(&image[i].coords.ctype[4], "-DIS")) {
+      x[0] = -0.5*image[i].NX; y[0] = -0.5*image[i].NY;
+      x[1] = +0.5*image[i].NX; y[1] = +0.5*image[i].NY;
+    } else {
+      x[0] = 0;                y[0] = 0;
+      x[1] = image[i].NX;      y[1] = image[i].NY;
+    }
+    status = FALSE;
+    
+    dX = (x[1] - x[0]) / Npts;
+    dY = (y[1] - y[0]) / Npts;
+    
+    for (Yi = y[0] + 0.5*dY; Yi < y[1]; Yi += dY) {
+      for (Xi = x[0] + 0.5*dX; Xi < x[1]; Xi += dX) {
+	XY_to_RD (&r, &d, Xi, Yi, &image[i].coords);
+	r = ohana_normalize_angle (r);
+	if (r - RaCenter > +180.0) r -= 360.0;
+	if (r - RaCenter < -180.0) r += 360.0;
+	status = RD_to_XY (&Xs, &Ys, r, d, &coords);
+	if (Xs < 0) continue;
+	if (Ys < 0) continue;
+	if (Xs >= Nx) continue;
+	if (Ys >= Ny) continue;
+	if (status) {
+	  xs = (int)Xs;
+	  ys = (int)Ys;
+	  switch (mode) {
+	    case COVERAGE:
+	      V[ys*Nx + xs] = 1;
+	      break;
+	    case DENSITY:
+	      V[ys*Nx + xs] += 1;
+	      break;
+	    case MIN_UBERCAL:
+	      V[ys*Nx + xs] = MIN(V[ys*Nx + xs], image[i].ubercalDist);
+	      break;
+	    case MIN_DMAG_SYS:
+	      V[ys*Nx + xs] = MIN(V[ys*Nx + xs], image[i].dMagSys);
+	      break;
+	    case MIN_MCAL:
+	      V[ys*Nx + xs] = MIN(V[ys*Nx + xs], image[i].Mcal);
+	      break;
+	    case MAX_MCAL:
+	      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; }
+	  }
+	}
+      }
+    }
+  }
+  FreeImagesDVO(image);
+  return (TRUE);
+}
+
+
Index: /branches/sc_branches/pantasks_condor/dvo/skydbtile.c
===================================================================
--- /branches/sc_branches/pantasks_condor/dvo/skydbtile.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/dvo/skydbtile.c	(revision 34783)
@@ -0,0 +1,283 @@
+# include "dvo2.h"
+SkyRegion *SkyDivideList (SkyRegion *list, int Nlist);
+
+/* region names:
+
+ depth Ntables  Name 
+ 0     1      	fullsky.cpt
+ 1     16     	n????.cpt, s????.cpt
+ 2     256    	n????/r????.cpt
+ 3     4096   	n????/t????.cpt
+ 4     65536  	n????/r????/r????.cpt
+ 5     1.0+06  	n????/t????/t????.cpt
+ 6     1.7+07  	n????/r????/r????/r????.cpt
+
+ depth Ntables  Name 
+ 0     1      	fullsky
+ 1     16     	d%02
+ 2     256    	d%02/r%03d
+ 3     4096   	d%02/t%04d
+ 4     65536  	d%02/r%03d/r%03d
+ 5     1.0+06  	d%02/t%04d/t%04d
+ 6     1.7+07  	d%02/r%03d/r%03d/r%03d
+ 7     2.7+08  	d%02/t%04d/t%04d/t%04d
+
+*/
+
+/* a valid SkyRegion set must always start with the fullsky as the first entry, 
+   with the sequence of entries ordered primarily by depth, followed by parent 
+*/
+
+int SkyMakeNames (SkyRegion *db, SkyRegion *ref, int depth) {
+
+  int i, j, N, Ns, Ne, ns, ne;
+
+  if (db == NULL) return (FALSE);
+  if (depth == 0) {
+    if (db[0].depth != 0) return (FALSE);
+    strcpy (db[0].name, "fullsky");
+    Ns = db[0].childS;
+    Ne = db[0].childE;
+    for (i = Ns; i < Ne; i++) {
+      sprintf (db[i].name, "d%02d", i - 1);
+      if (!SkyMakeNames (db, &db[i], 2)) return (FALSE);
+      if (!SkyMakeNames (db, &db[i], 3)) return (FALSE);
+    }
+    return (TRUE);
+  }
+
+  if (ref == NULL) return (FALSE);
+  
+  if (depth == 2) {
+    if (!ref[0].child) return (TRUE);
+    Ns = ref[0].childS;
+    Ne = ref[0].childE;
+    N = 0;
+    for (i = Ns; i < Ne; i++, N++) {
+      sprintf (db[i].name, "%s/r%02d", ref[0].name, N);
+      if (!SkyMakeNames (db, &db[i], depth + 2)) return (FALSE);
+    }
+  }
+  
+  if (depth > 2) {
+    Ns = ref[0].childS;
+    Ne = ref[0].childE;
+    N = 0;
+    for (i = Ns; i < Ne; i++) {
+      if (!ref[0].child) continue;
+      ns = db[i].childS;
+      ne = db[i].childE;
+      for (j = ns; j < ne; j++, N++) {
+	if (depth % 2) {
+	  sprintf (db[j].name, "%s/t%03d", ref[0].name, N);
+	} else {				      			    
+	  sprintf (db[j].name, "%s/r%02d", ref[0].name, N);
+	}
+	if (!SkyMakeNames (db, &db[j], depth + 2)) return (FALSE);
+      }
+    }  
+  }
+}
+
+SkyRegion *SkyMakeRegions (int Nlevels, int *nlist) {
+
+  int Nlist, Nnext, Ncurr, i, j;
+  SkyRegion *list, *curr, *next;
+
+  Nlist = 0;
+  Nnext = 1;
+  Ncurr = 1;
+  ALLOCATE (list, SkyRegion, Ncurr);
+  ALLOCATE (curr, SkyRegion, Ncurr);
+
+  curr[0].Rmin =   0;
+  curr[0].Rmax = 360;
+  curr[0].Dmin = -90;
+  curr[0].Dmax =  90;
+  curr[0].depth = 0;
+  curr[0].object = FALSE;
+  curr[0].image = FALSE;
+  curr[0].child = FALSE;
+  curr[0].childS = 0;
+  curr[0].childE = 0;
+
+  for (i = 1; i < Nlevels; i++) {
+    next  = SkyDivideRegions (curr, &Nnext);
+    for (j = 0; j < Nnext; j++) {
+      next[j].depth = i;
+    }   
+    REALLOCATE (list, SkyRegion, Nlist + Ncurr);
+    memcpy (&list[Nlist], curr, Ncurr*sizeof(SkyRegion));
+    for (j = Nlist; j < Nlist + Ncurr; j++) {
+      list[j].childS += Nlist + Ncurr;
+      list[j].childE += Nlist + Ncurr;
+    }
+    Nlist += Ncurr;
+    free (curr);
+    curr = next;
+    Ncurr = Nnext;
+  }
+  REALLOCATE (list, SkyRegion, Nlist + Ncurr);
+  memcpy (&list[Nlist], curr, Ncurr*sizeof(SkyRegion));
+  Nlist += Ncurr;
+  free (curr);
+
+  list[0].object = TRUE;
+  list[0].image = TRUE;
+  *nlist = Nlist;
+  return (list);
+}
+
+SkyRegion *SkyDivideRegions (SkyRegion *parents, int *N) {
+
+  int i, Nchildren, Nparents, Np;
+  SkyRegion *children, *temp;
+  float *child, *index;
+
+  Nchildren = Nparents = *N;
+
+  /* copy input list to a new list, set child to be sequence number */
+  ALLOCATE (children, SkyRegion, Nchildren);
+  memcpy (children, parents, Nchildren*sizeof(SkyRegion));
+  for (i = 0; i < Nchildren; i++) {
+    children[i].childS = i;
+    parents[i].childS = i;
+  }
+
+  /* double the number of children, dividing only the largest in half each pass */
+  for (i = 0; i < 4; i++) {
+    temp = SkyDivideList (children, Nchildren);
+    free (children);
+    children = temp;
+    Nchildren *= 2;
+  }
+
+  /* sort children by childS (currently is parent entry) */
+  ALLOCATE (child, float, Nchildren);
+  ALLOCATE (index, float, Nchildren);
+  for (i = 0; i < Nchildren; i++) {
+    index[i] = i;
+    child[i] = children[i].childS;
+  }
+  fsortpair (child, index, Nchildren);
+  ALLOCATE (temp, SkyRegion, Nchildren);
+  for (i = 0; i < Nchildren; i++) {
+    temp[i] = children[(int)index[i]];
+  }
+  free (children);
+  free (child);
+  free (index);
+  children = temp;
+  
+  /* look for all matching values of childS, setup parent values of childS, childE */
+  Np = children[0].childS;
+  parents[Np].childS = 0;
+  parents[Np].child = TRUE;
+  for (i = 0; i < Nchildren; i++) {
+    if (children[i].childS != Np) {
+      parents[Np].childE = i;
+      Np = children[i].childS;
+      parents[Np].childS = i;
+      parents[Np].child = TRUE;
+    }
+    children[i].childS = 0;
+    children[i].childE = 0;
+    children[i].child = FALSE;
+  }
+  parents[Np].childE = i;
+  *N = Nchildren;
+  return (children);
+}
+
+/* always break the region with the largest area */
+SkyRegion *SkyDivideBiggest (SkyRegion *list, int *nlist) {
+
+  int i, N, Nlist;
+  float *area, *indx, dec;
+  SkyRegion *out, save;
+
+  Nlist = *nlist;
+  ALLOCATE (area, float, Nlist);
+  ALLOCATE (indx, float, Nlist);
+  for (i = 0; i < Nlist; i++) {
+    dec = 0.5*(list[i].Dmax + list[i].Dmin);
+    area[i] = (list[i].Rmax - list[i].Rmin)*(list[i].Dmax - list[i].Dmin)*cos(RAD_DEG*dec);
+    indx[i] = i;
+  }
+  fsortpair (area, indx, Nlist);
+
+  N = indx[Nlist - 1];
+  out = SkyDivide (&list[N]);
+  
+  REALLOCATE (list, SkyRegion, Nlist + 1);
+  list[N] = out[0];
+  list[Nlist] = out[1];
+
+  *nlist = Nlist + 1;
+  free (out);
+  free (area);
+  free (indx);
+  return (list);
+}
+
+SkyRegion *SkyDivide (SkyRegion *in) {
+
+  SkyRegion *out;
+  double RA, DEC, R_div, D_div;
+
+  ALLOCATE (out, SkyRegion, 2);
+
+  out[0] = in[0];
+  out[1] = in[0];
+
+  RA  = 0.5*(in[0].Rmin + in[0].Rmax);
+  DEC = 0.5*(in[0].Dmin + in[0].Dmax);
+  if ((DEC >= 90) || (DEC <= -90)) {
+    gprint (GP_ERR, "error in Dmin");
+    return (NULL);
+  }
+ 
+  D_div = (in[0].Rmax - in[0].Rmin) * cos (RAD_DEG*DEC);
+  R_div = (in[0].Dmax - in[0].Dmin);
+  if (R_div < D_div) {
+    /* divide RA range */
+    out[0].Rmax = RA;
+    out[1].Rmin = RA;
+  } else {
+    out[0].Dmax = DEC;
+    out[1].Dmin = DEC;
+  }
+
+  return (out);
+}
+
+/* return a list with 2x as many entries, each split in two */
+SkyRegion *SkyDivideList (SkyRegion *in, int Nin) {
+
+  SkyRegion *out;
+  double RA, DEC, R_div, D_div;
+  int i;
+
+  ALLOCATE (out, SkyRegion, 2*Nin);
+
+  for (i = 0; i < Nin; i++) {
+    /* inherit properties from parent */
+    out[2*i + 0] = in[i];
+    out[2*i + 1] = in[i];
+
+    RA  = 0.5*(in[i].Rmin + in[i].Rmax);
+    DEC = 0.5*(in[i].Dmin + in[i].Dmax);
+    
+    /* split along longest axis */
+    D_div = (in[i].Rmax - in[i].Rmin) * cos (RAD_DEG*DEC);
+    R_div = (in[i].Dmax - in[i].Dmin);
+    if (R_div < D_div) {
+      out[2*i + 0].Rmax = RA;
+      out[2*i + 1].Rmin = RA;
+    } else {
+      out[2*i + 0].Dmax = DEC;
+      out[2*i + 1].Dmin = DEC;
+    }
+  }
+  return (out);
+}
Index: /branches/sc_branches/pantasks_condor/dvo/skyregion.c
===================================================================
--- /branches/sc_branches/pantasks_condor/dvo/skyregion.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/dvo/skyregion.c	(revision 34783)
@@ -0,0 +1,146 @@
+# include "dvoshell.h"
+
+// define the sky region for which extractions are limited
+int skyregion (int argc, char **argv) {
+  
+  int N;
+
+  // dvo_client should have 2 standard arguments: -hostID and -hostdir
+  int SaveRegion = FALSE;
+  if ((N = get_argument (argc, argv, "-save"))) {
+    remove_argument (N, &argc, argv);
+    SaveRegion = TRUE;
+  }
+
+  if (argc == 1) {
+    double Rmin, Rmax, Dmin, Dmax;
+    get_skyregion(&Rmin, &Rmax, &Dmin, &Dmax);
+
+    if (SaveRegion) {
+      set_variable ("Rmin", Rmin);
+      set_variable ("Rmax", Rmax);
+      set_variable ("Dmin", Dmin);
+      set_variable ("Dmax", Dmax);
+      return TRUE;
+    } else {
+      gprint (GP_ERR, "current skyregion: %f - %f : %f - %f\n", Rmin, Rmax, Dmin, Dmax);
+      gprint (GP_ERR, "USAGE:  skyregion (min RA) (max RA) (min DEC) (max DEC)\n");
+      return FALSE;
+    }
+  }
+
+  if (argc != 5) {
+    gprint (GP_ERR, "USAGE: skyregion (min RA) (max RA) (min DEC) (max DEC)\n");
+    return (FALSE);
+  }
+
+  set_skyregion (atof(argv[1]), atof(argv[2]), atof(argv[3]), atof(argv[4]));
+
+  return (TRUE);
+}
+
+/* find region which overlaps c at given depth (-1 : populated ) */
+int SkyRegionByPoint_r (SkyTable *table, SkyList *list, int depth, double ra, double dec) {
+  
+  int i, Ns, Ne, No;
+  SkyRegion *skyregion;
+
+  list[0].Nregions = 0;
+  list[0].ownElements = FALSE; // this list is only holding a view to the elements
+
+  skyregion = table[0].regions;
+
+  Ns = 0;
+  Ne = 1;
+
+  while (1) {
+    No = -1;
+    for (i = Ns; (i < Ne) && (i < table[0].Nregions); i++) {
+      if (ra  < skyregion[i].Rmin) continue;
+      if (ra  > skyregion[i].Rmax) continue;
+      if (dec < skyregion[i].Dmin) continue;
+      if (dec > skyregion[i].Dmax) continue;
+      No = i;
+      break;
+    }
+    if (No == -1) return (FALSE);
+    if ((depth == -1) && (skyregion[No].table)) break;
+    if (depth == skyregion[No].depth) break;
+    if ((depth > skyregion[No].depth) && !skyregion[No].child) return (FALSE);
+
+    /* need to check Ns, Ne, or guarantee valid range */
+    Ns = skyregion[No].childS;
+    Ne = skyregion[No].childE;
+  }
+
+  list[0].regions[0] = &skyregion[No];
+  list[0].filename[0] = table[0].filename[No];
+  list[0].Nregions = 1;
+  return (TRUE);
+}
+
+SkyList *SelectRegionsByCoordVectors (Vector *RA, Vector *DEC) {
+  
+  int i, j, Npts, Nout, NOUT, *found;
+  double ra, dec;
+  SkyList *new, *list;
+  SkyTable *sky;
+
+  sky = GetSkyTable ();
+
+  Npts = RA->Nelements;
+  ALLOCATE (found, int, Npts);
+  memset (found, 0, Npts*sizeof(int));
+
+  ALLOCATE (new, SkyList, 1);
+  ALLOCATE (new[0].regions,  SkyRegion *, 1);
+  ALLOCATE (new[0].filename,  char *, 1);
+  new[0].Nregions = 0;
+  new[0].ownElements = FALSE; // this list is only holding a view to the elements
+
+  // output list
+  Nout = 0;
+  NOUT = 100;
+  ALLOCATE (list, SkyList, 1);
+  ALLOCATE (list[0].regions,  SkyRegion *, NOUT);
+  ALLOCATE (list[0].filename,  char *, NOUT);
+  list[0].Nregions = 0;
+  list[0].ownElements = FALSE; // this list is only holding a view to the elements
+
+  for (i = 0; i < Npts; i++) {
+    if (found[i]) continue;
+
+    SkyRegionByPoint_r (sky, new, -1, RA->elements.Flt[i], DEC->elements.Flt[i]);
+    if (new->Nregions == 0) continue;
+    assert (new->Nregions == 1);
+    // append new region to output list
+    list[0].regions[Nout] = new[0].regions[0];
+    list[0].filename[Nout] = new[0].filename[0];
+    Nout ++;
+    if (Nout >= NOUT) {
+	NOUT += 100;
+	REALLOCATE (list[0].regions, SkyRegion *, NOUT);
+	REALLOCATE (list[0].filename, char *, NOUT);
+    }
+    found[i] = TRUE;
+
+    // scan over the remaining points to find any that lie within this region
+    // if we sorted the ra,dec inputs we could break out of this more quickly
+    for (j = i + 1; j < Npts; j++) {
+      ra = RA->elements.Flt[j];
+      dec = DEC->elements.Flt[j];
+      if (ra  < new[0].regions[0][0].Rmin) continue;
+      if (ra  > new[0].regions[0][0].Rmax) continue;
+      if (dec < new[0].regions[0][0].Dmin) continue;
+      if (dec > new[0].regions[0][0].Dmax) continue;
+      found[j] = TRUE;
+    }
+  }
+  list[0].Nregions = Nout;
+
+  free (found);
+  SkyListFree (new);
+
+  return (list);
+}
+
Index: /branches/sc_branches/pantasks_condor/dvo/subpix.c
===================================================================
--- /branches/sc_branches/pantasks_condor/dvo/subpix.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/dvo/subpix.c	(revision 34783)
@@ -0,0 +1,154 @@
+# include "dvoshell.h"
+
+int subpix (int argc, char **argv) {
+  
+  off_t Nlo, Nhi, *entry, Nentry;
+  off_t j, i, I, *index, Nstars, Nimage, Nmeasure;
+  off_t Nmin, Nsub, NSUB;
+  int status, TimeFormat;
+  time_t Timage, TimeReference;
+  double X, Y, Mabs, t;
+  double Ra, Dec, Radius, Radius2, r, Rmin;
+  double *RA, *DEC;
+  
+  SkyTable *sky;
+  SkyList *skylist;
+  Measure *measure;
+  Image *image;
+  Catalog catalog;
+
+  if (!InitPhotcodes ()) return (FALSE);
+
+  GetTimeFormat (&TimeReference, &TimeFormat);
+
+  if (argc != 4) {
+    gprint (GP_ERR, "USAGE: subpix ra dec radius\n");
+    return (FALSE);
+  }
+  if (!ohana_str_to_radec (&Ra, &Dec, argv[1], argv[2])) return (FALSE);
+  Ra = ohana_normalize_angle (Ra);
+
+  Radius = atof (argv[3]);
+
+  /* load star nearest position */
+  sky = GetSkyTable ();
+  skylist = SkyListByRadius (sky, -1, Ra, Dec, Radius);
+  if (skylist[0].Nregions > 1) {
+    gprint (GP_ERR, "warning, radius overlaps region boundary, not yet implemented\n");
+  }
+
+  /* lock, load, unlock catalog */
+  catalog.filename = skylist[0].filename[0];
+  catalog.catflags = LOAD_AVES | LOAD_MEAS;
+  catalog.Nsecfilt = 0;
+
+  // an error exit status here is a significant error
+  if (!dvo_catalog_open (&catalog, NULL, FALSE, "r")) {
+      fprintf (stderr, "ERROR: failure to open catalog file %s\n", catalog.filename);
+      exit (2);
+  }
+  dvo_catalog_unlock (&catalog);
+
+  /* quick search of star list for Ra, Dec */
+  Nstars = catalog.Naverage;
+  ALLOCATE (RA, double, Nstars);
+  ALLOCATE (DEC, double, Nstars);
+  ALLOCATE (index, off_t, Nstars);
+  for (i = 0; i < Nstars; i++) {
+    RA[i] = catalog.average[i].R;
+    DEC[i] = catalog.average[i].D;
+    index[i] = i;
+  }
+  if (Nstars > 1) sort_coords_index (DEC, RA, index, Nstars);
+
+  /* bracket the DEC range of interest */
+  Nlo = bracket (DEC, Nstars, FALSE, Dec - Radius);
+  Nhi = bracket (DEC, Nstars, TRUE,  Dec + Radius);
+  ALLOCATE (entry, off_t, MAX (Nhi - Nlo, 1));
+  Nentry = 0;
+
+  /* find the list of stars */
+  Radius2 = Radius*Radius;
+  for (i = Nlo; i < Nhi; i++) {
+    r = SQ(Dec - DEC[i]) + SQ(Ra - RA[i]);
+    if (r < Radius2) {
+      entry[Nentry] = i;
+      Nentry ++;
+    }
+  }
+  if (!Nentry) {
+    gprint (GP_ERR, "no stars found\n");
+    free (RA);
+    free (DEC);
+    free (entry);
+    free (index);
+    dvo_catalog_free (&catalog);
+    SkyListFree (skylist);
+    return (TRUE);
+  }
+
+  /* find the closest star */
+  Nmin = 0;
+  Rmin = SQ(Dec - DEC[entry[0]]) + SQ(Ra - RA[entry[0]]);
+  for (i = 1; i < Nentry; i++) {
+    r = SQ(Dec - DEC[entry[i]]) + SQ(Ra - RA[entry[i]]);
+    if (r < Rmin) {
+      Rmin = r;
+      Nmin = i;
+    }
+  }
+  Nentry = index[entry[Nmin]];
+  Ra = RA[entry[Nmin]];
+  Dec = DEC[entry[Nmin]];
+  gprint (GP_ERR, "finding subpix values for star @ %f %f\n", Ra, Dec);
+
+  free (RA);
+  free (DEC);
+  free (entry);
+  free (index);
+
+  /* storage for the image references */
+  Nsub = 0;
+  NSUB = 100;
+  ALLOCATE (index, off_t, NSUB);
+
+  /* load all images, extract those touching Ra, Dec */
+  if ((image = LoadImagesDVO (&Nimage)) == NULL) return (FALSE);
+  BuildChipMatch (image, Nimage);
+
+  for (i = 0; i < Nimage; i++) {
+    if (!FindMosaicForImage (image, Nimage, i)) continue;
+    status = RD_to_XY (&X, &Y, Ra, Dec, &image[i].coords);
+    if (!status || (X < 0) || (X > image[i].NX) || (Y < 0) || (Y > image[i].NY)) continue;
+    index[Nsub] = i;
+    Nsub ++;
+    if (Nsub == NSUB - 1) {
+      NSUB += 100;
+      REALLOCATE (index, off_t, NSUB);
+    }
+  }
+
+  /* only print the entries for existing measurements of this star */ 
+  measure = &catalog.measure[catalog.average[Nentry].measureOffset];
+  Nmeasure = catalog.average[Nentry].Nmeasure;
+  for (i = 0; i < Nsub; i++) {
+    I = index[i];
+    if (!FindMosaicForImage (image, Nimage, I)) continue;
+    Timage = image[I].tzero;
+    for (j = 0; j < Nmeasure; j++) {
+      if (measure[j].t == Timage) { 
+	Mabs = PhotCat (&measure[j]);
+	RD_to_XY (&X, &Y, Ra, Dec, &image[I].coords);
+	t = TimeValue (measure[j].t, TimeReference, TimeFormat);
+	gprint (GP_LOG, "%f %6.3f %7.2f %7.2f %5.3f\n", t, Mabs, X, Y, image[I].secz);
+      } 
+    }
+  }
+
+  dvo_catalog_free (&catalog);
+  free (index);
+  SkyListFree (skylist);
+
+  FreeImagesDVO(image);
+  return (TRUE);
+}
Index: /branches/sc_branches/pantasks_condor/dvo/test/mmatch.sh
===================================================================
--- /branches/sc_branches/pantasks_condor/dvo/test/mmatch.sh	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/dvo/test/mmatch.sh	(revision 34783)
@@ -0,0 +1,36 @@
+
+macro test
+
+  #$Ro = 185.382364
+  #$Do =  34.697608
+  #catdir /data/pikake.1/eugene/dvodist.20120514/dvodist/catdir.syn.test/
+  $Gname = g_SYNTH
+  $Rname = r_SYNTH
+
+  $Ro = 2.59
+  $Do = 1.23
+  catdir /data/pikake.0/eugene/src/ipp-dev/Ohana/src/dvomerge/test/catdir.merge
+  $Gname = g
+  $Rname = r
+
+  skyregion {$Ro - 0.3} {$Ro + 0.3} {$Do - 0.3} {$Do + 0.3}
+  avextract ra dec $Gname $Rname
+  subset R = ra  if (ra > $Ro - 0.1) && (ra > $Ro + 0.1) && (dec > $Do - 0.1) && (dec < $Do + 0.1)
+  subset D = dec if (ra > $Ro - 0.1) && (ra > $Ro + 0.1) && (dec > $Do - 0.1) && (dec < $Do + 0.1)
+  subset g_ave = $Gname  if (ra > $Ro - 0.1) && (ra > $Ro + 0.1) && (dec > $Do - 0.1) && (dec < $Do + 0.1)
+  subset r_ave = $Rname  if (ra > $Ro - 0.1) && (ra > $Ro + 0.1) && (dec > $Do - 0.1) && (dec < $Do + 0.1)
+  vectors 
+  mmatch -v -parallel R D 1.0 RA DEC MAG PHOTCODE -index index
+
+  reindex g_ave_match = g_ave using index
+  reindex r_ave_match = r_ave using index
+
+  set dg = g_ave_match - MAG
+  set dr = r_ave_match - MAG
+  
+  subset  g_match = MAG if (PHOTCODE == 3001)
+  subset dg_match = dg if (PHOTCODE == 3001)
+
+  subset dr_match = dr if (PHOTCODE == 3002)
+  subset  r_match = MAG if (PHOTCODE == 3002)
+end
Index: /branches/sc_branches/pantasks_condor/dvo/version.c
===================================================================
--- /branches/sc_branches/pantasks_condor/dvo/version.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/dvo/version.c	(revision 34783)
@@ -0,0 +1,18 @@
+# include "dvoshell.h"
+static char *name = "$Name: not supported by cvs2svn $";
+
+int version (int argc, char **argv) {
+
+  char *tmp;
+
+  gprint (GP_LOG, "\n");
+  gprint (GP_LOG, "dvo version: %s\n", (tmp = strip_version (name))); free (tmp);
+
+  gprint (GP_LOG, "opihi version: %s\n", (tmp = strip_version (opihi_version()))); free (tmp);
+  gprint (GP_LOG, "libohana version: %s\n", (tmp = strip_version (ohana_version()))); free (tmp);
+  gprint (GP_LOG, "libdvo version: %s\n", (tmp = strip_version (libdvo_version()))); free (tmp);
+  gprint (GP_LOG, "libfits version: %s\n", (tmp = strip_version (gfits_version()))); free (tmp);
+
+  gprint (GP_LOG, "compiled on %s %s\n", __DATE__, __TIME__);
+  return (TRUE);
+}
Index: /branches/sc_branches/pantasks_condor/include/astro.h
===================================================================
--- /branches/sc_branches/pantasks_condor/include/astro.h	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/include/astro.h	(revision 34783)
@@ -0,0 +1,36 @@
+# include "external.h"
+# include "shell.h"
+# include "dvomath.h"
+# include "convert.h"
+# include "display.h"
+# include "data.h"
+
+# ifndef ASTRO_H
+# define ASTRO_H
+
+void InitAstro (void);
+void FreeAstro (void);
+
+typedef struct {
+  int    isIdentity;	      // identity transformation 
+  double phi;		      // saved in radians
+  double Xo;		      // saved in radians
+  double xo;		      // saved in degrees
+  double sin_phi_cos_Xo;      // pre-computed values
+  double sin_phi_sin_Xo;      // pre-computed values
+  double cos_phi_cos_Xo;      // pre-computed values
+  double cos_phi_sin_Xo;      // pre-computed values
+  double cos_phi;	      // pre-computed values
+  double sin_phi;	      // pre-computed values
+  double cos_Xo;	      // pre-computed values
+  double sin_Xo;	      // pre-computed values
+} CoordTransform;
+
+#ifndef MOVED_TO_LIBDVO
+typedef enum {COORD_NONE, COORD_CELESTIAL, COORD_GALACTIC, COORD_ECLIPTIC} CoordTransformSystem;
+#endif
+
+CoordTransform *InitTransform (CoordTransformSystem input, CoordTransformSystem output);
+int ApplyTransform (double *x, double *y, double X, double Y, CoordTransform *transform);
+
+# endif
Index: /branches/sc_branches/pantasks_condor/include/basic.h
===================================================================
--- /branches/sc_branches/pantasks_condor/include/basic.h	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/include/basic.h	(revision 34783)
@@ -0,0 +1,8 @@
+# include "external.h"
+# include "shell.h"
+# include "dvomath.h"
+
+void cleanup (void);
+void InitBasic (void);
+void FreeBasic (void);
+void InitBasic_PantasksClient (void);
Index: /branches/sc_branches/pantasks_condor/include/data.h
===================================================================
--- /branches/sc_branches/pantasks_condor/include/data.h	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/include/data.h	(revision 34783)
@@ -0,0 +1,209 @@
+# include "external.h"
+# include "shell.h"
+# include "dvomath.h"
+# include "convert.h"
+# include "display.h"
+# include "get_graphdata.h"
+
+# ifndef DATA_H
+# define DATA_H
+
+/*** typedef structs used by math functions ***/
+typedef struct {
+  int   NLINES;
+  int   Nlines;
+  char **lines;
+  char  *name;
+} Queue;
+
+typedef struct {
+  char *name;
+  int NWORDS;
+  int Nwords;
+  char **words;
+  char **value;
+} Page;
+
+typedef struct {
+  char *name;
+  int NPAGES;
+  int Npages;
+  Page **pages;
+  // int *index; (why did I define this?  is it not used?)
+  char **pageIDs;
+} Book;
+
+void InitData (void);
+void FreeData (void);
+
+/* in book.c */
+void InitBooks (void);
+void InitBook (Book *book, char *name);
+void FreeBook (Book *book);
+Book *FindBook (char *name);
+Book *GetBook (int where);
+Book *CreateBook (char *name);
+int DeleteBook (Book *book);
+void ListBooks (void);
+
+/* in page.c */
+void InitPage (Page *page, char *name);
+void FreePage (Page *page);
+Page *FindPage (Book *book, char *name);
+Page *GetPage (Book *book, int where);
+Page *GetPageRestricted (Book *book, int where, char *keyName, char *keyValue);
+Page *CreatePage (Book *book, char *name);
+int ShufflePages (Book *book);
+int DeletePage (Book *book, Page *page);
+void ListPages (Book *book);
+void ListWords (Page *page);
+int BookSetWord (Page *page, char *word, char *value);
+char *BookGetWord (Page *page, char *word);
+
+/* in queues.c */
+void InitQueues (void);
+void ListQueues (void);
+Queue *FindQueue (char *name);
+Queue *CreateQueue (char *name);
+void PushQueue (Queue *queue, char *line);
+void PushNamedQueue (char *name, char *line);
+char *PopQueue (Queue *queue);
+char *PopQueueMatch (Queue *queue, char *Key, char *value);
+void PushQueueUnique (Queue *queue, char *line, char *Key);
+void PushQueueReplace (Queue *queue, char *line, char *Key);
+int InitQueue (Queue *queue);
+int DeleteQueue (Queue *queue);
+int PrintQueue (Queue *queue);
+
+/* in fft.c */
+void fft1D (float *dataRe, float *dataIm, int N, int Nbit, int forward);
+int fftND (float *dataRe, float *dataIm, int Ndim, off_t *Nsize, int forward);
+void dfft1D (double *dataRe, double *dataIm, int N, int Nbit, int forward);
+int dfftND (double *dataRe, double *dataIm, int Ndim, int *Nsize, int forward);
+int IsBinary (int N, int *Nbit);
+
+/* in spline.c */
+void spline_construct_flt (float *x, float *y, int N, float *y2);
+float spline_apply_flt (float *x, float *y, float *y2, int N, float X);
+void spline_construct_dbl (opihi_flt *x, opihi_flt *y, int N, opihi_flt *y2);
+opihi_flt spline_apply_dbl (opihi_flt *x, opihi_flt *y, opihi_flt *y2, int N, opihi_flt X);
+
+/* in svdcmp.c */
+int svdcmp (float *a, opihi_flt *w, float *v, int Nx, int Ny);
+
+/* in svdcmp_bond_raw.c */
+int svdcmp_bond_raw(int m, int n, int withu, int withv, double eps, double tol, double **a, double *q, double **u, double **v);
+
+/* in svdcmp_bond_new.c */
+int svdcmp_bond_new(int m, int n, int withu, int withv, double eps, double tol, double **a, double *q, double **u, double **v);
+
+/* mrqmin.c */
+opihi_flt mrqcof (opihi_flt *x, opihi_flt *y, opihi_flt *dy, int Npts, 
+	      opihi_flt *par, int Npar, opihi_flt **ta, opihi_flt **tb, 
+	      opihi_flt (funcs)(opihi_flt, opihi_flt *, int, opihi_flt *));
+
+opihi_flt mrqmin (opihi_flt *x, opihi_flt *y, opihi_flt *dy, int Npts, 
+	      opihi_flt *par, int Npar, 
+	      opihi_flt (funcs)(opihi_flt, opihi_flt *, int, opihi_flt *), int VERBOSE);
+
+opihi_flt mrqinit (opihi_flt *x, opihi_flt *y, opihi_flt *dy, int Npts, 
+	       opihi_flt *par, int Npar, 
+	       opihi_flt (funcs)(opihi_flt, opihi_flt *, int, opihi_flt *), int VERBOSE);
+
+opihi_flt **mrqcovar (int Npar);
+
+void mrqfree (int Npar);
+
+/* mrq2dmin.c */
+opihi_flt mrq2dcof (opihi_flt *x, opihi_flt *t, opihi_flt *y, opihi_flt *dy, int Npts, 
+		opihi_flt *par, int Npar, opihi_flt **ta, opihi_flt **tb, 
+		opihi_flt (funcs)(opihi_flt, opihi_flt, opihi_flt *, int, opihi_flt *));
+
+opihi_flt mrq2dmin (opihi_flt *x, opihi_flt *t, opihi_flt *y, opihi_flt *dy, int Npts, 
+		opihi_flt *par, int Npar, 
+		opihi_flt (funcs)(opihi_flt, opihi_flt, opihi_flt *, int, opihi_flt *), int VERBOSE);
+
+opihi_flt mrq2dinit (opihi_flt *x, opihi_flt *t, opihi_flt *y, opihi_flt *dy, int Npts, 
+		 opihi_flt *par, int Npar, 
+		 opihi_flt (funcs)(opihi_flt, opihi_flt, opihi_flt *, int, opihi_flt *), int VERBOSE);
+
+opihi_flt mrq2dchi (opihi_flt *x, opihi_flt *t, opihi_flt *y, opihi_flt *dy, int Npts, 
+		opihi_flt *par, int Npar, 
+		opihi_flt (funcs)(opihi_flt, opihi_flt, opihi_flt *, int, opihi_flt *));
+
+int mrq2dlimits (opihi_flt *pmin, opihi_flt *pmax, int Npar);
+
+opihi_flt **mrq2dcovar (int Npar);
+
+void mrq2dfree (int Npar);
+
+/* gaussian.c */
+double gaussian (double x, double mean, double sigma);
+void gauss_init (int Nbin);
+double rnd_gauss (double mean, double sigma);
+
+/* starfuncs.c */
+double get_aperture_stats (Matrix *matrix, int X, int Y, int Npix, int Nborder, double max);
+int set_rough_radii (double Ra, double Ri, double Ro);
+int get_rough_star (float *data, int Nx, int Ny, int x, int y, opihi_flt *xc, opihi_flt *yc, opihi_flt *sx, opihi_flt *sy, opihi_flt *sxy, opihi_flt *zs, opihi_flt *zp, opihi_flt *sk);
+
+/* precess.c */
+double BtoJ (double in_epoch);
+double get_epoch (char *in_epoch, char mode);
+
+int SetGridScales (double *major, double *minor, double range);
+
+/* graphtools.c */
+void          SetLimits             PROTO((Vector *xvec, Vector *yvec, Graphdata *graphmode));
+void          SetLimitsRaw          PROTO((float *xvec, float *yvec, int Npts, Graphdata *graphmode));
+void          ApplyLimits           PROTO((int Xgraph, Graphdata *graphmode, int apply));
+int           style_args            PROTO((Graphdata *graphmode, int *argc, char **argv, int *kapa));
+
+int read_table_vectors (int argc, char **argv, char *extname);
+
+void *db_getConnection (void);
+
+int bracket (double *list, int Nlist, int mode, double value);
+int ibracket (int *list, int Nlist, int mode, double value);
+
+void FreeKapa (void);
+void FreeQueues (void);
+void FreeBooks (void);
+
+// the interpolating spline has valu
+typedef struct {
+  int Nknots;
+  opihi_flt *xk;
+  opihi_flt *yk;
+  opihi_flt *y2;
+  char *name;
+} Spline;
+
+/* in SplineOps.c */
+void InitSplines ();
+void FreeSplines ();
+void InitSpline (Spline *spline, char *name, int Nknots);
+void FreeSpline (Spline *spline);
+Spline *GetSpline (int where);
+Spline *FindSpline (char *name);
+Spline *CreateSpline (char *name, int Nknots);
+int DeleteSpline (Spline *spline);
+void ListSplines ();
+int SaveSpline (char *filename, char *name, int append);
+int LoadSpline (char *filename, char *name);
+
+/* hermitian functions */
+double hermitian_polynomial (double x, int order);
+double hermitian_00(double x);
+double hermitian_01(double x);
+double hermitian_02(double x);
+double hermitian_03(double x);
+double hermitian_04(double x);
+double hermitian_05(double x);
+double hermitian_06(double x);
+double hermitian_07(double x);
+double hermitian_08(double x);
+double hermitian_09(double x);
+double hermitian_10(double x);
+
+# endif
Index: /branches/sc_branches/pantasks_condor/include/dimm.h
===================================================================
--- /branches/sc_branches/pantasks_condor/include/dimm.h	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/include/dimm.h	(revision 34783)
@@ -0,0 +1,71 @@
+# include "external.h"
+# include "shell.h"
+# include "dvomath.h"
+# include "convert.h"
+# include "display.h"
+# include "sbig.h"
+
+void InitDIMM (void);
+
+/* telescope.c */
+double distSky (double r1, double r2, double d1, double d2);
+int getRD (double *r, double *d) ;
+int gotoRD (double r, double d);
+int offset (char *direction, double distance);
+int toffset (char *direction, char *rate, double duration);
+int getXY (double *x, double *y);
+int setRD (double r, double d);
+int setSite (char *sitename, double lon, double lat);
+int setTime (char *lst);
+int getSite (double *lon, double *lat, double *lst);
+int ParkScope(void);
+int SleepScope(void);
+int WakeScope(void);
+
+/* Serial.c */
+int SerialVerbose (int mode);
+int SerialInit (char *port);
+int SerialOpen (char *port);
+int SerialBaudRate (int fdesc, int rate);
+int SerialParity (int fdesc, int parity); 
+int SerialDataBits(int fdesc, int bits); 
+int SerialStopBit(int fdesc, int stpbit); 
+void SerialStop (int fdesc);
+int SerialCommand (char *in, char **out, int wait);
+
+/* camera.c */
+int InitCamera (int port);
+void DumpCameraInfo (void);
+void CameraFullSize (int *x, int *y);
+int SetTemperature (double temp);
+double GetTemperature (void);
+int DumpCameraStatus (void);
+int Exposure (double exptime);
+static int readout_callback (float percent);
+int ReadOut (int x, int y, int dx, int dy, int binning, unsigned short *buffer);
+int OpenShutter (void);
+int CloseShutter (void);
+
+  /* this image structure stuff is considered for further development */ 
+# if (0)
+/** this Image is incompatible with the DVO Image struct */
+typedef struct {
+
+  /* image data area */
+  int Nx, Ny;
+  char *buffer;
+  int Nbytes;
+
+  /* image metadata */
+  double ccdtemp;
+  double airtemp;
+  double ra, dec, airmass;
+  double exptime;
+  int binning;
+} Image;
+
+/* analysis.c */
+int subtractImage (Image *a, Image *b);
+void statsImage (Image *image, Stats *stats);
+void findStars (Image *image, Stars **stars, int *Nstars, double threshold);
+# endif
Index: /branches/sc_branches/pantasks_condor/include/display.h
===================================================================
--- /branches/sc_branches/pantasks_condor/include/display.h	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/include/display.h	(revision 34783)
@@ -0,0 +1,43 @@
+# include "external.h"
+# include "kapa.h"
+# include "dvomath.h"
+
+# ifndef DISPLAY_H
+# define DISPLAY_H
+
+/*** kapa graph functions ***/
+int           PlotVectorSingle      PROTO((int kapa, Vector *vec, char *mask, char *mode));
+int           PlotVectorPair        PROTO((int kapa, Vector *xVec, Vector *yVec, char *mask, Graphdata *graphmode));
+int           PlotVectorPairErrors  PROTO((int kapa, Vector *xVec, Vector *yVec, Vector *dyValues, Graphdata *graphmode));
+
+int           PlotVectorTriplet     PROTO((int kapa, Vector *xVec, Vector *yVec, Vector *zValues, char *mask, Graphdata *graphmode));
+// int           GetGraphData          PROTO((Graphdata *data, int *kapa, char *name));
+int           GetGraph              PROTO((Graphdata *data, int *kapa, char *name));
+int           SetGraph              PROTO((Graphdata *data));
+
+/*** kapa image functions */
+int           GetImageData          PROTO((KapaImageData *data, int *kapa, char *name));
+int           GetImage              PROTO((KapaImageData *data, int *kapa, char *name));
+int           SetImage              PROTO((KapaImageData *data));
+
+void	      QuitKapa              PROTO((void));
+void	      InitKapa		    PROTO((void));
+int 	      open_kapa		    PROTO((int entry));
+int 	      close_kapa	    PROTO((char *name));
+int 	      AddKapaDevice	    PROTO((char *name));
+int 	      DelKapaDevice	    PROTO((char *name));
+int 	      FindKapaDevice	    PROTO((char *name));
+char	     *GetKapaName	    PROTO((void));
+
+/* calling program need to define a function 'get_variable' which
+ * returns the name of the executable for each of KAPA and KII
+ */
+char         *get_variable          PROTO((char *name));
+
+int SendLabel (char *string, int Xgraph, int mode);
+
+int SendGraphMessage (int device, char *format, ...) OHANA_FORMAT(printf, 2, 3);
+int SendGraphCommand (int device, int length, char *format, ...) OHANA_FORMAT(printf, 3, 4);
+int SendGraphCommandV (int device, int length, char *format, va_list argp);
+
+# endif
Index: /branches/sc_branches/pantasks_condor/include/dvomath.h
===================================================================
--- /branches/sc_branches/pantasks_condor/include/dvomath.h	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/include/dvomath.h	(revision 34783)
@@ -0,0 +1,164 @@
+/*** dvomath.h ***/
+
+# ifndef DVOMATH_H
+# define DVOMATH_H
+
+# define OPIHI_NAME_SIZE 1024
+
+# define NCHARS 256
+// # define opihi_flt double
+// # define opihi_int int
+// NOTE: if opihi_int is changed to unsigned, all subtraction and negation operations
+// need to result in a float value (or 3 - 5 will yield the unexpected value 2^32 - 2)
+
+# define REQUIRE_VECTOR_FLT(VECT,RVAL) { \
+  if (VECT->type != OPIHI_FLT) { \
+    gprint (GP_ERR, "function requires vector of type FLT\n"); \
+    return (RVAL); \
+  } }
+
+# define REQUIRE_VECTOR_INT(VECT,RVAL) {	\
+  if (VECT->type != OPIHI_INT) { \
+    gprint (GP_ERR, "function requires vector of type INT\n"); \
+    return (RVAL); \
+  } }
+
+enum {ANYVECTOR, NEWVECTOR, OLDVECTOR};
+enum {ANYBUFFER, NEWBUFFER, OLDBUFFER};
+#ifdef NOT_MOVED_TO_LIBDVO
+enum {OPIHI_FLT, OPIHI_INT};
+#endif
+
+typedef struct {			/* representation of a variable (0-D) */
+  char     *name;
+  char     *value;
+} Variable;
+
+typedef struct {			/* representation of a vector (1-D) */
+  char name[OPIHI_NAME_SIZE];
+  char type;
+  union {
+    void      *Ptr;
+    opihi_flt *Flt;
+    opihi_int *Int;
+  } elements;
+  int Nelements;
+} Vector;
+
+typedef struct {			/* representation of buffer (image) */
+  char name[OPIHI_NAME_SIZE];
+  char file[OPIHI_NAME_SIZE];
+  Header header;
+  Matrix matrix;
+  int  bitpix, unsign;
+  double bscale, bzero;
+} Buffer;
+
+typedef struct {			/* math stack structure */
+  char   *name;
+  char    type;
+  Buffer *buffer;
+  Vector *vector;
+  opihi_flt FltValue;
+  opihi_int IntValue;
+} StackVar;
+
+/* math functions */
+char         *dvomath               PROTO((int argc, char **argv, int *size, int maxsize));
+// MOVED to libohana
+// char        **isolate_elements      PROTO((int argc, char **argv, int *nstack));
+StackVar     *convert_to_RPN        PROTO((int argc, char **argv, int *nstack));
+int           check_stack           PROTO((StackVar *stack, int Nstack, int validsize));
+int           evaluate_stack        PROTO((StackVar *stack, int *Nstack));
+void          init_stack            PROTO((StackVar *stack));
+void          copy_stack	    PROTO((StackVar *stack1, StackVar *stack2));
+void          move_stack	    PROTO((StackVar *stack1, StackVar *stack2));
+void          clean_stack	    PROTO((StackVar *stack, int Nstack));
+void          delete_stack	    PROTO((StackVar *stack, int Nstack));
+void          clear_stack 	    PROTO((StackVar *stack));
+void          assign_stack 	    PROTO((StackVar *stack, char *name, int type));
+
+int           VV_binary             PROTO((StackVar *OUT, StackVar *V1, StackVar *V2, char *op));
+int           SV_binary             PROTO((StackVar *OUT, StackVar *V1, StackVar *V2, char *op));
+int           VS_binary             PROTO((StackVar *OUT, StackVar *V1, StackVar *V2, char *op));
+int           MV_binary             PROTO((StackVar *OUT, StackVar *V1, StackVar *V2, char *op));
+int           VM_binary             PROTO((StackVar *OUT, StackVar *V1, StackVar *V2, char *op));
+int           MM_binary             PROTO((StackVar *OUT, StackVar *V1, StackVar *V2, char *op));
+int           MS_binary             PROTO((StackVar *OUT, StackVar *V1, StackVar *V2, char *op));
+int           SM_binary             PROTO((StackVar *OUT, StackVar *V1, StackVar *V2, char *op));
+int           SS_binary             PROTO((StackVar *OUT, StackVar *V1, StackVar *V2, char *op));
+int           WW_binary             PROTO((StackVar *OUT, StackVar *V1, StackVar *V2, char *op));
+int           S_unary               PROTO((StackVar *OUT, StackVar *V1, char *op));
+int           V_unary               PROTO((StackVar *OUT, StackVar *V1, char *op));
+int           M_unary               PROTO((StackVar *OUT, StackVar *V1, char *op));
+
+/* variable handling */
+char         *get_variable          PROTO((char *name));
+char         *get_variable_ptr      PROTO((char *name));
+char         *get_local_variable_ptr PROTO((char *name));
+double        get_double_variable   PROTO((char *name, int *found));
+int           get_int_variable      PROTO((char *name, int *found));
+int           DeleteNamedScalar     PROTO((char *name));
+int           IsScalar              PROTO((char *name));
+int           set_variable          PROTO((char *name, double dvalue));
+int           set_int_variable      PROTO((char *name, int ivalue));
+int           set_str_variable      PROTO((char *name, char *value));
+int           set_local_variable    PROTO((char *name, char *value));
+void          InitVariables         PROTO((void));
+void          ListVariables         PROTO((void));
+float         get_variable_default  PROTO((char *name, float dvalue));
+int           SelectScalar          PROTO((char *string, double *value));
+
+/* vector handling */
+void          InitVectors           PROTO((void));
+void          FreeVectors           PROTO((void));
+Vector       *InitVector            PROTO((void));
+void          FreeVectorArray       PROTO((Vector **vec, int Nvec));
+int           CopyVector            PROTO((Vector *out, Vector *in));
+int           ResetVector           PROTO((Vector *vec, char type, int Nelements));
+int           SetVector             PROTO((Vector *vec, char type, int Nelements));
+int           CastVector            PROTO((Vector *vec, char type));
+int           MatchVector           PROTO((Vector *out, Vector *in, char type));
+int           MoveVector            PROTO((Vector *out, Vector *in));
+int           DeleteVector          PROTO((Vector *vec));
+int           CopyNamedVector       PROTO((char *out, char *in));
+int           MoveNamedVector       PROTO((char *out, char *in));
+int           DeleteNamedVector     PROTO((char *name));
+int           IsVector              PROTO((char *name));
+int           IsVectorPtr           PROTO((Vector *vec));
+int           ListVectors           PROTO((void));
+int           ListVectorsToList     PROTO((char *name));
+Vector       *SelectVector          PROTO((char *name, int mode, int verbose));
+int           AssignVector          PROTO((Vector *vec, char *name, int mode, int verbose));
+Vector      **MergeVectors          PROTO((Vector **vec, int *Nvec, Vector **invec, int Ninvec));
+Vector      **MergeVectorsByIndex   PROTO((Vector **vec, int *Nvec, Vector **invec, int Ninvec, int Nelements));
+
+/* vector IO functions */
+int           WriteVectorTableFITS  PROTO((char *filename, char *extname, Vector **vec, int Nvec, int append, char *format));
+Vector      **ReadVectorTableFITS   PROTO((char *filename, char *extname, int *Nvec));
+
+/* buffer handling */
+Buffer       *InitBuffer            PROTO((void));
+void          InitBuffers           PROTO((void));
+int           CopyBuffer            PROTO((Buffer *out, Buffer *in));
+int           MoveBuffer            PROTO((Buffer *out, Buffer *in));
+int           DeleteBuffer          PROTO((Buffer *buf));
+int           CopyNamedBuffer       PROTO((char *out, char *in));
+int           MoveNamedBuffer       PROTO((char *out, char *in));
+int           DeleteNamedBuffer     PROTO((char *name));
+int           IsBuffer              PROTO((char *name));
+int           IsBufferPtr           PROTO((Buffer *buf));
+int           PrintBuffers          PROTO((int Long));
+int           ListBuffersToList     PROTO((char *name));
+int           CreateBuffer          PROTO((Buffer *buf, int Nx, int Ny, int bitpix, float bzero, float bscale));
+Buffer       *SelectBuffer          PROTO((char *name, int mode, int verbose));
+void          dump_buffers          PROTO((int n));  /* deprecated? */
+int           SelectOverlay         PROTO((char *name, int *number));
+
+/* why are these in here? */
+int           gfits_copy_matrix_info (Matrix *matrix1, Matrix *matrix2);
+#ifndef MOVED_TO_LIBDVO
+int           GetTimeFormat         PROTO((time_t *TimeReference, int *TimeFormat));
+#endif
+
+# endif
Index: /branches/sc_branches/pantasks_condor/include/dvoshell.h
===================================================================
--- /branches/sc_branches/pantasks_condor/include/dvoshell.h	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/include/dvoshell.h	(revision 34783)
@@ -0,0 +1,105 @@
+# include "data.h"
+# include "basic.h"
+# include "astro.h"
+# include "dvo.h"
+
+# ifndef DVOSHELL_H
+# define DVOSHELL_H
+
+typedef struct {
+  char name[64];
+  double RA0, RA1, DEC0, DEC1;
+} RegionFile;
+
+typedef struct {
+  double X;
+  double Y;
+  double R;
+  double D;
+  double M, dM;
+  char   dophot;
+  double sky;
+  double fx, fy, df;
+  double Mgal, Map;
+  int found;
+  short int code;
+  e_time t;
+} CMPstars;
+
+/** some globals used particularly by DVO_CLIENT **/
+int   HOST_ID;
+char *HOSTDIR;
+char *RESULT_FILE;
+
+/*** dvo prototypes ***/
+int           DetermineTypeCode     PROTO((Average *average, Measure *measure, int code));
+double        DetermineTypefrac     PROTO((Average *average, Measure *measure, PhotCode *code));
+double        ExtractAverages       PROTO((PhotCode *code, int mode, Average *average, SecFilt *secfilt, Measure *measure, int param));
+double       *ExtractByDMag         PROTO((PhotCode **code, int *mode, Average *average, SecFilt *secfilt, Measure *measure, off_t *nlist, int param));
+double       *ExtractDMag           PROTO((PhotCode **code, int *mode, Average *average, SecFilt *secfilt, Measure *measure, off_t *nlist));
+double       *ExtractMagnitudes     PROTO((PhotCode *code, int mode, Average *average, SecFilt *secfilt, Measure *measure, off_t *n));
+double       *ExtractMeasures       PROTO((PhotCode *code, int mode, Average *average, SecFilt *secfilt, Measure *measure, off_t *nlist, int param));
+double       *ExtractMeasuresByDMag PROTO((PhotCode **code, int *mode, int use_first, Average *average, SecFilt *secfilt, Measure *measure, off_t *nlist, int param));
+double       *ExtractMeasuresDMag   PROTO((PhotCode **code, int *mode, Average *average, SecFilt *secfilt, Measure *measure, off_t *nlist));
+void          FreeImageSelection    PROTO((void));
+void          FreeImageSelection    PROTO((void));
+int           GetAverageParam       PROTO((char *parname));
+void          GetAverageParamHelp   PROTO((void));
+int           GetMagMode            PROTO((char *string));
+double        GetMeasure            PROTO((int param, Average *average, Measure *measure, double mag));
+int           GetMeasureParam       PROTO((char *parname));
+int           GetMeasureTypeCode    PROTO((Measure *measure));
+int           GetPhotcodeInfo       PROTO((char *string, PhotCode **Code, int *Mode));
+int           GetSelectionParam     PROTO((void));
+int           GetTimeSelection      PROTO((time_t *tz, time_t *te));
+void          InitDVO               PROTO((void));
+void          FreeDVO               PROTO((void));
+int           InitPhotcodes         PROTO((void));
+Coords       *MatchMosaic           PROTO((unsigned int time, short int source));
+int           Quality               PROTO((Measure *measure, int IsDophot));
+int           SelectMags            PROTO((int Nphot, int Tphot, int Ns, Average *average, Measure *measure, SecFilt *secfilt, int UL));
+
+int           SetSkyRegions         PROTO((SkyRegionSelection *selection));
+SkyList      *SelectRegions         PROTO((SkyRegionSelection *selection));
+SkyList      *SkyListLoadFile       PROTO((char *filename));
+int           SetCATDIR             PROTO((char *path, int verbose));
+char *        GetCATDIR             PROTO((void));
+SkyTable     *GetSkyTable           PROTO((void));
+SkyList      *SkyListFromFile       PROTO((char *filename));
+SkyRegionSelection *SetRegionSelection    PROTO((int *argc, char **argv));
+
+int           SkyRegionByPoint_r    PROTO((SkyTable *table, SkyList *list, int depth, double ra, double dec));
+SkyList      *SelectRegionsByCoordVectors PROTO((Vector *RA, Vector *DEC));
+
+int           find_matches_by_vectors PROTO((SkyRegion *region, Catalog *catalog, Vector *RAvec, Vector *DECvec, float RADIUS, off_t *index));
+
+int           SetImageSelection     PROTO((int mode, SkyRegionSelection *selection));
+int           SetPhotSelections     PROTO((int *argc, char **argv, int Nparams));
+int           SetSelectionParam     PROTO((int param));
+int           TestAverage           PROTO((PhotCode *code, Average *average, SecFilt *secfilt, Measure *measure));
+int           TestPhotSelections    PROTO((PhotCode **code, int *mode, int param));
+void          compare               PROTO((Catalog *catlog1, Catalog *catlog2, Vector *rvec,  Vector *dvec,  Vector *mvec, Vector *drvec, Vector *ddvec, Vector *dmvec, double radius));
+void          cprecess              PROTO((Average *average, off_t Naverage, double in_epoch, double out_epoch));
+void          image_subset          PROTO((Image *image, off_t Nimage, off_t **Subset, off_t *Nsubset, SkyRegionSelection *selection, unsigned long int tzero, double trange, int TimeSelect));
+off_t         match_image           PROTO((Image *image, off_t Nimage, unsigned int T, short int S));
+off_t         match_image_subset    PROTO((Image *image, off_t *subset, off_t Nsubset, unsigned int T, short int S));
+void          print_value           PROTO((double value, short int ival));
+CMPstars     *cmpReadFits           PROTO((FILE *f, off_t *nstars));
+CMPstars     *cmpReadText           PROTO((FILE *f, off_t *nstars));
+int           RD_to_XYpic           PROTO((double *x, double *y, double r, double d, Coords *coords, double Rmin, double Rmax, double Rmid, int *leftside));
+int           wordhash              PROTO((char *word));
+
+int          GetMagMode             PROTO((char *string));
+PhotCode    *ParsePhotcodeField     PROTO((char *field, int *mode, int def));
+int          ParseMeasureField      PROTO((dbField *field, char *fieldName));
+int          ParseAverageField      PROTO((dbField *field, char *fieldName));
+int          ParseImageField        PROTO((dbField *field, char *fieldName));
+
+dbValue      dbExtractMeasures      PROTO((Average *average, SecFilt *secfilt, Measure *measure, dbField *field));
+dbValue      dbExtractImages        PROTO((Image *image, off_t Nimage, off_t N, dbField *field));
+
+int          HostTableLaunchJobs    PROTO((HostTable *table, char *basecmd, char *options, int VERBOSE));
+int          HostTableParallelOps   PROTO((int argc, char **argv, char *ResultFile, int Nelements, int VERBOSE));
+int          HostTableReloadResults PROTO((char *uniquer, int VERBOSE));
+
+# endif // DVOSHELL_H
Index: /branches/sc_branches/pantasks_condor/include/external.h
===================================================================
--- /branches/sc_branches/pantasks_condor/include/external.h	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/include/external.h	(revision 34783)
@@ -0,0 +1,30 @@
+# include <signal.h>
+# include <unistd.h>
+# include <sys/uio.h>
+# include <fcntl.h>
+# include <sys/types.h>
+# include <sys/wait.h>
+# include <sys/socket.h>
+# include <sys/un.h>
+# include <sys/time.h>
+# include <time.h>
+# include <errno.h>
+# include <pthread.h>
+
+# include <netinet/ip.h>
+# include <netdb.h>
+# include <arpa/inet.h>
+
+# include <ohana.h>
+# include <dvo.h>
+
+/* provide missing external defines */
+# ifdef MISSING_SOCKET_INFO
+#   define F_SETFL         4   
+#   define O_NONBLOCK       0200000  
+#   define AF_UNIX         1          
+#   define SOCK_STREAM     1          
+#   define ENOENT          2 
+# endif
+
+# define strlen(A) ((int)strlen(A))
Index: /branches/sc_branches/pantasks_condor/include/hstgsc.h
===================================================================
--- /branches/sc_branches/pantasks_condor/include/hstgsc.h	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/include/hstgsc.h	(revision 34783)
@@ -0,0 +1,27 @@
+
+static double BigDecBounds[] = {0.0, 7.5, 15.0, 22.5, 30.0, 37.5, 45.0, 
+				52.5, 60.0, 67.5, 75.0, 82.5, 90.0,
+				0.0, -7.5, -15.0, -22.5, -30.0, -37.5, -45.0, 
+				-52.5, -60.0, -67.5, -75.0, -82.5, -90.0};
+static char *Dec2Sections[] = {"n0000", "n0730", "n1500", "n2230", "n3000", "n3730", "n4500", 
+			       "n5230", "n6000", "n6730", "n7500", "n8230", "weirdness", 
+			       "s0000", "s0730", "s1500", "s2230", "s3000", "s3730", "s4500", 
+			       "s5230", "s6000", "s6730", "s7500", "s8230", "weirdness"};
+
+static int NDecLines[] = {593, 584, 551, 530, 522, 465, 406, 362, 280, 198, 123, 24, 
+			  0, 597, 578, 574, 577, 534, 499, 442, 376, 294, 212, 144, 48};
+
+/** older data layout concepts 
+static char *DecSections[] = {"N0000", "N0730", "N1500", "N2230", "N3000", "N3730", "N4500", 
+			      "N5230", "N6000", "N6730", "N7500", "N8230", "weirdness", 
+			      "S0000", "S0730", "S1500", "S2230", "S3000", "S3730", "S4500", 
+			      "S5230", "S6000", "S6730", "S7500", "S8230", "weirdness"};
+
+static char *disk[] = {"disk 1", "disk 1", "disk 1", "disk 1", "disk 1", "disk 1", "disk 1", 
+		       "disk 1", "disk 1", "disk 1", "disk 1", "disk 1", "weirdness", 
+		       "disk 1", "disk 2", "disk 2", "disk 2", "disk 2", "disk 2", "disk 2", 
+		       "disk 2", "disk 2", "disk 2", "disk 2", "disk 2", "weirdness"};
+
+static int NBigRASections [] = {48, 47, 45, 43, 40, 36, 32, 28, 21, 15, 9, 3, 3, 48, 47, 45, 43, 40, 36, 32, 28, 21, 15, 9, 3, 3};
+
+**/
Index: /branches/sc_branches/pantasks_condor/include/imfit.h
===================================================================
--- /branches/sc_branches/pantasks_condor/include/imfit.h	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/include/imfit.h	(revision 34783)
@@ -0,0 +1,20 @@
+# include "astro.h"
+
+int Npar;
+int Nfpar;
+opihi_flt *par;
+opihi_flt *fpar;
+opihi_flt *sky;
+
+opihi_flt (*fitfunc)(opihi_flt, opihi_flt, opihi_flt *, int, opihi_flt *);
+void (*imfit_cleanup)(void);
+
+void fgauss_setup (char *name);
+void pgauss_setup (char *name);
+void sgauss_setup (char *name);
+void qgauss_setup (char *name);
+void qfgauss_setup (char *name);
+void qrgauss_setup (char *name);
+void pgauss_psf_setup (char *name);
+void qgauss_psf_setup (char *name);
+void sgauss_psf_setup (char *name);
Index: /branches/sc_branches/pantasks_condor/include/mana.h
===================================================================
--- /branches/sc_branches/pantasks_condor/include/mana.h	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/include/mana.h	(revision 34783)
@@ -0,0 +1,11 @@
+# include "data.h"
+# include "basic.h"
+# include "astro.h"
+
+# ifndef MANA_H
+
+void InitMana (void);
+void FreeMana (void);
+int *findrowpeaks (float *row, int Nrow, float threshold, int *npeaks);
+
+# endif
Index: /branches/sc_branches/pantasks_condor/include/opihi.h
===================================================================
--- /branches/sc_branches/pantasks_condor/include/opihi.h	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/include/opihi.h	(revision 34783)
@@ -0,0 +1,5 @@
+# include "external.h"
+# include "shell.h"
+# include "dvomath.h"
+# include "convert.h"
+# include "display.h"
Index: /branches/sc_branches/pantasks_condor/include/pantasks.h
===================================================================
--- /branches/sc_branches/pantasks_condor/include/pantasks.h	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/include/pantasks.h	(revision 34783)
@@ -0,0 +1,308 @@
+# include "data.h"
+# include "basic.h"
+# include "astro.h"
+
+# include <sys/time.h>
+# include <time.h>
+# include <zlib.h>
+
+# define DEBUG 0
+
+typedef enum {
+  JOB_NONE,
+  JOB_BUSY,
+  JOB_EXIT,
+  JOB_HUNG,
+  JOB_CRASH,
+  JOB_PENDING,
+} JobStat;
+
+typedef enum {
+  JOB_LOCAL,
+  JOB_CONTROLLER,
+} JobMode;
+
+typedef enum {
+  CONTROLLER_HUNG = -1,
+  CONTROLLER_DOWN = 0,
+  CONTROLLER_GOOD = 1,
+} ControllerStat;
+
+enum {RANGE_ABS, RANGE_DAY, RANGE_WEEK};
+enum {TIMER_ALLJOBS, TIMER_SUCCESS, TIMER_FAILURE};
+
+enum {TASK_NONE,
+      TASK_EMPTY,
+      TASK_COMMENT,
+      TASK_NMAX,
+      TASK_ACTIVE,
+      TASK_TRANGE,
+      TASK_END,
+      TASK_HOST,
+      TASK_NICE,
+      TASK_STDOUT,
+      TASK_STDERR,
+      TASK_COMMAND,
+      TASK_OPTIONS,
+      TASK_PERIODS,
+      TASK_NPENDING,
+      TASK_EXIT,
+      TASK_EXEC
+} TaskHashResults;
+
+typedef struct {
+  time_t start;
+  time_t stop;
+  char type;
+  char include;
+  int Nmax;
+  int Nrun;
+} TimeRange;
+
+/* data to define a host machine */
+typedef struct {
+  char       *hostname;
+  int         max_threads;
+} Host;
+
+/* a task is a description of the wrapping of a job */
+typedef struct {
+  Macro  *exec;                         /* name is 'exec' */
+  Macro  *crash;                        /* name is 'crash' */
+  Macro  *timeout;
+  Macro  *defexit;
+
+  int     NEXIT;
+  int     Nexit;
+  Macro **exit;                         /* name is exit status */
+
+  int     argc;
+  char  **argv;
+
+  int     optc;
+  char  **optv;
+
+  char   *host;
+  int     host_required;
+
+  char   *name;
+
+  char   *stdout_dump;
+  char   *stderr_dump;
+
+  int       Nranges;
+  TimeRange *ranges;
+
+  int     Nmax;  // only construct Ntotal jobs for this task
+  int     Njobs;
+
+  int     Npending;  // number of currently pending jobs
+  int     NpendingMax;  // max number of pending jobs allowed
+
+  float   poll_period;
+  float   exec_period;
+  float   timeout_period;
+
+  struct timeval last;
+
+  int Nsuccess;
+  int Nfailure;
+  int Ntimeout;
+  int Nskipexec;
+
+  double dtimeAve_alljobs, dtimeMin_alljobs, dtimeMax_alljobs;
+  double dtimeAve_success, dtimeMin_success, dtimeMax_success;
+  double dtimeAve_failure, dtimeMin_failure, dtimeMax_failure;
+
+  int active;
+  int priority;
+
+} Task;
+
+// time period include/exclude periods:
+// date ranges (e_time - e_time)
+// time ranges (e_time - e_time) (use e_time % 86400)
+// time-of-week ranges  (e_time - e_time
+// -trange Mon Fri (inclusive on days -- end defaults to Fri@23:59:59)
+// -trange 08:00 - 17:00
+// -trange 2005/12/24 2005/12/31
+// be careful of HST!
+// type: day, week, date
+// keep: TRUE: perform action within this time period
+// keep: FALSE: do not perform action within this time period
+
+typedef struct {
+  int JobID;                            /* internal ID for job */
+  int pid;                              /* external ID for job */
+
+  struct timeval last;
+  struct timeval start;
+  int state;
+  int exit_status;
+
+  int     argc;
+  char  **argv;
+
+  int     optc;
+  char  **optv;
+
+  Task   *task;
+
+  /* this cries out for another structure... */
+  IOBuffer    stdout_buff;    /* stdout storage buffer */
+  char       *stdout_dump;    // output target file for stdout
+  int         stdout_size;    /* size of pending stdout buffer (controller) */
+  int         stdout_fd;      /* stdout pipe (local only) */
+
+  IOBuffer    stderr_buff;    /* stderr storage buffer */
+  char       *stderr_dump;    // output target file for stderr
+  int         stderr_size;    /* size of pending stderr buffer (controller) */
+  int         stderr_fd;      /* stderr pipe (local only) */
+
+  JobMode     mode;                     /* local or controller? */
+  int     priority;
+  char   *realhost;
+
+  double dtime;
+} Job;
+
+# define CONTROLLER_PROMPT "pcontrol:"
+
+/* scheduler prototypes */
+
+void InitPantasks (void);
+void FreePantasks (void);
+
+void InitPantasksServer (void);
+void FreePantasksServer (void);
+
+void InitPantasksClient (void);
+void FreePantasksClient (void);
+
+void InitTasks (void);
+void FreeTasks (void);
+
+Task *NextTask (void);
+Task *FindTask (char *name);
+void ListTasks (int verbose);
+int ShowTask (char *name);
+int FreeTask (Task *task);
+Task *CreateTask (char *name);
+int ValidateTask (Task *task, int RequireStatic);
+int RegisterNewTask (void);
+int DeleteNewTask (void);
+Task *GetNewTask (void);
+Task *GetActiveTask (void);
+void SetTaskTimer (struct timeval *timer);
+double GetTaskTimer (struct timeval start, int verbose);
+void InitTaskTimers (void);
+int TaskHash (char *input);
+int RemoveTask (Task *task);
+Task *SetNewTask (Task *task);
+void ListTaskStats (char *regex);
+void ResetTaskStats (char *regex);
+void UpdateTaskTimerStats (Task *task, int mode, double dtime);
+
+int NextJobID (void);
+void InitJobIDs (void);
+void FreeJobIDs (void);
+int FreeJobID (int ID);
+
+void InitJobs (void);
+void FreeJobs (void);
+
+Job *NextJob (void);
+Job *FindJob (int JobID);
+void ListJobs (void);
+Job *CreateJob (Task *task);
+int SubmitJob (Job *job);
+int CheckJob (Job *job);
+int DeleteJob (Job *job);
+void FreeJob (Job *job);
+char *JobStateToString (JobStat state);
+
+float CheckJobs (void);
+float CheckTasks (void);
+int CheckSystem (void);
+int CheckController (void);
+int CheckTimeRanges (TimeRange *ranges, int Nranges);
+int BumpTimeRanges (TimeRange *ranges, int Nranges);
+
+int GetJobOutput (char *channel, int pid, IOBuffer *buffer, int Nbytes);
+int CheckControllerJob (Job *job);
+int CheckControllerJobStatus (Job *job);
+int SubmitControllerJob (Job *job);
+int DeleteControllerJob (Job *job);
+Job *FindControllerJob (int JobID);
+int StartController (void);
+int ControllerCommand (char *command, char *response, IOBuffer *buffer);
+int SubmitLocalJob (Job *job);
+int CheckLocalJob (Job *job);
+int CheckLocalJobStatus (Job *job);
+void InitTaskTimers (void);
+CommandF *FindControllerCommand (char *cmd);
+int QuitController (void);
+int StopController (void);
+int RestartController (void);
+int VerboseMode (void);
+int KillLocalJob (Job *job);
+int CheckControllerOutput (void);
+int PrintControllerOutput (void);
+void PrintControllerBusyJobs ();
+
+int AddHost (char *hostname, int max_threads);
+int DeleteHost (char *hostname);
+
+int FlushControllerOutput (void);
+int KillControllerJob (Job *job);
+int CheckControllerStatus (void);
+void gotsignal (int signum);
+int client_shell (int argc, char **argv);
+
+void InitClients (void);
+void AddNewClient (int client);
+int  DeleteClient (int client);
+void *ListenClients (void *data);
+
+// functions related to the server threads
+void *CheckJobsAndTasksThread (void *data);
+
+void CheckTasksSetState (int state);
+int CheckTasksGetState (void);
+
+void CheckJobsSetState (int state);
+int CheckJobsGetState (void);
+
+void CheckControllerSetState (int state);
+int CheckControllerGetState (void);
+void *CheckControllerThread (void *data);
+
+void CheckInputsSetState (int state);
+int CheckInputsGetState (void);
+void *CheckInputsThread (void *data);
+
+// functions related to the queue of input files
+void InitInputs (void);
+void FreeInputs (void);
+void AddNewInput (char *input);
+int DeleteInput (char *input);
+void CheckInputs (void);
+
+void ClientLock (void);
+void ClientUnlock (void);
+void CommandLock (void);
+void CommandUnlock (void);
+void ControlLock (const char *func);
+void ControlUnlock (const char *func);
+void JobTaskLock (void);
+void JobTaskUnlock (void);
+
+int InitPassword (void);
+int CheckPassword (int BindSocket);
+
+int FlushJobs (void);
+
+#ifdef IPP_CONDOR
+char* get_hosts_requirement(void);
+char *get_host_status(char* hostname);
+#endif
Index: /branches/sc_branches/pantasks_condor/include/pclient.h
===================================================================
--- /branches/sc_branches/pantasks_condor/include/pclient.h	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/include/pclient.h	(revision 34783)
@@ -0,0 +1,57 @@
+# include "data.h"
+# include "basic.h"
+
+/** pclient global data **/
+
+/** child status values **/
+enum {
+  PCLIENT_NONE,
+  PCLIENT_BUSY,  
+  PCLIENT_EXIT,
+  PCLIENT_CRASH,
+};
+
+int ChildPID;				/** current child PID **/
+int ChildStatus;			/** current status of child **/
+int ChildExitStatus;			/** recent exit status of child **/
+
+/** child may be in the following states:
+    
+PCLIENT_NONE
+PCLIENT_BUSY
+PCLIENT_EXIT
+PCLIENT_CRASH
+
+allowed transitions:
+
+PCLIENT_NONE  ->    PCLIENT_BUSY  (start a job)
+
+PCLIENT_BUSY  ->    PCLIENT_EXIT  (job complete)
+PCLIENT_BUSY  ->    PCLIENT_CRASH (job crashed)
+
+PCLIENT_EXIT  ->    PCLIENT_NONE  (cleanup)
+PCLIENT_CRASH ->    PCLIENT_NONE  (cleanup)
+
+**/
+
+/** file descriptors for child I/O **/
+int child_stdin_fd[2];
+int child_stdout_fd[2];
+int child_stderr_fd[2];
+
+/** buffers for I/O storage **/
+IOBuffer child_stdout;
+IOBuffer child_stderr;
+
+int InitChild (void);
+int FreeChild (void);
+int CheckChild (void);
+void CheckChildStatus (void);
+
+void InitPclient (void);
+void FreePclient (void);
+void gotsignal (int signum);
+void pipe_signal (int signum);
+void pipe_signal_clear(void);
+
+# define DTIME(A,B) ((A.tv_sec - B.tv_sec) + 1e-6*(A.tv_usec - B.tv_usec))
Index: /branches/sc_branches/pantasks_condor/include/pcontrol.h
===================================================================
--- /branches/sc_branches/pantasks_condor/include/pcontrol.h	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/include/pcontrol.h	(revision 34783)
@@ -0,0 +1,347 @@
+# include "data.h"
+# include "basic.h"
+# define THREADED
+
+typedef struct timeval Ptime;
+typedef unsigned long long IDtype;
+
+/** job status values **/
+typedef enum {
+  PCONTROL_JOB_ALLJOBS,
+  PCONTROL_JOB_PENDING,
+  PCONTROL_JOB_BUSY,  
+  PCONTROL_JOB_RESP,  
+  PCONTROL_JOB_HUNG,  
+  PCONTROL_JOB_DONE,  
+  PCONTROL_JOB_KILL,  
+  PCONTROL_JOB_EXIT,
+  PCONTROL_JOB_CRASH,
+} JobStat;
+
+/** job status values **/
+typedef enum {
+  PCONTROL_JOB_ANYHOST,
+  PCONTROL_JOB_WANTHOST,
+  PCONTROL_JOB_NEEDHOST,
+} JobMode;
+
+/** job mode check stages **/
+typedef enum {
+  PCONTROL_JOB_STAGE_ANYHOST,
+  PCONTROL_JOB_STAGE_WANTHOST,
+  PCONTROL_JOB_STAGE_NEEDHOST,
+  PCONTROL_JOB_STAGE_OLDWANT,
+} JobCheckStage;
+
+/** job thread options values **/
+typedef enum {
+  PCONTROL_JOB_THREADS_NONE,
+  PCONTROL_JOB_THREADS_MAX,
+} JobThreadMode;
+
+/** host status values **/
+typedef enum {
+  PCONTROL_HOST_ALLHOSTS,
+  PCONTROL_HOST_IDLE,
+  PCONTROL_HOST_BUSY,  
+  PCONTROL_HOST_RESP,
+  PCONTROL_HOST_DOWN,
+  PCONTROL_HOST_DONE,
+  PCONTROL_HOST_OFF,
+} HostStat;
+
+/** host response options **/
+typedef enum {
+  PCONTROL_RESP_NONE,
+  PCONTROL_RESP_START_JOB,
+  PCONTROL_RESP_CHECK_BUSY_JOB,  
+  PCONTROL_RESP_CHECK_DONE_HOST,  
+  PCONTROL_RESP_CHECK_HOST,
+  PCONTROL_RESP_KILL_JOB,
+  PCONTROL_RESP_STOP_HOST,
+  PCONTROL_RESP_DOWN_HOST,
+} HostResp;
+
+typedef enum {
+  PCONTROL_RUN_UNKNOWN,
+  PCONTROL_RUN_NONE,
+  PCONTROL_RUN_HOSTS,
+  PCONTROL_RUN_REAP,
+  PCONTROL_RUN_ALL,
+} RunLevels;
+
+/* stack special positions */
+typedef enum {
+  STACK_TOP = 0,
+  STACK_BOTTOM = -1,
+} StackWhere;
+
+typedef enum {
+  PCLIENT_HUNG = -1,
+  PCLIENT_DOWN = 0,
+  PCLIENT_GOOD = 1,
+} PclientStat;
+
+typedef struct {
+  char *buffer;
+  int   Nalloc;
+  int   Nmaxread;
+  int   Nextra;
+  int   Nlast;
+  int   Nbuffer;
+} Fifo;
+
+typedef struct {
+  IOBuffer     buffer;
+  int          completed;
+  int          size;
+  int          requested;
+} JobOutput;
+
+/* A machine has a unique name and may have multiple Hosts (each of which can run a single job)
+   We use this to track aspects of the analysis per machine, to eg, limit the number of jobs 
+   desired on a single machine */
+typedef struct {
+  char *name;
+  int Nhosts;		      // how many hosts are selected for this machine (whatever state)
+  int NjobsRealhost;
+  int NjobsWanthost;
+} Machine;
+
+/* data to define a job */
+typedef struct {
+  int          argc; 
+  char       **argv;
+  char        *hostname;
+  char        *realhost;
+  char       **xhosts;
+  int          Nxhosts;
+  int          exit_status;
+  int          Reset;
+  int          priority;
+  JobMode      mode;
+  JobStat      state;
+  JobStat      stack;
+  JobOutput    stdout_buf;
+  JobOutput    stderr_buf;
+  Ptime        start;
+  Ptime        stop;
+  double       dtime;
+  int          pid;
+  IDtype       JobID;
+  struct Host *host;
+} Job;
+
+/* data to define a host machine */
+typedef struct {
+  char       *hostname;
+  int         stdin_fd;
+  int         stdout_fd;
+  int         stderr_fd;
+  int         max_threads;
+  int         markoff;
+  int         pid;
+  HostStat    stack;
+  IDtype      HostID;
+  IOBuffer    comms_buffer;
+  char       *response;
+  HostResp    response_state;
+  Ptime       last_start_try; // last (UNIX) time we attempted to connect to this host (0 on success)
+  Ptime       next_start_try; // next (UNIX) time we should attempt to connect to this host (0 on success)
+  Ptime       connect_time; // (UNIX) time we connected to this host
+  struct Job *job;
+} Host;
+
+# if (USE_LLIST)
+typedef struct StackItem {
+    StackItem *next;
+    StackItem *prev;
+    void *object;
+    char *name;
+    int   id;
+} StackItem;
+
+/* the Jobs and Hosts are managed in a set of Stacks which define their state */
+typedef struct {
+    StackItem *head;
+    StackItem *tail; // use this?
+    int    Nobject;
+# ifdef THREADED    
+  pthread_mutex_t mutex;
+# endif
+} Stack;
+
+# else
+
+/* the Jobs and Hosts are managed in a set of Stacks which define their state */
+typedef struct {
+  void **object;
+  char **name;
+  int   *id;
+  int    Nobject;
+  int    NOBJECT;
+# ifdef THREADED    
+  pthread_mutex_t mutex;
+# endif
+} Stack;
+# endif
+
+/* XXX if this is hard-wired, we can't change shell name in StartHost */
+# define PCLIENT_PROMPT "pclient:"
+
+// # define FREE(X) if (X != NULL) { free (X); }
+# define CLOSE(FD) { if (FD) close (FD); FD = 0; }
+# define DTIME(A,B) ((A.tv_sec - B.tv_sec) + 1e-6*(A.tv_usec - B.tv_usec))
+# define ZTIME(A) ((A.tv_sec == 0) && (A.tv_usec == 0))
+
+# define ASSERT(TEST,STRING) { if (!(TEST)) { gprint (GP_ERR, "programming error: %s\n", STRING); abort (); }}
+# define ABORT(STRING) { gprint (GP_ERR, "programming error: %s\n", STRING); abort (); }
+// # define ASSERT(TEST,STRING) { if (!(TEST)) { gprint (GP_ERR, "programming error: %s\n", STRING); raise (SIGINT); exit (2); }}
+// # define ABORT(STRING) { gprint (GP_ERR, "programming error: %s\n", STRING); raise (SIGINT); exit (2); }
+
+void InitPcontrol (void);
+void FreePcontrol (void);
+
+/*** StackOps.c ***/
+Stack *InitStack (void);
+void   FreeStack (Stack *stack);
+int    PushStack (Stack *stack, int where, void *object, int id, char *name);
+void  *PullStackByLocation (Stack *stack, int where);
+void  *PullStackByName (Stack *stack, char *name);
+void  *PullStackByID (Stack *stack, int id);
+int    RemoveStackEntry (Stack *stack, int where);
+void  *RemoveStackByID (Stack *stack, int id);
+void   LockStack (Stack *stack);
+void   UnlockStack (Stack *stack);
+
+// void  *FindStackByID (Stack *stack, int id);
+// void  *FindStackByName (Stack *stack, char *name);
+
+/*** CheckSystem.c ***/
+int   CheckSystem (void);
+void *CheckSystem_Threaded (void *data);
+int   CheckBusyJobs (float delay);
+int   CheckDoneJobs (float delay);
+int   CheckKillJobs (float delay);
+int   CheckDoneHosts (float delay);
+int   CheckDownHosts (float delay);
+int   CheckIdleHosts (float delay, int Stage);
+int   CheckLiveHosts (float delay);
+int   SetRunSystem (int state);
+RunLevels SetRunLevel (RunLevels level);
+
+/*** own files ***/
+int StartJob (Job *job, Host *host);
+int StartJobResponse (Host *host);
+
+int CheckHost (Host *host);
+int CheckHostResponse (Host *host);
+
+int CheckDoneHost (Host *host);
+int CheckDoneHostResponse (Host *host);
+
+int CheckBusyJob (Job *job, Host *host);
+int CheckBusyJobResponse (Host *host);
+
+int KillJob (Job *job, Host *host);
+int KillJobResponse (Host *host);
+
+int StartHost (Host *host);
+int CheckResetHost (Host *host);
+int CheckIdleHost (Host *host, int Stage);
+int CheckDoneJob (Job *job, Host *host);
+int GetJobOutput (char *command, Host *host, JobOutput *output);
+
+int PclientCommand (Host *host, char *command, char *response, HostResp response_state);
+int PclientResponse (Host *host, char *response, IOBuffer *buffer);
+
+int CheckRespHosts (float MaxDelay);
+int CheckRespHost (Host *host);
+
+/*** misc files ***/
+int    VerboseMode (void);  // in verbose.c
+void   gotsignal (int signum); // in pcontrol.c
+
+/*** IDops.c ***/
+void InitIDs (void);
+IDtype NextJobID (void);
+IDtype NextHostID (void);
+void PrintID (gpDest dest, IDtype ID);
+IDtype GetID (char *IDword);
+
+/*** CheckPoint.c ***/
+int SetCheckPoint (void);
+int ClearCheckPoint (void);
+int TestCheckPoint (void);
+
+/*** HostOps.c ***/
+void   InitHostStacks (void);
+void   FreeHostStacks (void);
+Stack *GetHostStack (int StackID);
+char  *GetHostStackName (int StackID);
+Stack *GetHostStackByName (char *name);
+int    PutHost (Host *host, int StackID, int where);
+Host  *PullHostByID (IDtype HostID, int *StackID);
+Host  *PullHostByName (char *name, int *StackID);
+Host  *PullHostFromStackByID (int StackID, IDtype ID);
+Host  *PullHostFromStackByName (int StackID, char *name);
+IDtype AddHost (char *hostname, int max_threads);
+void   DelHost (Host *host);
+
+/*** StopHosts.c ***/
+void   DownHost (Host *host);
+int    DownHosts (void);
+int    DownHostResponse (Host *host);
+void   OffHost (Host *host);
+int    StopHost (Host *host, int mode);
+int    StopHostResponse (Host *host);
+int    HarvestHost (int pid);
+int    AddZombie(int pid);
+int    DelZombies();
+int    CheckZombies();
+
+/*** JobOps.c ***/
+int InitJobOutput (JobOutput *output);
+int ResetJobOutput (JobOutput *output);
+void   InitJobStacks (void);
+void   FreeJobStacks (void);
+Stack *GetJobStack (int StackID);
+char  *GetJobStackName (int StackID);
+Stack *GetJobStackByName (char *name);
+int    PutJob (Job *job, int StackID, int where);
+int    PutJobSetState (Job *job, int StackID, int where, int state);
+Job   *PullJobByID (IDtype JobID, int *StackID);
+Job   *PullJobFromStackByID (int StackID, int ID);
+IDtype AddJob (char *hostname, JobMode mode, int timeout, int priority, int argc, char **argv, int Nxhosts, char **xhosts);
+void   DelJob (Job *job);
+Host  *UnlinkJobAndHost (Job *job);
+void   LinkJobAndHost (Job *job, Host *host);
+
+/*** MachineOps.c ***/
+void InitMachines ();
+void FreeMachines ();
+Machine *FindMachineByName (char *name);
+Machine *AddMachine (char *name);
+int DelMachine (char *name);
+int AddMachineHost (Host *host);
+int DelMachineHost (Host *host);
+int AddMachineJob (Host *host, Job *job);
+int DelMachineJob (Host *host, Job *job);
+int PrintMachines ();
+int CheckMachineJobs (Host *host, Job *job);
+int GetMaxUnwantedHostJobs (void);
+void SetMaxUnwantedHostJobs (int value);
+
+float GetMaxConnectTime (void);
+void SetMaxConnectTime (float value);
+float GetMaxWantHostWait (void);
+void SetMaxWantHostWait (float value);
+
+void pcontrol_exit (int n);
+
+// Job   *FindJobByID (IDtype JobID, int *StackID);
+// Job   *FindJobInStackByID (int StackID, int ID);
+// Host  *FindHostByID (IDtype HostID, int *StackID);
+// Host  *FindHostByName (char *name, int *StackID);
+// Host  *FindHostInStackByID (int StackID, IDtype ID);
+// Host  *FindHostInStackByName (int StackID, char *name);
Index: /branches/sc_branches/pantasks_condor/include/shell.h
===================================================================
--- /branches/sc_branches/pantasks_condor/include/shell.h	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/include/shell.h	(revision 34783)
@@ -0,0 +1,200 @@
+/*** shell.h ***/
+# include "external.h"
+
+# ifndef SHELL_H
+# define SHELL_H
+
+# define ISVAR(a) (isalnum (a) || (a == ':') || (a == '_'))
+# define ISWORD(a,q) ((q) ? (a != '"') : (isalnum(a) || (a == '/') || (a == '.') || (a == '_') || (a == '-')))
+# define ISNUM(c) (isdigit(c) || (c == '-') || (c == '.'))
+
+# define MACRO_STRING(s) #s
+# define MACRO_NAME(s) MACRO_STRING(s)
+
+typedef enum {GP_FILE, GP_BUFF} gpMode;
+#ifdef NOT_MOVED_TO_LIBOHANA
+/* enums used by gprint functions */
+typedef enum {GP_LOG, GP_ERR} gpDest;
+#endif
+typedef enum {OPIHI_VERBOSE_OFF, OPIHI_VERBOSE_ON, OPIHI_VERBOSE_ERROR} OpihiVerboseMode;
+
+typedef int CommandF (int argc, char **argv);
+
+typedef struct sockaddr_in SockAddress;
+
+/*** typedef structs used by shell functions ***/
+typedef struct {			/* basic opihi command structure */
+  char      real;
+  char     *name;
+  CommandF *func;    
+  char     *help;
+} Command;
+
+typedef struct {			/* a macro (collection of commands) */
+  char   *name;
+  char  **line;
+  int     Nlines;
+} Macro;
+
+typedef struct {			/* a list (macro/loop currently being executed) */
+  char **line;
+  int    n;
+  int    Nlines;
+  int    Nalloc;
+} List;
+
+/* structure used to represent the gprint i/o stream */
+typedef struct {
+  FILE *file;
+  char *name;
+  IOBuffer *buffer;
+  gpMode mode;
+  gpDest dest;
+  pthread_t thread;
+} gpStream;
+
+/*** globals used to track the shell language concepts  ***/
+int 	     interrupt;			/* true if C-C has been pressed */
+int 	     auto_break;		/* if true, zero exit status forces macros to escape */
+int 	     loop_next; 		/* set to true when next (or continue) is called */
+int 	     loop_last; 		/* set to true when last (or return) is called */
+int 	     loop_break;		/* set to true when break is called */
+int          is_script;                 /* being run within a shell script */
+
+/*** basic opihi shell functions ***/
+void          general_init            	PROTO((int *argc, char **argv));
+void          program_init            	PROTO((int *argc, char **argv));
+void          startup               	PROTO((int *argc, char **argv));
+int           opihi                     PROTO((int argc, char **argv));
+int           multicommand          	PROTO((char *line));
+int getServer (void);
+void          multicommand_InitServer   PROTO((void));
+void multicommand_StopServer (void);
+
+int           command               	PROTO((char *line, char **outline, int VERBOSE));
+void          set_verbose_shell         PROTO((int mode));
+int           get_verbose_shell         PROTO((void));
+
+char         *expand_vars           	PROTO((char *line));
+char         *expand_vectors        	PROTO((char *line));
+char         *parse                 	PROTO((int *status, char *line));
+char        **parse_commands        	PROTO((char *, int *));
+void          welcome                   PROTO((void));
+
+int           add_listentry             PROTO((int ThisList, char *line));
+int           is_for_loop           	PROTO((char *line));
+int           is_if_block           	PROTO((char *line));
+int           is_list               	PROTO((char *line));
+int           is_loop               	PROTO((char *line));
+int           is_task               	PROTO((char *line));
+int           is_task_exit             	PROTO((char *line));
+int           is_task_exec             	PROTO((char *line));
+int           is_macro_create       	PROTO((char *line));
+void          InitLists                 PROTO((void));
+int current_list_depth (void);
+int increase_list_depth (void);
+int decrease_list_depth (void);
+char *get_next_listentry (int ThisList);
+
+void          InitCommands              PROTO((void));
+void          AddCommand                PROTO((Command *new));
+int           DeleteCommand             PROTO((Command *command));
+Command      *MatchCommand              PROTO((char *name, int VERBOSE, int EXACT));
+void          sort_commands             PROTO((int *seq));
+
+void          SetCurrentMacroData	PROTO((char *name, int depth));
+Macro        *NewMacro			PROTO((char *name));
+int           DeleteMacro		PROTO((Macro *macro));
+Macro        *MatchMacro		PROTO((char *name, int VERBOSE, int EXACT));
+void          InitMacros                PROTO((void));
+char         *GetMacroName              PROTO((void));
+int           GetMacroDepth             PROTO((void));
+void          ListMacro                 PROTO((Macro *macro));
+void          ListMacros                PROTO((void));
+void          FreeMacro                 PROTO((Macro *macro));
+CommandF     *find_macro_command        PROTO((char *name));
+
+int           exec_loop                 PROTO((Macro *loop));
+/* char         *get_next_listentry    	PROTO((int ThisList)); */
+/* char         *remove_listentry      	PROTO((int current)); */
+
+int           ConfigInit            	PROTO((int *argc, char **argv));
+void          ConfigFree            	PROTO((void));
+char         *VarConfig             	PROTO((char *keyword, char *mode, void *ptr));
+char         *VarConfigEntry           	PROTO((char *keyword, char *mode, int entry, void *ptr));
+
+#ifndef NOT_MOVED_TO_DVO
+int           init_error                PROTO((void));
+int           push_error                PROTO((char *line));
+int           print_error               PROTO((void));
+#endif
+
+void          handle_interrupt      	PROTO((int));
+char        **command_completer     	PROTO((const char *, int, int));
+char         *command_generator     	PROTO((const char *text, int state));
+char        **completion_matches    	PROTO((char *, rl_compentry_func_t *));
+void          print_commands            PROTO((FILE *f));
+void          InitCommands              PROTO((void));
+
+/* command line parsing */
+char         *thisword              	PROTO((char *));
+char         *nextword              	PROTO((char *));
+char         *lastword              	PROTO((char *, char *));
+char         *thisvar               	PROTO((char *));
+char         *aftervar              	PROTO((char *));
+char         *lastvar               	PROTO((char *, char *));
+char         *thiscomm              	PROTO((char *));
+char         *nextcomm              	PROTO((char *));
+char         *opihi_append              PROTO((char *output, int *Noutput, char *start, char *stop));
+void          interpolate_slash         PROTO((char *line));
+char         *paste_args                PROTO((int argc, char **argv));
+
+/* macro functions (mapped to commands) */
+int 	      macro_create 		PROTO((int, char **)); 
+int 	      macro_delete 		PROTO((int, char **)); 
+int 	      macro_edit   		PROTO((int, char **));
+int 	      macro_exec   		PROTO((int, char **));
+int 	      macro_list_f 		PROTO((int, char **));  /* "macro_list" is a readline func */
+int 	      macro_read   		PROTO((int, char **));
+int 	      macro_write   		PROTO((int, char **));
+
+char 	     *memstr        		PROTO((char *m1, char *m2, int n));
+int  	      write_fmt     		PROTO((int fd, char *format, ...)) OHANA_FORMAT(printf, 2, 3);
+char 	     *opihi_version 		PROTO((void));
+char 	     *strip_version 		PROTO((char *input));
+
+// wrap readline in ohana mem functions:
+char         *opihi_readline            PROTO((char *prompt));
+
+/* gprint functions */
+void          gprintInit      		PROTO((void));
+gpStream     *gprintGetStream 		PROTO((gpDest dest));
+void          gprintSetBuffer 		PROTO((gpDest dest));
+IOBuffer     *gprintGetBuffer 		PROTO((gpDest dest));
+void          gprintSetFileAllThreads   PROTO((gpDest dest, char *filename));
+void          gprintSetFileThisThread   PROTO((gpDest dest, char *filename));
+void          gprintSetFile   		PROTO((gpStream *stream, gpDest dest, char *filename));
+FILE         *gprintGetFile   		PROTO((gpDest dest));
+char         *gprintGetName   		PROTO((gpDest dest));
+#ifdef NOT_MOVED_TO_LIBOHANA
+int           gprint          		PROTO((gpDest dest, char *format, ...)) OHANA_FORMAT(printf, 2, 3);
+#endif
+int           gwrite          		PROTO((char *buffer, int size, int N, gpDest dest));
+int           gprint_syserror           PROTO((gpDest dest, int myError, char *format, ...)) OHANA_FORMAT(printf, 3, 4);
+int           gprintv                   PROTO((gpDest dest, char *format, va_list argp));
+
+/* socket functions */
+int InitServerSocket (SockAddress *Address, char *hostname, char *portinfo);
+int WaitServerSocket (int InitSocket, SockAddress *Address);
+int GetClientSocket (char *hostname, char *portinfo);
+int InitServerSocket_Named (char *hostname, SockAddress *Address);
+int DefineValidIP (void);
+
+void FreeCommands (void);
+void FreeMacros (void);
+void FreeBuffers (void);
+void FreeVectors (void);
+void FreeVariables (void);
+void FreeLists (void);
+
+# endif
Index: /branches/sc_branches/pantasks_condor/include/user.h
===================================================================
--- /branches/sc_branches/pantasks_condor/include/user.h	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/include/user.h	(revision 34783)
@@ -0,0 +1,260 @@
+int abszero         PROTO((int, char **));
+int applyfit        PROTO((int, char **));
+int applyfit2d      PROTO((int, char **));
+int badimages       PROTO((int, char **));
+int box             PROTO((int, char **));
+int calextract      PROTO((int, char **));
+int calmextract     PROTO((int, char **));
+int cals            PROTO((int, char **));
+int catlog          PROTO((int, char **));
+int ccd             PROTO((int, char **));
+int ccdextract      PROTO((int, char **));
+int center          PROTO((int, char **));
+int cgrid           PROTO((int, char **));
+int clear           PROTO((int, char **));
+int cmatch          PROTO((int, char **));
+int cmd             PROTO((int, char **));
+int cmdextract      PROTO((int, char **));
+int cmpread         PROTO((int, char **));
+int concat          PROTO((int, char **));
+int contour         PROTO((int, char **));
+int cplot           PROTO((int, char **));
+int create          PROTO((int, char **));
+int csystem         PROTO((int, char **));
+int ctimes          PROTO((int, char **));
+int cursor          PROTO((int, char **));
+int czplot          PROTO((int, char **));
+int datafile        PROTO((int, char **));
+int date            PROTO((int, char **));
+int ddmagextract    PROTO((int, char **));
+int ddmags          PROTO((int, char **));
+int delete          PROTO((int, char **));
+int device          PROTO((int, char **));
+int dmagaves        PROTO((int, char **));
+int dmagextract     PROTO((int, char **));
+int dmagmeas        PROTO((int, char **));
+int dmags           PROTO((int, char **));
+int dmt             PROTO((int, char **));
+int dumpmags        PROTO((int, char **));
+int elixir          PROTO((int, char **));
+int extract         PROTO((int, char **));
+int file            PROTO((int, char **));
+int fit             PROTO((int, char **));
+int fit2d           PROTO((int, char **));
+int gaussjordan     PROTO((int, char **));
+int gcat            PROTO((int, char **));
+int gimages         PROTO((int, char **));
+int grid            PROTO((int, char **));
+int gstar           PROTO((int, char **));
+int gtypes          PROTO((int, char **));
+int histogram       PROTO((int, char **));
+int images          PROTO((int, char **));
+int imbox           PROTO((int, char **));
+int imdata          PROTO((int, char **));
+int imdense         PROTO((int, char **)); 
+int imextract       PROTO((int, char **));
+int imlist          PROTO((int, char **));
+int imphot          PROTO((int, char **));
+int imrough         PROTO((int, char **));
+int imsearch        PROTO((int, char **));
+int imstats         PROTO((int, char **));
+int interpolate     PROTO((int, char **));
+int jpeg            PROTO((int, char **));
+int labels          PROTO((int, char **));
+int lcat            PROTO((int, char **));
+int lcurve          PROTO((int, char **));
+int limits          PROTO((int, char **));
+int list_buffers    PROTO((int, char **));
+int list_vectors    PROTO((int, char **));
+int mcreate         PROTO((int, char **));
+int mextract        PROTO((int, char **));
+int mget            PROTO((int, char **));
+int mset            PROTO((int, char **));
+int pcat            PROTO((int, char **));
+int photcodes       PROTO((int, char **));
+int photresid       PROTO((int, char **));
+int plot            PROTO((int, char **));
+int pmeasure        PROTO((int, char **));
+int precess         PROTO((int, char **));
+int print           PROTO((int, char **));
+int procks          PROTO((int, char **));
+int ps              PROTO((int, char **));
+int rd              PROTO((int, char **));
+int read_vectors    PROTO((int, char **));
+int region          PROTO((int, char **));
+int resid           PROTO((int, char **));
+int resize          PROTO((int, char **));
+int section         PROTO((int, char **));
+int set             PROTO((int, char **));
+int simage          PROTO((int, char **));
+int sort_vectors    PROTO((int, char **));
+int sprintf_opihi   PROTO((int, char **));
+int stats           PROTO((int, char **));
+int style           PROTO((int, char **));
+int subpix          PROTO((int, char **));
+int subraster       PROTO((int, char **));
+int subset          PROTO((int, char **));
+int textline        PROTO((int, char **));
+int tv              PROTO((int, char **));
+int uniq            PROTO((int, char **));
+int vectobuf        PROTO((int, char **));
+int vstat           PROTO((int, char **));
+int wd              PROTO((int, char **));
+int write_vectors   PROTO((int, char **));
+int zap             PROTO((int, char **));
+int zeropts         PROTO((int, char **));
+int zplot           PROTO((int, char **));
+
+static Command user[] = {  
+  {"abszero", 	   abszero,       "find filter zeropts"},
+  {"applyfit",     applyfit,      "apply fit to new vector"},
+  {"applyfit2d",   applyfit2d,    "apply 2-d fit to new vector"},
+  {"badimages",    badimages,     "look for images with anomalous astrometry"},
+  {"badimages",    badimages,     "look for images with anomalous astrometry"},
+  {"box",     	   box,           "draw a box on the plot"},
+  {"calextract",   calextract,    "extract dmags"},
+  {"calextract",   calextract,    "extract dmags"},
+  {"calmextract",  calmextract,   "extract dmags"},
+  {"cals",    	   cals,          "plot calibration data"},
+  {"catalog", 	   catlog,        "plot catalog stars"},
+  {"catalog", 	   catlog,        "plot catalog stars"},
+  {"ccd",     	   ccd,           "plot color-color diagram"},
+  {"ccd",     	   ccd,           "plot color-color diagram"},
+  {"ccdextract",   ccdextract,    "extract star coords from color-color diagram"},
+  {"center",       center,        "center image on coords"},
+  {"center",       center,        "center image on coords"},
+  {"cgrid",   	   cgrid,         "plot sky coordinate grid"},
+  {"cgrid",   	   cgrid,         "plot sky coordinate grid"},
+  {"clear",   	   clear,         "erase plot"},
+  {"cmatch",  	   cmatch,        "match two catalogs"},
+  {"cmatch",  	   cmatch,        "match two catalogs"},
+  {"cmd",     	   cmd,           "plot cmd of stars in current region"},
+  {"cmd",     	   cmd,           "plot cmd of stars in current region"},
+  {"cmdextract",   cmdextract,    "extract stars based on cmd regions"},
+  {"cmpread",      cmpread,       "read data from cmp format files"},
+  {"cmpread",      cmpread,       "read data from cmp format files"},
+  {"concat",  	   concat,        "reduce vector dimension"},
+  {"contour", 	   contour,       "create contour from image"},
+  {"cplot",   	   cplot,         "plot vectors in sky coordinates"},
+  {"cplot",   	   cplot,         "plot vectors in sky coordinates"},
+  {"create",  	   create,        "create a new vector"},
+  {"csystem", 	   csystem,       "convert between coordinate systems"},
+  {"csystem", 	   csystem,       "convert between coordinate systems"},
+  {"ctimes",  	   ctimes,        "convert between time formats"},
+  {"ctimes",  	   ctimes,        "convert between time formats"},
+  {"cursor",  	   cursor,        "get coords from cursor"},
+  {"czplot",  	   czplot,        "plot scaled vectors in sky coordinates"},
+  {"czplot",  	   czplot,        "plot scaled vectors in sky coordinates"},
+  {"datafile",     datafile,      "define file to read vectors"},
+  {"date",    	   date,          "get current date"},
+  {"ddmagextr",    ddmagextract,  "plot magnitude differences"},
+  {"ddmags",       ddmags,        "plot magnitude differences"},
+  {"ddmags",       ddmags,        "plot magnitude differences"},
+  {"delete",  	   delete,        "delete vectors or matrices"},
+  {"device",  	   device,        "set / get current graphics device"},
+  {"dmagaves",     dmagaves,      "plot differential magnitudes between filters"},
+  {"dmagextract",  dmagextract,   "extract stars based on differential magnitudes between filters"},
+  {"dmagmeas",     dmagmeas,      "plot differential magnitudes between filters"},
+  {"dmags",   	   dmags,         "plot differential magnitudes between filters"},
+  {"dmags",   	   dmags,         "plot differential magnitudes between filters"},
+  {"dmt",          dmt,           "plot mag scatter"},
+  {"dmt",          dmt,           "plot mag scatter"},
+  {"dumpmags",     dumpmags,      "custom dB dumping thingy"},
+  {"dumpmags",     dumpmags,      "custom dB dumping thingy"},
+  {"elixir",       elixir,        "get status info from elixir"},
+  {"elixir",       elixir,        "get status info from elixir"},
+  {"extract", 	   extract,       "extract vectors from catalogs"},
+  {"file",    	   file,          "test for a file"},
+  {"fit",     	   fit,           "fit polynomial to vector pair"},
+  {"fit2d",        fit2d,         "fit 2-d polynomial to vector triplet"},
+  {"gaussj",  	   gaussjordan,   "solve Ax = B (N-D)"},
+  {"gcat",         gcat,          "get catalog at location"},
+  {"gcat",         gcat,          "get catalog at location"},
+  {"gimages",      gimages,       "get images at location"},
+  {"gimages",      gimages,       "get images at location"},
+  {"grid",    	   grid,          "plot cartesian grid"},
+  {"gstar",   	   gstar,         "get star statistics"},
+  {"gstar",   	   gstar,         "get star statistics"},
+  {"gtypes",       gtypes,        "get type fractions"},
+  {"gtypes",       gtypes,        "get type fractions"},
+  {"histogram",    histogram,     "generate histogram from vector"},
+  {"images",  	   images,        "plot image boxes"},
+  {"images",  	   images,        "plot image boxes"},
+  {"imbox",   	   imbox,         "plot expected image box"},
+  {"imbox",   	   imbox,         "plot expected image box"},
+  {"imdata",  	   imdata,        "extract data for specific images"},
+  {"imdata",  	   imdata,        "extract data for specific images"},
+  {"imdense", 	   imdense,       "image density plot"},
+  {"imdense", 	   imdense,       "image density plot"},
+  {"imextract",    imextract,     "extract vectors from catalogs"},
+  {"imextract",    imextract,     "extract vectors from catalogs"},
+  {"imlist",  	   imlist,        "list image info"},
+  {"imlist",  	   imlist,        "list image info"},
+  {"imphot",  	   imphot,        "image photometry info"},
+  {"imphot",  	   imphot,        "image photometry info"},
+  {"imrough",      imrough,       "get info from imruf database"},
+  {"imrough",      imrough,       "get info from imruf database"},
+  {"imsearch",     imsearch,      "get info from imreg database"},
+  {"imsearch",     imsearch,      "get info from imreg database"},
+  {"imstats", 	   imstats,       "plot image statistics"},
+  {"imstats", 	   imstats,       "plot image statistics"},
+  {"interpolate",  interpolate,   "interpolate between vector pairs"},
+  {"jpeg",         jpeg,          "write text line on graph"},
+  {"labels",  	   labels,        "define labels for plot"},
+  {"lcat",    	   lcat,          "list catalogs in region"},
+  {"lcat",    	   lcat,          "list catalogs in region"},
+  {"lcurve",  	   lcurve,        "plot lightcurve for a star"},
+  {"lcurve",  	   lcurve,        "plot lightcurve for a star"},
+  {"limits",  	   limits,        "define plot limits"},
+  {"buffers",      list_buffers,  "list the currently allocated buffers"},
+  {"vectors", 	   list_vectors,  "list vectors"},
+  {"local",  	   local,         "define local variables"},
+  {"mcreate", 	   mcreate,       "create a matrix"},
+  {"mextract",	   mextract,      "extract vectors from catalogs"},
+  {"mextract",	   mextract,      "extract vectors from catalogs"},
+  {"mget",    	   mget,          "extract a vector from a matrix"},
+  {"mset",    	   mset,          "insert a vector in a matrix"},
+  {"pcat",    	   pcat,          "plot catalog boundaries"},
+  {"pcat",    	   pcat,          "plot catalog boundaries"},
+  {"photcodes",    photcodes,     "list photometry codes"},
+  {"photcodes",    photcodes,     "list photometry codes"},
+  {"photresid",    photresid,     "plot photometry residuals"},
+  {"plot",    	   plot,          "plot a pair of vectors"},
+  {"pmeasure",	   pmeasure,      "plot individual measurements"},
+  {"pmeasure",	   pmeasure,      "plot individual measurements"},
+  {"precess", 	   precess,       "precess coordinates"},
+  {"precess", 	   precess,       "precess coordinates"},
+  {"print",   	   print,         "write vectors to file"},
+  {"print",   	   print,         "write vectors to file"},
+  {"procks",  	   procks,        "plot rocks"},
+  {"procks",  	   procks,        "plot rocks"},
+  {"ps",      	   ps,            "define labels for plot"},
+  {"rd",           rd,            "load fits image"},
+  {"read",         read_vectors,  "read vectors from datafile"},
+  {"region",  	   region,        "define sky region for plot"},
+  {"region",  	   region,        "define sky region for plot"},
+  {"resid",   	   resid,         "plot residuals"},
+  {"resize",  	   resize,        "set graphics/image window size"},
+  {"section", 	   section,       "define section of graph"},
+  {"set",     	   set,           "vector math"},
+  {"simage",  	   simage,        "plot stars in an image"},
+  {"simage",  	   simage,        "plot stars in an image"},
+  {"sort",    	   sort_vectors,  "sort list of vectors"},
+  {"sprintf", 	   sprintf_opihi, "formated print to variable"},
+  {"stats",   	   stats,         "give statistics on a portion of a buffer"},
+  {"style",   	   style,         "set the style for graph plots"},
+  {"subpix",  	   subpix,        "get subpixel positions"},
+  {"subpix",  	   subpix,        "get subpixel positions"},
+  {"subraster",    subraster,     "subraster of fits image"},
+  {"subset",  	   subset,        "expand vector dimension"},
+  {"textline",     textline,      "write text line on graph"},
+  {"tv",      	   tv,            "display an image on the Kii window"},
+  {"uniq",    	   uniq,          "create a uniq vector subset from a vector"},
+  {"vectobuf",     vectobuf,      "convert vector triplet to buffer"},
+  {"vstat",        vstat,         "get info from imreg database"},
+  {"wd",      	   wd,            "write an image to a file"},
+  {"write",   	   write_vectors, "write vectors to datafile"},
+  {"zap",     	   zap,           "delete pixels"},
+  {"zeropts", 	   zeropts,       "show filter zeropts"},
+  {"zplot",   	   zplot,         "plot x y with size scaled by z"},
+}; 
Index: /branches/sc_branches/pantasks_condor/lib.data/Makefile
===================================================================
--- /branches/sc_branches/pantasks_condor/lib.data/Makefile	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/lib.data/Makefile	(revision 34783)
@@ -0,0 +1,64 @@
+default: libdata
+
+include ../../../Makefile.System
+HOME    =       $(ROOT)/src/opihi
+BIN     =       $(HOME)/bin
+LIB     =       $(HOME)/lib
+INC     =       $(HOME)/include
+SDIR    =       $(HOME)/lib.data
+include ../Makefile.Common
+
+# programs may add their own internal requirements here
+FULL_CFLAGS   = $(BASE_CFLAGS) -fPIC
+FULL_CPPFLAGS = $(BASE_CPPFLAGS)
+FULL_LDFLAGS  = $(BASE_LDFLAGS)
+
+# general numerical functions (libdata) #####################
+srcs = \
+$(SDIR)/book.$(ARCH).o                  \
+$(SDIR)/page.$(ARCH).o                  \
+$(SDIR)/fft.$(ARCH).o			\
+$(SDIR)/svdcmp.$(ARCH).o		\
+$(SDIR)/svdcmp_bond_new.$(ARCH).o		\
+$(SDIR)/bracket.$(ARCH).o		\
+$(SDIR)/spline.$(ARCH).o		\
+$(SDIR)/SplineOps.$(ARCH).o		\
+$(SDIR)/mrqmin.$(ARCH).o		\
+$(SDIR)/mrq2dmin.$(ARCH).o		\
+$(SDIR)/precess.$(ARCH).o		\
+$(SDIR)/starfuncs.$(ARCH).o		\
+$(SDIR)/hermitian.$(ARCH).o		\
+$(SDIR)/gaussian.$(ARCH).o		\
+$(SDIR)/graphtools.$(ARCH).o            \
+$(SDIR)/queues.$(ARCH).o		\
+$(SDIR)/PlotVectors.$(ARCH).o		\
+$(SDIR)/open_kapa.$(ARCH).o             \
+$(SDIR)/style_args.$(ARCH).o
+
+# fix malloc
+#$(SDIR)/svdcmp_bond_raw.$(ARCH).o		\
+
+#$(SDIR)/open_graph.$(ARCH).o            \
+#$(SDIR)/open_image.$(ARCH).o            \
+
+# dependancy rules for include files ########################
+incs = \
+$(INC)/opihi.h \
+$(INC)/external.h \
+$(INC)/shell.h \
+$(INC)/dvomath.h \
+$(INC)/display.h 
+
+$(srcs): $(incs)
+
+$(LIB)/libdata.$(ARCH).a: $(srcs)
+$(LIB)/libdata.$(ARCH).$(DLLTYPE): $(srcs)
+
+$(DESTLIB)/libdata.a: $(LIB)/libdata.$(ARCH).a
+$(DESTLIB)/libdata.$(DLLTYPE): $(LIB)/libdata.$(ARCH).$(DLLTYPE)
+
+libdata: $(DESTLIB)/libdata.a $(DESTLIB)/libdata.$(DLLTYPE)
+
+uninstall:
+	rm -f $(DESTLIB)/libdata.a
+	rm -f $(DESTLIB)/libdata.$(DLLTYPE)
Index: /branches/sc_branches/pantasks_condor/lib.data/PlotVectors.c
===================================================================
--- /branches/sc_branches/pantasks_condor/lib.data/PlotVectors.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/lib.data/PlotVectors.c	(revision 34783)
@@ -0,0 +1,214 @@
+# include "display.h"
+
+int PlotVectorSingle (int kapa, Vector *vec, char *mask, char *mode) {
+
+  int i, Npts, Nout;
+  float *temp;
+
+  Npts = vec->Nelements;
+  ALLOCATE (temp, float, Npts);
+
+  Nout = 0;
+  if (vec->type == OPIHI_FLT) {
+    opihi_flt *value = vec->elements.Flt;
+    for (i = 0; i < Npts; i++) {
+      if (mask && mask[i]) continue;
+      temp[Nout] = value[i];
+      Nout ++;
+    }
+  } else {
+    opihi_int *value = vec->elements.Int;
+    for (i = 0; i < Npts; i++) {
+      if (mask && mask[i]) continue;
+      temp[Nout] = value[i];
+      Nout ++;
+    }
+  }
+  KapaPlotVector (kapa, Nout, temp, mode);
+
+  free (temp);
+
+  return (TRUE);
+}
+
+int PlotVectorPair (int kapa, Vector *xVec, Vector *yVec, char *mask, Graphdata *graphmode) {
+
+  int i, Npts, Nout;
+  float *temp;
+
+  if (xVec->Nelements != yVec->Nelements) return (FALSE);
+  Npts = xVec->Nelements;
+
+  ALLOCATE (temp, float, Npts);
+
+  Nout = 0;
+  if (xVec->type == OPIHI_FLT) {
+    opihi_flt *value = xVec->elements.Flt;
+    for (i = 0; i < Npts; i++) {
+      if (mask && mask[i]) continue;
+      temp[Nout] = value[i];
+      Nout ++;
+    }
+  } else {
+    opihi_int *value = xVec->elements.Int;
+    for (i = 0; i < Npts; i++) {
+      if (mask && mask[i]) continue;
+      temp[Nout] = value[i];
+      Nout ++;
+    }
+  }
+  KapaPrepPlot (kapa, Nout, graphmode);
+  KapaPlotVector (kapa, Nout, temp, "x");
+
+  Nout = 0;
+  if (yVec->type == OPIHI_FLT) {
+    opihi_flt *value = yVec->elements.Flt;
+    for (i = 0; i < Npts; i++) {
+      if (mask && mask[i]) continue;
+      temp[Nout] = value[i];
+      Nout ++;
+    }
+  } else {
+    opihi_int *value = yVec->elements.Int;
+    for (i = 0; i < Npts; i++) {
+      if (mask && mask[i]) continue;
+      temp[Nout] = value[i];
+      Nout ++;
+    }
+  }
+  KapaPlotVector (kapa, Nout, temp, "y");
+
+  free (temp);
+
+  return (TRUE);
+}
+
+int PlotVectorTriplet (int kapa, Vector *xVec, Vector *yVec, Vector *zVec, char *mask, Graphdata *graphmode) {
+
+  int i, Npts, Nout;
+  float *temp;
+
+  if (xVec->Nelements != yVec->Nelements) return (FALSE);
+  if (xVec->Nelements != zVec->Nelements) return (FALSE);
+  Npts = xVec->Nelements;
+
+  ALLOCATE (temp, float, Npts);
+
+  Nout = 0;
+  if (xVec->type == OPIHI_FLT) {
+    opihi_flt *value = xVec->elements.Flt;
+    for (i = 0; i < Npts; i++) {
+      if (mask && mask[i]) continue;
+      temp[Nout] = value[i];
+      Nout ++;
+    }
+  } else {
+    opihi_int *value = xVec->elements.Int;
+    for (i = 0; i < Npts; i++) {
+      if (mask && mask[i]) continue;
+      temp[Nout] = value[i];
+      Nout ++;
+    }
+  }
+  KapaPrepPlot (kapa, Nout, graphmode);
+  KapaPlotVector (kapa, Nout, temp, "x");
+
+  Nout = 0;
+  if (yVec->type == OPIHI_FLT) {
+    opihi_flt *value = yVec->elements.Flt;
+    for (i = 0; i < Npts; i++) {
+      if (mask && mask[i]) continue;
+      temp[Nout] = value[i];
+      Nout ++;
+    }
+  } else {
+    opihi_int *value = yVec->elements.Int;
+    for (i = 0; i < Npts; i++) {
+      if (mask && mask[i]) continue;
+      temp[Nout] = value[i];
+      Nout ++;
+    }
+  }
+  KapaPlotVector (kapa, Nout, temp, "y");
+
+  Nout = 0;
+  if (zVec->type == OPIHI_FLT) {
+    opihi_flt *value = zVec->elements.Flt;
+    for (i = 0; i < Npts; i++) {
+      if (mask && mask[i]) continue;
+      temp[Nout] = value[i];
+      Nout ++;
+    }
+  } else {
+    opihi_int *value = zVec->elements.Int;
+    for (i = 0; i < Npts; i++) {
+      if (mask && mask[i]) continue;
+      temp[Nout] = value[i];
+      Nout ++;
+    }
+  }
+  KapaPlotVector (kapa, Nout, temp, "z");
+
+  free (temp);
+
+  return (TRUE);
+}
+
+int PlotVectorPairErrors (int kapa, Vector *xVec, Vector *yVec, Vector *dyVec, Graphdata *graphmode) {
+
+  int i, Npts;
+  float *temp;
+
+  if (xVec->Nelements != yVec->Nelements) return (FALSE);
+  if (xVec->Nelements != dyVec->Nelements) return (FALSE);
+  Npts = xVec->Nelements;
+
+  ALLOCATE (temp, float, Npts);
+
+  KapaPrepPlot (kapa, Npts, graphmode);
+
+  if (xVec->type == OPIHI_FLT) {
+    opihi_flt *value = xVec->elements.Flt;
+    for (i = 0; i < Npts; i++) {
+      temp[i] = value[i];
+    }
+  } else {
+    opihi_int *value = xVec->elements.Int;
+    for (i = 0; i < Npts; i++) {
+      temp[i] = value[i];
+    }
+  }
+  KapaPlotVector (kapa, Npts, temp, "x");
+
+  if (yVec->type == OPIHI_FLT) {
+    opihi_flt *value = yVec->elements.Flt;
+    for (i = 0; i < Npts; i++) {
+      temp[i] = value[i];
+    }
+  } else {
+    opihi_int *value = yVec->elements.Int;
+    for (i = 0; i < Npts; i++) {
+      temp[i] = value[i];
+    }
+  }
+  KapaPlotVector (kapa, Npts, temp, "y");
+
+  if (dyVec->type == OPIHI_FLT) {
+    opihi_flt *value = dyVec->elements.Flt;
+    for (i = 0; i < Npts; i++) {
+      temp[i] = value[i];
+    }
+  } else {
+    opihi_int *value = dyVec->elements.Int;
+    for (i = 0; i < Npts; i++) {
+      temp[i] = value[i];
+    }
+  }
+  KapaPlotVector (kapa, Npts, temp, "dym");
+  KapaPlotVector (kapa, Npts, temp, "dyp");
+
+  free (temp);
+
+  return (TRUE);
+}
+
Index: /branches/sc_branches/pantasks_condor/lib.data/SplineOps.c
===================================================================
--- /branches/sc_branches/pantasks_condor/lib.data/SplineOps.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/lib.data/SplineOps.c	(revision 34783)
@@ -0,0 +1,263 @@
+# include "data.h"
+
+/* this file contains functions to manage the collection of Splines.
+   A spline in opihi is a native data structure which defines a 1D spline.  
+ */
+
+Spline **splines;   /* book to store the list of all splines */
+int     Nsplines;   /* number of currently defined splines */
+int     NSPLINES;   /* number of currently allocated splines */
+
+void InitSplines () {
+  Nsplines = 0;
+  NSPLINES = 16;
+  ALLOCATE (splines, Spline *, NSPLINES); 
+}
+
+void FreeSplines () {
+
+  int i;
+
+  for (i = 0; i < Nsplines; i++) {
+    FreeSpline (splines[i]);
+  }
+  free (splines);
+}
+
+void InitSpline (Spline *spline, char *name, int Nknots) {
+
+  if (!spline[0].name) {
+    spline[0].name = strcreate (name);
+  }
+  spline[0].Nknots = Nknots;
+  
+  if (spline[0].xk) {
+    REALLOCATE (spline[0].xk, opihi_flt, spline[0].Nknots);
+    REALLOCATE (spline[0].yk, opihi_flt, spline[0].Nknots);
+    REALLOCATE (spline[0].y2, opihi_flt, spline[0].Nknots);
+  } else {
+    ALLOCATE (spline[0].xk, opihi_flt, spline[0].Nknots);
+    ALLOCATE (spline[0].yk, opihi_flt, spline[0].Nknots);
+    ALLOCATE (spline[0].y2, opihi_flt, spline[0].Nknots);
+  }
+  memset (spline[0].xk, 0, spline[0].Nknots * sizeof(opihi_flt));
+  memset (spline[0].yk, 0, spline[0].Nknots * sizeof(opihi_flt));
+  memset (spline[0].y2, 0, spline[0].Nknots * sizeof(opihi_flt));
+}
+
+void FreeSpline (Spline *spline) {
+
+    free (spline[0].name);
+    free (spline[0].xk);
+    free (spline[0].yk);
+    free (spline[0].y2);
+    free (spline);
+}
+
+/* return the given spline */
+Spline *GetSpline (int where) {
+
+  if (where < 0) where += Nsplines;
+  if (where < 0) return NULL;
+  if (where >= Nsplines) return NULL;
+  return (splines[where]);
+}
+
+/* return the given spline */
+Spline *FindSpline (char *name) {
+
+  int i;
+
+  for (i = 0; i < Nsplines; i++) {
+    if (!strcmp (splines[i][0].name, name)) {
+      return (splines[i]);
+    }
+  }
+  return (NULL);
+}
+
+/* make a new named spline */
+Spline *CreateSpline (char *name, int Nknots) {
+
+  int N;
+  Spline *spline;
+
+  spline = FindSpline (name);
+  if (spline != NULL) {
+    InitSpline (spline, name, Nknots);
+    return (spline);
+  }
+
+  N = Nsplines;
+  Nsplines ++;
+  CHECK_REALLOCATE (splines, Spline *, NSPLINES, Nsplines, 16);
+  ALLOCATE (spline, Spline, 1);
+  spline->name = NULL;
+  spline->xk = NULL;
+  spline->yk = NULL;
+  spline->y2 = NULL;
+  InitSpline (spline, name, Nknots);
+  splines[N] = spline;
+  return (spline);
+}
+
+/* delete a spline */
+int DeleteSpline (Spline *spline) {
+
+  int i, N, NSPLINES_2;
+
+  /* find spline in spline list */
+  N = -1;
+  for (i = 0; i < Nsplines; i++) {
+    if (splines[i] == spline) {
+      N = i;
+      break;
+    }
+  }
+  if (N == -1) return (FALSE);
+
+  for (i = N; i < Nsplines - 1; i++) {
+    splines[i] = splines[i + 1];
+  }
+  Nsplines --;
+  NSPLINES_2 = MAX (16, NSPLINES / 2);
+  if (Nsplines < NSPLINES_2) {
+    NSPLINES = NSPLINES_2;
+    REALLOCATE (splines, Spline *, NSPLINES);
+  }
+
+  FreeSpline (spline);
+  return (TRUE);
+}
+
+/* list known books */
+void ListSplines () {
+
+  int i;
+
+  for (i = 0; i < Nsplines; i++) {
+    gprint (GP_ERR, "%-15s %3d\n", splines[i][0].name, splines[i][0].Nknots);
+  }
+  return;
+}
+
+// write the spline data to a FITS file.
+int SaveSpline (char *filename, char *name, int append) {
+  
+  Header header;
+  Matrix matrix;
+  Header theader;
+  FTable ftable;
+
+  Spline *myspline = FindSpline (name);
+  if (!myspline) {
+    gprint (GP_ERR, "can't find spline for write : %s\n", name);
+    return (FALSE);
+  }
+
+  FILE *f = NULL;
+
+  /* open file for output */
+  if (append) {
+    f = fopen (filename, "a");
+  } else {
+    f = fopen (filename, "w");
+  }
+  if (f == (FILE *) NULL) {
+    gprint (GP_ERR, "can't open file for write : %s\n", filename);
+    return (FALSE);
+  }
+
+  gfits_create_table_header (&theader, "BINTABLE", name);
+
+  gfits_define_bintable_column (&theader, "D", "X_KNOT", NULL, NULL, 1.0, 0.0);
+  gfits_define_bintable_column (&theader, "D", "Y_KNOT", NULL, NULL, 1.0, 0.0);
+  gfits_define_bintable_column (&theader, "D", "DY2_DX", NULL, NULL, 1.0, 0.0);
+
+  // generate the output array that carries the data
+  gfits_create_table (&theader, &ftable);
+
+  gfits_set_bintable_column_reformat (&theader, &ftable, "X_KNOT", "double", myspline->xk, myspline->Nknots);
+  gfits_set_bintable_column_reformat (&theader, &ftable, "Y_KNOT", "double", myspline->yk, myspline->Nknots);
+  gfits_set_bintable_column_reformat (&theader, &ftable, "DY2_DX", "double", myspline->y2, myspline->Nknots);
+
+  if (!append) {
+    gfits_init_header (&header);
+    header.extend = TRUE;
+
+    gfits_create_header (&header);
+    gfits_create_matrix (&header, &matrix);
+
+    gfits_fwrite_header  (f, &header);
+    gfits_fwrite_matrix  (f, &matrix);
+
+    gfits_free_header (&header);
+    gfits_free_matrix (&matrix);
+  }
+  gfits_fwrite_Theader (f, &theader);
+  gfits_fwrite_table  (f, &ftable);
+
+  gfits_free_header (&theader);
+  gfits_free_table (&ftable);
+
+  fclose (f);
+  fflush (f);
+  return (TRUE);
+}
+
+// read the spline data from a FITS file.
+// these functions (LoadSpline, SaveSpline) should probably take spline pointers not names
+int LoadSpline (char *filename, char *name) {
+  
+  int i, Ncol;
+  off_t Nrow;
+  char type[16];
+
+  Header theader;
+  FTable ftable;
+
+  FILE *f = NULL;
+
+  /* open file for input */
+  f = fopen (filename, "r");
+  if (f == (FILE *) NULL) {
+    gprint (GP_ERR, "can't open file for read : %s\n", filename);
+    return FALSE;
+  }
+
+  ftable.header = &theader;
+
+  // read the full table data into a buffer
+  if (!gfits_fread_ftable (f, &ftable, name)) {
+    fclose (f);
+    gprint (GP_ERR, "can't read FITS file : %s\n", filename);
+    return FALSE;
+  }
+
+  // XXX: need to handle case of spline data not existing...
+
+  // need to create and assign to flat-field correction
+  double *xk = gfits_get_bintable_column_data (&theader, &ftable, "X_KNOT", type, &Nrow, &Ncol);
+  assert (!strcmp(type, "double"));
+  
+  // need to create and assign to flat-field correction
+  double *yk = gfits_get_bintable_column_data (&theader, &ftable, "Y_KNOT", type, &Nrow, &Ncol);
+  assert (!strcmp(type, "double"));
+
+  // need to create and assign to flat-field correction
+  double *y2 = gfits_get_bintable_column_data (&theader, &ftable, "DY2_DX", type, &Nrow, &Ncol);
+  assert (!strcmp(type, "double"));
+
+  Spline *myspline = CreateSpline (name, Nrow);
+  for (i = 0; i < Nrow; i++) {
+    myspline->xk[i] = xk[i];
+    myspline->yk[i] = yk[i];
+    myspline->y2[i] = y2[i];
+  }
+  free (xk);
+  free (yk);
+  free (y2);
+
+  fclose (f);
+  return (TRUE);
+}
Index: /branches/sc_branches/pantasks_condor/lib.data/book.c
===================================================================
--- /branches/sc_branches/pantasks_condor/lib.data/book.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/lib.data/book.c	(revision 34783)
@@ -0,0 +1,127 @@
+# include "data.h"
+
+Book **books;   /* book to store the list of all books */
+int    Nbooks;   /* number of currently defined books */
+int    NBOOKS;   /* number of currently allocated books */
+
+void InitBooks () {
+  Nbooks = 0;
+  NBOOKS = 16;
+  ALLOCATE (books, Book *, NBOOKS); 
+}
+
+void FreeBooks () {
+
+  int i;
+
+  for (i = 0; i < Nbooks; i++) {
+    FreeBook (books[i]);
+  }
+  free (books);
+}
+
+void InitBook (Book *book, char *name) {
+
+    book[0].name = strcreate (name);
+
+    book[0].Npages = 0;
+    book[0].NPAGES = 16;
+    ALLOCATE (book[0].pages, Page *, book[0].NPAGES);
+    ALLOCATE (book[0].pageIDs, char *, book[0].NPAGES);
+    // ALLOCATE (book[0].index, int, book[0].NPAGES);
+}
+
+void FreeBook (Book *book) {
+
+    int i;
+
+    free (book[0].name);
+    for (i = 0; i < book[0].Npages; i++) {
+	FreePage (book[0].pages[i]);
+	free (book[0].pageIDs[i]);
+    }
+    free (book[0].pages);
+    free (book[0].pageIDs);
+    // free (book[0].index);
+    free (book);
+}
+
+/* return the given book */
+Book *GetBook (int where) {
+
+  if (where < 0) where += Nbooks;
+  if (where < 0) return NULL;
+  if (where >= Nbooks) return NULL;
+  return (books[where]);
+}
+
+/* return the given book */
+Book *FindBook (char *name) {
+
+  int i;
+
+  for (i = 0; i < Nbooks; i++) {
+    if (!strcmp (books[i][0].name, name)) {
+      return (books[i]);
+    }
+  }
+  return (NULL);
+}
+
+/* make a new named book */
+Book *CreateBook (char *name) {
+
+  int N;
+  Book *book;
+
+  book = FindBook (name);
+  if (book != NULL) return (book);
+
+  N = Nbooks;
+  Nbooks ++;
+  CHECK_REALLOCATE (books, Book *, NBOOKS, Nbooks, 16);
+  ALLOCATE (book, Book, 1);
+  InitBook (book, name);
+  books[N] = book;
+  return (book);
+}
+
+/* delete a book */
+int DeleteBook (Book *book) {
+
+  int i, N, NBOOKS_2;
+
+  /* find book in book list */
+  N = -1;
+  for (i = 0; i < Nbooks; i++) {
+    if (books[i] == book) {
+      N = i;
+      break;
+    }
+  }
+  if (N == -1) return (FALSE);
+
+  for (i = N; i < Nbooks - 1; i++) {
+    books[i] = books[i + 1];
+  }
+  Nbooks --;
+  NBOOKS_2 = MAX (16, NBOOKS / 2);
+  if (Nbooks < NBOOKS_2) {
+    NBOOKS = NBOOKS_2;
+    REALLOCATE (books, Book *, NBOOKS);
+  }
+
+  FreeBook (book);
+  return (TRUE);
+}
+
+/* list known books */
+void ListBooks () {
+
+  int i;
+
+  for (i = 0; i < Nbooks; i++) {
+    gprint (GP_ERR, "%-15s %3d\n", books[i][0].name, books[i][0].Npages);
+  }
+  return;
+}
Index: /branches/sc_branches/pantasks_condor/lib.data/bracket.c
===================================================================
--- /branches/sc_branches/pantasks_condor/lib.data/bracket.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/lib.data/bracket.c	(revision 34783)
@@ -0,0 +1,65 @@
+# include "data.h"
+
+/* fast operation to find an entry just below (0) or above (1) value */
+/* input list must be sorted */
+int bracket (double *list, int Nlist, int mode, double value) {
+
+  int Nlo, Nhi, N;
+
+  if (mode == 0) {
+    Nlo = 0; Nhi = Nlist;
+    while (Nhi - Nlo > 10) {
+      N = 0.5*(Nlo + Nhi);
+      if (list[N] < value) {
+	Nlo = N;
+      } else {
+	Nhi = N + 1;
+      }
+    }
+    return (Nlo);
+  }
+  if (mode == 1) {
+    Nlo = 0; Nhi = Nlist;
+    while (Nhi - Nlo > 10) {
+      N = 0.5*(Nlo + Nhi);
+      if (list[N] > value) {
+	Nhi = N;
+      } else {
+	Nlo = N - 1;
+      }
+    }
+    return (Nhi);
+  }
+  return (0);
+}
+
+int ibracket (int *list, int Nlist, int mode, double value) {
+
+  int Nlo, Nhi, N;
+
+  if (mode == 0) {
+    Nlo = 0; Nhi = Nlist;
+    while (Nhi - Nlo > 10) {
+      N = 0.5*(Nlo + Nhi);
+      if (list[N] < value) {
+	Nlo = N;
+      } else {
+	Nhi = N + 1;
+      }
+    }
+    return (Nlo);
+  }
+  if (mode == 1) {
+    Nlo = 0; Nhi = Nlist;
+    while (Nhi - Nlo > 10) {
+      N = 0.5*(Nlo + Nhi);
+      if (list[N] > value) {
+	Nhi = N;
+      } else {
+	Nlo = N - 1;
+      }
+    }
+    return (Nhi);
+  }
+  return (0);
+}
Index: /branches/sc_branches/pantasks_condor/lib.data/fft.c
===================================================================
--- /branches/sc_branches/pantasks_condor/lib.data/fft.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/lib.data/fft.c	(revision 34783)
@@ -0,0 +1,304 @@
+# include "data.h"
+
+// fft based on code by Douglas L. Jones (see note at EOF). modified for Ohana C style
+void fft1D (float *x, float *y, int n, int Nbit, int forward) {
+
+  int i,j,k,n1,n2;
+  float c,s,e,a,t1,t2;        
+  double factor;
+         
+  // bit-reverse
+  j = 0; 
+  n2 = n/2;
+  for (i = 1; i < n - 1; i++) {
+    n1 = n2;
+    while ( j >= n1 ) {
+      j -= n1;
+      n1 /= 2;
+    }
+    j += n1;
+               
+    if (i < j) {
+      t1 = x[i];
+      x[i] = x[j];
+      x[j] = t1;
+      t1 = y[i];
+      y[i] = y[j];
+      y[j] = t1;
+    }
+  }
+                                          
+  n1 = 0; /* FFT */
+  n2 = 1;
+                                             
+  if (forward) {
+    factor = +2.0*M_PI;
+  } else {
+    factor = -2.0*M_PI;
+  }
+
+  for (i=0; i < Nbit; i++) {
+    n1 = n2;
+    n2 = n2 + n2;
+    e = factor/n2;
+    a = 0.0;
+                                             
+    for (j=0; j < n1; j++) {
+      c = cos(a);
+      s = sin(a);
+      a = a + e;
+                                            
+      for (k=j; k < n; k=k+n2) {
+	t1 = c*x[k+n1] - s*y[k+n1];
+	t2 = s*x[k+n1] + c*y[k+n1];
+	x[k+n1] = x[k] - t1;
+	y[k+n1] = y[k] - t2;
+	x[k] = x[k] + t1;
+	y[k] = y[k] + t2;
+      }
+    }
+  }
+                                      
+  // re-normalize
+  for (i = 0; i < n; i++) {
+    x[i] /= n;
+    y[i] /= n;
+  }
+
+  return;
+}                          
+
+// This implementation uses the 1-D fft above for each of the vectors in each dimension.
+// This requires 2(Nx*Ny*...) mem copies, but the fft operations are likely to happen in
+// cache.
+int fftND (float *x, float *y, int Ndim, off_t *Nsize, int forward) {
+
+  int i, nIndex, minor, major, iDim;
+  int step, Nmajor, Nminor, Nmax, Ntotal;
+  int *Nbit;
+  float *tmpX, *tmpY;
+
+  ALLOCATE (Nbit, int, Ndim);
+
+  // find the longest axis and allocate storage for that length
+  Nmax = 0;
+  Ntotal = 1;
+  for (i = 0; i < Ndim; i++) {
+    Nmax = MAX(Nmax, Nsize[i]);
+    Ntotal *= Nsize[i];
+    if (!IsBinary (Nsize[i], &Nbit[i])) {
+      free (Nbit);
+      return (FALSE);
+    }
+  }
+  ALLOCATE (tmpX, float, Nmax);
+  ALLOCATE (tmpY, float, Nmax);
+  
+  step = 1;
+  Nminor = 1;
+  Nmajor = Ntotal;
+  for (iDim = 0; iDim < Ndim; iDim++) {
+    step *= Nsize[iDim];
+    Nmajor /= Nsize[iDim];
+
+    // we perform the FFT along all other dimensions 
+    for (major = 0; major < Nmajor; major++) {
+      for (minor = 0; minor < Nminor; minor++) {
+	// nIndex = minor + i*Nminor + major*step;
+	// extract the data values to the temp vector
+	nIndex = minor + major*step;
+	for (i = 0; i < Nsize[iDim]; i++) {
+	  tmpX[i] = x[nIndex];
+	  tmpY[i] = y[nIndex];
+	  nIndex += Nminor;
+	}
+
+	fft1D (tmpX, tmpY, Nsize[iDim], Nbit[iDim], forward);
+
+	// replace the result vectors
+	nIndex = minor + major*step;
+	for (i = 0; i < Nsize[iDim]; i++) {
+	  x[nIndex] = tmpX[i];
+	  y[nIndex] = tmpY[i];
+	  nIndex += Nminor;
+	}
+      }
+    }
+    Nminor *= Nsize[iDim];
+  }
+  free (Nbit);
+  free (tmpX);
+  free (tmpY);
+  return (TRUE);
+}
+
+// fft based on code by Douglas L. Jones (see note at EOF). modified for Ohana C style
+void dfft1D (double *x, double *y, int n, int Nbit, int forward) {
+
+  int i,j,k,n1,n2;
+  double c,s,e,a,t1,t2;        
+  double factor;
+         
+  // bit-reverse
+  j = 0; 
+  n2 = n/2;
+  for (i = 1; i < n - 1; i++) {
+    n1 = n2;
+    while ( j >= n1 ) {
+      j -= n1;
+      n1 /= 2;
+    }
+    j += n1;
+               
+    if (i < j) {
+      t1 = x[i];
+      x[i] = x[j];
+      x[j] = t1;
+      t1 = y[i];
+      y[i] = y[j];
+      y[j] = t1;
+    }
+  }
+                                          
+  n1 = 0; /* FFT */
+  n2 = 1;
+                                             
+  if (forward) {
+    factor = +2.0*M_PI;
+  } else {
+    factor = -2.0*M_PI;
+  }
+
+  for (i=0; i < Nbit; i++) {
+    n1 = n2;
+    n2 = n2 + n2;
+    e = factor/n2;
+    a = 0.0;
+                                             
+    for (j=0; j < n1; j++) {
+      c = cos(a);
+      s = sin(a);
+      a = a + e;
+                                            
+      for (k=j; k < n; k=k+n2) {
+	t1 = c*x[k+n1] - s*y[k+n1];
+	t2 = s*x[k+n1] + c*y[k+n1];
+	x[k+n1] = x[k] - t1;
+	y[k+n1] = y[k] - t2;
+	x[k] = x[k] + t1;
+	y[k] = y[k] + t2;
+      }
+    }
+  }
+                                      
+  // re-normalize
+  for (i = 0; i < n; i++) {
+    x[i] /= n;
+    y[i] /= n;
+  }
+
+  return;
+}                          
+
+// This implementation uses the 1-D fft above for each of the vectors in each dimension.
+// This requires 2(Nx*Ny*...) mem copies, but the fft operations are likely to happen in
+// cache.
+int dfftND (double *x, double *y, int Ndim, int *Nsize, int forward) {
+
+  int i, nIndex, minor, major, iDim;
+  int step, Nmajor, Nminor, Nmax, Ntotal;
+  int *Nbit;
+  double *tmpX, *tmpY;
+
+  ALLOCATE (Nbit, int, Ndim);
+
+  // find the longest axis and allocate storage for that length
+  Nmax = 0;
+  Ntotal = 1;
+  for (i = 0; i < Ndim; i++) {
+    Nmax = MAX(Nmax, Nsize[i]);
+    Ntotal *= Nsize[i];
+    if (!IsBinary (Nsize[i], &Nbit[i])) {
+      free (Nbit);
+      return (FALSE);
+    }
+  }
+  ALLOCATE (tmpX, double, Nmax);
+  ALLOCATE (tmpY, double, Nmax);
+  
+  step = 1;
+  Nminor = 1;
+  Nmajor = Ntotal;
+  for (iDim = 0; iDim < Ndim; iDim++) {
+    step *= Nsize[iDim];
+    Nmajor /= Nsize[iDim];
+
+    // we perform the FFT along all other dimensions 
+    for (major = 0; major < Nmajor; major++) {
+      for (minor = 0; minor < Nminor; minor++) {
+	// nIndex = minor + i*Nminor + major*step;
+	// extract the data values to the temp vector
+	nIndex = minor + major*step;
+	for (i = 0; i < Nsize[iDim]; i++) {
+	  tmpX[i] = x[nIndex];
+	  tmpY[i] = y[nIndex];
+	  nIndex += Nminor;
+	}
+
+	dfft1D (tmpX, tmpY, Nsize[iDim], Nbit[iDim], forward);
+
+	// replace the result vectors
+	nIndex = minor + major*step;
+	for (i = 0; i < Nsize[iDim]; i++) {
+	  x[nIndex] = tmpX[i];
+	  y[nIndex] = tmpY[i];
+	  nIndex += Nminor;
+	}
+      }
+    }
+    Nminor *= Nsize[iDim];
+  }
+  free (Nbit);
+  free (tmpX);
+  free (tmpY);
+  return (TRUE);
+}
+
+// check that a number is binary (2^Nbit).  returns int(log_2(N)) in Nbit
+int IsBinary (int N, int *Nbit) {
+
+  int i, Nset;
+
+  if (Nbit != NULL) *Nbit = 0;
+  Nset = 0;
+  for (i = 0; i < 8*sizeof(int); i++) {
+    if (N & 0x01) {
+      Nset ++;
+      if (Nbit != NULL) *Nbit = i;
+    }
+    N >>= 1;
+  }
+  if (Nset > 1) return (FALSE);
+  return (TRUE);  
+}
+
+/**********************************************************/
+/* fft.c                                                  */
+/* (c) Douglas L. Jones                                   */
+/* University of Illinois at Urbana-Champaign             */
+/* January 19, 1992                                       */
+/*                                                        */
+/*   fft: in-place radix-2 DIT DFT of a complex input     */
+/*                                                        */
+/*   input:                                               */
+/* n: length of FFT: must be a power of two               */
+/* m: n = 2**m                                            */
+/*   input/output                                         */
+/* x: float array of length n with real part of data     */
+/* y: float array of length n with imag part of data     */
+/*                                                        */
+/*   Permission to copy and use this program is granted   */
+/*   under a Creative Commons "Attribution" license       */
+/*   http://creativecommons.org/licenses/by/1.0/          */
+/**********************************************************/
+
Index: /branches/sc_branches/pantasks_condor/lib.data/gaussian.c
===================================================================
--- /branches/sc_branches/pantasks_condor/lib.data/gaussian.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/lib.data/gaussian.c	(revision 34783)
@@ -0,0 +1,75 @@
+# include "data.h"
+
+static int Ngaussint = 0;
+static double *gaussint;
+
+extern double drand48();
+
+double gaussian (double x, double mean, double sigma) {
+
+  double f;
+
+  f = exp (-0.5 * SQ(x - mean) / SQ(sigma)) / sqrt(2 * M_PI * SQ(sigma));
+
+  return (f);
+
+}
+
+/* integrate a gaussian from -5 sigma to +5 sigma */
+void gauss_init (int Nbin) {
+ 
+  int i;
+  long A, B;
+  double val, x, dx, dx1, dx2, dx3, df;
+  double mean, sigma;
+ 
+  /* no need to generate this if it already exists */
+  if (Ngaussint == Nbin) return;
+
+  A = time(NULL);
+  for (B = 0; A == time(NULL); B++);
+  srand48(B);
+ 
+  Ngaussint = Nbin;
+  ALLOCATE (gaussint, double, Ngaussint + 1);
+
+  val = 0;
+  dx = 1.0 / Ngaussint;
+  dx1 = dx / 3.0;
+  dx2 = 2.0*dx/3.0;
+  dx3 = dx;
+  mean = 0.0;
+  sigma = 1.0;
+ 
+  for (i = 0, x = -7.0; (i < Ngaussint) && (x < 7.0); x += dx)  {
+    df = (3.0*gaussian(x    , mean, sigma) + 
+          9.0*gaussian(x+dx1, mean, sigma) +
+          9.0*gaussian(x+dx2, mean, sigma) + 
+          3.0*gaussian(x+dx3, mean, sigma)) * (dx1/8.0);
+    val += df;
+    if (val > (i + 0.5) / (double) Ngaussint) {
+      gaussint[i] = x + dx / 2.0;
+      i++;
+    }
+  }
+}
+
+double rnd_gauss (double mean, double sigma) {
+ 
+  int i;
+  double y;
+ 
+  y = drand48();
+  i = Ngaussint*y;
+  y = gaussint[i]*sigma + mean;
+ 
+  return (y);
+ 
+}
+ 
+double int_gauss (int i) {
+  double y;
+  y = gaussint[i];
+  return (y);
+}
+ 
Index: /branches/sc_branches/pantasks_condor/lib.data/graphtools.c
===================================================================
--- /branches/sc_branches/pantasks_condor/lib.data/graphtools.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/lib.data/graphtools.c	(revision 34783)
@@ -0,0 +1,111 @@
+# include "data.h"
+
+// XXX need to select the active vector for the range analysis
+// this function accepts either FLT or INT vectors
+void SetLimits (Vector *xvec, Vector *yvec, Graphdata *graphmode) {
+
+  double maxX, minX, maxY, minY, range;
+  int i;
+
+  if (xvec != NULL) {
+    if (xvec->type == OPIHI_FLT) {
+      maxX = minX = xvec[0].elements.Flt[0];
+      for (i = 1; i < xvec[0].Nelements; i++) {
+	if (!finite(xvec[0].elements.Flt[i])) continue;
+	maxX = MAX (maxX, xvec[0].elements.Flt[i]);
+	minX = MIN (minX, xvec[0].elements.Flt[i]);
+      }
+    } else {
+      maxX = minX = xvec[0].elements.Int[0];
+      for (i = 1; i < xvec[0].Nelements; i++) {
+	if (!finite(xvec[0].elements.Int[i])) continue;
+	maxX = MAX (maxX, xvec[0].elements.Int[i]);
+	minX = MIN (minX, xvec[0].elements.Int[i]);
+      }
+    }
+    range = maxX - minX;
+    if (range == 0) range = 0.001 * maxX;
+    if (range == 0) range = 0.001;
+    graphmode[0].xmin = minX - 0.05*range;
+    graphmode[0].xmax = maxX + 0.05*range;
+  }
+
+  if (yvec != NULL) {
+    if (yvec->type == OPIHI_FLT) {
+      maxY = minY = yvec[0].elements.Flt[0];
+      for (i = 1; i < yvec[0].Nelements; i++) {
+	if (!finite(yvec[0].elements.Flt[i])) continue;
+	maxY = MAX (maxY, yvec[0].elements.Flt[i]);
+	minY = MIN (minY, yvec[0].elements.Flt[i]);
+      }
+    } else {
+      maxY = minY = yvec[0].elements.Int[0];
+      for (i = 1; i < yvec[0].Nelements; i++) {
+	if (!finite(yvec[0].elements.Int[i])) continue;
+	maxY = MAX (maxY, yvec[0].elements.Int[i]);
+	minY = MIN (minY, yvec[0].elements.Int[i]);
+      }
+    }
+    range = maxY - minY;
+    if (range == 0) range = 0.0011 * maxY;
+    if (range == 0) range = 0.0011;
+    graphmode[0].ymin = minY - 0.05*range;
+    graphmode[0].ymax = maxY + 0.05*range;
+  }
+  SetGraph (graphmode);
+
+  set_variable ("XMIN", graphmode[0].xmin);
+  set_variable ("XMAX", graphmode[0].xmax);
+  set_variable ("YMIN", graphmode[0].ymin);
+  set_variable ("YMAX", graphmode[0].ymax);
+
+  set_variable ("KAPA_XMIN", graphmode[0].xmin);
+  set_variable ("KAPA_XMAX", graphmode[0].xmax);
+  set_variable ("KAPA_YMIN", graphmode[0].ymin);
+  set_variable ("KAPA_YMAX", graphmode[0].ymax);
+}
+
+void SetLimitsRaw (float *xvec, float *yvec, int Nelements, Graphdata *graphmode) {
+
+  double maxX, minX, maxY, minY, range;
+  int i;
+
+  if (xvec != NULL) {
+    maxX = minX = xvec[0];
+    for (i = 1; i < Nelements; i++) {
+      if (!finite(xvec[i])) continue;
+      maxX = MAX (maxX, xvec[i]);
+      minX = MIN (minX, xvec[i]);
+    }
+    range = maxX - minX;
+    if (range == 0) range = 0.001 * maxX;
+    if (range == 0) range = 0.001;
+    graphmode[0].xmin = minX - 0.05*range;
+    graphmode[0].xmax = maxX + 0.05*range;
+  }
+
+  if (yvec != NULL) {
+    maxY = minY = yvec[0];
+    for (i = 1; i < Nelements; i++) {
+      if (!finite(yvec[i])) continue;
+      maxY = MAX (maxY, yvec[i]);
+      minY = MIN (minY, yvec[i]);
+    }
+    range = maxY - minY;
+    if (range == 0) range = 0.0011 * maxY;
+    if (range == 0) range = 0.0011;
+    graphmode[0].ymin = minY - 0.05*range;
+    graphmode[0].ymax = maxY + 0.05*range;
+  }
+  SetGraph (graphmode);
+
+  set_variable ("KAPA_XMIN", graphmode[0].xmin);
+  set_variable ("KAPA_XMAX", graphmode[0].xmax);
+  set_variable ("KAPA_YMIN", graphmode[0].ymin);
+  set_variable ("KAPA_YMAX", graphmode[0].ymax);
+
+  set_variable ("XMIN", graphmode[0].xmin);
+  set_variable ("XMAX", graphmode[0].xmax);
+  set_variable ("YMIN", graphmode[0].ymin);
+  set_variable ("YMAX", graphmode[0].ymax);
+}
Index: /branches/sc_branches/pantasks_condor/lib.data/hashes.c
===================================================================
--- /branches/sc_branches/pantasks_condor/lib.data/hashes.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/lib.data/hashes.c	(revision 34783)
@@ -0,0 +1,49 @@
+
+/* I need a structure which will be appropriate to carry the psMetadataConfig data.  the main 
+   requirements are:
+
+   - name-based components
+   - string-indexing on columns
+
+   basic elements:
+
+   container->item->element->value
+
+   element: name, value (type?)
+   item->name, Nelement, elements
+   container->items, Nitems
+
+   user interactions
+
+   blob list
+   blob listitems (blob)
+   blob create (blob)
+   blob delete (blob)
+   blob listkeys (blob.item)
+
+   blob getvalue (blob.item.key) -var word
+   blob setvalue (blob.item.key) value
+
+   blob getitem (blob.item)  : list all key/value pairs
+   blob newitem (blob.item)
+   blob delitem (blob.item)
+   blob popitem (blob)
+
+   blob readqueue (queue)    : convert queue in MDC format to blob
+   *** this needs to be able to match by keys against existing items
+   
+   need equivalents to:
+   queuepush -uniq key
+   queuepop -
+   queuesize
+   
+
+   items should be sorted by name so we can lookup an item quickly
+
+
+
+   other related opihi data concepts
+   
+   $a = @function (output of function set to value?)
+
+*/
Index: /branches/sc_branches/pantasks_condor/lib.data/hermitian.c
===================================================================
--- /branches/sc_branches/pantasks_condor/lib.data/hermitian.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/lib.data/hermitian.c	(revision 34783)
@@ -0,0 +1,120 @@
+# include "data.h"
+
+double hermitian_polynomial (double x, int order) {
+  double value;
+    switch (order) {
+      case 0:
+	value = hermitian_00(x);
+	break;
+      case 1:
+	value = hermitian_01(x);
+	break;
+      case 2:
+	value = hermitian_02(x);
+	break;
+      case 3:
+	value = hermitian_03(x);
+	break;
+      case 4:
+	value = hermitian_04(x);
+	break;
+      case 5:
+	value = hermitian_05(x);
+	break;
+      case 6:
+	value = hermitian_06(x);
+	break;
+      case 7:
+	value = hermitian_07(x);
+	break;
+      case 8:
+	value = hermitian_08(x);
+	break;
+      case 9:
+	value = hermitian_09(x);
+	break;
+      case 10:
+	value = hermitian_10(x);
+	break;
+      default:
+	value = NAN;
+	break;
+    }
+    return value;
+}
+
+double hermitian_00(double x) {
+    double value;
+    // H_0(x) = 1
+    value = 1;
+    return value;
+}
+double hermitian_01(double x) {
+    double value;
+    // H_1(x) = x
+    value = x;
+    return value;
+}
+double hermitian_02(double x) {
+    double value, x2;
+    // H_2(x) = x^2-1
+    x2 = x*x;
+    value = x2 - 1.0;
+    return value;
+}
+double hermitian_03(double x) {
+    double value, x2;
+    // H_3(x) = x^3-3x
+    x2 = x*x;
+    value = x*(x2 - 3.0);
+    return value;
+}
+double hermitian_04(double x) {
+    double value, x2;
+    // H_4(x) = x^4-6x^2+3
+    x2 = x*x;
+    value = (x2 - 6.0)*x2 + 3.0;
+    return value;
+}
+double hermitian_05(double x) {
+    double value, x2;
+    // H_5(x) = x^5-10x^3+15x
+    x2 = x*x;
+    value = ((x2 - 10.0)*x2 + 15.0)*x;
+    return value;
+}
+double hermitian_06(double x) {
+    double value, x2;
+    // H_6(x) = x^6-15x^4+45x^2-15
+    x2 = x*x;
+    value = (((x2 - 15.0)*x2 + 45.0)*x2) - 15.0;
+    return value;
+}
+double hermitian_07(double x) {
+    double value, x2;
+    // H_7(x) = x^7-21x^5+105x^3-105x
+    x2 = x*x;
+    value = (((x2 - 21.0)*x2+105.0)*x2 - 105.0)*x;
+    return value;
+}
+double hermitian_08(double x) {
+    double value, x2;
+    // H_8(x) = x^8-28x^6+210x^4-420x^2+105
+    x2 = x*x;
+    value = ((((x2 - 28.0)*x2 + 210.0)*x2 - 420.0)*x2 + 105.0);
+    return value;
+}
+double hermitian_09(double x) {
+    double value, x2;
+    // H_9(x) = x^9-36x^7+378x^5-1260x^3+945x
+    x2 = x*x;
+    value = ((((x2 - 36.0)*x2 + 378.0)*x2 - 1260.0)*x2 + 945.0)*x;
+    return value;
+}
+double hermitian_10(double x) {
+    double value, x2;
+    // H_{10}(x) = x^{10}-45x^8+630x^6-3150x^4+4725x^2-945 
+    x2 = x*x;
+    value = (((((x2 - 45.0)*x2 + 630.0)*x2 - 3150.0)*x2 + 4725.0)*x2 - 945.0);
+    return value;
+}
Index: /branches/sc_branches/pantasks_condor/lib.data/mrq2dmin.c
===================================================================
--- /branches/sc_branches/pantasks_condor/lib.data/mrq2dmin.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/lib.data/mrq2dmin.c	(revision 34783)
@@ -0,0 +1,210 @@
+# include "data.h"  /* only needed for the ALLOCATE def */
+
+/* need to pass in a function of the form:
+   funcs (x, t, a, Npar, dy/da) 
+   returns y (x,t) for Npar parameters a along with dy/da at (x,t)
+   dy carries 1/sig^2 
+*/
+
+# define VERY_VERBOSE 0
+
+static opihi_flt **alpha, **talpha;
+static opihi_flt **beta, **tbeta;
+static opihi_flt *partry, *dyda;
+static opihi_flt ochisq, lambda;
+
+static opihi_flt *parmin = NULL;
+static opihi_flt *parmax = NULL;
+
+opihi_flt mrq2dcof (opihi_flt *x, opihi_flt *t, opihi_flt *y, opihi_flt *dy, int Npts, 
+	      opihi_flt *par, int Npar, opihi_flt **ta, opihi_flt **tb, 
+	      opihi_flt (funcs)(opihi_flt, opihi_flt, opihi_flt *, int, opihi_flt *)) {
+
+  int k, j, i;
+  opihi_flt ydiff, wt, chisq;
+
+  for (j = 0; j < Npar; j++) {
+    for (k = 0; k <= j; k++) ta[j][k] = 0.0;
+    tb[j][0] = 0.0;
+  }
+
+  chisq = 0.0;
+  for (i = 0; i < Npts; i++) {
+
+    ydiff = funcs (x[i], t[i], par, Npar, dyda) - y[i];
+    chisq += SQ(ydiff) * dy[i];
+    
+    for (j = 0; j < Npar; j++) {
+      wt = dyda[j] * dy[i];
+      for (k = 0; k <= j; k++) ta[j][k] += wt * dyda[k];
+      tb[j][0] += wt * ydiff;
+    }
+  }
+
+  for (j = 1; j < Npar; j++)
+    for (k = 0; k < j; k++)
+      ta[k][j] = ta[j][k];
+      
+# if (VERY_VERBOSE)
+  for (j = 0; j < Npar; j++) {
+    for (k = 0; k < Npar; k++) {
+      gprint (GP_ERR, "%9.3e  ", ta[j][k]);
+    }
+    gprint (GP_ERR, "    :   %9.3e  ", tb[j][0]);
+    gprint (GP_ERR, "\n");
+  }
+# endif
+
+  return (chisq);
+
+}
+
+opihi_flt mrq2dchi (opihi_flt *x, opihi_flt *t, opihi_flt *y, opihi_flt *dy, int Npts, 
+		opihi_flt *par, int Npar, 
+		opihi_flt (funcs)(opihi_flt, opihi_flt, opihi_flt *, int, opihi_flt *)) {
+
+  int i;
+  opihi_flt ydiff, chisq;
+
+  chisq = 0.0;
+  for (i = 0; i < Npts; i++) {
+    ydiff = funcs (x[i], t[i], par, Npar, dyda) - y[i];
+    chisq += SQ(ydiff) * dy[i];
+  }
+  return (chisq);
+}
+
+opihi_flt mrq2dmin (opihi_flt *x, opihi_flt *t, opihi_flt *y, opihi_flt *dy, int Npts, 
+	      opihi_flt *par, int Npar, 
+	      opihi_flt (funcs)(opihi_flt, opihi_flt, opihi_flt *, int, opihi_flt *), int VERBOSE) {
+
+  int j, k;
+  opihi_flt chisq;
+
+  /* set up test matrixes for this run */
+  for (j = 0; j < Npar; j++) {
+    for (k = 0; k < Npar; k++) talpha[j][k] = alpha[j][k];
+    talpha[j][j] = alpha[j][j] * (1.0 + lambda);
+    tbeta[j][0] = beta[j][0];
+  }
+
+  /* keep this test in here? */
+  if (!dgaussjordan (talpha, tbeta, Npar, 1)) {
+    lambda *= 10.0;
+    return (ochisq);
+  }
+
+  for (j = 0; j < Npar; j++) {
+    partry[j] = par[j] - tbeta[j][0];
+    /*
+    if (parmin != NULL) partry[j] = MAX (parmin[j], partry[j]);
+    if (parmax != NULL) partry[j] = MIN (parmax[j], partry[j]);
+    */
+  }
+
+  chisq = mrq2dcof (x, t, y, dy, Npts, partry, Npar, talpha, tbeta, funcs);
+  if (VERBOSE) { 
+    gprint (GP_ERR, "chisq: %f  ", chisq);
+    gprint (GP_ERR, "lambda: %f  ", lambda);
+    for (j = 0; j < Npar; j++) {
+      gprint (GP_ERR, "%f ", partry[j]);
+    }
+    gprint (GP_ERR, "\n");
+  }
+
+  /* if good, save temp values */
+  if (chisq < ochisq) {
+    lambda *= 0.1;
+    ochisq = chisq;
+    for (j = 0; j < Npar; j++) {
+      for (k = 0; k < Npar; k++) alpha[j][k] = talpha[j][k];
+      beta[j][0] = tbeta[j][0];
+      par[j] = partry[j];
+    }
+  } else {
+    lambda *= 10.0;
+    chisq = ochisq;
+  }
+
+  return (chisq);
+
+}
+
+int mrq2dlimits (opihi_flt *pmin, opihi_flt *pmax, int Npar) {
+
+  int i;
+
+  ALLOCATE (parmin, opihi_flt, Npar);
+  ALLOCATE (parmax, opihi_flt, Npar);
+  for (i = 0; i < Npar; i++) {
+    parmin[i] = pmin[i];
+    parmax[i] = pmax[i];
+  }
+  return (TRUE);
+}
+
+opihi_flt mrq2dinit (opihi_flt *x, opihi_flt *t, opihi_flt *y, opihi_flt *dy, int Npts, 
+	      opihi_flt *par, int Npar, 
+	      opihi_flt (funcs)(opihi_flt, opihi_flt, opihi_flt *, int, opihi_flt *), int VERBOSE) {
+
+  int i;
+
+  ALLOCATE (dyda, opihi_flt, Npar);
+  ALLOCATE (partry, opihi_flt, Npar);
+  ALLOCATE (alpha, opihi_flt *, Npar);
+  ALLOCATE (beta, opihi_flt *, Npar);
+  ALLOCATE (talpha, opihi_flt *, Npar);
+  ALLOCATE (tbeta, opihi_flt *, Npar);
+  for (i = 0; i < Npar; i++) {
+    ALLOCATE (alpha[i], opihi_flt, Npar);
+    ALLOCATE (beta[i], opihi_flt, Npar);
+    ALLOCATE (talpha[i], opihi_flt, Npar);
+    ALLOCATE (tbeta[i], opihi_flt, Npar);
+  }
+
+  
+  lambda = 0.001;
+  
+  ochisq = mrq2dcof (x, t, y, dy, Npts, par, Npar, alpha, beta, funcs);
+  if (VERBOSE) {
+    gprint (GP_ERR, "chisq: %f  ", ochisq);
+    gprint (GP_ERR, "lambda: %f  ", lambda);
+    for (i = 0; i < Npar; i++) {
+      gprint (GP_ERR, "%f ", par[i]);
+    }
+    gprint (GP_ERR, "\n");
+  }
+
+  return (ochisq);
+
+}
+
+/* don't invoke this in the middle of a run, only near the end */ 
+opihi_flt **mrq2dcovar (int Npar) {
+
+  dgaussjordan (alpha, beta, Npar, 1);
+  return (alpha);
+
+} 
+
+void mrq2dfree (int Npar) {
+
+  int i;
+
+  for (i = 0; i < Npar; i++) {
+    free (alpha[i]);
+    free (talpha[i]);
+    free (beta[i]);
+    free (tbeta[i]);
+  }
+  free (alpha);
+  free (talpha);
+  free (beta);
+  free (tbeta);
+  free (partry);
+  free (dyda);
+
+  if (parmin != NULL) free (parmin);
+  if (parmax != NULL) free (parmax);
+
+}
Index: /branches/sc_branches/pantasks_condor/lib.data/mrqmin.c
===================================================================
--- /branches/sc_branches/pantasks_condor/lib.data/mrqmin.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/lib.data/mrqmin.c	(revision 34783)
@@ -0,0 +1,162 @@
+# include "data.h"  /* only needed for the ALLOCATE def */
+
+/* need to pass in a function of the form:
+   funcs (x, a, Npar, dyda) 
+   returns f (x) for Npar parameters a, also df/da at x 
+   dy carries 1/sig^2 
+*/
+
+static opihi_flt **alpha, **talpha;
+static opihi_flt **beta, **tbeta;
+static opihi_flt *partry, *dyda;
+static opihi_flt ochisq, lambda;
+
+opihi_flt mrqcof (opihi_flt *x, opihi_flt *y, opihi_flt *dy, int Npts, 
+	      opihi_flt *par, int Npar, opihi_flt **ta, opihi_flt **tb, 
+	      opihi_flt (funcs)(opihi_flt, opihi_flt *, int, opihi_flt *)) {
+
+  int k, j, i;
+  opihi_flt ymodel, ydiff, wt, chisq;
+
+  for (j = 0; j < Npar; j++) {
+    for (k = 0; k <= j; k++) ta[j][k] = 0.0;
+    tb[j][0] = 0.0;
+  }
+
+  chisq = 0.0;
+  for (i = 0; i < Npts; i++) {
+
+    ymodel = funcs (x[i], par, Npar, dyda);
+    ydiff = ymodel - y[i];
+    chisq += SQ(ydiff) * dy[i];
+
+    // fprintf (stderr, "%f %f - %f : %f -> %f\n", x[i], y[i], ymodel, dy[i], chisq);
+
+    for (j = 0; j < Npar; j++) {
+      wt = dyda[j] * dy[i];
+      for (k = 0; k <= j; k++) ta[j][k] += wt * dyda[k];
+      tb[j][0] += wt * ydiff;
+    }
+  }
+
+  for (j = 1; j < Npar; j++)
+    for (k = 0; k < j; k++) 
+      ta[k][j] = ta[j][k];
+
+  return (chisq);
+
+}
+
+opihi_flt mrqmin (opihi_flt *x, opihi_flt *y, opihi_flt *dy, int Npts, 
+	      opihi_flt *par, int Npar, 
+	      opihi_flt (funcs)(opihi_flt, opihi_flt *, int, opihi_flt *), int VERBOSE) {
+
+  int j, k;
+  opihi_flt chisq;
+  opihi_flt rho, dX, dL;
+
+  /* set up test matrixes for this run */
+  for (j = 0; j < Npar; j++) {
+    for (k = 0; k < Npar; k++) talpha[j][k] = alpha[j][k];
+    talpha[j][j] = alpha[j][j] * (1.0 + lambda);
+    tbeta[j][0] = beta[j][0];
+  }
+
+  dgaussjordan (talpha, tbeta, Npar, 1);
+
+  for (j = 0; j < Npar; j++) partry[j] = par[j] - tbeta[j][0];
+
+  /* get linear model prediction */
+  dL = 0;
+  for (j = 0; j < Npar; j++) {
+      dL += 0.5*lambda*SQ(tbeta[j][0]) + tbeta[j][0]*beta[j][0];
+  }
+
+  chisq = mrqcof (x, y, dy, Npts, partry, Npar, talpha, tbeta, funcs);
+  if (VERBOSE) { 
+    gprint (GP_ERR, "chisq: %f  ", chisq);
+    gprint (GP_ERR, "lambda: %f  ", lambda);
+    for (j = 0; j < Npar; j++) {
+      gprint (GP_ERR, "%f ", partry[j]);
+    }
+    gprint (GP_ERR, "\n");
+  }
+
+  /* compare linear model with actual */
+  dX = ochisq - chisq;
+  rho = dX / dL;
+
+  /* if good, save temp values */
+  if ((chisq > 1e-3) && (rho > -1e-6)) {
+    lambda *= 0.1;
+    ochisq = chisq;
+    for (j = 0; j < Npar; j++) {
+      for (k = 0; k < Npar; k++) alpha[j][k] = talpha[j][k];
+      beta[j][0] = tbeta[j][0];
+      par[j] = partry[j];
+    }
+  } else {
+    lambda *= 10.0;
+    chisq = ochisq;
+  }
+
+  return (chisq);
+
+}
+
+opihi_flt mrqinit (opihi_flt *x, opihi_flt *y, opihi_flt *dy, int Npts, 
+	      opihi_flt *par, int Npar, 
+	      opihi_flt (funcs)(opihi_flt, opihi_flt *, int, opihi_flt *), int VERBOSE) {
+
+  int i;
+
+  ALLOCATE (dyda, opihi_flt, Npar);
+  ALLOCATE (partry, opihi_flt, Npar);
+  ALLOCATE (alpha, opihi_flt *, Npar);
+  ALLOCATE (beta, opihi_flt *, Npar);
+  ALLOCATE (talpha, opihi_flt *, Npar);
+  ALLOCATE (tbeta, opihi_flt *, Npar);
+  for (i = 0; i < Npar; i++) {
+    ALLOCATE (alpha[i], opihi_flt, Npar);
+    ALLOCATE (beta[i], opihi_flt, Npar);
+    ALLOCATE (talpha[i], opihi_flt, Npar);
+    ALLOCATE (tbeta[i], opihi_flt, Npar);
+  }
+  
+  lambda = 0.01;
+  
+  ochisq = mrqcof (x, y, dy, Npts, par, Npar, alpha, beta, funcs);
+  if (VERBOSE) {
+    gprint (GP_ERR, "chisq: %f  ", ochisq);
+    gprint (GP_ERR, "lambda: %f  ", lambda);
+    for (i = 0; i < Npar; i++) {
+      gprint (GP_ERR, "%f ", par[i]);
+    }
+    gprint (GP_ERR, "\n");
+  }
+  return (ochisq);
+}
+
+/* don't invoke this in the middle of a run, only near the end */ 
+opihi_flt **mrqcovar (int Npar) {
+  dgaussjordan (alpha, beta, Npar, 1);
+  return (alpha);
+} 
+
+void mrqfree (int Npar) {
+
+  int i;
+
+  for (i = 0; i < Npar; i++) {
+    free (alpha[i]);
+    free (talpha[i]);
+    free (beta[i]);
+    free (tbeta[i]);
+  }
+  free (alpha);
+  free (talpha);
+  free (beta);
+  free (tbeta);
+  free (partry);
+  free (dyda);
+}
Index: /branches/sc_branches/pantasks_condor/lib.data/open_kapa.c
===================================================================
--- /branches/sc_branches/pantasks_condor/lib.data/open_kapa.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/lib.data/open_kapa.c	(revision 34783)
@@ -0,0 +1,292 @@
+# include "display.h"
+# include "shell.h"
+# include <assert.h>
+
+
+/* kapa support for the new version of kapa (v2.0), which has both graph and image
+ * elements merged into a single display device.  The user may now open an arbitrary
+ * number of kapa windows, and the display information is retrieved from kapa across the
+ * socket when it is needed.  Communication is now via an INET socket (not a UNIX socket).
+ */
+
+/* list of available socket connections */
+static int        Active;        // currently active socket entry (index value, not socket value)
+static int       *Socket = NULL; // list of available sockets
+static char     **Device = NULL; // list of device names for each socket
+static int       Ndevice = 0;    // number of available sockets / devices
+
+void InitKapa () {
+
+  Active  = -1;					  // -1 is the INVALID entry
+  Ndevice = 0;					  // number of defined sockets
+  ALLOCATE (Device, char *, 1);			  // for future REALLOCATE calls
+  ALLOCATE (Socket, int, 1);			  // for future REALLOCATE calls
+}
+
+void FreeKapa () {
+  
+  free (Device);
+  free (Socket);
+}
+
+// add new device name if not found
+int AddKapaDevice (char *name) {
+
+  int N;
+
+  N = FindKapaDevice (name);
+  if (N != -1) return (N);
+  N = Ndevice;
+  Ndevice ++;
+  REALLOCATE (Device, char *, Ndevice);
+  REALLOCATE (Socket, int, Ndevice);
+  Device[N] = strcreate (name);
+  Socket[N] = -1;
+  return (N);
+}
+
+// delete device by name, close if not closed
+int DelKapaDevice (char *name) {
+
+  int i, N;
+
+  N = FindKapaDevice (name);
+  if (N == -1) return (FALSE);
+
+  if (Socket[N] != -1) close (Socket[N]);
+  free (Device[N]);
+  for (i = N; i < Ndevice - 1; i++) {
+    Device[i] = Device[i+1];
+    Socket[i] = Socket[i+1];
+  }
+
+  if (N == Active) {
+    Active = -1;
+  }
+
+  Ndevice --;
+  REALLOCATE (Device, char *, Ndevice);
+  REALLOCATE (Socket, int, Ndevice);
+
+  return (TRUE);
+}
+
+// returns the entry of the requested device, or -1 if not found
+int FindKapaDevice (char *name) {
+
+  int i;
+
+  if (name == NULL) return (-1); 
+
+  for (i = 0; i < Ndevice; i++) {
+    if (!strcmp(Device[i], name)) return (i);
+  }
+  return (-1);
+}
+
+// set the active device to the given device, open if needed
+int open_kapa (int entry) {
+
+  int fd;
+  char *kapa_exec, *kapa_name;
+
+  // find the given device number, or create. set this to active
+  assert (entry >= 0);
+  assert (entry <  Ndevice);
+
+  // if the (now) active socket is not open, open it
+  if (Socket[entry] < 0) {
+    kapa_exec = get_variable ("KAPA");
+    if (kapa_exec == (char *) NULL) {
+      gprint (GP_ERR, "variable KAPA not found\n");
+      return (FALSE);
+    }
+
+    // KAPA may be either kapa://host or /path/to/program
+    ALLOCATE (kapa_name, char, strlen(Device[entry]) + 5);
+    snprintf (kapa_name, strlen(Device[entry]) + 5, "[%s]", Device[entry]);
+
+    if (!strncmp (kapa_exec, "unix://", 7)) {
+        fd = KapaOpenNamedSocket (&kapa_exec[7], kapa_name);
+    } else {
+	fd = KapaOpen (kapa_exec, kapa_name);
+    }
+
+    free (kapa_exec);
+    free (kapa_name);
+
+    if (fd < 0) {
+      gprint (GP_ERR, "error starting kapa device %s\n", Device[entry]);
+      return (FALSE);
+    } 
+    Socket[entry] = fd;
+  } 
+  Active = entry;
+  return (TRUE);
+}
+
+/**************** graph specific ops *******************/
+
+// return the current device name, if set 
+char *GetKapaName () {
+  if (Active < 0) return NULL;
+  return Device[Active];
+}
+
+/* return pointers for named device or current; open if needed */
+// if fd == NULL, don't return the value
+// if name == NULL, use the currently active device
+int GetGraph (Graphdata *data, int *fd, char *name) {
+
+  int entry;
+
+  if (name == NULL) {
+    if (Active < 0) {
+      entry = AddKapaDevice ("0");
+    } else {
+      entry = Active;
+    }
+  } else {
+    entry = AddKapaDevice (name);
+  }
+  
+  if (!open_kapa (entry)) {
+    return (FALSE);
+  }
+  
+  if (data != NULL) KapaGetGraphData (Socket[Active], data);
+  if (fd != NULL) *fd = Socket[Active];
+
+  return (TRUE);
+}
+
+/* return pointers for given kapa, don't set or open */
+int GetGraphdata (Graphdata *data, int *fd, char *name) {
+
+  int entry;
+
+  if (name == NULL) {
+    if (Active < 0) {
+      gprint (GP_ERR, "no active kapa window\n"); 
+      return (FALSE);
+    }
+    entry = Active;
+  } else {
+    entry = FindKapaDevice (name);
+    if (entry < 0) {
+      gprint (GP_ERR, "invalid kapa window %s\n", name); 
+      return (FALSE);
+    }
+  }
+
+  if (fd != NULL) *fd = Socket[entry];
+  if (data != NULL) KapaGetGraphData (Socket[entry], data);
+  return (TRUE);
+}
+
+/* assign given values to current kapa */
+int SetGraph (Graphdata *data) {
+  if (Active < 0) {
+    gprint (GP_ERR, "no active kapa window\n"); 
+    return (FALSE);
+  }
+  if (Socket[Active] == -1) {
+    gprint (GP_ERR, "no active kapa window\n"); 
+    return (FALSE);
+  }
+  KapaSetGraphData (Socket[Active], data);
+  return (TRUE);
+}
+
+/************* image ops ***********/
+
+/* return pointers for current Ximage, set if desired, test, open if needed */
+int GetImage (KapaImageData *data, int *fd, char *name) {
+
+  int entry;
+
+  if (name == NULL) {
+    if (Active < 0) {
+      entry = AddKapaDevice ("0");
+    } else {
+      entry = Active;
+    }
+  } else {
+    entry = AddKapaDevice (name);
+  }
+  
+  if (!open_kapa (entry)) {
+    return (FALSE);
+  }
+  
+  if (data != NULL) KapaGetImageData (Socket[Active], data);
+  if (fd != NULL) *fd = Socket[Active];
+
+  return (TRUE);
+}
+
+/* return pointers for given kapa, don't set or open */
+int GetImageData (KapaImageData *data, int *fd, char *name) {
+
+  int entry;
+
+  if (name == NULL) {
+    if (Active < 0) {
+      gprint (GP_ERR, "no active kapa window\n"); 
+      return (FALSE);
+    }
+    entry = Active;
+  } else {
+    entry = FindKapaDevice (name);
+    if (entry < 0) {
+      gprint (GP_ERR, "invalid kapa window %s\n", name); 
+      return (FALSE);
+    }
+  }
+
+  if (fd != NULL) *fd = Socket[entry];
+  if (data != NULL) KapaGetImageData (Socket[entry], data);
+  return (TRUE);
+}
+
+/* assign given values to current kapa */
+int SetImage (KapaImageData *data) {
+  if (Active < 0) {
+    gprint (GP_ERR, "no active kapa window\n"); 
+    return (FALSE);
+  }
+  if (Socket[Active] == -1) {
+    gprint (GP_ERR, "no active kapa window\n"); 
+    return (FALSE);
+  }
+  KapaSetImageData (Socket[Active], data);
+  return (TRUE);
+}
+
+int close_kapa (char *name) {
+
+  int N;
+
+  N = FindKapaDevice (name);
+  if (N == -1) {
+    if (Active < 0) return (FALSE);
+    name = Device[Active];
+  }
+  DelKapaDevice (name);
+  return (TRUE);
+}
+
+void QuitKapa () {
+
+  int i;
+  
+  for (i = 0; i < Ndevice; i++) {
+    if (Socket[i] != -1) close (Socket[i]);
+    if (Device[i] != NULL) free (Device[i]);
+  }
+  REALLOCATE (Socket, int, 1);
+  REALLOCATE (Device, char *, 1);
+
+  Ndevice = 0;
+  Active = -1;
+}
Index: /branches/sc_branches/pantasks_condor/lib.data/page.c
===================================================================
--- /branches/sc_branches/pantasks_condor/lib.data/page.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/lib.data/page.c	(revision 34783)
@@ -0,0 +1,244 @@
+# include "data.h"
+
+void InitPage (Page *page, char *name) {
+
+    page[0].name = strcreate (name);
+
+    page[0].Nwords = 0;
+    page[0].NWORDS = 16;
+    ALLOCATE (page[0].words, char *, page[0].NWORDS);
+    ALLOCATE (page[0].value, char *, page[0].NWORDS);
+}
+
+void FreePage (Page *page) {
+
+    int i;
+
+    free (page[0].name);
+    for (i = 0; i < page[0].Nwords; i++) {
+      free (page[0].words[i]);
+      free (page[0].value[i]);
+    }
+    free (page[0].words);
+    free (page[0].value);
+    free (page);
+}
+
+/* return the given page */
+Page *GetPage (Book *book, int where) {
+
+  if (where < 0) where += book[0].Npages;
+  if (where < 0) return NULL;
+  if (where >= book[0].Npages) return NULL;
+  return (book[0].pages[where]);
+}
+
+/* return the given page with key restrictions */
+Page *GetPageRestricted (Book *book, int where, char *keyName, char *keyValue) {
+
+  int i;
+  int N, Nout;
+  char *value;
+
+  if (where < 0) where += book[0].Npages;
+  if (where < 0) return NULL;
+  if (where >= book[0].Npages) return NULL;
+
+  Nout = -1;
+  if (where >= 0) {
+    N = -1;
+    for (i = 0; (i < book[0].Npages) && (N < where); i++) {
+      value = BookGetWord (book[0].pages[i], keyName);
+      if ((value == NULL) && !strcmp (keyValue, "NULL")) {
+	N++;
+	Nout = i;
+      } 
+      if ((value != NULL) && !strcmp (keyValue, value)) {
+	N++;
+	Nout = i;
+      }
+    }
+  } else {
+    N = 0;
+    for (i = book[0].Npages - 1; (i >= 0) && (N > where); i--) {
+      value = BookGetWord (book[0].pages[i], keyName);
+      if ((value == NULL) && !strcmp (keyValue, "NULL")) {
+	N--;
+      } 
+      if ((value != NULL) && !strcmp (keyValue, value)) {
+	N--;
+      }
+    }
+  }
+
+  if (N != where) return NULL;
+
+  return (book[0].pages[Nout]);
+}
+
+/* return the given page */
+/* XXX use index to find more quickly */
+Page *FindPage (Book *book, char *name) {
+
+  int i;
+
+  for (i = 0; i < book[0].Npages; i++) {
+    if (!strcmp (book[0].pages[i][0].name, name)) {
+      return (book[0].pages[i]);
+    }
+  }
+  return (NULL);
+}
+
+/* make a new named page */
+Page *CreatePage (Book *book, char *name) {
+
+  int N;
+  Page *page;
+
+  page = FindPage (book, name);
+  if (page != NULL) return (page);
+
+  N = book[0].Npages;
+  book[0].Npages ++;
+  if (book[0].Npages >= book[0].NPAGES) {
+    book[0].NPAGES += 16;
+    REALLOCATE (book[0].pages, Page *, book[0].NPAGES);
+    REALLOCATE (book[0].pageIDs, char *, book[0].NPAGES);
+    // REALLOCATE (book[0].index, int, book[0].NPAGES);
+  }
+  ALLOCATE (page, Page, 1);
+  InitPage (page, name);
+  book[0].pages[N] = page;
+  book[0].pageIDs[N] = strcreate(name);
+  // book[0].index[N] = N;
+  
+  /* at this point, I should sort the index */
+
+  return (page);
+}
+
+void sortpages (int *seq, Page **pages, char **pageIDs, int N) {
+
+# define SWAPFUNC(A,B){ Page *tmpPage; char *tmpID; \
+  tmpPage = pages[A];   pages[A]   = pages[B];   pages[B] = tmpPage; \
+  tmpID   = pageIDs[A]; pageIDs[A] = pageIDs[B]; pageIDs[B] = tmpID; \
+}
+# define COMPARE(A,B)(seq[A] < seq[B])
+
+  OHANA_SORT (N, COMPARE, SWAPFUNC);
+
+# undef SWAPFUNC
+# undef COMPARE
+}
+
+/* make a new named page */
+int ShufflePages (Book *book) {
+
+  int i, N, *seq;
+
+  N = book[0].Npages;
+
+  // generate a random index
+  ALLOCATE (seq, int, N);
+  for (i = 0; i < N; i++) {
+    seq[i] = N * drand48();
+  }
+
+  // sort the pages by the random index
+  sortpages (seq, book[0].pages, book[0].pageIDs, N);
+  free (seq);
+
+  return (TRUE);
+}
+
+/* delete a page in a book */
+int DeletePage (Book *book, Page *page) {
+
+  char *pageID;
+  int i, N, NPAGES_2;
+
+  /* find page in page list */
+  N = -1;
+  for (i = 0; i < book[0].Npages; i++) {
+    if (book[0].pages[i] == page) {
+      N = i;
+      break;
+    }
+  }
+  if (N == -1) return (FALSE);
+
+  pageID = book[0].pageIDs[i];
+
+  for (i = N; i < book[0].Npages - 1; i++) {
+    book[0].pages[i] = book[0].pages[i + 1];
+    book[0].pageIDs[i] = book[0].pageIDs[i + 1];
+    // book[0].index[i] = book[0].index[i + 1];
+  }
+  book[0].Npages --;
+  NPAGES_2 = MAX (16, book[0].NPAGES / 2);
+  if (book[0].Npages < NPAGES_2) {
+    book[0].NPAGES = NPAGES_2;
+    REALLOCATE (book[0].pages, Page *, book[0].NPAGES);
+    REALLOCATE (book[0].pageIDs, char *, book[0].NPAGES);
+    // REALLOCATE (book[0].index, int, book[0].NPAGES);
+  }
+
+  FreePage (page);
+  free (pageID);
+  return (TRUE);
+}
+
+void ListPages (Book *book) {
+
+  int i;
+
+  for (i = 0; i < book[0].Npages; i++) {
+    gprint (GP_ERR, "%-15s %3d\n", book[0].pages[i][0].name, book[0].pages[i][0].Nwords);
+  }
+  return;
+}
+
+void ListWords (Page *page) {
+
+  int i;
+
+  for (i = 0; i < page[0].Nwords; i++) {
+    gprint (GP_ERR, "%-15s %15s\n", page[0].words[i], page[0].value[i]);
+  }
+  return;
+}
+
+int BookSetWord (Page *page, char *word, char *value) {
+
+  int i;
+
+  for (i = 0; i < page[0].Nwords; i++) {
+    if (!strcmp (page[0].words[i], word)) {
+      free (page[0].value[i]);
+      page[0].value[i] = strcreate (value);
+      return TRUE;
+    }
+  }
+
+  page[0].Nwords ++;
+  if (page[0].Nwords >= page[0].NWORDS) {
+    page[0].NWORDS += 16;
+    REALLOCATE (page[0].words, char *, page[0].NWORDS);
+    REALLOCATE (page[0].value, char *, page[0].NWORDS);
+  }      
+  page[0].words[i] = strcreate (word);
+  page[0].value[i] = strcreate (value);
+  return (TRUE);
+}
+
+char *BookGetWord (Page *page, char *word) {
+  int i;
+
+  for (i = 0; i < page[0].Nwords; i++) {
+    if (!strcmp (page[0].words[i], word)) {
+      return (page[0].value[i]);
+    }
+  }
+  return NULL;
+}
Index: /branches/sc_branches/pantasks_condor/lib.data/precess.c
===================================================================
--- /branches/sc_branches/pantasks_condor/lib.data/precess.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/lib.data/precess.c	(revision 34783)
@@ -0,0 +1,49 @@
+# include "data.h"
+    
+double get_epoch (char *in_epoch, char mode) {
+
+  int done;
+  double epoch;
+
+  epoch = 2000.0;
+  done = FALSE;
+  if (in_epoch[0] == 'B') {
+    epoch = BtoJ(atof(&in_epoch[1]));
+    done = TRUE;
+  }
+
+  if (in_epoch[0] == 'J') {
+    epoch = atof(&in_epoch[1]);
+    done = TRUE;
+  }
+
+  if (!done && (mode == 'B')) {
+    epoch = BtoJ(atof(in_epoch));
+    done = TRUE;
+  }
+    
+  if (!done && (mode == 'J')) {
+    epoch = atof(in_epoch);
+    done = TRUE;
+  }
+
+  if (!done) {
+    gprint (GP_ERR, "error finding epoch %s\n", in_epoch);
+    return FALSE;
+  }
+  
+  return (epoch);
+
+}
+
+  
+double BtoJ (double in_epoch) {
+
+  double JD, out_epoch;
+
+  JD = (in_epoch - 1900.0)*365.242198781 + 2415020.31352;
+  out_epoch = 2000.0 + (JD - 2451545.0)/365.25;
+
+  return (out_epoch);
+}
+
Index: /branches/sc_branches/pantasks_condor/lib.data/queues.c
===================================================================
--- /branches/sc_branches/pantasks_condor/lib.data/queues.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/lib.data/queues.c	(revision 34783)
@@ -0,0 +1,399 @@
+# include "data.h"
+# define DEBUG 0
+
+Queue **queues;   /* queue to store the list of all queues */
+int    Nqueues;   /* number of currently defined queues */
+int    NQUEUES;   /* number of currently allocated queues */
+
+void InitQueues () {
+  Nqueues = 0;
+  NQUEUES = 16;
+  ALLOCATE (queues, Queue *, NQUEUES); 
+}
+
+void FreeQueues () {
+
+  int i, j;
+
+  for (i = 0; i < Nqueues; i++) {
+    for (j = 0; j < queues[i][0].Nlines; j++) {
+      free (queues[i][0].lines[j]);
+    }
+    free (queues[i][0].lines);
+    free (queues[i][0].name);
+    free (queues[i]);
+  }
+  free (queues);
+}
+
+/* list known queues */
+void ListQueues () {
+
+  int i;
+
+  for (i = 0; i < Nqueues; i++) {
+    gprint (GP_LOG, "%-15s %3d\n", queues[i][0].name, queues[i][0].Nlines);
+  }
+  return;
+}
+
+/* return the given queue */
+Queue *FindQueue (char *name) {
+
+  int i;
+
+  for (i = 0; i < Nqueues; i++) {
+    if (!strcmp (queues[i][0].name, name)) {
+      return (&queues[i][0]);
+    }
+  }
+  return (NULL);
+}
+
+/* make a new named queue */
+int InitQueue (Queue *queue) {
+
+  int i;
+
+  for (i = 0; i < queue[0].Nlines; i++) {
+    free (queue[0].lines[i]);
+  }
+  queue[0].Nlines = 0;
+  queue[0].NLINES = 16;
+  REALLOCATE (queue[0].lines, char *, queue[0].NLINES);
+
+  if (DEBUG) fprintf (stderr, "init: %s (%zx) : %d of %d\n", queue[0].name, (size_t) queue, queue[0].Nlines, queue[0].NLINES);
+  
+  return (TRUE);
+}
+
+/* make a new named queue */
+Queue *CreateQueue (char *name) {
+
+  int N;
+  Queue *queue;
+
+  queue = FindQueue (name);
+  if (queue != NULL) return (queue);
+
+  N = Nqueues;
+  Nqueues ++;
+  CHECK_REALLOCATE (queues, Queue *, NQUEUES, Nqueues, 16);
+  ALLOCATE (queue, Queue, 1);
+  queue[0].Nlines = 0;
+  queue[0].NLINES = 16;
+  queue[0].name = strcreate (name);
+  ALLOCATE (queue[0].lines, char *, queue[0].NLINES);
+  queues[N] = queue;
+  return (queue);
+}
+
+/* delete a queue */
+int DeleteQueue (Queue *queue) {
+
+  int i, N, NQUEUES_2;
+
+  /* find queue in queue list */
+  N = -1;
+  for (i = 0; i < Nqueues; i++) {
+    if (queues[i] == queue) {
+      N = i;
+      break;
+    }
+  }
+  if (N == -1) return (FALSE);
+
+  for (i = N; i < Nqueues - 1; i++) {
+    queues[i] = queues[i + 1];
+  }
+  Nqueues --;
+  NQUEUES_2 = MAX (16, NQUEUES / 2);
+  if (Nqueues < NQUEUES_2) {
+    NQUEUES = NQUEUES_2;
+    REALLOCATE (queues, Queue *, NQUEUES);
+  }
+
+  free (queue[0].name);
+  for (i = 0; i < queue[0].Nlines; i++) {
+    free (queue[0].lines[i]);
+  }
+  free (queue[0].lines);
+  free (queue);
+  return (TRUE);
+}
+
+void PushNamedQueue (char *name, char *line) {
+
+  Queue *queue;
+  
+  queue = FindQueue (name);
+  if (queue == NULL) {
+    queue = CreateQueue (name);
+  }
+  PushQueue (queue, line);
+  return;
+}
+
+/* push line onto queue.  return chars create new lines */
+void PushQueue (Queue *queue, char *line) {
+
+  int N;
+  char *p, *q;
+
+  p = line;
+  q = strchr (line, '\n');
+  N = queue[0].Nlines;
+  while (q != NULL) {
+    queue[0].lines[N] = strncreate (p, q - p);
+    N++;
+    CHECK_REALLOCATE (queue[0].lines, char *, queue[0].NLINES, N, 16);
+    p = q + 1;
+    q = strchr (p, '\n');
+  }    
+  if (*p) {
+    queue[0].lines[N] = strcreate (p);
+    N++;
+    CHECK_REALLOCATE (queue[0].lines, char *, queue[0].NLINES, N, 16);
+  }
+  queue[0].Nlines = N;
+  return;
+}
+
+// return a newly allocated string containing the requested key value
+char *ChooseSingleKey (char *line, int Key) {
+
+  int i;
+  char *key, *p;
+
+  if (Key == -1) {
+    key = strcreate (line);
+    return (key);
+  }
+
+  key = line;
+  for (i = 0; (i < Key) && (key != NULL); i++) {
+    p = nextword (key);
+    key = p;
+  }
+  key = thisword (key);
+  return (key);
+}
+
+/* construct merged key given keylist of the form K:N:M */ 
+char *ChooseKey (char *line, char *keylist) {
+
+  char *output, *entry, *key, *p, *q;
+  int first, keynum;
+
+  if (line == NULL) return (NULL);
+  if (keylist == NULL) return (line);
+
+  ALLOCATE (output, char, strlen(line) + 1);
+  memset (output, 0, strlen(line) + 1);
+
+  first = TRUE;
+  p = q = keylist;
+  while (q != NULL) {
+    q = strchr (p, ':');
+    if (q == NULL) {
+      entry = strcreate (p);
+    } else {
+      entry = strncreate (p, q - p);
+    }
+    keynum = atoi (entry);
+    free (entry);
+
+    key = ChooseSingleKey (line, keynum);
+
+    if (!first) strcat (output, ":");
+    if (key) {
+	strcat (output, key);
+	free (key);
+    }
+
+    if (q != NULL) p = q + 1;
+    first = FALSE;
+  }
+  return (output);
+}
+
+/* push line onto queue, skipping existing matches (optionally by key) */
+void PushQueueUnique (Queue *queue, char *line, char *Key) {
+
+  int i, j, N, found;
+  char *p, *q, *key1, *key2;
+  Queue tmp;
+
+  /* init tmp queue */
+  tmp.Nlines = 0;
+  tmp.NLINES = 16;
+  ALLOCATE (tmp.lines, char *, tmp.NLINES);
+
+  /* push entries on tmp queue */
+  p = line;
+  q = strchr (line, '\n');
+  N = tmp.Nlines;
+  while (q != NULL) {
+    tmp.lines[N] = strncreate (p, q - p);
+    N++;
+    CHECK_REALLOCATE (tmp.lines, char *, tmp.NLINES, N, 16);
+    p = q + 1;
+    q = strchr (p, '\n');
+  }    
+  if (*p) {
+    tmp.lines[N] = strcreate (p);
+    N++;
+    CHECK_REALLOCATE (tmp.lines, char *, tmp.NLINES, N, 16);
+  }
+  tmp.Nlines = N;
+
+  /* add unique entries in tmp to queue */
+  for (i = 0; i < tmp.Nlines; i++) {
+    key1 = ChooseKey (tmp.lines[i], Key);
+    if (key1 == NULL) continue;
+    found = FALSE;
+    for (j = 0; !found && (j < queue[0].Nlines); j++) {
+      key2 = ChooseKey (queue[0].lines[j], Key);
+      if (key2 == NULL) continue;
+      found = !strcmp (key1, key2);
+      free (key2);
+    }      
+    if (!found) PushQueue (queue, tmp.lines[i]);
+    free (key1);
+  }
+  for (i = 0; i < tmp.Nlines; i++) {
+    free (tmp.lines[i]);
+  } 
+  free (tmp.lines);
+  return;
+}
+
+/* push line onto queue, replacing matches (optionally by Key) */
+void PushQueueReplace (Queue *queue, char *line, char *Key) {
+
+  int i, j, N, found;
+  char *p, *q, *key1, *key2;
+  Queue tmp;
+
+  /* init tmp queue */
+  tmp.Nlines = 0;
+  tmp.NLINES = 16;
+  ALLOCATE (tmp.lines, char *, tmp.NLINES);
+
+  /* push entries on tmp queue */
+  p = line;
+  q = strchr (line, '\n');
+  N = tmp.Nlines;
+  while (q != NULL) {
+    tmp.lines[N] = strncreate (p, q - p);
+    N++;
+    CHECK_REALLOCATE (tmp.lines, char *, tmp.NLINES, N, 16);
+    p = q + 1;
+    q = strchr (p, '\n');
+  }    
+  if (*p) {
+    tmp.lines[N] = strcreate (p);
+    N++;
+    CHECK_REALLOCATE (tmp.lines, char *, tmp.NLINES, N, 16);
+  }
+  tmp.Nlines = N;
+
+  /* add unique entries in tmp to queue */
+  for (i = 0; i < tmp.Nlines; i++) {
+    key1 = ChooseKey (tmp.lines[i], Key);
+    if (key1 == NULL) continue;
+    found = FALSE;
+    for (j = 0; !found && (j < queue[0].Nlines); j++) {
+      key2 = ChooseKey (queue[0].lines[j], Key);
+      if (key2 == NULL) continue;
+      found = !strcmp (key1, key2);
+      if (found) {
+	// XXX do I need to free queue[0].lines[j]??
+	queue[0].lines[j] = strcreate (tmp.lines[i]);
+      }
+      free (key2);
+    }      
+    if (!found) PushQueue (queue, tmp.lines[i]);
+    free (key1);
+  }
+  for (i = 0; i < tmp.Nlines; i++) {
+    free (tmp.lines[i]);
+  } 
+  free (tmp.lines);
+  return;
+}
+
+char *PopQueue (Queue *queue) {
+
+  int i, NLINES_2;
+  char *line;
+
+  if (queue[0].Nlines == 0) return (NULL);
+  line = queue[0].lines[0];
+
+  for (i = 0; i < queue[0].Nlines - 1; i++) {
+    queue[0].lines[i] = queue[0].lines[i+1];
+  }
+  queue[0].Nlines --;
+
+  /* shrink queue allocation if small enough */
+  NLINES_2 = MAX (16, queue[0].NLINES / 2);
+  if (queue[0].Nlines < NLINES_2) {
+    queue[0].NLINES = NLINES_2;
+    REALLOCATE (queue[0].lines, char *, queue[0].NLINES);
+  }    
+  return (line);
+}
+
+/* pop the first entry which for which the key matches */
+char *PopQueueMatch (Queue *queue, char *Key, char *value) {
+
+  int i, choice, NLINES_2;
+  char *line, *test;
+
+  if (queue[0].Nlines == 0) return (NULL);
+
+  /* find the matching key */
+  choice = -1;
+  for (i = 0; (i < queue[0].Nlines) && (choice == -1); i++) {
+      test = ChooseKey (queue[0].lines[i], Key);
+      if (test == NULL) continue;
+      if (strcmp (value, test)) { 
+	free (test);
+	continue;
+      }
+      free (test);
+      choice = i;
+  }
+  if (choice == -1) return NULL;
+
+  line = queue[0].lines[choice];
+
+  for (i = choice; i < queue[0].Nlines - 1; i++) {
+    queue[0].lines[i] = queue[0].lines[i+1];
+  }
+  queue[0].Nlines --;
+
+  /* shrink queue allocation if small enough */
+  NLINES_2 = MAX (16, queue[0].NLINES / 2);
+  if (queue[0].Nlines < NLINES_2) {
+    queue[0].NLINES = NLINES_2;
+    REALLOCATE (queue[0].lines, char *, queue[0].NLINES);
+  }    
+  return (line);
+}
+
+int PrintQueue (Queue *queue) {
+
+  int i;
+
+  if (queue[0].Nlines == 0) return (TRUE);
+
+  if (DEBUG) fprintf (stderr, "print: %s (%zx) : %d of %d\n", queue[0].name, (size_t) queue, queue[0].Nlines, queue[0].NLINES);
+
+  for (i = 0; i < queue[0].Nlines; i++) {
+    gprint (GP_LOG, "%s\n", queue[0].lines[i]);
+  }
+  return (TRUE);
+}
+
Index: /branches/sc_branches/pantasks_condor/lib.data/spline.c
===================================================================
--- /branches/sc_branches/pantasks_condor/lib.data/spline.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/lib.data/spline.c	(revision 34783)
@@ -0,0 +1,117 @@
+# include "data.h"
+
+/* construct the natural spline for x, y in y2 */
+void spline_construct_flt (float *x, float *y, int N, float *y2) {
+
+  int i;
+  float dy, dx, *tmp;
+  
+  ALLOCATE (tmp, float, N);
+
+  y2[0] = tmp[0] = 0.0;
+  
+  for (i = 1; i < N-1; i++) {
+    dx = (x[i+0] - x[i-1]) / (x[i+1] - x[i-1]);
+    dy = dx * y2[i-1] + 2.0;
+    y2[i] = (dx - 1.0) / dy;
+    tmp[i] = (y[i+1] - y[i+0]) / (x[i+1] - x[i+0]) - (y[i+0] - y[i-1]) / (x[i+0] - x[i-1]);
+    tmp[i] = (6.0 * tmp[i] / (x[i+1] - x[i-1]) - dx*tmp[i-1]) / dy;
+  }
+  
+  y2[N-1] = 0;
+  for (i = N-2; i >= 1; i--)
+    y2[i] = y2[i]*y2[i+1] + tmp[i];
+
+  free (tmp);
+}
+
+/* evaluate spline for x, y, y2 at X */
+float spline_apply_flt (float *x, float *y, float *y2, int N, float X) {
+
+  int i, lo, hi;
+  float dx, a, b, value;
+  
+  /* find correct element in array (x must be sorted) */
+  lo = 0;
+  hi = N-1;
+  while (hi - lo > 1) {
+    i = 0.5*(hi+lo);
+    if (x[i] > X) {
+      hi = i;
+    } else {
+      lo = i;
+    }
+  }
+
+  /* error condition: duplicate abssisca */
+  dx = x[hi] - x[lo];
+  if (dx == 0.0) {
+    return (HUGE_VAL);
+  }
+
+  /* evaluate spline */
+  a = (x[hi] - X) / dx;
+  b = (X - x[lo]) / dx;
+
+  value = a*y[lo] + b*y[hi] + ((a*a*a - a)*y2[lo] + (b*b*b - b)*y2[hi])*(dx*dx) / 6.0;
+  return (value);
+
+}
+
+/* construct the natural spline for x, y in y2 */
+void spline_construct_dbl (opihi_flt *x, opihi_flt *y, int N, opihi_flt *y2) {
+
+  int i;
+  opihi_flt dy, dx, *tmp;
+  
+  ALLOCATE (tmp, opihi_flt, N);
+
+  y2[0] = tmp[0] = 0.0;
+  
+  for (i = 1; i < N-1; i++) {
+    dx = (x[i+0] - x[i-1]) / (x[i+1] - x[i-1]);
+    dy = dx * y2[i-1] + 2.0;
+    y2[i] = (dx - 1.0) / dy;
+    tmp[i] = (y[i+1] - y[i+0]) / (x[i+1] - x[i+0]) - (y[i+0] - y[i-1]) / (x[i+0] - x[i-1]);
+    tmp[i] = (6.0 * tmp[i] / (x[i+1] - x[i-1]) - dx*tmp[i-1]) / dy;
+  }
+  
+  y2[N-1] = 0;
+  for (i = N-2; i >= 1; i--)
+    y2[i] = y2[i]*y2[i+1] + tmp[i];
+
+  free (tmp);
+}
+
+/* evaluate spline for x, y, y2 at X */
+opihi_flt spline_apply_dbl (opihi_flt *x, opihi_flt *y, opihi_flt *y2, int N, opihi_flt X) {
+
+  int i, lo, hi;
+  opihi_flt dx, a, b, value;
+  
+  /* find correct element in array (x must be sorted) */
+  lo = 0;
+  hi = N-1;
+  while (hi - lo > 1) {
+    i = 0.5*(hi+lo);
+    if (x[i] > X) {
+      hi = i;
+    } else {
+      lo = i;
+    }
+  }
+
+  /* error condition: duplicate abssisca */
+  dx = x[hi] - x[lo];
+  if (dx == 0.0) {
+    return (HUGE_VAL);
+  }
+
+  /* evaluate spline */
+  a = (x[hi] - X) / dx;
+  b = (X - x[lo]) / dx;
+
+  value = a*y[lo] + b*y[hi] + ((a*a*a - a)*y2[lo] + (b*b*b - b)*y2[hi])*(dx*dx) / 6.0;
+  return (value);
+
+}
Index: /branches/sc_branches/pantasks_condor/lib.data/starfuncs.c
===================================================================
--- /branches/sc_branches/pantasks_condor/lib.data/starfuncs.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/lib.data/starfuncs.c	(revision 34783)
@@ -0,0 +1,256 @@
+# include "data.h"
+
+double get_aperture_stats (Matrix *matrix, int X, int Y, int Npix, int Nborder, double max) {
+
+  double *ring;
+  double x, y, x2, y2, xy, I, sky, FWHMx, FWHMy, value, mag, Sxy;
+  int i, j, n, Radius, Nring, Nmax;
+  double Npts, gain, dsky2, dmag, peak, offset;
+  char *string;
+  
+  string = get_variable ("GAIN");
+  if (string == (char *) NULL) {
+    gprint (GP_ERR, "assuming a value of 1.0\n");
+    gain = 1.0;
+  } else {
+    gain = atof (string);
+  }
+  Nborder = MAX (1, Nborder);
+  Nborder = MIN (1000, Nborder);
+  
+  Radius = (int)(0.5*Npix);
+  Npix = 2 * Radius + 1;
+  Nring = 4*Nborder*(Nborder + Npix);
+  ALLOCATE (ring, double, Nring);
+  bzero (ring, sizeof(double)*Nring);
+
+  n = 0;  
+  for (j = 0; j < Nborder; j++) {
+    for (i = X - Radius - Nborder; i < X + Radius + Nborder + 1; i++, n+=2) {
+      ring[n]   = gfits_get_matrix_value (matrix, i, (int)(Y - Radius - j));
+      ring[n+1] = gfits_get_matrix_value (matrix, i, (int)(Y + Radius + j));
+    }
+    for (i = Y - Radius; i < Y + Radius + 1; i++, n+=2) {
+      ring[n]   = gfits_get_matrix_value (matrix, (int)(X - Radius - j), i);
+      ring[n+1] = gfits_get_matrix_value (matrix, (int)(X + Radius + j), i);
+    }
+  }
+  dsort (ring, Nring);
+  for (Npts = sky = dsky2 = 0, 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);
+
+  float dx, dy;
+
+  peak = 0;
+  Npts = Nmax = 0;
+  x = y = x2 = y2 = xy = I = 0;
+  for (i = X - Radius; i < X + Radius + 1; i++) {
+    for (j = Y - Radius; j < Y + Radius + 1; j++) {
+      if (hypot((i-X), (j-Y)) > Radius) continue;
+      value = gfits_get_matrix_value (matrix, i, j);
+      offset = value - sky;
+      dx = i - X;
+      dy = j - Y;
+      x  += dx*offset;
+      y  += dy*offset;
+      x2 += dx*dx*offset;
+      y2 += dy*dy*offset;
+      xy += dx*dy*offset;
+      I  += offset;
+      Npts ++;
+      if (value > max) {
+	Nmax ++;
+      }
+      if (value > peak) peak = value;
+    }
+  }
+
+  x = x / I;
+  y = y / I;
+  FWHMx = 2.355*sqrt (fabs(x2 / I - x*x));
+  FWHMy = 2.355*sqrt (fabs(y2 / I - y*y));
+  Sxy   = xy / I - x*y;
+  mag = -2.5*log10(I);
+  dmag = sqrt (fabs(1.0 / (gain*I) + Npts*dsky2 / (I*I)));
+  x = x + X;
+  y = y + Y;
+  
+  set_variable ("Xg", x);
+  set_variable ("Yg", y);
+  set_variable ("SXg", FWHMx);
+  set_variable ("SYg", FWHMy);
+  set_variable ("SXYg", Sxy);
+  set_variable ("Sg", sky);
+  set_variable ("dSg", sqrt (fabs (dsky2)));
+  set_variable ("Zg", mag);
+  set_variable ("dZg", dmag);
+  set_variable ("Zcg", I);
+  set_variable ("Zpk", peak);
+  set_int_variable ("Nsat", Nmax);
+  set_int_variable ("Npts", Npts);
+  
+  gprint (GP_LOG, "%f %f %f %f %f %f %f %f\n", x, y, FWHMx, FWHMy, sky, I, mag, dmag);
+
+  return (mag);
+
+}
+
+static double Raper  =  5;
+static double Rinner = 10;
+static double Router = 15;
+static double *sky = NULL;
+
+int set_rough_radii (double Ra, double Ri, double Ro) {
+
+  Raper = Ra;
+  Rinner = Ri;
+  Router = Ro;
+  if (sky == NULL) {
+    ALLOCATE (sky, double, SQ(2*Router + 1));
+  } else {
+    REALLOCATE (sky, double, SQ(2*Router + 1));
+  }
+  return (TRUE);
+}
+
+/* use a circular aperture */
+int get_rough_star (float *data, int Nx, int Ny, int x, int y,
+		    opihi_flt *xc, opihi_flt *yc, 
+		    opihi_flt *sx, opihi_flt *sy, opihi_flt *sxy,
+		    opihi_flt *zs, opihi_flt *zp, opihi_flt *sk) {
+
+  double Ro2, rad2;
+  int i, j, Npts, Nsky;
+  int Xs, Xe, Ys, Ye, off, Xc, Yc;
+  double peak, fsky, value;
+  double Sx, Sy, Sx2, Sy2, Sxy, Sum;
+  
+  /* define circular boundaries */
+  Ro2 = SQ(Router);
+
+  /* measure the sky level */
+  /* boundaries for the outer sky region */
+  Xs = MAX (x - Router, 0);
+  Xe = MIN (x + Router + 1, Nx);
+  Ys = MAX (y - Router, 0);
+  Ye = MIN (y + Router + 1, Ny);
+
+/* this sample uses a circular aperture */
+# if (0)
+  double Ri2 = SQ(Rinner);
+  Nsky = 0;  
+  for (j = Ys; j < Ye; j++) {
+    off = j*Nx;
+    for (i = Xs; i < Xe; i++) { 
+      rad2 = SQ(i - x) + SQ(j - y);
+      if (rad2 > Ro2) continue;
+      if (rad2 < Ri2) continue;
+      sky[Nsky] = data[i+off];
+      Nsky ++;
+    }
+  }
+  dsort (sky, Nsky);
+  for (Npts = fsky = 0, i = 0.25*Nsky; i < 0.75*Nsky; i++, Npts += 1.0) {
+    fsky += sky[i];
+  }
+  fsky = fsky / Npts;
+# else
+
+/* this sample uses a square outer annulus, without loop if tests */
+  Nsky = 0;  
+  Xs = MAX (x - Router, 0);
+  Xe = MIN (x - Rinner + 1, Nx);
+  Ys = MAX (y - Rinner, 0);
+  Ye = MIN (y + Rinner + 1, Ny);
+  for (j = Ys; j < Ye; j++) {
+    off = j*Nx;
+    for (i = Xs; i < Xe; i++) { 
+      sky[Nsky] = data[i+off];
+      Nsky ++;
+    }
+  }
+  Xs = MAX (x + Rinner, 0);
+  Xe = MIN (x + Router + 1, Nx);
+  for (j = Ys; j < Ye; j++) {
+    off = j*Nx;
+    for (i = Xs; i < Xe; i++) { 
+      sky[Nsky] = data[i+off];
+      Nsky ++;
+    }
+  }
+  Xs = MAX (x - Rinner, 0);
+  Xe = MIN (x - Rinner + 1, Nx);
+  Ys = MAX (y - Router, 0);
+  Ye = MIN (y - Rinner + 1, Ny);
+  for (j = Ys; j < Ye; j++) {
+    off = j*Nx;
+    for (i = Xs; i < Xe; i++) { 
+      sky[Nsky] = data[i+off];
+      Nsky ++;
+    }
+  }
+  Ys = MAX (y + Rinner, 0);
+  Ye = MIN (y + Router + 1, Ny);
+  for (j = Ys; j < Ye; j++) {
+    off = j*Nx;
+    for (i = Xs; i < Xe; i++) { 
+      sky[Nsky] = data[i+off];
+      Nsky ++;
+    }
+  }
+  dsort (sky, Nsky);
+  for (Npts = fsky = 0, i = 0.25*Nsky; i < 0.75*Nsky; i++, Npts += 1.0) {
+    fsky += sky[i];
+  }
+  fsky = fsky / Npts;
+# endif
+
+  /* boundaries for the star region */
+  Xs = MAX (x - Raper, 0);
+  Xe = MIN (x + Raper + 1, Nx);
+  Ys = MAX (y - Raper, 0);
+  Ye = MIN (y + Raper + 1, Ny);
+
+  /** note that this will fail on negative flux objects */
+  peak = Npts = 0;
+  Sx = Sy = Sx2 = Sy2 = Sxy = Sum = 0;
+  for (j = Ys; j < Ye; j++) {
+    off = j*Nx;
+    Yc = j - y;
+    for (i = Xs; i < Xe; i++) {
+      Xc = i - x;
+      rad2 = SQ(Xc) + SQ(Yc);
+      if (rad2 > Ro2) continue;
+      value = data[i+off] - fsky;
+      Sx  += Xc*value;
+      Sy  += Yc*value;
+      Sx2 += Xc*Xc*value;
+      Sy2 += Yc*Yc*value;
+      Sxy += Xc*Yc*value;
+      Sum += value;
+      Npts ++;
+      if (value > peak) peak = value;
+    }
+  }
+
+  *xc = Sx / Sum;
+  *yc = Sy / Sum;
+  *sx = sqrt (fabs (Sx2 / Sum - SQ(*xc)));
+  *sy = sqrt (fabs (Sy2 / Sum - SQ(*yc)));
+  *sxy = Sxy / Sum;
+  *xc += x;
+  *yc += y;
+  *zs = Sum;
+  *zp = peak;
+  *sk = fsky;
+  /* note sigma is rough: round-off errors can introduce errors */
+  /* using values relative to x,y should minimize this effect */
+
+  return (Npts);
+}
+
Index: /branches/sc_branches/pantasks_condor/lib.data/style_args.c
===================================================================
--- /branches/sc_branches/pantasks_condor/lib.data/style_args.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/lib.data/style_args.c	(revision 34783)
@@ -0,0 +1,70 @@
+# include "data.h"
+# include "display.h"
+
+int style_args (Graphdata *graphmode, int *argc, char **argv, int *kapa) {
+  
+  int N;
+  char *colorName;
+  char *kapaName;
+
+  kapaName = NULL;
+  if ((N = get_argument (*argc, argv, "-n"))) {
+    remove_argument (N, argc, argv);
+    kapaName = strcreate (argv[N]);
+    remove_argument (N, argc, argv);
+  }
+  if (!GetGraph (graphmode, kapa, kapaName)) return (FALSE);
+  FREE (kapaName);
+
+  if (*argc == 1) {
+    kapaName = GetKapaName();
+    colorName = KapaColorName (graphmode[0].color);
+    gprint (GP_ERR, "current style (%s): -x %d -c %s -pt %d -lt %d -lw %f -sz %f\n", kapaName,
+	     graphmode[0].style, colorName, graphmode[0].ptype, 
+	     graphmode[0].ltype, graphmode[0].lweight,
+	     graphmode[0].size);
+    return (TRUE);
+  }
+
+  if ((N = get_argument (*argc, argv, "-lt"))) {
+    remove_argument (N, argc, argv);
+    graphmode[0].ltype = atof(argv[N]);
+    remove_argument (N, argc, argv);
+  }
+  if ((N = get_argument (*argc, argv, "-lw"))) {
+    remove_argument (N, argc, argv);
+    graphmode[0].lweight = atof(argv[N]);
+    remove_argument (N, argc, argv);
+  }
+  if ((N = get_argument (*argc, argv, "-pt"))) {
+    remove_argument (N, argc, argv);
+    graphmode[0].ptype = atof(argv[N]);
+    remove_argument (N, argc, argv);
+  }
+  if ((N = get_argument (*argc, argv, "+eb"))) {
+    remove_argument (N, argc, argv);
+    graphmode[0].ebar = TRUE;
+  }
+  if ((N = get_argument (*argc, argv, "-eb"))) {
+    remove_argument (N, argc, argv);
+    graphmode[0].ebar = FALSE;
+  }
+  if ((N = get_argument (*argc, argv, "-sz"))) {
+    remove_argument (N, argc, argv);
+    graphmode[0].size = atof(argv[N]);
+    remove_argument (N, argc, argv);
+  }
+  if ((N = get_argument (*argc, argv, "-c"))) {
+    remove_argument (N, argc, argv);
+    graphmode[0].color = KapaColorByName (argv[N]);
+    if (graphmode[0].color == -1) return (FALSE);
+    remove_argument (N, argc, argv);
+  }
+  if ((N = get_argument (*argc, argv, "-x"))) {
+    remove_argument (N, argc, argv);
+    graphmode[0].style = atof(argv[N]);
+    remove_argument (N, argc, argv);
+  }
+
+  return (TRUE);
+}
Index: /branches/sc_branches/pantasks_condor/lib.data/svdcmp.c
===================================================================
--- /branches/sc_branches/pantasks_condor/lib.data/svdcmp.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/lib.data/svdcmp.c	(revision 34783)
@@ -0,0 +1,211 @@
+# include "data.h"
+
+/*
+  static float at,bt,ct;
+  #define PYTHAG(a,b) ((at=fabs(a)) > (bt=fabs(b)) ? \
+  (ct=bt/at,at*sqrt(1.0+ct*ct)) : (bt ? (ct=at/bt,bt*sqrt(1.0+ct*ct)): 0.0))
+  pythag -> hypot (check on roundoff errors) 
+*/
+
+/* use simple max? */
+#define FSIGN(a,b) ((b) >= 0.0 ? fabs(a) : -fabs(a))
+
+/* n == Nx, m == Ny */
+int svdcmp (float *a, opihi_flt *w, float *v, int Nx, int Ny) {
+
+  int flag, i, its, j, jj, k, l, nm;
+  float c, f, h, s, x, y, z;
+  float anorm=0.0, g = 0.0, scale = 0.0;
+  float *rv1;
+
+  if (Ny < Nx) return (0);
+
+  l = nm = 0;
+  ALLOCATE (rv1, float, Nx);
+  for (i = 0; i < Nx; i++) {
+    l = i + 1;
+    rv1[i] = scale*g;
+    g = s = scale = 0.0;
+    if (i < Ny) {
+      for (k = i; k < Ny; k++) scale += fabs(a[k*Nx + i]);
+      if (scale) {
+	for (k = i; k < Ny; k++) {
+	  a[k*Nx + i] /= scale;
+	  s += a[k*Nx + i]*a[k*Nx + i];
+	}
+	f = a[i*Nx + i];
+	g  = -FSIGN(sqrt(s), f);
+	h = f*g - s;
+	a[i*Nx + i] = f-g;
+	if (i != Nx - 1) {
+	  for (j = l; j < Nx; j++) {
+	    for (s = 0.0, k = i; k < Ny; k++) s += a[k*Nx + i]*a[k*Nx + j];
+	    f = s/h;
+	    for (k = i; k < Ny; k++) a[k*Nx + j] += f*a[k*Nx + i];
+	  }
+	}
+	for (k = i; k < Ny; k++) a[k*Nx + i] *= scale;
+      }
+    }
+    w[i] = scale*g;
+    g = s = scale = 0.0;
+    if ((i < Ny) && (i != (Nx - 1))) {
+      for (k = l; k < Nx; k++) scale += fabs(a[i*Nx + k]);
+      if (scale) {
+	for (k = l; k < Nx; k++) {
+	  a[i*Nx + k] /= scale;
+	  s += a[i*Nx + k]*a[i*Nx + k];
+	}
+	f = a[i*Nx + l];
+	g = -FSIGN(sqrt(s), f);
+	h = f*g - s;
+	a[i*Nx + l] = f-g;
+	for (k = l; k < Nx; k++) rv1[k] = a[i*Nx + k]/h;
+	if (i != Ny - 1) {
+	  for (j = l; j < Ny; j++) {
+	    for (s = 0.0, k = l; k < Nx; k++) s += a[j*Nx + k]*a[i*Nx + k];
+	    for (k = l; k < Nx; k++) a[j*Nx + k] += s*rv1[k];
+	  }
+	}
+	for (k = l; k < Nx; k++) a[i*Nx + k] *= scale;
+      }
+    }
+    anorm = MAX(anorm, (fabs(w[i]) + fabs(rv1[i])));
+  }
+
+  for (i = Nx - 1; i >= 0; i--) {
+    if (i < Nx - 1) {
+      if (g) {
+	/* isn't l == n to start?? */
+	for (j = l; j < Nx; j++) v[j*Nx + i] = (a[i*Nx + j]/a[i*Nx + l])/g;
+	for (j = l; j < Nx; j++) {
+	  for (s = 0.0, k = l; k < Nx; k++) s += a[i*Nx + k]*v[k*Nx + j];
+	  for (k = l; k < Nx; k++) v[k*Nx + j] += s*v[k*Nx + i];
+	}
+      }
+      for (j = l; j < Nx; j++) v[i*Nx + j] = v[j*Nx + i] = 0.0;
+    }
+    v[i*Nx + i] = 1.0;
+    g = rv1[i];
+    l = i;
+  }
+  for (i = Nx - 1; i >= 0; i--) {
+    l = i + 1;
+    g = w[i];
+    if (i < Nx - 1)
+      for (j = l; j < Nx; j++) a[i*Nx + j] = 0.0;
+    if (g) {
+      g = 1.0/g;
+      if (i != Nx - 1) {
+	for (j = l; j < Nx; j++) {
+	  for (s = 0.0, k = l; k < Ny; k++) s += a[k*Nx + i]*a[k*Nx + j];
+	  f = (s/a[i*Nx + i])*g;
+	  for (k = i; k < Ny; k++) a[k*Nx + j] += f*a[k*Nx + i];
+	}
+      }
+      for (j = i; j < Ny; j++) a[j*Nx + i] *= g;
+    } else {
+      for (j = i; j < Ny; j++) a[j*Nx + i] = 0.0;
+    }
+    ++a[i*Nx + i];
+  }
+
+  // int status = 1;
+  for (k = Nx - 1; k >= 0; k--) {
+    for (its = 0; its < 30; its++) {
+      flag = 1;
+      for (l = k; l >= 0; l--) {
+	nm = l - 1;
+	if (fabs(rv1[l])+anorm == anorm) {
+	  flag = 0;
+	  break;
+	}
+	if (fabs(w[nm])+anorm == anorm) break;
+      }
+      if (flag) {
+	c = 0.0;
+	s = 1.0;
+	for (i = l; i < k; i++) {
+	  f = s*rv1[i];
+	  if (fabs(f)+anorm != anorm) {
+	    g = w[i];
+	    h = hypot (f, g);
+	    w[i] = h;
+	    h = 1.0/h;
+	    c = g*h;
+	    s = (-f*h);
+	    for (j = 0; j < Ny; j++) {
+	      y = a[j*Nx + nm];
+	      z = a[j*Nx + i];
+	      a[j*Nx + nm] = y*c + z*s;
+	      a[j*Nx + i] = z*c - y*s;
+	    }
+	  }
+	}
+      }
+      z = w[k];
+      if (l == k) {
+	if (z < 0.0) {
+	  w[k] = -z;
+	  for (j = 0; j < Nx; j++) v[j*Nx + k] = (-v[j*Nx + k]);
+	}
+	break;
+      }
+      // if (its == 29) status = 0;
+      x = w[l];
+      nm = k-1;
+      y = w[nm];
+      g = rv1[nm];
+      h = rv1[k];
+      f = ((y-z)*(y+z) + (g-h)*(g+h))/(2.0*h*y);
+      g = hypot (f, 1.0);
+      f = ((x-z)*(x+z) + h*((y/(f+FSIGN(g, f)))-h))/x;
+      c = s = 1.0;
+      for (j = l; j < nm; j++) {
+	i = j+1;
+	g = rv1[i];
+	y = w[i];
+	h = s*g;
+	g = c*g;
+	z = hypot (f, h);
+	rv1[j] = z;
+	c = f/z;
+	s = h/z;
+	f = x*c+g*s;
+	g = g*c-x*s;
+	h = y*s;
+	y = y*c;
+	for (jj = 0; jj < Nx; jj++) {
+	  x = v[jj*Nx + j];
+	  z = v[jj*Nx + i];
+	  v[jj*Nx + j] = x*c+z*s;
+	  v[jj*Nx + i] = z*c-x*s;
+	}
+	z = hypot(f, h);
+	w[j] = z;
+	if (z) {
+	  z = 1.0/z;
+	  c = f*z;
+	  s = h*z;
+	}
+	f = (c*g) + (s*y);
+	x = (c*y) - (s*g);
+	for (jj = 0; jj < Ny; jj++) {
+	  y = a[jj*Nx + j];
+	  z = a[jj*Nx + i];
+	  a[jj*Nx + j] = y*c+z*s;
+	  a[jj*Nx + i] = z*c-y*s;
+	}
+      }
+      rv1[l] = 0.0;
+      rv1[k] = f;
+      w[k] = x;
+    }
+  }
+  free(rv1);
+  return (1);
+}
+
+#undef FSIGN
+#undef MAX
+#undef PYTHAG
Index: /branches/sc_branches/pantasks_condor/lib.data/svdcmp_bond_new.c
===================================================================
--- /branches/sc_branches/pantasks_condor/lib.data/svdcmp_bond_new.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/lib.data/svdcmp_bond_new.c	(revision 34783)
@@ -0,0 +1,246 @@
+# include "data.h"
+
+/*  svd.c -- Singular value decomposition. Translated to 'C' from the
+ *           original Algol code in "Handbook for Automatic Computation,
+ *           vol. II, Linear Algebra", Springer-Verlag.
+ *
+ *  (C) 2000, C. Bond. All rights reserved.
+ *
+ *  This is almost an exact translation from the original, except that
+ *  an iteration counter is added to prevent stalls. This corresponds
+ *  to similar changes in other translations.
+ *
+ *  Returns an error code = 0, if no errors and 'k' if a failure to
+ *  converge at the 'kth' singular value.
+ * 
+ */
+
+int svdcmp_bond_new(int m, int n, int withu, int withv, double eps, double tol, double **a, double *q, double **u, double **v)
+{
+	int i,j,k,l,l1,iter,retval;
+	double c,f,g,h,s,x,y,z;
+	double *e;
+
+	ALLOCATE (e, double, n);
+	memset (e, 0, n*sizeof(double));
+	retval = 0;
+
+/* Copy 'a' to 'u' */    
+	for (i=0;i<m;i++) {
+		for (j=0;j<n;j++)
+			u[i][j] = a[i][j];
+	}
+/* Householder's reduction to bidiagonal form. */
+	g = x = 0.0;    
+	l = 0;
+	for (i=0;i<n;i++) {
+		e[i] = g;
+		s = 0.0;
+		l = i+1;
+		for (j=i;j<m;j++)
+			s += (u[j][i]*u[j][i]);
+		if (s < tol)
+			g = 0.0;
+		else {
+			f = u[i][i];
+			g = (f < 0) ? sqrt(s) : -sqrt(s);
+			h = f * g - s;
+			u[i][i] = f - g;
+			for (j=l;j<n;j++) {
+				s = 0.0;
+				for (k=i;k<m;k++)
+					s += (u[k][i] * u[k][j]);
+				f = s / h;
+				for (k=i;k<m;k++)
+					u[k][j] += (f * u[k][i]);
+			} /* end j */
+		} /* end s */
+		q[i] = g;
+		s = 0.0;
+		for (j=l;j<n;j++)
+			s += (u[i][j] * u[i][j]);
+		if (s < tol)
+			g = 0.0;
+		else {
+			f = u[i][i+1];
+			g = (f < 0) ? sqrt(s) : -sqrt(s);
+			h = f * g - s;
+			u[i][i+1] = f - g;
+			for (j=l;j<n;j++) 
+				e[j] = u[i][j]/h;
+			for (j=l;j<m;j++) {
+				s = 0.0;
+				for (k=l;k<n;k++) 
+					s += (u[j][k] * u[i][k]);
+				for (k=l;k<n;k++)
+					u[j][k] += (s * e[k]);
+			} /* end j */
+		} /* end s */
+		y = fabs(q[i]) + fabs(e[i]);                         
+		if (y > x)
+			x = y;
+	} /* end i */
+
+/* accumulation of right-hand transformations */
+	if (withv) {
+		for (i=n-1;i>=0;i--) {
+			if (g != 0.0) {
+				h = u[i][i+1] * g;
+				for (j=l;j<n;j++)
+					v[j][i] = u[i][j]/h;
+				for (j=l;j<n;j++) {
+					s = 0.0;
+					for (k=l;k<n;k++) 
+						s += (u[i][k] * v[k][j]);
+					for (k=l;k<n;k++)
+						v[k][j] += (s * v[k][i]);
+
+				} /* end j */
+			} /* end g */
+			for (j=l;j<n;j++)
+				v[i][j] = v[j][i] = 0.0;
+			v[i][i] = 1.0;
+			g = e[i];
+			l = i;
+		} /* end i */
+ 
+	} /* end withv, parens added for clarity */
+
+/* accumulation of left-hand transformations */
+	if (withu) {
+		for (i=n;i<m;i++) {
+			for (j=n;j<m;j++)
+				u[i][j] = 0.0;
+			u[i][i] = 1.0;
+		}
+	}
+	if (withu) {
+		for (i=n-1;i>=0;i--) {
+			l = i + 1;
+			g = q[i];
+			for (j=l;j<m;j++)  /* upper limit was 'n' */
+				u[i][j] = 0.0;
+			if (g != 0.0) {
+				h = u[i][i] * g;
+				for (j=l;j<m;j++) { /* upper limit was 'n' */
+					s = 0.0;
+					for (k=l;k<m;k++)
+						s += (u[k][i] * u[k][j]);
+					f = s / h;
+					for (k=i;k<m;k++) 
+						u[k][j] += (f * u[k][i]);
+				} /* end j */
+				for (j=i;j<m;j++) 
+					u[j][i] /= g;
+			} /* end g */
+			else {
+				for (j=i;j<m;j++)
+					u[j][i] = 0.0;
+			}
+			u[i][i] += 1.0;
+		} /* end i*/
+	} /* end withu, parens added for clarity */
+
+/* diagonalization of the bidiagonal form */
+	eps *= x;
+	for (k=n-1;k>=0;k--) {
+		iter = 0;
+test_f_splitting:
+		for (l=k;l>=0;l--) {
+			if (fabs(e[l]) <= eps) goto test_f_convergence;
+			if (fabs(q[l-1]) <= eps) goto cancellation;
+		} /* end l */
+
+/* cancellation of e[l] if l > 0 */
+cancellation:
+		c = 0.0;
+		s = 1.0;
+		l1 = l - 1;
+		for (i=l;i<=k;i++) {
+			f = s * e[i];
+			e[i] *= c;
+			if (fabs(f) <= eps) goto test_f_convergence;
+			g = q[i];
+			h = q[i] = sqrt(f*f + g*g);
+			c = g / h;
+			s = -f / h;
+			if (withu) {
+				for (j=0;j<m;j++) {
+					y = u[j][l1];
+					z = u[j][i];
+					u[j][l1] = y * c + z * s;
+					u[j][i] = -y * s + z * c;
+				} /* end j */
+			} /* end withu, parens added for clarity */
+		} /* end i */
+test_f_convergence:
+		z = q[k];
+		if (l == k) goto convergence;
+
+/* shift from bottom 2x2 minor */
+		iter++;
+		if (iter > 30) {
+			retval = k;
+			break;
+		}
+		x = q[l];
+		y = q[k-1];
+		g = e[k-1];
+		h = e[k];
+		f = ((y-z)*(y+z) + (g-h)*(g+h)) / (2*h*y);
+		g = sqrt(f*f + 1.0);
+		f = ((x-z)*(x+z) + h*(y/((f<0)?(f-g):(f+g))-h))/x;
+/* next QR transformation */
+		c = s = 1.0;
+		for (i=l+1;i<=k;i++) {
+			g = e[i];
+			y = q[i];
+			h = s * g;
+			g *= c;
+			e[i-1] = z = sqrt(f*f+h*h);
+			c = f / z;
+			s = h / z;
+			f = x * c + g * s;
+			g = -x * s + g * c;
+			h = y * s;
+			y *= c;
+			if (withv) {
+				for (j=0;j<n;j++) {
+					x = v[j][i-1];
+					z = v[j][i];
+					v[j][i-1] = x * c + z * s;
+					v[j][i] = -x * s + z * c;
+				} /* end j */
+			} /* end withv, parens added for clarity */
+			q[i-1] = z = sqrt(f*f + h*h);
+			c = f/z;
+			s = h/z;
+			f = c * g + s * y;
+			x = -s * g + c * y;
+			if (withu) {
+				for (j=0;j<m;j++) {
+					y = u[j][i-1];
+					z = u[j][i];
+					u[j][i-1] = y * c + z * s;
+					u[j][i] = -y * s + z * c;
+				} /* end j */
+			} /* end withu, parens added for clarity */
+		} /* end i */
+		e[l] = 0.0;
+		e[k] = f;
+		q[k] = x;
+		goto test_f_splitting;
+convergence:
+		if (z < 0.0) {
+/* q[k] is made non-negative */
+			q[k] = - z;
+			if (withv) {
+				for (j=0;j<n;j++)
+					v[j][k] = -v[j][k];
+			} /* end withv, parens added for clarity */
+		} /* end z */
+	} /* end k */
+	
+	free(e);
+	return retval;
+}
Index: /branches/sc_branches/pantasks_condor/lib.data/svdcmp_bond_raw.c
===================================================================
--- /branches/sc_branches/pantasks_condor/lib.data/svdcmp_bond_raw.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/lib.data/svdcmp_bond_raw.c	(revision 34783)
@@ -0,0 +1,244 @@
+/*  svd.c -- Singular value decomposition. Translated to 'C' from the
+ *           original Algol code in "Handbook for Automatic Computation,
+ *           vol. II, Linear Algebra", Springer-Verlag.
+ *
+ *  (C) 2000, C. Bond. All rights reserved.
+ *
+ *  This is almost an exact translation from the original, except that
+ *  an iteration counter is added to prevent stalls. This corresponds
+ *  to similar changes in other translations.
+ *
+ *  Returns an error code = 0, if no errors and 'k' if a failure to
+ *  converge at the 'kth' singular value.
+ * 
+ */
+#include <malloc.h> /* for array allocation */
+#include <math.h>    /* for 'fabs'           */
+
+int svdcmp_bond_raw(int m, int n, int withu, int withv, double eps, double tol, double **a, double *q, double **u, double **v)
+{
+	int i,j,k,l,l1,iter,retval;
+	double c,f,g,h,s,x,y,z;
+	double *e;
+
+	e = (double *)calloc(n,sizeof(double));
+	retval = 0;
+
+/* Copy 'a' to 'u' */    
+	for (i=0;i<m;i++) {
+		for (j=0;j<n;j++)
+			u[i][j] = a[i][j];
+	}
+/* Householder's reduction to bidiagonal form. */
+	g = x = 0.0;    
+	for (i=0;i<n;i++) {
+		e[i] = g;
+		s = 0.0;
+		l = i+1;
+		for (j=i;j<m;j++)
+			s += (u[j][i]*u[j][i]);
+		if (s < tol)
+			g = 0.0;
+		else {
+			f = u[i][i];
+			g = (f < 0) ? sqrt(s) : -sqrt(s);
+			h = f * g - s;
+			u[i][i] = f - g;
+			for (j=l;j<n;j++) {
+				s = 0.0;
+				for (k=i;k<m;k++)
+					s += (u[k][i] * u[k][j]);
+				f = s / h;
+				for (k=i;k<m;k++)
+					u[k][j] += (f * u[k][i]);
+			} /* end j */
+		} /* end s */
+		q[i] = g;
+		s = 0.0;
+		for (j=l;j<n;j++)
+			s += (u[i][j] * u[i][j]);
+		if (s < tol)
+			g = 0.0;
+		else {
+			f = u[i][i+1];
+			g = (f < 0) ? sqrt(s) : -sqrt(s);
+			h = f * g - s;
+			u[i][i+1] = f - g;
+			for (j=l;j<n;j++) 
+				e[j] = u[i][j]/h;
+			for (j=l;j<m;j++) {
+				s = 0.0;
+				for (k=l;k<n;k++) 
+					s += (u[j][k] * u[i][k]);
+				for (k=l;k<n;k++)
+					u[j][k] += (s * e[k]);
+			} /* end j */
+		} /* end s */
+		y = fabs(q[i]) + fabs(e[i]);                         
+		if (y > x)
+			x = y;
+	} /* end i */
+
+/* accumulation of right-hand transformations */
+	if (withv) {
+		for (i=n-1;i>=0;i--) {
+			if (g != 0.0) {
+				h = u[i][i+1] * g;
+				for (j=l;j<n;j++)
+					v[j][i] = u[i][j]/h;
+				for (j=l;j<n;j++) {
+					s = 0.0;
+					for (k=l;k<n;k++) 
+						s += (u[i][k] * v[k][j]);
+					for (k=l;k<n;k++)
+						v[k][j] += (s * v[k][i]);
+
+				} /* end j */
+			} /* end g */
+			for (j=l;j<n;j++)
+				v[i][j] = v[j][i] = 0.0;
+			v[i][i] = 1.0;
+			g = e[i];
+			l = i;
+		} /* end i */
+ 
+	} /* end withv, parens added for clarity */
+
+/* accumulation of left-hand transformations */
+	if (withu) {
+		for (i=n;i<m;i++) {
+			for (j=n;j<m;j++)
+				u[i][j] = 0.0;
+			u[i][i] = 1.0;
+		}
+	}
+	if (withu) {
+		for (i=n-1;i>=0;i--) {
+			l = i + 1;
+			g = q[i];
+			for (j=l;j<m;j++)  /* upper limit was 'n' */
+				u[i][j] = 0.0;
+			if (g != 0.0) {
+				h = u[i][i] * g;
+				for (j=l;j<m;j++) { /* upper limit was 'n' */
+					s = 0.0;
+					for (k=l;k<m;k++)
+						s += (u[k][i] * u[k][j]);
+					f = s / h;
+					for (k=i;k<m;k++) 
+						u[k][j] += (f * u[k][i]);
+				} /* end j */
+				for (j=i;j<m;j++) 
+					u[j][i] /= g;
+			} /* end g */
+			else {
+				for (j=i;j<m;j++)
+					u[j][i] = 0.0;
+			}
+			u[i][i] += 1.0;
+		} /* end i*/
+	} /* end withu, parens added for clarity */
+
+/* diagonalization of the bidiagonal form */
+	eps *= x;
+	for (k=n-1;k>=0;k--) {
+		iter = 0;
+test_f_splitting:
+		for (l=k;l>=0;l--) {
+			if (fabs(e[l]) <= eps) goto test_f_convergence;
+			if (fabs(q[l-1]) <= eps) goto cancellation;
+		} /* end l */
+
+/* cancellation of e[l] if l > 0 */
+cancellation:
+		c = 0.0;
+		s = 1.0;
+		l1 = l - 1;
+		for (i=l;i<=k;i++) {
+			f = s * e[i];
+			e[i] *= c;
+			if (fabs(f) <= eps) goto test_f_convergence;
+			g = q[i];
+			h = q[i] = sqrt(f*f + g*g);
+			c = g / h;
+			s = -f / h;
+			if (withu) {
+				for (j=0;j<m;j++) {
+					y = u[j][l1];
+					z = u[j][i];
+					u[j][l1] = y * c + z * s;
+					u[j][i] = -y * s + z * c;
+				} /* end j */
+			} /* end withu, parens added for clarity */
+		} /* end i */
+test_f_convergence:
+		z = q[k];
+		if (l == k) goto convergence;
+
+/* shift from bottom 2x2 minor */
+		iter++;
+		if (iter > 30) {
+			retval = k;
+			break;
+		}
+		x = q[l];
+		y = q[k-1];
+		g = e[k-1];
+		h = e[k];
+		f = ((y-z)*(y+z) + (g-h)*(g+h)) / (2*h*y);
+		g = sqrt(f*f + 1.0);
+		f = ((x-z)*(x+z) + h*(y/((f<0)?(f-g):(f+g))-h))/x;
+/* next QR transformation */
+		c = s = 1.0;
+		for (i=l+1;i<=k;i++) {
+			g = e[i];
+			y = q[i];
+			h = s * g;
+			g *= c;
+			e[i-1] = z = sqrt(f*f+h*h);
+			c = f / z;
+			s = h / z;
+			f = x * c + g * s;
+			g = -x * s + g * c;
+			h = y * s;
+			y *= c;
+			if (withv) {
+				for (j=0;j<n;j++) {
+					x = v[j][i-1];
+					z = v[j][i];
+					v[j][i-1] = x * c + z * s;
+					v[j][i] = -x * s + z * c;
+				} /* end j */
+			} /* end withv, parens added for clarity */
+			q[i-1] = z = sqrt(f*f + h*h);
+			c = f/z;
+			s = h/z;
+			f = c * g + s * y;
+			x = -s * g + c * y;
+			if (withu) {
+				for (j=0;j<m;j++) {
+					y = u[j][i-1];
+					z = u[j][i];
+					u[j][i-1] = y * c + z * s;
+					u[j][i] = -y * s + z * c;
+				} /* end j */
+			} /* end withu, parens added for clarity */
+		} /* end i */
+		e[l] = 0.0;
+		e[k] = f;
+		q[k] = x;
+		goto test_f_splitting;
+convergence:
+		if (z < 0.0) {
+/* q[k] is made non-negative */
+			q[k] = - z;
+			if (withv) {
+				for (j=0;j<n;j++)
+					v[j][k] = -v[j][k];
+			} /* end withv, parens added for clarity */
+		} /* end z */
+	} /* end k */
+	
+	free(e);
+	return retval;
+}
Index: /branches/sc_branches/pantasks_condor/lib.shell/BufferOps.c
===================================================================
--- /branches/sc_branches/pantasks_condor/lib.shell/BufferOps.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/lib.shell/BufferOps.c	(revision 34783)
@@ -0,0 +1,271 @@
+# include "opihi.h"
+
+static Buffer **buffers;
+static int     Nbuffers;
+
+static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
+
+// this function is NOT thread protected : it is only used in startup and/or shutdown
+void InitBuffers () {
+  Nbuffers = 0;
+  ALLOCATE (buffers, Buffer *, 1);
+}
+
+// this function is NOT thread protected : it is only used in startup and/or shutdown
+void FreeBuffers () {
+
+  int i;
+
+  for (i = 0; i < Nbuffers; i++) {
+    gfits_free_header (&buffers[i][0].header);
+    gfits_free_matrix (&buffers[i][0].matrix);
+    free (buffers[i]);
+  }
+  free (buffers);
+}
+
+Buffer *InitBuffer () {
+  Buffer *buf;
+
+  ALLOCATE (buf, Buffer, 1);
+  bzero (buf[0].name, 1024);
+  bzero (buf[0].file, 1024);
+  ALLOCATE (buf[0].matrix.buffer, char, 1);
+  ALLOCATE (buf[0].header.buffer, char, 1);
+  return (buf);
+}
+
+int IsBuffer (char *name) {
+ 
+  int i;
+
+  if (name == NULL) return (FALSE);
+
+  for (i = 0; (i < Nbuffers) && (strcmp(buffers[i][0].name, name)); i++);
+  if (i == Nbuffers) return (FALSE);
+  return (TRUE);
+}
+
+int IsBufferPtr (Buffer *buf) {
+ 
+  int i;
+
+  if (buf == NULL) return (FALSE);
+
+  for (i = 0; (i < Nbuffers) && (buffers[i] != buf); i++);
+  if (i == Nbuffers) return (FALSE);
+  return (TRUE);
+}
+
+Buffer *SelectBuffer (char *name, int mode, int verbose) {
+
+  int i;
+
+  if (name == NULL) goto error;
+  if (ISNUM(name[0])) goto error;
+  if (IsVector(name)) goto error;
+
+  for (i = 0; (i < Nbuffers) && (strcmp(buffers[i][0].name, name)); i++);
+  /* is a new buffer */
+  if (i == Nbuffers) { 
+    if (mode == OLDBUFFER) goto error;
+    // lock to protect static array
+    pthread_mutex_lock (&mutex);
+    Nbuffers ++;
+    REALLOCATE (buffers, Buffer *, Nbuffers);
+    buffers[i] = InitBuffer ();
+    strcpy (buffers[i][0].name, name);
+    pthread_mutex_unlock (&mutex);
+    return (buffers[i]);
+  } 
+  /* is an old buffer */
+  if (mode == NEWBUFFER) goto error;
+  return (buffers[i]);
+
+error:
+  if (verbose) gprint (GP_ERR, "invalid matrix %s\n", name);
+  return (NULL);
+}
+
+int CreateBuffer (Buffer *buf, int Nx, int Ny, int bitpix, float bzero, float bscale) {
+
+  /* store the default output values */
+  gfits_init_header (&buf[0].header);
+
+  /* assign the necessary internal values */
+  buf[0].header.bitpix   = -32;
+  buf[0].header.Naxes = 2;
+  buf[0].header.Naxis[0] = Nx;
+  buf[0].header.Naxis[1] = Ny;
+
+  buf[0].bitpix = bitpix;
+  buf[0].bzero  = bzero;
+  buf[0].bscale = bscale;
+  
+  /* make some test of the validity of the values */
+
+  /* create the appropriate header and matrix */
+  gfits_create_header (&buf[0].header);
+  gfits_create_matrix (&buf[0].header, &buf[0].matrix);
+
+  return (TRUE);
+}
+  
+/* copy data from in to out - new memory space */
+int CopyNamedBuffer (char *out, char *in) {
+  Buffer *In, *Out;
+  if ((In  = SelectBuffer (in,  OLDBUFFER, FALSE)) == NULL) return (FALSE);
+  if ((Out = SelectBuffer (out, ANYBUFFER, FALSE)) == NULL) return (FALSE);
+  CopyBuffer (Out, In);
+  return (TRUE);
+}
+
+int CopyBuffer (Buffer *out, Buffer *in) {
+  out[0].bitpix = in[0].bitpix;
+  out[0].unsign = in[0].unsign;
+  out[0].bscale = in[0].bscale;
+  out[0].bzero  = in[0].bzero;
+  strcpy (out[0].file, in[0].file);
+  gfits_copy_matrix (&in[0].matrix, &out[0].matrix);
+  gfits_copy_header (&in[0].header, &out[0].header);
+  return (TRUE);
+}
+
+/* move data from in to out - use old memory space */
+int MoveNamedBuffer (char *out, char *in) {
+  Buffer *In, *Out;
+  if ((In  = SelectBuffer (in,  OLDBUFFER, FALSE)) == NULL) return (FALSE);
+  if ((Out = SelectBuffer (out, ANYBUFFER, FALSE)) == NULL) return (FALSE);
+  MoveBuffer (Out, In);
+  return (TRUE);
+}
+int MoveBuffer (Buffer *out, Buffer *in) {
+  int i, j;
+
+  free (out[0].matrix.buffer);
+  free (out[0].header.buffer);
+  out[0].bitpix = in[0].bitpix;
+  out[0].unsign = in[0].unsign;
+  out[0].bscale = in[0].bscale;
+  out[0].bzero  = in[0].bzero;
+  out[0].matrix = in[0].matrix;
+  out[0].header = in[0].header;
+  strcpy (out[0].file, in[0].file);
+
+  /* delete buffer entry from buffer list, if it exists */
+  for (i = 0; (i < Nbuffers) && (in != buffers[i]); i++);
+  if (i == Nbuffers) {
+    free (in);
+    return (TRUE);
+  }
+  free (in);
+
+  pthread_mutex_lock (&mutex);
+  for (j = i; j < Nbuffers - 1; j++) buffers[j] = buffers[j + 1];
+  Nbuffers --;
+  REALLOCATE (buffers, Buffer *, MAX (Nbuffers, 1));
+  pthread_mutex_unlock (&mutex);
+  return (TRUE);
+}
+
+/* delete by ptr */
+int DeleteBuffer (Buffer *buf) {
+
+  int i, j;
+
+  if (buf == NULL) return (FALSE);
+  for (i = 0; (i < Nbuffers) && (buf != buffers[i]); i++);
+  if (i == Nbuffers) return (FALSE);
+
+  gfits_free_header (&buffers[i][0].header);
+  gfits_free_matrix (&buffers[i][0].matrix);
+  free (buffers[i]);
+
+  pthread_mutex_lock (&mutex);
+  for (j = i; j < Nbuffers - 1; j++) buffers[j] = buffers[j + 1];
+  Nbuffers --;
+  REALLOCATE (buffers, Buffer *, MAX (Nbuffers, 1));
+  pthread_mutex_unlock (&mutex);
+  return (TRUE);
+}
+  
+/* delete by name */
+int DeleteNamedBuffer (char *name) {
+
+  int i, j;
+
+  if (name == NULL) return (FALSE);
+  for (i = 0; (i < Nbuffers) && (strcmp(buffers[i][0].name, name)); i++);
+  if (i == Nbuffers) return (FALSE);
+
+  gfits_free_header (&buffers[i][0].header);
+  gfits_free_matrix (&buffers[i][0].matrix);
+  free (buffers[i]);
+
+  pthread_mutex_lock (&mutex);
+  for (j = i; j < Nbuffers - 1; j++) buffers[j] = buffers[j + 1];
+  Nbuffers --;
+  REALLOCATE (buffers, Buffer *, MAX (Nbuffers, 1));
+  pthread_mutex_unlock (&mutex);
+  return (TRUE);
+}
+
+void dump_buffers (int n) {
+
+  int i;
+
+  gprint (GP_ERR, "try %d\n", n);
+  for (i = 0; i < Nbuffers; i++) {
+    gprint (GP_ERR, "%d  %lx\n", i, (long) buffers[i]);
+    gprint (GP_ERR, "%d  %lx  %s\n", i, (long) buffers[i][0].name, buffers[i][0].name);
+    gprint (GP_ERR, "%d  %lx  %s\n", i, (long) buffers[i][0].file, buffers[i][0].file);
+    gprint (GP_ERR, "%d  %lx  %lx\n", i, (long) &buffers[i][0].header, (long) buffers[i][0].header.buffer);
+    gprint (GP_ERR, "%d  %lx  %lx\n", i, (long) &buffers[i][0].matrix, (long) buffers[i][0].matrix.buffer);
+    gprint (GP_ERR, "%d  %d  %d  %f %f\n", i, buffers[i][0].bitpix, buffers[i][0].unsign, buffers[i][0].bscale, buffers[i][0].bzero);
+  }
+}
+
+int PrintBuffers (int Long) {
+
+  int i;
+
+  if (Nbuffers == 0) {
+    gprint (GP_ERR, "No allocated buffers\n");
+    return (TRUE);
+  }
+  
+  if (Long) {
+    gprint (GP_LOG, "    N       name                      file     X     Y    bytes BP  U   bzero     bscale  \n");
+    for (i = 0; i < Nbuffers; i++) {
+      gprint (GP_LOG, "%5d %10s %25s %5lld %5lld %10lld %3d %1d %10.4e %10.4e\n",
+	      i, buffers[i][0].name, buffers[i][0].file, 
+	      (long long) buffers[i][0].header.Naxis[0], (long long) buffers[i][0].header.Naxis[1],
+ 	      (long long) buffers[i][0].header.datasize + buffers[i][0].matrix.datasize, buffers[i][0].bitpix, 
+	      buffers[i][0].unsign, buffers[i][0].bzero, buffers[i][0].bscale);
+    }
+    return (TRUE);
+  }
+
+  gprint (GP_LOG, "    N       name                      file     X     Y    bytes\n");
+  for (i = 0; i < Nbuffers; i++) {
+    gprint (GP_LOG, "%5d %10s %25s %5lld %5lld %10lld\n",
+	    i, buffers[i][0].name, buffers[i][0].file, 
+	    (long long) buffers[i][0].header.Naxis[0], (long long) buffers[i][0].header.Naxis[1],
+	    (long long) buffers[i][0].header.datasize + buffers[i][0].matrix.datasize);
+  }
+  return (TRUE);
+}
+
+int ListBuffersToList (char *name) {
+
+  int i;
+  char line[1024];
+
+  for (i = 0; i < Nbuffers; i++) {
+    sprintf (line, "%s:%d", name, i);
+    set_str_variable (line, buffers[i][0].name);
+  }
+  sprintf (line, "%s:n", name);
+  set_int_variable (line, Nbuffers);
+  return (Nbuffers);
+}
Index: /branches/sc_branches/pantasks_condor/lib.shell/CommandOps.c
===================================================================
--- /branches/sc_branches/pantasks_condor/lib.shell/CommandOps.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/lib.shell/CommandOps.c	(revision 34783)
@@ -0,0 +1,193 @@
+# include "opihi.h"
+
+// the command table is only modified by macro_create: if thread protection is needed, it
+// should be added there.  it may not be needed if programs only call macro_create in a
+// single thread.  pantasks_server only calls macro_create in the 'input' thread, so it is
+// safe.
+
+// if the user has installed the libedit version of readline, we need to modify a couple symbols:
+# ifndef RL_READLINE_VERSION
+# define rl_completion_matches(A,B) completion_matches(A,B)
+# endif
+
+static Command  *commands;
+static int      Ncommands;
+static int      NCOMMANDS;
+
+void InitCommands () {
+  NCOMMANDS = 20;
+  Ncommands = 0;
+  ALLOCATE (commands, Command, NCOMMANDS);
+}
+
+void FreeCommands () {
+  int i;
+
+  for (i = 0; i < Ncommands; i++) {
+    if (commands[i].real) continue;
+    free (commands[i].name);
+    free (commands[i].help);
+  }
+  free (commands);
+}
+
+void AddCommand (Command *new) {
+  
+  commands[Ncommands] = *new;
+  Ncommands ++;
+  if (Ncommands == NCOMMANDS) {
+    NCOMMANDS += 20;
+    REALLOCATE (commands, Command, NCOMMANDS);
+  }
+}
+
+int DeleteCommand (Command *command) {
+
+  int i, Nc;
+
+  Nc = -1;
+  for (i = 0; i < Ncommands; i++) {
+    if (command == &commands[i]) {
+      Nc = i;
+      break;
+    }
+  }
+  if (Nc == -1) {
+    gprint (GP_ERR, "programming error: command not found\n");
+    return (FALSE);
+  }
+
+  free (commands[Nc].name);
+  for (i = Nc + 1; i < Ncommands; i++)
+    commands[i - 1] = commands[i];
+  Ncommands --;
+  REALLOCATE (commands, Command, Ncommands);
+  return (TRUE);
+}
+
+/* return command which unambiguously matches name */
+Command *MatchCommand (char *name, int VERBOSE, int EXACT) {
+
+  int i, match[10], Nmatch;
+
+  /* try for an exact match first */
+  for (i = 0; i < Ncommands; i++) {
+    if (!strcmp (commands[i].name, name)) {
+      return (&commands[i]);
+    }
+  }
+  if (EXACT) {
+    if (VERBOSE) gprint (GP_ERR, "no exact match to %s\n", name);
+    return (NULL);
+  }
+
+  /* not found as complete command, try partial */
+  Nmatch = 0;
+  for (i = 0; (Nmatch < 10) && (i < Ncommands); i++) {
+    if (!strncmp (commands[i].name, name, strlen(name))) {  /* found a command */
+      match[Nmatch] = i;
+      Nmatch ++;
+    }
+  }
+  if (Nmatch == 1) return (&commands[match[0]]);
+
+  if (Nmatch > 1) {
+    if (VERBOSE) {
+      gprint (GP_ERR, "ambiguous command: %s ( ", name);
+      for (i = 0; i < Nmatch; i++) {
+	gprint (GP_ERR, "%s ", commands[match[i]].name);
+      }
+      gprint (GP_ERR, ")\n");
+    }
+    return (NULL);
+  }
+  if (VERBOSE) gprint (GP_ERR, "%s: Command not found.\n", name);
+  return (NULL);
+}  
+
+/* we need a strcreate function that does NOT use the ohana memory management
+ * system to interact with some external libraries (which themselves free the memory)
+ */
+char *strcreate_sans_ohana (char *string) {
+
+  char *line;
+
+  if (string == (char *) NULL) return ((char *) NULL);
+  
+  line = malloc (MAX (1, strlen(string)) + 1);
+  line = strcpy (line, string);
+
+  return (line);
+}
+
+/* generate a command completion list for readline */
+/**** these probably do not interact well with OHANA memory!!! ****/
+char *command_generator (const char *text, int state) {
+
+  /* i must be remembered from call to call */
+  static int i, len;
+
+  /* On first call, state is set to 0: initial the state */
+  if (!state) {
+    i = -1;  
+    len = strlen (text);
+  }
+
+  /* Return the next partial match from the command list */
+  for (i++; i < Ncommands; i++) {
+    if (!len) 
+
+
+      return (strcreate_sans_ohana(commands[i].name));
+    if (!strncmp (commands[i].name, text, len)) {
+      return (strcreate_sans_ohana(commands[i].name));
+    }
+  }
+
+  /* If no names matched, then return NULL. */
+  return ((char *)NULL);
+}
+
+/* tell readline to use out command_generator rather than basic completion */
+char **command_completer (const char *text, int start, int end) {
+  
+  char **matches;
+
+  matches = (char **) NULL;
+  
+  if (start == 0) {
+    matches = rl_completion_matches (text, command_generator);
+  }
+
+  return (matches);
+}
+
+void sort_commands (int *seq) {
+
+  int i;
+
+  for (i = 0; i < Ncommands; i++) seq[i] = i;
+
+# define SWAPFUNC(A,B){ int tmp = seq[A]; seq[A] = seq[B]; seq[B] = tmp; }
+# define COMPARE(A,B)(strcmp (commands[seq[A]].name, commands[seq[B]].name) < 0)
+
+  OHANA_SORT (Ncommands, COMPARE, SWAPFUNC);
+
+# undef SWAPFUNC
+# undef COMPARE
+  
+}
+
+void print_commands (FILE *f) {
+
+  int i, *seq;
+
+  ALLOCATE (seq, int, Ncommands);
+  sort_commands (seq);
+  for (i = 0; i < Ncommands; i++) {
+    fprintf (f, "%-25s -- %s\n", commands[seq[i]].name, commands[seq[i]].help);
+  }
+  free (seq);
+
+  return;
+}
Index: /branches/sc_branches/pantasks_condor/lib.shell/ConfigInit.c
===================================================================
--- /branches/sc_branches/pantasks_condor/lib.shell/ConfigInit.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/lib.shell/ConfigInit.c	(revision 34783)
@@ -0,0 +1,73 @@
+# include "opihi.h"
+
+static char *GlobalConfig = NULL;
+
+// this function is only called at start, so it is not thread protect
+int ConfigInit (int *argc, char **argv) {
+
+  char *config, *file;
+
+  /*** load configuration info ***/
+  file = SelectConfigFile (argc, argv, "ptolemy");
+  config = LoadConfigFile (file);
+  if (config == (char *) NULL) {
+    if (file != (char *) NULL) free (file);
+    return (FALSE);
+  }
+
+  GlobalConfig = config;
+
+  free (file);
+  return (TRUE);
+}
+
+// this function is only called at shutdown, so it is not thread protect
+void ConfigFree () {
+  if (GlobalConfig) free (GlobalConfig);
+  return;
+}
+
+char *VarConfig (char *keyword, char *mode, void *ptr) {
+
+  char *answer;
+
+  answer = get_variable (keyword);
+  if (answer == (char *) NULL) {
+    answer = ScanConfig (GlobalConfig, keyword, mode, 0, ptr);
+    return (answer);
+  }
+
+  if (!strcmp (mode, "%s"))  strcpy ((char *) ptr, answer);
+  if (!strcmp (mode, "%d"))  *(int *) ptr       = atoi (answer);
+  if (!strcmp (mode, "%u"))  *(unsigned *) ptr  = atoi (answer);
+  if (!strcmp (mode, "%ld")) *(long *) ptr      = atoi (answer);
+  if (!strcmp (mode, "%hd")) *(short *) ptr     = atoi (answer);
+  if (!strcmp (mode, "%f"))  *(float *) ptr     = atof (answer);
+  if (!strcmp (mode, "%lf")) *(double *) ptr    = atof (answer);
+
+  free (answer);
+  return (ptr);
+}
+
+char *VarConfigEntry (char *keyword, char *mode, int entry, void *ptr) {
+
+  char *answer;
+
+  answer = get_variable (keyword);
+  if (answer == (char *) NULL) {
+    answer = ScanConfig (GlobalConfig, keyword, mode, entry, ptr);
+    return (answer);
+  }
+
+  if (!strcmp (mode, "%s"))  strcpy ((char *) ptr, answer);
+  if (!strcmp (mode, "%d"))  *(int *) ptr       = atoi (answer);
+  if (!strcmp (mode, "%u"))  *(unsigned *) ptr  = atoi (answer);
+  if (!strcmp (mode, "%ld")) *(long *) ptr      = atoi (answer);
+  if (!strcmp (mode, "%hd")) *(short *) ptr     = atoi (answer);
+  if (!strcmp (mode, "%f"))  *(float *) ptr     = atof (answer);
+  if (!strcmp (mode, "%lf")) *(double *) ptr    = atof (answer);
+
+  free (answer);
+  return (ptr);
+}
+
Index: /branches/sc_branches/pantasks_condor/lib.shell/ListOps.c
===================================================================
--- /branches/sc_branches/pantasks_condor/lib.shell/ListOps.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/lib.shell/ListOps.c	(revision 34783)
@@ -0,0 +1,290 @@
+# include "opihi.h"
+# include "macro.h"
+
+/*** local static variables used to track the command lists  ***/
+static List *lists = NULL;	/* variable to store the list of all lists */
+static int  Nlists = 0;		/* number of currently available lists */
+
+void InitLists () {
+  Nlists = 0;
+  ALLOCATE (lists, List, 1); 
+  return;
+}
+
+void FreeLists () {
+
+  int i, j;
+
+  // Nlists is a bit weird: it is the currently highest valid list, not the number of lists
+  // Nlists = 0 is never allocated
+  for (i = 1; i < Nlists + 1; i++) {
+    for (j = 0; j < lists[i].Nlines; j++) {
+      free (lists[i].line[j]);
+    }
+    free (lists[i].line);
+  }
+  free (lists);
+}
+
+int current_list_depth () {
+  return Nlists;
+}
+
+int increase_list_depth () {
+  Nlists ++;
+  REALLOCATE (lists, List, MAX (Nlists + 1, 0) + 1);
+  ALLOCATE (lists[Nlists].line, char *, 16);
+  lists[Nlists].Nalloc = 16;
+  lists[Nlists].Nlines = 0;
+  lists[Nlists].n = 0;
+  return Nlists;
+}
+
+int decrease_list_depth () {
+  
+  int i;
+
+  for (i = 0; i < lists[Nlists].Nlines; i++) {
+    free (lists[Nlists].line[i]);
+  }
+  free (lists[Nlists].line);
+  Nlists --;
+  REALLOCATE (lists, List, MAX (Nlists + 1, 0) + 1);
+  return Nlists;
+}
+
+/* return a new string consisting of the next line in the current list */
+char *get_next_listentry (int ThisList) {
+
+  int Nline;
+  char *output;
+
+  Nline = lists[ThisList].n;
+
+  if (Nline >= lists[ThisList].Nlines) return (NULL);
+
+  output = strcreate (lists[ThisList].line[Nline]);
+  lists[ThisList].n ++;
+  
+  return (output);
+}
+
+# if (0)
+char *remove_listentry (int current) {
+
+  int i;
+  char *output;
+
+  if ((current + 1) >= lists[Nlists].Nlines) 
+    return ((char *) NULL);
+
+  output = lists[Nlists].line[current + 1];
+  
+  for (i = current + 1; i < lists[Nlists].Nlines - 1; i++) {
+    lists[Nlists].line[i] = lists[Nlists].line[i + 1];
+  }
+
+  lists[Nlists].Nlines --;
+  return (output);
+
+}
+# endif 
+
+int add_listentry (int ThisList, char *line) {
+
+  int Nlines;
+
+  Nlines = lists[ThisList].Nlines;
+  lists[ThisList].line[Nlines] = strcreate (line);
+  lists[ThisList].Nlines ++;
+
+  if (lists[ThisList].Nlines == lists[ThisList].Nalloc) {
+    lists[ThisList].Nalloc += 16;
+    REALLOCATE (lists[ThisList].line, char *, lists[ThisList].Nalloc);
+  }
+    
+  return (lists[ThisList].Nlines);
+}
+
+int is_for_loop (char *line) {
+
+  int status;
+  char *comm;
+
+  comm = thisword (line);
+  if (comm == (char *) NULL) return (FALSE);
+  
+  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);
+}
+
+int is_macro_create (char *line) {
+
+  int status;
+  char *comm;
+  char *this_macro;
+  CommandF *cmd;
+
+  comm = thisword (line);
+  if (comm == NULL) return (FALSE);
+
+  status = !strcmp (comm, "macro");
+  free (comm);
+  if (!status) return (FALSE);
+  
+  this_macro = thisword (nextword (line));
+  if (this_macro == NULL) return (FALSE);
+
+  cmd = find_macro_command (this_macro);
+  free (this_macro);
+
+  if (cmd == NULL) {
+    return (FALSE);
+  }
+
+  return (TRUE);
+}
+
+int is_if_block (char *line) {
+
+  char *comm, *temp;
+
+  temp = thisword (nextword (nextword (line)));
+  comm = thisword (line);
+
+  if (comm == NULL) goto escape;
+
+  if (strcmp (comm, "if")) goto escape;
+
+  /* if (cond) (command) does not define a complete block */
+  if (temp != NULL) goto escape;
+
+  if (temp != NULL) free (temp);
+  if (comm != NULL) free (comm);
+  return (TRUE);
+
+escape: 
+  if (comm != NULL) free (comm);
+  if (temp != NULL) free (temp);
+  return (FALSE);
+}
+
+// list (word) : nested list
+// list (word) -x : not nested list
+// list (word) -split : not nested list
+int is_list_data (char *line) {
+
+  char *comm, *temp;
+
+  temp = thisword (nextword (nextword (line)));
+  comm = thisword (line);
+
+  if (comm == NULL) goto escape;
+
+  if (strcmp (comm, "list")) goto escape;
+
+  /* if (cond) (command) does not define a complete block */
+  if (temp != NULL) {
+      if (!strcmp (temp, "-x")) goto escape;
+      if (!strcmp (temp, "-split")) goto escape;
+      if (!strcmp (temp, "-splitbychar")) goto escape;
+      if (!strcmp (temp, "-copy")) goto escape;
+      if (!strcmp (temp, "-add")) goto escape;
+      if (!strcmp (temp, "-del")) goto escape;
+      if (!strcmp (temp, "-vectors")) goto escape;
+      if (!strcmp (temp, "-buffers")) goto escape;
+  }
+
+  if (temp != NULL) free (temp);
+  if (comm != NULL) free (comm);
+  return (TRUE);
+
+escape: 
+  if (comm != NULL) free (comm);
+  if (temp != NULL) free (temp);
+  return (FALSE);
+}
+
+int is_loop (char *line) {
+
+  int status;
+  char *comm;
+
+  comm = thisword (line);
+  if (comm == (char *) NULL) return (FALSE);
+
+  status = !strcmp (comm, "while");
+  free (comm);
+  return (status);
+}
+
+int is_task (char *line) {
+
+  int status;
+  char *comm;
+
+  comm = thisword (line);
+  if (comm == (char *) NULL) return (FALSE);
+
+  status = !strcmp (comm, "task");
+  free (comm);
+  return (status);
+}
+
+int is_task_exit (char *line) {
+
+  int status;
+  char *comm;
+
+  comm = thisword (line);
+  if (comm == (char *) NULL) return (FALSE);
+
+  status = !strcmp (comm, "task.exit");
+  free (comm);
+  return (status);
+}
+
+int is_task_exec (char *line) {
+
+  int status;
+  char *comm;
+
+  comm = thisword (line);
+  if (comm == (char *) NULL) return (FALSE);
+
+  status = !strcmp (comm, "task.exec");
+  free (comm);
+  return (status);
+}
+
+int is_list (char *line) {
+  
+  int status;
+
+  status = is_if_block (line);
+  status |= is_macro_create (line);
+  status |= is_for_loop (line);
+  status |= is_foreach_loop (line);
+  status |= is_list_data (line);
+  status |= is_loop (line);
+  status |= is_task (line);
+  status |= is_task_exit (line);
+  status |= is_task_exec (line);
+
+  return (status);
+
+}
Index: /branches/sc_branches/pantasks_condor/lib.shell/MacroOps.c
===================================================================
--- /branches/sc_branches/pantasks_condor/lib.shell/MacroOps.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/lib.shell/MacroOps.c	(revision 34783)
@@ -0,0 +1,158 @@
+# include "opihi.h"
+
+// the macro table is only modified by macro_create: if thread protection is needed, it
+// should be added there.  it may not be needed if programs only call macro_create in a
+// single thread.  pantasks_server only calls macro_create in the 'input' thread, so it is
+// safe.
+
+static char dot[] = ".";
+
+static Macro *macros;
+static int   Nmacros;
+static int   NMACROS;
+
+static char *MacroName = dot;
+static int   MacroDepth = 0;
+
+void InitMacros () {
+  NMACROS = 20;
+  Nmacros = 0;
+  ALLOCATE (macros, Macro, NMACROS);
+  MacroName = dot;
+  MacroDepth = 0;
+}
+
+void FreeMacros () {
+
+  int i;
+
+  for (i = 0; i < Nmacros; i++) {
+    FreeMacro (&macros[i]);
+  }
+  free (macros);
+}
+
+void SetCurrentMacroData (char *name, int depth) {
+  MacroName = name;
+  MacroDepth = depth;
+}
+
+char *GetMacroName () {
+  return (MacroName);
+}
+
+int GetMacroDepth () {
+  return (MacroDepth);
+}
+
+Macro *NewMacro (char *name) {
+  
+  macros[Nmacros].name = strcreate (name);;
+  macros[Nmacros].Nlines = 0;
+  ALLOCATE (macros[Nmacros].line, char *, 1);
+  Nmacros ++;
+  if (Nmacros == NMACROS) {
+    NMACROS += 20;
+    REALLOCATE (macros, Macro, NMACROS);
+  }
+  return (&macros[Nmacros-1]);
+}
+
+void ListMacro (Macro *macro) {
+
+  int i;
+
+  if ((macro == NULL) || (macro[0].Nlines == 0)) {
+    gprint (GP_ERR, "  macro not defined\n");
+    return;
+  }
+  for (i = 0; i < macro[0].Nlines; i++) {
+    gprint (GP_ERR, "  %s\n", macro[0].line[i]);
+  }
+  return;
+}
+
+void ListMacros () {
+  int i;
+  for (i = 0; i < Nmacros; i++) {
+    gprint (GP_ERR, "%s\n", macros[i].name);
+  }
+}
+
+void FreeMacro (Macro *macro) {
+  
+  int i;
+
+  if (macro == NULL) return;
+
+  for (i = 0; i < macro[0].Nlines; i++) {
+    free (macro[0].line[i]);
+  }
+  free (macro[0].line);
+  free (macro[0].name);
+  return;
+}
+
+int DeleteMacro (Macro *macro) {
+
+  int i, Nm;
+
+  Nm = -1;
+  for (i = 0; i < Nmacros; i++) {
+    if (macro == &macros[i]) {
+      Nm = i;
+      break;
+    }
+  }
+  if (Nm == -1) {
+    gprint (GP_ERR, "programming error: macro not found\n");
+    return (FALSE);
+  }
+
+  FreeMacro (&macros[Nm]);
+  for (i = Nm + 1; i < Nmacros; i++)
+    macros[i - 1] = macros[i];
+  Nmacros --;
+  return (TRUE);
+}
+
+/* return macro which unambiguously matches name */
+Macro *MatchMacro (char *name, int VERBOSE, int EXACT) {
+
+  int i, match[10], Nmatch;
+
+  /* try for an exact match first */
+  for (i = 0; i < Nmacros; i++) {
+    if (!strcmp (macros[i].name, name)) {
+      return (&macros[i]);
+    }
+  }
+  if (EXACT) {
+    if (VERBOSE) gprint (GP_ERR, "no exact match to %s\n", name);
+    return (NULL);
+  }
+      
+  /* not found as complete macro, try partial */
+  Nmatch = 0;
+  for (i = 0; (Nmatch < 10) && (i < Nmacros); i++) {
+    if (!strncmp (macros[i].name, name, strlen(name))) {  /* found a macro */
+      match[Nmatch] = i;
+      Nmatch ++;
+    }
+  }
+  if (Nmatch == 1) return (&macros[match[0]]);
+
+  if (Nmatch > 1) {
+    if (VERBOSE) {
+      gprint (GP_ERR, "ambiguous macro: %s ( ", name);
+      for (i = 0; i < Nmatch; i++) {
+	gprint (GP_ERR, "%s ", macros[match[i]].name);
+      }
+      gprint (GP_ERR, ")\n");
+    }
+    return (NULL);
+  }
+  if (VERBOSE) gprint (GP_ERR, "%s: Macro not found.\n", name);
+  return (NULL);
+}  
+
Index: /branches/sc_branches/pantasks_condor/lib.shell/Makefile
===================================================================
--- /branches/sc_branches/pantasks_condor/lib.shell/Makefile	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/lib.shell/Makefile	(revision 34783)
@@ -0,0 +1,76 @@
+default: libshell
+
+include ../../../Makefile.System
+HOME    =       $(ROOT)/src/opihi
+BIN     =       $(HOME)/bin
+LIB     =       $(HOME)/lib
+INC     =       $(HOME)/include
+SDIR    =       $(HOME)/lib.shell
+include ../Makefile.Common
+
+# programs may add their own internal requirements here
+FULL_CFLAGS   = $(BASE_CFLAGS) -fPIC
+FULL_CPPFLAGS = $(BASE_CPPFLAGS)
+FULL_LDFLAGS  = $(BASE_LDFLAGS)
+
+# opihi shell functions (libopihi) ########################
+shell = \
+$(SDIR)/BufferOps.$(ARCH).o             \
+$(SDIR)/CommandOps.$(ARCH).o		\
+$(SDIR)/ConfigInit.$(ARCH).o		\
+$(SDIR)/ListOps.$(ARCH).o	   	\
+$(SDIR)/MacroOps.$(ARCH).o		\
+$(SDIR)/SocketOps.$(ARCH).o             \
+$(SDIR)/VariableOps.$(ARCH).o	   	\
+$(SDIR)/VectorOps.$(ARCH).o             \
+$(SDIR)/VectorIO.$(ARCH).o             \
+$(SDIR)/check_stack.$(ARCH).o           \
+$(SDIR)/command.$(ARCH).o               \
+$(SDIR)/convert_to_RPN.$(ARCH).o	\
+$(SDIR)/dvomath.$(ARCH).o               \
+$(SDIR)/evaluate_stack.$(ARCH).o  	\
+$(SDIR)/exec_loop.$(ARCH).o             \
+$(SDIR)/expand_vars.$(ARCH).o           \
+$(SDIR)/expand_vectors.$(ARCH).o        \
+$(SDIR)/gprint.$(ARCH).o		\
+$(SDIR)/interrupt.$(ARCH).o	        \
+$(SDIR)/macro_create.$(ARCH).o          \
+$(SDIR)/macro_delete.$(ARCH).o          \
+$(SDIR)/macro_edit.$(ARCH).o            \
+$(SDIR)/macro_exec.$(ARCH).o            \
+$(SDIR)/macro_funcs.$(ARCH).o           \
+$(SDIR)/macro_list.$(ARCH).o            \
+$(SDIR)/macro_read.$(ARCH).o            \
+$(SDIR)/macro_write.$(ARCH).o		\
+$(SDIR)/memstr.$(ARCH).o                \
+$(SDIR)/multicommand.$(ARCH).o          \
+$(SDIR)/parse.$(ARCH).o                 \
+$(SDIR)/parse_commands.$(ARCH).o	\
+$(SDIR)/stack_math.$(ARCH).o		\
+$(SDIR)/startup.$(ARCH).o		\
+$(SDIR)/string.$(ARCH).o                \
+$(SDIR)/timeformat.$(ARCH).o            \
+$(SDIR)/version.$(ARCH).o	   	\
+$(SDIR)/opihi.$(ARCH).o
+
+# dependancy rules for include files ########################
+incs = \
+$(INC)/opihi.h \
+$(INC)/external.h \
+$(INC)/shell.h \
+$(INC)/dvomath.h \
+$(INC)/display.h 
+
+$(shell) : $(incs)
+
+$(LIB)/libshell.$(ARCH).a: $(shell)
+$(LIB)/libshell.$(ARCH).$(DLLTYPE): $(shell)
+
+$(DESTLIB)/libshell.a: $(LIB)/libshell.$(ARCH).a
+$(DESTLIB)/libshell.$(DLLTYPE): $(LIB)/libshell.$(ARCH).$(DLLTYPE)
+
+libshell: $(DESTLIB)/libshell.a $(DESTLIB)/libshell.$(DLLTYPE)
+
+uninstall:
+	rm -f $(DESTLIB)/libshell.a
+	rm -f $(DESTLIB)/libshell.$(DLLTYPE)
Index: /branches/sc_branches/pantasks_condor/lib.shell/SocketOps.c
===================================================================
--- /branches/sc_branches/pantasks_condor/lib.shell/SocketOps.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/lib.shell/SocketOps.c	(revision 34783)
@@ -0,0 +1,326 @@
+# include "shell.h"
+# include <unistd.h>
+# define HOST_NAME_MAX 256
+
+# define MY_PORT 2000
+# define MY_WAIT 500
+# define DEBUG 0
+
+// these three static variables are only modified in the setup command before
+// the threads are started.  Thread-safety is not a problem for these.
+static int NVALID;
+static int Nvalid;
+static int *VALID;
+
+int GetPortRange (int *start, int *stop, char *portinfo);
+
+int InitServerSocket (SockAddress *Address, char *hostname, char *portinfo) {
+
+  int start, stop;
+  int status, InitSocket, length;
+  char myHostname[HOST_NAME_MAX];
+
+  status = gethostname (myHostname, HOST_NAME_MAX);
+
+  fprintf (stderr, "target host: %s, real host: %s\n", hostname, myHostname);
+
+  GetPortRange (&start, &stop, portinfo);
+
+  fprintf (stderr, "using port range %d - %d\n", start, stop);
+
+  Address[0].sin_family = AF_INET;
+  Address[0].sin_port   = start;
+  Address[0].sin_addr.s_addr = INADDR_ANY; // use this line to bind any address / port?
+
+retry_server:
+
+  length = sizeof(Address[0]);
+
+  InitSocket = socket (PF_INET, SOCK_STREAM, 0);
+  if (InitSocket == -1) {
+    perror ("socket: ");
+    exit (5);
+  }
+
+  if (DEBUG) gprint (GP_ERR, "init sock: %d, len: %d\n", InitSocket, length);
+  status = bind (InitSocket, (struct sockaddr *) Address, length);
+  if (status == -1) {
+
+# if (DEBUG)
+    fprintf (stderr, "errno: %d\n", errno);
+    fprintf (stderr, "EACCES: %d\n", EACCES);
+    fprintf (stderr, "EBADF: %d\n", EBADF);
+    fprintf (stderr, "EINVAL: %d\n", EINVAL);
+    fprintf (stderr, "ENOTSOCK: %d\n", ENOTSOCK);
+    fprintf (stderr, "EFAULT: %d\n", EFAULT);
+    fprintf (stderr, "ELOOP: %d\n", ELOOP);
+    fprintf (stderr, "ENAMETOOLONG: %d\n", ENAMETOOLONG);
+    fprintf (stderr, "ENOENT: %d\n", ENOENT);
+    fprintf (stderr, "ENOMEM: %d\n", ENOMEM);
+    fprintf (stderr, "ENOTDIR: %d\n", ENOTDIR);
+    fprintf (stderr, "EROFS: %d\n", EROFS);
+    fprintf (stderr, "EADDRNOTAVAIL: %d\n", EADDRNOTAVAIL);
+    fprintf (stderr, "EADDRINUSE: %d\n", EADDRINUSE);
+    fprintf (stderr, "ENOSR: %d\n", ENOSR);
+# endif
+
+    if (errno == EADDRINUSE) {
+	Address[0].sin_port ++;
+	if (Address[0].sin_port > stop) {
+	  fprintf (stderr, "failed to find a usable port\n");
+	  exit (6);
+	}
+	goto retry_server;
+    }
+    perror ("bind: ");
+    exit (7);
+  }
+  /* repeated starts of the server are limited by xinetd or something:
+     requires 60sec timeout of the selected socket */
+
+  fprintf (stderr, "bound to port: %d\n", Address[0].sin_port);
+  status = listen (InitSocket, 10);
+  if (status == -1) {
+    perror ("listen: ");
+    exit (8);
+  }
+  return (InitSocket);
+}
+
+int WaitServerSocket (int InitSocket, SockAddress *Address) {
+
+  int i, BindSocket;
+  SockAddress Address_in;
+  socklen_t length;
+  u_int32_t addr;
+
+  Address_in = Address[0];
+
+  length = sizeof(Address_in);
+
+  /* this is a blocking wait; use in a separate thread */
+  fcntl (InitSocket, F_SETFL, !O_NONBLOCK); 
+
+  if (DEBUG) gprint (GP_ERR, "init sock: %d, len: %d\n", InitSocket, length);
+  BindSocket = accept (InitSocket, (struct sockaddr *) &Address_in, &length);
+  if (DEBUG) gprint (GP_ERR, "bind sock: %d\n", BindSocket);
+  if (BindSocket == -1) {
+    perror ("accept: ");
+    exit (9);
+  }
+
+  addr = Address_in.sin_addr.s_addr;
+  if (DEBUG) {
+    gprint (GP_ERR, "incoming connection from: ");
+    gprint (GP_ERR, " %u", (0xff & (addr >>  0)));
+    gprint (GP_ERR, ".%u", (0xff & (addr >>  8)));
+    gprint (GP_ERR, ".%u", (0xff & (addr >> 16)));
+    gprint (GP_ERR, ".%u", (0xff & (addr >> 24)));
+    gprint (GP_ERR, "\n");
+  }
+
+  if (Nvalid == 0) goto accepted;
+
+  for (i = 0; i < Nvalid; i++) {
+    /* valid IP addresses may be machines (120.90.121.142) or 
+       class C networks (120.90.121.0) */
+       
+    /* for machine, address must match */
+    if ((0xff & (VALID[i] >> 24)) != 0) {
+      if (addr == VALID[i]) goto accepted;
+    }
+
+    /* for network, lower three bytes of address must match */
+    if ((0xff & (VALID[i] >> 24)) == 0) {
+      if ((0x00ffffff & addr) == VALID[i]) goto accepted;
+    }
+  }
+
+  if (DEBUG) gprint (GP_ERR, "connection rejected\n");
+  close (BindSocket);
+  return (-1);
+
+accepted:
+  if (DEBUG) gprint (GP_ERR, "connection accepted\n");
+  fcntl (BindSocket, F_SETFL, O_NONBLOCK); 
+  return (BindSocket);
+}
+
+int GetPortRange (int *start, int *stop, char *portinfo) {
+
+  // portinfo is a port or port range of the form NN or NN:MM
+  *start = MY_PORT;
+  *stop = *start + 10;
+  if (!portinfo) return TRUE;
+  if (*portinfo == 0) return TRUE;
+
+  char *endptr;
+  *start = strtol (portinfo, &endptr, 0);
+  *stop = *start + 10; // default range of 10
+  if (!endptr) {
+    gprint (GP_ERR, "error in port range parsing : %s\n", portinfo);
+    exit (20);
+  }
+  if (endptr == portinfo) {
+    gprint (GP_ERR, "error in port range (%s), must be in form NN:MM or NN\n", portinfo);
+    exit (20);
+  }
+  if (*endptr == 0) return TRUE;
+
+  if (*endptr != ':') {
+    gprint (GP_ERR, "error in port range %s (wrong range separator, must be in form NN:MM)\n", portinfo);
+    exit (20);
+  }
+  char *ptr = endptr + 1;
+  *stop = strtol (ptr, &endptr, 0);
+  if (endptr == ptr)  {
+    gprint (GP_ERR, "error in port range (%s), must be in form NN:MM or NN\n", portinfo);
+    exit (20);
+  }
+  if (*stop - *start > 20) {
+    gprint (GP_ERR, "error in port range (%s), range must be 20 or less\n", portinfo);
+    exit (20);
+  }
+  if (*stop < *start) {
+    gprint (GP_ERR, "error in port range (%s), stop must >= start\n", portinfo);
+    exit (20);
+  }
+  return TRUE;
+}
+
+int GetClientSocket (char *hostname, char *portinfo) {
+
+  int i, status, InitSocket, length, start, stop;
+  SockAddress Address;
+  struct hostent  *host;
+  char tmpline[80], hostip[80];
+
+  host = gethostbyname (hostname);
+  bzero (hostip, 80);
+  for (i = 0; i < host[0].h_length; i++) {
+    sprintf (tmpline, "%u", (0xff & host[0].h_addr[i]));
+    strcat (hostip, tmpline);
+    if (i < host[0].h_length - 1) strcat (hostip, ".");
+  }
+
+  GetPortRange (&start, &stop, portinfo);
+
+  if (DEBUG) {
+    gprint (GP_ERR, "trying %s (%s:%d)...", host[0].h_name, hostip, start);
+  }
+
+  Address.sin_family = AF_INET;
+  Address.sin_port   = start;
+
+retry_client:
+  status = inet_aton (hostip, &Address.sin_addr);
+  if (!status) {
+    gprint (GP_ERR, "invalid address\n");
+    exit (10);
+  }
+
+  length = sizeof(Address);
+
+  InitSocket = socket (PF_INET, SOCK_STREAM, 0);
+  if (InitSocket == -1) {
+    perror ("socket: ");
+    exit (11);
+  }
+
+  status = connect (InitSocket, (struct sockaddr *) &Address, length);
+  if (status == -1) {
+    if (errno == ECONNREFUSED) {
+      Address.sin_port ++;
+      if (Address.sin_port > stop) exit (12);
+      goto retry_client;
+    }
+    perror ("connect: ");
+    exit (13);
+  }
+
+  if (DEBUG) gprint (GP_ERR, "connected on port: %d\n", Address.sin_port);
+  if (DEBUG) gprint (GP_ERR, "connected\n");
+  fcntl (InitSocket, F_SETFL, O_NONBLOCK); 
+  return (InitSocket);
+}
+
+int InitServerSocket_Named (char *hostname, SockAddress *Address) {
+
+  int i, status, InitSocket, length;
+  struct hostent  *host;
+  char tmpline[80], hostip[80];
+
+  host = gethostbyname (hostname);
+  bzero (hostip, 80);
+  for (i = 0; i < host[0].h_length; i++) {
+    sprintf (tmpline, "%u", (0xff & host[0].h_addr[i]));
+    strcat (hostip, tmpline);
+    if (i < host[0].h_length - 1) strcat (hostip, ".");
+  }
+  
+  Address[0].sin_family = AF_INET;
+  Address[0].sin_port   = MY_PORT;
+  status = inet_aton (hostip, &Address[0].sin_addr);
+  if (!status) {
+    gprint (GP_ERR, "invalid address\n");
+    exit (14);
+  }
+
+  length = sizeof(Address[0]);
+
+  InitSocket = socket (PF_INET, SOCK_STREAM, 0);
+  if (InitSocket == -1) {
+    perror ("socket: ");
+    exit (15);
+  }
+
+  if (DEBUG) gprint (GP_ERR, "init sock: %d, len: %d\n", InitSocket, length);
+  status = bind (InitSocket, (struct sockaddr *) Address, length);
+  if (status == -1) {
+    perror ("bind: ");
+    exit (16);
+  }
+
+  status = listen (InitSocket, 10);
+  if (status == -1) {
+    perror ("listen: ");
+    exit (17);
+  }
+
+  if (DEBUG) gprint (GP_ERR, "socket listening on %s (%s:%d)\n", host[0].h_name, hostip, MY_PORT);
+  return (InitSocket);
+}
+
+/* load valid ip list */
+int DefineValidIP () {
+
+  int i, Nvalid, ip1, ip2, ip3, ip4, test, status;
+  char string[80];
+
+  Nvalid = 0;
+  NVALID = 10;
+  ALLOCATE (VALID, int, NVALID);
+  for (i = 0; VarConfigEntry ("VALID_IP", "%s", i, string) != NULL; i++) {
+    status = sscanf (string, "%d.%d.%d.%d", &ip1, &ip2, &ip3, &ip4);
+    test = TRUE;
+    test &= (status == 4);
+    test &= ((ip1 > 0) && (ip1 < 256)); 
+    test &= ((ip2 > 0) && (ip2 < 256)); 
+    test &= ((ip3 > 0) && (ip3 < 256)); 
+    test &= ((ip4 >=0) && (ip4 < 256)); 
+    if (!test) {
+      gprint (GP_ERR, "invalid IP address %s\n", string);
+      exit (18);
+    }
+    VALID[Nvalid] = ip1 | (ip2 << 8) | (ip3 << 16) | (ip4 << 24);
+    Nvalid ++;
+    CHECK_REALLOCATE (VALID, int, NVALID, Nvalid, 10);
+  }
+  NVALID = Nvalid;
+  REALLOCATE (VALID, int, NVALID);
+  if (NVALID == 0) {
+    free (VALID);
+    VALID = NULL;
+  }
+  return (TRUE);
+}
Index: /branches/sc_branches/pantasks_condor/lib.shell/VariableOps.c
===================================================================
--- /branches/sc_branches/pantasks_condor/lib.shell/VariableOps.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/lib.shell/VariableOps.c	(revision 34783)
@@ -0,0 +1,318 @@
+# include "opihi.h"
+
+Variable *variables;   /* variable to store the list of all variables */
+int      Nvariables;   /* number of currently available variables */
+
+static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
+
+// this function is NOT thread protected : it is only used in startup and/or shutdown
+void InitVariables () {
+  Nvariables = 0;
+  ALLOCATE (variables, Variable, 1); 
+}
+
+// this function is NOT thread protected : it is only used in startup and/or shutdown
+void FreeVariables () {
+
+  int i; 
+
+  for (i = 0; i < Nvariables; i++) {
+    free (variables[i].name);
+    free (variables[i].value);
+  }
+  free (variables);
+}
+
+int set_local_variable (char *name, char *value) {
+
+  int i;
+  char *local, *MacroName;
+
+  /* a local variable has the form (macroname).(varname) */
+  MacroName = GetMacroName ();
+
+  /* need to use a mutex to prevent two threads from changing variable list simultaneously */
+  pthread_mutex_lock (&mutex);
+
+  /* look for existing local variable */
+  ALLOCATE (local, char, strlen(name) + strlen(MacroName) + 2);
+  sprintf (local, "%s.%s", MacroName, name);
+  for (i = 0; i < Nvariables; i++) {
+    if (!strcmp (local, variables[i].name)) {
+      free (variables[i].value);
+      free (local);
+      variables[i].value = strcreate (value);
+      pthread_mutex_unlock (&mutex);
+      return (TRUE);
+    }
+  }
+  /* NEW variable */
+  Nvariables ++;
+  REALLOCATE (variables, Variable, Nvariables);
+  variables[Nvariables - 1].name = local;
+  variables[Nvariables - 1].value = strcreate (value);
+  pthread_mutex_unlock (&mutex);
+  return (TRUE);
+}
+
+int set_str_variable (char *name, char *value) {
+
+  int i;
+  char *local, *MacroName;
+
+  MacroName = GetMacroName ();
+
+  /* need to use a mutex to prevent two threads from changing variable list simultaneously */
+  pthread_mutex_lock (&mutex);
+
+  /* look for local variable first */
+  ALLOCATE (local, char, strlen(name) + strlen(MacroName) + 2);
+  sprintf (local, "%s.%s", MacroName, name);
+  for (i = 0; i < Nvariables; i++) {
+    if (!strcmp (local, variables[i].name)) {
+      free (variables[i].value);
+      free (local);
+      variables[i].value = strcreate (value);
+      pthread_mutex_unlock (&mutex);
+      return (TRUE);
+    }
+  }
+  free (local);
+
+  /* look for global variable */
+  for (i = 0; i < Nvariables; i++) {
+    if (!strcmp (name, variables[i].name)) {
+      free (variables[i].value);
+      variables[i].value = strcreate (value);
+      pthread_mutex_unlock (&mutex);
+      return (TRUE);
+    }
+  }
+  /* NEW variable */
+  Nvariables ++;
+  REALLOCATE (variables, Variable, Nvariables);
+  variables[Nvariables - 1].name = strcreate (name);
+  variables[Nvariables - 1].value = strcreate (value);
+  pthread_mutex_unlock (&mutex);
+  return (TRUE);
+}
+
+int set_variable (char *name, double dvalue) {
+
+  char value[1024];
+
+  sprintf (value, "%.12g", dvalue);
+  set_str_variable (name, value);
+  return (TRUE);
+}
+
+int set_int_variable (char *name, int ivalue) {
+
+  char value[1024];
+
+  sprintf (value, "%d", ivalue);
+  set_str_variable (name, value);
+  return (TRUE);
+}
+
+static char variable_true[] = "1";
+static char variable_false[] = "0";
+
+char *get_local_variable_ptr (char *name) {
+  
+  int i;
+  char *local, *MacroName;
+
+  MacroName = GetMacroName ();
+
+  /* look for local variable first */
+  ALLOCATE (local, char, strlen(name) + strlen(MacroName) + 2);
+  sprintf (local, "%s.%s", MacroName, name);
+
+  /* need to use a mutex to prevent another thread from misleading on size of Nvariables */
+  pthread_mutex_lock (&mutex);
+
+  for (i = 0; i < Nvariables; i++) { /* find the variable mentioned */
+    if (!strcmp(local, variables[i].name)) {
+      pthread_mutex_unlock (&mutex);
+      free (local);
+      return (variables[i].value);
+    }
+  }
+  pthread_mutex_unlock (&mutex);
+  free (local);
+  return (NULL);
+}
+
+char *get_variable_ptr (char *name) {
+  
+  int i;
+  char *local, *value;
+
+  if (name == NULL) return (NULL);
+  if (*name == 0) return (NULL);
+
+  /* check for the existence of the given name (after ?) */
+  /* return a string which should not be freed */
+  if (*name == '?') {
+    value = get_variable_ptr (&name[1]);
+    if (value == NULL) {
+      return variable_false;
+    }
+    return variable_true;
+  }
+
+  // check first for a local variable
+  local = get_local_variable_ptr (name);
+  if (local != NULL) return (local);
+
+  /* need to use a mutex to prevent another thread from misleading on size of Nvariables */
+  pthread_mutex_lock (&mutex);
+
+  /* look for global variable */
+  for (i = 0; i < Nvariables; i++) { /* find the variable mentioned */
+    if (!strcmp(name, variables[i].name)) {
+      pthread_mutex_unlock (&mutex);
+      return (variables[i].value);
+    }
+  }
+  pthread_mutex_unlock (&mutex);
+  return (NULL);
+}
+
+char *get_variable (char *name) {
+  
+  char *ptr, *value;
+
+  if (name == NULL) return (NULL);
+  if (*name == 0) return (NULL);
+
+  ptr = get_variable_ptr (name);
+  if (ptr) {
+    value = strcreate (ptr);
+    return value;
+  }
+  return (NULL);
+}
+
+/* return TRUE / FALSE if name is an existant variable */
+int get_variable_exists (char *name) {
+  
+  char *ptr;
+
+  ptr = get_variable_ptr (name);
+  if (ptr) return (TRUE);
+  return (FALSE);
+}
+
+float get_variable_default (char *name, float dvalue) {
+
+  char *value;
+  float fvalue;
+
+  value = get_variable (name);
+  if (value == NULL) {
+    return (dvalue);
+  }
+  fvalue = atof (value);
+  return (fvalue);
+}
+
+double get_double_variable (char *name, int *found) {
+  
+  char *ptr;
+
+  ptr = get_variable_ptr (name);
+  if (ptr) {
+    *found = TRUE;
+    return (atof (ptr));
+  }
+
+  *found = FALSE;
+  return (0.0);
+}
+
+int get_int_variable (char *name, int *found) {
+  
+  char *ptr;
+
+  ptr = get_variable_ptr (name);
+  if (ptr) {
+    *found = TRUE;
+    return (atof (ptr));
+  }
+  *found = FALSE;
+  return (0.0);
+}
+
+int DeleteNamedScalar (char *name) {
+
+  int i, j;
+
+  /* need to use a mutex to prevent two threads from simultaneously modifying Nvariables */
+  pthread_mutex_lock (&mutex);
+
+  for (i = 0; i < Nvariables; i++) {
+    if (!strcmp (name, variables[i].name)) {
+      free (variables[i].name);
+      free (variables[i].value);
+      for (j = i; j < Nvariables - 1; j++) {
+	variables[j] = variables[j + 1];
+      }
+      Nvariables --;
+      REALLOCATE (variables, Variable, MAX (Nvariables, 1));
+      pthread_mutex_unlock (&mutex);
+      return (TRUE);
+    }
+  }
+  pthread_mutex_unlock (&mutex);
+  return (FALSE);
+}
+
+int IsScalar (char *name) { 
+
+  int i;
+
+  /* need to use a mutex to prevent another thread from misleading on size of Nvariables */
+  pthread_mutex_lock (&mutex);
+
+  for (i = 0; i < Nvariables; i++) {
+    if (!strcmp (name, variables[i].name)) {
+      pthread_mutex_unlock (&mutex);
+      return (TRUE);
+    }
+  }
+  pthread_mutex_unlock (&mutex);
+  return (FALSE);
+}
+
+void ListVariables () {
+
+  int i;
+
+  if (Nvariables == 0) {
+    gprint (GP_ERR, "No defined variables\n");
+    return;
+  }
+
+  /* need to use a mutex to prevent another thread from misleading on size of Nvariables */
+  pthread_mutex_lock (&mutex);
+
+  for (i = 0; i < Nvariables; i++) {
+    gprint (GP_ERR, "%s = %s\n", variables[i].name, variables[i].value);
+  }
+
+  pthread_mutex_unlock (&mutex);
+  return;
+}
+
+int SelectScalar (char *string, double *value) {
+
+  char *end;
+
+  /* if string is a number, return TRUE */
+  *value = strtod (string, &end);
+  if (end == string + strlen(string)) return (TRUE);
+
+  return (FALSE);
+}
Index: /branches/sc_branches/pantasks_condor/lib.shell/VectorIO.c
===================================================================
--- /branches/sc_branches/pantasks_condor/lib.shell/VectorIO.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/lib.shell/VectorIO.c	(revision 34783)
@@ -0,0 +1,242 @@
+# include "opihi.h"
+  
+// write a set of vectors to a FITS file (vectors names become fits column names)
+int WriteVectorTable (Header *theader, FTable *ftable, char *extname, Vector **vec, int Nvec, char *format) {
+  
+  int j;
+
+  char *tformat = NULL;
+
+  gfits_create_table_header (theader, "BINTABLE", extname);
+
+  ALLOCATE (tformat, char, 2*Nvec);
+  if (format) {
+    // the bintable format string can only define the byte-width of each field.  valid output columns are currently:
+    // B (char), I (16 bit short), J (32 bit int), E (32 bit float), D (64 bit double).
+    // the format string is just the sequence of types, eg: LIIJEED
+    // validate the format string
+    char *ptr = format;
+    for (j = 0; j < Nvec; j++) {
+      while (*ptr && OHANA_WHITESPACE (*ptr)) ptr++;
+      if (*ptr == 0) {
+	gprint (GP_ERR, "error in binary table format %s (insufficient format chars)\n", format);
+	goto escape;
+      }
+      if ((*ptr != 'B') && (*ptr != 'I') && (*ptr != 'J') && (*ptr != 'D') && (*ptr != 'E')) {
+	gprint (GP_ERR, "error in binary table format %s: invalid character %c\n", format, *ptr);
+	goto escape;
+      }
+      tformat[2*j + 0] = *ptr;
+      tformat[2*j + 1] = 0; // a bit sleazy : use a 2xN string to store N 1-byte strings
+      ptr ++;
+    }
+    while (*ptr && OHANA_WHITESPACE (*ptr)) ptr++;
+    if (*ptr) {
+      gprint (GP_ERR, "error in binary table format %s (extra characters in format)\n", format);
+      goto escape;
+    }
+  } else {
+    for (j = 0; j < Nvec; j++) {
+      // if the format is not defined, just use the native byte-widths
+      tformat[2*j + 0] = (vec[j][0].type == OPIHI_FLT) ? 'D' : 'J';
+      tformat[2*j + 1] = 0;
+    }
+  }
+
+  // define the columns of the table.  XXX NOTE: we cannot have duplicate names in
+  // output table (because the data goes to the named column below).  need to enforce
+  // this somehow
+  for (j = 0; j < Nvec; j++) {
+    gfits_define_bintable_column (theader, &tformat[2*j], vec[j][0].name, NULL, NULL, 1.0, 0.0);
+  }
+  free (tformat);
+
+  // generate the output array that carries the data
+  gfits_create_table (theader, ftable);
+
+  // add the vectors to the output array
+  for (j = 0; j < Nvec; j++) {
+    if (vec[j][0].type == OPIHI_FLT) {
+      gfits_set_bintable_column_reformat (theader, ftable, vec[j][0].name, "double", vec[j][0].elements.Flt, vec[j][0].Nelements);
+    } else {
+      gfits_set_bintable_column_reformat (theader, ftable, vec[j][0].name, "int", vec[j][0].elements.Int, vec[j][0].Nelements);
+    }
+  }
+  return (TRUE);
+
+ escape:
+  if (tformat) free (tformat);
+  return (FALSE);
+}
+  
+// write a set of vectors to a FITS file (vectors names become fits column names)
+int WriteVectorTableFITS (char *filename, char *extname, Vector **vec, int Nvec, int append, char *format) {
+  
+  Header header;
+  Matrix matrix;
+  Header theader;
+  FTable ftable;
+
+  FILE *f = NULL;
+
+  /* open file for outuput */
+  if (append) {
+    f = fopen (filename, "a");
+  } else {
+    f = fopen (filename, "w");
+  }
+  if (f == (FILE *) NULL) {
+    gprint (GP_ERR, "can't open file for write : %s\n", filename);
+    return (FALSE);
+  }
+
+  if (!WriteVectorTable (&theader, &ftable, extname, vec, Nvec, format)) goto escape;
+
+  if (!append) {
+    gfits_init_header (&header);
+    header.extend = TRUE;
+    gfits_create_header (&header);
+    gfits_create_matrix (&header, &matrix);
+    gfits_fwrite_header  (f, &header);
+    gfits_fwrite_matrix  (f, &matrix);
+    gfits_free_header (&header);
+    gfits_free_matrix (&matrix);
+  }
+  gfits_fwrite_Theader (f, &theader);
+  gfits_fwrite_table  (f, &ftable);
+
+  gfits_free_header (&theader);
+  gfits_free_table (&ftable);
+
+  fclose (f);
+  fflush (f);
+  return (TRUE);
+
+ escape:
+  if (!append) {
+    gfits_free_header (&header);
+    gfits_free_matrix (&matrix);
+  }
+  gfits_free_header (&theader);
+  gfits_free_table (&ftable);
+
+  fclose (f);
+  fflush (f);
+  return (FALSE);
+}
+  
+// read the complete set of vectors from the given FITS file & extension
+// XXX not quite right : I need to merge multiple vectors together:
+// do not associate with an Opihi vector until later in mextract
+Vector **ReadVectorTableFITS (char *filename, char *extname, int *nvec) {
+  
+  Header header;
+  FTable ftable;
+
+  int i, j, k;
+  FILE *f = NULL;
+
+  /* open file for input */
+  f = fopen (filename, "r");
+  if (f == (FILE *) NULL) {
+    gprint (GP_ERR, "can't open file for read\n");
+    return NULL;
+  }
+
+  ftable.header = &header;
+
+  // read the full table data into a buffer
+  if (!gfits_fread_ftable (f, &ftable, extname)) {
+    fclose (f);
+    return NULL;
+  }
+
+  // XXX handle binary and ascii tables (see read_vectors.c)
+  // find the columns in the table
+  int Nfields;
+  gfits_scan (&header, "TFIELDS", "%d", 1, &Nfields);
+  int Nrows = header.Naxis[1];
+
+  // how many output vectors do we need?  depends on number of columns per field, but min is Nfields
+  int Nvec = 0;
+  int NVEC = Nfields;
+  Vector **vec = NULL;
+  ALLOCATE (vec, Vector *, NVEC);
+  
+  for (i = 0; i < Nfields; i++) {
+    int Nval;
+    char type[16], label[16], name[80];
+
+    // determine the column name, data type, and number of sub-fields
+    sprintf (label, "TTYPE%d", i + 1);
+    int status = gfits_scan (&header, label, "%s", 1, name);
+    assert (status);
+
+    status = gfits_get_bintable_column_type_by_N (&header, i + 1, type, &Nval);
+    assert (status);
+
+    if (Nvec + Nval >= NVEC) {
+      NVEC = Nvec + Nval + 16;
+      REALLOCATE (vec, Vector *, NVEC);
+    }
+
+    int vecType = OPIHI_INT;
+    if (!strcmp (type, "double") || !strcmp (type, "float")) {
+      vecType = OPIHI_FLT;
+    }
+
+    // generate the needed vectors
+    for (j = 0; j < Nval; j++) {
+      vec[Nvec + j] = InitVector();
+      if (Nval == 1) {
+	strcpy (vec[Nvec + j]->name, name);
+      } else {
+	snprintf (vec[Nvec + j]->name, OPIHI_NAME_SIZE, "%s:%d", name, j);
+      }
+      ResetVector (vec[Nvec + j], vecType, Nrows);
+    }
+
+    // read the actual table data into a column
+    void *data;
+    status = gfits_get_bintable_column (&header, &ftable, name, &data);
+    assert (status);
+
+# define ASSIGN_DATA(TYPE,OPTYPE) \
+    /* assign the data to the actual vector */ \
+    if (!strcmp (type, #TYPE)) { \
+      TYPE *Ptr = data;	    \
+      for (k = 0; k < Nrows; k++) { \
+	for (j = 0; j < Nval; j++, Ptr++) { \
+	  vec[Nvec + j][0].elements.OPTYPE[k] = *Ptr; \
+	} } }
+
+    // assign the data to the actual vector
+    ASSIGN_DATA(char,    Int);
+    ASSIGN_DATA(short,   Int);
+    ASSIGN_DATA(int,     Int);
+    ASSIGN_DATA(int64_t, Int);
+    ASSIGN_DATA(float,   Flt);
+    ASSIGN_DATA(double,  Flt);
+
+    free (data);
+    Nvec += Nval;
+  }
+
+  gfits_free_header (&header);
+  gfits_free_table (&ftable);
+
+  fclose (f);
+
+  *nvec = Nvec;
+  return vec;
+
+//escape:
+//  gfits_free_header (&header);
+//  gfits_free_matrix (&matrix);
+//  gfits_free_header (&theader);
+//  gfits_free_table (&ftable);
+//
+//  fclose (f);
+//  fflush (f);
+//  return (FALSE);
+}
Index: /branches/sc_branches/pantasks_condor/lib.shell/VectorOps.c
===================================================================
--- /branches/sc_branches/pantasks_condor/lib.shell/VectorOps.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/lib.shell/VectorOps.c	(revision 34783)
@@ -0,0 +1,435 @@
+# include "opihi.h"
+
+// NOTE: we refer to elements.Ptr if we do not care about the actual type
+
+static Vector **vectors;
+static int     Nvectors;
+  
+static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
+
+// this function is NOT thread protected : it is only used in startup and/or shutdown
+void InitVectors () {
+  Nvectors = 0;
+  ALLOCATE (vectors, Vector *, 1);
+}
+
+// this function is NOT thread protected : it is only used in startup and/or shutdown
+void FreeVectorArray (Vector **vec, int Nvec) {
+
+  int i;
+
+  if (!vec) return;
+  for (i = 0; i < Nvec; i++) {
+    if (!vec[i]) continue;
+    if (vec[i]->elements.Int) {
+      free (vec[i]->elements.Int);
+    }
+    free (vec[i]);
+  }
+  free (vec);
+}
+
+// this function is NOT thread protected : it is only used in startup and/or shutdown
+void FreeVectors () {
+  FreeVectorArray (vectors, Nvectors);
+}
+
+Vector *InitVector () {
+  Vector *vec;
+
+  ALLOCATE (vec, Vector, 1);
+
+  vec[0].type = OPIHI_FLT;    // is a float unless specified otherwise
+  ALLOCATE (vec[0].elements.Flt, opihi_flt, 1);
+
+  bzero (vec[0].name, OPIHI_NAME_SIZE);
+  vec[0].Nelements = 0;
+  return (vec);
+}
+
+int IsVector (char *name) {
+ 
+  int i;
+
+  if (name == NULL) return (FALSE);
+
+  for (i = 0; (i < Nvectors) && (strcmp(vectors[i][0].name, name)); i++);
+  if (i == Nvectors) return (FALSE);
+  return (TRUE);
+}
+
+int IsVectorPtr (Vector *vec) {
+ 
+  int i;
+
+  if (vec == NULL) return (FALSE);
+
+  for (i = 0; (i < Nvectors) && (vectors[i] != vec); i++);
+  if (i == Nvectors) return (FALSE);
+  return (TRUE);
+}
+
+// XXX add TYPE to arguments?
+Vector *SelectVector (char *name, int mode, int verbose) {
+
+  int i;
+
+  if (name == NULL) goto error;
+  if (ISNUM(name[0])) goto error;
+  if (IsBuffer(name)) goto error;
+
+  for (i = 0; (i < Nvectors) && (strcmp(vectors[i][0].name, name)); i++);
+  /* is a new vector */
+  if (i == Nvectors) { 
+    if (mode == OLDVECTOR) goto error;
+    pthread_mutex_lock (&mutex);
+    Nvectors ++;
+    REALLOCATE (vectors, Vector *, Nvectors);
+    vectors[i] = InitVector ();
+    strcpy (vectors[i][0].name, name);
+    pthread_mutex_unlock (&mutex);
+    return (vectors[i]);
+  } 
+  /* is an old vector */
+  if (mode == NEWVECTOR) goto error;
+  return (vectors[i]);
+
+ error:
+  if (verbose) gprint (GP_ERR, "invalid vector %s\n", name);
+  return (NULL);
+}
+  
+// Assign the given Vector to the internal array of vectors by name
+int AssignVector (Vector *vec, char *name, int mode, int verbose) {
+
+  int i;
+
+  if (name == NULL) goto error;
+  if (ISNUM(name[0])) goto error;
+  if (IsBuffer(name)) goto error;
+
+  for (i = 0; (i < Nvectors) && (strcmp(vectors[i][0].name, name)); i++);
+  /* is a new vector */
+  if (i == Nvectors) { 
+    if (mode == OLDVECTOR) goto error;
+    pthread_mutex_lock (&mutex);
+    Nvectors ++;
+    REALLOCATE (vectors, Vector *, Nvectors);
+    vectors[i] = vec;
+    pthread_mutex_unlock (&mutex);
+    return TRUE;
+  } 
+  /* is an old vector */
+  if (mode == NEWVECTOR) goto error;
+  if (vectors[i]) {
+    if (vectors[i][0].elements.Ptr) free (vectors[i][0].elements.Ptr);
+    free (vectors[i]);
+  }
+  vectors[i] = vec;
+  return TRUE;
+
+ error:
+  if (verbose) gprint (GP_ERR, "invalid vector %s\n", name);
+  return FALSE;
+}
+  
+/* delete by pointer */
+int DeleteVector (Vector *vec) {
+
+  int i, j;
+
+  if (vec == NULL) return (FALSE);
+
+  for (i = 0; (i < Nvectors) && (vec != vectors[i]); i++);
+  if (i == Nvectors) return (FALSE);
+
+  if (vectors[i][0].elements.Ptr) free (vectors[i][0].elements.Ptr);
+  free (vectors[i]);
+
+  pthread_mutex_lock (&mutex);
+  for (j = i; j < Nvectors - 1; j++) vectors[j] = vectors[j + 1];
+  Nvectors --;
+  REALLOCATE (vectors, Vector *, MAX (Nvectors, 1));
+  pthread_mutex_unlock (&mutex);
+  return (TRUE);
+}
+  
+/* delete by name */
+int DeleteNamedVector (char *name) {
+
+  int i, j;
+
+  if (name == NULL) return (FALSE);
+  for (i = 0; (i < Nvectors) && (strcmp(vectors[i][0].name, name)); i++);
+  if (i == Nvectors) return (FALSE);
+
+  if (vectors[i][0].elements.Ptr) free (vectors[i][0].elements.Ptr);
+  free (vectors[i]);
+
+  pthread_mutex_lock (&mutex);
+  for (j = i; j < Nvectors - 1; j++) vectors[j] = vectors[j + 1];
+  Nvectors --;
+  REALLOCATE (vectors, Vector *, MAX (Nvectors, 1));
+  pthread_mutex_unlock (&mutex);
+  return (TRUE);
+}
+
+/* copy data from in to out - new memory space */
+int CopyNamedVector (char *out, char *in) {
+  Vector *In, *Out;
+  if ((In  = SelectVector (in,  OLDVECTOR, FALSE)) == NULL) return (FALSE);
+  if ((Out = SelectVector (out, ANYVECTOR, FALSE)) == NULL) return (FALSE);
+  CopyVector (Out, In);
+  return (TRUE);
+}
+
+int CopyVector (Vector *out, Vector *in) {
+  if (out[0].elements.Ptr) free (out[0].elements.Ptr);
+  out[0].Nelements = in[0].Nelements;
+  if (in[0].elements.Ptr) {
+    if (in[0].type == OPIHI_FLT) {
+      ALLOCATE (out[0].elements.Flt, opihi_flt, MAX(1,out[0].Nelements));
+      memcpy (out[0].elements.Flt, in[0].elements.Flt, out[0].Nelements*sizeof(opihi_flt));
+      out[0].type = OPIHI_FLT;
+    } else {
+      ALLOCATE (out[0].elements.Int, opihi_int, MAX(1,out[0].Nelements));
+      memcpy (out[0].elements.Int, in[0].elements.Int, out[0].Nelements*sizeof(opihi_int));
+      out[0].type = OPIHI_INT;
+    }
+  }
+  return (TRUE);
+}
+
+int MatchVector(Vector *out, Vector *in, char type) {
+  if (out[0].elements.Ptr) free (out[0].elements.Ptr);
+  out[0].Nelements = in[0].Nelements;
+  if (type == OPIHI_FLT) {
+    ALLOCATE (out[0].elements.Flt, opihi_flt, MAX(1,out[0].Nelements));
+    out[0].type = OPIHI_FLT;
+  } else {
+    ALLOCATE (out[0].elements.Int, opihi_int, MAX(1,out[0].Nelements));
+    out[0].type = OPIHI_INT;
+  }
+  return (TRUE);
+}
+
+int ResetVector (Vector *vec, char type, int Nelements) {
+
+  // a vector can only have >= 0 elements
+  vec[0].Nelements = MAX(Nelements,0);
+  if (type == OPIHI_FLT) {
+    REALLOCATE (vec[0].elements.Flt, opihi_flt, MAX(1, Nelements));
+    vec[0].type = OPIHI_FLT;
+  } else {
+    REALLOCATE (vec[0].elements.Int, opihi_int, MAX(1, Nelements));
+    vec[0].type = OPIHI_INT;
+  }
+  return TRUE;
+}
+
+// SetVector (vecx, OPIHI_FLT, MAX (Npts, 1));
+int SetVector (Vector *vec, char type, int Nelements) {
+
+  vec[0].Nelements = MAX(Nelements,0);
+  if (type == OPIHI_FLT) {
+    ALLOCATE (vec[0].elements.Flt, opihi_flt, MAX(1,Nelements));
+    vec[0].type = OPIHI_FLT;
+  } else {
+    ALLOCATE (vec[0].elements.Int, opihi_int, MAX(1,Nelements));
+    vec[0].type = OPIHI_INT;
+  }
+  return TRUE;
+}
+
+// recast the vector to the specified type
+int CastVector (Vector *vec, char type) {
+
+  int i;
+
+  // the trivial case
+  if (vec[0].type == type) return TRUE;
+
+  if (type == OPIHI_FLT) {
+    opihi_flt *temp;
+    ALLOCATE (temp, opihi_flt, vec[0].Nelements);
+    opihi_flt *vo = temp;
+    opihi_int *vi = vec[0].elements.Int;
+    for (i = 0; i < vec[0].Nelements; i++, vo++, vi++) {
+      *vo = *vi;
+    }
+    free (vec[0].elements.Int);
+    vec[0].elements.Flt = temp;
+    vec[0].type = OPIHI_FLT;
+  } else {
+    opihi_int *temp;
+    ALLOCATE (temp, opihi_int, vec[0].Nelements);
+    opihi_int *vo = temp;
+    opihi_flt *vi = vec[0].elements.Flt;
+    for (i = 0; i < vec[0].Nelements; i++, vo++, vi++) {
+      *vo = *vi;
+    }
+    free (vec[0].elements.Flt);
+    vec[0].elements.Int = temp;
+    vec[0].type = OPIHI_INT;
+  }
+  return TRUE;
+}
+
+/* move data from in to out - use old memory space */
+int MoveNamedVector (char *out, char *in) {
+  Vector *In, *Out;
+  if ((In  = SelectVector (in,  OLDVECTOR, FALSE)) == NULL) return (FALSE);
+  if ((Out = SelectVector (out, ANYVECTOR, FALSE)) == NULL) return (FALSE);
+  MoveVector (Out, In);
+  return (TRUE);
+}
+
+int MoveVector (Vector *out, Vector *in) {
+  int i, j;
+
+  if (out[0].elements.Ptr) free (out[0].elements.Ptr);
+  out[0].Nelements    = in[0].Nelements;
+  out[0].elements.Ptr = in[0].elements.Ptr;
+  out[0].type         = in[0].type;
+
+  /* delete vector entry from vector list, if it exists */
+  for (i = 0; (i < Nvectors) && (in != vectors[i]); i++);
+  if (i == Nvectors) {
+    free (in);
+    return (TRUE);
+  }
+
+  pthread_mutex_lock (&mutex);
+  for (j = i; j < Nvectors - 1; j++) vectors[j] = vectors[j + 1];
+  Nvectors --;
+  REALLOCATE (vectors, Vector *, MAX (Nvectors, 1));
+  free (in);
+  pthread_mutex_unlock (&mutex);
+  return (TRUE);
+}
+
+int ListVectors () {
+
+  int i;
+
+  if (Nvectors == 0) {
+    gprint (GP_ERR, "No defined vectors\n");
+    return (FALSE);
+  }
+
+  gprint (GP_LOG, "    N       name      size\n");
+  for (i = 0; i < Nvectors; i++) {
+    if (vectors[i][0].type == OPIHI_FLT) {
+      gprint (GP_LOG, "%5d %10s %10d (FLT)\n", i, vectors[i][0].name, vectors[i][0].Nelements);
+    } else {
+      gprint (GP_LOG, "%5d %10s %10d (INT)\n", i, vectors[i][0].name, vectors[i][0].Nelements);
+    }
+  }
+  return (TRUE);
+}
+
+int ListVectorsToList (char *name) {
+
+  int i;
+  char line[1024];
+
+  for (i = 0; i < Nvectors; i++) {
+    sprintf (line, "%s:%d", name, i);
+    set_str_variable (line, vectors[i][0].name);
+  }
+  sprintf (line, "%s:n", name);
+  set_int_variable (line, Nvectors);
+  return (Nvectors);
+}
+
+// Take two arrays of vectors and merge equal named vectors.
+// Output is a single array in (vec), with vectors lengths of len(vec) + len(invec)
+// For ease, require that the order of the names match & number of vectors match
+Vector **MergeVectors (Vector **vec, int *Nvec, Vector **invec, int Ninvec) {
+
+  int i, j;
+
+  if (vec == NULL) {
+    *Nvec = Ninvec;
+    return invec;
+  }
+
+  myAssert (*Nvec == Ninvec, "programming error (1) %d vs %d", *Nvec, Ninvec);
+
+  for (i = 0; i < Ninvec; i++) {
+    myAssert (!strcmp(vec[i]->name, invec[i]->name), "programming error (2) %s vs %s", vec[i]->name, invec[i]->name);
+    myAssert (vec[i]->type == invec[i]->type, "programming error (3), %d vs %d", vec[i]->type, invec[i]->type);
+
+    int N = vec[i]->Nelements;
+    if (vec[i]->type == OPIHI_FLT) {
+      REALLOCATE (vec[i]->elements.Flt, opihi_flt, vec[i]->Nelements + invec[i]->Nelements);
+      for (j = 0; j < invec[i]->Nelements; j++) {
+	vec[i]->elements.Flt[N+j] = invec[i]->elements.Flt[j];
+      }
+    } else {
+      REALLOCATE (vec[i]->elements.Int, opihi_int, vec[i]->Nelements + invec[i]->Nelements);
+      for (j = 0; j < invec[i]->Nelements; j++) {
+	vec[i]->elements.Int[N+j] = invec[i]->elements.Int[j];
+      }
+    }
+    vec[i]->Nelements += invec[i]->Nelements;
+  }
+  return vec;
+}
+
+// Take two arrays of vectors and merge equal named vectors, where the last vector
+// specifies the element in the output vector.  All input vectors must have a max sequence
+// value of Nelements.  Output is a single array in (vec), with vector lengths of
+// Nelements for ease, require that the order of the names match & number of vectors match
+Vector **MergeVectorsByIndex (Vector **vec, int *Nvec, Vector **invec, int Ninvec, int Nelements) {
+
+  int i, j;
+
+  // on first call, allocate a new vector, excluding the index
+  int newArray = FALSE;
+  if (vec == NULL) {
+    ALLOCATE (vec, Vector *, Ninvec - 1);
+    *Nvec = Ninvec - 1;
+    newArray = TRUE;
+  } 
+
+  myAssert (*Nvec == Ninvec - 1, "programming error (1)");
+
+  // find the index vector
+  int idx = Ninvec - 1;
+  myAssert (!strcmp(invec[idx]->name, "index"), "failed to find index vector");
+  
+  for (i = 0; i < Ninvec - 1; i++) {
+    // on first call, create the output vector
+    if (newArray) {
+      vec[i] = InitVector();
+      strcpy (vec[i]->name, invec[i]->name);
+      ResetVector (vec[i], invec[i]->type, Nelements);
+      for (j = 0; j < Nelements; j++) {
+	if (vec[i][0].type == OPIHI_FLT) {
+	  vec[i][0].elements.Flt[j] = NAN;
+	} else {
+	  vec[i][0].elements.Int[j] = 0; // or NAN_INT?
+	}
+      }
+    }
+
+    myAssert (!strcmp(vec[i]->name, invec[i]->name), "programming error (2)");
+    myAssert (vec[i]->type == invec[i]->type, "programming error (2)");
+
+    // copy vector elements from input to output, matching location
+    if (vec[i]->type == OPIHI_FLT) {
+      for (j = 0; j < invec[i]->Nelements; j++) {
+	int seq = invec[idx]->elements.Int[j];
+	vec[i]->elements.Flt[seq] = invec[i]->elements.Flt[j];
+      }
+    } else {
+      for (j = 0; j < invec[i]->Nelements; j++) {
+	int seq = invec[idx]->elements.Int[j];
+	vec[i]->elements.Int[seq] = invec[i]->elements.Int[j];
+      }
+    }
+  }
+  return vec;
+}
+
Index: /branches/sc_branches/pantasks_condor/lib.shell/check_stack.c
===================================================================
--- /branches/sc_branches/pantasks_condor/lib.shell/check_stack.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/lib.shell/check_stack.c	(revision 34783)
@@ -0,0 +1,102 @@
+# include "opihi.h"
+# ifndef MAX_INT
+# define MAX_INT 2147483647
+# endif
+
+int check_stack (StackVar *stack, int Nstack, int validsize) {
+
+  int i, Nx, Ny, Nv, size;
+  char *c1, *c2;
+
+  Nv = Nx = Ny = -1;
+
+  for (i = 0; i < Nstack; i++) {
+    if (stack[i].type == 'X') {
+
+      /** if this is a number, put it on the list of scalars and move on.  assume value is
+       * an int unless proven otherwise. 
+
+       If we have built libdvo with opihi_int defined as a 32bit signed int, then we have
+       an overflow for values > MAX_INT (2^31).  If the float value is larger than this,
+       we should treat the value as a float.  (NOTE: this means we cannot use 32bit flags,
+       only 31bit flags.
+
+      **/
+
+      stack[i].FltValue = strtod (stack[i].name, &c1);
+      stack[i].IntValue = strtol (stack[i].name, &c2, 0);
+      if ((fabs(stack[i].FltValue) > MAX_INT) && (c1 == stack[i].name + strlen (stack[i].name))) {
+	stack[i].type  = 'S'; // 'S' == (float)
+	continue;
+      } 
+      if (c2 == stack[i].name + strlen (stack[i].name)) {
+	stack[i].type  = 's'; // 's' == (int)
+	continue;
+      } 
+      if (c1 == stack[i].name + strlen (stack[i].name)) {
+	stack[i].type  = 'S'; // 'S' == (float)
+	continue;
+      } 
+
+      /** if this is a matrix, find the dimensions and check with existing values **/
+      if (IsBuffer (stack[i].name)) {
+	stack[i].buffer = SelectBuffer (stack[i].name, OLDBUFFER, TRUE);
+	stack[i].type   = 'M';
+	if (Nx == -1) {
+	  Nx = stack[i].buffer[0].matrix.Naxis[0];
+	  Ny = stack[i].buffer[0].matrix.Naxis[1];
+	} 
+	if ((Nx != stack[i].buffer[0].matrix.Naxis[0]) && (Ny != stack[i].buffer[0].matrix.Naxis[1])) {
+	  push_error ("dimensions don't match");
+	  return (-1);
+	}	
+	if (Nv != -1) {
+	  if ((Nv != Nx) && (Nv != Ny)) {
+	    push_error ("dimensions don't match");
+	    return (-1);
+	  }
+	}	
+	continue;
+      }
+
+      /** if this is a vector, find the dimensions and check with existing values **/
+      if (IsVector (stack[i].name)) {
+	stack[i].vector = SelectVector (stack[i].name, OLDVECTOR, FALSE);
+	stack[i].type   = 'V';
+
+	if (Nv == -1) Nv = stack[i].vector[0].Nelements;
+	if (Nv != stack[i].vector[0].Nelements) {
+	  push_error ("dimensions don't match");
+	  return (-1);
+	}
+	if (Nx != -1) {
+	  if ((Nx != Nv) && (Ny != Nv)) {
+	    push_error ("dimensions don't match");
+	    return (-1);
+	  }
+	}	
+	continue;
+      }
+
+      /* this is not a scalar, vector, or matrix.  must be string */
+      stack[i].type  = 'W';
+    }
+  }
+
+  /* return object dimensions */
+  size = 0;
+  if (Nv != -1) size = 1;
+  if (Nx != -1) size = 2;
+  if (validsize == -1)   return (size);
+  if (validsize != size) return (-1);
+  return (size);
+}
+
+/* check stack identifies the data elements as scalar, vector, matrix, or word.
+   operators have already been identified.  
+   check stack returns the total stack dimensionality (0,1,2)
+   on error, check stack returns -1
+   possible errors:
+     - mismatch with requested dimensionality
+     - mismatch in data dimensions
+*/
Index: /branches/sc_branches/pantasks_condor/lib.shell/command.c
===================================================================
--- /branches/sc_branches/pantasks_condor/lib.shell/command.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/lib.shell/command.c	(revision 34783)
@@ -0,0 +1,115 @@
+# include "opihi.h"
+# define DEBUG 0
+
+static int VERBOSE_SHELL = OPIHI_VERBOSE_OFF;
+
+int command (char *line, char **outline, int VERBOSE) {
+
+  int i, status, argc;
+  char **argv, **targv, *rawline;
+  Command *cmd;
+
+  // the input line is never NULL
+  if (!line) { fprintf (stderr, "programming error\n"); abort(); }
+
+  rawline = strcreate (line);  // used for error messages which should echo the unparsed line
+
+  /* force a space between ! and first word: !ls becomes ! ls */
+  if (line[0] == '!') {
+    REALLOCATE (line, char, strlen(line) + 5);
+    memmove (&line[2], &line[1], strlen(&line[1]) + 1);
+    line[1] = ' ';
+  }
+
+  /* expand anything of the form $fred or $fred$sam, etc */ 
+  line = expand_vars (line);     /* line is freed here, new one allocated */
+  if (!line) goto escape;
+
+  /* expand anything of the form fred[N] */ 
+  line = expand_vectors (line);  /* line is freed here, new one allocated */
+  if (!line) goto escape;
+
+  // print the line with the variables and vectors expanded, but before evaluating 
+  // any in-line math expression
+  if (VERBOSE_SHELL == OPIHI_VERBOSE_ON) gprint (GP_ERR, "opihi: %s\n", line);
+
+  /* solve math expresions, assign variable, if needed.  any entry in line of the form {foo}
+   * returns value or tmp vector / buffer */
+  line = parse (&status, line);        /* line is freed here, new one allocated */
+  if (!status) goto escape;
+
+  /* we may have reallocated line, return new pointer */
+  *outline = line;
+  
+  argv = parse_commands (line, &argc);
+  if (argc == 0) {
+      FREE (rawline);
+      set_int_variable ("STATUS", TRUE);
+      return (TRUE);  /* empty command or assignment */
+  }
+
+  /* save the original values of argv since command may modify the array */
+  ALLOCATE (targv, char *, argc);
+  for (i = 0; i < argc; i++) targv[i] = argv[i];
+
+  cmd = MatchCommand (argv[0], VERBOSE, FALSE);
+  if (cmd == NULL) {
+    status = -1;
+  } else {
+    free (argv[0]);
+    argv[0] = strcreate (cmd[0].name);
+    targv[0] = argv[0];
+    status = (*cmd[0].func) (argc, argv);
+  }
+  for (i = 0; i < argc; i++) free (targv[i]);
+  free (targv);
+  free (argv);
+
+  if (!status) {
+    char *msg;
+    msg = get_variable_ptr ("ERRORMSG");
+    if (msg != (char *) NULL) gprint (GP_ERR, "%s\n", msg);
+    if (VERBOSE_SHELL != OPIHI_VERBOSE_OFF) gprint (GP_ERR, "error on line: %s\n", rawline);
+  }
+
+  set_int_variable ("STATUS", status);
+
+  # if (DEBUG) 
+  gprint (GP_ERR, "command: %s, status: %d\n", line, status);
+  # endif
+
+  FREE (rawline);
+  return (status);
+
+ escape:
+  set_int_variable ("STATUS", FALSE);
+
+  # if (DEBUG) 
+  gprint (GP_ERR, "command: %s, status: %d\n", line, FALSE);
+  # endif
+
+  FREE (rawline);
+
+  if (VERBOSE_SHELL != OPIHI_VERBOSE_OFF) gprint (GP_ERR, "error on line: %s\n", rawline);
+
+  // return the current value of line, in case it was modified
+  *outline = line;
+  return FALSE;
+}
+
+void set_verbose_shell(int mode) {
+    VERBOSE_SHELL = mode;
+}
+
+int get_verbose_shell(void) {
+    return VERBOSE_SHELL;
+}
+
+    
+
+/* parse the input line, search for the corresponding command, and execute it
+   if no match is found, return -1; this is used above to distinguish between
+   a command error and an unknown command.  if VERBOSE is true, unknown commands
+   result in an error message.  The input line is freed and the resulting parsed
+   line is returned on 'outline'
+*/
Index: /branches/sc_branches/pantasks_condor/lib.shell/convert_to_RPN.c
===================================================================
--- /branches/sc_branches/pantasks_condor/lib.shell/convert_to_RPN.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/lib.shell/convert_to_RPN.c	(revision 34783)
@@ -0,0 +1,212 @@
+# include "opihi.h"
+# define DUMPSTACK 0
+
+StackVar *convert_to_RPN (int argc, char **argv, int *nstack) {
+  
+  int type;
+  int i, j, Nstack, Nop_stack, NSTACK;
+  StackVar *stack, *op_stack;
+
+  /* max total stack size is argc, though should be less, this is safe */
+  NSTACK = argc + 5;
+  ALLOCATE (stack, StackVar, NSTACK);
+  ALLOCATE (op_stack, StackVar, NSTACK);
+  for (i = 0; i < NSTACK; i++) {
+    init_stack (&stack[i]);
+    init_stack (&op_stack[i]);
+  }
+  
+  Nstack = Nop_stack = 0;
+  for (i = 0; i < argc; i++) {
+    
+    /* decide on priority of object */
+    type = 0;
+    /* unary operations */
+    if (!strcmp (argv[i], "abs"))    { type = 9; goto gotit; }
+    if (!strcmp (argv[i], "int"))    { type = 9; goto gotit; }
+    if (!strcmp (argv[i], "exp"))    { type = 9; goto gotit; }
+    if (!strcmp (argv[i], "ten"))    { type = 9; goto gotit; }
+    if (!strcmp (argv[i], "log"))    { type = 9; goto gotit; }
+    if (!strcmp (argv[i], "ln"))     { type = 9; goto gotit; }
+    if (!strcmp (argv[i], "sqrt"))   { type = 9; goto gotit; }
+    if (!strcmp (argv[i], "erf"))    { type = 9; goto gotit; }
+
+    if (!strcmp (argv[i], "sinh"))   { type = 9; goto gotit; }
+    if (!strcmp (argv[i], "cosh"))   { type = 9; goto gotit; }
+    if (!strcmp (argv[i], "asinh"))  { type = 9; goto gotit; }
+    if (!strcmp (argv[i], "acosh"))  { type = 9; goto gotit; }
+
+    if (!strcmp (argv[i], "sin"))    { type = 9; goto gotit; }
+    if (!strcmp (argv[i], "cos"))    { type = 9; goto gotit; }
+    if (!strcmp (argv[i], "tan"))    { type = 9; goto gotit; }
+    if (!strcmp (argv[i], "dsin"))   { type = 9; goto gotit; }
+    if (!strcmp (argv[i], "dcos"))   { type = 9; goto gotit; }
+    if (!strcmp (argv[i], "dtan"))   { type = 9; goto gotit; }
+    if (!strcmp (argv[i], "asin"))   { type = 9; goto gotit; }
+    if (!strcmp (argv[i], "acos"))   { type = 9; goto gotit; }
+    if (!strcmp (argv[i], "atan"))   { type = 9; goto gotit; }
+    if (!strcmp (argv[i], "dasin"))  { type = 9; goto gotit; }
+    if (!strcmp (argv[i], "dacos"))  { type = 9; goto gotit; }
+    if (!strcmp (argv[i], "datan"))  { type = 9; goto gotit; }
+
+    if (!strcmp (argv[i], "lgamma")) { type = 9; goto gotit; }
+
+    if (!strcmp (argv[i], "rnd"))    { type = 9; goto gotit; }
+    if (!strcmp (argv[i], "xramp"))  { type = 9; goto gotit; }
+    if (!strcmp (argv[i], "yramp"))  { type = 9; goto gotit; }
+    if (!strcmp (argv[i], "ramp"))   { type = 9; goto gotit; }
+    if (!strcmp (argv[i], "zero"))   { type = 9; goto gotit; }
+    if (!strcmp (argv[i], "--"))     { type = 9; goto gotit; }
+    if (!strcmp (argv[i], "not"))    { type = 9; goto gotit; }
+    if (!strcmp (argv[i], "isinf"))  { type = 9; goto gotit; }
+    if (!strcmp (argv[i], "isnan"))  { type = 9; goto gotit; }
+
+    /* binary operations */
+    if (!strcmp (argv[i], "^"))      { type = 8; goto gotit; }
+
+    if (!strcmp (argv[i], "@"))      { type = 7; goto gotit; }
+    if (!strcmp (argv[i], "/"))      { type = 7; goto gotit; }
+    if (!strcmp (argv[i], "*"))      { type = 7; goto gotit; }
+    if (!strcmp (argv[i], "%"))      { type = 7; goto gotit; }
+
+    if (!strcmp (argv[i], "+"))      { type = 6; goto gotit; }
+    if (!strcmp (argv[i], "-"))      { type = 6; goto gotit; }
+	
+    if (!strcmp (argv[i], "&"))      { type = 5; goto gotit; }
+    if (!strcmp (argv[i], "|"))      { type = 5; goto gotit; }
+
+    if (!strcmp (argv[i], "<"))      { type = 4; goto gotit; }
+    if (!strcmp (argv[i], ">"))      { type = 4; goto gotit; }
+    if (!strcmp (argv[i], "=="))     { type = 4; strcpy (argv[i], "E"); goto gotit; }
+    if (!strcmp (argv[i], "!="))     { type = 4; strcpy (argv[i], "N"); goto gotit; }
+    if (!strcmp (argv[i], "<="))     { type = 4; strcpy (argv[i], "L"); goto gotit; }
+    if (!strcmp (argv[i], ">="))     { type = 4; strcpy (argv[i], "G"); goto gotit; }
+    if (!strcmp (argv[i], ">>"))     { type = 4; strcpy (argv[i], "U"); goto gotit; }
+    if (!strcmp (argv[i], "<<"))     { type = 4; strcpy (argv[i], "D"); goto gotit; }
+
+    if (!strcmp (argv[i], "&&"))     { type = 3; strcpy (argv[i], "A"); goto gotit; }
+    if (!strcmp (argv[i], "||"))     { type = 3; strcpy (argv[i], "O"); goto gotit; }
+
+    if (!strcmp (argv[i], "("))      { type = 2; goto gotit; }
+    if (!strcmp (argv[i], ")"))      { type = 1; goto gotit; }
+
+  gotit:
+    /* choose how to deal with object */
+    switch (type) {
+      case 8:  /* exponentiation: 2^2^3 = 64 != 256 (precedence is right-to-left, not left-to-right!) */
+	/* pop previous, higher operators from OP stack to stack */
+	for (j = Nop_stack - 1; (j >= 0) && (op_stack[j].type > type); j--) {
+	  move_stack (&stack[Nstack], &op_stack[j]);
+	  Nstack ++;
+	  Nop_stack --;
+	}
+	/* push operator on OP stack */
+	assign_stack (&op_stack[Nop_stack], argv[i], type);
+	Nop_stack ++;
+	break;
+      case 9: /* unary OPs */
+      case 7: /* binary OPs */
+      case 6:
+      case 5: 
+      case 4: 
+      case 3: 
+	/* pop previous, higher or equal operators from OP stack to stack */
+	for (j = Nop_stack - 1; (j >= 0) && (op_stack[j].type >= type); j--) {
+	  move_stack (&stack[Nstack], &op_stack[j]);
+	  Nstack ++;
+	  Nop_stack --;
+	}
+	/* push operator on OP stack */
+	assign_stack (&op_stack[Nop_stack], argv[i], type);
+	Nop_stack ++;
+	break;
+      case 2:  
+	/* push operator on OP stack */
+	assign_stack (&op_stack[Nop_stack], argv[i], type);
+	// fprintf (stderr, "push %s\n", op_stack[Nop_stack].name);
+	Nop_stack ++;
+	break;
+      case 1: 
+	/* pop rest of operators from OP stack to stack, looking for '(' */
+	for (j = Nop_stack - 1; (j >= 0) && (op_stack[j].type != 2); j--) {
+	  move_stack (&stack[Nstack], &op_stack[j]);
+	  Nstack ++;
+	  Nop_stack --;
+	}
+	if ((j == -1) || (op_stack[j].type != 2)) {
+	  push_error ("syntax error: mismatched parenthesis");
+	  Nstack = 0;
+	  goto cleanup;
+	}
+	// delete the '(' from op_stack:
+	// fprintf (stderr, "pop %s\n", op_stack[j].name);
+	clear_stack (&op_stack[j]);
+	Nop_stack --;
+	break;
+      case 0:
+	/* place the value (number or vector/matrix name) on stack */
+	/* value of 'X' is used as sentinel until we sort out values */
+	assign_stack (&stack[Nstack], argv[i], 'X');
+	Nstack ++;
+	break;
+    }
+  }
+
+  /* dump remaining operators on stack, checking for ')' */
+  for (j = Nop_stack - 1; j >= 0; j--) {
+    if (op_stack[j].type == 2) {
+      push_error ("syntax error: mismatched parenthesis");
+      Nstack = 0;
+      goto cleanup;
+    }
+    move_stack (&stack[Nstack],  &op_stack[j]);
+    Nstack ++;
+  }
+
+cleanup: 
+  /*** free up unused stack space ***/
+
+  // If we parsed everything above, there should not be any un-freed op_stacks at this
+  // point.  However, if we had a syntax error, we will have op_stack entries left behind.
+  delete_stack (op_stack, NSTACK);
+  free (op_stack);
+
+  // XXX there should not be any data on higher stack entries
+  // clean_stack (&stack[Nstack], NSTACK - Nstack);
+  REALLOCATE (stack, StackVar, MAX (Nstack, 1));
+  *nstack = Nstack;
+
+  for (i = 0; i < argc; i++) {
+    free (argv[i]);
+  }
+  free (argv);
+
+# if (DUMPSTACK)
+  for (i = 0; i < Nstack; i++) {
+    gprint (GP_ERR, "%s ", stack[i].name);
+  }
+  if (Nstack > 0) gprint (GP_ERR, "\n");
+  for (i = 0; i < Nstack; i++) {
+    gprint (GP_ERR, "%d ", stack[i].type);
+  }
+  if (Nstack > 0) gprint (GP_ERR, "\n");
+# endif
+
+  return (stack);
+
+}
+
+/* here are the rules for parsing a math AOL line to RPN:
+
+1) if object is a number, push on stack
+2) if object is a third order operand (exp, sin, cos), push on op stack
+3) if object is an open paren, push on op stack,
+4) if object is a second order operand, push on stack
+5) if object is a first order operand, pop all second order operands from stack 
+until paren, push on stack
+6) if object is an end paren, pop all objects from stack until paren, 
+pop next stack, if third order op
+7) if end of line, pop all remaining objects, second order first, etc.
+   
+*/
+
Index: /branches/sc_branches/pantasks_condor/lib.shell/dvomath.c
===================================================================
--- /branches/sc_branches/pantasks_condor/lib.shell/dvomath.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/lib.shell/dvomath.c	(revision 34783)
@@ -0,0 +1,108 @@
+# include "opihi.h"
+# include "dvo.h"
+
+/* return value on success is temp vector/buffer name or scalar value return value on error is NULL, all
+   internals freed.  errors are sent to error stack.  may be printed by calling function */
+
+/* XXX this function breaks the elements of the line into objects, numbers, and operators.  it
+ * then converts the list to an RPN expresssion.  it then evaluates the expression.  if an
+ * expression is not a valid math expression (string - string), it results in an error.  The
+ * problem is that elements of a valid expression may be invalid as expressions on their own
+ * (eg, string consisting of word-word).  this function probably should be somewhat smarter
+ * about breaking down items into strings and objects.  for the time being, the user must
+ * protect string items with double quotes for safety.
+ */
+   
+char *dvomath (int argc, char **argv, int *size, int validsize) {
+  
+  int  i, Nstack, Ncstack;
+  char   **cstack, *outname;
+  StackVar *stack;
+  Buffer *buf;
+  Vector *vec;
+
+  buf = NULL;
+  vec = NULL;
+  ALLOCATE (outname, char, 256);
+
+  /* take char array with expression, convert to important elements */
+  cstack = isolate_elements (argc, argv, &Ncstack); 
+
+  /* generate RPN stack from cstack arguments */
+  stack = convert_to_RPN (Ncstack, cstack, &Nstack);
+  if (Nstack < 1) goto error;
+
+  /* distinguish scalar, vector, matrix, check dimensions */
+  *size = check_stack (stack, Nstack, validsize);
+  if (*size < 0) goto error;
+
+  switch (*size) {
+    case 0:
+      break;
+    case 1:  /* allocate temp vector */
+      vec = NULL;
+      for (i = 0; (i < 1000) && (vec == NULL); i++) {
+	sprintf (outname, "tmp%03d", i);
+	vec = SelectVector (outname, NEWVECTOR, FALSE);
+      }
+      if (vec == NULL) { 
+	push_error ("too many tmp vectors");
+	goto error;
+      }
+      break;
+    case 2:  /* allocate temp buffer */
+      buf = NULL;
+      for (i = 0; (i < 1000) && (buf == NULL); i++) {
+	sprintf (outname, "tmp%03d", i);
+	buf = SelectBuffer (outname, NEWBUFFER, FALSE);
+      }
+      if (buf == NULL) {
+	push_error ("too many tmp buffers");
+	goto error;
+      }
+      break;
+    default:
+      goto error;
+  }
+
+  /* evaluate operations, free stack on error */
+  Ncstack = Nstack;
+  if (!evaluate_stack (stack, &Nstack)) {
+    if (*size == 1) DeleteVector (vec);
+    if (*size == 2) DeleteBuffer (buf);
+    goto error;
+  }
+
+  switch (*size) {
+    case 0:
+      if (Ncstack == 1) {
+	/* use exact input word */
+	sprintf (outname, "%s", stack[0].name);
+      } else {
+	if (stack[0].type == 's') {
+	  sprintf (outname, "%d", stack[0].IntValue);
+	} else {
+	  sprintf (outname, "%.12g", stack[0].FltValue);
+	}
+      }
+      break;
+
+    case 1:
+      MoveVector (vec, stack[0].vector);
+      break;
+  
+    case 2:
+      MoveBuffer (buf, stack[0].buffer);
+      break;
+  }
+
+  clean_stack (stack, Nstack);
+  free (stack);
+  return (outname);
+
+error:  
+  clean_stack (stack, Nstack);
+  free (stack);
+  free (outname);
+  return (NULL);
+}
Index: /branches/sc_branches/pantasks_condor/lib.shell/evaluate_stack.c
===================================================================
--- /branches/sc_branches/pantasks_condor/lib.shell/evaluate_stack.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/lib.shell/evaluate_stack.c	(revision 34783)
@@ -0,0 +1,206 @@
+# include "opihi.h"
+# define VERBOSE 0
+
+# define TWO_OP(A,B,FUNC) \
+  if (!strncasecmp (&stack[i - 2].type, A, 1) && !strncasecmp (&stack[i - 1].type, B, 1)) \
+    status = FUNC (&tmp_stack, &stack[i - 2], &stack[i - 1], stack[i].name); 
+
+# define ONE_OP(A,FUNC) \
+  if (!strncasecmp (&stack[i - 1].type, A, 1)) \
+    status = FUNC (&tmp_stack, &stack[i - 1], stack[i].name); 
+
+int evaluate_stack (StackVar *stack, int *Nstack) {
+  
+  int i, j, status;
+  char line[512]; // this is only used to report an error
+  StackVar tmp_stack;
+
+  status = TRUE;
+  init_stack (&tmp_stack);
+
+  if (*Nstack == 1) {
+    if ((stack[0].type == 'S') || (stack[0].type == 's')) {
+      clear_stack (&tmp_stack);
+      return (TRUE);
+    }
+    if (stack[0].type == 'V') {
+      /* need to make a copy so we set output value? */
+      V_unary (&tmp_stack, &stack[0], "=");
+      move_stack (&stack[0], &tmp_stack);
+      return (TRUE);
+    }
+    if (stack[0].type == 'M') {
+      /* need to make a copy so we set output value? */
+      M_unary (&tmp_stack, &stack[0], "=");
+      move_stack (&stack[0], &tmp_stack);
+      return (TRUE);
+    }
+    push_error ("syntax error: not a math expression");
+    clear_stack (&tmp_stack);
+    return (FALSE);
+  }      
+    
+  for (i = 0; i < *Nstack; i++) {
+
+    if (VERBOSE) {
+      gprint (GP_ERR, "%d: ", i);
+      for (j = 0; j < *Nstack; j++) {
+	gprint (GP_ERR, "%s ", stack[j].name);
+      }
+      if (*Nstack > 0) gprint (GP_ERR, "\n");
+      gprint (GP_ERR, "%d: ", i);
+      for (j = 0; j < *Nstack; j++) {
+	gprint (GP_ERR, "%d ", stack[j].type);
+      }
+      if (*Nstack > 0) gprint (GP_ERR, "\n");
+    }
+
+    /***** binary operators *****/
+    if ((stack[i].type >= 3) && (stack[i].type <= 8)) {
+
+      if (i < 2) {  /* need two variables to operate on */
+	sprintf (line, "syntax error: binary operator with one operand: %s\n", stack[i].name);
+	push_error (line);
+	clear_stack (&tmp_stack);
+	return (FALSE);
+      }
+
+      status = FALSE;
+      TWO_OP ("M","M",MM_binary);
+      TWO_OP ("M","V",MV_binary);
+      TWO_OP ("M","S",MS_binary);
+      TWO_OP ("V","M",VM_binary);
+      TWO_OP ("V","V",VV_binary);
+      TWO_OP ("V","S",VS_binary);
+      TWO_OP ("S","M",SM_binary);
+      TWO_OP ("S","V",SV_binary);
+      TWO_OP ("S","S",SS_binary);      
+      TWO_OP ("W","W",WW_binary);      
+      TWO_OP ("W","S",WW_binary);      
+      TWO_OP ("S","W",WW_binary);      
+      
+      if (!status) {
+	sprintf (line, "syntax error: invalid operand for binary operation: %s or %s\n", stack[i-1].name, stack[i-2].name);
+	push_error (line);
+	clear_stack (&tmp_stack);
+	return (FALSE);
+      }
+      move_stack (&stack[i-2], &tmp_stack);
+      delete_stack (&stack[i-1], 2);
+      for (j = i + 1; j < *Nstack; j++) {
+	move_stack (&stack[j - 2], &stack[j]);
+      }
+      *Nstack -= 2;
+      i -= 2;
+      init_stack (&tmp_stack);
+      continue;
+    }
+
+    /***** unary operators **/
+    if (stack[i].type == 9) {
+
+      if (i < 1) {  /* need one variable to operate on */
+	push_error ("syntax error: unary operator with no operand");
+	clear_stack (&tmp_stack);
+	return (FALSE);
+      }
+
+      ONE_OP ("M", M_unary);
+      ONE_OP ("V", V_unary);
+      ONE_OP ("S", S_unary);
+
+      /* there are no valid unary string operators */
+      if (!strncasecmp (&stack[i - 1].type, "W", 1)) {
+	push_error ("syntax error: no valid string unary ops");
+	clear_stack (&tmp_stack);
+	return (FALSE);
+      }
+
+      move_stack (&stack[i-1], &tmp_stack);
+      delete_stack (&stack[i], 1);
+      for (j = i + 1; j < *Nstack; j++) {
+	move_stack (&stack[j - 1], &stack[j]);
+      }
+      init_stack (&tmp_stack);
+      *Nstack -= 1;
+      i -= 1;
+      continue;
+    } 
+  }
+  clear_stack (&tmp_stack);
+
+  if (*Nstack > 1) {
+    push_error ("syntax error in evaluation");
+    return (FALSE);
+  }
+  return (TRUE);
+}
+
+/* copy data to new stack variable */
+void copy_stack (StackVar *stack1, StackVar *stack2) {
+  stack1[0].name     = stack2[0].name  ;
+  stack1[0].type     = stack2[0].type  ;
+  stack1[0].buffer   = stack2[0].buffer;
+  stack1[0].vector   = stack2[0].vector;
+  stack1[0].FltValue = stack2[0].FltValue;
+  stack1[0].IntValue = stack2[0].IntValue;
+}
+
+/* replace data with new stack variable */
+void move_stack (StackVar *stack1, StackVar *stack2) {
+  clear_stack (stack1);
+  copy_stack (stack1, stack2);
+  stack2[0].name = NULL;
+}
+
+/* delete name and data */
+void clean_stack (StackVar *stack, int Nstack) {
+
+  int i;
+
+  for (i = 0; i < Nstack; i++) {
+    if (IsBufferPtr (stack[i].buffer) && (stack[i].type == 'm')) {
+      if (VERBOSE) gprint (GP_ERR, "free %s (buff) (%lx)\n", stack[i].name, (long) stack[i].buffer);
+      free (stack[i].buffer[0].header.buffer);
+      free (stack[i].buffer[0].matrix.buffer);
+      free (stack[i].buffer);
+      stack[i].buffer = NULL;
+    }	
+    if (IsVectorPtr (stack[i].vector) && (stack[i].type == 'v')) {
+      if (VERBOSE) gprint (GP_ERR, "free %s (vect) (%lx)\n", stack[i].name, (long) stack[i].vector);
+      free (stack[i].vector[0].elements.Ptr);
+      free (stack[i].vector);
+      stack[i].vector = NULL;
+    }	
+    if (VERBOSE) gprint (GP_ERR, "free %s (name) (%d) (%lx)\n", stack[i].name, i, (long) stack[i].name);
+    clear_stack (&stack[i]);
+  }
+
+}
+
+/* delete name only, not data */
+void delete_stack (StackVar *stack, int Nstack) {
+  int i;
+  for (i = 0; i < Nstack; i++) {
+    clear_stack (&stack[i]);
+  }
+}
+
+void init_stack (StackVar *stack) {
+  stack[0].buffer = NULL;
+  stack[0].vector = NULL;
+  stack[0].name = NULL;
+}
+
+void assign_stack (StackVar *stack, char *name, int type) {
+  stack->name = strcreate (name);
+  stack->type = type;
+}
+
+void clear_stack (StackVar *stack) {
+  if (stack->name == NULL) return;
+  // fprintf (stderr, "free %s\n", stack->name);
+  free (stack->name);
+  stack->name = NULL;
+  return;
+}
Index: /branches/sc_branches/pantasks_condor/lib.shell/exec_loop.c
===================================================================
--- /branches/sc_branches/pantasks_condor/lib.shell/exec_loop.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/lib.shell/exec_loop.c	(revision 34783)
@@ -0,0 +1,44 @@
+# include "opihi.h"
+
+int exec_loop (Macro *loop) {
+
+  void *Signal;
+  int j, status, ThisList;
+  char *line;
+  
+  /* increase the shell level (Nlists) by one */
+  ThisList = increase_list_depth();
+  if (ThisList == 0) abort();
+
+  /* copy the macro to the current list */
+  for (j = 0; j < loop[0].Nlines; j++) {
+    add_listentry (ThisList, loop[0].line[j]);
+  }
+
+  /* set up interrupts */
+  Signal = signal (SIGINT, handle_interrupt);
+  interrupt = FALSE;
+
+  /* process the list */
+  loop_next = loop_break = loop_last = FALSE;
+  status = TRUE;
+
+  while (!interrupt) {
+    line = get_next_listentry (ThisList);
+    if (line == NULL) break;
+    status = multicommand (line);
+    free (line);
+    if (auto_break && !status) loop_break = TRUE;
+    if (loop_break || loop_last || loop_next) break;
+  }
+  signal (SIGINT, Signal);
+
+  /* free remaining lines on the list, free the list, decrement the shell level */
+  /*** can we free a list which is not the bottom lists? */
+  decrease_list_depth();
+
+  if (loop_break) return (FALSE);
+  return (TRUE);
+}
+
+/** note that the list number runs from 1 - Nlists+1 **/
Index: /branches/sc_branches/pantasks_condor/lib.shell/expand_vars.c
===================================================================
--- /branches/sc_branches/pantasks_condor/lib.shell/expand_vars.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/lib.shell/expand_vars.c	(revision 34783)
@@ -0,0 +1,154 @@
+# include "opihi.h"
+
+char *expand_vars (char *line) {
+
+  char *L, *N, *V0, *V1, *Val, *newline, *c, found;
+  int done, MacroDepth, NLINE, Noff;
+
+  if (line == NULL) return (NULL);
+  MacroDepth = GetMacroDepth ();
+
+  found = FALSE;
+  NLINE = MAX (128, strlen(line));
+  ALLOCATE (newline, char, NLINE);  /* WARNING: this limits the length of the input line */
+
+  V0 = thiscomm (line);
+  if (V0 && !strncmp ("while", V0, strlen(V0))) {
+      // special case: don't expand variables in while () statement
+      strcpy (newline, line);
+      free (line);
+      return (newline);
+  }
+  free (V0);
+
+  /* look for form $a$b..$n and expand only the last ones */
+  for (L = line, N = newline; *L != 0; N++, L++) {  /* loop until end of line */
+    for (done = FALSE; !done;) {
+      if (*L == 0) done = TRUE;
+      if (*L == '$') {
+	V1 = aftervar(L);
+	if ((V1 != NULL) && (*V1 != '$')) done = TRUE;
+	if (V1 == NULL) done = TRUE;
+      }
+      if (!done) { 
+	*N = *L;
+	 L++; 
+	 N++;
+	 if (N - newline >= NLINE - 5) {
+	   Noff = N - newline;
+	   NLINE += 128;
+	   REALLOCATE (newline, char, NLINE);
+	   N = newline + Noff;
+	 }
+      }
+    }
+
+    if (*L == 0) break;
+
+    V1 = aftervar (L);           /* V1 points to the first non-WHITESPACE after the variable */
+    V0 = thisvar (L);            /* V0 points to the name of the var */
+    /* note: V1 points to a fraction of L, it does not need to be freed */
+
+    /* no variable name */
+    if (V0 == NULL) {
+      *N = *L;
+      if (N - newline >= NLINE - 5) {
+	Noff = N - newline;
+	NLINE += 128;
+	REALLOCATE (newline, char, NLINE);
+	N = newline + Noff;
+      }
+      continue;
+    }
+
+    /* variable assignment (skip these variables) */
+    if ((L == line) && (V1 != NULL)) {
+      int isAssignment;
+
+      isAssignment = FALSE;
+      isAssignment |= (V1[0] == '=');
+      isAssignment |= (V1[0] == '+') && (V1[1] == '=');
+      isAssignment |= (V1[0] == '-') && (V1[1] == '=');
+      isAssignment |= (V1[0] == '+') && (V1[1] == '+');
+      isAssignment |= (V1[0] == '-') && (V1[1] == '-');
+
+      if (isAssignment) {
+	*N = *L;
+	free (V0);
+	if (N - newline >= NLINE - 5) {
+	  Noff = N - newline;
+	  NLINE += 128;
+	  REALLOCATE (newline, char, NLINE);
+	  N = newline + Noff;
+	}
+	continue;
+      }
+    }
+
+    found = TRUE;
+    for (c = V0; isdigit(*c); c++); /* test if this is a macro argument variable (ie, $1.2) */
+    if (*c == 0) {  /* all digit var == macro parameter */
+      if (!MacroDepth) {
+	gprint (GP_ERR, "not in a macro\n");
+	goto error;
+      } else { /* if we are executing a macro, attach the list depth to the front, pass on down the line */
+	// XXX this is limiting!!!
+	ALLOCATE (c, char, 1024);
+	sprintf (c, "%d.%s", MacroDepth, V0);
+	free (V0);
+	V0 = c;
+      }
+    }
+
+    Val = get_variable_ptr (V0);
+    if (Val == NULL) {   /* var was not found! */
+      gprint (GP_ERR, "variable %s not found\n", V0);
+      goto error;
+    }
+    for (; *Val != 0; N++, Val++)  {
+      *N = *Val; /* place the value of the variable in the newline */
+      if (N - newline >= NLINE - 5) {
+	Noff = N - newline;
+	NLINE += 128;
+	REALLOCATE (newline, char, NLINE);
+	N = newline + Noff;
+      }
+    }
+    N--; /* we overshoot on last loop */
+
+    /* skip past the variable in the source line */
+    /* L currently points at $ */
+    L++;
+    if (*L == '?') L++;  /* skip past ? in $?name */
+
+    while (ISVAR(*L)) L++;
+    L--; /* we overshoot */
+
+    if (V0 != NULL) free (V0);
+  }
+
+  *N = 0;
+  free (line);
+  REALLOCATE (newline, char, strlen (newline) + 1);
+  if (found) { /* try again for new variables */
+    newline = expand_vars (newline);
+  }
+  return (newline);
+
+error:
+  free (line);
+  free (V0);
+  free (newline);
+  return (NULL);
+}
+
+
+  /************************
+    we go through the line looking for the dollar signs ($) marking the 
+    variables.  We don't expand a variable if it is the first thing on the line 
+    AND it is followed by an equals sign.  
+    that is, don't expand if:
+    V0 = NULL ($ is last non-blank char on line -- no variable) or
+    V1[0] is = (assignment of variable value), AND L == line (ie, at beginning of line)
+    *************************/
+
Index: /branches/sc_branches/pantasks_condor/lib.shell/expand_vectors.c
===================================================================
--- /branches/sc_branches/pantasks_condor/lib.shell/expand_vectors.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/lib.shell/expand_vectors.c	(revision 34783)
@@ -0,0 +1,177 @@
+# include "opihi.h"
+
+char *expand_vectors (char *line) {
+
+  char *newline, *tmpline, strValue[128], *val;
+  char *L, *N, *p, *q, *p2, *w;
+  int n, I, J, size, showLength, NLINE, Noff, isBuffer, inRange;
+  float *ptr;
+  double f1;
+  Vector *vec;
+  Buffer *buf;
+
+  if (line == NULL) return (NULL);
+
+  NLINE = MAX (128, strlen(line));
+  ALLOCATE (newline, char, NLINE);
+
+  /* look for form fred[stuff] */
+  /* skip first word (command) */
+  L = nextword (line);
+  if (L == NULL) {
+    free (newline);
+    return (line);
+  }
+
+  n = L - line;
+  strncpy (newline, line, n);
+  N = newline + n;
+  J = 0;
+  while (1) {
+    /* find square-bracket pair [..] */
+    p = strchr (L, '[');
+    q = strchr (L, ']');
+    if ((p == NULL) && (q != NULL)) goto dumpline;
+    if ((p != NULL) && (q == NULL)) goto dumpline;
+    if ((p == NULL) && (q == NULL)) goto dumpline;
+    if (p > q) goto dumpline; /* odd state: unmatched pair: ][ */
+
+    /* find vector subscript */
+    n = (int) (q - p - 1);
+    val = NULL;
+    showLength = FALSE;
+    if (n == 0) {
+      showLength = TRUE;
+    } else {
+      tmpline = strncreate (p+1, n);
+      val = dvomath (1, &tmpline, &size, 0);
+      free (tmpline);
+      if (val == NULL) goto dumpline; /* not a valid vector subscript */
+    }
+    I = 0; 
+    if (val != NULL) {
+      I = atoi (val);
+      free (val);
+    }      
+
+    /* if a second [..] immediately follows, we are a buffer, not a vector */
+    isBuffer = FALSE;
+    if (*(q + 1) == '[') {
+      p2 = strchr (q + 1, ']');
+      if (p2 != NULL) {
+	if (showLength) {
+	  gprint (GP_ERR, "unsupported : name[][..]\n"); 
+	  goto asVector;
+	}
+
+	isBuffer = TRUE;
+
+	/* find buffer second subscript */
+	n = (int) (p2 - q - 2);
+	val = NULL;
+	if (n == 0) {
+	  gprint (GP_ERR, "unsupported : name[..][]\n"); 
+	  isBuffer = FALSE;
+	  goto asVector;
+	} 
+	tmpline = strncreate (q+2, n);
+	val = dvomath (1, &tmpline, &size, 0);
+	free (tmpline);
+	if (val == NULL) {
+	  isBuffer = FALSE;
+	  goto asVector; /* not a valid vector subscript */
+	}
+	J = 0; 
+	if (val != NULL) {
+	  J = atoi (val);
+	  free (val);
+	}      
+      }
+      q = p2;
+    }
+
+  asVector:
+    /* find vector/buffer name */
+    for (w = p - 1; (w >= line) && !OHANA_WHITESPACE(*w) && (isalnum(*w) || (*w == ':') || (*w == '_')); w--);
+    w ++;
+    n = (int)(p - w);
+    tmpline = strncreate (w, n);
+
+    if (isBuffer) {
+      if ((buf = SelectBuffer (tmpline, OLDBUFFER, TRUE)) == NULL) goto dumpline;
+
+      /* find buffer element */
+      inRange = TRUE;
+      inRange &= (I <  +1*buf[0].header.Naxis[0]);
+      inRange &= (I >= -1*buf[0].header.Naxis[0]);
+      inRange &= (J <  +1*buf[0].header.Naxis[1]);
+      inRange &= (J >= -1*buf[0].header.Naxis[1]);
+      if (!inRange) {
+	gprint (GP_ERR, "buffer subscript out of range\n"); 
+	goto escape;
+      }
+      if (I < 0) I += buf[0].header.Naxis[0];
+      if (J < 0) J += buf[0].header.Naxis[1];
+      ptr = (float *) buf[0].matrix.buffer;
+      f1 = ptr[I + J*buf[0].header.Naxis[0]];
+    } else {
+      if ((vec = SelectVector (tmpline, OLDVECTOR, TRUE)) == NULL) goto dumpline;
+
+      /* find vector element */
+      if (showLength) {
+	f1 = vec[0].Nelements;
+      } else {
+	if ((I >= vec[0].Nelements) || (I < -1*vec[0].Nelements)) {
+	  gprint (GP_ERR, "vector subscript out of range\n"); 
+	  goto escape;
+	}
+	if (I < 0) I += vec[0].Nelements;
+	f1 = (vec[0].type == OPIHI_FLT) ? vec[0].elements.Flt[I] : vec[0].elements.Int[I];
+      }
+    }
+
+    free (tmpline);
+    if ((int)f1 == f1) 
+      snprintf (strValue, 128, "%.0f", f1); 
+    else
+      snprintf (strValue, 128, "%.12g", f1);
+
+    /* interpolate vector element into newline (being accumulated) */
+    size = (N - newline) + (w - L) + strlen(strValue);
+    if (size >= NLINE) {
+      Noff = N - newline;
+      NLINE += 128 + strlen(strValue) + (w - L);
+      REALLOCATE (newline, char, NLINE);
+      N = newline + Noff;
+    }
+
+    n = (int) (w - L);
+    strncpy (N, L, n);
+    N += n;
+    n = strlen (strValue);
+    strncpy (N, strValue, n);
+    N += n;
+    L = q + 1;
+  }
+
+dumpline:
+  size = (N - newline) + strlen(L);
+  if (size >= NLINE) {
+    Noff = N - newline;
+    NLINE += 128 + strlen(L);
+    REALLOCATE (newline, char, NLINE);
+    N = newline + Noff;
+  }
+
+  n = strlen (L);
+  strncpy (N, L, n);
+  N[n] = 0;
+  free (line);
+  return (newline);
+  
+escape:
+  free (line);
+  free (newline);
+  return (NULL);
+}
+
Index: /branches/sc_branches/pantasks_condor/lib.shell/gprint.c
===================================================================
--- /branches/sc_branches/pantasks_condor/lib.shell/gprint.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/lib.shell/gprint.c	(revision 34783)
@@ -0,0 +1,366 @@
+# include "shell.h"
+
+/* we need to control the output destinations a bit carefully
+   in the pantasks_server mode.  The server needs to be able to 
+   send the output to a specific output device (eg, logging file)
+   of to save it within an internal buffer */
+
+/* further notes:
+   gprintf (int destination, char *format, ...);
+
+   the destination may be GP_LOG or GP_ERR.  by default, these go to stdout and
+   stderr.  either one may be redirected to another file or to a buffer.  as a
+   stand-along program, the outfile command redirects the GP_LOG output to an
+   alternate output file.  in the server mode, we redirect LOG to a standard log
+   file and ERR to a standard error file for server messages.  Commands executed
+   by the client have both streams returned to the client in turn, and are in
+   turn sent to stderr or the current stdout destination.
+
+   each thread has an independently set output destination.
+
+   option 1: NULL for invalid element:
+
+     a stream is either set to a FILE or an IOBuffer.  when it is set to a FILE,
+     the IOBuffer is freed and set to NULL.  when it is an IOBuffer, the file
+     pointer is closed and set to NULL.  setting a new FILE results in the 
+     IObuffer being freed and an already opened FILE to be flushed and closed.
+
+
+  gprint & mutex:
+
+     gprintInit : init_stream_mutex
+     gprintGetStream : init_stream_mutex
+     gprintCloseFile : init_stream_mutex
+
+     gprintSetFileAllThreads : set_file_mutex
+     (gprintSetFile -> gprintCloseFile)
+
+     gprintSetFileThisThread : set_file_mutex
+     (gprintGetStream)
+     (gprintSetFile -> gprintCloseFile)
+
+     we are safe from dead locks: init_stream_mutex can be wrapped by set_file_mutex, but
+     not vice-versa
+
+*/
+
+static gpStream **streams = NULL;
+static int Nstreams = 0;
+
+static pthread_mutex_t init_stream_mutex = PTHREAD_MUTEX_INITIALIZER;
+
+void gprintInit () {
+
+  int N;
+  pthread_t id;
+
+  /* need to use a mutex to prevent two threads from initing simultaneously */
+  pthread_mutex_lock (&init_stream_mutex);
+
+  // streams is an array of pointers so we can add more streams without changing pointers 
+  if (streams == NULL) {
+    Nstreams = 2;
+    ALLOCATE (streams, gpStream *, Nstreams);
+  } else {
+    Nstreams += 2;
+    REALLOCATE (streams, gpStream *, Nstreams);
+  }
+
+  /* create two output streams for this thread: LOG and ERR */
+  id = pthread_self();
+
+  N = Nstreams - 2;
+  ALLOCATE (streams[N], gpStream, 1);
+  streams[N][0].dest = GP_LOG;
+  streams[N][0].file = stdout;
+  streams[N][0].name = strcreate ("stdout");
+
+  ALLOCATE (streams[N][0].buffer, IOBuffer, 1);
+  InitIOBuffer (streams[N][0].buffer, 64);
+  streams[N][0].mode = GP_FILE;
+  streams[N][0].thread = id;
+
+  N = Nstreams - 1;
+  ALLOCATE (streams[N], gpStream, 1);
+  streams[N][0].dest = GP_ERR;
+  streams[N][0].file = stderr;
+  streams[N][0].name = strcreate ("stderr");
+
+  ALLOCATE (streams[N][0].buffer, IOBuffer, 1);
+  InitIOBuffer (streams[N][0].buffer, 64);
+  streams[N][0].mode = GP_FILE;
+  streams[N][0].thread = id;
+
+  pthread_mutex_unlock (&init_stream_mutex);
+}
+
+gpStream *gprintGetStream (gpDest dest) {
+
+  int i;
+  pthread_t id;
+  gpStream *stream;
+
+  // need to wait for initialization to be finished before getting stream or the array
+  // (streams[i]) may move
+  pthread_mutex_lock (&init_stream_mutex);
+
+  id = pthread_self();
+
+  /* find the existing output stream which matches */
+  for (i = 0; i < Nstreams; i++) {
+    if (streams[i][0].dest != dest) continue;
+    if (!pthread_equal (streams[i][0].thread, id)) continue;
+    stream = streams[i];
+    pthread_mutex_unlock (&init_stream_mutex);
+    return stream;
+  }
+  pthread_mutex_unlock (&init_stream_mutex);
+  fprintf (stderr, "programming error: gprintInit not called for thread\n");
+  abort ();
+}
+
+// close if necessary, set to file (may be NULL)
+void gprintCloseFile (gpStream *stream, FILE *file) {
+
+  int i, Nmatch;
+
+  // do not close the file if the new file is the same
+  if (stream[0].file == file) return;
+
+  // do not close the file if the old one is NULL
+  if (stream[0].file == NULL) {
+    stream[0].file = file;    
+    return;
+  }
+
+  // check the special cases (do not close old file in these cases)
+  if (stream[0].file == stdout) {
+    stream[0].file = file;
+    return;
+  }
+  if (stream[0].file == stderr) {
+    stream[0].file = file;
+    return;
+  }
+
+  // we cannot do the operation below while another thread is initing
+  pthread_mutex_lock (&init_stream_mutex);
+
+  // must we close the existing file? if still being used, then no
+  Nmatch = 0;
+  for (i = 0; i < Nstreams; i++) {
+    if (stream == streams[i]) continue;
+    if (streams[i][0].file == stream[0].file) Nmatch ++;
+  }
+  if (Nmatch == 0) {
+    // fprintf (stderr, "closed %x, opened %x (%s) -- thread %d\n", stream[0].file, file, stream[0].name, stream[0].thread);
+    fflush (stream[0].file);
+    fclose (stream[0].file);
+  }
+  stream[0].file = file;
+  pthread_mutex_unlock (&init_stream_mutex);
+
+  return;
+}
+
+// this thread only operates on its own stream
+void gprintSetBuffer (gpDest dest) {
+
+  gpStream *stream;
+
+  stream = gprintGetStream (dest);
+
+  // close the existing file (if needed), set to NULL
+  gprintCloseFile (stream, NULL);
+
+  assert (stream[0].buffer);
+  FlushIOBuffer (stream[0].buffer);
+  
+  stream[0].mode = GP_BUFF;
+}
+
+// this thread only operates on its own stream
+IOBuffer *gprintGetBuffer (gpDest dest) {
+
+  gpStream *stream;
+  stream = gprintGetStream (dest);
+  return (stream[0].buffer);
+}
+
+static pthread_mutex_t set_file_mutex = PTHREAD_MUTEX_INITIALIZER;
+
+void gprintSetFileAllThreads (gpDest dest, char *filename) {
+
+  int i;
+
+  // be sure we are not colliding with gprintSetFileThisThread
+  pthread_mutex_lock (&set_file_mutex);
+
+  for (i = 0; i < Nstreams; i++) {
+    if (streams[i][0].dest != dest) continue;
+    gprintSetFile (streams[i], dest, filename);
+  }
+
+  pthread_mutex_unlock (&set_file_mutex);
+  return;
+}
+
+// this thread only operates on its own stream
+void gprintSetFileThisThread (gpDest dest, char *filename) {
+
+  gpStream *stream;
+
+  // be sure we are not colliding with gprintSetFileAllThreads
+  pthread_mutex_lock (&set_file_mutex);
+
+  stream = gprintGetStream (dest);
+  gprintSetFile (stream, dest, filename);
+
+  pthread_mutex_unlock (&set_file_mutex);
+  return;
+}
+
+void gprintSetFile (gpStream *stream, gpDest dest, char *filename) {
+
+  FILE *file;
+
+  assert (stream[0].buffer);
+
+  /* if we have an open buffer, free it and null it */
+  if (stream[0].buffer[0].Nbuffer) {
+    // XXX we drop what was on the buffer, send it to the old or the new file?
+    FlushIOBuffer (stream[0].buffer);
+  }
+
+  stream[0].mode = GP_FILE;
+
+  // if NULL, reuse exising stream name
+  if (filename != NULL) {
+    free (stream[0].name);
+    stream[0].name = strcreate (filename);
+  }
+
+  // we allow the user to set stdout to ERR and stderr to LOG if they want
+  if (!strcmp (stream[0].name, "stdout")) {
+    gprintCloseFile (stream, stdout);
+    return;
+  }
+  if (!strcmp (stream[0].name, "stderr")) {
+    gprintCloseFile (stream, stderr);
+    return;
+  }
+  
+  // open the file. only close old pointer if no one else is using it
+  file = fopen (stream[0].name, "a");
+  if (file == NULL) {
+    // XXX this is a problem: we are leaving open the old file
+    fprintf (stderr, "cannot open file %s\n", stream[0].name);
+    free (stream[0].name);
+    file = (dest == GP_LOG) ? stdout : stderr;
+    stream[0].name = (dest == GP_LOG) ? strcreate ("stdout") : strcreate("stderr");
+    gprintCloseFile (stream, file);
+    return;
+  }
+
+  // close the existing file (if needed), set to new file
+  gprintCloseFile (stream, file);
+  return;
+}
+
+// this thread only operates on its own stream
+FILE *gprintGetFile (gpDest dest) {
+
+  gpStream *stream;
+  stream = gprintGetStream (dest);
+  return (stream[0].file);
+}
+
+// this thread only operates on its own stream
+char *gprintGetName (gpDest dest) {
+
+  gpStream *stream;
+  stream = gprintGetStream (dest);
+  return (stream[0].name);
+}
+
+int gprint (gpDest dest, char *format, ...) {
+
+  int status;
+  va_list argp;  
+
+  va_start (argp, format);
+  status = gprintv (dest, format, argp);
+  va_end (argp);
+  return (status);
+}
+
+int gprintv (gpDest dest, char *format, va_list argp) {
+
+  int status;
+  gpStream *stream;
+
+  // this thread only writes to its own stream
+  stream = gprintGetStream (dest);
+
+  if (stream[0].mode == GP_FILE) {
+    status = vfprintf (stream[0].file, format, argp);
+    fflush (stream[0].file);
+    if (status < 0) {
+      return (FALSE);
+    }
+  } else {
+    vPrintIOBuffer (stream[0].buffer, format, argp);
+  }
+  return (TRUE);
+}
+
+int gwrite (char *buffer, int size, int N, gpDest dest) {
+
+  int Nbyte;
+  IOBuffer *outbuff;
+  gpStream *stream;
+
+  // this thread only writes to its own stream
+  stream = gprintGetStream (dest);
+
+  if (stream[0].mode == GP_FILE) {
+    fwrite (buffer, size, N, stream[0].file);
+    fflush (stream[0].file);
+  } else {
+    // XXX can we not use exising IOBuffer APIs here?
+    outbuff = stream[0].buffer;
+    Nbyte = size * N;
+    if (outbuff[0].Nbuffer + Nbyte >= outbuff[0].Nalloc) {
+      outbuff[0].Nalloc = outbuff[0].Nbuffer + Nbyte + 64;
+      REALLOCATE (outbuff[0].buffer, char, outbuff[0].Nalloc);
+    }
+    memcpy (&outbuff[0].buffer[outbuff[0].Nbuffer], buffer, Nbyte);
+    outbuff[0].Nbuffer += Nbyte;
+  }
+  return (TRUE);
+}
+
+# define MAX_ERROR_LENGTH 256           // Maximum length string for error messages
+
+// print an error (based on errno values) to gprint destination
+int gprint_syserror (gpDest dest, int myError, char *format, ...) {
+
+  char errorBuf[MAX_ERROR_LENGTH];
+  char *errorMsg;
+  va_list argp;  
+
+  // there are two strerror_r implementations; choose the right one:
+#if (((_POSIX_C_SOURCE >= 200112L || _XOPEN_SOURCE >= 600) && ! _GNU_SOURCE) || __APPLE__)
+  strerror_r (myError, errorBuf, MAX_ERROR_LENGTH);
+  errorMsg = errorBuf;
+#else
+  errorMsg = strerror_r (myError, errorBuf, MAX_ERROR_LENGTH);
+#endif
+
+  va_start (argp, format);
+  gprintv (dest, format, argp);
+  va_end (argp);
+
+  gprint (dest, "%s\n", errorMsg);
+  return TRUE;
+}
Index: /branches/sc_branches/pantasks_condor/lib.shell/interrupt.c
===================================================================
--- /branches/sc_branches/pantasks_condor/lib.shell/interrupt.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/lib.shell/interrupt.c	(revision 34783)
@@ -0,0 +1,53 @@
+# include "opihi.h"
+
+static int Nint = 0;
+
+/* return FALSE if we are called many times in a row 
+   without a valid answer or any answer */
+
+void handle_interrupt (int input) {
+  
+  char string[64];
+  int Nask;
+
+  signal (SIGINT, SIG_IGN);
+
+  Nask = 0;
+  Nint ++;
+
+  // 3 ctrl-c in a row will interrupt regardless
+  if (Nint > 3) { 
+    interrupt = TRUE;
+    return;
+  }
+  if (Nint > 100) { 
+    exit (3);
+  }
+  
+  while (1) {
+    gprint (GP_ERR, "operation halted, continue? (y/n) ");
+    if (fscanf (stdin, "%s", string) != 1) fprintf (stderr, "what?\n");
+    
+    if ((string[0] == 'y') || (string[0] == 'Y')) {
+      interrupt = FALSE;
+      signal (SIGINT, handle_interrupt);
+      Nint = 0;
+      return;
+    }
+
+    if ((string[0] == 'n') || (string[0] == 'N')) {
+      interrupt = TRUE;
+      signal (SIGINT, handle_interrupt);
+      Nint = 0;
+      return;
+    }
+    Nask ++;
+    if (Nask > 3) {
+      interrupt = TRUE;
+      signal (SIGINT, handle_interrupt);
+      Nint = 0;
+      return;
+    }
+  }  
+
+}
Index: /branches/sc_branches/pantasks_condor/lib.shell/macro_create.c
===================================================================
--- /branches/sc_branches/pantasks_condor/lib.shell/macro_create.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/lib.shell/macro_create.c	(revision 34783)
@@ -0,0 +1,113 @@
+# include "opihi.h"
+# define D_NLINES 100
+# define prompt "> "
+int macro_exec   PROTO((int, char **));
+
+int macro_create (int argc, char **argv) {
+
+  int ThisList, depth, i, done, NLINES, N;
+  char *input, *help;
+  Command *cmd;
+  Macro *macro;
+
+  if ((N = get_argument (argc, argv, "-c"))) {
+    remove_argument (N, &argc, argv);
+    help = strcreate(argv[N]);
+    remove_argument (N, &argc, argv);
+  } else {
+    help = strcreate ("(macro)");
+  }
+
+  if (argc != 2) {
+    gprint (GP_ERR, "USAGE: macro <name> [-c \"comment line\"]\n");
+    gprint (GP_ERR, "  (enter commands, end with the word 'END')\n");
+    return (FALSE);
+  }
+
+  /**** Check for existence of this macro ****/
+  cmd = MatchCommand (argv[1], FALSE, TRUE);
+  macro = MatchMacro (argv[1], FALSE, TRUE);
+  
+  if ((macro == NULL) && (cmd != NULL)) {
+    gprint (GP_ERR, "cannot redefine inherent command %s\n", argv[1]);
+    return (FALSE);
+  }
+  if ((macro != NULL) && (cmd == NULL)) {
+    gprint (GP_ERR, "programming error: macro not in command list (%s)\n", argv[1]);
+    return (FALSE);
+  }
+
+  if (macro == NULL) { /**** New Macro ****/
+    ALLOCATE (cmd, Command, 1);
+    cmd[0].real = FALSE;
+    cmd[0].name = strcreate (argv[1]);
+    cmd[0].help = help;
+    cmd[0].func = macro_exec;
+    AddCommand (cmd);
+    free (cmd);
+    macro = NewMacro (argv[1]);
+  } else { /**** Old Macro ****/
+    /* replace existing command help with new value */
+    free (cmd[0].help);
+    cmd[0].help = help;
+  }
+
+  /* reallocate space for macro lines */
+  NLINES = D_NLINES;
+  for (i = 0; i < macro[0].Nlines; i++) free (macro[0].line[i]);
+  REALLOCATE (macro[0].line, char *, NLINES);
+
+  /* read in macro
+     If we are entering at the keyboard (ThisList == 0), use readline.
+     Otherwise, read from the current list
+     End when we hit the final "END" (the true END -- we count macro defines!!) */
+     
+  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);
+    }
+
+    if ((ThisList == 0) && (input == (char *) NULL)) {
+      gprint (GP_ERR, "end macro with 'END'\n");
+      continue;
+    }
+
+    if ((ThisList > 0) && (input == (char *) NULL)) {
+      gprint (GP_ERR, "missing 'END' in macro definition\n");
+      input = strcreate ("end");
+    }
+
+    stripwhite (input);
+
+    /* 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) { /* we hit the last "END", macro is done */
+	free (input);
+	macro[0].Nlines = i;
+	if (macro[0].Nlines == 0) i = 1;
+	REALLOCATE (macro[0].line, char *, i);
+	return (TRUE);
+      }
+    }
+
+    if (*input) { 
+      macro[0].line[i] = input;
+      i++;
+      if (i == NLINES - 1) {
+	NLINES += D_NLINES;
+	REALLOCATE (macro[0].line, char *, NLINES);
+      }
+    }
+  }
+  return (TRUE);
+}
Index: /branches/sc_branches/pantasks_condor/lib.shell/macro_delete.c
===================================================================
--- /branches/sc_branches/pantasks_condor/lib.shell/macro_delete.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/lib.shell/macro_delete.c	(revision 34783)
@@ -0,0 +1,28 @@
+# include "opihi.h"
+
+int macro_delete (int argc, char **argv) {
+
+  Command *cmd;
+  Macro *macro;
+
+  if (argc != 2) {
+    gprint (GP_ERR, "USAGE: macro delete <macro>\n");
+    return (FALSE);
+  }
+
+  macro = MatchMacro (argv[1], FALSE, TRUE);
+  if (macro == NULL) {
+    gprint (GP_ERR, "Macro %s not found\n", argv[1]);
+    return (FALSE);
+  }
+  cmd = MatchCommand (argv[1], FALSE, TRUE);
+  if (cmd == NULL) {
+    gprint (GP_ERR, "programming error: macro exists but not command\n");
+    return (FALSE);
+  }
+
+  DeleteMacro (macro);
+  DeleteCommand (cmd);
+  return (TRUE);
+}
+
Index: /branches/sc_branches/pantasks_condor/lib.shell/macro_edit.c
===================================================================
--- /branches/sc_branches/pantasks_condor/lib.shell/macro_edit.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/lib.shell/macro_edit.c	(revision 34783)
@@ -0,0 +1,9 @@
+# include "opihi.h"
+
+int macro_edit (int argc, char **argv) {
+
+  gprint (GP_ERR, "this function is not implemented yet\n");
+  return (FALSE);
+
+}
+
Index: /branches/sc_branches/pantasks_condor/lib.shell/macro_exec.c
===================================================================
--- /branches/sc_branches/pantasks_condor/lib.shell/macro_exec.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/lib.shell/macro_exec.c	(revision 34783)
@@ -0,0 +1,47 @@
+# include "opihi.h"
+
+int macro_exec (int argc, char **argv) {
+
+  int i, status, MacroDepth;
+  char *PreviousName, **params, tmp[1024];
+  Macro *macro;
+  
+  macro = MatchMacro (argv[0], FALSE, TRUE);
+  if (macro == NULL) {
+    gprint (GP_ERR, "%s: Command not found.\n", argv[0]);
+    return (FALSE);
+  }
+
+  /* increase MacroDepth by one - governs macro args */
+  PreviousName = GetMacroName ();
+  MacroDepth = GetMacroDepth ();
+  MacroDepth ++;
+  SetCurrentMacroData (macro[0].name, MacroDepth);
+
+  ALLOCATE (params, char *, argc);
+  sprintf (tmp, "%d.%d", MacroDepth, 0);
+  params[0] = strcreate (tmp);
+  sprintf (tmp, "%d", argc);
+  set_str_variable (params[0], tmp);
+  for (i = 1; i < argc; i++) {
+    sprintf (tmp, "%d.%d", MacroDepth, i);
+    params[i] = strcreate (tmp);
+    set_str_variable (params[i], argv[i]);
+  }
+
+  /* process this list */
+  status = exec_loop (&macro[0]);
+  loop_last = loop_next = FALSE; 
+  /* 'last' and 'next' should only affect one loop */
+
+  /* clear out the command line variables */
+  for (i = 0; i < argc; i++) {
+    DeleteNamedScalar (params[i]);
+    free (params[i]);
+  }
+  free (params);
+  
+  MacroDepth --;
+  SetCurrentMacroData (PreviousName, MacroDepth);
+  return (status);
+}
Index: /branches/sc_branches/pantasks_condor/lib.shell/macro_funcs.c
===================================================================
--- /branches/sc_branches/pantasks_condor/lib.shell/macro_funcs.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/lib.shell/macro_funcs.c	(revision 34783)
@@ -0,0 +1,26 @@
+# include "basic.h"
+
+static Command macro_command[] = {
+  {1, "create", macro_create, "create a macro *"},
+  {1, "delete", macro_delete, "delete a macro *"},
+  {1, "list",   macro_list_f, "list a macro *"},
+  {1, "edit",   macro_edit,   "edit a macro <not working yet!> *"},
+  {1, "read",   macro_read,   "read a macro from a file <not working yet!> *"},
+  {1, "write",  macro_write,  "write a macro to a file <not working yet!> *"}
+};
+
+CommandF *find_macro_command (char *name) {
+
+  int i, N;
+
+  N = sizeof (macro_command) / sizeof (Command);
+
+  /* find the macro sub-command which matches from the list. */
+  for (i = 0; i < N; i++) {
+    if (!strcmp (macro_command[i].name, name)) {
+      return (macro_command[i].func);
+    }
+  }
+
+  return NULL;
+}
Index: /branches/sc_branches/pantasks_condor/lib.shell/macro_list.c
===================================================================
--- /branches/sc_branches/pantasks_condor/lib.shell/macro_list.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/lib.shell/macro_list.c	(revision 34783)
@@ -0,0 +1,23 @@
+# include "opihi.h"
+
+int macro_list_f (int argc, char **argv) {
+
+  int i;
+  Macro *macro;
+
+  if (argc != 2) {
+    gprint (GP_ERR, "USAGE: macro list <macro>\n");
+    return (FALSE);
+  }
+
+  macro = MatchMacro (argv[0], FALSE, TRUE);
+  if (macro == NULL) {
+    gprint (GP_ERR, "%s: Macro not found\n", argv[1]);
+    return (FALSE);
+  }
+
+  for (i = 0; i < macro[0].Nlines; i++) {
+    gprint (GP_ERR, "%s\n", macro[0].line[i]);
+  }
+  return (TRUE);
+}
Index: /branches/sc_branches/pantasks_condor/lib.shell/macro_read.c
===================================================================
--- /branches/sc_branches/pantasks_condor/lib.shell/macro_read.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/lib.shell/macro_read.c	(revision 34783)
@@ -0,0 +1,9 @@
+# include "opihi.h"
+
+int macro_read (int argc, char **argv) {
+
+  gprint (GP_ERR, "this function is not implemented yet\n");
+  return (FALSE);
+
+}
+
Index: /branches/sc_branches/pantasks_condor/lib.shell/macro_write.c
===================================================================
--- /branches/sc_branches/pantasks_condor/lib.shell/macro_write.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/lib.shell/macro_write.c	(revision 34783)
@@ -0,0 +1,9 @@
+# include "opihi.h"
+
+int macro_write (int argc, char **argv) {
+
+  gprint (GP_ERR, "this function is not implemented yet\n");
+  return (FALSE);
+
+}
+
Index: /branches/sc_branches/pantasks_condor/lib.shell/memstr.c
===================================================================
--- /branches/sc_branches/pantasks_condor/lib.shell/memstr.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/lib.shell/memstr.c	(revision 34783)
@@ -0,0 +1,35 @@
+# include "shell.h"
+
+/* memstr returns a view, not an allocated string : don't free */
+/* returns pointer to start of m2 in m1, or NULL if failure */ 
+char *memstr (char *m1, char *m2, int n) {
+
+  int i, N;
+
+  N = strlen (m2);
+  for (i = 0; (i < n - N + 1) && memcmp (m1, m2, N); i++, m1++);
+  if (memcmp (m1, m2, N)) return (NULL);
+  return (m1);
+
+}
+
+/* formatted write statement, with intelligent allocation */
+int write_fmt (int fd, char *format, ...) {
+
+  int Nbyte, status;
+  char tmp, *line;
+  va_list argp;  
+
+  va_start (argp, format);
+  Nbyte = vsnprintf (&tmp, 0, format, argp);
+  va_end (argp);
+
+  va_start (argp, format);
+  ALLOCATE (line, char, Nbyte + 1);
+  vsnprintf (line, Nbyte + 1, format, argp);
+  status = write (fd, line, strlen(line));
+  va_end (argp);
+
+  free (line);
+  return (status);
+}
Index: /branches/sc_branches/pantasks_condor/lib.shell/multicommand.c
===================================================================
--- /branches/sc_branches/pantasks_condor/lib.shell/multicommand.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/lib.shell/multicommand.c	(revision 34783)
@@ -0,0 +1,165 @@
+# include "opihi.h"
+
+static int server = 0;
+static int bufferPending = FALSE;
+
+#define MSG_TIMEOUT 3.0
+
+int getServer () {
+  return (server);
+}
+
+// XXX this is rather pantasks-specific...
+void multicommand_InitServer () {
+
+  char hostname[256], PASSWORD[256], portinfo[256];
+
+  if (server != 0) {
+    /* check if down? */
+    fprintf (stderr, "error: server fd already defined\n");
+    exit (30);
+  }
+
+  /* find the defined server hostname */
+  if (VarConfig ("PANTASKS_SERVER", "%s", hostname) == NULL) {
+    gprint (GP_ERR, "pantasks server host undefined\n");
+    exit (31);
+  }
+
+  /* is a port range defined? otherwise use the default */
+  memset (portinfo, 0, 256);
+  VarConfig ("PANTASKS_SERVER_PORT", "%s", portinfo);
+
+  /* attempt to connect to the server */
+  server = GetClientSocket (hostname, portinfo);
+
+  /* here we can perform the security handshaking */
+  VarConfig ("PASSWORD", "%s", PASSWORD);
+  SendCommand (server, strlen(PASSWORD), "%s", PASSWORD);
+  
+  return;
+}
+
+// close connection with remote server
+void multicommand_StopServer () {
+
+  if (!server) return;
+  close (server);
+  server = 0;
+  return;
+}
+
+/* take input line and split into multiple lines
+   at the semi-colons.  send these to 'command' */
+
+int multicommand (char *line) {
+ 
+  int done, status, verbose;
+  char *p, *q, *tmpline, *outline;
+  IOBuffer message;
+
+  /* if no server is defined, use 'verbose' mode for 'command' */ 
+  verbose = (server == 0);
+
+  p = line; 
+  done = FALSE;
+  status = TRUE;
+  while (!done) {
+    q = strchr (p, ';');
+    if (q == NULL) {
+      q = p + strlen(p);
+      done = TRUE;
+    }
+    tmpline = strncreate (p, q - p);
+    stripwhite (tmpline);
+    if (*tmpline) {
+
+      if (bufferPending) {
+	// flush old messages
+	ExpectMessage (server, 0.2, &message);
+	bufferPending = FALSE;
+      }
+
+      status = command (tmpline, &outline, verbose);
+
+      if (status == -1) {
+	if (server) {
+	  // send the command to the server instead
+	  if (!SendMessage (server, "%s", outline)) {
+	    switch (errno) {
+	      case EPIPE:
+		gprint (GP_ERR, "server connection has died\n");
+		exit (32);
+	      default:
+		gprint (GP_ERR, "server is busy...32\n");
+		bufferPending = TRUE;
+		goto escape;
+	    }
+	  }
+
+	  // receive the command exit status
+	  if (ExpectMessage (server, MSG_TIMEOUT, &message)) {
+	    switch (errno) {
+	      case EPIPE:
+		gprint (GP_ERR, "server connection has died\n");
+		exit (33);
+	      default:
+		gprint (GP_ERR, "server is busy...33\n");
+		bufferPending = TRUE;
+		goto escape;
+	    }
+	  } else {
+	    sscanf (message.buffer, "STATUS %d", &status);
+	  }
+
+	  // receive the resulting stderr
+	  if (ExpectMessage (server, MSG_TIMEOUT, &message)) {
+	    switch (errno) {
+	      case EPIPE:
+		gprint (GP_ERR, "server connection has died\n");
+		exit (34);
+	      default:
+		gprint (GP_ERR, "server is busy...34\n");
+		bufferPending = TRUE;
+		goto escape;
+	    }
+	  } else {
+	    gwrite (message.buffer, 1, message.Nbuffer, GP_ERR);
+	  }
+
+	  // receive the resulting stdout
+	  if (ExpectMessage (server, MSG_TIMEOUT, &message)) {
+	    switch (errno) {
+	      case EPIPE:
+		gprint (GP_ERR, "server connection has died\n");
+		exit (35);
+	      default:
+		gprint (GP_ERR, "server is busy...35\n");
+		bufferPending = TRUE;
+		goto escape;
+	    }
+	  } else {
+	    gwrite (message.buffer, 1, message.Nbuffer, GP_LOG);
+	  }
+	} else {
+	  // if no server is defined, we treat an unknown command as an error
+	  status = FALSE;
+	}	
+      }
+    escape:
+      if (outline != NULL) free (outline);
+      if (!status && auto_break) done = TRUE;
+    }
+    p = q + 1;
+  }
+  return (status);
+}
+
+/* should skip ; surrounded by "" */
+
+/* commands which are not found are sent to the server socket if 
+   defined. commands executed by the server return their stderr
+   and stdout streams.  server commands should probably not block
+   the server for very long or multiple clients will interfere
+   with each other.  
+*/
Index: /branches/sc_branches/pantasks_condor/lib.shell/opihi.c
===================================================================
--- /branches/sc_branches/pantasks_condor/lib.shell/opihi.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/lib.shell/opihi.c	(revision 34783)
@@ -0,0 +1,63 @@
+# include "opihi.h"
+
+/******************/
+int opihi (int argc, char **argv) {
+
+  int Nbad;
+  char *line, *prompt, *history;
+  pid_t ppid;
+
+  general_init (&argc, argv);
+  program_init (&argc, argv);
+  startup (&argc, argv);
+  prompt = get_variable("PROMPT");
+  history = get_variable("HISTORY");
+  welcome ();
+
+  Nbad = 0;
+  while (1) {  /** must exit with command "exit" or "quit" */
+    if (Nbad == 10) exit (20);
+
+    line = opihi_readline (prompt);
+
+    if (line == NULL) { 
+      
+      ppid = getppid();
+      if (ppid == 1) {
+	signal (SIGPIPE, SIG_IGN);
+	gprint (GP_ERR, "caught parent shutdown\n");
+	exit (21);
+      }
+      if (!isatty (STDIN_FILENO)) exit (21);
+      gprint (GP_LOG, "Use \"quit\" to exit\n");
+      Nbad ++;
+      continue;
+    }
+
+    Nbad = 0;
+
+    stripwhite (line);
+    if (*line) {
+      multicommand (line);
+      add_history (line);
+
+// the libedit version of readline does not support an incremental write to history file
+# ifdef RL_READLINE_VERSION
+      if (history != NULL) append_history (1, history);
+# endif
+
+    }
+    free (line);
+    line = (char *) NULL;
+  }
+}
+
+/* 
+   startup sequence:
+
+   - general_init
+   - program_init
+   - startup (exit if non-interactive)
+   - welcome
+
+*/
Index: /branches/sc_branches/pantasks_condor/lib.shell/parse.c
===================================================================
--- /branches/sc_branches/pantasks_condor/lib.shell/parse.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/lib.shell/parse.c	(revision 34783)
@@ -0,0 +1,372 @@
+# include "opihi.h"
+
+char *parse (int *status, char *line) {
+
+  double fval;
+  float *fptr;
+  char *newline, *N, *L, *val, *B, *V, *V0, *V1, *c1, *c2, *p;
+  int Nx, Ny, Nbytes, filestatus, size, NLINE, isBuffer, inRange;
+  FILE *f;
+  Vector *vec;
+  Buffer *buf;
+
+  *status = TRUE; 
+
+  Ny = 0;
+  val = V0 = NULL;
+
+  if (line == (char *) NULL) goto error;
+
+  /* case 1: $var = expression */
+  if (line[0] == '$') {  
+    /* find the target variable name and the rest of the line */
+    interpolate_slash (line);
+    V0 = thisvar (line);
+    if (V0 == NULL) goto error;
+    V1 = aftervar (line);
+    if (V1 == NULL) goto error;
+ 
+    /* increment operator */
+    if (!strcmp (V1, "++")) {
+      val = get_variable (V0);
+      if (val == NULL) {
+	fval = 1.0;
+      } else {
+	fval = atof (val) + 1.0;
+      }
+      set_variable (V0, fval);
+      goto escape;
+    }
+
+    /* decrement operator */
+    if (!strcmp (V1, "--")) {
+      val = get_variable (V0);
+      if (val == NULL) {
+	fval = -1.0;
+      } else {
+	fval = atof (val) - 1.0;
+      }
+      set_variable (V0, fval);
+      goto escape;
+    }
+
+    if (!strncmp (V1, "+=", 2)) {
+	V1 ++;
+	if (*V1 == 0) goto error;
+	V1 ++;
+	if (*V1 == 0) goto error;
+
+	val = get_variable (V0);
+	if (val == NULL) {
+	    fval = 0.0;
+	} else {
+	    fval = atof (val);
+	}
+
+	/* dvomath returns a new string, or NULL, with the result of the expression */
+	val = dvomath (1, &V1, &size, 0);
+	if (val == NULL) goto error;
+	fval += atof(val);
+	// save the result
+	set_variable (V0, fval);
+	goto escape;
+    }
+
+    if (!strncmp (V1, "-=", 2)) {
+	V1 ++;
+	if (*V1 == 0) goto error;
+	V1 ++;
+	if (*V1 == 0) goto error;
+
+	val = get_variable (V0);
+	if (val == NULL) {
+	    fval = 0.0;
+	} else {
+	    fval = atof (val);
+	}
+
+	/* dvomath returns a new string, or NULL, with the result of the expression */
+	val = dvomath (1, &V1, &size, 0);
+	if (val == NULL) goto error;
+	fval -= atof(val);
+	// save the result
+	set_variable (V0, fval);
+	goto escape;
+    }
+
+    /* not an assignement, syntax error */
+    if (*V1 != '=') goto error;
+
+    /* find first non-WHITESPACE character after = */
+    V1 ++;
+    while (isspace (*V1)) V1++;
+    if (*V1 == 0) {
+      // assign empty vector
+      set_str_variable (V0, "");
+      goto escape;
+    }
+
+    /* command replacement.  execute the line, place answer in val. */
+    if (*V1 == '`') {
+
+      /* look for end of line, must be ` */
+      B = V1 + strlen (V1) - 1;
+      if (*B != '`') goto error;
+
+      /* val will hold the result */
+      // XXX this is limiting!!!
+      ALLOCATE (val, char, 1024);
+
+      /* B is the command to be executed */
+      B = strncreate (V1 + 1, strlen(V1) - 2);
+      f = popen (B, "r");
+      Nbytes = fread (val, 1, 1023, f);
+      val[Nbytes] = 0;
+      filestatus = pclose (f);
+      free (B);
+
+      if (filestatus) gprint (GP_ERR, "warning: exit status of command %d\n", filestatus);
+
+      /* convert all but last return to ' '.  drop last return */
+      for (B = val; *B != 0; B++) {
+	if (*B == '\n') {
+	  if (B[1]) {
+	    *B = ' ';
+	  } else {
+	    *B = 0;
+	  }
+	}
+      }
+      // save the resulting value
+      set_str_variable (V0, val);
+      goto escape;  /* frees temp variables */
+    }
+
+    /* simple variable assignment */
+    /* dvomath returns a new string, or NULL, with the result of the expression */
+    val = dvomath (1, &V1, &size, 0);
+    if (val == NULL) { 
+      while (OHANA_WHITESPACE (*V1)) V1++;
+      val = strcreate (V1);
+    } 
+    // save the result
+    set_str_variable (V0, val);
+    goto escape;  /* frees temp variables */
+  }
+
+  /* case 2: vect[N] = value or buff[N][M] = value */
+  V0 = thiscomm (line);
+  if (strchr(V0, '[') != (char *) NULL) { 
+    /* get vector name (left in V0) */
+    N = strchr (V0, '[');
+    if (N == V0) goto error;
+
+    /* get bracket contents (left in V) */
+    L = strchr (V0, ']');
+    if (L == (char *) NULL) goto error;
+
+    *N = 0;
+    V = strncreate (N+1, L - N - 1);
+
+    /* expand value in brackets */
+    val = dvomath (1, &V, &size, 0);
+    if (val == NULL) {
+      print_error ();
+      goto error;
+    }
+    Nx = atoi (val);
+    free (val); val = NULL;
+    free (V);
+
+    isBuffer = FALSE;
+    N = L + 1;
+    if (*N == '[') {
+      isBuffer = TRUE;
+      L = strchr (N, ']');
+
+      V = strncreate (N+1, L - N - 1);
+
+      /* expand value in brackets */
+      val = dvomath (1, &V, &size, 0);
+      if (val == NULL) {
+	print_error ();
+	goto error;
+      }
+      Ny = atoi (val);
+      free (val); val = NULL;
+      free (V);
+    }
+
+    /* find value for assignment */
+    V1 = nextcomm (line);
+    if (V1 == (char *) NULL) goto error;
+    if (V1[0] != '=') goto error;
+    V1 ++;
+
+    /*** assign vector element to value ***/
+    val = dvomath (1, &V1, &size, 0); 
+    if (val == (char *) NULL) {
+      print_error ();
+      goto error;
+    }
+
+    /* find vector */
+    if (isBuffer) {
+      if ((buf = SelectBuffer (V0, OLDBUFFER, TRUE)) == NULL) goto error;
+      free (V0); V0 = (char *) NULL;
+
+      inRange = TRUE;
+      inRange &= (Nx <  +1*buf[0].header.Naxis[0]);
+      inRange &= (Nx >= -1*buf[0].header.Naxis[0]);
+      inRange &= (Ny <  +1*buf[0].header.Naxis[1]);
+      inRange &= (Ny >= -1*buf[0].header.Naxis[1]);
+      if (!inRange) {
+	gprint (GP_ERR, "no element %d,%d\n", Nx, Ny);
+	goto error;
+      }
+      if (Nx < 0) Nx += buf[0].header.Naxis[0];
+      if (Ny < 0) Ny += buf[0].header.Naxis[1];
+
+      fptr = (float *) buf[0].matrix.buffer;
+      fptr[Nx + Ny*buf[0].header.Naxis[0]] = atof (val);
+    } else {
+      if ((vec = SelectVector (V0, OLDVECTOR, TRUE)) == NULL) goto error;
+      free (V0); V0 = (char *) NULL;
+
+      inRange = TRUE;
+      inRange &= (Nx <  +1*vec[0].Nelements);
+      inRange &= (Nx >= -1*vec[0].Nelements);
+      if (!inRange) {
+	gprint (GP_ERR, "no element %d\n", Nx);
+	goto error;
+      }
+      if (Nx < 0) Nx += vec[0].Nelements;
+      if (vec[0].type == OPIHI_FLT) {
+	vec[0].elements.Flt[Nx] = atof (val);
+      } else {
+	vec[0].elements.Int[Nx] = atol (val);
+      }
+    }
+    goto escape;
+  }
+  free (V0); V0 = (char *) NULL;
+
+  /* case 3: {expression} */
+  NLINE = MAX (128, strlen (line));
+  ALLOCATE (newline, char, NLINE);
+  memset (newline, 0, NLINE);
+  for (L = line; *L != 0; ) { 
+
+    // copy elements from L (line) to newline up to first '{'
+    p = strchr (L, '{');
+    newline = opihi_append (newline, &NLINE, L, p);
+    if (p == NULL) break;
+    L = p + 1;
+
+    // check for \{ at this point, replace \ in newline with {
+    if ((p > line) && (*(p - 1) == 0x5c)) {
+      N = newline + strlen(newline) - 1;
+      *N = '{'; // replace \ with {
+      continue;
+    }
+
+    /* check on the syntax of the line */
+    L = p + 1;
+    c1 = strchr (L, '}');
+    if (c1 == NULL) {
+      gprint (GP_ERR, "no close brackets!\n");
+      goto error;
+    }
+    c2 = strchr (L, '{');
+    if ((c2 != NULL) && (c2 < c1)) {
+      gprint (GP_ERR, "can't nest brackets!\n");
+      goto error;
+    }
+    *c1 = 0;
+
+    /* value in brackets must be a math expression of some sort.
+       isolated words are not valid here */
+    val = dvomath (1, &L, &size, -1);
+    if (val == NULL) {
+      print_error ();
+      goto error;
+    } 
+
+    /* interpolate vector element into newline (being accumulated) */
+    newline = opihi_append (newline, &NLINE, val, NULL);
+
+    /* copy val to outline */
+    L = c1 + 1;
+    free (val); val = (char *) NULL;
+  }
+
+  free (line); line = (char *) NULL;
+
+  /* \ protects next character */
+  interpolate_slash (newline);
+  
+  REALLOCATE (newline, char, strlen (newline) + 1);
+  *status = TRUE;
+  return (newline); 
+
+error:
+  gprint (GP_ERR, "syntax error\n");
+  // assignment or increment operation: free line and return NULL
+  if (line != (char *) NULL) free (line);
+  if (val != (char *) NULL) free (val);
+  if (V0 != (char *) NULL) free (V0);
+  *status = FALSE;
+  return NULL;
+
+escape:
+  // assignment or increment operation: free line and return NULL
+  if (line != (char *) NULL) free (line);
+  if (val != (char *) NULL) free (val);
+  if (V0 != (char *) NULL) free (V0);
+  *status = TRUE;
+  return (NULL);
+}
+
+/* this routine looks for math and/or logic expressions that 
+   need to be evaluated and passes them on to "parenthesis",
+   which evaluates the expression */
+
+/* There are two situations now which define an expression to 
+   be evaluated:
+   1) $var = expression   -- everything after the = must be a math expression or a string
+   2) {expression}        -- everything within the {} must be a math expression
+*/
+
+/* case 1: $var = expression.  test that
+   a) there is a $ as the first character
+   b) the variable defined by that $ is not null
+   c) there is an = sign following the variable
+   d) there is something following the = sign
+*/
+  
+/* case 2: vect[N] = expression. check syntax:
+   a) last char must be ]
+   b) word must contain [
+   c) between [ & ] must be an integer
+   d) vector must exist
+   e) there is an = sign following the first word
+   f) there is something following the = sign
+*/
+     
+
+/* case 3: we hunt for '{', and then the first '}' and pass everything
+   inbetween.  no nested {{}}s are allowed! */
+  
+/* thisword ALLOCATES
+   thisvar  ALLOCATES
+   thiscomm ALLOCATES
+   
+   nextword !ALLOCATE
+   nextcomm !ALLOCATE
+   
+   lastword !ALLOCATE
+   lastvar !ALLOCATE
+   
+   aftervar !ALLOCATE
+*/   
+
Index: /branches/sc_branches/pantasks_condor/lib.shell/parse.cleanup.c
===================================================================
--- /branches/sc_branches/pantasks_condor/lib.shell/parse.cleanup.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/lib.shell/parse.cleanup.c	(revision 34783)
@@ -0,0 +1,428 @@
+# include "opihi.h"
+
+enum {OP_SET_SCALAR, OP_INCR_SCALAR, OP_DECR_SCALAR, OP_ADD_SCALAR, OP_SUB_SCALAR, OP_SET_VECTOR, OP_INCR_VECTOR, OP_DECR_VECTOR, OP_ADD_VECTOR, OP_SUB_VECTOR};
+
+char *parse (int *status, char *line) {
+
+  double fval;
+  float *fptr;
+  char *newline, *N, *L, *val, *B, *V, *V0, *V1, *c1, *c2, *p, *px, *py;
+  int Nx, Ny, Nbytes, filestatus, size, NLINE, isBuffer, inRange;
+  FILE *f;
+  Vector *vec;
+  Buffer *buf;
+
+  *status = TRUE; 
+
+  Ny = 0;
+  val = V0 = NULL;
+
+  if (line == (char *) NULL) goto error;
+
+  // what are we trying to do here?
+  /* case 1: $var = expression */
+  if (line[0] == '$') {  
+    /* find the target variable name and the rest of the line */
+    interpolate_slash (line);
+    V0 = thisvar (line);
+    if (V0 == NULL) goto error;
+    V1 = aftervar (line);
+    if (V1 == NULL) goto error;
+ 
+    if (!strcmp  (V1, "++"))    { mode = OP_INCR_SCALAR; }
+    if (!strcmp  (V1, "--"))    { mode = OP_DECR_SCALAR; }
+    if (!strncmp (V1, "+=", 2)) { mode = OP_ADD_SCALAR;  }
+    if (!strncmp (V1, "-=", 2)) { mode = OP_SUB_SCALAR;  }
+    if (!strcmp  (V1, "="))     { mode = OP_SET_SCALAR;  }
+  }
+
+  /* case 2: vect[N] = value or buff[N][M] = value */
+  V0 = thiscomm (line);
+  px = py = NULL;
+  if (strchr(V0, '[') != (char *) NULL) { 
+    isBuffer = FALSE;
+
+    N = strchr (V0, '[');
+    if (N == V0) goto error;
+
+    // find end of 1st bracket
+    L = strchr (V0, ']');
+    if (!L) goto error;
+    px = N + 1;
+
+    // is there a second bracket?
+    N = L + 1;
+    if (*N == '[') {
+      isBuffer = TRUE;
+
+      // find end of 2nd bracket
+      L = strchr (N, ']');
+      if (!L) goto error;
+      py = N + 1;
+    }
+
+    V1 = nextcomm (line);
+
+    if (!strcmp  (V1, "++"))    { mode = OP_INCR_VECTOR; }
+    if (!strcmp  (V1, "--"))    { mode = OP_DECR_VECTOR; }
+    if (!strncmp (V1, "+=", 2)) { mode = OP_ADD_VECTOR;  }
+    if (!strncmp (V1, "-=", 2)) { mode = OP_SUB_VECTOR;  }
+    if (!strcmp  (V1, "="))     { mode = OP_SET_VECTOR;  }
+  }
+  free (V0); V0 = (char *) NULL;
+
+  
+
+  /* case 1: $var = expression */
+  if (line[0] == '$') {  
+    /* find the target variable name and the rest of the line */
+    interpolate_slash (line);
+    V0 = thisvar (line);
+    if (V0 == NULL) goto error;
+    V1 = aftervar (line);
+    if (V1 == NULL) goto error;
+ 
+    /* increment operator */
+    if (!strcmp (V1, "++")) {
+      val = get_variable (V0);
+      if (val == NULL) {
+	fval = 1.0;
+      } else {
+	fval = atof (val) + 1.0;
+      }
+      set_variable (V0, fval);
+      goto escape;
+    }
+
+    /* decrement operator */
+    if (!strcmp (V1, "--")) {
+      val = get_variable (V0);
+      if (val == NULL) {
+	fval = -1.0;
+      } else {
+	fval = atof (val) - 1.0;
+      }
+      set_variable (V0, fval);
+      goto escape;
+    }
+
+    if (!strncmp (V1, "+=", 2)) {
+	V1 ++;
+	if (*V1 == 0) goto error;
+	V1 ++;
+	if (*V1 == 0) goto error;
+
+	val = get_variable (V0);
+	if (val == NULL) {
+	    fval = 0.0;
+	} else {
+	    fval = atof (val);
+	}
+
+	/* dvomath returns a new string, or NULL, with the result of the expression */
+	val = dvomath (1, &V1, &size, 0);
+	if (val == NULL) goto error;
+	fval += atof(val);
+	// save the result
+	set_variable (V0, fval);
+	goto escape;
+    }
+
+    if (!strncmp (V1, "-=", 2)) {
+	V1 ++;
+	if (*V1 == 0) goto error;
+	V1 ++;
+	if (*V1 == 0) goto error;
+
+	val = get_variable (V0);
+	if (val == NULL) {
+	    fval = 0.0;
+	} else {
+	    fval = atof (val);
+	}
+
+	/* dvomath returns a new string, or NULL, with the result of the expression */
+	val = dvomath (1, &V1, &size, 0);
+	if (val == NULL) goto error;
+	fval -= atof(val);
+	// save the result
+	set_variable (V0, fval);
+	goto escape;
+    }
+
+    /* not an assignement, syntax error */
+    if (*V1 != '=') goto error;
+
+    /* find first non-WHITESPACE character after = */
+    V1 ++;
+    while (isspace (*V1)) V1++;
+    if (*V1 == 0) {
+      // assign empty vector
+      set_str_variable (V0, "");
+      goto escape;
+    }
+
+    /* command replacement.  execute the line, place answer in val. */
+    if (*V1 == '`') {
+
+      /* look for end of line, must be ` */
+      B = V1 + strlen (V1) - 1;
+      if (*B != '`') goto error;
+
+      /* val will hold the result */
+      // XXX this is limiting!!!
+      ALLOCATE (val, char, 1024);
+
+      /* B is the command to be executed */
+      B = strncreate (V1 + 1, strlen(V1) - 2);
+      f = popen (B, "r");
+      Nbytes = fread (val, 1, 1023, f);
+      val[Nbytes] = 0;
+      filestatus = pclose (f);
+      free (B);
+
+      if (filestatus) gprint (GP_ERR, "warning: exit status of command %d\n", filestatus);
+
+      /* convert all but last return to ' '.  drop last return */
+      for (B = val; *B != 0; B++) {
+	if (*B == '\n') {
+	  if (B[1]) {
+	    *B = ' ';
+	  } else {
+	    *B = 0;
+	  }
+	}
+      }
+      // save the resulting value
+      set_str_variable (V0, val);
+      goto escape;  /* frees temp variables */
+    }
+
+    /* simple variable assignment */
+    /* dvomath returns a new string, or NULL, with the result of the expression */
+    val = dvomath (1, &V1, &size, 0);
+    if (val == NULL) { 
+      while (OHANA_WHITESPACE (*V1)) V1++;
+      val = strcreate (V1);
+    } 
+    // save the result
+    set_str_variable (V0, val);
+    goto escape;  /* frees temp variables */
+  }
+
+  /* case 2: vect[N] = value or buff[N][M] = value */
+  V0 = thiscomm (line);
+  if (strchr(V0, '[') != (char *) NULL) { 
+    /* get vector name (left in V0) */
+    N = strchr (V0, '[');
+    if (N == V0) goto error;
+
+    /* get bracket contents (left in V) */
+    L = strchr (V0, ']');
+    if (L == (char *) NULL) goto error;
+
+    *N = 0;
+    V = strncreate (N+1, L - N - 1);
+
+    /* expand value in brackets */
+    val = dvomath (1, &V, &size, 0);
+    if (val == NULL) {
+      print_error ();
+      goto error;
+    }
+    Nx = atoi (val);
+    free (val); val = NULL;
+    free (V);
+
+    isBuffer = FALSE;
+    N = L + 1;
+    if (*N == '[') {
+      isBuffer = TRUE;
+      L = strchr (N, ']');
+
+      V = strncreate (N+1, L - N - 1);
+
+      /* expand value in brackets */
+      val = dvomath (1, &V, &size, 0);
+      if (val == NULL) {
+	print_error ();
+	goto error;
+      }
+      Ny = atoi (val);
+      free (val); val = NULL;
+      free (V);
+    }
+
+    /* find value for assignment */
+    V1 = nextcomm (line);
+    if (V1 == (char *) NULL) goto error;
+    if (V1[0] != '=') goto error;
+    V1 ++;
+
+    /*** assign vector element to value ***/
+    val = dvomath (1, &V1, &size, 0); 
+    if (val == (char *) NULL) {
+      print_error ();
+      goto error;
+    }
+
+    /* find vector */
+    if (isBuffer) {
+      if ((buf = SelectBuffer (V0, OLDBUFFER, TRUE)) == NULL) goto error;
+      free (V0); V0 = (char *) NULL;
+
+      inRange = TRUE;
+      inRange &= (Nx <  +1*buf[0].header.Naxis[0]);
+      inRange &= (Nx >= -1*buf[0].header.Naxis[0]);
+      inRange &= (Ny <  +1*buf[0].header.Naxis[1]);
+      inRange &= (Ny >= -1*buf[0].header.Naxis[1]);
+      if (!inRange) {
+	gprint (GP_ERR, "no element %d,%d\n", Nx, Ny);
+	goto error;
+      }
+      if (Nx < 0) Nx += buf[0].header.Naxis[0];
+      if (Ny < 0) Ny += buf[0].header.Naxis[1];
+
+      fptr = (float *) buf[0].matrix.buffer;
+      fptr[Nx + Ny*buf[0].header.Naxis[0]] = atof (val);
+    } else {
+      if ((vec = SelectVector (V0, OLDVECTOR, TRUE)) == NULL) goto error;
+      free (V0); V0 = (char *) NULL;
+
+      inRange = TRUE;
+      inRange &= (Nx <  +1*vec[0].Nelements);
+      inRange &= (Nx >= -1*vec[0].Nelements);
+      if (!inRange) {
+	gprint (GP_ERR, "no element %d\n", Nx);
+	goto error;
+      }
+      if (Nx < 0) Nx += vec[0].Nelements;
+      if (vec[0].type == OPIHI_FLT) {
+	vec[0].elements.Flt[Nx] = atof (val);
+      } else {
+	vec[0].elements.Int[Nx] = atol (val);
+      }
+    }
+    goto escape;
+  }
+  free (V0); V0 = (char *) NULL;
+
+  /* case 3: {expression} */
+  NLINE = MAX (128, strlen (line));
+  ALLOCATE (newline, char, NLINE);
+  memset (newline, 0, NLINE);
+  for (L = line; *L != 0; ) { 
+
+    // copy elements from L (line) to newline up to first '{'
+    p = strchr (L, '{');
+    newline = opihi_append (newline, &NLINE, L, p);
+    if (p == NULL) break;
+    L = p + 1;
+
+    // check for \{ at this point, replace \ in newline with {
+    if ((p > line) && (*(p - 1) == 0x5c)) {
+      N = newline + strlen(newline) - 1;
+      *N = '{'; // replace \ with {
+      continue;
+    }
+
+    /* check on the syntax of the line */
+    L = p + 1;
+    c1 = strchr (L, '}');
+    if (c1 == NULL) {
+      gprint (GP_ERR, "no close brackets!\n");
+      goto error;
+    }
+    c2 = strchr (L, '{');
+    if ((c2 != NULL) && (c2 < c1)) {
+      gprint (GP_ERR, "can't nest brackets!\n");
+      goto error;
+    }
+    *c1 = 0;
+
+    /* value in brackets must be a math expression of some sort.
+       isolated words are not valid here */
+    val = dvomath (1, &L, &size, -1);
+    if (val == NULL) {
+      print_error ();
+      goto error;
+    } 
+
+    /* interpolate vector element into newline (being accumulated) */
+    newline = opihi_append (newline, &NLINE, val, NULL);
+
+    /* copy val to outline */
+    L = c1 + 1;
+    free (val); val = (char *) NULL;
+  }
+
+  free (line); line = (char *) NULL;
+
+  /* \ protects next character */
+  interpolate_slash (newline);
+  
+  REALLOCATE (newline, char, strlen (newline) + 1);
+  *status = TRUE;
+  return (newline); 
+
+error:
+  gprint (GP_ERR, "syntax error\n");
+  // assignment or increment operation: free line and return NULL
+  if (line != (char *) NULL) free (line);
+  if (val != (char *) NULL) free (val);
+  if (V0 != (char *) NULL) free (V0);
+  *status = FALSE;
+  return NULL;
+
+escape:
+  // assignment or increment operation: free line and return NULL
+  if (line != (char *) NULL) free (line);
+  if (val != (char *) NULL) free (val);
+  if (V0 != (char *) NULL) free (V0);
+  *status = TRUE;
+  return (NULL);
+}
+
+/* this routine looks for math and/or logic expressions that 
+   need to be evaluated and passes them on to "parenthesis",
+   which evaluates the expression */
+
+/* There are two situations now which define an expression to 
+   be evaluated:
+   1) $var = expression   -- everything after the = must be a math expression or a string
+   2) {expression}        -- everything within the {} must be a math expression
+*/
+
+/* case 1: $var = expression.  test that
+   a) there is a $ as the first character
+   b) the variable defined by that $ is not null
+   c) there is an = sign following the variable
+   d) there is something following the = sign
+*/
+  
+/* case 2: vect[N] = expression. check syntax:
+   a) last char must be ]
+   b) word must contain [
+   c) between [ & ] must be an integer
+   d) vector must exist
+   e) there is an = sign following the first word
+   f) there is something following the = sign
+*/
+     
+
+/* case 3: we hunt for '{', and then the first '}' and pass everything
+   inbetween.  no nested {{}}s are allowed! */
+  
+/* thisword ALLOCATES
+   thisvar  ALLOCATES
+   thiscomm ALLOCATES
+   
+   nextword !ALLOCATE
+   nextcomm !ALLOCATE
+   
+   lastword !ALLOCATE
+   lastvar !ALLOCATE
+   
+   aftervar !ALLOCATE
+*/   
+
Index: /branches/sc_branches/pantasks_condor/lib.shell/parse_commands.c
===================================================================
--- /branches/sc_branches/pantasks_condor/lib.shell/parse_commands.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/lib.shell/parse_commands.c	(revision 34783)
@@ -0,0 +1,56 @@
+# include "opihi.h"
+# define D_NARG 10
+
+char **parse_commands (char *line, int *argc) {
+
+  int i, NARG;
+  char *c;
+  char **argv;
+
+  NARG = D_NARG;
+  ALLOCATE (argv, char *, NARG);
+
+  argv[0] = thisword (line);
+  if (argv[0] == (char *) NULL) {
+    free (argv);
+    *argc = 0;
+    return ((char **) NULL);
+  }
+
+  c = nextword (line);
+  for (i = 1; c != (char *) NULL; i++) {
+    argv[i] = thisword (c);
+
+    /* if one of the words does not parse (eg, ""), skip it */
+    if (argv[i] == NULL) i--;
+
+    c = nextword (c);
+    if (i == NARG - 1) {
+      NARG += D_NARG;
+      REALLOCATE (argv, char *, NARG);
+    }
+  }
+  REALLOCATE (argv, char *, i);
+  *argc = i;
+
+  return (argv);
+
+}
+
+  /* parse out the command line into (char **argv).  line looks like:
+     command word word word ... */
+
+  /* argv[0] and argv[1..argc-1] are handled differently because
+     the syntax of a valid command and a valid word are slighly 
+     different (not that this is obviously wanted...) */
+
+
+
+/* old code:
+      for (j = 0; j < i; j++) 
+	free (argv[i]);
+      free (argv);
+      *argc = 0;
+      return (argv);
+    }
+*/
Index: /branches/sc_branches/pantasks_condor/lib.shell/stack_math.c
===================================================================
--- /branches/sc_branches/pantasks_condor/lib.shell/stack_math.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/lib.shell/stack_math.c	(revision 34783)
@@ -0,0 +1,1147 @@
+# include "opihi.h"
+
+/* the result of a matrix operation must go into a temp variable,
+   labeled by "m". if one of the input matrices is already a temp variable, we 
+   can continue using it this round 
+*/
+
+// XXX we temporarily drop the concept of using one of the temporary input vectors for 
+// the output vector (thus saving an ALLOC): we have to juggle the size of the input vectors 
+// as well as their temporary state
+
+int VV_binary (StackVar *OUT, StackVar *V1, StackVar *V2, char *op) {
+
+  int i, Nx;
+  char line[512]; // this is only used to report an error 
+  
+  // the vectors have to match in length
+  if (V1[0].vector[0].Nelements != V2[0].vector[0].Nelements) {
+    return (FALSE);
+  }
+
+  Nx = V1[0].vector[0].Nelements;
+
+  // create the output vector guaranteed to be temporary until the very end
+  OUT[0].vector = InitVector ();
+  OUT[0].type = 'v';
+
+  // set up the possible operations : int OP int -> int, all else yield float
+  // OP is the operation performed on *M1 and *M2
+# define VV_FUNC(FTYPE,OP) {						\
+    if ((V1->vector->type == OPIHI_FLT) && (V2->vector->type == OPIHI_FLT)) { \
+      CopyVector (OUT[0].vector, V1[0].vector);				\
+      opihi_flt *M1  =  V1[0].vector[0].elements.Flt;			\
+      opihi_flt *M2  =  V2[0].vector[0].elements.Flt;			\
+      opihi_flt *out = OUT[0].vector[0].elements.Flt;			\
+      for (i = 0; i < Nx; i++, out++, M1++, M2++) {			\
+  	*out = OP;							\
+      }									\
+      break;								\
+    }									\
+    if ((V1->vector->type == OPIHI_FLT) && (V2->vector->type != OPIHI_FLT)) { \
+      CopyVector (OUT[0].vector, V1[0].vector);				\
+      opihi_flt *M1  =  V1[0].vector[0].elements.Flt;			\
+      opihi_int *M2  =  V2[0].vector[0].elements.Int;			\
+      opihi_flt *out = OUT[0].vector[0].elements.Flt;			\
+      for (i = 0; i < Nx; i++, out++, M1++, M2++) {			\
+  	*out = OP;							\
+      }									\
+      break;								\
+    }									\
+    if ((V1->vector->type != OPIHI_FLT) && (V2->vector->type == OPIHI_FLT)) { \
+      CopyVector (OUT[0].vector, V2[0].vector);				\
+      opihi_int *M1  =  V1[0].vector[0].elements.Int;			\
+      opihi_flt *M2  =  V2[0].vector[0].elements.Flt;			\
+      opihi_flt *out = OUT[0].vector[0].elements.Flt;			\
+      for (i = 0; i < Nx; i++, out++, M1++, M2++) {			\
+  	*out = OP;							\
+      }									\
+      break;								\
+    }									\
+    if ((FTYPE == 'S') && (V1->vector->type != OPIHI_FLT) && (V2->vector->type != OPIHI_FLT)) { \
+      MatchVector (OUT[0].vector, V1[0].vector, OPIHI_FLT);		\
+      opihi_int *M1  =  V1[0].vector[0].elements.Int;			\
+      opihi_int *M2  =  V2[0].vector[0].elements.Int;			\
+      opihi_flt *out = OUT[0].vector[0].elements.Flt;			\
+      for (i = 0; i < Nx; i++, out++, M1++, M2++) {			\
+	*out = OP;							\
+      }									\
+      break;								\
+    }									\
+    if ((V1->vector->type != OPIHI_FLT) && (V2->vector->type != OPIHI_FLT)) { \
+      CopyVector (OUT[0].vector, V1[0].vector);				\
+      opihi_int *M1  =  V1[0].vector[0].elements.Int;			\
+      opihi_int *M2  =  V2[0].vector[0].elements.Int;			\
+      opihi_int *out = OUT[0].vector[0].elements.Int;			\
+      for (i = 0; i < Nx; i++, out++, M1++, M2++) {			\
+	*out = OP;							\
+      }									\
+      break;								\
+    }									\
+  }
+
+  switch (op[0]) {
+    case '+': VV_FUNC('s', *M1 + *M2);
+    case '-': VV_FUNC('s', *M1 - *M2);
+    case '*': VV_FUNC('s', *M1 * *M2);
+    case '/': VV_FUNC('S', *M1 / (opihi_flt) *M2);
+    case '%': VV_FUNC('s', (long long)*M1 % (long long)*M2);
+    case '^': VV_FUNC('S', pow (*M1, *M2));
+    case '@': VV_FUNC('S', DEG_RAD*atan2 (*M1, *M2));
+    case 'D': VV_FUNC('s', MIN (*M1, *M2));
+    case 'U': VV_FUNC('s', MAX (*M1, *M2));
+    case '<': VV_FUNC('s', (*M1 < *M2) ? 1 : 0);
+    case '>': VV_FUNC('s', (*M1 > *M2) ? 1 : 0);
+    case '&': VV_FUNC('s', ((long long)*M1 & (long long)*M2));
+    case '|': VV_FUNC('s', ((long long)*M1 | (long long)*M2));
+    case 'E': VV_FUNC('s', (*M1 == *M2) ? 1 : 0);
+    case 'N': VV_FUNC('s', (*M1 != *M2) ? 1 : 0);
+    case 'L': VV_FUNC('s', (*M1 <= *M2) ? 1 : 0);
+    case 'G': VV_FUNC('s', (*M1 >= *M2) ? 1 : 0);
+    case 'A': VV_FUNC('s', (*M1 && *M2) ? 1 : 0);
+    case 'O': VV_FUNC('s', (*M1 || *M2) ? 1 : 0);
+    default:
+      sprintf (line, "error: op %c not defined!", op[0]);
+      push_error (line);
+      return (FALSE);
+  }
+# undef VV_FUNC
+
+  /** free up any temporary buffers: **/
+
+  if (V1[0].type == 'v') {
+    free (V1[0].vector[0].elements.Ptr);
+    free (V1[0].vector);
+  }
+  if (V2[0].type == 'v') {
+    free (V2[0].vector[0].elements.Ptr);
+    free (V2[0].vector);
+  }
+  /* at the end, V1 and V2 are deleted only if they were temporary */
+
+  clear_stack (V1);
+  clear_stack (V2);
+  return (TRUE);
+
+}
+
+int SV_binary (StackVar *OUT, StackVar *V1, StackVar *V2, char *op) {
+
+  int i, Nx;
+  char line[512]; // this is only used to report an error 
+  
+  Nx = V2[0].vector[0].Nelements;
+
+  OUT[0].vector = InitVector ();
+  OUT[0].type = 'v';   /*** <<--- says this is a temporary matrix ***/
+
+  // set up the possible operations : int OP int -> int, all else yield float
+  // OP is the operation performed on *M1 and *M2
+# define SV_FUNC(FTYPE,OP) {						\
+    if ((V1->type == 'S') && (V2->vector->type == OPIHI_FLT)) {		\
+      CopyVector (OUT[0].vector, V2[0].vector);				\
+      opihi_flt  M1  =  V1[0].FltValue;					\
+      opihi_flt *M2  =  V2[0].vector[0].elements.Flt;			\
+      opihi_flt *out = OUT[0].vector[0].elements.Flt;			\
+      for (i = 0; i < Nx; i++, out++, M2++) {				\
+	*out = OP;							\
+      }									\
+      break;								\
+    }									\
+    if ((V1->type == 'S') && (V2->vector->type != OPIHI_FLT)) {		\
+      MatchVector (OUT[0].vector, V2[0].vector, OPIHI_FLT);		\
+      opihi_flt  M1  =  V1[0].FltValue;					\
+      opihi_int *M2  =  V2[0].vector[0].elements.Int;			\
+      opihi_flt *out = OUT[0].vector[0].elements.Flt;			\
+      for (i = 0; i < Nx; i++, out++, M2++) {				\
+	*out = OP;							\
+      }									\
+      break;								\
+    }									\
+    if ((V1->type == 's') && (V2->vector->type == OPIHI_FLT)) {		\
+      CopyVector (OUT[0].vector, V2[0].vector);				\
+      opihi_int  M1  =  V1[0].IntValue;					\
+      opihi_flt *M2  =  V2[0].vector[0].elements.Flt;			\
+      opihi_flt *out = OUT[0].vector[0].elements.Flt;			\
+      for (i = 0; i < Nx; i++, out++, M2++) {				\
+	*out = OP;							\
+      }									\
+      break;								\
+    }									\
+    if ((FTYPE == 'S') && (V1->type == 's') && (V2->vector->type != OPIHI_FLT)) { \
+      MatchVector (OUT[0].vector, V2[0].vector, OPIHI_FLT);		\
+      opihi_int  M1  =  V1[0].IntValue;					\
+      opihi_int *M2  =  V2[0].vector[0].elements.Int;			\
+      opihi_flt *out = OUT[0].vector[0].elements.Flt;			\
+      for (i = 0; i < Nx; i++, out++, M2++) {				\
+	*out = OP;							\
+      }									\
+      break;								\
+    }									\
+    if ((V1->type == 's') && (V2->vector->type != OPIHI_FLT)) {		\
+      CopyVector (OUT[0].vector, V2[0].vector);				\
+      opihi_int  M1  =  V1[0].IntValue;					\
+      opihi_int *M2  =  V2[0].vector[0].elements.Int;			\
+      opihi_int *out = OUT[0].vector[0].elements.Int;			\
+      for (i = 0; i < Nx; i++, out++, M2++) {				\
+	*out = OP;							\
+      }									\
+      break;								\
+    }									\
+  }
+
+  switch (op[0]) { 
+    case '+': SV_FUNC('s', M1 + *M2);
+    case '-': SV_FUNC('s', M1 - *M2);
+    case '*': SV_FUNC('s', M1 * *M2);
+    case '/': SV_FUNC('S', M1 / (opihi_flt) *M2);
+    case '%': SV_FUNC('s', (long long) M1 % (long long) *M2);
+    case '^': SV_FUNC('S', pow (M1, *M2));
+    case '@': SV_FUNC('S', DEG_RAD*atan2 (M1, *M2));
+    case 'D': SV_FUNC('s', MIN (M1, *M2));
+    case 'U': SV_FUNC('s', MAX (M1, *M2));
+    case '<': SV_FUNC('s', (M1 < *M2) ? 1 : 0);
+    case '>': SV_FUNC('s', (M1 > *M2) ? 1 : 0);
+    case '&': SV_FUNC('s', ((long long)M1 & (long long)*M2));
+    case '|': SV_FUNC('s', ((long long)M1 | (long long)*M2));
+    case 'E': SV_FUNC('s', (M1 == *M2) ? 1 : 0);
+    case 'N': SV_FUNC('s', (M1 != *M2) ? 1 : 0);
+    case 'L': SV_FUNC('s', (M1 <= *M2) ? 1 : 0);
+    case 'G': SV_FUNC('s', (M1 >= *M2) ? 1 : 0);
+    case 'A': SV_FUNC('s', (M1 && *M2) ? 1 : 0);
+    case 'O': SV_FUNC('s', (M1 || *M2) ? 1 : 0);
+    default:
+      sprintf (line, "error: op %c not defined!", op[0]);
+      push_error (line);
+      return (FALSE);
+  }
+# undef SV_FUNC
+
+  /** free up any temporary buffers: **/
+  if (V2[0].type == 'v') {
+    free (V2[0].vector[0].elements.Ptr);
+    free (V2[0].vector);
+  }
+
+  clear_stack (V1);
+  clear_stack (V2);
+
+  /* at the end, V1 and V2 are deleted only if they were temporary */
+  return (TRUE);
+
+}
+
+int VS_binary (StackVar *OUT, StackVar *V1, StackVar *V2, char *op) {
+
+  int i, Nx;
+  char line[512]; // this is only used to report an error 
+  
+  Nx = V1[0].vector[0].Nelements;
+
+  OUT[0].vector = InitVector ();
+  OUT[0].type = 'v';   /*** <<--- says this is a temporary matrix ***/
+
+  // set up the possible operations : int OP int -> int, all else yield float
+  // OP is the operation performed on *M1 and *M2
+# define VS_FUNC(FTYPE,OP) {						\
+    if ((V2->type == 'S') && (V1->vector->type == OPIHI_FLT)) {		\
+      CopyVector (OUT[0].vector, V1[0].vector);				\
+      opihi_flt *M1  =  V1[0].vector[0].elements.Flt;			\
+      opihi_flt  M2  =  V2[0].FltValue;					\
+      opihi_flt *out = OUT[0].vector[0].elements.Flt;			\
+      for (i = 0; i < Nx; i++, out++, M1++) {				\
+	*out = OP;							\
+      }									\
+      break;								\
+    }									\
+    if ((V2->type == 'S') && (V1->vector->type != OPIHI_FLT)) {		\
+      MatchVector (OUT[0].vector, V1[0].vector, OPIHI_FLT);		\
+      opihi_int *M1  =  V1[0].vector[0].elements.Int;			\
+      opihi_flt  M2  =  V2[0].FltValue;					\
+      opihi_flt *out = OUT[0].vector[0].elements.Flt;			\
+      for (i = 0; i < Nx; i++, out++, M1++) {				\
+	*out = OP;							\
+      }									\
+      break;								\
+    }									\
+    if ((V2->type == 's') && (V1->vector->type == OPIHI_FLT)) {		\
+      CopyVector (OUT[0].vector, V1[0].vector);				\
+      opihi_flt *M1  =  V1[0].vector[0].elements.Flt;			\
+      opihi_int  M2  =  V2[0].IntValue;					\
+      opihi_flt *out = OUT[0].vector[0].elements.Flt;			\
+      for (i = 0; i < Nx; i++, out++, M1++) {				\
+	*out = OP;							\
+      }									\
+      break;								\
+    }									\
+    if ((FTYPE == 'S') && (V2->type == 's') && (V1->vector->type != OPIHI_FLT)) { \
+      CopyVector (OUT[0].vector, V1[0].vector);				\
+      opihi_int *M1  =  V1[0].vector[0].elements.Int;			\
+      opihi_int  M2  =  V2[0].IntValue;					\
+      opihi_int *out = OUT[0].vector[0].elements.Int;			\
+      for (i = 0; i < Nx; i++, out++, M1++) {				\
+	*out = OP;							\
+      }									\
+      break;								\
+    }									\
+    if ((V2->type == 's') && (V1->vector->type != OPIHI_FLT)) {		\
+      CopyVector (OUT[0].vector, V1[0].vector);				\
+      opihi_int *M1  =  V1[0].vector[0].elements.Int;			\
+      opihi_int  M2  =  V2[0].IntValue;					\
+      opihi_int *out = OUT[0].vector[0].elements.Int;			\
+      for (i = 0; i < Nx; i++, out++, M1++) {				\
+	*out = OP;							\
+      }									\
+      break;								\
+    }									\
+  }
+
+  switch (op[0]) { 
+    case '+': VS_FUNC('s', *M1 + M2);
+    case '-': VS_FUNC('s', *M1 - M2);
+    case '*': VS_FUNC('s', *M1 * M2);
+    case '/': VS_FUNC('S', *M1 / (opihi_flt) M2);
+    case '%': VS_FUNC('s', (long long) *M1 % (long long) M2);
+    case '^': VS_FUNC('S', pow (*M1, M2));
+    case '@': VS_FUNC('S', DEG_RAD*atan2 (*M1, M2));
+    case 'D': VS_FUNC('s', MIN (*M1, M2));
+    case 'U': VS_FUNC('s', MAX (*M1, M2));
+    case '<': VS_FUNC('s', (*M1 < M2) ? 1 : 0);
+    case '>': VS_FUNC('s', (*M1 > M2) ? 1 : 0);
+    case '&': VS_FUNC('s', ((long long)*M1 & (long long)M2));
+    case '|': VS_FUNC('s', ((long long)*M1 | (long long)M2));
+    case 'E': VS_FUNC('s', (*M1 == M2) ? 1 : 0);
+    case 'N': VS_FUNC('s', (*M1 != M2) ? 1 : 0);
+    case 'L': VS_FUNC('s', (*M1 <= M2) ? 1 : 0);
+    case 'G': VS_FUNC('s', (*M1 >= M2) ? 1 : 0);
+    case 'A': VS_FUNC('s', (*M1 && M2) ? 1 : 0);
+    case 'O': VS_FUNC('s', (*M1 || M2) ? 1 : 0);
+    default:
+      sprintf (line, "error: op %c not defined!", op[0]);
+      push_error (line);
+      return (FALSE);
+  }
+# undef VS_FUNC
+
+  /** free up any temporary buffers: **/
+
+  if (V1[0].type == 'v') {
+    free (V1[0].vector[0].elements.Ptr);
+    free (V1[0].vector);
+  }
+
+  clear_stack (V1);
+  clear_stack (V2);
+
+  /* at the end, V1 and V2 are deleted only if they were temporary */
+  return (TRUE);
+
+}
+
+// the vector is applied to each column
+int MV_binary (StackVar *OUT, StackVar *V1, StackVar *V2, char *op) {
+
+  int i, j, Nx, Ny;
+  char line[512]; // this is only used to report an error 
+ 
+  Nx = V1[0].buffer[0].matrix.Naxis[0];
+  Ny = V1[0].buffer[0].matrix.Naxis[1];
+  if (Ny != V2[0].vector[0].Nelements) {
+    push_error ("dimension mismatch");
+    return (FALSE);
+  }
+
+  /* if possible, use V1 as temp buffer, otherwise create new one */
+  if (V1[0].type == 'm') {  
+    OUT[0].buffer = V1[0].buffer;
+    V1[0].type = 'M'; /* prevent it from being freed below */
+  } else {  
+    /* do buffer.matrix.buffer and buffer.header.buffer get correctly zeroed? */
+    OUT[0].buffer = InitBuffer ();
+    CopyBuffer (OUT[0].buffer, V1[0].buffer);
+  }
+  OUT[0].type = 'm'; /*** <<--- says this is a temporary matrix ***/
+
+  float     *M1  = (float *) V1[0].buffer[0].matrix.buffer;
+  float     *out = (float *)OUT[0].buffer[0].matrix.buffer;
+
+# define MV_FUNC(OP) {					\
+    if (V2->vector->type == OPIHI_FLT) {		\
+      opihi_flt *M2  =  V2[0].vector[0].elements.Flt;	\
+      for (i = 0; i < Ny; i++, M2++) {			\
+	for (j = 0; j < Nx; j++, out++, M1++) {		\
+	  *out = OP;					\
+	}						\
+      }							\
+      break;						\
+    }							\
+    if (V2->vector->type != OPIHI_FLT) {		\
+      opihi_int *M2  =  V2[0].vector[0].elements.Int;	\
+      for (i = 0; i < Ny; i++, M2++) {			\
+	for (j = 0; j < Nx; j++, out++, M1++) {		\
+	  *out = OP;					\
+	}						\
+      }							\
+      break;						\
+    }							\
+  }
+
+  switch (op[0]) { 
+    case '+': MV_FUNC(*M1 + *M2);
+    case '-': MV_FUNC(*M1 - *M2);
+    case '*': MV_FUNC(*M1 * *M2);
+    case '/': MV_FUNC(*M1 / (opihi_flt) *M2);
+    case '%': MV_FUNC((long long) *M1 % (long long) *M2);
+    case '^': MV_FUNC(pow (*M1, *M2));
+    case '@': MV_FUNC(DEG_RAD*atan2 (*M1, *M2));
+    case 'D': MV_FUNC(MIN (*M1, *M2));
+    case 'U': MV_FUNC(MAX (*M1, *M2));
+    case '<': MV_FUNC((*M1 < *M2) ? 1 : 0);
+    case '>': MV_FUNC((*M1 > *M2) ? 1 : 0);
+    case '&': MV_FUNC(((long long)*M1 & (long long)*M2));
+    case '|': MV_FUNC(((long long)*M1 | (long long)*M2));
+    case 'E': MV_FUNC((*M1 == *M2) ? 1 : 0);
+    case 'N': MV_FUNC((*M1 != *M2) ? 1 : 0);
+    case 'L': MV_FUNC((*M1 <= *M2) ? 1 : 0);
+    case 'G': MV_FUNC((*M1 >= *M2) ? 1 : 0);
+    case 'A': MV_FUNC((*M1 && *M2) ? 1 : 0);
+    case 'O': MV_FUNC((*M1 || *M2) ? 1 : 0);
+    default:
+      sprintf (line, "error: op %c not defined!", op[0]);
+      push_error (line);
+      return (FALSE);
+  }
+# undef MV_FUNC
+
+  /** free up any temporary buffers: **/
+
+  if (V1[0].type == 'm') {
+    free (V1[0].buffer[0].header.buffer);
+    free (V1[0].buffer[0].matrix.buffer);
+    free (V1[0].buffer);
+  }
+  if (V2[0].type == 'v') {
+    free (V2[0].vector[0].elements.Ptr);
+    free (V2[0].vector);
+  }
+
+  clear_stack (V1);
+  clear_stack (V2);
+
+  /* at the end, V1 and V2 are deleted only if they were temporary */
+  return (TRUE);
+}
+
+// the vector is applied to each row
+int VM_binary (StackVar *OUT, StackVar *V1, StackVar *V2, char *op) {
+
+  int i, j, Nx, Ny;
+  char line[512]; // this is only used to report an error 
+  
+  Nx = V2[0].buffer[0].matrix.Naxis[0];
+  Ny = V2[0].buffer[0].matrix.Naxis[1];
+  if (Nx != V1[0].vector[0].Nelements) {
+    return (FALSE);
+  }
+
+  /* if possible, use V2 as temp buffer, otherwise create new one */
+  if (V2[0].type == 'm') {
+    OUT[0].buffer = V2[0].buffer;
+    V2[0].type = 'M'; /* prevent it from being freed below */
+  } else {  /* no spare temp buffer */
+    OUT[0].buffer = InitBuffer ();
+    CopyBuffer (OUT[0].buffer, V2[0].buffer);
+  }
+  OUT[0].type = 'm'; /*** <<--- says this is a temporary matrix ***/
+
+  float     *M2  = (float *) V2[0].buffer[0].matrix.buffer;
+  float     *out = (float *)OUT[0].buffer[0].matrix.buffer;
+
+# define VM_FUNC(OP) {					\
+    if (V1->vector->type == OPIHI_FLT) {		\
+      for (i = 0; i < Ny; i++) {			\
+	opihi_flt *M1  = V1[0].vector[0].elements.Flt;	\
+	for (j = 0; j < Nx; j++, out++, M1++, M2++) {	\
+	  *out = OP;					\
+	}						\
+      }							\
+      break;						\
+    }							\
+    if (V1->vector->type != OPIHI_FLT) {		\
+      for (i = 0; i < Ny; i++) {			\
+	opihi_int *M1  =  V1[0].vector[0].elements.Int;	\
+	for (j = 0; j < Nx; j++, out++, M1++, M2++) {	\
+	  *out = OP;					\
+	}						\
+      }							\
+      break;						\
+    }							\
+  }
+
+  switch (op[0]) { 
+    case '+': VM_FUNC(*M1 + *M2);
+    case '-': VM_FUNC(*M1 - *M2);
+    case '*': VM_FUNC(*M1 * *M2);
+    case '/': VM_FUNC(*M1 / (opihi_flt) *M2);
+    case '%': VM_FUNC((long long) *M1 % (long long) *M2);
+    case '^': VM_FUNC(pow (*M1, *M2));
+    case '@': VM_FUNC(DEG_RAD*atan2 (*M1, *M2));
+    case 'D': VM_FUNC(MIN (*M1, *M2));
+    case 'U': VM_FUNC(MAX (*M1, *M2));
+    case '<': VM_FUNC((*M1 < *M2) ? 1 : 0);
+    case '>': VM_FUNC((*M1 > *M2) ? 1 : 0);
+    case '&': VM_FUNC(((long long)*M1 & (long long)*M2));
+    case '|': VM_FUNC(((long long)*M1 | (long long)*M2));
+    case 'E': VM_FUNC((*M1 == *M2) ? 1 : 0);
+    case 'N': VM_FUNC((*M1 != *M2) ? 1 : 0);
+    case 'L': VM_FUNC((*M1 <= *M2) ? 1 : 0);
+    case 'G': VM_FUNC((*M1 >= *M2) ? 1 : 0);
+    case 'A': VM_FUNC((*M1 && *M2) ? 1 : 0);
+    case 'O': VM_FUNC((*M1 || *M2) ? 1 : 0);
+    default:
+      sprintf (line, "error: op %c not defined!", op[0]);
+      push_error (line);
+      return (FALSE);
+  }
+# undef VM_FUNC
+
+  /** free up any temporary buffers: **/
+
+  if (V1[0].type == 'v') {
+    free (V1[0].vector[0].elements.Ptr);
+    free (V1[0].vector);
+  }
+  if (V2[0].type == 'm') {
+    free (V2[0].buffer[0].header.buffer);
+    free (V2[0].buffer[0].matrix.buffer);
+    free (V2[0].buffer);
+  }
+
+  clear_stack (V1);
+  clear_stack (V2);
+
+  /* at the end, V1 and V2 are deleted only if they were temporary */
+  return (TRUE);
+
+}
+
+int MM_binary (StackVar *OUT, StackVar *V1, StackVar *V2, char *op) {
+
+  int i, Nx, Ny;
+  float *out, *M1, *M2;
+  char line[512]; // this is only used to report an error 
+  
+  Nx = V1[0].buffer[0].matrix.Naxis[0];
+  Ny = V1[0].buffer[0].matrix.Naxis[1];
+
+  if (V1[0].type == 'm') {  /** use V1 as temp buffer **/
+    OUT[0].buffer = V1[0].buffer;
+    V1[0].type = 'M'; /* prevent it from being freed below */
+  } else {
+    if (V2[0].type == 'm') { /** use V2 as temp buffer, but header of V1 **/
+      OUT[0].buffer = V2[0].buffer;
+      V2[0].type = 'M'; /* prevent it from being freed below */
+    } else {  /* no spare temp buffer */
+      OUT[0].buffer = InitBuffer ();
+      CopyBuffer (OUT[0].buffer, V1[0].buffer);
+    }
+  }
+  OUT[0].type = 'm'; /*** <<--- says this is a temporary matrix ***/
+
+  M1  = (float *)V1[0].buffer[0].matrix.buffer;
+  M2  = (float *)V2[0].buffer[0].matrix.buffer;
+  out = (float *)OUT[0].buffer[0].matrix.buffer;
+
+# define MM_FUNC(OP)					\
+  for (i = 0; i < Nx*Ny; i++, out++, M1++, M2++) {	\
+    *out = OP;						\
+  }							\
+  break; 
+
+  switch (op[0]) { 
+    case '+': MM_FUNC(*M1 + *M2);
+    case '-': MM_FUNC(*M1 - *M2);
+    case '*': MM_FUNC(*M1 * *M2);
+    case '/': MM_FUNC(*M1 / (float) *M2);
+    case '%': MM_FUNC((long long) *M1 % (long long) *M2);
+    case '^': MM_FUNC(pow (*M1, *M2));
+    case '@': MM_FUNC(DEG_RAD*atan2 (*M1, *M2));
+    case 'D': MM_FUNC(MIN (*M1, *M2));
+    case 'U': MM_FUNC(MAX (*M1, *M2));
+    case '<': MM_FUNC((*M1 < *M2) ? 1 : 0);
+    case '>': MM_FUNC((*M1 > *M2) ? 1 : 0);
+    case '&': MM_FUNC(((long long)*M1 & (long long)*M2));
+    case '|': MM_FUNC(((long long)*M1 | (long long)*M2));
+    case 'E': MM_FUNC((*M1 == *M2) ? 1 : 0);
+    case 'N': MM_FUNC((*M1 != *M2) ? 1 : 0);
+    case 'L': MM_FUNC((*M1 <= *M2) ? 1 : 0);
+    case 'G': MM_FUNC((*M1 >= *M2) ? 1 : 0);
+    case 'A': MM_FUNC((*M1 && *M2) ? 1 : 0);
+    case 'O': MM_FUNC((*M1 || *M2) ? 1 : 0);
+    default:
+      sprintf (line, "error: op %c not defined!", op[0]);
+      push_error (line);
+      return (FALSE);
+  }
+# undef MM_FUNC
+
+  /** free up any temporary buffers: **/
+
+  if (V1[0].type == 'm') {
+    free (V1[0].buffer[0].header.buffer);
+    free (V1[0].buffer[0].matrix.buffer);
+    free (V1[0].buffer);
+  }
+  if (V2[0].type == 'm') {
+    free (V2[0].buffer[0].header.buffer);
+    free (V2[0].buffer[0].matrix.buffer);
+    free (V2[0].buffer);
+  }
+
+  clear_stack (V1);
+  clear_stack (V2);
+
+  /* at the end, V1 and V2 are deleted only if they were temporary */
+  return (TRUE);
+}
+
+int MS_binary (StackVar *OUT, StackVar *V1, StackVar *V2, char *op) {
+
+  int i, Nx, Ny;
+  char line[512]; // this is only used to report an error 
+  
+  Nx = V1[0].buffer[0].matrix.Naxis[0];
+  Ny = V1[0].buffer[0].matrix.Naxis[1];
+
+  /* if possible, use V1 as temp buffer, otherwise create new one */
+  if (V1[0].type == 'm') {
+    OUT[0].buffer = V1[0].buffer;
+    V1[0].type = 'M'; /* prevent it from being freed below */
+  } else {
+    OUT[0].buffer = InitBuffer ();
+    CopyBuffer (OUT[0].buffer, V1[0].buffer);
+  }
+  OUT[0].type = 'm';      /*** <<--- says this is a temporary matrix ***/
+
+  float *M1    = (float *)V1[0].buffer[0].matrix.buffer;
+  float *out   = (float *)OUT[0].buffer[0].matrix.buffer;
+
+# define MS_FUNC(OP) {					\
+    if (V2->type == 'S')  {				\
+      opihi_flt M2 = V2[0].FltValue;			\
+      for (i = 0; i < Nx*Ny; i++, out++, M1++) {	\
+	*out = OP;					\
+      }							\
+      break;						\
+    }							\
+    if (V2->type == 's')  {				\
+      opihi_int M2 = V2[0].IntValue;			\
+      for (i = 0; i < Nx*Ny; i++, out++, M1++) {	\
+	*out = OP;					\
+      }							\
+      break;						\
+    }							\
+  }
+
+  switch (op[0]) { 
+    case '+': MS_FUNC(*M1 + M2);
+    case '-': MS_FUNC(*M1 - M2);
+    case '*': MS_FUNC(*M1 * M2);
+    case '/': MS_FUNC(*M1 / (float) M2);
+    case '%': MS_FUNC((long long) *M1 % (long long) M2);
+    case '^': MS_FUNC(pow (*M1, M2));
+    case '@': MS_FUNC(DEG_RAD*atan2 (*M1, M2));
+    case 'D': MS_FUNC(MIN (*M1, M2));
+    case 'U': MS_FUNC(MAX (*M1, M2));
+    case '<': MS_FUNC((*M1 < M2) ? 1 : 0);
+    case '>': MS_FUNC((*M1 > M2) ? 1 : 0);
+    case '&': MS_FUNC(((long long)*M1 & (long long)M2));
+    case '|': MS_FUNC(((long long)*M1 | (long long)M2));
+    case 'E': MS_FUNC((*M1 == M2) ? 1 : 0);
+    case 'N': MS_FUNC((*M1 != M2) ? 1 : 0);
+    case 'L': MS_FUNC((*M1 <= M2) ? 1 : 0);
+    case 'G': MS_FUNC((*M1 >= M2) ? 1 : 0);
+    case 'A': MS_FUNC((*M1 && M2) ? 1 : 0);
+    case 'O': MS_FUNC((*M1 || M2) ? 1 : 0);
+    default:
+      sprintf (line, "error: op %c not defined!", op[0]);
+      push_error (line);
+      return (FALSE);
+  }
+# undef MS_FUNC
+
+  if (V1[0].type == 'm') {
+    free (V1[0].buffer[0].header.buffer);
+    free (V1[0].buffer[0].matrix.buffer);
+    free (V1[0].buffer);
+  }
+  clear_stack (V1);
+  clear_stack (V2);
+
+  return (TRUE);
+}
+
+
+int SM_binary (StackVar *OUT, StackVar *V1, StackVar *V2, char *op) {
+
+  int i, Nx, Ny;
+  char line[512]; // this is only used to report an error 
+  
+  Nx = V2[0].buffer[0].matrix.Naxis[0];
+  Ny = V2[0].buffer[0].matrix.Naxis[1];
+
+  if (V2[0].type == 'm') {  /* V2[0] is NOT temporary, we can't use it for storage */
+    OUT[0].buffer = V2[0].buffer;
+    V2[0].type = 'M'; /* prevent it from being freed below */
+  } else {
+    OUT[0].buffer = InitBuffer ();
+    CopyBuffer (OUT[0].buffer, V2[0].buffer);
+  }
+  OUT[0].type = 'm'; /*** <<--- says this is a temporary matrix ***/
+
+  float *M2    = (float *)V2[0].buffer[0].matrix.buffer;
+  float *out   = (float *)OUT[0].buffer[0].matrix.buffer;
+
+# define SM_FUNC(OP) {					\
+    if (V1->type == 'S')  {				\
+      opihi_flt M1 = V1[0].FltValue;			\
+      for (i = 0; i < Nx*Ny; i++, out++, M2++) {	\
+	*out = OP;					\
+      }							\
+      break;						\
+    }							\
+    if (V1->type == 's')  {				\
+      opihi_int M1 = V1[0].IntValue;			\
+      for (i = 0; i < Nx*Ny; i++, out++, M2++) {	\
+	*out = OP;					\
+      }							\
+      break;						\
+    }							\
+  }
+
+  switch (op[0]) { 
+    case '+': SM_FUNC(M1 + *M2);
+    case '-': SM_FUNC(M1 - *M2);
+    case '*': SM_FUNC(M1 * *M2);
+    case '/': SM_FUNC(M1 / (float) *M2);
+    case '%': SM_FUNC((long long) M1 % (long long) *M2);
+    case '^': SM_FUNC(pow (M1, *M2));
+    case '@': SM_FUNC(DEG_RAD*atan2 (M1, *M2));
+    case 'D': SM_FUNC(MIN (M1, *M2));
+    case 'U': SM_FUNC(MAX (M1, *M2));
+    case '<': SM_FUNC((M1 < *M2) ? 1 : 0);
+    case '>': SM_FUNC((M1 > *M2) ? 1 : 0);
+    case '&': SM_FUNC(((long long)M1 & (long long)*M2));
+    case '|': SM_FUNC(((long long)M1 | (long long)*M2));
+    case 'E': SM_FUNC((M1 == *M2) ? 1 : 0);
+    case 'N': SM_FUNC((M1 != *M2) ? 1 : 0);
+    case 'L': SM_FUNC((M1 <= *M2) ? 1 : 0);
+    case 'G': SM_FUNC((M1 >= *M2) ? 1 : 0);
+    case 'A': SM_FUNC((M1 && *M2) ? 1 : 0);
+    case 'O': SM_FUNC((M1 || *M2) ? 1 : 0);
+    default:
+      sprintf (line, "error: op %c not defined!", op[0]);
+      push_error (line);
+      return (FALSE);
+  }
+# undef SM_FUNC
+
+  if (V2[0].type == 'm') {
+    free (V2[0].buffer[0].header.buffer);
+    free (V2[0].buffer[0].matrix.buffer);
+    free (V2[0].buffer);
+  }
+  clear_stack (V1);
+  clear_stack (V2);
+
+  return (TRUE);
+
+}
+
+int SS_binary (StackVar *OUT, StackVar *V1, StackVar *V2, char *op) {
+
+  char line[512]; // this is only used to report an error 
+
+# define SS_FUNC(FTYPE,OP) {						\
+    if ((V1->type == 'S') && (V2->type == 'S')) {			\
+      opihi_flt M1 = V1[0].FltValue;					\
+      opihi_flt M2 = V2[0].FltValue;					\
+      OUT[0].type = 'S';						\
+      OUT[0].FltValue = OP;						\
+      break;								\
+    }									\
+    if ((V1->type == 'S') && (V2->type == 's')) {			\
+      opihi_flt M1 = V1[0].FltValue;					\
+      opihi_int M2 = V2[0].IntValue;					\
+      OUT[0].type = 'S';						\
+      OUT[0].FltValue = OP;						\
+      break;								\
+    }									\
+    if ((V1->type == 's') && (V2->type == 'S')) {			\
+      opihi_int M1 = V1[0].IntValue;					\
+      opihi_flt M2 = V2[0].FltValue;					\
+      OUT[0].type = 'S';						\
+      OUT[0].FltValue = OP;						\
+      break;								\
+    }									\
+    if ((FTYPE == 'S') && (V1->type == 's') && (V2->type == 's')) {	\
+      opihi_int M1 = V1[0].IntValue;					\
+      opihi_int M2 = V2[0].IntValue;					\
+      OUT[0].type = 'S';						\
+      OUT[0].FltValue = OP;						\
+      break;								\
+    }									\
+    if ((V1->type == 's') && (V2->type == 's')) {			\
+      opihi_int M1 = V1[0].IntValue;					\
+      opihi_int M2 = V2[0].IntValue;					\
+      OUT[0].type = 's';						\
+      OUT[0].IntValue = OP;						\
+      break;								\
+    }									\
+  }
+
+  switch (op[0]) { 
+    case '+': SS_FUNC('s', M1 + M2);
+    case '-': SS_FUNC('s', M1 - M2);
+    case '*': SS_FUNC('s', M1 * M2);
+    case '/': SS_FUNC('S', M1 / (opihi_flt) M2);
+    case '%': SS_FUNC('s', (long long) M1 % (long long) M2);
+    case '^': SS_FUNC('S', pow (M1, M2));
+    case '@': SS_FUNC('S', DEG_RAD*atan2 (M1, M2));
+    case 'D': SS_FUNC('s', MIN (M1, M2));
+    case 'U': SS_FUNC('s', MAX (M1, M2));
+    case '<': SS_FUNC('s', (M1 < M2) ? 1 : 0);
+    case '>': SS_FUNC('s', (M1 > M2) ? 1 : 0);
+    case '&': SS_FUNC('s', ((long long)M1 & (long long)M2));
+    case '|': SS_FUNC('s', ((long long)M1 | (long long)M2));
+    case 'E': SS_FUNC('s', (M1 == M2) ? 1 : 0);
+    case 'N': SS_FUNC('s', (M1 != M2) ? 1 : 0);
+    case 'L': SS_FUNC('s', (M1 <= M2) ? 1 : 0);
+    case 'G': SS_FUNC('s', (M1 >= M2) ? 1 : 0);
+    case 'A': SS_FUNC('s', (M1 && M2) ? 1 : 0);
+    case 'O': SS_FUNC('s', (M1 || M2) ? 1 : 0);
+    default:
+      sprintf (line, "error: op %c not defined!", op[0]);
+      push_error (line);
+      return (FALSE);
+  }
+# undef SS_FUNC
+
+  clear_stack (V1);
+  clear_stack (V2);
+
+  return (TRUE);
+
+}
+
+int WW_binary (StackVar *OUT, StackVar *V1, StackVar *V2, char *op) {
+
+  int value;
+  char line[512]; // this is only used to report an error 
+
+  /* only 'N' and 'E' are allowed for WW_binary operations. anything else is either a
+     syntax error or is a string which looks like a math expression. */
+
+  if ((op[0] != 'N') && (op[0] != 'E')) {
+    sprintf (line, "error: op %c not defined for string operations!", op[0]);
+    push_error (line);
+    return (FALSE);
+  }
+
+  /* evaluate stack will only call WW_binary with one numerical value,
+     and only in the case that the string did not parse to a number 
+     thus: string == number -> false */
+
+  if (!strncasecmp (&V1[0].type, "S", 1)) {
+    value = (op[0] == 'N');
+    goto escape;
+  }
+  if (!strncasecmp (&V2[0].type, "S", 1)) {
+    value = (op[0] == 'N');
+    goto escape;
+  }
+
+  switch (op[0]) { 
+    case 'E': 
+      value = strcmp (V1[0].name, V2[0].name) ? 0 : 1;
+    break; 
+    case 'N': 
+      value = strcmp (V1[0].name, V2[0].name) ? 1 : 0;
+    break; 
+    default:
+      sprintf (line, "error: op %c not defined for string operations!", op[0]);
+      push_error (line);
+      return (FALSE);
+  }
+
+escape:
+  OUT[0].FltValue = value;
+  OUT[0].type = 'S';
+
+  clear_stack (V1);
+  clear_stack (V2);
+  return (TRUE);
+
+}
+
+
+int S_unary (StackVar *OUT, StackVar *V1, char *op) {
+
+  char line[512]; // this is only used to report an error 
+  
+# define S_FUNC(OP,FTYPE) {			\
+    if (V1->type == 'S') {			\
+      opihi_flt M1  = V1[0].FltValue;		\
+      OUT[0].type = 'S';			\
+      OUT[0].FltValue = OP;			\
+      clear_stack (V1);				\
+      return (TRUE);				\
+    }						\
+    if ((FTYPE == 'S') && (V1->type == 's')) {	\
+      opihi_int M1  = V1[0].IntValue;		\
+      OUT[0].type = 'S';			\
+      OUT[0].FltValue = OP;			\
+      clear_stack (V1);				\
+      return (TRUE);				\
+    }						\
+    if ((FTYPE == 's') && (V1->type == 's')) {	\
+      opihi_int M1  = V1[0].IntValue;		\
+      OUT[0].type = 's';			\
+      OUT[0].IntValue = OP;			\
+      clear_stack (V1);				\
+      return (TRUE);				\
+    }						\
+  }
+
+  if (!strcmp (op, "="))      S_FUNC(M1, 's');
+  if (!strcmp (op, "abs"))    S_FUNC(fabs(M1), 's');
+  if (!strcmp (op, "int"))    S_FUNC((long long)(M1), 's');
+  if (!strcmp (op, "exp"))    S_FUNC(exp (M1), 'S');
+  if (!strcmp (op, "ten"))    S_FUNC(pow (10.0,M1), 'S');
+  if (!strcmp (op, "log"))    S_FUNC(log10 (M1), 'S');
+  if (!strcmp (op, "ln"))     S_FUNC(log (M1), 'S');
+  if (!strcmp (op, "sqrt"))   S_FUNC(sqrt (M1), 'S');
+  if (!strcmp (op, "erf"))    S_FUNC(erf (M1), 'S');
+  if (!strcmp (op, "sinh"))   S_FUNC(sinh (M1), 'S');
+  if (!strcmp (op, "cosh"))   S_FUNC(cosh (M1), 'S');
+  if (!strcmp (op, "asinh"))  S_FUNC(asinh (M1), 'S');
+  if (!strcmp (op, "acosh"))  S_FUNC(acosh (M1), 'S');
+  if (!strcmp (op, "lgamma")) S_FUNC(lgamma (M1), 'S');
+  if (!strcmp (op, "sin"))    S_FUNC(sin (M1), 'S');
+  if (!strcmp (op, "cos"))    S_FUNC(cos (M1), 'S');
+  if (!strcmp (op, "tan"))    S_FUNC(tan (M1), 'S');
+  if (!strcmp (op, "dsin"))   S_FUNC(sin (M1*RAD_DEG), 'S');
+  if (!strcmp (op, "dcos"))   S_FUNC(cos (M1*RAD_DEG), 'S');
+  if (!strcmp (op, "dtan"))   S_FUNC(tan (M1*RAD_DEG), 'S');
+  if (!strcmp (op, "asin"))   S_FUNC(asin (M1), 'S');
+  if (!strcmp (op, "acos"))   S_FUNC(acos (M1), 'S');
+  if (!strcmp (op, "atan"))   S_FUNC(atan (M1), 'S');
+  if (!strcmp (op, "dasin"))  S_FUNC(asin (M1)*DEG_RAD, 'S');
+  if (!strcmp (op, "dacos"))  S_FUNC(acos (M1)*DEG_RAD, 'S');
+  if (!strcmp (op, "datan"))  S_FUNC(atan (M1)*DEG_RAD, 'S');
+  if (!strcmp (op, "rnd"))    S_FUNC(M1*0.0 + drand48(), 'S');
+  if (!strcmp (op, "not"))    S_FUNC(!(M1), 's');
+  if (!strcmp (op, "--"))     S_FUNC(-1*M1, 's'); // NOTE: opihi_int is signed, 
+  if (!strcmp (op, "isinf"))  S_FUNC(!finite(M1), 'S'); // XXX modify in future 
+  if (!strcmp (op, "isnan"))  S_FUNC(isnan(M1), 'S'); // XXX modify in future   
+
+# undef S_FUNC
+
+  clear_stack (V1);
+  sprintf (line, "error: op %s not defined!", op);
+  push_error (line);
+  return (FALSE);
+
+}
+
+int V_unary (StackVar *OUT, StackVar *V1, char *op) {
+
+  int i, Nx;
+  
+  Nx = V1[0].vector[0].Nelements;
+
+  OUT[0].vector = InitVector ();
+  OUT[0].type = 'v'; /*** <<--- says this is a temporary matrix ***/
+
+# define V_FUNC(OP,FTYPE) {					\
+    if (V1->vector->type == OPIHI_FLT) {			\
+      CopyVector (OUT[0].vector, V1[0].vector);			\
+      opihi_flt *M1  = V1[0].vector[0].elements.Flt;		\
+      opihi_flt *out = OUT[0].vector[0].elements.Flt;		\
+      for (i = 0; i < Nx; i++, out++, M1++) {			\
+	*out = OP;						\
+      }								\
+      goto escape;						\
+    }								\
+    if ((V1->vector->type == OPIHI_INT) && (FTYPE == 'S')) {	\
+      MatchVector (OUT[0].vector, V1[0].vector, OPIHI_FLT);	\
+      opihi_int *M1  = V1[0].vector[0].elements.Int;		\
+      opihi_flt *out = OUT[0].vector[0].elements.Flt;		\
+      for (i = 0; i < Nx; i++, out++, M1++) {			\
+	*out = OP;						\
+      }								\
+      goto escape;						\
+    }								\
+    if ((V1->vector->type == OPIHI_INT) && (FTYPE == 's')) {	\
+      CopyVector (OUT[0].vector, V1[0].vector);			\
+      opihi_int *M1  = V1[0].vector[0].elements.Int;		\
+      opihi_int *out = OUT[0].vector[0].elements.Int;		\
+      for (i = 0; i < Nx; i++, out++, M1++) {			\
+	*out = OP;						\
+      }								\
+      goto escape;						\
+    } }							
+
+  if (!strcmp (op, "="))      V_FUNC(*M1, 's');
+  if (!strcmp (op, "abs"))    V_FUNC(fabs(*M1), 's');
+  if (!strcmp (op, "int"))    V_FUNC((long long)(*M1), 's');
+  if (!strcmp (op, "exp"))    V_FUNC(exp(*M1), 'S');
+  if (!strcmp (op, "ten"))    V_FUNC(pow(10.0,*M1), 'S');
+  if (!strcmp (op, "log"))    V_FUNC(log10(*M1), 'S');
+  if (!strcmp (op, "ln"))     V_FUNC(log(*M1), 'S');
+  if (!strcmp (op, "sqrt"))   V_FUNC(sqrt(*M1), 'S');
+  if (!strcmp (op, "erf"))    V_FUNC(erf(*M1), 'S');
+  if (!strcmp (op, "sinh"))   V_FUNC(sinh(*M1), 'S');
+  if (!strcmp (op, "cosh"))   V_FUNC(cosh(*M1), 'S');
+  if (!strcmp (op, "asinh"))  V_FUNC(asinh(*M1), 'S');
+  if (!strcmp (op, "acosh"))  V_FUNC(acosh(*M1), 'S');
+  if (!strcmp (op, "lgamma")) V_FUNC(lgamma(*M1), 'S');
+  if (!strcmp (op, "sin"))    V_FUNC(sin(*M1), 'S');
+  if (!strcmp (op, "cos"))    V_FUNC(cos(*M1), 'S');
+  if (!strcmp (op, "tan"))    V_FUNC(tan(*M1), 'S');
+  if (!strcmp (op, "dsin"))   V_FUNC(sin(*M1*RAD_DEG), 'S');
+  if (!strcmp (op, "dcos"))   V_FUNC(cos(*M1*RAD_DEG), 'S');
+  if (!strcmp (op, "dtan"))   V_FUNC(tan(*M1*RAD_DEG), 'S');
+  if (!strcmp (op, "asin"))   V_FUNC(asin(*M1), 'S');
+  if (!strcmp (op, "acos"))   V_FUNC(acos(*M1), 'S');
+  if (!strcmp (op, "atan"))   V_FUNC(atan(*M1), 'S');
+  if (!strcmp (op, "dasin"))  V_FUNC(asin(*M1)*DEG_RAD, 'S');
+  if (!strcmp (op, "dacos"))  V_FUNC(acos(*M1)*DEG_RAD, 'S');
+  if (!strcmp (op, "datan"))  V_FUNC(atan(*M1)*DEG_RAD, 'S');
+  if (!strcmp (op, "rnd"))    V_FUNC(drand48(), 'S');
+  if (!strcmp (op, "ramp"))   V_FUNC(i, 's');
+  if (!strcmp (op, "zero"))   V_FUNC(0, 's');
+  if (!strcmp (op, "not"))    V_FUNC(!(*M1), 's');
+  if (!strcmp (op, "--"))     V_FUNC(-1*(*M1), 's'); // NOTE: opihi_int is signed
+  if (!strcmp (op, "isinf"))  V_FUNC(!finite(*M1), 'S');
+  if (!strcmp (op, "isnan"))  V_FUNC(isnan(*M1), 'S');
+  if (!strcmp (op, "xramp"))  V_FUNC(i, 's');
+  if (!strcmp (op, "yramp"))  V_FUNC(0, 's');
+  /* xramp and yramp above only make sense for matrices. for vectors, xramp = ramp, yramp = zero */
+
+# undef V_FUNC
+
+escape:
+
+  if (V1[0].type == 'v') {
+    free (V1[0].vector[0].elements.Ptr);
+    free (V1[0].vector);
+    V1[0].vector = NULL;
+  }  
+
+  clear_stack (V1);
+  return (TRUE);
+
+}
+
+int M_unary (StackVar *OUT, StackVar *V1, char *op) {
+
+  int i, j, Nx, Ny;
+  float *out, *M1;
+  
+  Nx = V1[0].buffer[0].matrix.Naxis[0];
+  Ny = V1[0].buffer[0].matrix.Naxis[1];
+
+  if (V1[0].type == 'm') {
+    OUT[0].buffer = V1[0].buffer;
+    V1[0].type = 'M'; /* prevent it from being freed below */
+  } else {
+    OUT[0].buffer = InitBuffer ();
+    CopyBuffer (OUT[0].buffer, V1[0].buffer);
+  }
+  OUT[0].type = 'm';      /*** <<--- says this is a temporary matrix ***/
+  M1  = (float *) V1[0].buffer[0].matrix.buffer;
+  out = (float *)OUT[0].buffer[0].matrix.buffer;
+
+  if (!strcmp (op, "="))     { }
+  if (!strcmp (op, "abs"))   { for (i = 0; i < Nx*Ny; i++, out++, M1++) { *out = fabs(*M1);         }}
+  if (!strcmp (op, "int"))   { for (i = 0; i < Nx*Ny; i++, out++, M1++) { *out = (opihi_flt)(long long)(*M1); }}
+  if (!strcmp (op, "exp"))   { for (i = 0; i < Nx*Ny; i++, out++, M1++) { *out = exp(*M1);          }}
+  if (!strcmp (op, "ten"))   { for (i = 0; i < Nx*Ny; i++, out++, M1++) { *out = pow(10.0,*M1);     }}
+  if (!strcmp (op, "log"))   { for (i = 0; i < Nx*Ny; i++, out++, M1++) { *out = log10(*M1);        }}
+  if (!strcmp (op, "ln"))    { for (i = 0; i < Nx*Ny; i++, out++, M1++) { *out = log(*M1);          }}
+  if (!strcmp (op, "sqrt"))  { for (i = 0; i < Nx*Ny; i++, out++, M1++) { *out = sqrt(*M1);         }}
+  if (!strcmp (op, "erf"))   { for (i = 0; i < Nx*Ny; i++, out++, M1++) { *out = erf(*M1);          }}
+
+  if (!strcmp (op, "sinh"))  { for (i = 0; i < Nx*Ny; i++, out++, M1++) { *out = sinh(*M1);         }}
+  if (!strcmp (op, "cosh"))  { for (i = 0; i < Nx*Ny; i++, out++, M1++) { *out = cosh(*M1);         }}
+  if (!strcmp (op, "asinh")) { for (i = 0; i < Nx*Ny; i++, out++, M1++) { *out = asinh(*M1);        }}
+  if (!strcmp (op, "acosh")) { for (i = 0; i < Nx*Ny; i++, out++, M1++) { *out = acosh(*M1);        }}
+  if (!strcmp (op, "lgamma")) { for (i = 0; i < Nx*Ny; i++, out++, M1++) { *out = lgamma(*M1);      }}
+
+  if (!strcmp (op, "sin"))   { for (i = 0; i < Nx*Ny; i++, out++, M1++) { *out = sin(*M1);          }}
+  if (!strcmp (op, "cos"))   { for (i = 0; i < Nx*Ny; i++, out++, M1++) { *out = cos(*M1);          }}
+  if (!strcmp (op, "tan"))   { for (i = 0; i < Nx*Ny; i++, out++, M1++) { *out = tan(*M1);          }}
+  if (!strcmp (op, "dsin"))  { for (i = 0; i < Nx*Ny; i++, out++, M1++) { *out = sin(*M1*RAD_DEG);  }}
+  if (!strcmp (op, "dcos"))  { for (i = 0; i < Nx*Ny; i++, out++, M1++) { *out = cos(*M1*RAD_DEG);  }}
+  if (!strcmp (op, "dtan"))  { for (i = 0; i < Nx*Ny; i++, out++, M1++) { *out = tan(*M1*RAD_DEG);  }}
+  if (!strcmp (op, "asin"))  { for (i = 0; i < Nx*Ny; i++, out++, M1++) { *out = asin(*M1);         }}
+  if (!strcmp (op, "acos"))  { for (i = 0; i < Nx*Ny; i++, out++, M1++) { *out = acos(*M1);         }}
+  if (!strcmp (op, "atan"))  { for (i = 0; i < Nx*Ny; i++, out++, M1++) { *out = atan(*M1);         }}
+  if (!strcmp (op, "dasin")) { for (i = 0; i < Nx*Ny; i++, out++, M1++) { *out = asin(*M1)*DEG_RAD; }}
+  if (!strcmp (op, "dacos")) { for (i = 0; i < Nx*Ny; i++, out++, M1++) { *out = acos(*M1)*DEG_RAD; }}
+  if (!strcmp (op, "datan")) { for (i = 0; i < Nx*Ny; i++, out++, M1++) { *out = atan(*M1)*DEG_RAD; }}
+  if (!strcmp (op, "not"))   { for (i = 0; i < Nx*Ny; i++, out++, M1++) { *out = !(*M1);            }}
+  if (!strcmp (op, "--"))    { for (i = 0; i < Nx*Ny; i++, out++, M1++) { *out = -(*M1);            }}
+  if (!strcmp (op, "rnd"))   { for (i = 0; i < Nx*Ny; i++, out++, M1++) { *out = drand48();         }}
+  if (!strcmp (op, "ramp"))  { for (i = 0; i < Nx*Ny; i++, out++, M1++) { *out = i;                 }}
+  if (!strcmp (op, "zero"))  { for (i = 0; i < Nx*Ny; i++, out++, M1++) { *out = 0;                 }}
+  if (!strcmp (op, "isinf")) { for (i = 0; i < Nx*Ny; i++, out++, M1++) { *out = !finite(*M1);      }}
+  if (!strcmp (op, "isnan")) { for (i = 0; i < Nx*Ny; i++, out++, M1++) { *out = isnan(*M1);        }}
+
+  /* xrm and yrm only make sense in for matrices. see special meaning for vectors */
+  if (!strcmp (op, "xramp")) {
+    for (j = 0; j < Ny; j++) {
+      for (i = 0; i < Nx; i++, out++, M1++) {
+	*out = i;
+      }
+    }
+  }
+  if (!strcmp (op, "yramp")) {
+    for (j = 0; j < Ny; j++) {
+      for (i = 0; i < Nx; i++, out++, M1++) {
+	*out = j;
+      }
+    }
+  }
+  
+  if (V1[0].type == 'm') {
+    free (V1[0].buffer[0].header.buffer);
+    free (V1[0].buffer[0].matrix.buffer);
+    free (V1[0].buffer);
+  }
+
+  clear_stack (V1);
+  return (TRUE);
+
+}
+
+/*********************** fits copy header ***********************************/
+int gfits_copy_matrix_info (Matrix *matrix1, Matrix *matrix2) {
+
+  int i;
+
+  /* copy all but the matrix */
+
+  matrix2[0].unsign   = matrix1[0].unsign;
+  matrix2[0].bitpix   = matrix1[0].bitpix;
+  matrix2[0].datasize = matrix1[0].datasize;
+  matrix2[0].bzero    = matrix1[0].bzero;
+  matrix2[0].bscale   = matrix1[0].bscale;
+  matrix2[0].Naxes    = matrix1[0].Naxes;
+  for (i = 0; i < FT_MAX_NAXES; i++) {
+    matrix2[0].Naxis[i] = matrix1[0].Naxis[i];
+  }
+
+  return (TRUE);
+}       
Index: /branches/sc_branches/pantasks_condor/lib.shell/startup.c
===================================================================
--- /branches/sc_branches/pantasks_condor/lib.shell/startup.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/lib.shell/startup.c	(revision 34783)
@@ -0,0 +1,166 @@
+# include "opihi.h"
+
+/* program-independent initialization
+ * these steps do not depend on the opihi implementation
+ * (ie, the supplied commands or data structures)
+ */
+
+void general_init (int *argc, char **argv) {
+
+  /* init srand for rnd numbers elsewhere */
+  long A, B;
+  A = time(NULL);
+  for (B = 0; A == time(NULL); B++);
+  srand48(B);
+ 
+  /* set signals */
+  signal (SIGINT, SIG_IGN);
+
+  /* init for main (or only) thread */
+  gprintInit ();
+
+  init_error ();
+
+  /* load config data (.ptolemyrc) */
+  if (!ConfigInit (argc, argv)) {
+    gprint (GP_ERR, "can't find config file. some functions will be unavailable\n");
+  }
+
+  return;
+}
+
+void startup (int *argc, char **argv) {
+    
+    int i, N, status, ONLY_INPUT, LOAD_RC;
+    char *line, *home, *outline, *varname;
+    char *rcfile, **list;
+    int Nlist, NLIST;
+   
+    /* load in interesting environment variables */
+    ALLOCATE (line, char, 1024);
+    
+    home = getenv ("HOME");
+    if (home == NULL) 
+      set_str_variable ("HOME", ".");
+    else 
+      set_str_variable ("HOME", home);
+    set_str_variable ("KAPA", "kapa");
+    set_variable ("PID", getpid());
+
+    set_int_variable ("UNSIGN", 0);
+    gfits_set_unsign_mode (FALSE);
+    
+  /* check history file permission */
+  {
+    FILE *f;
+    char *opihi_history;
+
+    opihi_history = get_variable ("HISTORY");
+    if (opihi_history && *opihi_history) {
+      f = fopen (opihi_history, "a");
+      if (f == NULL) /* no current history file here */
+	gprint (GP_ERR, "can't save history.\n");
+      else
+	fclose (f);
+      stifle_history (200);
+      read_history (opihi_history);
+    }
+    if (opihi_history) free (opihi_history);
+  }
+
+  if (0) {
+    /* fix the history list to remove the timestamp */
+    char *c;
+    int i;
+    HIST_ENTRY **entry;
+    
+    entry = history_list ();
+    if (entry != (HIST_ENTRY **) NULL) {
+      for (i = 0; entry[i]; i++) {
+	if ((strlen (entry[i][0].line) > 19) &&
+	    (entry[i][0].line[2] == '/') && 
+	    (entry[i][0].line[5] == '/') && 
+	    (entry[i][0].line[11] == ':') && 
+	    (entry[i][0].line[14] == ':') && 
+	    (entry[i][0].line[17] == ':')) {
+	  c = entry[i][0].line + 19;
+	  memmove (entry[i][0].line, c, strlen(c) + 1);
+	}
+      }
+    }
+  }
+
+    LOAD_RC = TRUE;
+    if ((N = get_argument (*argc, argv, "--norc"))) {
+      remove_argument (N, argc, argv);
+      LOAD_RC = FALSE;
+    }
+    
+    ONLY_INPUT = FALSE;
+    if ((N = get_argument (*argc, argv, "--only"))) {
+      remove_argument (N, argc, argv);
+      ONLY_INPUT = TRUE;
+    }
+
+    /* load cmdline files */
+    Nlist = 0;
+    NLIST = 10;
+    ALLOCATE (list, char *, NLIST);
+    while ((N = get_argument (*argc, argv, "--load"))) {
+      remove_argument (N, argc, argv);
+      list[Nlist] = strcreate (argv[N]);
+      remove_argument (N, argc, argv);
+      Nlist++;
+      if (Nlist == NLIST) {
+	NLIST += 10;
+	REALLOCATE (list, char *, NLIST);
+      }
+    }
+
+    is_script = TRUE;
+    if (*argc == 1) is_script = FALSE;
+    set_int_variable ("SCRIPT", is_script);
+
+    if (LOAD_RC && !is_script) {
+      rcfile = get_variable ("RCFILE");
+      if (rcfile && check_file_access (rcfile, FALSE, FALSE, FALSE)) {
+	sprintf (line, "input %s/%s", home, rcfile);
+	status = command (line, &outline, TRUE);
+	if (outline != (char *) NULL) free (outline);
+	if (status) {
+	  gprint (GP_LOG, "loaded file %s\n", rcfile); 
+	}	  
+      }
+      if (rcfile) free (rcfile);
+    }
+      
+    set_int_variable ("argv:n", 0);
+    if (is_script) {
+      /* first argument in input script, rest are argv */
+      set_str_variable ("SCRIPT_NAME", argv[1]);
+      list[Nlist] = strcreate (argv[1]);
+      Nlist ++;
+      /* generate list argv:0 - argv:n from arguments */
+      ALLOCATE (varname, char, 256);
+      for (i = 2; i < *argc; i++) {
+	sprintf (varname, "argv:%d", i - 2);
+	set_str_variable (varname, argv[i]);
+      }
+      set_int_variable ("argv:n", i - 2);
+      free (varname);
+    }
+
+    /* execute command-line entries */
+    for (i = 0; i < Nlist; i++) {
+      ALLOCATE (line, char, 1024);
+      sprintf (line, "input %s", list[i]);
+      status = command (line, &outline, TRUE);
+      if (outline != (char *) NULL) free (outline);
+    }
+    free (list);
+
+    /* if this is not an interactive session, exit here */
+    if (ONLY_INPUT) exit (40);
+    if (is_script) exit (41);
+    return;
+}
Index: /branches/sc_branches/pantasks_condor/lib.shell/string.c
===================================================================
--- /branches/sc_branches/pantasks_condor/lib.shell/string.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/lib.shell/string.c	(revision 34783)
@@ -0,0 +1,323 @@
+# include "opihi.h"
+
+/**********************************************************************/
+/* returns a pointer to an isolated string containing the first word,
+   removing leading WHITESPACE.  A "word" is a contiguous set of 
+   characters from the set: alphanumerics, and any of: / . _ -
+   Any other single, non-WHITESPACE characters are considered to be 
+   complete words in themselves.  Any characters surrounded by quotes 
+   make a single word 
+*/
+
+char *thisword (char *string) {
+
+  int i, j, N;
+  char *word;
+
+  if (string == (char *) NULL) return ((char *) NULL);
+
+  for (i = 0; OHANA_WHITESPACE (string[i]); i++);
+  if (string[i] == 0) return ((char *)NULL);
+  if (string[i] == ';') {
+    word = strncreate (&string[i], 1);
+    return (word);
+  }
+
+  /* a string of characters contained within double quotes is 
+      a single entry.  check that there are pairs of quotes,
+      return only the string between the quotes */
+  if (string[i] == '"') { 
+    i++;
+    if (string[i] == 0) return ((char *) NULL);
+    for (j = i; (string[j] != 0) && (string[j] != '"'); j++);
+    if (string[j] == 0) {
+      gprint (GP_ERR, "misbalanced quotes\n");
+      return ((char *)NULL);
+    }
+    //    for (; (string[j] != 0) && (!OHANA_WHITESPACE(string[j])); j++);
+    word = strncreate (&string[i], j - i);
+    return (word);
+  } 
+
+  /* a string of characters contained within parentheses is 
+      a single entry.  check that there are matched pairs of
+      parentheses, return string, including exterior parentheses */
+  if (string[i] == '(') { 
+    i++;
+    N = 1;
+    if (string[i] == 0) return ((char *) NULL);
+    for (j = i; (string[j] != 0) && (N > 0); j++) {
+      if (string[j] == '(') {
+	N++;
+      }
+      if (string[j] == ')') {
+	N--;
+      }
+    }
+    if ((string[j] == 0) && (N != 0)) {
+      gprint (GP_ERR, "misbalanced parenthesis\n");
+      return ((char *)NULL);
+    }
+    word = strncreate (&string[i-1], j - i + 1);
+    return (word);
+  } 
+
+
+  for (j = i; (string[j] != 0) && (string[j] != ';') && !OHANA_WHITESPACE(string[j]); j++);
+  word = strncreate (&string[i], j - i);
+  return (word);
+
+}
+
+/* returns a pointer to an isolated string containing the first command,
+   removing leading WHITESPACE.  A command ends with the first non WHITESPACE */
+char *thiscomm (char *string) {
+
+  int i, j;
+  char *word;
+
+  if (string == (char *) NULL) return ((char *) NULL);
+
+  for (i = 0; OHANA_WHITESPACE (string[i]); i++);
+  if (string[i] == 0) return ((char *)NULL);
+
+  for (j = i; ((string[j] != 0) && !OHANA_WHITESPACE (string[j])); j++);
+  if (i == j) return ((char *) NULL);
+
+  word = strncreate (&string[i], j - i);
+  return (word);
+
+}
+
+/* take a pointer to the beginning of a variable (ie $foo)
+and extract only the variable name (eg, foo) */
+
+char *thisvar (char *string) {
+
+  int i, start;
+  char *word;
+
+  if (string == (char *) NULL) return ((char *) NULL);
+  if (string[0] != '$') return ((char *) NULL);
+
+  /* special case $?name : check that name is valid */
+  start = 1;
+  if (string[1] == '?') start = 2;
+
+  for (i = start; ISVAR(string[i]); i++);
+  if (i == start) return ((char *) NULL);
+
+  /* the ? is part of the variable */
+  word = strncreate (&string[1], i - 1);
+  return (word);
+
+}
+
+
+/**********************************************************************/
+/* returns a pointer to the next word, or (char *) NULL if there is not a next word */
+char *nextword (char *string) {
+
+  int i, j;
+
+  if (string == (char *) NULL) return ((char *) NULL);
+
+  for (i = 0; OHANA_WHITESPACE (string[i]); i++);
+  if (string[i] == 0) return ((char *)NULL);
+
+  if (string[i] == '"') { 
+    i++;
+    if (string[i] == 0) return ((char *) NULL);
+    for (; (string[i] != 0) && (string[i] != '"'); i++);
+    if (string[i] == 0) {
+      gprint (GP_ERR, "misbalanced quotes\n");
+      return ((char *)NULL);
+    }
+    i++;
+    for (; (string[i] != 0) && OHANA_WHITESPACE (string[i]); i++);
+    if (string[i] == 0) return ((char *) NULL);
+    return (&string[i]);
+  } 
+
+  if (string[i] == '(') { 
+    i++; 
+    j = 1;
+    if (string[i] == 0) return ((char *) NULL);
+    for (; (string[i] != 0) && (j > 0); i++) {
+      if (string[i] == '(') {
+	j++;
+      }
+      if (string[i] == ')') {
+	j--;
+      }
+    }
+    if ((string[i] == 0) && (j != 0)) {
+      gprint (GP_ERR, "misbalanced parenthesis\n");
+      return ((char *)NULL);
+    }
+    for (; (string[i] != 0) && OHANA_WHITESPACE (string[i]); i++);
+    if (string[i] == 0) return ((char *) NULL);
+    return (&string[i]);
+  } 
+
+  if (string[i] == ';') i++;
+  for (; (string[i] != 0) && (string[i] != ';') && !OHANA_WHITESPACE(string[i]); i++);
+  for (; (string[i] != 0) && OHANA_WHITESPACE (string[i]); i++);
+  if (string[i] == 0) return ((char *) NULL);
+
+  return (&string[i]);
+}
+
+/* returns a pointer to the next command, or (char *) NULL 
+   if there is not a next command.  A command is bounded by WHITESPACE */
+char *nextcomm (char *string) {
+
+  int i;
+
+  if (string == (char *) NULL) return ((char *) NULL);
+  
+  for (i = 0; (string[i] != 0) && !OHANA_WHITESPACE (string[i]); i++);
+  if (string[i] == 0) return ((char *) NULL);
+  
+  for (; OHANA_WHITESPACE (string[i]); i++);
+  if (string[i] == 0) return ((char *) NULL);
+
+  return (&string[i]);
+}
+
+/* returns a pointer to the previous word,
+   or (char *) NULL if there is not a previous word
+*/
+char *lastword (char *string, char *c) {
+
+  if (string == (char *) NULL) return ((char *) NULL);
+  if (c == (char *) NULL) return ((char *) NULL);
+
+  for (; !OHANA_WHITESPACE(*c) && (c >= string); c--);
+  if (c < string) return ((char *)NULL);
+
+  for (; OHANA_WHITESPACE(*c) && (c >= string); c--);
+  if (c < string)
+    return ((char *)NULL);
+  for (; !OHANA_WHITESPACE(*c) && (c >= string); c--);
+  c++;
+  return (c);
+}
+
+
+
+/* take a pointer to the beginning of a variable (ie $fred) and return
+   a pointer to the next thing (non WHITESPACE) which is not part of the
+   variable extract only the variable name */
+
+char *aftervar (char *string) {
+
+  int i, j, start;
+
+  if (string == (char *) NULL) return ((char *) NULL);
+  if (string[0] != '$') return ((char *) NULL);
+
+  /* special case: $?name : test only name */
+  start = 1;
+  if (string[1] == '?') start = 2;
+
+  for (i = start; ISVAR(string[i]); i++);
+  if (i == start) return ((char *) NULL);
+
+  for (j = i; OHANA_WHITESPACE (string[j]); j++);
+  if (string[j] == 0) return ((char *)NULL);
+
+  return (&string[j]);
+
+}
+
+
+/* returns a pointer to the previous var, 
+   or (char *) NULL if there is not a previous word
+*/
+char *lastvar (char *string, char *c) {
+
+  if (string == (char *) NULL) return ((char *) NULL);
+  if (c == (char *) NULL) return ((char *) NULL);
+
+  for (; (c >= string) && OHANA_WHITESPACE(*c); c--);
+  if (c < string) return ((char *)NULL);
+
+  for (; (c >= string) && ISVAR(*c) ; c--);
+  if ((c < string) || (*c != '$')) return ((char *)NULL);
+
+  return (c);
+}
+
+// append string defined by range start - stop to the end of output, which
+// currently has an allocated size of Noutput.  if this operation would overshoot Noutput, 
+// output is reallocated to a sufficiently large size
+char *opihi_append (char *output, int *Noutput, char *start, char *stop) {
+
+  int N1, N2, outlen;
+
+  // a NULL end pointer means 'go to end of line'
+  if (stop == NULL) {
+    stop = start + strlen(start);
+  }
+
+  // enough space?
+  N1 = strlen(output);
+  N2 = stop - start;
+  outlen = N1 + N2;
+  if (outlen >= *Noutput) {
+    *Noutput = outlen + 128;
+    REALLOCATE (output, char, *Noutput);
+    memset (&output[N1], 0, N2 + 128);
+  }
+
+  strncat (output, start, stop - start);
+  return output;
+}
+
+char *opihi_readline (char *prompt) {
+
+# ifdef OHANA_MEMORY
+    char *raw = readline (prompt);
+    char *line = strcreate (raw);
+    real_free (raw);
+    return line;
+# else
+    char *line = readline (prompt);
+    return line;
+# endif
+}
+
+/* replace all instances of \A with A in line */
+void interpolate_slash (char *line) {
+
+  char *in, *out;
+
+  for (in = out = line; *in != 0; in++, out++) {
+    if (*in == 0x5c) in++;
+    *out = *in;
+    if (*in == 0) return;
+  }
+  *out = *in;
+}
+
+// paste together argv[0] .. argv[N] into a single string
+char *paste_args (int argc, char **argv) {
+
+  int i;
+
+  int length = 0;
+  for (i = 0; i < argc; i++) {
+    length += strlen(argv[i]) + 1;
+  }
+  
+  char *string = NULL;
+  ALLOCATE (string, char, length);
+  string[0] = 0;
+  for (i = 0; i < argc; i++) {
+    strcat (string, argv[i]);
+    if (i < argc - 1) strcat (string, " ");
+  }
+  return string;
+}
+
Index: /branches/sc_branches/pantasks_condor/lib.shell/timeformat.c
===================================================================
--- /branches/sc_branches/pantasks_condor/lib.shell/timeformat.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/lib.shell/timeformat.c	(revision 34783)
@@ -0,0 +1,30 @@
+# include "opihi.h"
+
+int GetTimeFormat (time_t *TimeReference, int *TimeFormat) {
+
+  char *p;
+
+  *TimeReference = 0;
+  if ((p = get_variable ("TIMEREF")) != (char *) NULL) {
+    if (!ohana_str_to_time (p, TimeReference)) {
+      gprint (GP_ERR, "error in TIME_REF format\n");
+      return (FALSE);
+    }
+    free (p);
+  }
+  *TimeFormat = FALSE;
+  if ((p = get_variable ("TIMEFORMAT")) != (char *) NULL) {
+    if (!strcasecmp (p, "JD")) *TimeFormat     = TIME_JD;
+    if (!strcasecmp (p, "MJD")) *TimeFormat    = TIME_MJD;
+    if (!strcasecmp (p, "date")) *TimeFormat   = TIME_DATE;
+    if (!strcasecmp (p, "days")) *TimeFormat   = TIME_DAYS;
+    if (!strcasecmp (p, "hours")) *TimeFormat  = TIME_HOURS;
+    if (!strcasecmp (p, "min")) *TimeFormat    = TIME_MINUTES;
+    if (!strcasecmp (p, "sec")) *TimeFormat    = TIME_SECONDS;
+    if (!*TimeFormat) gprint (GP_ERR, "unknown TIME_FORMAT\n");
+    free (p);
+    return (FALSE);
+  }
+  if (!*TimeFormat) *TimeFormat = TIME_SECONDS;
+  return (TRUE);
+}
Index: /branches/sc_branches/pantasks_condor/lib.shell/version.c
===================================================================
--- /branches/sc_branches/pantasks_condor/lib.shell/version.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/lib.shell/version.c	(revision 34783)
@@ -0,0 +1,6 @@
+# include "shell.h"
+static char *name = "$Name: not supported by cvs2svn $";
+
+char *opihi_version () {
+  return (name);
+}
Index: /branches/sc_branches/pantasks_condor/mana/Makefile
===================================================================
--- /branches/sc_branches/pantasks_condor/mana/Makefile	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/mana/Makefile	(revision 34783)
@@ -0,0 +1,49 @@
+default: mana
+
+include ../../../Makefile.System
+HOME    =       $(ROOT)/src/opihi
+SRC     =       $(HOME)/mana
+BIN     =       $(HOME)/bin
+LIB     =       $(HOME)/lib
+INC     =       $(HOME)/include
+DATA    =       $(DESTDATA)/mana
+include ../Makefile.Common
+
+# programs may add their own internal requirements here
+LIBS1   =       -lbasiccmd -ldatacmd -lastrocmd -lshell -ldata 
+LIBS2   =       -ldvo -lkapa -lFITS -lohana
+FULL_CFLAGS   = $(BASE_CFLAGS)
+FULL_CPPFLAGS = $(BASE_CPPFLAGS)
+FULL_LDFLAGS  = $(LIBS1) $(LIBS2) $(BASE_LDFLAGS)
+
+# mana user commands and support functions ########################
+funcs = \
+$(SRC)/init.$(ARCH).o \
+$(SRC)/mana.$(ARCH).o \
+$(SRC)/findrowpeaks.$(ARCH).o 
+
+cmds = \
+$(SRC)/rawstars.$(ARCH).o \
+$(SRC)/fitcontour.$(ARCH).o \
+$(SRC)/starcontour.$(ARCH).o \
+$(SRC)/version.$(ARCH).o \
+$(SRC)/findpeaks.$(ARCH).o 
+
+libs = \
+$(DESTLIB)/libshell.a \
+$(DESTLIB)/libdata.a \
+$(DESTLIB)/libbasiccmd.a \
+$(DESTLIB)/libastrocmd.a \
+$(DESTLIB)/libdatacmd.a
+
+mana: $(BIN)/mana.$(ARCH)
+$(SRC)/mana.$(ARCH).o : $(libs)
+$(BIN)/mana.$(ARCH)   : $(cmds) $(funcs)
+
+install: $(DESTBIN)/mana help modules
+
+help: clean-help cmd.basic.help cmd.data.help cmd.astro.help
+
+modules: mana.modules
+
+.PHONY: mana
Index: /branches/sc_branches/pantasks_condor/mana/adc.c
===================================================================
--- /branches/sc_branches/pantasks_condor/mana/adc.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/mana/adc.c	(revision 34783)
@@ -0,0 +1,117 @@
+# include "dimm.h"
+
+# define DIGITAL_OUT 0x40
+# define ANALOG_IN_INIT 0x00
+# define ANALOG_IN_HIGH 0xa1
+# define ANALOG_IN_LOW  0x91
+
+static char SerialConnected = FALSE;
+static unsigned char DigitalOutState = 0x00;
+static struct timeval reftime; 
+static char reftimeset = FALSE;
+
+# define DTIME(A,B) ((A.tv_sec - B.tv_sec) + 1e-6*(A.tv_usec - B.tv_usec))
+
+int adc (int argc, char **argv) {
+  
+  struct timeval now;
+  int i, N, mode, hi, lo, value;
+  unsigned char setbit, output[5], *input;
+  double dtime;
+
+  if (N = get_argument (argc, argv, "-reset")) {
+    remove_argument (N, &argc, argv);
+    gettimeofday (&reftime, (struct timeval *) NULL);
+    reftimeset = TRUE;
+    return (TRUE);
+  }
+
+  if (argc != 4) {
+    gprint (GP_ERR, "USAGE: adc ai N (variable)\n");
+    gprint (GP_ERR, "USAGE: adc di N (variable)\n");
+    gprint (GP_ERR, "USAGE: adc do N mode\n");
+    return (FALSE);
+  }
+
+  if (!SerialConnected) {
+    if (!SerialInit ('b')) {
+      gprint (GP_ERR, "error opening serial line\n");
+      return (FALSE);
+    }
+    SerialConnected = TRUE;
+  }
+
+  if (!reftimeset) {
+    gettimeofday (&reftime, (struct timeval *) NULL);
+    reftimeset = TRUE;
+  }      
+
+  gettimeofday (&now, (struct timeval *) NULL);
+  dtime = DTIME (now, reftime);
+  set_variable ("TIME", dtime);
+  
+  /* set the digital outputs */
+  if (!strcmp (argv[1], "do")) {
+    N = atof (argv[2]);
+    if ((N < 1) || (N > 6)) {
+      gprint (GP_ERR, "digital output is between 1 and 6\n");
+      return (FALSE);
+    }
+    if (!strcasecmp (argv[3], "on")) {
+      mode = TRUE;
+    } else {
+      mode = FALSE;
+    }
+    setbit = (0x01 << N-1);
+    if (mode) {
+      DigitalOutState |= setbit;
+    } else {
+      DigitalOutState &= ~setbit;
+    }      
+    output[0] = (DIGITAL_OUT | DigitalOutState);
+    output[1] = 0;
+    SerialCommand (output, &input, 50);
+    gprint (GP_ERR, "%x (%x, %x) -> %x (%d, %d)\n", 
+	     output[0], setbit, DigitalOutState, input[0], 
+	     strlen(input), strlen(output));
+    free (input);
+    return (TRUE);
+  }
+  /* read the analog inputs */
+  if (!strcmp (argv[1], "ai")) {
+    N = atof (argv[2]);
+    if ((N < 2) || (N > 16)) {
+      gprint (GP_ERR, "analog input is between 2 and 16\n");
+      gprint (GP_ERR, " (problem with 1 for the moment...)\n");
+      return (FALSE);
+    }
+    /* init A/D converter */
+    output[0] = (ANALOG_IN_INIT | (N-1));
+    output[1] = 0;
+    SerialCommand (output, &input, 50);
+    free (input);
+
+    usleep (10000);
+    /* get high byte */
+    output[0] = ANALOG_IN_HIGH;
+    output[1] = 0;
+    do {
+      SerialCommand (output, &input, 50);
+      hi = input[0];
+      free (input);
+    } while (hi & 0x80);
+
+    output[0] = ANALOG_IN_LOW;
+    output[1] = 0;
+    SerialCommand (output, &input, 50);
+    /* gprint (GP_ERR, "%x -> %x\n", output[0], input[0]); */
+    lo = input[0];
+    free (input);
+    value = ((hi & 0x0f) << 8) | lo;
+    if (hi & 0x10) { value = -value; }
+    set_variable (argv[3], 0.0001*value);
+    return (TRUE);
+  }
+
+
+}
Index: /branches/sc_branches/pantasks_condor/mana/demux.c
===================================================================
--- /branches/sc_branches/pantasks_condor/mana/demux.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/mana/demux.c	(revision 34783)
@@ -0,0 +1,72 @@
+# include "dimm.h"
+
+int demux (int argc, char **argv) {
+  
+  int i, j, k, N;
+  float *out, *in, *inptr, **outptr;
+  int nx, ny, Nx, Ny, NX, NY, Nmux, Nbuf;
+
+  if (argc != 4) {
+    gprint (GP_ERR, "USAGE: demux <buffer> nx ny\n");
+    return (FALSE);
+  }
+
+  if (!SelectBuffer (&Nbuf, argv[1], OLDBUFFER)) return (FALSE);
+
+  nx = atof (argv[2]);
+  ny = atof (argv[3]);
+  Nmux = nx*ny;
+
+  Nx = buffers[Nbuf].matrix.Naxis[0];
+  Ny = buffers[Nbuf].matrix.Naxis[1];
+
+  NX = Nx / nx;
+  NY = Ny / ny;
+
+  inptr = in = (float *) buffers[Nbuf].matrix.buffer;  /* don't lose reference */
+
+  ALLOCATE (out, float, Nx*Ny);
+  ALLOCATE (outptr, float *, Nmux);
+  
+  for (N = i = 0; i < nx; i++) {
+    for (j = 0; j < ny; j++, N++) {
+      outptr[N] = &out[i*NX + j*NX*NY*nx];
+    }
+  }
+
+  for (N = j = 0; j < NY; j++) {
+    for (i = 0; i < NX; i++) {
+      for (k = 0; k < Nmux; k++) {
+	*outptr[k] = *inptr;
+	outptr[k] ++;
+	inptr ++;
+      }
+    }
+    for (k = 0; k < Nmux; k++) outptr[k] += NX;
+  }
+
+  /*
+  for (X = Y = x = y = i = 0; i < NX*NY; i++) {
+    out[X + Y*NX + x*NX*(Y+1) + y*NX*NY*nx] = *in;
+    X++;
+    if (X == NX) {
+      X = 0;
+      Y++;
+    }
+    if (Y == NY) {
+      Y = 0;
+      x++;
+    }
+    if (x == nx) {
+      x = 0;
+      y++;
+    }
+  }
+  */
+
+  free (in);
+  buffers[Nbuf].matrix.buffer = (char *) out;
+
+  return (TRUE);
+}
+
Index: /branches/sc_branches/pantasks_condor/mana/findpeaks.c
===================================================================
--- /branches/sc_branches/pantasks_condor/mana/findpeaks.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/mana/findpeaks.c	(revision 34783)
@@ -0,0 +1,107 @@
+# include "mana.h"
+
+int findpeaks (int argc, char **argv) {
+  
+  int i, j, n, N, Nx, Ny;
+  int xo, yo;
+  int Npeak, NPEAK, Npeaks;
+  float *v;
+  int *peaks, *keep, *xp, *yp, *zp;
+  float threshold, vt, vo;
+  Vector *vecx, *vecy, *vecz;
+  Buffer *buf;
+
+  if (argc < 3) goto usage;
+
+  if ((buf = SelectBuffer (argv[1], OLDBUFFER, TRUE)) == NULL) return (FALSE);
+  threshold = atof (argv[2]);
+
+  if ((vecx = SelectVector ("xp", ANYVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((vecy = SelectVector ("yp", ANYVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((vecz = SelectVector ("zp", ANYVECTOR, TRUE)) == NULL) return (FALSE);
+
+  Nx = buf[0].matrix.Naxis[0];
+  Ny = buf[0].matrix.Naxis[1];
+
+  Npeak = 0;
+  NPEAK = Nx;
+  ALLOCATE (xp, int, NPEAK);
+  ALLOCATE (yp, int, NPEAK);
+  ALLOCATE (zp, int, NPEAK);
+
+  /* find peaks for each row */
+  v = (float *) buf[0].matrix.buffer;
+  for (j = 0; j < Ny; j++) {
+    peaks = findrowpeaks (&v[j*Nx], Nx, threshold, &Npeaks);
+
+    if (Npeak + Npeaks >= NPEAK) {
+      NPEAK = Npeak + Npeaks + Nx;
+      REALLOCATE (xp, int, NPEAK);
+      REALLOCATE (yp, int, NPEAK);
+      REALLOCATE (zp, int, NPEAK);
+    }
+    for (i = 0; i < Npeaks; i++) {
+      xp[Npeak + i] = peaks[i];
+      yp[Npeak + i] = j;
+      zp[Npeak + i] = v[peaks[i] + j*Nx];
+    }
+    Npeak += Npeaks;
+    free (peaks);
+  }
+  
+  /* identify non-local peaks */
+  ALLOCATE (keep, int, MAX (Npeak, 1));
+  v = (float *) buf[0].matrix.buffer;
+  for (n = 0; n < Npeak; n++) {
+    xo = xp[n];
+    yo = yp[n];
+    vo = v[xo + yo*Nx];
+    keep[n] = TRUE;
+    for (i = xo - 1; i <= xo + 1; i++) {
+      if (i < 0) continue;
+      if (i >= Nx) continue;
+      for (j = yo - 1; j <= yo + 1; j++) {
+	if ((i == xo) && (j == yo)) continue;
+	if (j < 0) continue;
+	if (j >= Ny) continue;
+	vt = v[i + j*Nx];
+	if (vt > vo) {
+	  keep[n] = FALSE;
+	  goto next_peak;
+	}
+      }
+    }
+  next_peak:
+    continue;
+  }
+
+  ResetVector (vecx, OPIHI_FLT, Npeak);
+  ResetVector (vecy, OPIHI_FLT, Npeak);
+  ResetVector (vecz, OPIHI_FLT, Npeak);
+
+  /* eliminate non-local peaks */
+  for (N = n = 0; n < Npeak; n++) {
+    if (!keep[n]) continue;
+    vecx[0].elements.Flt[N] = xp[n];
+    vecy[0].elements.Flt[N] = yp[n];
+    vecz[0].elements.Flt[N] = zp[n];
+    N ++;
+  }
+  free (xp);
+  free (yp);
+  free (zp);
+  free (keep);
+
+  REALLOCATE (vecx[0].elements.Flt, opihi_flt, MAX (N, 1));
+  REALLOCATE (vecy[0].elements.Flt, opihi_flt, MAX (N, 1));
+  REALLOCATE (vecz[0].elements.Flt, opihi_flt, MAX (N, 1));
+  vecx[0].Nelements = vecy[0].Nelements = vecz[0].Nelements = N;
+
+  return (TRUE);
+
+ usage:
+  gprint (GP_ERR, "findpeaks (buffer) (threshold)\n");
+  return (FALSE);
+}
+
+
Index: /branches/sc_branches/pantasks_condor/mana/findrowpeaks.c
===================================================================
--- /branches/sc_branches/pantasks_condor/mana/findrowpeaks.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/mana/findrowpeaks.c	(revision 34783)
@@ -0,0 +1,45 @@
+# include "mana.h"
+
+int *findrowpeaks (float *row, int Nrow, float threshold, int *npeaks) {
+  
+  int i;
+  int Npeaks, NPEAKS;
+  int *peaks;
+
+  Npeaks = 0;
+  NPEAKS = 100;
+  ALLOCATE (peaks, int, NPEAKS);
+
+  if (Nrow < 3) {
+    *npeaks = Npeaks;
+    return (peaks);
+  }
+    
+  /* special case for first pixel in row */
+  if ((row[0] > row[1]) && (row[0] > threshold)) {
+    peaks[Npeaks] = 0;
+    Npeaks ++;
+  }    
+
+  for (i = 1; i < Nrow - 1; i++) {
+    if (row[i] < threshold) continue;
+    if (row[i] < row[i-1]) continue;
+    if (row[i] <= row[i+1]) continue;
+
+    peaks[Npeaks] = i;
+    Npeaks ++;
+    if (Npeaks >= NPEAKS) {
+      NPEAKS += 100;
+      REALLOCATE (peaks, int, NPEAKS);
+    }
+  }      
+
+  /* special case for last pixel in row */
+  if ((row[Nrow-1] >= row[Nrow-2]) && (row[Nrow-1] > threshold)) {
+    peaks[Npeaks] = Nrow-1;
+    Npeaks ++;
+  }    
+
+  *npeaks = Npeaks;
+  return (peaks);
+}
Index: /branches/sc_branches/pantasks_condor/mana/fitcontour.c
===================================================================
--- /branches/sc_branches/pantasks_condor/mana/fitcontour.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/mana/fitcontour.c	(revision 34783)
@@ -0,0 +1,143 @@
+# include "mana.h"
+# define NTERM 3
+
+int fitcontour (int argc, char **argv) {
+  
+  int i;
+  double **C, **B;
+  float cs1, sn1, cs, sn, x, y, r, xo, yo;
+  float dR, Rmin, Rmaj, Theta, Rx, Ry, Rxy, R1, R2, R3;
+  Vector *vecx, *vecy;
+
+  /* USAGE fitcontour x y Xo Yo */
+  if (argc < 5) goto usage;
+
+  if ((vecx = SelectVector (argv[1], ANYVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((vecy = SelectVector (argv[2], ANYVECTOR, TRUE)) == NULL) return (FALSE);
+  xo = atof (argv[3]);
+  yo = atof (argv[4]);
+
+  // require these to be float vectors
+  REQUIRE_VECTOR_FLT (vecx, FALSE);
+  REQUIRE_VECTOR_FLT (vecy, FALSE);
+
+  ALLOCATE (B, double *, NTERM);
+  ALLOCATE (C, double *, NTERM);
+  for (i = 0; i < NTERM; i++) {
+    ALLOCATE (C[i], double, NTERM);
+    bzero (C[i], NTERM*sizeof(double));
+    ALLOCATE (B[i], double, 1);
+    bzero (B[i], sizeof(double));
+  }
+
+  /* we are fitting r = ro + rs*sin(2theta) + rc*cos(2theta) */
+  /* sin(2t) = 2cos(t)sin(t)
+     cos(2t) = cos^2(t) - sin^2(t)
+  */
+  for (i = 0; i < vecx[0].Nelements; i++) {
+    x = vecx[0].elements.Flt[i] - xo;
+    y = vecy[0].elements.Flt[i] - yo;
+    r = hypot (x, y);
+
+    /* calculate sin(2t), cos(2t) using 1/2 angle tri relationship above */
+    sn1 = y / r;
+    cs1 = x / r;
+    sn = 2*sn1*cs1;
+    cs = cs1*cs1 - sn1*sn1;
+
+    C[0][0] += 1.0;
+    C[1][0] += sn;
+    C[1][1] += SQ(sn);
+    C[2][0] += cs; 
+    C[2][1] += cs*sn; 
+    C[2][2] += SQ(cs);
+
+    B[0][0] += r;
+    B[1][0] += r*sn;
+    B[2][0] += r*cs; 
+  }
+  C[0][1] = C[1][0];
+  C[0][2] = C[2][0];
+  C[1][2] = C[2][1];
+    
+  dgaussjordan (C, B, NTERM, 1);
+  
+  /** this is somewhat weak: if the object is too elongated, Rmin can be < 0 **/
+  dR = hypot (B[1][0], B[2][0]);
+  Rmaj = B[0][0] + dR;
+  Rmin = B[0][0] - dR;
+  Theta = DEG_RAD*atan2 (B[1][0], B[2][0]) / 2;
+
+  sn = B[1][0] / dR;
+  cs = B[2][0] / dR;
+
+  R1 = SQ(Rmaj) + SQ(Rmin);
+  R2 = SQ(Rmaj) - SQ(Rmin);
+  R3 = Rmaj*Rmin;
+
+  Rx = R3 / sqrt (R1 - R2*cs);
+  Ry = R3 / sqrt (R1 + R2*cs);
+  Rxy = -sn*R2 / SQ(R3);
+
+  set_variable ("Rx", Rx);
+  set_variable ("Ry", Ry);
+  set_variable ("Rxy", Rxy);
+
+  set_variable ("Rmin", Rmin);
+  set_variable ("Rmaj", Rmaj);
+  set_variable ("Theta", Theta);
+
+  for (i = 0; i < NTERM; i++) {
+    free (B[i]);
+    free (C[i]);
+  }
+  free (B);
+  free (C);
+
+  return (TRUE);
+
+ usage:
+  gprint (GP_ERR, "fitcontour x y (xo) (yo)\n");
+  return (FALSE);
+}
+
+
+/* this routine fits a single sine and cosine to the value of dr as a function of the angle around the central
+ * point, theta.  this is NOT an exact fit, an is increasingly in error for more eccentric ellipses.  however,
+ * it does a reasonable job of approximating the value of the major and minor axes, and it gets the angle of
+ * orientation right at well.  We then assume the values of major and minor axis and angle are correct to get
+ * the parameters for the elliptical gaussian fit:
+ 
+ z = (x^2) / (2 Rx^2) + (y^2) / (2 Ry^2) + Rxy x y
+
+ the functions above give the correct relationship between these:
+
+   R1 = SQ(Rmaj) + SQ(Rmin);
+   R2 = SQ(Rmaj) - SQ(Rmin);
+   R3 = Rmaj*Rmin;
+
+   Rx = R3 / sqrt (R1 - R2*cs);
+   Ry = R3 / sqrt (R1 + R2*cs);
+   Rxy = -sn*R2 / SQ(R3);
+
+ to derive these relationships, write the equation for an ellipse with major axis in the x-dir and minor in
+ the y-dir:
+
+ z = (x^2) / (2 Rmaj^2) + (y^2) / (2 Rmin^2)
+
+ apply the rotation matrix:
+
+ (x)' = (cos, -sin)(x,y)
+ (y)' = (sin, +cos)(x,y)  (modulo the sign on the sine)
+
+ then group the x^2 terms, the y^2 terms, and the xy terms to find the three coeffs.
+
+ NOTE: I spent a while having trouble deriving this from the polar form of an ellipse:
+
+ x = Rmaj*cos(theta)
+ y = Rmin*sin(theta)
+
+ it turns out that 'theta' above is NOT the angle (0,1)-(0,0)-(x,y). rather, it should be viewed as a
+ paraterization of the ellipse.  
+
+*/
Index: /branches/sc_branches/pantasks_condor/mana/focus.c
===================================================================
--- /branches/sc_branches/pantasks_condor/mana/focus.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/mana/focus.c	(revision 34783)
@@ -0,0 +1,68 @@
+# include "dimm.h"
+
+int focus (int argc, char **argv) {
+  
+  if (argc < 2) goto usage;
+
+  if (!strcasecmp (argv[1], "init")) {
+    if (argc != 3) {
+      gprint (GP_ERR, "USAGE: focus init (port)\n");
+      return (FALSE);
+    }
+    if (!SerialInit (argv[2])) return (FALSE);
+    gprint (GP_ERR, "focus on port %s\n", argv[2]);
+    return (TRUE);
+  }
+
+  if (!strcasecmp (argv[1], "pos")) {
+
+    int status, servo, angle;
+    char *answer = (char *) NULL;
+    char line[64];
+
+    if (argc != 4) {
+      gprint (GP_ERR, "USAGE: focus pos Nservo (angle)\n");
+      return (FALSE);
+    }
+
+    servo = atoi (argv[2]);
+    angle = atoi (argv[3]);
+
+    sprintf (line, "%c%c%c\n", 0xff, servo, angle);
+    status = SerialCommand (line, &answer, 10);
+    gprint (GP_ERR, "status: %d\n", status);
+    if (answer != (char *) NULL) {
+      gprint (GP_ERR, "answer: ..%s..\n", answer);
+    }
+    return (TRUE);
+  }
+
+  if (!strcasecmp (argv[1], "raw")) {
+
+    int status, value;
+    char *answer = (char *) NULL;
+    char line[64];
+
+    if (argc != 3) {
+      gprint (GP_ERR, "USAGE: focus raw value\n");
+      return (FALSE);
+    }
+
+    value = atoi (argv[2]);
+
+    sprintf (line, "%c", value);
+    status = SerialCommand (line, &answer, 10);
+    gprint (GP_ERR, "status: %d\n", status);
+    if (answer != (char *) NULL) {
+      gprint (GP_ERR, "answer: ..%s..\n", answer);
+    }
+    return (TRUE);
+  }
+
+ usage:
+  gprint (GP_ERR, "focus init port\n");
+  gprint (GP_ERR, "focus pos (string)\n");
+  gprint (GP_ERR, "focus raw (value)\n");
+  return (FALSE);
+}
+
Index: /branches/sc_branches/pantasks_condor/mana/init.c
===================================================================
--- /branches/sc_branches/pantasks_condor/mana/init.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/mana/init.c	(revision 34783)
@@ -0,0 +1,28 @@
+# include "opihi.h"
+
+int findpeaks	    PROTO((int, char **));
+int fitcontour	    PROTO((int, char **));
+int starcontour	    PROTO((int, char **));
+int rawstars	    PROTO((int, char **));
+int version	    PROTO((int, char **));
+
+static Command cmds[] = {  
+  {1, "findpeaks",   findpeaks,    "find image peaks"},
+  {1, "fitcontour",  fitcontour,   "fit ellipse contour"},
+  {1, "starcontour", starcontour,  "object contour"},
+  {1, "rawstars",    rawstars,     "find raw star stats"},
+  {1, "version",     version,      "show version information"},
+}; 
+
+void InitMana () {
+  
+  int i;
+
+  for (i = 0; i < sizeof (cmds) / sizeof (Command); i++) {
+    AddCommand (&cmds[i]);
+  }
+
+}
+
+void FreeMana () {
+}
Index: /branches/sc_branches/pantasks_condor/mana/mana.c.in
===================================================================
--- /branches/sc_branches/pantasks_condor/mana/mana.c.in	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/mana/mana.c.in	(revision 34783)
@@ -0,0 +1,69 @@
+# include "mana.h"
+
+# define opihi_name "MANA"
+# define opihi_prompt "mana: "
+# define opihi_description "an image manipulation tool\n"
+# define opihi_history ".mana"
+# define opihi_rcfile ".manarc"
+
+/* program-dependent initialization */
+void program_init (int *argc, char **argv) {
+  
+  auto_break = TRUE;
+
+  /* load the commands used by this implementation */
+  InitBasic ();
+  InitData ();
+  InitAstro ();
+  InitMana ();
+
+  rl_readline_name = opihi_name;
+  rl_attempted_completion_function = command_completer;
+
+  set_str_variable ("HISTORY", opihi_history);
+  set_str_variable ("PROMPT", opihi_prompt);
+  set_str_variable ("RCFILE", opihi_rcfile);
+
+  {
+    char *helpdir;
+    char *modules;
+    static char *datadir = "@DATADIR@";
+    ALLOCATE (helpdir, char, strlen(datadir) + strlen("/help") + 2);
+    sprintf (helpdir, "%s/help", datadir);
+    set_str_variable ("HELPDIR", helpdir);
+    free (helpdir);
+    ALLOCATE (modules, char, strlen(datadir) + strlen("/modules") + 2);
+    sprintf (modules, "%s/modules", datadir);
+    set_str_variable ("MODULES:0", modules);
+    set_int_variable ("MODULES:n", 1);
+    free (modules);
+  }
+
+  return;
+}
+
+/* standard welcome message */
+void welcome () {
+  gprint (GP_ERR, "\n");
+  gprint (GP_ERR, "Welcome to %s - %s\n\n", opihi_name, opihi_description);
+}
+
+/* add program-dependent exit functions here */
+void cleanup () {
+  QuitKapa ();
+  ConfigFree ();
+  
+  FreeBasic ();
+  FreeData ();
+  FreeAstro ();
+  FreeMana ();
+
+  return;
+}
+
+/* call to opihi shell */
+int main (int argc, char **argv) {
+  int status;
+  status = opihi (argc, argv);
+  exit (status);
+}
Index: /branches/sc_branches/pantasks_condor/mana/rawstars.c
===================================================================
--- /branches/sc_branches/pantasks_condor/mana/rawstars.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/mana/rawstars.c	(revision 34783)
@@ -0,0 +1,92 @@
+# include "mana.h"
+
+int rawstars (int argc, char **argv) {
+  
+  int i, x, y, N, Nx, Ny, Np;
+  float *v;
+  double Raper, Rinner, Router;
+  Vector *xp, *yp;
+  Vector *xc, *yc, *sx, *sy, *sxy, *zs, *zc, *sk;
+  Buffer *buff;
+
+  Raper = 5;
+  if ((N = get_argument (argc, argv, "-Raper"))) {
+    remove_argument (N, &argc, argv);
+    Raper = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+  Rinner = 10;
+  if ((N = get_argument (argc, argv, "-Rinner"))) {
+    remove_argument (N, &argc, argv);
+    Rinner = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+  Router = 15;
+  if ((N = get_argument (argc, argv, "-Router"))) {
+    remove_argument (N, &argc, argv);
+    Router = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  if (argc < 4) goto usage;
+
+  if ((buff = SelectBuffer (argv[1], OLDBUFFER, TRUE)) == NULL) return (FALSE);
+  if ((xp = SelectVector (argv[2], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((yp = SelectVector (argv[3], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+  if (xp[0].Nelements != yp[0].Nelements) {
+    gprint (GP_ERR, "vectors are not the same length\n");
+    return (FALSE);
+  }
+
+  set_rough_radii (Raper, Rinner, Router);
+
+  /* output vectors */
+  if ((xc = SelectVector ("xc", ANYVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((yc = SelectVector ("yc", ANYVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((zc = SelectVector ("zc", ANYVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((zs = SelectVector ("zs", ANYVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((sk = SelectVector ("sk", ANYVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((sx = SelectVector ("sx", ANYVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((sy = SelectVector ("sy", ANYVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((sxy = SelectVector ("sxy", ANYVECTOR, TRUE)) == NULL) return (FALSE);
+
+  Nx = buff[0].matrix.Naxis[0];
+  Ny = buff[0].matrix.Naxis[1];
+  Np = xp[0].Nelements;
+
+  ResetVector (xc, OPIHI_FLT, Np);
+  ResetVector (yc, OPIHI_FLT, Np);
+  ResetVector (sx, OPIHI_FLT, Np);
+  ResetVector (sy, OPIHI_FLT, Np);
+  ResetVector (sxy, OPIHI_FLT, Np);
+  ResetVector (zs, OPIHI_FLT, Np);
+  ResetVector (zc, OPIHI_FLT, Np);
+  ResetVector (sk, OPIHI_FLT, Np);
+
+  v = (float *) buff[0].matrix.buffer;
+  for (i = 0; i < Np; i++) {
+    x = (xp[0].type == OPIHI_FLT) ? xp[0].elements.Flt[i] : xp[0].elements.Int[i];
+    y = (yp[0].type == OPIHI_FLT) ? yp[0].elements.Flt[i] : yp[0].elements.Int[i];
+    if (x < 0) continue;
+    if (x >= Nx) continue;
+    if (y < 0) continue;
+    if (y >= Ny) continue;
+
+    get_rough_star (v, Nx, Ny, x, y, 
+		    &xc[0].elements.Flt[i], 
+		    &yc[0].elements.Flt[i], 
+		    &sx[0].elements.Flt[i], 
+		    &sy[0].elements.Flt[i], 
+		    &sxy[0].elements.Flt[i], 
+		    &zs[0].elements.Flt[i], 
+		    &zc[0].elements.Flt[i],
+		    &sk[0].elements.Flt[i]);
+  }
+
+  return (TRUE);
+
+ usage:
+  gprint (GP_ERR, "rawstars (buffer) (xp) (yp)\n");
+  return (FALSE);
+}
+
Index: /branches/sc_branches/pantasks_condor/mana/simsignal.c
===================================================================
--- /branches/sc_branches/pantasks_condor/mana/simsignal.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/mana/simsignal.c	(revision 34783)
@@ -0,0 +1,50 @@
+# include "dimm.h"
+
+int simsignal (int argc, char **argv) {
+  
+  int Nvect, Nbin, Nbit, i, ivalue, jvalue, Nshift, mask, scale;
+  float *buf;
+  double cvalue, dvalue, sigma, SN, period;
+  double rnd_gauss ();
+
+  if (argc != 5) {
+    gprint (GP_ERR, "USAGE: simsignal (vector) Nbin Nbits period\n");
+    return (FALSE);
+  }
+
+  if (!SelectVector (&Nvect, argv[1], ANYVECTOR)) return (FALSE);
+  Nbin = atof (argv[2]);
+  Nbit = atof (argv[3]);
+  /* SN = atof (argv[4]); */
+  period = atof (argv[4]);
+
+  vectors[Nvect].Nelements = Nbin;
+  REALLOCATE (vectors[Nvect].elements, float, vectors[Nvect].Nelements);
+
+  scale = (0x1 << Nbit) - 1;
+
+  buf = vectors[Nvect].elements;
+  for (i = 0; i < Nbin; i++, buf++) {
+    ivalue = scale * 0.5 * (sin (i*2*M_PI/period) + 1) + 0.5;
+    /*
+    dvalue = rnd_gauss (cvalue, sigma);
+    cvalue = (dvalue + range) / (2.0*range);
+    dvalue = MAX (0, MIN (0.99999, cvalue));
+    ivalue = scale * dvalue;
+    *buf = (2.0 * range * ((double) ivalue + 0.5)) / scale - range; 
+    */
+    *buf = ivalue;
+  }
+
+  return (TRUE);
+}
+
+/* 
+
+8 bit = 2^8
+
+  gauss_init (2*scale);
+  sigma = 2.0 / SN;
+  range = 1 + 5*sigma;
+
+*/
Index: /branches/sc_branches/pantasks_condor/mana/starcontour.c
===================================================================
--- /branches/sc_branches/pantasks_condor/mana/starcontour.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/mana/starcontour.c	(revision 34783)
@@ -0,0 +1,130 @@
+# include "mana.h"
+
+int starcontour (int argc, char **argv) {
+  
+  int x, y, xs, xp, yp;
+  int Nx, Ny, N, Npts, min, max;
+  float *v;
+  float zt, zo, xmin, xmax;
+  Vector *vecx, *vecy;
+  Buffer *buf;
+
+  if (argc < 5) goto usage;
+
+  if ((buf = SelectBuffer (argv[1], OLDBUFFER, TRUE)) == NULL) return (FALSE);
+  xp = atof (argv[2]);
+  yp = atof (argv[3]);
+  zo = atof (argv[4]);
+
+  if ((vecx = SelectVector ("xo", ANYVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((vecy = SelectVector ("yo", ANYVECTOR, TRUE)) == NULL) return (FALSE);
+
+  N = 0;
+  Npts = 100;
+  ResetVector (vecx, OPIHI_FLT, Npts);
+  ResetVector (vecy, OPIHI_FLT, Npts);
+
+  Nx = buf[0].matrix.Naxis[0];
+  Ny = buf[0].matrix.Naxis[1];
+  v = (float *)buf[0].matrix.buffer;
+
+  /* find transition below (limit range?) */
+  xmin = xmax = 0;
+  xs = xp;
+  for (y = yp; (y >= 0) && (v[xs + y*Nx] > zo); y--) {
+    /* find transition below (limit range?) */
+    min = max = FALSE;
+    for (x = xs; (x >= 0) && !min; x--) {
+      min = FALSE;
+      zt = v[x + y*Nx];
+      if (zt < zo) {
+	min = TRUE;
+	xmin = x + (zo - zt)/(v[x + 1 + y*Nx] - zt);
+	vecx[0].elements.Flt[N] = xmin;
+	vecy[0].elements.Flt[N] = y;
+	N ++;
+	if (N >= Npts) {
+	  Npts += 100;
+	  REALLOCATE (vecx[0].elements.Flt, opihi_flt, MAX (Npts, 1));
+	  REALLOCATE (vecy[0].elements.Flt, opihi_flt, MAX (Npts, 1));
+	}
+      }
+      /* ignore edge cases? */
+    }
+    /* find transition above (limit range?) */
+    for (x = xs; (x < Nx) && !max; x++) {
+      max = FALSE;
+      zt = v[x + y*Nx];
+      if (zt < zo) {
+	max = TRUE;
+	xmax = x - (zo - zt)/(v[x - 1 + y*Nx] - zt);
+	vecx[0].elements.Flt[N] = xmax;
+	vecy[0].elements.Flt[N] = y;
+	N ++;
+	if (N >= Npts) {
+	  Npts += 100;
+	  REALLOCATE (vecx[0].elements.Flt, opihi_flt, MAX (Npts, 1));
+	  REALLOCATE (vecy[0].elements.Flt, opihi_flt, MAX (Npts, 1));
+	}
+      }
+      /* ignore edge cases? */
+    }
+    if (min && max) {
+      xs = 0.5*(xmin + xmax);
+    }
+  }
+
+  /* find transition above (limit range?) */
+  xs = xp;
+  for (y = yp; (y < Ny) && (v[xs + y*Nx] > zo); y++) {
+    /* find transition below (limit range?) */
+    min = max = FALSE;
+    for (x = xs; (x >= 0) && !min; x--) {
+      min = FALSE;
+      zt = v[x + y*Nx];
+      if (zt < zo) {
+	min = TRUE;
+	xmin = x + (zo - zt)/(v[x + 1 + y*Nx] - zt);
+	vecx[0].elements.Flt[N] = xmin;
+	vecy[0].elements.Flt[N] = y;
+	N ++;
+	if (N >= Npts) {
+	  Npts += 100;
+	  REALLOCATE (vecx[0].elements.Flt, opihi_flt, MAX (Npts, 1));
+	  REALLOCATE (vecy[0].elements.Flt, opihi_flt, MAX (Npts, 1));
+	}
+      }
+      /* ignore edge cases? */
+    }
+    /* find transition above (limit range?) */
+    for (x = xs; (x < Nx) && !max; x++) {
+      max = FALSE;
+      zt = v[x + y*Nx];
+      if (zt < zo) {
+	max = TRUE;
+	xmax = x - (zo - zt)/(v[x - 1 + y*Nx] - zt);
+	vecx[0].elements.Flt[N] = xmax;
+	vecy[0].elements.Flt[N] = y;
+	N ++;
+	if (N >= Npts) {
+	  Npts += 100;
+	  REALLOCATE (vecx[0].elements.Flt, opihi_flt, MAX (Npts, 1));
+	  REALLOCATE (vecy[0].elements.Flt, opihi_flt, MAX (Npts, 1));
+	}
+      }
+      /* ignore edge cases? */
+    }
+    if (min && max) {
+      xs = 0.5*(xmin + xmax);
+    }
+  }
+  vecx[0].Nelements = N;
+  vecy[0].Nelements = N;
+
+  return (TRUE);
+
+ usage:
+  gprint (GP_ERR, "starcontour (buffer) (xpeak) (ypeak) (level)\n");
+  return (FALSE);
+}
+
Index: /branches/sc_branches/pantasks_condor/mana/tests.c
===================================================================
--- /branches/sc_branches/pantasks_condor/mana/tests.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/mana/tests.c	(revision 34783)
@@ -0,0 +1,108 @@
+# include "opihi.h"
+
+run_tests () {
+
+  /* test1 ();
+     test1b ();
+     test2 ();
+     test3 (); */
+  test4 (); 
+  test5 ();
+}
+
+test1 () {
+
+  int i, size;
+  char *test, *line;
+
+  gprint (GP_ERR, "starting test 1...  ");
+  ALLOCATE (test, char, 256);
+  sprintf (test, "100");
+  for (i = 0; i < 1000000; i++) {
+    line = dvomath (1, &test, &size, 0);
+    if (line != NULL) free (line);
+  }
+  gprint (GP_ERR, "done with test 1\n");
+  sleep (1);
+}
+
+test1b () {
+
+  int i, size;
+  char *test, *line;
+
+  gprint (GP_ERR, "starting test 1b...  ");
+  ALLOCATE (test, char, 256);
+  sprintf (test, "dsin(45)");
+  for (i = 0; i < 1000000; i++) {
+    line = dvomath (1, &test, &size, 0);
+    if (line != NULL) free (line);
+  }
+  gprint (GP_ERR, "done with test 1b\n");
+  sleep (1);
+}
+
+test2 () {
+
+  int i, size;
+  char *test, *line;
+
+  gprint (GP_ERR, "starting test 2...  ");
+  ALLOCATE (test, char, 256);
+  sprintf (test, "5 * 100");
+  for (i = 0; i < 1000000; i++) {
+    line = dvomath (1, &test, &size, 0);
+    if (line != NULL) free (line);
+  }
+  gprint (GP_ERR, "done with test 2\n");
+  sleep (1);
+}
+
+test3 () {
+
+  int i, size;
+  char *test, *line;
+
+  gprint (GP_ERR, "starting test 3...  ");
+  ALLOCATE (test, char, 256);
+  sprintf (test, "5 * dsin(45)");
+  for (i = 0; i < 1000000; i++) {
+    line = dvomath (1, &test, &size, 0);
+    if (line != NULL) free (line);
+  }
+  gprint (GP_ERR, "done with test 3\n");
+  sleep (1);
+}
+
+test4 () {
+
+  int i;
+  char *test, *line;
+
+  gprint (GP_ERR, "starting test 4...  ");
+  for (i = 0; i < 1000000; i++) {
+    ALLOCATE (test, char, 256);
+    sprintf (test, "$N = 100");
+    line = parse (test);
+    if (line != NULL) free (line);
+  }
+  gprint (GP_ERR, "done with test 4\n");
+  sleep (1);
+}
+
+test5 () {
+
+  int i;
+  char *test, *line;
+
+  gprint (GP_ERR, "starting test 5...  ");
+  for (i = 0; i < 1000000; i++) {
+    ALLOCATE (test, char, 256);
+    sprintf (test, "echo {100 * 800}");
+    line = parse (test);
+    if (line != NULL) free (line);
+  }
+  gprint (GP_ERR, "done with test 5\n");
+  sleep (1);
+}
+
Index: /branches/sc_branches/pantasks_condor/mana/version.c
===================================================================
--- /branches/sc_branches/pantasks_condor/mana/version.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/mana/version.c	(revision 34783)
@@ -0,0 +1,17 @@
+# include "mana.h"
+static char *name = "$Name: not supported by cvs2svn $";
+
+int version (int argc, char **argv) {
+
+  char *tmp;
+
+  gprint (GP_LOG, "\n");
+  gprint (GP_LOG, "mana version: %s\n", (tmp = strip_version (name))); free (tmp);
+
+  gprint (GP_LOG, "opihi version: %s\n", (tmp = strip_version (opihi_version()))); free (tmp);
+  gprint (GP_LOG, "libohana version: %s\n", (tmp = strip_version (ohana_version()))); free (tmp);
+  gprint (GP_LOG, "libfits version: %s\n", (tmp = strip_version (gfits_version()))); free (tmp);
+
+  gprint (GP_LOG, "compiled on %s %s\n", __DATE__, __TIME__);
+  return (TRUE);
+}
Index: /branches/sc_branches/pantasks_condor/modules/dvo/navigate
===================================================================
--- /branches/sc_branches/pantasks_condor/modules/dvo/navigate	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/modules/dvo/navigate	(revision 34783)
@@ -0,0 +1,623 @@
+# -*- perl -*-
+
+macro navigate
+  style -n 0
+  limits
+  $DRAWSTARS  = -1
+  $DRAWIMAGES =  1
+  $DRAWGRID   = -1
+  $ZOOM = 180 / ($YMAX - $YMIN)
+  #this should be changed to a while loop, except 'while' is broken for some reason
+  $KEY = "none"
+  while ("$KEY" != "q")
+    cursor -g 1
+
+    # help list
+    if ("$KEY" == "h")
+     echo "Arrow Keys - pan in that direction"
+     echo "PgUp,PgDn - zoom in/out a factor of 1.2"
+     echo "Home,End  - zoom in/out a factor of 2"
+     echo "1 - zoom in factor of 2 at the cursor"
+     echo "2 - zoom in factor of 1.2 at the cursor"
+     echo "3 - recenter at cursor"
+     echo "4 - zoom out factor of 1.2 at the cursor"
+     echo "5 - zoom out factor of 2 at the cursor"
+     echo "6 - zoom out factor of 10 at the cursor"
+     echo "z - zoom to radius (requires 2nd keystroke)"
+     echo "f - show full sky"
+     echo ""
+     echo "q - quit"
+     echo "S - toggle auto-plotting of stars"
+     echo "A - toggle auto-plotting of image borders"
+     echo "g - toggle skygrid on/off"
+     echo "c - plot status catalog boundaries"
+     echo "C - list catalog at cursor location"
+     echo "i - list info about images touching cursor location" 
+     echo "I - list info about images, with pixel coords of cursor position"
+     echo "j - adjust mag scale, +0.5"
+     echo "k - adjust mag scale, -0.5"
+     echo "J - adjust dmag scale, /1.25"
+     echo "K - adjust dmag scale, *1.25"
+     echo "r - plot detected asteroids (rocks)"
+     echo "l - plot HST GSC"
+     echo "L - plot Landolt stars"
+     echo "m - list measurements for stars within 1 pixel of cursor"
+     echo "M - list measurements for stars within 1.8 arcsec of cursor"
+     echo "p - ****(don't know what this does)"
+     echo "s - ****(don't know what this does)"
+     echo "t - plot light curve for star within 2 arcsec of cursor position"
+     echo "T - plot 'galaxy' light curve for star within 2 arcsec of cursor position"
+     echo "u - ****(don't know what this does)"
+     echo "x - plot stars scaled by magnitude Chisq"
+     echo "X - plot stars by magnitude scatter"
+     echo "y - ****(don't know what this does)"
+     echo ""
+     echo "@ - execute macro `user_macro`"
+     echo ": - ****input a line and execute (not yet implemented)"
+    end
+
+    # quit from navigate
+    if ("$KEY" == "q") continue
+
+    #pan controls
+    if (("$KEY" == "Left") || ("$KEY" == "Right") || ("$KEY" == "Up") || ("$KEY" == "Down"))
+      $SHIFT = 0.2
+      $R$KEY  = $RMAX-$XMAX  
+      $D$KEY  = $DMAX-$YMAX
+      #assumes standard sky orientation!! (N up, E left)
+      if ("$KEY"=="Left")
+        $R$KEY = $R$KEY + $SHIFT*$XMAX
+      end
+      if ("$KEY"=="Right")
+        $R$KEY = $R$KEY + $SHIFT*$XMIN
+      end
+      if ("$KEY"=="Up")
+        $D$KEY = $D$KEY + $SHIFT*$YMAX
+      end
+      if ("$KEY"=="Down")
+        $D$KEY = $D$KEY + $SHIFT*$YMIN
+      end
+      #pretend like I hit '3' in the place to recenter it
+      nav_zoom 1      
+    end
+
+    # NEW zoom controls
+    if (("$KEY" == "Prior") || ("$KEY" == "Next") || ("$KEY" == "Home") || ("$KEY" == "End") || ("$KEY" == "Button4") || ("$KEY" == "Button5"))    
+      #move where key was hit to center      
+      $R$KEY  = $RMAX-$XMAX  
+      $D$KEY  = $DMAX-$YMAX
+      if ("$KEY" == "Prior")
+        $zfac=1.2
+      end
+      if ("$KEY" == "Next")
+        $zfac={1/1.2}
+      end
+      if ("$KEY" == "Home")
+        $zfac=2
+      end
+      if ("$KEY" == "End")
+        $zfac={1/2.}
+      end
+      if ("$KEY"=="Button4")
+        $zfac=1.6
+      end
+      if ("$KEY"=="Button5")
+        $zfac={1/1.6}
+      end
+      nav_zoom $zfac
+    end
+
+    if ("$KEY"=="Button1")
+      nav_zoom 1
+    end
+    if ("$KEY"=="Button2")
+      nav_zoom {1/2.}
+    end
+    if ("$KEY"=="Button3")
+      nav_zoom 2
+    end
+
+
+
+    # zoom controls
+    if ("$KEY" == "1")
+      nav_zoom 2
+    end
+    if ("$KEY" == "2")
+      nav_zoom 1.2
+    end
+    if ("$KEY" == "3")
+      nav_zoom 1
+    end
+    if ("$KEY" == "4")
+      nav_zoom {1/1.2}
+    end
+    if ("$KEY" == "5")
+      nav_zoom {1/2.}
+    end
+    if ("$KEY" == "6")
+      nav_zoom {1/20.}
+    end
+
+ 
+   # measure distance
+    if ("$KEY" == "d")
+      $r0 = $R$KEY
+      $d0 = $D$KEY
+      $ok = $KEY
+      echo "type 'd' again at endpoint"
+      cursor -g 1
+      $r1 = $R$KEY
+      $d1 = $D$KEY
+      $dr = 3600*((dcos($d0)*($r0-$r1))^2 + ($d0-$d1)^2)^0.5
+      echo "$dr arcsec"
+    end
+    # show ra, dec
+    if ("$KEY" == "w")
+      $tmp = $R$KEY
+      if ($tmp < 0) 
+        $tmp = $R$KEY + 360.0
+      end
+      echo "$tmp $D$KEY" 
+      exec echo $tmp $D$KEY | radec -hh
+    end
+    # zoom to radius
+    if ("$KEY" == "z")
+      $r0 = $R$KEY
+      $d0 = $D$KEY
+      $ok = $KEY
+      echo "type 'z' again at radius"
+      cursor -g 1
+      $r1 = $R$KEY
+      $d1 = $D$KEY
+      $dr = (($r0-$r1)^2 + ($d0-$d1)^2)^0.5
+      $ZOOM = $RAD / $dr
+      nav_recenter
+      nav_redraw
+      $KEY = $ok
+      $R$KEY = $r0
+      $D$KEY = $d0
+    end
+
+    # adjust mag scaling
+    if ("$KEY" == "J")
+      $MAG = $MAG - 0.5
+      nav_redraw
+    end
+    if ("$KEY" == "K")
+      $MAG = $MAG + 0.5
+      nav_redraw
+    end
+    if ("$KEY" == "j")
+      $dMAG = $dMAG * 0.8
+      nav_redraw
+    end
+    if ("$KEY" == "k")
+      $dMAG = $dMAG * 1.25
+      nav_redraw
+    end
+    echo "mag, dmag: $MAG, $dMAG"
+
+
+
+    # plot full sky
+    if ("$KEY" == "f") 
+      echo "full"
+      $ZOOM = 1
+      resize 1150 600		      
+      region 0 0 90 ait
+      $RMIN = 0
+      $RMAX = 360
+      $DMIN = -90
+      $DMAX = +90
+      style -c red; cgrid
+      style -c black
+      images
+    end
+
+    # plot rocks
+    if ("$KEY" == "r") 
+#      plot.rocks
+      style -c blue   -pt 1; procks -speed 0.0041 1
+      style -c red    -pt 1; procks -speed 0.00041 0.0041
+      style -c indigo -pt 1; procks -speed 0 0.00041
+      style -c black -lw 0;
+    end
+    # plot HST-GSC
+    if ("$KEY" == "l") 
+      style -c blue -pt 7; cat -all -g -m 9 16
+      style -c black
+    end
+    # plot Landolt
+    if ("$KEY" == "L") 
+#      style -c red  -lw 2 -pt 3; cat -a 1 2 3 /data/elixir/srcdir/refs/stetson/stetsonBn.txt -m 9 18
+#      style -c blue -lw 2 -pt 3; cat -a 25 26 8 /data/elixir/srcdir/refs/landolt/new/Landolt92.fix -m 9 18
+      style -c red -lw 2 -pt 7; cat -a 1 2 3 /data/elixir/srcdir/refs/sdss/g_SDSS.dat -m 9 14
+#      style -c red -lw 2 -pt 3; cat -a 25 26 8 /data/elixir/srcdir/refs/landolt/new/Landolt92.hq -m 9 18
+#      style -c red -lw 2 -pt 3; cat -a 22 23 8 /data/elixir/srcdir/refs/landolt/new/Landolt92.unfix -m 9 18
+#      style -c blue -lw 2 -pt 7; cat -a 1 2 4 /data/elixir/srcdir/refs/landolt/extreme/extreme.match -m 0 20
+#      style -x 2 -c red -pt 7 ; cplot RA DEC
+      style -c black -lw 0
+    end
+
+    # list star measurements
+    if ("$KEY" == "m") 
+        $dR = $RAD/$ZOOM/300
+        if ($dR < 0.0005)
+	 $dR = 0.0005
+        end
+	gstar $R$KEY $D$KEY $dR -m
+    end
+
+    # plot mag residuals
+    if ("$KEY" == "R") 
+      echo "filter: "
+      cursor 1
+      clear -n 1 -s; lim 10 22 -0.2 0.2; clear; box
+      dmags $KEY\:rel - $KEY : $KEY -type 0
+      plot -x 2 -pt 0 -sz 0.3 -c red yv xv
+      dmags $KEY\:rel - $KEY : $KEY -type 0 -flag 0 -nphot +3 -chisq 2.0
+      plot -x 2 -pt 2 -sz 0.5 -c black yv xv
+      $KEY = R
+      style -n 0
+    end
+
+    if ("$KEY" == "M") 
+	gstar $R$KEY $D$KEY 0.0005 -m
+    end
+    # list images
+    if ("$KEY" == "i") 
+	gimages $R$KEY $D$KEY
+    end
+    if ("$KEY" == "I") 
+	gimages $R$KEY $D$KEY -pix
+    end
+
+    #toggle images on / off
+    if ("$KEY" == "A")
+      $DRAWIMAGES = $DRAWIMAGES * -1
+      if ($DRAWIMAGES == 1)
+        images
+      end
+    end
+    # toggle stars on / off
+    if ("$KEY" == "S")
+      $DRAWSTARS = $DRAWSTARS * -1
+      if (($ZOOM > 20) && ($DRAWSTARS == 1))
+       style -pt 7
+       pmeasure -all -m $MAG {$MAG + $dMAG}
+      end
+    end
+    # turn grid on / off
+    if ("$KEY" == "g")
+      $DRAWGRID = $DRAWGRID * -1
+      if (($ZOOM > 20) && ($DRAWGRID==1))
+        style -c black; cgrid
+      end
+      if (($ZOOM > 20) && ($DRAWGRID==-1))
+        nav_redraw
+      end
+    end
+
+    # plot light-curve interactive
+    if ("$KEY" == "t")
+      style -n 1 -pt 2 -x 2
+      clear
+      if ($R$KEY < 0) 
+       $R$KEY = $R$KEY + 360
+      end
+      lcurve -l $R$KEY $D$KEY {30/3600} -d -v time mag
+      box
+      lcv
+      style -n 0
+    end
+    # plot light-curve 
+    if ("$KEY" == "T")
+      style -n 1 -pt 1 -c red -x 2
+      lcurve $R$KEY $D$KEY {30/3600} -d
+      style -c black
+      style -n 0 
+    end
+    # plot catalogs
+    if ("$KEY" == "c")
+      style -c blue; pcat; style -c black
+    end
+    # list catalogs
+    if ("$KEY" == "C")
+      gcat $R$KEY $D$KEY
+    end
+
+    # plot image chisqs
+    if ("$KEY" == "x") 
+       gcat $R$KEY $D$KEY
+       extract $CATNAME Xm -photcode R
+       extract $CATNAME ra
+       extract $CATNAME dec
+       style -x 2 -pt 7 -c blue
+       czplot ra dec Xm 3 30
+       style -c black -pt 1
+    end
+    # plot meas errors
+    if ("$KEY" == "X") 
+       gcat $R$KEY $D$KEY
+       extract $CATNAME dM -photcode R
+       extract $CATNAME ra
+       extract $CATNAME dec
+       style -x 2 -pt 7 -c red
+       czplot ra dec dM 0 30
+       style -c black -pt 1
+    end
+
+
+    # temp plot for skyprobe
+    if ("$KEY" == "u") 
+      imextract -region time
+      imextract -region mcal
+      imextract -region airmass
+      imextract -region nstar
+      vstat time
+      clear -n 1;
+      section a 0 0.00 1 0.33
+      lim {$MEDIAN-0.3} {$MEDIAN+0.3} -0.8 -0.5; box; plot time mcal
+      section b 0 0.33 1 0.33
+      lim {$MEDIAN-0.3} {$MEDIAN+0.3}  0.95 3.0; box; plot time airmass
+      section c 0 0.66 1 0.33
+      lim {$MEDIAN-0.3} {$MEDIAN+0.3} 0 3000; box; plot time nstar
+      style -n 0
+    end
+    if ("$KEY" == "s")
+      $tmp = $R$KEY
+      if ($tmp < 0) 
+        $tmp = $R$KEY + 360.0
+      end
+      $line = `echo $tmp $D$KEY | radec -hh`
+      imextract -region photcode
+      imextract -region time
+     
+      subset t = time if (int(photcode/100) == 1)
+      uniq t T
+      $Bn = t[]
+      $BN = T[]
+      
+      subset t = time if (int(photcode/100) == 2)
+      uniq t T
+      $Vn = t[]
+      $VN = T[]
+      
+      subset t = time if (int(photcode/100) == 3)
+      uniq t T
+      $Rn = t[]
+      $RN = T[]
+      
+      subset t = time if (int(photcode/100) == 4)
+      uniq t T
+      $In = t[]
+      $IN = T[]
+     
+      echo "$line  $Bn $BN  $Vn $VN  $Rn $RN  $In $IN"
+    end
+
+    if ("$KEY" == "p") 
+      echo "P - new coords; p - old coords"
+      cursor -g 1
+      exec echo $Rp $Dp $RP $DP >> fix.coords
+    end
+
+    if ("$KEY" == "y")
+      ccd I - 2MASS_J : 2MASS_J - 2MASS_K
+      lim -n 1 -1 10 -1 3; clear; box; plot -x 2 -pt 2 -sz 0.5 xv yv
+      dev -n 0 -g
+    end
+
+    #  User-defined macro
+    if ("$KEY" == "at")
+      user_macro
+    end
+
+    if ("$KEY" == "colon")
+      #make this work similar to ':' in vi or iraf
+      #does not work correctly now.
+      scan stdin line
+      $line
+    end
+
+  end
+end
+
+#define this so navigate doesn't crash if you try to call it.
+#If you define a user_macro, be sure to do so AFTER this in .dvorc.
+macro user_macro
+  #echo "success!"
+  $do_nothing=0
+end
+    
+
+macro nav_zoom
+  $ZOOM = $ZOOM * $1
+  nav_recenter
+  nav_redraw
+  $Rnum = $R$KEY		      
+  $Dnum = $D$KEY
+  $KEY = num
+end
+
+macro nav_recenter
+  region $R$KEY $D$KEY {$RAD/$ZOOM} sin
+  #assumes standard sky orientation!! (N up, E left)
+  $RMIN = $R$KEY + $XMIN
+  $RMAX = $R$KEY + $XMAX
+  $DMIN = $D$KEY + $YMIN
+  $DMAX = $D$KEY + $YMAX
+end
+
+macro nav_redraw
+  clear
+  if ($ZOOM <= 20) 
+    style -c red; cgrid
+  end
+  if (($ZOOM > 20) && ($DRAWGRID==1))
+    style -c black; cgrid
+  end
+  if (($ZOOM > 20) && ($DRAWSTARS == 1))
+    pmeasure -all -m $MAG {$MAG + $dMAG}
+  end    
+  style -c black
+  if ($DRAWIMAGES == 1)
+    images
+  end
+end
+
+
+
+#==================================================
+#=================   END BSNAV   ==================
+#==================================================
+
+
+macro sigclip
+  if ("$0" == "1")
+    echo ""
+    echo "sigclip <clipvector> <N_iterations> <N_sigma> [other vectors ..]"
+    echo ""
+  end
+
+  #required parameters
+  $CLIPVECT = $1
+  $NITERATE = $2
+  $NSIGCLIP = $3
+  
+  for i 0 $NITERATE
+    vstat -q $CLIPVECT
+    #clip boundaries
+    $top = $MEAN + ($NSIGCLIP*$SIGMA)
+    $bot = $MEAN - ($NSIGCLIP*$SIGMA)
+    
+    #clip it good.
+    subset temp = $CLIPVECT if (($CLIPVECT < $top) && ($CLIPVECT > $bot))
+    
+    #if you specify other vectors, clip the same elements from them too.
+    #they must all be the same length, of course!!
+    if ($0>4)
+      for j 4 $0
+        subset $$j = $$j if (($CLIPVECT < $top) && ($CLIPVECT > $bot))
+      end
+    end
+    
+    #copy temp back to $CLIPVECT and reiterate!
+    delete $CLIPVECT
+    concat temp $CLIPVECT  
+  end
+end
+
+
+macro binvec
+  if ("$0" == "1")
+    echo ""
+    echo "binvec <vec> <Nbins> [other vectors...]"
+    echo ""
+    echo "Bin the vector 'vec' into Nbin bins.  Listing other vectors will"
+    echo "put the corresponding elements of those into other vectors which"
+    echo "are the subset of the vector in that bin.  (That can probably be"
+    echo "stated better.)  This macro makes lots of new vectors.  Hooray!"
+    echo ""
+    echo "Creates"
+  end
+
+  #REQUIRED PARAMS
+  $binvect = $1
+  $NBINS   = $2
+
+  vstat -q $binvect
+  $step = ($MAX-$MIN)/$NBINS
+  $vmin = $MIN
+  $vmax = $MAX
+  delete -q $binvect\_bins
+  delete -q $binvect\_num
+  for i 1 {$NBINS+1}
+    $top = $vmin + ( $i   *$step)
+    $bot = $vmin + (($i-1)*$step)
+    #      sightly different behavior for last bin    -------v
+    if ($i != $NBINS)
+      subset temp = $binvect if (($binvect>=$bot)&&($binvect< $top))
+    else
+      subset temp = $binvect if (($binvect>=$bot)&&($binvect<=$top))
+    end
+    set $binvect\_bin$i = temp
+    set temp2 = temp
+    delete temp
+    #if you specify other vectors, grab the same elements from them too.
+    #they must all be the same length, of course!!
+
+    if ($0>3)
+      for j 3 $0
+        if ($i != $NBINS)
+          subset temp = $$j if (($binvect>=$bot)&&($binvect< $top))
+        else 
+          subset temp = $$j if (($binvect>=$bot)&&($binvect<=$top))
+        end
+        set $$j\_bin$i = temp
+        delete temp
+      end
+    end
+
+
+    concat {$bot+($step/2)} $binvect\_bins
+    concat temp2[] $binvect\_num
+    #dvo didn't like me saying 'concat $binvect\_bin$i[] $binvect\_num
+    delete temp2
+  end
+end
+
+macro binvec.2
+  if ("$0" == "1")
+    echo ""
+    echo "binvec.2 <vec> <min> <max> <binsize> [other vectors...]"
+    echo ""
+    echo ""
+    echo "see also 'binvec'"
+  end
+
+  #REQUIRED PARAMS
+  $binvect = $1
+  $vmin    = $2
+  $vmax    = $3
+  $step    = $4
+
+  $NBINS = ($MAX-$MIN)/$step
+
+  delete -q $binvect\_bins
+  delete -q $binvect\_num
+  for i 1 {$NBINS+1}
+    $top = $vmin + ( $i   *$step)
+    $bot = $vmin + (($i-1)*$step)
+    #      sightly different behavior for last bin    -------v
+    if ($i != $NBINS)
+      subset temp = $binvect if (($binvect>=$bot)&&($binvect< $top))
+    else
+      subset temp = $binvect if (($binvect>=$bot)&&($binvect<=$top))
+    end
+    set $binvect\_bin$i = temp
+    set temp2 = temp
+    delete temp
+    #if you specify other vectors, grab the same elements from them too.
+    #they must all be the same length, of course!!
+
+    if ($0>5)
+      for j 5 $0
+        if ($i != $NBINS)
+          subset temp = $$j if (($binvect>=$bot)&&($binvect< $top))
+        else 
+          subset temp = $$j if (($binvect>=$bot)&&($binvect<=$top))
+        end
+        set $$j\_bin$i = temp
+        delete temp
+      end
+    end
+
+
+    concat {$bot+($step/2)} $binvect\_bins
+    concat temp2[] $binvect\_num
+    #dvo didn't like me saying 'concat $binvect\_bin$i[] $binvect\_num
+    delete temp2
+  end
+end
Index: /branches/sc_branches/pantasks_condor/modules/mana/plots
===================================================================
--- /branches/sc_branches/pantasks_condor/modules/mana/plots	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/modules/mana/plots	(revision 34783)
@@ -0,0 +1,41 @@
+
+macro cvplot 
+  if ($0 != 6)
+    echo "USAGE: cvplot r d ur ud scale"
+    break
+  end
+ 
+  local i
+  delete -q _tmpr _tmpd
+
+  for i 0 $1[]
+   concat $1[$i] _tmpr
+   concat $2[$i] _tmpd
+   concat {$1[$i]+$3[$i]*$5} _tmpr
+   concat {$2[$i]+$4[$i]*$5} _tmpd
+  end
+  style -pt 100
+  cplot _tmpr _tmpd
+  style -pt 7
+end
+
+macro vplot 
+  if ($0 != 6)
+    echo "USAGE: cvplot r d ur ud scale"
+    break
+  end
+ 
+  local i
+  delete -q _tmpr _tmpd
+
+  for i 0 $1[]
+   concat $1[$i] _tmpr
+   concat $2[$i] _tmpd
+   concat {$1[$i]+$3[$i]*$5} _tmpr
+   concat {$2[$i]+$4[$i]*$5} _tmpd
+  end
+  style -pt 100
+  plot _tmpr _tmpd
+  style -pt 7
+end
+
Index: /branches/sc_branches/pantasks_condor/pantasks/CheckController.c
===================================================================
--- /branches/sc_branches/pantasks_condor/pantasks/CheckController.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/pantasks/CheckController.c	(revision 34783)
@@ -0,0 +1,122 @@
+# include "pantasks.h"
+
+void TimerMark (struct timeval *start);
+float TimerElapsed (struct timeval *start, int reset);
+
+int CheckController () {
+
+  char *p, *q;
+  int i, Njobs, status, JobID;
+  Job *job;
+  IOBuffer buffer;
+  struct timeval start;
+
+  /* get the list of completed jobs (exit / crash), update the job status */
+  if (!CheckControllerStatus()) return (TRUE);
+
+  /*** check EXIT jobs ***/
+  InitIOBuffer (&buffer, 0x100);
+
+  TimerMark (&start);
+  FlushIOBuffer (&buffer);
+
+  status = ControllerCommand ("jobstack exit", CONTROLLER_PROMPT, &buffer);
+  if (VerboseMode()) gprint (GP_ERR, "check exit stack %f\n", TimerElapsed(&start, TRUE));
+  if (!status) goto escape;
+
+  /** programming error **/
+  p = memstr (buffer.buffer, "USAGE: jobstack", buffer.Nbuffer);
+  if (p != NULL) goto escape;
+
+  /** parse job list **/
+  status = sscanf (buffer.buffer, "%*s %d", &Njobs);
+  if (status != 1) goto escape;
+  if (VerboseMode()) gprint (GP_ERR, "parse %d jobs on stack %f\n", Njobs, TimerElapsed(&start, TRUE));
+
+  p = buffer.buffer;
+  for (i = 0; i < Njobs; i++) {
+    q = strchr (p, '\n');
+    if (q == NULL) {
+      gprint (GP_ERR, "controller message error: incomplete job list\n");
+      break;
+    }
+    p = q + 1;
+    status = sscanf (p, "%d", &JobID);
+
+    // the operations within this locked block only interact with the controller or
+    // modify the properties of the selected job
+    JobTaskLock();
+    job = FindControllerJob (JobID);
+    if (job == NULL) {
+      gprint (GP_ERR, "misplaced job? %d not in EXIT job list\n", JobID);
+      JobTaskUnlock();
+      continue;
+    }
+    /* this checks the individual job status, grabs stdout/stderr */
+    CheckControllerJob (job);
+    JobTaskUnlock();
+  }
+  if (VerboseMode()) gprint (GP_ERR, "clear %d exit jobs %f\n", i, TimerElapsed(&start, TRUE));
+
+  /*** check CRASH jobs ***/
+  FlushIOBuffer (&buffer);
+  status = ControllerCommand ("jobstack crash", CONTROLLER_PROMPT, &buffer);
+  if (!status) goto escape;
+
+  p = memstr (buffer.buffer, "USAGE: jobstack", buffer.Nbuffer);
+  if (p != NULL) goto escape;
+
+  /** parse job list **/
+  status = sscanf (buffer.buffer, "%*s %d", &Njobs);
+  if (status != 1) goto escape;
+  if (VerboseMode()) gprint (GP_ERR, "check crash stack %f\n", TimerElapsed(&start, TRUE)); 
+
+  p = buffer.buffer;
+  for (i = 0; i < Njobs; i++) {
+    q = strchr (p, '\n');
+    if (q == NULL) {
+      gprint (GP_ERR, "controller message error: incomplete job list\n");
+      break;
+    }
+    p = q + 1;
+    status = sscanf (p, "%d", &JobID);
+
+    // the operations within this locked block only interact with the controller or
+    // modify the properties of the selected job
+    JobTaskLock();
+    job = FindControllerJob (JobID);
+    if (job == NULL) {
+      gprint (GP_ERR, "misplaced job? %d not in CRASH job list\n", JobID);
+      JobTaskUnlock();
+      continue;
+    }
+    /* this checks the individual job status, grabs stdout/stderr */
+    CheckControllerJob (job);
+    JobTaskUnlock();
+  }
+  if (VerboseMode()) gprint (GP_ERR, "clear %d crash jobs %f\n", i, TimerElapsed(&start, TRUE)); 
+
+  FlushIOBuffer (&buffer);
+  FreeIOBuffer (&buffer);
+  return (TRUE);
+
+ escape:
+  FlushIOBuffer (&buffer);
+  FreeIOBuffer (&buffer);
+  return (FALSE);
+}
+
+void TimerMark (struct timeval *start) {
+    gettimeofday (start, (void *) NULL);
+}
+
+float TimerElapsed (struct timeval *start, int reset) {
+
+  float dtime;
+  struct timeval stop;
+
+  gettimeofday (&stop, (void *) NULL);
+  dtime = DTIME (stop, start[0]);
+  if (reset) gettimeofday (start, (void *) NULL);
+  return (dtime);
+}
Index: /branches/sc_branches/pantasks_condor/pantasks/CheckJobs.c
===================================================================
--- /branches/sc_branches/pantasks_condor/pantasks/CheckJobs.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/pantasks/CheckJobs.c	(revision 34783)
@@ -0,0 +1,247 @@
+# include "pantasks.h"
+
+float CheckJobs () {
+
+  FILE *f;
+  Job *job;
+  Task *task;
+  Macro *macro;
+  int i, status;
+  char varname[64];
+  Queue *queue;
+  float time_running, next_timeout;
+
+  // int Ncheck;
+  // Ncheck = 0;
+
+  // actual maximum delay is controlled in job_threads.c
+  next_timeout = 1.0;
+
+  JobTaskLock();
+  /** test all jobs: ready to test?  finished? **/
+  while ((job = NextJob ()) != NULL) {
+    // Ncheck ++;
+
+    task = job[0].task;
+    // XXX we need to guarantee that the task exists
+    // if we delete a task, we need to keep a copy until all task jobs are
+    // removed
+
+    /* check poll period (ready to ask for status?) */
+    time_running = GetTaskTimer(job[0].last, FALSE);
+    // fprintf (stderr, "next: %f, poll: %f, run: %f\n", next_timeout, task[0].poll_period, time_running);
+    if (time_running < task[0].poll_period) {
+      next_timeout = MIN (next_timeout, task[0].poll_period - time_running);
+      continue;
+    }
+    next_timeout = 0.0;
+
+    /* check current status */
+    status = CheckJob (job);
+    switch (status) {
+      case JOB_PENDING:
+	/* if (VerboseMode()) gprint (GP_LOG, "job %s (%d) pending\n", task[0].name, job[0].JobID); */
+	break;
+
+      case JOB_BUSY:
+	/* if (VerboseMode()) gprint (GP_LOG, "job %s (%d) busy\n", task[0].name, job[0].JobID); */
+	break;
+
+      case JOB_CRASH:
+      case JOB_EXIT:
+	/* push output buffer data to the stdout and stderr queues */
+	/* XXX this will break on 0 values in output streams */
+	if (DEBUG) fprintf (stderr, "job: (%zx) %d of %d\n", (size_t) job[0].stdout_buff.buffer, job[0].stdout_buff.Nbuffer, job[0].stdout_buff.Nalloc); 
+	PushNamedQueue ("stdout", job[0].stdout_buff.buffer);
+	PushNamedQueue ("stderr", job[0].stderr_buff.buffer);
+
+	/* save the stdout and stderr if desired */
+	if ((job[0].stdout_dump != NULL) && strcasecmp(job[0].stdout_dump, "NULL")) {
+	  f = fopen (job[0].stdout_dump, "a");
+	  if (f == NULL) {
+	    gprint (GP_ERR, "unable to open stdout dump file %s\n", job[0].stdout_dump);
+	  } else {
+	    fwrite (job[0].stdout_buff.buffer, 1, job[0].stdout_buff.Nbuffer, f);
+	    fclose (f);
+	  }
+	}
+	if ((job[0].stderr_dump != NULL) && strcasecmp(job[0].stderr_dump, "NULL")) {
+	  f = fopen (job[0].stderr_dump, "a");
+	  if (f == NULL) {
+	    gprint (GP_ERR, "unable to open stderr dump file %s\n", job[0].stderr_dump);
+	  } else {
+	    fwrite (job[0].stderr_buff.buffer, 1, job[0].stderr_buff.Nbuffer, f);
+	    fclose (f);
+	  }
+	}
+
+	/* set taskarg variables */
+	for (i = 0; i < job[0].argc; i++) {
+	  sprintf (varname, "taskarg:%d", i);
+	  set_str_variable (varname, job[0].argv[i]);
+	}
+	set_int_variable ("taskarg:n", job[0].argc);
+
+	/* set options variables */
+	for (i = 0; i < job[0].optc; i++) {
+	  sprintf (varname, "options:%d", i);
+	  set_str_variable (varname, job[0].optv[i]);
+	}
+	set_int_variable ("options:n", job[0].optc);
+
+	set_variable ("JOB_DTIME", job[0].dtime);
+
+	if (job[0].realhost == NULL) {
+	  set_str_variable ("JOB_HOSTNAME", "localhost");
+	} else {
+	  set_str_variable ("JOB_HOSTNAME", job[0].realhost);
+	}	    
+
+	if (status == JOB_CRASH) {
+	  /* XXX add an Ncrash element? */
+	  task[0].Nfailure ++;
+	  UpdateTaskTimerStats (task, TIMER_FAILURE, job[0].dtime);
+
+	  /* run task[0].crash macro, if it exists */
+	  /* perhaps define PushNamedQueueBuffer */
+
+	  set_str_variable ("JOB_STATUS", "CRASH");
+
+	  if (VerboseMode()) gprint (GP_LOG, "job %s (%d) crash\n", task[0].name, job[0].JobID);
+	  if (task[0].crash != NULL) {
+	    // we need to unlock JobTask since JobTaskLock is called inside CommandLock in the client thread
+	    JobTaskUnlock();
+	    CommandLock();
+	    exec_loop (task[0].crash);
+	    CommandUnlock();
+	    JobTaskLock();
+	  }
+	}
+	if (status == JOB_EXIT) {
+	  /* update the exit status counters */
+	  if (job[0].exit_status) {
+	    task[0].Nfailure ++;
+	    UpdateTaskTimerStats (task, TIMER_FAILURE, job[0].dtime);
+	  } else {
+	    task[0].Nsuccess ++;
+	    UpdateTaskTimerStats (task, TIMER_SUCCESS, job[0].dtime);
+	  }
+
+	  set_int_variable ("JOB_STATUS", job[0].exit_status);
+
+	  /* run corresponding task[0].exit macro, if it exists */
+	  if (VerboseMode()) gprint (GP_LOG, "job %s (%d) exit\n", task[0].name, job[0].JobID);
+	  macro = task[0].defexit;
+	  for (i = 0; i < task[0].Nexit; i++) {
+	    if (job[0].exit_status == atoi(task[0].exit[i][0].name)) {
+	      macro = task[0].exit[i];
+	      break;
+	    }
+	  }
+	  if (macro != NULL) {
+	    // we need to unlock JobTask since JobTaskLock is called inside CommandLock in the client thread
+	    JobTaskUnlock();
+	    CommandLock();
+	    exec_loop (macro);
+	    CommandUnlock();
+	    JobTaskLock();
+	  }
+	}
+
+	/* flush the stderr and stdout queues */
+	queue = FindQueue ("stdout");
+	if (queue) InitQueue (queue);
+	queue = FindQueue ("stderr");
+	if (queue) InitQueue (queue);
+
+	UpdateTaskTimerStats (task, TIMER_ALLJOBS, job[0].dtime);
+
+	DeleteJob (job);
+	continue;
+
+      default:
+	if (VerboseMode()) gprint (GP_LOG, "unknown exit status: %d\n", status);
+	/** do something more useful here ?? **/
+	break;
+    }
+
+    /* check for timeout - (local jobs only) 
+       we only check timeout after a poll (forces at least one poll)
+    */
+    if (job[0].mode == JOB_LOCAL) {
+      if (GetTaskTimer(job[0].start, FALSE) < task[0].timeout_period) { 
+	/* reset polling clock */
+	SetTaskTimer (&job[0].last);
+	continue;
+      }
+      if (VerboseMode()) gprint (GP_LOG, "timeout on %s\n", task[0].name);
+
+      // XXX harvest STDERR and STDOUT from timeout job (should be available...)
+      // XXX add this to controller as well
+
+      /* update the timeout counter */
+      task[0].Ntimeout ++;
+
+      if (!KillLocalJob (job)) {
+	job[0].state = JOB_HUNG;
+	if (VerboseMode()) gprint (GP_LOG, "child process %d is hung, cannot kill\n", job[0].pid);
+	continue;
+      }
+
+      /* set taskarg variables */
+      for (i = 0; i < job[0].argc; i++) {
+	sprintf (varname, "taskarg:%d", i);
+	set_str_variable (varname, job[0].argv[i]);
+      }
+      set_int_variable ("taskarg:n", job[0].argc);
+
+      /* set options variables */
+      for (i = 0; i < job[0].optc; i++) {
+	sprintf (varname, "options:%d", i);
+	set_str_variable (varname, job[0].optv[i]);
+      }
+      set_int_variable ("options:n", job[0].optc);
+
+      /* run task[0].timeout macro, if it exists */
+      if (task[0].timeout != NULL) {
+	// we need to unlock JobTask since JobTaskLock is called inside CommandLock in the client thread
+	JobTaskUnlock();
+	CommandLock();
+	exec_loop (task[0].timeout);
+	CommandUnlock();
+	JobTaskLock();
+      }
+
+      DeleteJob (job);
+      continue;
+    }
+
+    /* reset polling clock */
+    SetTaskTimer (&job[0].last);
+  }
+  // fprintf (stderr, "check %d jobs\n", Ncheck);
+  JobTaskUnlock();
+  return (next_timeout);
+}
+
+/* 
+
+   job / task timeline:
+
+   task:
+   0           exec     
+   start       create
+   task clock  new job
+
+   job:
+   0           1xpoll     2xpoll     3xpoll
+   start       check      check      check 
+   job clock   status     status     status
+
+   .           .          .          timeout
+   run
+   timeout
+
+   must be at least one poll before timeout 
+   (timeout >= poll)
+*/
Index: /branches/sc_branches/pantasks_condor/pantasks/CheckPassword.c
===================================================================
--- /branches/sc_branches/pantasks_condor/pantasks/CheckPassword.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/pantasks/CheckPassword.c	(revision 34783)
@@ -0,0 +1,36 @@
+# include "pantasks.h"
+# define DEBUG 0
+
+// this static var is only used by InitPassword and CheckPassword below.
+// Both functions are only called by the main thread.
+static char PASSWORD[256];
+
+int InitPassword () {
+
+  VarConfig ("PASSWORD", "%s", PASSWORD);
+  return (TRUE);
+}
+
+int CheckPassword (int BindSocket) {
+
+  IOBuffer message;
+  int status;
+
+  if (DEBUG) gprint (GP_ERR, "waiting for password %s\n", PASSWORD);
+
+  status = ExpectCommand (BindSocket, strlen(PASSWORD), 0.1, &message);
+  if (status != 0) {
+    if (DEBUG) gprint (GP_ERR, "failed connection\n");
+    FreeIOBuffer (&message);
+    close (BindSocket);
+    return (FALSE);
+  }
+  if (strncmp (message.buffer, PASSWORD, strlen(PASSWORD))) {
+    if (DEBUG) gprint (GP_ERR, "invalid password\n");
+    close (BindSocket);
+    return (FALSE);
+  }
+  if (DEBUG) gprint (GP_ERR, "accepted password (%s)\n", message.buffer);
+  
+  return (TRUE);
+}
Index: /branches/sc_branches/pantasks_condor/pantasks/CheckTasks.c
===================================================================
--- /branches/sc_branches/pantasks_condor/pantasks/CheckTasks.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/pantasks/CheckTasks.c	(revision 34783)
@@ -0,0 +1,93 @@
+# include "pantasks.h"
+
+float CheckTasks () {
+
+  Job *job;
+  Task *task;
+  int status;
+  float time_running, next_timeout, fuzz;
+
+  // actual maximum delay is controlled in job_threads.c
+  next_timeout = 1.0;
+
+  JobTaskLock();
+  /** test all tasks: ready to test? ready to run? **/
+  while ((task = NextTask ()) != NULL) {
+
+    /*** test for all reasons we should skip this task ***/
+
+    /* task has been de-activated by the user */
+    if (!task[0].active) {
+      continue;
+    }
+    /* current time is not within valid/invalid periods */
+    if (!CheckTimeRanges (task[0].ranges, task[0].Nranges)) {
+      continue;
+    }
+    /* all allowed tasks have been run in this period */
+    if (task[0].Nmax && (task[0].Njobs >= task[0].Nmax)) {
+      continue;
+    }
+    /* too many outstanding jobs */
+    if (task[0].NpendingMax && (task[0].Npending >= task[0].NpendingMax)) {
+      continue;
+    }
+
+    /* ready to test? : check time since last exec */
+    time_running = GetTaskTimer(task[0].last, FALSE);
+    if (time_running < task[0].exec_period) {
+      // is we are to ready to run, set time to timeout, if shortest of all tasks
+      next_timeout = MIN (next_timeout, task[0].exec_period - time_running);
+      continue;
+    }
+
+    /* ready to try running the task : reset the timer */
+    next_timeout = 0.0;
+    gettimeofday (&task[0].last, (void *) NULL);
+
+    // add random offset between 0 and 5% of exec_period
+    // XXX this should be optional
+    fuzz = task[0].exec_period*(0.5*drand48() - 0.25);
+    task[0].last.tv_usec = 1e6*(fuzz - (int)fuzz);
+    task[0].last.tv_sec += (int) fuzz;
+
+    /* ready to run? : run task.exec macro */
+    if (task[0].exec != NULL) {
+      // we need to unlock JobTask since JobTaskLock is called inside CommandLock in the client thread
+      JobTaskUnlock();
+      CommandLock();
+      status = exec_loop (task[0].exec);
+      CommandUnlock();
+      JobTaskLock();
+      if (!status) {
+	task[0].Nskipexec ++;
+	continue;
+      }
+    }
+
+    /* check if there are errors with this task */
+    if (!ValidateTask (task, TRUE)) { 
+      continue;
+    }
+    
+    /* construct job from task */
+    job = CreateJob (task);
+    if (!job) {
+      continue;
+    }
+
+    if (DEBUG) fprintf (stderr, "create job: (%zx) %d of %d\n", (size_t) job[0].stdout_buff.buffer, job[0].stdout_buff.Nbuffer, job[0].stdout_buff.Nalloc); 
+
+    /* execute job */
+    if (!SubmitJob (job)) {
+      DeleteJob (job);
+      continue;
+    }
+    task[0].Njobs ++; // number of jobs successfully submitted
+
+    /* increment Nrun for inclusive ranges with Nmax */
+    BumpTimeRanges (task[0].ranges, task[0].Nranges);
+  }
+  JobTaskUnlock();
+  return (next_timeout);
+}
Index: /branches/sc_branches/pantasks_condor/pantasks/CheckTimeRanges.c
===================================================================
--- /branches/sc_branches/pantasks_condor/pantasks/CheckTimeRanges.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/pantasks/CheckTimeRanges.c	(revision 34783)
@@ -0,0 +1,97 @@
+# include "pantasks.h"
+
+/* the tested time is saved by CheckTimeRanges for a following BumpTimeRanges
+   otherwise we could have an inconsistency between valid ranges and Nrun */
+static time_t daytime, weektime, abstime;
+
+/* test if we meet all time range qualifications */
+int CheckTimeRanges (TimeRange *ranges, int Nranges) {
+
+  int i, intime, Ninclude, include, exclude, valid;
+  time_t testtime;
+  struct timeval now;
+  struct tm Now;
+
+  /* get the current time */
+  gettimeofday (&now, NULL);
+  gmtime_r (&now.tv_sec, &Now);
+  daytime  = Now.tm_sec + Now.tm_min*60 + Now.tm_hour*3600;
+  weektime = Now.tm_sec + Now.tm_min*60 + Now.tm_hour*3600 + Now.tm_wday*86400;
+  abstime  = now.tv_sec;
+
+  Ninclude = 0;
+  include = FALSE;
+  exclude = FALSE;
+  
+  for (i = 0; i < Nranges; i++) {
+    if (ranges[i].include) Ninclude ++;
+
+    switch (ranges[i].type) {
+      /* set the testtime */
+      case RANGE_ABS:
+	testtime = abstime;
+	break;
+      case RANGE_DAY:
+	testtime = daytime;
+	break;
+      case RANGE_WEEK:
+	testtime = weektime;
+	break;
+      default:
+	abort ();
+    }
+    intime = (testtime >= ranges[i].start) && (testtime <= ranges[i].stop);
+
+    /* check for more than max runs in time range */
+    if (ranges[i].include && intime && ranges[i].Nmax) {
+      if (ranges[i].Nrun >= ranges[i].Nmax) return (FALSE);
+    }
+    /* reset Nrun if we are outside of intime */
+    if (ranges[i].include && !intime && ranges[i].Nmax && ranges[i].Nrun) {
+      ranges[i].Nrun = 0;
+    }
+
+    /* is this a valid time? */
+    if ( ranges[i].include &&  intime) include = TRUE;
+    if (!ranges[i].include && !intime) exclude = TRUE;
+  }
+
+  if (Ninclude == 0) include = TRUE;
+  valid = include && !exclude;
+
+  return (valid);
+}  
+
+/* increment the number of runs for all inclusive time ranges with Nmax > 0
+   (only call when we execute a task -- after CheckTimeRanges) */
+int BumpTimeRanges (TimeRange *ranges, int Nranges) {
+
+  int i, intime;
+  time_t testtime;
+
+  /* only increment the counter for ranges which are valid */
+  for (i = 0; i < Nranges; i++) {
+    if (!ranges[i].Nmax) continue;
+    if (!ranges[i].include) continue;
+
+    switch (ranges[i].type) {
+      /* set the testtime */
+      case RANGE_ABS:
+	testtime = abstime;
+	break;
+      case RANGE_DAY:
+	testtime = daytime;
+	break;
+      case RANGE_WEEK:
+	testtime = weektime;
+	break;
+      default:
+	abort ();
+    }
+    intime = (testtime >= ranges[i].start) && (testtime <= ranges[i].stop);
+
+    /* reset Nrun if we are outside of intime */
+    if (intime) ranges[i].Nrun ++;
+  }
+  return (TRUE);
+}  
Index: /branches/sc_branches/pantasks_condor/pantasks/ControllerOps.c
===================================================================
--- /branches/sc_branches/pantasks_condor/pantasks/ControllerOps.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/pantasks/ControllerOps.c	(revision 34783)
@@ -0,0 +1,721 @@
+# include "pantasks.h"
+/* adding a new host can delay controller up to a second or so */
+# define CONTROLLER_TIMEOUT 5000
+# define CONNECT_TIMEOUT 1000
+
+/* local static variables to hold the connection to the controller */
+static int ControllerStatus = FALSE;
+static int stdin_cntl, stdout_cntl, stderr_cntl;
+static IOBuffer stdout_buffer;
+static IOBuffer stderr_buffer;
+#ifndef IPP_CONDOR
+static int ControllerPID = 0;
+#else
+#include "ipp_condor.h"
+#include <condor/condor.nsmap>
+#include <shell.h>
+static struct soap soap;
+#endif
+
+/* local static variables to track the controller host properties */
+/* these are used by AddHost and DeleteHost, and are only called by controller_host.c (clientThread) */
+/* or by RestartController : lock between these two? */
+static Host *hosts = NULL;
+static int Nhosts = 0;
+static int NHOSTS = 0;
+
+#ifdef IPP_CONDOR
+#include "ipp_condor.h"
+static int hosts_list_modified = TRUE;
+static char* hosts_requirement = NULL;
+static int is_host_in_list(char *hostname);
+#endif
+
+int AddHost (char *hostname, int max_threads) {
+  int N;
+#ifdef IPP_CONDOR
+  int index;
+  index = is_host_in_list(hostname);
+  if (index != -1) {
+      if (hosts[index].max_threads != max_threads) {
+          fprintf(stderr, "Hostname [%s] already declared. Changing max_threads from %d to %d",
+                 hostname,
+                 hosts[index].max_threads,
+                 max_threads);
+          hosts[index].max_threads = max_threads;
+      } else {
+          fprintf(stderr, "Hostname [%s] already declared. Aborting", hostname);
+      }
+      return (TRUE);
+  }
+#endif
+  N = Nhosts;
+  Nhosts ++;
+  CHECK_REALLOCATE (hosts, Host, NHOSTS, Nhosts, 16);
+  hosts[N].hostname = strcreate (hostname);
+  hosts[N].max_threads = max_threads;
+#ifdef IPP_CONDOR
+  fprintf(stderr, "Adding host %s (%d)\n", hostname, max_threads);
+  hosts_list_modified = TRUE; //Tell get_hosts_requirements to rebuild the hosts based requirement
+#endif
+  return (TRUE);
+}
+
+// XXX possible race condition problem: if we delete a host while the controller
+// is being restarted.  to fix this, we need to keep the deletion in controller_thread,
+// but perhaps mark it in the client_thread?
+int DeleteHost (char *hostname) {
+  int i, j;
+  fprintf(stderr, "Attempting to delete %s\n", hostname);
+#ifdef IPP_CONDOR
+  int index;
+  index = is_host_in_list(hostname);
+  if (index == -1) {
+      return (FALSE);
+  }
+#endif
+  for (i = 0; i < Nhosts; i++) { //Optimize this for condor
+    if (strcmp (hosts[i].hostname, hostname)) continue;
+    // delete this one
+    free (hosts[i].hostname);
+    for (j = i; j < Nhosts - 1; j++) {
+      hosts[j].hostname = hosts[j+1].hostname;
+      hosts[j].max_threads = hosts[j+1].max_threads;
+    }
+    Nhosts --;
+#ifdef IPP_CONDOR
+    hosts_list_modified = TRUE; //Tell get_hosts_requirements to rebuild the hosts based requirement
+#endif
+    return (TRUE);
+  }
+  return (FALSE);
+}
+
+/* test if the controller is running */
+int CheckControllerStatus () {
+  return (ControllerStatus);
+}
+
+/* check job / get output if done */
+int CheckControllerJob (Job *job) {
+  struct timeval start, stop;
+
+  gettimeofday (&start, (void *) NULL);
+  CheckControllerJobStatus (job);
+  gettimeofday (&stop, (void *) NULL);
+  // float dtime = DTIME (stop, start);
+  // if (VerboseMode()) gprint (GP_ERR, "check job status %f\n", dtime);
+
+  if ((job[0].state == JOB_EXIT) || (job[0].state == JOB_CRASH)) {
+    gettimeofday (&start, (void *) NULL);
+    GetJobOutput ("stdout", job[0].pid, &job[0].stdout_buff, job[0].stdout_size);
+    gettimeofday (&stop, (void *) NULL);
+    // float dtime = DTIME (stop, start);
+    /* if (VerboseMode()) gprint (GP_ERR, "get stdout %f\n", dtime); */
+
+    gettimeofday (&start, (void *) NULL);
+    GetJobOutput ("stderr", job[0].pid, &job[0].stderr_buff, job[0].stderr_size);
+    gettimeofday (&stop, (void *) NULL);
+    // float dtime = DTIME (stop, start);
+    /* if (VerboseMode()) gprint (GP_ERR, "get stderr %f\n", dtime); */
+
+    gettimeofday (&start, (void *) NULL);
+    DeleteControllerJob (job);
+    gettimeofday (&stop, (void *) NULL);
+    // float dtime = DTIME (stop, start);
+    /* if (VerboseMode()) gprint (GP_ERR, "delete job %f\n", dtime); */
+  }
+  return (TRUE);
+}
+
+int DeleteControllerJob (Job *job) {
+
+  int status;
+  char cmd[128];
+  IOBuffer buffer;
+
+  sprintf (cmd, "delete %d", job[0].pid);
+  InitIOBuffer (&buffer, 0x100);
+  status = ControllerCommand (cmd, CONTROLLER_PROMPT, &buffer);
+  FreeIOBuffer (&buffer);
+  return (status);
+}
+
+/* ask controller about job status */
+int CheckControllerJobStatus (Job *job) {
+
+  int outstate, status;
+  char cmd[128], status_string[64], string[128];
+  char *p;
+  IOBuffer buffer;
+
+  sprintf (cmd, "check job %d", job[0].pid);
+  InitIOBuffer (&buffer, 0x100);
+  status = ControllerCommand (cmd, CONTROLLER_PROMPT, &buffer);
+  if (!status) {
+    FreeIOBuffer (&buffer);
+    return (FALSE);
+  }
+
+  /** was this a valid job? **/
+  p = memstr (buffer.buffer, "job not found", buffer.Nbuffer);
+  if (p != NULL) {
+    gprint (GP_ERR, "unknown job %d\n", job[0].pid);
+    FreeIOBuffer (&buffer);
+    return (FALSE);
+  }
+
+  /** parse status message **/
+  p = memstr (buffer.buffer, "STATUS",   buffer.Nbuffer);
+  if (p == NULL) goto escape;
+  sscanf (p, "%*s %s", status_string);
+  p = memstr (buffer.buffer, "EXITST",   buffer.Nbuffer);
+  if (p == NULL) goto escape;
+  sscanf (p, "%*s %d", &job[0].exit_status);
+  p = memstr (buffer.buffer, "STDOUT",   buffer.Nbuffer);
+  if (p == NULL) goto escape;
+  sscanf (p, "%*s %d", &job[0].stdout_size);
+  p = memstr (buffer.buffer, "STDERR",   buffer.Nbuffer);
+  if (p == NULL) goto escape;
+  sscanf (p, "%*s %d", &job[0].stderr_size);
+  p = memstr (buffer.buffer, "DTIME",    buffer.Nbuffer);
+  if (p == NULL) goto escape;
+  sscanf (p, "%*s %lf", &job[0].dtime);
+  p = memstr (buffer.buffer, "HOSTNAME", buffer.Nbuffer);
+  if (p == NULL) goto escape;
+  sscanf (p, "%*s %s", string);
+  job[0].realhost = strcreate (string);
+  FreeIOBuffer (&buffer);
+
+  /* possible exit status values */
+  outstate = -1;
+  if (!strcmp(status_string, "BUSY"))    outstate = JOB_BUSY;
+  if (!strcmp(status_string, "DONE"))    outstate = JOB_BUSY;
+  if (!strcmp(status_string, "PENDING")) outstate = JOB_PENDING;
+  if (!strcmp(status_string, "EXIT"))    outstate = JOB_EXIT;
+  if (!strcmp(status_string, "CRASH"))   outstate = JOB_CRASH;
+  if (outstate == -1) goto escape;
+
+  job[0].state = outstate;
+  return (TRUE);
+
+escape:
+  gprint (GP_ERR, "garbage in pcontrol reponse\n");
+  FreeIOBuffer (&buffer);
+  return (FALSE);
+}
+
+/* we read Nbytes from the host, then watch for the prompt */
+int GetJobOutput (char *cmd, int pid, IOBuffer *buffer, int Nbytes) {
+
+  int i, status, Nstart;
+  char *line;
+  struct timespec request, remain;
+
+  /* avoid blocking on waitpid, test every 100 usec, up to 50 msec */
+  request.tv_sec = 0;
+  request.tv_nsec = 100000;
+
+  /* flush any earlier messages */
+  ReadtoIOBuffer (buffer, stdout_cntl);
+  FlushIOBuffer (buffer);
+  Nstart = buffer[0].Nbuffer;
+
+  /* send command to get appropriate channel */
+  status = write_fmt (stdin_cntl, "%s %d\n", cmd, pid);
+
+  /* is pipe still open? */
+  // XXX call StopController() here?
+  if ((status == -1) && (errno == EPIPE)) {
+    StopController ();
+    return (CONTROLLER_DOWN);
+  }
+
+  /* read at least Nbytes, then watch for CONTROLLER_PROMPT */
+  line = NULL;
+  status = -1;
+  for (i = 0; (i < CONTROLLER_TIMEOUT) && (status != 0) && (line == NULL); i++) {
+    status = ReadtoIOBuffer (buffer, stdout_cntl);
+    if ((buffer[0].Nbuffer - Nstart) >= Nbytes) {
+      line = memstr (buffer[0].buffer, CONTROLLER_PROMPT, buffer[0].Nbuffer);
+    }
+    if (status == -1) nanosleep (&request, &remain);
+  }
+  if (status ==  0) {
+    StopController ();
+    return (CONTROLLER_DOWN);
+  }
+  if (status == -1) return (CONTROLLER_HUNG);
+
+  /* if (VerboseMode()) gprint (GP_ERR, "message received (GetJobOutput : %s)\n", cmd);   */
+  /* drop extra bytes from pcontrol (not pclient:job) */
+  buffer[0].Nbuffer = Nstart + Nbytes;
+  if (buffer[0].Nalloc > buffer[0].Nbuffer) {
+    bzero (buffer[0].buffer + buffer[0].Nbuffer, buffer[0].Nalloc - buffer[0].Nbuffer);
+  }
+  return (CONTROLLER_GOOD);
+}
+
+/* submitting a job to the controller automatically starts controller */
+int SubmitControllerJob (Job *job) {
+#ifndef IPP_CONDOR
+  fprintf(stderr, "Submitting job to pcontrol");
+  fprintf(stdout, "Submitting job to pcontrol");
+  int i, Nchar, status;
+  char *cmd, *p, string[64];
+  IOBuffer buffer;
+  if (job[0].task[0].host == NULL) return (FALSE);
+  if (!StartController ()) {
+    gprint (GP_ERR, "failure to start pcontrol\n");
+    return (FALSE);
+  }
+  /** construct the line to be sent to the controller **/
+  /* determine the total line length */
+  Nchar = 0;
+  for (i = 0; i < job[0].argc; i++) {
+    Nchar += strlen (job[0].argv[i]) + 1;
+  }
+  if (job[0].task[0].host) {
+    Nchar += strlen (job[0].task[0].host) + 1;
+  }
+  Nchar += 10;
+  ALLOCATE (cmd, char, Nchar);
+  bzero (cmd, Nchar);
+  /* construct the controller command portion */
+  if (!strcasecmp (job[0].task[0].host, "ANYHOST")) {
+    sprintf (cmd, "job");
+  } else {
+    if (job[0].task[0].host_required) {
+      sprintf (cmd, "job +host %s", job[0].task[0].host);
+    } else {
+      sprintf (cmd, "job -host %s", job[0].task[0].host);
+    }
+  }
+  if (job[0].priority) {
+    char tmp[64];
+    snprintf (tmp, 64, " -nice %d", job[0].priority);
+    strcat (cmd, tmp);
+  }
+  /* add the command arguments */
+  for (i = 0; i < job[0].task[0].argc; i++) {
+    strcat (cmd, " ");
+    strcat (cmd, job[0].task[0].argv[i]);
+  }
+  // This function is called by the JobTaskThread via SubmitJob.  We need to unlock the
+  // JobTaskLock to avoid a dead lock with the JobTaskLock called in CheckController
+  JobTaskUnlock();
+  ControlLock(__func__);
+  InitIOBuffer (&buffer, 0x100);
+  status = ControllerCommand (cmd, CONTROLLER_PROMPT, &buffer);
+  free (cmd);
+  ControlUnlock(__func__);
+  JobTaskLock();
+  /* extract the job PID from the controller response */
+  p = memstr (buffer.buffer, "JobID", buffer.Nbuffer);
+  if (p == NULL) {
+    gprint (GP_ERR, "missing PID in pcontrol message : programming error\n");
+    gprint (GP_ERR, "ControllerCommand returns: %d\n", status);
+    gprint (GP_ERR, "ControllerCommand response: %s\n", buffer.buffer);
+    exit (1);
+  }
+  sscanf (p, "%*s %s", string);
+  FreeIOBuffer (&buffer);
+  job[0].pid = atoi (string);
+  if (job[0].pid < 0) {
+    return (FALSE);
+  }
+#else
+  struct ns1__Transaction *transaction;
+  int clusterId;
+  //Create a Transaction
+  transaction = beginTransaction(&soap, 10);
+  //Get a clusterId
+  clusterId = newCluster(&soap, transaction);
+  fprintf(stderr, "ClusterId = %d\n", clusterId);
+  fprintf(stdout, "Submitting job to HTCondor\n");
+  char *args = paste_args(job[0].task[0].argc-1, job[0].task[0].argv+1);
+  submitJob(&soap, transaction, clusterId, 0,
+            job[0].task[0].argv[0],
+            args,
+            get_hosts_requirements());
+  commitTransaction(&soap, transaction);
+#endif
+  return (TRUE);
+}
+
+int StartController () {
+#ifndef IPP_CONDOR
+  char *p;
+  char **argv, cmd[128];
+  int i, pid, status;
+  int stdin_fd[2], stdout_fd[2], stderr_fd[2];
+  IOBuffer buffer;
+  if (ControllerStatus) return (TRUE);
+  if (VarConfig ("CONTROLLER", "%s", cmd) == NULL) strcpy (cmd, "pcontrol");
+  if (hosts == NULL) {
+    NHOSTS = 16;
+    ALLOCATE (hosts, Host, NHOSTS);
+  }
+  bzero (stdin_fd,  2*sizeof(int));
+  bzero (stdout_fd, 2*sizeof(int));
+  bzero (stderr_fd, 2*sizeof(int));
+  if (pipe (stdin_fd)  < 0) goto pipe_error;
+  if (pipe (stdout_fd) < 0) goto pipe_error;
+  if (pipe (stderr_fd) < 0) goto pipe_error;
+  ALLOCATE (argv, char *, 2);
+  argv[0] = cmd;
+  argv[1] = 0;
+  pid = fork ();
+  if (!pid) { /* must be child process */
+    gprint (GP_LOG, "starting controller connection\n");
+    /* close the other ends of the pipes */
+    close (stdin_fd[1]);
+    close (stdout_fd[0]);
+    close (stderr_fd[0]);
+    /* tie our ends of the pipes to stdin, stdout, stderr */
+    dup2 (stdin_fd[0],  STDIN_FILENO);
+    dup2 (stdout_fd[1], STDOUT_FILENO);
+    dup2 (stderr_fd[1], STDERR_FILENO);
+    /* set all three unblocking */
+    setvbuf (stdin,  (char *) NULL, _IONBF, BUFSIZ);
+    setvbuf (stdout, (char *) NULL, _IONBF, BUFSIZ);
+    setvbuf (stderr, (char *) NULL, _IONBF, BUFSIZ);
+    status = execvp (argv[0], argv);
+    exit (1);
+  }
+  free (argv);
+  /* close the other ends of the pipes */
+  close (stdin_fd[0]);  stdin_fd[0]  = 0;
+  close (stdout_fd[1]); stdout_fd[1] = 0;
+  close (stderr_fd[1]); stderr_fd[1] = 0;
+  /* make the pipes non-blocking */
+  fcntl (stdin_fd[1],  F_SETFL, O_NONBLOCK);
+  fcntl (stdout_fd[0], F_SETFL, O_NONBLOCK);
+  fcntl (stderr_fd[0], F_SETFL, O_NONBLOCK);
+  /* perform handshake with controller to verify alive & running */
+  /** this handshake is similar to ControllerCommand, but has important differences **/
+  InitIOBuffer (&buffer, 0x100);
+  /* send handshake command */
+  status = write_fmt (stdin_fd[1], "echo CONNECTED\n");
+  if ((status == -1) && (errno == EPIPE)) goto pipe_error;
+  /* try to get evidence connection is alive - wait upto a few seconds */
+  /* connection is likely slow; don't bother with nanosleep here */
+  p = NULL;
+  status = -1;
+  for (i = 0; (i < CONNECT_TIMEOUT) && (status != 0) && (p == NULL); i++) {
+    status = ReadtoIOBuffer (&buffer, stdout_fd[0]);
+    p = memstr (buffer.buffer, "CONNECTED", buffer.Nbuffer);
+    usleep (50000); // wait for controller to start up
+  }
+  if (status == 0) goto pipe_error;
+  if (status == -1) goto io_error;
+  FreeIOBuffer (&buffer);
+  /* set local static vars to pipe connections */
+  stdin_cntl  = stdin_fd[1];
+  stdout_cntl = stdout_fd[0];
+  stderr_cntl = stderr_fd[0];
+  InitIOBuffer (&stdout_buffer, 0x100);
+  InitIOBuffer (&stderr_buffer, 0x100);
+  ControllerPID = pid;
+#else
+  if (ControllerStatus == FALSE) {
+      //Initiate the HTcondor connection
+      soap_init(&soap);
+      soap_set_namespaces(&soap, condor_namespaces);
+  }
+#endif
+  ControllerStatus = TRUE;
+  gprint (GP_LOG, "Connected\n");
+  return (TRUE);
+#ifndef IPP_CONDOR
+pipe_error:
+  perror ("pipe error:");
+  goto close_pipes;
+io_error:
+  gprint (GP_ERR, "timeout while connecting\n");
+  goto close_pipes;
+close_pipes:
+  if (stdin_fd[0]  != 0) close (stdin_fd[0]);
+  if (stdin_fd[1]  != 0) close (stdin_fd[1]);
+  if (stdout_fd[0] != 0) close (stdout_fd[0]);
+  if (stdout_fd[1] != 0) close (stdout_fd[1]);
+  if (stderr_fd[0] != 0) close (stderr_fd[0]);
+  if (stderr_fd[1] != 0) close (stderr_fd[1]);
+  return (FALSE);
+#endif
+}
+
+int ControllerCommand (char *cmd, char *response, IOBuffer *buffer) {
+#ifndef IPP_CONDOR
+  int i, j, status;
+  char *line;
+  struct timespec request, remain;
+  //fprintf(stderr, "ControllerCommand: [%s]\n", cmd);
+  /* avoid blocking on waitpid, test every 100 usec, up to 50 msec */
+  request.tv_sec = 0;
+  request.tv_nsec = 100000;
+  ReadtoIOBuffer (buffer, stdout_cntl);
+  FlushIOBuffer (buffer);
+  /* send command, is pipe still open? */
+  status = write_fmt (stdin_cntl, "%s\n", cmd);
+  if ((status == -1) && (errno == EPIPE)) {
+    StopController ();
+    fprintf (stderr, "controller is down (pipe closed), restarting\n");
+    if (!RestartController ()) {
+      return (FALSE);
+    }
+    return (FALSE);
+  }
+  /* for commands which don't return a prompt, don't look for one */
+  if (response == NULL) {
+    return (TRUE);
+  }
+  /* watch for response - wait up to 1 second */
+  line = NULL;
+  status = -1;
+  for (j = 0; (status == -1) && (j < 10); j++) {
+    for (i = 0; (i < CONTROLLER_TIMEOUT) && (status != 0) && (line == NULL); i++) {
+      status = ReadtoIOBuffer (buffer, stdout_cntl);
+      line = memstr (buffer[0].buffer, response, buffer[0].Nbuffer);
+      if (status == -1) nanosleep (&request, &remain);
+    }
+    if (status ==  0) {
+      StopController ();
+      fprintf (stderr, "controller is down (EOF), restarting\n");
+      if (!RestartController ()) {
+        return (FALSE);
+      }
+      return (FALSE);
+    }
+    if (status == -1) {
+      fprintf (stderr, "controller is not responding (%d tries)\n", j);
+      gwrite (buffer[0].buffer, 1, buffer[0].Nbuffer, GP_ERR);
+    }
+  }
+  if (status == -1) {
+    fprintf (stderr, "controller still not responding, giving up\n");
+    return (FALSE);
+  }
+  /* need to strip off the prompt */
+  line = memstr (buffer[0].buffer, response, buffer[0].Nbuffer);
+  if (line != NULL) {
+    buffer[0].Nbuffer = line - buffer[0].buffer;
+    bzero (buffer[0].buffer + buffer[0].Nbuffer, buffer[0].Nalloc - buffer[0].Nbuffer);
+  }
+  if (VerboseMode()) fprintf (stderr, "message received, %d cycles\n", i);
+#endif
+  return (TRUE);
+}
+
+int CheckControllerOutput () {
+
+  int Nread;
+
+  if (!ControllerStatus) return (TRUE);
+
+  /* read stdout buffer */
+  Nread = ReadtoIOBuffer (&stdout_buffer, stdout_cntl);
+  switch (Nread) {
+    case -2:  /* error in read (programming error?  system level error?) */
+      gprint (GP_ERR, "serious IO error\n");
+      exit (2);
+    case -1:  /* no data in pipe */
+    case 0:   /* pipe is closed, change child state? **/
+    default:  /* data in pipe */
+      break;
+  }
+
+  /* read stderr buffer */
+  Nread = ReadtoIOBuffer (&stderr_buffer, stderr_cntl);
+  switch (Nread) {
+    case -2:  /* error in read (programming error?  system level error?) */
+      gprint (GP_ERR, "serious IO error\n");
+      exit (2);
+    case -1:  /* no data in pipe */
+    case 0:   /* pipe is closed, change child state? **/
+    default:  /* data in pipe */
+      break;
+  }
+  return (TRUE);
+}
+
+void PrintControllerBusyJobs () {
+
+  int status;
+  char command[1024];
+  IOBuffer buffer;
+
+  /* check if controller is running */
+  status = CheckControllerStatus ();
+  if (!status) {
+    return;
+  }
+
+  sprintf (command, "jobstack busy");
+  InitIOBuffer (&buffer, 0x100);
+
+  status = ControllerCommand (command, CONTROLLER_PROMPT, &buffer);
+
+  if (status) {
+    gprint (GP_LOG, " jobs currently running remotely:\n");
+    gwrite (buffer.buffer, 1, buffer.Nbuffer, GP_LOG);
+  } else {
+    gprint (GP_LOG, "controller is not responding\n");
+  }
+  FreeIOBuffer (&buffer);
+  return;
+}
+
+int PrintControllerOutput () {
+
+  gprint (GP_LOG, "--- stdout ---\n");
+  gwrite (stdout_buffer.buffer, 1, stdout_buffer.Nbuffer, GP_LOG);
+  gprint (GP_LOG, "--- stderr ---\n");
+  gwrite (stderr_buffer.buffer, 1, stderr_buffer.Nbuffer, GP_LOG);
+  gprint (GP_LOG, "---  done  ---\n");
+  return (TRUE);
+}
+
+int FlushControllerOutput () {
+
+  FlushIOBuffer (&stdout_buffer);
+  FlushIOBuffer (&stderr_buffer);
+
+  return (TRUE);
+}
+
+int KillControllerJob (Job *job) {
+
+  int status;
+  char cmd[128];
+  IOBuffer buffer;
+
+  if (!ControllerStatus) return (TRUE);
+
+  sprintf (cmd, "kill %d", job[0].pid);
+  InitIOBuffer (&buffer, 0x100);
+  status = ControllerCommand (cmd, CONTROLLER_PROMPT, &buffer);
+  FreeIOBuffer (&buffer);
+  return (status);
+
+  /** need to interpret output message & free things **/
+}
+
+int QuitController () {
+#ifndef IPP_CONDOR
+  char cmd[128];
+  IOBuffer buffer;
+  if (!ControllerStatus) return (TRUE);
+  sprintf (cmd, "quit");
+  InitIOBuffer (&buffer, 0x100);
+  ControllerCommand (cmd, NULL, &buffer);
+  FreeIOBuffer (&buffer);
+  /* the quit command does not return a prompt,
+     check that the controller exited */
+  StopController ();
+#else
+  soap_destroy(&soap);
+  soap_end(&soap);
+  soap_done(&soap);
+#endif
+  return (TRUE);
+}
+
+int StopController () {
+#ifndef IPP_CONDOR
+  int i, waitstatus, result;
+  if (!ControllerStatus) return (TRUE);
+  ControllerStatus = FALSE;
+  result = waitpid (ControllerPID, &waitstatus, WNOHANG);
+  for (i = 0; (i < 10) && (result == 0); i++) {
+    usleep (10000);  // wait for controller to exit
+    result = waitpid (ControllerPID, &waitstatus, WNOHANG);
+  }
+  ControllerPID = 0;
+  close (stdin_cntl);
+  close (stdout_cntl);
+  close (stderr_cntl);
+  FreeIOBuffer (&stdout_buffer);
+  FreeIOBuffer (&stderr_buffer);
+#endif
+  return (TRUE);
+}
+
+int RestartController () {
+#ifndef IPP_CONDOR
+  int i, status;
+  char command[256];
+  IOBuffer buffer;
+  gprint (GP_ERR, "attempting to restart pcontrol\n");
+  if (!StartController ()) {
+    gprint (GP_ERR, "failure to re-start pcontrol\n");
+    return (FALSE);
+  }
+  InitIOBuffer (&buffer, 0x100);
+  status = TRUE;
+  // XXX lock the host table? no: that would risk a dead lock between client and controller threads:
+  gprint (GP_ERR, "pcontrol restarted, reloading hosts\n");
+  for (i = 0; i < Nhosts; i++) {
+    snprintf (command, 256, "host add %s -threads %d\n", hosts[i].hostname, hosts[i].max_threads);
+    gprint (GP_ERR, "sending: %s\n", command);
+    status = ControllerCommand (command, CONTROLLER_PROMPT, &buffer);
+  }
+  if (status) gwrite (buffer.buffer, 1, buffer.Nbuffer, GP_LOG);
+  FreeIOBuffer (&buffer);
+#endif
+  return (TRUE);
+}
+
+#ifdef IPP_CONDOR
+char* get_hosts_requirements(void) {
+    int i;
+    int hr_length;
+    char* base_requirement_pattern_b = " (machine == \"";
+    char* base_requirement_pattern_e = "\")";
+    char* base_requirement_pattern_or = " || ";
+    if ( (hosts_requirement != NULL) && (hosts_list_modified == FALSE) ) {
+        return hosts_requirement;
+    }
+    if (hosts_requirement != NULL) {
+        free(hosts_requirement);
+    }
+    printf("Generating hosts based requirement\n");
+    hosts_requirement = strcreate("");
+    hr_length = strlen(hosts_requirement);
+    if (Nhosts > 0) {
+        hosts_requirement = opihi_append(hosts_requirement, &hr_length, "( ", NULL);
+        for (i = 0; i < Nhosts; i++) {
+            hosts_requirement = opihi_append(hosts_requirement, &hr_length,
+                                             base_requirement_pattern_b, NULL);
+            hosts_requirement = opihi_append(hosts_requirement, &hr_length,
+                                             hosts[i].hostname, NULL);
+            hosts_requirement = opihi_append(hosts_requirement, &hr_length,
+                                             base_requirement_pattern_e, NULL);
+            if (i != Nhosts-1) {
+                hosts_requirement = opihi_append(hosts_requirement, &hr_length,
+                                                 base_requirement_pattern_or, NULL);
+            }
+        }
+    } else { // No host defined: leave it empty
+        //pass
+    }
+    hosts_list_modified = FALSE;
+    printf("end of Generating hosts based requirement\n");
+    return hosts_requirement;
+}
+
+static int is_host_in_list(char *hostname) {
+    int i;
+    for (i = 0; i < Nhosts; i++) {
+        if (strcmp(hosts[i].hostname, hostname) == 0) {
+            return i;
+        }
+    }
+    return -1;
+}
+
+char *get_host_status(char* hostname) {
+    return getHostStatus(&soap, hostname);
+}
+
+#endif
Index: /branches/sc_branches/pantasks_condor/pantasks/InputQueue.c
===================================================================
--- /branches/sc_branches/pantasks_condor/pantasks/InputQueue.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/pantasks/InputQueue.c	(revision 34783)
@@ -0,0 +1,93 @@
+# include "pantasks.h"
+// we use fprintf for DEBUG statements to avoid deadlocking issues
+
+static int Ninputs = 0;
+static int NINPUTS = 0;
+static char **inputs;
+
+void InitInputs () {
+
+  Ninputs = 0;
+  NINPUTS = 10;
+  ALLOCATE (inputs, char *, NINPUTS);
+}
+
+void FreeInputs () {
+  int i;
+  for (i = 0; i < Ninputs; i++) {
+    free (inputs[i]);
+  }
+  free (inputs);
+}
+
+/* add this input to the inputs table */
+void AddNewInput (char *input) {
+
+  // XXX define the InputMutex
+  SerialThreadLock ();
+
+  if (DEBUG) fprintf (stderr, "adding a new input (%s)\n", input);
+  inputs[Ninputs] = input;
+  Ninputs ++;
+  if (Ninputs >= NINPUTS - 1) {
+    NINPUTS += 10;
+    REALLOCATE (inputs, char *, NINPUTS);
+  }
+  if (DEBUG) fprintf (stderr, "done new input (%s)\n", input);
+  SerialThreadUnlock ();
+}
+
+/* remove this input from the inputs table */
+int DeleteInput (char *input) {
+
+  int i, j;
+
+  if (DEBUG) fprintf (stderr, "deleting an input (%s)\n", input);
+
+  // XXX lock here
+  for (i = 0; i < Ninputs; i++) {
+    if (inputs[i] == input) {
+      free (inputs[i]);
+      for (j = i; j < Ninputs - 1; j++) {
+	inputs[j] = inputs[j+1];
+      }
+      Ninputs --;
+      if ((Ninputs > 10) && (Ninputs / 2 < NINPUTS)) {
+	NINPUTS = Ninputs + 10;
+	REALLOCATE (inputs, char *, NINPUTS);
+      }
+      // XXX unlock here
+      if (DEBUG) fprintf (stderr, "deleted an input\n");
+      return TRUE;
+    }
+  }
+  // did not find the input
+  // XXX unlock here
+  return FALSE;
+}
+
+/* if any inputs are pending, run one */
+void CheckInputs () {
+
+  int Nbytes, status;
+  char *input, *line, *outline, tmp;
+
+  // XXX lock here
+  if (Ninputs < 1) {
+    // XXX unlock here
+    return;
+  }
+
+  input = inputs[0];
+  if (DEBUG) fprintf (stderr, "got an input (%s)\n", input);
+  
+  Nbytes = snprintf (&tmp, 0, "input %s", input);
+  ALLOCATE (line, char, Nbytes + 1);
+  snprintf (line, Nbytes + 1, "input %s", input);
+  // XXX unlock here
+
+  status = command (line, &outline, TRUE);
+  free (outline);
+  
+  DeleteInput (input);
+}
Index: /branches/sc_branches/pantasks_condor/pantasks/JobIDOps.c
===================================================================
--- /branches/sc_branches/pantasks_condor/pantasks/JobIDOps.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/pantasks/JobIDOps.c	(revision 34783)
@@ -0,0 +1,44 @@
+# include "pantasks.h"
+
+# define MAX_N_JOBS 10000
+static char *JobIDList;
+static int   JobIDPtr;
+
+void InitJobIDs () {
+
+  JobIDPtr = 0;
+  ALLOCATE (JobIDList, char, MAX_N_JOBS);
+  bzero (JobIDList, MAX_N_JOBS*sizeof(char));
+}  
+
+void FreeJobIDs () {
+  free (JobIDList);
+}
+
+/* return next unique ID, recycle every MAX_N_JOBS */
+int NextJobID () {
+
+  int Ntry;
+
+  JobIDPtr ++;
+  if (JobIDPtr >= MAX_N_JOBS) JobIDPtr = 0;
+
+  Ntry = 0;
+  while (JobIDList[JobIDPtr]) {
+    Ntry ++;
+    JobIDPtr ++;
+    if (JobIDPtr >= MAX_N_JOBS) JobIDPtr = 0;
+    if (Ntry == MAX_N_JOBS) return (-1);
+  }
+  JobIDList[JobIDPtr] = TRUE;
+  return (JobIDPtr);
+}
+
+int FreeJobID (int JobID) {
+
+  if (JobID < 0) return (FALSE);
+  if (JobID >= MAX_N_JOBS) return (FALSE);
+
+  JobIDList[JobID] = FALSE;
+  return (TRUE);
+}
Index: /branches/sc_branches/pantasks_condor/pantasks/JobOps.c
===================================================================
--- /branches/sc_branches/pantasks_condor/pantasks/JobOps.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/pantasks/JobOps.c	(revision 34783)
@@ -0,0 +1,313 @@
+# include "pantasks.h"
+
+static Job **jobs;
+static int   Njobs;
+static int   NJOBS;
+
+/* counter marking job being visited by the run loop */
+static int   ActiveJob;
+
+/* set up the jobs list */
+void InitJobs () {
+  NJOBS = 20;
+  Njobs = 0;
+  ALLOCATE (jobs, Job *, NJOBS);
+  ActiveJob = -1;
+}
+
+/* free all jobs, only used on shutdown */
+void FreeJobs () {
+  int i;
+  for (i = 0; i < Njobs; i++) {
+    FreeJob (jobs[i]);
+  }
+  free (jobs);
+}
+
+/* provide a mechanism to loop over the list of jobs */
+Job *NextJob () {
+
+  Job *job;
+
+  ActiveJob ++;
+  if (ActiveJob < 0) ActiveJob = 0;
+  if (ActiveJob >= Njobs) {
+    ActiveJob = -1;
+    return (NULL);
+  }
+  job = jobs[ActiveJob];
+  return (job);
+}
+
+/* return job with given ID */
+Job *FindJob (int JobID) {
+
+  int i;
+
+  /* return job with matching JobID */
+  for (i = 0; i < Njobs; i++) {
+    if (jobs[i][0].JobID == JobID) {
+      return (jobs[i]);
+    }
+  }
+  return (NULL);
+}
+
+/* return job with given controller Job ID */
+Job *FindControllerJob (int JobID) {
+
+  int i;
+
+  /* return job with matching JobID */
+  for (i = 0; i < Njobs; i++) {
+    if (jobs[i][0].mode == JOB_LOCAL) continue;
+    if (jobs[i][0].pid  == JobID) {
+      return (jobs[i]);
+    }
+  }
+  return (NULL);
+}
+
+/* list known jobs */
+void ListJobs () {
+
+  int i;
+
+  gprint (GP_LOG, "\n");
+  if (Njobs == 0) {
+    gprint (GP_LOG, " no defined jobs\n");
+    return;
+  }
+
+  gprint (GP_LOG, " Jobs in Pantasks Queue\n");
+  for (i = 0; i < Njobs; i++) {
+    gprint (GP_LOG, " %4d  %6d: %-25s %10s %20s\n", Njobs, jobs[i][0].JobID, jobs[i][0].task[0].name, JobStateToString(jobs[i][0].state), jobs[i][0].argv[0]);
+  }
+
+  return;
+}
+
+/* make a new job from a task */
+Job *CreateJob (Task *task) {
+
+  int i;
+  Job *job;
+
+  ALLOCATE (job, Job, 1);
+
+  job[0].JobID = NextJobID ();
+  if (job[0].JobID < 0) {
+    free (job);
+    return NULL;
+  }
+
+  job[0].pid = 0;
+  job[0].mode = JOB_LOCAL;
+  if (task[0].host != NULL) {
+    job[0].mode = JOB_CONTROLLER;
+  }
+  job[0].priority = task[0].priority;
+
+  /* we need our own copy of task[0].argv argc is the number of valid args, like the usual command line.  we
+   *  allocate one extra element, with value 0 to be passed to execvp
+   */
+  job[0].argc = task[0].argc;
+  ALLOCATE (job[0].argv, char *, MAX (task[0].argc + 1, 1));
+  for (i = 0; i < task[0].argc; i++) {
+    job[0].argv[i] = strcreate (task[0].argv[i]);
+  }
+  job[0].argv[i] = 0;
+
+  /* we need our own copy of task[0].optv: optc is the number of valid opts.  */
+  job[0].optc = task[0].optc;
+  ALLOCATE (job[0].optv, char *, MAX (task[0].optc, 1));
+  for (i = 0; i < job[0].optc; i++) {
+    job[0].optv[i] = strcreate (task[0].optv[i]);
+  }
+
+  /* Other data from the task is needed by the job. We carry a pointer back to the task.  Changes to an
+     executing task are applied to the existing jobs (exit macros, poll_period, timeout) */
+
+  job[0].task = task;
+  job[0].realhost = NULL;
+
+  /* if we decide we need to be able to dynamically set task qualities (like host, timeouts, etc), the we will
+     need to have matched entries to these quantites in the job structure */
+
+  InitIOBuffer (&job[0].stdout_buff, 0x100);
+  InitIOBuffer (&job[0].stderr_buff, 0x100);
+
+  job[0].stdout_dump = NULL;
+  job[0].stderr_dump = NULL;
+  if (task[0].stdout_dump != NULL) job[0].stdout_dump = strcreate (task[0].stdout_dump);
+  if (task[0].stderr_dump != NULL) job[0].stderr_dump = strcreate (task[0].stderr_dump);
+
+  job[0].stdout_fd = -1;
+  job[0].stderr_fd = -1;
+
+  jobs[Njobs] = job;
+  Njobs ++;
+  if (Njobs == NJOBS) {
+    NJOBS += 20;
+    REALLOCATE (jobs, Job *, NJOBS);
+  }
+
+  /* increment job counters */
+  task[0].Npending ++;
+
+  return (jobs[Njobs-1]);
+}
+
+void FreeJob (Job *job) {
+
+  int i;
+
+  if (job == NULL) return;
+
+  FreeJobID (job[0].JobID);
+
+  for (i = 0; i < job[0].argc; i++) {
+    free (job[0].argv[i]);
+  }
+  free (job[0].argv);
+
+  for (i = 0; i < job[0].optc; i++) {
+    free (job[0].optv[i]);
+  }
+  free (job[0].optv);
+
+  if (job[0].stdout_fd >= 0) close (job[0].stdout_fd);
+  if (job[0].stderr_fd >= 0) close (job[0].stderr_fd);
+
+  if (job[0].stdout_dump != NULL) free (job[0].stdout_dump);
+  if (job[0].stderr_dump != NULL) free (job[0].stderr_dump);
+  if (job[0].realhost != NULL) free (job[0].realhost);
+
+  FreeIOBuffer (&job[0].stdout_buff);
+  FreeIOBuffer (&job[0].stderr_buff);
+  free (job);
+  return;
+}
+
+/* delete the job from the job list & adjust ActiveJob counter */
+int DeleteJob (Job *job) {
+
+  int i, Nm;
+
+  Nm = -1;
+  for (i = 0; i < Njobs; i++) {
+    if (job == jobs[i]) {
+      Nm = i;
+      break;
+    }
+  }
+  if (Nm == -1) {
+    gprint (GP_ERR, "programming error: job not found\n");
+    return (FALSE);
+  }
+
+  FreeJob (jobs[Nm]);
+  for (i = Nm; i < Njobs - 1; i++) {
+    jobs[i] = jobs[i + 1];
+  }
+  Njobs --;
+
+  /* adjust active job number */
+  if (ActiveJob >= Nm) {
+    ActiveJob --;
+  }
+
+  job[0].task[0].Npending --;
+  return (TRUE);
+}
+
+int FlushJobs () {
+
+  int i;
+
+  // kill outstanding jobs
+  for (i = 0; i < Njobs; i++) {
+    if (jobs[i][0].mode == JOB_LOCAL) {
+      if (!KillLocalJob (jobs[i])) {
+        jobs[i][0].state = JOB_HUNG;
+        if (VerboseMode()) gprint (GP_LOG, "child process %d is hung, cannot kill\n", jobs[i][0].pid);
+      }
+    } else {
+      if (!KillControllerJob (jobs[i])) {
+        jobs[i][0].state = JOB_HUNG;
+        if (VerboseMode()) gprint (GP_LOG, "child process %d is hung, cannot kill\n", jobs[i][0].pid);
+      }
+    }
+    jobs[i][0].task[0].Npending = 0;
+    FreeJob (jobs[i]);
+  }
+
+  Njobs = 0;
+  NJOBS = 20;
+  REALLOCATE (jobs, Job *, NJOBS);
+  ActiveJob = -1;
+  return (TRUE);
+}
+
+int SubmitJob (Job *job) {
+
+  int status;
+
+  if (job[0].mode == JOB_LOCAL) {
+      if (DEBUG) { fprintf (stderr, "submit job: (%zx) %d of %d\n", (size_t) job[0].stdout_buff.buffer, job[0].stdout_buff.Nbuffer, job[0].stdout_buff.Nalloc); }
+      fprintf (stdout, "submit job (local)\n");
+      status = SubmitLocalJob (job);
+  } else {
+      fprintf (stdout, "submit job (non local)\n");
+      status = SubmitControllerJob (job);
+  }
+  if (!status) {
+    return FALSE;
+  }
+
+  /* reset clock for start and poll-test */
+  gettimeofday (&job[0].start, (void *) NULL);
+  job[0].last = job[0].start;
+  job[0].state = JOB_PENDING;
+  return (TRUE);
+}
+
+int CheckJob (Job *job) {
+
+  /* add checks for timeouts */
+
+  if (job[0].mode == JOB_LOCAL) {
+    CheckLocalJob (job);
+    /* controller jobs are checked en masse by CheckController */
+  }
+  return (job[0].state);
+}
+
+static char *JobStateNone    = "NONE";
+static char *JobStateBusy    = "BUSY";
+static char *JobStateExit    = "EXIT";
+static char *JobStateHung    = "HUNG";
+static char *JobStateCrash   = "CRASH";
+static char *JobStatePending = "PENDING";
+
+char *JobStateToString (JobStat state) {
+
+  switch (state) {
+    case JOB_NONE:
+      return JobStateNone;
+    case JOB_BUSY:
+      return JobStateBusy;
+    case JOB_EXIT:
+      return JobStateExit;
+    case JOB_HUNG:
+      return JobStateHung;
+    case JOB_CRASH:
+      return JobStateCrash;
+    case JOB_PENDING:
+      return JobStatePending;
+    default:
+      return JobStateNone;
+  }
+  return JobStateNone;
+}
+
Index: /branches/sc_branches/pantasks_condor/pantasks/ListenClients.c
===================================================================
--- /branches/sc_branches/pantasks_condor/pantasks/ListenClients.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/pantasks/ListenClients.c	(revision 34783)
@@ -0,0 +1,180 @@
+# include "pantasks.h"
+# define DEBUG 0
+
+// XXX make the calling functions thread-safe
+static int NCLIENTS;
+static int Nclients;
+static int *clients;
+static IOBuffer **buffers;
+
+void InitClients () {
+
+  ClientLock();
+  Nclients = 0;
+  NCLIENTS = 10;
+  ALLOCATE (clients, int, NCLIENTS);
+  ALLOCATE (buffers, IOBuffer *, NCLIENTS);
+  ClientUnlock();
+}
+
+/* add this client to the client table, create an IOBuffer for it */
+void AddNewClient (int client) {
+
+  ClientLock();
+  if (DEBUG) fprintf (stderr, "adding a new client (%d)\n", client);
+  clients[Nclients] = client;
+  ALLOCATE (buffers[Nclients], IOBuffer, 1);
+  InitIOBuffer(buffers[Nclients], 256);
+  Nclients ++;
+  if (Nclients >= NCLIENTS - 1) {
+    NCLIENTS += 10;
+    REALLOCATE (clients, int, NCLIENTS);
+    REALLOCATE (buffers, IOBuffer *, NCLIENTS);
+  }
+  ClientUnlock();
+}
+
+/* add this client to the client table, create an IOBuffer for it */
+int DeleteClient (int client) {
+
+  int i, j;
+
+  ClientLock();
+  if (DEBUG) fprintf (stderr, "deleting a client (%d)\n", client);
+  for (i = 0; i < Nclients; i++) {
+    if (clients[i] == client) {
+      FreeIOBuffer (buffers[i]);
+      free (buffers[i]);
+      close (clients[i]);
+      for (j = i; j < Nclients - 1; j++) {
+	clients[j] = clients[j+1];
+	buffers[j] = buffers[j+1];
+      }
+      Nclients --;
+      if ((Nclients > 10) && (Nclients / 2 < NCLIENTS)) {
+	NCLIENTS = Nclients + 10;
+	REALLOCATE (clients, int, NCLIENTS);
+	REALLOCATE (buffers, IOBuffer *, NCLIENTS);
+      }
+      ClientUnlock();
+      return TRUE;
+    }
+  }
+  // did not find the client
+  ClientUnlock();
+  return FALSE;
+}
+
+/* select for messages from the current clients; wait for 0.5s before updating client list */
+void *ListenClients (void *data) {
+  
+  int i, Ncurrent, Nmax, status, Nread;
+  char *line;
+  fd_set fdSet;
+  struct timeval timeout;
+  IOBuffer *outbuffer;
+
+  InitClients ();
+  gprintInit ();  // each thread needs to init the printing system
+
+  /* set buffers for the output for this client */
+  gprintSetBuffer (GP_LOG);
+  gprintSetBuffer (GP_ERR);
+
+  while (1) {
+
+    /* Wait up to 0.5 second - need to timeout to update client list */
+    /* timeout gets mucked: need to reset before each select */
+    timeout.tv_sec = 0;
+    timeout.tv_usec = 500000;
+
+    /* place all of the clients in the fdSet */
+    Nmax = 0;
+    FD_ZERO (&fdSet);
+    ClientLock();
+    Ncurrent = Nclients;
+    ClientUnlock();
+    for (i = 0; i < Ncurrent; i++) {
+      Nmax = MAX (Nmax, clients[i]);
+      FD_SET (clients[i], &fdSet);
+    }    
+    Nmax ++;
+
+    /* block until we have some data on the pipes (or timeout) */
+    if (DEBUG) fprintf (stderr, "listening to %d clients\n", Ncurrent);
+    status = select (Nmax, &fdSet, NULL, NULL, &timeout);
+    if (status == -1) {
+      perror("select()");
+      return (FALSE);
+    }
+
+    /* if no data, update client list, wait for another select */
+    if (status <= 0) continue;
+
+    /* loop over the clients with data */
+    for (i = 0; i < Ncurrent; i++) {
+      /* if client has no data, skip it */
+      if (!FD_ISSET(clients[i], &fdSet)) continue;
+
+      /* read until the pipe is empty: 0 is closed, -1 is empty, -2 is error */
+      Nread = 1;
+      while (Nread > 0) {
+	Nread = ReadtoIOBuffer (buffers[i], clients[i]);	
+      }
+      if ((Nread == 0) || (Nread == -2)) {
+	/* error: do something */
+	if (DEBUG && (Nread == 0)) fprintf (stderr, "socket is closed\n");
+	if (DEBUG && (Nread == -2)) fprintf (stderr, "error reading from socket\n");
+	DeleteClient (clients[i]);
+	break;  // the other thread could also have modified the list; restart with new Ncurrent
+      }
+
+      if (DEBUG) fprintf (stderr, "read %d total bytes\n", buffers[i][0].Nbuffer);
+
+      /* see if we have a complete message waiting; if not, keep waiting for messages */
+      line = CheckForMessage (buffers[i]);
+      if (line == NULL) continue;
+
+      /* we now have a possible command from the client: run it */
+      /* in this thread, we set the print output destination to be an
+	 internal buffer, which we dump at the end of the execution */
+      /* the commands sent to the server should not have ; */
+      stripwhite (line);
+      if (*line) {
+
+	/* run the command, return the exit status */
+	CommandLock();
+	status = multicommand (line);
+	CommandUnlock();
+	SendMessage (clients[i], "STATUS %d", status);
+
+	// return the stderr messages first
+	outbuffer = gprintGetBuffer (GP_ERR);
+	if (outbuffer) {
+	  SendMessageFixed (clients[i], outbuffer[0].Nbuffer, outbuffer[0].buffer);
+	} else {
+	  SendMessageFixed (clients[i], 0, "");
+	}	  
+	FlushIOBuffer (outbuffer);
+
+	// return the stdout messages first
+	outbuffer = gprintGetBuffer (GP_LOG);
+	if (outbuffer) {
+	  SendMessageFixed (clients[i], outbuffer[0].Nbuffer, outbuffer[0].buffer);
+	} else {
+	  SendMessageFixed (clients[i], 0, "");
+	}	  
+	FlushIOBuffer (outbuffer);
+      }
+      free (line);
+    }
+    /* if Nread == -2, we probably need to drop the client */
+    /* check if we need to drop / remove any clients */
+    /* check if we need to shut down the thread */
+  }
+}
+
+/* the AddClient commands are issued by the parent thread
+   the value of Nclients may increase after we check it here. 
+   only this thread is allowed to decrease Nclients and remove
+   a client from the table */
Index: /branches/sc_branches/pantasks_condor/pantasks/LocalJob.c
===================================================================
--- /branches/sc_branches/pantasks_condor/pantasks/LocalJob.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/pantasks/LocalJob.c	(revision 34783)
@@ -0,0 +1,239 @@
+# include "pantasks.h"
+
+/* local jobs are forked in the background 
+   we might need to limit the maximum number of local jobs.
+   should we have a queue/stack of pending local jobs, much
+   like controller? */
+
+/* update current state, drain stdout/stderr buffers */
+int CheckLocalJob (Job *job) {
+
+  int Nread;
+
+  // XXX do something useful with exit status?
+  CheckLocalJobStatus (job);
+
+  if ((job[0].state == JOB_EXIT) || (job[0].state == JOB_CRASH)) {
+    if (DEBUG) fprintf (stderr, "empty buffer 0: (%zx) %d of %d\n", (size_t) job[0].stdout_buff.buffer, job[0].stdout_buff.Nbuffer, job[0].stdout_buff.Nalloc); 
+    EmptyIOBuffer (&job[0].stdout_buff, 10, job[0].stdout_fd);
+    EmptyIOBuffer (&job[0].stderr_buff, 10, job[0].stderr_fd);
+    if (DEBUG) fprintf (stderr, "empty buffer 1: (%zx) %d of %d\n", (size_t) job[0].stdout_buff.buffer, job[0].stdout_buff.Nbuffer, job[0].stdout_buff.Nalloc); 
+    close (job[0].stdout_fd);
+    close (job[0].stderr_fd);
+    job[0].stdout_fd = -1; // prevent FreeJob from trying to close again
+    job[0].stderr_fd = -1; // prevent FreeJob from trying to close again
+  } else {
+    /* read stdout buffer */
+    if (DEBUG) fprintf (stderr, "read buffer 0: (%zx) %d of %d\n", (size_t) job[0].stdout_buff.buffer, job[0].stdout_buff.Nbuffer, job[0].stdout_buff.Nalloc); 
+    while ((Nread = ReadtoIOBuffer (&job[0].stdout_buff, job[0].stdout_fd)) > 0);
+    switch (Nread) {
+      case -2:  /* error in read (programming error?  system level error?) */
+	gprint (GP_ERR, "serious IO error\n");
+	exit (2);
+      case -1:  /* no data in pipe */
+      case 0:   /* pipe is closed, change child state? **/
+      default:  /* data in pipe */
+	// fprintf (stderr, "read %d bytes (Nblock: %d, Nbuffer: %d)\n", Nread, job[0].stdout.Nblock, job[0].stdout.Nbuffer);
+	break;
+    }
+    if (DEBUG) fprintf (stderr, "read buffer 1: (%zx) %d of %d\n", (size_t) job[0].stdout_buff.buffer, job[0].stdout_buff.Nbuffer, job[0].stdout_buff.Nalloc); 
+  
+    /* read stderr buffer */
+    while ((Nread = ReadtoIOBuffer (&job[0].stderr_buff, job[0].stderr_fd)) > 0);
+    switch (Nread) {
+      case -2:  /* error in read (programming error?  system level error?) */
+	gprint (GP_ERR, "serious IO error\n");
+	exit (2);
+      case -1:  /* no data in pipe */
+      case 0:   /* pipe is closed, change child state? **/
+      default:  /* data in pipe */
+	break;
+    }
+  }
+  return (TRUE);
+}
+
+int CheckLocalJobStatus (Job *job) {
+
+  int result, waitstatus;
+
+  /* check local job status */
+  result = waitpid (job[0].pid, &waitstatus, WNOHANG);
+  switch (result) {
+    case -1:  /* error with waitpid */
+      switch (errno) {
+	case ECHILD:
+	  gprint (GP_ERR, "unknown PID, not a child proc\n");
+	  gprint (GP_ERR, "did process already exit?  programming error?\n");
+	  job[0].state = JOB_NONE;
+	  job[0].exit_status = 0;
+	  return (FALSE);
+	case EINVAL:
+	  gprint (GP_ERR, "error EINVAL (waitpid): programming error\n");
+	  exit (1);
+	case EINTR:
+	  gprint (GP_ERR, "error EINTR (waitpid): programming error\n");
+	  exit (1);
+	default:
+	  gprint (GP_ERR, "unknown error for waitpid (%d): programming error\n", errno);
+	  exit (1);
+      }
+      break;
+      
+    case 0:  /* process not exited */
+      job[0].state = JOB_BUSY;
+      job[0].exit_status = 0;
+      return (TRUE);
+
+    default:
+      if (result != job[0].pid) {
+	gprint (GP_ERR, "waitpid error: mis-matched PID (%d vs %d).  programming error\n", result, job[0].pid);
+	exit (1);
+      }
+      if (WIFEXITED(waitstatus)) {
+	job[0].state = JOB_EXIT;
+	job[0].exit_status = WEXITSTATUS(waitstatus);
+      }
+      if (WIFSIGNALED(waitstatus)) {
+	job[0].state = JOB_CRASH;
+	job[0].exit_status = WTERMSIG(waitstatus);
+      }
+      if (WIFSTOPPED(waitstatus)) {
+	gprint (GP_ERR, "waitpid returns 'stopped': programming error\n");
+	exit (1);
+      }
+      job[0].dtime = GetTaskTimer (job[0].start, FALSE);
+      break;
+  }
+  return (FALSE);
+}
+
+/* this could be written a just a one-way pipe */
+int SubmitLocalJob (Job *job) {
+
+  int status, pid;
+  int stdout_fd[2], stderr_fd[2];
+
+  bzero (stdout_fd, 2*sizeof(int));
+  bzero (stderr_fd, 2*sizeof(int));
+
+  if (pipe (stdout_fd) < 0) goto pipe_error;
+  if (pipe (stderr_fd) < 0) goto pipe_error;
+
+  // XXX nothing to be read at this point
+  // other threads are already halted here.
+  fflush (stdout);
+  fflush (stderr);
+
+  pid = fork ();
+  if (pid == -1) {
+    gprint_syserror (GP_ERR, errno, "error starting local job: ");
+    goto pipe_error;
+  }
+
+  if (!pid) { /* must be child process */
+    if (VerboseMode()) gprint (GP_ERR, "starting local job\n");
+
+    /* close the other ends of the pipes */
+    close (stdout_fd[0]);
+    close (stderr_fd[0]);
+
+    // XXX neither of these work to empty the child stdout buffer
+    // fflush (stdout);
+    // fflush (stderr);
+    // close (STDOUT_FILENO);
+    // close (STDERR_FILENO);
+
+    /* tie our ends of the pipes to stdin, stdout, stderr */
+    dup2 (stdout_fd[1], STDOUT_FILENO);
+    dup2 (stderr_fd[1], STDERR_FILENO);
+
+    /* set all three unblocking */
+    setvbuf (stdout, (char *) NULL, _IONBF, BUFSIZ);
+    setvbuf (stderr, (char *) NULL, _IONBF, BUFSIZ);
+
+    // XXX allow the parent time to read the stdout/stderr buffers
+    usleep (10000);
+
+    status = execvp (job[0].argv[0], job[0].argv); 
+    exit (1);
+  }
+  if (VerboseMode()) gprint (GP_ERR, "local job launched\n");
+
+  /* set nice level for the child process -- maybe I should not exit here... */
+  if (job[0].priority) {
+      status = setpriority (PRIO_PROCESS, pid, job[0].priority);
+      if (status == -1) {
+	  gprint (GP_ERR, "error setting priority\n");
+	  perror ("setpriority: ");
+	  exit (2);
+      }
+  }
+
+  /* close the other ends of the pipes */
+  close (stdout_fd[1]); stdout_fd[1] = 0;
+  close (stderr_fd[1]); stderr_fd[1] = 0;
+
+  /* make the pipes non-blocking */
+  fcntl (stdout_fd[0], F_SETFL, O_NONBLOCK);
+  fcntl (stderr_fd[0], F_SETFL, O_NONBLOCK);
+
+  // XXX There seems to always be extra data on the pipe, specifically the 
+  // stdout buffer from the parent.  If I read it here, then it clears out that data.
+  // But, how can I be sure I will not start reading data from the exec-ed process?
+
+  { // test read of the stdout buffer
+    int Nread;
+    char buffer[0x1000];
+
+    Nread = read (stdout_fd[0], buffer, 0x1000);
+    if (DEBUG) fprintf (stderr, "read from stdout before exec: %d bytes\n", Nread);
+
+    Nread = read (stderr_fd[0], buffer, 0x1000);
+    if (DEBUG) fprintf (stderr, "read from stderr before exec: %d bytes\n", Nread);
+  }
+
+  job[0].stdout_fd = stdout_fd[0];
+  job[0].stderr_fd = stderr_fd[0];
+  job[0].pid = pid;
+
+  return (TRUE);
+
+pipe_error:
+  perror ("pipe error:");
+  if (stdout_fd[0] != 0) close (stdout_fd[0]);
+  if (stdout_fd[1] != 0) close (stdout_fd[1]);
+  if (stderr_fd[0] != 0) close (stderr_fd[0]);
+  if (stderr_fd[1] != 0) close (stderr_fd[1]);
+  return (FALSE);
+}
+
+/* should this function close the fd's? */
+int KillLocalJob (Job *job) {
+
+  int i, result, waitstatus;
+
+  if (job[0].state != JOB_BUSY) return (TRUE);
+
+  /* send SIGTERM signal to job */
+  kill (job[0].pid, SIGTERM);
+  result = 0;
+  for (i = 0; (i < 10) && (result == 0); i++) {
+    usleep (10000);  // wait for job to exit
+    result = waitpid (job[0].pid, &waitstatus, WNOHANG);
+  }
+  if (result) return (TRUE);
+
+  /* send SIGKILL signal to job */
+  kill (job[0].pid, SIGKILL);
+  result = 0;
+  for (i = 0; (i < 10) && (result == 0); i++) {
+    usleep (10000);  // wait for job to exit
+    result = waitpid (job[0].pid, &waitstatus, WNOHANG);
+  }
+  if (result) return (TRUE);
+
+  /* total failure, don't reset */
+  return (FALSE);
+}
+
Index: /branches/sc_branches/pantasks_condor/pantasks/Makefile
===================================================================
--- /branches/sc_branches/pantasks_condor/pantasks/Makefile	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/pantasks/Makefile	(revision 34783)
@@ -0,0 +1,132 @@
+default: pantasks pantasks_client pantasks_server
+
+include ../../../Makefile.System
+HOME    =       $(ROOT)/src/opihi
+SRC     =       $(HOME)/pantasks
+BIN     =       $(HOME)/bin
+LIB     =       $(HOME)/lib
+INC     =       $(HOME)/include
+DATA    =       $(DESTDATA)/pantasks
+include ../Makefile.Common
+
+# programs may add their own internal requirements here
+LIBS1   =       -lbasiccmd -ldatacmd -lastrocmd -lshell -ldata 
+LIBS2   =       -ldvo -lkapa -lFITS -lohana -lCondor -lgsoap 
+FULL_CFLAGS   = $(BASE_CFLAGS) -I/home/panstarrs/ippdor/local/include/gsoap -DIPP_CONDOR
+FULL_CPPFLAGS = $(BASE_CPPFLAGS) -DIPP_CONDOR
+FULL_LDFLAGS  = $(LIBS1) $(LIBS2) $(BASE_LDFLAGS)
+
+# sched user commands and support functions ########################
+
+client = \
+$(SRC)/pantasks_client.$(ARCH).o \
+$(SRC)/client_shell.$(ARCH).o \
+$(SRC)/invalid.$(ARCH).o \
+$(SRC)/server_shutdown.$(ARCH).o \
+$(SRC)/server_disconnect.$(ARCH).o \
+$(SRC)/server_connect.$(ARCH).o \
+$(SRC)/init_client.$(ARCH).o
+
+single = \
+$(SRC)/init.$(ARCH).o \
+$(SRC)/run.$(ARCH).o \
+$(SRC)/stop.$(ARCH).o \
+$(SRC)/pantasks.$(ARCH).o \
+$(SRC)/thread_locks.$(ARCH).o \
+$(SRC)/jobs_and_tasks_thread.$(ARCH).o \
+$(SRC)/controller_threads.$(ARCH).o 
+
+server = \
+$(SRC)/pantasks_server.$(ARCH).o \
+$(SRC)/server_run.$(ARCH).o \
+$(SRC)/ListenClients.$(ARCH).o \
+$(SRC)/server.$(ARCH).o \
+$(SRC)/status_server.$(ARCH).o \
+$(SRC)/init_server.$(ARCH).o \
+$(SRC)/CheckPassword.$(ARCH).o \
+$(SRC)/thread_locks.$(ARCH).o \
+$(SRC)/jobs_and_tasks_thread.$(ARCH).o \
+$(SRC)/controller_threads.$(ARCH).o
+
+funcs = \
+$(SRC)/CheckJobs.$(ARCH).o \
+$(SRC)/CheckController.$(ARCH).o \
+$(SRC)/CheckTasks.$(ARCH).o \
+$(SRC)/CheckTimeRanges.$(ARCH).o \
+$(SRC)/ControllerOps.$(ARCH).o \
+$(SRC)/LocalJob.$(ARCH).o \
+$(SRC)/JobOps.$(ARCH).o \
+$(SRC)/JobIDOps.$(ARCH).o \
+$(SRC)/TaskOps.$(ARCH).o \
+$(SRC)/ipp_condor.$(ARCH).o
+
+cmds = \
+$(SRC)/status.$(ARCH).o \
+$(SRC)/flush.$(ARCH).o \
+$(SRC)/showtask.$(ARCH).o \
+$(SRC)/kill.$(ARCH).o \
+$(SRC)/delete.$(ARCH).o \
+$(SRC)/verbose.$(ARCH).o \
+$(SRC)/controller.$(ARCH).o \
+$(SRC)/controller_host.$(ARCH).o \
+$(SRC)/controller_exit.$(ARCH).o \
+$(SRC)/controller_check.$(ARCH).o \
+$(SRC)/controller_status.$(ARCH).o \
+$(SRC)/controller_jobstack.$(ARCH).o \
+$(SRC)/controller_hoststack.$(ARCH).o \
+$(SRC)/controller_machines.$(ARCH).o \
+$(SRC)/controller_parameters.$(ARCH).o \
+$(SRC)/controller_version.$(ARCH).o \
+$(SRC)/controller_verbose.$(ARCH).o \
+$(SRC)/controller_run.$(ARCH).o \
+$(SRC)/controller_output.$(ARCH).o \
+$(SRC)/controller_pulse.$(ARCH).o \
+$(SRC)/task.$(ARCH).o \
+$(SRC)/task_host.$(ARCH).o \
+$(SRC)/task_nice.$(ARCH).o \
+$(SRC)/task_nmax.$(ARCH).o \
+$(SRC)/task_active.$(ARCH).o \
+$(SRC)/task_macros.$(ARCH).o \
+$(SRC)/task_trange.$(ARCH).o \
+$(SRC)/task_stdout.$(ARCH).o \
+$(SRC)/task_periods.$(ARCH).o \
+$(SRC)/task_command.$(ARCH).o \
+$(SRC)/task_options.$(ARCH).o \
+$(SRC)/version.$(ARCH).o \
+$(SRC)/ipptool2book.$(ARCH).o
+
+libs = \
+$(DESTLIB)/libshell.a \
+$(DESTLIB)/libdata.a \
+$(DESTLIB)/libbasiccmd.a \
+$(DESTLIB)/libastrocmd.a \
+$(DESTLIB)/libdatacmd.a
+
+pantasks: $(BIN)/pantasks.$(ARCH)
+$(SRC)/pantasks.$(ARCH).o : $(libs)
+$(BIN)/pantasks.$(ARCH) : $(single) $(funcs) $(cmds)
+
+pantasks_client: $(BIN)/pantasks_client.$(ARCH)
+$(SRC)/pantasks_client.$(ARCH).o : $(libs)
+$(BIN)/pantasks_client.$(ARCH) : $(client)
+
+pantasks_server: $(BIN)/pantasks_server.$(ARCH)
+$(SRC)/pantasks_server.$(ARCH).o : $(libs)
+$(BIN)/pantasks_server.$(ARCH) : $(server) $(funcs) $(cmds)
+
+pantasks_client.install: $(DESTBIN)/pantasks_client
+pantasks_server.install: $(DESTBIN)/pantasks_server
+
+$(single) $(client) $(server) $(funcs) $(cmds) : $(INC)/pantasks.h
+
+install: $(DESTBIN)/pantasks $(DESTBIN)/pantasks_client $(DESTBIN)/pantasks_server help modules
+
+help: clean-help cmd.basic.help cmd.data.help cmd.astro.help pantasks.help
+
+modules: pantasks.modules
+
+.PHONY: pantasks pantasks_client pantasks_server
+
+.PRECIOUS: $(SRC)/pantasks.c
+.PRECIOUS: $(SRC)/pantasks_client.c 
+.PRECIOUS: $(SRC)/pantasks_server.c
Index: /branches/sc_branches/pantasks_condor/pantasks/TaskOps.c
===================================================================
--- /branches/sc_branches/pantasks_condor/pantasks/TaskOps.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/pantasks/TaskOps.c	(revision 34783)
@@ -0,0 +1,694 @@
+# include "pantasks.h"
+# include <regex.h>
+
+static Task **tasks;
+static int    Ntasks;
+static int    NTASKS;
+
+/* counter marking task being visited by the run loop */
+static int   ActiveTask;
+
+/* temporary holder for a new task */
+static Task *NewTask = NULL;
+
+/* set up the task list system */
+void InitTasks () {
+  NTASKS = 20;
+  Ntasks = 0;
+  ALLOCATE (tasks, Task *, NTASKS);
+  ActiveTask = -1;
+}
+
+void FreeTasks () {
+  int i;
+  for (i = 0; i < Ntasks; i++) {
+    FreeTask (tasks[i]);
+  }
+  free (tasks);
+}
+
+/* provide a mechanism to loop over the list of tasks */
+Task *NextTask () {
+  
+  Task *task;
+
+  /* move to the next task and return it */
+  ActiveTask ++;
+  if (ActiveTask < 0) ActiveTask = 0;
+  if (ActiveTask >= Ntasks) {
+    ActiveTask = -1;
+    return (NULL);
+  }
+  task = tasks[ActiveTask];
+  return (task);
+}
+
+/* return task with given name */
+Task *FindTask (char *name) {
+
+  int i;
+
+  /* try for an exact match first */
+  for (i = 0; i < Ntasks; i++) {
+    if (!strcmp (tasks[i][0].name, name)) {
+      return (tasks[i]);
+    }
+  }
+  return (NULL);
+}  
+
+# define getnchars(LEN, MINLEN) { float loglength; loglength = log10(LEN); LEN = ((int)loglength < loglength) ? loglength + 1 : loglength; LEN = (LEN < MINLEN) ? MINLEN : LEN; }
+
+/* list known tasks */
+void ListTasks (int verbose) {
+
+  int i, j, valid, nameLength, cmdLength, maxJobs, maxRun, maxDone, maxFail, maxTime, Nrun;
+  char *start, *stop;
+  char format[128];
+
+  gprint (GP_LOG, "\n");
+  if (Ntasks == 0) {
+    gprint (GP_LOG, " no defined tasks\n");
+    return;
+  }
+
+  /* find string lengths */
+  nameLength = cmdLength = 0;
+  maxJobs = maxRun = maxDone = maxFail = maxTime = 0;
+  for (i = 0; i < Ntasks; i++) {
+    nameLength = MAX (nameLength, strlen(tasks[i][0].name));
+    if (tasks[i][0].argv == NULL) {
+      cmdLength = MAX (nameLength, strlen("(dynamic)"));
+    } else {
+      cmdLength = MAX (nameLength, strlen(tasks[i][0].argv[0]));
+    }
+    maxJobs = MAX(maxJobs, tasks[i][0].Njobs);
+    maxDone = MAX(maxDone, tasks[i][0].Nsuccess);
+    maxFail = MAX(maxFail, tasks[i][0].Nfailure);
+    maxTime = MAX(maxTime, tasks[i][0].Ntimeout);
+    maxRun  = MAX(maxRun,  tasks[i][0].Njobs - tasks[i][0].Nsuccess - tasks[i][0].Nfailure - tasks[i][0].Ntimeout);
+  }
+
+  gprint (GP_LOG, " Task Status\n");
+
+  getnchars(maxJobs, 5);
+  getnchars(maxRun,  5);
+  getnchars(maxDone, 5);
+  getnchars(maxFail, 5);
+  getnchars(maxTime, 5);
+
+  snprintf (format, 128, "  AV %%-%ds  %%%ds  %%%ds  %%%ds %%%ds %%%ds %%-%ds\n", nameLength, maxRun, maxJobs, maxDone, maxFail, maxTime, cmdLength);
+  gprint (GP_LOG, format, "Name", "Nrun", "Njobs", "Ngood", "Nfail", "Ntime", "Command");
+
+  snprintf (format, 128, "%%-%ds  %%%dd  %%%dd  %%%dd %%%dd %%%dd %%-%ds\n", nameLength, maxRun, maxJobs, maxDone, maxFail, maxTime, cmdLength);
+  for (i = 0; i < Ntasks; i++) {
+    valid = CheckTimeRanges (tasks[i][0].ranges, tasks[i][0].Nranges);
+    if (verbose) gprint (GP_LOG, "\n");
+    if (tasks[i][0].active) {
+      gprint (GP_LOG, "  +");
+    } else {
+      gprint (GP_LOG, "  -");
+    }
+    if (valid) {
+      gprint (GP_LOG, "+ ");
+    } else {
+      gprint (GP_LOG, "- ");
+    }
+    Nrun = tasks[i][0].Njobs - tasks[i][0].Nsuccess - tasks[i][0].Nfailure - tasks[i][0].Ntimeout;
+    if (tasks[i][0].argv == NULL) {
+      gprint (GP_LOG, format, tasks[i][0].name, Nrun, tasks[i][0].Njobs, tasks[i][0].Nsuccess, tasks[i][0].Nfailure, tasks[i][0].Ntimeout, "(dynamic)");
+    } else {
+      gprint (GP_LOG, format, tasks[i][0].name, Nrun, tasks[i][0].Njobs, tasks[i][0].Nsuccess, tasks[i][0].Nfailure, tasks[i][0].Ntimeout, tasks[i][0].argv[0]);
+    }
+    if (verbose) {
+      gprint (GP_LOG, "    spawn period: %f, polling period: %f, timeout period: %f\n", 
+	       tasks[i][0].exec_period, tasks[i][0].poll_period, tasks[i][0].timeout_period);
+      for (j = 0; j < tasks[i][0].Nranges; j++) {
+	switch (tasks[i][0].ranges[j].type) {
+	  case RANGE_ABS:
+	    start = ohana_sec_to_date (tasks[i][0].ranges[j].start);
+	    stop  = ohana_sec_to_date (tasks[i][0].ranges[j].stop);
+	    break;
+	  case RANGE_DAY:
+	    start = ohana_sec_to_hms (tasks[i][0].ranges[j].start);
+	    stop  = ohana_sec_to_hms (tasks[i][0].ranges[j].stop);
+	    break;
+	  case RANGE_WEEK:
+	    start = ohana_sec_to_day (tasks[i][0].ranges[j].start);
+	    stop  = ohana_sec_to_day (tasks[i][0].ranges[j].stop);
+	    break;
+	  default:
+	    abort ();
+	}
+	if (tasks[i][0].ranges[j].include) {
+	  gprint (GP_LOG, "     active : %s - %s", start, stop);
+	  if (tasks[i][0].ranges[j].Nmax) gprint (GP_LOG, " (%d of %d)", tasks[i][0].ranges[j].Nrun, tasks[i][0].ranges[j].Nmax);
+	  gprint (GP_LOG, "\n");
+	} else {
+	  gprint (GP_LOG, "     avoid  : %s - %s\n", start, stop);
+	}
+	free (start);
+	free (stop);
+      }
+      gprint (GP_LOG, "     Nskip exec: %d\n", tasks[i][0].Nskipexec);
+      if (tasks[i][0].host == NULL) {
+	gprint (GP_LOG, "    task runs locally\n");
+	continue;
+      }
+      if (!strcasecmp(tasks[i][0].host, "ANYHOST")) {
+	gprint (GP_LOG, "    task host selected by controller\n");
+	continue;
+      }
+      if (tasks[i][0].host_required) {
+	gprint (GP_LOG, "    host %s (required)\n", tasks[i][0].host);
+      } else {
+	gprint (GP_LOG, "    host %s (desired)\n", tasks[i][0].host);
+      }
+    }
+  }
+  return;
+}
+
+/* list known tasks */
+void ListTaskStats (char *regex) {
+
+  int i, valid, nameLength;
+  char format[128];
+  regex_t preg;
+
+  gprint (GP_LOG, "\n");
+  if (Ntasks == 0) {
+    gprint (GP_LOG, " no defined tasks\n");
+    return;
+  }
+
+  if (regex != NULL) {
+    regcomp (&preg, regex, REG_EXTENDED);
+  }
+
+  /* find string lengths */
+  nameLength = 0;
+  for (i = 0; i < Ntasks; i++) {
+    nameLength = MAX (nameLength, strlen(tasks[i][0].name));
+  }
+
+  gprint (GP_LOG, " Task Statistics\n");
+
+  snprintf (format, 128, "     %%-%ds |           alljobs          |           success          |           failure          |\n", nameLength);
+  gprint (GP_LOG, format, "");
+  snprintf (format, 128, "  AV %%-%ds | Njobs   Tmin   Tave   Tmax | Njobs   Tmin   Tave   Tmax | Njobs   Tmin   Tave   Tmax |\n", nameLength);
+  gprint (GP_LOG, format, "Name");
+
+  snprintf (format, 128, "%%-%ds", nameLength);
+  for (i = 0; i < Ntasks; i++) {
+      
+    if ((regex != NULL) && regexec (&preg, tasks[i][0].name, 0, NULL, 0)) continue;
+
+    valid = CheckTimeRanges (tasks[i][0].ranges, tasks[i][0].Nranges);
+    if (tasks[i][0].active) {
+      gprint (GP_LOG, "  +");
+    } else {
+      gprint (GP_LOG, "  -");
+    }
+    if (valid) {
+      gprint (GP_LOG, "+ ");
+    } else {
+      gprint (GP_LOG, "- ");
+    }
+    if (tasks[i][0].argv == NULL) {
+      gprint (GP_LOG, format, tasks[i][0].name);
+    } else {
+      gprint (GP_LOG, format, tasks[i][0].name);
+    }
+    if (tasks[i][0].dtimeMin_alljobs < 0) {
+      gprint (GP_LOG, " | %5d %6s %6.2f %6.2f",     tasks[i][0].Njobs,    "NONE",                       tasks[i][0].dtimeAve_alljobs, tasks[i][0].dtimeMax_alljobs);
+    } else {
+      gprint (GP_LOG, " | %5d %6.2f %6.2f %6.2f",   tasks[i][0].Njobs,    tasks[i][0].dtimeMin_alljobs, tasks[i][0].dtimeAve_alljobs, tasks[i][0].dtimeMax_alljobs);
+    }      
+    if (tasks[i][0].dtimeMin_success < 0) {
+      gprint (GP_LOG, " | %5d %6s %6.2f %6.2f",     tasks[i][0].Nsuccess, "NONE",                       tasks[i][0].dtimeAve_success, tasks[i][0].dtimeMax_success);
+    } else {
+      gprint (GP_LOG, " | %5d %6.2f %6.2f %6.2f",   tasks[i][0].Nsuccess, tasks[i][0].dtimeMin_success, tasks[i][0].dtimeAve_success, tasks[i][0].dtimeMax_success);
+    }
+    if (tasks[i][0].dtimeMin_failure < 0) {
+      gprint (GP_LOG, " | %5d %6s %6.2f %6.2f |\n",   tasks[i][0].Nfailure, "NONE",                       tasks[i][0].dtimeAve_failure, tasks[i][0].dtimeMax_failure);
+    } else {
+      gprint (GP_LOG, " | %5d %6.2f %6.2f %6.2f |\n", tasks[i][0].Nfailure, tasks[i][0].dtimeMin_failure, tasks[i][0].dtimeAve_failure, tasks[i][0].dtimeMax_failure);
+    }
+  }
+  return;
+}
+
+/* list known tasks */
+void ResetTaskStats (char *regex) {
+
+  int i, nameLength;
+  regex_t preg;
+
+  if (Ntasks == 0) {
+    return;
+  }
+
+  if (regex != NULL) {
+    regcomp (&preg, regex, REG_EXTENDED);
+  }
+
+  /* find string lengths */
+  nameLength = 0;
+  for (i = 0; i < Ntasks; i++) {
+    nameLength = MAX (nameLength, strlen(tasks[i][0].name));
+  }
+
+  for (i = 0; i < Ntasks; i++) {
+      
+    if ((regex != NULL) && regexec (&preg, tasks[i][0].name, 0, NULL, 0)) continue;
+
+    tasks[i][0].Njobs = 0;
+    tasks[i][0].dtimeMin_alljobs = 0;
+    tasks[i][0].dtimeAve_alljobs = 0;
+    tasks[i][0].dtimeMax_alljobs = 0;
+
+    tasks[i][0].Nsuccess = 0;
+    tasks[i][0].dtimeMin_success = 0;
+    tasks[i][0].dtimeAve_success = 0;
+    tasks[i][0].dtimeMax_success = 0;
+
+    tasks[i][0].Nfailure = 0;
+    tasks[i][0].dtimeMin_failure = 0;
+    tasks[i][0].dtimeAve_failure = 0;
+    tasks[i][0].dtimeMax_failure = 0;
+  }
+  return;
+}
+
+/* show details of a task */
+int ShowTask (char *name) {
+
+  int i, j;
+  char *start, *stop;
+  Task *task;
+
+  task = FindTask (name);
+  if (task == NULL) {
+    gprint (GP_LOG, "task %s not found\n", name);
+    return (FALSE);
+  }
+
+  gprint (GP_LOG, "\n macro %s\n", task[0].name);
+
+  gprint (GP_LOG, "\n command: ");
+  for (i = 0; i < task[0].argc; i++) {
+    gprint (GP_LOG, "%s ", task[0].argv[i]);
+  }
+  gprint (GP_LOG, "\n\n");
+
+  gprint (GP_LOG, "\n options: ");
+  for (i = 0; i < task[0].optc; i++) {
+    gprint (GP_LOG, "%s ", task[0].optv[i]);
+  }
+  gprint (GP_LOG, "\n\n");
+
+  if (task[0].host == NULL) {
+    gprint (GP_LOG, " task runs locally\n");
+    goto periods;
+  }
+  if (!strcasecmp(task[0].host, "ANYHOST")) {
+    gprint (GP_LOG, " task host selected by controller\n");
+    goto periods;
+  }
+  if (task[0].host_required) {
+    gprint (GP_LOG, " host %s (required)\n", task[0].host);
+  } else {
+    gprint (GP_LOG, " host %s (desired)\n", task[0].host);
+  }
+
+periods:
+  gprint (GP_LOG, " time periods: exec: %f  poll: %f  timeout: %f\n", 
+	  task[0].exec_period, task[0].poll_period, task[0].timeout_period);
+
+  for (j = 0; j < tasks[i][0].Nranges; j++) {
+    switch (tasks[i][0].ranges[j].type) {
+      case RANGE_ABS:
+	start = ohana_sec_to_date (tasks[i][0].ranges[j].start);
+	stop  = ohana_sec_to_date (tasks[i][0].ranges[j].stop);
+	break;
+      case RANGE_DAY:
+	start = ohana_sec_to_hms (tasks[i][0].ranges[j].start);
+	stop  = ohana_sec_to_hms (tasks[i][0].ranges[j].stop);
+	break;
+      case RANGE_WEEK:
+	start = ohana_sec_to_day (tasks[i][0].ranges[j].start);
+	stop  = ohana_sec_to_day (tasks[i][0].ranges[j].stop);
+	break;
+      default:
+	abort ();
+    }
+    if (tasks[i][0].ranges[j].include) {
+      gprint (GP_LOG, "     active : %s - %s", start, stop);
+      if (tasks[i][0].ranges[j].Nmax) gprint (GP_LOG, " (%d of %d)", tasks[i][0].ranges[j].Nrun, tasks[i][0].ranges[j].Nmax);
+      gprint (GP_LOG, "\n");
+    } else {
+      gprint (GP_LOG, "     avoid  : %s - %s\n", start, stop);
+    }
+    free (start);
+    free (stop);
+  }
+
+  gprint (GP_LOG, "\n pre-execute macro\n");
+  ListMacro (task[0].exec);
+
+  gprint (GP_LOG, "\n timeout macro\n");
+  ListMacro (task[0].timeout);
+
+  gprint (GP_LOG, "\n crash macro\n");
+  ListMacro (task[0].crash);
+
+  gprint (GP_LOG, "\n default exit macro\n");
+  ListMacro (task[0].defexit);
+
+  for (i = 0; i < task[0].Nexit; i++) {
+    gprint (GP_LOG, "\n exit macro (status == %d)\n", atoi(task[0].exit[i][0].name));
+    ListMacro (task[0].exit[i]);
+  }
+
+  return (TRUE);
+}
+
+/* make a new named task */
+int FreeTask (Task *task) {
+  
+  int i;
+
+  if (task == NULL) return (FALSE);
+  
+  if (task[0].name != NULL) free (task[0].name);
+  if (task[0].host != NULL) free (task[0].host);
+  if (task[0].argv != NULL) {
+    for (i = 0; i < task[0].argc; i++) {
+      free (task[0].argv[i]);
+    }
+    free (task[0].argv);
+  }
+  if (task[0].optv != NULL) {
+    for (i = 0; i < task[0].optc; i++) {
+      free (task[0].optv[i]);
+    }
+    free (task[0].optv);
+  }
+  if (task[0].exec != NULL) {
+    FreeMacro (task[0].exec);
+    free (task[0].exec);
+  }
+  if (task[0].crash != NULL) {
+    FreeMacro (task[0].crash);
+    free (task[0].crash);
+  }
+  if (task[0].timeout != NULL) {
+    FreeMacro (task[0].timeout);
+    free (task[0].timeout);
+  }
+  for (i = 0; i < task[0].Nexit; i++) {
+    if (task[0].exit[i] != NULL) {
+      FreeMacro (task[0].exit[i]);
+    }
+    free (task[0].exit[i]);
+  }
+  free (task[0].exit);
+
+  if (task[0].ranges != NULL) {
+    free (task[0].ranges);
+  }
+  return (TRUE);
+}
+
+/**** new task functions ***/
+
+/* make a new named task */
+Task *CreateTask (char *name) {
+  
+  ALLOCATE (NewTask, Task, 1);
+
+  NewTask[0].name = strcreate (name);;
+
+  NewTask[0].host = NULL;
+  NewTask[0].host_required = FALSE;
+
+  NewTask[0].argc = 0;
+  NewTask[0].argv = NULL;
+
+  NewTask[0].optc = 0;
+  NewTask[0].optv = NULL;
+
+  NewTask[0].stdout_dump = NULL;
+  NewTask[0].stderr_dump = NULL;
+
+  NewTask[0].exec = NULL;
+  NewTask[0].crash = NULL;
+  NewTask[0].timeout = NULL;
+  NewTask[0].defexit = NULL;
+
+  NewTask[0].Nexit = 0;
+  NewTask[0].NEXIT = 10;
+  ALLOCATE (NewTask[0].exit, Macro *, NewTask[0].NEXIT);
+  /* don't free tasks[0].exit, keep at least 1 allocated */
+
+  NewTask[0].exec_period = 1.0;
+  NewTask[0].poll_period = 1.0;
+  NewTask[0].timeout_period = 1.0;
+
+  NewTask[0].Nranges = 0;
+  ALLOCATE (NewTask[0].ranges, TimeRange, 1);
+
+  /* init task timer (is reset by 'run') */  
+  gettimeofday (&NewTask[0].last, (void *) NULL);
+  NewTask[0].Nmax = 0;  /* default value means 'no limit' */
+
+  NewTask[0].NpendingMax = 0;  /* default value means 'no limit' */
+  NewTask[0].Npending = 0;  /* default value means 'no limit' */
+
+  NewTask[0].Njobs = 0;
+  NewTask[0].Nsuccess = 0;
+  NewTask[0].Nfailure = 0;
+  NewTask[0].Ntimeout = 0;
+  NewTask[0].Nskipexec = 0;
+
+  /* jobs timing statistics */
+  NewTask[0].dtimeAve_alljobs =  0.0;
+  NewTask[0].dtimeMin_alljobs = -1.0;
+  NewTask[0].dtimeMax_alljobs =  0.0;
+
+  NewTask[0].dtimeAve_success =  0.0;
+  NewTask[0].dtimeMin_success = -1.0;
+  NewTask[0].dtimeMax_success =  0.0;
+
+  NewTask[0].dtimeAve_failure =  0.0;
+  NewTask[0].dtimeMin_failure = -1.0;
+  NewTask[0].dtimeMax_failure =  0.0;
+
+  NewTask[0].active = TRUE;
+  NewTask[0].priority = 0;
+  return (NewTask);
+}
+
+/* remove the task from the task list */
+int RemoveTask (Task *task) {
+  
+  int i, Nt;
+
+  /* find task in task list */
+  Nt = -1;
+  for (i = 0; i < Ntasks; i++) {
+    if (task == tasks[i]) {
+      Nt = i;
+      break;
+    }
+  }
+  if (Nt == -1) {
+    gprint (GP_ERR, "programming error: task not found\n");
+    return (FALSE);
+  }
+  for (i = Nt; i < Ntasks - 1; i++) {
+    tasks[i] = tasks[i+1];
+  }
+  Ntasks --;
+  return (TRUE);
+}
+
+int ValidateTask (Task *task, int RequireStatic) {
+
+  int i, hash;
+
+  /* is a static command defined? */
+  if (task[0].argc != 0) {
+    if (task[0].argv == NULL) {
+      gprint (GP_ERR, "task command arguments not defined (programming error)\n");
+      return (FALSE);
+    }
+    return (TRUE);
+  }
+  if (RequireStatic) {
+    gprint (GP_ERR, "task command not defined\n");
+    return (FALSE);
+  }
+
+  /* no static command; dynamic command? */
+  if (task[0].exec != NULL) {
+    for (i = 0; i < task[0].exec[0].Nlines; i++) {
+      hash = TaskHash (task[0].exec[0].line[i]);
+      if (hash == TASK_COMMAND) return (TRUE);
+    }
+  }
+  gprint (GP_ERR, "task command not defined\n");
+  return (FALSE);
+}
+
+int RegisterNewTask () {
+  
+  int N;
+
+  N = Ntasks;
+  Ntasks ++;
+  if (Ntasks == NTASKS) {
+    NTASKS += 20;
+    REALLOCATE (tasks, Task *, NTASKS);
+  }
+  tasks[N] = NewTask;
+  NewTask = NULL;
+  return (TRUE);
+}
+
+int DeleteNewTask () {
+  if (NewTask != NULL) {
+    FreeTask (NewTask);
+    free (NewTask);
+    NewTask = NULL;
+  }
+  return (TRUE);
+}
+
+Task *SetNewTask (Task *task) {
+  NewTask = task;
+  return (task);
+}
+
+Task *GetNewTask () {
+  return (NewTask);
+}
+
+Task *GetActiveTask () {
+  Task *task;
+  if (ActiveTask < 0) return (NULL);
+  task = tasks[ActiveTask];
+  return (task);
+}
+
+int TaskHash (char *input) {
+  
+  int hash;
+  char *command;
+
+  hash = TASK_NONE;
+
+  command = thisword (input);
+  if (command == NULL) return (TASK_EMPTY);
+
+  if (command[0] == '#')                  hash = TASK_COMMENT;
+  if (!strcasecmp (command, "END"))       hash = TASK_END;
+  if (!strcasecmp (command, "HOST"))      hash = TASK_HOST;
+  if (!strcasecmp (command, "NICE"))      hash = TASK_NICE;
+  if (!strcasecmp (command, "NMAX"))      hash = TASK_NMAX;
+  if (!strcasecmp (command, "ACTIVE"))    hash = TASK_ACTIVE;
+  if (!strcasecmp (command, "TRANGE"))    hash = TASK_TRANGE;
+  if (!strcasecmp (command, "STDOUT"))    hash = TASK_STDOUT;
+  if (!strcasecmp (command, "STDERR"))    hash = TASK_STDERR;
+  if (!strcasecmp (command, "COMMAND"))   hash = TASK_COMMAND;
+  if (!strcasecmp (command, "OPTIONS"))   hash = TASK_OPTIONS;
+  if (!strcasecmp (command, "PERIODS"))   hash = TASK_PERIODS;
+  if (!strcasecmp (command, "NPENDING"))  hash = TASK_NPENDING;
+  if (!strcasecmp (command, "TASK.EXIT")) hash = TASK_EXIT;
+  if (!strcasecmp (command, "TASK.EXEC")) hash = TASK_EXEC;
+
+  free (command);
+  return (hash);
+}
+
+/*** task timer functions ***/
+
+double GetTaskTimer (struct timeval start, int verbose) {
+
+  double dtime;
+  struct timeval now;
+  
+  gettimeofday (&now, (void *) NULL);
+  dtime = DTIME (now, start);
+  
+  if (verbose) {
+      fprintf (stderr, "tt: %d %6d  - %d %6d : %f\n", 
+	       (int) now.tv_sec, (int) now.tv_usec, 
+	       (int) start.tv_sec, (int) start.tv_usec, dtime);
+  }
+
+  return (dtime);
+}
+
+void SetTaskTimer (struct timeval *timer) {
+  gettimeofday (timer, (void *) NULL);
+}
+
+/* start the clock for all tasks */
+void InitTaskTimers () {
+
+  Task *task;
+  double isec, fsec;
+
+  while ((task = NextTask ()) != NULL) {
+    gettimeofday (&task[0].last, (void *) NULL);
+    fsec = modf (task[0].exec_period, &isec);
+    task[0].last.tv_sec -= isec;
+    task[0].last.tv_usec -= 1e6*fsec;
+ }
+}
+
+/* must call this after updating the corresponding counter */
+void UpdateTaskTimerStats (Task *task, int mode, double dtime) {
+
+  double total;
+
+  switch (mode) {
+    case TIMER_ALLJOBS:
+      total = task[0].dtimeAve_alljobs * (task[0].Njobs - 1);
+      total += dtime;
+      task[0].dtimeAve_alljobs = total / (float) task[0].Njobs;
+      if (task[0].dtimeMin_alljobs < 0) {
+	task[0].dtimeMin_alljobs = dtime;
+      } else {
+	task[0].dtimeMin_alljobs = MIN (task[0].dtimeMin_alljobs, dtime);
+      }
+      task[0].dtimeMax_alljobs = MAX (task[0].dtimeMax_alljobs, dtime);
+      break;
+    case TIMER_SUCCESS:
+      total = task[0].dtimeAve_success * (task[0].Nsuccess - 1);
+      total += dtime;
+      task[0].dtimeAve_success = total / (float) task[0].Nsuccess;
+      if (task[0].dtimeMin_success < 0) {
+	task[0].dtimeMin_success = dtime;
+      } else {
+	task[0].dtimeMin_success = MIN (task[0].dtimeMin_success, dtime);
+      }
+      task[0].dtimeMax_success = MAX (task[0].dtimeMax_success, dtime);
+      break;
+    case TIMER_FAILURE:
+      total = task[0].dtimeAve_failure * (task[0].Nfailure - 1);
+      total += dtime;
+      task[0].dtimeAve_failure = total / (float) task[0].Nfailure;
+      if (task[0].dtimeMin_failure < 0) {
+	task[0].dtimeMin_failure = dtime;
+      } else {
+	task[0].dtimeMin_failure = MIN (task[0].dtimeMin_failure, dtime);
+      }
+      task[0].dtimeMax_failure = MAX (task[0].dtimeMax_failure, dtime);
+      break;
+    default:
+      abort();
+  }
+}
Index: /branches/sc_branches/pantasks_condor/pantasks/client_shell.c
===================================================================
--- /branches/sc_branches/pantasks_condor/pantasks/client_shell.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/pantasks/client_shell.c	(revision 34783)
@@ -0,0 +1,99 @@
+# include "opihi.h"
+
+/******************/
+int client_shell (int argc, char **argv) {
+
+  int Nbad;
+  char *line, *prompt, *history;
+  pid_t ppid;
+
+  general_init (&argc, argv);
+  program_init (&argc, argv);
+  startup (&argc, argv);
+  prompt = get_variable("PROMPT");
+  history = get_variable("HISTORY");
+  welcome ();
+
+  /* attempt to connect to the pantasks server (exit on failure) */
+  multicommand_InitServer ();
+
+  Nbad = 0;
+  while (1) {  /** must exit with command "exit" or "quit" */
+    if (Nbad == 10) exit (3);
+
+    line = opihi_readline (prompt);
+
+    if (line == NULL) { 
+      
+      ppid = getppid();
+      if (ppid == 1) {
+	gprint (GP_ERR, "caught parent shutdown\n");
+	exit (2);
+      }
+      if (!isatty (STDIN_FILENO)) exit (2);
+      gprint (GP_ERR, "Use \"quit\" to exit\n");
+      Nbad ++;
+      continue;
+    }
+    Nbad = 0;
+
+    stripwhite (line);
+
+    if (*line) {
+      // status = multicommand (line); do something different if false?
+	multicommand (line);
+	add_history (line);
+	append_history (1, history);
+    }
+    free (line);
+  }
+}
+
+/* 
+   startup sequence:
+
+   - general_init
+   - program_init
+   - startup (exit if non-interactive)
+   - welcome
+
+*/
+
+/* client issues and questions:
+
+- need to identify the commands to be caught by the client
+- pass each input line through the command parser first, then
+  send to server if not identified as a valid command
+- this raises the question of variables: do we parse variables
+  at the client level?  I would not think so.  what about input?
+  if the input is to be parsed by the client, but executed on the server, 
+  I'll need to re-work the input parsing system.  would be better 
+  for the server to parse the input command.  in this case, the client 
+  user needs to have access to the directories with input scripts.
+  this may be an advantage: it would allow some limitation on who can 
+  install server scripts.
+- variables, vectors and buffers: where are they valid (client or server?)
+  it seems they should all be defined locally on the server.  this makes
+  graphics plotting a server-side action as well.  this is probably easier,
+  but we do need to be careful about multiple clients trying to make plots
+  on the same window at the same time.  Exporting the window can be done 
+  for one client with the $KII variable, but could be tricky for more 
+  complex interactions
+- list of commands which clearly must be parsed by the client:
+  - quit / exit
+  - exec / !
+  - ?
+
+- i may need an alternative version of command, modified to avoid parsing the 
+  variables and vectors.  I also need an alternative version of the Init commands
+  to just list the blocked ones.
+
+- the client now executes all of the basic opihi commands, and only passes
+  through the pantasks commands.  The pantasks server interface should only
+  accept functions which immediately return, sending back the result to be
+  printed by the client.  
+
+  The server should not accept the 'task' commands from the client command-line.
+  To define and run macros or tasks on the server, these need to be added to 
+  a script which is loaded with a varient on the input command.  
+*/
Index: /branches/sc_branches/pantasks_condor/pantasks/controller.c
===================================================================
--- /branches/sc_branches/pantasks_condor/pantasks/controller.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/pantasks/controller.c	(revision 34783)
@@ -0,0 +1,80 @@
+# include "pantasks.h"
+
+int controller_host    	  PROTO((int, char **));
+int controller_exit    	  PROTO((int, char **));
+int controller_status  	  PROTO((int, char **));
+int controller_jobstack   PROTO((int, char **));
+int controller_hoststack  PROTO((int, char **));
+int controller_machines   PROTO((int, char **));
+int controller_parameters PROTO((int, char **));
+int controller_verbose    PROTO((int, char **));
+int controller_version    PROTO((int, char **));
+int controller_run     	  PROTO((int, char **));
+int controller_stop    	  PROTO((int, char **));
+int controller_check   	  PROTO((int, char **));
+int controller_output  	  PROTO((int, char **));
+int controller_pulse   	  PROTO((int, char **));
+
+static Command controller_cmds[] = {
+  {1, "check",     controller_check,      "check controller host/job"},
+  // {1, "delete",    controller_delete,   "delete job on controller"},
+  {1, "exit",      controller_exit,       "shutdown controller"},
+  {1, "host",      controller_host,       "define host for controller"},
+  {1, "hoststack", controller_hoststack,  "define host for controller"},
+  // {1, "job",       controller_job,      "add jobs to controller"},
+  {1, "jobstack",  controller_jobstack,   "check controller status"},
+  {1, "machines",  controller_machines,   "print controller machine status"},
+  {1, "parameters",controller_parameters, "modify controller parameters"},
+  {1, "output",    controller_output,     "print controller output"},
+  {1, "run",       controller_run,        "start controller operation / set run levels"},
+  {1, "status",    controller_status,     "check controller status"},
+  {1, "stop",      controller_run,        "stop controller (no disconnect)"},
+  {1, "verbose",   controller_verbose,    "set controller verbosity"},
+  {1, "version",   controller_version,    "show controller version"},
+  {1, "pulse",     controller_pulse,      "set controller pulse"},
+};
+
+int controller (int argc, char **argv) {
+
+  int i, N, status;
+  CommandF *func;
+
+  if (argc < 2) {
+    gprint (GP_ERR, "USAGE: controller (command) ... \n");
+    return (FALSE);
+  }
+
+  if (!strcasecmp (argv[1], "help")) {
+    N = sizeof (controller_cmds) / sizeof (Command);
+
+    for (i = 0; i < N; i++) {
+      gprint (GP_LOG, "%-15s %s\n", controller_cmds[i].name, controller_cmds[i].help);
+    }
+    return (TRUE);
+  }
+
+  func = FindControllerCommand (argv[1]);
+  if (func == NULL) {
+    gprint (GP_ERR, "invalid controller command\n");
+    return (FALSE);
+  }
+
+  ControlLock(__func__);
+  status = (*func)(argc - 1, argv + 1);
+  ControlUnlock(__func__);
+  return (status);
+}
+
+CommandF *FindControllerCommand (char *cmd) {
+
+  int i, N;
+
+  N = sizeof (controller_cmds) / sizeof (Command);
+
+  for (i = 0; i < N; i++) {
+    if (!strcmp (controller_cmds[i].name, cmd)) {
+      return (controller_cmds[i].func);
+    }
+  }
+  return (NULL);
+}
Index: /branches/sc_branches/pantasks_condor/pantasks/controller_check.c
===================================================================
--- /branches/sc_branches/pantasks_condor/pantasks/controller_check.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/pantasks/controller_check.c	(revision 34783)
@@ -0,0 +1,45 @@
+# include "pantasks.h"
+
+#ifdef IPP_CONDOR
+#include "ipp_condor.h"
+#endif
+
+int controller_check (int argc, char **argv) {
+
+  int status;
+  char command[1024];
+  IOBuffer buffer;
+
+  if (argc != 3) goto usage;
+  if (strcasecmp (argv[1], "JOB") &&
+      strcasecmp (argv[1], "HOST")) goto usage;
+
+#ifndef IPP_CONDOR
+  /* check if controller is running */
+  status = CheckControllerStatus ();
+  if (!status) {
+    gprint (GP_LOG, "controller is not running\n");
+    return (TRUE);
+  }
+
+  sprintf (command, "check %s %s", argv[1], argv[2]);
+  InitIOBuffer (&buffer, 0x100);
+  status = ControllerCommand (command, CONTROLLER_PROMPT, &buffer);
+  if (VerboseMode()) {
+    gprint (GP_LOG, "controller command sent\n");
+    gprint (GP_LOG, "\n Nbytes received: %d\n", buffer.Nbuffer);
+  }
+  gwrite (buffer.buffer, 1, buffer.Nbuffer, GP_LOG);
+  FreeIOBuffer (&buffer);
+#else
+  status = 2;
+  sprintf(command, "balh");
+  InitIOBuffer (&buffer, 0x100);
+#endif
+  return (TRUE);
+
+usage:
+  gprint (GP_ERR, "USAGE: controller check job (jobID)\n");
+  gprint (GP_ERR, "USAGE: controller check host (hostID)\n");
+  return (FALSE);
+}
Index: /branches/sc_branches/pantasks_condor/pantasks/controller_exit.c
===================================================================
--- /branches/sc_branches/pantasks_condor/pantasks/controller_exit.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/pantasks/controller_exit.c	(revision 34783)
@@ -0,0 +1,14 @@
+# include "pantasks.h"
+
+int controller_exit (int argc, char **argv) {
+
+  if (argc != 2) {
+    gprint (GP_ERR, "USAGE: controller exit TRUE\n");
+    return (FALSE);
+  }
+
+  if (strcasecmp (argv[1], "TRUE")) return (FALSE);
+
+  QuitController ();
+  return (TRUE);
+}
Index: /branches/sc_branches/pantasks_condor/pantasks/controller_host.c
===================================================================
--- /branches/sc_branches/pantasks_condor/pantasks/controller_host.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/pantasks/controller_host.c	(revision 34783)
@@ -0,0 +1,69 @@
+#include "pantasks.h"
+
+#ifdef IPP_CONDOR
+#include "ipp_condor.h"
+#endif
+
+int controller_host (int argc, char **argv) {
+  int N, max_threads;
+
+  fprintf(stderr, "in controller_host\n");
+
+  max_threads = 0;
+  if ((N = get_argument (argc, argv, "-threads"))) {
+    remove_argument (N, &argc, argv);
+    max_threads = atoi(argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  if (argc != 3) goto usage;
+  if (max_threads && strcasecmp (argv[1], "ADD")) goto usage;
+
+  char command[1024];
+  IOBuffer buffer;
+  int status;
+
+  /* start controller connection (if needed) */
+  if (!StartController ()) {
+    gprint (GP_ERR, "failure to start pcontrol\n");
+    return (FALSE);
+  }
+
+  // the user may issue any of these commands:
+  // ADD, ON, RETRY, CHECK, OFF, DELETE
+  // we need to catch ADD and DELETE and modify our host table accordingly
+  if (!strcasecmp (argv[1], "ADD")) {
+    AddHost (argv[2], max_threads);
+  }
+
+  if (!strcasecmp (argv[1], "DELETE")) {
+    DeleteHost (argv[2]);
+  }
+
+  if (max_threads) {
+    sprintf (command, "host %s %s -threads %d", argv[1], argv[2], max_threads);
+  } else {
+    sprintf (command, "host %s %s", argv[1], argv[2]);
+  }
+  InitIOBuffer (&buffer, 0x100);
+
+  status = ControllerCommand (command, CONTROLLER_PROMPT, &buffer);
+
+  if (status) gwrite (buffer.buffer, 1, buffer.Nbuffer, GP_LOG);
+
+  FreeIOBuffer (&buffer);
+
+#ifdef IPP_CONDOR
+# ifdef DEBUG
+  printf("Showing hosts requirements\n");
+  printf("[%s]\n", get_hosts_requirements());
+# endif
+#endif
+  return (TRUE);
+
+usage:
+  gprint (GP_LOG, "USAGE: controller host (command) (hostname)\n");
+  gprint (GP_ERR, "  valid commands: add, on, retry, check, off, delete\n");
+  gprint (GP_ERR, "  -threads Nthreads is optional for 'add'\n");
+  return (FALSE);
+}
Index: /branches/sc_branches/pantasks_condor/pantasks/controller_hoststack.c
===================================================================
--- /branches/sc_branches/pantasks_condor/pantasks/controller_hoststack.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/pantasks/controller_hoststack.c	(revision 34783)
@@ -0,0 +1,35 @@
+# include "pantasks.h"
+
+int controller_hoststack (int argc, char **argv) {
+
+  int status;
+  char command[1024];
+  IOBuffer buffer;
+
+  if (argc != 2) {
+    gprint (GP_ERR, "USAGE: controller hoststack (hoststack)\n");
+    gprint (GP_ERR, "       (hoststack) : pending, busy, exit, crash, hung, done\n");
+    return (FALSE);
+  }
+
+  /* check if controller is running */
+  status = CheckControllerStatus ();
+  if (!status) {
+    gprint (GP_LOG, "controller is not active\n");
+    return (TRUE);
+  }
+
+  // XXX this has an error?  test this out...
+  sprintf (command, "hoststack %s", argv[1]);
+  InitIOBuffer (&buffer, 0x100);
+
+  status = ControllerCommand (command, CONTROLLER_PROMPT, &buffer);
+
+  if (status) {
+    gwrite (buffer.buffer, 1, buffer.Nbuffer, GP_LOG);
+  } else {
+    gprint (GP_LOG, "controller is not communicating\n");
+  }
+  FreeIOBuffer (&buffer);
+  return (TRUE);
+}
Index: /branches/sc_branches/pantasks_condor/pantasks/controller_jobstack.c
===================================================================
--- /branches/sc_branches/pantasks_condor/pantasks/controller_jobstack.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/pantasks/controller_jobstack.c	(revision 34783)
@@ -0,0 +1,35 @@
+# include "pantasks.h"
+
+int controller_jobstack (int argc, char **argv) {
+
+  int status;
+  char command[1024];
+  IOBuffer buffer;
+
+  if (argc != 2) {
+    gprint (GP_ERR, "USAGE: controller jobstack (jobstack)\n");
+    gprint (GP_ERR, "       (jobstack) : pending, busy, exit, crash, hung, done\n");
+    return (FALSE);
+  }
+
+  /* check if controller is running */
+  status = CheckControllerStatus ();
+  if (!status) {
+    gprint (GP_LOG, "controller is not active\n");
+    return (TRUE);
+  }
+
+  // XXX this has an error?  test this out...
+  sprintf (command, "jobstack %s", argv[1]);
+  InitIOBuffer (&buffer, 0x100);
+
+  status = ControllerCommand (command, CONTROLLER_PROMPT, &buffer);
+
+  if (status) {
+    gwrite (buffer.buffer, 1, buffer.Nbuffer, GP_LOG);
+  } else {
+    gprint (GP_LOG, "controller is not communicating\n");
+  }
+  FreeIOBuffer (&buffer);
+  return (TRUE);
+}
Index: /branches/sc_branches/pantasks_condor/pantasks/controller_machines.c
===================================================================
--- /branches/sc_branches/pantasks_condor/pantasks/controller_machines.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/pantasks/controller_machines.c	(revision 34783)
@@ -0,0 +1,35 @@
+# include "pantasks.h"
+
+int controller_machines (int argc, char **argv) {
+
+  int status;
+  char command[1024];
+  IOBuffer buffer;
+
+  if (argc != 1) {
+    gprint (GP_ERR, "USAGE: controller machines\n");
+    gprint (GP_ERR, "       prints status of controller machines\n");
+    return (FALSE);
+  }
+
+  /* check if controller is running */
+  status = CheckControllerStatus ();
+  if (!status) {
+    gprint (GP_LOG, "controller is not active\n");
+    return (TRUE);
+  }
+
+  // XXX this has an error?  test this out...
+  sprintf (command, "machines");
+  InitIOBuffer (&buffer, 0x100);
+
+  status = ControllerCommand (command, CONTROLLER_PROMPT, &buffer);
+
+  if (status) {
+    gwrite (buffer.buffer, 1, buffer.Nbuffer, GP_LOG);
+  } else {
+    gprint (GP_LOG, "controller is not communicating\n");
+  }
+  FreeIOBuffer (&buffer);
+  return (TRUE);
+}
Index: /branches/sc_branches/pantasks_condor/pantasks/controller_output.c
===================================================================
--- /branches/sc_branches/pantasks_condor/pantasks/controller_output.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/pantasks/controller_output.c	(revision 34783)
@@ -0,0 +1,18 @@
+# include "pantasks.h"
+
+int controller_output (int argc, char **argv) {
+
+  if ((argc != 1) || ((argc == 2) && (strcmp(argv[1], "flush")))) {
+    gprint (GP_ERR, "USAGE: controller output\n");
+    return (FALSE);
+  }
+
+  CheckControllerOutput ();
+  PrintControllerOutput ();
+
+  if (argc == 2) {
+    FlushControllerOutput ();
+  }
+
+  return (TRUE);
+}
Index: /branches/sc_branches/pantasks_condor/pantasks/controller_parameters.c
===================================================================
--- /branches/sc_branches/pantasks_condor/pantasks/controller_parameters.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/pantasks/controller_parameters.c	(revision 34783)
@@ -0,0 +1,46 @@
+# include "pantasks.h"
+
+int controller_parameters (int argc, char **argv) {
+
+  int status;
+  char command[1024];
+  IOBuffer buffer;
+
+  if (argc < 2) goto usage;
+  if (argc > 4) goto usage;
+  if (argc == 3) goto usage;
+  if ((argc == 4) && strcmp(argv[2], "=")) goto usage;
+
+  /* check if controller is running */
+  status = CheckControllerStatus ();
+  if (!status) {
+    gprint (GP_LOG, "controller is not active\n");
+    return (TRUE);
+  }
+
+  // XXX this has an error?  test this out...
+  if (argc == 2) {
+    sprintf (command, "parameters %s", argv[1]);
+  } else {
+    sprintf (command, "parameters %s = %s", argv[1], argv[3]);
+  }
+  InitIOBuffer (&buffer, 0x100);
+
+  status = ControllerCommand (command, CONTROLLER_PROMPT, &buffer);
+
+  if (status) {
+    gwrite (buffer.buffer, 1, buffer.Nbuffer, GP_LOG);
+  } else {
+    gprint (GP_LOG, "controller is not communicating\n");
+  }
+  FreeIOBuffer (&buffer);
+  return (TRUE);
+
+  usage:
+
+  gprint (GP_LOG, "USAGE: controller parameters (param) [= value])\n");
+  gprint (GP_LOG, "  valid parameters: connect_time, wanthost_wait, unwanted_host_jobs\n");
+  gprint (GP_LOG, "  (minimum matching word is allowed)\n");
+  gprint (GP_LOG, "  example: controller parameters connect = 2.0\n");
+  return (FALSE);
+}
Index: /branches/sc_branches/pantasks_condor/pantasks/controller_pulse.c
===================================================================
--- /branches/sc_branches/pantasks_condor/pantasks/controller_pulse.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/pantasks/controller_pulse.c	(revision 34783)
@@ -0,0 +1,26 @@
+# include "pantasks.h"
+
+int controller_pulse (int argc, char **argv) {
+
+  int status;
+  char command[1024];
+  IOBuffer buffer;
+
+  if (argc != 2) {
+    gprint (GP_ERR, "USAGE: controller pulse (usec)\n");
+    return (FALSE);
+  }
+
+  /* start controller connection (if needed) */
+  if (!StartController ()) {
+    gprint (GP_ERR, "failure to start pcontrol\n");
+    return (FALSE);
+  }
+
+  sprintf (command, "pulse %d", atoi(argv[1]));
+  InitIOBuffer (&buffer, 0x100);
+  status = ControllerCommand (command, CONTROLLER_PROMPT, &buffer);
+  if (status) gwrite (buffer.buffer, 1, buffer.Nbuffer, GP_LOG);
+  FreeIOBuffer (&buffer);
+  return (TRUE);
+}
Index: /branches/sc_branches/pantasks_condor/pantasks/controller_run.c
===================================================================
--- /branches/sc_branches/pantasks_condor/pantasks/controller_run.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/pantasks/controller_run.c	(revision 34783)
@@ -0,0 +1,54 @@
+# include "pantasks.h"
+
+int controller_run (int argc, char **argv) {
+
+#ifndef IPP_CONDOR
+  int status;
+  char command[1024];
+  IOBuffer buffer;
+
+  if ((argc > 2) ||
+      get_argument (argc, argv, "help") ||
+      get_argument (argc, argv, "-h") ||
+      get_argument (argc, argv, "--help"))
+  {
+    if (!strcmp (argv[0], "run")) {
+      gprint (GP_ERR, "USAGE: controller run [level]\n");
+      gprint (GP_ERR, "  allowed levels:\n");
+      gprint (GP_ERR, "  all (default) : manage machines, spawn jobs, harvest jobs\n");
+      gprint (GP_ERR, "  reap          : manage machines, harvest jobs\n");
+      gprint (GP_ERR, "  hosts         : manage machines, not jobs\n");
+      gprint (GP_ERR, "  none          : all stop\n");
+    } else {
+      gprint (GP_ERR, "USAGE: controller stop (immediate processing halt)\n");
+    }
+    return (FALSE);
+  }
+
+  /* check if controller is running */
+  status = CheckControllerStatus ();
+  if (!status) {
+    gprint (GP_LOG, "controller is not active\n");
+    return (TRUE);
+  }
+
+  if (argc == 1) {
+    strcpy (command, argv[0]);
+  } else {
+    sprintf (command, "run %s", argv[1]);
+  }
+
+  InitIOBuffer (&buffer, 0x100);
+  status = ControllerCommand (command, CONTROLLER_PROMPT, &buffer);
+  if (status) {
+    gwrite (buffer.buffer, 1, buffer.Nbuffer, GP_LOG);
+  } else {
+    gprint (GP_LOG, "controller is not responding\n");
+  }
+  FreeIOBuffer (&buffer);
+#else
+  gprint (GP_ERR, "Not used in Condor\n");
+#endif
+
+  return (TRUE);
+}
Index: /branches/sc_branches/pantasks_condor/pantasks/controller_status.c
===================================================================
--- /branches/sc_branches/pantasks_condor/pantasks/controller_status.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/pantasks/controller_status.c	(revision 34783)
@@ -0,0 +1,34 @@
+#include "pantasks.h"
+#include "ipp_condor.h"
+
+int controller_status (int argc, char **argv) {
+#ifndef IPP_CONDOR
+  int status;
+  char command[1024];
+  IOBuffer buffer;
+  if (argc != 1) {
+    gprint (GP_ERR, "USAGE: controller status\n");
+    return (FALSE);
+  }
+  /* check if controller is running */
+  status = CheckControllerStatus ();
+  if (!status) {
+    gprint (GP_LOG, "controller is not active\n");
+    return (TRUE);
+  }
+  sprintf (command, "status");
+  InitIOBuffer (&buffer, 0x100);
+  status = ControllerCommand (command, CONTROLLER_PROMPT, &buffer);
+  if (status) {
+    gwrite (buffer.buffer, 1, buffer.Nbuffer, GP_LOG);
+  } else {
+    gprint (GP_LOG, "controller is not communicating\n");
+  }
+  FreeIOBuffer (&buffer);
+#else
+  gprint(GP_LOG, strprintf("%s\n", get_hosts_requirements()));
+  gprint(GP_LOG, strprintf("%s\n", get_host_status("ippc63.ifa.hawaii.edu")));
+
+#endif
+  return (TRUE);
+}
Index: /branches/sc_branches/pantasks_condor/pantasks/controller_threads.c
===================================================================
--- /branches/sc_branches/pantasks_condor/pantasks/controller_threads.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/pantasks/controller_threads.c	(revision 34783)
@@ -0,0 +1,48 @@
+# include "pantasks.h"
+
+/** things related to CheckController **/
+
+static int CheckControllerRun = FALSE;
+
+void CheckControllerSetState (int state) {
+  CheckControllerRun = state;
+}
+int CheckControllerGetState () {
+  return (CheckControllerRun);
+}
+
+void *CheckControllerThread (void *data) {
+
+  char log_stdout[128], log_stderr[128];
+
+  gprintInit ();  // each thread needs to init the printing system
+  // define server output log files
+  if (VarConfig ("PANTASKS_SERVER_STDOUT", "%s", log_stdout) != NULL) {
+      gprintSetFileThisThread (GP_LOG, log_stdout);
+  }
+  if (VarConfig ("PANTASKS_SERVER_STDERR", "%s", log_stderr) != NULL) {
+      gprintSetFileThisThread (GP_ERR, log_stderr);
+  }
+
+  while (1) {
+
+    // check for thread suspend
+    if (!CheckControllerRun) {
+      usleep (100000); // idle if thread action is suspended
+      continue;
+    }
+
+    // one run of the task checker
+    ControlLock(__func__);
+    CheckController ();
+    ControlUnlock(__func__);
+
+    ControlLock(__func__);
+    CheckControllerOutput ();
+    ControlUnlock(__func__);
+
+    if (VerboseMode() == 2) fprintf (stderr, "C");
+    // fprintf (stderr, "**** C ****");
+    usleep (500000); // allow other threads a chance to run
+  }
+}
Index: /branches/sc_branches/pantasks_condor/pantasks/controller_verbose.c
===================================================================
--- /branches/sc_branches/pantasks_condor/pantasks/controller_verbose.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/pantasks/controller_verbose.c	(revision 34783)
@@ -0,0 +1,35 @@
+# include "pantasks.h"
+
+int controller_verbose (int argc, char **argv) {
+
+  int status;
+  char command[1024];
+  IOBuffer buffer;
+
+  if (argc != 2) {
+    gprint (GP_ERR, "USAGE: controller verbose (level)\n");
+    gprint (GP_ERR, "       (level) : off, on, toggle\n");
+    return (FALSE);
+  }
+
+  /* check if controller is running */
+  status = CheckControllerStatus ();
+  if (!status) {
+    gprint (GP_LOG, "controller is not active\n");
+    return (TRUE);
+  }
+
+  // XXX this has an error?  test this out...
+  sprintf (command, "verbose %s", argv[1]);
+  InitIOBuffer (&buffer, 0x100);
+
+  status = ControllerCommand (command, CONTROLLER_PROMPT, &buffer);
+
+  if (status) {
+    gwrite (buffer.buffer, 1, buffer.Nbuffer, GP_LOG);
+  } else {
+    gprint (GP_LOG, "controller is not communicating\n");
+  }
+  FreeIOBuffer (&buffer);
+  return (TRUE);
+}
Index: /branches/sc_branches/pantasks_condor/pantasks/controller_version.c
===================================================================
--- /branches/sc_branches/pantasks_condor/pantasks/controller_version.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/pantasks/controller_version.c	(revision 34783)
@@ -0,0 +1,39 @@
+# include "pantasks.h"
+
+int controller_version (int argc, char **argv) {
+
+#ifndef IPP_CONDOR
+  int status;
+  char command[1024];
+  IOBuffer buffer;
+
+  if (argc != 1) {
+    gprint (GP_ERR, "USAGE: controller version\n");
+    return (FALSE);
+  }
+
+  /* check if controller is running */
+  status = CheckControllerStatus ();
+  if (!status) {
+    gprint (GP_LOG, "controller is not active\n");
+    return (TRUE);
+  }
+
+  sprintf (command, "version");
+  InitIOBuffer (&buffer, 0x100);
+
+  status = ControllerCommand (command, CONTROLLER_PROMPT, &buffer);
+
+  if (status) {
+    gwrite (buffer.buffer, 1, buffer.Nbuffer, GP_LOG);
+  } else {
+    gprint (GP_LOG, "controller is not communicating\n");
+  }
+  FreeIOBuffer (&buffer);
+
+#else
+  gprint(GP_ERR, "TODO: Show a version here\n");
+#endif
+
+  return (TRUE);
+}
Index: /branches/sc_branches/pantasks_condor/pantasks/delete.c
===================================================================
--- /branches/sc_branches/pantasks_condor/pantasks/delete.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/pantasks/delete.c	(revision 34783)
@@ -0,0 +1,53 @@
+# include "pantasks.h"
+
+int delete_job (int argc, char **argv) {
+
+  Job *job;
+  int JobID;
+
+  if (argc != 3) goto usage;
+
+  if (!strcasecmp (argv[1], "job")) {
+    JobID = atoi (argv[2]);
+
+    JobTaskLock();
+    job = FindJob (JobID);
+    if (job == NULL) {
+      gprint (GP_LOG, "job not found\n");
+      JobTaskUnlock();
+      return (TRUE);
+    }
+
+    if (job[0].mode == JOB_LOCAL) {
+      if (!KillLocalJob (job)) {
+	job[0].state = JOB_HUNG;
+	if (VerboseMode()) gprint (GP_LOG, "child process %d is hung, cannot kill\n", job[0].pid);
+	JobTaskUnlock();
+	return (FALSE);
+      }
+    } else {
+      if (!KillControllerJob (job)) {
+	job[0].state = JOB_HUNG;
+	if (VerboseMode()) gprint (GP_LOG, "child process %d is hung, cannot kill\n", job[0].pid);
+	JobTaskUnlock();
+	return (FALSE);
+      }
+    }    
+    DeleteJob (job);
+    gprint (GP_LOG, "job removed\n");
+    JobTaskUnlock();
+    return (TRUE);
+  }
+
+  if (!strcasecmp (argv[1], "task")) {
+    gprint (GP_ERR, "delete task not implemented yet\n");
+    return (FALSE);
+  }
+
+usage:
+  gprint (GP_ERR, "USAGE: delete job (JobID)\n");
+  gprint (GP_ERR, "USAGE: delete task (TaskName)\n");
+  return (FALSE);
+}
+
+/* find job from jobID, kill and delete */
Index: /branches/sc_branches/pantasks_condor/pantasks/flush.c
===================================================================
--- /branches/sc_branches/pantasks_condor/pantasks/flush.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/pantasks/flush.c	(revision 34783)
@@ -0,0 +1,19 @@
+# include "pantasks.h"
+
+int flush_jobs (int argc, char **argv) {
+
+  if (argc != 2) goto usage;
+
+  if (!strcasecmp (argv[1], "jobs")) {
+    JobTaskLock();
+    FlushJobs ();
+    JobTaskUnlock();
+    return (TRUE);
+  }
+  
+usage:
+  gprint (GP_ERR, "USAGE: flush jobs\n");
+  return (FALSE);
+}
+
+/* find job from jobID, kill and delete */
Index: /branches/sc_branches/pantasks_condor/pantasks/help/controller
===================================================================
--- /branches/sc_branches/pantasks_condor/pantasks/help/controller	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/pantasks/help/controller	(revision 34783)
@@ -0,0 +1,29 @@
+
+   controller (command) ...
+
+   Interact with the parallel controller 'pcontrol'.  This command
+   groups together several controller sub-commands, listed below.
+
+
+   controller host (hostname)
+
+   Define a new machine available to the parallel controller.  
+
+
+   controller check (host) (HostID)
+   controller check (job) (JobID)
+
+   Check on the status of a specific host or job known to the parallel
+   controller.
+
+
+   controller status
+
+   Query the parallel controller for its current status
+
+
+   controller output
+
+   Dump the contents of the parallel controller message buffers.
+   These are the messages from the controller itself, rather than the
+   messages generated by jobs run by the controller.
Index: /branches/sc_branches/pantasks_condor/pantasks/help/task
===================================================================
--- /branches/sc_branches/pantasks_condor/pantasks/help/task	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/pantasks/help/task	(revision 34783)
@@ -0,0 +1,27 @@
+
+   task (name)
+
+   Define or modify a task.  A task is defined by issuing a series of
+   task commands, listed below (see their individual help pages).  A
+   task describes the rules for constructing jobs.  At a minimum, a
+   task must include a command, defining the UNIX command to run for
+   each job.  Other task commands define optional concepts for the
+   task.  These include: 
+
+   - a macro to be run before the job is constructed (task.exec).
+   - a set of macros to be run when the job is completed (task.exit)
+   - time ranges in which the task should be run (or not) (trange) 
+   - required / desired parallel controller hosts (host)
+   - job contruction time interval (periods -exec)
+   - job polling time interval (periods -poll)
+   - job timeouts (periods -timeout)
+   - maximum number of jobs to construct
+
+   See also:
+   task.command
+   task.exit
+   task.exec
+   task.trange
+   task.nmax
+   task.host
+   task.periods
Index: /branches/sc_branches/pantasks_condor/pantasks/help/task.command
===================================================================
--- /branches/sc_branches/pantasks_condor/pantasks/help/task.command	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/pantasks/help/task.command	(revision 34783)
@@ -0,0 +1,15 @@
+
+   command (arg0) (arg1) ...
+
+   Define the rule for constructing a job command for a task.  The
+   arguments are UNIX command and the collection of command arguments.
+   The arguments may include references to Opihi variables.  The task
+   command may be included at the top level of the task, or it may be
+   defined within the task exec macro.  In the latter case, the
+   command is constructed only when the exec macro is run, every job
+   construction interval.  Thus, the command arguments may depend on
+   the result of the exec macro operation.   
+
+   See also:
+   task
+   task.exec
Index: /branches/sc_branches/pantasks_condor/pantasks/help/task.exec
===================================================================
--- /branches/sc_branches/pantasks_condor/pantasks/help/task.exec	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/pantasks/help/task.exec	(revision 34783)
@@ -0,0 +1,17 @@
+
+   task.exec
+
+   A special macro to be executed before constructing the job command.
+   The macro is defined by commands following the 'task.exec' command,
+   ending with the word 'end'.  If the exit status of the task.exec
+   macro is FALSE, then the job is NOT constructed.  This allows the
+   construction of a job to be conditional on other, external
+   quantites.  (Note: the exit status of the macro is false if 'break'
+   is called).  The task command may be defined within the task.exec
+   macro, in which case the command is constructed only when the exec
+   macro is run, every job construction interval.  Thus, the command
+   arguments may depend on the result of the exec macro operation.
+
+   See also:
+   task
+   task.command
Index: /branches/sc_branches/pantasks_condor/pantasks/init.c
===================================================================
--- /branches/sc_branches/pantasks_condor/pantasks/init.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/pantasks/init.c	(revision 34783)
@@ -0,0 +1,75 @@
+# include "pantasks.h"
+
+int controller      PROTO((int, char **));
+int task            PROTO((int, char **));
+int task_host       PROTO((int, char **));
+int task_nice       PROTO((int, char **));
+int task_nmax       PROTO((int, char **));
+int task_npending   PROTO((int, char **));
+int task_active     PROTO((int, char **));
+int task_trange     PROTO((int, char **));
+int task_macros     PROTO((int, char **));
+int task_command    PROTO((int, char **));
+int task_options    PROTO((int, char **));
+int task_periods    PROTO((int, char **));
+int task_stdout     PROTO((int, char **));
+int task_stderr     PROTO((int, char **));
+int run             PROTO((int, char **));
+int stop            PROTO((int, char **));
+int halt            PROTO((int, char **));
+int flush_jobs      PROTO((int, char **));
+int showtask        PROTO((int, char **));
+int status_sys      PROTO((int, char **));
+int kill_job        PROTO((int, char **));
+int delete_job      PROTO((int, char **));
+int verbose         PROTO((int, char **));
+int version         PROTO((int, char **));
+int ipptool2book    PROTO((int, char **));
+
+static Command cmds[] = {  
+  {1, "active",     task_active,   "set the active state of a task"},
+  {1, "command",    task_command,  "define executed command for a task"},
+  {1, "controller", controller,    "controller commands"},
+  {1, "delete",     delete_job,    "delete job"},
+  {1, "halt",       halt,          "halt the scheduler (no job harvesting)"},
+  {1, "host",       task_host,     "define host machine for a task"},
+  {1, "nice",       task_nice,     "set nice priority level for a task"},
+  {1, "ipptool2book", ipptool2book, "convert queue with ipptool output to book"},
+  {1, "kill",       kill_job,      "kill job"},
+  {1, "nmax",       task_nmax,     "define maximum number of jobs for a task"},
+  {1, "npending",   task_npending, "define maximum number of outstanding jobs for a task"},
+  {1, "options",    task_options,  "define optional variables associated with the job task"},
+  {1, "periods",    task_periods,  "define time scales for a task"},
+  {1, "run",        run,           "run the scheduler"},
+  {1, "flush",      flush_jobs,    "flush all jobs from the queue"},
+  {1, "showtask",   showtask,      "list a task"},
+  {1, "status",     status_sys,    "get system or task status"},
+  {1, "stderr",     task_stderr,   "define a file for the job stderr dump"},
+  {1, "stdout",     task_stdout,   "define a file for the job stdout dump"},
+  {1, "stop",       stop,          "stop the scheduler (continue job harvesting)"},
+  {1, "task",       task,          "define a schedulable task"},
+  {1, "task.exec",  task_macros,   "define pre-exec macro for a task"},
+  {1, "task.exit",  task_macros,   "define exit macros for a task"},
+  {1, "trange",     task_trange,   "define valid/invalid time periods for a task"},
+  {1, "verbose",    verbose,       "set/toggle verbose mode"},
+  {1, "version",    version,       "show version information"},
+}; 
+
+void InitPantasks () {
+  
+  int i;
+
+  InitTasks ();
+  InitJobs ();
+  InitJobIDs ();
+
+  for (i = 0; i < sizeof (cmds) / sizeof (Command); i++) {
+    AddCommand (&cmds[i]);
+  }
+}
+
+void FreePantasks () {
+  FreeTasks ();
+  FreeJobs ();
+  FreeJobIDs ();
+}
Index: /branches/sc_branches/pantasks_condor/pantasks/init_client.c
===================================================================
--- /branches/sc_branches/pantasks_condor/pantasks/init_client.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/pantasks/init_client.c	(revision 34783)
@@ -0,0 +1,34 @@
+# include "pantasks.h"
+
+int invalid           PROTO((int, char **));
+int server_shutdown   PROTO((int, char **));
+int server_disconnect PROTO((int, char **));
+int server_connect    PROTO((int, char **));
+
+/* we also list here commands which are not valid for the client, but represent valid
+   pantasks commands.  these are caught by the client and sent to the 'invalid'
+   function */
+
+static Command cmds[] = {  
+  {1, "task",       invalid,  	 "define a schedulable task"},
+  {1, "task.exit",  invalid,  	 "define exit macros for a task"},
+  {1, "task.exec",  invalid,  	 "define pre-exec macro for a task"},
+
+  {1, "shutdown",   server_shutdown,   "send exit signal to server"},
+  {1, "disconnect", server_disconnect, "close connection to server"},
+  {1, "connect",    server_connect,    "open connection to server"},
+
+}; 
+
+void InitPantasksClient () {
+  
+  int i;
+
+  for (i = 0; i < sizeof (cmds) / sizeof (Command); i++) {
+    AddCommand (&cmds[i]);
+  }
+
+}
+
+void FreePantasksClient () {
+}
Index: /branches/sc_branches/pantasks_condor/pantasks/init_server.c
===================================================================
--- /branches/sc_branches/pantasks_condor/pantasks/init_server.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/pantasks/init_server.c	(revision 34783)
@@ -0,0 +1,79 @@
+# include "pantasks.h"
+
+int controller      PROTO((int, char **));
+int task            PROTO((int, char **));
+int task_host       PROTO((int, char **));
+int task_nice       PROTO((int, char **));
+int task_nmax       PROTO((int, char **));
+int task_npending   PROTO((int, char **));
+int task_active     PROTO((int, char **));
+int task_trange     PROTO((int, char **));
+int task_macros     PROTO((int, char **));
+int task_command    PROTO((int, char **));
+int task_options    PROTO((int, char **));
+int task_periods    PROTO((int, char **));
+int task_stdout     PROTO((int, char **));
+int task_stderr     PROTO((int, char **));
+int flush_jobs      PROTO((int, char **));
+int status_server   PROTO((int, char **));
+int showtask        PROTO((int, char **));
+int kill_job        PROTO((int, char **));
+int delete_job      PROTO((int, char **));
+int verbose         PROTO((int, char **));
+int version         PROTO((int, char **));
+int server          PROTO((int, char **));
+int ipptool2book    PROTO((int, char **));
+
+int server_run	    PROTO((int, char **));
+int server_stop	    PROTO((int, char **));
+int server_halt	    PROTO((int, char **));
+
+static Command cmds[] = {  
+  {1, "active",     task_active,   "set the active state of a task"},
+  {1, "command",    task_command,  "define executed command for a task"},
+  {1, "controller", controller,    "controller commands"},
+  {1, "delete",     delete_job,    "delete job"},
+  {1, "host",       task_host,     "define host machine for a task"},
+  {1, "nice",       task_nice,     "set nice priority level for a task"},
+  {1, "ipptool2book", ipptool2book, "convert queue with ipptool output to book"},
+  {1, "kill",       kill_job,      "kill job"},
+  {1, "nmax",       task_nmax,     "define maximum number of jobs for a task"},
+  {1, "options",    task_options, "define optional variables associated with the job task"},
+  {1, "npending",   task_npending, "define maximum number of outstanding jobs for a task"},
+  {1, "periods",    task_periods,  "define time scales for a task"},
+  {1, "flush",      flush_jobs,    "flush all jobs from the queue"},
+  {1, "server",     server,        "server-specific commands"},
+  {1, "showtask",   showtask,      "list a task"},
+  {1, "status",     status_server, "get system status"},
+  {1, "stderr",     task_stderr,   "define a file for the job stderr dump"},
+  {1, "stdout",     task_stdout,   "define a file for the job stdout dump"},
+  {1, "task",       task,          "define a schedulable task"},
+  {1, "task.exec",  task_macros,   "define pre-exec macro for a task"},
+  {1, "task.exit",  task_macros,   "define exit macros for a task"},
+  {1, "trange",     task_trange,   "define valid/invalid time periods for a task"},
+  {1, "verbose",    verbose,       "set/toggle verbose mode"},
+  {1, "version",    version,       "show version information"},
+
+  {1, "run",        server_run,    "run scheduler"},
+  {1, "stop",       server_stop,   "stop scheduler"},
+  {1, "halt",       server_halt,   "halt scheduler"},
+}; 
+
+void InitPantasksServer () {
+  
+  int i;
+
+  InitTasks ();
+  InitJobs ();
+  InitJobIDs ();
+
+  for (i = 0; i < sizeof (cmds) / sizeof (Command); i++) {
+    AddCommand (&cmds[i]);
+  }
+}
+
+void FreePantasksServer () {
+  FreeTasks ();
+  FreeJobs ();
+  FreeJobIDs ();
+}
Index: /branches/sc_branches/pantasks_condor/pantasks/input_threads.c
===================================================================
--- /branches/sc_branches/pantasks_condor/pantasks/input_threads.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/pantasks/input_threads.c	(revision 34783)
@@ -0,0 +1,43 @@
+# include "pantasks.h"
+
+/** things related to CheckInputs **/
+
+static int CheckInputsRun = TRUE;
+
+void CheckInputsSetState (int state) {
+  CheckInputsRun = state;
+}
+int CheckInputsGetState () {
+  return (CheckInputsRun);
+}
+
+void *CheckInputsThread (void *data) {
+
+  char log_stdout[128], log_stderr[128];
+
+  gprintInit ();  // each thread needs to init the printing system
+
+  // define server output log files
+  if (VarConfig ("PANTASKS_SERVER_STDOUT", "%s", log_stdout) != NULL) {
+      gprintSetFileThisThread (GP_LOG, log_stdout);
+  }
+  if (VarConfig ("PANTASKS_SERVER_STDERR", "%s", log_stderr) != NULL) {
+      gprintSetFileThisThread (GP_ERR, log_stderr);
+  }
+
+  while (1) {
+
+    // check for thread suspend
+    if (!CheckInputsRun) {
+      usleep (100000); // idle if thread action is suspended
+      continue;
+    }
+
+    // one run of the task checker
+    SerialThreadLock ();
+    CheckInputs ();
+    SerialThreadUnlock ();
+    if (VerboseMode() == 2) fprintf (stderr, "I");
+    usleep (10000); // allow other threads a chance to run
+  }
+}
Index: /branches/sc_branches/pantasks_condor/pantasks/invalid.c
===================================================================
--- /branches/sc_branches/pantasks_condor/pantasks/invalid.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/pantasks/invalid.c	(revision 34783)
@@ -0,0 +1,8 @@
+# include "pantasks.h"
+
+int invalid (int argc, char **argv) {
+
+  gprint (GP_ERR, "%s is not valid for the pantasks client\n", argv[0]);
+  return (TRUE);
+
+}
Index: /branches/sc_branches/pantasks_condor/pantasks/ipptool2book.c
===================================================================
--- /branches/sc_branches/pantasks_condor/pantasks/ipptool2book.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/pantasks/ipptool2book.c	(revision 34783)
@@ -0,0 +1,247 @@
+# include "pantasks.h"
+
+# define FREEKEYS \
+  if (keys) { for (i = 0; i < Nkeys; i++) { free (keys[i]); } free (keys); } \
+  for (i = 0; i < setWordN; i++) { free (setWordList[i]); free (setWordValue[i]); } \
+  free (setWordValue); free (setWordList);
+
+// convert the named queue with ipptool metadata output into book format
+int ipptool2book (int argc, char **argv) {
+
+  int i, N, onPage, found, Unique, Nkeys, NKEYS;
+  char *line, *tmpword, *tmpvalue;
+  char pagename[512]; // XXX this should be made dynamic, though it is an unlikey problem
+  char *bookName, **keys, *p, *q;
+  char **setWordList;
+  char **setWordValue;
+  int setWordN, setWordNalloc;
+
+  Page *page = NULL;
+  Book *book = NULL;
+  Queue *queue = NULL;
+
+  /* supply additional constant words */
+  setWordN = 0;
+  setWordNalloc = 10;
+  ALLOCATE (setWordList, char *, setWordNalloc);
+  ALLOCATE (setWordValue, char *, setWordNalloc);
+  while ((N = get_argument (argc, argv, "-setword"))) {
+    remove_argument (N, &argc, argv);
+    setWordList[setWordN] = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+    setWordValue[setWordN] = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+    setWordN ++;
+    if (setWordN >= setWordNalloc) {
+      setWordNalloc += 10;
+      REALLOCATE (setWordList, char *, setWordNalloc);
+      REALLOCATE (setWordValue, char *, setWordNalloc);
+    }      
+  }
+
+  Unique = FALSE;
+  if ((N = get_argument (argc, argv, "-uniq"))) {
+    remove_argument (N, &argc, argv);
+    Unique = TRUE;
+  }
+
+  Nkeys = 0;
+  keys = NULL;
+  if ((N = get_argument (argc, argv, "-key"))) {
+    remove_argument (N, &argc, argv);
+
+    /* parse the key (word:word:word) into constituents */
+    NKEYS = 10;
+    ALLOCATE (keys, char *, NKEYS);
+    
+    p = q = argv[N];
+    while (q != NULL) {
+      q = strchr (p, ':');
+      keys[Nkeys] = (q == NULL) ? strcreate (p) : strncreate (p, q - p);
+      p = q + 1;
+      Nkeys ++;
+      CHECK_REALLOCATE (keys, char *, NKEYS, Nkeys, 10);
+    }
+    remove_argument (N, &argc, argv);
+  }
+
+  if (argc != 3) {
+    gprint (GP_ERR, "USAGE: ipptool2book (queue) (book) [-uniq] [-key key[:key..]]\n");
+    FREEKEYS;
+    return (TRUE);
+  }
+
+  queue = FindQueue (argv[1]);
+  if (queue == NULL) {
+    gprint (GP_ERR, "queue %s not found\n", argv[1]);
+    FREEKEYS;
+    return (TRUE);
+  }
+    
+  book = CreateBook (argv[2]);
+
+  /* the first non-whitespace line of the queue should contain:
+     BookName MULTI
+  */
+
+  /* scan for first non-emtpy line */
+  while (TRUE) {
+    line = PopQueue (queue);
+    if (!line) {
+      FREEKEYS;
+      return (TRUE); // no output in queue - not an error?
+    }
+    stripwhite (line);
+    if (line[0] == '#') {
+      free (line);
+      continue;
+    }
+    if (*line) break;
+    free (line);
+  }
+  bookName = thisword (line);
+  tmpword = nextword (line);
+
+  if (!tmpword || strcmp(tmpword, "MULTI")) {
+    gprint (GP_ERR, "ERROR: missing metadata output name on first line\n");
+    free (bookName);
+    free (line);
+    FREEKEYS;
+    return (TRUE);
+  }
+  free (line);
+  
+  onPage = FALSE;
+  while (TRUE) {
+    line = PopQueue (queue);
+    if (!line) {
+      free (bookName);
+      if (onPage) {
+	gprint (GP_ERR, "ERROR: unterminated metadata\n");
+	FREEKEYS;
+	return (TRUE);
+      }
+      FREEKEYS;
+      return (TRUE);
+    }
+    stripwhite (line);
+    if (!*line) {
+      free (line);
+      continue;
+    }
+    if (line[0] == '#') {
+      free (line);
+      continue;
+    }
+    
+    tmpword = thisword (line);
+    if (tmpword == NULL) abort(); // should not happen if line exists
+
+    if (!onPage) {
+      if (strcmp(tmpword, bookName)) {
+	gprint (GP_ERR, "ERROR: unexpected metadata name %s\n", tmpword);
+	free (line);
+	free (tmpword);
+	free (bookName);
+	FREEKEYS;
+	return (TRUE);
+      }
+      free (tmpword);
+      tmpword = thisword (nextword (line));
+      if (strcmp(tmpword, "METADATA")) {
+	gprint (GP_ERR, "ERROR: missing METADATA tag\n");
+	free (line);
+	free (tmpword);
+	free (bookName);
+	FREEKEYS;
+	return TRUE;
+      }
+      onPage = TRUE;
+      // we don't know the page name until we load its data?
+      sprintf (pagename, "page.%03d", book[0].Npages);
+      page = CreatePage (book, pagename);
+      free (line);
+      free (tmpword);
+      continue;
+    } 
+
+    /* close out the page */
+    if (!strcmp(tmpword, "END")) {
+      // XXX set the page name based on the contents
+      free (tmpword);
+      free (line);
+
+      if (keys) {
+	memset (pagename, 0, 512);
+	for (i = 0; i < Nkeys; i++) {
+	  tmpword = BookGetWord (page, keys[i]);
+	  if (tmpword == NULL) {
+	    gprint (GP_ERR, "ERROR: missing key %s for ID\n", keys[i]);
+	    FREEKEYS;
+	    return (TRUE);
+	  }
+	  if (i > 0) strcat (pagename, ":");
+	  strcat (pagename, tmpword);
+	}
+	free (page[0].name);
+	page[0].name = strcreate (pagename);
+      }
+
+      // add the fixed words to the page
+      for (i = 0; i < setWordN; i++) {
+	BookSetWord (page, setWordList[i], setWordValue[i]);
+      }
+
+      if (Unique) {
+	/* search for an existing page with this name
+	   delete this one if one is found */
+	/* XXX don't attach this page to the book until this step is passed */	
+
+	found = FALSE;
+	for (i = 0; !found && (i < book[0].Npages); i++) {
+	  if (book[0].pages[i] == page) continue;
+	  if (!strcmp (book[0].pages[i][0].name, page[0].name)) {
+	    found = TRUE;
+	  }
+	}
+	if (found) {
+	  DeletePage (book, page);
+	}
+      }
+
+      onPage = FALSE;
+      continue;
+    }
+
+    tmpvalue = thisword (nextword(nextword(line)));
+    if (tmpvalue == NULL) {
+      gprint (GP_ERR, "ERROR: missing value for %s\n", tmpword);
+      free (tmpword);
+      free (line);
+      free (bookName);
+      FREEKEYS;
+      return (TRUE);
+    }
+    BookSetWord (page, tmpword, tmpvalue);
+    free (tmpword);
+    free (tmpvalue);
+    free (line);
+  }    
+  FREEKEYS;
+  return (TRUE);
+}
+
+/* scan through the queue lines.  these should look like:  
+
+bookName METADATA
+key type value
+key type value
+...
+END
+
+bookName METADATA
+key type value
+key type value
+...
+END
+*/
Index: /branches/sc_branches/pantasks_condor/pantasks/job.c
===================================================================
--- /branches/sc_branches/pantasks_condor/pantasks/job.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/pantasks/job.c	(revision 34783)
@@ -0,0 +1,68 @@
+# include "pantasks.h"
+
+/* this will require a bit of care: to define a job, we need
+   to specify:
+   - the command 
+     this is defined in the job command: job argv0 argv1 argv2...
+   - timeout (an option to the command)
+   - exit macros?
+     do nothing by default?
+   - stderr / stdout disposal?
+     save in a specific buffer by default?
+     send to a file?
+
+   the command can look just like the controller equivalent one:
+   job [-host host] [-timeout timeout] args...
+
+*/
+
+int job (int argc, char **argv) {
+
+  char *Host, **targv;
+  int i, N, Mode, targc, Timeout;
+  IDtype JobID;
+
+  Host = NULL;
+  Mode = PCONTROL_JOB_ANYHOST;
+  if ((N = get_argument (argc, argv, "-host"))) {
+    remove_argument (N, &argc, argv);
+    Host = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+    Mode = PCONTROL_JOB_WANTHOST;
+  }
+  if ((N = get_argument (argc, argv, "+host"))) {
+    if (Mode == PCONTROL_JOB_WANTHOST) {
+      gprint (GP_ERR, "ERROR: -host and +host are incompatible\n");
+      FREE (Host);
+      return (FALSE);
+    }
+    remove_argument (N, &argc, argv);
+    Host = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+    Mode = PCONTROL_JOB_NEEDHOST;
+  }
+  if (Host == NULL) Host = strcreate ("anyhost");
+ 
+  Timeout = 100;
+  if ((N = get_argument (argc, argv, "-timeout"))) {
+    remove_argument (N, &argc, argv);
+    Timeout = atoi (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  if (argc < 2) {
+    gprint (GP_ERR, "USAGE: job [options] (arg0) (arg1) ... (argN)\n");
+    FREE (Host);
+    return (FALSE);
+  }
+  
+  targc = argc - 1;
+  ALLOCATE (targv, char *, targc);
+  for (i = 1; i < argc; i++) {
+    targv[i-1] = strcreate (argv[i]);
+  }
+
+  JobID = AddJob (Host, Mode, Timeout, targc, targv);
+  gprint (GP_LOG, "JobID: %d\n", JobID);
+  return (TRUE);
+}
Index: /branches/sc_branches/pantasks_condor/pantasks/job_threads.c
===================================================================
--- /branches/sc_branches/pantasks_condor/pantasks/job_threads.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/pantasks/job_threads.c	(revision 34783)
@@ -0,0 +1,48 @@
+# include "pantasks.h"
+
+/** things related to CheckJobs **/
+
+static int CheckJobsRun = FALSE;
+
+void CheckJobsSetState (int state) {
+  CheckJobsRun = state;
+}
+int CheckJobsGetState () {
+  return (CheckJobsRun);
+}
+
+void *CheckJobsThread (void *data) {
+
+  char log_stdout[128], log_stderr[128];
+  float next_timeout;
+
+  gprintInit ();  // each thread needs to init the printing system
+
+  // define server output log files
+  if (VarConfig ("PANTASKS_SERVER_STDOUT", "%s", log_stdout) != NULL) {
+      gprintSetFileThisThread (GP_LOG, log_stdout);
+  }
+  if (VarConfig ("PANTASKS_SERVER_STDERR", "%s", log_stderr) != NULL) {
+      gprintSetFileThisThread (GP_ERR, log_stderr);
+  }
+
+  while (1) {
+
+    // check for thread suspend
+    if (!CheckJobsRun) {
+      usleep (100000); // idle if thread action is suspended
+      continue;
+    }
+
+    // one run of the task checker
+    SerialThreadLock ();
+    next_timeout = CheckJobs ();
+    SerialThreadUnlock ();
+    if (VerboseMode() == 2) fprintf (stderr, "J");
+
+    next_timeout = MIN (next_timeout, 0.1);
+    if (next_timeout > 0.001) {
+      usleep ((int)(1000000*next_timeout)); // allow other threads a chance to run
+    }      
+  }
+}
Index: /branches/sc_branches/pantasks_condor/pantasks/jobs_and_tasks_thread.c
===================================================================
--- /branches/sc_branches/pantasks_condor/pantasks/jobs_and_tasks_thread.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/pantasks/jobs_and_tasks_thread.c	(revision 34783)
@@ -0,0 +1,95 @@
+# include "pantasks.h"
+
+void ResetTaskTimers ();
+
+static int CheckTasksRun = FALSE;
+static int CheckJobsRun = FALSE;
+
+void CheckJobsSetState (int state) {
+  CheckJobsRun = state;
+}
+int CheckJobsGetState () {
+  return (CheckJobsRun);
+}
+
+void CheckTasksSetState (int state) {
+  if (state && !CheckTasksRun) {
+    ResetTaskTimers ();
+  }
+  CheckTasksRun = state;
+}
+int CheckTasksGetState () {
+  return (CheckTasksRun);
+}
+
+void *CheckJobsAndTasksThread (void *data) {
+
+  char log_stdout[128], log_stderr[128];
+  float job_timeout, task_timeout, next_timeout;
+
+  gprintInit ();  // each thread needs to init the printing system
+
+  // define server output log files
+  if (VarConfig ("PANTASKS_SERVER_STDOUT", "%s", log_stdout) != NULL) {
+      gprintSetFileThisThread (GP_LOG, log_stdout);
+  }
+  if (VarConfig ("PANTASKS_SERVER_STDERR", "%s", log_stderr) != NULL) {
+      gprintSetFileThisThread (GP_ERR, log_stderr);
+  }
+
+  while (1) {
+
+    // one run of the task checker
+    task_timeout = 0.25;
+    if (CheckTasksRun) {
+      task_timeout = CheckTasks ();
+      if (VerboseMode() == 2) fprintf (stderr, "T");
+    }
+
+    // one run of the task checker
+    job_timeout = 0.25;
+    if (CheckJobsRun) {
+      job_timeout = CheckJobs ();
+      if (VerboseMode() == 2) fprintf (stderr, "J");
+    }
+
+    // job_timeout, task_timeout is time until next job,task is ready
+    // sleep more-or-less that long (but no longer than 250msec)
+    next_timeout = MIN (job_timeout, task_timeout);
+    next_timeout = MIN (next_timeout, 0.25);
+    if (next_timeout > 0.001) {
+      usleep ((int)(1000000*next_timeout)); // allow other threads a chance to run
+    }      
+  }
+}
+
+// I sleep for a small amount of time here, based on how long until the next expected
+// timeout.  this enforces a certain granularity in the task creation, but prevents the task
+// thread from driving the load up to silly levels.
+
+// reset all of the task timers, with a bit of fuzz.  this is called 
+// whenever we transition from 'stop' to 'run'
+void ResetTaskTimers () {
+
+  Task *task;
+  struct timeval now;
+  float fuzz;
+
+  // get the current time
+  gettimeofday (&now, NULL);
+
+  // check all tasks
+  while ((task = NextTask ()) != NULL) {
+    task[0].last.tv_usec = now.tv_usec;
+    task[0].last.tv_sec  = now.tv_sec;
+
+    // add random offset between 0 and 10% of exec_period
+    // XXX this should be optional
+    fuzz = task[0].exec_period*drand48();
+    task[0].last.tv_usec += 1e6*(fuzz - (int)fuzz);
+    task[0].last.tv_sec += (int) fuzz;
+
+    // gprint (GP_LOG, "fuzz: %f, last: %d %d\n", fuzz, task[0].last.tv_sec, task[0].last.tv_usec);
+  }
+  return;
+}
Index: /branches/sc_branches/pantasks_condor/pantasks/kill.c
===================================================================
--- /branches/sc_branches/pantasks_condor/pantasks/kill.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/pantasks/kill.c	(revision 34783)
@@ -0,0 +1,41 @@
+# include "pantasks.h"
+
+int kill_job (int argc, char **argv) {
+
+  Job *job;
+  int JobID;
+
+  if (argc < 2) {
+    gprint (GP_ERR, "USAGE: kill (JobID)\n");
+    return (FALSE);
+  }
+  JobID = atoi (argv[1]);
+
+  JobTaskLock();
+  job = FindJob (JobID);
+  if (job == NULL) {
+    gprint (GP_LOG, "job not found\n");
+    JobTaskUnlock();
+    return (TRUE);
+  }
+
+  if (job[0].mode == JOB_LOCAL) {
+    if (!KillLocalJob (job)) {
+      job[0].state = JOB_HUNG;
+      if (VerboseMode()) gprint (GP_LOG, "child process %d is hung, cannot kill\n", job[0].pid);
+      JobTaskUnlock();
+      return (FALSE);
+    }
+  } else {
+    if (!KillControllerJob (job)) {
+      job[0].state = JOB_HUNG;
+      if (VerboseMode()) gprint (GP_LOG, "child process %d is hung, cannot kill\n", job[0].pid);
+      JobTaskUnlock();
+      return (FALSE);
+    }
+  }    
+  DeleteJob (job);
+  gprint (GP_LOG, "job removed\n");
+  JobTaskUnlock();
+  return (TRUE);
+}
Index: /branches/sc_branches/pantasks_condor/pantasks/notes.txt
===================================================================
--- /branches/sc_branches/pantasks_condor/pantasks/notes.txt	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/pantasks/notes.txt	(revision 34783)
@@ -0,0 +1,176 @@
+
+2011.06.01
+
+  addressing some details:
+
+  tasks->Njobs:
+    ++ : CheckTasks.c:86 after successful submission
+
+  tasks->Nsuccess:
+    ++ : CheckJobs.c:126 : on exit if exit_status == 0
+  
+  tasks->Nfailure:
+    ++ : CheckJobs.c:102 : on exit if exit_status == 0
+    ++ : CheckJobs.c:123 : on crash
+
+  tasks->Ntimeout:
+
+stdout_cntl:
+  GetJobOutout
+    CheckControllerJob
+      CheckController
+        controller_threads (controlThread)
+  StartController
+  ControllerCommand
+    CheckController
+      controller_threads (controlThread)
+    controller_check (clientThread)
+    controller_host (clientThread)
+    controller_jobstack (clientThread)
+    controller_run (clientThread)
+    controller_status (clientThread)
+    controller_verbose (clientThread)
+    DeleteControllerJob
+      CheckControllerJob
+        CheckController
+	  controller_threads (controlThread)
+    CheckControllerJobStatus
+      CheckControllerJob
+        CheckController
+	  controller_threads (controlThread)
+    SubmitControllerJob
+      SubmitJob
+        CheckTasks (JobTaskThread)
+    PrintControllerBusyJobs
+    KillControllerJob
+    QuitController
+    RestartController    
+  CheckControllerOutput
+    controller_output (clientThread)
+    controller_threads (controlThread)
+
+
+
+
+
+
+
+20090322
+
+  We've been surviving with a slightly broken threading / locking
+  model.  I would like to clean it up.  Previously, the threads where
+  blocking at a very coarse level, and there were certain interactions
+  that were not thread-safe.  Here are my notes on re-working this:
+
+  * we have 6 threads:
+
+    * main (top-level parent) : after it spawns the 5 child threads,
+      this thread waits for new clients and adds them to the list of
+      active clients with 'AddNewClient'
+
+    * clientsThread (ListenClients): this thread is listening for
+      commands from the clients; when it receives a complete command,
+      it executes the command using 'multicommand'.
+
+    * tasksThread
+      CheckTasks
+	NextTask
+
+
+
+    * jobsThread
+
+    * controllerThread
+      CheckControllerStatus : race condition is irrelevant
+
+    * inputsThread
+      AddNewInput called by clientThread
+      (not sure this is really being used: 
+      	   server input (filename) calls 'input'
+	   server module (filename) calls 'module'
+
+  * functions which need to be thread-safe:
+    * AddNewClient
+    * gprint and supporting functions must be thread safe (extensively
+      used...)
+
+
+* race condition is irrelevant for this variable
+static int ControllerStatus = FALSE;
+
+static int stdin_cntl, stdout_cntl, stderr_cntl;
+GetJobOutput -> CheckControllerJob -> CheckController -> controllerThread
+ControllerCommand -> controllerThread / clientThread
+CheckControllerOutput -> controllerThread / clientThread
+StartController -> controllerThread / clientThread
+StopController
+
+
+
+static IOBuffer stdout_buffer;
+static IOBuffer stderr_buffer;
+static int ControllerPID = 0;
+
+/* local static variables to track the controller host properties */
+static Host *hosts = NULL;
+static int Nhosts = 0;
+static int NHOSTS = 0;
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+PanTasks Client / Server design
+
+Client
+
+client uses readline to parse the incoming commands commands are only
+validated by the client before being sent to the server:
+
+client loop:
+
+   line = readline ()
+   validate argv[0]
+   send to server
+   wait for response?
+
+Server
+
+Server does not use readline at all.  server has two threads.  first
+thread listens for commands from the clients.  how many clients?  use
+poll to wait for data?  can we manage enough clients with a single
+thread?
+
+the second thread runs the scheduler loop: this is the same background
+check that is being run by the current model on the timeouts.
+
+server loop:
+
+  check socket for new incoming client connection
+  check known clients for new messages
+    execute new command
+    send response to the client
+  run the scheduler for a single loop
+
+client / server communication is modelled after addstar client /
+server?
+
Index: /branches/sc_branches/pantasks_condor/pantasks/pantasks.c.in
===================================================================
--- /branches/sc_branches/pantasks_condor/pantasks/pantasks.c.in	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/pantasks/pantasks.c.in	(revision 34783)
@@ -0,0 +1,95 @@
+# include "pantasks.h"
+
+# define opihi_name "pantasks"
+# define opihi_prompt "pantasks: "
+# define opihi_description "parallel task scheduler\n"
+# define opihi_history ".pantasks"
+# define opihi_rcfile ".pantasksrc"
+
+/* program-dependent initialization */
+void program_init (int *argc, char **argv) {
+  
+  pthread_t JobsAndTasksThread;
+  pthread_t controllerThread;
+
+  auto_break = TRUE;
+
+  /* load the commands used by this implementation */
+  InitBasic ();
+  InitData ();
+  InitAstro ();
+  InitPantasks ();
+
+  rl_readline_name = opihi_name;
+  rl_attempted_completion_function = command_completer;
+  rl_event_hook = NULL;
+  rl_set_keyboard_input_timeout (1000); 
+
+  set_str_variable ("HISTORY", opihi_history);
+  set_str_variable ("PROMPT", opihi_prompt);
+  set_str_variable ("RCFILE", opihi_rcfile);
+
+  { 
+    char *helpdir;
+    char *modules;
+    static char *datadir = "@DATADIR@";
+    ALLOCATE (helpdir, char, strlen(datadir) + strlen("/help") + 2);
+    sprintf (helpdir, "%s/help", datadir);
+    set_str_variable ("HELPDIR", helpdir);
+    free (helpdir);
+    ALLOCATE (modules, char, strlen(datadir) + strlen("/modules") + 2);
+    sprintf (modules, "%s/modules", datadir);
+    set_str_variable ("MODULES:0", modules);
+    set_int_variable ("MODULES:n", 1);
+    free (modules);
+  }
+
+  signal (SIGPIPE, gotsignal);
+  signal (SIGTSTP, gotsignal);
+  signal (SIGTTIN, gotsignal);
+
+  /* start up the background threads here */
+  // pthread_create (&tasksThread,      NULL, &CheckTasksThread, 	   NULL);
+  // pthread_create (&jobsThread,       NULL, &CheckJobsThread, 	   NULL);
+  pthread_create (&JobsAndTasksThread, NULL, &CheckJobsAndTasksThread, NULL);
+  pthread_create (&controllerThread,   NULL, &CheckControllerThread,   NULL);
+  return;
+}
+
+/* standard welcome message */
+void welcome () {
+  gprint (GP_LOG, "\n");
+  gprint (GP_LOG, "Welcome to %s - %s\n\n", opihi_name, opihi_description);
+}
+
+/* add program-dependent exit functions here */
+void cleanup () {
+  QuitKapa ();
+  QuitController ();
+  ConfigFree ();
+
+  FreeBasic ();
+  FreeData ();
+  FreePantasks ();
+
+  return;
+}
+
+void gotsignal (int signum) {
+  gprint (GP_ERR, "got signal : %d\n", signum);
+  return;
+}
+
+/* call to opihi shell */
+int main (int argc, char **argv) {
+  int status;
+  status = opihi (argc, argv);
+  exit (status);
+}
+
+/* pantasks runs three (or four) threads:
+   - one thread parses commands from the connected clients
+   - one thread runs the scheduler loop
+   - one thread may run the controller loop independently
+   - the main thread runs readline and accepts user input
+*/
Index: /branches/sc_branches/pantasks_condor/pantasks/pantasks_client.c.in
===================================================================
--- /branches/sc_branches/pantasks_condor/pantasks/pantasks_client.c.in	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/pantasks/pantasks_client.c.in	(revision 34783)
@@ -0,0 +1,78 @@
+# include "pantasks.h"
+
+# define opihi_name "pantasks client shell"
+# define opihi_prompt "pantasks: "
+# define opihi_description "parallel task scheduler\n"
+# define opihi_history ".pantasks"
+# define opihi_rcfile ".pantasksrc"
+
+/* program-dependent initialization */
+void program_init (int *argc, char **argv) {
+  
+  auto_break = TRUE;
+
+  /* load the commands used by this implementation */
+  InitBasic ();
+  InitData ();
+  InitAstro ();
+  InitPantasksClient ();
+
+  rl_readline_name = opihi_name;
+  rl_attempted_completion_function = command_completer;
+
+  set_str_variable ("HISTORY", opihi_history);
+  set_str_variable ("PROMPT", opihi_prompt);
+  set_str_variable ("RCFILE", opihi_rcfile);
+
+  {
+      char *helpdir;
+      char *modules;
+      static char *datadir = "@DATADIR@";
+      ALLOCATE (helpdir, char, strlen(datadir) + strlen("/help") + 2);
+      sprintf (helpdir, "%s/help", datadir);
+      set_str_variable ("HELPDIR", helpdir);
+      free (helpdir);
+      ALLOCATE (modules, char, strlen(datadir) + strlen("/modules") + 2);
+      sprintf (modules, "%s/modules", datadir);
+      set_str_variable ("MODULES:0", modules);
+      set_int_variable ("MODULES:n", 1);
+      free (modules);
+  }
+
+  signal (SIGPIPE, gotsignal);
+  signal (SIGTSTP, gotsignal);
+  signal (SIGTTIN, gotsignal);
+  return;
+}
+
+/* standard welcome message */
+void welcome () {
+  gprint (GP_LOG, "\n");
+  gprint (GP_LOG, "Welcome to %s - %s\n\n", opihi_name, opihi_description);
+}
+
+/* add program-dependent exit functions here */
+void cleanup () {
+  QuitKapa ();
+  ConfigFree ();
+
+  FreeBasic ();
+  FreeData ();
+  FreeAstro ();
+  FreePantasksClient ();
+  return;
+}
+
+/* default signal handling */
+void gotsignal (int signum) {
+  gprint (GP_ERR, "got signal : %d\n", signum);
+  return;
+}
+
+/* call to client_shell: this opihi tool uses a slightly different
+   shell function from the standard tools */
+int main (int argc, char **argv) {
+  int status;
+  status = client_shell (argc, argv);
+  exit (status);
+}
Index: /branches/sc_branches/pantasks_condor/pantasks/pantasks_server.c.in
===================================================================
--- /branches/sc_branches/pantasks_condor/pantasks/pantasks_server.c.in	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/pantasks/pantasks_server.c.in	(revision 34783)
@@ -0,0 +1,171 @@
+# include "pantasks.h"
+
+/* pantasks-server is not a readline program.
+   it runs three (or four) threads:
+   - one thread listens on the socket for incomming connections 
+     and performs client validation
+   - one thread parses commands from the connected clients
+   - one thread runs the scheduler loop
+   - one thread may run the controller loop independently
+   
+   * note: the client shell traps certain commands and performs them
+   locally. at a minimum: help.  perhaps: input, other I/O commands?
+*/
+
+void special_init (int *argc, char **argv);
+
+/* program-dependent initialization */
+int main (int argc, char **argv) {
+  
+  char hostname[256], portinfo[256];
+  char log_stdout[1024], log_stderr[1024];
+  pthread_t JobsAndTasksThread;
+  pthread_t clientsThread;
+  pthread_t controllerThread;
+  int InitSocket, BindSocket;
+  SockAddress Address;
+
+  strcpy (log_stdout, "stdout");
+  strcpy (log_stderr, "stderr");
+
+  special_init (&argc, argv);
+
+  // 
+  stdin = freopen ("/dev/zero", "r", stdin);
+
+  // this block redirects the actual stderr, stdout steams to the output files
+  if (VarConfig ("PANTASKS_SERVER_STDOUT", "%s", log_stdout) != NULL) {
+    if (strcmp (log_stdout, "stdout")) {
+      stdout = freopen (log_stdout, "a", stdout);
+      if (!stdout) {
+	fprintf (stderr, "failed to open %s for output\n", log_stdout);
+	exit (1);
+      }
+    }
+  }
+  if (VarConfig ("PANTASKS_SERVER_STDERR", "%s", log_stderr) != NULL) {
+    if (strcmp (log_stderr, "stderr")) {
+      stderr = freopen (log_stderr, "a", stderr);
+      if (!stderr) {
+	fprintf (stdout, "failed to open %s for error output\n", log_stderr);
+	exit (1);
+      }
+    }
+  }
+
+  /* init for main (or only) thread */
+  gprintInit ();
+
+  // this block changes the target of the gprint output streams
+  if (strcmp (log_stdout, "stdout")) {
+      gprintSetFileThisThread (GP_LOG, log_stdout);
+  }
+  if (strcmp (log_stderr, "stderr")) {
+      gprintSetFileThisThread (GP_ERR, log_stderr);
+  }
+
+  DefineValidIP ();
+
+  /* XXX not sure what is a good setting for this in the server */
+  auto_break = TRUE;
+
+  /* load the commands used by this implementation */
+  InitBasic ();
+  InitData ();
+  InitAstro ();
+  InitPantasksServer ();
+
+  { 
+    char *modules;
+    static char *datadir = "@DATADIR@";
+    ALLOCATE (modules, char, strlen(datadir) + strlen("/modules") + 2);
+    sprintf (modules, "%s/modules", datadir);
+    set_str_variable ("MODULES:0", modules);
+    set_int_variable ("MODULES:n", 1);
+    free (modules);
+  }
+
+  signal (SIGPIPE, gotsignal);
+  // signal (SIGTSTP, gotsignal);
+  // signal (SIGTTIN, gotsignal);
+  signal (SIGINT, SIG_DFL);
+
+  startup (&argc, argv);
+
+  /* start up the background threads here */
+  pthread_create (&clientsThread,       NULL, &ListenClients,    	NULL);
+  pthread_create (&JobsAndTasksThread,  NULL, &CheckJobsAndTasksThread, NULL);
+  pthread_create (&controllerThread, 	NULL, &CheckControllerThread,   NULL);
+
+  /* in this loop, we listen for incoming connections, validate, and
+     pass them to the ListenClient thread */
+
+  /* find the defined server hostname */
+  if (VarConfig ("PANTASKS_SERVER", "%s", hostname) == NULL) {
+    gprint (GP_ERR, "pantasks server host undefined\n");
+    exit (31);
+  }
+
+  /* is a port range defined? otherwise use the default */
+  memset (portinfo, 0, 256);
+  VarConfig ("PANTASKS_SERVER_PORT", "%s", portinfo);
+
+  /* create the listening socket */
+  InitSocket = InitServerSocket (&Address, hostname, portinfo);
+  
+  InitPassword ();
+
+  while (1) {
+
+    /* wait for clients to make connection */
+    BindSocket = WaitServerSocket (InitSocket, &Address);
+    if (BindSocket == -1) continue;
+
+    /* validate : wait for password */
+    if (!CheckPassword (BindSocket)) continue;
+    
+    AddNewClient (BindSocket);
+  }
+}
+
+/* add program-dependent exit functions here */
+void cleanup () {
+  QuitKapa ();
+  QuitController ();
+  ConfigFree ();
+  InitBasic ();
+  InitData ();
+  InitPantasksServer ();
+  return;
+}
+
+void gotsignal (int signum) {
+  gprint (GP_ERR, "got signal : %d\n", signum);
+  return;
+}
+
+/* these are needed to resolve symbols expected by libshell.so */
+void welcome () { }
+void program_init (int *argc, char **argv) {}
+
+// I need to do things in a different order here to have config / freopen / gprint sequenced correctly
+void special_init (int *argc, char **argv) {
+
+  /* init srand for rnd numbers elsewhere */
+  long A, B;
+  A = time(NULL);
+  for (B = 0; A == time(NULL); B++);
+  srand48(B);
+ 
+  /* set signals */
+  signal (SIGINT, SIG_IGN);
+
+  init_error ();
+
+  /* load config data (.ptolemyrc) */
+  if (!ConfigInit (argc, argv)) {
+    fprintf (stderr, "can't find (.ptolemyrc,ptolemy.rc) config file. some functions will be unavailable\n");
+  }
+
+  return;
+}
Index: /branches/sc_branches/pantasks_condor/pantasks/pulse.c
===================================================================
--- /branches/sc_branches/pantasks_condor/pantasks/pulse.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/pantasks/pulse.c	(revision 34783)
@@ -0,0 +1,16 @@
+# include "pantasks.h"
+
+int pulse (int argc, char **argv) {
+
+  int Nusec;
+
+  if (argc != 2) {
+    gprint (GP_ERR, "USAGE: pulse (microseconds)\n");
+    return (FALSE);
+  }
+
+  Nusec = atoi (argv[1]);
+  rl_set_keyboard_input_timeout (Nusec); 
+
+  return (TRUE);
+}
Index: /branches/sc_branches/pantasks_condor/pantasks/run.c
===================================================================
--- /branches/sc_branches/pantasks_condor/pantasks/run.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/pantasks/run.c	(revision 34783)
@@ -0,0 +1,25 @@
+# include "pantasks.h"
+
+// XXX for client/server, we need to simply start or stop the
+// appropriate threads
+// with one thread for each of the major actions, this would
+// make it easy to keep the controller running and stop the 
+// scheduler (don't run CheckTasks, but run everything else 
+// until nothing is left...
+
+int run (int argc, char **argv) {
+
+  if (argc != 1) {
+    gprint (GP_ERR, "USAGE: run\n");
+    return (FALSE);
+  }
+
+  CheckTasksSetState (TRUE);
+  CheckControllerSetState (TRUE);
+  CheckJobsSetState (TRUE);
+
+  // InitTaskTimers ();
+  // rl_event_hook = CheckSystem;
+
+  return (TRUE);
+}
Index: /branches/sc_branches/pantasks_condor/pantasks/server.c
===================================================================
--- /branches/sc_branches/pantasks_condor/pantasks/server.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/pantasks/server.c	(revision 34783)
@@ -0,0 +1,70 @@
+# include "pantasks.h"
+
+/* most of these are defined in cmd.basic */
+int quit            PROTO((int, char **));
+int input      	    PROTO((int, char **));
+int module     	    PROTO((int, char **));
+int server_run	    PROTO((int, char **));
+int server_stop	    PROTO((int, char **));
+int server_halt	    PROTO((int, char **));
+int cd              PROTO((int, char **));
+int pwd        	    PROTO((int, char **));
+int output     	    PROTO((int, char **));
+
+CommandF *FindServerCommand (char *cmd);
+
+static Command server_cmds[] = {
+  {1, "exit",   quit,   "shutdown server"},
+  {1, "quit",   quit,   "shutdown server"},
+  {1, "input",  input,  "load input file on server"},
+  {1, "module", module, "load module file on server"},
+  {1, "run",    server_run,  "run scheduler"},
+  {1, "stop",   server_stop, "stop scheduler"},
+  {1, "halt",   server_halt, "halt scheduler"},
+  {1, "cd",     cd,     "set local directory"},
+  {1, "pwd",    pwd,    "check local directory"},
+  {1, "output", output, "set server output destinations"},
+};
+
+int server (int argc, char **argv) {
+
+  int i, N, status;
+  CommandF *func;
+
+  if (argc < 2) {
+    gprint (GP_ERR, "USAGE: server (command) ... \n");
+    return (FALSE);
+  }
+
+  if (!strcasecmp (argv[1], "help")) {
+    N = sizeof (server_cmds) / sizeof (Command);
+
+    for (i = 0; i < N; i++) {
+      gprint (GP_LOG, "%-15s %s\n", server_cmds[i].name, server_cmds[i].help);
+    }
+    return (TRUE);
+  }
+
+  func = FindServerCommand (argv[1]);
+  if (func == NULL) {
+    gprint (GP_ERR, "invalid server command\n");
+    return (FALSE);
+  }
+
+  status = (*func)(argc - 1, argv + 1);
+  return (status);
+}
+
+CommandF *FindServerCommand (char *cmd) {
+
+  int i, N;
+
+  N = sizeof (server_cmds) / sizeof (Command);
+
+  for (i = 0; i < N; i++) {
+    if (!strcmp (server_cmds[i].name, cmd)) {
+      return (server_cmds[i].func);
+    }
+  }
+  return (NULL);
+}
Index: /branches/sc_branches/pantasks_condor/pantasks/server_connect.c
===================================================================
--- /branches/sc_branches/pantasks_condor/pantasks/server_connect.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/pantasks/server_connect.c	(revision 34783)
@@ -0,0 +1,20 @@
+# include "pantasks.h"
+
+int server_connect (int argc, char **argv) {
+
+  int server;
+
+  if (argc != 1) {
+    gprint (GP_ERR, "USAGE: disconnect\n");
+    return (FALSE);
+  }
+  
+  server = getServer ();
+  if (server) return (TRUE);
+
+  // close connection with server
+  // catch errors (don't exit)
+  multicommand_InitServer ();
+
+  return (TRUE);
+}
Index: /branches/sc_branches/pantasks_condor/pantasks/server_disconnect.c
===================================================================
--- /branches/sc_branches/pantasks_condor/pantasks/server_disconnect.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/pantasks/server_disconnect.c	(revision 34783)
@@ -0,0 +1,20 @@
+# include "pantasks.h"
+
+int server_disconnect (int argc, char **argv) {
+
+  int server;
+
+  if (argc != 1) {
+    gprint (GP_ERR, "USAGE: disconnect\n");
+    return (FALSE);
+  }
+  
+  server = getServer ();
+  
+  if (!server) return (TRUE);
+
+  // close connection with server
+  multicommand_StopServer ();
+
+  return (TRUE);
+}
Index: /branches/sc_branches/pantasks_condor/pantasks/server_load.c
===================================================================
--- /branches/sc_branches/pantasks_condor/pantasks/server_load.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/pantasks/server_load.c	(revision 34783)
@@ -0,0 +1,18 @@
+# include "pantasks.h"
+
+int server_load (int argc, char **argv) {
+
+  char *input;
+
+  if (argc != 2) {
+    gprint (GP_ERR, "USAGE: server load (file)\n");
+    return (FALSE);
+  }
+
+  // the allocated input string is eventually freed 
+  // by the InputQueue system
+  input = strcreate (argv[1]);
+  AddNewInput (input);
+
+  return (TRUE);
+}
Index: /branches/sc_branches/pantasks_condor/pantasks/server_run.c
===================================================================
--- /branches/sc_branches/pantasks_condor/pantasks/server_run.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/pantasks/server_run.c	(revision 34783)
@@ -0,0 +1,38 @@
+# include "pantasks.h"
+
+int server_run (int argc, char **argv) {
+
+  if (argc != 1) {
+    gprint (GP_ERR, "USAGE: server run\n");
+    return (FALSE);
+  }
+
+  CheckTasksSetState (TRUE);
+  CheckJobsSetState (TRUE);
+  CheckControllerSetState (TRUE);
+  return (TRUE);
+}
+
+int server_stop (int argc, char **argv) {
+
+  if (argc != 1) {
+    gprint (GP_ERR, "USAGE: server stop\n");
+    return (FALSE);
+  }
+
+  CheckTasksSetState (FALSE);
+  return (TRUE);
+}
+
+int server_halt (int argc, char **argv) {
+
+  if (argc != 1) {
+    gprint (GP_ERR, "USAGE: server halt\n");
+    return (FALSE);
+  }
+
+  CheckTasksSetState (FALSE);
+  CheckJobsSetState (FALSE);
+  CheckControllerSetState (FALSE);
+  return (TRUE);
+}
Index: /branches/sc_branches/pantasks_condor/pantasks/server_shutdown.c
===================================================================
--- /branches/sc_branches/pantasks_condor/pantasks/server_shutdown.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/pantasks/server_shutdown.c	(revision 34783)
@@ -0,0 +1,36 @@
+# include "pantasks.h"
+
+int server_shutdown (int argc, char **argv) {
+
+  int status, server;
+  IOBuffer message;
+
+  if (argc != 2) {
+    gprint (GP_ERR, "USAGE: shutdown now\n");
+    return (FALSE);
+  }
+  if (strcmp (argv[1], "now")) {
+    gprint (GP_ERR, "USAGE: shutdown now\n");
+    return (FALSE);
+  }
+  
+  server = getServer ();
+  
+  if (!server) return (TRUE);
+
+  // send the command to the server instead
+  status = SendMessage (server, "exit");
+  fprintf (stderr, "exit status of %d\n", status);
+
+  // try to read from the server until we get a status of 0
+  status = ExpectMessage (server, 2.0, &message);
+  fprintf (stderr, "message status of %d\n", status);
+
+  // XXX I should probably loop until we get an exit status of -3 or EPIPE
+
+  // close connection with server
+  multicommand_StopServer ();
+
+  return (TRUE);
+}
+
Index: /branches/sc_branches/pantasks_condor/pantasks/showtask.c
===================================================================
--- /branches/sc_branches/pantasks_condor/pantasks/showtask.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/pantasks/showtask.c	(revision 34783)
@@ -0,0 +1,15 @@
+# include "pantasks.h"
+
+int showtask (int argc, char **argv) {
+
+  if (argc != 2) {
+    gprint (GP_LOG, "USAGE: showtask (task)\n");
+    return (FALSE);
+  }
+
+  JobTaskLock();
+  ShowTask (argv[1]);
+  JobTaskUnlock();
+
+  return (TRUE);
+}
Index: /branches/sc_branches/pantasks_condor/pantasks/status.c
===================================================================
--- /branches/sc_branches/pantasks_condor/pantasks/status.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/pantasks/status.c	(revision 34783)
@@ -0,0 +1,70 @@
+# include "pantasks.h"
+
+int status_sys (int argc, char **argv) {
+
+  int N;
+
+  if (get_argument (argc, argv, "-h")) goto help;
+  if (get_argument (argc, argv, "-help")) goto help;
+  if (get_argument (argc, argv, "--help")) goto help;
+
+  if ((N = get_argument (argc, argv, "-tasks"))) {
+    remove_argument (N, &argc, argv);
+    ListTasks (FALSE);
+    return (TRUE);
+  }
+
+  if ((N = get_argument (argc, argv, "-taskinfo"))) {
+    remove_argument (N, &argc, argv);
+    ListTasks (TRUE);
+    return (TRUE);
+  }
+
+  if ((N = get_argument (argc, argv, "-taskstats"))) {
+    remove_argument (N, &argc, argv);
+    if (argc == 2) {
+      ListTaskStats (argv[N]);
+    } else {
+      ListTaskStats (NULL);
+    }      
+    return (TRUE);
+  }
+
+  if ((N = get_argument (argc, argv, "-taskstatsreset"))) {
+    remove_argument (N, &argc, argv);
+    if (argc == 2) {
+      ResetTaskStats (argv[N]);
+    } else {
+      ResetTaskStats (NULL);
+    }      
+    return (TRUE);
+  }
+
+  gprint (GP_LOG, "\n");
+  if (CheckTasksGetState()) {
+    gprint (GP_LOG, " Scheduler is running\n");
+  } else {
+    if (CheckJobsGetState()) {
+      gprint (GP_LOG, " Scheduler is stopped, harvesting jobs\n");
+    } else {
+      gprint (GP_LOG, " Scheduler is stopped\n");
+    }
+  }
+  if (CheckControllerStatus()) {
+    gprint (GP_LOG, " Controller is running\n");
+  } else {
+    gprint (GP_LOG, " Controller is stopped\n");
+  }
+  ListTasks (FALSE);
+  ListJobs ();
+  return (TRUE);
+
+help:
+  gprint (GP_LOG, "USAGE: status [options]\n");
+  gprint (GP_LOG, "       (without options: over system status)\n");
+  gprint (GP_LOG, "       -tasks     : list defined tasks\n");
+  gprint (GP_LOG, "       -taskinfo  : details for tasks\n");
+  gprint (GP_LOG, "       -taskstats : processing statistics for tasks\n");
+  gprint (GP_LOG, "       -taskstatsreset : reset processing statistics for tasks\n");
+  return (FALSE);
+}
Index: /branches/sc_branches/pantasks_condor/pantasks/status_server.c
===================================================================
--- /branches/sc_branches/pantasks_condor/pantasks/status_server.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/pantasks/status_server.c	(revision 34783)
@@ -0,0 +1,127 @@
+# include "pantasks.h"
+// this is the server version of the status_sys function (status.c)
+// the server verion needs to include locks on the jobs.
+
+int status_scheduler(void);
+
+int status_server (int argc, char **argv) {
+
+  int N;
+  int checkController,showJobs, showDetails;
+
+  if (get_argument (argc, argv, "-h")) goto help;
+  if (get_argument (argc, argv, "-help")) goto help;
+  if (get_argument (argc, argv, "--help")) goto help;
+
+  // special options for task timing stats
+  if ((N = get_argument (argc, argv, "-taskstats"))) {
+    remove_argument (N, &argc, argv);
+    status_scheduler();
+    JobTaskLock();
+    if (argc == 2) {
+      ListTaskStats (argv[N]);
+    } else {
+      ListTaskStats (NULL);
+    }      
+    JobTaskUnlock();
+    return (TRUE);
+  }
+
+  if ((N = get_argument (argc, argv, "-taskstatsreset"))) {
+    remove_argument (N, &argc, argv);
+    JobTaskLock();
+    if (argc == 2) {
+      ResetTaskStats (argv[N]);
+    } else {
+      ResetTaskStats (NULL);
+    }      
+    JobTaskUnlock();
+    return (TRUE);
+  }
+
+  checkController = FALSE;
+  if ((N = get_argument (argc, argv, "-c"))) {
+    remove_argument (N, &argc, argv);
+    checkController = TRUE;
+  }
+  if ((N = get_argument (argc, argv, "-control"))) {
+    remove_argument (N, &argc, argv);
+    checkController = TRUE;
+  }
+  if ((N = get_argument (argc, argv, "-controller"))) {
+    remove_argument (N, &argc, argv);
+    checkController = TRUE;
+  }
+
+  showJobs = FALSE;
+  if ((N = get_argument (argc, argv, "-jobs"))) {
+    remove_argument (N, &argc, argv);
+    showJobs = TRUE;
+  }
+
+  showDetails = FALSE;
+  if ((N = get_argument (argc, argv, "-info"))) {
+    remove_argument (N, &argc, argv);
+    showDetails = TRUE;
+  }
+  if ((N = get_argument (argc, argv, "-v"))) {
+    remove_argument (N, &argc, argv);
+    showDetails = TRUE;
+  }
+  if ((N = get_argument (argc, argv, "-details"))) {
+    remove_argument (N, &argc, argv);
+    showDetails = TRUE;
+  }
+  if ((N = get_argument (argc, argv, "-verbose"))) {
+    remove_argument (N, &argc, argv);
+    showDetails = TRUE;
+  }
+
+  status_scheduler();
+
+  JobTaskLock();
+  ListTasks (showDetails);
+  if (showJobs) {
+    ListJobs();
+  }
+  JobTaskUnlock();
+
+  if (checkController) {
+    ControlLock(__func__);
+    PrintControllerBusyJobs();
+    ControlUnlock(__func__);
+  }
+
+  gprint (GP_LOG, "\n");
+  return (TRUE);
+
+help:
+  gprint (GP_LOG, "USAGE: status [options]\n");
+  gprint (GP_LOG, "       (without options: show system status)\n");
+  gprint (GP_LOG, "       -jobs : list active jobs\n");
+  gprint (GP_LOG, "       -c | -control | -controller      : add status of jobs on controller\n");
+  gprint (GP_LOG, "       -v | -verbose | -details | -info : add details for tasks\n");
+  gprint (GP_LOG, "       -taskstats                       : show timing statistics for tasks\n");
+  gprint (GP_LOG, "       -taskstatsreset                  : reset timing statistics for tasks\n");
+  return (FALSE);
+}
+
+int status_scheduler(void) {
+  
+  gprint (GP_LOG, "\n");
+  if (CheckTasksGetState()) {
+    gprint (GP_LOG, " Scheduler is running\n");
+  } else {
+    if (CheckJobsGetState()) {
+      gprint (GP_LOG, " Scheduler is stopped, harvesting jobs\n");
+    } else {
+      gprint (GP_LOG, " Scheduler is stopped\n");
+    }
+  }
+  if (CheckControllerStatus()) {
+    gprint (GP_LOG, " Controller is running\n");
+  } else {
+    gprint (GP_LOG, " Controller is stopped\n");
+  }
+  return (TRUE);
+}
Index: /branches/sc_branches/pantasks_condor/pantasks/stop.c
===================================================================
--- /branches/sc_branches/pantasks_condor/pantasks/stop.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/pantasks/stop.c	(revision 34783)
@@ -0,0 +1,29 @@
+# include "pantasks.h"
+
+int stop (int argc, char **argv) {
+
+  if (argc != 1) {
+    gprint (GP_ERR, "USAGE: stop\n");
+    return (FALSE);
+  }
+
+  CheckTasksSetState (FALSE);
+  // CheckControllerSetState (FALSE);
+  // CheckJobsSetState (FALSE);
+
+  return (TRUE);
+}
+
+int halt (int argc, char **argv) {
+
+  if (argc != 1) {
+    gprint (GP_ERR, "USAGE: halt\n");
+    return (FALSE);
+  }
+
+  CheckTasksSetState (FALSE);
+  CheckControllerSetState (FALSE);
+  CheckJobsSetState (FALSE);
+
+  return (TRUE);
+}
Index: /branches/sc_branches/pantasks_condor/pantasks/task.c
===================================================================
--- /branches/sc_branches/pantasks_condor/pantasks/task.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/pantasks/task.c	(revision 34783)
@@ -0,0 +1,102 @@
+# include "pantasks.h"
+# define prompt "> "
+
+int task (int argc, char **argv) {
+
+  int hash;
+  int ThisList;
+  char *input, *outline;
+  Task *task;
+
+  if (argc != 2) goto usage;
+
+  JobTaskLock();
+  task = FindTask (argv[1]);
+  if (task == NULL) { /**** new task ****/
+    task = CreateTask (argv[1]);
+  } else {
+    RemoveTask (task);
+    SetNewTask (task);
+  }
+  JobTaskUnlock();
+
+  /* While a task is being defined, it is removed from the task list.  The new task is added to the task list
+     when the definition process is complete.  
+     XXX If an outstanding job has a task deleted, it will not be able to complete... */
+
+  /* read in task from appropriate source (keyboard or list) until end */
+
+  /* allowed tokens: command, host, stderr, periods, trange, nmax, task.exit, task.exec, end */
+
+  ThisList = current_list_depth();
+  while (1) {
+
+    /* get the next line (from correct place) */
+    if (ThisList == 0) 
+      input = opihi_readline (prompt);
+    else 
+      input = get_next_listentry (ThisList);
+
+    if ((ThisList == 0) && (input == (char *) NULL)) {
+      gprint (GP_ERR, "end task with 'END'\n");
+      continue;
+    }
+    if ((ThisList > 0) && (input == (char *) NULL)) {
+      gprint (GP_ERR, "missing 'END' in task definition\n");
+      input = strcreate ("end");
+    }
+
+    stripwhite (input);
+    hash = TaskHash (input);
+    switch (hash) {
+
+      case TASK_EMPTY:
+      case TASK_COMMENT:
+	free (input);
+	break;
+
+      case TASK_END:
+	free (input);
+	/* validate the new task: all mandatory elements defined? */ 
+	if (!ValidateTask (task, FALSE)) {
+	  DeleteNewTask ();
+	  return (FALSE);
+	}
+	JobTaskLock();
+	RegisterNewTask ();
+	JobTaskUnlock();
+	return (TRUE);
+	break;
+
+      case TASK_TRANGE:
+      case TASK_NMAX:
+      case TASK_HOST:
+      case TASK_NICE:
+      case TASK_EXIT:
+      case TASK_EXEC:
+      case TASK_STDOUT:
+      case TASK_STDERR:
+      case TASK_COMMAND:
+      case TASK_OPTIONS:
+      case TASK_PERIODS:
+      case TASK_NPENDING:
+      case TASK_ACTIVE:
+	// status = command(); do something with this info?
+	command (input, &outline, TRUE);
+	if (outline != NULL) free (outline);
+	break;
+
+      case TASK_NONE:
+	gprint (GP_ERR, "unknown task command %s\n", input);
+	break;
+    }
+  }
+
+usage:
+  gprint (GP_ERR, "USAGE: task -list\n");
+  gprint (GP_ERR, "USAGE: task -longlist\n");
+  gprint (GP_ERR, "USAGE: task -show (task)\n");
+  gprint (GP_ERR, "USAGE: task <name>\n");
+  gprint (GP_ERR, "  (enter commands & task functions; end with the word 'END')\n");
+  return (FALSE);
+}
Index: /branches/sc_branches/pantasks_condor/pantasks/task_active.c
===================================================================
--- /branches/sc_branches/pantasks_condor/pantasks/task_active.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/pantasks/task_active.c	(revision 34783)
@@ -0,0 +1,35 @@
+# include "pantasks.h"
+
+int task_active (int argc, char **argv) {
+
+  Task *task;
+
+  if (argc != 2) goto usage;
+
+  JobTaskLock();
+  task = GetNewTask ();
+  if (task == NULL) {
+    gprint (GP_ERR, "ERROR: not defining or running a task\n");
+    JobTaskUnlock();
+    return (FALSE);
+  }
+
+  if (!strcasecmp (argv[1], "true")) {
+    task[0].active = TRUE;
+    JobTaskUnlock();
+    return (TRUE);
+  }
+  if (!strcasecmp (argv[1], "false")) {
+    task[0].active = FALSE;
+    JobTaskUnlock();
+    return (TRUE);
+  }
+
+  gprint (GP_ERR, "ERROR: invalid option: %s\n", argv[1]);
+  JobTaskUnlock();
+  return (FALSE);
+
+usage:
+  gprint (GP_ERR, "USAGE: active (true|false)\n");
+  return (FALSE);
+}
Index: /branches/sc_branches/pantasks_condor/pantasks/task_command.c
===================================================================
--- /branches/sc_branches/pantasks_condor/pantasks/task_command.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/pantasks/task_command.c	(revision 34783)
@@ -0,0 +1,41 @@
+# include "pantasks.h"
+
+int task_command (int argc, char **argv) {
+
+  int i;
+  Task *task;
+
+  if (argc < 2) {
+    gprint (GP_ERR, "USAGE: command <command> <arg>. ..\n");
+    gprint (GP_ERR, "  (define command for this task)\n");
+    return (FALSE);
+  }
+
+  JobTaskLock();
+  task = GetNewTask ();
+  if (task == NULL) {
+    task = GetActiveTask ();
+    if (task == NULL) {
+      gprint (GP_ERR, "ERROR: not defining or running a task\n");
+      JobTaskUnlock();
+      return (FALSE);
+    }
+  }
+
+  /* free existing memory used by argv */
+  if (task[0].argc != 0) {
+    for (i = 0; i < task[0].argc; i++) {
+      if (task[0].argv[i] != NULL) free (task[0].argv[i]);
+    }
+    free (task[0].argv);
+  }
+
+  /* create new memory for argv */
+  task[0].argc = argc - 1;
+  ALLOCATE (task[0].argv, char *, MAX (task[0].argc, 1));
+  for (i = 0; i < task[0].argc; i++) {
+    task[0].argv[i] = strcreate (argv[i+1]);
+  }
+  JobTaskUnlock();
+  return (TRUE);
+}
Index: /branches/sc_branches/pantasks_condor/pantasks/task_host.c
===================================================================
--- /branches/sc_branches/pantasks_condor/pantasks/task_host.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/pantasks/task_host.c	(revision 34783)
@@ -0,0 +1,48 @@
+# include "pantasks.h"
+
+int task_host (int argc, char **argv) {
+
+  int N, RequiredHost;
+  Task *task;
+
+  RequiredHost = FALSE;
+  if ((N = get_argument (argc, argv, "-required"))) {
+    remove_argument (N, &argc, argv);
+    RequiredHost = TRUE;
+  }
+
+  if (argc != 2) {
+    gprint (GP_ERR, "USAGE: host <name> [-required]\n");
+    gprint (GP_ERR, "  define host machine for this task\n");
+    gprint (GP_ERR, "  -required flags indicates controller must use this host\n");
+    gprint (GP_ERR, "  value of 'local' for host indicates process not using controller\n");
+    gprint (GP_ERR, "  value of 'anyhost' for host indicates controller may assign at will\n");
+    return (FALSE);
+  }
+
+  task = GetNewTask ();
+  JobTaskLock();
+  if (task == NULL) {
+    task = GetActiveTask ();
+    if (task == NULL) {
+      gprint (GP_ERR, "ERROR: not defining or running a task\n");
+      JobTaskUnlock();
+      return (FALSE);
+    }
+  }
+  task[0].host_required = RequiredHost;
+
+  if (task[0].host != NULL) free (task[0].host);
+  task[0].host = NULL;
+
+  if (!strcasecmp (argv[1], "LOCAL")) {
+    JobTaskUnlock();
+    return (TRUE);
+  }
+
+  task[0].host = strcreate (argv[1]);
+  JobTaskUnlock();
+  return (TRUE);
+}
+
+/* apparently, local is the default! */
Index: /branches/sc_branches/pantasks_condor/pantasks/task_macros.c
===================================================================
--- /branches/sc_branches/pantasks_condor/pantasks/task_macros.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/pantasks/task_macros.c	(revision 34783)
@@ -0,0 +1,154 @@
+# include "pantasks.h"
+# define D_NLINES 100
+# define prompt "> "
+
+int task_macros (int argc, char **argv) {
+
+  int i, N, NLINES, done, depth, ThisList;
+  char *input;
+  Macro *macro;
+  Task *task;
+
+  if (!strcmp (argv[0], "task.exec") && (argc != 1)) {
+    gprint (GP_ERR, "USAGE: task.exec\n");
+    gprint (GP_ERR, "  (define pre-exec macro for this task)\n");
+    return (FALSE);
+  }
+
+  if (!strcmp (argv[0], "task.exit") && (argc != 2)) {
+    gprint (GP_ERR, "USAGE: task.exit (state)\n");
+    gprint (GP_ERR, "  (define exit state macro for this task)\n");
+    return (FALSE);
+  }
+
+  JobTaskLock();
+  task = GetNewTask ();
+  if (task == NULL) {
+    gprint (GP_ERR, "ERROR: not defining or running a task\n");
+    JobTaskUnlock();
+    return (FALSE);
+  }
+
+  macro = NULL;
+
+  /*** identify which Macro in Task this particular macro goes with ***/ 
+  if (!strcmp (argv[0], "task.exec")) {
+    if (task[0].exec != NULL) {
+      FreeMacro (task[0].exec);
+      free (task[0].exec);
+    }
+    ALLOCATE (task[0].exec, Macro, 1);
+    macro = task[0].exec;
+    macro[0].name = strcreate ("exec");
+    goto found;
+  }
+  if (!strcmp (argv[0], "task.exit") && !strcmp (argv[1], "timeout")) {
+    if (task[0].timeout != NULL) {
+      FreeMacro (task[0].timeout);
+      free (task[0].timeout);
+    }
+    ALLOCATE (task[0].timeout, Macro, 1);
+    macro = task[0].timeout;
+    macro[0].name = strcreate ("timeout");
+    goto found;
+  }
+  if (!strcmp (argv[0], "task.exit") && !strcmp (argv[1], "crash")) {
+    if (task[0].crash != NULL) {
+      FreeMacro (task[0].crash);
+      free (task[0].crash);
+    }
+    ALLOCATE (task[0].crash, Macro, 1);
+    macro = task[0].crash;
+    macro[0].name = strcreate ("crash");
+    goto found;
+  }
+  if (!strcmp (argv[0], "task.exit") && !strcmp (argv[1], "default")) {
+    if (task[0].defexit != NULL) {
+      FreeMacro (task[0].defexit);
+      free (task[0].defexit);
+    }
+    ALLOCATE (task[0].defexit, Macro, 1);
+    macro = task[0].defexit;
+    macro[0].name = strcreate ("default");
+    goto found;
+  }
+  if (!strcmp (argv[0], "task.exit")) {	/* generic exit function */
+    for (i = 0; i < task[0].Nexit; i++) { /* find exiting exit function with same name */
+      if (!strcmp (task[0].exit[i][0].name, argv[1])) {	/*** replace this one ***/
+	FreeMacro (task[0].exit[i]);
+	free (task[0].exit[i]);
+	ALLOCATE (task[0].exit[i], Macro, 1);
+	macro = task[0].exit[i];
+	macro[0].name = strcreate (argv[1]);
+	goto found;
+      }
+    }
+    /** create a new exit status macro **/
+    N = task[0].Nexit;
+    ALLOCATE (task[0].exit[N], Macro, 1);
+    task[0].Nexit ++;
+    if (task[0].Nexit == task[0].NEXIT) {
+      task[0].NEXIT += 10;
+      REALLOCATE (task[0].exit, Macro *, task[0].NEXIT);
+    }
+    macro = task[0].exit[N];
+    macro[0].name = strcreate (argv[1]);
+    goto found;
+  }
+
+found:
+
+  macro[0].Nlines = 0;
+  NLINES = D_NLINES;
+  ALLOCATE (macro[0].line, char *, NLINES);
+
+  /* load macro lines from appropriate source (readline / list) */
+
+  depth = 0;
+  done = FALSE;
+  ThisList = current_list_depth();
+  while (!done) {
+
+    /* get the next line (from correct place) */
+    if (ThisList == 0) 
+      input = opihi_readline (prompt);
+    else 
+      input = get_next_listentry (ThisList);
+
+    if ((ThisList == 0) && (input == (char *) NULL)) {
+      gprint (GP_ERR, "end macro with 'END'\n");
+      continue;
+    }
+    if ((ThisList > 0) && (input == (char *) NULL)) {
+      gprint (GP_ERR, "missing 'END' in macro definition\n");
+      input = strcreate ("end");
+    }
+
+    stripwhite (input);
+
+    /* 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) { /* we hit the last "END", macro is done */
+	free (input);
+	REALLOCATE (macro[0].line, char *, MAX (1, macro[0].Nlines));
+	JobTaskUnlock();
+	return (TRUE);
+      }
+    }
+
+    if (*input) { 
+      macro[0].line[macro[0].Nlines] = input;
+      macro[0].Nlines ++;
+      if (macro[0].Nlines == NLINES - 1) {
+	NLINES += D_NLINES;
+	REALLOCATE (macro[0].line, char *, NLINES);
+      }
+    }
+  }
+  JobTaskUnlock();
+  return (TRUE);
+}
Index: /branches/sc_branches/pantasks_condor/pantasks/task_nice.c
===================================================================
--- /branches/sc_branches/pantasks_condor/pantasks/task_nice.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/pantasks/task_nice.c	(revision 34783)
@@ -0,0 +1,34 @@
+# include "pantasks.h"
+
+int task_nice (int argc, char **argv) {
+
+  char *endptr = NULL;
+  int priority;
+  Task *task;
+
+  if (argc != 2) {
+    gprint (GP_ERR, "USAGE: nice (priority)\n");
+    return (FALSE);
+  }
+
+  JobTaskLock();
+  task = GetNewTask ();
+  if (task == NULL) {
+    gprint (GP_ERR, "ERROR: not defining or running a task\n");
+    JobTaskUnlock();
+    return (FALSE);
+  }
+
+  priority = strtol (argv[1], &endptr, 10);
+  if (*endptr) goto fail;
+  if (priority < 0) goto fail;
+  if (priority > 20) goto fail;
+
+  task[0].priority = priority;
+  JobTaskUnlock();
+  return (TRUE);
+
+fail:
+    gprint (GP_ERR, "ERROR: nice (priority) -- priority must be an integer 0 to 20\n");
+    return (FALSE);
+}
Index: /branches/sc_branches/pantasks_condor/pantasks/task_nmax.c
===================================================================
--- /branches/sc_branches/pantasks_condor/pantasks/task_nmax.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/pantasks/task_nmax.c	(revision 34783)
@@ -0,0 +1,47 @@
+# include "pantasks.h"
+
+int task_nmax (int argc, char **argv) {
+
+  Task *task;
+
+  if (argc != 2) goto usage;
+
+  JobTaskLock();
+  task = GetNewTask ();
+  if (task == NULL) {
+    gprint (GP_ERR, "ERROR: not defining or running a task\n");
+    JobTaskUnlock();
+    return (FALSE);
+  }
+
+  task[0].Nmax = atoi (argv[1]);
+  JobTaskUnlock();
+  return (TRUE);
+
+usage:
+  gprint (GP_ERR, "USAGE: nmax N\n");
+  return (FALSE);
+}
+
+int task_npending (int argc, char **argv) {
+
+  Task *task;
+
+  if (argc != 2) goto usage;
+
+  JobTaskLock();
+  task = GetNewTask ();
+  if (task == NULL) {
+    gprint (GP_ERR, "ERROR: not defining or running a task\n");
+    JobTaskUnlock();
+    return (FALSE);
+  }
+
+  task[0].NpendingMax = atoi (argv[1]);
+  JobTaskUnlock();
+  return (TRUE);
+
+usage:
+  gprint (GP_ERR, "USAGE: npending N\n");
+  return (FALSE);
+}
Index: /branches/sc_branches/pantasks_condor/pantasks/task_options.c
===================================================================
--- /branches/sc_branches/pantasks_condor/pantasks/task_options.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/pantasks/task_options.c	(revision 34783)
@@ -0,0 +1,41 @@
+# include "pantasks.h"
+
+int task_options (int argc, char **argv) {
+
+  int i;
+  Task *task;
+
+  if (argc < 2) {
+    gprint (GP_ERR, "USAGE: options <opt>. ..\n");
+    gprint (GP_ERR, "  (define options for this task)\n");
+    return (FALSE);
+  }
+
+  JobTaskLock();
+  task = GetNewTask ();
+  if (task == NULL) {
+    task = GetActiveTask ();
+    if (task == NULL) {
+      gprint (GP_ERR, "ERROR: not defining or running a task\n");
+      JobTaskUnlock();
+      return (FALSE);
+    }
+  }
+
+  /* free existing memory used by optv */
+  if (task[0].optc != 0) {
+    for (i = 0; i < task[0].optc; i++) {
+      if (task[0].optv[i] != NULL) free (task[0].optv[i]);
+    }
+    free (task[0].optv);
+  }
+
+  /* create new memory for optv */
+  task[0].optc = argc - 1;
+  ALLOCATE (task[0].optv, char *, MAX (task[0].optc, 1));
+  for (i = 0; i < task[0].optc; i++) {
+    task[0].optv[i] = strcreate (argv[i+1]);
+  }
+  JobTaskUnlock();
+  return (TRUE);
+}
Index: /branches/sc_branches/pantasks_condor/pantasks/task_periods.c
===================================================================
--- /branches/sc_branches/pantasks_condor/pantasks/task_periods.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/pantasks/task_periods.c	(revision 34783)
@@ -0,0 +1,59 @@
+# include "pantasks.h"
+
+int task_periods (int argc, char **argv) {
+
+  int N, Poll, Exec, Timeout;
+  float PollValue, ExecValue, TimeoutValue;
+  Task *task;
+
+  PollValue = 0;
+  Poll = FALSE;
+  if ((N = get_argument (argc, argv, "-poll"))) {
+    Poll = TRUE;
+    remove_argument (N, &argc, argv);
+    PollValue = atof(argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  ExecValue = 0;
+  Exec = FALSE;
+  if ((N = get_argument (argc, argv, "-exec"))) {
+    Exec = TRUE;
+    remove_argument (N, &argc, argv);
+    ExecValue = atof(argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  TimeoutValue = 0;
+  Timeout = FALSE;
+  if ((N = get_argument (argc, argv, "-timeout"))) {
+    Timeout = TRUE;
+    remove_argument (N, &argc, argv);
+    TimeoutValue = atof(argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  if (argc != 1) {
+    gprint (GP_ERR, "USAGE: periods [-poll value] [-exec value] [-timeout value]\n");
+    gprint (GP_ERR, "  define time periods for this task\n");
+    return (FALSE);
+  }
+
+  JobTaskLock();
+  task = GetNewTask ();
+  if (task == NULL) {
+    task = GetActiveTask ();
+    if (task == NULL) {
+      gprint (GP_ERR, "ERROR: not defining or running a task\n");
+      JobTaskUnlock();
+      return (FALSE);
+    }
+  }
+
+  if (Poll) task[0].poll_period = PollValue;
+  if (Exec) task[0].exec_period = ExecValue;
+  if (Timeout) task[0].timeout_period = TimeoutValue;
+
+  JobTaskUnlock();
+  return (TRUE);
+}
Index: /branches/sc_branches/pantasks_condor/pantasks/task_stdout.c
===================================================================
--- /branches/sc_branches/pantasks_condor/pantasks/task_stdout.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/pantasks/task_stdout.c	(revision 34783)
@@ -0,0 +1,55 @@
+# include "pantasks.h"
+
+int task_stdout (int argc, char **argv) {
+
+  Task *task;
+
+  if (argc != 2) {
+    gprint (GP_ERR, "USAGE: stdout <name>\n");
+    gprint (GP_ERR, "  define dump file for job stdout for this task\n");
+    return (FALSE);
+  }
+
+  JobTaskLock();
+  task = GetNewTask ();
+  if (task == NULL) {
+    task = GetActiveTask ();
+    if (task == NULL) {
+      gprint (GP_ERR, "ERROR: not defining or running a task\n");
+      JobTaskUnlock();
+      return (FALSE);
+    }
+  }
+  if (task[0].stdout_dump != NULL) free (task[0].stdout_dump);
+  task[0].stdout_dump = strcreate (argv[1]);
+  JobTaskUnlock();
+  return (TRUE);
+}
+
+int task_stderr (int argc, char **argv) {
+
+  Task *task;
+
+  if (argc != 2) {
+    gprint (GP_ERR, "USAGE: stderr <name>\n");
+    gprint (GP_ERR, "  define dump file for job stderr for this task\n");
+    return (FALSE);
+  }
+
+  JobTaskLock();
+  task = GetNewTask ();
+  if (task == NULL) {
+    task = GetActiveTask ();
+    if (task == NULL) {
+      gprint (GP_ERR, "ERROR: not defining or running a task\n");
+      JobTaskUnlock();
+      return (FALSE);
+    }
+  }
+  if (task[0].stderr_dump != NULL) free (task[0].stderr_dump);
+  task[0].stderr_dump = strcreate (argv[1]);
+  JobTaskUnlock();
+  return (TRUE);
+}
+
+/* apparently, local is the default! */
Index: /branches/sc_branches/pantasks_condor/pantasks/task_threads.c
===================================================================
--- /branches/sc_branches/pantasks_condor/pantasks/task_threads.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/pantasks/task_threads.c	(revision 34783)
@@ -0,0 +1,83 @@
+# include "pantasks.h"
+
+/** things related to CheckTasks **/
+void ResetTaskTimers ();
+
+static int CheckTasksRun = FALSE;
+
+void CheckTasksSetState (int state) {
+  if (state && !CheckTasksRun) {
+    ResetTaskTimers ();
+  }
+  CheckTasksRun = state;
+}
+int CheckTasksGetState () {
+  return (CheckTasksRun);
+}
+
+void *CheckTasksThread (void *data) {
+
+  float next_timeout;
+  char log_stdout[128], log_stderr[128];
+
+  gprintInit ();  // each thread needs to init the printing system
+
+  // define server output log files
+  if (VarConfig ("PANTASKS_SERVER_STDOUT", "%s", log_stdout) != NULL) {
+      gprintSetFileThisThread (GP_LOG, log_stdout);
+  }
+  if (VarConfig ("PANTASKS_SERVER_STDERR", "%s", log_stderr) != NULL) {
+      gprintSetFileThisThread (GP_ERR, log_stderr);
+  }
+
+  while (1) {
+
+    // check for thread suspend
+    if (!CheckTasksRun) {
+      usleep (100000); // idle if thread action is suspended
+      continue;
+    }
+
+    // one run of the task checker
+    SerialThreadLock ();
+    next_timeout = CheckTasks ();
+    SerialThreadUnlock ();
+    if (VerboseMode() == 2) fprintf (stderr, "T");
+
+    next_timeout = MIN (next_timeout, 0.1);
+    if (next_timeout > 0.001) {
+      usleep ((int)(1000000*next_timeout)); // allow other threads a chance to run
+    }      
+  }
+}
+
+// I sleep for a small amount of time here, based on how long until the next expected
+// timeout.  this enforces a certain granularity in the task creation, but prevents the task
+// thread from driving the load up to silly levels.
+
+// reset all of the task timers, with a bit of fuzz.  this is called 
+// whenever we transition from 'stop' to 'run'
+void ResetTaskTimers () {
+
+  Task *task;
+  struct timeval now;
+  float fuzz;
+
+  // get the current time
+  gettimeofday (&now, NULL);
+
+  // check all tasks
+  while ((task = NextTask ()) != NULL) {
+    task[0].last.tv_usec = now.tv_usec;
+    task[0].last.tv_sec  = now.tv_sec;
+
+    // add random offset between 0 and 10% of exec_period
+    // XXX this should be optional
+    fuzz = task[0].exec_period*drand48();
+    task[0].last.tv_usec += 1e6*(fuzz - (int)fuzz);
+    task[0].last.tv_sec += (int) fuzz;
+
+    // gprint (GP_LOG, "fuzz: %f, last: %d %d\n", fuzz, task[0].last.tv_sec, task[0].last.tv_usec);
+  }
+  return;
+}
Index: /branches/sc_branches/pantasks_condor/pantasks/task_trange.c
===================================================================
--- /branches/sc_branches/pantasks_condor/pantasks/task_trange.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/pantasks/task_trange.c	(revision 34783)
@@ -0,0 +1,102 @@
+# include "pantasks.h"
+
+int task_trange (int argc, char **argv) {
+
+  int N;
+  Task *task;
+  TimeRange range;
+
+  /* reset the tranges for the current task */
+  if ((N = get_argument (argc, argv, "-reset"))) {
+    remove_argument (N, &argc, argv);
+    if (argc != 1) goto usage;
+
+    JobTaskLock();
+    task = GetNewTask ();
+    if (task == NULL) {
+      gprint (GP_ERR, "ERROR: not defining or running a task\n");
+      JobTaskUnlock();
+      return (FALSE);
+    }
+
+    task[0].Nranges = 0;
+    REALLOCATE (task[0].ranges, TimeRange, 1);
+    JobTaskUnlock();
+    return (TRUE);
+  } 
+
+  range.Nmax = 0;
+  range.Nrun = 0;
+  if ((N = get_argument (argc, argv, "-nmax"))) {
+    remove_argument (N, &argc, argv);
+    range.Nmax = atoi (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  /* keep = true means the time range defines a valid time range
+     keep = false means the time range defines an invalid range */
+  range.include = TRUE;
+  if ((N = get_argument (argc, argv, "-exclude"))) {
+    remove_argument (N, &argc, argv);
+    range.include = FALSE;
+  }
+
+  if (argc != 3) goto usage;
+
+  /* test for Mon[@HH:MM:SS] - both must match */
+  if (day_to_sec (argv[1], &range.start)) {
+    if (!day_to_sec (argv[2], &range.stop)) {
+      gprint (GP_ERR, "invalid day/time %s\n", argv[2]);
+      goto usage;
+    }
+    range.type = RANGE_WEEK;
+    goto valid;
+  }
+
+  /* allow a syntax which means something like "every hour at MM:SS"
+     this could by something like *:MM:SS ? */
+
+  /* test for HH:MM:SS */
+  if (hms_to_sec (argv[1], &range.start)) {
+    if (!hms_to_sec (argv[2], &range.stop)) {
+      gprint (GP_ERR, "invalid time %s\n", argv[2]);
+      goto usage;
+    }
+    range.type = RANGE_DAY;
+    goto valid;
+  }
+
+  /* this test does not fail sufficiently robustly for invalid inputs */
+  /* test for YYYY/MM/DD - both must match */
+  if (ohana_str_to_time (argv[1], &range.start)) {
+    if (!ohana_str_to_time (argv[2], &range.stop)) {
+      gprint (GP_ERR, "invalid date/time %s\n", argv[2]);
+      goto usage;
+    }
+    range.type = RANGE_ABS;
+    goto valid;
+  }
+  goto usage;
+
+valid:
+  JobTaskLock();
+  task = GetNewTask ();
+  if (task == NULL) {
+    gprint (GP_ERR, "ERROR: not defining or running a task\n");
+    JobTaskUnlock();
+    return (FALSE);
+  }
+
+  N = task[0].Nranges;
+  task[0].Nranges ++;
+  REALLOCATE (task[0].ranges, TimeRange, task[0].Nranges);
+  
+  task[0].ranges[N] = range;
+  JobTaskUnlock();
+  return (TRUE);
+
+usage:
+  gprint (GP_ERR, "USAGE: trange start end [-nmax N]\n");
+  gprint (GP_ERR, "USAGE: trange -reset\n");
+  return (FALSE);
+}
Index: /branches/sc_branches/pantasks_condor/pantasks/test/iotest.sh
===================================================================
--- /branches/sc_branches/pantasks_condor/pantasks/test/iotest.sh	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/pantasks/test/iotest.sh	(revision 34783)
@@ -0,0 +1,78 @@
+
+$BLOCK = 0
+
+macro testloop
+  queuepush stdout start
+  for i 0 5
+    echo $i
+    queueprint stdout
+  end
+end
+
+# a basic task which just runs 'echo foobar'
+task	       basic
+  command      echo foobar
+  host         local
+
+  periods      -poll 0.1
+  periods      -exec 1.0
+  periods      -timeout 20
+  active       false
+  npending 5
+  
+  stdout tmp.txt
+  stderr tmp.txt
+
+  task.exec
+    if ($BLOCK) break
+    queuepush stdout start
+    echo "command start"
+    queuelist
+    queueprint stdout
+    queueinit stdout
+    queuelist
+    echo "command stop"
+  end
+
+  # success
+  task.exit    0
+    $BLOCK = 1
+    echo "exit start"
+    queuelist
+    queueprint stdout
+    queueinit stdout
+    queuelist
+    echo "exit stop"
+    $BLOCK = 0
+  end
+
+  # default exit status
+  task.exit    default
+    echo       "failure: exit status: $EXIT"
+  end
+
+  # operation times out?
+  task.exit    timeout
+    echo       "timeout"
+  end
+end
+
+macro show.book
+  if ($0 != 2)
+   echo "USAGE: show.book (book)"
+   break
+  end
+
+  book npages $1 -var npages
+  for i 0 $npages
+    book getpage $1 $i -var pagename
+    book listpage $1 $pagename
+  end
+
+  echo "npages: $npages"
+end
+
+macro load.book
+  queueload stdout -x "cat iotest.dat"
+  ipptool2book stdout testbook -key chip_id
+end
Index: /branches/sc_branches/pantasks_condor/pantasks/test/joberror.sh
===================================================================
--- /branches/sc_branches/pantasks_condor/pantasks/test/joberror.sh	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/pantasks/test/joberror.sh	(revision 34783)
@@ -0,0 +1,29 @@
+
+# a basic task which just runs 'echo'
+task	       basic
+  host         local
+
+  periods      -poll 0.005
+  periods      -exec 0.005
+  periods      -timeout 2
+  command      ls -l /usr/bin /tmp/foobar
+  
+  stdout tmp.txt
+  stderr tmp.txt
+
+  # success
+  task.exit    0
+    $Npass ++
+  end
+
+  # default exit status
+  task.exit    default
+    echo       "basic: exit status: $JOB_STATUS"
+  end
+
+  # operation times out?
+  task.exit    timeout
+    echo       "basic: timeout"
+  end
+end
+
Index: /branches/sc_branches/pantasks_condor/pantasks/test/load.sh
===================================================================
--- /branches/sc_branches/pantasks_condor/pantasks/test/load.sh	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/pantasks/test/load.sh	(revision 34783)
@@ -0,0 +1,37 @@
+
+# a basic task which just runs 'sleep'
+task	       newtask
+  # command      sleep 1
+  command      echo hi
+  host         local
+
+  periods      -poll 0.01
+  periods      -exec 0.01
+  periods      -timeout 20
+  npending 5
+  
+  stdout tmp.txt
+  stderr tmp.txt
+
+  task.exec
+    # echo "create command"
+  end
+
+  # success
+  task.exit    0
+    $Npass ++
+    # echo done sleep
+    # status
+    # halt
+  end
+
+  # default exit status
+  task.exit    default
+    echo       "basic: exit status: $EXIT"
+  end
+
+  # operation times out?
+  task.exit    timeout
+    echo       "basic: timeout"
+  end
+end
Index: /branches/sc_branches/pantasks_condor/pantasks/test/local.sh
===================================================================
--- /branches/sc_branches/pantasks_condor/pantasks/test/local.sh	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/pantasks/test/local.sh	(revision 34783)
@@ -0,0 +1,78 @@
+
+## a basic test of memory allocation
+
+exec rm -f tmp.txt
+verbose off
+$Npass = 0
+
+# a basic task which just runs 'echo'
+task	       basic
+  command      echo "a test line"
+  host         local
+
+  periods      -poll 0.1
+  periods      -exec 0.2
+  periods      -timeout 2
+  
+  stdout tmp.txt
+  stderr tmp.txt
+
+  # success
+  task.exit    0
+    $Npass ++
+  end
+
+  # default exit status
+  task.exit    default
+    echo       "basic: exit status: $EXIT"
+  end
+
+  # operation times out?
+  task.exit    timeout
+    echo       "basic: timeout"
+  end
+end
+
+macro memcheck.init
+ list word -x "ps -p $PID -o rss"
+ $startmem = $word:1
+end
+macro memcheck
+ list word -x "ps -p $PID -o rss"
+ $endmem = $word:1
+ # echo growth: {$endmem - $startmem}
+end
+
+macro done
+ stop
+ date -var dtime -seconds -reftime $start
+ if ($VERBOSE > 3)
+  status
+  exec wc -l tmp.txt
+ end
+
+ $answer = `wc -l tmp.txt`
+ list word -split $answer
+ if ($word:0 != $Npass) 
+   echo "missing lines in tmp.txt: $word:0"
+ end
+ if ($Npass/$dtime < 4.2) 
+   echo "tasks running too slow: {$Npass/$dtime}"
+ end
+
+ memcheck
+
+ if ($endmem - $startmem > 10)
+   $PASS = 0
+   echo "failed memcheck"
+   echo "growth: {$endmem-$startmem}"
+   echo "kB/loop: {($endmem-$startmem)/10000}"
+ end
+end
+
+memcheck.init
+echo "starting memcheck"
+echo "wait a few seconds and type 'done'"
+
+run
+date -var start -seconds
Index: /branches/sc_branches/pantasks_condor/pantasks/test/local2.sh
===================================================================
--- /branches/sc_branches/pantasks_condor/pantasks/test/local2.sh	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/pantasks/test/local2.sh	(revision 34783)
@@ -0,0 +1,79 @@
+
+## a basic test of memory allocation
+
+exec rm -f tmp.txt
+verbose off
+$Npass = 0
+
+# a basic task which just runs 'echo'
+task	       basic
+  command      echo "a test line"
+  host         local
+
+  periods      -poll 0.1
+  periods      -exec 0.2
+  periods      -timeout 2
+  
+  stdout tmp.txt
+  stderr tmp.txt
+
+  # success
+  task.exit    0
+    $Npass ++
+  end
+
+  # default exit status
+  task.exit    default
+    echo       "basic: exit status: $EXIT"
+  end
+
+  # operation times out?
+  task.exit    timeout
+    echo       "basic: timeout"
+  end
+end
+
+macro memcheck.init
+ list word -x "ps -p $PID -o rss"
+ $startmem = $word:1
+end
+macro memcheck
+ list word -x "ps -p $PID -o rss"
+ $endmem = $word:1
+ echo growth: {$endmem - $startmem}
+end
+
+macro done
+ stop
+ date -var dtime -seconds -reftime $start
+ if ($VERBOSE > 3)
+  status
+  exec wc -l tmp.txt
+ end
+
+ $answer = `wc -l tmp.txt`
+ list word -split $answer
+ if ($word:0 != $Npass) 
+   echo "missing lines in tmp.txt: $word:0"
+ end
+ if ($Npass/$dtime < 4.2) 
+   echo "tasks running too slow: {$Npass/$dtime}"
+ end
+
+ memcheck
+
+ if ($endmem - $startmem > 30)
+   $PASS = 0
+   echo "failed memcheck"
+   echo "growth: {$endmem-$startmem}"
+   echo "kB/loop: {($endmem-$startmem)/10000}"
+ end
+end
+
+
+memcheck.init
+echo "starting memcheck"
+echo "wait a few seconds and type 'done'"
+
+run
+date -var start -seconds
Index: /branches/sc_branches/pantasks_condor/pantasks/test/local3.sh
===================================================================
--- /branches/sc_branches/pantasks_condor/pantasks/test/local3.sh	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/pantasks/test/local3.sh	(revision 34783)
@@ -0,0 +1,88 @@
+
+## a basic test of memory allocation
+
+exec rm -f tmp.txt
+verbose off
+$Npass = 0
+if ($?VERBOSE == 0)
+  $VERBOSE = 4
+end
+
+# a basic task which just runs 'echo'
+task	       basic
+  command      echo "a test line"
+  host         local
+
+  periods      -poll 0.005
+  periods      -exec 0.005
+  periods      -timeout 2
+  
+  stdout tmp.txt
+  stderr tmp.txt
+
+  # success
+  task.exit    0
+    $Npass ++
+  end
+
+  # default exit status
+  task.exit    default
+    echo       "basic: exit status: $EXIT"
+  end
+
+  # operation times out?
+  task.exit    timeout
+    echo       "basic: timeout"
+  end
+end
+
+macro memcheck.init
+ list word -x "ps -p $PID -o rss"
+ $startmem = $word:1
+end
+macro memcheck
+ list word -x "ps -p $PID -o rss"
+ $endmem = $word:1
+ if ($VERBOSE > 3)
+   echo growth: {$endmem - $startmem}
+ end
+end
+
+macro done
+ stop
+ date -var dtime -seconds -reftime $start
+ if ($VERBOSE > 3)
+  status
+  exec wc -l tmp.txt
+ end
+
+ $answer = `wc -l tmp.txt`
+ list word -split $answer
+ if ($word:0 != $Npass) 
+   echo "missing lines in tmp.txt: $word:0"
+ end
+ if ($VERBOSE > 3)
+   echo "task speed: {$dtime/$Npass} sec/tasks"
+ end
+ ### this test depends on the value of periods -exec above
+ if ($dtime/$Npass > 0.010) 
+   echo "tasks running too slow: {$dtime/$Npass}"
+ end
+
+ memcheck
+
+ if ($endmem - $startmem > 30)
+   $PASS = 0
+   echo "failed memcheck"
+   echo "growth: {$endmem-$startmem}"
+   echo "kB/loop: {($endmem-$startmem)/10000}"
+ end
+end
+
+
+memcheck.init
+echo "starting memcheck"
+echo "wait a few seconds and type 'done'"
+
+run
+date -var start -seconds
Index: /branches/sc_branches/pantasks_condor/pantasks/test/local4.sh
===================================================================
--- /branches/sc_branches/pantasks_condor/pantasks/test/local4.sh	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/pantasks/test/local4.sh	(revision 34783)
@@ -0,0 +1,93 @@
+
+## a basic test of memory allocation
+## task.exec test
+
+exec rm -f tmp.txt
+verbose off
+$Npass = 0
+if ($?VERBOSE == 0)
+  $VERBOSE = 4
+end
+
+# a basic task which just runs 'echo'
+task	       basic
+  command      echo "a test line"
+  host         local
+
+  periods      -poll 0.005
+  periods      -exec 0.005
+  periods      -timeout 2
+  
+  stdout tmp.txt
+  stderr tmp.txt
+
+  task.exec
+    $Nrun = 1
+  end
+
+  # success
+  task.exit    0
+    $Npass ++
+  end
+
+  # default exit status
+  task.exit    default
+    echo       "basic: exit status: $EXIT"
+  end
+
+  # operation times out?
+  task.exit    timeout
+    echo       "basic: timeout"
+  end
+end
+
+macro memcheck.init
+ list word -x "ps -p $PID -o rss"
+ $startmem = $word:1
+end
+macro memcheck
+ list word -x "ps -p $PID -o rss"
+ $endmem = $word:1
+ if ($VERBOSE > 3)
+   echo growth: {$endmem - $startmem}
+ end
+end
+
+macro done
+ stop
+ date -var dtime -seconds -reftime $start
+ if ($VERBOSE > 3)
+  status
+  exec wc -l tmp.txt
+ end
+
+ $answer = `wc -l tmp.txt`
+ list word -split $answer
+ if ($word:0 != $Npass) 
+   echo "missing lines in tmp.txt: $word:0"
+ end
+ if ($VERBOSE > 3)
+   echo "task speed: {$dtime/$Npass} sec/tasks"
+ end
+ ### this test depends on the value of periods -exec above
+ if ($dtime/$Npass > 0.010) 
+   echo "tasks running too slow: {$dtime/$Npass}"
+ end
+
+ memcheck
+
+ if ($endmem - $startmem > 30)
+   $PASS = 0
+   echo "failed memcheck"
+   echo "growth: {$endmem-$startmem}"
+   echo "kB/loop: {($endmem-$startmem)/10000}"
+ end
+end
+
+
+memcheck.init
+echo "starting memcheck"
+echo "wait a few seconds and type 'done'"
+
+run
+date -var start -seconds
Index: /branches/sc_branches/pantasks_condor/pantasks/test/local5.sh
===================================================================
--- /branches/sc_branches/pantasks_condor/pantasks/test/local5.sh	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/pantasks/test/local5.sh	(revision 34783)
@@ -0,0 +1,93 @@
+
+## a basic test of memory allocation
+## dynamic command
+
+exec rm -f tmp.txt
+verbose off
+$Npass = 0
+if ($?VERBOSE == 0)
+  $VERBOSE = 4
+end
+
+# a basic task which just runs 'echo'
+task	       basic
+  host         local
+
+  periods      -poll 0.005
+  periods      -exec 0.005
+  periods      -timeout 2
+  
+  stdout tmp.txt
+  stderr tmp.txt
+
+  task.exec
+    $Nrun = 1
+    command echo "$Npass : test line"
+  end
+
+  # success
+  task.exit    0
+    $Npass ++
+  end
+
+  # default exit status
+  task.exit    default
+    echo       "basic: exit status: $EXIT"
+  end
+
+  # operation times out?
+  task.exit    timeout
+    echo       "basic: timeout"
+  end
+end
+
+macro memcheck.init
+ list word -x "ps -p $PID -o rss"
+ $startmem = $word:1
+end
+macro memcheck
+ list word -x "ps -p $PID -o rss"
+ $endmem = $word:1
+ if ($VERBOSE > 3)
+   echo growth: {$endmem - $startmem}
+ end
+end
+
+macro done
+ stop
+ date -var dtime -seconds -reftime $start
+ if ($VERBOSE > 3)
+  status
+  exec wc -l tmp.txt
+ end
+
+ $answer = `wc -l tmp.txt`
+ list word -split $answer
+ if ($word:0 != $Npass) 
+   echo "missing lines in tmp.txt: $word:0"
+ end
+ if ($VERBOSE > 3)
+   echo "task speed: {$dtime/$Npass} sec/tasks"
+ end
+ ### this test depends on the value of periods -exec above
+ if ($dtime/$Npass > 0.010) 
+   echo "tasks running too slow: {$dtime/$Npass}"
+ end
+
+ memcheck
+
+ if ($endmem - $startmem > 30)
+   $PASS = 0
+   echo "failed memcheck"
+   echo "growth: {$endmem-$startmem}"
+   echo "kB/loop: {($endmem-$startmem)/10000}"
+ end
+end
+
+
+memcheck.init
+echo "starting memcheck"
+echo "wait a few seconds and type 'done'"
+
+run
+date -var start -seconds
Index: /branches/sc_branches/pantasks_condor/pantasks/test/local6.sh
===================================================================
--- /branches/sc_branches/pantasks_condor/pantasks/test/local6.sh	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/pantasks/test/local6.sh	(revision 34783)
@@ -0,0 +1,94 @@
+
+## a basic test of memory allocation
+## test of 'options'
+
+exec rm -f tmp.txt
+verbose off
+$Npass = 0
+if ($?VERBOSE == 0)
+  $VERBOSE = 4
+end
+
+# a basic task which just runs 'echo'
+task	       basic
+  host         local
+
+  periods      -poll 0.005
+  periods      -exec 0.005
+  periods      -timeout 2
+  
+  stdout tmp.txt
+  stderr tmp.txt
+
+  task.exec
+    $Nrun = 1
+    options "test options line"
+    command echo "$Npass : test line"
+  end
+
+  # success
+  task.exit    0
+    $Npass ++
+  end
+
+  # default exit status
+  task.exit    default
+    echo       "basic: exit status: $EXIT"
+  end
+
+  # operation times out?
+  task.exit    timeout
+    echo       "basic: timeout"
+  end
+end
+
+macro memcheck.init
+ list word -x "ps -p $PID -o rss"
+ $startmem = $word:1
+end
+macro memcheck
+ list word -x "ps -p $PID -o rss"
+ $endmem = $word:1
+ if ($VERBOSE > 3)
+   echo growth: {$endmem - $startmem}
+ end
+end
+
+macro done
+ stop
+ date -var dtime -seconds -reftime $start
+ if ($VERBOSE > 3)
+  status
+  exec wc -l tmp.txt
+ end
+
+ $answer = `wc -l tmp.txt`
+ list word -split $answer
+ if ($word:0 != $Npass) 
+   echo "missing lines in tmp.txt: $word:0"
+ end
+ if ($VERBOSE > 3)
+   echo "task speed: {$dtime/$Npass} sec/tasks"
+ end
+ ### this test depends on the value of periods -exec above
+ if ($dtime/$Npass > 0.010) 
+   echo "tasks running too slow: {$dtime/$Npass}"
+ end
+
+ memcheck
+
+ if ($endmem - $startmem > 30)
+   $PASS = 0
+   echo "failed memcheck"
+   echo "growth: {$endmem-$startmem}"
+   echo "kB/loop: {($endmem-$startmem)/10000}"
+ end
+end
+
+
+memcheck.init
+echo "starting memcheck"
+echo "wait a few seconds and type 'done'"
+
+run
+date -var start -seconds
Index: /branches/sc_branches/pantasks_condor/pantasks/test/local7.sh
===================================================================
--- /branches/sc_branches/pantasks_condor/pantasks/test/local7.sh	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/pantasks/test/local7.sh	(revision 34783)
@@ -0,0 +1,127 @@
+
+## a basic test of memory allocation
+## test of 'exec failure'
+
+exec rm -f tmp.txt
+verbose off
+$Npass = 0
+$Ntry = 0
+if ($?VERBOSE == 0)
+  $VERBOSE = 4
+end
+
+# a basic task which just runs 'echo'
+task	       basic
+  host         local
+
+  periods      -poll 0.005
+  periods      -exec 0.005
+  periods      -timeout 2
+  
+  stdout tmp.txt
+  stderr tmp.txt
+
+  task.exec
+    $Ntry ++
+    if ($Ntry > -1) break
+
+    echo "this is a line in the task exec code"
+    echo "this is a line in the task exec code"
+    echo "this is a line in the task exec code"
+    echo "this is a line in the task exec code"
+    echo "this is a line in the task exec code"
+    echo "this is a line in the task exec code"
+    echo "this is a line in the task exec code"
+    echo "this is a line in the task exec code"
+    echo "this is a line in the task exec code"
+    echo "this is a line in the task exec code"
+    echo "this is a line in the task exec code"
+    echo "this is a line in the task exec code"
+    echo "this is a line in the task exec code"
+    echo "this is a line in the task exec code"
+    echo "this is a line in the task exec code"
+    echo "this is a line in the task exec code"
+    echo "this is a line in the task exec code"
+    echo "this is a line in the task exec code"
+    echo "this is a line in the task exec code"
+    echo "this is a line in the task exec code"
+    echo "this is a line in the task exec code"
+    echo "this is a line in the task exec code"
+    echo "this is a line in the task exec code"
+    echo "this is a line in the task exec code"
+    echo "this is a line in the task exec code"
+    echo "this is a line in the task exec code"
+    echo "this is a line in the task exec code"
+    echo "this is a line in the task exec code"
+    echo "this is a line in the task exec code"
+    echo "this is a line in the task exec code"
+    echo "this is a line in the task exec code"
+    echo "this is a line in the task exec code"
+    echo "this is a line in the task exec code"
+    echo "this is a line in the task exec code"
+    echo "this is a line in the task exec code"
+
+    command echo "$Npass : test line"
+  end
+
+  # success
+  task.exit    0
+    $Npass ++
+  end
+
+  # default exit status
+  task.exit    default
+    echo       "basic: exit status: $EXIT"
+  end
+
+  # operation times out?
+  task.exit    timeout
+    echo       "basic: timeout"
+  end
+end
+
+macro memcheck.init
+ list word -x "ps -p $PID -o rss"
+ $startmem = $word:1
+end
+macro memcheck
+ list word -x "ps -p $PID -o rss"
+ $endmem = $word:1
+ if ($VERBOSE > 3)
+   echo growth: {$endmem - $startmem}
+ end
+end
+
+macro done
+ stop
+ date -var dtime -seconds -reftime $start
+ if ($VERBOSE > 3)
+  status
+ end
+
+ # this task generates no output files
+ if ($VERBOSE > 3)
+   echo "task test speed: {$dtime/$Ntry} sec/tasks"
+ end
+ ### this test depends on the value of periods -exec above
+ if ($dtime/$Ntry > 0.010) 
+   echo "tasks tests running too slow: {$dtime/$Ntry}"
+ end
+
+ memcheck
+
+ if ($endmem - $startmem > 30)
+   $PASS = 0
+   echo "failed memcheck"
+   echo "growth: {$endmem-$startmem}"
+   echo "kB/loop: {($endmem-$startmem)/10000}"
+ end
+end
+
+
+memcheck.init
+echo "starting memcheck"
+echo "wait a few seconds and type 'done'"
+
+run
+date -var start -seconds
Index: /branches/sc_branches/pantasks_condor/pantasks/test/nice.sh
===================================================================
--- /branches/sc_branches/pantasks_condor/pantasks/test/nice.sh	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/pantasks/test/nice.sh	(revision 34783)
@@ -0,0 +1,70 @@
+
+# a basic task which just runs 'sleep 10'
+task	       nicetask_local
+  command      sleep 10
+  host         local
+  nice         10
+
+  periods      -poll 0.1
+  periods      -exec 1.0
+  periods      -timeout 20
+  active       true
+  npending 5
+  
+  stdout tmp.txt
+  stderr tmp.txt
+
+  task.exec
+    echo "nicetask_local start"
+  end
+
+  # success
+  task.exit    0
+    echo "nicetask_local stop"
+  end
+
+  # default exit status
+  task.exit    default
+    echo       "failure: exit status: $EXIT"
+  end
+
+  # operation times out?
+  task.exit    timeout
+    echo       "timeout"
+  end
+end
+
+# a basic task which just runs 'sleep 10'
+task	       meantask_local
+  command      sleep 10
+  host         local
+
+  periods      -poll 0.1
+  periods      -exec 1.0
+  periods      -timeout 20
+  active       true
+  npending 5
+  
+  stdout tmp.txt
+  stderr tmp.txt
+
+  task.exec
+    echo "meantask_local start"
+  end
+
+  # success
+  task.exit    0
+    echo "meantask_local stop"
+  end
+
+  # default exit status
+  task.exit    default
+    echo       "failure: exit status: $EXIT"
+  end
+
+  # operation times out?
+  task.exit    timeout
+    echo       "timeout"
+  end
+end
+
Index: /branches/sc_branches/pantasks_condor/pantasks/test/nice_remote.sh
===================================================================
--- /branches/sc_branches/pantasks_condor/pantasks/test/nice_remote.sh	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/pantasks/test/nice_remote.sh	(revision 34783)
@@ -0,0 +1,70 @@
+
+# a basic task which just runs 'sleep 10'
+task	       nicetask_local
+  command      sleep 10
+  host         anyhost
+  nice         10
+
+  periods      -poll 0.1
+  periods      -exec 1.0
+  periods      -timeout 20
+  active       true
+  npending 5
+  
+  stdout tmp.txt
+  stderr tmp.txt
+
+  task.exec
+    echo "nicetask_local start"
+  end
+
+  # success
+  task.exit    0
+    echo "nicetask_local stop"
+  end
+
+  # default exit status
+  task.exit    default
+    echo       "failure: exit status: $EXIT"
+  end
+
+  # operation times out?
+  task.exit    timeout
+    echo       "timeout"
+  end
+end
+
+# a basic task which just runs 'sleep 10'
+task	       meantask_local
+  command      sleep 10
+  host         anyhost
+
+  periods      -poll 0.1
+  periods      -exec 1.0
+  periods      -timeout 20
+  active       true
+  npending 5
+  
+  stdout tmp.txt
+  stderr tmp.txt
+
+  task.exec
+    echo "meantask_local start"
+  end
+
+  # success
+  task.exit    0
+    echo "meantask_local stop"
+  end
+
+  # default exit status
+  task.exit    default
+    echo       "failure: exit status: $EXIT"
+  end
+
+  # operation times out?
+  task.exit    timeout
+    echo       "timeout"
+  end
+end
+
Index: /branches/sc_branches/pantasks_condor/pantasks/test/sleep.sh
===================================================================
--- /branches/sc_branches/pantasks_condor/pantasks/test/sleep.sh	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/pantasks/test/sleep.sh	(revision 34783)
@@ -0,0 +1,34 @@
+
+# a basic task which just runs 'sleep'
+task	       basic
+  command      sleep 10
+  host         local
+
+  periods      -poll 0.1
+  periods      -exec 0.2
+  periods      -timeout 20
+  npending 5
+  
+  stdout tmp.txt
+  stderr tmp.txt
+
+  task.exec
+    echo "create command"
+  end
+
+  # success
+  task.exit    0
+    $Npass ++
+    echo done sleep
+  end
+
+  # default exit status
+  task.exit    default
+    echo       "basic: exit status: $EXIT"
+  end
+
+  # operation times out?
+  task.exit    timeout
+    echo       "basic: timeout"
+  end
+end
Index: /branches/sc_branches/pantasks_condor/pantasks/test/sleep2.sh
===================================================================
--- /branches/sc_branches/pantasks_condor/pantasks/test/sleep2.sh	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/pantasks/test/sleep2.sh	(revision 34783)
@@ -0,0 +1,39 @@
+
+$hostname = `hostname`
+
+controller exit true
+controller host add $hostname
+
+# a basic task which just runs 'sleep'
+task	       basic
+  command      sleep 1
+  host         anyhost
+
+  periods      -poll 0.01
+  periods      -exec 0.02
+  periods      -timeout 20
+  npending 100
+  
+  stdout tmp.txt
+  stderr tmp.txt
+
+  task.exec
+    # echo "create command"
+  end
+
+  # success
+  task.exit    0
+    $Npass ++
+    # echo done sleep
+  end
+
+  # default exit status
+  task.exit    default
+    echo       "basic: exit status: $EXIT"
+  end
+
+  # operation times out?
+  task.exit    timeout
+    echo       "basic: timeout"
+  end
+end
Index: /branches/sc_branches/pantasks_condor/pantasks/test/sleep3.sh
===================================================================
--- /branches/sc_branches/pantasks_condor/pantasks/test/sleep3.sh	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/pantasks/test/sleep3.sh	(revision 34783)
@@ -0,0 +1,47 @@
+
+$hostname = `hostname`
+
+controller exit true
+controller host add po02
+controller host add po03
+controller host add po04
+controller host add po05
+controller host add po06
+controller host add po07
+controller host add po08
+controller host add po09
+controller host add po10
+
+# a basic task which just runs 'sleep'
+task	       basic
+  command      sleep 1
+  host         anyhost
+
+  periods      -poll 0.01
+  periods      -exec 0.02
+  periods      -timeout 20
+  npending 100
+  
+  stdout tmp.txt
+  stderr tmp.txt
+
+  task.exec
+    # echo "create command"
+  end
+
+  # success
+  task.exit    0
+    $Npass ++
+    # echo done sleep
+  end
+
+  # default exit status
+  task.exit    default
+    echo       "basic: exit status: $EXIT"
+  end
+
+  # operation times out?
+  task.exit    timeout
+    echo       "basic: timeout"
+  end
+end
Index: /branches/sc_branches/pantasks_condor/pantasks/test/threadload.sh
===================================================================
--- /branches/sc_branches/pantasks_condor/pantasks/test/threadload.sh	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/pantasks/test/threadload.sh	(revision 34783)
@@ -0,0 +1,108 @@
+
+$hostname = `hostname`
+
+controller exit true
+# controller host add $hostname -threads 0
+controller host add $hostname -threads 3
+
+# a basic task which just runs 'sleep'
+task	       basic
+  command      ls -lRrt /tmp 
+  host         anyhost
+
+  periods      -poll 0.1
+  periods      -exec 0.5
+  periods      -timeout 20
+  npending 10
+  
+  stdout tmp.txt
+  stderr tmp.txt
+
+  task.exec
+    # echo "create command"
+  end
+
+  # success
+  task.exit    0
+    $Npass ++
+    # echo done sleep
+  end
+
+  # default exit status
+  task.exit    default
+    echo       "basic: exit status: $JOB_STATUS"
+  end
+
+  # operation times out?
+  task.exit    timeout
+    echo       "basic: timeout"
+  end
+end
+
+# a basic task which just runs 'sleep'
+task	       bigger
+  command      ls -lRrt /usr/bin
+  host         anyhost
+
+  periods      -poll 0.1
+  periods      -exec 0.5
+  periods      -timeout 20
+  npending 10
+  
+  stdout tmp.txt
+  stderr tmp.txt
+
+  task.exec
+    # echo "create command"
+  end
+
+  # success
+  task.exit    0
+    $Npass ++
+    # echo done sleep
+  end
+
+  # default exit status
+  task.exit    default
+    echo       "basic: exit status: $JOB_STATUS"
+  end
+
+  # operation times out?
+  task.exit    timeout
+    echo       "basic: timeout"
+  end
+end
+
+# a basic task which just runs 'sleep'
+task	       biggest
+  command      ls -lRrt /usr/lib
+  host         anyhost
+
+  periods      -poll 0.1
+  periods      -exec 0.5
+  periods      -timeout 20
+  npending 10
+  
+  stdout tmp.txt
+  stderr tmp.txt
+
+  task.exec
+    # echo "create command"
+  end
+
+  # success
+  task.exit    0
+    $Npass ++
+    # echo done sleep
+  end
+
+  # default exit status
+  task.exit    default
+    echo       "basic: exit status: $JOB_STATUS"
+  end
+
+  # operation times out?
+  task.exit    timeout
+    echo       "basic: timeout"
+  end
+end
Index: /branches/sc_branches/pantasks_condor/pantasks/test/threadload2.sh
===================================================================
--- /branches/sc_branches/pantasks_condor/pantasks/test/threadload2.sh	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/pantasks/test/threadload2.sh	(revision 34783)
@@ -0,0 +1,146 @@
+
+$hostname = `hostname`
+
+controller exit true
+# controller host add $hostname -threads 0
+controller host add $hostname -threads 3
+controller host add $hostname -threads 3
+controller host add $hostname -threads 3
+
+# a basic task which just runs 'sleep'
+task	       basic
+  command      ls -lRrt /tmp 
+  host         anyhost
+
+  periods      -poll 0.1
+  periods      -exec 0.5
+  periods      -timeout 20
+  npending 10
+  
+  stdout tmp.txt
+  stderr tmp.txt
+
+  task.exec
+    # echo "create command"
+  end
+
+  # success
+  task.exit    0
+    $Npass ++
+    # output out.dat
+    local i myvalue N
+    queuesize stdout -var N
+    for i 0 $N
+      queuepop stdout -var myvalue
+      # echo $myvalue
+    end
+    queuesize stderr -var N
+    for i 0 $N
+      queuepop stderr -var myvalue
+      # echo $myvalue
+    end
+    # output stdout
+  end
+
+  # default exit status
+  task.exit    default
+    echo       "basic: exit status: $EXIT"
+  end
+
+  # operation times out?
+  task.exit    timeout
+    echo       "basic: timeout"
+  end
+end
+
+# a basic task which just runs 'sleep'
+task	       bigger
+  command      ls -lRrt /usr/bin
+  host         anyhost
+
+  periods      -poll 0.1
+  periods      -exec 0.5
+  periods      -timeout 20
+  npending 10
+  
+  stdout tmp.txt
+  stderr tmp.txt
+
+  task.exec
+    # echo "create command"
+  end
+
+  # success
+  task.exit    0
+    $Npass ++
+    # output out.dat
+    local i myvalue N
+    queuesize stdout -var N
+    for i 0 $N
+      queuepop stdout -var myvalue
+      # echo $myvalue
+    end
+    queuesize stderr -var N
+    for i 0 $N
+      queuepop stderr -var myvalue
+      # echo $myvalue
+    end
+    # output stdout
+  end
+
+  # default exit status
+  task.exit    default
+    echo       "basic: exit status: $EXIT"
+  end
+
+  # operation times out?
+  task.exit    timeout
+    echo       "basic: timeout"
+  end
+end
+
+# a basic task which just runs 'sleep'
+task	       biggest
+  command      ls -lRrt /usr/lib
+  host         anyhost
+
+  periods      -poll 0.1
+  periods      -exec 0.5
+  periods      -timeout 20
+  npending 10
+  
+  stdout tmp.txt
+  stderr tmp.txt
+
+  task.exec
+    # echo "create command"
+  end
+
+  # success
+  task.exit    0
+    $Npass ++
+    # output out.dat
+    local i myvalue N
+    queuesize stdout -var N
+    for i 0 $N
+      queuepop stdout -var myvalue
+      # echo $myvalue
+    end
+    queuesize stderr -var N
+    for i 0 $N
+      queuepop stderr -var myvalue
+      # echo $myvalue
+    end
+    # output stdout
+  end
+
+  # default exit status
+  task.exit    default
+    echo       "basic: exit status: $EXIT"
+  end
+
+  # operation times out?
+  task.exit    timeout
+    echo       "basic: timeout"
+  end
+end
Index: /branches/sc_branches/pantasks_condor/pantasks/test/threads.sh
===================================================================
--- /branches/sc_branches/pantasks_condor/pantasks/test/threads.sh	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/pantasks/test/threads.sh	(revision 34783)
@@ -0,0 +1,40 @@
+
+$hostname = `hostname`
+
+controller exit true
+# controller host add $hostname -threads 0
+controller host add $hostname -threads 3
+
+# a basic task which just runs 'sleep'
+task	       basic
+  command      echo -threads @MAX_THREADS@
+  host         anyhost
+
+  periods      -poll 0.1
+  periods      -exec 0.5
+  periods      -timeout 20
+  npending 10
+  
+  stdout tmp.txt
+  stderr tmp.txt
+
+  task.exec
+    # echo "create command"
+  end
+
+  # success
+  task.exit    0
+    $Npass ++
+    # echo done sleep
+  end
+
+  # default exit status
+  task.exit    default
+    echo       "basic: exit status: $EXIT"
+  end
+
+  # operation times out?
+  task.exit    timeout
+    echo       "basic: timeout"
+  end
+end
Index: /branches/sc_branches/pantasks_condor/pantasks/thread_locks.c
===================================================================
--- /branches/sc_branches/pantasks_condor/pantasks/thread_locks.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/pantasks/thread_locks.c	(revision 34783)
@@ -0,0 +1,52 @@
+# include "pantasks.h"
+
+/* mutex to lock Client table operations */
+static pthread_mutex_t ClientMutex = PTHREAD_MUTEX_INITIALIZER;
+
+void ClientLock (void) {
+  pthread_mutex_lock (&ClientMutex);
+}
+
+void ClientUnlock (void) {
+  pthread_mutex_unlock (&ClientMutex);
+}
+
+/* mutex to lock Command / Multicommand operations */
+static pthread_mutex_t CommandMutex = PTHREAD_MUTEX_INITIALIZER;
+
+void CommandLock (void) {
+  //  fprintf (stderr, "command lock\n");
+  pthread_mutex_lock (&CommandMutex);
+}
+
+void CommandUnlock (void) {
+  //  fprintf (stderr, "command unlock\n");
+  pthread_mutex_unlock (&CommandMutex);
+}
+
+/* mutex to lock Control operations */
+static pthread_mutex_t ControlMutex = PTHREAD_MUTEX_INITIALIZER;
+
+void ControlLock (const char *func) {
+  // fprintf (stderr, "control lock %s\n", func);
+  pthread_mutex_lock (&ControlMutex);
+}
+
+void ControlUnlock (const char *func) {
+  // fprintf (stderr, "control unlock %s\n", func);
+  pthread_mutex_unlock (&ControlMutex);
+}
+
+/* mutex to lock Job / Task operations */
+static pthread_mutex_t JobTaskMutex = PTHREAD_MUTEX_INITIALIZER;
+
+void JobTaskLock (void) {
+  //  fprintf (stderr, "jobtask lock\n");
+  pthread_mutex_lock (&JobTaskMutex);
+}
+
+void JobTaskUnlock (void) {
+  //  fprintf (stderr, "jobtask unlock\n");
+  pthread_mutex_unlock (&JobTaskMutex);
+}
+
Index: /branches/sc_branches/pantasks_condor/pantasks/verbose.c
===================================================================
--- /branches/sc_branches/pantasks_condor/pantasks/verbose.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/pantasks/verbose.c	(revision 34783)
@@ -0,0 +1,41 @@
+# include "pantasks.h"
+
+static int VERBOSE = FALSE;
+
+int verbose (int argc, char **argv) {
+
+  if (argc == 1) {
+    if (VERBOSE) {
+      gprint (GP_LOG, "verbose mode ON\n");
+    } else {
+      gprint (GP_LOG, "verbose mode OFF\n");
+    }
+    return (TRUE);
+  }
+
+  if (argc == 2) {
+    if (!strcasecmp (argv[1], "ON")) {
+      VERBOSE = TRUE;
+      return (TRUE);
+    }
+    if (!strcasecmp (argv[1], "OFF")) {
+      VERBOSE = FALSE;
+      return (TRUE);
+    }
+    if (!strcasecmp (argv[1], "THREADS")) {
+      VERBOSE = 2;
+      return (TRUE);
+    }
+    if (!strcasecmp (argv[1], "TOGGLE")) {
+      VERBOSE = ~VERBOSE;
+      return (TRUE);
+    }
+  }
+
+  gprint (GP_ERR, "USAGE: verbose (on/off/threads/toggle)\n");
+  return (FALSE);
+}
+
+int VerboseMode () {
+  return (VERBOSE);
+}
Index: /branches/sc_branches/pantasks_condor/pantasks/version.c
===================================================================
--- /branches/sc_branches/pantasks_condor/pantasks/version.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/pantasks/version.c	(revision 34783)
@@ -0,0 +1,17 @@
+# include "pantasks.h"
+static char *name = "$Name: not supported by cvs2svn $";
+
+int version (int argc, char **argv) {
+
+  char *tmp;
+
+  gprint (GP_LOG, "\n");
+  gprint (GP_LOG, "pantasks version: %s\n", (tmp = strip_version (name))); free (tmp);
+
+  gprint (GP_LOG, "opihi version: %s\n", (tmp = strip_version (opihi_version()))); free (tmp);
+  gprint (GP_LOG, "ohana version: %s\n", (tmp = strip_version (ohana_version()))); free (tmp);
+  gprint (GP_LOG, "gfits version: %s\n", (tmp = strip_version (gfits_version()))); free (tmp);
+
+  gprint (GP_LOG, "compiled on %s %s\n", __DATE__, __TIME__);
+  return (TRUE);
+}
Index: /branches/sc_branches/pantasks_condor/pclient/ChildOps.c
===================================================================
--- /branches/sc_branches/pantasks_condor/pclient/ChildOps.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/pclient/ChildOps.c	(revision 34783)
@@ -0,0 +1,144 @@
+# include "pclient.h"
+# include <sys/ioctl.h>
+# include <sys/types.h>
+# include <unistd.h>
+// #include <stropts.h>
+
+static int Nbad = 0;
+static struct timeval last = {0, 0};
+
+int InitChild () {
+
+  ChildPID         = 0;                /** current child PID **/
+  ChildStatus      = PCLIENT_NONE;     /** current status of child **/
+  ChildExitStatus  = 0;                /** recent exit status of child **/
+  
+  child_stdin_fd[0]  = 0;
+  child_stdin_fd[1]  = 0;
+  child_stdout_fd[0] = 0;
+  child_stdout_fd[1] = 0;
+  child_stderr_fd[0] = 0;
+  child_stderr_fd[1] = 0;
+
+  InitIOBuffer (&child_stdout, 1024);
+  InitIOBuffer (&child_stderr, 1024);
+  return (TRUE);
+}
+
+int FreeChild () {
+
+  FreeIOBuffer (&child_stdout);
+  FreeIOBuffer (&child_stderr);
+  return (TRUE);
+}
+
+int CheckChild () {
+
+  int Nread;
+  double dtime;
+  struct timeval now;
+
+  /* runaway test - if pcontrol is killed, pclient starts running away.  this test is a bit
+     dangerous: the choice of dtime probably depends on the processor and the value provided to
+     pclient.c:rl_set_keyboard_input_timeout (1000); note that we cannot use getppid == 1 as a test
+     because the parent of pclient is the ssh process on the pclient host, not pcontrol.  in any
+     case, the opihi shell catches if the ssh dies using getppid
+   */
+  gettimeofday (&now, (void *) NULL);
+  dtime = 1e6*DTIME (now, last);
+  if (dtime < 50) {
+    Nbad ++;
+    if (Nbad > 100) {
+      abort ();
+    }
+  }
+  if (dtime > 950) Nbad = 0;
+  last = now;
+
+  CheckChildStatus ();
+  
+  /* read stdout buffer */
+  Nread = ReadtoIOBuffer (&child_stdout, child_stdout_fd[0]);
+  switch (Nread) {
+    case -2:  /* error in read (programming error?  system level error?) */
+      abort ();
+    case -1:  /* no data in pipe */
+      break;
+    case 0:   /* pipe is closed */
+      /** change child state? **/
+      pipe_signal_clear();
+      break;
+    default:  /* data in pipe */
+      break;
+  }
+  
+  /* read stderr buffer */
+  Nread = ReadtoIOBuffer (&child_stderr, child_stderr_fd[0]);
+  switch (Nread) {
+    case -2:  /* error in read (programming error?  system level error?) */
+      abort ();
+    case -1:  /* no data in pipe */
+      break;
+    case 0:   /* pipe is closed */
+      /** change child state? **/
+      pipe_signal_clear();
+      break;
+    default:  /* data in pipe */
+      break;
+  }
+  return (TRUE);
+}
+
+void CheckChildStatus () {
+
+  int result, waitstatus;
+
+  if (ChildStatus != PCLIENT_BUSY) return;
+
+  /* check current child status */
+  result = waitpid (ChildPID, &waitstatus, WNOHANG);
+  switch (result) {
+    case -1:  /* error with waitpid */
+      switch (errno) {
+	case ECHILD:
+	  gprint (GP_ERR, "unknown PID, not a child proc\n");
+	  gprint (GP_ERR, "did process already exit?  programming error?\n");
+	  ChildStatus = PCLIENT_NONE;
+	  break;
+	case EINVAL:
+	  gprint (GP_ERR, "error EINVAL (waitpid): programming error\n");
+	  exit (1);
+	case EINTR:
+	  gprint (GP_ERR, "error EINTR (waitpid): programming error\n");
+	  exit (1);
+	default:
+	  gprint (GP_ERR, "unknown error for waitpid (%d): programming error\n", errno);
+	  exit (1);
+      }
+      break;
+      
+    case 0:  /* process not exited */
+      ChildStatus = PCLIENT_BUSY;
+      break;
+
+    default:
+      if (result != ChildPID) {
+	gprint (GP_ERR, "waitpid error: mis-matched PID (%d vs %d).  programming error\n", result, ChildPID);
+	exit (1);
+      }
+      
+      if (WIFEXITED(waitstatus)) {
+	ChildStatus = PCLIENT_EXIT;
+	ChildExitStatus = WEXITSTATUS(waitstatus);
+      }
+      if (WIFSIGNALED(waitstatus)) {
+	ChildStatus = PCLIENT_CRASH;
+	ChildExitStatus = WTERMSIG(waitstatus);
+      }
+      if (WIFSTOPPED(waitstatus)) {
+	gprint (GP_ERR, "waitpid returns 'stopped': programming error\n");
+	exit (1);
+      }
+  }
+  return;
+}
Index: /branches/sc_branches/pantasks_condor/pclient/Makefile
===================================================================
--- /branches/sc_branches/pantasks_condor/pclient/Makefile	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/pclient/Makefile	(revision 34783)
@@ -0,0 +1,46 @@
+default: pclient
+
+include ../../../Makefile.System
+HOME    =       $(ROOT)/src/opihi
+BIN     =       $(HOME)/bin
+LIB     =       $(HOME)/lib
+INC     =       $(HOME)/include
+SDIR    =       $(HOME)/pclient
+DATA    =       $(DESTDATA)/pclient
+include ../Makefile.Common
+
+# programs may add their own internal requirements here
+LIBS1         = -lbasiccmd -lshell -ldata 
+LIBS2         = -ldvo -lkapa -lFITS -lohana
+FULL_CFLAGS   = $(BASE_CFLAGS)
+FULL_CPPFLAGS = $(BASE_CPPFLAGS)
+FULL_LDFLAGS  = $(LIBS1) $(LIBS2) $(BASE_LDFLAGS)
+
+# pclient user commands and support functions ########################
+funcs = \
+$(SDIR)/init.$(ARCH).o \
+$(SDIR)/pclient.$(ARCH).o \
+$(SDIR)/ChildOps.$(ARCH).o \
+
+cmds = \
+$(SDIR)/job.$(ARCH).o \
+$(SDIR)/reset.$(ARCH).o \
+$(SDIR)/check.$(ARCH).o \
+$(SDIR)/status.$(ARCH).o \
+$(SDIR)/stdout.$(ARCH).o  \
+$(SDIR)/version.$(ARCH).o
+
+libs = \
+$(DESTLIB)/libbasiccmd.a \
+$(DESTLIB)/libshell.a \
+$(DESTLIB)/libdata.a
+
+pclient: $(BIN)/pclient.$(ARCH)
+$(SRC)/pclient.$(ARCH).o : $(libs)
+$(BIN)/pclient.$(ARCH)   : $(funcs) $(cmds)
+
+install: $(DESTBIN)/pclient help
+
+help: clean-help cmd.basic.help pclient.help
+
+.PHONY: pclient
Index: /branches/sc_branches/pantasks_condor/pclient/check.c
===================================================================
--- /branches/sc_branches/pantasks_condor/pclient/check.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/pclient/check.c	(revision 34783)
@@ -0,0 +1,9 @@
+# include "pclient.h"
+
+int check (int argc, char **argv) {
+
+  /* force a check */
+  CheckChild ();
+  return (TRUE);
+
+}
Index: /branches/sc_branches/pantasks_condor/pclient/help/job
===================================================================
--- /branches/sc_branches/pantasks_condor/pclient/help/job	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/pclient/help/job	(revision 34783)
@@ -0,0 +1,1 @@
+0
Index: /branches/sc_branches/pantasks_condor/pclient/init.c
===================================================================
--- /branches/sc_branches/pantasks_condor/pclient/init.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/pclient/init.c	(revision 34783)
@@ -0,0 +1,33 @@
+# include "pclient.h"
+
+int job	            PROTO((int, char **));
+int reset           PROTO((int, char **));
+int check           PROTO((int, char **));
+int status	    PROTO((int, char **));
+int stdout_pclient  PROTO((int, char **));
+int stderr_pclient  PROTO((int, char **));
+int version         PROTO((int, char **));
+
+static Command cmds[] = {  
+  {1, "job",       job,      "start job"},
+  {1, "reset",     reset,    "reset job"},
+  {1, "check",     check,    "check job"},
+  {1, "status",    status,   "check job status"},
+  {1, "stdout",    stdout_pclient,   "get stdout buffer"},
+  {1, "stderr",    stderr_pclient,   "get stderr buffer"},
+  {1, "version",   version,      "show version information"},
+}; 
+
+void InitPclient () {
+  
+  int i;
+
+  for (i = 0; i < sizeof (cmds) / sizeof (Command); i++) {
+    AddCommand (&cmds[i]);
+  }
+  InitChild ();
+}
+
+void FreePclient () {
+  FreeChild ();
+}
Index: /branches/sc_branches/pantasks_condor/pclient/job.c
===================================================================
--- /branches/sc_branches/pantasks_condor/pclient/job.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/pclient/job.c	(revision 34783)
@@ -0,0 +1,115 @@
+# include "pclient.h"
+
+int job (int argc, char **argv) {
+
+  int i, N, pid, status, priority;
+  char **targv;
+
+  priority = 0;
+  if ((N = get_argument (argc, argv, "-nice"))) {
+    remove_argument (N, &argc, argv);
+    priority = atoi (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  if (argc < 2) {
+    gprint (GP_ERR, "USAGE: job (arg0) (arg1) ... (argN)\n");
+    gprint (GP_LOG, "PCLIENT_PID: %d\n", -2);
+    return (FALSE);
+  }
+  
+  if (ChildStatus != PCLIENT_NONE) {
+    gprint (GP_ERR, "need to clear existing child\n");
+    gprint (GP_LOG, "PCLIENT_PID: %d\n", -3);
+    return (FALSE);
+  }
+
+  if (pipe (child_stdin_fd)  < 0) goto pipe_error;
+  if (pipe (child_stdout_fd) < 0) goto pipe_error;
+  if (pipe (child_stderr_fd) < 0) goto pipe_error;
+
+  /* need to define arg list with NULL termination */
+  ALLOCATE (targv, char *, argc);
+  for (i = 1; i < argc; i++) {
+    targv[i-1] = strcreate (argv[i]);
+  }
+  targv[i-1] = 0;
+
+  pid = fork ();
+  if (!pid) { /* must be child process */
+    /* gprint (GP_ERR, "starting child process %s...\n", targv[0]); */
+
+    /* close the other ends of the pipes */
+    close (child_stdin_fd[1]);
+    close (child_stdout_fd[0]);
+    close (child_stderr_fd[0]);
+
+    /* tie our ends of the pipes to stdin, stdout, stderr */
+    dup2 (child_stdin_fd[0],  STDIN_FILENO);
+    dup2 (child_stdout_fd[1], STDOUT_FILENO);
+    dup2 (child_stderr_fd[1], STDERR_FILENO);
+
+    /* set all three unblocking */
+    setvbuf (stdin,  (char *) NULL, _IONBF, BUFSIZ);
+    setvbuf (stdout, (char *) NULL, _IONBF, BUFSIZ);
+    setvbuf (stderr, (char *) NULL, _IONBF, BUFSIZ);
+
+    /* exec job */ 
+    status = execvp (targv[0], targv); 
+    gprint (GP_ERR, "error starting child process: %d\n", status);
+    exit (1);
+  }
+
+  /* set nice level for the child process */
+  if (priority) {
+      status = setpriority (PRIO_PROCESS, pid, priority);
+      if (status == -1) {
+	  gprint (GP_ERR, "error setting priority\n");
+	  perror ("setpriority: ");
+	  exit (2);
+      }
+  }
+
+  /* free temporary arg list */
+  for (i = 0; i < argc - 1; i++) {
+    free (targv[i]);
+  }
+  free (targv);
+
+  /* close the other ends of the pipes */
+  close (child_stdin_fd[0]);
+  close (child_stdout_fd[1]);
+  close (child_stderr_fd[1]);
+
+  /* make the pipes non-blocking */
+  fcntl (child_stdin_fd[1],  F_SETFL, O_NONBLOCK);
+  fcntl (child_stdout_fd[0], F_SETFL, O_NONBLOCK);
+  fcntl (child_stderr_fd[0], F_SETFL, O_NONBLOCK);
+  
+  ChildStatus = PCLIENT_BUSY;
+  ChildPID = pid;
+
+  gprint (GP_LOG, "PCLIENT_PID: %d\n", ChildPID);
+  return (TRUE);
+
+pipe_error:
+  perror ("pipe error:");
+  if (child_stdin_fd[0]  != 0) close (child_stdin_fd[0]);
+  if (child_stdin_fd[1]  != 0) close (child_stdin_fd[1]);
+  if (child_stdout_fd[0] != 0) close (child_stdout_fd[0]);
+  if (child_stdout_fd[1] != 0) close (child_stdout_fd[1]);
+  if (child_stderr_fd[0] != 0) close (child_stderr_fd[0]);
+  if (child_stderr_fd[1] != 0) close (child_stderr_fd[1]);
+
+  gprint (GP_LOG, "PCLIENT_PID: %d\n", -1);
+  return (FALSE);
+}
+
+/* possible responses:
+
+PCLIENT_PID: -1 - pipe error
+PCLIENT_PID: -2 - syntax error
+PCLIENT_PID: -3 - existing child
+PCLIENT_PID: >0 - success (PID)
+
+*/
Index: /branches/sc_branches/pantasks_condor/pclient/notes.txt
===================================================================
--- /branches/sc_branches/pantasks_condor/pclient/notes.txt	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/pclient/notes.txt	(revision 34783)
@@ -0,0 +1,17 @@
+
+pclient special commands:
+
+ job 
+
+ reset
+
+ check
+
+ status
+
+ stdout
+
+ stderr
+
+- communication with pclient should search for the pclient prompt:
+  pclient:
Index: /branches/sc_branches/pantasks_condor/pclient/outline.txt
===================================================================
--- /branches/sc_branches/pantasks_condor/pclient/outline.txt	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/pclient/outline.txt	(revision 34783)
@@ -0,0 +1,12 @@
+
+init stage:
+
+  - set child 
+
+readline loop:
+
+  - wait for commands
+
+
+
+  
Index: /branches/sc_branches/pantasks_condor/pclient/pclient.c.in
===================================================================
--- /branches/sc_branches/pantasks_condor/pclient/pclient.c.in	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/pclient/pclient.c.in	(revision 34783)
@@ -0,0 +1,105 @@
+# include "pclient.h"
+
+# define opihi_name "PCLIENT"
+# define opihi_prompt "pclient: "
+# define opihi_description "pcontrol client shell\n"
+# define opihi_history ""
+# define opihi_rcfile ".pcontrolrc"
+
+void dummy_prep_term (int flag) {
+}
+
+/* program-dependent initialization */
+void program_init (int *argc, char **argv) {
+  
+  auto_break = TRUE;
+
+  /* load the commands used by this implementation */
+  InitBasic ();
+  InitPclient ();
+
+  rl_prep_term_function = dummy_prep_term;
+  rl_readline_name = opihi_name;
+  rl_attempted_completion_function = command_completer;
+  rl_event_hook = CheckChild;
+  rl_set_keyboard_input_timeout (20000); 
+  /* 1 ms seems to be the minimum valid number */
+
+  // set_str_variable ("HISTORY", opihi_history);
+  set_str_variable ("PROMPT", opihi_prompt);
+  set_str_variable ("RCFILE", opihi_rcfile);
+
+  {
+    char *helpdir;
+    char *modules;
+    static char *datadir = "@DATADIR@";
+    ALLOCATE (helpdir, char, strlen(datadir) + strlen("/help") + 2);
+    sprintf (helpdir, "%s/help", datadir);
+    set_str_variable ("HELPDIR", helpdir);
+    free (helpdir);
+    ALLOCATE (modules, char, strlen(datadir) + strlen("/modules") + 2);
+    sprintf (modules, "%s/modules", datadir);
+    set_str_variable ("MODULES:0", modules);
+    set_int_variable ("MODULES:n", 1);
+    free (modules);
+  }
+
+  /* ignore the history file.  to change this, see, eg, mana.c */
+
+  /* We do not want pclient to exit just because the connection to the 
+     child process is broken.  However, if the connection to the parent 
+     process is broken, we should exit.  To handle this, we define 
+     pipe_signal() which tracks the number of times it has been called.
+     Every time an I/O operation on the child file descriptor raises this signal, 
+     we need to manage the crashed child and then clear the count.  Signals raised by 
+     I/O to the parent do not result in clearing the count.  If the count passes a maximum,
+     we exit pclient */
+     
+  signal (SIGPIPE, pipe_signal);
+  signal (SIGTSTP, gotsignal);
+  signal (SIGTTIN, gotsignal);
+  return;
+}
+
+/* standard welcome message */
+void welcome () {
+  gprint (GP_ERR, "\n");
+  gprint (GP_ERR, "Welcome to %s - %s\n\n", opihi_name, opihi_description);
+}
+
+/* add program-dependent exit functions here */
+void cleanup () {
+  ConfigFree ();
+  FreeBasic ();
+  FreePclient ();
+  gprint (GP_LOG, "Goodbye\n");
+  return;
+}
+
+static int Npipe = 0;
+
+void pipe_signal (int signum) {
+  Npipe ++;
+  if (Npipe > 100) {
+    exit (5);
+  }
+  gprint (GP_ERR, "closed pipe : did child process exit unexpectedly?\n");
+  return;
+}
+
+void pipe_signal_clear(void) {
+  Npipe = 0;
+}
+
+void gotsignal (int signum) {
+  gprint (GP_ERR, "got signal : %d\n", signum);
+  return;
+}
+
+/* call to opihi shell */
+int main (int argc, char **argv) {
+  int status;
+  status = opihi (argc, argv);
+  exit (status);
+}
+
Index: /branches/sc_branches/pantasks_condor/pclient/reset.c
===================================================================
--- /branches/sc_branches/pantasks_condor/pclient/reset.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/pclient/reset.c	(revision 34783)
@@ -0,0 +1,75 @@
+# include "pclient.h"
+
+int reset (int argc, char **argv) {
+
+  int i, result, waitstatus;
+  struct timespec request, remain;
+
+  if (argc != 1) {
+    gprint (GP_ERR, "USAGE: reset\n");
+    gprint (GP_LOG, "RESET_RESULT: -1\n");
+    return (FALSE);
+  }
+  
+  if (ChildStatus == PCLIENT_NONE) {
+    gprint (GP_ERR, "no child process, cannot reset\n");
+    gprint (GP_LOG, "RESET_RESULT: 2\n");
+    return (TRUE);
+  }
+
+  /* avoid blocking on waitpid, test every 100 usec, up to 50 msec */
+  request.tv_sec = 0;
+  request.tv_nsec = 100000;
+
+  if (ChildStatus == PCLIENT_BUSY) {
+    /* job is still running, send SIGTERM signal to job */
+    kill (ChildPID, SIGTERM);
+    result = waitpid (ChildPID, &waitstatus, WNOHANG);
+    for (i = 0; (i < 500) && (result == 0); i++) {
+      nanosleep (&request, &remain);
+      result = waitpid (ChildPID, &waitstatus, WNOHANG);
+    }
+    if (result) goto reset_job;
+
+    /* job did not exit, send SIGKILL signal to job */
+    kill (ChildPID, SIGKILL);
+    result = waitpid (ChildPID, &waitstatus, WNOHANG);
+    for (i = 0; (i < 500) && (result == 0); i++) {
+      nanosleep (&request, &remain);
+      result = waitpid (ChildPID, &waitstatus, WNOHANG);
+    }
+    if (result) goto reset_job;
+
+    /* total failure, don't reset */
+    gprint (GP_ERR, "child process %d is hung, cannot reset\n", ChildPID);
+    gprint (GP_LOG, "RESET_RESULT: 0\n");
+    return (FALSE);
+  }
+
+reset_job:
+  /* close out ends of the pipes */
+  close (child_stdin_fd[1]);
+  close (child_stdout_fd[0]);
+  close (child_stderr_fd[0]);
+  child_stdin_fd[1] = 0;
+  child_stdout_fd[0] = 0;
+  child_stderr_fd[0] = 0;
+
+  FlushIOBuffer (&child_stdout);
+  FlushIOBuffer (&child_stderr);
+  ChildStatus = PCLIENT_NONE;
+  ChildPID = 0;
+  ChildExitStatus = 0;
+
+  gprint (GP_LOG, "RESET_RESULT: 1\n");
+  return (TRUE);
+}
+
+/* exit conditions:
+   -1 - usage message / syntax error
+    2 - unknown job
+    0 - process hung
+    1 - successful resetn
+*/
+
+/* the linux kernel timer sticks in a 10ms lag between kill and the harvest */
Index: /branches/sc_branches/pantasks_condor/pclient/status.c
===================================================================
--- /branches/sc_branches/pantasks_condor/pclient/status.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/pclient/status.c	(revision 34783)
@@ -0,0 +1,24 @@
+# include "pclient.h"
+
+int status (int argc, char **argv) {
+
+  char status_string[64];
+
+  if (argc != 1) {
+    gprint (GP_ERR, "USAGE: status\n");
+    return (FALSE);
+  }
+  
+  if (ChildStatus == PCLIENT_NONE)  strcpy (status_string, "NONE");
+  if (ChildStatus == PCLIENT_BUSY)  strcpy (status_string, "BUSY");
+  if (ChildStatus == PCLIENT_EXIT)  strcpy (status_string, "EXIT");
+  if (ChildStatus == PCLIENT_CRASH) strcpy (status_string, "CRASH");
+
+  gprint (GP_LOG, "STATUS %s\n", status_string);
+  gprint (GP_LOG, "EXITST %d\n", ChildExitStatus);
+  gprint (GP_LOG, "STDOUT %d\n", child_stdout.Nbuffer);
+  gprint (GP_LOG, "STDERR %d\n", child_stderr.Nbuffer);
+
+  set_str_variable ("JOBSTATUS", status_string);
+  return (TRUE);
+}
Index: /branches/sc_branches/pantasks_condor/pclient/stdout.c
===================================================================
--- /branches/sc_branches/pantasks_condor/pclient/stdout.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/pclient/stdout.c	(revision 34783)
@@ -0,0 +1,29 @@
+# include "pclient.h"
+
+int stdout_pclient (int argc, char **argv) {
+
+  if (argc != 1) {
+    gprint (GP_ERR, "USAGE: stdout\n");
+    gprint (GP_LOG, "STATUS %d\n", -1);
+    return (FALSE);
+  }
+  
+  fwrite (child_stdout.buffer, 1, child_stdout.Nbuffer, stdout);
+  gprint (GP_LOG, "STATUS %d\n", 0);
+  return (TRUE);
+
+}
+
+int stderr_pclient (int argc, char **argv) {
+
+  if (argc != 1) {
+    gprint (GP_ERR, "USAGE: stderr\n");
+    gprint (GP_LOG, "STATUS %d\n", -1);
+    return (FALSE);
+  }
+  
+  fwrite (child_stderr.buffer, 1, child_stderr.Nbuffer, stdout);
+  gprint (GP_LOG, "STATUS %d\n", 0);
+  return (TRUE);
+
+}
Index: /branches/sc_branches/pantasks_condor/pclient/test/status.sh
===================================================================
--- /branches/sc_branches/pantasks_condor/pclient/test/status.sh	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/pclient/test/status.sh	(revision 34783)
@@ -0,0 +1,64 @@
+
+list tests
+ test1
+ test2
+end
+
+# check status without any jobs
+macro test1
+ $PASS = 1
+ break -auto off
+
+ output -err /dev/null
+ reset
+ exec rm -f test.txt
+ output -err stderr
+
+ output test.txt
+ status
+ output stdout
+
+ # this test requires a specific set of output
+ list line -x "cat test.txt"
+ if ($line:n != 4)
+   $PASS = 0
+ end
+ if ("$line:0" != "STATUS NONE")
+   $PASS = 0
+ end
+ if ("$line:1" != "EXITST 0")
+   $PASS = 0
+ end
+ if ("$line:2" != "STDOUT 0")
+   $PASS = 0
+ end
+ if ("$line:3" != "STDERR 0")
+   $PASS = 0
+ end
+end
+
+# check status with a basic job
+macro test2
+ $PASS = 1
+ break -auto off
+
+ output -err /dev/null
+ reset
+ exec rm -f test.txt
+ job ls 
+ usleep 500000
+ output -err stderr
+
+ output test.txt
+ status
+ output stdout
+
+ # this test requires a specific set of output
+ list line -x "cat test.txt"
+ if ($line:n != 4)
+   $PASS = 0
+ end
+ if ("$line:0" != "STATUS EXIT")
+   $PASS = 0
+ end
+end
Index: /branches/sc_branches/pantasks_condor/pclient/test/version.sh
===================================================================
--- /branches/sc_branches/pantasks_condor/pclient/test/version.sh	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/pclient/test/version.sh	(revision 34783)
@@ -0,0 +1,106 @@
+
+list tests
+ test1
+ test2
+ test3
+ test4
+ test5
+ test6
+ test7
+end
+
+## these tests check that the version command reports the versions
+## they do not validate the actual versions themselves
+
+# set up the version output file
+macro test1
+ $PASS = 1
+ break -auto off
+ exec rm test.txt
+ output -err test.txt
+ version
+ output -err stderr
+ file test.txt found
+ if ($found != 1)
+   $PASS = 0
+ end
+end
+
+# test for the correct number of version lines
+macro test2
+ $PASS = 1
+ break -auto off
+ 
+ $line = `wc -l test.txt`
+
+ list word -split $line
+ if ($word:0 != 6)
+   $PASS = 0
+ end
+end
+
+# test for pclient version name
+macro test3
+ $PASS = 1
+ break -auto off
+ 
+ list line -x "cat test.txt"
+ list word -split $line:0
+
+ if ("$word:0" != "pclient")
+   $PASS = 0
+ end
+end
+
+# test for opihi version name
+macro test4
+ $PASS = 1
+ break -auto off
+ 
+ list line -x "cat test.txt"
+ list word -split $line:1
+
+ if ("$word:0" != "opihi")
+   $PASS = 0
+ end
+end
+
+# test for ohana version name
+macro test5
+ $PASS = 1
+ break -auto off
+ 
+ list line -x "cat test.txt"
+ list word -split $line:2
+
+ if ("$word:0" != "ohana")
+   $PASS = 0
+ end
+end
+
+# test for gfits version name
+macro test6
+ $PASS = 1
+ break -auto off
+ 
+ list line -x "cat test.txt"
+ list word -split $line:3
+
+ if ("$word:0" != "gfits")
+   $PASS = 0
+ end
+end
+
+# test for compilation date/time
+macro test7
+ $PASS = 1
+ break -auto off
+ 
+ list line -x "cat test.txt"
+ list word -split $line:4
+
+ if ("$word:0" != "compiled")
+   $PASS = 0
+ end
+end
+
Index: /branches/sc_branches/pantasks_condor/pclient/version.c
===================================================================
--- /branches/sc_branches/pantasks_condor/pclient/version.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/pclient/version.c	(revision 34783)
@@ -0,0 +1,17 @@
+# include "pclient.h"
+static char *name = "$Name: not supported by cvs2svn $";
+
+int version (int argc, char **argv) {
+
+  char *tmp;
+
+  gprint (GP_LOG, "\n");
+  gprint (GP_LOG, "pclient version: %s\n", (tmp = strip_version (name))); free (tmp);
+
+  gprint (GP_LOG, "opihi version: %s\n", (tmp = strip_version (opihi_version()))); free (tmp);
+  gprint (GP_LOG, "ohana version: %s\n", (tmp = strip_version (ohana_version()))); free (tmp);
+  gprint (GP_LOG, "gfits version: %s\n", (tmp = strip_version (gfits_version()))); free (tmp);
+
+  gprint (GP_LOG, "compiled on %s %s\n", __DATE__, __TIME__);
+  return (TRUE);
+}
Index: /branches/sc_branches/pantasks_condor/pcontrol/CheckBusyJob.c
===================================================================
--- /branches/sc_branches/pantasks_condor/pcontrol/CheckBusyJob.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/pcontrol/CheckBusyJob.c	(revision 34783)
@@ -0,0 +1,113 @@
+# include "pcontrol.h"
+# define DEBUG 0
+
+int CheckBusyJob (Job *job, Host *host) {
+
+  int status;
+
+  /* we are checking a job which is currently busy.  it has been pulled from the
+     JOB_BUSY stack, and is linked to a host in the HOST_BUSY stack.  
+     XXX need to check on state of HOST on return */
+
+  ASSERT (job, "job not set");
+  ASSERT (host, "host not set");
+  ASSERT (host == (Host *) job[0].host, "invalid host");
+  ASSERT (job  == (Job *) host[0].job, "invalid job");
+
+  status = PclientCommand (host, "status", PCLIENT_PROMPT, PCONTROL_RESP_CHECK_BUSY_JOB);
+
+  /* check on success of pclient command */
+  switch (status) {
+    case PCLIENT_DOWN:
+      // free the realhost name
+      if (job[0].realhost) free (job[0].realhost);
+      job[0].realhost = NULL;
+
+      // unlink host & job
+      if (VerboseMode()) gprint (GP_ERR, "host %s is down\n", host[0].hostname);
+      job[0].host = NULL;
+      host[0].job = NULL;
+      HarvestHost (host[0].pid);
+      PutHost (host, PCONTROL_HOST_DOWN, STACK_BOTTOM);
+      PutJob (job, PCONTROL_JOB_PENDING, STACK_BOTTOM);
+      return (FALSE);
+
+    case PCLIENT_GOOD:
+      if (DEBUG || VerboseMode()) gprint (GP_ERR, "message received (CheckBusyJob)");
+      PutHost (host, PCONTROL_HOST_RESP, STACK_BOTTOM);
+      PutJob (job, PCONTROL_JOB_RESP, STACK_BOTTOM);
+      return (TRUE);
+
+    default:
+      ABORT ("unknown status for pclient command");  
+  }
+}
+
+int CheckBusyJobResponse (Host *host) {
+
+  int      outstate;
+  char    *p;
+  char     string[64];
+  IOBuffer *buffer;
+  Job *job;
+
+  /* job must have assigned host */
+  ASSERT (host, "missing host");
+  ASSERT (host[0].job, "missing job");
+  buffer = &host[0].comms_buffer;
+  job = (Job *) host[0].job;
+  ASSERT (host == (Host *) job[0].host, "invalid host");
+
+  /** host is up, need to parse message **/
+  p = memstr (buffer[0].buffer, "STATUS", buffer[0].Nbuffer);
+  if (p == NULL) {
+      if (DEBUG || VerboseMode()) gprint (GP_ERR, "missing STATUS in response; try again\n");
+      PutHost (host, PCONTROL_HOST_BUSY, STACK_BOTTOM);
+      PutJob (job, PCONTROL_JOB_BUSY, STACK_BOTTOM);
+      return (TRUE);
+  }
+
+  sscanf (p, "%*s %s", string);
+  ASSERT (strcmp(string, "NONE"), "no current job\n");
+
+  /** no status change, return to BUSY stack **/
+  if (!strcmp(string, "BUSY")) {
+    PutHost (host, PCONTROL_HOST_BUSY, STACK_BOTTOM);
+    PutJob (job, PCONTROL_JOB_BUSY, STACK_BOTTOM);
+    return (TRUE);
+  }
+
+  /* exit status better be either EXIT or CRASH */
+  outstate = PCONTROL_JOB_BUSY;
+  if (!strcmp(string, "EXIT")) outstate = PCONTROL_JOB_EXIT;
+  if (!strcmp(string, "CRASH")) outstate = PCONTROL_JOB_CRASH;
+  if (outstate == PCONTROL_JOB_BUSY) {
+    if (DEBUG || VerboseMode()) gprint (GP_ERR, "invalid status response (CheckBusyJobResponse), try again\n");
+    PutHost (host, PCONTROL_HOST_BUSY, STACK_BOTTOM);
+    PutJob (job, PCONTROL_JOB_BUSY, STACK_BOTTOM);
+    return (TRUE);
+  }
+  ASSERT (outstate != PCONTROL_JOB_DONE, "impossible outstate for job");
+
+  /* parse the exit status and sizes of output buffers */
+  p = memstr (buffer[0].buffer, "EXITST", buffer[0].Nbuffer);
+  sscanf (p, "%*s %d", &job[0].exit_status);
+  p = memstr (buffer[0].buffer, "STDOUT", buffer[0].Nbuffer);
+  sscanf (p, "%*s %d", &job[0].stdout_buf.size);
+  p = memstr (buffer[0].buffer, "STDERR", buffer[0].Nbuffer);
+  sscanf (p, "%*s %d", &job[0].stderr_buf.size);
+
+  // XXX runaway job if output too large?
+  if (job[0].stdout_buf.size > 0x1000000) abort();
+  if (job[0].stderr_buf.size > 0x1000000) abort();
+
+  // job has exited : move to DONE stack 
+  // the host is still BUSY until job output is gathered (int CheckDoneJob)
+  // don't unlink job and host yet
+  PutHost (host, PCONTROL_HOST_BUSY, STACK_BOTTOM);
+  ASSERT (job[0].host, "host not set for job");
+  PutJobSetState (job, PCONTROL_JOB_DONE, STACK_BOTTOM, outstate);
+  gettimeofday (&job[0].stop, NULL);
+  job[0].dtime = DTIME(job[0].stop, job[0].start);
+  return (TRUE);
+}
Index: /branches/sc_branches/pantasks_condor/pcontrol/CheckDoneHost.c
===================================================================
--- /branches/sc_branches/pantasks_condor/pcontrol/CheckDoneHost.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/pcontrol/CheckDoneHost.c	(revision 34783)
@@ -0,0 +1,71 @@
+# include "pcontrol.h"
+# define DEBUG 0
+
+int CheckDoneHost (Host *host) {
+  
+  int       status;
+
+  ASSERT (host, "host not set");
+
+  status = PclientCommand (host, "reset", PCLIENT_PROMPT, PCONTROL_RESP_CHECK_DONE_HOST);
+
+  /* check on success of pclient command */
+  switch (status) {
+    case PCLIENT_DOWN:
+      if (DEBUG || VerboseMode()) gprint (GP_ERR, "host %s is down\n", host[0].hostname);
+      HarvestHost (host[0].pid);
+      PutHost (host, PCONTROL_HOST_DOWN, STACK_BOTTOM);
+      return (FALSE);
+      /* DONE host does not have an incomplete job */
+      // XXX do we need to close the connection?
+
+    case PCLIENT_GOOD:
+      if (VerboseMode()) gprint (GP_ERR, "checking done host %s\n", host[0].hostname);  
+      PutHost (host, PCONTROL_HOST_RESP, STACK_BOTTOM);
+      return (TRUE);
+
+    default:
+      ABORT ("unknown status for pclient command");  
+  }
+  ABORT ("should not reach here (CheckDoneHost)"); 
+}
+
+int CheckDoneHostResponse (Host *host) {
+
+  int status;
+  char *p;
+  IOBuffer *buffer;
+
+  /* job must have assigned host */
+  ASSERT (host, "missing host");
+  buffer = &host[0].comms_buffer;
+
+  /** successful command, examine result **/
+  p = memstr (buffer[0].buffer, "RESET_RESULT:", buffer[0].Nbuffer);
+  if (p == NULL) {
+      if (DEBUG || VerboseMode()) gprint (GP_ERR, "missing RESET_RESULT: in response; try again\n");
+      PutHost (host, PCONTROL_HOST_DONE, STACK_BOTTOM);
+      return (FALSE);
+  }
+
+  sscanf (p, "%*s %d", &status);
+  switch (status) {
+    case -1:
+      ABORT ("reset syntax error");
+      
+    case 0:
+      if (DEBUG || VerboseMode()) gprint (GP_ERR, "reset failed\n");
+      PutHost (host, PCONTROL_HOST_DONE, STACK_BOTTOM);
+      return (FALSE);
+      
+    case 1:
+    case 2:
+      if (DEBUG || VerboseMode()) gprint (GP_ERR, "successful reset\n");
+      PutHost (host, PCONTROL_HOST_IDLE, STACK_BOTTOM);
+      return (FALSE);
+
+    default:
+      ABORT ("should not reach here (CheckDoneHost)");
+  }
+  ABORT ("should not reach here (CheckDoneHost)");
+}
Index: /branches/sc_branches/pantasks_condor/pcontrol/CheckDoneJob.c
===================================================================
--- /branches/sc_branches/pantasks_condor/pcontrol/CheckDoneJob.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/pcontrol/CheckDoneJob.c	(revision 34783)
@@ -0,0 +1,73 @@
+# include "pcontrol.h"
+# define DEBUG 0
+
+int CheckDoneJob (Job *job, Host *host) {
+  
+  int status1, status2;
+
+  ASSERT (job, "job not set");
+  ASSERT (host, "host not set");
+
+  ASSERT (host == (Host *) job[0].host, "invalid host");
+  ASSERT (job == (Job *) host[0].job, "invalid job");
+
+  // we have four possible states here:
+  // 1) stdout & stderr not yet requested
+  // 2) stdout requested, not yet completed
+  // 3) stdout completed, stderr not yet requested
+  // 4) stdout completed, stderr requested, stderr not yet completed
+
+  // we can always call this for stdout (if it is done, this is a NOP)
+  status1 = GetJobOutput ("stdout", host, &job[0].stdout_buf);
+
+  // we cannot try stderr until stdout is completed
+  status2 = PCLIENT_HUNG;
+  if (job[0].stdout_buf.completed) {
+      status2 = GetJobOutput ("stderr", host, &job[0].stderr_buf);
+  }
+
+  if ((status1 == PCLIENT_DOWN) || (status2 == PCLIENT_DOWN)) {
+
+    // decrement the machine job-host counters
+    DelMachineJob (host, job);
+
+    // unlink host & job
+    if (DEBUG || VerboseMode()) gprint (GP_ERR, "host %s is down\n", host[0].hostname);
+    job[0].host = NULL;
+    host[0].job = NULL;
+	
+    PutJob (job, PCONTROL_JOB_PENDING, STACK_BOTTOM);
+
+    // clear the response data
+    host[0].response_state = PCONTROL_RESP_NONE;
+    host[0].response = NULL;
+
+    // host has shutdown; harvest the defunct process
+    HarvestHost (host[0].pid);
+    PutHost (host, PCONTROL_HOST_DOWN, STACK_BOTTOM);
+    return (FALSE);
+  }
+
+  // try again if we are still waiting
+  if ((status1 == PCLIENT_HUNG) || (status2 == PCLIENT_HUNG)) {
+    PutHost (host, PCONTROL_HOST_BUSY, STACK_BOTTOM);
+    ASSERT (job[0].host, "host not set for job");
+    PutJobSetState (job, PCONTROL_JOB_DONE, STACK_BOTTOM, job[0].state); // keep the incoming state
+    return (FALSE);
+  }
+
+  // decrement the machine job-host counters
+  DelMachineJob (host, job);
+
+  /* job's state is either EXIT or CRASH (verify?) */
+  // unlink host & job
+  job[0].host = NULL;
+  host[0].job = NULL;
+  PutHost (host, PCONTROL_HOST_DONE, STACK_BOTTOM);
+
+  ASSERT ((job[0].state == PCONTROL_JOB_EXIT) || (job[0].state == PCONTROL_JOB_CRASH), "unexpected job state");
+
+  PutJob (job, job[0].state, STACK_BOTTOM);
+
+  return (TRUE);
+}
Index: /branches/sc_branches/pantasks_condor/pcontrol/CheckHost.c
===================================================================
--- /branches/sc_branches/pantasks_condor/pcontrol/CheckHost.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/pcontrol/CheckHost.c	(revision 34783)
@@ -0,0 +1,49 @@
+# include "pcontrol.h"
+
+// if the host has a job, we skip it (down or crash state will be caught elsewhere)
+// in fact, just touch the IDLE hosts, not the BUSY hosts?
+int CheckHost (Host *host) {
+  
+  int status;
+
+  ASSERT (host, "host not set");
+
+  if (host[0].job != NULL) return (TRUE);
+
+  /* if this host has been marked to be turned off, do that and return */
+  if (host[0].markoff) {
+    host[0].markoff = FALSE;
+    StopHost (host, PCONTROL_HOST_OFF);
+    return (TRUE);
+  }
+
+  // the argument to echo (OK) is the expected response below in CheckHostResponse
+  status = PclientCommand (host, "echo OK", PCLIENT_PROMPT, PCONTROL_RESP_CHECK_HOST);
+
+  switch (status) {
+    case PCLIENT_DOWN:
+      if (VerboseMode()) gprint (GP_ERR, "host %s is down\n", host[0].hostname);
+      HarvestHost (host[0].pid);
+      PutHost (host, PCONTROL_HOST_DOWN, STACK_BOTTOM);
+      return (FALSE);
+      
+    case PCLIENT_GOOD:
+      PutHost (host, PCONTROL_HOST_RESP, STACK_BOTTOM);
+      return (TRUE);
+
+    default:
+      ABORT ("unknown status for pclient command");  
+  }
+  ABORT ("should not reach here (CheckHost)"); 
+}
+
+int CheckHostResponse (Host *host) {
+  
+  /* we only check IDLE hosts without jobs */
+  ASSERT (host, "missing host");
+
+  // XXX check on the value of the response? (OK)
+
+  PutHost (host, PCONTROL_HOST_IDLE, STACK_BOTTOM);
+  return (TRUE);
+}
Index: /branches/sc_branches/pantasks_condor/pcontrol/CheckIdleHost.c
===================================================================
--- /branches/sc_branches/pantasks_condor/pcontrol/CheckIdleHost.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/pcontrol/CheckIdleHost.c	(revision 34783)
@@ -0,0 +1,200 @@
+# include "pcontrol.h"
+
+// The connection to the remote host is only allowed to live for MAX_CONNECT_TIME seconds.
+// We disconnect and reconnect if a remote host has been connected for too long.  This is
+// a (temporary?) work-around for the problem that the remote pclient job tends to grow
+// too large over time.
+
+/****
+     
+     queueing strategy:
+
+     We have a problem when the queue contains many jobs of different processing times.  If we
+     start with an equal number of jobs in two classes, fast and slow, eventually, we will end
+     up with all hosts running the slow jobs and the fast jobs completely blocked.  If J1 takes
+     T1 and J2 takes T2, and jobs have equal probability to land in a slot... ??
+
+     It seems like we should boost the probability of the fast jobs over the slow jobs (of
+     course, the end result of that will be all fast jobs draining and the slow jobs hanging
+     around).  
+
+     There is also a problem related to LAP, in that chip stage has a huge number of tasks in
+     the queue (effectively infinite).  Make the probability of the job being selected
+     proportional to something.  In any case, we need a user-controllable way to change the
+     probability of selection
+
+     As things are implemented below, the selection order depends on the queue order.  The
+     easiest way to modify the probabilities is to re-sort based on something.
+
+ ****/
+
+static float MAX_WANTHOST_WAIT = 10.0;
+static float MAX_CONNECT_TIME = 36000.0;
+
+  /* if this host has been connected for too long, disconnect (will automatically reconnect) */
+int CheckResetHost (Host *host) {
+
+  struct timeval now;
+  float dtime;
+
+  /* if this host has been connected for too long, disconnect (will automatically reconnect) */
+  gettimeofday (&now, (void *) NULL);
+  dtime = DTIME (now, host[0].connect_time);
+  if (dtime > MAX_CONNECT_TIME) {
+      if (VerboseMode()) gprint (GP_ERR, "disconnect from %s\n", host[0].hostname);
+      StopHost (host, PCONTROL_HOST_DOWN);
+      return (TRUE);
+  }
+  return FALSE;
+}
+
+/* the supplied host is not on a stack: it cannot be taken by the other thread */
+int CheckIdleHost (Host *host, int Stage) {
+
+  int i;
+  Stack *stack;
+  Job *job;
+  struct timeval now;
+  float dtime;
+
+  ASSERT (host, "host not set");
+
+  /* if this host has been marked to be turned off, do that and return */
+  if (host[0].markoff) {
+    host[0].markoff = FALSE;
+    StopHost (host, PCONTROL_HOST_OFF);
+    return (TRUE);
+  }
+    
+  /* check if host has been connected for too long */
+  if (CheckResetHost (host)) {
+    return (TRUE);
+  }
+
+  /* search the JOB_PENDING stack for an appropriate job */
+  stack = GetJobStack (PCONTROL_JOB_PENDING);
+  LockStack (stack);
+  
+  /* look for first NEEDHOST matching this host */
+  for (i = 0; (Stage == PCONTROL_JOB_STAGE_NEEDHOST) && (i < stack[0].Nobject); i++) {
+    job = (Job *) stack[0].object[i];
+    if (job[0].mode != PCONTROL_JOB_NEEDHOST) continue;
+    ASSERT (job[0].hostname != NULL, "NEEDHOST hostname missing");
+    if (strcasecmp (job[0].hostname, host[0].hostname)) continue;
+
+    if (!CheckMachineJobs (host, job)) continue;
+
+    /* we have found an appropriate job; link it to the host and send to StartJob */
+    job[0].host = (struct Host *) host;
+    host[0].job = (struct Job *) job;
+
+    // gprint (GP_ERR, "start needhost %s (job host %s) : %s\n", host[0].hostname, job[0].hostname, job[0].argv[0]);
+    AddMachineJob (host, job);
+
+    /* take the job off the stack and unlock the stack */
+    RemoveStackEntry (stack, i);
+    UnlockStack (stack);
+    StartJob (job, host);
+    return (TRUE);
+  }
+
+  /* no NEEDHOST entry, look for first WANTHOST matching this host */
+  for (i = 0; (Stage == PCONTROL_JOB_STAGE_WANTHOST) && (i < stack[0].Nobject); i++) {
+    job = (Job *) stack[0].object[i];
+    if (job[0].mode != PCONTROL_JOB_WANTHOST) continue;
+    ASSERT (job[0].hostname != NULL, "WANTHOST hostname missing");
+    if (strcasecmp (job[0].hostname, host[0].hostname)) continue;
+
+    if (!CheckMachineJobs (host, job)) continue;
+
+    /* we have found an appropriate job; link it to the host and send to StartJob */
+    job[0].host = (struct Host *) host;
+    host[0].job = (struct Job *) job;
+
+    // gprint (GP_ERR, "start wanthost %s (job host %s) : %s\n", host[0].hostname, job[0].hostname, job[0].argv[0]);
+    AddMachineJob (host, job);
+
+    /* take the job off the stack and unlock the stack */
+    RemoveStackEntry (stack, i);
+    UnlockStack (stack);
+    StartJob (job, host);
+    return (TRUE);
+  }
+
+  /* no WANTHOST entry, look for first ANYHOST matching this host */
+  for (i = 0; (Stage == PCONTROL_JOB_STAGE_ANYHOST) && (i < stack[0].Nobject); i++) {
+    job = (Job *) stack[0].object[i];
+    if (job[0].mode != PCONTROL_JOB_ANYHOST) continue;
+
+    if (!CheckMachineJobs (host, job)) continue;
+
+    /* we have found an appropriate job; link it to the host and send to StartJob */
+    job[0].host = (struct Host *) host;
+    host[0].job = (struct Job *) job;
+
+    // gprint (GP_ERR, "start  anyhost %s (job host %s) : %s\n", host[0].hostname, job[0].hostname, job[0].argv[0]);
+    AddMachineJob (host, job);
+
+    /* take the job off the stack and unlock the stack */
+    RemoveStackEntry (stack, i);
+    UnlockStack (stack);
+    StartJob (job, host);
+    return (TRUE);
+  }
+
+  /* no ANYHOST entry, look for first WANTHOST with old time */
+  for (i = 0; (Stage == PCONTROL_JOB_STAGE_OLDWANT) && (i < stack[0].Nobject); i++) {
+    job = (Job *) stack[0].object[i];
+    if (job[0].mode != PCONTROL_JOB_WANTHOST) continue;
+
+    // allow WANT jobs to wait up to 10.0 sec for the host to be free before giving up
+    gettimeofday (&now, (void *) NULL);
+    dtime = DTIME (now, job[0].start);
+    if (dtime < MAX_WANTHOST_WAIT) continue;
+
+    if (!CheckMachineJobs (host, job)) continue;
+
+    gprint (GP_ERR, "start wanthost(2) %s (job host %s) : %s\n", host[0].hostname, job[0].hostname, job[0].argv[0]);
+    AddMachineJob (host, job);
+
+    /* we have found an appropriate job; link it to the host and send to StartJob */
+    job[0].host = (struct Host *) host;
+    host[0].job = (struct Job *) job;
+
+    /* take the job off the stack and unlock the stack */
+    RemoveStackEntry (stack, i);
+    UnlockStack (stack);
+    StartJob (job, host);
+    return (TRUE);
+  }
+  UnlockStack (stack);
+
+  /* no jobs for host, put it back on IDLE stack */
+  PutHost (host, PCONTROL_HOST_IDLE, STACK_BOTTOM);
+  return (TRUE);
+}
+
+void SetMaxWantHostWait (float value) {
+
+  MAX_WANTHOST_WAIT = value;
+  return;
+}
+
+float GetMaxWantHostWait (void) {
+
+  return MAX_WANTHOST_WAIT;
+}
+
+void SetMaxConnectTime (float value) {
+
+  MAX_CONNECT_TIME = value;
+  return;
+}
+
+float GetMaxConnectTime (void) {
+
+  return MAX_CONNECT_TIME;
+}
+
+/** note : host and job popped off IDLE and PENDING stacks, 
+    unless no job is available **/
Index: /branches/sc_branches/pantasks_condor/pcontrol/CheckPoint.c
===================================================================
--- /branches/sc_branches/pantasks_condor/pcontrol/CheckPoint.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/pcontrol/CheckPoint.c	(revision 34783)
@@ -0,0 +1,69 @@
+# include "pcontrol.h"
+
+// one thread (user) interacts with the user and blocks for long periods on input.
+// one thread (client) spins continuously and monitors the hosts and jobs
+// in some cases, the user thread needs to set a check point on the client thread 
+// to ensure the all HOSTs and JOBs are on one of the stacks (nothing 'in flight').
+// these are not symmetric: the client thread should not call Set/Clear, the user 
+// thread should not call Test
+
+# ifdef THREADED
+static pthread_mutex_t client = PTHREAD_MUTEX_INITIALIZER;
+static pthread_mutex_t user = PTHREAD_MUTEX_INITIALIZER;
+# endif
+
+// The user thread calls this to stop the client thread from shuffling the Host/Job stacks
+int SetCheckPoint () {
+
+# ifdef THREADED
+  int status;
+  int Nwait;
+
+  // set my lock
+  pthread_mutex_lock (&user);
+
+  // wait until client thread sets its lock
+  Nwait = 0;
+  while (1) {
+    status = pthread_mutex_trylock (&client);
+    if (status == EBUSY) {
+      // client has reached the check-point
+      return (TRUE);
+    }
+    pthread_mutex_unlock (&client);
+    usleep (10000); // wait for client thread to set lock
+    Nwait ++;
+  }
+  // put in a timeout?  (client thread not spinning...)
+  return (FALSE);
+# else
+  return (TRUE);
+# endif
+}
+
+// The user thread calls this to allow the client thread to continue
+int ClearCheckPoint () {
+  // clear my lock
+# ifdef THREADED
+  pthread_mutex_unlock (&user);
+# endif
+  return (TRUE);
+}
+
+// The client thread calls in the thread loop somewhere the stacks are stable
+// (ie, no jobs or hosts currently in flight)
+int TestCheckPoint () {
+
+# ifdef THREADED
+  // set my lock
+  pthread_mutex_lock (&client);
+  
+  // try the user-thread lock
+  pthread_mutex_lock (&user);
+  pthread_mutex_unlock (&user);
+
+  // clear my lock
+  pthread_mutex_unlock (&client);
+# endif
+  return (TRUE);
+}
Index: /branches/sc_branches/pantasks_condor/pcontrol/CheckRespHost.c
===================================================================
--- /branches/sc_branches/pantasks_condor/pcontrol/CheckRespHost.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/pcontrol/CheckRespHost.c	(revision 34783)
@@ -0,0 +1,112 @@
+# include "pcontrol.h"
+# define DEBUG 0
+
+// this function operates on hosts waiting for a response. we simply check if the message is
+// complete, and if so, send it to the correct parsing function
+int CheckRespHost (Host *host) {
+  
+  int status;
+  Job  *job;
+
+  ASSERT (host, "host not set");
+  job = (Job *) host[0].job;
+
+  status = PclientResponse (host, host[0].response, &host[0].comms_buffer);
+
+  /* check on output from pclient command */
+  switch (status) {
+    case PCLIENT_DOWN:
+      if (DEBUG || VerboseMode()) gprint (GP_ERR, "host %s is down\n", host[0].hostname);
+
+      // not all hosts here have a job; if it does, return it to PENDING
+      if (job) {
+	// unlink host & job
+	if (DEBUG || VerboseMode()) gprint (GP_ERR, "host %s is down\n", host[0].hostname);
+	job[0].host = NULL;
+	host[0].job = NULL;
+	PutJob (job, PCONTROL_JOB_PENDING, STACK_BOTTOM);
+      }
+
+      // clear the response data
+      host[0].response_state = PCONTROL_RESP_NONE;
+      host[0].response = NULL;
+
+      // if want the host to be shutdown, accept the result
+      if (host[0].response_state == PCONTROL_RESP_DOWN_HOST) {
+	if (DEBUG) fprintf (stderr, "PCONTROL_RESP_DOWN_HOST\n");
+	DownHostResponse (host);
+	return TRUE;
+      }	
+      if (host[0].response_state == PCONTROL_RESP_STOP_HOST) {
+	if (DEBUG) fprintf (stderr, "PCONTROL_RESP_STOP_HOST\n");
+	StopHostResponse (host);
+	return TRUE;
+      }
+
+      // host has unexpectedly shutdown; harvest the defunct process
+      HarvestHost (host[0].pid);
+      PutHost (host, PCONTROL_HOST_DOWN, STACK_BOTTOM);
+      return (FALSE);
+
+    case PCLIENT_HUNG:
+      // not done yet; try again later
+      PutHost (host, PCONTROL_HOST_RESP, STACK_BOTTOM);
+      if (job) {
+	PutJob (job, PCONTROL_JOB_RESP, STACK_BOTTOM);
+      }
+      if (DEBUG || VerboseMode()) gprint (GP_ERR, "host %s is not responding\n", host[0].hostname);
+      return (FALSE);
+
+    case PCLIENT_GOOD:
+      break;
+
+    default:
+      ABORT ("unknown status for pclient command");  
+  }
+
+  switch (host[0].response_state) {
+    case PCONTROL_RESP_START_JOB:
+      if (DEBUG) fprintf (stderr, "PCONTROL_RESP_START_JOB\n");
+      status = StartJobResponse (host);
+      break;
+
+    case PCONTROL_RESP_CHECK_HOST:
+      if (DEBUG) fprintf (stderr, "PCONTROL_RESP_CHECK_HOST\n");
+      status = CheckHostResponse (host);
+      break;
+
+    case PCONTROL_RESP_CHECK_DONE_HOST:
+      if (DEBUG) fprintf (stderr, "PCONTROL_RESP_CHECK_DONE_HOST\n");
+      status = CheckDoneHostResponse (host);
+      break;
+
+    case PCONTROL_RESP_CHECK_BUSY_JOB:
+      if (DEBUG) fprintf (stderr, "PCONTROL_RESP_BUSY_JOB\n");
+      status = CheckBusyJobResponse (host);
+      break;
+
+    case PCONTROL_RESP_KILL_JOB:
+      if (DEBUG) fprintf (stderr, "PCONTROL_RESP_KILL_JOB\n");
+      status = KillJobResponse (host);
+      break;
+
+    case PCONTROL_RESP_DOWN_HOST:
+      if (DEBUG) fprintf (stderr, "PCONTROL_RESP_DOWN_HOST\n");
+      status = DownHostResponse (host);
+      break;
+
+    case PCONTROL_RESP_STOP_HOST:
+      if (DEBUG) fprintf (stderr, "PCONTROL_RESP_STOP_HOST\n");
+      status = StopHostResponse (host);
+      break;
+
+    default:
+      ABORT ("undefined response state");
+  }
+
+  // we have detected a valid response, clear the response data
+  host[0].response_state = PCONTROL_RESP_NONE;
+  host[0].response = NULL;
+  return (status);
+}      
+
Index: /branches/sc_branches/pantasks_condor/pcontrol/CheckSystem.c
===================================================================
--- /branches/sc_branches/pantasks_condor/pcontrol/CheckSystem.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/pcontrol/CheckSystem.c	(revision 34783)
@@ -0,0 +1,466 @@
+# include "pcontrol.h"
+# define DEBUG 0
+
+static struct timeval lastlive = {0, 0};
+static RunLevels RunLevel = PCONTROL_RUN_NONE;
+
+RunLevels SetRunLevel (RunLevels level) {
+  RunLevels oldlevel;
+  oldlevel = RunLevel;
+  RunLevel = level;
+  return oldlevel;
+}
+
+RunLevels GetRunLevel () {
+  return RunLevel;
+}
+
+int CheckSystem () {
+
+  struct timeval now;
+  float dtime;
+
+  /* we want to give each block a maximum allowed time */
+  CheckIdleHosts(0.015, PCONTROL_JOB_STAGE_NEEDHOST); /* submit a new job */
+  CheckIdleHosts(0.015, PCONTROL_JOB_STAGE_WANTHOST); /* submit a new job */
+  CheckIdleHosts(0.015, PCONTROL_JOB_STAGE_ANYHOST);  /* submit a new job */
+  CheckIdleHosts(0.015, PCONTROL_JOB_STAGE_OLDWANT);  /* submit a new job */
+
+  CheckBusyJobs(0.020);  /* get job status */
+  CheckDoneJobs(0.020);  /* harvest job stdout/stderr */
+  CheckKillJobs(0.020);  /* harvest job stdout/stderr */
+
+  CheckDoneHosts(0.020); /* reset the host */
+  CheckDownHosts(0.100); /* launch the host */
+
+  /* always allow at least one test */
+  /* most tests require about 2ms per host.  
+     CheckDoneJobs must depend on the size of the output buffer */
+
+  gettimeofday (&now, (void *) NULL);
+  dtime = DTIME (now, lastlive);
+  if (dtime > 1.0) {
+    CheckLiveHosts(0.040);
+    lastlive = now;
+  } 
+
+  if (DEBUG) { 
+    Stack *stack;
+    int Nidle, Ndown, Nbusy;
+    stack = GetHostStack (PCONTROL_HOST_IDLE);
+    Nidle = stack[0].Nobject;
+    stack = GetHostStack (PCONTROL_HOST_DOWN);
+    Ndown = stack[0].Nobject;
+    stack = GetHostStack (PCONTROL_HOST_BUSY);
+    Nbusy = stack[0].Nobject;
+    gprint (GP_ERR, "busy, idle, down: %2d %2d %2d\n", Nbusy, Nidle, Ndown);
+  }
+
+  return (TRUE);
+}
+
+void *CheckSystem_Threaded (void *data) {
+
+  int Njobchecks, Nhostchecks, Ndonejobs;
+
+  gprintInit ();
+
+  while (1) {
+    // stop here if the user-thread requests (no objects in flight) 
+    TestCheckPoint ();
+
+    // don't run the system checks if RunLevel is FALSE
+    // XXX stop should not suspend all checks: we should continue
+    // to harvest completed jobs and migrate idle machines to down
+    if (RunLevel == PCONTROL_RUN_NONE) {
+      usleep (100000); // idle if we are running nothing
+      continue;
+    }
+
+    /* always allow at least one test */
+    /* most tests require about 2ms per host.  
+       CheckDoneJobs must depend on the size of the output buffer */
+    /* the max delay times are fairly arbitrary and do not impact
+       the user interface.
+     */
+
+    Njobchecks = 0;
+    Nhostchecks = 0;
+    Ndonejobs = 0;
+
+    if ((RunLevel == PCONTROL_RUN_ALL) || (RunLevel == PCONTROL_RUN_REAP)) {
+      Njobchecks  += CheckBusyJobs(0.020);  /* get job status (PCLIENT) */
+      TestCheckPoint ();
+      Ndonejobs    = CheckDoneJobs(0.020);  /* harvest job stdout/stderr (!PCLIENT) */
+      Njobchecks  += Ndonejobs;
+      TestCheckPoint ();
+      Njobchecks  += CheckKillJobs(0.020);  /* harvest job stdout/stderr (PCLIENT) */
+      TestCheckPoint ();
+    }
+
+    if (RunLevel != PCONTROL_RUN_NONE) {
+      Nhostchecks += CheckRespHosts(0.020); /* check for incoming messages */
+      TestCheckPoint ();
+      Nhostchecks += CheckDoneHosts(0.020); /* reset the host */
+      TestCheckPoint ();
+      Nhostchecks += CheckDownHosts(0.100); /* launch the host */
+      TestCheckPoint ();
+      CheckZombies(); /* launch the host */
+      TestCheckPoint ();
+    }
+
+    if (RunLevel == PCONTROL_RUN_ALL) {
+      // we want to give each block a maximum allowed time
+      Nhostchecks += CheckIdleHosts(0.015, PCONTROL_JOB_STAGE_NEEDHOST); /* submit a new job (PCLIENT) */
+      Nhostchecks += CheckIdleHosts(0.015, PCONTROL_JOB_STAGE_WANTHOST); /* submit a new job (PCLIENT) */
+      Nhostchecks += CheckIdleHosts(0.015, PCONTROL_JOB_STAGE_ANYHOST); /* submit a new job (PCLIENT) */
+      Nhostchecks += CheckIdleHosts(0.015, PCONTROL_JOB_STAGE_OLDWANT); /* submit a new job (PCLIENT) */
+      TestCheckPoint ();
+    }
+
+    // there is nothing on the stacks.  test the hosts and wait a bit
+    if (!Njobchecks && !Nhostchecks && (RunLevel != PCONTROL_RUN_NONE)) {
+      CheckLiveHosts(0.040);
+      // fprintf (stderr, "sleep a bit\n");
+      usleep (100000); // idle if no jobs are waiting
+    } else {
+      // if we only have busy jobs, pause a moment before trying again
+      if (!Ndonejobs) {
+	// fprintf (stderr, "sleep a bit\n");
+	usleep (100000);
+      }
+    }
+
+    if (DEBUG) { 
+      Stack *stack;
+      int Nidle, Ndown, Nbusy;
+      stack = GetHostStack (PCONTROL_HOST_IDLE);
+      Nidle = stack[0].Nobject;
+      stack = GetHostStack (PCONTROL_HOST_DOWN);
+      Ndown = stack[0].Nobject;
+      stack = GetHostStack (PCONTROL_HOST_BUSY);
+      Nbusy = stack[0].Nobject;
+      gprint (GP_ERR, "Njobchecks: %d, busy, idle, down: %2d %2d %2d\n", Njobchecks, Nbusy, Nidle, Ndown);
+    }
+  }
+  return (NULL);
+}
+
+int CheckBusyJobs (float MaxDelay) {
+
+  struct timeval start, stop;
+  int i, Nobject;
+  Stack *hoststack;
+  Stack *jobstack;
+  Job   *job;
+  Host  *host;
+  float dtime;
+
+  /* Loop through objects on the stack, no more than once.  Note that it is not important if the
+     stack size is modified by other threads or is changed by any of the actions performed during
+     this loop: the Nobject value is only used to get a rough number for the number of iterations.
+   */
+
+  hoststack = GetHostStack (PCONTROL_HOST_BUSY);
+  jobstack  = GetJobStack (PCONTROL_JOB_BUSY);
+  Nobject   = jobstack[0].Nobject;
+
+  /* always allow at least one test */
+  gettimeofday (&start, (void *) NULL);
+  dtime = 0.0;
+  for (i = 0; (i < Nobject) && (dtime < MaxDelay); i++) {
+    // pull both job and host from their stacks
+    LockStack (hoststack);
+    job = PullStackByLocation (jobstack, STACK_TOP);
+    if (job == NULL) {
+      UnlockStack (hoststack);
+      break;
+    }
+    host = (Host *) job[0].host;
+    ASSERT (host != NULL, "host is NULL");
+    RemoveStackByID (hoststack, host[0].HostID);
+    UnlockStack (hoststack);
+
+    CheckBusyJob (job, host);
+    gettimeofday (&stop, (void *) NULL);
+    dtime = DTIME (stop, start);
+  }
+  if (DEBUG && (Nobject > 0)) gprint (GP_ERR, "checked %d of %d jobs\n", i, Nobject);
+  return (i);
+}
+
+int CheckDoneJobs (float MaxDelay) {
+
+  struct timeval start, stop;
+  int i, Nobject;
+  Stack *hoststack;
+  Stack *jobstack;
+  Job   *job;
+  Host  *host;
+  float dtime;
+
+  /* Loop through objects on the stack, no more than once. see note above */
+  hoststack = GetHostStack (PCONTROL_HOST_BUSY);
+  jobstack  = GetJobStack (PCONTROL_JOB_DONE);
+  Nobject   = jobstack[0].Nobject;
+
+  /* always allow at least one test */
+  gettimeofday (&start, (void *) NULL);
+  dtime = 0.0;
+  for (i = 0; (i < Nobject) && (dtime < MaxDelay); i++) {
+    LockStack (hoststack);
+    job = PullStackByLocation (jobstack, STACK_TOP);
+    if (job == NULL) {
+      UnlockStack (hoststack);
+      break;
+    }
+    host = (Host *) job[0].host;
+    ASSERT (host, "host is NULL");
+
+    RemoveStackByID (hoststack, host[0].HostID);
+    UnlockStack (hoststack);
+
+    CheckDoneJob (job, host);
+    gettimeofday (&stop, (void *) NULL);
+    dtime = DTIME (stop, start);
+  }
+  if (DEBUG && (Nobject > 0)) gprint (GP_ERR, "checked %d of %d jobs\n", i, Nobject);
+  return (i);
+}
+
+int CheckKillJobs (float MaxDelay) {
+
+  struct timeval start, stop;
+  int i, Nobject;
+  Stack *hoststack;
+  Stack *jobstack;
+  Job   *job;
+  Host  *host;
+  float dtime;
+
+  /* Loop through objects on the stack, no more than once. see note above */
+  hoststack = GetHostStack (PCONTROL_HOST_BUSY);
+  jobstack = GetJobStack (PCONTROL_JOB_KILL);
+  Nobject = jobstack[0].Nobject;
+
+  /* always allow at least one test */
+  gettimeofday (&start, (void *) NULL);
+  dtime = 0.0;
+  for (i = 0; (i < Nobject) && (dtime < MaxDelay); i++) {
+    LockStack (hoststack);
+    job = PullStackByLocation (jobstack, STACK_TOP);
+    if (job == NULL) {
+      UnlockStack (hoststack);
+      break;
+    }
+    host = (Host *) job[0].host;
+    ASSERT (host, "host is NULL");
+
+    RemoveStackByID (hoststack, host[0].HostID);
+    UnlockStack (hoststack);
+
+    KillJob (job, host);
+    gettimeofday (&stop, (void *) NULL);
+    dtime = DTIME (stop, start);
+  }
+  if (DEBUG && (Nobject > 0)) gprint (GP_ERR, "checked %d of %d jobs\n", i, Nobject);
+  return (i);
+}
+
+int CheckRespHosts (float MaxDelay) {
+
+  struct timeval start, stop;
+  int i, Nobject;
+  Stack *hoststack;
+  Stack *jobstack;
+  Host *host;
+  Job *job;
+  float dtime;
+
+  /* Loop through objects on the stack, no more than once. see note above */
+  hoststack = GetHostStack (PCONTROL_HOST_RESP);
+  jobstack = GetJobStack (PCONTROL_JOB_RESP);
+  Nobject = hoststack[0].Nobject;
+
+  /* always allow at least one test */
+  gettimeofday (&start, (void *) NULL);
+  dtime = 0.0;
+  for (i = 0; (i < Nobject) && (dtime < MaxDelay); i++) {
+    LockStack (jobstack);
+    host = PullStackByLocation (hoststack, STACK_TOP);
+    if (host == NULL) {
+	UnlockStack (jobstack);
+	break;
+    }
+
+    // if the host has a job, we need to pull the job from its stack
+    job = (Job *) host[0].job;
+    if (job != NULL) {
+	RemoveStackByID (jobstack, job[0].JobID);
+    }
+    UnlockStack (jobstack);
+
+    CheckRespHost (host);
+    gettimeofday (&stop, (void *) NULL);
+    dtime = DTIME (stop, start);
+  }
+  if (DEBUG) gprint (GP_ERR, "checked %d hosts\n", i);
+  return (i);
+}
+
+int CheckDoneHosts (float MaxDelay) {
+
+  struct timeval start, stop;
+  int i, Nobject;
+  Stack *stack;
+  Host  *host;
+  float dtime;
+
+  /* Loop through objects on the stack, no more than once. see note above */
+  stack = GetHostStack (PCONTROL_HOST_DONE);
+  Nobject = stack[0].Nobject;
+
+  /* always allow at least one test */
+  gettimeofday (&start, (void *) NULL);
+  dtime = 0.0;
+  for (i = 0; (i < Nobject) && (dtime < MaxDelay); i++) {
+    host = PullStackByLocation (stack, STACK_TOP);
+    if (host == NULL) break;
+    CheckDoneHost (host);
+    gettimeofday (&stop, (void *) NULL);
+    dtime = DTIME (stop, start);
+  }
+  if (DEBUG) gprint (GP_ERR, "checked %d hosts\n", i);
+  return (i);
+}
+
+int CheckDownHosts (float MaxDelay) {
+
+  int i, Nobject;
+  Stack *stack;
+  Host  *host;
+  struct timeval start, stop;
+  float dtime;
+
+  /* Loop through objects on the stack, no more than once. see note above */
+  stack = GetHostStack (PCONTROL_HOST_DOWN);
+  Nobject = stack[0].Nobject;
+
+  /* always allow at least one test */
+  gettimeofday (&start, (void *) NULL);
+  dtime = 0.0;
+  for (i = 0; (i < Nobject) && (dtime < MaxDelay); i++) {
+    host = PullStackByLocation (stack, STACK_TOP);
+    if (host == NULL) break;
+    if (host[0].markoff) {
+      // DOWN -> OFF
+      host[0].markoff = FALSE;
+      OffHost (host);
+      return (TRUE);
+    }
+    dtime = DTIME (host[0].next_start_try, start);
+    if (dtime > 0) {
+      PutHost (host, PCONTROL_HOST_DOWN, STACK_BOTTOM);
+    } else {
+      // DOWN -> IDLE (maybe)
+      // this is a race condition with "host retry", but the only 
+      // consequence is that both StartHost and reset set the times to 0.0
+      StartHost (host);
+    }
+    gettimeofday (&stop, (void *) NULL);
+    dtime = DTIME (stop, start);
+  }
+  if (DEBUG) gprint (GP_ERR, "checked %d hosts\n", i);
+  return (i);
+}
+
+// if we have any IDLE hosts, check if there are jobs to be launched 
+// for each pass, we only check one type of job: stage = NEED, WANT, ANY, OLDWANT
+int CheckIdleHosts (float MaxDelay, int Stage) {
+
+  struct timeval start, stop;
+  int i, Nobject;
+  Stack *stack;
+  Host  *host;
+  float dtime;
+
+  /* check if there are any pending jobs */
+  stack = GetJobStack (PCONTROL_JOB_PENDING);
+
+  /* if there are no pending jobs and we are not in STAGE_NEEDHOST, skip test */
+  if (!stack[0].Nobject && (Stage != PCONTROL_JOB_STAGE_NEEDHOST)) return (0);
+
+  /* if there are no pending jobs, check for hosts that need to be reset */
+  if (!stack[0].Nobject) {
+    /* cycle through IDLE hosts */
+    stack = GetHostStack (PCONTROL_HOST_IDLE);
+    Nobject = stack[0].Nobject;
+    for (i = 0; i < Nobject; i++) {
+      host = PullStackByLocation (stack, STACK_TOP);
+      if (host == NULL) break;
+      if (CheckResetHost (host)) {
+	return (1);
+      }
+      PutHost (host, PCONTROL_HOST_IDLE, STACK_BOTTOM);
+    }
+    return (0);
+  }
+
+  /* Loop through objects on the stack, no more than once. see note above */
+  stack = GetHostStack (PCONTROL_HOST_IDLE);
+  Nobject = stack[0].Nobject;
+
+  /* always allow at least one test */
+  gettimeofday (&start, (void *) NULL);
+  dtime = 0.0;
+  for (i = 0; (i < Nobject) && (dtime < MaxDelay); i++) {
+    host = PullStackByLocation (stack, STACK_TOP);
+    if (host == NULL) break;
+    CheckIdleHost (host, Stage);
+    gettimeofday (&stop, (void *) NULL);
+    dtime = DTIME (stop, start);
+  }
+
+  if (DEBUG) gprint (GP_ERR, "checked %d hosts\n", i);
+  return (i);
+}
+
+/* this is just a heartbeat check (only IDLE hosts) */
+int CheckLiveHosts (float MaxDelay) {
+
+  struct timeval start, stop;
+  int i, Nobject;
+  Stack *stack;
+  Host  *host;
+  float dtime;
+
+  /* Loop through objects on the stack, no more than once. see note above */
+  stack = GetHostStack (PCONTROL_HOST_IDLE);
+  Nobject = stack[0].Nobject;
+
+  gettimeofday (&start, (void *) NULL);
+
+  dtime = 0.0;
+  for (i = 0; (i < Nobject) && (dtime < MaxDelay); i++) {
+    host = PullStackByLocation (stack, STACK_TOP);
+    if (host == NULL) break;
+    CheckHost (host);
+    gettimeofday (&stop, (void *) NULL);
+    dtime = DTIME (stop, start);
+  }
+  if (DEBUG) gprint (GP_ERR, "checked %d idle hosts\n", i);
+  return (TRUE);
+}
+
+/*
+
+  gettimeofday (&stop, (void *) NULL);
+  dtime = DTIME (stop, start);
+  if (VerboseMode()) gprint (GP_ERR, "check 4: %f seconds\n", dtime);
+
+  gettimeofday (&start, (void *) NULL);
+*/
+
+/** All of the CheckFooBar entries cycle though their respective queues, popping from the top and
+    pushing to the bottom.  if we stop before the loop is done there is no tendancy for bias because
+    we continue where we left off next round **/
Index: /branches/sc_branches/pantasks_condor/pcontrol/GetJobOutput.c
===================================================================
--- /branches/sc_branches/pantasks_condor/pcontrol/GetJobOutput.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/pcontrol/GetJobOutput.c	(revision 34783)
@@ -0,0 +1,72 @@
+# include "pcontrol.h"
+# define PCLIENT_TIMEOUT 100
+# define DEBUG 0
+
+// we are trying to read a total of Nbytes from the host.  This function may be called
+// repeatedly until the buffer has the complete set of data.  We need to read output[0].size
+// bytes, then look for the PCLIENT_PROMPT in the output stream
+
+int GetJobOutput (char *command, Host *host, JobOutput *output) {
+  
+  char *line;
+  int i, status;
+  struct timespec request, remain;
+
+  ASSERT (command, "command missing");
+  ASSERT (host, "host missing");
+  ASSERT (output, "output missing");
+
+  /* avoid blocking on waitpid, test every 100 usec, up to 10 msec */
+  request.tv_sec = 0;
+  request.tv_nsec = 100000;
+
+  if (!output[0].requested) {
+      /* send command (stdout / stderr) */
+      status = write_fmt (host[0].stdin_fd, "%s\n", command);
+
+      /* is pipe still open? */
+      if ((status == -1) && (errno == EPIPE)) return PCLIENT_DOWN;
+      output[0].requested = TRUE;
+  }
+
+  if (output[0].completed) return PCLIENT_GOOD;
+
+  // attempt to read the output->size bytes from the host 
+  if (output[0].buffer.Nbuffer < output[0].size) {
+      status = -1;
+      for (i = 0; (i < PCLIENT_TIMEOUT) && (status != 0) && (output[0].buffer.Nbuffer < output[0].size); i++) {
+	  status = ReadtoIOBuffer (&output[0].buffer, host[0].stdout_fd);
+	  if (status == -1) nanosleep (&request, &remain);
+      }
+      if (VerboseMode()) gprint (GP_ERR, "%s\n Read %d of %d bytes so far\n", output[0].buffer.buffer, output[0].buffer.Nbuffer, output[0].size);
+      if (status == 0) {
+	  if (VerboseMode()) gprint (GP_ERR, "host %s is down\n", host[0].hostname);
+	  return PCLIENT_DOWN;
+      }
+      if (output[0].buffer.Nbuffer < output[0].size) {
+	  if (VerboseMode()) gprint (GP_ERR, "host %s still has data, keep trying\n", host[0].hostname);
+	  return PCLIENT_HUNG;
+      }
+  }
+
+  // keep trying to read until we get the prompt
+  line = NULL;
+  status = -1;
+  for (i = 0; (i < PCLIENT_TIMEOUT) && (status != 0) && (line == NULL); i++) {
+    status = ReadtoIOBuffer (&output[0].buffer, host[0].stdout_fd);
+    line = memstr (output[0].buffer.buffer, PCLIENT_PROMPT, output[0].buffer.Nbuffer);
+    if (status == -1) nanosleep (&request, &remain);
+  }
+  if (VerboseMode()) gprint (GP_ERR, "%s\n Read %d of %d bytes so far\n", output[0].buffer.buffer, output[0].buffer.Nbuffer, output[0].size);
+  if (status == 0) {
+    if (VerboseMode()) gprint (GP_ERR, "host %s is down\n", host[0].hostname);
+    return PCLIENT_DOWN;
+  }
+  if (line == NULL) {
+    if (VerboseMode()) gprint (GP_ERR, "host %s not yet at prompt, keep trying\n", host[0].hostname);
+    return PCLIENT_HUNG;
+  }
+
+  output[0].completed = TRUE;
+  return PCLIENT_GOOD;
+}
Index: /branches/sc_branches/pantasks_condor/pcontrol/HostOps.c
===================================================================
--- /branches/sc_branches/pantasks_condor/pcontrol/HostOps.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/pcontrol/HostOps.c	(revision 34783)
@@ -0,0 +1,236 @@
+# include "pcontrol.h"
+
+Stack *HostPool_AllHosts;  // virtual pool for user status queries
+
+Stack *HostPool_Idle; // these hosts are waiting for something to do
+Stack *HostPool_Busy; // these hosts are working
+Stack *HostPool_Resp; // these hosts are trying to respond
+Stack *HostPool_Done; // these hosts have finished a job
+Stack *HostPool_Down; // these hosts are not responding
+Stack *HostPool_Off;  // these hosts are off
+
+void InitHostStacks () {
+  HostPool_AllHosts = InitStack ();
+
+  HostPool_Idle = InitStack ();
+  HostPool_Busy = InitStack ();
+  HostPool_Resp = InitStack ();
+  HostPool_Done = InitStack ();
+  HostPool_Down = InitStack ();
+  HostPool_Off  = InitStack ();
+}
+
+void FreeHostStack (Stack *stack) {
+  Host *host;
+  while ((host = PullStackByLocation (stack, stack[0].Nobject - 1)) != NULL) {
+    DelHost (host);
+  }
+  FreeStack (stack);
+}
+
+void FreeHostStacks () {
+  FreeHostStack (HostPool_Idle);
+  FreeHostStack (HostPool_Busy);
+  FreeHostStack (HostPool_Resp);
+  FreeHostStack (HostPool_Done);
+  FreeHostStack (HostPool_Down);
+  FreeHostStack (HostPool_Off );
+
+  // AllHosts is a virtual stack : all hosts are references
+  FreeStack (HostPool_AllHosts);
+}
+
+char *GetHostStackName (int StackID) {
+  switch (StackID) {
+    case PCONTROL_HOST_ALLHOSTS: return ("ALLHOSTS");
+    case PCONTROL_HOST_IDLE: return ("IDLE");
+    case PCONTROL_HOST_DOWN: return ("DOWN");
+    case PCONTROL_HOST_RESP: return ("RESP");
+    case PCONTROL_HOST_DONE: return ("DONE");
+    case PCONTROL_HOST_BUSY: return ("BUSY");
+    case PCONTROL_HOST_OFF:  return ("OFF");
+  }
+  gprint (GP_ERR, "error: unknown host stack : programming error\n");
+  pcontrol_exit (51);
+  return (NULL);
+}
+
+Stack *GetHostStack (int StackID) {
+  switch (StackID) {
+    case PCONTROL_HOST_ALLHOSTS: return (HostPool_AllHosts);
+    case PCONTROL_HOST_IDLE: return (HostPool_Idle);
+    case PCONTROL_HOST_DOWN: return (HostPool_Down);
+    case PCONTROL_HOST_RESP: return (HostPool_Resp);
+    case PCONTROL_HOST_DONE: return (HostPool_Done);
+    case PCONTROL_HOST_BUSY: return (HostPool_Busy);
+    case PCONTROL_HOST_OFF:  return (HostPool_Off);
+  }
+  gprint (GP_ERR, "error: unknown host stack : programming error\n");
+  pcontrol_exit (52);
+  return (NULL);
+}
+
+Stack *GetHostStackByName (char *name) {
+  if (!strcasecmp (name, "all")) return (HostPool_AllHosts);
+  if (!strcasecmp (name, "idle")) return (HostPool_Idle);
+  if (!strcasecmp (name, "down")) return (HostPool_Down);
+  if (!strcasecmp (name, "resp")) return (HostPool_Resp);
+  if (!strcasecmp (name, "done")) return (HostPool_Done);
+  if (!strcasecmp (name, "busy")) return (HostPool_Busy);
+  if (!strcasecmp (name, "off"))  return (HostPool_Off);
+  return (NULL);
+}
+
+/* add host to position in stack */
+int PutHost (Host *host, int StackID, int where) {
+
+  int stat;
+  Stack *stack;
+
+  // fprintf (stderr, "move host %s to %s\n", host[0].hostname, GetHostStackName(StackID));
+
+  stack = GetHostStack (StackID);
+  if (stack == NULL) return (FALSE);
+
+  host[0].stack = StackID;
+  stat = PushStack (stack, where, host, host[0].HostID, host[0].hostname);
+  // XXX need to handle the error conditions, or we drop the host & leak memory
+  return (stat);
+}
+  
+/* find the host by ID in the defined host stacks */
+Host *PullHostByID (IDtype HostID, int *StackID) {
+
+  Host *host;
+
+  *StackID = PCONTROL_HOST_IDLE;
+  host = PullHostFromStackByID (*StackID, HostID);
+  if (host != NULL) return (host);
+
+  *StackID = PCONTROL_HOST_DOWN;
+  host = PullHostFromStackByID (*StackID, HostID);
+  if (host != NULL) return (host);
+
+  *StackID = PCONTROL_HOST_RESP;
+  host = PullHostFromStackByID (*StackID, HostID);
+  if (host != NULL) return (host);
+
+  *StackID = PCONTROL_HOST_DONE;
+  host = PullHostFromStackByID (*StackID, HostID);
+  if (host != NULL) return (host);
+
+  *StackID = PCONTROL_HOST_BUSY;
+  host = PullHostFromStackByID (*StackID, HostID);
+  if (host != NULL) return (host);
+
+  *StackID = PCONTROL_HOST_OFF;
+  host = PullHostFromStackByID (*StackID, HostID);
+  if (host != NULL) return (host);
+
+  *StackID = -1;
+  return (NULL);
+}
+
+/* find the host by ID in the defined host stacks */
+Host *PullHostByName (char *name, int *StackID) {
+
+  Host *host;
+
+  *StackID = PCONTROL_HOST_IDLE;
+  host = PullHostFromStackByName (*StackID, name);
+  if (host != NULL) return (host);
+
+  *StackID = PCONTROL_HOST_DOWN;
+  host = PullHostFromStackByName (*StackID, name);
+  if (host != NULL) return (host);
+
+  *StackID = PCONTROL_HOST_RESP;
+  host = PullHostFromStackByName (*StackID, name);
+  if (host != NULL) return (host);
+
+  *StackID = PCONTROL_HOST_DONE;
+  host = PullHostFromStackByName (*StackID, name);
+  if (host != NULL) return (host);
+
+  *StackID = PCONTROL_HOST_BUSY;
+  host = PullHostFromStackByName (*StackID, name);
+  if (host != NULL) return (host);
+
+  *StackID = PCONTROL_HOST_OFF;
+  host = PullHostFromStackByName (*StackID, name);
+  if (host != NULL) return (host);
+
+  *StackID = -1;
+  return (NULL);
+}
+
+Host *PullHostFromStackByID (int StackID, IDtype ID) {
+
+  Host *host;
+  Stack *stack;
+
+  stack = GetHostStack (StackID);
+  if (stack == NULL) return (NULL);
+
+  host = PullStackByID (stack, ID);
+  return (host);
+}
+
+Host *PullHostFromStackByName (int StackID, char *name) {
+
+  Host *host;
+  Stack *stack;
+
+  stack = GetHostStack (StackID);
+  if (stack == NULL) return (NULL);
+
+  host = PullStackByName (stack, name);
+  return (host);
+}
+
+IDtype AddHost (char *hostname, int max_threads) {
+
+  Host *host;
+
+  ALLOCATE (host, Host, 1);
+
+  host[0].hostname    = strcreate (hostname);
+  host[0].max_threads = max_threads;
+  host[0].stdin_fd    = 0;
+  host[0].stdout_fd   = 0;
+  host[0].stderr_fd   = 0;
+  host[0].HostID      = NextHostID();
+
+  host[0].last_start_try.tv_sec  = 0;
+  host[0].last_start_try.tv_usec = 0;
+  host[0].next_start_try.tv_sec  = 0;
+  host[0].next_start_try.tv_usec = 0;
+
+  InitIOBuffer (&host[0].comms_buffer, 0x100);
+  host[0].response_state = PCONTROL_RESP_NONE;
+  host[0].response = NULL;
+
+  host[0].markoff  = FALSE;
+  host[0].job      = NULL;
+
+  AddMachineHost (host);
+
+  PutHost (host, PCONTROL_HOST_ALLHOSTS, STACK_BOTTOM);
+  PutHost (host, PCONTROL_HOST_DOWN, STACK_BOTTOM);
+  return (host[0].HostID);
+}
+
+void DelHost (Host *host) {
+
+  Host *copy;
+
+  copy = PullStackByID (HostPool_AllHosts, host[0].HostID);
+  ASSERT (copy == host, "programming error: ALLHOSTS entry does not match");
+
+  DelMachineHost (host);
+
+  FreeIOBuffer (&host[0].comms_buffer);
+  FREE (host[0].hostname);
+  FREE (host[0].job);
+  FREE (host);
+}
Index: /branches/sc_branches/pantasks_condor/pcontrol/IDops.c
===================================================================
--- /branches/sc_branches/pantasks_condor/pcontrol/IDops.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/pcontrol/IDops.c	(revision 34783)
@@ -0,0 +1,70 @@
+# include "pcontrol.h"
+
+static IDtype CurrentJobID  = 1;
+static IDtype CurrentHostID = 1;
+
+/* for now, no persistence between sessions : we could use the date/time to seed the upper
+ * byte(s) if needed */
+void InitIDs () {
+  CurrentJobID = 1;
+  CurrentHostID = 1;
+}
+
+IDtype NextJobID () {
+
+  IDtype ID;
+
+  ID = CurrentJobID;
+  CurrentJobID ++;
+  return (ID);
+}
+
+/* only used by the User thread */
+IDtype NextHostID () {
+
+  IDtype ID;
+
+  ID = CurrentHostID;
+  CurrentHostID ++;
+  return (ID);
+}
+
+void PrintID (gpDest dest, IDtype ID) {
+
+  unsigned short int word0, word1, word2, word3;
+
+  word0 = 0xffff & ID;
+  word1 = 0xffff & (ID >> 16);
+  word2 = 0xffff & (ID >> 32);
+  word3 = 0xffff & (ID >> 48);
+
+  gprint (dest, "%x.%x.%x.%x", word3, word2, word1, word0);
+}
+
+IDtype GetID (char *IDword) {
+
+  int Nargs;
+  IDtype ID;
+  unsigned int word0, word1, word2, word3;
+  char *endptr;
+
+  Nargs = sscanf (IDword, "%x.%x.%x.%x", &word3, &word2, &word1, &word0);
+  if (Nargs == 4) {
+      IDtype tmp;
+    ID = 0;
+    ID |= (word0 << 0);
+    ID |= (word1 << 16);
+    tmp = word2;
+    ID |= (tmp << 32);
+    tmp = word3;
+    ID |= (tmp << 48);
+    return ID;
+  } 
+    
+  ID = strtoll (IDword, &endptr, 10);
+  if (*endptr == 0) {
+    return ID;
+  }
+
+  return 0;
+}
Index: /branches/sc_branches/pantasks_condor/pcontrol/JobOps.c
===================================================================
--- /branches/sc_branches/pantasks_condor/pcontrol/JobOps.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/pcontrol/JobOps.c	(revision 34783)
@@ -0,0 +1,305 @@
+# include "pcontrol.h"
+
+Stack *JobPool_AllJobs;  // virtual pool for user status queries
+
+Stack *JobPool_Pending;
+Stack *JobPool_Busy;
+Stack *JobPool_Resp;
+Stack *JobPool_Done;
+Stack *JobPool_Kill;
+Stack *JobPool_Exit;
+Stack *JobPool_Crash;
+
+void InitJobStacks () {
+  JobPool_AllJobs = InitStack ();
+
+  JobPool_Pending = InitStack ();
+  JobPool_Busy    = InitStack ();
+  JobPool_Resp    = InitStack ();
+  JobPool_Done    = InitStack ();
+  JobPool_Kill    = InitStack ();
+  JobPool_Exit    = InitStack ();
+  JobPool_Crash   = InitStack ();
+}
+
+void FreeJobStack (Stack *stack) {
+  Job *job;
+  while ((job = PullStackByLocation (stack, stack[0].Nobject - 1)) != NULL) {
+    DelJob (job);
+  }
+  FreeStack (stack);
+}
+
+void FreeJobStacks () {
+  FreeJobStack (JobPool_Pending);
+  FreeJobStack (JobPool_Busy   );
+  FreeJobStack (JobPool_Resp   );
+  FreeJobStack (JobPool_Done   );
+  FreeJobStack (JobPool_Kill   );
+  FreeJobStack (JobPool_Exit   );
+  FreeJobStack (JobPool_Crash  );
+
+  // AllJobs is a virtual stack : all jobs are references
+  FreeStack (JobPool_AllJobs);
+}
+
+char *GetJobStackName (int StackID) {
+  switch (StackID) {
+    case PCONTROL_JOB_ALLJOBS: return ("ALLJOBS");
+
+    case PCONTROL_JOB_PENDING: return ("PENDING");
+    case PCONTROL_JOB_BUSY:    return ("BUSY");
+    case PCONTROL_JOB_RESP:    return ("RESP");
+    case PCONTROL_JOB_DONE:    return ("DONE");
+    case PCONTROL_JOB_KILL:    return ("KILL");
+    case PCONTROL_JOB_EXIT:    return ("EXIT");
+    case PCONTROL_JOB_CRASH:   return ("CRASH");
+  }
+  gprint (GP_ERR, "error: unknown host stack : programming error\n");
+  pcontrol_exit (53);
+  return (NULL);
+}
+
+Stack *GetJobStack (int StackID) {
+  switch (StackID) {
+    case PCONTROL_JOB_ALLJOBS: return (JobPool_AllJobs);
+
+    case PCONTROL_JOB_PENDING: return (JobPool_Pending);
+    case PCONTROL_JOB_BUSY:    return (JobPool_Busy);
+    case PCONTROL_JOB_RESP:    return (JobPool_Resp);
+    case PCONTROL_JOB_DONE:    return (JobPool_Done);
+    case PCONTROL_JOB_KILL:    return (JobPool_Kill);
+    case PCONTROL_JOB_EXIT:    return (JobPool_Exit);
+    case PCONTROL_JOB_CRASH:   return (JobPool_Crash);
+  }
+  gprint (GP_ERR, "error: unknown job stack : programming error\n");
+  pcontrol_exit (54);
+  return (NULL);
+}
+
+Stack *GetJobStackByName (char *name) {
+
+  if (!strcasecmp (name, "all"))     return (JobPool_AllJobs);
+
+  if (!strcasecmp (name, "pending")) return (JobPool_Pending);
+  if (!strcasecmp (name, "busy"))    return (JobPool_Busy);
+  if (!strcasecmp (name, "resp"))    return (JobPool_Resp);
+  if (!strcasecmp (name, "done"))    return (JobPool_Done);
+  if (!strcasecmp (name, "exit"))    return (JobPool_Exit);
+  if (!strcasecmp (name, "crash"))   return (JobPool_Crash);
+  return (NULL);
+}
+
+/* add job to position in stack, use StackID as default state */
+int PutJob (Job *job, int StackID, int where) {
+
+  int stat;
+  Stack *stack;
+
+  // fprintf (stderr, "move job %s to %s\n", job[0].argv[0], GetJobStackName(StackID));
+
+  stack = GetJobStack (StackID);
+  if (stack == NULL) return (FALSE);
+
+  /* by default, these are both the same - to override, use PutJobSetState */
+  job[0].state = StackID;
+  job[0].stack = StackID;
+  stat = PushStack (stack, where, job, job[0].JobID, job[0].argv[0]);
+  // XXX need to handle the error conditions, or we drop the host & leak memory
+  return (stat);
+}
+  
+/* add job to position in stack.  set state to 'state' */
+int PutJobSetState (Job *job, int StackID, int where, int state) {
+
+  int stat;
+  Stack *stack;
+
+  stack = GetJobStack (StackID);
+  if (stack == NULL) return (FALSE);
+
+  /* alternate state specified by user */
+  job[0].state = state;
+  job[0].stack = StackID;
+  stat = PushStack (stack, where, job, job[0].JobID, job[0].argv[0]);
+  // XXX need to handle the error conditions, or we drop the host & leak memory
+  return (stat);
+}
+  
+Job *PullJobByID (IDtype JobID, int *StackID) {
+
+  Job *job;
+
+  *StackID = PCONTROL_JOB_PENDING;
+  job = PullJobFromStackByID (*StackID, JobID);
+  if (job != NULL) return (job);
+
+  *StackID = PCONTROL_JOB_BUSY;
+  job = PullJobFromStackByID (*StackID, JobID);
+  if (job != NULL) return (job);
+
+  *StackID = PCONTROL_JOB_RESP;
+  job = PullJobFromStackByID (*StackID, JobID);
+  if (job != NULL) return (job);
+
+  *StackID = PCONTROL_JOB_EXIT;
+  job = PullJobFromStackByID (*StackID, JobID);
+  if (job != NULL) return (job);
+
+  *StackID = PCONTROL_JOB_CRASH;
+  job = PullJobFromStackByID (*StackID, JobID);
+  if (job != NULL) return (job);
+
+  *StackID = PCONTROL_JOB_DONE;
+  job = PullJobFromStackByID (*StackID, JobID);
+  if (job != NULL) return (job);
+
+  *StackID = PCONTROL_JOB_KILL;
+  job = PullJobFromStackByID (*StackID, JobID);
+  if (job != NULL) return (job);
+
+  return (NULL);
+}
+
+/* remove job from position in stack */
+Job *PullJobFromStackByID (int StackID, int ID) {
+
+  Job *job;
+  Stack *stack;
+
+  stack = GetJobStack (StackID);
+  if (stack == NULL) return (NULL);
+
+  job = PullStackByID (stack, ID);
+  return (job);
+}
+
+int InitJobOutput (JobOutput *output) {
+
+  output[0].size = 0;
+  output[0].requested = FALSE;
+  output[0].completed = FALSE;
+  InitIOBuffer (&output[0].buffer, 0x1000);
+
+  return TRUE;
+}
+
+int ResetJobOutput (JobOutput *output) {
+
+  output[0].size = 0;
+  output[0].requested = FALSE;
+  output[0].completed = FALSE;
+  FlushIOBuffer (&output[0].buffer);
+
+  return TRUE;
+}
+
+IDtype AddJob (char *hostname, JobMode mode, int timeout, int priority, int argc, char **argv, int Nxhosts, char **xhosts) {
+
+  int JobID;
+  Job *job;
+
+  ALLOCATE (job, Job, 1);
+
+  job[0].JobID    = NextJobID();
+  if (job[0].JobID < 0) {
+    free (job);
+    return -1;
+  }
+
+  job[0].argc     = argc;
+  job[0].argv     = argv;
+  job[0].hostname = hostname;
+  job[0].realhost = NULL;
+
+  job[0].exit_status = 0;
+  job[0].Reset    = FALSE;
+
+  InitJobOutput (&job[0].stdout_buf);
+  InitJobOutput (&job[0].stderr_buf);
+
+  job[0].mode     = mode;
+  job[0].priority = priority;
+
+  job[0].state = 0;
+  job[0].stack = 0;
+
+  job[0].dtime = 0.0;
+  job[0].pid = 0;
+  job[0].host     = NULL;
+
+  job[0].xhosts = xhosts;
+  job[0].Nxhosts = Nxhosts;
+
+  JobID = job[0].JobID;
+
+  // Put a copy of all created jobs on the ALLJOBS stack
+  // This is a virtual stack: do not free the job from this stack
+  PutJob (job, PCONTROL_JOB_ALLJOBS, STACK_BOTTOM);
+  PutJob (job, PCONTROL_JOB_PENDING, STACK_BOTTOM);
+
+  // until the job is launched, we use 'start' to time how long the job is waiting on the queue
+  gettimeofday (&job[0].start, (void *) NULL);
+
+  if (VerboseMode()) gprint (GP_ERR, "added new job\n");
+  return (JobID);
+}
+
+void DelJob (Job *job) {
+
+  int i;
+
+  Job *copy;
+
+  copy = PullStackByID (JobPool_AllJobs, job[0].JobID);
+  ASSERT (copy == job, "programming error: ALLJOBS entry does not match");
+
+  FREE (job[0].hostname);
+  for (i = 0; i < job[0].argc; i++) {
+    FREE (job[0].argv[i]);
+  }
+  FREE (job[0].argv);
+
+  for (i = 0; i < job[0].Nxhosts; i++) {
+    FREE (job[0].xhosts[i]);
+  }
+  FREE (job[0].xhosts);
+
+  FreeIOBuffer (&job[0].stdout_buf.buffer);
+  FreeIOBuffer (&job[0].stderr_buf.buffer);
+
+  FREE (job);
+}
+
+/*** ResortJobStack can be used to adjust priorities based on some info we supply.  This
+     is not finished -- to finish this, I need to define the metric of interest and
+     arrange for that to be passed to the jobs in pcontrol
+
+void ResortJobStack (int StackID) {
+
+  Stack *stack = GetJobStack (StackID);
+  LockStack (stack);
+
+# define SWAPFUNC(A,B){				\
+    void *tmpObject = stack[0].object[A];	\
+    stack[0].object[A] = stack[0].object[B];	\
+    stack[0].object[B] = tmpObject;		\
+    void *tmpName = stack[0].name[A];		\
+    stack[0].name[A] = stack[0].name[B];	\
+    stack[0].name[B] = tmpName;			\
+    void *tmpID = stack[0].id[A];		\
+    stack[0].id[A] = stack[0].id[B];		\
+    stack[0].id[B] = tmpID;			\
+  }
+
+# define COMPARE(A,B)(((Job *)stack[0].object[A]).VALUE < ((Job *)stack[0].object[B]).VALUE)
+
+  OHANA_SORT (stack[0].Nobject, COMPARE, SWAPFUNC);
+
+# undef SWAPFUNC
+# undef COMPARE
+
+  UnlockStack (stack);
+}
+
+***/
Index: /branches/sc_branches/pantasks_condor/pcontrol/KillJob.c
===================================================================
--- /branches/sc_branches/pantasks_condor/pcontrol/KillJob.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/pcontrol/KillJob.c	(revision 34783)
@@ -0,0 +1,91 @@
+# include "pcontrol.h"
+
+int KillJob (Job *job, Host *host) {
+  
+  int status;
+
+  ASSERT (host != NULL, "host missing");
+  ASSERT (job != NULL, "job missing");
+  ASSERT (host == (Host *) job[0].host, "invalid host");
+  ASSERT (job  == (Job *) host[0].job, "invalid job");
+
+  status = PclientCommand (host, "reset", PCLIENT_PROMPT, PCONTROL_RESP_KILL_JOB);
+
+  /* check on success of pclient command */
+  switch (status) {
+    case PCLIENT_DOWN:
+      // unlink host & job
+      if (VerboseMode()) gprint (GP_ERR, "host %s is down\n", host[0].hostname);
+      job[0].host = NULL;
+      host[0].job = NULL;
+
+      // decrement the machine job-host counters
+      DelMachineJob (host, job);
+
+      HarvestHost (host[0].pid);
+      PutHost (host, PCONTROL_HOST_DOWN, STACK_BOTTOM);
+      PutJob (job, PCONTROL_JOB_CRASH, STACK_BOTTOM);
+      return (FALSE);
+
+    case PCLIENT_GOOD:
+      if (VerboseMode()) gprint (GP_ERR, "kill job on host %s\n", host[0].hostname);  
+      FlushIOBuffer (&host[0].comms_buffer);
+      PutHost (host, PCONTROL_HOST_RESP, STACK_BOTTOM);
+      PutJob (job, PCONTROL_JOB_RESP, STACK_BOTTOM);
+      return (TRUE);
+
+    default:
+      ABORT ("unknown status for pclient command");  
+  }
+}
+
+int KillJobResponse (Host *host) {
+  
+  int status;
+  char *p;
+  IOBuffer *buffer;
+  Job *job;
+
+  ASSERT (host != NULL, "host missing");
+  ASSERT (host[0].job, "missing job");
+  buffer = &host[0].comms_buffer;
+  job = (Job *) host[0].job;
+
+  /** check on response to pclient command **/
+  p = memstr (buffer[0].buffer, "STATUS", buffer[0].Nbuffer);
+  if (p == NULL) {
+      if (VerboseMode()) gprint (GP_ERR, "missing STATUS in response; try again\n");
+      PutHost (host, PCONTROL_HOST_BUSY, STACK_BOTTOM);
+      PutJob (job, PCONTROL_JOB_KILL, STACK_BOTTOM);
+      return (FALSE);
+  }
+  if (VerboseMode()) gprint (GP_ERR, "client message: %s\n", buffer[0].buffer);
+
+  sscanf (p, "%*s %d", &status);
+  gprint (GP_ERR, "client status: %d\n", status);
+
+  switch (status) {
+    case -1:
+      ABORT ("syntax error to pclient");
+    case 0:
+      gprint (GP_ERR, "failure to kill child process\n");
+      PutHost (host, PCONTROL_HOST_BUSY, STACK_BOTTOM);
+      PutJob (job, PCONTROL_JOB_KILL, STACK_BOTTOM);
+      return (FALSE);
+    case 1:
+      gprint (GP_ERR, "killed job %s on %s\n", job[0].argv[0], host[0].hostname);
+      // unlink host & job
+      job[0].host = NULL;
+      host[0].job = NULL;
+
+      // decrement the machine job-host counters
+      DelMachineJob (host, job);
+
+      PutHost (host, PCONTROL_HOST_IDLE, STACK_BOTTOM);
+      PutJob (job, PCONTROL_JOB_CRASH, STACK_BOTTOM);
+      return (TRUE);
+    case 2:
+      ABORT ("client has no job");
+  }
+  ABORT ("should not reach here (KillJob)");
+}
Index: /branches/sc_branches/pantasks_condor/pcontrol/MachineOps.c
===================================================================
--- /branches/sc_branches/pantasks_condor/pcontrol/MachineOps.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/pcontrol/MachineOps.c	(revision 34783)
@@ -0,0 +1,239 @@
+# include "pcontrol.h"
+# define DEBUG 0
+
+static int MAX_UNWANTED_HOST_JOBS = 5;
+
+static Machine **machines = NULL;
+static int      Nmachines = 0;
+static int      NMACHINES = 0;
+
+void InitMachines () {
+
+  if (machines != NULL) return;
+
+  NMACHINES = 10;
+  Nmachines =  0;
+
+  ALLOCATE (machines, Machine *, NMACHINES);
+  memset (machines, 0, NMACHINES*sizeof(Machine *));
+
+  return;
+}
+
+void FreeMachines () {
+
+  int i;
+
+  for (i = 0; i < NMACHINES; i++) {
+    FREE (machines[i]);
+  }
+  FREE (machines);
+
+  return;
+}
+
+// find a machine matching the given name (if not found, return NULL)
+Machine *FindMachineByName (char *name) {
+
+  int i;
+
+  for (i = 0; i < Nmachines; i++) {
+    if (!strcmp (machines[i][0].name, name)) {
+      return machines[i];
+    }
+  }
+  return NULL;
+}
+
+// add a new machine (if not new, return TRUE)
+Machine *AddMachine (char *name) {
+
+  Machine *machine;
+
+  machine = FindMachineByName (name);
+  if (machine) return machine;
+
+  ALLOCATE (machines[Nmachines], Machine, 1);
+  machines[Nmachines][0].name = strcreate (name);
+  machines[Nmachines][0].Nhosts = 0;
+  machines[Nmachines][0].NjobsRealhost = 0;
+  machines[Nmachines][0].NjobsWanthost = 0;
+
+  machine = machines[Nmachines];
+  Nmachines ++;
+
+  if (Nmachines >= NMACHINES - 1) {
+    NMACHINES += 10;
+    REALLOCATE (machines, Machine *, NMACHINES);
+    memset (&machines[Nmachines], 0, (NMACHINES - Nmachines)*sizeof(Machine *));
+  }
+
+  return machine;
+}
+
+// delete a new machine (if not new, return TRUE)
+int DelMachine (char *name) {
+
+  return TRUE;
+}
+
+int AddMachineHost (Host *host) {
+
+  Machine *machine;
+
+  // find or add machine matching this host name
+  machine = AddMachine (host[0].hostname); // Can this fail?
+  machine[0].Nhosts ++;
+  // XXX do we need this? host[0].machine = machine;
+  return (TRUE);
+}
+
+int DelMachineHost (Host *host) {
+
+  Machine *machine;
+
+  // machine = host[0].machine;
+  machine = FindMachineByName (host[0].hostname);
+  if (machine == NULL) return (FALSE);
+
+  machine[0].Nhosts --;
+  if (machine[0].Nhosts < 0) {
+    fprintf (stderr, "warning: mis-match in host count for machine %s\n", machine[0].name);
+    machine[0].Nhosts = 0;
+  }
+
+  return (TRUE);
+}
+
+int AddMachineJob (Host *host, Job *job) {
+
+  int i;
+  Machine *machine;
+
+  // find machine matching the real host name
+  machine = FindMachineByName (host[0].hostname); // Can this fail?
+  ASSERT (machine, "cannot find machine associated with host");
+  machine[0].NjobsRealhost ++;
+
+  // skip jobs that do not have a targeted host or any xhosts
+  if (!job[0].hostname && !job[0].Nxhosts) {
+    return (TRUE);
+  }
+
+  for (i = 0; i < job[0].Nxhosts; i++) {
+    // find machine matching the xhost name (these count against the unwanted host total)
+    machine = FindMachineByName (job[0].xhosts[i]); // Can this fail?
+    if (!machine) continue;
+    machine[0].NjobsWanthost ++;
+  }
+
+  // do not double count jobs on the wanted host
+  if (!strcmp (job[0].hostname, host[0].hostname)) {
+    return (TRUE);
+  }
+
+  // find machine matching the want host name (these are running on an unwanted host)
+  machine = FindMachineByName (job[0].hostname); // Can this fail?
+  if (machine == NULL) return (TRUE);
+  machine[0].NjobsWanthost ++;
+
+  return (TRUE);
+}
+
+int DelMachineJob (Host *host, Job *job) {
+
+  int i;
+  Machine *machine;
+
+  // find machine matching the real host name
+  machine = FindMachineByName (host[0].hostname); // Can this fail?
+  ASSERT (machine, "cannot find machine associated with host");
+  machine[0].NjobsRealhost --;
+
+  // skip jobs that do not have a targeted host or any xhosts
+  if (!job[0].hostname && !job[0].Nxhosts) {
+    return (TRUE);
+  }
+
+  for (i = 0; i < job[0].Nxhosts; i++) {
+    // find machine matching the xhost name (these count against the unwanted host total)
+    machine = FindMachineByName (job[0].xhosts[i]); // Can this fail?
+    if (!machine) continue;
+    machine[0].NjobsWanthost --;
+  }
+
+  // do not double count jobs on the wanted host
+  if (!strcmp (job[0].hostname, host[0].hostname)) {
+    return (TRUE);
+  }
+
+  // find machine matching the want host name
+  machine = FindMachineByName (job[0].hostname); // Can this fail?
+  if (machine == NULL) return (TRUE);
+  machine[0].NjobsWanthost --;
+
+  return (TRUE);
+}
+
+int PrintMachines () {
+
+  int i;
+  Machine *machine;
+
+  gprint (GP_LOG, "Nmachines: %d\n", Nmachines);
+  for (i = 0; i < Nmachines; i++) {
+    machine = machines[i];
+    gprint (GP_LOG, "%s : %d : %d : %d\n", machine[0].name, machine[0].Nhosts, machine[0].NjobsRealhost, machine[0].NjobsWanthost);
+  }
+
+  return (TRUE);
+}
+
+int CheckMachineJobs (Host *host, Job *job) {
+
+  int i;
+  Machine *machine;
+
+  machine = FindMachineByName (job[0].hostname);
+  if (machine) {
+    if (DEBUG) fprintf (stderr, "wanthost: %s, Ntotal: %d, Nmax: %d\n", machine[0].name, machine[0].NjobsWanthost + machine[0].NjobsRealhost, machine[0].Nhosts + MAX_UNWANTED_HOST_JOBS);
+    if (machine[0].NjobsWanthost >= MAX_UNWANTED_HOST_JOBS) {
+      if (DEBUG) fprintf (stderr, "too many outstanding jobs wanting host %s, delay job %s for now\n", machine[0].name, job[0].argv[0]);
+      return (FALSE);
+    }
+  }
+
+  for (i = 0; i < job[0].Nxhosts; i++) {
+    machine = FindMachineByName (job[0].xhosts[i]);
+    if (machine) {
+      if (DEBUG) fprintf (stderr, "xhost: %s, Ntotal: %d, Nmax: %d\n", machine[0].name, machine[0].NjobsWanthost + machine[0].NjobsRealhost, machine[0].Nhosts + MAX_UNWANTED_HOST_JOBS);
+      if (machine[0].NjobsWanthost >= MAX_UNWANTED_HOST_JOBS) {
+	if (DEBUG) fprintf (stderr, "too many outstanding jobs wanting host %s, delay job %s for now\n", machine[0].name, job[0].argv[0]);
+	return (FALSE);
+      }
+    }
+  }    
+
+  machine = FindMachineByName (host[0].hostname);
+  if (machine) {
+    if (DEBUG) fprintf (stderr, "realhost: %s, Ntotal: %d, Nmax: %d\n", machine[0].name, machine[0].NjobsWanthost + machine[0].NjobsRealhost, machine[0].Nhosts + MAX_UNWANTED_HOST_JOBS);
+    if (machine[0].NjobsWanthost >= MAX_UNWANTED_HOST_JOBS) {
+      if (DEBUG) fprintf (stderr, "too many outstanding jobs wanting host %s, delay job %s for now\n", machine[0].name, job[0].argv[0]);
+      return (FALSE);
+    }
+  }
+
+  return (TRUE);
+}
+
+void SetMaxUnwantedHostJobs (int value) {
+
+  MAX_UNWANTED_HOST_JOBS = value;
+  return;
+}
+
+int GetMaxUnwantedHostJobs (void) {
+
+  return MAX_UNWANTED_HOST_JOBS;
+}
+
Index: /branches/sc_branches/pantasks_condor/pcontrol/Makefile
===================================================================
--- /branches/sc_branches/pantasks_condor/pcontrol/Makefile	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/pcontrol/Makefile	(revision 34783)
@@ -0,0 +1,78 @@
+default: pcontrol
+
+include ../../../Makefile.System
+HOME    =       $(ROOT)/src/opihi
+BIN     =       $(HOME)/bin
+LIB     =       $(HOME)/lib
+INC     =       $(HOME)/include
+SRC     =       $(HOME)/pcontrol
+DATA    =       $(DESTDATA)/pcontrol
+include ../Makefile.Common
+
+# programs may add their own internal requirements here
+LIBS1         = -lkapa -lFITS -lohana
+LIBS2         = -lbasiccmd -lshell -ldata 
+FULL_CFLAGS   = $(BASE_CFLAGS)
+FULL_CPPFLAGS = $(BASE_CPPFLAGS)
+FULL_LDFLAGS  = $(LIBS1) $(LIBS2) $(BASE_LDFLAGS)
+
+# to build the non-threaded version, remove -lpthread and comment out
+# the THREADED line in include/pcontrol.h
+
+# pcontrol user commands and support functions ########################
+funcs = \
+$(SRC)/init.$(ARCH).o \
+$(SRC)/pcontrol.$(ARCH).o \
+$(SRC)/CheckBusyJob.$(ARCH).o \
+$(SRC)/CheckDoneHost.$(ARCH).o \
+$(SRC)/CheckRespHost.$(ARCH).o \
+$(SRC)/CheckDoneJob.$(ARCH).o \
+$(SRC)/CheckHost.$(ARCH).o \
+$(SRC)/CheckIdleHost.$(ARCH).o \
+$(SRC)/CheckPoint.$(ARCH).o \
+$(SRC)/CheckSystem.$(ARCH).o \
+$(SRC)/GetJobOutput.$(ARCH).o \
+$(SRC)/HostOps.$(ARCH).o \
+$(SRC)/IDops.$(ARCH).o \
+$(SRC)/JobOps.$(ARCH).o \
+$(SRC)/MachineOps.$(ARCH).o \
+$(SRC)/StackOps.$(ARCH).o \
+$(SRC)/PclientCommand.$(ARCH).o \
+$(SRC)/StartHost.$(ARCH).o \
+$(SRC)/StopHosts.$(ARCH).o \
+$(SRC)/KillJob.$(ARCH).o \
+$(SRC)/StartJob.$(ARCH).o
+
+cmds = \
+$(SRC)/check.$(ARCH).o \
+$(SRC)/delete.$(ARCH).o \
+$(SRC)/host.$(ARCH).o \
+$(SRC)/job.$(ARCH).o \
+$(SRC)/jobstack.$(ARCH).o \
+$(SRC)/hoststack.$(ARCH).o \
+$(SRC)/kill.$(ARCH).o \
+$(SRC)/pulse.$(ARCH).o \
+$(SRC)/parameters.$(ARCH).o \
+$(SRC)/run.$(ARCH).o \
+$(SRC)/machines.$(ARCH).o \
+$(SRC)/status.$(ARCH).o \
+$(SRC)/stdout.$(ARCH).o \
+$(SRC)/version.$(ARCH).o \
+$(SRC)/verbose.$(ARCH).o
+
+libs = \
+$(DESTLIB)/libbasiccmd.a \
+$(DESTLIB)/libshell.a \
+$(DESTLIB)/libdata.a
+
+pcontrol: $(BIN)/pcontrol.$(ARCH)
+$(SRC)/pcontrol.$(ARCH).o : $(libs)
+$(BIN)/pcontrol.$(ARCH)   : $(cmds) $(funcs)
+
+$(cmds) $(funcs) : $(INC)/pcontrol.h
+
+install: $(DESTBIN)/pcontrol help
+
+help: clean-help cmd.basic.help pcontrol.help
+
+.PHONY: pcontrol
Index: /branches/sc_branches/pantasks_condor/pcontrol/PclientCommand.c
===================================================================
--- /branches/sc_branches/pantasks_condor/pcontrol/PclientCommand.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/pcontrol/PclientCommand.c	(revision 34783)
@@ -0,0 +1,82 @@
+# include "pcontrol.h"
+# define PCLIENT_TIMEOUT 100
+
+// send a command and check for errors; ignore output
+int PclientCommand (Host *host, char *command, char *response, HostResp response_state) {
+
+  int status;
+
+  ASSERT (host != NULL, "host missing");
+  ASSERT (command != NULL, "command missing");
+
+  // flush the stdout and stderr buffers here
+  // recycle comms_buffer to minimize page thrashing
+  ReadtoIOBuffer (&host[0].comms_buffer, host[0].stdout_fd);
+  FlushIOBuffer (&host[0].comms_buffer);
+  ReadtoIOBuffer (&host[0].comms_buffer, host[0].stderr_fd);
+  FlushIOBuffer (&host[0].comms_buffer);
+
+  /* send command to client (adding on \n) */
+  status = write_fmt (host[0].stdin_fd, "%s\n", command);
+
+  /* is pipe still open? */
+  if ((status == -1) && (errno == EPIPE)) {
+    gprint (GP_ERR, "pclient read gives pipe error for %s\n", command);
+    return (PCLIENT_DOWN);
+  }
+  
+  // prepare host to accept response
+  host[0].response_state = response_state;
+  host[0].response = response;
+  FlushIOBuffer (&host[0].comms_buffer);
+
+  // fprintf (stderr, "command: %s\n", command);
+
+  return (PCLIENT_GOOD);
+}
+  
+// check for response; message must end with specified string.
+// accumulate the response in the buffer
+int PclientResponse (Host *host, char *response, IOBuffer *buffer) {
+
+  int i;
+  int status;
+  char *line;
+  struct timespec request, remain;
+
+  ASSERT (response != NULL, "response missing");
+  ASSERT (buffer != NULL, "buffer missing");
+
+  /* avoid blocking very long on read, test every 100 usec, up to 0.1 sec */
+  request.tv_sec = 0;
+  request.tv_nsec = 100000;
+
+  /* watch for response - wait up to 1 second */
+  line = NULL;
+  status = -1;
+
+  // how long does each cycle really take?
+  for (i = 0; (i < PCLIENT_TIMEOUT) && (status != 0) && (line == NULL); i++) {
+    status = ReadtoIOBuffer (buffer, host[0].stdout_fd);
+    line = memstr (buffer[0].buffer, response, buffer[0].Nbuffer);
+    if (status == -1) nanosleep (&request, &remain);
+  }
+  if (status ==  0) {
+    gprint (GP_ERR, "pclient read returns 0 for %s\n", response);
+    return (PCLIENT_DOWN);
+  }
+  if (line == NULL) return (PCLIENT_HUNG);
+  if (status == -1) return (PCLIENT_HUNG);
+
+  // fprintf (stderr, "response: %s\n", buffer[0].buffer);
+
+  return (PCLIENT_GOOD);
+}
+
+/* memstr returns a view, not an allocated string : don't free */
+/* ReadtoIOBuffer returns : 
+    0 - pipe closed
+   -1 - no more data in pipe, data not ready
+   -2 - serious error reading from pipe
+   >0 - data read from pipe
+*/
Index: /branches/sc_branches/pantasks_condor/pcontrol/StackOps.c
===================================================================
--- /branches/sc_branches/pantasks_condor/pcontrol/StackOps.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/pcontrol/StackOps.c	(revision 34783)
@@ -0,0 +1,224 @@
+# include "pcontrol.h"
+
+/* these stacks are not super efficient, and should probably be replaced with linked lists, 
+   but I find these easier to get my brain around.
+*/
+
+/* Stacks and thread locks: interacting with the Stacks needs to be thread-safe so that the user may
+ * perform operations which interact with the stacks at the same time that the background loops
+ * check the current status of the jobs and hosts in the different stacks.  The simplest way in
+ * which the stacks are made thread safe is to lock them with a mutex before every interaction
+ */
+
+# define DEBUG 0
+
+void PrintStackInfo (Stack *stack, const char *func) {
+
+  if (!DEBUG) return;
+  fprintf (stderr, "%s: %p  ", func, stack);
+  fprintf (stderr, "objects: %p  ", stack[0].object);
+  fprintf (stderr, "Nobjects: %d, NOBJECTS: %d\n", stack[0].Nobject, stack[0].NOBJECT);
+}
+
+/* allocate stack, setup with default values, allocate data */
+Stack *InitStack () {
+
+  Stack *stack;
+
+  ALLOCATE (stack, Stack, 1);
+
+  stack[0].Nobject = 0;
+  stack[0].NOBJECT = 50;
+
+  ALLOCATE (stack[0].object, void *, stack[0].NOBJECT);
+  ALLOCATE (stack[0].name,   char *, stack[0].NOBJECT);
+  ALLOCATE (stack[0].id,     int,    stack[0].NOBJECT);
+
+# ifdef THREADED
+  pthread_mutex_init (&stack[0].mutex, NULL);
+# endif
+  return (stack);
+}
+
+void FreeStack (Stack *stack) {
+
+  free (stack[0].object);
+  free (stack[0].name);
+  free (stack[0].id);
+  free (stack);
+}
+
+/* STACK_TOP == 0, STACK_BOTTOM == -1 */
+/* this code correctly handles the negative 'where' and Nobject == 0 */
+
+/* push object on stack at given location */
+int PushStack (Stack *stack, int where, void *object, int id, char *name) {
+
+  int i;
+
+  PrintStackInfo (stack, __func__);
+  ASSERT (stack != NULL, "stack not set");
+  LockStack (stack);
+
+  if (where < 0) where += stack[0].Nobject + 1;
+  if (where < 0) {
+    UnlockStack (stack);
+    return (FALSE);
+  }
+  if (where > stack[0].Nobject) {
+    UnlockStack (stack);
+    return (FALSE);
+  }
+
+  /* extend stack as needed */
+  if (stack[0].Nobject >= stack[0].NOBJECT) {
+    stack[0].NOBJECT += 100;
+    REALLOCATE (stack[0].object, void *, stack[0].NOBJECT);
+    REALLOCATE (stack[0].name,   char *, stack[0].NOBJECT);
+    REALLOCATE (stack[0].id,     int, stack[0].NOBJECT);
+  }
+
+  for (i = stack[0].Nobject; i > where; i--) {
+    stack[0].object[i] = stack[0].object[i-1];
+    stack[0].name[i]   = stack[0].name[i-1];
+    stack[0].id[i]     = stack[0].id[i-1];
+  }
+  stack[0].object[where] = object;
+  stack[0].name[where]   = name;
+  stack[0].id[where]     = id;
+  stack[0].Nobject ++;
+
+  UnlockStack (stack);
+  return (TRUE);
+}
+
+/* get object from specified point in stack (negative == distance from end) */
+void *PullStackByLocation (Stack *stack, int where) {
+
+  void *object;
+  
+  PrintStackInfo (stack, __func__);
+  ASSERT (stack != NULL, "stack not set");
+  LockStack (stack);
+
+  if (where < 0) where += stack[0].Nobject;
+  if (where < 0) { 
+    UnlockStack (stack); 
+    return (NULL); 
+  }
+  if (where >= stack[0].Nobject) {
+    UnlockStack (stack); 
+    return (NULL);
+  }
+
+  object = stack[0].object[where];
+  RemoveStackEntry (stack, where);
+  UnlockStack (stack); 
+  return (object);
+}
+
+/* get object from stack which matches name */
+void *PullStackByName (Stack *stack, char *name) {
+
+  int i;
+  void *object;
+
+  PrintStackInfo (stack, __func__);
+  ASSERT (stack != NULL, "stack not set");
+  LockStack (stack);
+
+  for (i = 0; i < stack[0].Nobject; i++) {
+    if (strcasecmp (stack[0].name[i], name)) continue;
+
+    /* here is the element of interest */
+    object = stack[0].object[i];
+    RemoveStackEntry (stack, i);
+    UnlockStack (stack); 
+    return (object);
+  }
+  UnlockStack (stack); 
+  return (NULL);
+}
+
+/* get object from point in stack (negative == distance from end) */
+void *PullStackByID (Stack *stack, int id) {
+
+  int i;
+  void *object;
+  
+  PrintStackInfo (stack, __func__);
+  ASSERT (stack != NULL, "stack not set");
+  LockStack (stack);
+
+  for (i = 0; i < stack[0].Nobject; i++) {
+    if (stack[0].id[i] != id) continue;
+
+    /* here is the element of interest */
+    object = stack[0].object[i];
+    RemoveStackEntry (stack, i);
+    UnlockStack (stack); 
+    return (object);
+  }
+  UnlockStack (stack); 
+  return (NULL);
+}
+
+/* should only be called if you know where is a valid entry */
+int RemoveStackEntry (Stack *stack, int where) {
+
+  int i;
+
+  PrintStackInfo (stack, __func__);
+  ASSERT (stack != NULL, "stack not set");
+
+  if (where < 0) abort();
+  if (where >= stack[0].Nobject) abort();
+  if (stack[0].Nobject < 1) abort();
+
+  /* shift the remaining entries by one */
+  /* XXX free associated memory */
+  stack[0].Nobject --;
+  for (i = where; i < stack[0].Nobject; i++) {
+    stack[0].object[i] = stack[0].object[i+1];
+    stack[0].name[i]   = stack[0].name[i+1];
+    stack[0].id[i]     = stack[0].id[i+1];
+  }
+  return (TRUE);
+}
+
+/* should only be called if you manually lock the stack */
+void *RemoveStackByID (Stack *stack, int id) {
+
+  int i;
+  void *object;
+  
+  PrintStackInfo (stack, __func__);
+  ASSERT (stack != NULL, "stack not set");
+  for (i = 0; i < stack[0].Nobject; i++) {
+    if (stack[0].id[i] != id) continue;
+
+    /* here is the element of interest */
+    object = stack[0].object[i];
+    RemoveStackEntry (stack, i);
+    return (object);
+  }
+  return (NULL);
+}
+
+void LockStack (Stack *stack) {
+# ifdef THREADED
+  PrintStackInfo (stack, __func__);
+  ASSERT (stack != NULL, "stack not set");
+  pthread_mutex_lock (&stack[0].mutex);
+# endif
+  return;
+}
+
+void UnlockStack (Stack *stack) {
+# ifdef THREADED
+  PrintStackInfo (stack, __func__);
+  ASSERT (stack != NULL, "stack not set");
+  pthread_mutex_unlock (&stack[0].mutex);
+# endif
+  return;
+}
Index: /branches/sc_branches/pantasks_condor/pcontrol/StackOpsLL.c
===================================================================
--- /branches/sc_branches/pantasks_condor/pcontrol/StackOpsLL.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/pcontrol/StackOpsLL.c	(revision 34783)
@@ -0,0 +1,227 @@
+# include "pcontrol.h"
+
+/* Stacks and thread locks: interacting with the Stacks needs to be thread-safe so that the user may
+ * perform operations which interact with the stacks at the same time that the background loops
+ * check the current status of the jobs and hosts in the different stacks.  The simplest way in
+ * which the stacks are made thread safe is to lock them with a mutex before every interaction
+ */
+
+# define DEBUG 0
+
+void PrintStackInfo (Stack *stack, const char *func) {
+
+  if (!DEBUG) return;
+  fprintf (stderr, "%s: %p  ", func, stack);
+  fprintf (stderr, "objects: %p  ", stack[0].object);
+  fprintf (stderr, "Nobjects: %d (linked list)\n", stack[0].Nobject);
+}
+
+/* allocate stack: create head node (empty node?) */
+Stack *InitStack () {
+
+  Stack *stack;
+
+  ALLOCATE (stack, Stack, 1);
+
+  stack->Nobject = 0;
+  stack->head = NULL;
+  stack->tail = NULL;
+
+# ifdef THREADED
+  pthread_mutex_init (&stack[0].mutex, NULL);
+# endif
+  return (stack);
+}
+
+void FreeStack (Stack *stack) {
+
+  ASSERT (!stack->head, "stack items not freed");
+  ASSERT (!stack->tail, "stack items not freed");
+  free (stack);
+}
+
+/* STACK_TOP == 0, STACK_BOTTOM == -1 
+   in this version, entries by seq number other than head and tail are not allowed
+ */
+
+StackItem InitStackItem (void *object, char *name, int id) {
+
+  StackItem *item;
+  ALLOCATE (item, StackItem, 1);
+  item->object = object;
+  item->name = name;
+  item->id = id;
+
+  return item;
+}
+
+/* push object on stack at given location */
+int PushStack (Stack *stack, int where, void *object, int id, char *name) {
+
+  int i;
+
+  ASSERT (where == STACK_TOP || where == STACK_BOTTOM, "invalid location");
+  ASSERT (stack != NULL, "stack not set");
+
+  PrintStackInfo (stack, __func__);
+  LockStack (stack);
+
+  // make this a StackItem generator
+  StackItem *item = InitStackItem (object, name, id);
+  ALLOCATE (item, StackItem, 1);
+
+  /* there is a special case when the stack is empty.  in this case, we have to update
+     both head and tail.
+   */
+  if (!stack->head && !stack->tail) {
+    stack->head = item;
+    stack->tail = item;
+    stack->Nobject ++;
+    UnlockStack (stack);
+    return (TRUE);
+  }
+
+  if (where == STACK_TOP) {
+    StackHead *oldHead = stack->head;
+    stack->head = item;
+    item->next = oldHead;
+    oldHead->prev = item;
+  } else {
+    StackHead *oldTail = stack->tail;
+    stack->tail = item;
+    item->prev = oldTail;
+    oldTail->next = item;
+  }
+  stack->Nobject ++;
+  UnlockStack (stack);
+  return (TRUE);
+}
+
+/* get object from specified point in stack (negative == distance from end) */
+void *PullStackByLocation (Stack *stack, int where) {
+
+  void *object;
+  
+  ASSERT (where == STACK_TOP || where == STACK_BOTTOM, "invalid location");
+  ASSERT (stack != NULL, "stack not set");
+
+  PrintStackInfo (stack, __func__);
+  LockStack (stack);
+
+  if (
+
+  object = stack[0].object[where];
+  RemoveStackEntry (stack, where);
+  UnlockStack (stack); 
+  return (object);
+}
+
+/* get object from stack which matches name */
+void *PullStackByName (Stack *stack, char *name) {
+
+  int i;
+  void *object;
+
+  PrintStackInfo (stack, __func__);
+  ASSERT (stack != NULL, "stack not set");
+  LockStack (stack);
+
+  for (i = 0; i < stack[0].Nobject; i++) {
+    if (strcasecmp (stack[0].name[i], name)) continue;
+
+    /* here is the element of interest */
+    object = stack[0].object[i];
+    RemoveStackEntry (stack, i);
+    UnlockStack (stack); 
+    return (object);
+  }
+  UnlockStack (stack); 
+  return (NULL);
+}
+
+/* get object from point in stack (negative == distance from end) */
+void *PullStackByID (Stack *stack, int id) {
+
+  int i;
+  void *object;
+  
+  PrintStackInfo (stack, __func__);
+  ASSERT (stack != NULL, "stack not set");
+  LockStack (stack);
+
+  for (i = 0; i < stack[0].Nobject; i++) {
+    if (stack[0].id[i] != id) continue;
+
+    /* here is the element of interest */
+    object = stack[0].object[i];
+    RemoveStackEntry (stack, i);
+    UnlockStack (stack); 
+    return (object);
+  }
+  UnlockStack (stack); 
+  return (NULL);
+}
+
+/* should only be called if you know where is a valid entry */
+int RemoveStackEntry (Stack *stack, int where) {
+
+  int i;
+
+  PrintStackInfo (stack, __func__);
+  ASSERT (stack != NULL, "stack not set");
+
+  if (where < 0) abort();
+  if (where >= stack[0].Nobject) abort();
+  if (stack[0].Nobject < 1) abort();
+
+  /* shift the remaining entries by one */
+  /* XXX free associated memory */
+  stack[0].Nobject --;
+  for (i = where; i < stack[0].Nobject; i++) {
+    stack[0].object[i] = stack[0].object[i+1];
+    stack[0].name[i]   = stack[0].name[i+1];
+    stack[0].id[i]     = stack[0].id[i+1];
+  }
+  return (TRUE);
+}
+
+/* should only be called if you manually lock the stack */
+void *RemoveStackByID (Stack *stack, int id) {
+
+  int i;
+  void *object;
+  
+  PrintStackInfo (stack, __func__);
+  ASSERT (stack != NULL, "stack not set");
+  for (i = 0; i < stack[0].Nobject; i++) {
+    if (stack[0].id[i] != id) continue;
+
+    /* here is the element of interest */
+    object = stack[0].object[i];
+    RemoveStackEntry (stack, i);
+    return (object);
+  }
+  return (NULL);
+}
+
+void LockStack (Stack *stack) {
+# ifdef THREADED
+  PrintStackInfo (stack, __func__);
+  ASSERT (stack != NULL, "stack not set");
+  pthread_mutex_lock (&stack[0].mutex);
+# endif
+  return;
+}
+
+void UnlockStack (Stack *stack) {
+# ifdef THREADED
+  PrintStackInfo (stack, __func__);
+  ASSERT (stack != NULL, "stack not set");
+  pthread_mutex_unlock (&stack[0].mutex);
+# endif
+  return;
+}
+
+/*** I need a function to sort a stack by something.
+     I also need to replace these arrays with linked lists.
+ ***/
Index: /branches/sc_branches/pantasks_condor/pcontrol/StartHost.c
===================================================================
--- /branches/sc_branches/pantasks_condor/pcontrol/StartHost.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/pcontrol/StartHost.c	(revision 34783)
@@ -0,0 +1,51 @@
+# include "pcontrol.h"
+# define RETRY_BASE 10.0
+
+int StartHost (Host *host) {
+
+  int pid;
+  int stdio[3];
+  char command[64], shell[64];
+  struct timeval now;
+  float delta;
+
+  /* perhaps change the name of these config variables... */
+  if (VarConfig ("COMMAND", "%s", command) == NULL) strcpy (command, "ssh");
+  if (VarConfig ("SHELL", "%s", shell)     == NULL) strcpy (shell, "pclient");
+
+  if (VerboseMode()) gprint (GP_ERR, "starting remote connection to %s...", host[0].hostname);
+
+  int errorInfo;
+  pid = rconnect (command, host[0].hostname, shell, stdio, &errorInfo, TRUE);
+  if (!pid) {     
+    /** failure to start: extend retry period **/
+    if (VerboseMode()) gprint (GP_ERR, "failure to start %s (error %d)\n", host[0].hostname, errorInfo);
+    gettimeofday (&now, (void *) NULL);
+    if (ZTIME(host[0].next_start_try) || ZTIME(host[0].last_start_try)) {
+      /* reset retry period if either is zero */
+      delta = RETRY_BASE;
+    } else {
+      delta = MAX(1.0, 2*DTIME (host[0].next_start_try, host[0].last_start_try));
+    }
+    host[0].next_start_try.tv_sec  = now.tv_sec  + delta;
+    host[0].next_start_try.tv_usec = now.tv_usec;
+    host[0].last_start_try.tv_sec  = now.tv_sec;
+    host[0].last_start_try.tv_usec = now.tv_usec;
+    PutHost (host, PCONTROL_HOST_DOWN, STACK_BOTTOM);
+    return (FALSE);
+  }
+  host[0].next_start_try.tv_sec  = 0;
+  host[0].next_start_try.tv_usec = 0;
+  host[0].last_start_try.tv_sec  = 0;
+  host[0].last_start_try.tv_usec = 0;
+
+  // set the connection time
+  gettimeofday (&host[0].connect_time, (void *) NULL);
+
+  host[0].stdin_fd  = stdio[0];
+  host[0].stdout_fd = stdio[1];
+  host[0].stderr_fd = stdio[2];
+  host[0].pid       = pid;
+  PutHost (host, PCONTROL_HOST_IDLE, STACK_BOTTOM);
+  return (TRUE);
+}
Index: /branches/sc_branches/pantasks_condor/pcontrol/StartJob.c
===================================================================
--- /branches/sc_branches/pantasks_condor/pcontrol/StartJob.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/pcontrol/StartJob.c	(revision 34783)
@@ -0,0 +1,130 @@
+# include "pcontrol.h"
+
+// job and host are bound together (why pass in both?)
+int StartJob (Job *job, Host *host) {
+
+  int  i, Nline, status;
+  char *line;
+
+  /* job must have assigned host */
+  ASSERT (job != NULL, "missing job");
+  ASSERT (host != NULL, "missing host");
+  ASSERT (host == (Host *) job[0].host, "invalid host");
+  ASSERT (job  == (Job *) host[0].job, "invalid job");
+
+  ResetJobOutput (&job[0].stdout_buf);
+  ResetJobOutput (&job[0].stderr_buf);
+
+  /* construct command line : job arg0 arg1 ... argN\n */
+  // arguments of the form @MAX_THREADS@ are replaced here
+  Nline = 10 + job[0].argc;
+  for (i = 0; i < job[0].argc; i++) {
+    Nline += strlen (job[0].argv[i]);
+  }
+  ALLOCATE (line, char, Nline);
+  bzero (line, Nline);
+  strcpy (line, "job");
+  if (job[0].priority) {
+    char tmp[64];
+    snprintf (tmp, 64, " -nice %d", job[0].priority);
+    strcat (line, tmp);
+  }
+  for (i = 0; i < job[0].argc; i++) {
+    strcat (line, " ");
+    if (!strcmp (job[0].argv[i], "@MAX_THREADS@")) {
+      char threads[10];
+      snprintf (threads, 10, "%5d", host[0].max_threads);
+      strcat (line, threads);
+      continue;
+    } 
+    strcat (line, job[0].argv[i]);
+  }
+
+  // fprintf (stderr, "command: %s\n", line);
+
+  status = PclientCommand (host, line, PCLIENT_PROMPT, PCONTROL_RESP_START_JOB);
+  free (line);
+
+  /* check on success of pclient command */
+  switch (status) {
+    case PCLIENT_DOWN:
+      // unlink host & job
+      if (VerboseMode()) gprint (GP_ERR, "host %s is down\n", host[0].hostname);
+      job[0].host = NULL;
+      host[0].job = NULL;
+      HarvestHost (host[0].pid);
+      PutHost (host, PCONTROL_HOST_DOWN, STACK_BOTTOM);
+      PutJob (job, PCONTROL_JOB_PENDING, STACK_BOTTOM);
+      return (FALSE);
+
+    case PCLIENT_GOOD:
+      job[0].realhost = strcreate (host[0].hostname);
+      job[0].pid = -1;
+      gettimeofday (&job[0].start, (void *) NULL);
+
+      if (VerboseMode()) gprint (GP_ERR, "started job on host %s\n", host[0].hostname);  
+      PutHost (host, PCONTROL_HOST_RESP, STACK_BOTTOM);
+      PutJob (job, PCONTROL_JOB_RESP, STACK_BOTTOM);
+      return (TRUE);
+
+    default:
+      ABORT ("unknown status for pclient command");  
+  }
+}
+
+// message has been received from the host, interpret results
+int StartJobResponse (Host *host) {
+  
+  int status;
+  char *p;
+  IOBuffer *buffer;
+  Job *job;
+
+  /* job must have assigned host */
+  ASSERT (host, "missing host");
+  ASSERT (host[0].job, "missing job");
+  buffer = &host[0].comms_buffer;
+  job = (Job *) host[0].job;
+
+  /* check on result of pclient command */
+  p = memstr (buffer[0].buffer, "PCLIENT_PID:", buffer[0].Nbuffer);
+  if (p == NULL) {
+      // failed to get a valid response.  kill the job and try again, 
+      // or accept a running process without a PID?
+      if (VerboseMode()) gprint (GP_ERR, "failed to get a valid PID, trying to continue without\n");
+      PutHost (host, PCONTROL_HOST_BUSY, STACK_BOTTOM);
+      PutJob (job, PCONTROL_JOB_BUSY, STACK_BOTTOM);
+      gettimeofday (&job[0].start, NULL);
+      return (TRUE);
+  }
+
+  sscanf (p, "%*s %d", &status);
+  switch (status) {
+    case -1:
+      if (VerboseMode()) gprint (GP_ERR, "error in pclient child\n");
+      // unlink host & job
+      job[0].host = NULL;
+      host[0].job = NULL;
+      HarvestHost (host[0].pid);
+      PutHost (host, PCONTROL_HOST_DOWN, STACK_BOTTOM);
+      PutJob (job, PCONTROL_JOB_PENDING, STACK_BOTTOM);
+      return (FALSE);
+
+    case -2:
+      ABORT ("syntax error in pclient command");
+
+    case -3:
+      ABORT ("existing child on pclient");
+
+    default:
+      if (VerboseMode()) gprint (GP_ERR, "message received (StartJobResponse)\n");  
+      job[0].pid = status;
+      PutHost (host, PCONTROL_HOST_BUSY, STACK_BOTTOM);
+      PutJob (job, PCONTROL_JOB_BUSY, STACK_BOTTOM);
+      gettimeofday (&job[0].start, NULL);
+      return (TRUE);
+  }
+
+  /* we should never reach here */
+  ABORT ("should not reach here (StartJob)");
+}
Index: /branches/sc_branches/pantasks_condor/pcontrol/StopHosts.c
===================================================================
--- /branches/sc_branches/pantasks_condor/pcontrol/StopHosts.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/pcontrol/StopHosts.c	(revision 34783)
@@ -0,0 +1,278 @@
+# include "pcontrol.h"
+
+// we attempt to harvest the 'down' hosts in HarvestHost.  However, sometimes the
+// child is busy and does not exit in the timeout period.  we need to keep a list and
+// try again occasionally to free up the needed resources
+static int NUNHARVESTED = 0;
+static int Nunharvested = 0;
+static int *unharvested = NULL;
+
+void DownHost (Host *host) {
+  CLOSE (host[0].stdin_fd);
+  CLOSE (host[0].stdout_fd);
+  CLOSE (host[0].stderr_fd);
+  host[0].job = NULL;
+  PutHost (host, PCONTROL_HOST_DOWN, STACK_BOTTOM);
+}
+
+void OffHost (Host *host) {
+  CLOSE (host[0].stdin_fd);
+  CLOSE (host[0].stdout_fd);
+  CLOSE (host[0].stderr_fd);
+  host[0].job = NULL;
+  PutHost (host, PCONTROL_HOST_OFF, STACK_BOTTOM);
+}
+
+/* for use by shutdown: force machines which are up to go down
+   wait for a little while for the client thread to take care 
+   of them
+*/
+   
+int DownHosts () {
+
+  int i, Nobject, Nwait;
+  Stack *stack;
+  Host  *host;
+
+  SetCheckPoint (); // ensure we can find the specified host
+  stack = GetHostStack (PCONTROL_HOST_IDLE);
+  ASSERT (stack != NULL, "stack missing");
+  Nobject = stack[0].Nobject;
+  for (i = 0; i < Nobject; i++) {
+    host = PullStackByLocation (stack, STACK_TOP);
+    if (host == NULL) continue;
+    host[0].markoff = TRUE;
+    PutHost (host, PCONTROL_HOST_IDLE, STACK_BOTTOM);
+  }
+
+  stack = GetHostStack (PCONTROL_HOST_BUSY);
+  ASSERT (stack != NULL, "stack missing");
+  Nobject = stack[0].Nobject;
+  for (i = 0; i < Nobject; i++) {
+    host = PullStackByLocation (stack, STACK_TOP);
+    if (host == NULL) continue;
+    host[0].markoff = TRUE;
+    PutHost (host, PCONTROL_HOST_IDLE, STACK_BOTTOM);
+  }
+  ClearCheckPoint ();
+
+  Nwait = 0;
+  stack = GetHostStack (PCONTROL_HOST_IDLE);
+  ASSERT (stack != NULL, "stack missing");
+
+  gprint (GP_ERR, "waiting for clients to exit");
+  while ((Nwait < 15) && stack[0].Nobject) {
+    gprint (GP_ERR, ".");
+    usleep (100000); // wait for clients to exit
+    Nwait++;
+  }
+  gprint (GP_ERR, "\n");
+  if (stack[0].Nobject) {
+    gprint (GP_ERR, "trouble shutting down all pclient instances: %d still alive\n", stack[0].Nobject);
+  } else {
+    gprint (GP_ERR, "done\n");
+  }
+  return (TRUE);
+}
+
+int StopHost (Host *host, int mode) {
+
+  int       status;
+
+  switch (mode) {
+    case PCONTROL_HOST_DOWN:
+      status = PclientCommand (host, "exit", "Goodbye", PCONTROL_RESP_DOWN_HOST);
+      break;
+    case PCONTROL_HOST_OFF:
+      status = PclientCommand (host, "exit", "Goodbye", PCONTROL_RESP_STOP_HOST);
+      break;
+    default:
+      ABORT ("programming error: invalid StopHost mode");
+  }
+
+  /* check on success of pclient command */
+  switch (status) {
+    case PCLIENT_DOWN:
+      PutHost (host, PCONTROL_HOST_RESP, STACK_BOTTOM);
+      return (TRUE);
+
+    case PCLIENT_GOOD:
+      if (VerboseMode()) gprint (GP_ERR, "stop host %s\n", host[0].hostname);  
+      FlushIOBuffer (&host[0].comms_buffer);
+      PutHost (host, PCONTROL_HOST_RESP, STACK_BOTTOM);
+      return (TRUE);
+
+    default:
+      ABORT ("unknown status for pclient command");  
+  }
+  ABORT ("should not reach here");  
+}
+
+int StopHostResponse (Host *host) {
+
+  OffHost (host);
+  HarvestHost (host[0].pid);
+  return (TRUE);
+}
+
+int DownHostResponse (Host *host) {
+
+  DownHost (host);
+  HarvestHost (host[0].pid);
+  return (TRUE);
+}
+
+/* the host is thought to be down; check for child exit status */
+int HarvestHost (int pid) {
+  
+  int i, result, waitstatus;
+
+  if (VerboseMode()) gprint (GP_ERR, "harvesting within thread\n");
+  if (VerboseMode()) gprint (GP_ERR, "child process %d is down, wait for exit status\n", pid);
+  
+  // Loop a few times waiting for child to exit
+  for (i = 0; i < 50; i++) {
+    result = waitpid (pid, &waitstatus, WNOHANG);
+    if ((result == -1) && (errno == ECHILD)) {
+      usleep (10000); // wait for child to exit
+      continue;
+    } else {
+      break;
+    }
+  }
+  switch (result) {
+    case -1:  /* error with waitpid */
+      switch (errno) {
+	case ECHILD:
+	  gprint (GP_ERR, "HarvestHost: unknown PID (%d), not a child proc\n", pid);
+	  gprint (GP_ERR, "did process already exit?  programming error?\n");
+	  break;
+	case EINTR:
+	case EINVAL:
+	default:
+	  perror ("unexpected error");
+	  ABORT ("(HarvestHost)");
+      }
+      break;
+      
+    case 0:
+      gprint (GP_ERR, "HarvestHost: child with connection to remote host failed to exit: may be hung\n");
+      AddZombie(pid);
+      break;
+
+    default:
+      if (result != pid) {
+	gprint (GP_ERR, "waitpid error: mis-matched PID (%d vs %d).  programming error\n", result, pid);
+	pcontrol_exit (58);
+      }
+      
+      if (WIFEXITED(waitstatus)) {
+	if (VerboseMode()) gprint (GP_ERR, "child exited with status %d\n", WEXITSTATUS(waitstatus));
+      }
+      if (WIFSIGNALED(waitstatus)) {
+	if (VerboseMode()) gprint (GP_ERR, "child crashed with status %d\n", WTERMSIG(waitstatus));
+      }
+      if (WIFSTOPPED(waitstatus)) {
+        gprint (GP_ERR, "waitpid returns 'stopped': programming error\n");
+	pcontrol_exit (59);
+      }
+  }
+  return (TRUE);
+}
+
+int AddZombie(int pid) {
+
+  if (unharvested == NULL) {
+    NUNHARVESTED = 128;
+    ALLOCATE (unharvested, int, NUNHARVESTED);
+    memset (unharvested, 0, NUNHARVESTED*sizeof(int));
+  }
+  unharvested[Nunharvested] = pid;
+
+  Nunharvested ++;
+  if (Nunharvested >= NUNHARVESTED) {
+    NUNHARVESTED += 128;
+    REALLOCATE (unharvested, int, NUNHARVESTED);
+    memset (&unharvested[Nunharvested], 0, (NUNHARVESTED - Nunharvested)*sizeof(int));
+  }
+  return TRUE;
+}
+
+int DelZombies() {
+
+  int i, j;
+
+  if (!unharvested) return FALSE;
+  if (!Nunharvested) return FALSE;
+  if (!NUNHARVESTED) return FALSE;
+
+  int *newlist = NULL;
+
+  ALLOCATE (newlist, int, NUNHARVESTED);
+  memset (newlist, 0, NUNHARVESTED*sizeof(int));
+
+  j = 0;
+  for (i = 0; i < NUNHARVESTED; i++) {
+    if (!unharvested[i]) continue;
+    newlist[j] = unharvested[i];
+    j++;
+  }
+  free (unharvested);
+  unharvested = newlist;
+  Nunharvested = j;
+  return TRUE;
+}
+
+int CheckZombies() {
+
+  int pid, i, result, waitstatus;
+
+  if (!unharvested) return FALSE;
+  if (!Nunharvested) return FALSE;
+  if (!NUNHARVESTED) return FALSE;
+
+  for (i = 0; i < Nunharvested; i++) {
+    if (!unharvested[i]) continue;
+    pid = unharvested[i];
+    result = waitpid (pid, &waitstatus, WNOHANG);
+    switch (result) {
+      case -1:  /* error with waitpid */
+	switch (errno) {
+	  case ECHILD:
+	    gprint (GP_ERR, "CheckZombies: unknown PID (%d), not a child proc\n", pid);
+	    gprint (GP_ERR, "did process already exit?  programming error?\n");
+	    break;
+	  case EINTR:
+	  case EINVAL:
+	  default:
+	    perror ("unexpected error");
+	    ABORT ("CheckZombies impossible condition");
+	}
+	break;
+      
+      case 0:
+	if (VerboseMode()) gprint (GP_ERR, "CheckZombies: still waiting on %d\n", pid);
+	break;
+
+      default:
+	if (result != pid) {
+	  gprint (GP_ERR, "waitpid error: mis-matched PID (%d vs %d).  programming error\n", result, pid);
+	  ABORT ("CheckZombies impossible condition");
+	}
+	
+	if (WIFEXITED(waitstatus)) {
+	  if (VerboseMode()) gprint (GP_ERR, "child exited with status %d\n", WEXITSTATUS(waitstatus));
+	}
+	if (WIFSIGNALED(waitstatus)) {
+	  if (VerboseMode()) gprint (GP_ERR, "child crashed with status %d\n", WTERMSIG(waitstatus));
+	}
+	if (WIFSTOPPED(waitstatus)) {
+	  ABORT ("waitpid returns 'stopped': programming error\n");
+	}
+	unharvested[i] = 0;
+	break;
+    }
+  }
+  DelZombies();
+  return (TRUE);
+}
Index: /branches/sc_branches/pantasks_condor/pcontrol/check.c
===================================================================
--- /branches/sc_branches/pantasks_condor/pcontrol/check.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/pcontrol/check.c	(revision 34783)
@@ -0,0 +1,88 @@
+# include "pcontrol.h"
+
+int check (int argc, char **argv) {
+
+  int N, Save;
+  int JobID, HostID;
+
+  Stack *stack = NULL;
+  Job *job = NULL;
+  Host *host = NULL;
+
+  Save = FALSE;
+  if ((N = get_argument (argc, argv, "-save"))) {
+    remove_argument (N, &argc, argv);
+    Save = TRUE;
+  }
+
+  if (argc != 3) {
+    gprint (GP_LOG, "USAGE: check job (JobID)\n");
+    gprint (GP_LOG, "USAGE: check host (HostID)\n");
+    return (FALSE);
+  }
+
+  if (!strcasecmp (argv[1], "JOB")) {
+    JobID = GetID (argv[2]);
+    if (!JobID) {
+      gprint (GP_ERR, "invalid job id %s\n", argv[2]);
+      return (FALSE);
+    }
+
+    stack = GetJobStack (PCONTROL_JOB_ALLJOBS);
+    job = PullStackByID (stack, JobID);
+    if (job == NULL) {
+      gprint (GP_LOG, "job not found\n");
+      return (FALSE);
+    }
+
+    gprint (GP_LOG, "STATUS %s\n", GetJobStackName(job[0].stack));
+    gprint (GP_LOG, "EXITST %d\n", job[0].exit_status);
+    gprint (GP_LOG, "STDOUT %d\n", job[0].stdout_buf.size);
+    gprint (GP_LOG, "STDERR %d\n", job[0].stderr_buf.size);
+    gprint (GP_LOG, "DTIME %lf\n", job[0].dtime);
+    if (job[0].realhost) {
+	gprint (GP_LOG, "HOSTNAME %s\n", job[0].realhost);
+    } else {
+	gprint (GP_LOG, "HOSTNAME NONE\n");
+    }
+
+    if (Save) {
+	set_str_variable ("JOB_STATUS", GetJobStackName(job[0].stack));
+	set_int_variable ("JOB_EXITST", job[0].exit_status);
+	set_int_variable ("JOB_STDOUT_SIZE", job[0].stdout_buf.size);
+	set_int_variable ("JOB_STDERR_SIZE", job[0].stderr_buf.size);
+	set_variable ("JOB_DTIME", job[0].dtime);
+	set_str_variable ("JOB_HOSTNAME", job[0].hostname);
+	if (job[0].realhost) {
+	    set_str_variable ("JOB_REALHOST", job[0].realhost);
+	} else {
+	    set_str_variable ("JOB_REALHOST", "NONE");
+	}
+    }
+
+    PushStack (stack, STACK_BOTTOM, job, job[0].JobID, job[0].argv[0]);
+    return (TRUE);
+  }
+
+  if (!strcasecmp (argv[1], "HOST")) {
+    HostID = atoi (argv[2]);
+
+    stack = GetHostStack (PCONTROL_HOST_ALLHOSTS);
+    host = PullStackByID (stack, HostID);
+    if (host == NULL) {
+      gprint (GP_LOG, "host not found\n");
+      return (FALSE);
+    }
+    gprint (GP_LOG, "host %s\n", GetHostStackName(host[0].stack));
+
+    if (Save) {
+	set_str_variable ("HOST_STATE", GetHostStackName(host[0].stack));
+    }
+
+    PushStack (stack, STACK_BOTTOM, host, host[0].HostID, host[0].hostname);
+    return (TRUE);
+  }
+
+  gprint (GP_LOG, "unknown item to check\n");
+  return (FALSE);
+}
Index: /branches/sc_branches/pantasks_condor/pcontrol/delete.c
===================================================================
--- /branches/sc_branches/pantasks_condor/pcontrol/delete.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/pcontrol/delete.c	(revision 34783)
@@ -0,0 +1,50 @@
+# include "pcontrol.h"
+
+int delete (int argc, char **argv) {
+
+  Job *job;
+  int JobID;
+
+  if (argc < 2) {
+    gprint (GP_ERR, "USAGE: delete (JobID)\n");
+    return (FALSE);
+  }
+  JobID = GetID (argv[1]);
+  if (!JobID) {
+    gprint (GP_ERR, "invalid job id %s\n", argv[1]);
+    return (FALSE);
+  }
+      
+  /* use a string interp to convert JobIDs to ints ? */
+
+  job = PullJobFromStackByID (PCONTROL_JOB_PENDING, JobID);
+  if (job != NULL) goto found;
+
+  job = PullJobFromStackByID (PCONTROL_JOB_CRASH, JobID);
+  if (job != NULL) goto found;
+
+  job = PullJobFromStackByID (PCONTROL_JOB_EXIT, JobID);
+  if (job != NULL) goto found;
+
+  gprint (GP_ERR, "job %s not PENDING, CRASH, EXIT\n", argv[1]);
+  return (FALSE);
+  
+found:
+  {
+    int j;
+    gprint (GP_LOG, "deleting job  %s  %d  ", job[0].hostname, job[0].argc);
+    for (j = 0; j < job[0].argc; j++) {
+      gprint (GP_LOG, "%s ", job[0].argv[j]);
+    }
+    PrintID (GP_LOG, job[0].JobID);
+    gprint (GP_LOG, "\n");
+  }  
+  DelJob (job);
+
+  return (TRUE);
+}
+
+/**** at the moment, this function requires the job to be in the correct state
+      to be deleted.  This should be changed to kill, then delete job if it is 
+      not in the correct state 
+****/
Index: /branches/sc_branches/pantasks_condor/pcontrol/help/host
===================================================================
--- /branches/sc_branches/pantasks_condor/pcontrol/help/host	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/pcontrol/help/host	(revision 34783)
@@ -0,0 +1,1 @@
+empty
Index: /branches/sc_branches/pantasks_condor/pcontrol/host.c
===================================================================
--- /branches/sc_branches/pantasks_condor/pcontrol/host.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/pcontrol/host.c	(revision 34783)
@@ -0,0 +1,101 @@
+# include "pcontrol.h"
+
+int host (int argc, char **argv) {
+
+  int N, max_threads;
+  IDtype HostID;
+  Host *host;
+  Stack *AllHosts;
+
+  max_threads = 0;
+  if ((N = get_argument (argc, argv, "-threads"))) {
+    remove_argument (N, &argc, argv);
+    max_threads = atoi(argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  if (argc != 3) goto usage;
+
+  AllHosts = GetHostStack (PCONTROL_HOST_ALLHOSTS);
+
+  if (!strcasecmp (argv[1], "ADD")) {
+    HostID = AddHost (argv[2], max_threads);
+    gprint (GP_LOG, "HostID: %d\n", (int) HostID);
+    return (TRUE);
+  }
+
+  if (max_threads) goto usage;
+
+  // this one is safe from in-flight entries: no one else pulls from OFF
+  if (!strcasecmp (argv[1], "ON")) {
+    host = PullHostFromStackByName (PCONTROL_HOST_OFF, argv[2]);
+    if (!host) {
+      gprint (GP_LOG, "host %s is not OFF\n", argv[2]);
+      return (FALSE);
+    }
+    host[0].markoff = FALSE;
+    DownHost (host);
+    return (TRUE);
+  }
+
+  // this is a race condition with "CheckDownHosts", but the only 
+  // consequence is that both StartHost and reset set the times to 0.0
+  if (!strcasecmp (argv[1], "RETRY")) {
+    // no need to use a check point [thief: CheckDownHost (DOWN->IDLE)]
+    host = PullHostFromStackByName (PCONTROL_HOST_ALLHOSTS, argv[2]);
+    if (!host) {
+      gprint (GP_LOG, "host %s not found\n", argv[2]);
+      return (FALSE);
+    }
+    if (host[0].stack != PCONTROL_HOST_DOWN) {
+      gprint (GP_LOG, "host %s is not DOWN\n", argv[2]);
+      return (FALSE);
+    }
+    /* reset time, place back on ALLHOSTS stack */
+    host[0].next_start_try.tv_sec  = 0;
+    host[0].next_start_try.tv_usec = 0;
+    host[0].last_start_try.tv_sec  = 0;
+    host[0].last_start_try.tv_usec = 0;
+    PushStack (AllHosts, STACK_BOTTOM, host, host[0].HostID, host[0].hostname);
+    return (TRUE);
+  }
+
+  if (!strcasecmp (argv[1], "CHECK")) {
+    host = PullHostFromStackByName (PCONTROL_HOST_ALLHOSTS, argv[2]);
+    if (host == NULL) {
+      gprint (GP_LOG, "host %s not found\n", argv[2]);
+      return (FALSE);
+    }
+    gprint (GP_LOG, "host %s is %s\n", argv[2], GetHostStackName (host[0].stack));
+    PushStack (AllHosts, STACK_BOTTOM, host, host[0].HostID, host[0].hostname);
+    return (TRUE);
+  }
+
+  if (!strcasecmp (argv[1], "OFF")) {
+    host = PullHostFromStackByName (PCONTROL_HOST_ALLHOSTS, argv[2]);
+    if (host == NULL) {
+      gprint (GP_LOG, "host %s not found\n", argv[2]);
+      return (FALSE);
+    }
+    host[0].markoff = TRUE;
+    PushStack (AllHosts, STACK_BOTTOM, host, host[0].HostID, host[0].hostname);
+    return (TRUE);
+  }
+
+  // this one is safe from in-flight entries: no one else pulls from OFF
+  if (!strcasecmp (argv[1], "DELETE")) {
+    host = PullHostFromStackByName (PCONTROL_HOST_OFF, argv[2]);
+    if (!host) {
+      gprint (GP_LOG, "host %s is not OFF\n", argv[2]);
+      return (FALSE);
+    }
+    DelHost (host);
+    return (TRUE);
+  }
+  
+usage:
+  gprint (GP_LOG, "USAGE: host (command) (hostname)\n");
+  gprint (GP_LOG, "  valid commands: add, on, retry, check, off, delete\n");
+  gprint (GP_LOG, "  -threads Nthreads is optional for 'add'\n");
+  return (FALSE);
+}
Index: /branches/sc_branches/pantasks_condor/pcontrol/hoststack.c
===================================================================
--- /branches/sc_branches/pantasks_condor/pcontrol/hoststack.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/pcontrol/hoststack.c	(revision 34783)
@@ -0,0 +1,34 @@
+# include "pcontrol.h"
+
+int hoststack (int argc, char **argv) {
+
+  int i;
+  Stack *stack;
+  Host *host;
+
+  if (argc != 2) {
+    gprint (GP_ERR, "USAGE: hoststack (hoststack)\n");
+    gprint (GP_ERR, "       (hoststack) : idle, busy, done, down, off\n");
+    return (FALSE);
+  }
+
+  /* select hoststack */
+  stack = GetHostStackByName (argv[1]);
+  if (stack == NULL) {
+    gprint (GP_ERR, "hoststack not found\n");
+    return (FALSE);
+  }
+
+  /* print list */
+  LockStack (stack);
+  gprint (GP_LOG, "Nhosts: %d\n", stack[0].Nobject);
+  for (i = 0; i < stack[0].Nobject; i++) {
+    host = stack[0].object[i];
+    gprint (GP_LOG, "%lld %s\n", host[0].HostID, host[0].hostname);
+  }
+  UnlockStack (stack);
+
+  return (TRUE);
+}
+
+// Safe with PTHREAD_MUTEX_INITIALIZER lock
Index: /branches/sc_branches/pantasks_condor/pcontrol/init.c
===================================================================
--- /branches/sc_branches/pantasks_condor/pcontrol/init.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/pcontrol/init.c	(revision 34783)
@@ -0,0 +1,60 @@
+# include "pcontrol.h"
+
+int check       PROTO((int, char **));
+int delete      PROTO((int, char **));
+int host        PROTO((int, char **));
+int hoststack   PROTO((int, char **));
+int job	        PROTO((int, char **));
+int jobstack    PROTO((int, char **));
+int kill_pc     PROTO((int, char **));
+int machines    PROTO((int, char **));
+int parameters  PROTO((int, char **));
+int run         PROTO((int, char **));
+int status      PROTO((int, char **));
+int stderr_pc   PROTO((int, char **));
+int stdout_pc   PROTO((int, char **));
+int verbose     PROTO((int, char **));
+int version     PROTO((int, char **));
+
+// pulse is only available in the un-threaded version
+int pulse       PROTO((int, char **));
+
+static Command cmds[] = {  
+  {1, "check",      check,      "get job or host status"},
+  {1, "delete",     delete,     "delete job"},
+  {1, "host",       host,       "add / delete / modify host"},
+  {1, "hoststack",  hoststack,  "list hosts for a single stack"},
+  {1, "job",        job,        "add job"},
+  {1, "jobstack",   jobstack,   "list jobs for a single stack"},
+  {1, "kill",       kill_pc,    "kill job"},
+  {1, "machines",   machines,   "list machines"},
+  {1, "parameters", parameters, "get / set system parameters"},
+  {1, "run",        run,        "set controller runlevel"},
+  {1, "status",     status,     "get system status"},
+  {1, "stderr",     stderr_pc,  "get stderr buffer for job"},
+  {1, "stdout",     stdout_pc,  "get stdout buffer for job"},
+  {1, "stop",       run,        "stop controller processing"},
+  {1, "verbose",    verbose,    "set the verbose mode for job"},
+  {1, "version",    version,    "show version information"},
+# ifndef THREADED   	        
+  {1, "pulse",      pulse,      "set system pulse"},
+# endif
+}; 
+
+void InitPcontrol () {
+  
+  int i;
+
+  for (i = 0; i < sizeof (cmds) / sizeof (Command); i++) {
+    AddCommand (&cmds[i]);
+  }
+  InitJobStacks ();
+  InitHostStacks ();
+  InitMachines ();
+}
+
+void FreePcontrol () {
+  FreeJobStacks ();
+  FreeHostStacks ();
+  FreeMachines ();
+}
Index: /branches/sc_branches/pantasks_condor/pcontrol/job.c
===================================================================
--- /branches/sc_branches/pantasks_condor/pcontrol/job.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/pcontrol/job.c	(revision 34783)
@@ -0,0 +1,93 @@
+# include "pcontrol.h"
+
+int job (int argc, char **argv) {
+
+  char *Host = NULL;
+  char **targv = NULL;
+  int i, N, Mode, targc, Timeout, priority;
+  IDtype JobID;
+  char **xhosts = NULL;
+  int Nxhosts = 0;
+  int NXHOSTS = 0;
+
+  if (get_argument (argc, argv, "-host") && get_argument (argc, argv, "+host")) {
+      gprint (GP_ERR, "ERROR: -host and +host are incompatible\n");
+      return (FALSE);
+  }    
+
+  if (get_argument (argc, argv, "-h")) goto usage;
+  if (get_argument (argc, argv, "-help")) goto usage;
+  if (get_argument (argc, argv, "--help")) goto usage;
+
+  Host = NULL;
+  Mode = PCONTROL_JOB_ANYHOST;
+  if ((N = get_argument (argc, argv, "-host"))) {
+    remove_argument (N, &argc, argv);
+    Host = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+    Mode = PCONTROL_JOB_WANTHOST;
+  }
+  if ((N = get_argument (argc, argv, "+host"))) {
+    remove_argument (N, &argc, argv);
+    Host = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+    Mode = PCONTROL_JOB_NEEDHOST;
+  }
+  if (Host == NULL) Host = strcreate ("anyhost");
+ 
+  Timeout = 100;
+  if ((N = get_argument (argc, argv, "-timeout"))) {
+    remove_argument (N, &argc, argv);
+    Timeout = atoi (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  priority = 0;
+  if ((N = get_argument (argc, argv, "-nice"))) {
+    remove_argument (N, &argc, argv);
+    priority = atoi (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  xhosts = NULL;
+  Nxhosts = 0;
+  NXHOSTS = 10;
+  while ((N = get_argument (argc, argv, "-xhost"))) {
+    if (xhosts == NULL) {
+      ALLOCATE (xhosts, char *, NXHOSTS);
+    }
+    remove_argument (N, &argc, argv);
+    xhosts[Nxhosts] = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+    Nxhosts ++;
+    if (Nxhosts == NXHOSTS) {
+      NXHOSTS += 10;
+      REALLOCATE (xhosts, char *, NXHOSTS);
+    }
+  }
+
+  if (argc < 2) goto usage;
+  
+  targc = argc - 1;
+  ALLOCATE (targv, char *, targc);
+  for (i = 1; i < argc; i++) {
+    targv[i-1] = strcreate (argv[i]);
+  }
+
+  // a JobID < 0 mean the job was not accepted
+  JobID = AddJob (Host, Mode, Timeout, priority, targc, targv, Nxhosts, xhosts);
+  gprint (GP_LOG, "JobID: %d\n", (int) JobID);
+  return (TRUE);
+
+ usage:
+    gprint (GP_ERR, "USAGE: job [options] (arg0) (arg1) ... (argN)\n");
+    gprint (GP_ERR, "  options: -host, +host, -timeout, -xhost (host)\n");
+    gprint (GP_ERR, "  arguments of the form @MAX_THREADS@ will be replaced when the job is launched\n");
+
+    FREE (Host);
+    for (i = 0; i < Nxhosts; i++) {
+      FREE (xhosts[i]);
+    }
+    FREE (xhosts);
+    return (FALSE);
+}
Index: /branches/sc_branches/pantasks_condor/pcontrol/jobstack.c
===================================================================
--- /branches/sc_branches/pantasks_condor/pcontrol/jobstack.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/pcontrol/jobstack.c	(revision 34783)
@@ -0,0 +1,40 @@
+# include "pcontrol.h"
+
+int jobstack (int argc, char **argv) {
+
+  int i;
+  Stack *stack;
+  Job *job;
+
+  if (argc != 2) {
+    gprint (GP_ERR, "USAGE: jobstack (jobstack)\n");
+    gprint (GP_ERR, "       (jobstack) : pending, busy, exit, crash, hung, done\n");
+    return (FALSE);
+  }
+
+  /* select jobstack */
+  stack = GetJobStackByName (argv[1]);
+  if (stack == NULL) {
+    gprint (GP_ERR, "jobstack not found\n");
+    return (FALSE);
+  }
+
+  /* print list */
+  LockStack (stack);
+  gprint (GP_LOG, "Njobs: %d\n", stack[0].Nobject);
+  for (i = 0; i < stack[0].Nobject; i++) {
+    job = stack[0].object[i];
+    /* PrintID (GP_LOG, job[0].JobID); */
+    gprint (GP_LOG, "%lld ", job[0].JobID);
+    if (job[0].realhost) {
+	gprint (GP_LOG, "%s   %s\n", job[0].argv[0], job[0].realhost);
+    } else {
+	gprint (GP_LOG, "%s  (%s)\n", job[0].argv[0], job[0].hostname);
+    }
+  }
+  UnlockStack (stack);
+
+  return (TRUE);
+}
+
+// Safe with PTHREAD_MUTEX_INITIALIZER lock
Index: /branches/sc_branches/pantasks_condor/pcontrol/kill.c
===================================================================
--- /branches/sc_branches/pantasks_condor/pcontrol/kill.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/pcontrol/kill.c	(revision 34783)
@@ -0,0 +1,24 @@
+# include "pcontrol.h"
+
+int kill_pc (int argc, char **argv) {
+
+  Job *job;
+  int JobID;
+
+  if (argc < 2) {
+    gprint (GP_ERR, "USAGE: kill (JobID)\n");
+    return (FALSE);
+  }
+  JobID = atoi (argv[1]);
+
+  /* XXX this function should only fail if a process is hung */
+  job = PullJobFromStackByID (PCONTROL_JOB_BUSY, JobID);
+  if (job == NULL) {
+    gprint (GP_ERR, "job %s not BUSY\n", argv[1]);
+    /* make output message more readable by scheduler */
+    return (FALSE);
+  }
+
+  PutJob (job, PCONTROL_JOB_KILL, STACK_BOTTOM);
+  return (TRUE);
+}
Index: /branches/sc_branches/pantasks_condor/pcontrol/machines.c
===================================================================
--- /branches/sc_branches/pantasks_condor/pcontrol/machines.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/pcontrol/machines.c	(revision 34783)
@@ -0,0 +1,9 @@
+# include "pcontrol.h"
+
+int machines (int argc, char **argv) {
+
+  PrintMachines ();
+
+  return (TRUE);
+}
+
Index: /branches/sc_branches/pantasks_condor/pcontrol/notes.txt
===================================================================
--- /branches/sc_branches/pantasks_condor/pcontrol/notes.txt	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/pcontrol/notes.txt	(revision 34783)
@@ -0,0 +1,69 @@
+
+2008.06.11
+
+  Upgrade: add a virtual stack of the jobs and hosts so that the user
+  status queries do not need to block until the processing thread is
+  caught up.
+
+  
+
+2008.04.21 
+
+  I am having trouble with pcontrol managing the clients.  The
+  functions which call PclientCommand expect to see a complete
+  response in the same call.  However, latency in the client means, if
+  the client is super busy, that this interaction will either result
+  in dropped messages (which look like HUNG machines) or in long
+  blocks against the pantasks/pcontrol interface.  
+
+  A possible solution may be to break the functions which call
+  PclientCommand into a PclientCommand and a PclientResponse call.
+  The PclientCommand would send the command and report any comms
+  errors in that interaction (pipe closed, interrupts, etc).  It would
+  return immediately, pushing the 
+
+-- pcontrol / pclient interactions
+
+StartJob:
+xmt: job argv[0] argv[1] ...
+rcv: 
+ STATUS (PID)
+ pclient:
+
+CheckBusyJob:
+xmt: status
+rcv: 
+ EXITST N
+ STDOUT N
+ STDERR N
+ STATUS (value)
+ pclient:
+
+CheckDoneHost:
+xmt: reset
+rcv: 
+ STATUS (status)
+ pclient:
+
+CheckHost:
+xmt: echo OK
+rcv:
+ OK
+ pclient:
+
+KillJob:
+xmt: reset
+rcv: 
+ STATUS (status)
+ pclient:
+
+ResetJob:
+xmt: reset
+rcv: 
+ STATUS (status)
+ pclient:
+
+StopHost:
+xmt: ext
+rcv: 
+ Goodbye
Index: /branches/sc_branches/pantasks_condor/pcontrol/parameters.c
===================================================================
--- /branches/sc_branches/pantasks_condor/pcontrol/parameters.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/pcontrol/parameters.c	(revision 34783)
@@ -0,0 +1,57 @@
+# include "pcontrol.h"
+
+int parameters (int argc, char **argv) {
+
+  int ivalue;
+  float value;
+
+  if (argc < 2) goto usage;
+  if (argc > 4) goto usage;
+  if (argc == 3) goto usage;
+  if ((argc == 4) && strcmp(argv[2], "=")) goto usage;
+
+  if (!strncasecmp (argv[1], "connect_time", strlen(argv[1]))) {
+    if (argc == 2) {
+      value = GetMaxConnectTime();
+      gprint (GP_LOG, "Max Connect Time : %f\n", value);
+      return (TRUE);
+    }
+
+    value = atof(argv[3]);
+    SetMaxConnectTime(value);
+    return (TRUE);
+  }
+
+  if (!strncasecmp (argv[1], "wanthost_wait", strlen(argv[1]))) {
+    if (argc == 2) {
+      value = GetMaxWantHostWait();
+      gprint (GP_LOG, "Max WantHost Wait : %f\n", value);
+      return (TRUE);
+    }
+
+    value = atof(argv[3]);
+    SetMaxWantHostWait(value);
+    return (TRUE);
+  }
+
+  if (!strncasecmp (argv[1], "unwanted_host_jobs", strlen(argv[1]))) {
+    if (argc == 2) {
+      ivalue = GetMaxUnwantedHostJobs();
+      gprint (GP_LOG, "Max Unwanted Host Jobs : %d\n", ivalue);
+      return (TRUE);
+    }
+
+    ivalue = atof(argv[3]);
+    SetMaxUnwantedHostJobs(ivalue);
+    return (TRUE);
+  }
+
+  usage:
+
+  gprint (GP_LOG, "USAGE: parameters (param) [= value])\n");
+  gprint (GP_LOG, "  valid parameters: connect_time, wanthost_wait, unwanted_host_jobs\n");
+  gprint (GP_LOG, "  (minimum matching word is allowed)\n");
+  gprint (GP_LOG, "  example: parameters connect = 2.0\n");
+  return (FALSE);
+}
+
Index: /branches/sc_branches/pantasks_condor/pcontrol/pcontrol.c.in
===================================================================
--- /branches/sc_branches/pantasks_condor/pcontrol/pcontrol.c.in	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/pcontrol/pcontrol.c.in	(revision 34783)
@@ -0,0 +1,108 @@
+# include "pcontrol.h"
+
+# define STDERR_FILE "pcontrol.log"
+
+# define opihi_name "PCONTROL"
+# define opihi_prompt "pcontrol: "
+# define opihi_description "pcontrol client shell\n"
+# define opihi_history ".pcontrol"
+# define opihi_rcfile ".pcontrolrc"
+
+/* program-dependent initialization */
+void program_init (int *argc, char **argv) {
+  
+# ifdef THREADED  
+  pthread_t clientsThread;
+# endif
+
+  auto_break = TRUE;
+
+  /* load the commands used by this implementation */
+  InitBasic ();
+  InitPcontrol ();
+
+  /* set global signal masks (these apply to all threads launched below) */
+  signal (SIGPIPE, SIG_IGN); // must ignore SIGPIPE or we get in an infinite loop when pantasks exits
+  signal (SIGTSTP, gotsignal);
+  signal (SIGTTIN, gotsignal);
+
+  // stdin / stdout are used for communication with pantasks.
+  // redirect stderr so various error messages are saved
+  stderr = freopen (STDERR_FILE, "a", stderr);
+  if (!stderr) {
+      fprintf (stdout, "failed to open %s for error output\n", STDERR_FILE);
+      exit (1);
+  }
+
+  rl_readline_name = opihi_name;
+  rl_attempted_completion_function = command_completer;
+# ifdef THREADED
+  SetRunLevel (PCONTROL_RUN_ALL);
+  pthread_create (&clientsThread, NULL, &CheckSystem_Threaded, NULL);
+  rl_event_hook = NULL;
+  rl_set_keyboard_input_timeout (1000); 
+# else
+  rl_event_hook = CheckSystem;
+  rl_set_keyboard_input_timeout (1000); 
+# endif  
+
+  // set_str_variable ("HISTORY", opihi_history);
+  set_str_variable ("PROMPT", opihi_prompt);
+  set_str_variable ("RCFILE", opihi_rcfile);
+
+  {
+    char *helpdir;
+    char *modules;
+    static char *datadir = "@DATADIR@";
+    ALLOCATE (helpdir, char, strlen(datadir) + strlen("/help") + 2);
+    sprintf (helpdir, "%s/help", datadir);
+    set_str_variable ("HELPDIR", helpdir);
+    free (helpdir);
+    ALLOCATE (modules, char, strlen(datadir) + strlen("/modules") + 2);
+    sprintf (modules, "%s/modules", datadir);
+    set_str_variable ("MODULES:0", modules);
+    set_int_variable ("MODULES:n", 1);
+    free (modules);
+  }
+
+  /* ignore the history file.  to change this, see, eg, mana.c */
+  return;
+}
+
+/* standard welcome message */
+void welcome () {
+  gprint (GP_ERR, "\n");
+  gprint (GP_ERR, "Welcome to %s - %s\n\n", opihi_name, opihi_description);
+}
+
+/* add program-dependent exit functions here */
+void cleanup () {
+  // stop checking on the jobs
+  SetRunLevel (PCONTROL_RUN_HOSTS); 
+  DownHosts ();
+  ConfigFree ();
+
+  FreeBasic ();
+  FreePcontrol ();
+
+  return;
+}
+
+void gotsignal (int signum) {
+  // this message is lost if we are connected to a pantasks
+  fprintf (stderr, "got signal : %d\n", signum);
+  return;
+}
+
+/* call to opihi shell */
+int main (int argc, char **argv) {
+  int status;
+  status = opihi (argc, argv);
+  abort ();	
+  exit (status);
+}
+
+void pcontrol_exit (int n) {
+  abort ();
+  exit (n);
+}
Index: /branches/sc_branches/pantasks_condor/pcontrol/pulse.c
===================================================================
--- /branches/sc_branches/pantasks_condor/pcontrol/pulse.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/pcontrol/pulse.c	(revision 34783)
@@ -0,0 +1,16 @@
+# include "pantasks.h"
+
+int pulse (int argc, char **argv) {
+
+  int Nusec;
+
+  if (argc != 2) {
+    gprint (GP_ERR, "USAGE: pulse (microseconds)\n");
+    return (FALSE);
+  }
+
+  Nusec = atoi (argv[1]);
+  rl_set_keyboard_input_timeout (Nusec); 
+
+  return (TRUE);
+}
Index: /branches/sc_branches/pantasks_condor/pcontrol/rconnect.c
===================================================================
--- /branches/sc_branches/pantasks_condor/pcontrol/rconnect.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/pcontrol/rconnect.c	(revision 34783)
@@ -0,0 +1,148 @@
+# include "pcontrol.h"
+
+/* connection can take a while, allow up to 2 sec */
+# define CONNECT_TIMEOUT 5000
+
+/* connect to host, start the shell: ssh hostname pclient -> command hostname shell
+   stdio is an array of file descriptors (stdio[3])
+*/
+
+int rconnect (char *command, char *hostname, char *shell, int *stdio) {
+
+  int i = 0, stdin_fd[2], stdout_fd[2], stderr_fd[2], status;
+  int result, waitstatus;
+  pid_t pid;
+  char *p;
+  char **argv;
+  IOBuffer buffer;
+  struct timespec request, remain;
+
+  ASSERT (command != NULL, "command is NULL");
+  ASSERT (hostname != NULL, "hostname is NULL");
+  ASSERT (shell != NULL, "shell is NULL");
+  ASSERT (stdio != NULL, "stdio is NULL");
+
+  bzero (stdin_fd,  2*sizeof(int));
+  bzero (stdout_fd, 2*sizeof(int));
+  bzero (stderr_fd, 2*sizeof(int));
+
+  if (pipe (stdin_fd)  < 0) goto pipe_error;
+  if (pipe (stdout_fd) < 0) goto pipe_error;
+  if (pipe (stderr_fd) < 0) goto pipe_error;
+
+  ALLOCATE (argv, char *, 4);
+  argv[0] = command;
+  argv[1] = hostname;
+  argv[2] = shell;
+  argv[3] = 0;
+
+  pid = fork ();
+  if (!pid) { /* must be child process */
+    if (VerboseMode()) gprint (GP_ERR, "starting remote connection to %s...", hostname);
+
+    /* close the other ends of the pipes */
+    close (stdin_fd[1]);
+    close (stdout_fd[0]);
+    close (stderr_fd[0]);
+
+    /* tie our ends of the pipes to stdin, stdout, stderr */
+    dup2 (stdin_fd[0],  STDIN_FILENO);
+    dup2 (stdout_fd[1], STDOUT_FILENO);
+    dup2 (stderr_fd[1], STDERR_FILENO);
+
+    /* set all three unblocking */
+    setvbuf (stdin,  (char *) NULL, _IONBF, BUFSIZ);
+    setvbuf (stdout, (char *) NULL, _IONBF, BUFSIZ);
+    setvbuf (stderr, (char *) NULL, _IONBF, BUFSIZ);
+
+    status = execvp (argv[0], argv);
+    gprint (GP_ERR, "error starting remote shell process\n");
+    pcontrol_exit (60);
+  }
+  free (argv);
+
+  /* close the other ends of the pipes */
+  close (stdin_fd[0]);  stdin_fd[0]  = 0;
+  close (stdout_fd[1]); stdout_fd[1] = 0;
+  close (stderr_fd[1]); stderr_fd[1] = 0;
+
+  /* make the pipes non-blocking */
+  fcntl (stdin_fd[1],  F_SETFL, O_NONBLOCK);
+  fcntl (stdout_fd[0], F_SETFL, O_NONBLOCK);
+  fcntl (stderr_fd[0], F_SETFL, O_NONBLOCK);
+
+  /* perform handshake with pclient to verify alive & running */
+  /** this handshake is similar to PclientCommand, but has important differences **/
+  InitIOBuffer (&buffer, 0x100);
+
+  /* send handshake command */
+  status = write_fmt (stdin_fd[1], "echo CONNECTED\n");
+  if ((status == -1) && (errno == EPIPE)) goto connect_error;
+
+  /* try to get evidence connection is alive - wait upto a few seconds */
+  p = NULL;
+  status = -1;
+  for (i = 0; (i < CONNECT_TIMEOUT) && (status != 0) && (p == NULL); i++) {
+    status = ReadtoIOBuffer (&buffer, stdout_fd[0]);
+    p = memstr (buffer.buffer, "CONNECTED", buffer.Nbuffer);
+    usleep (10000); // wait for client to be connected
+  }
+  if (status == 0) goto connect_error;
+  if (status == -1) goto connect_error;
+  if (VerboseMode()) gprint (GP_ERR, "%d cycles to connect\n", i);
+  FreeIOBuffer (&buffer);
+
+  if (VerboseMode()) gprint (GP_ERR, "Connected\n");
+
+  stdio[0] = stdin_fd[1];
+  stdio[1] = stdout_fd[0];
+  stdio[2] = stderr_fd[0];
+
+  return (pid);
+
+pipe_error:
+  perror ("pipe error:");
+  goto close_pipes;
+
+connect_error:
+  if (VerboseMode()) gprint (GP_ERR, "error while connecting, status: %d, ncycles: %d\n", status, i);
+
+  /* avoid blocking on waitpid, test every 100 usec, up to 50 msec */
+  request.tv_sec = 0;
+  request.tv_nsec = 100000;
+
+  /* harvest the child process: kill & wait (< 100 ms) for exit */
+  kill (pid, SIGKILL);
+  result = waitpid (pid, &waitstatus, WNOHANG);
+  for (i = 0; (i < 50) && (result == 0); i++) {
+    nanosleep (&request, &remain);
+    result = waitpid (pid, &waitstatus, WNOHANG);
+  }
+
+  if ((result == -1) && (errno != ECHILD)) {
+    gprint (GP_ERR, "unexpected error from waitpid (%d): programming error\n", errno);
+    pcontrol_exit (61);
+  }
+  if (result == 0) {
+    if (VerboseMode()) gprint (GP_ERR, "child did not exit (rconnect)??");
+  }
+  if (result > 0) {
+    if (result != pid) {
+      gprint (GP_ERR, "waitpid error: mis-matched PID (%d vs %d).  programming error\n", result, pid);
+      pcontrol_exit (62);
+    }
+    if (WIFSTOPPED(waitstatus)) {
+      gprint (GP_ERR, "waitpid returns 'stopped': programming error\n");
+      pcontrol_exit (63);
+    }
+  }
+
+close_pipes:
+  if (stdin_fd[0]  != 0) close (stdin_fd[0]);
+  if (stdin_fd[1]  != 0) close (stdin_fd[1]);
+  if (stdout_fd[0] != 0) close (stdout_fd[0]);
+  if (stdout_fd[1] != 0) close (stdout_fd[1]);
+  if (stderr_fd[0] != 0) close (stderr_fd[0]);
+  if (stderr_fd[1] != 0) close (stderr_fd[1]);
+  return (FALSE);
+}
Index: /branches/sc_branches/pantasks_condor/pcontrol/run.c
===================================================================
--- /branches/sc_branches/pantasks_condor/pcontrol/run.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/pcontrol/run.c	(revision 34783)
@@ -0,0 +1,66 @@
+# include "pcontrol.h"
+
+int run (int argc, char **argv) {
+
+  RunLevels level;
+
+  if ((argc > 2) ||
+      get_argument (argc, argv, "help") ||
+      get_argument (argc, argv, "-h") ||
+      get_argument (argc, argv, "--help")) 
+  {
+    if (!strcmp (argv[0], "run")) {
+      gprint (GP_ERR, "USAGE: run [level]\n");
+      gprint (GP_ERR, "  allowed levels:\n");
+      gprint (GP_ERR, "  all (default) : manage machines, spawn jobs, harvest jobs\n");
+      gprint (GP_ERR, "  reap          : manage machines, harvest jobs\n");
+      gprint (GP_ERR, "  hosts         : manage machines, not jobs\n");
+      gprint (GP_ERR, "  none          : all stop\n");
+    } else {
+      gprint (GP_ERR, "USAGE: stop (immediate processing halt)\n");
+    }
+    return (FALSE);
+  }
+
+  level = PCONTROL_RUN_UNKNOWN;
+  if (argc == 1) {
+    if (!strcasecmp (argv[0], "run")) level = PCONTROL_RUN_ALL;
+    if (!strcasecmp (argv[0], "stop")) level = PCONTROL_RUN_NONE;
+  } else {
+    if (!strcasecmp (argv[1], "all")) level = PCONTROL_RUN_ALL;
+    if (!strcasecmp (argv[1], "reap")) level = PCONTROL_RUN_REAP;
+    if (!strcasecmp (argv[1], "host")) level = PCONTROL_RUN_HOSTS;
+    if (!strcasecmp (argv[1], "hosts")) level = PCONTROL_RUN_HOSTS;
+    if (!strcasecmp (argv[1], "none")) level = PCONTROL_RUN_NONE;
+  }
+
+  if (level == PCONTROL_RUN_UNKNOWN) {
+    gprint (GP_ERR, "  unknown run level %s\n", argv[1]);
+    gprint (GP_ERR, "  allowed levels:\n");
+    gprint (GP_ERR, "  all (default) : manage machines, spawn jobs, harvest jobs\n");
+    gprint (GP_ERR, "  reap          : manage machines, harvest jobs\n");
+    gprint (GP_ERR, "  hosts         : manage machines, not jobs\n");
+    gprint (GP_ERR, "  none          : all stop\n");
+    return (FALSE);
+  }
+
+# ifdef THREADED
+  SetRunLevel (level);
+# else
+  if (level == PCONTROL_RUN_NONE) {
+    rl_event_hook = NULL;
+  } else {
+    rl_event_hook = CheckSystem;
+  }
+# endif
+
+  return (TRUE);
+}
+
+/* 
+   run levels:
+   all (manage machines, spawn jobs, harvest jobs)
+   reap (manage machines, harvest jobs)
+   hosts (manage machines, not jobs)
+   none (all stop)
+*/
Index: /branches/sc_branches/pantasks_condor/pcontrol/status.c
===================================================================
--- /branches/sc_branches/pantasks_condor/pcontrol/status.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/pcontrol/status.c	(revision 34783)
@@ -0,0 +1,97 @@
+# include "pcontrol.h"
+
+int PrintJobStack (int Nstack);
+int PrintHostStack (int Nstack);
+
+int status (int argc, char **argv) {
+
+  PrintJobStack (PCONTROL_JOB_ALLJOBS);
+  PrintHostStack (PCONTROL_HOST_ALLHOSTS);
+
+  return (TRUE);
+}
+
+int PrintJobStack (int Nstack) {
+
+  int i, j, Nobject;
+  Stack *stack;
+  Job *job;
+  struct timeval now;
+  float dtime;
+
+  stack = GetJobStack (Nstack);
+  ASSERT (stack != NULL, "programming error");
+
+  LockStack (stack);
+  Nobject = stack[0].Nobject;
+  gprint (GP_LOG, "job stack %s:  %d objects\n", GetJobStackName(Nstack), Nobject);
+
+  for (i = 0; i < Nobject; i++) {
+    job = stack[0].object[i];
+    ASSERT (job != NULL, "programming error");
+    if (job[0].realhost == NULL) {
+	gprint (GP_LOG, "%3d %9s ", i, job[0].hostname);
+    } else {
+	gprint (GP_LOG, "%3d %9s ", i, job[0].realhost);
+    }
+    gprint (GP_LOG, "%7s  ", GetJobStackName (job[0].state));
+
+    switch (job[0].state) {
+	// for active jobs or pending jobs, print time since start (or create in the case of pending)
+      case PCONTROL_JOB_PENDING:
+      case PCONTROL_JOB_BUSY:
+      case PCONTROL_JOB_RESP:
+      case PCONTROL_JOB_HUNG:
+	gettimeofday (&now, (void *) NULL);
+	dtime = DTIME (now, job[0].start);
+	gprint (GP_LOG, "%8.2f ", dtime);
+	break;
+
+	// for active jobs or pending jobs, print time since start (or create in the case of pending)
+      case PCONTROL_JOB_DONE:
+      case PCONTROL_JOB_KILL:
+      case PCONTROL_JOB_EXIT:
+      case PCONTROL_JOB_CRASH:
+      default:
+	dtime = DTIME (job[0].stop, job[0].start);
+	gprint (GP_LOG, "%8.2f ", dtime);
+	break;
+    }
+
+    PrintID (GP_LOG, job[0].JobID);
+    gprint (GP_LOG, " %2d ", job[0].priority);
+    for (j = 0; j < job[0].argc; j++) {
+      gprint (GP_LOG, "%s ", job[0].argv[j]);
+    }
+    gprint (GP_LOG, "\n");
+  }
+  UnlockStack (stack);
+
+  return (TRUE);
+}
+
+int PrintHostStack (int Nstack) {
+
+  int i, Nobject;
+  Stack *stack;
+  Host *host;
+
+  stack = GetHostStack (Nstack);
+
+  LockStack (stack);
+  Nobject = stack[0].Nobject;
+  gprint (GP_LOG, "host stack %s:  %d objects\n", GetHostStackName(Nstack), Nobject);
+
+  for (i = 0; i < Nobject; i++) {
+    host = stack[0].object[i];
+    gprint (GP_LOG, "%d  %s  ", i, host[0].hostname);
+    gprint (GP_LOG, "%5s  ", GetHostStackName (host[0].stack));
+    PrintID (GP_LOG, host[0].HostID);
+    gprint (GP_LOG, "\n");
+  }
+  UnlockStack (stack);
+
+  return (TRUE);
+}
+
+// Safe with PTHREAD_MUTEX_INITIALIZER lock
Index: /branches/sc_branches/pantasks_condor/pcontrol/stdout.c
===================================================================
--- /branches/sc_branches/pantasks_condor/pcontrol/stdout.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/pcontrol/stdout.c	(revision 34783)
@@ -0,0 +1,110 @@
+# include "pcontrol.h"
+
+// XXX unify by testing for value of argv[0]
+int stdout_pc (int argc, char **argv) {
+
+  int N, JobID, StackID;
+  Job *job;
+  IOBuffer *buffer;
+  char *varName;
+
+  varName = NULL;
+  if ((N = get_argument (argc, argv, "-var"))) {
+    remove_argument (N, &argc, argv);
+    varName = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  if (argc != 2) {
+    gprint (GP_ERR, "USAGE: stdout (JobID) [-var name]\n");
+    gprint (GP_LOG, "STATUS %d\n", -1);
+    return (FALSE);
+  }
+  
+  /* find Job of interest (must be EXIT or CRASH) */
+  JobID = atoi (argv[1]);
+
+  StackID = PCONTROL_JOB_EXIT;
+  job = PullJobFromStackByID (StackID, JobID);
+  if (job != NULL) goto found_stdout;
+
+  StackID = PCONTROL_JOB_CRASH;
+  job = PullJobFromStackByID (StackID, JobID);
+  if (job != NULL) goto found_stdout;
+
+  gprint (GP_ERR, "job not found in EXIT or CRASH\n");
+  if (varName == NULL) {
+    gprint (GP_LOG, "STATUS %d\n", -2);
+  } else {
+    set_str_variable (varName, "NULL");
+    free (varName);
+  }
+  return (FALSE);
+
+found_stdout:
+  buffer = &job[0].stdout_buf.buffer;
+  if (varName == NULL) {
+    fwrite (buffer[0].buffer, 1, buffer[0].Nbuffer, stdout);
+    gprint (GP_LOG, "STATUS %d\n", 0);
+  } else {
+    // XXX this can drop '0' values
+    set_str_variable (varName, buffer[0].buffer);
+    free (varName);
+  }
+  PutJob (job, StackID, STACK_BOTTOM);
+  return (TRUE);
+}
+
+int stderr_pc (int argc, char **argv) {
+
+  int N, JobID, StackID;
+  Job *job;
+  IOBuffer *buffer;
+  char *varName;
+
+  varName = NULL;
+  if ((N = get_argument (argc, argv, "-var"))) {
+    remove_argument (N, &argc, argv);
+    varName = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  if (argc != 2) {
+    gprint (GP_ERR, "USAGE: stderr (JobID) [-var name]\n");
+    gprint (GP_LOG, "STATUS %d\n", -1);
+    return (FALSE);
+  }
+  
+  /* find Job of interest (must be EXIT or CRASH) */
+  JobID = atoi (argv[1]);
+
+  StackID = PCONTROL_JOB_EXIT;
+  job = PullJobFromStackByID (StackID, JobID);
+  if (job != NULL) goto found_stderr;
+
+  StackID = PCONTROL_JOB_CRASH;
+  job = PullJobFromStackByID (StackID, JobID);
+  if (job != NULL) goto found_stderr;
+
+  gprint (GP_ERR, "job not found in EXIT or CRASH\n");
+  if (varName == NULL) {
+    gprint (GP_LOG, "STATUS %d\n", -2);
+  } else {
+    set_str_variable (varName, "NULL");
+    free (varName);
+  }
+  return (FALSE);
+
+found_stderr:
+  buffer = &job[0].stderr_buf.buffer;
+  if (varName == NULL) {
+    fwrite (buffer[0].buffer, 1, buffer[0].Nbuffer, stdout);
+    gprint (GP_LOG, "STATUS %d\n", 0);
+  } else {
+    // XXX this can drop '0' values
+    set_str_variable (varName, buffer[0].buffer);
+    free (varName);
+  }
+  PutJob (job, StackID, STACK_BOTTOM);
+  return (TRUE);
+}
Index: /branches/sc_branches/pantasks_condor/pcontrol/test/hosttargets.sh
===================================================================
--- /branches/sc_branches/pantasks_condor/pcontrol/test/hosttargets.sh	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/pcontrol/test/hosttargets.sh	(revision 34783)
@@ -0,0 +1,45 @@
+
+# generate a bunch of jobs which are one of the three states NEED, WANT, (ANY?)
+# check the rate at which they are placed on the appropriate type of host.
+
+# ANY : is there a valid test for ANY?
+# NEED must always be sent to the NEED host
+# WANT : report the fraction of time : if job time is << timeout -> WANT host rate should be high
+# WANT : report the fraction of time : if job time is >> timeout -> WANT host rate should be low
+# WANT : report the fraction of time : if job time is  ~ timeout -> WANT host rate should be ~NN%
+
+macro hosttargets
+
+  if ($0 != 1) 
+    echo "USAGE: hosttargets"
+    break
+  end
+
+  if ($?hostlist:n == 0)
+    echo "create a list of target hosts called 'hostlist'"
+    break
+  end
+
+  stop
+
+  for i 0 $hostlist:n
+    # generate a set of jobs with desired targets
+    for j 0 10
+      job -host $hostlist:$i sleep 1
+    end
+    host add $hostlist:$i
+  end
+  
+  run
+
+  # wait 'till jobs complete
+  sleep 12
+
+  # compare desired with actual host
+  # for i 1 20
+  #   check job $i
+  # end
+
+end
+
+## NOTE : dtime in 'status' is not working
Index: /branches/sc_branches/pantasks_condor/pcontrol/test/linesize-remote.sh
===================================================================
--- /branches/sc_branches/pantasks_condor/pcontrol/test/linesize-remote.sh	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/pcontrol/test/linesize-remote.sh	(revision 34783)
@@ -0,0 +1,48 @@
+# we are having trouble with pcontrol -> pclient comms with very long lines (> 512)
+# this seems to be a problem with readline buffering
+
+# generate jobs with very long lines
+# XXX this must be run as the only pass in pcontrol (to get the JobIDs right)
+
+macro test
+  if ($0 != 2)
+    echo "USAGE: test (remotehost)"
+    break
+  end
+
+  host add $1
+
+  $inline1 = abcdefghijklmnopqrstuvwxyz
+  $inline2 = $inline1+$inline1
+  $inline3 = $inline2-$inline2
+  $inline4 = $inline3=$inline3
+  $inline5 = $inline4*$inline4
+  $inline6 = $inline5^$inline5
+  $inline7 = $inline6%$inline6
+
+  strlen "inline1: $inline1"
+  strlen "inline2: $inline2"
+  strlen "inline3: $inline3"
+  strlen "inline4: $inline4"
+  strlen "inline5: $inline5"
+  strlen "inline6: $inline6"
+  strlen "inline7: $inline7"
+
+  for i 1 10
+    job echo $inline7
+  end
+
+  sleep 5
+
+  for i 1 10
+    stdout $i -var outline
+    strlen "$inline7" Nin
+    strlen "$outline" Nout
+
+    echo Nin: $Nin, Nout: $Nout
+
+    if ($Nin != $Nout - 1)
+      echo "mismatch in length!"
+    end
+  end
+end
Index: /branches/sc_branches/pantasks_condor/pcontrol/test/linesize.sh
===================================================================
--- /branches/sc_branches/pantasks_condor/pcontrol/test/linesize.sh	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/pcontrol/test/linesize.sh	(revision 34783)
@@ -0,0 +1,45 @@
+# we are having trouble with pcontrol -> pclient comms with very long lines (> 512)
+# this seems to be a problem with readline buffering
+
+# generate jobs with very long lines
+# XXX this must be run as the only pass in pcontrol (to get the JobIDs right)
+
+$localhost = `hostname`
+
+macro test
+  host add $localhost
+
+  $inline1 = abcdefghijklmnopqrstuvwxyz
+  $inline2 = $inline1+$inline1
+  $inline3 = $inline2-$inline2
+  $inline4 = $inline3=$inline3
+  $inline5 = $inline4*$inline4
+  $inline6 = $inline5^$inline5
+  $inline7 = $inline6%$inline6
+
+  strlen "inline1: $inline1"
+  strlen "inline2: $inline2"
+  strlen "inline3: $inline3"
+  strlen "inline4: $inline4"
+  strlen "inline5: $inline5"
+  strlen "inline6: $inline6"
+  strlen "inline7: $inline7"
+
+  for i 1 10
+    job echo $inline7
+  end
+
+  sleep 5
+
+  for i 1 10
+    stdout $i -var outline
+    strlen "$inline7" Nin
+    strlen "$outline" Nout
+
+    echo Nin: $Nin, Nout: $Nout
+
+    if ($Nin != $Nout - 1)
+      echo "mismatch in length!"
+    end
+  end
+end
Index: /branches/sc_branches/pantasks_condor/pcontrol/test/machines.sh
===================================================================
--- /branches/sc_branches/pantasks_condor/pcontrol/test/machines.sh	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/pcontrol/test/machines.sh	(revision 34783)
@@ -0,0 +1,74 @@
+
+macro load.hosts
+  parameters unwant = 1
+  parameters want = 2.0
+
+  host add pikake
+  host add pikake
+  host add ipp022
+  host add ipp022
+  host add ipp022
+  host add ipp022
+  host add ipp002
+  host off ipp002
+
+  machines
+end
+
+macro load.jobs
+  job -host pikake sleep 10
+  job -host pikake sleep 10
+  job -host pikake sleep 10
+  job -host pikake sleep 10
+  job -host pikake sleep 10
+  job -host pikake sleep 10
+  job -host pikake sleep 10
+  job -host pikake sleep 10
+end
+
+macro load.xhost
+  job -host pikake -xhost ipp002 sleep 10
+  job -host pikake -xhost ipp002 sleep 10
+  job -host ipp022 -xhost ipp002 sleep 10
+  job -host ipp022 -xhost ipp002 sleep 10
+  job -host pikake -xhost ipp002 sleep 10
+  job -host pikake -xhost ipp002 sleep 10
+  job -host ipp022 -xhost ipp002 sleep 10
+  job -host ipp022 -xhost ipp002 sleep 10
+end
+
+macro load.2xhost
+  job -host pikake -xhost ipp002 -xhost ipp022 sleep 10
+  job -host pikake -xhost ipp002 -xhost ipp022 sleep 10
+  job -host ipp022 -xhost ipp002 -xhost ipp022 sleep 10
+  job -host ipp022 -xhost ipp002 -xhost ipp022 sleep 10
+  job -host pikake -xhost ipp002 -xhost ipp022 sleep 10
+  job -host pikake -xhost ipp002 -xhost ipp022 sleep 10
+  job -host ipp022 -xhost ipp002 -xhost ipp022 sleep 10
+  job -host ipp022 -xhost ipp002 -xhost ipp022 sleep 10
+end
+
+macro load.hosts.zombie
+  parameters connect = 2.0
+
+  host add pikake
+  host add pikake
+  host add ipp022
+  host add ipp022
+  host add ipp022
+  host add ipp022
+
+  machines
+end
+
+macro load.jobs.zombie
+  job sleep 10
+  job sleep 10
+  job sleep 10
+  job sleep 10
+  job sleep 10
+  job sleep 10
+  job sleep 10
+  job sleep 10
+end
+
Index: /branches/sc_branches/pantasks_condor/pcontrol/verbose.c
===================================================================
--- /branches/sc_branches/pantasks_condor/pcontrol/verbose.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/pcontrol/verbose.c	(revision 34783)
@@ -0,0 +1,37 @@
+# include "pcontrol.h"
+
+static int VERBOSE = FALSE;
+
+int verbose (int argc, char **argv) {
+
+  if (argc == 1) {
+    if (VERBOSE) {
+      gprint (GP_ERR, "verbose mode ON\n");
+    } else {
+      gprint (GP_ERR, "verbose mode OFF\n");
+    }
+    return (TRUE);
+  }
+
+  if (argc == 2) {
+    if (!strcasecmp (argv[1], "ON")) {
+      VERBOSE = TRUE;
+      return (TRUE);
+    }
+    if (!strcasecmp (argv[1], "OFF")) {
+      VERBOSE = FALSE;
+      return (TRUE);
+    }
+    if (!strcasecmp (argv[1], "TOGGLE")) {
+      VERBOSE = ~VERBOSE;
+      return (TRUE);
+    }
+  }
+
+  gprint (GP_ERR, "USAGE: verbose (on/off/toggle)\n");
+  return (FALSE);
+}
+
+int VerboseMode () {
+  return (VERBOSE);
+}
Index: /branches/sc_branches/pantasks_condor/pcontrol/version.c
===================================================================
--- /branches/sc_branches/pantasks_condor/pcontrol/version.c	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/pcontrol/version.c	(revision 34783)
@@ -0,0 +1,17 @@
+# include "pcontrol.h"
+static char *name = "$Name: not supported by cvs2svn $";
+
+int version (int argc, char **argv) {
+
+  char *tmp;
+
+  gprint (GP_LOG, "\n");
+  gprint (GP_LOG, "pcontrol version: %s\n", (tmp = strip_version (name))); free (tmp);
+
+  gprint (GP_LOG, "opihi version: %s\n", (tmp = strip_version (opihi_version()))); free (tmp);
+  gprint (GP_LOG, "ohana version: %s\n", (tmp = strip_version (ohana_version()))); free (tmp);
+  gprint (GP_LOG, "gfits version: %s\n", (tmp = strip_version (gfits_version()))); free (tmp);
+
+  gprint (GP_LOG, "compiled on %s %s\n", __DATE__, __TIME__);
+  return (TRUE);
+}
Index: /branches/sc_branches/pantasks_condor/scripts/copy.pro
===================================================================
--- /branches/sc_branches/pantasks_condor/scripts/copy.pro	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/scripts/copy.pro	(revision 34783)
@@ -0,0 +1,136 @@
+
+# identify the images ready for copy 
+task	       new.images
+  command      new.images
+  host         local
+
+  periods      -poll 1
+  periods      -exec 30
+  periods      -timeout 2
+
+  # success
+  task.exit    0
+    push new.images $stdout
+    $new.image.failure = 0
+  end
+
+  # locked list
+  task.exit    1
+    echo       "new.images: exec failure"
+    $new.image.failure ++
+  end
+
+  # default exit status
+  task.exit    -
+    echo       "new.images: unknown exit status: $EXIT"
+    $new.image.failure ++
+  end
+
+  # operation times out?
+  task.exit    timeout
+    echo       "new.images: timeout"
+    $new.image.failure ++
+  end
+end
+
+# copy specific images from the summit
+task           copy.images
+
+  # these define task properties which are fixed
+
+  period       -exec 2
+  period       -poll 1
+  period       -timeout 50
+
+  # these commands are executed at the start of a new task
+  task.exec
+    local        RemoteName
+    local        FileID
+    local        Host
+
+    queuesize  NewImages -var N
+    if ($N > 0) break
+    if ($network == 0) break
+    if ($filesystem == 1) break
+
+    pop NewImages -var line
+    list tmp -split $line
+    $RemoteName = $tmp:0
+    $FileID     = $tmp:1
+    $Host       = $tmp:2
+    $Ntry       = $tmp:3
+
+    stderr       /data/logfiles/copy/$FileID.log
+    stdout       -queue $FileIDlog
+
+    echo $TaskID
+    echo $TaskName
+    spawn copy.images $RemoteName $FileID $Host -host $Host
+  end
+
+  # success
+  task.exit      0
+    $new.image.failure --
+  end
+
+  # summmit connection failed
+  task.exit      1
+    echo         "copy.images: copy failed $RemoteName $FileID"
+    push         NewImages "$RemoteName $FileID $Host 0"
+    $copy.image.failure ++
+  end
+
+  # target disk full
+  task.exit      2
+    echo         "copy.images: disk full"
+    push         NewImages "$RemoteName $FileID $Host 0"
+    $copy.image.failure ++
+  end
+
+  # file not found
+  task.exit      3
+    echo         "copy.images: missing file"
+    # send a message to OTIS?
+  end
+
+  # task timed out
+  task.exit      timeout
+    echo        "copy.images: timeout"
+    push        NewImages "$RemoteName $FileID $Host 0"
+    $copy.image.failure ++
+  end
+end
+
+# identify the images ready for copy 
+TASK	       new.images
+  COMMAND      new.images
+  HOST         -
+  STDERR       /data/logfiles/new.images.log
+  STDOUT       $stdout
+  EXEC_PERIOD  30
+  POLL_PERIOD  1
+
+  # success
+  EXIT         0
+    STDOUT     @new.images
+    $new.image.failure = 0
+  END
+
+  # locked list
+  EXIT         1
+    MESSAGE    "new.images: exec failure"
+    $new.image.failure ++
+  END
+
+  # default exit status
+  EXIT         -
+    MESSAGE    "new.images: unknown exit status: $EXIT"
+    $new.image.failure ++
+  END
+
+  # operation times out?
+  TIMEOUT      2
+    MESSAGE    "new.images: timeout"
+    $new.image.failure ++
+  END
+END
Index: /branches/sc_branches/pantasks_condor/scripts/copypool.pro
===================================================================
--- /branches/sc_branches/pantasks_condor/scripts/copypool.pro	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/scripts/copypool.pro	(revision 34783)
@@ -0,0 +1,117 @@
+
+## A test task to demonstrate cycled copying using pending and done pools (directories)
+##   and pending queues
+
+queueinit stdout
+queueinit stderr
+queueinit pending
+
+$indir = dir.01
+$outdir = dir.02
+
+# set the test rate to be 1ms
+pulse 1000
+
+macro init
+  exec rm -r dir.*
+  exec mkdir $indir
+  exec mkdir $outdir
+end
+
+$Nfile = 0
+task pool.create
+  periods -exec 0.02
+  periods -poll 0.02
+
+  task.exec
+    sprintf filename "foo.%04d.dat" $Nfile
+    $Nfile ++
+    host local
+    command copypool.sh -create $indir $filename
+  end
+
+  task.exit  default
+    queueinit stdout
+    queueinit stderr
+  end
+end
+
+task pool.pending
+  command copypool.sh -pending $indir
+  host local
+
+  periods -exec 5
+  periods -poll 1
+
+  # success
+  task.exit    0
+    local i Nstdout
+
+    # keep only new, unique entries (name is key)
+    queuesize stdout -var Nstdout
+    for i 0 $Nstdout
+      queuepop stdout -var line
+      queuepush pending -uniq -key 0 "$line new"
+    end
+    queueprint pending
+  end
+end
+
+task pool.copy
+  host local
+
+  periods -exec 1
+  periods -poll 1
+
+  task.exec
+    local Npending
+    queuesize pending -var Npending
+    if ($Npending == 0) 
+      periods -exec 1
+      periods -poll 1
+      break
+    end
+
+    # if data is available, run fast to clear it out
+    periods -exec 0.002
+    periods -poll 0.002
+
+    # this step grabs and entry by key and updates a field
+    # can we do this in one step?
+    queuepop pending -var line -key 1 new
+    if ("$line" == "NULL") break
+
+    list tmp -split $line
+    $name = $tmp:0
+
+    queuepush pending "$name run"
+
+    host local
+    command copypool.sh -copy $outdir $name
+  end
+
+  # success
+  task.exit 0
+    echo "done with $taskarg:3"
+    queueinit stdout
+    queueinit stderr
+    queuepop pending -key 0 $taskarg:3 -var line
+    # echo "got line: $line"
+
+    if ("$line" == "NULL") 
+      echo "missing entry in pending queue?"
+      break
+    end
+  end
+end
+
+task pool.list
+  host local
+  periods -exec 3
+  command queueprint pending
+
+  task.exit default
+    queueinit stdout
+    queueinit stderr
+  end
+end
Index: /branches/sc_branches/pantasks_condor/scripts/copypool.sh
===================================================================
--- /branches/sc_branches/pantasks_condor/scripts/copypool.sh	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/scripts/copypool.sh	(revision 34783)
@@ -0,0 +1,31 @@
+#!/bin/csh -f
+
+# copypool.sh -pending (indir)
+# copypool.sh -copy (outdir) (path)
+# copypool.sh -create (outdir) (name)
+
+if ($#argv < 2) then
+  echo "USAGE: copypool.sh -pending (indir)"
+  echo "USAGE: copypool.sh -copy (outdir) (path)"
+  echo "USAGE: copypool.sh -create (outdir) (name)"
+  exit 2
+endif
+
+if ("$1" == "-pending") then
+  set n = `ls $2 | wc -l`
+  if ($n == 0) exit 0
+  ls $2/* | cat
+  exit 0
+endif
+
+if ("$1" == "-copy") then
+  set name = `basename $3`
+  rm $3
+  touch $2/$name
+  exit 0
+endif
+
+if ("$1" == "-create") then
+  touch $2/$3
+  exit 0
+endif
Index: /branches/sc_branches/pantasks_condor/scripts/diff-opihi
===================================================================
--- /branches/sc_branches/pantasks_condor/scripts/diff-opihi	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/scripts/diff-opihi	(revision 34783)
@@ -0,0 +1,38 @@
+#!/bin/csh -f
+
+# this just organizes the tagging of the opihi components
+
+if ($#argv != 2) then
+  echo "USAGE: tag-elixir (MODULE) (TAG)"
+  echo "  MODULE choices: base mana dvo dimm pantasks pcontrol pclient"
+  exit 2
+endif
+
+if ("$1" == "base") then
+  cvs diff -r $2 Makefile
+  cvs diff -r $2 Makefile.Common
+  cvs diff -r $2 include
+  cvs diff -r $2 test
+  cvs diff -r $2 lib.data
+  cvs diff -r $2 lib.shell
+  cvs diff -r $2 cmd.astro
+  cvs diff -r $2 cmd.basic
+  cvs diff -r $2 cmd.data
+  cvs diff -r $2 scripts
+  cvs diff -r $2 doc
+  exit 0;
+endif
+
+if ("$1" == "mana") goto valid;
+if ("$1" == "dvo") goto valid;
+if ("$1" == "dimm") goto valid;
+if ("$1" == "pantasks") goto valid;
+if ("$1" == "pclient") goto valid;
+if ("$1" == "pcontrol") goto valid;
+echo "invalid opihi module $1"
+exit 1;
+
+valid:
+  # diff the module ($1) with the specified tag ($2)
+  cvs diff -r $2 $1
+  exit 0;
Index: /branches/sc_branches/pantasks_condor/scripts/fft.pro
===================================================================
--- /branches/sc_branches/pantasks_condor/scripts/fft.pro	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/scripts/fft.pro	(revision 34783)
@@ -0,0 +1,25 @@
+
+macro fft.subraster
+
+  delete B
+
+  $Nx = $1
+  $Ny = $2
+  $DX = 4096/$Nx
+  $DY = 4096/$Nx
+  date
+  for ix 0 $Nx
+    for iy 0 $Ny
+      subraster b B {$DX*$ix} {$DY*$iy} $DX $DY 0 0 $DX $DY
+      fft2d B 0 to Br Bi
+    end
+  end
+  date
+end
+
+macro fft.timing
+  fft.subraster 1 1
+  fft.subraster 2 2
+  fft.subraster 4 4
+  fft.subraster 8 8
+end
Index: /branches/sc_branches/pantasks_condor/scripts/fiterrors.pro
===================================================================
--- /branches/sc_branches/pantasks_condor/scripts/fiterrors.pro	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/scripts/fiterrors.pro	(revision 34783)
@@ -0,0 +1,27 @@
+
+# create and fit a quadratic:
+macro fiterrors
+
+  if ($0 != 2)
+    echo "USAGE: fiterrors (sigma)"
+    break
+  end
+
+  create x 0 30 
+  set y = 1000 - 5*(x-15)^2
+
+  gaussdev sigma x[] 0.0 1.0
+  set Y = $1*(y + sigma)
+  set dY = zero(x) + $1
+
+  lim x Y; clear; box; plot x Y -dy dY
+
+  fit x Y 2 -dy dY
+  applyfit x Yf
+  plot -x 0 x Yf -c red
+  
+  $xo = -0.5*$C1 / $C2
+  $dxo = $xo * sqrt (($dC1/$C1)^2 + ($dC2/$C2)^2)
+  echo "$xo +/- $dxo"
+
+end
Index: /branches/sc_branches/pantasks_condor/scripts/jobs.plot
===================================================================
--- /branches/sc_branches/pantasks_condor/scripts/jobs.plot	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/scripts/jobs.plot	(revision 34783)
@@ -0,0 +1,24 @@
+
+macro jobs.rates
+ clear -s
+
+ section a 0.0 0.0 1.0 0.5
+ data jobs.stop.dat
+ read t 6
+ set T = t*(t > 30) + (t+60)*(t<30)
+ create n 0 t[]
+ lim T n; box; plot T n
+ subset tf = T if (T > 70)
+ subset nf = n if (T > 70)
+ fit tf nf 1
+ 
+ section b 0.0 0.5 1.0 0.5
+ data jobs.run.dat
+ read t 6
+ set T = t*(t > 30) + (t+60)*(t<30)
+ create n 0 t[]
+ lim T n; box; plot T n
+ subset tf = T if (T > 70)
+ subset nf = n if (T > 70)
+ fit tf nf 1
+end
Index: /branches/sc_branches/pantasks_condor/scripts/manytasks.pro
===================================================================
--- /branches/sc_branches/pantasks_condor/scripts/manytasks.pro	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/scripts/manytasks.pro	(revision 34783)
@@ -0,0 +1,72 @@
+
+controller exit true
+controller host add kiawe
+
+$Ntest = 0
+pulse 1000
+controller pulse 1000
+
+macro load.machines
+  if ($0 != 2)
+    echo "load.machines (nmach)"
+    break
+  end
+
+  for i 0 $1
+    $n = $i + 1
+    sprintf host "po%02d" $n
+    controller host add $host
+  end
+end
+
+macro mktask
+  if ($0 != 2)
+    echo "USAGE: mktask (n)"
+    break
+  end
+
+  $N = $1
+
+  task test.$N
+    command partest
+    # polling period is no longer valid: we check for completed controller tasks
+    # correction: still valid for local tasks
+    periods -poll 0.2
+    periods -exec 0.01
+    periods -timeout 10.0
+    nmax 20
+    host anyhost
+
+    # stdout / stderr lines on named queues
+    task.exit 0
+      # echo "task exit 0"
+      queuedelete stdout
+      queuedelete stderr
+      date date
+      queuepush done "$date"
+      $Ntest ++
+    end
+
+    task.exit 1
+      # echo "task exit 1"
+      queuesize stdout
+      queuesize stderr
+    end
+
+    task.exit crash
+      echo "crashed job"
+    end
+
+    task.exit timeout
+      output timeout.log
+      echo $stdout
+      output stdout
+    end
+  end
+end
+
+macro mktasks
+ for i 0 $1
+  mktask $i
+ end
+end
Index: /branches/sc_branches/pantasks_condor/scripts/notes.txt
===================================================================
--- /branches/sc_branches/pantasks_condor/scripts/notes.txt	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/scripts/notes.txt	(revision 34783)
@@ -0,0 +1,18 @@
+
+This directory contains useful opihi / dvo / mana scripts
+
+Some possible scripts:
+
+module psphot
+
+  * load image, display, overlay the detections of several classes:
+    - psf stars
+    - good non-linear fit stars
+    - good linear fit stars
+    - good extended sources
+    - saturated stars
+    - defects
+    - failed fits
+    - ??
+
+  * load image, display, load psastro reference stars, display at multiple rotation angles / offsets?
Index: /branches/sc_branches/pantasks_condor/scripts/psched.pro
===================================================================
--- /branches/sc_branches/pantasks_condor/scripts/psched.pro	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/scripts/psched.pro	(revision 34783)
@@ -0,0 +1,99 @@
+
+controller exit true
+# controller host add kiawe
+$Ntest = 0
+# controller host add alala
+# verbose on
+pulse 1000
+controller pulse 1000
+
+macro load.machines
+  if ($0 != 2)
+    echo "load.machines (nmach)"
+    break
+  end
+
+  for i 0 $1
+    $n = $i + 1
+    sprintf host "po%02d" $n
+    controller host add $host
+  end
+end
+
+task test
+  command partest
+  # polling period is no longer valid: we check for completed controller tasks
+  # correction: still valid for local tasks
+  periods -poll 0.20
+  periods -exec 0.0001
+  periods -timeout 10.0
+  nmax 1024
+#  nmax 100
+  host anyhost
+
+  # stdout / stderr lines on named queues
+  task.exit 0
+    # echo "task exit 0"
+    queuedelete stdout
+    queuedelete stderr
+    date date
+    queuepush done "$date"
+    $Ntest ++
+#   memory leaks
+#   queuesize stdout -var Nstdout
+#    for i 0 $Nstdout
+#      queuepop stdout -var line
+#      queuepush results "$line"
+#    end
+  end
+
+  task.exit 1
+    # echo "task exit 1"
+    queuesize stdout
+    queuesize stderr
+  end
+
+  task.exit crash
+    echo "crashed job"
+  end
+
+  task.exit timeout
+    output timeout.log
+    echo $stdout
+    output stdout
+  end
+end
+
+# pulse == 100ms
+# poll/exit = 0.2  : 29 sec / 100 jobs
+# poll/exit = 0.1  : 20 sec / 100 jobs
+# poll/exit = 0.05 : 17 sec / 100 jobs
+# poll/exit = 0.01 : 18 sec / 100 jobs
+
+# pulse == 10ms
+# poll/exit = 0.2  : 20 sec / 100 jobs
+# poll/exit = 0.10 : 12 sec / 100 jobs
+# poll/exit = 0.05 : 12 sec / 100 jobs
+# poll/exit = 0.01 :  9 sec / 100 jobs
+
+# we are limited here by how quickly we can send data to the 
+# controller.  this is limited by the occasional 'CheckSystem'
+# loops, with ~40ms minimum.
+
+# seems to be faster on po01 from kiawe (less interference?)
+
+# pulse == 1ms, controller pulse == 1ms
+# poll/exit = 0.01 :  3 sec / 100 jobs
+# 2 mach, 3 sec
+# 4 mach, 3 sec
+# 8 mach, 3 sec
+
+# 16 machines, 500 jobs, 13 sec: 26ms / job
+# 32 machines, 1024 jobs, 26 sec: 26ms / job
+# job harvesting rate is still the limitation.  Each job harvest requires:
+#  - jobstack exit
+#  - stdout
+#  - stderr
+#  - delete
+#  - jobstack crash
+
Index: /branches/sc_branches/pantasks_condor/scripts/queuetests
===================================================================
--- /branches/sc_branches/pantasks_condor/scripts/queuetests	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/scripts/queuetests	(revision 34783)
@@ -0,0 +1,19 @@
+
+macro test.unique
+  list line -x "cat new.list"
+  for i 0 $line:n
+    push t1 "$line:$i"
+  end
+
+  queuelist
+  queuesize t1
+
+  list line -x "cat copy.list"
+  for i 0 $line:n
+    push t1 "$line:$i" -uniq
+  end
+
+  queuelist
+  queuesize t1
+
+end
Index: /branches/sc_branches/pantasks_condor/scripts/rdiff-elixir
===================================================================
--- /branches/sc_branches/pantasks_condor/scripts/rdiff-elixir	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/scripts/rdiff-elixir	(revision 34783)
@@ -0,0 +1,31 @@
+#!/bin/csh -f
+
+# this just organizes the tagging of the elixir components
+
+if ($#argv != 1) then
+  echo "USAGE: rdiff-elixir (TAG)"
+  exit 2
+endif
+
+cvs rdiff -r $1 opihi/Makefile
+cvs rdiff -r $1 opihi/bin/
+cvs rdiff -r $1 opihi/cmd.astro/
+cvs rdiff -r $1 opihi/cmd.basic/
+cvs rdiff -r $1 opihi/cmd.data/
+cvs rdiff -r $1 opihi/doc/
+cvs rdiff -r $1 opihi/dvo/
+cvs rdiff -r $1 opihi/help/
+cvs rdiff -r $1 opihi/include/
+cvs rdiff -r $1 opihi/lib/
+cvs rdiff -r $1 opihi/lib.data/
+cvs rdiff -r $1 opihi/lib.shell/
+cvs rdiff -r $1 opihi/mana/
+cvs rdiff -r $1 opihi/scripts/
+
+# don't place tag on these implementations
+# cvs tag $1 dimm/
+# cvs tag $1 dvo2/
+# cvs tag $1 old/
+# cvs tag $1 pclient/
+# cvs tag $1 pcontrol/
+# cvs tag $1 sched/
Index: /branches/sc_branches/pantasks_condor/scripts/run.pclient
===================================================================
--- /branches/sc_branches/pantasks_condor/scripts/run.pclient	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/scripts/run.pclient	(revision 34783)
@@ -0,0 +1,4 @@
+#!/bin/csh -f
+
+# pclient
+mana
Index: /branches/sc_branches/pantasks_condor/scripts/sample.sh
===================================================================
--- /branches/sc_branches/pantasks_condor/scripts/sample.sh	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/scripts/sample.sh	(revision 34783)
@@ -0,0 +1,9 @@
+#!/usr/bin/env dvo
+
+echo "test"
+
+for i 0 $argv:n
+ echo $i $argv:$i
+end
+
+exit 0
Index: /branches/sc_branches/pantasks_condor/scripts/sched.pro
===================================================================
--- /branches/sc_branches/pantasks_condor/scripts/sched.pro	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/scripts/sched.pro	(revision 34783)
@@ -0,0 +1,43 @@
+
+task test
+  command ls
+  periods -poll 1.0
+  periods -exec 2.0
+  periods -timeout 10.0
+  # trange 07:09 07:10
+  # trange -exclude 07:09:30 07:09:45
+  nmax 5
+  host local
+  # host localhost
+  # local is default 
+
+  task.exec
+    # echo "starting job sched.test"
+  end
+
+  # stdout / stderr lines on named queues
+  task.exit 0
+#    echo "task exit 0"
+#    queuesize stdout
+#    queuesize stderr
+    queuedelete stdout
+    queuedelete stderr
+    memory leaks
+  end
+
+  task.exit 1
+    echo "task exit 1"
+    queuesize stdout
+    queuesize stderr
+  end
+
+  task.exit crash
+    echo "crashed job"
+  end
+
+  task.exit timeout
+    output timeout.log
+    echo $stdout
+    output stdout
+  end
+end
Index: /branches/sc_branches/pantasks_condor/scripts/tag-opihi
===================================================================
--- /branches/sc_branches/pantasks_condor/scripts/tag-opihi	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/scripts/tag-opihi	(revision 34783)
@@ -0,0 +1,38 @@
+#!/bin/csh -f
+
+# this just organizes the tagging of the opihi components
+
+if ($#argv != 2) then
+  echo "USAGE: tag-elixir (MODULE) (TAG)"
+  echo "  MODULE choices: base mana dvo dimm pantasks pcontrol pclient"
+  exit 2
+endif
+
+if ("$1" == "base") then
+  cvs tag $2 Makefile
+  cvs tag $2 Makefile.Common
+  cvs tag $2 include/
+  cvs tag $2 test/
+  cvs tag $2 lib.data/
+  cvs tag $2 lib.shell/
+  cvs tag $2 cmd.astro/
+  cvs tag $2 cmd.basic/
+  cvs tag $2 cmd.data/
+  cvs tag $2 scripts/
+  cvs tag $2 doc/
+  exit 0;
+endif
+
+if ("$1" == "mana") goto valid;
+if ("$1" == "dvo") goto valid;
+if ("$1" == "dimm") goto valid;
+if ("$1" == "pantasks") goto valid;
+if ("$1" == "pclient") goto valid;
+if ("$1" == "pcontrol") goto valid;
+echo "invalid opihi module $1"
+exit 1;
+
+valid:
+  # tag the module ($1) with the specified tag ($2)
+  cvs tag $2 $1
+  exit 0;
Index: /branches/sc_branches/pantasks_condor/scripts/test.pro
===================================================================
--- /branches/sc_branches/pantasks_condor/scripts/test.pro	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/scripts/test.pro	(revision 34783)
@@ -0,0 +1,144 @@
+
+macro testgauss
+ create x -20 20 0.1
+ set dy = zero(x) + 0.03
+ set X = x - $1
+ set y = $3*exp(-0.5*(X^2/$2^2)) + $4 + 0.1*(rnd(X) - 0.5)
+# set y = $3*exp(-0.5*(X^2/$2^2)) + $4
+ lim x y; clear; box; plot x y -dy dy -x 2 -pt 2
+
+ $C0 = $1+5
+ $C1 = $2-10
+ $C2 = $3
+ $C3 = $4
+
+ vgauss x y dy yf
+end
+
+macro testpoor
+ create x -20 20 0.1
+ set dy = zero(x) + 0.03
+ set X = x - $1
+ set y = 3 + zero(x) + 0.1*(rnd(X) - 0.5)
+# set y = (x + 20)/10 + $4 + 0.1*(rnd(X) - 0.5)
+# set y = $3*exp(-0.5*(X^2/$2^2)) + $4
+ lim x y; clear; box; plot x y -dy dy -x 2 -pt 2
+
+ $C0 = $1
+ $C1 = $2
+ $C2 = $3
+ $C3 = $4
+
+ vgauss x y dy yf
+end
+
+macro testfit
+ create x 0 1000
+ set y = 3 + 5*x
+ set dy = 10*(rnd(x) - 0.5)
+ set yr = y + dy
+ fit x yr 1
+ echo $Cn, $Cnv
+ echo $dC
+
+ yr[100] = yr[100] + 50
+ yr[200] = yr[200] - 50
+ yr[300] = yr[300] + 100
+ yr[400] = yr[400] - 500
+ yr[500] = yr[500] + 30
+
+ fit x yr 1
+ echo $Cn, $Cnv
+ echo $dC
+
+ fit x yr 1 -clip 3 3
+ echo $Cn, $Cnv
+ echo $dC
+end
+# expected output:
+# y = 3.013781 x^0 5.000005 x^1
+#     0.063198     0.000110
+# 1, 1000
+# 2.91071543189
+# y = 2.495040 x^0 5.000303 x^1
+#     0.063198     0.000110
+# 1, 1000
+# 16.5928919554
+# y = 3.021974 x^0 4.999991 x^1
+#     0.063419     0.000110
+# 1, 995
+# 2.90820510001
+
+macro testloops
+ for i 0 100
+  if ($i < 20)
+   continue
+  end
+  if ($i > 40)
+   break
+  end
+  echo $i
+ end
+end  
+
+# test math parsing
+macro testvars
+ echo testing variable assignment
+ $a = 1
+ echo $a is 1
+ $b = 5*3 + $a
+ echo $b is 16
+
+ $a = `ls`
+ echo $a
+ exec ls
+
+ echo testing in-line math
+ $a = 1
+ echo {1 + 1} is 2
+ echo {2^3} is 8
+ echo {$a + 2} is 3
+
+ echo testing vector assignment
+ create x 0 10
+ set y = x^2
+ echo x[2] is 2
+ echo y[2] is 4
+ lim x y; clear; box; plot x y
+ label -x "x axis" -y "&s y axis"
+end
+
+macro testspeed
+ exec date
+ create x 0 100000
+ for i 0 5000
+  set y = 3*x + $i
+ end
+ exec date
+end
+
+macro testmemory.buffers
+ mcreate a 2048 2048
+ exec date
+ for i 0 500
+  set b = 3*a + 6
+ end
+ exec date
+end
+
+macro testmemory.macros
+ exec date
+ for i 0 1000
+   for j 0 1000
+    $N = $N + 1
+   end
+ end
+ exec date
+end
+
+macro sample
+ $N = 0
+ for j 0 1000000
+  $N = 100
+ end
+end
Index: /branches/sc_branches/pantasks_condor/scripts/testloops.pro
===================================================================
--- /branches/sc_branches/pantasks_condor/scripts/testloops.pro	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/scripts/testloops.pro	(revision 34783)
@@ -0,0 +1,90 @@
+
+macro testloop1
+ local i
+ for i 0 10
+  echo "1: $i"
+    end
+end
+
+macro testloop2
+ local i
+ for i 0 5
+   echo "2: $i"
+   testloop1
+end
+end
+
+macro testlocal1
+ local -static foo
+ echo "1 - foo: $foo"
+ $foo = local.1
+ echo "1 - foo: $foo"
+end
+
+macro testlocal2
+ local foo
+ $foo = local.2
+ echo "2 - foo: $foo"
+ testlocal1
+ echo "2 - foo: $foo"
+ testlocal1
+ echo "2 - foo: $foo"
+end
+
+macro test1
+ # echo step $1
+ if ($1 == 1)
+  echo "break here"
+  break
+ end
+ if ($1 == 2)
+  continue
+ end
+end
+
+# continue
+
+macro test2
+ echo "input test"
+ break
+ echo "error, can't get here"
+end
+
+macro testm
+ break -auto off
+ test1 0
+ # echo $STATUS
+ test1 1
+ # echo $STATUS
+ test1 2
+ # echo $STATUS
+ if ($1 == 10) 
+  test1 1
+  if ($STATUS == 0)
+   echo "breaking it"
+   break
+  end
+ end
+ echo done
+end
+
+echo "done loading"
+
+macro test3
+ for i 0 12
+  echo $i
+  testm $i
+  echo "status: $STATUS"
+ end
+end
+
+macro testL
+ for i 0 10
+  for j 0 10
+   for k 0 10
+    echo $i $j $k
+    mcreate a 1024 1024   
+   end
+  end
+ end
+end
Index: /branches/sc_branches/pantasks_condor/scripts/testscript
===================================================================
--- /branches/sc_branches/pantasks_condor/scripts/testscript	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/scripts/testscript	(revision 34783)
@@ -0,0 +1,16 @@
+#!/bin/csh -f
+
+date
+
+ls -d *
+# sleep 3
+ls foo
+
+foreach f (*)
+  echo $f
+  ls $f/*
+#  sleep 1
+end
+
+date
+#exit 3
Index: /branches/sc_branches/pantasks_condor/test/basic.sh
===================================================================
--- /branches/sc_branches/pantasks_condor/test/basic.sh	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/test/basic.sh	(revision 34783)
@@ -0,0 +1,9 @@
+
+list testdir
+  cmd.data/test
+  cmd.basic/test
+end
+
+input test/tests.sh
+
+fulltests
Index: /branches/sc_branches/pantasks_condor/test/pclient.sh
===================================================================
--- /branches/sc_branches/pantasks_condor/test/pclient.sh	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/test/pclient.sh	(revision 34783)
@@ -0,0 +1,8 @@
+
+list testdir
+  pclient/test
+end
+
+input test/tests.sh
+
+fulltests
Index: /branches/sc_branches/pantasks_condor/test/strings.pro
===================================================================
--- /branches/sc_branches/pantasks_condor/test/strings.pro	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/test/strings.pro	(revision 34783)
@@ -0,0 +1,32 @@
+
+macro test1
+ $test1 = 01234567890.01234567890.1234567890
+ $test2 = $test1 + $test1
+ $test3 = $test2 and $test2
+ $test4 = $test3 or $test3
+ $test5 = $test4 with $test4
+
+ echo strlen test1; strlen "$test1" 
+ echo strlen test2; strlen "$test2"
+ echo strlen test3; strlen "$test3"
+ echo strlen test4; strlen "$test4"
+ echo strlen test5; strlen "$test5"
+end
+
+macro test2
+ $test1 = 01234567890.01234567890.1234567890
+ appendup test1 test2
+ appendup test2 test3
+ appendup test3 test4
+ appendup test4 test5
+
+ echo strlen test1; strlen "$test1" 
+ echo strlen test2; strlen "$test2"
+ echo strlen test3; strlen "$test3"
+ echo strlen test4; strlen "$test4"
+ echo strlen test5; strlen "$test5"
+end
+ 
+macro appendup
+  $$2 = $$1 and $$1
+end
Index: /branches/sc_branches/pantasks_condor/test/tests.sh
===================================================================
--- /branches/sc_branches/pantasks_condor/test/tests.sh	(revision 34783)
+++ /branches/sc_branches/pantasks_condor/test/tests.sh	(revision 34783)
@@ -0,0 +1,100 @@
+
+if ($?VERBOSE == 0)
+ $VERBOSE = 0
+end
+
+$failtest:n = 0
+$failfile:n = 0
+$faildirs:n = 0
+$currentdir = .
+
+macro fulltests
+  $Npass = 0
+  $Nfail = 0
+  $Ntest = 0
+  break -auto off
+
+  $failtest:n = 0
+  $failfile:n = 0
+  $faildirs:n = 0
+
+  for Ti 0 $testdir:n
+    if ($VERBOSE > 0)
+       echo "directory $testdir:$Ti"
+    end
+    runtestdir $testdir:$Ti
+  end
+
+  echo "completed $Ntest tests"
+  echo "$Npass tests passed"
+  echo "$Nfail tests failed"
+  echo "examined $testdir:n directories"
+
+  if ($Nfail > 0) 
+    echo " ** failed tests **"
+    for i 0 $Nfail
+      echo $faildirs:$i $failfile:$i $failtest:$i
+    end
+  end
+end
+
+macro runtestdir
+  if ($0 != 2)
+    echo "USAGE: runtestdir (testdir)"
+    break -auto on
+    break
+  end
+  
+  pwd -var startdir
+  cd -q $1
+  list testscripts -x "ls *.sh"
+
+  $currentdir = $1
+  for Tj 0 $testscripts:n
+    if ($VERBOSE > 1)
+      echo " running $testscripts:$Tj"
+    end
+    runtests $testscripts:$Tj      
+  end
+  
+  cd -q $startdir
+end
+
+macro runtests
+  if ($0 != 2)
+    echo "USAGE: runtests (script.sh)"
+    break -auto on
+    break
+  end
+  
+  local Tk
+
+  input $1
+  for Tk 0 $tests:n
+    if ($VERBOSE > 2)
+      echo "   running $tests:$Tk"
+    end
+    $PASS = 1
+    $tests:$Tk
+    if ($PASS == 0)
+      echo "   ** failed $tests:$Tk $1"
+      $Nfail ++
+      $n = $failtest:n
+      $failtest:$n = $tests:$Tk
+      $failfile:$n = $1
+      $faildirs:$n = $currentdir
+      $failtest:n ++
+      $failfile:n ++
+      $faildirs:n ++
+    else
+      $Npass ++
+    end
+    $Ntest ++
+    # echo "finished $tests:$Tk"
+  end
+end
+
+echo "USAGE:"
+echo "runtests (script.sh)"
+echo "runtestdir (testdir)"
+echo "set VERBOSE to 0 through 3"
