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 <sys/termios.h>
13 #include <libc.h>
14 #include <draw.h>
15 #include <bio.h>
16 #include <fcall.h>
17 #include <9pclient.h>
18 #include <thread.h>
20 #define WIDTH 80
21 #define TAB 4
22 #define WORD_ALLOC_QUANTA 1024
23 #define ALLOC_QUANTA 4096
25 int wordsize(Rune*, int);
26 int nexttab(int);
28 int tabwid;
29 int mintab = 1;
30 int linewidth=WIDTH;
31 int colonflag=0;
32 int tabflag=0; /* -t flag turned off forever, except in acme */
33 Rune *cbuf, *cbufp;
34 Rune **word;
35 int maxwidth=0;
36 int nalloc=ALLOC_QUANTA;
37 int nwalloc=WORD_ALLOC_QUANTA;
38 int nchars=0;
39 int nwords=0;
40 Biobuf bin;
41 Biobuf bout;
43 void getwidth(void), readbuf(int), error(char *);
44 void scanwords(void), columnate(void), morechars(void);
46 void
47 threadmain(int argc, char *argv[])
48 {
49 int i;
50 int lineset;
51 int ifd;
53 lineset = 0;
54 Binit(&bout, 1, OWRITE);
55 while(argc > 1 && argv[1][0] == '-'){
56 --argc; argv++;
57 switch(argv[0][1]){
58 case '\0':
59 colonflag = 1;
60 break;
61 case 't':
62 tabflag = 0;
63 break;
64 default:
65 linewidth = atoi(&argv[0][1]);
66 if(linewidth <= 1)
67 linewidth = WIDTH;
68 lineset = 1;
69 break;
70 }
71 }
72 if(lineset == 0)
73 getwidth();
74 cbuf = cbufp = malloc(ALLOC_QUANTA*(sizeof *cbuf));
75 word = malloc(WORD_ALLOC_QUANTA*(sizeof *word));
76 if(word == 0 || cbuf == 0)
77 error("out of memory");
78 if(argc == 1)
79 readbuf(0);
80 else{
81 for(i = 1; i < argc; i++){
82 if((ifd = open(*++argv, OREAD)) == -1)
83 fprint(2, "mc: can't open %s (%r)\n", *argv);
84 else{
85 readbuf(ifd);
86 Bflush(&bin);
87 close(ifd);
88 }
89 }
90 }
91 columnate();
92 Bflush(&bout);
93 threadexitsall(0);
94 }
95 void
96 error(char *s)
97 {
98 fprint(2, "mc: %s\n", s);
99 threadexitsall(s);
101 void
102 readbuf(int fd)
104 int lastwascolon = 0;
105 long c;
106 int linesiz = 0;
108 Binit(&bin, fd, OREAD);
109 do{
110 if(nchars++ >= nalloc)
111 morechars();
112 *cbufp++ = c = Bgetrune(&bin);
113 linesiz++;
114 if(c == '\t') {
115 cbufp[-1] = L' ';
116 while(linesiz%TAB != 0) {
117 if(nchars++ >= nalloc)
118 morechars();
119 *cbufp++ = L' ';
120 linesiz++;
123 if(colonflag && c == ':')
124 lastwascolon++;
125 else if(lastwascolon){
126 if(c == '\n'){
127 --nchars; /* skip newline */
128 *cbufp = L'\0';
129 while(nchars > 0 && cbuf[--nchars] != '\n')
131 if(nchars)
132 nchars++;
133 columnate();
134 if (nchars)
135 Bputc(&bout, '\n');
136 Bprint(&bout, "%S", cbuf+nchars);
137 nchars = 0;
138 cbufp = cbuf;
140 lastwascolon = 0;
142 if(c == '\n')
143 linesiz = 0;
144 }while(c >= 0);
146 void
147 scanwords(void)
149 Rune *p, *q;
150 int i, w;
152 nwords=0;
153 maxwidth=0;
154 for(p = q = cbuf, i = 0; i < nchars; i++){
155 if(*p++ == L'\n'){
156 if(nwords >= nwalloc){
157 nwalloc += WORD_ALLOC_QUANTA;
158 if((word = realloc(word, nwalloc*sizeof(*word)))==0)
159 error("out of memory");
161 word[nwords++] = q;
162 p[-1] = L'\0';
163 w = wordsize(q, p-q-1);
164 if(w > maxwidth)
165 maxwidth = w;
166 q = p;
171 void
172 columnate(void)
174 int i, j;
175 int words_per_line;
176 int nlines;
177 int col;
178 int endcol;
181 scanwords();
182 if(nwords==0)
183 return;
184 maxwidth = nexttab(maxwidth+mintab-1);
185 words_per_line = linewidth/maxwidth;
186 if(words_per_line <= 0)
187 words_per_line = 1;
188 nlines=(nwords+words_per_line-1)/words_per_line;
189 for(i = 0; i < nlines; i++){
190 col = endcol = 0;
191 for(j = i; j < nwords; j += nlines){
192 endcol += maxwidth;
193 Bprint(&bout, "%S", word[j]);
194 col += wordsize(word[j], runestrlen(word[j]));
195 if(j+nlines < nwords){
196 if(tabflag) {
197 while(col < endcol){
198 Bputc(&bout, '\t');
199 col = nexttab(col);
201 }else{
202 while(col < endcol){
203 Bputc(&bout, ' ');
204 col++;
209 Bputc(&bout, '\n');
213 int
214 wordsize(Rune *w, int nw)
216 if(nw < 0)
217 abort();
218 if(font)
219 return runestringnwidth(font, w, nw);
220 return nw;
223 int
224 nexttab(int col)
226 if(tabwid){
227 col += tabwid;
228 col -= col%tabwid;
229 return col;
231 return col+1;
234 void
235 morechars(void)
237 nalloc += ALLOC_QUANTA;
238 if((cbuf = realloc(cbuf, nalloc*sizeof(*cbuf))) == 0)
239 error("out of memory");
240 cbufp = cbuf+nchars-1;
243 /*
244 * These routines discover the width of the display.
245 * It takes some work. If we do the easy calls to the
246 * draw library, the screen flashes due to repainting
247 * when mc exits.
248 */
249 int
250 windowrect(struct winsize *ws)
252 int tty;
254 if((tty = open("/dev/tty", OWRITE)) < 0)
255 tty = 1;
257 if(ioctl(tty, TIOCGWINSZ, ws) < 0){
258 if(tty != 1)
259 close(tty);
260 return -1;
262 if(tty != 1)
263 close(tty);
264 return 0;
267 void
268 getwidth(void)
270 CFsys *fs;
271 char buf[500], *p, *f[10];
272 int fd, n, nf;
273 struct winsize ws;
275 if((p = getenv("winid")) != nil){
276 fs = nsmount("acme", "");
277 if(fs == nil)
278 return;
279 snprint(buf, sizeof buf, "acme/%d/ctl", atoi(p));
280 if((fd = fsopenfd(fs, buf, OREAD)) < 0)
281 return;
282 if((n=readn(fd, buf, sizeof buf-1)) <= 0)
283 return;
284 buf[n] = 0;
285 if((nf=tokenize(buf, f, nelem(f))) < 7)
286 return;
287 tabwid = 0;
288 if(nf >= 8 && (tabwid = atoi(f[7])) == 0)
289 return;
290 if((font = openfont(nil, f[6])) == nil)
291 return;
292 mintab = stringwidth(font, "0");
293 if(tabwid == 0)
294 tabwid = mintab*4;
295 linewidth = atoi(f[5]);
296 tabflag = 1;
297 return;
300 if((p = getenv("TERM")) != nil && strcmp(p, "9term") == 0)
301 if((p = getenv("font")) != nil)
302 font = openfont(nil, p);
304 if(windowrect(&ws) < 0)
305 return;
306 if(ws.ws_xpixel == 0)
307 font = nil;
308 if(font){
309 mintab = stringwidth(font, "0");
310 if((p = getenv("tabstop")) != nil)
311 tabwid = atoi(p)*mintab;
312 else
313 tabwid = 4*mintab;
314 tabflag = 1;
315 linewidth = ws.ws_xpixel;
316 }else
317 linewidth = ws.ws_col;