[net.sources] Multiple column filter

dan@rna.UUCP (Dan Ts'o) (01/26/85)

x
	Here is a filter "mc" which rearranges input lines to multicolumned
output. That is,

	ls | mc

is like

	ls -C			(On Berkeley systems)

	There are other ways of producing multicolumned output, of course.
Two string to mind:

	1)
		ls | pr -t -4 -l1

	2)
		ls | paste - - - -

	However, neither of these filters duplicates the "ls -C" output,
where the columns proceed downward first, then on to the next column, which
is what "mc" does.
	The original code came from the old Harvard UNIX V6 systems, but I
had to modify it a bit to move out of the V6 PDP-11 world.
	To compile it,
		cc -O mc.c -ltermlib
	although, if you don't have libtermlib.a, commenting out three lines
would do fine. In addition,
		mc -132
	specifies a 132-column output. Normally, columns is taken from TERMCAP.
	I find mc useful for all sorts of general purpose re-formatting, like
directory listing of other OS's and long columns of numbers.

					Cheers,
					Dan Ts'o
					Dept. Neurobiology
					Rockefeller Univ.
					1230 York Ave.
					NY, NY 10021
					212-570-7671
					...cmcl2!rna!dan

/*
 * mc - Multiple column filter
 *	Transform lines of input into listing of multiple columns
 *	Original code from Harvard V6 Unix
 *	Updated and reworked by Dan Ts'o, Rockefeller Univ.
 *	Now has cat-like syntax:
 *		mc [-] [file ...]
 */
#include <stdio.h>

#define	MEMINCR	1024L		/* Memory buffer increments */
#define	ZSTACK1	(-1)		/* An impossible (char *) (Sorry) */

char *nodename;
int width;
char *malloc(), **stack1();
char *getenv();

main(c,v)
char **v;
{
	register int i,f;
	register char *cp;
	FILE *fd;
	char tbuf[1024];
/* Uncomment if your stdio doesn't buffer stdout
	char buf[BUFSIZ];

	setbuf(stdout, buf);
 */
	f = 0;
	nodename = *v;
	cp = getenv("TERM");
	if (cp == NULL || tgetent(tbuf, cp) <= 0
		|| (width = tgetnum("co")-1) < 8)
		width = 79;
	while (c > 1 && v[1][0] == '-') {
		c--;
		v++;
		switch (v[0][1]) {
		case '0': case '1': case '2': case '3': case '4':
		case '5': case '6': case '7': case '8': case '9':
			width = atoi(&v[0][1]);
			break;
		default:
			error("%s: Bad option\n", *v);
		}
	}

	if (--c == 0)
		exit(xfer(stdin));
	else {
		while (c--) {
			v++;
			if (**v == '-' && v[0][1] == 0) {
				xfer(stdin);
				clearerr(stdin);
			}
			else {
				if ((fd = fopen(*v, "r")) == NULL) {
					fprintf(stderr, "%s: %s: Can't open\n",
						nodename, *v);
					f++;
				}
				else {
					xfer(fd);
					fclose(fd);
				}
			}
		}
	}
	exit(f ? -1 : 0);

}

xfer(fin)
FILE *fin;
{
	register int i;
	register char *cp;
	char line[1024];
	int max, items, columns;
	int row_p, index, rows, col_p;
	char **bot;

	items = max = 0;
	while(fgets(line, sizeof line, fin) != NULL) {
		i = strlen(line);
		if (line[--i] == '\n')
		    line[i] = 0;
		else
		    i++;
		if(i >= width) {
		    error("Line of length=%d is too long for width=%d\n",
			i, width);
		}
		if((cp = (char *)malloc(i+1)) == NULL) 
		    error("Out of memory\n");
		strcpy(cp, line);
		bot = stack1(cp);
		if(i > max) max = i;
		items++;
	}
	columns = width / (max+1);
	rows = (items + columns - 1) / columns;
	for(row_p = 0; row_p < rows; row_p++) {
	    for(col_p = 0; col_p < columns; col_p++) {
		index = (col_p * rows) + row_p;
		if(index >= items)
		    continue;
		if((col_p + 1) * rows + row_p >= items)
		    printf("%s", bot[items - index - 1]);
		else
		    printf("%-*s ", max, bot[items - index - 1]);
	    }
	    printf("\n");
	}
	fflush(stdout);
	stack1(ZSTACK1);
	return ferror(fin);
}

error(a, b, c, d, e)
{
	fprintf(stderr, "%s: ", nodename);
	fprintf(stderr, a, b, c, d, e);
	exit (1);
}

char **stack1(s)
char *s;
{
	static char **s_beg, **s_end;
	static long nbuf = 0;
	register char **v, **u;
	register long n;

	if (s == (char *) ZSTACK1) {
		if (nbuf > 0)
			free(s_beg);
		nbuf = 0;
		return 0;
	}
	if (nbuf == 0 || s_end <= s_beg) {
		n = MEMINCR * (nbuf+1);
		v = (char **)malloc(n*(sizeof (char *)));
		if (v == NULL)
			error("mc: Out of memory\n");
		s_beg = v;
		v += n;
		if (nbuf > 0) {
			u = s_end+(MEMINCR*nbuf);
			while (u > s_end)
				*--v = *--u;
			free(s_end);
		}
		nbuf++;
		s_end = v;
	}
	*--s_end = s;
	return s_end;
}