xref: /plan9/sys/src/cmd/mc.c (revision b8661318e8f82fa2a6c406ca28596a6aef982bd2)
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