xref: /original-bsd/usr.bin/column/column.c (revision 502feadc)
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 are permitted
6  * provided that the above copyright notice and this paragraph are
7  * duplicated in all such forms and that any documentation,
8  * advertising materials, and other materials related to such
9  * distribution and use acknowledge that the software was developed
10  * by the University of California, Berkeley.  The name of the
11  * University may not be used to endorse or promote products derived
12  * from this software without specific prior written permission.
13  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
14  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
15  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
16  */
17 
18 #ifndef lint
19 char copyright[] =
20 "@(#) Copyright (c) 1989 The Regents of the University of California.\n\
21  All rights reserved.\n";
22 #endif /* not lint */
23 
24 #ifndef lint
25 static char sccsid[] = "@(#)column.c	5.5 (Berkeley) 05/15/90";
26 #endif /* not lint */
27 
28 #include <sys/types.h>
29 #include <sys/ioctl.h>
30 #include <stdio.h>
31 #include <string.h>
32 #include <ctype.h>
33 
34 int termwidth = 80;		/* default terminal width */
35 
36 int entries;			/* number of records */
37 int eval;			/* exit value */
38 int maxlength;			/* longest record */
39 char **list;			/* array of pointers to records */
40 char *separator = "\t ";	/* field separator for table option */
41 
42 main(argc, argv)
43 	int argc;
44 	char **argv;
45 {
46 	extern char *optarg;
47 	extern int errno, optind;
48 	struct winsize win;
49 	FILE *fp;
50 	int ch, tflag, xflag;
51 	char *p, *getenv();
52 
53 	if (ioctl(1, TIOCGWINSZ, &win) == -1 || !win.ws_col) {
54 		if (p = getenv("COLUMNS"))
55 			termwidth = atoi(p);
56 	} else
57 		termwidth = win.ws_col;
58 
59 	xflag = 0;
60 	while ((ch = getopt(argc, argv, "c:s:tx")) != EOF)
61 		switch(ch) {
62 		case 'c':
63 			termwidth = atoi(optarg);
64 			break;
65 		case 's':
66 			separator = optarg;
67 			break;
68 		case 't':
69 			tflag = 1;
70 			break;
71 		case 'x':
72 			xflag = 1;
73 			break;
74 		case '?':
75 		default:
76 			usage();
77 		}
78 	argc -= optind;
79 	argv += optind;
80 
81 	if (!*argv)
82 		input(stdin);
83 	else for (; *argv; ++argv)
84 		if (fp = fopen(*argv, "r")) {
85 			input(fp);
86 			(void)fclose(fp);
87 		} else {
88 			(void)fprintf(stderr, "column: %s: %s\n", *argv,
89 			    strerror(errno));
90 			eval = 1;
91 		}
92 
93 	if (!entries)
94 		exit(eval);
95 
96 	if (tflag)
97 		maketbl();
98 	else {
99 		if (maxlength >= termwidth)
100 			print();
101 		if (xflag)
102 			c_columnate();
103 		else
104 			r_columnate();
105 	}
106 	exit(eval);
107 }
108 
109 #define	TAB	8
110 c_columnate()
111 {
112 	register int chcnt, col, cnt, numcols;
113 	int endcol;
114 	char **lp;
115 
116 	maxlength = (maxlength + TAB) & ~(TAB - 1);
117 	numcols = termwidth / maxlength;
118 	endcol = maxlength;
119 	for (chcnt = col = 0, lp = list;; ++lp) {
120 		chcnt += printf("%s", *lp);
121 		if (!--entries)
122 			break;
123 		if (++col == numcols) {
124 			chcnt = col = 0;
125 			endcol = maxlength;
126 			putchar('\n');
127 		} else {
128 			while ((cnt = (chcnt + TAB & ~(TAB - 1))) <= endcol) {
129 				(void)putchar('\t');
130 				chcnt = cnt;
131 			}
132 			endcol += maxlength;
133 		}
134 	}
135 	if (chcnt)
136 		putchar('\n');
137 }
138 
139 r_columnate()
140 {
141 	register int base, chcnt, cnt, col;
142 	int endcol, numcols, numrows, row;
143 
144 	maxlength = (maxlength + TAB) & ~(TAB - 1);
145 	numcols = termwidth / maxlength;
146 	numrows = entries / numcols;
147 	if (entries % numcols)
148 		++numrows;
149 
150 	for (row = 0; row < numrows; ++row) {
151 		endcol = maxlength;
152 		for (base = row, chcnt = col = 0; col < numcols; ++col) {
153 			chcnt += printf("%s", list[base]);
154 			if ((base += numrows) >= entries)
155 				break;
156 			while ((cnt = (chcnt + TAB & ~(TAB - 1))) <= endcol) {
157 				(void)putchar('\t');
158 				chcnt = cnt;
159 			}
160 			endcol += maxlength;
161 		}
162 		putchar('\n');
163 	}
164 }
165 
166 print()
167 {
168 	register int cnt;
169 	register char **lp;
170 
171 	for (cnt = entries, lp = list; cnt--; ++lp)
172 		(void)printf("%s\n", *lp);
173 }
174 
175 typedef struct _tbl {
176 	char **list;
177 	int cols, *len;
178 } TBL;
179 #define	DEFCOLS	25
180 
181 maketbl()
182 {
183 	register TBL *t;
184 	register int coloff, cnt;
185 	register char *p, **lp;
186 	int *lens, maxcols;
187 	TBL *tbl;
188 	char **cols, *emalloc(), *realloc();
189 
190 	t = tbl = (TBL *)emalloc(entries * sizeof(TBL));
191 	cols = (char **)emalloc((maxcols = DEFCOLS) * sizeof(char *));
192 	lens = (int *)emalloc(maxcols * sizeof(int));
193 	for (cnt = 0, lp = list; cnt < entries; ++cnt, ++lp, ++t) {
194 		for (coloff = 0, p = *lp; cols[coloff] = strtok(p, separator);
195 		    p = NULL)
196 			if (++coloff == maxcols) {
197 				if (!(cols = (char **)realloc((char *)cols,
198 				    (u_int)maxcols + DEFCOLS * sizeof(char *))) ||
199 				    !(lens = (int *)realloc((char *)lens,
200 				    (u_int)maxcols + DEFCOLS * sizeof(int))))
201 					nomem();
202 				bzero((char *)lens + maxcols * sizeof(int),
203 				    DEFCOLS * sizeof(int));
204 				maxcols += DEFCOLS;
205 			}
206 		t->list = (char **)emalloc(coloff * sizeof(char *));
207 		t->len = (int *)emalloc(coloff * sizeof(int));
208 		for (t->cols = coloff; --coloff >= 0;) {
209 			t->list[coloff] = cols[coloff];
210 			t->len[coloff] = strlen(cols[coloff]);
211 			if (t->len[coloff] > lens[coloff])
212 				lens[coloff] = t->len[coloff];
213 		}
214 	}
215 	for (cnt = 0, t = tbl; cnt < entries; ++cnt, ++t) {
216 		for (coloff = 0; coloff < t->cols  - 1; ++coloff)
217 			(void)printf("%s%*s", t->list[coloff],
218 			    lens[coloff] - t->len[coloff] + 2, " ");
219 		(void)printf("%s\n", t->list[coloff]);
220 	}
221 }
222 
223 #define	DEFNUM		1000
224 #define	MAXLINELEN	(2048 + 1)
225 
226 input(fp)
227 	register FILE *fp;
228 {
229 	static int maxentry;
230 	register int len;
231 	register char *p;
232 	char buf[MAXLINELEN], *emalloc(), *realloc();
233 
234 	if (!list)
235 		list = (char **)emalloc((maxentry = DEFNUM) * sizeof(char *));
236 	while (fgets(buf, MAXLINELEN, fp)) {
237 		for (p = buf; *p && isspace(*p); ++p);
238 		if (!*p)
239 			continue;
240 		if (!(p = index(p, '\n'))) {
241 			(void)fprintf(stderr, "column: line too long.\n");
242 			eval = 1;
243 			continue;
244 		}
245 		*p = '\0';
246 		len = p - buf;
247 		if (maxlength < len)
248 			maxlength = len;
249 		if (entries == maxentry) {
250 			maxentry += DEFNUM;
251 			if (!(list =
252 			    (char **)realloc((char *)list,
253 			    (u_int)maxentry * sizeof(char *))))
254 				nomem();
255 		}
256 		list[entries++] = strdup(buf);
257 	}
258 }
259 
260 char *
261 emalloc(size)
262 	int size;
263 {
264 	char *p, *malloc();
265 
266 	/* NOSTRICT */
267 	if (!(p = malloc((u_int)size)))
268 		nomem();
269 	bzero(p, size);
270 	return(p);
271 }
272 
273 nomem()
274 {
275 	(void)fprintf(stderr, "column: out of memory.\n");
276 	exit(1);
277 }
278 
279 usage()
280 {
281 	(void)fprintf(stderr,
282 	    "usage: column [-tx] [-c columns] [file ...]\n");
283 	exit(1);
284 }
285