xref: /original-bsd/usr.bin/column/column.c (revision 62cd422e)
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.7 (Berkeley) 02/24/91";
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 if (maxlength >= termwidth)
89 		print();
90 	else if (xflag)
91 		c_columnate();
92 	else
93 		r_columnate();
94 	exit(eval);
95 }
96 
97 #define	TAB	8
98 c_columnate()
99 {
100 	register int chcnt, col, cnt, numcols;
101 	int endcol;
102 	char **lp;
103 
104 	maxlength = (maxlength + TAB) & ~(TAB - 1);
105 	numcols = termwidth / maxlength;
106 	endcol = maxlength;
107 	for (chcnt = col = 0, lp = list;; ++lp) {
108 		chcnt += printf("%s", *lp);
109 		if (!--entries)
110 			break;
111 		if (++col == numcols) {
112 			chcnt = col = 0;
113 			endcol = maxlength;
114 			putchar('\n');
115 		} else {
116 			while ((cnt = (chcnt + TAB & ~(TAB - 1))) <= endcol) {
117 				(void)putchar('\t');
118 				chcnt = cnt;
119 			}
120 			endcol += maxlength;
121 		}
122 	}
123 	if (chcnt)
124 		putchar('\n');
125 }
126 
127 r_columnate()
128 {
129 	register int base, chcnt, cnt, col;
130 	int endcol, numcols, numrows, row;
131 
132 	maxlength = (maxlength + TAB) & ~(TAB - 1);
133 	numcols = termwidth / maxlength;
134 	numrows = entries / numcols;
135 	if (entries % numcols)
136 		++numrows;
137 
138 	for (row = 0; row < numrows; ++row) {
139 		endcol = maxlength;
140 		for (base = row, chcnt = col = 0; col < numcols; ++col) {
141 			chcnt += printf("%s", list[base]);
142 			if ((base += numrows) >= entries)
143 				break;
144 			while ((cnt = (chcnt + TAB & ~(TAB - 1))) <= endcol) {
145 				(void)putchar('\t');
146 				chcnt = cnt;
147 			}
148 			endcol += maxlength;
149 		}
150 		putchar('\n');
151 	}
152 }
153 
154 print()
155 {
156 	register int cnt;
157 	register char **lp;
158 
159 	for (cnt = entries, lp = list; cnt--; ++lp)
160 		(void)printf("%s\n", *lp);
161 }
162 
163 typedef struct _tbl {
164 	char **list;
165 	int cols, *len;
166 } TBL;
167 #define	DEFCOLS	25
168 
169 maketbl()
170 {
171 	register TBL *t;
172 	register int coloff, cnt;
173 	register char *p, **lp;
174 	int *lens, maxcols;
175 	TBL *tbl;
176 	char **cols, *emalloc(), *realloc();
177 
178 	t = tbl = (TBL *)emalloc(entries * sizeof(TBL));
179 	cols = (char **)emalloc((maxcols = DEFCOLS) * sizeof(char *));
180 	lens = (int *)emalloc(maxcols * sizeof(int));
181 	for (cnt = 0, lp = list; cnt < entries; ++cnt, ++lp, ++t) {
182 		for (coloff = 0, p = *lp; cols[coloff] = strtok(p, separator);
183 		    p = NULL)
184 			if (++coloff == maxcols) {
185 				if (!(cols = (char **)realloc((char *)cols,
186 				    (u_int)maxcols + DEFCOLS * sizeof(char *))) ||
187 				    !(lens = (int *)realloc((char *)lens,
188 				    (u_int)maxcols + DEFCOLS * sizeof(int))))
189 					nomem();
190 				bzero((char *)lens + maxcols * sizeof(int),
191 				    DEFCOLS * sizeof(int));
192 				maxcols += DEFCOLS;
193 			}
194 		t->list = (char **)emalloc(coloff * sizeof(char *));
195 		t->len = (int *)emalloc(coloff * sizeof(int));
196 		for (t->cols = coloff; --coloff >= 0;) {
197 			t->list[coloff] = cols[coloff];
198 			t->len[coloff] = strlen(cols[coloff]);
199 			if (t->len[coloff] > lens[coloff])
200 				lens[coloff] = t->len[coloff];
201 		}
202 	}
203 	for (cnt = 0, t = tbl; cnt < entries; ++cnt, ++t) {
204 		for (coloff = 0; coloff < t->cols  - 1; ++coloff)
205 			(void)printf("%s%*s", t->list[coloff],
206 			    lens[coloff] - t->len[coloff] + 2, " ");
207 		(void)printf("%s\n", t->list[coloff]);
208 	}
209 }
210 
211 #define	DEFNUM		1000
212 #define	MAXLINELEN	(2048 + 1)
213 
214 input(fp)
215 	register FILE *fp;
216 {
217 	static int maxentry;
218 	register int len;
219 	register char *p;
220 	char buf[MAXLINELEN], *emalloc(), *realloc();
221 
222 	if (!list)
223 		list = (char **)emalloc((maxentry = DEFNUM) * sizeof(char *));
224 	while (fgets(buf, MAXLINELEN, fp)) {
225 		for (p = buf; *p && isspace(*p); ++p);
226 		if (!*p)
227 			continue;
228 		if (!(p = index(p, '\n'))) {
229 			(void)fprintf(stderr, "column: line too long.\n");
230 			eval = 1;
231 			continue;
232 		}
233 		*p = '\0';
234 		len = p - buf;
235 		if (maxlength < len)
236 			maxlength = len;
237 		if (entries == maxentry) {
238 			maxentry += DEFNUM;
239 			if (!(list =
240 			    (char **)realloc((char *)list,
241 			    (u_int)maxentry * sizeof(char *))))
242 				nomem();
243 		}
244 		list[entries++] = strdup(buf);
245 	}
246 }
247 
248 char *
249 emalloc(size)
250 	int size;
251 {
252 	char *p, *malloc();
253 
254 	/* NOSTRICT */
255 	if (!(p = malloc((u_int)size)))
256 		nomem();
257 	bzero(p, size);
258 	return(p);
259 }
260 
261 nomem()
262 {
263 	(void)fprintf(stderr, "column: out of memory.\n");
264 	exit(1);
265 }
266 
267 usage()
268 {
269 	(void)fprintf(stderr,
270 	    "usage: column [-tx] [-c columns] [file ...]\n");
271 	exit(1);
272 }
273