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