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