Raise WARNS to 6.
[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  * $DragonFly: src/usr.bin/column/column.c,v 1.5 2005/01/16 04:28:00 cpressey 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 const 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 }