Index: /branches/eam_branches/ipp-20140610/Ohana/src/opihi/cmd.basic/Makefile
===================================================================
--- /branches/eam_branches/ipp-20140610/Ohana/src/opihi/cmd.basic/Makefile	(revision 36940)
+++ /branches/eam_branches/ipp-20140610/Ohana/src/opihi/cmd.basic/Makefile	(revision 36941)
@@ -55,4 +55,5 @@
 $(SRC)/substr.$(ARCH).o     \
 $(SRC)/strhash.$(ARCH).o     \
+$(SRC)/strmatch.$(ARCH).o     \
 $(SRC)/strpop.$(ARCH).o     \
 $(SRC)/strsub.$(ARCH).o     \
Index: /branches/eam_branches/ipp-20140610/Ohana/src/opihi/cmd.basic/init.c
===================================================================
--- /branches/eam_branches/ipp-20140610/Ohana/src/opihi/cmd.basic/init.c	(revision 36940)
+++ /branches/eam_branches/ipp-20140610/Ohana/src/opihi/cmd.basic/init.c	(revision 36941)
@@ -40,4 +40,5 @@
 int fprintf_opihi   PROTO((int, char **));
 int strlen_func     PROTO((int, char **));
+int strmatch        PROTO((int, char **));
 int substr_func     PROTO((int, char **));
 int strpop          PROTO((int, char **));
@@ -97,4 +98,5 @@
   {1, "strpop",        strpop,             "pop a string"},
   {1, "strsub",        strsub,             "replace instances of a key in a string"},
+  {1, "strmatch",      strmatch,           "string length"},
   {1, "wait",          wait_func,          "wait until return is typed"},
   {1, "which",         which,              "show command *"}
Index: /branches/eam_branches/ipp-20140610/Ohana/src/opihi/cmd.basic/list.c
===================================================================
--- /branches/eam_branches/ipp-20140610/Ohana/src/opihi/cmd.basic/list.c	(revision 36940)
+++ /branches/eam_branches/ipp-20140610/Ohana/src/opihi/cmd.basic/list.c	(revision 36941)
@@ -2,6 +2,4 @@
 # define D_NLINES 100
 static char prompt[] = ">> ";
-
-static int set_list_varname (char *line, char *base, int N, int excelStyle);
 
 int list (int argc, char **argv) {
@@ -311,32 +309,2 @@
   return (TRUE);
 }
-
-static int set_list_varname (char *line, char *base, int N, int excelStyle) {
-
-  int i;
-    
-  // A-Z correspond to 0 - 25
-
-  if (excelStyle) {
-    float f = log(26.0);
-    float g = (N == 0) ? 0.0 : log(1.0*N);
-    int Ndigit = (int) (g / f) + 1;
-    if (Ndigit > 10) {
-      sprintf (line, "%s:ZZZZZZZZZZ", base);
-      return FALSE;
-    }
-    char name[12];
-    memset (name, 0, 12);
-    for (i = 0; i < Ndigit; i++) {
-      float Npow = Ndigit - i - 1;
-      float g = pow(26.0, Npow);
-      int V = (int) (N / g);
-      name[i] = (Npow == 0.0) ? 'A' + V : 'A' + V - 1;
-      N -= V * g;
-    }
-    sprintf (line, "%s:%s", base, name);
-  } else {
-    sprintf (line, "%s:%d", base, N);
-  }
-  return TRUE;
-}
Index: /branches/eam_branches/ipp-20140610/Ohana/src/opihi/cmd.basic/strmatch.c
===================================================================
--- /branches/eam_branches/ipp-20140610/Ohana/src/opihi/cmd.basic/strmatch.c	(revision 36941)
+++ /branches/eam_branches/ipp-20140610/Ohana/src/opihi/cmd.basic/strmatch.c	(revision 36941)
@@ -0,0 +1,176 @@
+# include "basic.h"
+
+int strmatch (int argc, char **argv) {
+
+  /* returns position of the first given character class */ 
+  int i, N;
+
+  char *startName = NULL;
+  char *lengthName = NULL;
+  if ((N = get_argument (argc, argv, "-output"))) {
+    remove_argument (N, &argc, argv);
+    startName = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+    lengthName = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  if (argc != 3) {
+    gprint (GP_ERR, "USAGE: strchr (string) (class) [-output start length]\n");
+    return (FALSE);
+  }
+
+  // 'class' may be alpha, float, int, 
+
+  if (!strcasecmp(argv[2], "alpha")) {
+    int myLength = strlen(argv[1]);
+    int found = FALSE;
+    int start = -1;
+    int length = -1;
+    for (i = 0; i < myLength; i++) {
+      int isUpper = (argv[1][i] >= 'A') && (argv[1][i] <= 'Z');
+      int isLower = (argv[1][i] >= 'a') && (argv[1][i] <= 'z');
+      if (!found && (isLower || isUpper)) {
+	start = i;
+	found = TRUE;
+	continue;
+      }
+      if (found && !(isLower || isUpper)) {
+	length = i - start;
+	break;
+      }
+    }
+    if (found && (length < 0)) {
+      length = myLength - start;
+    }
+    if (startName && lengthName) {
+      set_int_variable (startName, start);
+      set_int_variable (lengthName, length);
+    } else {
+      gprint (GP_LOG, "match: %d %d\n", start, length);
+    }
+    return TRUE;
+  }
+
+  if (!strcasecmp(argv[2], "upper")) {
+    int myLength = strlen(argv[1]);
+    int found = FALSE;
+    int start = -1;
+    int length = -1;
+    for (i = 0; i < myLength; i++) {
+      int isUpper = (argv[1][i] >= 'A') && (argv[1][i] <= 'Z');
+      // int isLower = (argv[1][i] >= 'a') && (argv[1][i] <= 'z');
+      if (!found && isUpper) {
+	start = i;
+	found = TRUE;
+	continue;
+      }
+      if (found && !isUpper) {
+	length = i - start;
+	break;
+      }
+    }
+    if (found && (length < 0)) {
+      length = myLength - start;
+    }
+    if (startName && lengthName) {
+      set_int_variable (startName, start);
+      set_int_variable (lengthName, length);
+    } else {
+      gprint (GP_LOG, "match: %d %d\n", start, length);
+    }
+    return TRUE;
+  }
+
+  if (!strcasecmp(argv[2], "lower")) {
+    int myLength = strlen(argv[1]);
+    int found = FALSE;
+    int start = -1;
+    int length = -1;
+    for (i = 0; i < myLength; i++) {
+      // int isUpper = (argv[1][i] >= 'A') && (argv[1][i] <= 'Z');
+      int isLower = (argv[1][i] >= 'a') && (argv[1][i] <= 'z');
+      if (!found && isLower) {
+	start = i;
+	found = TRUE;
+	continue;
+      }
+      if (found && !isLower) {
+	length = i - start;
+	break;
+      }
+    }
+    if (found && (length < 0)) {
+      length = myLength - start;
+    }
+    if (startName && lengthName) {
+      set_int_variable (startName, start);
+      set_int_variable (lengthName, length);
+    } else {
+      gprint (GP_LOG, "match: %d %d\n", start, length);
+    }
+    return TRUE;
+  }
+
+  if (!strcasecmp(argv[2], "float")) {
+    int myLength = strlen(argv[1]);
+    int found = FALSE;
+    int start = -1;
+    int length = -1;
+    for (i = 0; i < myLength; i++) {
+      int isDigit = (argv[1][i] >= '0') && (argv[1][i] <= '9');
+      int isFloat = isDigit || (argv[1][i] == '-') || (argv[1][i] == '.');
+      if (!found && isFloat) {
+	start = i;
+	found = TRUE;
+	continue;
+      }
+      if (found && !isFloat) {
+	length = i - start;
+	break;
+      }
+    }
+    if (found && (length < 0)) {
+      length = myLength - start;
+    }
+    if (startName && lengthName) {
+      set_int_variable (startName, start);
+      set_int_variable (lengthName, length);
+    } else {
+      gprint (GP_LOG, "match: %d %d\n", start, length);
+    }
+    return TRUE;
+  }
+
+  if (!strcasecmp(argv[2], "int")) {
+    int myLength = strlen(argv[1]);
+    int found = FALSE;
+    int start = -1;
+    int length = -1;
+    for (i = 0; i < myLength; i++) {
+      int isDigit = (argv[1][i] >= '0') && (argv[1][i] <= '9');
+      if (!found && isDigit) {
+	start = i;
+	found = TRUE;
+	continue;
+      }
+      if (found && !isDigit) {
+	length = i - start;
+	break;
+      }
+    }
+    if (found && (length < 0)) {
+      length = myLength - start;
+    }
+    if (startName && lengthName) {
+      set_int_variable (startName, start);
+      set_int_variable (lengthName, length);
+    } else {
+      gprint (GP_LOG, "match: %d %d\n", start, length);
+    }
+    return TRUE;
+  }
+
+  gprint (GP_ERR, "unknown character class %s\n", argv[2]);
+  return (TRUE);
+}
Index: /branches/eam_branches/ipp-20140610/Ohana/src/opihi/cmd.data/Makefile
===================================================================
--- /branches/eam_branches/ipp-20140610/Ohana/src/opihi/cmd.data/Makefile	(revision 36940)
+++ /branches/eam_branches/ipp-20140610/Ohana/src/opihi/cmd.data/Makefile	(revision 36941)
@@ -98,4 +98,5 @@
 $(SRC)/point.$(ARCH).o		\
 $(SRC)/ps.$(ARCH).o		\
+$(SRC)/print_vectors.$(ARCH).o 	\
 $(SRC)/queuedelete.$(ARCH).o	\
 $(SRC)/queuedrop.$(ARCH).o	\
Index: /branches/eam_branches/ipp-20140610/Ohana/src/opihi/cmd.data/init.c
===================================================================
--- /branches/eam_branches/ipp-20140610/Ohana/src/opihi/cmd.data/init.c	(revision 36940)
+++ /branches/eam_branches/ipp-20140610/Ohana/src/opihi/cmd.data/init.c	(revision 36941)
@@ -87,4 +87,5 @@
 int point            PROTO((int, char **));
 int ps               PROTO((int, char **));
+int print_vectors    PROTO((int, char **));
 int queuelist        PROTO((int, char **));
 int queueload        PROTO((int, char **));
@@ -250,4 +251,5 @@
   {1, "ppm",          jpeg,             "convert display graphic to PPM"},
   {1, "ps",           ps,               "convert display to PostScript"},
+  {1, "print_vectors", print_vectors,   "print a set of vectors"},
   {1, "queuedelete",  queuedelete,      "delete a queue"},
   {1, "queuedrop",    queuedrop,        "drop values from queue matching a key"},
Index: /branches/eam_branches/ipp-20140610/Ohana/src/opihi/cmd.data/print_vectors.c
===================================================================
--- /branches/eam_branches/ipp-20140610/Ohana/src/opihi/cmd.data/print_vectors.c	(revision 36941)
+++ /branches/eam_branches/ipp-20140610/Ohana/src/opihi/cmd.data/print_vectors.c	(revision 36941)
@@ -0,0 +1,47 @@
+# include "data.h"
+
+int print_vectors (int argc, char **argv) {
+
+  Vector **vec;
+  int i, j;
+
+  if (argc < 2) {
+    gprint (GP_ERR, "USAGE: print_vectors (vector)[,vector,...]\n");
+    return (FALSE);
+  }
+
+  int Nvec = argc - 1;
+  ALLOCATE (vec, Vector *, Nvec);
+  for (i = 1; i < argc; i++) {
+    vec[i-1] = SelectVector (argv[i], OLDVECTOR, FALSE);
+    if (!vec[i-1]) {
+      gprint (GP_ERR, "unknown vector %s\n", argv[i]);
+      free (vec);
+      return FALSE;
+    }
+  }
+
+  int MaxLen = 0;
+  for (i = 0; i < Nvec; i++) {
+    MaxLen = MAX (MaxLen, vec[i][0].Nelements);
+  }
+
+  for (j = 0; j < MaxLen; j++) {
+    for (i = 0; i < Nvec; i++) {
+      if (j >= vec[i][0].Nelements) {
+	gprint (GP_LOG, "NaN ");
+      } else {
+	if (vec[i][0].type == OPIHI_FLT) {
+	  gprint (GP_LOG, "%f ", vec[i][0].elements.Flt[j]);
+	} else {
+	  gprint (GP_LOG, "%d ", vec[i][0].elements.Int[j]);
+	}
+      }
+    }
+    gprint (GP_LOG, "\n");
+  }
+  free (vec);
+
+  return (TRUE);
+}
+
Index: /branches/eam_branches/ipp-20140610/Ohana/src/opihi/cmd.data/read_vectors.c
===================================================================
--- /branches/eam_branches/ipp-20140610/Ohana/src/opihi/cmd.data/read_vectors.c	(revision 36940)
+++ /branches/eam_branches/ipp-20140610/Ohana/src/opihi/cmd.data/read_vectors.c	(revision 36941)
@@ -3,4 +3,6 @@
 FILE *f = (FILE *) NULL;
 char filename[2048];
+
+void read_vectors_cleanup ();
 
 int datafile (int argc, char **argv) {
@@ -23,5 +25,11 @@
 // vector types
 enum {COLTYPE_NONE, COLTYPE_FLT, COLTYPE_INT, COLTYPE_TIME, COLTYPE_CHAR};
-static int FITS_TRANSPOSE;
+
+static int      Nvec     = 0;
+static Vector **vec      = NULL;
+static char   **listname = NULL;
+static int     *col      = NULL;
+static int     *coltype  = NULL;
+static char    *buffer   = NULL;
 
 int read_vectors (int argc, char **argv) {
@@ -29,16 +37,8 @@
   int TimeFormat;
   time_t TimeReference;
-  int i, j, Nskip, Narg, Nvec, *col, IsCSV, VERBOSE;
-  int Nbytes, Nstart, NELEM, Nelem, nread, *coltype;
+  int i, j, Nskip, Narg, IsCSV, VERBOSE;
+  int Nbytes, Nstart, NELEM, Nelem, nread;
   char *colstr, *c0, *c1, *extname;
-  Vector **vec;
-
-  char *buffer = NULL;
-
-  FITS_TRANSPOSE = FALSE;
-  if ((Narg = get_argument (argc, argv, "-transpose"))) {
-    remove_argument (Narg, &argc, argv);
-    FITS_TRANSPOSE = TRUE;
-  }
+  char varname[1024];  // used as a buffer for the names of string fields
 
   /* auto-sense table type */
@@ -88,7 +88,12 @@
 
   Nvec = (argc - 1) / 2;
+  ALLOCATE (listname, char *, Nvec);
   ALLOCATE (vec, Vector *, Nvec);
   ALLOCATE (col, int, Nvec);
   ALLOCATE (coltype, int, Nvec);
+  for (i = 0; i < Nvec; i++) {
+    listname[i] = NULL;
+    vec[i] = NULL;
+  }
 
   for (i = 0; i < Nvec; i++) {
@@ -115,9 +120,12 @@
     }
 
-    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);    
+    if (coltype[i] == COLTYPE_CHAR) {
+      listname[i] = strcreate (argv[2*i + 1]);
+    } else {
+      if ((vec[i] = SelectVector (argv[2*i + 1], ANYVECTOR, TRUE)) == NULL) {
+	gprint (GP_ERR, "USAGE: read name N name N ...\n");
+	read_vectors_cleanup();
+	return (FALSE);    
+      }
     }
 
@@ -144,6 +152,5 @@
     bad_colname:
       gprint (GP_ERR, "USAGE: read name N name N ...\n");
-      free (vec);
-      free (col);
+      read_vectors_cleanup();
       return (FALSE);    
     }
@@ -153,9 +160,16 @@
   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);
+    switch (coltype[i]) {
+      case COLTYPE_INT:
+	ResetVector (vec[i], OPIHI_INT, NELEM);
+	break;
+      case COLTYPE_FLT:
+      case COLTYPE_TIME:
+	// note that COLTYPE_TIME is a type of float
+	ResetVector (vec[i], OPIHI_FLT, NELEM);
+	break;
+      case COLTYPE_CHAR:
+      default:
+	break;
     }
   }
@@ -166,6 +180,5 @@
     if (scan_line_maxlen (f, buffer, 0x10000) == EOF) {
       gprint (GP_ERR, "problem reading file %s\n", filename);
-      free (vec);
-      free (col);
+      read_vectors_cleanup();
       return FALSE;
     }
@@ -213,5 +226,4 @@
       for (i = 0; i < Nvec; i++) {
 	int ivalue;
-	char cvalue;
 	double dvalue;
 	time_t tvalue;
@@ -224,7 +236,21 @@
 	    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;
+	    {
+	      // I need to get an isolated word in 'value' with the string value of this field 
+	      char *ptr = IsCSV ? ptrparse_csv (col[i], c0) : ptrparse (col[i], c0);
+	      char *value = NULL;
+	      if (IsCSV) {
+		char *end = parse_nextword_csv (ptr);
+		if (end) {
+		  value = end ? strncreate (ptr, end - ptr) : strcreate (ptr);
+		}
+	      } else {
+		value = thisword(ptr);
+	      }
+	      set_list_varname (varname, listname[i], Nelem, FALSE);
+	      set_str_variable (varname, value);
+	      free (value);
+	      break;
+	    }
 	  case COLTYPE_FLT:
 	    readStatus = IsCSV ? dparse_csv (&dvalue, col[i], c0) : dparse (&dvalue, col[i], c0);
@@ -256,8 +282,15 @@
 	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);
+	  switch (coltype[i]) {
+	    case COLTYPE_INT:
+	      ResetVector (vec[i], OPIHI_INT, NELEM);
+	      break;
+	    case COLTYPE_FLT:
+	    case COLTYPE_TIME:
+	      ResetVector (vec[i], OPIHI_FLT, NELEM);
+	      break;
+	    case COLTYPE_CHAR:
+	    default:
+	      break;
 	  }
 	}
@@ -266,18 +299,24 @@
     }
   }
+  // set the final vector / list length
   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);
-  if (buffer) free (buffer);
+    switch (coltype[i]) {
+      case COLTYPE_INT:
+	ResetVector (vec[i], OPIHI_INT, Nelem);
+	break;
+      case COLTYPE_FLT:
+      case COLTYPE_TIME:
+	ResetVector (vec[i], OPIHI_FLT, Nelem);
+	break;
+      case COLTYPE_CHAR:
+	sprintf (varname, "%s:n", listname[i]);
+	set_int_variable (varname, Nelem);
+	break;
+      default:
+	break;
+    }
+  }
+  read_vectors_cleanup();
   return (TRUE);
-
 }
 
@@ -297,7 +336,14 @@
   Header header;
   Vector **vec;
+  int FITS_TRANSPOSE;
 
   table.buffer = NULL;
   header.buffer = NULL;
+
+  FITS_TRANSPOSE = FALSE;
+  if ((N = get_argument (argc, argv, "-transpose"))) {
+    remove_argument (N, &argc, argv);
+    FITS_TRANSPOSE = TRUE;
+  }
 
   CCDKeyword = NULL;
@@ -418,4 +464,26 @@
 	
     if (!FITS_TRANSPOSE) {
+      // read string column into a list rather than a vector
+      if (!strcmp (type, "char")) {
+	char *fieldName = argv[i];
+	char *Ptr = data;
+	char varname[1024];  // used as a buffer for the names of string fields
+	for (j = 0; j < Ny; j++) {
+	  set_list_varname (varname, fieldName, j, FALSE);
+	  char *value = strncreate (&Ptr[j*Nval], Nval);
+	  // replace instances of $ with _
+	  char *p = strchr (value, '$');
+	  while (p) { 
+	    *p = '_';
+	    p = strchr (p, '$');
+	  }
+	  set_str_variable (varname, value);
+	  free (value);
+	}
+	sprintf (varname, "%s:n", fieldName);
+	set_int_variable (varname, Ny);
+	continue;
+      }
+
       // define the multifield vector names (Nval vectors x Ny elements)
       ALLOCATE (vec, Vector *, Nval);
@@ -429,12 +497,4 @@
       }
 
-      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;
@@ -546,2 +606,18 @@
   return (TRUE);
 }
+
+void read_vectors_cleanup () {
+
+  int i;
+
+  if (col) free (col);
+  if (coltype) free (coltype);
+  if (buffer) free (buffer);
+  if (listname) {
+    for (i = 0; i < Nvec; i++) {
+      if (listname[i]) free (listname[i]);
+    }
+    free (listname);
+  }
+  if (vec) free (vec);
+}
Index: /branches/eam_branches/ipp-20140610/Ohana/src/opihi/include/shell.h
===================================================================
--- /branches/eam_branches/ipp-20140610/Ohana/src/opihi/include/shell.h	(revision 36940)
+++ /branches/eam_branches/ipp-20140610/Ohana/src/opihi/include/shell.h	(revision 36941)
@@ -166,4 +166,6 @@
 // wrap readline in ohana mem functions:
 char         *opihi_readline            PROTO((char *prompt));
+
+int set_list_varname (char *line, char *base, int N, int excelStyle);
 
 /* gprint functions */
Index: /branches/eam_branches/ipp-20140610/Ohana/src/opihi/lib.shell/string.c
===================================================================
--- /branches/eam_branches/ipp-20140610/Ohana/src/opihi/lib.shell/string.c	(revision 36940)
+++ /branches/eam_branches/ipp-20140610/Ohana/src/opihi/lib.shell/string.c	(revision 36941)
@@ -322,2 +322,31 @@
 }
 
+int set_list_varname (char *line, char *base, int N, int excelStyle) {
+
+  int i;
+    
+  // A-Z correspond to 0 - 25
+
+  if (excelStyle) {
+    float f = log(26.0);
+    float g = (N == 0) ? 0.0 : log(1.0*N);
+    int Ndigit = (int) (g / f) + 1;
+    if (Ndigit > 10) {
+      sprintf (line, "%s:ZZZZZZZZZZ", base);
+      return FALSE;
+    }
+    char name[12];
+    memset (name, 0, 12);
+    for (i = 0; i < Ndigit; i++) {
+      float Npow = Ndigit - i - 1;
+      float g = pow(26.0, Npow);
+      int V = (int) (N / g);
+      name[i] = (Npow == 0.0) ? 'A' + V : 'A' + V - 1;
+      N -= V * g;
+    }
+    sprintf (line, "%s:%s", base, name);
+  } else {
+    sprintf (line, "%s:%d", base, N);
+  }
+  return TRUE;
+}
