xref: /plan9/sys/src/cmd/mc.c (revision 219b2ee8daee37f4aad58d63f21287faa8e4ffdc)
13e12c5d1SDavid du Colombier /*
23e12c5d1SDavid du Colombier  * mc - columnate
33e12c5d1SDavid du Colombier  *
43e12c5d1SDavid du Colombier  * mc[-][-LINEWIDTH][-t][file...]
53e12c5d1SDavid du Colombier  *	- causes break on colon
63e12c5d1SDavid du Colombier  *	-LINEWIDTH sets width of line in which to columnate(default 80)
73e12c5d1SDavid du Colombier  *	-t suppresses expanding multiple blanks into tabs
83e12c5d1SDavid du Colombier  *
93e12c5d1SDavid du Colombier  */
103e12c5d1SDavid du Colombier #include	<u.h>
113e12c5d1SDavid du Colombier #include	<libc.h>
123e12c5d1SDavid du Colombier #include	<libg.h>
133e12c5d1SDavid du Colombier #include	<bio.h>
143e12c5d1SDavid du Colombier 
153e12c5d1SDavid du Colombier #define WIDTH			80
163e12c5d1SDavid du Colombier #define WORD_ALLOC_QUANTA	1024
173e12c5d1SDavid du Colombier #define ALLOC_QUANTA		4096
183e12c5d1SDavid du Colombier 
193e12c5d1SDavid du Colombier int linewidth=WIDTH;
203e12c5d1SDavid du Colombier int colonflag=0;
21*219b2ee8SDavid du Colombier int tabflag=0;	/* -t flag turned off forever */
223e12c5d1SDavid du Colombier Rune *cbuf, *cbufp;
233e12c5d1SDavid du Colombier Rune **word;
243e12c5d1SDavid du Colombier int maxwidth=0;
253e12c5d1SDavid du Colombier int nalloc=ALLOC_QUANTA;
263e12c5d1SDavid du Colombier int nwalloc=WORD_ALLOC_QUANTA;
273e12c5d1SDavid du Colombier int nchars=0;
283e12c5d1SDavid du Colombier int nwords=0;
293e12c5d1SDavid du Colombier Biobuf	bin;
303e12c5d1SDavid du Colombier Biobuf	bout;
313e12c5d1SDavid du Colombier 
32bd389b36SDavid du Colombier void getwidth(void), readbuf(int), error(char *);
333e12c5d1SDavid du Colombier void scanwords(void), columnate(void), morechars(void);
343e12c5d1SDavid du Colombier 
353e12c5d1SDavid du Colombier void
363e12c5d1SDavid du Colombier main(int argc, char *argv[])
373e12c5d1SDavid du Colombier {
383e12c5d1SDavid du Colombier 	int i;
393e12c5d1SDavid du Colombier 	int lineset;
40bd389b36SDavid du Colombier 	int ifd;
41bd389b36SDavid du Colombier 
423e12c5d1SDavid du Colombier 	lineset = 0;
433e12c5d1SDavid du Colombier 	Binit(&bout, 1, OWRITE);
443e12c5d1SDavid du Colombier 	while(argc > 1 && argv[1][0] == '-'){
453e12c5d1SDavid du Colombier 		--argc; argv++;
463e12c5d1SDavid du Colombier 		switch(argv[0][1]){
473e12c5d1SDavid du Colombier 		case '\0':
483e12c5d1SDavid du Colombier 			colonflag = 1;
493e12c5d1SDavid du Colombier 			break;
503e12c5d1SDavid du Colombier 		case 't':
513e12c5d1SDavid du Colombier 			tabflag = 0;
523e12c5d1SDavid du Colombier 			break;
533e12c5d1SDavid du Colombier 		default:
543e12c5d1SDavid du Colombier 			linewidth = atoi(&argv[0][1]);
553e12c5d1SDavid du Colombier 			if(linewidth <= 1)
563e12c5d1SDavid du Colombier 				linewidth = WIDTH;
573e12c5d1SDavid du Colombier 			lineset = 1;
583e12c5d1SDavid du Colombier 			break;
593e12c5d1SDavid du Colombier 		}
603e12c5d1SDavid du Colombier 	}
613e12c5d1SDavid du Colombier 	if(lineset == 0)
623e12c5d1SDavid du Colombier 		getwidth();
633e12c5d1SDavid du Colombier 	cbuf = cbufp = malloc(ALLOC_QUANTA*(sizeof *cbuf));
643e12c5d1SDavid du Colombier 	word = malloc(WORD_ALLOC_QUANTA*(sizeof *word));
653e12c5d1SDavid du Colombier 	if(word == 0 || cbuf == 0)
663e12c5d1SDavid du Colombier 		error("out of memory");
673e12c5d1SDavid du Colombier 	if(argc == 1)
68bd389b36SDavid du Colombier 		readbuf(0);
693e12c5d1SDavid du Colombier 	else{
703e12c5d1SDavid du Colombier 		for(i = 1; i < argc; i++){
71bd389b36SDavid du Colombier 			if((ifd = open(*++argv, OREAD)) == -1)
72bd389b36SDavid du Colombier 				fprint(2, "mc: can't open %s (%r)\n", *argv);
733e12c5d1SDavid du Colombier 			else{
74bd389b36SDavid du Colombier 				readbuf(ifd);
75bd389b36SDavid du Colombier 				Bflush(&bin);
76bd389b36SDavid du Colombier 				close(ifd);
773e12c5d1SDavid du Colombier 			}
783e12c5d1SDavid du Colombier 		}
793e12c5d1SDavid du Colombier 	}
803e12c5d1SDavid du Colombier 	columnate();
813e12c5d1SDavid du Colombier 	exits(0);
823e12c5d1SDavid du Colombier }
833e12c5d1SDavid du Colombier void
843e12c5d1SDavid du Colombier error(char *s)
853e12c5d1SDavid du Colombier {
863e12c5d1SDavid du Colombier 	fprint(2, "mc: %s\n", s);
873e12c5d1SDavid du Colombier 	exits(s);
883e12c5d1SDavid du Colombier }
893e12c5d1SDavid du Colombier void
90bd389b36SDavid du Colombier readbuf(int fd)
913e12c5d1SDavid du Colombier {
923e12c5d1SDavid du Colombier 	int lastwascolon = 0;
933e12c5d1SDavid du Colombier 	long c;
943e12c5d1SDavid du Colombier 	int linesiz = 0;
953e12c5d1SDavid du Colombier 
96bd389b36SDavid du Colombier 	Binit(&bin, fd, OREAD);
973e12c5d1SDavid du Colombier 	do{
983e12c5d1SDavid du Colombier 		if(nchars++ >= nalloc)
993e12c5d1SDavid du Colombier 			morechars();
1003e12c5d1SDavid du Colombier 		*cbufp++ = c = Bgetrune(&bin);
1013e12c5d1SDavid du Colombier 		linesiz++;
1023e12c5d1SDavid du Colombier 		if(c == '\t') {
1033e12c5d1SDavid du Colombier 			cbufp[-1] = L' ';
1043e12c5d1SDavid du Colombier 			while(linesiz%8 != 0) {
1053e12c5d1SDavid du Colombier 				if(nchars++ >= nalloc)
1063e12c5d1SDavid du Colombier 					morechars();
1073e12c5d1SDavid du Colombier 				*cbufp++ = L' ';
1083e12c5d1SDavid du Colombier 				linesiz++;
1093e12c5d1SDavid du Colombier 			}
1103e12c5d1SDavid du Colombier 		}
1113e12c5d1SDavid du Colombier 		if(colonflag && c == ':')
1123e12c5d1SDavid du Colombier 			lastwascolon++;
1133e12c5d1SDavid du Colombier 		else if(lastwascolon){
1143e12c5d1SDavid du Colombier 			if(c == '\n'){
1153e12c5d1SDavid du Colombier 				--nchars; 	/* skip newline */
1163e12c5d1SDavid du Colombier 				*cbufp = L'\0';
1173e12c5d1SDavid du Colombier 				while(nchars > 0 && cbuf[--nchars] != '\n')
1183e12c5d1SDavid du Colombier 					;
1193e12c5d1SDavid du Colombier 				if(nchars)
1203e12c5d1SDavid du Colombier 					nchars++;
1213e12c5d1SDavid du Colombier 				columnate();
1223e12c5d1SDavid du Colombier 				if (nchars)
1233e12c5d1SDavid du Colombier 					BPUTC(&bout, '\n');
1243e12c5d1SDavid du Colombier 				Bprint(&bout, "%S", cbuf+nchars);
1253e12c5d1SDavid du Colombier 				nchars = 0;
1263e12c5d1SDavid du Colombier 				cbufp = cbuf;
1273e12c5d1SDavid du Colombier 			}
1283e12c5d1SDavid du Colombier 			lastwascolon = 0;
1293e12c5d1SDavid du Colombier 		}
1303e12c5d1SDavid du Colombier 		if(c == '\n')
1313e12c5d1SDavid du Colombier 			linesiz = 0;
1323e12c5d1SDavid du Colombier 	}while(c >= 0);
1333e12c5d1SDavid du Colombier }
1343e12c5d1SDavid du Colombier void
1353e12c5d1SDavid du Colombier scanwords(void)
1363e12c5d1SDavid du Colombier {
1373e12c5d1SDavid du Colombier 	Rune *p, *q;
1383e12c5d1SDavid du Colombier 	int i;
1393e12c5d1SDavid du Colombier 
1403e12c5d1SDavid du Colombier 	nwords=0;
1413e12c5d1SDavid du Colombier 	maxwidth=0;
1423e12c5d1SDavid du Colombier 	for(p = q = cbuf, i = 0; i < nchars; i++){
1433e12c5d1SDavid du Colombier 		if(*p++ == L'\n'){
1443e12c5d1SDavid du Colombier 			if(nwords >= nwalloc){
1453e12c5d1SDavid du Colombier 				nwalloc += WORD_ALLOC_QUANTA;
1463e12c5d1SDavid du Colombier 				if((word = realloc(word, nwalloc*sizeof(*word)))==0)
1473e12c5d1SDavid du Colombier 					error("out of memory");
1483e12c5d1SDavid du Colombier 			}
1493e12c5d1SDavid du Colombier 			word[nwords++] = q;
1503e12c5d1SDavid du Colombier 			p[-1] = L'\0';
1513e12c5d1SDavid du Colombier 			if(p-q > maxwidth)
1523e12c5d1SDavid du Colombier 				maxwidth = p-q;
1533e12c5d1SDavid du Colombier 			q = p;
1543e12c5d1SDavid du Colombier 		}
1553e12c5d1SDavid du Colombier 	}
1563e12c5d1SDavid du Colombier }
1573e12c5d1SDavid du Colombier 
1583e12c5d1SDavid du Colombier void
1593e12c5d1SDavid du Colombier columnate(void)
1603e12c5d1SDavid du Colombier {
1613e12c5d1SDavid du Colombier 	int i, j;
1623e12c5d1SDavid du Colombier 	int words_per_line;
1633e12c5d1SDavid du Colombier 	int nlines;
1643e12c5d1SDavid du Colombier 	int col;
1653e12c5d1SDavid du Colombier 	int endcol;
1663e12c5d1SDavid du Colombier 
1673e12c5d1SDavid du Colombier 
1683e12c5d1SDavid du Colombier 	scanwords();
1693e12c5d1SDavid du Colombier 	if(nwords==0)
1703e12c5d1SDavid du Colombier 		return;
1713e12c5d1SDavid du Colombier 	words_per_line = linewidth/maxwidth;
1723e12c5d1SDavid du Colombier 	if(words_per_line <= 0)
1733e12c5d1SDavid du Colombier 		words_per_line = 1;
1743e12c5d1SDavid du Colombier 	nlines=(nwords+words_per_line-1)/words_per_line;
1753e12c5d1SDavid du Colombier 	for(i = 0; i < nlines; i++){
1763e12c5d1SDavid du Colombier 		col = endcol = 0;
1773e12c5d1SDavid du Colombier 		for(j = i; j < nwords; j += nlines){
1783e12c5d1SDavid du Colombier 			endcol += maxwidth;
1793e12c5d1SDavid du Colombier 			Bprint(&bout, "%S", word[j]);
1803e12c5d1SDavid du Colombier 			col += word[j+1]-word[j]-1;
1813e12c5d1SDavid du Colombier 			if(j+nlines < nwords){
1823e12c5d1SDavid du Colombier 				if(tabflag) {
1833e12c5d1SDavid du Colombier 					int tabcol = (col|07)+1;
1843e12c5d1SDavid du Colombier 					while(tabcol <= endcol){
1853e12c5d1SDavid du Colombier 						BPUTC(&bout, '\t');
1863e12c5d1SDavid du Colombier 						col = tabcol;
1873e12c5d1SDavid du Colombier 						tabcol += 8;
1883e12c5d1SDavid du Colombier 					}
1893e12c5d1SDavid du Colombier 				}
1903e12c5d1SDavid du Colombier 				while(col < endcol){
1913e12c5d1SDavid du Colombier 					BPUTC(&bout, ' ');
1923e12c5d1SDavid du Colombier 					col++;
1933e12c5d1SDavid du Colombier 				}
1943e12c5d1SDavid du Colombier 			}
1953e12c5d1SDavid du Colombier 		}
1963e12c5d1SDavid du Colombier 		BPUTC(&bout, '\n');
1973e12c5d1SDavid du Colombier 	}
1983e12c5d1SDavid du Colombier }
1993e12c5d1SDavid du Colombier 
2003e12c5d1SDavid du Colombier void
2013e12c5d1SDavid du Colombier morechars(void)
2023e12c5d1SDavid du Colombier {
2033e12c5d1SDavid du Colombier 	nalloc += ALLOC_QUANTA;
2043e12c5d1SDavid du Colombier 	if((cbuf = realloc(cbuf, nalloc*sizeof(*cbuf))) == 0)
2053e12c5d1SDavid du Colombier 		error("out of memory");
2063e12c5d1SDavid du Colombier 	cbufp = cbuf+nchars-1;
2073e12c5d1SDavid du Colombier }
2083e12c5d1SDavid du Colombier 
2093e12c5d1SDavid du Colombier void
2103e12c5d1SDavid du Colombier getwidth(void)
2113e12c5d1SDavid du Colombier {
2123e12c5d1SDavid du Colombier 	Rectangle r;
2133e12c5d1SDavid du Colombier 	char buf[5*12];
2143e12c5d1SDavid du Colombier 	int fd, width;
2153e12c5d1SDavid du Colombier 
2163e12c5d1SDavid du Colombier 		/* window stucture:
2173e12c5d1SDavid du Colombier 			4 bit left edge
2183e12c5d1SDavid du Colombier 			1 bit gap
2193e12c5d1SDavid du Colombier 			12 bit scrollbar
2203e12c5d1SDavid du Colombier 			4 bit gap
2213e12c5d1SDavid du Colombier 			text
2223e12c5d1SDavid du Colombier 			4 bit right edge
2233e12c5d1SDavid du Colombier 		*/
2243e12c5d1SDavid du Colombier 	fd = open("/dev/window", OREAD);
2253e12c5d1SDavid du Colombier 	if(fd < 0)
2263e12c5d1SDavid du Colombier 		fd = open("/dev/screen", OREAD);
2273e12c5d1SDavid du Colombier 	if(fd < 0)
2283e12c5d1SDavid du Colombier 		return;
2293e12c5d1SDavid du Colombier 	if(read(fd, buf, sizeof buf) != sizeof buf){
2303e12c5d1SDavid du Colombier 		close(fd);
2313e12c5d1SDavid du Colombier 		return;
2323e12c5d1SDavid du Colombier 	}
2333e12c5d1SDavid du Colombier 	close(fd);
2343e12c5d1SDavid du Colombier 	r.min.x = atoi(buf+1*12);
2353e12c5d1SDavid du Colombier 	r.min.y = atoi(buf+2*12);
2363e12c5d1SDavid du Colombier 	r.max.x = atoi(buf+3*12);
2373e12c5d1SDavid du Colombier 	r.max.y = atoi(buf+4*12);
2383e12c5d1SDavid du Colombier 	fd = open("/dev/bitblt", ORDWR);
2393e12c5d1SDavid du Colombier 	if(fd < 0)
2403e12c5d1SDavid du Colombier 		width = 9;
2413e12c5d1SDavid du Colombier 	else{
2423e12c5d1SDavid du Colombier 		close(fd);
2433e12c5d1SDavid du Colombier 		binit(0, 0, 0);
2443e12c5d1SDavid du Colombier 		width = charwidth(font, ' ');
2453e12c5d1SDavid du Colombier 		bclose();
2463e12c5d1SDavid du Colombier 	}
2473e12c5d1SDavid du Colombier 	linewidth = (r.max.x-r.min.x-(4+1+12+4+4))/width;
2483e12c5d1SDavid du Colombier }
249