11590Srgrimes/* 21590Srgrimes * Copyright (c) 1989, 1993, 1994 31590Srgrimes * The Regents of the University of California. All rights reserved. 41590Srgrimes * 51590Srgrimes * Redistribution and use in source and binary forms, with or without 61590Srgrimes * modification, are permitted provided that the following conditions 71590Srgrimes * are met: 81590Srgrimes * 1. Redistributions of source code must retain the above copyright 91590Srgrimes * notice, this list of conditions and the following disclaimer. 101590Srgrimes * 2. Redistributions in binary form must reproduce the above copyright 111590Srgrimes * notice, this list of conditions and the following disclaimer in the 121590Srgrimes * documentation and/or other materials provided with the distribution. 131590Srgrimes * 4. Neither the name of the University nor the names of its contributors 141590Srgrimes * may be used to endorse or promote products derived from this software 151590Srgrimes * without specific prior written permission. 161590Srgrimes * 171590Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 181590Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 191590Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 201590Srgrimes * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 211590Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 221590Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 231590Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 241590Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 251590Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 261590Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 271590Srgrimes * SUCH DAMAGE. 281590Srgrimes */ 291590Srgrimes 301590Srgrimes#ifndef lint 3141568Sarchiestatic const char copyright[] = 321590Srgrimes"@(#) Copyright (c) 1989, 1993, 1994\n\ 331590Srgrimes The Regents of the University of California. All rights reserved.\n"; 3487246Smarkm#endif 351590Srgrimes 3687628Sdwmalone#if 0 371590Srgrimes#ifndef lint 3887628Sdwmalonestatic char sccsid[] = "@(#)column.c 8.4 (Berkeley) 5/4/95"; 3987246Smarkm#endif 4087628Sdwmalone#endif 411590Srgrimes 4287628Sdwmalone#include <sys/cdefs.h> 4387628Sdwmalone__FBSDID("$FreeBSD$"); 4487628Sdwmalone 451590Srgrimes#include <sys/types.h> 461590Srgrimes#include <sys/ioctl.h> 47140784Sjmallett#include <sys/param.h> 481590Srgrimes 491590Srgrimes#include <err.h> 501590Srgrimes#include <limits.h> 51132835Stjr#include <locale.h> 521590Srgrimes#include <stdio.h> 531590Srgrimes#include <stdlib.h> 541590Srgrimes#include <string.h> 5523690Speter#include <unistd.h> 56132835Stjr#include <wchar.h> 57132835Stjr#include <wctype.h> 581590Srgrimes 5975137Sdwmalone#define TAB 8 6075137Sdwmalone 61227159Sedstatic void c_columnate(void); 62227159Sedstatic void input(FILE *); 63227159Sedstatic void maketbl(void); 64227159Sedstatic void print(void); 65227159Sedstatic void r_columnate(void); 66227159Sedstatic void usage(void); 67227159Sedstatic int width(const wchar_t *); 681590Srgrimes 69227159Sedstatic int termwidth = 80; /* default terminal width */ 701590Srgrimes 71227159Sedstatic int entries; /* number of records */ 72227159Sedstatic int eval; /* exit value */ 73227159Sedstatic int maxlength; /* longest record */ 74227159Sedstatic wchar_t **list; /* array of pointers to records */ 75227159Sedstatic const wchar_t *separator = L"\t "; /* field separator for table option */ 761590Srgrimes 771590Srgrimesint 78100818Sdwmalonemain(int argc, char **argv) 791590Srgrimes{ 801590Srgrimes struct winsize win; 811590Srgrimes FILE *fp; 821590Srgrimes int ch, tflag, xflag; 831590Srgrimes char *p; 84132835Stjr const char *src; 85132835Stjr wchar_t *newsep; 86132835Stjr size_t seplen; 871590Srgrimes 88132835Stjr setlocale(LC_ALL, ""); 89132835Stjr 901590Srgrimes if (ioctl(1, TIOCGWINSZ, &win) == -1 || !win.ws_col) { 9141568Sarchie if ((p = getenv("COLUMNS"))) 921590Srgrimes termwidth = atoi(p); 931590Srgrimes } else 941590Srgrimes termwidth = win.ws_col; 951590Srgrimes 961590Srgrimes tflag = xflag = 0; 9724360Simp while ((ch = getopt(argc, argv, "c:s:tx")) != -1) 981590Srgrimes switch(ch) { 991590Srgrimes case 'c': 1001590Srgrimes termwidth = atoi(optarg); 1011590Srgrimes break; 1021590Srgrimes case 's': 103132835Stjr src = optarg; 104132835Stjr seplen = mbsrtowcs(NULL, &src, 0, NULL); 105132835Stjr if (seplen == (size_t)-1) 106132835Stjr err(1, "bad separator"); 107132835Stjr newsep = malloc((seplen + 1) * sizeof(wchar_t)); 108132835Stjr if (newsep == NULL) 109132835Stjr err(1, NULL); 110132835Stjr mbsrtowcs(newsep, &src, seplen + 1, NULL); 111132835Stjr separator = newsep; 1121590Srgrimes break; 1131590Srgrimes case 't': 1141590Srgrimes tflag = 1; 1151590Srgrimes break; 1161590Srgrimes case 'x': 1171590Srgrimes xflag = 1; 1181590Srgrimes break; 1191590Srgrimes case '?': 1201590Srgrimes default: 1211590Srgrimes usage(); 1221590Srgrimes } 1231590Srgrimes argc -= optind; 1241590Srgrimes argv += optind; 1251590Srgrimes 1261590Srgrimes if (!*argv) 1271590Srgrimes input(stdin); 1281590Srgrimes else for (; *argv; ++argv) 12941568Sarchie if ((fp = fopen(*argv, "r"))) { 1301590Srgrimes input(fp); 1311590Srgrimes (void)fclose(fp); 1321590Srgrimes } else { 1331590Srgrimes warn("%s", *argv); 1341590Srgrimes eval = 1; 1351590Srgrimes } 1361590Srgrimes 1371590Srgrimes if (!entries) 1381590Srgrimes exit(eval); 1391590Srgrimes 140165249Sru maxlength = roundup(maxlength + 1, TAB); 1411590Srgrimes if (tflag) 1421590Srgrimes maketbl(); 1431590Srgrimes else if (maxlength >= termwidth) 1441590Srgrimes print(); 1451590Srgrimes else if (xflag) 1461590Srgrimes c_columnate(); 1471590Srgrimes else 1481590Srgrimes r_columnate(); 1491590Srgrimes exit(eval); 1501590Srgrimes} 1511590Srgrimes 152227159Sedstatic void 153100818Sdwmalonec_columnate(void) 1541590Srgrimes{ 1551590Srgrimes int chcnt, col, cnt, endcol, numcols; 156132835Stjr wchar_t **lp; 1571590Srgrimes 1581590Srgrimes numcols = termwidth / maxlength; 1591590Srgrimes endcol = maxlength; 1601590Srgrimes for (chcnt = col = 0, lp = list;; ++lp) { 161132835Stjr wprintf(L"%ls", *lp); 162132835Stjr chcnt += width(*lp); 1631590Srgrimes if (!--entries) 1641590Srgrimes break; 1651590Srgrimes if (++col == numcols) { 1661590Srgrimes chcnt = col = 0; 1671590Srgrimes endcol = maxlength; 168132835Stjr putwchar('\n'); 1691590Srgrimes } else { 170165249Sru while ((cnt = roundup(chcnt + 1, TAB)) <= endcol) { 171132835Stjr (void)putwchar('\t'); 1721590Srgrimes chcnt = cnt; 1731590Srgrimes } 1741590Srgrimes endcol += maxlength; 1751590Srgrimes } 1761590Srgrimes } 1771590Srgrimes if (chcnt) 178132835Stjr putwchar('\n'); 1791590Srgrimes} 1801590Srgrimes 181227159Sedstatic void 182100818Sdwmaloner_columnate(void) 1831590Srgrimes{ 1841590Srgrimes int base, chcnt, cnt, col, endcol, numcols, numrows, row; 1851590Srgrimes 1861590Srgrimes numcols = termwidth / maxlength; 1871590Srgrimes numrows = entries / numcols; 1881590Srgrimes if (entries % numcols) 1891590Srgrimes ++numrows; 1901590Srgrimes 1911590Srgrimes for (row = 0; row < numrows; ++row) { 1921590Srgrimes endcol = maxlength; 1931590Srgrimes for (base = row, chcnt = col = 0; col < numcols; ++col) { 194132835Stjr wprintf(L"%ls", list[base]); 195132835Stjr chcnt += width(list[base]); 1961590Srgrimes if ((base += numrows) >= entries) 1971590Srgrimes break; 198165249Sru while ((cnt = roundup(chcnt + 1, TAB)) <= endcol) { 199132835Stjr (void)putwchar('\t'); 2001590Srgrimes chcnt = cnt; 2011590Srgrimes } 2021590Srgrimes endcol += maxlength; 2031590Srgrimes } 204132835Stjr putwchar('\n'); 2051590Srgrimes } 2061590Srgrimes} 2071590Srgrimes 208227159Sedstatic void 209100818Sdwmaloneprint(void) 2101590Srgrimes{ 2111590Srgrimes int cnt; 212132835Stjr wchar_t **lp; 2131590Srgrimes 2141590Srgrimes for (cnt = entries, lp = list; cnt--; ++lp) 215132835Stjr (void)wprintf(L"%ls\n", *lp); 2161590Srgrimes} 2171590Srgrimes 2181590Srgrimestypedef struct _tbl { 219132835Stjr wchar_t **list; 2201590Srgrimes int cols, *len; 2211590Srgrimes} TBL; 2221590Srgrimes#define DEFCOLS 25 2231590Srgrimes 224227159Sedstatic void 225100818Sdwmalonemaketbl(void) 2261590Srgrimes{ 2271590Srgrimes TBL *t; 2281590Srgrimes int coloff, cnt; 229132835Stjr wchar_t *p, **lp; 2301590Srgrimes int *lens, maxcols; 2311590Srgrimes TBL *tbl; 232132835Stjr wchar_t **cols; 233132835Stjr wchar_t *last; 2341590Srgrimes 23580292Sobrien if ((t = tbl = calloc(entries, sizeof(TBL))) == NULL) 23680292Sobrien err(1, (char *)NULL); 237132835Stjr if ((cols = calloc((maxcols = DEFCOLS), sizeof(*cols))) == NULL) 23880292Sobrien err(1, (char *)NULL); 23980292Sobrien if ((lens = calloc(maxcols, sizeof(int))) == NULL) 24080292Sobrien err(1, (char *)NULL); 2411590Srgrimes for (cnt = 0, lp = list; cnt < entries; ++cnt, ++lp, ++t) { 242132835Stjr for (coloff = 0, p = *lp; 243132835Stjr (cols[coloff] = wcstok(p, separator, &last)); 2441590Srgrimes p = NULL) 2451590Srgrimes if (++coloff == maxcols) { 246162453Siedowse if (!(cols = realloc(cols, ((u_int)maxcols + 247162453Siedowse DEFCOLS) * sizeof(char *))) || 2481590Srgrimes !(lens = realloc(lens, 249162453Siedowse ((u_int)maxcols + DEFCOLS) * sizeof(int)))) 2501590Srgrimes err(1, NULL); 2511590Srgrimes memset((char *)lens + maxcols * sizeof(int), 2521590Srgrimes 0, DEFCOLS * sizeof(int)); 2531590Srgrimes maxcols += DEFCOLS; 2541590Srgrimes } 255132835Stjr if ((t->list = calloc(coloff, sizeof(*t->list))) == NULL) 25680292Sobrien err(1, (char *)NULL); 25780292Sobrien if ((t->len = calloc(coloff, sizeof(int))) == NULL) 25880292Sobrien err(1, (char *)NULL); 2591590Srgrimes for (t->cols = coloff; --coloff >= 0;) { 2601590Srgrimes t->list[coloff] = cols[coloff]; 261132835Stjr t->len[coloff] = width(cols[coloff]); 2621590Srgrimes if (t->len[coloff] > lens[coloff]) 2631590Srgrimes lens[coloff] = t->len[coloff]; 2641590Srgrimes } 2651590Srgrimes } 2661590Srgrimes for (cnt = 0, t = tbl; cnt < entries; ++cnt, ++t) { 2671590Srgrimes for (coloff = 0; coloff < t->cols - 1; ++coloff) 268132835Stjr (void)wprintf(L"%ls%*ls", t->list[coloff], 269132835Stjr lens[coloff] - t->len[coloff] + 2, L" "); 270132835Stjr (void)wprintf(L"%ls\n", t->list[coloff]); 2711590Srgrimes } 2721590Srgrimes} 2731590Srgrimes 2741590Srgrimes#define DEFNUM 1000 2751590Srgrimes#define MAXLINELEN (LINE_MAX + 1) 2761590Srgrimes 277227159Sedstatic void 278100818Sdwmaloneinput(FILE *fp) 2791590Srgrimes{ 2801590Srgrimes static int maxentry; 2811590Srgrimes int len; 282132835Stjr wchar_t *p, buf[MAXLINELEN]; 2831590Srgrimes 2841590Srgrimes if (!list) 285132835Stjr if ((list = calloc((maxentry = DEFNUM), sizeof(*list))) == 28680292Sobrien NULL) 28780292Sobrien err(1, (char *)NULL); 288132835Stjr while (fgetws(buf, MAXLINELEN, fp)) { 289132835Stjr for (p = buf; *p && iswspace(*p); ++p); 2901590Srgrimes if (!*p) 2911590Srgrimes continue; 292132835Stjr if (!(p = wcschr(p, L'\n'))) { 2931590Srgrimes warnx("line too long"); 2941590Srgrimes eval = 1; 2951590Srgrimes continue; 2961590Srgrimes } 297132835Stjr *p = L'\0'; 298132835Stjr len = width(buf); 2991590Srgrimes if (maxlength < len) 3001590Srgrimes maxlength = len; 3011590Srgrimes if (entries == maxentry) { 3021590Srgrimes maxentry += DEFNUM; 3031590Srgrimes if (!(list = realloc(list, 304132835Stjr (u_int)maxentry * sizeof(*list)))) 3051590Srgrimes err(1, NULL); 3061590Srgrimes } 307132835Stjr list[entries] = malloc((wcslen(buf) + 1) * sizeof(wchar_t)); 308132835Stjr if (list[entries] == NULL) 309132835Stjr err(1, NULL); 310132835Stjr wcscpy(list[entries], buf); 311132835Stjr entries++; 3121590Srgrimes } 3131590Srgrimes} 3141590Srgrimes 315132835Stjr/* Like wcswidth(), but ignores non-printing characters. */ 316227159Sedstatic int 317132835Stjrwidth(const wchar_t *wcs) 318132835Stjr{ 319132835Stjr int w, cw; 320132835Stjr 321132835Stjr for (w = 0; *wcs != L'\0'; wcs++) 322132835Stjr if ((cw = wcwidth(*wcs)) > 0) 323132835Stjr w += cw; 324132835Stjr return (w); 325132835Stjr} 326132835Stjr 327227159Sedstatic void 328100818Sdwmaloneusage(void) 3291590Srgrimes{ 3301590Srgrimes 3311590Srgrimes (void)fprintf(stderr, 33227093Scharnier "usage: column [-tx] [-c columns] [-s sep] [file ...]\n"); 3331590Srgrimes exit(1); 3341590Srgrimes} 335