Annotation of src/usr.bin/column/column.c, Revision 1.26
1.26 ! rob 1: /* $OpenBSD: column.c,v 1.25 2016/09/04 20:33:36 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);
1.7 millert 49: void input(FILE *);
50: void maketbl(void);
51: void print(void);
52: void r_columnate(void);
1.24 martijn 53: __dead void usage(void);
54:
55: struct field {
56: char *content;
57: int width;
58: };
1.1 deraadt 59:
1.23 bentley 60: int termwidth; /* default terminal width */
1.1 deraadt 61: int entries; /* number of records */
62: int eval; /* exit value */
1.24 martijn 63: int *maxwidths; /* longest record per column */
64: struct field **table; /* one array of pointers per line */
1.25 martijn 65: wchar_t *separator = L"\t "; /* field separator for table option */
1.1 deraadt 66:
67: int
1.9 deraadt 68: main(int argc, char *argv[])
1.1 deraadt 69: {
70: struct winsize win;
71: FILE *fp;
72: int ch, tflag, xflag;
73: char *p;
1.13 jdixon 74: const char *errstr;
1.1 deraadt 75:
1.25 martijn 76: setlocale(LC_CTYPE, "");
77:
1.23 bentley 78: termwidth = 0;
79: if ((p = getenv("COLUMNS")) != NULL)
80: termwidth = strtonum(p, 1, INT_MAX, NULL);
81: if (termwidth == 0 && ioctl(STDOUT_FILENO, TIOCGWINSZ, &win) == 0 &&
82: win.ws_col > 0)
1.1 deraadt 83: termwidth = win.ws_col;
1.23 bentley 84: if (termwidth == 0)
85: termwidth = 80;
1.1 deraadt 86:
1.21 deraadt 87: if (pledge("stdio rpath", NULL) == -1)
88: err(1, "pledge");
1.20 deraadt 89:
1.1 deraadt 90: tflag = xflag = 0;
1.24 martijn 91: while ((ch = getopt(argc, argv, "c:s:tx")) != -1) {
1.1 deraadt 92: switch(ch) {
93: case 'c':
1.13 jdixon 94: termwidth = strtonum(optarg, 1, INT_MAX, &errstr);
95: if (errstr != NULL)
96: errx(1, "%s: %s", errstr, optarg);
1.1 deraadt 97: break;
98: case 's':
1.25 martijn 99: if ((separator = reallocarray(NULL, strlen(optarg) + 1,
100: sizeof(*separator))) == NULL)
101: err(1, NULL);
102: if (mbstowcs(separator, optarg, strlen(optarg) + 1) ==
103: (size_t) -1)
104: err(1, "sep");
1.1 deraadt 105: break;
106: case 't':
107: tflag = 1;
108: break;
109: case 'x':
110: xflag = 1;
111: break;
112: default:
113: usage();
114: }
1.24 martijn 115: }
116:
117: if (!tflag)
1.25 martijn 118: separator = L"";
1.1 deraadt 119: argv += optind;
120:
1.24 martijn 121: if (*argv == NULL) {
1.1 deraadt 122: input(stdin);
1.20 deraadt 123: } else {
124: for (; *argv; ++argv) {
125: if ((fp = fopen(*argv, "r"))) {
126: input(fp);
127: (void)fclose(fp);
128: } else {
129: warn("%s", *argv);
130: eval = 1;
131: }
1.1 deraadt 132: }
1.20 deraadt 133: }
1.22 mmcc 134:
1.21 deraadt 135: if (pledge("stdio", NULL) == -1)
136: err(1, "pledge");
1.1 deraadt 137:
138: if (!entries)
1.24 martijn 139: return eval;
1.1 deraadt 140:
141: if (tflag)
142: maketbl();
1.24 martijn 143: else if (*maxwidths >= termwidth)
1.1 deraadt 144: print();
145: else if (xflag)
146: c_columnate();
147: else
148: r_columnate();
1.24 martijn 149: return eval;
1.1 deraadt 150: }
151:
1.24 martijn 152: #define INCR_NEXTTAB(x) (x = (x + 8) & ~7)
1.1 deraadt 153: void
1.9 deraadt 154: c_columnate(void)
1.1 deraadt 155: {
1.24 martijn 156: int col, numcols;
157: struct field **row;
1.1 deraadt 158:
1.24 martijn 159: INCR_NEXTTAB(*maxwidths);
160: if ((numcols = termwidth / *maxwidths) == 0)
161: numcols = 1;
162: for (col = 0, row = table;; ++row) {
163: fputs((*row)->content, stdout);
1.1 deraadt 164: if (!--entries)
165: break;
166: if (++col == numcols) {
1.24 martijn 167: col = 0;
1.1 deraadt 168: putchar('\n');
169: } else {
1.24 martijn 170: while (INCR_NEXTTAB((*row)->width) <= *maxwidths)
171: putchar('\t');
1.1 deraadt 172: }
173: }
1.24 martijn 174: putchar('\n');
1.1 deraadt 175: }
176:
177: void
1.9 deraadt 178: r_columnate(void)
1.1 deraadt 179: {
1.24 martijn 180: int base, col, numcols, numrows, row;
1.1 deraadt 181:
1.24 martijn 182: INCR_NEXTTAB(*maxwidths);
183: if ((numcols = termwidth / *maxwidths) == 0)
1.5 millert 184: numcols = 1;
1.1 deraadt 185: numrows = entries / numcols;
186: if (entries % numcols)
187: ++numrows;
188:
1.24 martijn 189: for (base = row = 0; row < numrows; base = ++row) {
190: for (col = 0; col < numcols; ++col, base += numrows) {
191: fputs(table[base]->content, stdout);
192: if (base + numrows >= entries)
1.1 deraadt 193: break;
1.24 martijn 194: while (INCR_NEXTTAB(table[base]->width) <= *maxwidths)
195: putchar('\t');
1.1 deraadt 196: }
197: putchar('\n');
198: }
199: }
200:
201: void
1.9 deraadt 202: print(void)
1.1 deraadt 203: {
1.24 martijn 204: int row;
1.1 deraadt 205:
1.24 martijn 206: for (row = 0; row < entries; row++)
207: puts(table[row]->content);
1.1 deraadt 208: }
209:
210:
211: void
1.9 deraadt 212: maketbl(void)
1.1 deraadt 213: {
1.24 martijn 214: struct field **row;
215: int col;
216:
217: for (row = table; entries--; ++row) {
218: for (col = 0; (*row)[col + 1].content != NULL; ++col)
219: printf("%s%*s ", (*row)[col].content,
220: maxwidths[col] - (*row)[col].width, "");
221: puts((*row)[col].content);
1.1 deraadt 222: }
223: }
224:
225: #define DEFNUM 1000
1.24 martijn 226: #define DEFCOLS 25
1.1 deraadt 227:
228: void
1.9 deraadt 229: input(FILE *fp)
1.1 deraadt 230: {
1.24 martijn 231: static int maxentry = 0;
232: static int maxcols = 0;
233: static struct field *cols = NULL;
1.25 martijn 234: int col, width, twidth;
1.24 martijn 235: size_t blen;
236: ssize_t llen;
237: char *p, *s, *buf = NULL;
1.25 martijn 238: wchar_t wc;
239: int wlen;
1.24 martijn 240:
241: while ((llen = getline(&buf, &blen, fp)) > -1) {
242: if (buf[llen - 1] == '\n')
243: buf[llen - 1] = '\0';
244:
245: p = buf;
246: for (col = 0;; col++) {
247:
248: /* Skip lines containing nothing but whitespace. */
249:
1.25 martijn 250: for (s = p; (wlen = mbtowc(&wc, s, MB_CUR_MAX)) > 0;
251: s += wlen)
252: if (!iswspace(wc))
1.24 martijn 253: break;
254: if (*s == '\0')
255: break;
256:
257: /* Skip leading, multiple, and trailing separators. */
258:
1.25 martijn 259: while ((wlen = mbtowc(&wc, p, MB_CUR_MAX)) > 0 &&
260: wcschr(separator, wc) != NULL)
261: p += wlen;
1.24 martijn 262: if (*p == '\0')
263: break;
264:
265: /*
266: * Found a non-empty field.
267: * Remember the start and measure the width.
268: */
269:
270: s = p;
271: width = 0;
1.25 martijn 272: while (*p != '\0') {
273: if ((wlen = mbtowc(&wc, p, MB_CUR_MAX)) == -1) {
274: width++;
275: p++;
276: continue;
277: }
278: if (wcschr(separator, wc) != NULL)
279: break;
280: if (*p == '\t')
1.24 martijn 281: INCR_NEXTTAB(width);
1.25 martijn 282: else {
283: width += (twidth = wcwidth(wc)) == -1 ?
284: 1 : twidth;
285: }
286: p += wlen;
1.24 martijn 287: }
288:
289: if (col + 1 >= maxcols) {
290: if (maxcols > INT_MAX - DEFCOLS)
291: err(1, "too many columns");
292: maxcols += DEFCOLS;
293: cols = ereallocarray(cols, maxcols,
294: sizeof(*cols));
295: maxwidths = ereallocarray(maxwidths, maxcols,
296: sizeof(*maxwidths));
297: memset(maxwidths + col, 0,
298: DEFCOLS * sizeof(*maxwidths));
299: }
300:
301: /*
302: * Remember the width of the field,
303: * NUL-terminate and remeber the content,
304: * and advance beyond the separator, if any.
305: */
306:
307: cols[col].width = width;
308: if (maxwidths[col] < width)
309: maxwidths[col] = width;
1.25 martijn 310: if (*p != '\0') {
311: *p = '\0';
312: p += wlen;
313: }
1.24 martijn 314: if ((cols[col].content = strdup(s)) == NULL)
315: err(1, NULL);
316: }
317: if (col == 0)
1.1 deraadt 318: continue;
1.24 martijn 319:
320: /* Found a non-empty line; remember it. */
321:
1.1 deraadt 322: if (entries == maxentry) {
1.24 martijn 323: if (maxentry > INT_MAX - DEFNUM)
324: errx(1, "too many input lines");
1.14 millert 325: maxentry += DEFNUM;
1.24 martijn 326: table = ereallocarray(table, maxentry, sizeof(*table));
1.1 deraadt 327: }
1.24 martijn 328: table[entries] = ereallocarray(NULL, col + 1,
329: sizeof(*(table[entries])));
330: table[entries][col].content = NULL;
331: while (col--)
332: table[entries][col] = cols[col];
333: entries++;
1.1 deraadt 334: }
335: }
336:
337: void *
1.24 martijn 338: ereallocarray(void *ptr, size_t nmemb, size_t size)
1.1 deraadt 339: {
1.24 martijn 340: if ((ptr = reallocarray(ptr, nmemb, size)) == NULL)
1.14 millert 341: err(1, NULL);
1.24 martijn 342: return ptr;
1.1 deraadt 343: }
344:
1.24 martijn 345: __dead void
1.9 deraadt 346: usage(void)
1.1 deraadt 347: {
348: (void)fprintf(stderr,
1.4 deraadt 349: "usage: column [-tx] [-c columns] [-s sep] [file ...]\n");
1.1 deraadt 350: exit(1);
351: }