Commit | Line | Data |
---|---|---|
8761f947 FF |
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 | ||
34 | #ifndef lint | |
35 | static const char copyright[] = | |
36 | "@(#) Copyright (c) 1989, 1993, 1994\n\ | |
37 | The Regents of the University of California. All rights reserved.\n"; | |
38 | #endif | |
39 | ||
40 | #if 0 | |
41 | #ifndef lint | |
42 | static char sccsid[] = "@(#)column.c 8.4 (Berkeley) 5/4/95"; | |
43 | #endif | |
44 | #endif | |
45 | ||
46 | #include <sys/cdefs.h> | |
47 | __FBSDID("$FreeBSD$"); | |
48 | ||
49 | #include <sys/types.h> | |
50 | #include <sys/ioctl.h> | |
51 | #include <sys/param.h> | |
52 | ||
53 | #include <err.h> | |
54 | #include <limits.h> | |
55 | #include <locale.h> | |
56 | #include <stdio.h> | |
57 | #include <stdlib.h> | |
58 | #include <string.h> | |
59 | #include <unistd.h> | |
60 | #include <wchar.h> | |
61 | #include <wctype.h> | |
62 | ||
63 | #define TAB 8 | |
64 | ||
65 | void c_columnate(void); | |
66 | void input(FILE *); | |
67 | void maketbl(void); | |
68 | void print(void); | |
69 | void r_columnate(void); | |
70 | void usage(void); | |
71 | int width(const wchar_t *); | |
72 | ||
73 | int termwidth = 80; /* default terminal width */ | |
74 | ||
75 | int entries; /* number of records */ | |
76 | int eval; /* exit value */ | |
77 | int maxlength; /* longest record */ | |
78 | wchar_t **list; /* array of pointers to records */ | |
79 | const wchar_t *separator = L"\t "; /* field separator for table option */ | |
80 | ||
81 | int | |
82 | main(int argc, char **argv) | |
83 | { | |
84 | struct winsize win; | |
85 | FILE *fp; | |
86 | int ch, tflag, xflag; | |
87 | char *p; | |
88 | const char *src; | |
89 | wchar_t *newsep; | |
90 | size_t seplen; | |
91 | ||
92 | setlocale(LC_ALL, ""); | |
93 | ||
94 | if (ioctl(1, TIOCGWINSZ, &win) == -1 || !win.ws_col) { | |
95 | if ((p = getenv("COLUMNS"))) | |
96 | termwidth = atoi(p); | |
97 | } else | |
98 | termwidth = win.ws_col; | |
99 | ||
100 | tflag = xflag = 0; | |
101 | while ((ch = getopt(argc, argv, "c:s:tx")) != -1) | |
102 | switch(ch) { | |
103 | case 'c': | |
104 | termwidth = atoi(optarg); | |
105 | break; | |
106 | case 's': | |
107 | src = optarg; | |
108 | seplen = mbsrtowcs(NULL, &src, 0, NULL); | |
109 | if (seplen == (size_t)-1) | |
110 | err(1, "bad separator"); | |
111 | newsep = malloc((seplen + 1) * sizeof(wchar_t)); | |
112 | if (newsep == NULL) | |
113 | err(1, NULL); | |
114 | mbsrtowcs(newsep, &src, seplen + 1, NULL); | |
115 | separator = newsep; | |
116 | break; | |
117 | case 't': | |
118 | tflag = 1; | |
119 | break; | |
120 | case 'x': | |
121 | xflag = 1; | |
122 | break; | |
123 | case '?': | |
124 | default: | |
125 | usage(); | |
126 | } | |
127 | argc -= optind; | |
128 | argv += optind; | |
129 | ||
130 | if (!*argv) | |
131 | input(stdin); | |
132 | else for (; *argv; ++argv) | |
133 | if ((fp = fopen(*argv, "r"))) { | |
134 | input(fp); | |
135 | (void)fclose(fp); | |
136 | } else { | |
137 | warn("%s", *argv); | |
138 | eval = 1; | |
139 | } | |
140 | ||
141 | if (!entries) | |
142 | exit(eval); | |
143 | ||
144 | maxlength = roundup(maxlength + 1, TAB); | |
145 | if (tflag) | |
146 | maketbl(); | |
147 | else if (maxlength >= termwidth) | |
148 | print(); | |
149 | else if (xflag) | |
150 | c_columnate(); | |
151 | else | |
152 | r_columnate(); | |
153 | exit(eval); | |
154 | } | |
155 | ||
156 | void | |
157 | c_columnate(void) | |
158 | { | |
159 | int chcnt, col, cnt, endcol, numcols; | |
160 | wchar_t **lp; | |
161 | ||
162 | numcols = termwidth / maxlength; | |
163 | endcol = maxlength; | |
164 | for (chcnt = col = 0, lp = list;; ++lp) { | |
165 | wprintf(L"%ls", *lp); | |
166 | chcnt += width(*lp); | |
167 | if (!--entries) | |
168 | break; | |
169 | if (++col == numcols) { | |
170 | chcnt = col = 0; | |
171 | endcol = maxlength; | |
172 | putwchar('\n'); | |
173 | } else { | |
174 | while ((cnt = roundup(chcnt + 1, TAB)) <= endcol) { | |
175 | (void)putwchar('\t'); | |
176 | chcnt = cnt; | |
177 | } | |
178 | endcol += maxlength; | |
179 | } | |
180 | } | |
181 | if (chcnt) | |
182 | putwchar('\n'); | |
183 | } | |
184 | ||
185 | void | |
186 | r_columnate(void) | |
187 | { | |
188 | int base, chcnt, cnt, col, endcol, numcols, numrows, row; | |
189 | ||
190 | numcols = termwidth / maxlength; | |
191 | numrows = entries / numcols; | |
192 | if (entries % numcols) | |
193 | ++numrows; | |
194 | ||
195 | for (row = 0; row < numrows; ++row) { | |
196 | endcol = maxlength; | |
197 | for (base = row, chcnt = col = 0; col < numcols; ++col) { | |
198 | wprintf(L"%ls", list[base]); | |
199 | chcnt += width(list[base]); | |
200 | if ((base += numrows) >= entries) | |
201 | break; | |
202 | while ((cnt = roundup(chcnt + 1, TAB)) <= endcol) { | |
203 | (void)putwchar('\t'); | |
204 | chcnt = cnt; | |
205 | } | |
206 | endcol += maxlength; | |
207 | } | |
208 | putwchar('\n'); | |
209 | } | |
210 | } | |
211 | ||
212 | void | |
213 | print(void) | |
214 | { | |
215 | int cnt; | |
216 | wchar_t **lp; | |
217 | ||
218 | for (cnt = entries, lp = list; cnt--; ++lp) | |
219 | (void)wprintf(L"%ls\n", *lp); | |
220 | } | |
221 | ||
222 | typedef struct _tbl { | |
223 | wchar_t **list; | |
224 | int cols, *len; | |
225 | } TBL; | |
226 | #define DEFCOLS 25 | |
227 | ||
228 | void | |
229 | maketbl(void) | |
230 | { | |
231 | TBL *t; | |
232 | int coloff, cnt; | |
233 | wchar_t *p, **lp; | |
234 | int *lens, maxcols; | |
235 | TBL *tbl; | |
236 | wchar_t **cols; | |
237 | wchar_t *last; | |
238 | ||
239 | if ((t = tbl = calloc(entries, sizeof(TBL))) == NULL) | |
240 | err(1, (char *)NULL); | |
241 | if ((cols = calloc((maxcols = DEFCOLS), sizeof(*cols))) == NULL) | |
242 | err(1, (char *)NULL); | |
243 | if ((lens = calloc(maxcols, sizeof(int))) == NULL) | |
244 | err(1, (char *)NULL); | |
245 | for (cnt = 0, lp = list; cnt < entries; ++cnt, ++lp, ++t) { | |
246 | for (coloff = 0, p = *lp; | |
247 | (cols[coloff] = wcstok(p, separator, &last)); | |
248 | p = NULL) | |
249 | if (++coloff == maxcols) { | |
250 | if (!(cols = realloc(cols, ((u_int)maxcols + | |
251 | DEFCOLS) * sizeof(char *))) || | |
252 | !(lens = realloc(lens, | |
253 | ((u_int)maxcols + DEFCOLS) * sizeof(int)))) | |
254 | err(1, NULL); | |
255 | memset((char *)lens + maxcols * sizeof(int), | |
256 | 0, DEFCOLS * sizeof(int)); | |
257 | maxcols += DEFCOLS; | |
258 | } | |
259 | if ((t->list = calloc(coloff, sizeof(*t->list))) == NULL) | |
260 | err(1, (char *)NULL); | |
261 | if ((t->len = calloc(coloff, sizeof(int))) == NULL) | |
262 | err(1, (char *)NULL); | |
263 | for (t->cols = coloff; --coloff >= 0;) { | |
264 | t->list[coloff] = cols[coloff]; | |
265 | t->len[coloff] = width(cols[coloff]); | |
266 | if (t->len[coloff] > lens[coloff]) | |
267 | lens[coloff] = t->len[coloff]; | |
268 | } | |
269 | } | |
270 | for (cnt = 0, t = tbl; cnt < entries; ++cnt, ++t) { | |
271 | for (coloff = 0; coloff < t->cols - 1; ++coloff) | |
272 | (void)wprintf(L"%ls%*ls", t->list[coloff], | |
273 | lens[coloff] - t->len[coloff] + 2, L" "); | |
274 | (void)wprintf(L"%ls\n", t->list[coloff]); | |
275 | } | |
276 | } | |
277 | ||
278 | #define DEFNUM 1000 | |
279 | #define MAXLINELEN (LINE_MAX + 1) | |
280 | ||
281 | void | |
282 | input(FILE *fp) | |
283 | { | |
284 | static int maxentry; | |
285 | int len; | |
286 | wchar_t *p, buf[MAXLINELEN]; | |
287 | ||
288 | if (!list) | |
289 | if ((list = calloc((maxentry = DEFNUM), sizeof(*list))) == | |
290 | NULL) | |
291 | err(1, (char *)NULL); | |
292 | while (fgetws(buf, MAXLINELEN, fp)) { | |
293 | for (p = buf; *p && iswspace(*p); ++p); | |
294 | if (!*p) | |
295 | continue; | |
296 | if (!(p = wcschr(p, L'\n'))) { | |
297 | warnx("line too long"); | |
298 | eval = 1; | |
299 | continue; | |
300 | } | |
301 | *p = L'\0'; | |
302 | len = width(buf); | |
303 | if (maxlength < len) | |
304 | maxlength = len; | |
305 | if (entries == maxentry) { | |
306 | maxentry += DEFNUM; | |
307 | if (!(list = realloc(list, | |
308 | (u_int)maxentry * sizeof(*list)))) | |
309 | err(1, NULL); | |
310 | } | |
311 | list[entries] = malloc((wcslen(buf) + 1) * sizeof(wchar_t)); | |
312 | if (list[entries] == NULL) | |
313 | err(1, NULL); | |
314 | wcscpy(list[entries], buf); | |
315 | entries++; | |
316 | } | |
317 | } | |
318 | ||
319 | /* Like wcswidth(), but ignores non-printing characters. */ | |
320 | int | |
321 | width(const wchar_t *wcs) | |
322 | { | |
323 | int w, cw; | |
324 | ||
325 | for (w = 0; *wcs != L'\0'; wcs++) | |
326 | if ((cw = wcwidth(*wcs)) > 0) | |
327 | w += cw; | |
328 | return (w); | |
329 | } | |
330 | ||
331 | void | |
332 | usage(void) | |
333 | { | |
334 | ||
335 | (void)fprintf(stderr, | |
336 | "usage: column [-tx] [-c columns] [-s sep] [file ...]\n"); | |
337 | exit(1); | |
338 | } |