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 }