Index: trunk/Ohana/src/opihi/cmd.data/Makefile
===================================================================
--- trunk/Ohana/src/opihi/cmd.data/Makefile	(revision 36679)
+++ trunk/Ohana/src/opihi/cmd.data/Makefile	(revision 37049)
@@ -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: trunk/Ohana/src/opihi/cmd.data/init.c
===================================================================
--- trunk/Ohana/src/opihi/cmd.data/init.c	(revision 36679)
+++ trunk/Ohana/src/opihi/cmd.data/init.c	(revision 37049)
@@ -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: trunk/Ohana/src/opihi/cmd.data/print_vectors.c
===================================================================
--- trunk/Ohana/src/opihi/cmd.data/print_vectors.c	(revision 37049)
+++ trunk/Ohana/src/opihi/cmd.data/print_vectors.c	(revision 37049)
@@ -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: trunk/Ohana/src/opihi/cmd.data/read_vectors.c
===================================================================
--- trunk/Ohana/src/opihi/cmd.data/read_vectors.c	(revision 36679)
+++ trunk/Ohana/src/opihi/cmd.data/read_vectors.c	(revision 37049)
@@ -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,22 +299,28 @@
     }
   }
+  // 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);
-
 }
 
-# define ESCAPE(MSG) { \
-  gprint (GP_ERR, "%s\n", MSG); \
+# define ESCAPE(...) {		\
+  gprint (GP_ERR, __VA_ARGS__); \
   if (CCDKeyword != NULL) free (CCDKeyword); \
   gfits_free_table  (&table); \
@@ -292,12 +331,19 @@
 
   off_t Nbytes;
-  int i, j, k, N, Nextend, Ny, Binary, vecType, padIfShort;
+  int i, j, N, Nextend, Ny, Binary, vecType, padIfShort;
   char type[16], ID[80], *CCDKeyword;
   FTable table;
   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;
@@ -328,7 +374,7 @@
   // }
 
-  if (argc < 2) ESCAPE ("USAGE: read -fits extension [-extnum] [-keyword key] name name ...");
-
-  if (f == NULL) ESCAPE ("file not found");
+  if (argc < 2) ESCAPE ("USAGE: read -fits extension [-extnum] [-keyword key] name name ...\n");
+
+  if (f == NULL) ESCAPE ("file not found\n");
   fseeko (f, 0LL, SEEK_SET);
   table.header = &header;
@@ -338,5 +384,5 @@
     // 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");
+    if (!gfits_load_header (f, &header)) ESCAPE ("error reading primary header for file\n");
     Nbytes = gfits_data_size (&header);
     fseeko (f, Nbytes, SEEK_CUR);
@@ -344,5 +390,5 @@
 
     for (i = 0; i < Nextend; i++) {
-      if (!gfits_load_header (f, &header)) ESCAPE ("extension not found");
+      if (!gfits_load_header (f, &header)) ESCAPE ("extension %d not found\n", i);
       Nbytes = gfits_data_size (&header);
       /* skip the prior data buffers */
@@ -350,6 +396,6 @@
       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");
+    if (!gfits_load_header (f, &header)) ESCAPE ("error reading header for extension %d\n", Nextend);
+    if (!gfits_fread_ftable_data (f, &table, padIfShort)) ESCAPE ("error reading table for extension %d\n", Nextend);
 
   } else {
@@ -382,5 +428,5 @@
 	continue;
       }
-      if (!gfits_fread_ftable_data (f, &table, padIfShort)) ESCAPE ("error reading table for extension");
+      if (!gfits_fread_ftable_data (f, &table, padIfShort)) ESCAPE ("error reading table for extension\n");
       break;
     }
@@ -391,5 +437,5 @@
   gfits_scan (&header, "XTENSION", "%s", 1, type);
   if (strcmp (type, "BINTABLE") && strcmp (type, "TABLE")) {
-    ESCAPE ("specified extension is not a table\n");
+    ESCAPE ("specified extension %s is not a table\n", type);
   }
   Binary = !strcmp (type, "BINTABLE");
@@ -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,52 +497,6 @@
       }
 
-      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;
-	  }
-	}
-      }
+      if (!VectorAssignData (vec, type, data, Ny, Nval)) ESCAPE ("bad column type %s", type);
+
     } else {
       // define the multifield vector names (Ny vectors x Nval elements)
@@ -489,52 +511,5 @@
       }
 
-      if (!strcmp (type, "char")) {
-	char *Ptr = data;
-	for (j = 0; j < Ny; j++) {
-	  for (k = 0; k < Nval; k++, Ptr++) {
-	    vec[j][0].elements.Int[k] = *Ptr;
-	  }
-	}
-      }
-      if (!strcmp (type, "short")) {
-	short *Ptr = data;
-	for (j = 0; j < Ny; j++) {
-	  for (k = 0; k < Nval; k++, Ptr++) {
-	    vec[j][0].elements.Int[k] = *Ptr;
-	  }
-	}
-      }
-      if (!strcmp (type, "int")) {
-	int *Ptr = data;
-	for (j = 0; j < Ny; j++) {
-	  for (k = 0; k < Nval; k++, Ptr++) {
-	    vec[j][0].elements.Int[k] = *Ptr;
-	  }
-	}
-      }
-      if (!strcmp (type, "int64_t")) {
-	int64_t *Ptr = data;
-	for (j = 0; j < Ny; j++) {
-	  for (k = 0; k < Nval; k++, Ptr++) {
-	    vec[j][0].elements.Int[k] = *Ptr;
-	  }
-	}
-      }
-      if (!strcmp (type, "float")) {
-	float *Ptr = data;
-	for (j = 0; j < Ny; j++) {
-	  for (k = 0; k < Nval; k++, Ptr++) {
-	    vec[j][0].elements.Flt[k] = *Ptr;
-	  }
-	}
-      }
-      if (!strcmp (type, "double")) {
-	double *Ptr = data;
-	for (j = 0; j < Ny; j++) {
-	  for (k = 0; k < Nval; k++, Ptr++) {
-	    vec[j][0].elements.Flt[k] = *Ptr;
-	  }
-	}
-      }
+      if (!VectorAssignDataTranspose (vec, type, data, Ny, Nval)) ESCAPE ("bad column type %s", type);
     }
     free (data);
@@ -546,2 +521,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);
+}
