Annotation of src/usr.bin/column/column.c, Revision 1.25
1.25 ! martijn 1: /* $OpenBSD: column.c,v 1.24 2016/08/31 20:43:57 martijn Exp $ */
1.1 deraadt 2: /* $NetBSD: column.c,v 1.4 1995/09/02 05:53:03 jtc Exp $ */
3:
4: /*
5: * Copyright (c) 1989, 1993, 1994
6: * The Regents of the University of California. All rights reserved.
7: *
8: * Redistribution and use in source and binary forms, with or without
9: * modification, are permitted provided that the following conditions
10: * are met:
11: * 1. Redistributions of source code must retain the above copyright
12: * notice, this list of conditions and the following disclaimer.
13: * 2. Redistributions in binary form must reproduce the above copyright
14: * notice, this list of conditions and the following disclaimer in the
15: * documentation and/or other materials provided with the distribution.
1.8 millert 16: * 3. Neither the name of the University nor the names of its contributors
1.1 deraadt 17: * may be used to endorse or promote products derived from this software
18: * without specific prior written permission.
19: *
20: * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21: * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23: * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24: * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25: * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26: * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27: * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28: * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29: * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30: * SUCH DAMAGE.
31: */
32:
33: #include <sys/types.h>
34: #include <sys/ioctl.h>
35:
36: #include <ctype.h>
37: #include <err.h>
38: #include <limits.h>
1.25 ! martijn 39: #include <locale.h>
1.1 deraadt 40: #include <stdio.h>
41: #include <stdlib.h>
42: #include <string.h>
43: #include <unistd.h>
1.25 ! martijn 44: #include <wchar.h>
! 45: #include <wctype.h>
1.1 deraadt 46:
1.7 millert 47: void c_columnate(void);
1.18 espie 48: void *ereallocarray(void *, size_t, size_t);
49: void *ecalloc(size_t, size_t);
1.7 millert 50: void input(FILE *);
51: void maketbl(void);
52: void print(void);
53: void r_columnate(void);
1.24 martijn 54: __dead void usage(void);
55:
56: struct field {
57: char *content;
58: int width;
59: };
1.1 deraadt 60:
1.23 bentley 61: int termwidth; /* default terminal width */
1.1 deraadt 62: int entries; /* number of records */
63: int eval; /* exit value */
1.24 martijn 64: int *maxwidths; /* longest record per column */
65: struct field **table; /* one array of pointers per line */
1.25 ! martijn 66: wchar_t *separator = L"\t "; /* field separator for table option */
1.1 deraadt 67:
68: int
1.9 deraadt 69: main(int argc, char *argv[])
1.1 deraadt 70: {
71: struct winsize win;
72: FILE *fp;
73: int ch, tflag, xflag;
74: char *p;
1.13 jdixon 75: const char *errstr;
1.1 deraadt 76:
1.25 ! martijn 77: setlocale(LC_CTYPE, "");
! 78:
1.23 bentley 79: termwidth = 0;
80: if ((p = getenv("COLUMNS")) != NULL)
81: termwidth = strtonum(p, 1, INT_MAX, NULL);
82: if (termwidth == 0 && ioctl(STDOUT_FILENO, TIOCGWINSZ, &win) == 0 &&
83: win.ws_col > 0)
1.1 deraadt 84: termwidth = win.ws_col;
1.23 bentley 85: if (termwidth == 0)
86: termwidth = 80;
1.1 deraadt 87:
1.21 deraadt 88: if (pledge("stdio rpath", NULL) == -1)
89: err(1, "pledge");
1.20 deraadt 90:
1.1 deraadt 91: tflag = xflag = 0;
1.24 martijn 92: while ((ch = getopt(argc, argv, "c:s:tx")) != -1) {
1.1 deraadt 93: switch(ch) {
94: case 'c':
1.13 jdixon 95: termwidth = strtonum(optarg, 1, INT_MAX, &errstr);
96: if (errstr != NULL)
97: errx(1, "%s: %s", errstr, optarg);
1.1 deraadt 98: break;
99: case 's':
1.25 ! martijn 100: if ((separator = reallocarray(NULL, strlen(optarg) + 1,
! 101: sizeof(*separator))) == NULL)
! 102: err(1, NULL);
! 103: if (mbstowcs(separator, optarg, strlen(optarg) + 1) ==
! 104: (size_t) -1)
! 105: err(1, "sep");
1.1 deraadt 106: break;
107: case 't':
108: tflag = 1;
109: break;
110: case 'x':
111: xflag = 1;
112: break;
113: default:
114: usage();
115: }
1.24 martijn 116: }
117:
118: if (!tflag)
1.25 ! martijn 119: separator = L"";
1.1 deraadt 120: argv += optind;
121:
1.24 martijn 122: if (*argv == NULL) {
1.1 deraadt 123: input(stdin);
1.20 deraadt 124: } else {
125: for (; *argv; ++argv) {
126: if ((fp = fopen(*argv, "r"))) {
127: input(fp);
128: (void)fclose(fp);
129: } else {
130: warn("%s", *argv);
131: eval = 1;
132: }
1.1 deraadt 133: }
1.20 deraadt 134: }
1.22 mmcc 135:
1.21 deraadt 136: if (pledge("stdio", NULL) == -1)
137: err(1, "pledge");
1.1 deraadt 138:
139: if (!entries)
1.24 martijn 140: return eval;
1.1 deraadt 141:
142: if (tflag)
143: maketbl();
1.24 martijn 144: else if (*maxwidths >= termwidth)
1.1 deraadt 145: print();
146: else if (xflag)
147: c_columnate();
148: else
149: r_columnate();
1.24 martijn 150: return eval;
1.1 deraadt 151: }
152:
1.24 martijn 153: #define INCR_NEXTTAB(x) (x = (x + 8) & ~7)
1.1 deraadt 154: void
1.9 deraadt 155: c_columnate(void)
1.1 deraadt 156: {
1.24 martijn 157: int col, numcols;
158: struct field **row;
1.1 deraadt 159:
1.24 martijn 160: INCR_NEXTTAB(*maxwidths);
161: if ((numcols = termwidth / *maxwidths) == 0)
162: numcols = 1;
163: for (col = 0, row = table;; ++row) {
164: fputs((*row)->content, stdout);
1.1 deraadt 165: if (!--entries)
166: break;
167: if (++col == numcols) {
1.24 martijn 168: col = 0;
1.1 deraadt 169: putchar('\n');
170: } else {
1.24 martijn 171: while (INCR_NEXTTAB((*row)->width) <= *maxwidths)
172: putchar('\t');
1.1 deraadt 173: }
174: }
1.24 martijn 175: putchar('\n');
1.1 deraadt 176: }
177:
178: void
1.9 deraadt 179: r_columnate(void)
1.1 deraadt 180: {
1.24 martijn 181: int base, col, numcols, numrows, row;
1.1 deraadt 182:
1.24 martijn 183: INCR_NEXTTAB(*maxwidths);
184: if ((numcols = termwidth / *maxwidths) == 0)
1.5 millert 185: numcols = 1;
1.1 deraadt 186: numrows = entries / numcols;
187: if (entries % numcols)
188: ++numrows;
189:
1.24 martijn 190: for (base = row = 0; row < numrows; base = ++row) {
191: for (col = 0; col < numcols; ++col, base += numrows) {
192: fputs(table[base]->content, stdout);
193: if (base + numrows >= entries)
1.1 deraadt 194: break;
1.24 martijn 195: while (INCR_NEXTTAB(table[base]->width) <= *maxwidths)
196: putchar('\t');
1.1 deraadt 197: }
198: putchar('\n');
199: }
200: }
201:
202: void
1.9 deraadt 203: print(void)
1.1 deraadt 204: {
1.24 martijn 205: int row;
1.1 deraadt 206:
1.24 martijn 207: for (row = 0; row < entries; row++)
208: puts(table[row]->content);
1.1 deraadt 209: }
210:
211:
212: void
1.9 deraadt 213: maketbl(void)
1.1 deraadt 214: {
1.24 martijn 215: struct field **row;
216: int col;
217:
218: for (row = table; entries--; ++row) {
219: for (col = 0; (*row)[col + 1].content != NULL; ++col)
220: printf("%s%*s ", (*row)[col].content,
221: maxwidths[col] - (*row)[col].width, "");
222: puts((*row)[col].content);
1.1 deraadt 223: }
224: }
225:
226: #define DEFNUM 1000
1.24 martijn 227: #define DEFCOLS 25
1.1 deraadt 228:
229: void
1.9 deraadt 230: input(FILE *fp)
1.1 deraadt 231: {
1.24 martijn 232: static int maxentry = 0;
233: static int maxcols = 0;
234: static struct field *cols = NULL;
1.25 ! martijn 235: int col, width, twidth;
1.24 martijn 236: size_t blen;
237: ssize_t llen;
238: char *p, *s, *buf = NULL;
1.25 ! martijn 239: wchar_t wc;
! 240: int wlen;
1.24 martijn 241:
242: while ((llen = getline(&buf, &blen, fp)) > -1) {
243: if (buf[llen - 1] == '\n')
244: buf[llen - 1] = '\0';
245:
246: p = buf;
247: for (col = 0;; col++) {
248:
249: /* Skip lines containing nothing but whitespace. */
250:
1.25 ! martijn 251: for (s = p; (wlen = mbtowc(&wc, s, MB_CUR_MAX)) > 0;
! 252: s += wlen)
! 253: if (!iswspace(wc))
1.24 martijn 254: break;
255: if (*s == '\0')
256: break;
257:
258: /* Skip leading, multiple, and trailing separators. */
259:
1.25 ! martijn 260: while ((wlen = mbtowc(&wc, p, MB_CUR_MAX)) > 0 &&
! 261: wcschr(separator, wc) != NULL)
! 262: p += wlen;
1.24 martijn 263: if (*p == '\0')
264: break;
265:
266: /*
267: * Found a non-empty field.
268: * Remember the start and measure the width.
269: */
270:
271: s = p;
272: width = 0;
1.25 ! martijn 273: while (*p != '\0') {
! 274: if ((wlen = mbtowc(&wc, p, MB_CUR_MAX)) == -1) {
! 275: width++;
! 276: p++;
! 277: continue;
! 278: }
! 279: if (wcschr(separator, wc) != NULL)
! 280: break;
! 281: if (*p == '\t')
1.24 martijn 282: INCR_NEXTTAB(width);
1.25 ! martijn 283: else {
! 284: width += (twidth = wcwidth(wc)) == -1 ?
! 285: 1 : twidth;
! 286: }
! 287: p += wlen;
1.24 martijn 288: }
289:
290: if (col + 1 >= maxcols) {
291: if (maxcols > INT_MAX - DEFCOLS)
292: err(1, "too many columns");
293: maxcols += DEFCOLS;
294: cols = ereallocarray(cols, maxcols,
295: sizeof(*cols));
296: maxwidths = ereallocarray(maxwidths, maxcols,
297: sizeof(*maxwidths));
298: memset(maxwidths + col, 0,
299: DEFCOLS * sizeof(*maxwidths));
300: }
301:
302: /*
303: * Remember the width of the field,
304: * NUL-terminate and remeber the content,
305: * and advance beyond the separator, if any.
306: */
307:
308: cols[col].width = width;
309: if (maxwidths[col] < width)
310: maxwidths[col] = width;
1.25 ! martijn 311: if (*p != '\0') {
! 312: *p = '\0';
! 313: p += wlen;
! 314: }
1.24 martijn 315: if ((cols[col].content = strdup(s)) == NULL)
316: err(1, NULL);
317: }
318: if (col == 0)
1.1 deraadt 319: continue;
1.24 martijn 320:
321: /* Found a non-empty line; remember it. */
322:
1.1 deraadt 323: if (entries == maxentry) {
1.24 martijn 324: if (maxentry > INT_MAX - DEFNUM)
325: errx(1, "too many input lines");
1.14 millert 326: maxentry += DEFNUM;
1.24 martijn 327: table = ereallocarray(table, maxentry, sizeof(*table));
1.1 deraadt 328: }
1.24 martijn 329: table[entries] = ereallocarray(NULL, col + 1,
330: sizeof(*(table[entries])));
331: table[entries][col].content = NULL;
332: while (col--)
333: table[entries][col] = cols[col];
334: entries++;
1.1 deraadt 335: }
336: }
337:
338: void *
1.24 martijn 339: ereallocarray(void *ptr, size_t nmemb, size_t size)
1.1 deraadt 340: {
1.24 martijn 341: if ((ptr = reallocarray(ptr, nmemb, size)) == NULL)
1.1 deraadt 342: err(1, NULL);
1.24 martijn 343: return ptr;
1.14 millert 344: }
345:
346: void *
1.24 martijn 347: ecalloc(size_t nmemb, size_t size)
1.14 millert 348: {
1.24 martijn 349: void *ptr;
1.14 millert 350:
1.24 martijn 351: if ((ptr = calloc(nmemb, size)) == NULL)
1.14 millert 352: err(1, NULL);
1.24 martijn 353: return ptr;
1.1 deraadt 354: }
355:
1.24 martijn 356: __dead void
1.9 deraadt 357: usage(void)
1.1 deraadt 358: {
359: (void)fprintf(stderr,
1.4 deraadt 360: "usage: column [-tx] [-c columns] [-s sep] [file ...]\n");
1.1 deraadt 361: exit(1);
362: }