6ce8e2a49e047984065d64c16b34219c8587d2ad
markd
  Fri Mar 7 13:37:21 2025 -0800
change tabFmt to require input file and generate help message if no arguments are given

diff --git src/utils/tabFmt/tabFmt.c src/utils/tabFmt/tabFmt.c
index 05bc22275eb..f7c3a488a24 100644
--- src/utils/tabFmt/tabFmt.c
+++ src/utils/tabFmt/tabFmt.c
@@ -1,274 +1,273 @@
 /* tabFmt - Format a tab-seperated file for human readability. */
 #include "common.h"
 #include "linefile.h"
 #include "options.h"
 
 void usage()
 /* Explain usage and exit. */
 {
 errAbort(
   "tabFmt - Format a tab-seperated file for human readability\n"
   "Usage:\n"
-  "   tabFmt [options] [inFile] [outFile]\n"
+  "   tabFmt [options] inFile [outFile]\n"
   "\n"
   "Options:\n"
     "  -right - right-justify\n"
     "  -numRight - right-justify numeric appearing columns (text header allowed)\n"
     "  -passNoTab - pass through lines with no tabs without including them in the\n"
     "   formatting\n"
     "  -h,-help - help\n"
   );
 }
 
 /* Command line validation table. */
 static struct optionSpec optionSpecs[] = {
     {"right", OPTION_BOOLEAN},
     {"numRight", OPTION_BOOLEAN},
     {"passNoTab", OPTION_BOOLEAN},
     {"h", OPTION_BOOLEAN},
     {"help", OPTION_BOOLEAN},
     {NULL, 0}
 };
 static boolean clRight = FALSE;
 static boolean clNumRight = FALSE;
 static boolean clPassNoTab = FALSE;
 
 /* column data type guess */
 enum colData {
     colUnknown = 0,
     colStr,
     colInt,
     colReal
 };
 
 /* Information about a column */
 struct colInfo {
     int width;
     int numCnt;  /* rows, except first, that appear numeric */
     boolean nonNumeric;  /* doen't look numeric */
 };
 
 /* Description of table. */
 struct tblInfo {
     int numCols;  /* current number of columns */
     int maxCols;  /* current maximum size */
     int numRows;  /* total number of rows */
     struct colInfo *cols;  /* array of column info */
 };
 
 static struct tblInfo* tblInfoNew()
 /* allocated new column sizes */
 {
 struct tblInfo* ti;
 AllocVar(ti);
 ti->maxCols = 256;
 AllocArray(ti->cols, ti->maxCols);
 return ti;
 }
 
 static void tblInfoFree(struct tblInfo** tip)
 /* free tblInfo object */
 {
 struct tblInfo* ti = *tip;
 if (ti != NULL)
     {
     freeMem(ti->cols);
     freeMem(ti);
     *tip = NULL;
     }
 }
 
 static void adjustColWidth(struct tblInfo *ti, int iCol, int width)
 /* set max on a column, expanding arrays as needed */
 {
 if (iCol >= ti->numCols)
     {
     ti->numCols++;
     if (ti->numCols > ti->maxCols)
         {
         int oldMax = ti->maxCols;
         ti->maxCols *= 2;
         ExpandArray(ti->cols, oldMax, ti->maxCols);
         }
     }
 ti->cols[iCol].width = max(ti->cols[iCol].width, width);
 }
 
 static boolean checkNumericLen(char *str,
                                int len)
 /* check if a string looks number. Does hex, and nn%,, but not scientific. */
 {
 char *p = str, *stop = str+len;
 if (((stop-p) >= 1) && ((*p == '+') || (*p == '-')))
     p++;
 if (((stop-p) >= 2) && (*p == '0') && ((*(p+1) == 'x') || (*(p+1) == 'X')))
     p += 2; /* looks hex */
 if (p == stop)
     return FALSE;  /* empty string */
 int dotCnt = 0;
 for (; p != stop; p++)
     {
     if (!((('0' <= *p) && (*p <= '9')) || (*p == '.') ||
           ((*p == '%') && (*(p+1) == '\0'))))
         return FALSE;
     if (*p == '.')
         dotCnt++;
     if (dotCnt > 1)
         return FALSE;
     }
 return TRUE;
 }
 
 /* check if a string looks number. Does hex, and nn%,, but not scientific. */
 static boolean checkNumeric(char *str)
 {
 return checkNumericLen(str, strlen(str));
 }
 
 static boolean looksNumeric(char *col)
 /* test if a column value looks numeric, handles scientific and hex
  * notation */
 {
 char *ePtr = strpbrk(col, "eE");
 if (ePtr == NULL)
     return checkNumeric(col);
 else
     {
     boolean ok = checkNumericLen(col, ePtr-col);
     if (ok)
         ok = checkNumeric(ePtr+1);
     return ok;
     }
 }
 
 static bool isIgnoredNoTabLine(char *line)
 /* should this line be ignored due to -passNoTab option */
 {
 return clPassNoTab && (strchr(line, '\t') == NULL);
 }
 
 static void colInfoUpdate(struct tblInfo *ti, char *col, int iCol, int dataLineNum)
 /* update information on a column */
 {
 adjustColWidth(ti, iCol, strlen(col));
 if (clNumRight && (dataLineNum > 0) && !looksNumeric(col))
     ti->cols[iCol].nonNumeric = TRUE;
 }
 
 static void tblInfoUpdate(struct tblInfo *ti, char *line, int dataLineNum)
 /* update tblInfo based on a line */
 {
 char *col = line;
 char *tab;
 int iCol = 0;
 while ((tab = strchr(col, '\t')) != NULL)
     {
     *tab = '\0';
     colInfoUpdate(ti, col, iCol, dataLineNum);
     *tab = '\t';
     iCol++;
     col = tab+1;
     }
 colInfoUpdate(ti, col, iCol, dataLineNum);
 ti->numRows++;
 }
 
 static struct slName *readLines(struct lineFile *inLf, struct tblInfo* ti)
 /* read lines and count tab widths */
 {
 struct slName *lines = NULL;
 char *line;
 int dataLineNum = 0;  // only count data lines, not ignored lines
 while (lineFileNext(inLf, &line, NULL))
     {
     if (!isIgnoredNoTabLine(line))
         {
         tblInfoUpdate(ti, line, dataLineNum);
         dataLineNum++;
         }
     slSafeAddHead(&lines, slNameNew(line));
     }
 slReverse(&lines);
 return lines;
 }
 
 static void writeCol(FILE *outFh, struct tblInfo* ti, int iCol, char *data, int len, boolean isLast)
 /* write a column, padding to width */
 {
 // isLast is an arg to allow variable number of columns
 int w;
 boolean rightFmt = clRight || (clNumRight && !ti->cols[iCol].nonNumeric);
 if (isLast && !rightFmt)
     w = len; // last column, don't add training white-space
 else
     w = (ti->cols[iCol].width) * (rightFmt ? 1 : -1);
 if (iCol > 0)
     fputs(" ", outFh);
 fprintf(outFh, "%*.*s", w, len, data);
 }
 
 static void writeLine(struct tblInfo* ti, char *line, FILE *outFh)
 /* write one line */
 {
 char *prev = line;
 char *tab;
 int iCol = 0;
 while ((tab = strchr(prev, '\t')) != NULL)
     {
     writeCol(outFh, ti, iCol, prev, tab-prev, FALSE);
     iCol++;
     prev = tab+1;
     }
 writeCol(outFh, ti, iCol, prev, strlen(prev), TRUE);
 fputc('\n', outFh);
 }
 
 static void writeLines(struct tblInfo* ti, struct slName *lines, FILE *outFh)
 /* write rows, padding columns to column widths */
 {
 for (struct slName *line = lines; line != NULL; line = line->next)
     {
     if (isIgnoredNoTabLine(line->name))
         {
         fputs(line->name, outFh);
         fputc('\n', outFh);
         }
     else
         {
         writeLine(ti, line->name, outFh);
         }
     }
 }
 
 static void tabFmt(char *inFile, char *outFile)
 /* format file */
 {
 struct tblInfo* ti = tblInfoNew();
 struct lineFile *inLf = lineFileOpen(inFile, TRUE);
 struct slName *lines = readLines(inLf, ti);
 lineFileClose(&inLf);
 
 FILE *outFh = mustOpen(outFile, "w");
 writeLines(ti, lines, outFh);
 carefulClose(&outFh);
 slFreeList(&lines);
 tblInfoFree(&ti);
 }
 
 int main(int argc, char **argv)
 /* Process command line. */
 {
 optionInit(&argc, argv, optionSpecs);
 if (optionExists("h") || optionExists("help"))
     usage();
 
-if (argc > 3)
+if ((argc < 2) || (argc > 3))
     usage();
 clRight = optionExists("right");
 clNumRight = optionExists("numRight");
 clPassNoTab = optionExists("passNoTab");
 
-tabFmt(((argc >= 2) ? argv[1] : "stdin"),
-       ((argc >= 3) ? argv[2] : "stdout"));
+tabFmt(argv[1], ((argc >= 3) ? argv[2] : "stdout"));
 return 0;
 }