version 1.23, 2016/03/17 05:27:10 |
version 1.24, 2016/08/31 20:43:57 |
|
|
void maketbl(void); |
void maketbl(void); |
void print(void); |
void print(void); |
void r_columnate(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 entries; /* number of records */ |
int eval; /* exit value */ |
int eval; /* exit value */ |
int maxlength; /* longest record */ |
int *maxwidths; /* longest record per column */ |
char **list; /* array of pointers to records */ |
struct field **table; /* one array of pointers per line */ |
char *separator = "\t "; /* field separator for table option */ |
char *separator = "\t "; /* field separator for table option */ |
|
|
int |
int |
|
|
err(1, "pledge"); |
err(1, "pledge"); |
|
|
tflag = xflag = 0; |
tflag = xflag = 0; |
while ((ch = getopt(argc, argv, "c:s:tx")) != -1) |
while ((ch = getopt(argc, argv, "c:s:tx")) != -1) { |
switch(ch) { |
switch(ch) { |
case 'c': |
case 'c': |
termwidth = strtonum(optarg, 1, INT_MAX, &errstr); |
termwidth = strtonum(optarg, 1, INT_MAX, &errstr); |
|
|
case 'x': |
case 'x': |
xflag = 1; |
xflag = 1; |
break; |
break; |
case '?': |
|
default: |
default: |
usage(); |
usage(); |
} |
} |
argc -= optind; |
} |
|
|
|
if (!tflag) |
|
separator = ""; |
argv += optind; |
argv += optind; |
|
|
if (!*argv) { |
if (*argv == NULL) { |
input(stdin); |
input(stdin); |
} else { |
} else { |
for (; *argv; ++argv) { |
for (; *argv; ++argv) { |
|
|
err(1, "pledge"); |
err(1, "pledge"); |
|
|
if (!entries) |
if (!entries) |
exit(eval); |
return eval; |
|
|
if (tflag) |
if (tflag) |
maketbl(); |
maketbl(); |
else if (maxlength >= termwidth) |
else if (*maxwidths >= termwidth) |
print(); |
print(); |
else if (xflag) |
else if (xflag) |
c_columnate(); |
c_columnate(); |
else |
else |
r_columnate(); |
r_columnate(); |
exit(eval); |
return eval; |
} |
} |
|
|
#define TAB 8 |
#define INCR_NEXTTAB(x) (x = (x + 8) & ~7) |
void |
void |
c_columnate(void) |
c_columnate(void) |
{ |
{ |
int chcnt, col, cnt, endcol, numcols; |
int col, numcols; |
char **lp; |
struct field **row; |
|
|
maxlength = (maxlength + TAB) & ~(TAB - 1); |
INCR_NEXTTAB(*maxwidths); |
numcols = termwidth / maxlength; |
if ((numcols = termwidth / *maxwidths) == 0) |
endcol = maxlength; |
numcols = 1; |
for (chcnt = col = 0, lp = list;; ++lp) { |
for (col = 0, row = table;; ++row) { |
chcnt += printf("%s", *lp); |
fputs((*row)->content, stdout); |
if (!--entries) |
if (!--entries) |
break; |
break; |
if (++col == numcols) { |
if (++col == numcols) { |
chcnt = col = 0; |
col = 0; |
endcol = maxlength; |
|
putchar('\n'); |
putchar('\n'); |
} else { |
} else { |
while ((cnt = ((chcnt + TAB) & ~(TAB - 1))) <= endcol) { |
while (INCR_NEXTTAB((*row)->width) <= *maxwidths) |
(void)putchar('\t'); |
putchar('\t'); |
chcnt = cnt; |
|
} |
|
endcol += maxlength; |
|
} |
} |
} |
} |
if (chcnt) |
putchar('\n'); |
putchar('\n'); |
|
} |
} |
|
|
void |
void |
r_columnate(void) |
r_columnate(void) |
{ |
{ |
int base, chcnt, cnt, col, endcol, numcols, numrows, row; |
int base, col, numcols, numrows, row; |
|
|
maxlength = (maxlength + TAB) & ~(TAB - 1); |
INCR_NEXTTAB(*maxwidths); |
numcols = termwidth / maxlength; |
if ((numcols = termwidth / *maxwidths) == 0) |
if (numcols == 0) |
|
numcols = 1; |
numcols = 1; |
numrows = entries / numcols; |
numrows = entries / numcols; |
if (entries % numcols) |
if (entries % numcols) |
++numrows; |
++numrows; |
|
|
for (row = 0; row < numrows; ++row) { |
for (base = row = 0; row < numrows; base = ++row) { |
endcol = maxlength; |
for (col = 0; col < numcols; ++col, base += numrows) { |
for (base = row, chcnt = col = 0; col < numcols; ++col) { |
fputs(table[base]->content, stdout); |
chcnt += printf("%s", list[base]); |
if (base + numrows >= entries) |
if ((base += numrows) >= entries) |
|
break; |
break; |
while ((cnt = ((chcnt + TAB) & ~(TAB - 1))) <= endcol) { |
while (INCR_NEXTTAB(table[base]->width) <= *maxwidths) |
(void)putchar('\t'); |
putchar('\t'); |
chcnt = cnt; |
|
} |
|
endcol += maxlength; |
|
} |
} |
putchar('\n'); |
putchar('\n'); |
} |
} |
|
|
void |
void |
print(void) |
print(void) |
{ |
{ |
int cnt; |
int row; |
char **lp; |
|
|
|
for (cnt = entries, lp = list; cnt--; ++lp) |
for (row = 0; row < entries; row++) |
(void)printf("%s\n", *lp); |
puts(table[row]->content); |
} |
} |
|
|
typedef struct _tbl { |
|
char **list; |
|
int cols, *len; |
|
} TBL; |
|
#define DEFCOLS 25 |
|
|
|
void |
void |
maketbl(void) |
maketbl(void) |
{ |
{ |
TBL *t; |
struct field **row; |
int coloff, cnt; |
int col; |
char *p, **lp; |
|
int *lens, maxcols = DEFCOLS; |
|
TBL *tbl; |
|
char **cols; |
|
|
|
t = tbl = ecalloc(entries, sizeof(TBL)); |
for (row = table; entries--; ++row) { |
cols = ereallocarray(NULL, maxcols, sizeof(char *)); |
for (col = 0; (*row)[col + 1].content != NULL; ++col) |
lens = ecalloc(maxcols, sizeof(int)); |
printf("%s%*s ", (*row)[col].content, |
for (cnt = 0, lp = list; cnt < entries; ++cnt, ++lp, ++t) { |
maxwidths[col] - (*row)[col].width, ""); |
for (coloff = 0, p = *lp; (cols[coloff] = strtok(p, separator)); |
puts((*row)[col].content); |
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]; |
|
} |
|
} |
} |
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 DEFNUM 1000 |
#define MAXLINELEN (LINE_MAX + 1) |
#define DEFCOLS 25 |
|
|
void |
void |
input(FILE *fp) |
input(FILE *fp) |
{ |
{ |
static size_t maxentry = DEFNUM; |
static int maxentry = 0; |
int len; |
static int maxcols = 0; |
char *p, buf[MAXLINELEN]; |
static struct field *cols = NULL; |
|
int col, width; |
|
size_t blen; |
|
ssize_t llen; |
|
char *p, *s, *buf = NULL; |
|
|
if (!list) |
while ((llen = getline(&buf, &blen, fp)) > -1) { |
list = ecalloc(maxentry, sizeof(char *)); |
if (buf[llen - 1] == '\n') |
while (fgets(buf, MAXLINELEN, fp)) { |
buf[llen - 1] = '\0'; |
for (p = buf; isspace((unsigned char)*p); ++p); |
|
if (!*p) |
p = buf; |
continue; |
for (col = 0;; col++) { |
if (!(p = strchr(p, '\n'))) { |
|
warnx("line too long"); |
/* Skip lines containing nothing but whitespace. */ |
eval = 1; |
|
continue; |
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'; |
if (col == 0) |
len = p - buf; |
continue; |
if (maxlength < len) |
|
maxlength = len; |
/* Found a non-empty line; remember it. */ |
|
|
if (entries == maxentry) { |
if (entries == maxentry) { |
|
if (maxentry > INT_MAX - DEFNUM) |
|
errx(1, "too many input lines"); |
maxentry += DEFNUM; |
maxentry += DEFNUM; |
list = ereallocarray(list, maxentry, sizeof(char *)); |
table = ereallocarray(table, maxentry, sizeof(*table)); |
memset(list + entries, 0, DEFNUM * sizeof(char *)); |
|
} |
} |
if (!(list[entries++] = strdup(buf))) |
table[entries] = ereallocarray(NULL, col + 1, |
err(1, NULL); |
sizeof(*(table[entries]))); |
|
table[entries][col].content = NULL; |
|
while (col--) |
|
table[entries][col] = cols[col]; |
|
entries++; |
} |
} |
} |
} |
|
|
void * |
void * |
ereallocarray(void *oldp, size_t sz1, size_t sz2) |
ereallocarray(void *ptr, size_t nmemb, size_t size) |
{ |
{ |
void *p; |
if ((ptr = reallocarray(ptr, nmemb, size)) == NULL) |
|
|
if (!(p = reallocarray(oldp, sz1, sz2))) |
|
err(1, NULL); |
err(1, NULL); |
return (p); |
return ptr; |
} |
} |
|
|
void * |
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); |
err(1, NULL); |
return (p); |
return ptr; |
} |
} |
|
|
void |
__dead void |
usage(void) |
usage(void) |
{ |
{ |
|
|
(void)fprintf(stderr, |
(void)fprintf(stderr, |
"usage: column [-tx] [-c columns] [-s sep] [file ...]\n"); |
"usage: column [-tx] [-c columns] [-s sep] [file ...]\n"); |
exit(1); |
exit(1); |