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 <libc.h> 12 #include <draw.h> 13 #include <bio.h> 14 15 #define WIDTH 80 16 #define TAB 4 17 #define WORD_ALLOC_QUANTA 1024 18 #define ALLOC_QUANTA 4096 19 20 int linewidth=WIDTH; 21 int colonflag=0; 22 int tabflag=0; /* -t flag turned off forever */ 23 Rune *cbuf, *cbufp; 24 Rune **word; 25 int maxwidth=0; 26 int nalloc=ALLOC_QUANTA; 27 int nwalloc=WORD_ALLOC_QUANTA; 28 int nchars=0; 29 int nwords=0; 30 Biobuf bin; 31 Biobuf bout; 32 33 void getwidth(void), readbuf(int), error(char *); 34 void scanwords(void), columnate(void), morechars(void); 35 36 void 37 main(int argc, char *argv[]) 38 { 39 int i; 40 int lineset; 41 int ifd; 42 43 lineset = 0; 44 Binit(&bout, 1, OWRITE); 45 while(argc > 1 && argv[1][0] == '-'){ 46 --argc; argv++; 47 switch(argv[0][1]){ 48 case '\0': 49 colonflag = 1; 50 break; 51 case 't': 52 tabflag = 0; 53 break; 54 default: 55 linewidth = atoi(&argv[0][1]); 56 if(linewidth <= 1) 57 linewidth = WIDTH; 58 lineset = 1; 59 break; 60 } 61 } 62 if(lineset == 0) 63 getwidth(); 64 cbuf = cbufp = malloc(ALLOC_QUANTA*(sizeof *cbuf)); 65 word = malloc(WORD_ALLOC_QUANTA*(sizeof *word)); 66 if(word == 0 || cbuf == 0) 67 error("out of memory"); 68 if(argc == 1) 69 readbuf(0); 70 else{ 71 for(i = 1; i < argc; i++){ 72 if((ifd = open(*++argv, OREAD)) == -1) 73 fprint(2, "mc: can't open %s (%r)\n", *argv); 74 else{ 75 readbuf(ifd); 76 Bflush(&bin); 77 close(ifd); 78 } 79 } 80 } 81 columnate(); 82 exits(0); 83 } 84 void 85 error(char *s) 86 { 87 fprint(2, "mc: %s\n", s); 88 exits(s); 89 } 90 void 91 readbuf(int fd) 92 { 93 int lastwascolon = 0; 94 long c; 95 int linesiz = 0; 96 97 Binit(&bin, fd, OREAD); 98 do{ 99 if(nchars++ >= nalloc) 100 morechars(); 101 *cbufp++ = c = Bgetrune(&bin); 102 linesiz++; 103 if(c == '\t') { 104 cbufp[-1] = L' '; 105 while(linesiz%TAB != 0) { 106 if(nchars++ >= nalloc) 107 morechars(); 108 *cbufp++ = L' '; 109 linesiz++; 110 } 111 } 112 if(colonflag && c == ':') 113 lastwascolon++; 114 else if(lastwascolon){ 115 if(c == '\n'){ 116 --nchars; /* skip newline */ 117 *cbufp = L'\0'; 118 while(nchars > 0 && cbuf[--nchars] != '\n') 119 ; 120 if(nchars) 121 nchars++; 122 columnate(); 123 if (nchars) 124 Bputc(&bout, '\n'); 125 Bprint(&bout, "%S", cbuf+nchars); 126 nchars = 0; 127 cbufp = cbuf; 128 } 129 lastwascolon = 0; 130 } 131 if(c == '\n') 132 linesiz = 0; 133 }while(c >= 0); 134 } 135 void 136 scanwords(void) 137 { 138 Rune *p, *q; 139 int i; 140 141 nwords=0; 142 maxwidth=0; 143 for(p = q = cbuf, i = 0; i < nchars; i++){ 144 if(*p++ == L'\n'){ 145 if(nwords >= nwalloc){ 146 nwalloc += WORD_ALLOC_QUANTA; 147 if((word = realloc(word, nwalloc*sizeof(*word)))==0) 148 error("out of memory"); 149 } 150 word[nwords++] = q; 151 p[-1] = L'\0'; 152 if(p-q > maxwidth) 153 maxwidth = p-q; 154 q = p; 155 } 156 } 157 } 158 159 void 160 columnate(void) 161 { 162 int i, j; 163 int words_per_line; 164 int nlines; 165 int col; 166 int endcol; 167 168 169 scanwords(); 170 if(nwords==0) 171 return; 172 words_per_line = linewidth/maxwidth; 173 if(words_per_line <= 0) 174 words_per_line = 1; 175 nlines=(nwords+words_per_line-1)/words_per_line; 176 for(i = 0; i < nlines; i++){ 177 col = endcol = 0; 178 for(j = i; j < nwords; j += nlines){ 179 endcol += maxwidth; 180 Bprint(&bout, "%S", word[j]); 181 col += word[j+1]-word[j]-1; 182 if(j+nlines < nwords){ 183 if(tabflag) { 184 int tabcol = (col|(TAB-1))+1; 185 while(tabcol <= endcol){ 186 Bputc(&bout, '\t'); 187 col = tabcol; 188 tabcol += TAB; 189 } 190 } 191 while(col < endcol){ 192 Bputc(&bout, ' '); 193 col++; 194 } 195 } 196 } 197 Bputc(&bout, '\n'); 198 } 199 } 200 201 void 202 morechars(void) 203 { 204 nalloc += ALLOC_QUANTA; 205 if((cbuf = realloc(cbuf, nalloc*sizeof(*cbuf))) == 0) 206 error("out of memory"); 207 cbufp = cbuf+nchars-1; 208 } 209 210 /* 211 * These routines discover the width of the display. 212 * It takes some work. If we do the easy calls to the 213 * draw library, the screen flashes due to repainting 214 * when mc exits. 215 */ 216 217 jmp_buf drawjmp; 218 219 void 220 terror(Display*, char*) 221 { 222 longjmp(drawjmp, 1); 223 } 224 225 Image* 226 window(void) 227 { 228 int n, fd; 229 char buf[128]; 230 231 /* under acme, don't want to read whole screen width */ 232 if(access("/dev/acme", OREAD) == 0) 233 return nil; 234 display = initdisplay("/dev", "/dev", terror); 235 if(display == nil) 236 return nil; 237 snprint(buf, sizeof buf, "%s/winname", display->windir); 238 fd = open(buf, OREAD); 239 if(fd<0 || (n=read(fd, buf, 64))<=0){ 240 return nil; 241 } 242 close(fd); 243 buf[n] = 0; 244 return namedimage(display, buf); 245 } 246 247 void 248 getwidth(void) 249 { 250 Image *w; 251 int width, fd, n; 252 char fontname[256]; 253 254 if(setjmp(drawjmp)) 255 return; 256 257 w = window(); 258 if(w == nil) 259 return; 260 fd = open("/env/font", OREAD); 261 if(fd < 0) 262 return; 263 n = read(fd, fontname, sizeof(fontname)-1); 264 close(fd); 265 if(n < 0) 266 return; 267 fontname[n] = 0; 268 if(fontname[0] == 0) 269 return; 270 font = openfont(display, fontname); 271 if(font == nil) 272 return; 273 width = stringwidth(font, " "); 274 /* window stucture: 275 4 bit left edge 276 1 bit gap 277 12 bit scrollbar 278 4 bit gap 279 text 280 4 bit right edge 281 */ 282 linewidth = (Dx(w->r)-(4+1+12+4+4))/width; 283 } 284