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