18a16b7a1SPedro F. Giffuni /*-
28a16b7a1SPedro F. Giffuni * SPDX-License-Identifier: BSD-3-Clause
38a16b7a1SPedro F. Giffuni *
40da30e9aSPeter Wemm * Copyright (c) 1989, 1993, 1994
50da30e9aSPeter Wemm * The Regents of the University of California. All rights reserved.
60da30e9aSPeter Wemm *
70da30e9aSPeter Wemm * Redistribution and use in source and binary forms, with or without
80da30e9aSPeter Wemm * modification, are permitted provided that the following conditions
90da30e9aSPeter Wemm * are met:
100da30e9aSPeter Wemm * 1. Redistributions of source code must retain the above copyright
110da30e9aSPeter Wemm * notice, this list of conditions and the following disclaimer.
120da30e9aSPeter Wemm * 2. Redistributions in binary form must reproduce the above copyright
130da30e9aSPeter Wemm * notice, this list of conditions and the following disclaimer in the
140da30e9aSPeter Wemm * documentation and/or other materials provided with the distribution.
15fbbd9655SWarner Losh * 3. Neither the name of the University nor the names of its contributors
160da30e9aSPeter Wemm * may be used to endorse or promote products derived from this software
170da30e9aSPeter Wemm * without specific prior written permission.
180da30e9aSPeter Wemm *
190da30e9aSPeter Wemm * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
200da30e9aSPeter Wemm * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
210da30e9aSPeter Wemm * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
220da30e9aSPeter Wemm * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
230da30e9aSPeter Wemm * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
240da30e9aSPeter Wemm * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
250da30e9aSPeter Wemm * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
260da30e9aSPeter Wemm * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
270da30e9aSPeter Wemm * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
280da30e9aSPeter Wemm * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
290da30e9aSPeter Wemm * SUCH DAMAGE.
300da30e9aSPeter Wemm */
310da30e9aSPeter Wemm
320da30e9aSPeter Wemm #include <sys/types.h>
330da30e9aSPeter Wemm #include <sys/ioctl.h>
34e2816c14SJuli Mallett #include <sys/param.h>
350da30e9aSPeter Wemm
360da30e9aSPeter Wemm #include <err.h>
370da30e9aSPeter Wemm #include <limits.h>
385480f120STim J. Robbins #include <locale.h>
390da30e9aSPeter Wemm #include <stdio.h>
400da30e9aSPeter Wemm #include <stdlib.h>
410da30e9aSPeter Wemm #include <string.h>
420da30e9aSPeter Wemm #include <unistd.h>
435480f120STim J. Robbins #include <wchar.h>
445480f120STim J. Robbins #include <wctype.h>
450da30e9aSPeter Wemm
46983e044aSDavid Malone #define TAB 8
47983e044aSDavid Malone
48316b87cbSEd Schouten static void c_columnate(void);
49316b87cbSEd Schouten static void input(FILE *);
50316b87cbSEd Schouten static void maketbl(void);
51316b87cbSEd Schouten static void print(void);
52316b87cbSEd Schouten static void r_columnate(void);
53316b87cbSEd Schouten static void usage(void);
54316b87cbSEd Schouten static int width(const wchar_t *);
550da30e9aSPeter Wemm
56316b87cbSEd Schouten static int termwidth = 80; /* default terminal width */
570da30e9aSPeter Wemm
58316b87cbSEd Schouten static int entries; /* number of records */
59316b87cbSEd Schouten static int eval; /* exit value */
60316b87cbSEd Schouten static int maxlength; /* longest record */
61316b87cbSEd Schouten static wchar_t **list; /* array of pointers to records */
62316b87cbSEd Schouten static const wchar_t *separator = L"\t "; /* field separator for table option */
630da30e9aSPeter Wemm
640da30e9aSPeter Wemm int
main(int argc,char ** argv)65823ab23fSDavid Malone main(int argc, char **argv)
660da30e9aSPeter Wemm {
670da30e9aSPeter Wemm struct winsize win;
680da30e9aSPeter Wemm FILE *fp;
690da30e9aSPeter Wemm int ch, tflag, xflag;
700da30e9aSPeter Wemm char *p;
715480f120STim J. Robbins const char *src;
725480f120STim J. Robbins wchar_t *newsep;
735480f120STim J. Robbins size_t seplen;
745480f120STim J. Robbins
755480f120STim J. Robbins setlocale(LC_ALL, "");
760da30e9aSPeter Wemm
770da30e9aSPeter Wemm if (ioctl(1, TIOCGWINSZ, &win) == -1 || !win.ws_col) {
78fa146c53SArchie Cobbs if ((p = getenv("COLUMNS")))
790da30e9aSPeter Wemm termwidth = atoi(p);
800da30e9aSPeter Wemm } else
810da30e9aSPeter Wemm termwidth = win.ws_col;
820da30e9aSPeter Wemm
830da30e9aSPeter Wemm tflag = xflag = 0;
841c8af878SWarner Losh while ((ch = getopt(argc, argv, "c:s:tx")) != -1)
850da30e9aSPeter Wemm switch(ch) {
860da30e9aSPeter Wemm case 'c':
870da30e9aSPeter Wemm termwidth = atoi(optarg);
880da30e9aSPeter Wemm break;
890da30e9aSPeter Wemm case 's':
905480f120STim J. Robbins src = optarg;
915480f120STim J. Robbins seplen = mbsrtowcs(NULL, &src, 0, NULL);
925480f120STim J. Robbins if (seplen == (size_t)-1)
935480f120STim J. Robbins err(1, "bad separator");
945480f120STim J. Robbins newsep = malloc((seplen + 1) * sizeof(wchar_t));
955480f120STim J. Robbins if (newsep == NULL)
965480f120STim J. Robbins err(1, NULL);
975480f120STim J. Robbins mbsrtowcs(newsep, &src, seplen + 1, NULL);
985480f120STim J. Robbins separator = newsep;
990da30e9aSPeter Wemm break;
1000da30e9aSPeter Wemm case 't':
1010da30e9aSPeter Wemm tflag = 1;
1020da30e9aSPeter Wemm break;
1030da30e9aSPeter Wemm case 'x':
1040da30e9aSPeter Wemm xflag = 1;
1050da30e9aSPeter Wemm break;
1060da30e9aSPeter Wemm case '?':
1070da30e9aSPeter Wemm default:
1080da30e9aSPeter Wemm usage();
1090da30e9aSPeter Wemm }
1100da30e9aSPeter Wemm argc -= optind;
1110da30e9aSPeter Wemm argv += optind;
1120da30e9aSPeter Wemm
1130da30e9aSPeter Wemm if (!*argv)
1140da30e9aSPeter Wemm input(stdin);
1150da30e9aSPeter Wemm else for (; *argv; ++argv)
116fa146c53SArchie Cobbs if ((fp = fopen(*argv, "r"))) {
1170da30e9aSPeter Wemm input(fp);
1180da30e9aSPeter Wemm (void)fclose(fp);
1190da30e9aSPeter Wemm } else {
1200da30e9aSPeter Wemm warn("%s", *argv);
1210da30e9aSPeter Wemm eval = 1;
1220da30e9aSPeter Wemm }
1230da30e9aSPeter Wemm
1240da30e9aSPeter Wemm if (!entries)
1250da30e9aSPeter Wemm exit(eval);
1260da30e9aSPeter Wemm
127090782f5SRuslan Ermilov maxlength = roundup(maxlength + 1, TAB);
1280da30e9aSPeter Wemm if (tflag)
1290da30e9aSPeter Wemm maketbl();
1300da30e9aSPeter Wemm else if (maxlength >= termwidth)
1310da30e9aSPeter Wemm print();
1320da30e9aSPeter Wemm else if (xflag)
1330da30e9aSPeter Wemm c_columnate();
1340da30e9aSPeter Wemm else
1350da30e9aSPeter Wemm r_columnate();
1360da30e9aSPeter Wemm exit(eval);
1370da30e9aSPeter Wemm }
1380da30e9aSPeter Wemm
139316b87cbSEd Schouten static void
c_columnate(void)140823ab23fSDavid Malone c_columnate(void)
1410da30e9aSPeter Wemm {
1420da30e9aSPeter Wemm int chcnt, col, cnt, endcol, numcols;
1435480f120STim J. Robbins wchar_t **lp;
1440da30e9aSPeter Wemm
1450da30e9aSPeter Wemm numcols = termwidth / maxlength;
1460da30e9aSPeter Wemm endcol = maxlength;
1470da30e9aSPeter Wemm for (chcnt = col = 0, lp = list;; ++lp) {
1485480f120STim J. Robbins wprintf(L"%ls", *lp);
1495480f120STim J. Robbins chcnt += width(*lp);
1500da30e9aSPeter Wemm if (!--entries)
1510da30e9aSPeter Wemm break;
1520da30e9aSPeter Wemm if (++col == numcols) {
1530da30e9aSPeter Wemm chcnt = col = 0;
1540da30e9aSPeter Wemm endcol = maxlength;
1555480f120STim J. Robbins putwchar('\n');
1560da30e9aSPeter Wemm } else {
157090782f5SRuslan Ermilov while ((cnt = roundup(chcnt + 1, TAB)) <= endcol) {
1585480f120STim J. Robbins (void)putwchar('\t');
1590da30e9aSPeter Wemm chcnt = cnt;
1600da30e9aSPeter Wemm }
1610da30e9aSPeter Wemm endcol += maxlength;
1620da30e9aSPeter Wemm }
1630da30e9aSPeter Wemm }
1640da30e9aSPeter Wemm if (chcnt)
1655480f120STim J. Robbins putwchar('\n');
1660da30e9aSPeter Wemm }
1670da30e9aSPeter Wemm
168316b87cbSEd Schouten static void
r_columnate(void)169823ab23fSDavid Malone r_columnate(void)
1700da30e9aSPeter Wemm {
1710da30e9aSPeter Wemm int base, chcnt, cnt, col, endcol, numcols, numrows, row;
1720da30e9aSPeter Wemm
1730da30e9aSPeter Wemm numcols = termwidth / maxlength;
1740da30e9aSPeter Wemm numrows = entries / numcols;
1750da30e9aSPeter Wemm if (entries % numcols)
1760da30e9aSPeter Wemm ++numrows;
1770da30e9aSPeter Wemm
1780da30e9aSPeter Wemm for (row = 0; row < numrows; ++row) {
1790da30e9aSPeter Wemm endcol = maxlength;
1800da30e9aSPeter Wemm for (base = row, chcnt = col = 0; col < numcols; ++col) {
1815480f120STim J. Robbins wprintf(L"%ls", list[base]);
1825480f120STim J. Robbins chcnt += width(list[base]);
1830da30e9aSPeter Wemm if ((base += numrows) >= entries)
1840da30e9aSPeter Wemm break;
185090782f5SRuslan Ermilov while ((cnt = roundup(chcnt + 1, TAB)) <= endcol) {
1865480f120STim J. Robbins (void)putwchar('\t');
1870da30e9aSPeter Wemm chcnt = cnt;
1880da30e9aSPeter Wemm }
1890da30e9aSPeter Wemm endcol += maxlength;
1900da30e9aSPeter Wemm }
1915480f120STim J. Robbins putwchar('\n');
1920da30e9aSPeter Wemm }
1930da30e9aSPeter Wemm }
1940da30e9aSPeter Wemm
195316b87cbSEd Schouten static void
print(void)196823ab23fSDavid Malone print(void)
1970da30e9aSPeter Wemm {
1980da30e9aSPeter Wemm int cnt;
1995480f120STim J. Robbins wchar_t **lp;
2000da30e9aSPeter Wemm
2010da30e9aSPeter Wemm for (cnt = entries, lp = list; cnt--; ++lp)
2025480f120STim J. Robbins (void)wprintf(L"%ls\n", *lp);
2030da30e9aSPeter Wemm }
2040da30e9aSPeter Wemm
2050da30e9aSPeter Wemm typedef struct _tbl {
2065480f120STim J. Robbins wchar_t **list;
2070da30e9aSPeter Wemm int cols, *len;
2080da30e9aSPeter Wemm } TBL;
2090da30e9aSPeter Wemm #define DEFCOLS 25
2100da30e9aSPeter Wemm
211316b87cbSEd Schouten static void
maketbl(void)212823ab23fSDavid Malone maketbl(void)
2130da30e9aSPeter Wemm {
2140da30e9aSPeter Wemm TBL *t;
2150da30e9aSPeter Wemm int coloff, cnt;
2165480f120STim J. Robbins wchar_t *p, **lp;
2170da30e9aSPeter Wemm int *lens, maxcols;
2180da30e9aSPeter Wemm TBL *tbl;
2195480f120STim J. Robbins wchar_t **cols;
2205480f120STim J. Robbins wchar_t *last;
2210da30e9aSPeter Wemm
222d54a8ce7SDavid E. O'Brien if ((t = tbl = calloc(entries, sizeof(TBL))) == NULL)
22318463c77SKevin Lo err(1, NULL);
2245480f120STim J. Robbins if ((cols = calloc((maxcols = DEFCOLS), sizeof(*cols))) == NULL)
22518463c77SKevin Lo err(1, NULL);
226d54a8ce7SDavid E. O'Brien if ((lens = calloc(maxcols, sizeof(int))) == NULL)
22718463c77SKevin Lo err(1, NULL);
2280da30e9aSPeter Wemm for (cnt = 0, lp = list; cnt < entries; ++cnt, ++lp, ++t) {
2295480f120STim J. Robbins for (coloff = 0, p = *lp;
2305480f120STim J. Robbins (cols[coloff] = wcstok(p, separator, &last));
2310da30e9aSPeter Wemm p = NULL)
2320da30e9aSPeter Wemm if (++coloff == maxcols) {
2336508a1ecSIan Dowse if (!(cols = realloc(cols, ((u_int)maxcols +
234c9e1c304SUlrich Spörlein DEFCOLS) * sizeof(wchar_t *))) ||
2350da30e9aSPeter Wemm !(lens = realloc(lens,
2366508a1ecSIan Dowse ((u_int)maxcols + DEFCOLS) * sizeof(int))))
2370da30e9aSPeter Wemm err(1, NULL);
2380da30e9aSPeter Wemm memset((char *)lens + maxcols * sizeof(int),
2390da30e9aSPeter Wemm 0, DEFCOLS * sizeof(int));
2400da30e9aSPeter Wemm maxcols += DEFCOLS;
2410da30e9aSPeter Wemm }
2425480f120STim J. Robbins if ((t->list = calloc(coloff, sizeof(*t->list))) == NULL)
24318463c77SKevin Lo err(1, NULL);
244d54a8ce7SDavid E. O'Brien if ((t->len = calloc(coloff, sizeof(int))) == NULL)
24518463c77SKevin Lo err(1, NULL);
2460da30e9aSPeter Wemm for (t->cols = coloff; --coloff >= 0;) {
2470da30e9aSPeter Wemm t->list[coloff] = cols[coloff];
2485480f120STim J. Robbins t->len[coloff] = width(cols[coloff]);
2490da30e9aSPeter Wemm if (t->len[coloff] > lens[coloff])
2500da30e9aSPeter Wemm lens[coloff] = t->len[coloff];
2510da30e9aSPeter Wemm }
2520da30e9aSPeter Wemm }
2530da30e9aSPeter Wemm for (cnt = 0, t = tbl; cnt < entries; ++cnt, ++t) {
2540da30e9aSPeter Wemm for (coloff = 0; coloff < t->cols - 1; ++coloff)
2555480f120STim J. Robbins (void)wprintf(L"%ls%*ls", t->list[coloff],
2565480f120STim J. Robbins lens[coloff] - t->len[coloff] + 2, L" ");
2575480f120STim J. Robbins (void)wprintf(L"%ls\n", t->list[coloff]);
258*290ce7d2SWarner Losh free(t->list);
259*290ce7d2SWarner Losh free(t->len);
2600da30e9aSPeter Wemm }
261*290ce7d2SWarner Losh free(lens);
262*290ce7d2SWarner Losh free(cols);
263*290ce7d2SWarner Losh free(tbl);
2640da30e9aSPeter Wemm }
2650da30e9aSPeter Wemm
2660da30e9aSPeter Wemm #define DEFNUM 1000
2670da30e9aSPeter Wemm #define MAXLINELEN (LINE_MAX + 1)
2680da30e9aSPeter Wemm
269316b87cbSEd Schouten static void
input(FILE * fp)270823ab23fSDavid Malone input(FILE *fp)
2710da30e9aSPeter Wemm {
2720da30e9aSPeter Wemm static int maxentry;
2730da30e9aSPeter Wemm int len;
2745480f120STim J. Robbins wchar_t *p, buf[MAXLINELEN];
2750da30e9aSPeter Wemm
2760da30e9aSPeter Wemm if (!list)
2775480f120STim J. Robbins if ((list = calloc((maxentry = DEFNUM), sizeof(*list))) ==
278d54a8ce7SDavid E. O'Brien NULL)
27918463c77SKevin Lo err(1, NULL);
2805480f120STim J. Robbins while (fgetws(buf, MAXLINELEN, fp)) {
2815480f120STim J. Robbins for (p = buf; *p && iswspace(*p); ++p);
2820da30e9aSPeter Wemm if (!*p)
2830da30e9aSPeter Wemm continue;
2845480f120STim J. Robbins if (!(p = wcschr(p, L'\n'))) {
2850da30e9aSPeter Wemm warnx("line too long");
2860da30e9aSPeter Wemm eval = 1;
2870da30e9aSPeter Wemm continue;
2880da30e9aSPeter Wemm }
2895480f120STim J. Robbins *p = L'\0';
2905480f120STim J. Robbins len = width(buf);
2910da30e9aSPeter Wemm if (maxlength < len)
2920da30e9aSPeter Wemm maxlength = len;
2930da30e9aSPeter Wemm if (entries == maxentry) {
2940da30e9aSPeter Wemm maxentry += DEFNUM;
2950da30e9aSPeter Wemm if (!(list = realloc(list,
2965480f120STim J. Robbins (u_int)maxentry * sizeof(*list))))
2970da30e9aSPeter Wemm err(1, NULL);
2980da30e9aSPeter Wemm }
2995480f120STim J. Robbins list[entries] = malloc((wcslen(buf) + 1) * sizeof(wchar_t));
3005480f120STim J. Robbins if (list[entries] == NULL)
3015480f120STim J. Robbins err(1, NULL);
3025480f120STim J. Robbins wcscpy(list[entries], buf);
3035480f120STim J. Robbins entries++;
3040da30e9aSPeter Wemm }
3050da30e9aSPeter Wemm }
3060da30e9aSPeter Wemm
3075480f120STim J. Robbins /* Like wcswidth(), but ignores non-printing characters. */
308316b87cbSEd Schouten static int
width(const wchar_t * wcs)3095480f120STim J. Robbins width(const wchar_t *wcs)
3105480f120STim J. Robbins {
3115480f120STim J. Robbins int w, cw;
3125480f120STim J. Robbins
3135480f120STim J. Robbins for (w = 0; *wcs != L'\0'; wcs++)
3145480f120STim J. Robbins if ((cw = wcwidth(*wcs)) > 0)
3155480f120STim J. Robbins w += cw;
3165480f120STim J. Robbins return (w);
3175480f120STim J. Robbins }
3185480f120STim J. Robbins
319316b87cbSEd Schouten static void
usage(void)320823ab23fSDavid Malone usage(void)
3210da30e9aSPeter Wemm {
3220da30e9aSPeter Wemm
3230da30e9aSPeter Wemm (void)fprintf(stderr,
3243b5ec5aaSPhilippe Charnier "usage: column [-tx] [-c columns] [-s sep] [file ...]\n");
3250da30e9aSPeter Wemm exit(1);
3260da30e9aSPeter Wemm }
327