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 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 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 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 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 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 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 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 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