GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.bin/column/column.c Lines: 115 148 77.7 %
Date: 2017-11-07 Branches: 86 121 71.1 %

Line Branch Exec Source
1
/*	$OpenBSD: column.c,v 1.25 2016/09/04 20:33:36 martijn Exp $	*/
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.
16
 * 3. Neither the name of the University nor the names of its contributors
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>
39
#include <locale.h>
40
#include <stdio.h>
41
#include <stdlib.h>
42
#include <string.h>
43
#include <unistd.h>
44
#include <wchar.h>
45
#include <wctype.h>
46
47
void  c_columnate(void);
48
void *ereallocarray(void *, size_t, size_t);
49
void *ecalloc(size_t, size_t);
50
void  input(FILE *);
51
void  maketbl(void);
52
void  print(void);
53
void  r_columnate(void);
54
__dead void usage(void);
55
56
struct field {
57
	char *content;
58
	int width;
59
};
60
61
int termwidth;			/* default terminal width */
62
int entries;			/* number of records */
63
int eval;			/* exit value */
64
int *maxwidths;			/* longest record per column */
65
struct field **table;		/* one array of pointers per line */
66
wchar_t *separator = L"\t ";	/* field separator for table option */
67
68
int
69
main(int argc, char *argv[])
70
{
71
270
	struct winsize win;
72
	FILE *fp;
73
	int ch, tflag, xflag;
74
	char *p;
75
135
	const char *errstr;
76
77
135
	setlocale(LC_CTYPE, "");
78
79
135
	termwidth = 0;
80
135
	if ((p = getenv("COLUMNS")) != NULL)
81
		termwidth = strtonum(p, 1, INT_MAX, NULL);
82

270
	if (termwidth == 0 && ioctl(STDOUT_FILENO, TIOCGWINSZ, &win) == 0 &&
83
	    win.ws_col > 0)
84
		termwidth = win.ws_col;
85
135
	if (termwidth == 0)
86
135
		termwidth = 80;
87
88
135
	if (pledge("stdio rpath flock cpath wpath", NULL) == -1)
89
		err(1, "pledge");
90
91
	tflag = xflag = 0;
92
459
	while ((ch = getopt(argc, argv, "c:s:tx")) != -1) {
93

189
		switch(ch) {
94
		case 'c':
95
108
			termwidth = strtonum(optarg, 1, INT_MAX, &errstr);
96
108
			if (errstr != NULL)
97
				errx(1, "%s: %s", errstr, optarg);
98
			break;
99
		case 's':
100
18
			if ((separator = reallocarray(NULL, strlen(optarg) + 1,
101
9
			    sizeof(*separator))) == NULL)
102
				err(1, NULL);
103
9
			if (mbstowcs(separator, optarg, strlen(optarg) + 1) ==
104
			    (size_t) -1)
105
				err(1, "sep");
106
			break;
107
		case 't':
108
			tflag = 1;
109
27
			break;
110
		case 'x':
111
			xflag = 1;
112
45
			break;
113
		default:
114
			usage();
115
		}
116
	}
117
118
135
	if (!tflag)
119
108
		separator = L"";
120
135
	argv += optind;
121
122
135
	if (*argv == NULL) {
123
135
		input(stdin);
124
135
	} 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
			}
133
		}
134
	}
135
136
135
	if (pledge("stdio flock rpath cpath wpath", NULL) == -1)
137
		err(1, "pledge");
138
139
135
	if (!entries)
140
		return eval;
141
142
135
	if (tflag)
143
27
		maketbl();
144
108
	else if (*maxwidths >= termwidth)
145
		print();
146
108
	else if (xflag)
147
45
		c_columnate();
148
	else
149
63
		r_columnate();
150
135
	return eval;
151
135
}
152
153
#define	INCR_NEXTTAB(x)	(x = (x + 8) & ~7)
154
void
155
c_columnate(void)
156
{
157
	int col, numcols;
158
	struct field **row;
159
160
90
	INCR_NEXTTAB(*maxwidths);
161
45
	if ((numcols = termwidth / *maxwidths) == 0)
162
		numcols = 1;
163
243
	for (col = 0, row = table;; ++row) {
164
243
		fputs((*row)->content, stdout);
165
243
		if (!--entries)
166
			break;
167
198
		if (++col == numcols) {
168
			col = 0;
169
180
			putchar('\n');
170
		} else {
171
225
			while (INCR_NEXTTAB((*row)->width) <= *maxwidths)
172
234
				putchar('\t');
173
		}
174
	}
175
90
	putchar('\n');
176
45
}
177
178
void
179
r_columnate(void)
180
{
181
	int base, col, numcols, numrows, row;
182
183
126
	INCR_NEXTTAB(*maxwidths);
184
63
	if ((numcols = termwidth / *maxwidths) == 0)
185
		numcols = 1;
186
63
	numrows = entries / numcols;
187
63
	if (entries % numcols)
188
18
		++numrows;
189
190
522
	for (base = row = 0; row < numrows; base = ++row) {
191
612
		for (col = 0; col < numcols; ++col, base += numrows) {
192
306
			fputs(table[base]->content, stdout);
193
306
			if (base + numrows >= entries)
194
				break;
195
225
			while (INCR_NEXTTAB(table[base]->width) <= *maxwidths)
196
234
				putchar('\t');
197
		}
198
396
		putchar('\n');
199
	}
200
63
}
201
202
void
203
print(void)
204
{
205
	int row;
206
207
	for (row = 0; row < entries; row++)
208
		puts(table[row]->content);
209
}
210
211
212
void
213
maketbl(void)
214
{
215
	struct field **row;
216
	int col;
217
218
207
	for (row = table; entries--; ++row) {
219
306
		for (col = 0; (*row)[col + 1].content != NULL; ++col)
220
90
			printf("%s%*s  ", (*row)[col].content,
221
90
			    maxwidths[col] - (*row)[col].width, "");
222
63
		puts((*row)[col].content);
223
	}
224
27
}
225
226
#define	DEFNUM		1000
227
#define	DEFCOLS		25
228
229
void
230
input(FILE *fp)
231
{
232
	static int maxentry = 0;
233
	static int maxcols = 0;
234
	static struct field *cols = NULL;
235
	int col, width, twidth;
236
270
	size_t blen;
237
	ssize_t llen;
238
135
	char *p, *s, *buf = NULL;
239
135
	wchar_t wc;
240
	int wlen;
241
242
1602
	while ((llen = getline(&buf, &blen, fp)) > -1) {
243
720
		if (buf[llen - 1] == '\n')
244
675
			buf[llen - 1] = '\0';
245
246
720
		p = buf;
247
1422
		for (col = 0;; col++) {
248
249
			/* Skip lines containing nothing but whitespace. */
250
251
3366
			for (s = p; (wlen = mbtowc(&wc, s, MB_CUR_MAX)) > 0;
252
261
			     s += wlen)
253
972
				if (!iswspace(wc))
254
					break;
255
1422
			if (*s == '\0')
256
				break;
257
258
			/* Skip leading, multiple, and trailing separators. */
259
260

1575
			while ((wlen = mbtowc(&wc, p, MB_CUR_MAX)) > 0 &&
261
756
			    wcschr(separator, wc) != NULL)
262
54
				p += wlen;
263
711
			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;
273
5886
			while (*p != '\0') {
274
2340
				if ((wlen = mbtowc(&wc, p, MB_CUR_MAX)) == -1) {
275
					width++;
276
					p++;
277
					continue;
278
				}
279
2340
				if (wcschr(separator, wc) != NULL)
280
					break;
281
2241
				if (*p == '\t')
282
					INCR_NEXTTAB(width);
283
				else  {
284
2241
					width += (twidth = wcwidth(wc)) == -1 ?
285
					    1 : twidth;
286
				}
287
2241
				p += wlen;
288
			}
289
290
702
			if (col + 1 >= maxcols) {
291
135
				if (maxcols > INT_MAX - DEFCOLS)
292
					err(1, "too many columns");
293
135
				maxcols += DEFCOLS;
294
135
				cols = ereallocarray(cols, maxcols,
295
				    sizeof(*cols));
296
135
				maxwidths = ereallocarray(maxwidths, maxcols,
297
				    sizeof(*maxwidths));
298
135
				memset(maxwidths + col, 0,
299
				    DEFCOLS * sizeof(*maxwidths));
300
135
			}
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
702
			cols[col].width = width;
309
702
			if (maxwidths[col] < width)
310
306
				maxwidths[col] = width;
311
702
			if (*p != '\0') {
312
99
				*p = '\0';
313
99
				p += wlen;
314
99
			}
315
702
			if ((cols[col].content = strdup(s)) == NULL)
316
				err(1, NULL);
317
		}
318
720
		if (col == 0)
319
			continue;
320
321
		/* Found a non-empty line; remember it. */
322
323
612
		if (entries == maxentry) {
324
135
			if (maxentry > INT_MAX - DEFNUM)
325
				errx(1, "too many input lines");
326
135
			maxentry += DEFNUM;
327
135
			table = ereallocarray(table, maxentry, sizeof(*table));
328
135
		}
329
612
		table[entries] = ereallocarray(NULL, col + 1,
330
		    sizeof(*(table[entries])));
331
612
		table[entries][col].content = NULL;
332
2628
		while (col--)
333
702
			table[entries][col] = cols[col];
334
612
		entries++;
335
	}
336
135
}
337
338
void *
339
ereallocarray(void *ptr, size_t nmemb, size_t size)
340
{
341
2034
	if ((ptr = reallocarray(ptr, nmemb, size)) == NULL)
342
		err(1, NULL);
343
1017
	return ptr;
344
}
345
346
void *
347
ecalloc(size_t nmemb, size_t size)
348
{
349
	void *ptr;
350
351
	if ((ptr = calloc(nmemb, size)) == NULL)
352
		err(1, NULL);
353
	return ptr;
354
}
355
356
__dead void
357
usage(void)
358
{
359
	(void)fprintf(stderr,
360
	    "usage: column [-tx] [-c columns] [-s sep] [file ...]\n");
361
	exit(1);
362
}