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