| /* | 
 |  * mc - columnate | 
 |  * | 
 |  * mc[-][-LINEWIDTH][-t][file...] | 
 |  *	- causes break on colon | 
 |  *	-LINEWIDTH sets width of line in which to columnate(default 80) | 
 |  *	-t suppresses expanding multiple blanks into tabs | 
 |  * | 
 |  */ | 
 | #include	<u.h> | 
 | #include	<sys/ioctl.h> | 
 | #include	<sys/termios.h> | 
 | #include	<libc.h> | 
 | #include	<draw.h> | 
 | #include	<bio.h> | 
 | #include	<fcall.h> | 
 | #include	<9pclient.h> | 
 | #include	<thread.h> | 
 |  | 
 | #define	WIDTH			80 | 
 | #define	TAB	4 | 
 | #define	WORD_ALLOC_QUANTA	1024 | 
 | #define	ALLOC_QUANTA		4096 | 
 |  | 
 | int wordsize(Rune*, int); | 
 | int nexttab(int); | 
 |  | 
 | int tabwid; | 
 | int mintab = 1; | 
 | int linewidth=WIDTH; | 
 | int colonflag=0; | 
 | int tabflag=0;	/* -t flag turned off forever, except in acme */ | 
 | Rune *cbuf, *cbufp; | 
 | Rune **word; | 
 | int maxwidth=0; | 
 | int nalloc=ALLOC_QUANTA; | 
 | int nwalloc=WORD_ALLOC_QUANTA; | 
 | int nchars=0; | 
 | int nwords=0; | 
 | Biobuf	bin; | 
 | Biobuf	bout; | 
 |  | 
 | void getwidth(void), readbuf(int), error(char *); | 
 | void scanwords(void), columnate(void), morechars(void); | 
 |  | 
 | void | 
 | threadmain(int argc, char *argv[]) | 
 | { | 
 | 	int i; | 
 | 	int lineset; | 
 | 	int ifd; | 
 |  | 
 | 	lineset = 0; | 
 | 	Binit(&bout, 1, OWRITE); | 
 | 	while(argc > 1 && argv[1][0] == '-'){ | 
 | 		--argc; argv++; | 
 | 		switch(argv[0][1]){ | 
 | 		case '\0': | 
 | 			colonflag = 1; | 
 | 			break; | 
 | 		case 't': | 
 | 			tabflag = 0; | 
 | 			break; | 
 | 		default: | 
 | 			linewidth = atoi(&argv[0][1]); | 
 | 			if(linewidth <= 1) | 
 | 				linewidth = WIDTH; | 
 | 			lineset = 1; | 
 | 			break; | 
 | 		} | 
 | 	} | 
 | 	if(lineset == 0) | 
 | 		getwidth(); | 
 | 	cbuf = cbufp = malloc(ALLOC_QUANTA*(sizeof *cbuf)); | 
 | 	word = malloc(WORD_ALLOC_QUANTA*(sizeof *word)); | 
 | 	if(word == 0 || cbuf == 0) | 
 | 		error("out of memory"); | 
 | 	if(argc == 1) | 
 | 		readbuf(0); | 
 | 	else{ | 
 | 		for(i = 1; i < argc; i++){ | 
 | 			if((ifd = open(*++argv, OREAD)) == -1) | 
 | 				fprint(2, "mc: can't open %s (%r)\n", *argv); | 
 | 			else{ | 
 | 				readbuf(ifd); | 
 | 				Bflush(&bin); | 
 | 				close(ifd); | 
 | 			} | 
 | 		} | 
 | 	} | 
 | 	columnate(); | 
 | 	Bflush(&bout); | 
 | 	threadexitsall(0); | 
 | } | 
 | void | 
 | error(char *s) | 
 | { | 
 | 	fprint(2, "mc: %s\n", s); | 
 | 	threadexitsall(s); | 
 | } | 
 | void | 
 | readbuf(int fd) | 
 | { | 
 | 	int lastwascolon = 0; | 
 | 	long c; | 
 | 	int linesiz = 0; | 
 |  | 
 | 	Binit(&bin, fd, OREAD); | 
 | 	do{ | 
 | 		if(nchars++ >= nalloc) | 
 | 			morechars(); | 
 | 		*cbufp++ = c = Bgetrune(&bin); | 
 | 		linesiz++; | 
 | 		if(c == '\t') { | 
 | 			cbufp[-1] = L' '; | 
 | 			while(linesiz%TAB != 0) { | 
 | 				if(nchars++ >= nalloc) | 
 | 					morechars(); | 
 | 				*cbufp++ = L' '; | 
 | 				linesiz++; | 
 | 			} | 
 | 		} | 
 | 		if(colonflag && c == ':') | 
 | 			lastwascolon++; | 
 | 		else if(lastwascolon){ | 
 | 			if(c == '\n'){ | 
 | 				--nchars; 	/* skip newline */ | 
 | 				*cbufp = L'\0'; | 
 | 				while(nchars > 0 && cbuf[--nchars] != '\n') | 
 | 					; | 
 | 				if(nchars) | 
 | 					nchars++; | 
 | 				columnate(); | 
 | 				if (nchars) | 
 | 					Bputc(&bout, '\n'); | 
 | 				Bprint(&bout, "%S", cbuf+nchars); | 
 | 				nchars = 0; | 
 | 				cbufp = cbuf; | 
 | 			} | 
 | 			lastwascolon = 0; | 
 | 		} | 
 | 		if(c == '\n') | 
 | 			linesiz = 0; | 
 | 	}while(c >= 0); | 
 | } | 
 | void | 
 | scanwords(void) | 
 | { | 
 | 	Rune *p, *q; | 
 | 	int i, w; | 
 |  | 
 | 	nwords=0; | 
 | 	maxwidth=0; | 
 | 	for(p = q = cbuf, i = 0; i < nchars; i++){ | 
 | 		if(*p++ == L'\n'){ | 
 | 			if(nwords >= nwalloc){ | 
 | 				nwalloc += WORD_ALLOC_QUANTA; | 
 | 				if((word = realloc(word, nwalloc*sizeof(*word)))==0) | 
 | 					error("out of memory"); | 
 | 			} | 
 | 			word[nwords++] = q; | 
 | 			p[-1] = L'\0'; | 
 | 			w = wordsize(q, p-q-1); | 
 | 			if(w > maxwidth) | 
 | 				maxwidth = w; | 
 | 			q = p; | 
 | 		} | 
 | 	} | 
 | } | 
 |  | 
 | void | 
 | columnate(void) | 
 | { | 
 | 	int i, j; | 
 | 	int words_per_line; | 
 | 	int nlines; | 
 | 	int col; | 
 | 	int endcol; | 
 |  | 
 |  | 
 | 	scanwords(); | 
 | 	if(nwords==0) | 
 | 		return; | 
 | 	maxwidth = nexttab(maxwidth+mintab-1); | 
 | 	words_per_line = linewidth/maxwidth; | 
 | 	if(words_per_line <= 0) | 
 | 		words_per_line = 1; | 
 | 	nlines=(nwords+words_per_line-1)/words_per_line; | 
 | 	for(i = 0; i < nlines; i++){ | 
 | 		col = endcol = 0; | 
 | 		for(j = i; j < nwords; j += nlines){ | 
 | 			endcol += maxwidth; | 
 | 			Bprint(&bout, "%S", word[j]); | 
 | 			col += wordsize(word[j], runestrlen(word[j])); | 
 | 			if(j+nlines < nwords){ | 
 | 				if(tabflag) { | 
 | 					while(col < endcol){ | 
 | 						Bputc(&bout, '\t'); | 
 | 						col = nexttab(col); | 
 | 					} | 
 | 				}else{ | 
 | 					while(col < endcol){ | 
 | 						Bputc(&bout, ' '); | 
 | 						col++; | 
 | 					} | 
 | 				} | 
 | 			} | 
 | 		} | 
 | 		Bputc(&bout, '\n'); | 
 | 	} | 
 | } | 
 |  | 
 | int | 
 | wordsize(Rune *w, int nw) | 
 | { | 
 | 	if(nw < 0) | 
 | 		abort(); | 
 | 	if(font) | 
 | 		return runestringnwidth(font, w, nw); | 
 | 	return nw; | 
 | } | 
 |  | 
 | int | 
 | nexttab(int col) | 
 | { | 
 | 	if(tabwid){ | 
 | 		col += tabwid; | 
 | 		col -= col%tabwid; | 
 | 		return col; | 
 | 	} | 
 | 	return col+1; | 
 | } | 
 |  | 
 | void | 
 | morechars(void) | 
 | { | 
 | 	nalloc += ALLOC_QUANTA; | 
 | 	if((cbuf = realloc(cbuf, nalloc*sizeof(*cbuf))) == 0) | 
 | 		error("out of memory"); | 
 | 	cbufp = cbuf+nchars-1; | 
 | } | 
 |  | 
 | /* | 
 |  * These routines discover the width of the display. | 
 |  * It takes some work.  If we do the easy calls to the | 
 |  * draw library, the screen flashes due to repainting | 
 |  * when mc exits. | 
 |  */ | 
 | int | 
 | windowrect(struct winsize *ws) | 
 | { | 
 | 	int tty; | 
 |  | 
 | 	if((tty = open("/dev/tty", OWRITE)) < 0) | 
 | 		tty = 1; | 
 |  | 
 | 	if(ioctl(tty, TIOCGWINSZ, ws) < 0){ | 
 | 		if(tty != 1) | 
 | 			close(tty); | 
 | 		return -1; | 
 | 	} | 
 | 	if(tty != 1) | 
 | 		close(tty); | 
 | 	return 0; | 
 | } | 
 |  | 
 | void | 
 | getwidth(void) | 
 | { | 
 | 	CFsys *fs; | 
 | 	char buf[500], *p, *f[10]; | 
 | 	int fd, n, nf; | 
 | 	struct winsize ws; | 
 |  | 
 | 	if((p = getenv("winid")) != nil){ | 
 | 		fs = nsmount("acme", ""); | 
 | 		if(fs == nil) | 
 | 			return; | 
 | 		snprint(buf, sizeof buf, "acme/%d/ctl", atoi(p)); | 
 | 		if((fd = fsopenfd(fs, buf, OREAD)) < 0) | 
 | 			return; | 
 | 		if((n=readn(fd, buf, sizeof buf-1)) <= 0) | 
 | 			return; | 
 | 		buf[n] = 0; | 
 | 		if((nf=tokenize(buf, f, nelem(f))) < 7) | 
 | 			return; | 
 | 		tabwid = 0; | 
 | 		if(nf >= 8 && (tabwid = atoi(f[7])) == 0) | 
 | 			return; | 
 | 		if((font = openfont(nil, f[6])) == nil) | 
 | 			return; | 
 | 		mintab = stringwidth(font, "0"); | 
 | 		if(tabwid == 0) | 
 | 			tabwid = mintab*4; | 
 | 		linewidth = atoi(f[5]); | 
 | 		tabflag = 1; | 
 | 		return; | 
 | 	} | 
 |  | 
 | 	if((p = getenv("termprog")) != nil && strcmp(p, "9term") == 0) | 
 | 	if((p = getenv("font")) != nil) | 
 | 		font = openfont(nil, p); | 
 |  | 
 | 	if(windowrect(&ws) < 0) | 
 | 		return; | 
 | 	if(ws.ws_xpixel == 0) | 
 | 		font = nil; | 
 | 	if(font){ | 
 | 		mintab = stringwidth(font, "0"); | 
 | 		if((p = getenv("tabstop")) != nil) | 
 | 			tabwid = atoi(p)*mintab; | 
 | 		else | 
 | 			tabwid = 4*mintab; | 
 | 		tabflag = 1; | 
 | 		linewidth = ws.ws_xpixel; | 
 | 	}else | 
 | 		linewidth = ws.ws_col; | 
 | } | 
 |  |