xref: /plan9/sys/src/cmd/mc.c (revision b8661318e8f82fa2a6c406ca28596a6aef982bd2)
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>
127dd7cddfSDavid du Colombier #include	<draw.h>
133e12c5d1SDavid du Colombier #include	<bio.h>
143e12c5d1SDavid du Colombier 
153e12c5d1SDavid du Colombier #define	WIDTH			80
167dd7cddfSDavid du Colombier #define	TAB	4
173e12c5d1SDavid du Colombier #define	WORD_ALLOC_QUANTA	1024
183e12c5d1SDavid du Colombier #define	ALLOC_QUANTA		4096
193e12c5d1SDavid du Colombier 
203e12c5d1SDavid du Colombier int linewidth=WIDTH;
21*b8661318SDavid du Colombier int mintab=1;
223e12c5d1SDavid du Colombier int colonflag=0;
23219b2ee8SDavid du Colombier int tabflag=0;	/* -t flag turned off forever */
243e12c5d1SDavid du Colombier Rune *cbuf, *cbufp;
253e12c5d1SDavid du Colombier Rune **word;
263e12c5d1SDavid du Colombier int maxwidth=0;
273e12c5d1SDavid du Colombier int nalloc=ALLOC_QUANTA;
283e12c5d1SDavid du Colombier int nwalloc=WORD_ALLOC_QUANTA;
293e12c5d1SDavid du Colombier int nchars=0;
303e12c5d1SDavid du Colombier int nwords=0;
31*b8661318SDavid du Colombier int tabwidth=0;
32*b8661318SDavid du Colombier Font *font;
333e12c5d1SDavid du Colombier Biobuf	bin;
343e12c5d1SDavid du Colombier Biobuf	bout;
353e12c5d1SDavid du Colombier 
36bd389b36SDavid du Colombier void getwidth(void), readbuf(int), error(char *);
373e12c5d1SDavid du Colombier void scanwords(void), columnate(void), morechars(void);
38*b8661318SDavid du Colombier int wordwidth(Rune*, int);
39*b8661318SDavid du Colombier int nexttab(int);
403e12c5d1SDavid du Colombier 
413e12c5d1SDavid du Colombier void
main(int argc,char * argv[])423e12c5d1SDavid du Colombier main(int argc, char *argv[])
433e12c5d1SDavid du Colombier {
443e12c5d1SDavid du Colombier 	int i;
453e12c5d1SDavid du Colombier 	int lineset;
46bd389b36SDavid du Colombier 	int ifd;
47bd389b36SDavid du Colombier 
483e12c5d1SDavid du Colombier 	lineset = 0;
493e12c5d1SDavid du Colombier 	Binit(&bout, 1, OWRITE);
503e12c5d1SDavid du Colombier 	while(argc > 1 && argv[1][0] == '-'){
513e12c5d1SDavid du Colombier 		--argc; argv++;
523e12c5d1SDavid du Colombier 		switch(argv[0][1]){
533e12c5d1SDavid du Colombier 		case '\0':
543e12c5d1SDavid du Colombier 			colonflag = 1;
553e12c5d1SDavid du Colombier 			break;
563e12c5d1SDavid du Colombier 		case 't':
573e12c5d1SDavid du Colombier 			tabflag = 0;
583e12c5d1SDavid du Colombier 			break;
593e12c5d1SDavid du Colombier 		default:
603e12c5d1SDavid du Colombier 			linewidth = atoi(&argv[0][1]);
613e12c5d1SDavid du Colombier 			if(linewidth <= 1)
623e12c5d1SDavid du Colombier 				linewidth = WIDTH;
633e12c5d1SDavid du Colombier 			lineset = 1;
643e12c5d1SDavid du Colombier 			break;
653e12c5d1SDavid du Colombier 		}
663e12c5d1SDavid du Colombier 	}
67*b8661318SDavid du Colombier 	if(lineset == 0){
683e12c5d1SDavid du Colombier 		getwidth();
69*b8661318SDavid du Colombier 		if(linewidth <= 1){
70*b8661318SDavid du Colombier 			linewidth = WIDTH;
71*b8661318SDavid du Colombier 			font = nil;
72*b8661318SDavid du Colombier 		}
73*b8661318SDavid du Colombier 	}
74*b8661318SDavid du Colombier 
753e12c5d1SDavid du Colombier 	cbuf = cbufp = malloc(ALLOC_QUANTA*(sizeof *cbuf));
763e12c5d1SDavid du Colombier 	word = malloc(WORD_ALLOC_QUANTA*(sizeof *word));
773e12c5d1SDavid du Colombier 	if(word == 0 || cbuf == 0)
783e12c5d1SDavid du Colombier 		error("out of memory");
793e12c5d1SDavid du Colombier 	if(argc == 1)
80bd389b36SDavid du Colombier 		readbuf(0);
813e12c5d1SDavid du Colombier 	else{
823e12c5d1SDavid du Colombier 		for(i = 1; i < argc; i++){
83bd389b36SDavid du Colombier 			if((ifd = open(*++argv, OREAD)) == -1)
84bd389b36SDavid du Colombier 				fprint(2, "mc: can't open %s (%r)\n", *argv);
853e12c5d1SDavid du Colombier 			else{
86bd389b36SDavid du Colombier 				readbuf(ifd);
87bd389b36SDavid du Colombier 				Bflush(&bin);
88bd389b36SDavid du Colombier 				close(ifd);
893e12c5d1SDavid du Colombier 			}
903e12c5d1SDavid du Colombier 		}
913e12c5d1SDavid du Colombier 	}
923e12c5d1SDavid du Colombier 	columnate();
933e12c5d1SDavid du Colombier 	exits(0);
943e12c5d1SDavid du Colombier }
953e12c5d1SDavid du Colombier void
error(char * s)963e12c5d1SDavid du Colombier error(char *s)
973e12c5d1SDavid du Colombier {
983e12c5d1SDavid du Colombier 	fprint(2, "mc: %s\n", s);
993e12c5d1SDavid du Colombier 	exits(s);
1003e12c5d1SDavid du Colombier }
1013e12c5d1SDavid du Colombier void
readbuf(int fd)102bd389b36SDavid du Colombier readbuf(int fd)
1033e12c5d1SDavid du Colombier {
1043e12c5d1SDavid du Colombier 	int lastwascolon = 0;
1053e12c5d1SDavid du Colombier 	long c;
1063e12c5d1SDavid du Colombier 	int linesiz = 0;
1073e12c5d1SDavid du Colombier 
108bd389b36SDavid du Colombier 	Binit(&bin, fd, OREAD);
1093e12c5d1SDavid du Colombier 	do{
1103e12c5d1SDavid du Colombier 		if(nchars++ >= nalloc)
1113e12c5d1SDavid du Colombier 			morechars();
1123e12c5d1SDavid du Colombier 		*cbufp++ = c = Bgetrune(&bin);
1133e12c5d1SDavid du Colombier 		linesiz++;
1143e12c5d1SDavid du Colombier 		if(c == '\t') {
1153e12c5d1SDavid du Colombier 			cbufp[-1] = L' ';
1167dd7cddfSDavid du Colombier 			while(linesiz%TAB != 0) {
1173e12c5d1SDavid du Colombier 				if(nchars++ >= nalloc)
1183e12c5d1SDavid du Colombier 					morechars();
1193e12c5d1SDavid du Colombier 				*cbufp++ = L' ';
1203e12c5d1SDavid du Colombier 				linesiz++;
1213e12c5d1SDavid du Colombier 			}
1223e12c5d1SDavid du Colombier 		}
1233e12c5d1SDavid du Colombier 		if(colonflag && c == ':')
1243e12c5d1SDavid du Colombier 			lastwascolon++;
1253e12c5d1SDavid du Colombier 		else if(lastwascolon){
1263e12c5d1SDavid du Colombier 			if(c == '\n'){
1273e12c5d1SDavid du Colombier 				--nchars; 	/* skip newline */
1283e12c5d1SDavid du Colombier 				*cbufp = L'\0';
1293e12c5d1SDavid du Colombier 				while(nchars > 0 && cbuf[--nchars] != '\n')
1303e12c5d1SDavid du Colombier 					;
1313e12c5d1SDavid du Colombier 				if(nchars)
1323e12c5d1SDavid du Colombier 					nchars++;
1333e12c5d1SDavid du Colombier 				columnate();
1343e12c5d1SDavid du Colombier 				if (nchars)
1357dd7cddfSDavid du Colombier 					Bputc(&bout, '\n');
1363e12c5d1SDavid du Colombier 				Bprint(&bout, "%S", cbuf+nchars);
1373e12c5d1SDavid du Colombier 				nchars = 0;
1383e12c5d1SDavid du Colombier 				cbufp = cbuf;
1393e12c5d1SDavid du Colombier 			}
1403e12c5d1SDavid du Colombier 			lastwascolon = 0;
1413e12c5d1SDavid du Colombier 		}
1423e12c5d1SDavid du Colombier 		if(c == '\n')
1433e12c5d1SDavid du Colombier 			linesiz = 0;
1443e12c5d1SDavid du Colombier 	}while(c >= 0);
1453e12c5d1SDavid du Colombier }
1463e12c5d1SDavid du Colombier void
scanwords(void)1473e12c5d1SDavid du Colombier scanwords(void)
1483e12c5d1SDavid du Colombier {
1493e12c5d1SDavid du Colombier 	Rune *p, *q;
150*b8661318SDavid du Colombier 	int i, w;
1513e12c5d1SDavid du Colombier 
1523e12c5d1SDavid du Colombier 	nwords=0;
1533e12c5d1SDavid du Colombier 	maxwidth=0;
1543e12c5d1SDavid du Colombier 	for(p = q = cbuf, i = 0; i < nchars; i++){
1553e12c5d1SDavid du Colombier 		if(*p++ == L'\n'){
1563e12c5d1SDavid du Colombier 			if(nwords >= nwalloc){
1573e12c5d1SDavid du Colombier 				nwalloc += WORD_ALLOC_QUANTA;
1583e12c5d1SDavid du Colombier 				if((word = realloc(word, nwalloc*sizeof(*word)))==0)
1593e12c5d1SDavid du Colombier 					error("out of memory");
1603e12c5d1SDavid du Colombier 			}
1613e12c5d1SDavid du Colombier 			word[nwords++] = q;
1623e12c5d1SDavid du Colombier 			p[-1] = L'\0';
163*b8661318SDavid du Colombier 			w = wordwidth(q, p-q-1);
164*b8661318SDavid du Colombier 			if(w > maxwidth)
165*b8661318SDavid du Colombier 				maxwidth = w;
1663e12c5d1SDavid du Colombier 			q = p;
1673e12c5d1SDavid du Colombier 		}
1683e12c5d1SDavid du Colombier 	}
1693e12c5d1SDavid du Colombier }
1703e12c5d1SDavid du Colombier 
1713e12c5d1SDavid du Colombier void
columnate(void)1723e12c5d1SDavid du Colombier columnate(void)
1733e12c5d1SDavid du Colombier {
1743e12c5d1SDavid du Colombier 	int i, j;
1753e12c5d1SDavid du Colombier 	int words_per_line;
1763e12c5d1SDavid du Colombier 	int nlines;
1773e12c5d1SDavid du Colombier 	int col;
1783e12c5d1SDavid du Colombier 	int endcol;
1793e12c5d1SDavid du Colombier 
1803e12c5d1SDavid du Colombier 
1813e12c5d1SDavid du Colombier 	scanwords();
1823e12c5d1SDavid du Colombier 	if(nwords==0)
1833e12c5d1SDavid du Colombier 		return;
184*b8661318SDavid du Colombier 	maxwidth = nexttab(maxwidth+mintab-1);
1853e12c5d1SDavid du Colombier 	words_per_line = linewidth/maxwidth;
1863e12c5d1SDavid du Colombier 	if(words_per_line <= 0)
1873e12c5d1SDavid du Colombier 		words_per_line = 1;
1883e12c5d1SDavid du Colombier 	nlines=(nwords+words_per_line-1)/words_per_line;
1893e12c5d1SDavid du Colombier 	for(i = 0; i < nlines; i++){
1903e12c5d1SDavid du Colombier 		col = endcol = 0;
1913e12c5d1SDavid du Colombier 		for(j = i; j < nwords; j += nlines){
1923e12c5d1SDavid du Colombier 			endcol += maxwidth;
1933e12c5d1SDavid du Colombier 			Bprint(&bout, "%S", word[j]);
194*b8661318SDavid du Colombier 			col += wordwidth(word[j], runestrlen(word[j]));
1953e12c5d1SDavid du Colombier 			if(j+nlines < nwords){
1963e12c5d1SDavid du Colombier 				if(tabflag) {
197*b8661318SDavid du Colombier 					while(col < endcol){
1987dd7cddfSDavid du Colombier 						Bputc(&bout, '\t');
199*b8661318SDavid du Colombier 						col = nexttab(col);
2003e12c5d1SDavid du Colombier 					}
201*b8661318SDavid du Colombier 				}else{
2023e12c5d1SDavid du Colombier 					while(col < endcol){
2037dd7cddfSDavid du Colombier 						Bputc(&bout, ' ');
2043e12c5d1SDavid du Colombier 						col++;
2053e12c5d1SDavid du Colombier 					}
2063e12c5d1SDavid du Colombier 				}
2073e12c5d1SDavid du Colombier 			}
208*b8661318SDavid du Colombier 		}
2097dd7cddfSDavid du Colombier 		Bputc(&bout, '\n');
2103e12c5d1SDavid du Colombier 	}
2113e12c5d1SDavid du Colombier }
2123e12c5d1SDavid du Colombier 
213*b8661318SDavid du Colombier int
wordwidth(Rune * w,int nw)214*b8661318SDavid du Colombier wordwidth(Rune *w, int nw)
215*b8661318SDavid du Colombier {
216*b8661318SDavid du Colombier 	if(font)
217*b8661318SDavid du Colombier 		return runestringnwidth(font, w, nw);
218*b8661318SDavid du Colombier 	return nw;
219*b8661318SDavid du Colombier }
220*b8661318SDavid du Colombier 
221*b8661318SDavid du Colombier int
nexttab(int col)222*b8661318SDavid du Colombier nexttab(int col)
223*b8661318SDavid du Colombier {
224*b8661318SDavid du Colombier 	if(tabwidth){
225*b8661318SDavid du Colombier 		col += tabwidth;
226*b8661318SDavid du Colombier 		col -= col%tabwidth;
227*b8661318SDavid du Colombier 		return col;
228*b8661318SDavid du Colombier 	}
229*b8661318SDavid du Colombier 	return col+1;
230*b8661318SDavid du Colombier }
231*b8661318SDavid du Colombier 
2323e12c5d1SDavid du Colombier void
morechars(void)2333e12c5d1SDavid du Colombier morechars(void)
2343e12c5d1SDavid du Colombier {
2353e12c5d1SDavid du Colombier 	nalloc += ALLOC_QUANTA;
2363e12c5d1SDavid du Colombier 	if((cbuf = realloc(cbuf, nalloc*sizeof(*cbuf))) == 0)
2373e12c5d1SDavid du Colombier 		error("out of memory");
2383e12c5d1SDavid du Colombier 	cbufp = cbuf+nchars-1;
2393e12c5d1SDavid du Colombier }
2403e12c5d1SDavid du Colombier 
2417dd7cddfSDavid du Colombier /*
2427dd7cddfSDavid du Colombier  * These routines discover the width of the display.
2437dd7cddfSDavid du Colombier  * It takes some work.  If we do the easy calls to the
2447dd7cddfSDavid du Colombier  * draw library, the screen flashes due to repainting
2457dd7cddfSDavid du Colombier  * when mc exits.
2467dd7cddfSDavid du Colombier  */
2477dd7cddfSDavid du Colombier 
2487dd7cddfSDavid du Colombier jmp_buf	drawjmp;
2497dd7cddfSDavid du Colombier 
2507dd7cddfSDavid du Colombier void
terror(Display *,char *)2517dd7cddfSDavid du Colombier terror(Display*, char*)
2527dd7cddfSDavid du Colombier {
2537dd7cddfSDavid du Colombier 	longjmp(drawjmp, 1);
2547dd7cddfSDavid du Colombier }
2557dd7cddfSDavid du Colombier 
2563e12c5d1SDavid du Colombier void
getwidth(void)2573e12c5d1SDavid du Colombier getwidth(void)
2583e12c5d1SDavid du Colombier {
259*b8661318SDavid du Colombier 	int n, fd;
260*b8661318SDavid du Colombier 	char buf[128], *f[10], *p;
2613e12c5d1SDavid du Colombier 
262*b8661318SDavid du Colombier 	if(access("/dev/acme", OREAD) >= 0){
263*b8661318SDavid du Colombier 		if((fd = open("/dev/acme/ctl", OREAD)) < 0)
2647dd7cddfSDavid du Colombier 			return;
265*b8661318SDavid du Colombier 		n = read(fd, buf, sizeof buf-1);
2667dd7cddfSDavid du Colombier 		close(fd);
267*b8661318SDavid du Colombier 		if(n <= 0)
2687dd7cddfSDavid du Colombier 			return;
269*b8661318SDavid du Colombier 		buf[n] = 0;
270*b8661318SDavid du Colombier 		n = tokenize(buf, f, nelem(f));
271*b8661318SDavid du Colombier 		if(n < 7)
2727dd7cddfSDavid du Colombier 			return;
273*b8661318SDavid du Colombier 		if((font = openfont(nil, f[6])) == nil)
2747dd7cddfSDavid du Colombier 			return;
275*b8661318SDavid du Colombier 		if(n >= 8)
276*b8661318SDavid du Colombier 			tabwidth = atoi(f[7]);
277*b8661318SDavid du Colombier 		else
278*b8661318SDavid du Colombier 			tabwidth = 4*stringwidth(font, "0");
279*b8661318SDavid du Colombier 		mintab = stringwidth(font, "0");
280*b8661318SDavid du Colombier 		linewidth = atoi(f[5]);
281*b8661318SDavid du Colombier 		tabflag = 1;
282*b8661318SDavid du Colombier 		return;
283*b8661318SDavid du Colombier 	}
284*b8661318SDavid du Colombier 
285*b8661318SDavid du Colombier 	if((p = getenv("font")) == nil)
286*b8661318SDavid du Colombier 		return;
287*b8661318SDavid du Colombier 	if((font = openfont(nil, p)) == nil)
288*b8661318SDavid du Colombier 		return;
289*b8661318SDavid du Colombier 	if((fd = open("/dev/window", OREAD)) < 0){
290*b8661318SDavid du Colombier 		font = nil;
291*b8661318SDavid du Colombier 		return;
292*b8661318SDavid du Colombier 	}
293*b8661318SDavid du Colombier 	n = read(fd, buf, 5*12);
294*b8661318SDavid du Colombier 	close(fd);
295*b8661318SDavid du Colombier 	if(n < 5*12){
296*b8661318SDavid du Colombier 		font = nil;
297*b8661318SDavid du Colombier 		return;
298*b8661318SDavid du Colombier 	}
299*b8661318SDavid du Colombier 	buf[n] = 0;
300*b8661318SDavid du Colombier 
3013e12c5d1SDavid du Colombier 	/* window stucture:
3023e12c5d1SDavid du Colombier 		4 bit left edge
3033e12c5d1SDavid du Colombier 		1 bit gap
3043e12c5d1SDavid du Colombier 		12 bit scrollbar
3053e12c5d1SDavid du Colombier 		4 bit gap
3063e12c5d1SDavid du Colombier 		text
3073e12c5d1SDavid du Colombier 		4 bit right edge
3083e12c5d1SDavid du Colombier 	*/
309*b8661318SDavid du Colombier 	linewidth = atoi(buf+3*12) - atoi(buf+1*12) - (4+1+12+4+4);
310*b8661318SDavid du Colombier 	mintab = stringwidth(font, "0");
311*b8661318SDavid du Colombier 	if((p = getenv("tabstop")) != nil)
312*b8661318SDavid du Colombier 		tabwidth = atoi(p)*stringwidth(font, "0");
313*b8661318SDavid du Colombier 	if(tabwidth == 0)
314*b8661318SDavid du Colombier 		tabwidth = 4*stringwidth(font, "0");
315*b8661318SDavid du Colombier 	tabflag = 1;
3163e12c5d1SDavid du Colombier }
317