]> git.lizzy.rs Git - plan9front.git/blob - sys/src/cmd/mc.c
cpu: make -p work with explicit service in dialstring (same as import)
[plan9front.git] / sys / src / cmd / mc.c
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
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
96 error(char *s)
97 {
98         fprint(2, "mc: %s\n", s);
99         exits(s);
100 }
101 void
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
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
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
214 wordwidth(Rune *w, int nw)
215 {
216         if(font)
217                 return runestringnwidth(font, w, nw);
218         return nw;
219 }
220
221 int
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
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
251 terror(Display*, char*)
252 {
253         longjmp(drawjmp, 1);
254 }
255
256 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 }