Add the DragonFly cvs id and perform general cleanups on cvs/rcs/sccs ids. Most
[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.2 2003/06/17 04:29:25 dillon 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 __P((void));
54 void  input __P((FILE *));
55 void  maketbl __P((void));
56 void  print __P((void));
57 void  r_columnate __P((void));
58 void  usage __P((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(argc, argv)
70         int argc;
71         char **argv;
72 {
73         struct winsize win;
74         FILE *fp;
75         int ch, tflag, xflag;
76         char *p;
77
78         if (ioctl(1, TIOCGWINSZ, &win) == -1 || !win.ws_col) {
79                 if ((p = getenv("COLUMNS")))
80                         termwidth = atoi(p);
81         } else
82                 termwidth = win.ws_col;
83
84         tflag = xflag = 0;
85         while ((ch = getopt(argc, argv, "c:s:tx")) != -1)
86                 switch(ch) {
87                 case 'c':
88                         termwidth = atoi(optarg);
89                         break;
90                 case 's':
91                         separator = optarg;
92                         break;
93                 case 't':
94                         tflag = 1;
95                         break;
96                 case 'x':
97                         xflag = 1;
98                         break;
99                 case '?':
100                 default:
101                         usage();
102                 }
103         argc -= optind;
104         argv += optind;
105
106         if (!*argv)
107                 input(stdin);
108         else for (; *argv; ++argv)
109                 if ((fp = fopen(*argv, "r"))) {
110                         input(fp);
111                         (void)fclose(fp);
112                 } else {
113                         warn("%s", *argv);
114                         eval = 1;
115                 }
116
117         if (!entries)
118                 exit(eval);
119
120         maxlength = (maxlength + TAB) & ~(TAB - 1);
121         if (tflag)
122                 maketbl();
123         else if (maxlength >= termwidth)
124                 print();
125         else if (xflag)
126                 c_columnate();
127         else
128                 r_columnate();
129         exit(eval);
130 }
131
132 void
133 c_columnate()
134 {
135         int chcnt, col, cnt, endcol, numcols;
136         char **lp;
137
138         numcols = termwidth / maxlength;
139         endcol = maxlength;
140         for (chcnt = col = 0, lp = list;; ++lp) {
141                 chcnt += printf("%s", *lp);
142                 if (!--entries)
143                         break;
144                 if (++col == numcols) {
145                         chcnt = col = 0;
146                         endcol = maxlength;
147                         putchar('\n');
148                 } else {
149                         while ((cnt = ((chcnt + TAB) & ~(TAB - 1))) <= endcol) {
150                                 (void)putchar('\t');
151                                 chcnt = cnt;
152                         }
153                         endcol += maxlength;
154                 }
155         }
156         if (chcnt)
157                 putchar('\n');
158 }
159
160 void
161 r_columnate()
162 {
163         int base, chcnt, cnt, col, endcol, numcols, numrows, row;
164
165         numcols = termwidth / maxlength;
166         numrows = entries / numcols;
167         if (entries % numcols)
168                 ++numrows;
169
170         for (row = 0; row < numrows; ++row) {
171                 endcol = maxlength;
172                 for (base = row, chcnt = col = 0; col < numcols; ++col) {
173                         chcnt += printf("%s", list[base]);
174                         if ((base += numrows) >= entries)
175                                 break;
176                         while ((cnt = ((chcnt + TAB) & ~(TAB - 1))) <= endcol) {
177                                 (void)putchar('\t');
178                                 chcnt = cnt;
179                         }
180                         endcol += maxlength;
181                 }
182                 putchar('\n');
183         }
184 }
185
186 void
187 print()
188 {
189         int cnt;
190         char **lp;
191
192         for (cnt = entries, lp = list; cnt--; ++lp)
193                 (void)printf("%s\n", *lp);
194 }
195
196 typedef struct _tbl {
197         char **list;
198         int cols, *len;
199 } TBL;
200 #define DEFCOLS 25
201
202 void
203 maketbl()
204 {
205         TBL *t;
206         int coloff, cnt;
207         char *p, **lp;
208         int *lens, maxcols;
209         TBL *tbl;
210         char **cols;
211
212         if ((t = tbl = calloc(entries, sizeof(TBL))) == NULL)
213                 err(1, (char *)NULL);
214         if ((cols = calloc((maxcols = DEFCOLS), sizeof(char *))) == NULL)
215                 err(1, (char *)NULL);
216         if ((lens = calloc(maxcols, sizeof(int))) == NULL)
217                 err(1, (char *)NULL);
218         for (cnt = 0, lp = list; cnt < entries; ++cnt, ++lp, ++t) {
219                 for (coloff = 0, p = *lp; (cols[coloff] = strtok(p, separator));
220                     p = NULL)
221                         if (++coloff == maxcols) {
222                                 if (!(cols = realloc(cols, (u_int)maxcols +
223                                     DEFCOLS * sizeof(char *))) ||
224                                     !(lens = realloc(lens,
225                                     (u_int)maxcols + DEFCOLS * sizeof(int))))
226                                         err(1, NULL);
227                                 memset((char *)lens + maxcols * sizeof(int),
228                                     0, DEFCOLS * sizeof(int));
229                                 maxcols += DEFCOLS;
230                         }
231                 if ((t->list = calloc(coloff, sizeof(char *))) == NULL)
232                         err(1, (char *)NULL);
233                 if ((t->len = calloc(coloff, sizeof(int))) == NULL)
234                         err(1, (char *)NULL);
235                 for (t->cols = coloff; --coloff >= 0;) {
236                         t->list[coloff] = cols[coloff];
237                         t->len[coloff] = strlen(cols[coloff]);
238                         if (t->len[coloff] > lens[coloff])
239                                 lens[coloff] = t->len[coloff];
240                 }
241         }
242         for (cnt = 0, t = tbl; cnt < entries; ++cnt, ++t) {
243                 for (coloff = 0; coloff < t->cols  - 1; ++coloff)
244                         (void)printf("%s%*s", t->list[coloff],
245                             lens[coloff] - t->len[coloff] + 2, " ");
246                 (void)printf("%s\n", t->list[coloff]);
247         }
248 }
249
250 #define DEFNUM          1000
251 #define MAXLINELEN      (LINE_MAX + 1)
252
253 void
254 input(fp)
255         FILE *fp;
256 {
257         static int maxentry;
258         int len;
259         char *p, buf[MAXLINELEN];
260
261         if (!list)
262                 if ((list = calloc((maxentry = DEFNUM), sizeof(char *))) ==
263                     NULL)
264                         err(1, (char *)NULL);
265         while (fgets(buf, MAXLINELEN, fp)) {
266                 for (p = buf; *p && isspace(*p); ++p);
267                 if (!*p)
268                         continue;
269                 if (!(p = strchr(p, '\n'))) {
270                         warnx("line too long");
271                         eval = 1;
272                         continue;
273                 }
274                 *p = '\0';
275                 len = p - buf;
276                 if (maxlength < len)
277                         maxlength = len;
278                 if (entries == maxentry) {
279                         maxentry += DEFNUM;
280                         if (!(list = realloc(list,
281                             (u_int)maxentry * sizeof(char *))))
282                                 err(1, NULL);
283                 }
284                 list[entries++] = strdup(buf);
285         }
286 }
287
288 void
289 usage()
290 {
291
292         (void)fprintf(stderr,
293             "usage: column [-tx] [-c columns] [-s sep] [file ...]\n");
294         exit(1);
295 }