diff options
author | Martijn van Duren <martijn@cvs.openbsd.org> | 2016-08-31 20:43:58 +0000 |
---|---|---|
committer | Martijn van Duren <martijn@cvs.openbsd.org> | 2016-08-31 20:43:58 +0000 |
commit | 2687a9d069df0a1fdce62fa5b640d6b0b8c16c1d (patch) | |
tree | d82e3ae605780545c9773db8577494007d677edb | |
parent | ea29da5441c855f93186146667e6aa4059377b8b (diff) |
Complete do-over for column to prepare for implementing UTF-8 support.
Fix a few bugs while here, namely:
- Fix the width-calculation of a tab-character
- Correct treatment of files without trailing newlines.
- Repair "-xc 7"
- Overflow protection for the number of rows and columns
Lots of help from and OK schwarze@
Get it in deraadt@
-rw-r--r-- | usr.bin/column/column.c | 272 |
1 files changed, 143 insertions, 129 deletions
diff --git a/usr.bin/column/column.c b/usr.bin/column/column.c index 918ba51c94d..b06b61e49ef 100644 --- a/usr.bin/column/column.c +++ b/usr.bin/column/column.c @@ -1,4 +1,4 @@ -/* $OpenBSD: column.c,v 1.23 2016/03/17 05:27:10 bentley Exp $ */ +/* $OpenBSD: column.c,v 1.24 2016/08/31 20:43:57 martijn Exp $ */ /* $NetBSD: column.c,v 1.4 1995/09/02 05:53:03 jtc Exp $ */ /* @@ -48,14 +48,18 @@ void input(FILE *); void maketbl(void); void print(void); void r_columnate(void); -void usage(void); +__dead void usage(void); -int termwidth; /* default terminal width */ +struct field { + char *content; + int width; +}; +int termwidth; /* default terminal width */ int entries; /* number of records */ int eval; /* exit value */ -int maxlength; /* longest record */ -char **list; /* array of pointers to records */ +int *maxwidths; /* longest record per column */ +struct field **table; /* one array of pointers per line */ char *separator = "\t "; /* field separator for table option */ int @@ -80,7 +84,7 @@ main(int argc, char *argv[]) err(1, "pledge"); tflag = xflag = 0; - while ((ch = getopt(argc, argv, "c:s:tx")) != -1) + while ((ch = getopt(argc, argv, "c:s:tx")) != -1) { switch(ch) { case 'c': termwidth = strtonum(optarg, 1, INT_MAX, &errstr); @@ -96,14 +100,16 @@ main(int argc, char *argv[]) case 'x': xflag = 1; break; - case '?': default: usage(); } - argc -= optind; + } + + if (!tflag) + separator = ""; argv += optind; - if (!*argv) { + if (*argv == NULL) { input(stdin); } else { for (; *argv; ++argv) { @@ -121,73 +127,63 @@ main(int argc, char *argv[]) err(1, "pledge"); if (!entries) - exit(eval); + return eval; if (tflag) maketbl(); - else if (maxlength >= termwidth) + else if (*maxwidths >= termwidth) print(); else if (xflag) c_columnate(); else r_columnate(); - exit(eval); + return eval; } -#define TAB 8 +#define INCR_NEXTTAB(x) (x = (x + 8) & ~7) void c_columnate(void) { - int chcnt, col, cnt, endcol, numcols; - char **lp; - - maxlength = (maxlength + TAB) & ~(TAB - 1); - numcols = termwidth / maxlength; - endcol = maxlength; - for (chcnt = col = 0, lp = list;; ++lp) { - chcnt += printf("%s", *lp); + int col, numcols; + struct field **row; + + INCR_NEXTTAB(*maxwidths); + if ((numcols = termwidth / *maxwidths) == 0) + numcols = 1; + for (col = 0, row = table;; ++row) { + fputs((*row)->content, stdout); if (!--entries) break; if (++col == numcols) { - chcnt = col = 0; - endcol = maxlength; + col = 0; putchar('\n'); } else { - while ((cnt = ((chcnt + TAB) & ~(TAB - 1))) <= endcol) { - (void)putchar('\t'); - chcnt = cnt; - } - endcol += maxlength; + while (INCR_NEXTTAB((*row)->width) <= *maxwidths) + putchar('\t'); } } - if (chcnt) - putchar('\n'); + putchar('\n'); } void r_columnate(void) { - int base, chcnt, cnt, col, endcol, numcols, numrows, row; + int base, col, numcols, numrows, row; - maxlength = (maxlength + TAB) & ~(TAB - 1); - numcols = termwidth / maxlength; - if (numcols == 0) + INCR_NEXTTAB(*maxwidths); + if ((numcols = termwidth / *maxwidths) == 0) numcols = 1; numrows = entries / numcols; if (entries % numcols) ++numrows; - for (row = 0; row < numrows; ++row) { - endcol = maxlength; - for (base = row, chcnt = col = 0; col < numcols; ++col) { - chcnt += printf("%s", list[base]); - if ((base += numrows) >= entries) + for (base = row = 0; row < numrows; base = ++row) { + for (col = 0; col < numcols; ++col, base += numrows) { + fputs(table[base]->content, stdout); + if (base + numrows >= entries) break; - while ((cnt = ((chcnt + TAB) & ~(TAB - 1))) <= endcol) { - (void)putchar('\t'); - chcnt = cnt; - } - endcol += maxlength; + while (INCR_NEXTTAB(table[base]->width) <= *maxwidths) + putchar('\t'); } putchar('\n'); } @@ -196,126 +192,144 @@ r_columnate(void) void print(void) { - int cnt; - char **lp; + int row; - for (cnt = entries, lp = list; cnt--; ++lp) - (void)printf("%s\n", *lp); + for (row = 0; row < entries; row++) + puts(table[row]->content); } -typedef struct _tbl { - char **list; - int cols, *len; -} TBL; -#define DEFCOLS 25 void maketbl(void) { - TBL *t; - int coloff, cnt; - char *p, **lp; - int *lens, maxcols = DEFCOLS; - TBL *tbl; - char **cols; - - t = tbl = ecalloc(entries, sizeof(TBL)); - cols = ereallocarray(NULL, maxcols, sizeof(char *)); - lens = ecalloc(maxcols, sizeof(int)); - for (cnt = 0, lp = list; cnt < entries; ++cnt, ++lp, ++t) { - for (coloff = 0, p = *lp; (cols[coloff] = strtok(p, separator)); - p = NULL) - if (++coloff == maxcols) { - maxcols += DEFCOLS; - cols = ereallocarray(cols, maxcols, - sizeof(char *)); - lens = ereallocarray(lens, maxcols, - sizeof(int)); - memset(lens + coloff, 0, DEFCOLS * sizeof(int)); - } - if (coloff == 0) - continue; - t->list = ecalloc(coloff, sizeof(char *)); - t->len = ecalloc(coloff, sizeof(int)); - for (t->cols = coloff; --coloff >= 0;) { - t->list[coloff] = cols[coloff]; - t->len[coloff] = strlen(cols[coloff]); - if (t->len[coloff] > lens[coloff]) - lens[coloff] = t->len[coloff]; - } + struct field **row; + int col; + + for (row = table; entries--; ++row) { + for (col = 0; (*row)[col + 1].content != NULL; ++col) + printf("%s%*s ", (*row)[col].content, + maxwidths[col] - (*row)[col].width, ""); + puts((*row)[col].content); } - for (cnt = 0, t = tbl; cnt < entries; ++cnt, ++t) { - if (t->cols > 0) { - for (coloff = 0; coloff < t->cols - 1; ++coloff) - (void)printf("%s%*s", t->list[coloff], - lens[coloff] - t->len[coloff] + 2, " "); - (void)printf("%s\n", t->list[coloff]); - } - } - free(tbl); - free(lens); - free(cols); } #define DEFNUM 1000 -#define MAXLINELEN (LINE_MAX + 1) +#define DEFCOLS 25 void input(FILE *fp) { - static size_t maxentry = DEFNUM; - int len; - char *p, buf[MAXLINELEN]; - - if (!list) - list = ecalloc(maxentry, sizeof(char *)); - while (fgets(buf, MAXLINELEN, fp)) { - for (p = buf; isspace((unsigned char)*p); ++p); - if (!*p) - continue; - if (!(p = strchr(p, '\n'))) { - warnx("line too long"); - eval = 1; - continue; + static int maxentry = 0; + static int maxcols = 0; + static struct field *cols = NULL; + int col, width; + size_t blen; + ssize_t llen; + char *p, *s, *buf = NULL; + + while ((llen = getline(&buf, &blen, fp)) > -1) { + if (buf[llen - 1] == '\n') + buf[llen - 1] = '\0'; + + p = buf; + for (col = 0;; col++) { + + /* Skip lines containing nothing but whitespace. */ + + for (s = p; s != '\0'; s++) + if (!isspace((unsigned char)*s)) + break; + if (*s == '\0') + break; + + /* Skip leading, multiple, and trailing separators. */ + + while (*p != '\0' && strchr(separator, *p) != NULL) + p++; + if (*p == '\0') + break; + + /* + * Found a non-empty field. + * Remember the start and measure the width. + */ + + s = p; + width = 0; + while (*p != '\0' && strchr(separator, *p) == NULL) { + if (*p++ == '\t') + INCR_NEXTTAB(width); + else + width++; + } + + if (col + 1 >= maxcols) { + if (maxcols > INT_MAX - DEFCOLS) + err(1, "too many columns"); + maxcols += DEFCOLS; + cols = ereallocarray(cols, maxcols, + sizeof(*cols)); + maxwidths = ereallocarray(maxwidths, maxcols, + sizeof(*maxwidths)); + memset(maxwidths + col, 0, + DEFCOLS * sizeof(*maxwidths)); + } + + /* + * Remember the width of the field, + * NUL-terminate and remeber the content, + * and advance beyond the separator, if any. + */ + + cols[col].width = width; + if (maxwidths[col] < width) + maxwidths[col] = width; + if (*p != '\0') + *p++ = '\0'; + if ((cols[col].content = strdup(s)) == NULL) + err(1, NULL); } - *p = '\0'; - len = p - buf; - if (maxlength < len) - maxlength = len; + if (col == 0) + continue; + + /* Found a non-empty line; remember it. */ + if (entries == maxentry) { + if (maxentry > INT_MAX - DEFNUM) + errx(1, "too many input lines"); maxentry += DEFNUM; - list = ereallocarray(list, maxentry, sizeof(char *)); - memset(list + entries, 0, DEFNUM * sizeof(char *)); + table = ereallocarray(table, maxentry, sizeof(*table)); } - if (!(list[entries++] = strdup(buf))) - err(1, NULL); + table[entries] = ereallocarray(NULL, col + 1, + sizeof(*(table[entries]))); + table[entries][col].content = NULL; + while (col--) + table[entries][col] = cols[col]; + entries++; } } void * -ereallocarray(void *oldp, size_t sz1, size_t sz2) +ereallocarray(void *ptr, size_t nmemb, size_t size) { - void *p; - - if (!(p = reallocarray(oldp, sz1, sz2))) + if ((ptr = reallocarray(ptr, nmemb, size)) == NULL) err(1, NULL); - return (p); + return ptr; } void * -ecalloc(size_t sz1, size_t sz2) +ecalloc(size_t nmemb, size_t size) { - void *p; + void *ptr; - if (!(p = calloc(sz1, sz2))) + if ((ptr = calloc(nmemb, size)) == NULL) err(1, NULL); - return (p); + return ptr; } -void +__dead void usage(void) { - (void)fprintf(stderr, "usage: column [-tx] [-c columns] [-s sep] [file ...]\n"); exit(1); |