column.c revision 1.9 1 /* $NetBSD: column.c,v 1.9 1998/11/06 22:59:12 christos Exp $ */
2
3 /*
4 * Copyright (c) 1989, 1993, 1994
5 * The Regents of the University of California. All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * 3. All advertising materials mentioning features or use of this software
16 * must display the following acknowledgement:
17 * This product includes software developed by the University of
18 * California, Berkeley and its contributors.
19 * 4. Neither the name of the University nor the names of its contributors
20 * may be used to endorse or promote products derived from this software
21 * without specific prior written permission.
22 *
23 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33 * SUCH DAMAGE.
34 */
35
36 #include <sys/cdefs.h>
37 #ifndef lint
38 __COPYRIGHT("@(#) Copyright (c) 1989, 1993, 1994\n\
39 The Regents of the University of California. All rights reserved.\n");
40 #endif /* not lint */
41
42 #ifndef lint
43 #if 0
44 static char sccsid[] = "@(#)column.c 8.4 (Berkeley) 5/4/95";
45 #endif
46 __RCSID("$NetBSD: column.c,v 1.9 1998/11/06 22:59:12 christos Exp $");
47 #endif /* not lint */
48
49 #include <sys/types.h>
50 #include <sys/ioctl.h>
51
52 #include <ctype.h>
53 #include <err.h>
54 #include <termios.h>
55 #include <limits.h>
56 #include <stdio.h>
57 #include <stdlib.h>
58 #include <string.h>
59 #include <unistd.h>
60
61 void c_columnate __P((void));
62 void *emalloc __P((int));
63 void input __P((FILE *));
64 void maketbl __P((void));
65 int main __P((int, char **));
66 void print __P((void));
67 void r_columnate __P((void));
68 void usage __P((void));
69
70 int termwidth = 80; /* default terminal width */
71
72 int entries; /* number of records */
73 int eval; /* exit value */
74 int maxlength; /* longest record */
75 char **list; /* array of pointers to records */
76 char *separator = "\t "; /* field separator for table option */
77
78 int
79 main(argc, argv)
80 int argc;
81 char **argv;
82 {
83 struct winsize win;
84 FILE *fp;
85 int ch, tflag, xflag;
86 const char *p;
87
88 if (ioctl(1, TIOCGWINSZ, &win) == -1 || !win.ws_col) {
89 if ((p = getenv("COLUMNS")) != NULL)
90 termwidth = atoi(p);
91 } else
92 termwidth = win.ws_col;
93
94 tflag = xflag = 0;
95 while ((ch = getopt(argc, argv, "c:s:tx")) != -1)
96 switch(ch) {
97 case 'c':
98 termwidth = atoi(optarg);
99 break;
100 case 's':
101 separator = optarg;
102 break;
103 case 't':
104 tflag = 1;
105 break;
106 case 'x':
107 xflag = 1;
108 break;
109 case '?':
110 default:
111 usage();
112 }
113 argc -= optind;
114 argv += optind;
115
116 if (!*argv)
117 input(stdin);
118 else for (; *argv; ++argv)
119 if ((fp = fopen(*argv, "r")) != NULL) {
120 input(fp);
121 (void)fclose(fp);
122 } else {
123 warn("%s", *argv);
124 eval = 1;
125 }
126
127 if (!entries)
128 exit(eval);
129
130 if (tflag)
131 maketbl();
132 else if (maxlength >= termwidth)
133 print();
134 else if (xflag)
135 c_columnate();
136 else
137 r_columnate();
138 exit(eval);
139 }
140
141 #define TAB 8
142 void
143 c_columnate()
144 {
145 int chcnt, col, cnt, endcol, numcols;
146 char **lp;
147
148 maxlength = (maxlength + TAB) & ~(TAB - 1);
149 numcols = termwidth / maxlength;
150 endcol = maxlength;
151 for (chcnt = col = 0, lp = list;; ++lp) {
152 chcnt += printf("%s", *lp);
153 if (!--entries)
154 break;
155 if (++col == numcols) {
156 chcnt = col = 0;
157 endcol = maxlength;
158 putchar('\n');
159 } else {
160 while ((cnt = ((chcnt + TAB) & ~(TAB - 1))) <= endcol) {
161 (void)putchar('\t');
162 chcnt = cnt;
163 }
164 endcol += maxlength;
165 }
166 }
167 if (chcnt)
168 putchar('\n');
169 }
170
171 void
172 r_columnate()
173 {
174 int base, chcnt, cnt, col, endcol, numcols, numrows, row;
175
176 maxlength = (maxlength + TAB) & ~(TAB - 1);
177 numcols = termwidth / maxlength;
178 numrows = entries / numcols;
179 if (entries % numcols)
180 ++numrows;
181
182 for (row = 0; row < numrows; ++row) {
183 endcol = maxlength;
184 for (base = row, chcnt = col = 0; col < numcols; ++col) {
185 chcnt += printf("%s", list[base]);
186 if ((base += numrows) >= entries)
187 break;
188 while ((cnt = ((chcnt + TAB) & ~(TAB - 1))) <= endcol) {
189 (void)putchar('\t');
190 chcnt = cnt;
191 }
192 endcol += maxlength;
193 }
194 putchar('\n');
195 }
196 }
197
198 void
199 print()
200 {
201 int cnt;
202 char **lp;
203
204 for (cnt = entries, lp = list; cnt--; ++lp)
205 (void)printf("%s\n", *lp);
206 }
207
208 typedef struct _tbl {
209 char **list;
210 int cols, *len;
211 } TBL;
212 #define DEFCOLS 25
213
214 void
215 maketbl()
216 {
217 TBL *t;
218 int coloff, cnt;
219 char *p, **lp;
220 int *lens, maxcols;
221 TBL *tbl;
222 char **cols;
223
224 t = tbl = emalloc(entries * sizeof(TBL));
225 cols = emalloc((maxcols = DEFCOLS) * sizeof(char *));
226 lens = emalloc(maxcols * sizeof(int));
227 for (cnt = 0, lp = list; cnt < entries; ++cnt, ++lp, ++t) {
228 for (coloff = 0, p = *lp;
229 (cols[coloff] = strtok(p, separator)) != NULL;
230 p = NULL)
231 if (++coloff == maxcols) {
232 if (!(cols = realloc(cols, (u_int)maxcols +
233 DEFCOLS * sizeof(char *))) ||
234 !(lens = realloc(lens,
235 (u_int)maxcols + DEFCOLS * sizeof(int))))
236 err(1, "realloc");
237 memset((char *)lens + maxcols * sizeof(int),
238 0, DEFCOLS * sizeof(int));
239 maxcols += DEFCOLS;
240 }
241 t->list = emalloc(coloff * sizeof(char *));
242 t->len = emalloc(coloff * sizeof(int));
243 for (t->cols = coloff; --coloff >= 0;) {
244 t->list[coloff] = cols[coloff];
245 t->len[coloff] = strlen(cols[coloff]);
246 if (t->len[coloff] > lens[coloff])
247 lens[coloff] = t->len[coloff];
248 }
249 }
250 for (cnt = 0, t = tbl; cnt < entries; ++cnt, ++t) {
251 for (coloff = 0; coloff < t->cols - 1; ++coloff)
252 (void)printf("%s%*s", t->list[coloff],
253 lens[coloff] - t->len[coloff] + 2, " ");
254 (void)printf("%s\n", t->list[coloff]);
255 }
256 }
257
258 #define DEFNUM 1000
259 #define MAXLINELEN (LINE_MAX + 1)
260
261 void
262 input(fp)
263 FILE *fp;
264 {
265 static int maxentry;
266 int len;
267 char *p, buf[MAXLINELEN];
268
269 if (!list)
270 list = emalloc((maxentry = DEFNUM) * sizeof(char *));
271 while (fgets(buf, MAXLINELEN, fp)) {
272 for (p = buf; *p && isspace((unsigned char)*p); ++p);
273 if (!*p)
274 continue;
275 if (!(p = strchr(p, '\n'))) {
276 warnx("line too long");
277 eval = 1;
278 continue;
279 }
280 *p = '\0';
281 len = p - buf;
282 if (maxlength < len)
283 maxlength = len;
284 if (entries == maxentry) {
285 maxentry += DEFNUM;
286 if (!(list = realloc(list,
287 (u_int)maxentry * sizeof(char *))))
288 err(1, "realloc");
289 }
290 list[entries++] = strdup(buf);
291 }
292 }
293
294 void *
295 emalloc(size)
296 int size;
297 {
298 char *p;
299
300 if (!(p = malloc(size)))
301 err(1, "malloc");
302 memset(p, 0, size);
303 return (p);
304 }
305
306 void
307 usage()
308 {
309
310 (void)fprintf(stderr,
311 "usage: column [-tx] [-c columns] [-s sep] [file ...]\n");
312 exit(1);
313 }
314