xref: /386bsd/usr/src/usr.bin/column/column.c (revision a2142627)
1 /*
2  * Copyright (c) 1989 The Regents of the University of California.
3  * 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
35 char copyright[] =
36 "@(#) Copyright (c) 1989 The Regents of the University of California.\n\
37  All rights reserved.\n";
38 #endif /* not lint */
39 
40 #ifndef lint
41 static char sccsid[] = "@(#)column.c	5.7 (Berkeley) 2/24/91";
42 #endif /* not lint */
43 
44 #include <sys/types.h>
45 #include <sys/ioctl.h>
46 #include <stdio.h>
47 #include <string.h>
48 #include <ctype.h>
49 
50 int termwidth = 80;		/* default terminal width */
51 
52 int entries;			/* number of records */
53 int eval;			/* exit value */
54 int maxlength;			/* longest record */
55 char **list;			/* array of pointers to records */
56 char *separator = "\t ";	/* field separator for table option */
57 
main(argc,argv)58 main(argc, argv)
59 	int argc;
60 	char **argv;
61 {
62 	extern char *optarg;
63 	extern int errno, optind;
64 	struct winsize win;
65 	FILE *fp;
66 	int ch, tflag, xflag;
67 	char *p, *getenv();
68 
69 	if (ioctl(1, TIOCGWINSZ, &win) == -1 || !win.ws_col) {
70 		if (p = getenv("COLUMNS"))
71 			termwidth = atoi(p);
72 	} else
73 		termwidth = win.ws_col;
74 
75 	xflag = 0;
76 	while ((ch = getopt(argc, argv, "c:s:tx")) != EOF)
77 		switch(ch) {
78 		case 'c':
79 			termwidth = atoi(optarg);
80 			break;
81 		case 's':
82 			separator = optarg;
83 			break;
84 		case 't':
85 			tflag = 1;
86 			break;
87 		case 'x':
88 			xflag = 1;
89 			break;
90 		case '?':
91 		default:
92 			usage();
93 		}
94 	argc -= optind;
95 	argv += optind;
96 
97 	if (!*argv)
98 		input(stdin);
99 	else for (; *argv; ++argv)
100 		if (fp = fopen(*argv, "r")) {
101 			input(fp);
102 			(void)fclose(fp);
103 		} else {
104 			(void)fprintf(stderr, "column: %s: %s\n", *argv,
105 			    strerror(errno));
106 			eval = 1;
107 		}
108 
109 	if (!entries)
110 		exit(eval);
111 
112 	if (tflag)
113 		maketbl();
114 	else if (maxlength >= termwidth)
115 		print();
116 	else if (xflag)
117 		c_columnate();
118 	else
119 		r_columnate();
120 	exit(eval);
121 }
122 
123 #define	TAB	8
c_columnate()124 c_columnate()
125 {
126 	register int chcnt, col, cnt, numcols;
127 	int endcol;
128 	char **lp;
129 
130 	maxlength = (maxlength + TAB) & ~(TAB - 1);
131 	numcols = termwidth / maxlength;
132 	endcol = maxlength;
133 	for (chcnt = col = 0, lp = list;; ++lp) {
134 		chcnt += printf("%s", *lp);
135 		if (!--entries)
136 			break;
137 		if (++col == numcols) {
138 			chcnt = col = 0;
139 			endcol = maxlength;
140 			putchar('\n');
141 		} else {
142 			while ((cnt = (chcnt + TAB & ~(TAB - 1))) <= endcol) {
143 				(void)putchar('\t');
144 				chcnt = cnt;
145 			}
146 			endcol += maxlength;
147 		}
148 	}
149 	if (chcnt)
150 		putchar('\n');
151 }
152 
r_columnate()153 r_columnate()
154 {
155 	register int base, chcnt, cnt, col;
156 	int endcol, numcols, numrows, row;
157 
158 	maxlength = (maxlength + TAB) & ~(TAB - 1);
159 	numcols = termwidth / maxlength;
160 	numrows = entries / numcols;
161 	if (entries % numcols)
162 		++numrows;
163 
164 	for (row = 0; row < numrows; ++row) {
165 		endcol = maxlength;
166 		for (base = row, chcnt = col = 0; col < numcols; ++col) {
167 			chcnt += printf("%s", list[base]);
168 			if ((base += numrows) >= entries)
169 				break;
170 			while ((cnt = (chcnt + TAB & ~(TAB - 1))) <= endcol) {
171 				(void)putchar('\t');
172 				chcnt = cnt;
173 			}
174 			endcol += maxlength;
175 		}
176 		putchar('\n');
177 	}
178 }
179 
print()180 print()
181 {
182 	register int cnt;
183 	register char **lp;
184 
185 	for (cnt = entries, lp = list; cnt--; ++lp)
186 		(void)printf("%s\n", *lp);
187 }
188 
189 typedef struct _tbl {
190 	char **list;
191 	int cols, *len;
192 } TBL;
193 #define	DEFCOLS	25
194 
maketbl()195 maketbl()
196 {
197 	register TBL *t;
198 	register int coloff, cnt;
199 	register char *p, **lp;
200 	int *lens, maxcols;
201 	TBL *tbl;
202 	char **cols, *emalloc(), *realloc();
203 
204 	t = tbl = (TBL *)emalloc(entries * sizeof(TBL));
205 	cols = (char **)emalloc((maxcols = DEFCOLS) * sizeof(char *));
206 	lens = (int *)emalloc(maxcols * sizeof(int));
207 	for (cnt = 0, lp = list; cnt < entries; ++cnt, ++lp, ++t) {
208 		for (coloff = 0, p = *lp; cols[coloff] = strtok(p, separator);
209 		    p = NULL)
210 			if (++coloff == maxcols) {
211 				if (!(cols = (char **)realloc((char *)cols,
212 				    (u_int)maxcols + DEFCOLS * sizeof(char *))) ||
213 				    !(lens = (int *)realloc((char *)lens,
214 				    (u_int)maxcols + DEFCOLS * sizeof(int))))
215 					nomem();
216 				bzero((char *)lens + maxcols * sizeof(int),
217 				    DEFCOLS * sizeof(int));
218 				maxcols += DEFCOLS;
219 			}
220 		t->list = (char **)emalloc(coloff * sizeof(char *));
221 		t->len = (int *)emalloc(coloff * sizeof(int));
222 		for (t->cols = coloff; --coloff >= 0;) {
223 			t->list[coloff] = cols[coloff];
224 			t->len[coloff] = strlen(cols[coloff]);
225 			if (t->len[coloff] > lens[coloff])
226 				lens[coloff] = t->len[coloff];
227 		}
228 	}
229 	for (cnt = 0, t = tbl; cnt < entries; ++cnt, ++t) {
230 		for (coloff = 0; coloff < t->cols  - 1; ++coloff)
231 			(void)printf("%s%*s", t->list[coloff],
232 			    lens[coloff] - t->len[coloff] + 2, " ");
233 		(void)printf("%s\n", t->list[coloff]);
234 	}
235 }
236 
237 #define	DEFNUM		1000
238 #define	MAXLINELEN	(2048 + 1)
239 
input(fp)240 input(fp)
241 	register FILE *fp;
242 {
243 	static int maxentry;
244 	register int len;
245 	register char *p;
246 	char buf[MAXLINELEN], *emalloc(), *realloc();
247 
248 	if (!list)
249 		list = (char **)emalloc((maxentry = DEFNUM) * sizeof(char *));
250 	while (fgets(buf, MAXLINELEN, fp)) {
251 		for (p = buf; *p && isspace(*p); ++p);
252 		if (!*p)
253 			continue;
254 		if (!(p = index(p, '\n'))) {
255 			(void)fprintf(stderr, "column: line too long.\n");
256 			eval = 1;
257 			continue;
258 		}
259 		*p = '\0';
260 		len = p - buf;
261 		if (maxlength < len)
262 			maxlength = len;
263 		if (entries == maxentry) {
264 			maxentry += DEFNUM;
265 			if (!(list =
266 			    (char **)realloc((char *)list,
267 			    (u_int)maxentry * sizeof(char *))))
268 				nomem();
269 		}
270 		list[entries++] = strdup(buf);
271 	}
272 }
273 
274 char *
emalloc(size)275 emalloc(size)
276 	int size;
277 {
278 	char *p, *malloc();
279 
280 	/* NOSTRICT */
281 	if (!(p = malloc((u_int)size)))
282 		nomem();
283 	bzero(p, size);
284 	return(p);
285 }
286 
nomem()287 nomem()
288 {
289 	(void)fprintf(stderr, "column: out of memory.\n");
290 	exit(1);
291 }
292 
usage()293 usage()
294 {
295 	(void)fprintf(stderr,
296 	    "usage: column [-tx] [-c columns] [file ...]\n");
297 	exit(1);
298 }
299