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