Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/usr.bin/column/column.c
34680 views
1
/*-
2
* SPDX-License-Identifier: BSD-3-Clause
3
*
4
* Copyright (c) 1989, 1993, 1994
5
* The Regents of the University of California. All rights reserved.
6
*
7
* Redistribution and use in source and binary forms, with or without
8
* modification, are permitted provided that the following conditions
9
* are met:
10
* 1. Redistributions of source code must retain the above copyright
11
* notice, this list of conditions and the following disclaimer.
12
* 2. Redistributions in binary form must reproduce the above copyright
13
* notice, this list of conditions and the following disclaimer in the
14
* documentation and/or other materials provided with the distribution.
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
17
* without specific prior written permission.
18
*
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
21
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
23
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
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
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
29
* SUCH DAMAGE.
30
*/
31
32
#include <sys/types.h>
33
#include <sys/ioctl.h>
34
#include <sys/param.h>
35
36
#include <err.h>
37
#include <limits.h>
38
#include <locale.h>
39
#include <stdio.h>
40
#include <stdlib.h>
41
#include <string.h>
42
#include <unistd.h>
43
#include <wchar.h>
44
#include <wctype.h>
45
46
#define TAB 8
47
48
static void c_columnate(void);
49
static void input(FILE *);
50
static void maketbl(void);
51
static void print(void);
52
static void r_columnate(void);
53
static void usage(void);
54
static int width(const wchar_t *);
55
56
static int termwidth = 80; /* default terminal width */
57
static int tblcols; /* number of table columns for -t */
58
59
static int entries; /* number of records */
60
static int eval; /* exit value */
61
static int maxlength; /* longest record */
62
static wchar_t **list; /* array of pointers to records */
63
static const wchar_t *separator = L"\t "; /* field separator for table option */
64
65
int
66
main(int argc, char **argv)
67
{
68
struct winsize win;
69
FILE *fp;
70
int ch, tflag, xflag;
71
char *p;
72
const char *errstr, *src;
73
wchar_t *newsep;
74
size_t seplen;
75
76
setlocale(LC_ALL, "");
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:l:s:tx")) != -1)
86
switch(ch) {
87
case 'c':
88
termwidth = strtonum(optarg, 0, INT_MAX, &errstr);
89
if (errstr != NULL)
90
errx(1, "invalid terminal width \"%s\": %s",
91
optarg, errstr);
92
break;
93
case 'l':
94
tblcols = strtonum(optarg, 0, INT_MAX, &errstr);
95
if (errstr != NULL)
96
errx(1, "invalid max width \"%s\": %s",
97
optarg, errstr);
98
break;
99
case 's':
100
src = optarg;
101
seplen = mbsrtowcs(NULL, &src, 0, NULL);
102
if (seplen == (size_t)-1)
103
err(1, "bad separator");
104
newsep = calloc(seplen + 1, sizeof(wchar_t));
105
if (newsep == NULL)
106
err(1, NULL);
107
mbsrtowcs(newsep, &src, seplen + 1, NULL);
108
separator = newsep;
109
break;
110
case 't':
111
tflag = 1;
112
break;
113
case 'x':
114
xflag = 1;
115
break;
116
case '?':
117
default:
118
usage();
119
}
120
argc -= optind;
121
argv += optind;
122
123
if (tblcols && !tflag)
124
errx(1, "the -l flag cannot be used without the -t flag");
125
126
if (!*argv)
127
input(stdin);
128
else for (; *argv; ++argv)
129
if ((fp = fopen(*argv, "r"))) {
130
input(fp);
131
(void)fclose(fp);
132
} else {
133
warn("%s", *argv);
134
eval = 1;
135
}
136
137
if (!entries)
138
exit(eval);
139
140
maxlength = roundup(maxlength + 1, TAB);
141
if (tflag)
142
maketbl();
143
else if (maxlength >= termwidth)
144
print();
145
else if (xflag)
146
c_columnate();
147
else
148
r_columnate();
149
exit(eval);
150
}
151
152
static void
153
c_columnate(void)
154
{
155
int chcnt, col, cnt, endcol, numcols;
156
wchar_t **lp;
157
158
numcols = termwidth / maxlength;
159
endcol = maxlength;
160
for (chcnt = col = 0, lp = list;; ++lp) {
161
wprintf(L"%ls", *lp);
162
chcnt += width(*lp);
163
if (!--entries)
164
break;
165
if (++col == numcols) {
166
chcnt = col = 0;
167
endcol = maxlength;
168
putwchar('\n');
169
} else {
170
while ((cnt = roundup(chcnt + 1, TAB)) <= endcol) {
171
(void)putwchar('\t');
172
chcnt = cnt;
173
}
174
endcol += maxlength;
175
}
176
}
177
if (chcnt)
178
putwchar('\n');
179
}
180
181
static void
182
r_columnate(void)
183
{
184
int base, chcnt, cnt, col, endcol, numcols, numrows, row;
185
186
numcols = termwidth / maxlength;
187
numrows = entries / numcols;
188
if (entries % numcols)
189
++numrows;
190
191
for (row = 0; row < numrows; ++row) {
192
endcol = maxlength;
193
for (base = row, chcnt = col = 0; col < numcols; ++col) {
194
wprintf(L"%ls", list[base]);
195
chcnt += width(list[base]);
196
if ((base += numrows) >= entries)
197
break;
198
while ((cnt = roundup(chcnt + 1, TAB)) <= endcol) {
199
(void)putwchar('\t');
200
chcnt = cnt;
201
}
202
endcol += maxlength;
203
}
204
putwchar('\n');
205
}
206
}
207
208
static void
209
print(void)
210
{
211
int cnt;
212
wchar_t **lp;
213
214
for (cnt = entries, lp = list; cnt--; ++lp)
215
(void)wprintf(L"%ls\n", *lp);
216
}
217
218
typedef struct _tbl {
219
wchar_t **list;
220
int cols, *len;
221
} TBL;
222
#define DEFCOLS 25
223
224
static void
225
maketbl(void)
226
{
227
TBL *t;
228
int coloff, cnt;
229
wchar_t *p, **lp;
230
int *lens, maxcols;
231
TBL *tbl;
232
wchar_t **cols;
233
wchar_t *s;
234
235
if ((t = tbl = calloc(entries, sizeof(TBL))) == NULL)
236
err(1, NULL);
237
if ((cols = calloc((maxcols = DEFCOLS), sizeof(*cols))) == NULL)
238
err(1, NULL);
239
if ((lens = calloc(maxcols, sizeof(int))) == NULL)
240
err(1, NULL);
241
for (cnt = 0, lp = list; cnt < entries; ++cnt, ++lp, ++t) {
242
for (p = *lp; wcschr(separator, *p); ++p)
243
/* nothing */ ;
244
for (coloff = 0; *p;) {
245
cols[coloff] = p;
246
247
if (++coloff == maxcols) {
248
if (!(cols = realloc(cols, ((u_int)maxcols +
249
DEFCOLS) * sizeof(wchar_t *))) ||
250
!(lens = realloc(lens,
251
((u_int)maxcols + DEFCOLS) * sizeof(int))))
252
err(1, NULL);
253
memset((char *)lens + maxcols * sizeof(int),
254
0, DEFCOLS * sizeof(int));
255
maxcols += DEFCOLS;
256
}
257
258
if ((!tblcols || coloff < tblcols) &&
259
(s = wcspbrk(p, separator))) {
260
*s++ = L'\0';
261
while (*s && wcschr(separator, *s))
262
++s;
263
p = s;
264
} else
265
break;
266
}
267
if ((t->list = calloc(coloff, sizeof(*t->list))) == NULL)
268
err(1, NULL);
269
if ((t->len = calloc(coloff, sizeof(int))) == NULL)
270
err(1, NULL);
271
for (t->cols = coloff; --coloff >= 0;) {
272
t->list[coloff] = cols[coloff];
273
t->len[coloff] = width(cols[coloff]);
274
if (t->len[coloff] > lens[coloff])
275
lens[coloff] = t->len[coloff];
276
}
277
}
278
for (cnt = 0, t = tbl; cnt < entries; ++cnt, ++t) {
279
for (coloff = 0; coloff < t->cols - 1; ++coloff)
280
(void)wprintf(L"%ls%*ls", t->list[coloff],
281
lens[coloff] - t->len[coloff] + 2, L" ");
282
(void)wprintf(L"%ls\n", t->list[coloff]);
283
free(t->list);
284
free(t->len);
285
}
286
free(lens);
287
free(cols);
288
free(tbl);
289
}
290
291
#define DEFNUM 1000
292
#define MAXLINELEN (LINE_MAX + 1)
293
294
static void
295
input(FILE *fp)
296
{
297
static int maxentry;
298
int len;
299
wchar_t *p, buf[MAXLINELEN];
300
301
if (!list)
302
if ((list = calloc((maxentry = DEFNUM), sizeof(*list))) ==
303
NULL)
304
err(1, NULL);
305
while (fgetws(buf, MAXLINELEN, fp)) {
306
for (p = buf; *p && iswspace(*p); ++p);
307
if (!*p)
308
continue;
309
if (!(p = wcschr(p, L'\n'))) {
310
warnx("line too long");
311
eval = 1;
312
continue;
313
}
314
*p = L'\0';
315
len = width(buf);
316
if (maxlength < len)
317
maxlength = len;
318
if (entries == maxentry) {
319
maxentry += DEFNUM;
320
if (!(list = realloc(list,
321
(u_int)maxentry * sizeof(*list))))
322
err(1, NULL);
323
}
324
list[entries] = malloc((wcslen(buf) + 1) * sizeof(wchar_t));
325
if (list[entries] == NULL)
326
err(1, NULL);
327
wcscpy(list[entries], buf);
328
entries++;
329
}
330
}
331
332
/* Like wcswidth(), but ignores non-printing characters. */
333
static int
334
width(const wchar_t *wcs)
335
{
336
int w, cw;
337
338
for (w = 0; *wcs != L'\0'; wcs++)
339
if ((cw = wcwidth(*wcs)) > 0)
340
w += cw;
341
return (w);
342
}
343
344
static void
345
usage(void)
346
{
347
(void)fprintf(stderr,
348
"usage: column [-tx] [-c columns] [-l tblcols]"
349
" [-s sep] [file ...]\n");
350
exit(1);
351
}
352
353