Index: /trunk/Ohana/src/libdvo/include/dvodb.h
===================================================================
--- /trunk/Ohana/src/libdvo/include/dvodb.h	(revision 41268)
+++ /trunk/Ohana/src/libdvo/include/dvodb.h	(revision 41269)
@@ -5,9 +5,11 @@
 
 // Some values used by code moved to libdvo from opihi.
-enum {OPIHI_NOTYPE, OPIHI_FLT, OPIHI_INT};
+typedef enum {OPIHI_NOTYPE, OPIHI_FLT, OPIHI_INT, OPIHI_STR} opihiVectorTypes;
+
 # define opihi_flt double
-// # define opihi_int int64_t
 # define opihi_int long long int
 # define OPIHI_INT_FMT "%lld"
+
+// # define opihi_int int64_t
 
 typedef enum {
Index: /trunk/Ohana/src/libkapa/rotfont/helvetica12.h
===================================================================
--- /trunk/Ohana/src/libkapa/rotfont/helvetica12.h	(revision 41268)
+++ /trunk/Ohana/src/libkapa/rotfont/helvetica12.h	(revision 41269)
@@ -565,5 +565,5 @@
 {  9,   9, 10.67,   9, helvetica12_37_bits},
 {  7,   9,  8.00,   9, helvetica12_38_bits},
-{  2,   3,  2.65,   9, helvetica12_39_bits},
+{  2,   3,  2.66,   9, helvetica12_39_bits},
 {  3,  12,  3.99,   9, helvetica12_40_bits},
 {  3,  12,  3.99,   9, helvetica12_41_bits},
@@ -752,14 +752,14 @@
 {  6,  10,  0.00,  10, helvetica12_224_bits},
 {  6,  10,  0.00,  10, helvetica12_225_bits},
-{  6,  10,  0.00,  10, helvetica12_226_bits},
+{  6,  10, -5708879119777792.00,  10, helvetica12_226_bits},
 {  6,  10,  0.00,  10, helvetica12_227_bits},
-{  6,   9,  0.00,   9, helvetica12_228_bits},
+{  6,   9, -5709290899767296.00,   9, helvetica12_228_bits},
 {  6,  10,  0.00,  10, helvetica12_229_bits},
-{  9,   7,  0.00,   7, helvetica12_230_bits},
-{  5,  10,  0.00,   7, helvetica12_231_bits},
+{  9,   7, -365774995456.00,   7, helvetica12_230_bits},
+{  5,  10, -439036674048.00,   7, helvetica12_231_bits},
 {  5,  10,  0.00,  10, helvetica12_232_bits},
-{  5,  10,  0.00,  10, helvetica12_233_bits},
+{  5,  10, -5710030171013120.00,  10, helvetica12_233_bits},
 {  5,  10,  0.00,  10, helvetica12_234_bits},
-{  5,   9,  0.00,   9, helvetica12_235_bits},
+{  5,   9, -5712675870867456.00,   9, helvetica12_235_bits},
 {  2,  10,  0.00,  10, helvetica12_236_bits},
 {  2,  10,  0.00,  10, helvetica12_237_bits},
@@ -767,13 +767,13 @@
 {  3,   9,  0.00,   9, helvetica12_239_bits},
 {  5,  10,  0.00,  10, helvetica12_240_bits},
-{  5,  10,  0.00,  10, helvetica12_241_bits},
+{  5,  10, -0.00,  10, helvetica12_241_bits},
 {  5,  10,  0.00,  10, helvetica12_242_bits},
 {  5,  10,  0.00,  10, helvetica12_243_bits},
-{  5,  10,  0.00,  10, helvetica12_244_bits},
-{  5,  10,  0.00,  10, helvetica12_245_bits},
+{  5,  10, -5710012991143936.00,  10, helvetica12_244_bits},
+{  5,  10, -5710012991143936.00,  10, helvetica12_245_bits},
 {  5,   9,  0.00,   9, helvetica12_246_bits},
 {  5,   5,  0.00,   6, helvetica12_247_bits},
 {  7,   7,  0.00,   7, helvetica12_248_bits},
-{  5,  10,  0.00,  10, helvetica12_249_bits},
+{  5,  10, -439038246912.00,  10, helvetica12_249_bits},
 {  5,  10,  0.00,  10, helvetica12_250_bits},
 {  5,  10,  0.00,  10, helvetica12_251_bits},
Index: /trunk/Ohana/src/opihi/cmd.astro/cgrid.c
===================================================================
--- /trunk/Ohana/src/opihi/cmd.astro/cgrid.c	(revision 41268)
+++ /trunk/Ohana/src/opihi/cmd.astro/cgrid.c	(revision 41269)
@@ -182,4 +182,5 @@
     }
   }
+
   
   /* set spacings for DEC */
Index: /trunk/Ohana/src/opihi/cmd.data/create.c
===================================================================
--- /trunk/Ohana/src/opihi/cmd.data/create.c	(revision 41268)
+++ /trunk/Ohana/src/opihi/cmd.data/create.c	(revision 41269)
@@ -7,4 +7,31 @@
   Vector *vec;
   
+  // create a vector of empty strings
+  if ((N = get_argument (argc, argv, "-str"))) {
+    remove_argument (N, &argc, argv);
+
+    char *stringValue = NULL;
+    if ((N = get_argument (argc, argv, "-value"))) {
+      remove_argument (N, &argc, argv);
+      stringValue = strcreate (argv[N]);
+      remove_argument (N, &argc, argv);
+    }
+
+    if (argc != 3) {
+      gprint (GP_ERR, "USAGE: create vector Nelements -value word\n");
+      return (FALSE);
+    }
+
+    if ((vec = SelectVector (argv[1], ANYVECTOR, TRUE)) == NULL) return (FALSE);
+    int Nelements = atoi (argv[2]);
+
+    // a newly reset vector has NULL-valued pointers
+    ResetVector (vec, OPIHI_STR, Nelements);
+    for (int i = 0; i < Nelements; i++) {
+      vec[0].elements.Str[i] = stringValue ? strcreate (stringValue) : strcreate ("");
+    }
+    return TRUE;
+  }
+
   INT = FALSE;
   if ((N = get_argument (argc, argv, "-int"))) {
@@ -16,4 +43,5 @@
     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");
+    gprint (GP_ERR, " -str : resulting vector is string type (only give number of elements)\n");
     return (FALSE);
   }
Index: /trunk/Ohana/src/opihi/cmd.data/list_vectors.c
===================================================================
--- /trunk/Ohana/src/opihi/cmd.data/list_vectors.c	(revision 41268)
+++ /trunk/Ohana/src/opihi/cmd.data/list_vectors.c	(revision 41269)
@@ -32,16 +32,26 @@
   }
 
-  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]);
-    }
+  switch (vec->type) {
+    case OPIHI_FLT:
+      if (Variable) {
+	set_str_variable (Variable, "FLT");
+      } else {
+	gprint (GP_LOG, "%s : FLT\n", argv[1]);
+      }
+      break;
+    case OPIHI_INT:
+      if (Variable) {
+	set_str_variable (Variable, "INT");
+      } else {
+	gprint (GP_LOG, "%s : INT\n", argv[1]);
+      }
+      break;
+    case OPIHI_STR:
+      if (Variable) {
+	set_str_variable (Variable, "STR");
+      } else {
+	gprint (GP_LOG, "%s : STR\n", argv[1]);
+      }
+      break;
   }
   if (Variable) free (Variable);
Index: /trunk/Ohana/src/opihi/cmd.data/print_vectors.c
===================================================================
--- /trunk/Ohana/src/opihi/cmd.data/print_vectors.c	(revision 41268)
+++ /trunk/Ohana/src/opihi/cmd.data/print_vectors.c	(revision 41269)
@@ -56,7 +56,11 @@
 	if (vec[i][0].type == OPIHI_FLT) {
 	  gprint (GP_LOG, "%f ", vec[i][0].elements.Flt[j]);
-	} else {
+	} 
+	if (vec[i][0].type == OPIHI_INT) {
 	  gprint (GP_LOG, OPIHI_INT_FMT" ", vec[i][0].elements.Int[j]);
-	}
+	} 
+	if (vec[i][0].type == OPIHI_STR) {
+	  gprint (GP_LOG, "%s ", vec[i][0].elements.Str[j]);
+	} 
       }
     }
Index: /trunk/Ohana/src/opihi/cmd.data/read_vectors.c
===================================================================
--- /trunk/Ohana/src/opihi/cmd.data/read_vectors.c	(revision 41268)
+++ /trunk/Ohana/src/opihi/cmd.data/read_vectors.c	(revision 41269)
@@ -35,5 +35,5 @@
 
 // vector types
-enum {COLTYPE_NONE, COLTYPE_FLT, COLTYPE_INT, COLTYPE_TIME, COLTYPE_CHAR};
+enum {COLTYPE_NONE, COLTYPE_FLT, COLTYPE_INT, COLTYPE_TIME, COLTYPE_CHAR, COLTYPE_STR};
 
 static int      Nvec     = 0;
@@ -91,6 +91,7 @@
     gprint (GP_ERR, "     -skip N : skip N lines before reading\n");
     gprint (GP_ERR, "     column names may include a type: name:type\n");
-    gprint (GP_ERR, "       type is int, float, char, time\n");
+    gprint (GP_ERR, "       type is int, float, char, str, time\n");
     gprint (GP_ERR, "       for char, values are placed into a list $name:0 - $name:n\n");
+    gprint (GP_ERR, "       for str, values are placed into a string-typed vector\n");
     gprint (GP_ERR, "       for time, values are human-readable date/time strings YYYY/MM/DD,hh:mm:ss\n");
     gprint (GP_ERR, "       the resulting vector is a float based on the TIMEFORMAT, TIMEREF values\n");
@@ -152,4 +153,5 @@
       if (!strcasecmp(ptr, "int"))   { coltype[i] = COLTYPE_INT; }
       if (!strcasecmp(ptr, "char"))  { coltype[i] = COLTYPE_CHAR; }
+      if (!strcasecmp(ptr, "str"))   { coltype[i] = COLTYPE_STR; }
       if (!strcasecmp(ptr, "time"))  { coltype[i] = COLTYPE_TIME; }
       if (!coltype[i]) goto bad_colname;
@@ -213,4 +215,7 @@
 	// note that COLTYPE_TIME is a type of float
 	ResetVector (vec[i], OPIHI_FLT, NELEM);
+	break;
+      case COLTYPE_STR:
+	ResetVector (vec[i], OPIHI_STR, NELEM);
 	break;
       case COLTYPE_CHAR:
@@ -311,4 +316,20 @@
 	      break;
 	    }
+	  case COLTYPE_STR:
+	    {
+	      // 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);
+	      }
+	      vec[i][0].elements.Str[Nelem] = value; // needs to be freed later.
+	      break;
+	    }
 	  case COLTYPE_FLT:
 	    readStatus = IsCSV ? dparse_csv (&dvalue, col[i], c0) : dparse (&dvalue, col[i], c0);
@@ -348,4 +369,7 @@
 	      ResetVector (vec[i], OPIHI_FLT, NELEM);
 	      break;
+	    case COLTYPE_STR:
+	      ResetVector (vec[i], OPIHI_STR, NELEM);
+	      break;
 	    case COLTYPE_CHAR:
 	    default:
@@ -369,4 +393,7 @@
 	ResetVector (vec[i], OPIHI_FLT, Nelem);
 	break;
+      case COLTYPE_STR:
+	ResetVector (vec[i], OPIHI_STR, Nelem);
+	break;
       case COLTYPE_CHAR:
 	sprintf (varname, "%s:n", listname[i]);
@@ -469,6 +496,10 @@
   }
 
-  // if CharAsVectors, char fields will be saved as vectors NAME:0 -- NAME:n for n characters
-  // else char fields will be saved as $NAME:0 - $NAME:m for m rows
+  // by default, we now store a string-type field as a string-type vector.
+
+  // if CharAsList is selected (and My < 10000), char fields will be saved as $NAME:0 - $NAME:m for m rows
+
+  // if CharAsVectors is selected, char fields will be saved as vectors NAME:0 -- NAME:n for n characters
+
   // if (Ny > 10000), force CharAsVectors
   int CharAsVectors = FALSE;
@@ -476,4 +507,9 @@
     remove_argument (N, &argc, argv);
     CharAsVectors = TRUE;
+  }
+  int CharAsList = FALSE;
+  if ((N = get_argument (argc, argv, "-char-list"))) {
+    remove_argument (N, &argc, argv);
+    CharAsList = TRUE;
   }
 
@@ -651,26 +687,46 @@
     if (!FITS_TRANSPOSE) {
       // read string column into a list rather than a vector
-      if (!strcmp (type, "char") && !CharAsVectors && (Ny < 3000)) {
-	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, '$');
+      if (!strcmp (type, "char")) {
+	// save char-type field as a List:
+	if (CharAsList && (Ny < 3000)) {
+	  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);
 	  }
-	  set_str_variable (varname, value);
-	  free (value);
+	  sprintf (varname, "%s:n", fieldName);
+	  set_int_variable (varname, Ny);
+	  continue;
 	}
-	sprintf (varname, "%s:n", fieldName);
-	set_int_variable (varname, Ny);
-	continue;
+	// save char-type field as a string-type vector:
+	if (!CharAsVectors) {
+	  Vector *myVector = NULL;
+	  sprintf (name, "%s", argv[i]);
+	  if ((myVector = SelectVector (name, ANYVECTOR, TRUE)) == NULL) ESCAPE ("bad vector name\n");
+	  ResetVector (myVector, OPIHI_STR, Ny);
+
+	  char *Ptr = data;
+	  for (j = 0; j < Ny; j++) {
+	    myVector[0].elements.Str[j] = strncreate (&Ptr[j*Nval], Nval);
+	    // replace instances of $ with _
+	    char *p = strchr (myVector[0].elements.Str[j], '$');
+	    while (p) { *p = '_'; p = strchr (p, '$'); }
+	  }
+	  continue;
+	}
       }
 
       // define the multifield vector names (Nval vectors x Ny elements)
+      // CharAsVectors is handled below automatically
       ALLOCATE (vec, Vector *, Nval);
       for (j = 0; j < Nval; j++) {
Index: /trunk/Ohana/src/opihi/cmd.data/rotate.c
===================================================================
--- /trunk/Ohana/src/opihi/cmd.data/rotate.c	(revision 41268)
+++ /trunk/Ohana/src/opihi/cmd.data/rotate.c	(revision 41269)
@@ -268,4 +268,5 @@
 
   out_buff = (float *)buf[0].matrix.buffer;
+
   for (j = 0; j < Ly; j++) {
     for (i = 0; i < Lx; i++, out_buff++) {
Index: /trunk/Ohana/src/opihi/cmd.data/tvcolors.c
===================================================================
--- /trunk/Ohana/src/opihi/cmd.data/tvcolors.c	(revision 41268)
+++ /trunk/Ohana/src/opihi/cmd.data/tvcolors.c	(revision 41269)
@@ -37,21 +37,8 @@
     gprint (GP_ERR, "USAGE: tvcolors (colormap) [-nan red green blue]\n");
     gprint (GP_ERR, " colormap options : greyscale, -greyscale, rainbow, heat, fullcolor, ruffcolor (also grayscale, -grayscale)\n");
-    gprint (GP_ERR, " colormap file name of the for WORD:PATH where WORD = (file,csvf,lgcy,cetf)\n");
-    gprint (GP_ERR, " lgcy:\n");
-    gprint (GP_ERR, "   the colormap file must contain 4 columns: f R B G; each line defines a color transition.\n");
-    gprint (GP_ERR, "   f: 0 - 1 defines the scale value for the transition\n");
-    gprint (GP_ERR, "   R,B,G: 0 - 1 define the value of the color at the transition point\n");
-    gprint (GP_ERR, " file:\n");
+    gprint (GP_ERR, " colormap file: if colormap name is given as file:path/to/file, the colormap is read from the file\n");
     gprint (GP_ERR, "   the colormap file must contain 4 columns: f R G B; each line defines a color transition.\n");
     gprint (GP_ERR, "   f: 0 - 1 defines the scale value for the transition\n");
     gprint (GP_ERR, "   R,G,B: 0 - 1 define the value of the color at the transition point\n");
-    gprint (GP_ERR, " csvf:\n");
-    gprint (GP_ERR, "   the colormap file must contain 4 columns: f,R,G,B separate by commas; each line defines a color transition.\n");
-    gprint (GP_ERR, "   f: 0 - 1 defines the scale value for the transition\n");
-    gprint (GP_ERR, "   R,G,B: 0 - 1 define the value of the color at the transition point\n");
-    gprint (GP_ERR, " cetf:\n");
-    gprint (GP_ERR, "   the colormap file must 256 rows contain 3 columns: R,G,B separate by commas; each line defines a color transition.\n");
-    gprint (GP_ERR, "   R,G,B: 0 - 1 define the value of the color at the transition point\n");
-    gprint (GP_ERR, "   CETF-format files can be found at : https://peterkovesi.com/projects/colourmaps\n");
     return (FALSE);
   }
Index: /trunk/Ohana/src/opihi/cmd.data/write_vectors.c
===================================================================
--- /trunk/Ohana/src/opihi/cmd.data/write_vectors.c	(revision 41268)
+++ /trunk/Ohana/src/opihi/cmd.data/write_vectors.c	(revision 41269)
@@ -154,8 +154,8 @@
   if (ADD_HEADER) {
     for (j = 0; j < Nvec; j++) {
+      if (j == 0) fprintf (f, "# ");
       if (CSV) {
 	fprintf (f, "%s,", vec[j][0].name);
       } else {
-	if (j == 0) fprintf (f, "# ");
 	fprintf (f, "%s ", vec[j][0].name);
       }
@@ -166,18 +166,17 @@
   /* default output format */
   if (format == (char *) NULL) {
+    char padChar = CSV ? ',' : ' ';
     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, OPIHI_INT_FMT",", vec[j][0].elements.Int[i]);
-	  } else {
-	    fprintf (f, OPIHI_INT_FMT" ", vec[j][0].elements.Int[i]);
-	  }
+	switch (vec[j][0].type) {
+	  case OPIHI_FLT:
+	    fprintf (f, "%.12g%c", vec[j][0].elements.Flt[i], padChar);
+	    break;
+	  case OPIHI_INT:
+	    fprintf (f, OPIHI_INT_FMT"%c", vec[j][0].elements.Int[i], padChar);
+	    break;
+	  case OPIHI_STR:
+	    fprintf (f, "%s%c", vec[j][0].elements.Str[i], padChar);
+	    break;
 	}
       }
@@ -226,6 +225,9 @@
 	fmttype[j] = 'd';
 	break;
+      case 's':
+	fmttype[j] = 's';
+	break;
       default:
-	gprint (GP_ERR, "syntax error in format (only e,f,d,c,x allowed)\n");
+	gprint (GP_ERR, "syntax error in format (only e,f,d,c,x,s allowed)\n");
 	return (FALSE);
     }
@@ -234,4 +236,23 @@
   strcat (fmtlist[Nvec-1], p0);
   
+  // check format types against vector types:
+  for (j = 0; j < Nvec; j++) {
+    switch (vec[j][0].type) {
+      case OPIHI_FLT:
+      case OPIHI_INT:
+	if (fmttype[j] == 's') {
+	  gprint (GP_ERR, "mismatch between string format and numerical vector for %s\n", vec[j][0].name);
+	  return FALSE;
+	}
+	break;
+      case OPIHI_STR:
+	if (fmttype[j] != 's') {
+	  gprint (GP_ERR, "mismatch between numerical format and string vector for %s\n", vec[j][0].name);
+	  return FALSE;
+	}
+	break;
+    }
+  }
+
   for (i = 0; i < vec[0][0].Nelements; i++) {
     for (j = 0; j < Nvec; j++) {
@@ -249,4 +270,7 @@
 	  fprintf (f, fmtlist[j], (opihi_flt)(vec[j][0].elements.Int[i]));
 	}
+      } 
+      if (fmttype[j] == 's') {
+	fprintf (f, fmtlist[j], vec[j][0].elements.Str[i]);
       } 
     }
Index: /trunk/Ohana/src/opihi/include/dvomath.h
===================================================================
--- /trunk/Ohana/src/opihi/include/dvomath.h	(revision 41268)
+++ /trunk/Ohana/src/opihi/include/dvomath.h	(revision 41269)
@@ -6,9 +6,7 @@
 # define OPIHI_NAME_SIZE 1024
 
+/* OPIHI_FLT, OPIHI_INT and related are defined in libdvo/include/dvodb.h */
+
 # 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) { \
@@ -26,7 +24,4 @@
 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) */
@@ -40,4 +35,5 @@
   union {
     void      *Ptr;
+    char     **Str;
     opihi_flt *Flt;
     opihi_int *Int;
Index: /trunk/Ohana/src/opihi/lib.shell/VectorIO.c
===================================================================
--- /trunk/Ohana/src/opihi/lib.shell/VectorIO.c	(revision 41268)
+++ /trunk/Ohana/src/opihi/lib.shell/VectorIO.c	(revision 41269)
@@ -2,4 +2,16 @@
 void gfits_compress_timing ();
   
+static int VectorGetMaxStringLength (Vector *vec) {
+
+  int MaxLength = 0;
+
+  if (vec[0].type != OPIHI_STR) return 0;
+
+  for (int i = 0; i < vec[0].Nelements; i++) {
+    MaxLength = MAX (MaxLength, strlen(vec[0].elements.Str[i]));
+  }
+  return MaxLength;
+}
+
 // write a set of vectors to a FITS FTable structure (vectors names become fits column names)
 static int WriteVectorTable (FTable *ftable, char *extname, Vector **vec, int Nvec, char *format, char nativeOrder) {
@@ -44,5 +56,5 @@
       }
       ptr = endptr; // this should now point at the letter that is the format type
-      if ((*ptr != 'B') && (*ptr != 'I') && (*ptr != 'J') && (*ptr != 'K') && (*ptr != 'D') && (*ptr != 'E')) {
+      if ((*ptr != 'B') && (*ptr != 'I') && (*ptr != 'J') && (*ptr != 'K') && (*ptr != 'D') && (*ptr != 'E') && (*ptr != 'A')) {
 	gprint (GP_ERR, "error in binary table format %s: invalid character %c\n", format, *ptr);
 	goto escape;
@@ -57,5 +69,20 @@
       // tformat[2*j + 1] = 0; // a bit sleazy : use a 2xN string to store N 1-byte strings
 
-      j += Nelement[Nfield]; // advance past 
+      // For numeric multi-valued fields, the number of elements from the format defines the number of
+      // vectors which go into that field.  For string-type vectors, the format specifies the maximum number of characters
+      // that go into the field.
+
+      // for example, a format code of 3E should match a list of three numeric-type vectors while a format code of 15A should
+      // match a single string-type vector which will supply up to 15 chars per row.
+
+      if (*ptr != 'A') {
+	j += Nelement[Nfield]; // advance past Nelement vectors
+      } else {
+	if (vec[j][0].type != OPIHI_STR) {
+	  gprint (GP_ERR, "error in binary table format %s (mismatch between string format and numeric vector %s)\n", format, vec[j][0].name);
+	  goto escape;
+	}
+	j ++; // advance past a single string-type vector (validate that?)
+      }
       if (j > Nvec) {
 	gprint (GP_ERR, "error in binary table format %s (too few vectors for listed field)\n", format);
@@ -73,9 +100,22 @@
   } else {
     for (j = 0; j < Nvec; j++) {
-      // if the format is not defined, just use the native byte-widths
-      ALLOCATE (tformat[j], char, 2);
-      tformat[j][0] = (vec[j][0].type == OPIHI_FLT) ? 'D' : 'K'; // this depends on opihi_int == int64_t for Int
-      tformat[j][1] = 0;
-      Nelement[j] = 1;
+      switch (vec[j][0].type) {
+	case OPIHI_FLT:
+	case OPIHI_INT:
+	  // if the format is not defined, just use the native byte-widths
+	  ALLOCATE (tformat[j], char, 2);
+	  tformat[j][0] = (vec[j][0].type == OPIHI_FLT) ? 'D' : 'K';
+	  tformat[j][1] = 0;
+	  Nelement[j] = 1;
+	  break;
+	case OPIHI_STR:
+	  // we need to examine the vector to determine the length
+	  Nelement[j] = VectorGetMaxStringLength(vec[j]); 
+	  int Nchar = snprintf (tformat[j], 0, "%d%c", Nelement[j], 'A');
+	  ALLOCATE (tformat[j], char, Nchar + 1);
+	  int Nout = snprintf (tformat[j], Nchar + 1, "%d%c", Nelement[j], 'A');
+	  myAssert (Nout <= Nchar, "oops");
+	  break;
+      }
     }
     Nfield = Nvec;
@@ -90,5 +130,9 @@
     // this call supported multiple columns per named field
     gfits_define_bintable_column (theader, tformat[j], vec[ivec][0].name, NULL, NULL, 1.0, 0.0);
-    ivec += Nelement[j];
+    if (vec[ivec][0].type == OPIHI_STR) {
+      ivec ++;
+    } else {
+      ivec += Nelement[j];
+    }
   }
 
@@ -109,10 +153,29 @@
     // the first vector provides the name for the field
     char *fieldname = vec[ivec][0].name;
-    for (int k = 0; k < Nelement[j]; k++, ivec++) {
+
+    if (vec[ivec][0].type == OPIHI_STR) {
+      // string-type vectors need to be copied into a contiguous buffer with the right dimensions:
       Vector *thisvec = vec[ivec];
-      if (thisvec->type == OPIHI_FLT) {
-	gfits_set_bintable_column_reformat (theader, ftable, fieldname, "double",  thisvec->elements.Flt, thisvec->Nelements, k, nativeOrder);
-      } else {
-	gfits_set_bintable_column_reformat (theader, ftable, fieldname, "int64_t", thisvec->elements.Int, thisvec->Nelements, k, nativeOrder);
+
+      ALLOCATE_PTR (strbuffer, char, Nelement[j]*thisvec->Nelements);
+      for (int i = 0; i < thisvec->Nelements; i++) {
+	int nChar = MIN (strlen (thisvec->elements.Str[i]), Nelement[j]);
+	// fprintf (stderr, "%d %d %d : %d : %d : %s\n", ivec, j, i, Nelement[j], nChar, thisvec->elements.Str[i]);
+	memcpy (&strbuffer[i*Nelement[j]], thisvec->elements.Str[i], nChar);
+      }
+      gfits_set_bintable_column (theader, ftable, fieldname, strbuffer, thisvec->Nelements);
+      free (strbuffer);
+      ivec ++;
+    } else {
+      for (int k = 0; k < Nelement[j]; k++, ivec++) {
+	Vector *thisvec = vec[ivec];
+	switch (thisvec->type) {
+	  case OPIHI_FLT:
+	    gfits_set_bintable_column_reformat (theader, ftable, fieldname, "double",  thisvec->elements.Flt, thisvec->Nelements, k, nativeOrder);
+	    break;
+	  case OPIHI_INT:
+	    gfits_set_bintable_column_reformat (theader, ftable, fieldname, "int64_t", thisvec->elements.Int, thisvec->Nelements, k, nativeOrder);
+	    break;
+	}
       }
     }
Index: /trunk/Ohana/src/opihi/lib.shell/VectorOps.c
===================================================================
--- /trunk/Ohana/src/opihi/lib.shell/VectorOps.c	(revision 41268)
+++ /trunk/Ohana/src/opihi/lib.shell/VectorOps.c	(revision 41269)
@@ -129,4 +129,5 @@
   if (mode == NEWVECTOR) goto error;
   if (vectors[i]) {
+    // XXX warning: this will be a leak if vector is type OPIHI_STR
     if (vectors[i][0].elements.Ptr) free (vectors[i][0].elements.Ptr);
     free (vectors[i]);
@@ -150,4 +151,5 @@
   if (i == Nvectors) return (FALSE);
 
+  // XXX warning: this will be a leak if vector is type OPIHI_STR
   if (vectors[i][0].elements.Ptr) free (vectors[i][0].elements.Ptr);
   free (vectors[i]);
@@ -170,4 +172,5 @@
   if (i == Nvectors) return (FALSE);
 
+  // XXX warning: this will be a leak if vector is type OPIHI_STR
   if (vectors[i][0].elements.Ptr) free (vectors[i][0].elements.Ptr);
   free (vectors[i]);
@@ -191,4 +194,5 @@
 
 int CopyVector (Vector *out, Vector *in) {
+  // XXX warning: this will be a leak if vector is type OPIHI_STR
   if (out[0].elements.Ptr) free (out[0].elements.Ptr);
   out[0].Nelements = in[0].Nelements;
@@ -208,4 +212,5 @@
 
 int MatchVector(Vector *out, Vector *in, char type) {
+  // XXX warning: this will be a leak if vector is type OPIHI_STR
   if (out[0].elements.Ptr) free (out[0].elements.Ptr);
   out[0].Nelements = in[0].Nelements;
@@ -222,12 +227,28 @@
 int ResetVector (Vector *vec, char type, int Nelements) {
 
+  // if the supplied vector is a string but the output is not a string, we need to free
+  // the unused elements
+  if ((vec[0].type == OPIHI_STR) && (vec[0].type != type)) {
+    for (int i = 0; i < vec[0].Nelements; i++) {
+      FREE (vec[0].elements.Str[i]);
+    }
+  }
+
   // 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;
+
+  switch (type) {
+    case OPIHI_FLT:
+      REALLOCATE (vec[0].elements.Flt, opihi_flt, MAX(1, Nelements));
+      vec[0].type = OPIHI_FLT;
+      break;
+    case OPIHI_INT:
+      REALLOCATE (vec[0].elements.Int, opihi_int, MAX(1, Nelements));
+      vec[0].type = OPIHI_INT;
+      break;
+    case OPIHI_STR:
+      REALLOCATE (vec[0].elements.Str, char *, MAX(1, Nelements));
+      vec[0].type = OPIHI_STR;
+      break;
   }
   return TRUE;
@@ -251,13 +272,22 @@
 
 // SetVector (vecx, OPIHI_FLT, MAX (Npts, 1));
+// Use this for an unallocated vector (e.g., static variable)
 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;
+
+  switch (type) {
+    case OPIHI_FLT:
+      ALLOCATE (vec[0].elements.Flt, opihi_flt, MAX(1,Nelements));
+      vec[0].type = OPIHI_FLT;
+      break;
+    case OPIHI_INT:
+      ALLOCATE (vec[0].elements.Int, opihi_int, MAX(1,Nelements));
+      vec[0].type = OPIHI_INT;
+      break;
+    case OPIHI_STR:
+      ALLOCATE (vec[0].elements.Ptr, char *, MAX(1,Nelements));
+      vec[0].type = OPIHI_STR;
+      break;
   }
   return TRUE;
@@ -272,26 +302,35 @@
   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;
+  switch (type) {
+    case 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;
+      break;
+    }
+    case OPIHI_INT: {
+      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;
+      break;
+    }
+    case OPIHI_STR: 
+    default: 
+      // does it make sense to cast an int/flt vector to string?
+      break;
   }
   return TRUE;
@@ -342,8 +381,14 @@
   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);
+    switch (vectors[i][0].type) {
+      case OPIHI_FLT:
+	gprint (GP_LOG, "%5d %10s %10d (FLT)\n", i, vectors[i][0].name, vectors[i][0].Nelements);
+	break;
+      case OPIHI_INT:
+	gprint (GP_LOG, "%5d %10s %10d (INT)\n", i, vectors[i][0].name, vectors[i][0].Nelements);
+	break;
+      case OPIHI_STR:
+	gprint (GP_LOG, "%5d %10s %10d (STR)\n", i, vectors[i][0].name, vectors[i][0].Nelements);
+	break;
     }
   }
