Blob


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>
23 #define WIDTH 80
24 #define TAB 4
25 #define WORD_ALLOC_QUANTA 1024
26 #define ALLOC_QUANTA 4096
28 int wordsize(Rune*, int);
29 int nexttab(int);
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;
46 void getwidth(void), readbuf(int), error(char *);
47 void scanwords(void), columnate(void), morechars(void);
49 void
50 threadmain(int argc, char *argv[])
51 {
52 int i;
53 int lineset;
54 int ifd;
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)
101 fprint(2, "mc: %s\n", s);
102 threadexitsall(s);
104 void
105 readbuf(int fd)
107 int lastwascolon = 0;
108 long c;
109 int linesiz = 0;
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++;
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')
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;
143 lastwascolon = 0;
145 if(c == '\n')
146 linesiz = 0;
147 }while(c >= 0);
149 void
150 scanwords(void)
152 Rune *p, *q;
153 int i, w;
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");
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;
174 void
175 columnate(void)
177 int i, j;
178 int words_per_line;
179 int nlines;
180 int col;
181 int endcol;
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);
204 }else{
205 while(col < endcol){
206 Bputc(&bout, ' ');
207 col++;
212 Bputc(&bout, '\n');
216 int
217 wordsize(Rune *w, int nw)
219 if(nw < 0)
220 abort();
221 if(font)
222 return runestringnwidth(font, w, nw);
223 return nw;
226 int
227 nexttab(int col)
229 if(tabwid){
230 col += tabwid;
231 col -= col%tabwid;
232 return col;
234 return col+1;
237 void
238 morechars(void)
240 nalloc += ALLOC_QUANTA;
241 if((cbuf = realloc(cbuf, nalloc*sizeof(*cbuf))) == 0)
242 error("out of memory");
243 cbufp = cbuf+nchars-1;
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)
255 int tty;
257 if((tty = open("/dev/tty", OWRITE)) < 0)
258 tty = 1;
260 if(ioctl(tty, TIOCGWINSZ, ws) < 0){
261 if(tty != 1)
262 close(tty);
263 return -1;
265 if(tty != 1)
266 close(tty);
267 return 0;
270 void
271 getwidth(void)
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;
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;
308 if((p = getenv("termprog")) != nil && strcmp(p, "9term") == 0)
309 if((p = getenv("font")) != nil)
310 font = openfont(nil, p);
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;
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;