plan9port

fork of plan9port with libvec, libstr and libsdb
Log | Files | Refs | README | LICENSE

mc.c (6417B)


      1 /*
      2  * mc - columnate
      3  *
      4  * mc[-][-LINEWIDTH][-t][file...]
      5  *	- causes break on colon
      6  *	-LINEWIDTH sets width of line in which to columnate(default 80)
      7  *	-t suppresses expanding multiple blanks into tabs
      8  *
      9  */
     10 #include	<u.h>
     11 #include	<sys/ioctl.h>
     12 #include	<termios.h>
     13 #ifdef HAS_SYS_TERMIOS
     14 #include	<sys/termios.h>
     15 #endif
     16 #include	<libc.h>
     17 #include	<draw.h>
     18 #include	<bio.h>
     19 #include	<fcall.h>
     20 #include	<9pclient.h>
     21 #include	<thread.h>
     22 
     23 #define	WIDTH			80
     24 #define	TAB	4
     25 #define	WORD_ALLOC_QUANTA	1024
     26 #define	ALLOC_QUANTA		4096
     27 
     28 int wordsize(Rune*, int);
     29 int nexttab(int);
     30 
     31 int tabwid;
     32 int mintab = 1;
     33 int linewidth=WIDTH;
     34 int colonflag=0;
     35 int tabflag=0;	/* -t flag turned off forever, except in acme */
     36 Rune *cbuf, *cbufp;
     37 Rune **word;
     38 int maxwidth=0;
     39 int nalloc=ALLOC_QUANTA;
     40 int nwalloc=WORD_ALLOC_QUANTA;
     41 int nchars=0;
     42 int nwords=0;
     43 Biobuf	bin;
     44 Biobuf	bout;
     45 
     46 void getwidth(void), readbuf(int), error(char *);
     47 void scanwords(void), columnate(void), morechars(void);
     48 
     49 void
     50 threadmain(int argc, char *argv[])
     51 {
     52 	int i;
     53 	int lineset;
     54 	int ifd;
     55 
     56 	lineset = 0;
     57 	Binit(&bout, 1, OWRITE);
     58 	while(argc > 1 && argv[1][0] == '-'){
     59 		--argc; argv++;
     60 		switch(argv[0][1]){
     61 		case '\0':
     62 			colonflag = 1;
     63 			break;
     64 		case 't':
     65 			tabflag = 0;
     66 			break;
     67 		default:
     68 			linewidth = atoi(&argv[0][1]);
     69 			if(linewidth <= 1)
     70 				linewidth = WIDTH;
     71 			lineset = 1;
     72 			break;
     73 		}
     74 	}
     75 	if(lineset == 0)
     76 		getwidth();
     77 	cbuf = cbufp = malloc(ALLOC_QUANTA*(sizeof *cbuf));
     78 	word = malloc(WORD_ALLOC_QUANTA*(sizeof *word));
     79 	if(word == 0 || cbuf == 0)
     80 		error("out of memory");
     81 	if(argc == 1)
     82 		readbuf(0);
     83 	else{
     84 		for(i = 1; i < argc; i++){
     85 			if((ifd = open(*++argv, OREAD)) == -1)
     86 				fprint(2, "mc: can't open %s (%r)\n", *argv);
     87 			else{
     88 				readbuf(ifd);
     89 				Bflush(&bin);
     90 				close(ifd);
     91 			}
     92 		}
     93 	}
     94 	columnate();
     95 	Bflush(&bout);
     96 	threadexitsall(0);
     97 }
     98 void
     99 error(char *s)
    100 {
    101 	fprint(2, "mc: %s\n", s);
    102 	threadexitsall(s);
    103 }
    104 void
    105 readbuf(int fd)
    106 {
    107 	int lastwascolon = 0;
    108 	long c;
    109 	int linesiz = 0;
    110 
    111 	Binit(&bin, fd, OREAD);
    112 	do{
    113 		if(nchars++ >= nalloc)
    114 			morechars();
    115 		*cbufp++ = c = Bgetrune(&bin);
    116 		linesiz++;
    117 		if(c == '\t') {
    118 			cbufp[-1] = L' ';
    119 			while(linesiz%TAB != 0) {
    120 				if(nchars++ >= nalloc)
    121 					morechars();
    122 				*cbufp++ = L' ';
    123 				linesiz++;
    124 			}
    125 		}
    126 		if(colonflag && c == ':')
    127 			lastwascolon++;
    128 		else if(lastwascolon){
    129 			if(c == '\n'){
    130 				--nchars; 	/* skip newline */
    131 				*cbufp = L'\0';
    132 				while(nchars > 0 && cbuf[--nchars] != '\n')
    133 					;
    134 				if(nchars)
    135 					nchars++;
    136 				columnate();
    137 				if (nchars)
    138 					Bputc(&bout, '\n');
    139 				Bprint(&bout, "%S", cbuf+nchars);
    140 				nchars = 0;
    141 				cbufp = cbuf;
    142 			}
    143 			lastwascolon = 0;
    144 		}
    145 		if(c == '\n')
    146 			linesiz = 0;
    147 	}while(c >= 0);
    148 }
    149 void
    150 scanwords(void)
    151 {
    152 	Rune *p, *q;
    153 	int i, w;
    154 
    155 	nwords=0;
    156 	maxwidth=0;
    157 	for(p = q = cbuf, i = 0; i < nchars; i++){
    158 		if(*p++ == L'\n'){
    159 			if(nwords >= nwalloc){
    160 				nwalloc += WORD_ALLOC_QUANTA;
    161 				if((word = realloc(word, nwalloc*sizeof(*word)))==0)
    162 					error("out of memory");
    163 			}
    164 			word[nwords++] = q;
    165 			p[-1] = L'\0';
    166 			w = wordsize(q, p-q-1);
    167 			if(w > maxwidth)
    168 				maxwidth = w;
    169 			q = p;
    170 		}
    171 	}
    172 }
    173 
    174 void
    175 columnate(void)
    176 {
    177 	int i, j;
    178 	int words_per_line;
    179 	int nlines;
    180 	int col;
    181 	int endcol;
    182 
    183 
    184 	scanwords();
    185 	if(nwords==0)
    186 		return;
    187 	maxwidth = nexttab(maxwidth+mintab-1);
    188 	words_per_line = linewidth/maxwidth;
    189 	if(words_per_line <= 0)
    190 		words_per_line = 1;
    191 	nlines=(nwords+words_per_line-1)/words_per_line;
    192 	for(i = 0; i < nlines; i++){
    193 		col = endcol = 0;
    194 		for(j = i; j < nwords; j += nlines){
    195 			endcol += maxwidth;
    196 			Bprint(&bout, "%S", word[j]);
    197 			col += wordsize(word[j], runestrlen(word[j]));
    198 			if(j+nlines < nwords){
    199 				if(tabflag) {
    200 					while(col < endcol){
    201 						Bputc(&bout, '\t');
    202 						col = nexttab(col);
    203 					}
    204 				}else{
    205 					while(col < endcol){
    206 						Bputc(&bout, ' ');
    207 						col++;
    208 					}
    209 				}
    210 			}
    211 		}
    212 		Bputc(&bout, '\n');
    213 	}
    214 }
    215 
    216 int
    217 wordsize(Rune *w, int nw)
    218 {
    219 	if(nw < 0)
    220 		abort();
    221 	if(font)
    222 		return runestringnwidth(font, w, nw);
    223 	return nw;
    224 }
    225 
    226 int
    227 nexttab(int col)
    228 {
    229 	if(tabwid){
    230 		col += tabwid;
    231 		col -= col%tabwid;
    232 		return col;
    233 	}
    234 	return col+1;
    235 }
    236 
    237 void
    238 morechars(void)
    239 {
    240 	nalloc += ALLOC_QUANTA;
    241 	if((cbuf = realloc(cbuf, nalloc*sizeof(*cbuf))) == 0)
    242 		error("out of memory");
    243 	cbufp = cbuf+nchars-1;
    244 }
    245 
    246 /*
    247  * These routines discover the width of the display.
    248  * It takes some work.  If we do the easy calls to the
    249  * draw library, the screen flashes due to repainting
    250  * when mc exits.
    251  */
    252 int
    253 windowrect(struct winsize *ws)
    254 {
    255 	int tty;
    256 
    257 	if((tty = open("/dev/tty", OWRITE)) < 0)
    258 		tty = 1;
    259 
    260 	if(ioctl(tty, TIOCGWINSZ, ws) < 0){
    261 		if(tty != 1)
    262 			close(tty);
    263 		return -1;
    264 	}
    265 	if(tty != 1)
    266 		close(tty);
    267 	return 0;
    268 }
    269 
    270 void
    271 getwidth(void)
    272 {
    273 	CFsys *fs;
    274 	char buf[500], *p, *q, *f[10], *fname;
    275 	int fd, n, nf, scale;
    276 	struct winsize ws;
    277 	Font *f1;
    278 
    279 	if((p = getenv("winid")) != nil){
    280 		fs = nsmount("acme", "");
    281 		if(fs == nil)
    282 			return;
    283 		snprint(buf, sizeof buf, "acme/%d/ctl", atoi(p));
    284 		if((fd = fsopenfd(fs, buf, OREAD)) < 0)
    285 			return;
    286 		if((n=readn(fd, buf, sizeof buf-1)) <= 0)
    287 			return;
    288 		buf[n] = 0;
    289 		if((nf=tokenize(buf, f, nelem(f))) < 7)
    290 			return;
    291 		// hidpi font in stringwidth(3) will call scalesubfont,
    292 		// which aborts in bytesperline, due to unknow depth,
    293 		// without initdraw.  We scale by ourselves.
    294 		scale = parsefontscale(f[6], &fname);
    295 		tabwid = 0;
    296 		if(nf >= 8 && (tabwid = atoi(f[7])/scale) == 0)
    297 			return;
    298 		if((font = openfont(nil, fname)) == nil)
    299 			return;
    300 		mintab = stringwidth(font, "0");
    301 		if(tabwid == 0)
    302 			tabwid = mintab*4;
    303 		linewidth = atoi(f[5]) / scale;
    304 		tabflag = 1;
    305 		return;
    306 	}
    307 
    308 	if((p = getenv("termprog")) != nil && strcmp(p, "9term") == 0)
    309 	if((p = getenv("font")) != nil)
    310 		font = openfont(nil, p);
    311 
    312 	if(windowrect(&ws) < 0)
    313 		return;
    314 	if(ws.ws_xpixel == 0)
    315 		font = nil;
    316 	if(font){
    317 		// 9term leaves "is this a hidpi display" in the low bit of the ypixel height.
    318 		if(ws.ws_ypixel&1) {
    319 			// need hidpi font.
    320 			// loadhifpi creates a font that crashes in stringwidth,
    321 			// for reasons i don't understand.
    322 			// do it ourselves
    323 			p = getenv("font");
    324 			q = strchr(p, ',');
    325 			f1 = nil;
    326 			if(q != nil)
    327 				f1 = openfont(nil, q+1);
    328 			if(f1 != nil)
    329 				font = f1;
    330 			else
    331 				ws.ws_xpixel /= 2;
    332 		}
    333 		mintab = stringwidth(font, "0");
    334 		if((p = getenv("tabstop")) != nil)
    335 			tabwid = atoi(p)*mintab;
    336 		else
    337 			tabwid = 4*mintab;
    338 		tabflag = 1;
    339 		linewidth = ws.ws_xpixel;
    340 	}else
    341 		linewidth = ws.ws_col;
    342 }