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