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