column.c revision 27093
1/*
2 * Copyright (c) 1989, 1993, 1994
3 *	The Regents of the University of California.  All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 * 3. All advertising materials mentioning features or use of this software
14 *    must display the following acknowledgement:
15 *	This product includes software developed by the University of
16 *	California, Berkeley and its contributors.
17 * 4. Neither the name of the University nor the names of its contributors
18 *    may be used to endorse or promote products derived from this software
19 *    without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 * SUCH DAMAGE.
32 */
33
34#ifndef lint
35static char copyright[] =
36"@(#) Copyright (c) 1989, 1993, 1994\n\
37	The Regents of the University of California.  All rights reserved.\n";
38#endif /* not lint */
39
40#ifndef lint
41static char sccsid[] = "@(#)column.c	8.4 (Berkeley) 5/4/95";
42#endif /* not lint */
43
44#include <sys/types.h>
45#include <sys/ioctl.h>
46
47#include <ctype.h>
48#include <err.h>
49#include <limits.h>
50#include <stdio.h>
51#include <stdlib.h>
52#include <string.h>
53#include <unistd.h>
54
55void  c_columnate __P((void));
56void *emalloc __P((int));
57void  input __P((FILE *));
58void  maketbl __P((void));
59void  print __P((void));
60void  r_columnate __P((void));
61void  usage __P((void));
62
63int termwidth = 80;		/* default terminal width */
64
65int entries;			/* number of records */
66int eval;			/* exit value */
67int maxlength;			/* longest record */
68char **list;			/* array of pointers to records */
69char *separator = "\t ";	/* field separator for table option */
70
71int
72main(argc, argv)
73	int argc;
74	char **argv;
75{
76	struct winsize win;
77	FILE *fp;
78	int ch, tflag, xflag;
79	char *p;
80
81	if (ioctl(1, TIOCGWINSZ, &win) == -1 || !win.ws_col) {
82		if (p = getenv("COLUMNS"))
83			termwidth = atoi(p);
84	} else
85		termwidth = win.ws_col;
86
87	tflag = xflag = 0;
88	while ((ch = getopt(argc, argv, "c:s:tx")) != -1)
89		switch(ch) {
90		case 'c':
91			termwidth = atoi(optarg);
92			break;
93		case 's':
94			separator = optarg;
95			break;
96		case 't':
97			tflag = 1;
98			break;
99		case 'x':
100			xflag = 1;
101			break;
102		case '?':
103		default:
104			usage();
105		}
106	argc -= optind;
107	argv += optind;
108
109	if (!*argv)
110		input(stdin);
111	else for (; *argv; ++argv)
112		if (fp = fopen(*argv, "r")) {
113			input(fp);
114			(void)fclose(fp);
115		} else {
116			warn("%s", *argv);
117			eval = 1;
118		}
119
120	if (!entries)
121		exit(eval);
122
123	if (tflag)
124		maketbl();
125	else if (maxlength >= termwidth)
126		print();
127	else if (xflag)
128		c_columnate();
129	else
130		r_columnate();
131	exit(eval);
132}
133
134#define	TAB	8
135void
136c_columnate()
137{
138	int chcnt, col, cnt, endcol, numcols;
139	char **lp;
140
141	maxlength = (maxlength + TAB) & ~(TAB - 1);
142	numcols = termwidth / maxlength;
143	endcol = maxlength;
144	for (chcnt = col = 0, lp = list;; ++lp) {
145		chcnt += printf("%s", *lp);
146		if (!--entries)
147			break;
148		if (++col == numcols) {
149			chcnt = col = 0;
150			endcol = maxlength;
151			putchar('\n');
152		} else {
153			while ((cnt = (chcnt + TAB & ~(TAB - 1))) <= endcol) {
154				(void)putchar('\t');
155				chcnt = cnt;
156			}
157			endcol += maxlength;
158		}
159	}
160	if (chcnt)
161		putchar('\n');
162}
163
164void
165r_columnate()
166{
167	int base, chcnt, cnt, col, endcol, numcols, numrows, row;
168
169	maxlength = (maxlength + TAB) & ~(TAB - 1);
170	numcols = termwidth / maxlength;
171	numrows = entries / numcols;
172	if (entries % numcols)
173		++numrows;
174
175	for (row = 0; row < numrows; ++row) {
176		endcol = maxlength;
177		for (base = row, chcnt = col = 0; col < numcols; ++col) {
178			chcnt += printf("%s", list[base]);
179			if ((base += numrows) >= entries)
180				break;
181			while ((cnt = (chcnt + TAB & ~(TAB - 1))) <= endcol) {
182				(void)putchar('\t');
183				chcnt = cnt;
184			}
185			endcol += maxlength;
186		}
187		putchar('\n');
188	}
189}
190
191void
192print()
193{
194	int cnt;
195	char **lp;
196
197	for (cnt = entries, lp = list; cnt--; ++lp)
198		(void)printf("%s\n", *lp);
199}
200
201typedef struct _tbl {
202	char **list;
203	int cols, *len;
204} TBL;
205#define	DEFCOLS	25
206
207void
208maketbl()
209{
210	TBL *t;
211	int coloff, cnt;
212	char *p, **lp;
213	int *lens, maxcols;
214	TBL *tbl;
215	char **cols;
216
217	t = tbl = emalloc(entries * sizeof(TBL));
218	cols = emalloc((maxcols = DEFCOLS) * sizeof(char *));
219	lens = emalloc(maxcols * sizeof(int));
220	for (cnt = 0, lp = list; cnt < entries; ++cnt, ++lp, ++t) {
221		for (coloff = 0, p = *lp; cols[coloff] = strtok(p, separator);
222		    p = NULL)
223			if (++coloff == maxcols) {
224				if (!(cols = realloc(cols, (u_int)maxcols +
225				    DEFCOLS * sizeof(char *))) ||
226				    !(lens = realloc(lens,
227				    (u_int)maxcols + DEFCOLS * sizeof(int))))
228					err(1, NULL);
229				memset((char *)lens + maxcols * sizeof(int),
230				    0, DEFCOLS * sizeof(int));
231				maxcols += DEFCOLS;
232			}
233		t->list = emalloc(coloff * sizeof(char *));
234		t->len = emalloc(coloff * sizeof(int));
235		for (t->cols = coloff; --coloff >= 0;) {
236			t->list[coloff] = cols[coloff];
237			t->len[coloff] = strlen(cols[coloff]);
238			if (t->len[coloff] > lens[coloff])
239				lens[coloff] = t->len[coloff];
240		}
241	}
242	for (cnt = 0, t = tbl; cnt < entries; ++cnt, ++t) {
243		for (coloff = 0; coloff < t->cols  - 1; ++coloff)
244			(void)printf("%s%*s", t->list[coloff],
245			    lens[coloff] - t->len[coloff] + 2, " ");
246		(void)printf("%s\n", t->list[coloff]);
247	}
248}
249
250#define	DEFNUM		1000
251#define	MAXLINELEN	(LINE_MAX + 1)
252
253void
254input(fp)
255	FILE *fp;
256{
257	static int maxentry;
258	int len;
259	char *p, buf[MAXLINELEN];
260
261	if (!list)
262		list = emalloc((maxentry = DEFNUM) * sizeof(char *));
263	while (fgets(buf, MAXLINELEN, fp)) {
264		for (p = buf; *p && isspace(*p); ++p);
265		if (!*p)
266			continue;
267		if (!(p = strchr(p, '\n'))) {
268			warnx("line too long");
269			eval = 1;
270			continue;
271		}
272		*p = '\0';
273		len = p - buf;
274		if (maxlength < len)
275			maxlength = len;
276		if (entries == maxentry) {
277			maxentry += DEFNUM;
278			if (!(list = realloc(list,
279			    (u_int)maxentry * sizeof(char *))))
280				err(1, NULL);
281		}
282		list[entries++] = strdup(buf);
283	}
284}
285
286void *
287emalloc(size)
288	int size;
289{
290	char *p;
291
292	if (!(p = malloc(size)))
293		err(1, NULL);
294	memset(p, 0, size);
295	return (p);
296}
297
298void
299usage()
300{
301
302	(void)fprintf(stderr,
303	    "usage: column [-tx] [-c columns] [-s sep] [file ...]\n");
304	exit(1);
305}
306