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