Index: trunk/Ohana/src/opihi/cmd.basic/list.c
===================================================================
--- trunk/Ohana/src/opihi/cmd.basic/list.c	(revision 39327)
+++ trunk/Ohana/src/opihi/cmd.basic/list.c	(revision 39328)
@@ -93,6 +93,42 @@
       set_str_variable (line, argv[i+3]);
     }
-    sprintf (line, "%s:n", argv[1]);
+    snprintf (line, 1024, "%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], "-join"))) {
+    if (argc != 4) {
+      gprint (GP_ERR, "USAGE: list (root) -join (variable)\n");
+      return (FALSE);
+    }
+    
+    int isFound;
+
+    snprintf (line, 1024, "%s:n", argv[1]);
+    int nList = get_int_variable (line, &isFound);
+    if (!isFound) {
+      gprint (GP_ERR, "list %s not found\n", argv[1]);
+      return FALSE;
+    }
+
+    char tmpline[1024], output[1024];
+    memset (tmpline, 0, 1024);
+    memset (output, 0, 1024);
+
+    for (i = 0; i < nList; i++) {
+      snprintf (line, 1024, "%s:%d", argv[1], i);
+      char *word = get_variable (line);
+      if (!word) break;
+      if (i > 0) {
+	snprintf (output, 1024, "%s %s", tmpline, word);
+      } else {
+	snprintf (output, 1024, "%s", word);
+      }
+      strcpy (tmpline, output);
+    }
+    set_str_variable (argv[3], output);
 
     return (TRUE);
@@ -118,5 +154,5 @@
     
     // old list must exist, or give an error
-    sprintf (line, "%s:n", argv[3]);
+    snprintf (line, 1024, "%s:n", argv[3]);
     N = get_int_variable (line, &found);
     if (!found) {
@@ -127,10 +163,10 @@
       
 
-    sprintf (line, "%s:n", argv[1]);
+    snprintf (line, 1024, "%s:n", argv[1]);
     set_int_variable (line, N);
     for (i = 0; i < N; i++) {
-      sprintf (line, "%s:%d", argv[3], i);
+      snprintf (line, 1024, "%s:%d", argv[3], i);
       value = get_variable (line);
-      // sprintf (line, "%s:%d", argv[1], i);
+      // snprintf (line, 1024, "%s:%d", argv[1], i);
       set_list_varname (line, argv[1], i, EXCEL_STYLE);
       set_str_variable (line, value);
@@ -146,12 +182,12 @@
     }
     
-    sprintf (line, "%s:n", argv[1]);
+    snprintf (line, 1024, "%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);
+      // snprintf (line, 1024, "%s:%d", argv[1], N + i);
       set_list_varname (line, argv[1], N + i, EXCEL_STYLE);
       set_str_variable (line, argv[i+3]);
     }
-    sprintf (line, "%s:n", argv[1]);
+    snprintf (line, 1024, "%s:n", argv[1]);
     set_int_variable (line, N + i);
 
@@ -170,8 +206,8 @@
     char line2[1024];
 
-    sprintf (line, "%s:n", argv[1]);
+    snprintf (line, 1024, "%s:n", argv[1]);
     N = get_int_variable (line, &found);
     for (i = 0; i < N; i++) {
-      // sprintf (line, "%s:%d", argv[1], i);
+      // snprintf (line, 1024, "%s:%d", argv[1], i);
       set_list_varname (line, argv[1], i, EXCEL_STYLE);
       value = get_variable (line);
@@ -180,5 +216,5 @@
 	free (value);
 	for (j = i + 1; j < N; j++) {
-	  // sprintf (line2, "%s:%d", argv[1], j);
+	  // snprintf (line, 10242, "%s:%d", argv[1], j);
 	  set_list_varname (line2, argv[1], j, EXCEL_STYLE);
 	  next_value = get_variable (line2);
@@ -187,5 +223,5 @@
 	}
 	DeleteNamedScalar (line);
-	sprintf (line2, "%s:n", argv[1]);
+	snprintf (line, 10242, "%s:n", argv[1]);
 	set_int_variable (line2, N - 1);
 	return (TRUE);
@@ -238,5 +274,5 @@
       depth --;
       if (depth < 0) { /* we hit the last "END", loop is done */
-	sprintf (line, "%s:n", argv[1]);
+	snprintf (line, 1024, "%s:n", argv[1]);
 	set_int_variable (line, i);
 	free (input);
@@ -246,5 +282,5 @@
 
     if (*input) { 
-      // sprintf (line, "%s:%d", argv[1], i);
+      // snprintf (line, 1024, "%s:%d", argv[1], i);
       set_list_varname (line, argv[1], i, EXCEL_STYLE);
       set_str_variable (line, input);
@@ -310,5 +346,5 @@
   free (val);
     
-  sprintf (line, "%s:n", listname);
+  snprintf (line, 1024, "%s:n", listname);
   set_int_variable (line, i);
   return (TRUE);
@@ -328,5 +364,5 @@
   // otherwise save all glob matches
   if (globList.gl_pathc == 0) {
-    sprintf (line, "%s:n", listname);
+    snprintf (line, 1024, "%s:n", listname);
     set_int_variable (line, 0);
     return TRUE;
@@ -338,5 +374,5 @@
     set_str_variable (line, globList.gl_pathv[i]);
   }
-  sprintf (line, "%s:n", listname);
+  snprintf (line, 1024, "%s:n", listname);
   set_int_variable (line, Nfile);
   return (TRUE);
@@ -371,5 +407,5 @@
     FREE (new);
   }
-  sprintf (line, "%s:n", argv[1]);
+  snprintf (line, 1024, "%s:n", argv[1]);
   set_int_variable (line, nWords);
 
Index: trunk/Ohana/src/opihi/cmd.data/read_vectors.c
===================================================================
--- trunk/Ohana/src/opihi/cmd.data/read_vectors.c	(revision 39327)
+++ trunk/Ohana/src/opihi/cmd.data/read_vectors.c	(revision 39328)
@@ -8,4 +8,6 @@
 void read_vectors_cleanup ();
 int read_table_sizes (Header *header);
+int read_table_fields (Header *header, int isCompressed, int VERBOSE);
+void list_extnames (void);
 
 int datafile (int argc, char **argv) {
@@ -87,4 +89,17 @@
     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");
+
+    gprint (GP_ERR, "     -fits options:\n");
+    gprint (GP_ERR, "       -transpose : read rows as columns and vice-versa\n");
+
+    gprint (GP_ERR, "       -keyword (FIELD) : use FIELD to identify the extension (default: EXTNAME)\n");
+    gprint (GP_ERR, "       -pad-if-short : allow reading of broken files by padding missing data\n");
+    gprint (GP_ERR, "       -sizes : return a list of table size information ($table:Nx, $table:Ny, $table:$Nfields)  \n");
+    gprint (GP_ERR, "       -list-fields : return a list of table columns ($tfields:0 to $tfields:n) (use -v to see the list)\n");
+    gprint (GP_ERR, "       -extnum (N) : use the number to find the Nth extension (ignore EXTNAME)\n");
+    gprint (GP_ERR, "       -range (start) (Nrows) : read the specified portion of the file\n");
+    gprint (GP_ERR, "        -v : verbose \n");
+    gprint (GP_ERR, "        -char-vectors : char fields will be saved as vectors NAME:0 -- NAME:n for n characters \n");
+
     return (FALSE);
   }
@@ -393,4 +408,10 @@
   }
 
+  int listFields = FALSE;
+  if ((N = get_argument (argc, argv, "-list-fields"))) {
+    remove_argument (N, &argc, argv);
+    listFields = TRUE;
+  }
+
   int Nextend = -1;
   if ((N = get_argument (argc, argv, "-extnum"))) {
@@ -432,10 +453,22 @@
   // }
 
-  if ((argc < 2) && !getSizes) ESCAPE ("USAGE: read -fits extension [-extnum] [-keyword key] name name ...\n");
-  if ((argc != 1) && getSizes) ESCAPE ("USAGE: read -sizes -fits extension [-extnum] (does not read data values)\n");
+  int listExtnames = FALSE;
+  if (!strcmp(extname, "-list")) {
+    listExtnames = TRUE;
+  }
+
+  if ((argc < 2) && !getSizes && !listFields && !listExtnames) ESCAPE ("USAGE: read -fits extension [-extnum] [-keyword key] name name ...\n");
+  if ((argc != 1) && getSizes)   ESCAPE ("USAGE: read -sizes -fits extension [-extnum] (does not read data values)\n");
+  if ((argc != 1) && listFields) ESCAPE ("USAGE: read -list-fields -fits extension [-extnum] (does not read data values)\n");
+  if ((argc != 1) && listExtnames) ESCAPE ("USAGE: read -list-fields -fits extension [-extnum] (does not read data values)\n");
 
   if (f == NULL) ESCAPE ("file not found\n");
   fseeko (f, 0LL, SEEK_SET);
   table.header = &header;
+
+  if (listExtnames) {
+    list_extnames ();
+    return TRUE;
+  }
 
   /**** find the appropriate extension and read header (if extname is a number, use count) ****/
@@ -491,4 +524,10 @@
   if (getSizes && !IsCompressed) {
     read_table_sizes (&header);
+    if (CCDKeyword != NULL) free (CCDKeyword); 
+    gfits_free_header (&header); 
+    return TRUE;
+  }
+  if (listFields) {
+    read_table_fields (&header, IsCompressed, VERBOSE);
     if (CCDKeyword != NULL) free (CCDKeyword); 
     gfits_free_header (&header); 
@@ -676,2 +715,126 @@
 }
 
+// read -fits foo -sizes -- Nx, Ny, Nfields -> $table:Nx, $table:Ny, $table:$Nfields
+// read -fits foo -fields 
+
+int read_table_fields (Header *header, int isCompressed, int VERBOSE) {
+  
+  int i, Nfields;
+  char field[32], varname[32], type[80], comment[80], format[80], unit[80], null[80], nval[80];
+
+  gfits_scan (header, "TFIELDS", "%d", 1, &Nfields);
+
+  set_int_variable ("table:Nx", header->Naxis[0]);
+  set_int_variable ("table:Ny", header->Naxis[1]);
+  set_int_variable ("table:Nfields", Nfields);
+  set_int_variable ("tfields:n", Nfields);
+
+  for (i = 1; i <= Nfields; i++) {
+    memset (field,   0, 32);
+    memset (type,    0, 80);
+    memset (comment, 0, 80);
+    memset (format,  0, 80);
+    memset (unit,    0, 80);
+    memset (null,    0, 80);
+    memset (nval,    0, 80);
+
+    snprintf (field, 32, "TTYPE%d", i);
+    gfits_scan (header, field, "%s", 1, type);
+    
+    snprintf (varname, 32, "tfields:%d", i - 1);
+    set_str_variable (varname, type);
+
+    gfits_scan_alt (header, field, "%C", 1, comment);
+
+    if (!isCompressed) {
+      snprintf (field, 32, "TFORM%d", i);
+    } else {
+      snprintf (field, 32, "ZFORM%d", i);
+    }
+    gfits_scan (header, field, "%s", 1, format);
+
+    snprintf (field, 32, "TUNIT%d", i);
+    gfits_scan (header, field, "%s", 1, unit);
+    snprintf (field, 32, "TNULL%d", i);
+    if (!gfits_scan (header, field, "%s", 1, null)) strcpy (null, "none");
+    snprintf (field, 32, "TNVAL%d", i);
+    if (!gfits_scan (header, field, "%s", 1, nval)) strcpy (nval, "none");
+
+    if (VERBOSE) gprint (GP_LOG, "%-18s %-18s %-6s %-8s %-8s %-32s\n", type, unit, format, null, nval, comment); 
+  }
+
+
+  return TRUE;
+}
+
+void list_extnames (void) {
+
+  off_t Nbytes, Nelem;
+  int i, Naxis, Ncomp, extend, status;
+  char extname[82], exttype[82], axisname[32];
+  Header header;
+
+  fseeko (f, 0, SEEK_SET);
+
+  gprint (GP_LOG, "%-30s %-15s NAXIS NAXIS(i)...\n", "extname", "datatype"); 
+
+  Ncomp = 0;
+  while (gfits_fread_header (f, &header)) {
+    /* extract the EXTNAME for this component (set to PHU for 0th component) */
+    status = gfits_scan (&header, "EXTNAME", "%s", 1, extname);
+    if (!status) {
+      if (Ncomp == 0) {
+	strcpy (extname, "PHU");
+      } else {
+	strcpy (extname, "UNKNOWN");
+      }
+    }
+    gprint (GP_LOG, "%-30s ", extname);
+
+    /* extract the datatype for this component (IMAGE for 0th component) */
+    if (Ncomp == 0) {
+      strcpy (exttype, "IMAGE");
+    } else {
+      status = gfits_scan (&header, "XTENSION", "%s", 1, exttype);
+      if (!status) {
+	strcpy (exttype, "UNKNOWN");
+      }
+    }
+    gprint (GP_LOG, "%-15s ", exttype);
+
+    /* extract the rank of the component */
+    status = gfits_scan (&header, "NAXIS",  "%d", 1, &Naxis);
+    if (!status) {
+      gprint (GP_ERR, "component %d is missing Naxis!\n", Ncomp);
+      Ncomp ++;
+      continue;
+    }
+    gprint (GP_LOG, " %4d", Naxis);
+
+    /* extract the individual axes */
+    for (i = 0; i < Naxis; i++) {
+      sprintf (axisname, "NAXIS%d", i+1);
+      status = gfits_scan (&header, axisname,  OFF_T_FMT, 1,  &Nelem);
+      if (!status) {
+	gprint (GP_ERR, "missing %s\n", axisname);
+      }
+      gprint (GP_LOG, " "OFF_T_FMT,  Nelem);
+    }
+    gprint (GP_LOG, "\n");
+
+    /* are extensions identified? (we will scan for them anyway) */
+    if (Ncomp == 0) {
+      extend = FALSE;
+      gfits_scan_alt (&header, "EXTEND", "%t", 1, &extend);
+      if (!extend) {
+	gprint (GP_ERR, "no extensions listed in file\n");
+      }
+    }
+
+    Nbytes = gfits_data_size (&header);
+    fseeko (f, Nbytes, SEEK_CUR);
+
+    Ncomp ++;
+  }
+  return;
+}
