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