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 mintab=1;
22 int colonflag=0;
23 int tabflag=0; /* -t flag turned off forever */
24 Rune *cbuf, *cbufp;
25 Rune **word;
26 int maxwidth=0;
27 int nalloc=ALLOC_QUANTA;
28 int nwalloc=WORD_ALLOC_QUANTA;
29 int nchars=0;
30 int nwords=0;
31 int tabwidth=0;
32 Font *font;
33 Biobuf bin;
34 Biobuf bout;
35
36 void getwidth(void), readbuf(int), error(char *);
37 void scanwords(void), columnate(void), morechars(void);
38 int wordwidth(Rune*, int);
39 int nexttab(int);
40
41 void
main(int argc,char * argv[])42 main(int argc, char *argv[])
43 {
44 int i;
45 int lineset;
46 int ifd;
47
48 lineset = 0;
49 Binit(&bout, 1, OWRITE);
50 while(argc > 1 && argv[1][0] == '-'){
51 --argc; argv++;
52 switch(argv[0][1]){
53 case '\0':
54 colonflag = 1;
55 break;
56 case 't':
57 tabflag = 0;
58 break;
59 default:
60 linewidth = atoi(&argv[0][1]);
61 if(linewidth <= 1)
62 linewidth = WIDTH;
63 lineset = 1;
64 break;
65 }
66 }
67 if(lineset == 0){
68 getwidth();
69 if(linewidth <= 1){
70 linewidth = WIDTH;
71 font = nil;
72 }
73 }
74
75 cbuf = cbufp = malloc(ALLOC_QUANTA*(sizeof *cbuf));
76 word = malloc(WORD_ALLOC_QUANTA*(sizeof *word));
77 if(word == 0 || cbuf == 0)
78 error("out of memory");
79 if(argc == 1)
80 readbuf(0);
81 else{
82 for(i = 1; i < argc; i++){
83 if((ifd = open(*++argv, OREAD)) == -1)
84 fprint(2, "mc: can't open %s (%r)\n", *argv);
85 else{
86 readbuf(ifd);
87 Bflush(&bin);
88 close(ifd);
89 }
90 }
91 }
92 columnate();
93 exits(0);
94 }
95 void
error(char * s)96 error(char *s)
97 {
98 fprint(2, "mc: %s\n", s);
99 exits(s);
100 }
101 void
readbuf(int fd)102 readbuf(int fd)
103 {
104 int lastwascolon = 0;
105 long c;
106 int linesiz = 0;
107
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++;
121 }
122 }
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')
130 ;
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;
139 }
140 lastwascolon = 0;
141 }
142 if(c == '\n')
143 linesiz = 0;
144 }while(c >= 0);
145 }
146 void
scanwords(void)147 scanwords(void)
148 {
149 Rune *p, *q;
150 int i, w;
151
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");
160 }
161 word[nwords++] = q;
162 p[-1] = L'\0';
163 w = wordwidth(q, p-q-1);
164 if(w > maxwidth)
165 maxwidth = w;
166 q = p;
167 }
168 }
169 }
170
171 void
columnate(void)172 columnate(void)
173 {
174 int i, j;
175 int words_per_line;
176 int nlines;
177 int col;
178 int endcol;
179
180
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 += wordwidth(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);
200 }
201 }else{
202 while(col < endcol){
203 Bputc(&bout, ' ');
204 col++;
205 }
206 }
207 }
208 }
209 Bputc(&bout, '\n');
210 }
211 }
212
213 int
wordwidth(Rune * w,int nw)214 wordwidth(Rune *w, int nw)
215 {
216 if(font)
217 return runestringnwidth(font, w, nw);
218 return nw;
219 }
220
221 int
nexttab(int col)222 nexttab(int col)
223 {
224 if(tabwidth){
225 col += tabwidth;
226 col -= col%tabwidth;
227 return col;
228 }
229 return col+1;
230 }
231
232 void
morechars(void)233 morechars(void)
234 {
235 nalloc += ALLOC_QUANTA;
236 if((cbuf = realloc(cbuf, nalloc*sizeof(*cbuf))) == 0)
237 error("out of memory");
238 cbufp = cbuf+nchars-1;
239 }
240
241 /*
242 * These routines discover the width of the display.
243 * It takes some work. If we do the easy calls to the
244 * draw library, the screen flashes due to repainting
245 * when mc exits.
246 */
247
248 jmp_buf drawjmp;
249
250 void
terror(Display *,char *)251 terror(Display*, char*)
252 {
253 longjmp(drawjmp, 1);
254 }
255
256 void
getwidth(void)257 getwidth(void)
258 {
259 int n, fd;
260 char buf[128], *f[10], *p;
261
262 if(access("/dev/acme", OREAD) >= 0){
263 if((fd = open("/dev/acme/ctl", OREAD)) < 0)
264 return;
265 n = read(fd, buf, sizeof buf-1);
266 close(fd);
267 if(n <= 0)
268 return;
269 buf[n] = 0;
270 n = tokenize(buf, f, nelem(f));
271 if(n < 7)
272 return;
273 if((font = openfont(nil, f[6])) == nil)
274 return;
275 if(n >= 8)
276 tabwidth = atoi(f[7]);
277 else
278 tabwidth = 4*stringwidth(font, "0");
279 mintab = stringwidth(font, "0");
280 linewidth = atoi(f[5]);
281 tabflag = 1;
282 return;
283 }
284
285 if((p = getenv("font")) == nil)
286 return;
287 if((font = openfont(nil, p)) == nil)
288 return;
289 if((fd = open("/dev/window", OREAD)) < 0){
290 font = nil;
291 return;
292 }
293 n = read(fd, buf, 5*12);
294 close(fd);
295 if(n < 5*12){
296 font = nil;
297 return;
298 }
299 buf[n] = 0;
300
301 /* window stucture:
302 4 bit left edge
303 1 bit gap
304 12 bit scrollbar
305 4 bit gap
306 text
307 4 bit right edge
308 */
309 linewidth = atoi(buf+3*12) - atoi(buf+1*12) - (4+1+12+4+4);
310 mintab = stringwidth(font, "0");
311 if((p = getenv("tabstop")) != nil)
312 tabwidth = atoi(p)*stringwidth(font, "0");
313 if(tabwidth == 0)
314 tabwidth = 4*stringwidth(font, "0");
315 tabflag = 1;
316 }
317