xref: /freebsd/usr.bin/column/column.c (revision fa146c53357ea20afd3661d8093ea1db44198d5f)
10da30e9aSPeter Wemm /*
20da30e9aSPeter Wemm  * Copyright (c) 1989, 1993, 1994
30da30e9aSPeter Wemm  *	The Regents of the University of California.  All rights reserved.
40da30e9aSPeter Wemm  *
50da30e9aSPeter Wemm  * Redistribution and use in source and binary forms, with or without
60da30e9aSPeter Wemm  * modification, are permitted provided that the following conditions
70da30e9aSPeter Wemm  * are met:
80da30e9aSPeter Wemm  * 1. Redistributions of source code must retain the above copyright
90da30e9aSPeter Wemm  *    notice, this list of conditions and the following disclaimer.
100da30e9aSPeter Wemm  * 2. Redistributions in binary form must reproduce the above copyright
110da30e9aSPeter Wemm  *    notice, this list of conditions and the following disclaimer in the
120da30e9aSPeter Wemm  *    documentation and/or other materials provided with the distribution.
130da30e9aSPeter Wemm  * 3. All advertising materials mentioning features or use of this software
140da30e9aSPeter Wemm  *    must display the following acknowledgement:
150da30e9aSPeter Wemm  *	This product includes software developed by the University of
160da30e9aSPeter Wemm  *	California, Berkeley and its contributors.
170da30e9aSPeter Wemm  * 4. Neither the name of the University nor the names of its contributors
180da30e9aSPeter Wemm  *    may be used to endorse or promote products derived from this software
190da30e9aSPeter Wemm  *    without specific prior written permission.
200da30e9aSPeter Wemm  *
210da30e9aSPeter Wemm  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
220da30e9aSPeter Wemm  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
230da30e9aSPeter Wemm  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
240da30e9aSPeter Wemm  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
250da30e9aSPeter Wemm  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
260da30e9aSPeter Wemm  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
270da30e9aSPeter Wemm  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
280da30e9aSPeter Wemm  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
290da30e9aSPeter Wemm  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
300da30e9aSPeter Wemm  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
310da30e9aSPeter Wemm  * SUCH DAMAGE.
320da30e9aSPeter Wemm  */
330da30e9aSPeter Wemm 
340da30e9aSPeter Wemm #ifndef lint
35fa146c53SArchie Cobbs static const char copyright[] =
360da30e9aSPeter Wemm "@(#) Copyright (c) 1989, 1993, 1994\n\
370da30e9aSPeter Wemm 	The Regents of the University of California.  All rights reserved.\n";
380da30e9aSPeter Wemm #endif /* not lint */
390da30e9aSPeter Wemm 
400da30e9aSPeter Wemm #ifndef lint
41fa146c53SArchie Cobbs static const char sccsid[] = "@(#)column.c	8.4 (Berkeley) 5/4/95";
420da30e9aSPeter Wemm #endif /* not lint */
430da30e9aSPeter Wemm 
440da30e9aSPeter Wemm #include <sys/types.h>
450da30e9aSPeter Wemm #include <sys/ioctl.h>
460da30e9aSPeter Wemm 
470da30e9aSPeter Wemm #include <ctype.h>
480da30e9aSPeter Wemm #include <err.h>
490da30e9aSPeter Wemm #include <limits.h>
500da30e9aSPeter Wemm #include <stdio.h>
510da30e9aSPeter Wemm #include <stdlib.h>
520da30e9aSPeter Wemm #include <string.h>
530da30e9aSPeter Wemm #include <unistd.h>
540da30e9aSPeter Wemm 
550da30e9aSPeter Wemm void  c_columnate __P((void));
560da30e9aSPeter Wemm void *emalloc __P((int));
570da30e9aSPeter Wemm void  input __P((FILE *));
580da30e9aSPeter Wemm void  maketbl __P((void));
590da30e9aSPeter Wemm void  print __P((void));
600da30e9aSPeter Wemm void  r_columnate __P((void));
610da30e9aSPeter Wemm void  usage __P((void));
620da30e9aSPeter Wemm 
630da30e9aSPeter Wemm int termwidth = 80;		/* default terminal width */
640da30e9aSPeter Wemm 
650da30e9aSPeter Wemm int entries;			/* number of records */
660da30e9aSPeter Wemm int eval;			/* exit value */
670da30e9aSPeter Wemm int maxlength;			/* longest record */
680da30e9aSPeter Wemm char **list;			/* array of pointers to records */
690da30e9aSPeter Wemm char *separator = "\t ";	/* field separator for table option */
700da30e9aSPeter Wemm 
710da30e9aSPeter Wemm int
720da30e9aSPeter Wemm main(argc, argv)
730da30e9aSPeter Wemm 	int argc;
740da30e9aSPeter Wemm 	char **argv;
750da30e9aSPeter Wemm {
760da30e9aSPeter Wemm 	struct winsize win;
770da30e9aSPeter Wemm 	FILE *fp;
780da30e9aSPeter Wemm 	int ch, tflag, xflag;
790da30e9aSPeter Wemm 	char *p;
800da30e9aSPeter Wemm 
810da30e9aSPeter Wemm 	if (ioctl(1, TIOCGWINSZ, &win) == -1 || !win.ws_col) {
82fa146c53SArchie Cobbs 		if ((p = getenv("COLUMNS")))
830da30e9aSPeter Wemm 			termwidth = atoi(p);
840da30e9aSPeter Wemm 	} else
850da30e9aSPeter Wemm 		termwidth = win.ws_col;
860da30e9aSPeter Wemm 
870da30e9aSPeter Wemm 	tflag = xflag = 0;
881c8af878SWarner Losh 	while ((ch = getopt(argc, argv, "c:s:tx")) != -1)
890da30e9aSPeter Wemm 		switch(ch) {
900da30e9aSPeter Wemm 		case 'c':
910da30e9aSPeter Wemm 			termwidth = atoi(optarg);
920da30e9aSPeter Wemm 			break;
930da30e9aSPeter Wemm 		case 's':
940da30e9aSPeter Wemm 			separator = optarg;
950da30e9aSPeter Wemm 			break;
960da30e9aSPeter Wemm 		case 't':
970da30e9aSPeter Wemm 			tflag = 1;
980da30e9aSPeter Wemm 			break;
990da30e9aSPeter Wemm 		case 'x':
1000da30e9aSPeter Wemm 			xflag = 1;
1010da30e9aSPeter Wemm 			break;
1020da30e9aSPeter Wemm 		case '?':
1030da30e9aSPeter Wemm 		default:
1040da30e9aSPeter Wemm 			usage();
1050da30e9aSPeter Wemm 		}
1060da30e9aSPeter Wemm 	argc -= optind;
1070da30e9aSPeter Wemm 	argv += optind;
1080da30e9aSPeter Wemm 
1090da30e9aSPeter Wemm 	if (!*argv)
1100da30e9aSPeter Wemm 		input(stdin);
1110da30e9aSPeter Wemm 	else for (; *argv; ++argv)
112fa146c53SArchie Cobbs 		if ((fp = fopen(*argv, "r"))) {
1130da30e9aSPeter Wemm 			input(fp);
1140da30e9aSPeter Wemm 			(void)fclose(fp);
1150da30e9aSPeter Wemm 		} else {
1160da30e9aSPeter Wemm 			warn("%s", *argv);
1170da30e9aSPeter Wemm 			eval = 1;
1180da30e9aSPeter Wemm 		}
1190da30e9aSPeter Wemm 
1200da30e9aSPeter Wemm 	if (!entries)
1210da30e9aSPeter Wemm 		exit(eval);
1220da30e9aSPeter Wemm 
1230da30e9aSPeter Wemm 	if (tflag)
1240da30e9aSPeter Wemm 		maketbl();
1250da30e9aSPeter Wemm 	else if (maxlength >= termwidth)
1260da30e9aSPeter Wemm 		print();
1270da30e9aSPeter Wemm 	else if (xflag)
1280da30e9aSPeter Wemm 		c_columnate();
1290da30e9aSPeter Wemm 	else
1300da30e9aSPeter Wemm 		r_columnate();
1310da30e9aSPeter Wemm 	exit(eval);
1320da30e9aSPeter Wemm }
1330da30e9aSPeter Wemm 
1340da30e9aSPeter Wemm #define	TAB	8
1350da30e9aSPeter Wemm void
1360da30e9aSPeter Wemm c_columnate()
1370da30e9aSPeter Wemm {
1380da30e9aSPeter Wemm 	int chcnt, col, cnt, endcol, numcols;
1390da30e9aSPeter Wemm 	char **lp;
1400da30e9aSPeter Wemm 
1410da30e9aSPeter Wemm 	maxlength = (maxlength + TAB) & ~(TAB - 1);
1420da30e9aSPeter Wemm 	numcols = termwidth / maxlength;
1430da30e9aSPeter Wemm 	endcol = maxlength;
1440da30e9aSPeter Wemm 	for (chcnt = col = 0, lp = list;; ++lp) {
1450da30e9aSPeter Wemm 		chcnt += printf("%s", *lp);
1460da30e9aSPeter Wemm 		if (!--entries)
1470da30e9aSPeter Wemm 			break;
1480da30e9aSPeter Wemm 		if (++col == numcols) {
1490da30e9aSPeter Wemm 			chcnt = col = 0;
1500da30e9aSPeter Wemm 			endcol = maxlength;
1510da30e9aSPeter Wemm 			putchar('\n');
1520da30e9aSPeter Wemm 		} else {
153fa146c53SArchie Cobbs 			while ((cnt = ((chcnt + TAB) & ~(TAB - 1))) <= endcol) {
1540da30e9aSPeter Wemm 				(void)putchar('\t');
1550da30e9aSPeter Wemm 				chcnt = cnt;
1560da30e9aSPeter Wemm 			}
1570da30e9aSPeter Wemm 			endcol += maxlength;
1580da30e9aSPeter Wemm 		}
1590da30e9aSPeter Wemm 	}
1600da30e9aSPeter Wemm 	if (chcnt)
1610da30e9aSPeter Wemm 		putchar('\n');
1620da30e9aSPeter Wemm }
1630da30e9aSPeter Wemm 
1640da30e9aSPeter Wemm void
1650da30e9aSPeter Wemm r_columnate()
1660da30e9aSPeter Wemm {
1670da30e9aSPeter Wemm 	int base, chcnt, cnt, col, endcol, numcols, numrows, row;
1680da30e9aSPeter Wemm 
1690da30e9aSPeter Wemm 	maxlength = (maxlength + TAB) & ~(TAB - 1);
1700da30e9aSPeter Wemm 	numcols = termwidth / maxlength;
1710da30e9aSPeter Wemm 	numrows = entries / numcols;
1720da30e9aSPeter Wemm 	if (entries % numcols)
1730da30e9aSPeter Wemm 		++numrows;
1740da30e9aSPeter Wemm 
1750da30e9aSPeter Wemm 	for (row = 0; row < numrows; ++row) {
1760da30e9aSPeter Wemm 		endcol = maxlength;
1770da30e9aSPeter Wemm 		for (base = row, chcnt = col = 0; col < numcols; ++col) {
1780da30e9aSPeter Wemm 			chcnt += printf("%s", list[base]);
1790da30e9aSPeter Wemm 			if ((base += numrows) >= entries)
1800da30e9aSPeter Wemm 				break;
181fa146c53SArchie Cobbs 			while ((cnt = ((chcnt + TAB) & ~(TAB - 1))) <= endcol) {
1820da30e9aSPeter Wemm 				(void)putchar('\t');
1830da30e9aSPeter Wemm 				chcnt = cnt;
1840da30e9aSPeter Wemm 			}
1850da30e9aSPeter Wemm 			endcol += maxlength;
1860da30e9aSPeter Wemm 		}
1870da30e9aSPeter Wemm 		putchar('\n');
1880da30e9aSPeter Wemm 	}
1890da30e9aSPeter Wemm }
1900da30e9aSPeter Wemm 
1910da30e9aSPeter Wemm void
1920da30e9aSPeter Wemm print()
1930da30e9aSPeter Wemm {
1940da30e9aSPeter Wemm 	int cnt;
1950da30e9aSPeter Wemm 	char **lp;
1960da30e9aSPeter Wemm 
1970da30e9aSPeter Wemm 	for (cnt = entries, lp = list; cnt--; ++lp)
1980da30e9aSPeter Wemm 		(void)printf("%s\n", *lp);
1990da30e9aSPeter Wemm }
2000da30e9aSPeter Wemm 
2010da30e9aSPeter Wemm typedef struct _tbl {
2020da30e9aSPeter Wemm 	char **list;
2030da30e9aSPeter Wemm 	int cols, *len;
2040da30e9aSPeter Wemm } TBL;
2050da30e9aSPeter Wemm #define	DEFCOLS	25
2060da30e9aSPeter Wemm 
2070da30e9aSPeter Wemm void
2080da30e9aSPeter Wemm maketbl()
2090da30e9aSPeter Wemm {
2100da30e9aSPeter Wemm 	TBL *t;
2110da30e9aSPeter Wemm 	int coloff, cnt;
2120da30e9aSPeter Wemm 	char *p, **lp;
2130da30e9aSPeter Wemm 	int *lens, maxcols;
2140da30e9aSPeter Wemm 	TBL *tbl;
2150da30e9aSPeter Wemm 	char **cols;
2160da30e9aSPeter Wemm 
2170da30e9aSPeter Wemm 	t = tbl = emalloc(entries * sizeof(TBL));
2180da30e9aSPeter Wemm 	cols = emalloc((maxcols = DEFCOLS) * sizeof(char *));
2190da30e9aSPeter Wemm 	lens = emalloc(maxcols * sizeof(int));
2200da30e9aSPeter Wemm 	for (cnt = 0, lp = list; cnt < entries; ++cnt, ++lp, ++t) {
221fa146c53SArchie Cobbs 		for (coloff = 0, p = *lp; (cols[coloff] = strtok(p, separator));
2220da30e9aSPeter Wemm 		    p = NULL)
2230da30e9aSPeter Wemm 			if (++coloff == maxcols) {
2240da30e9aSPeter Wemm 				if (!(cols = realloc(cols, (u_int)maxcols +
2250da30e9aSPeter Wemm 				    DEFCOLS * sizeof(char *))) ||
2260da30e9aSPeter Wemm 				    !(lens = realloc(lens,
2270da30e9aSPeter Wemm 				    (u_int)maxcols + DEFCOLS * sizeof(int))))
2280da30e9aSPeter Wemm 					err(1, NULL);
2290da30e9aSPeter Wemm 				memset((char *)lens + maxcols * sizeof(int),
2300da30e9aSPeter Wemm 				    0, DEFCOLS * sizeof(int));
2310da30e9aSPeter Wemm 				maxcols += DEFCOLS;
2320da30e9aSPeter Wemm 			}
2330da30e9aSPeter Wemm 		t->list = emalloc(coloff * sizeof(char *));
2340da30e9aSPeter Wemm 		t->len = emalloc(coloff * sizeof(int));
2350da30e9aSPeter Wemm 		for (t->cols = coloff; --coloff >= 0;) {
2360da30e9aSPeter Wemm 			t->list[coloff] = cols[coloff];
2370da30e9aSPeter Wemm 			t->len[coloff] = strlen(cols[coloff]);
2380da30e9aSPeter Wemm 			if (t->len[coloff] > lens[coloff])
2390da30e9aSPeter Wemm 				lens[coloff] = t->len[coloff];
2400da30e9aSPeter Wemm 		}
2410da30e9aSPeter Wemm 	}
2420da30e9aSPeter Wemm 	for (cnt = 0, t = tbl; cnt < entries; ++cnt, ++t) {
2430da30e9aSPeter Wemm 		for (coloff = 0; coloff < t->cols  - 1; ++coloff)
2440da30e9aSPeter Wemm 			(void)printf("%s%*s", t->list[coloff],
2450da30e9aSPeter Wemm 			    lens[coloff] - t->len[coloff] + 2, " ");
2460da30e9aSPeter Wemm 		(void)printf("%s\n", t->list[coloff]);
2470da30e9aSPeter Wemm 	}
2480da30e9aSPeter Wemm }
2490da30e9aSPeter Wemm 
2500da30e9aSPeter Wemm #define	DEFNUM		1000
2510da30e9aSPeter Wemm #define	MAXLINELEN	(LINE_MAX + 1)
2520da30e9aSPeter Wemm 
2530da30e9aSPeter Wemm void
2540da30e9aSPeter Wemm input(fp)
2550da30e9aSPeter Wemm 	FILE *fp;
2560da30e9aSPeter Wemm {
2570da30e9aSPeter Wemm 	static int maxentry;
2580da30e9aSPeter Wemm 	int len;
2590da30e9aSPeter Wemm 	char *p, buf[MAXLINELEN];
2600da30e9aSPeter Wemm 
2610da30e9aSPeter Wemm 	if (!list)
2620da30e9aSPeter Wemm 		list = emalloc((maxentry = DEFNUM) * sizeof(char *));
2630da30e9aSPeter Wemm 	while (fgets(buf, MAXLINELEN, fp)) {
2640da30e9aSPeter Wemm 		for (p = buf; *p && isspace(*p); ++p);
2650da30e9aSPeter Wemm 		if (!*p)
2660da30e9aSPeter Wemm 			continue;
2670da30e9aSPeter Wemm 		if (!(p = strchr(p, '\n'))) {
2680da30e9aSPeter Wemm 			warnx("line too long");
2690da30e9aSPeter Wemm 			eval = 1;
2700da30e9aSPeter Wemm 			continue;
2710da30e9aSPeter Wemm 		}
2720da30e9aSPeter Wemm 		*p = '\0';
2730da30e9aSPeter Wemm 		len = p - buf;
2740da30e9aSPeter Wemm 		if (maxlength < len)
2750da30e9aSPeter Wemm 			maxlength = len;
2760da30e9aSPeter Wemm 		if (entries == maxentry) {
2770da30e9aSPeter Wemm 			maxentry += DEFNUM;
2780da30e9aSPeter Wemm 			if (!(list = realloc(list,
2790da30e9aSPeter Wemm 			    (u_int)maxentry * sizeof(char *))))
2800da30e9aSPeter Wemm 				err(1, NULL);
2810da30e9aSPeter Wemm 		}
2820da30e9aSPeter Wemm 		list[entries++] = strdup(buf);
2830da30e9aSPeter Wemm 	}
2840da30e9aSPeter Wemm }
2850da30e9aSPeter Wemm 
2860da30e9aSPeter Wemm void *
2870da30e9aSPeter Wemm emalloc(size)
2880da30e9aSPeter Wemm 	int size;
2890da30e9aSPeter Wemm {
2900da30e9aSPeter Wemm 	char *p;
2910da30e9aSPeter Wemm 
2920da30e9aSPeter Wemm 	if (!(p = malloc(size)))
2930da30e9aSPeter Wemm 		err(1, NULL);
2940da30e9aSPeter Wemm 	memset(p, 0, size);
2950da30e9aSPeter Wemm 	return (p);
2960da30e9aSPeter Wemm }
2970da30e9aSPeter Wemm 
2980da30e9aSPeter Wemm void
2990da30e9aSPeter Wemm usage()
3000da30e9aSPeter Wemm {
3010da30e9aSPeter Wemm 
3020da30e9aSPeter Wemm 	(void)fprintf(stderr,
3033b5ec5aaSPhilippe Charnier 	    "usage: column [-tx] [-c columns] [-s sep] [file ...]\n");
3040da30e9aSPeter Wemm 	exit(1);
3050da30e9aSPeter Wemm }
306