xref: /plan9/sys/src/cmd/mc.c (revision ff8c3af2f44d95267f67219afa20ba82ff6cf7e4)
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 colonflag=0;
22 int tabflag=0;	/* -t flag turned off forever */
23 Rune *cbuf, *cbufp;
24 Rune **word;
25 int maxwidth=0;
26 int nalloc=ALLOC_QUANTA;
27 int nwalloc=WORD_ALLOC_QUANTA;
28 int nchars=0;
29 int nwords=0;
30 Biobuf	bin;
31 Biobuf	bout;
32 
33 void getwidth(void), readbuf(int), error(char *);
34 void scanwords(void), columnate(void), morechars(void);
35 
36 void
37 main(int argc, char *argv[])
38 {
39 	int i;
40 	int lineset;
41 	int ifd;
42 
43 	lineset = 0;
44 	Binit(&bout, 1, OWRITE);
45 	while(argc > 1 && argv[1][0] == '-'){
46 		--argc; argv++;
47 		switch(argv[0][1]){
48 		case '\0':
49 			colonflag = 1;
50 			break;
51 		case 't':
52 			tabflag = 0;
53 			break;
54 		default:
55 			linewidth = atoi(&argv[0][1]);
56 			if(linewidth <= 1)
57 				linewidth = WIDTH;
58 			lineset = 1;
59 			break;
60 		}
61 	}
62 	if(lineset == 0)
63 		getwidth();
64 	cbuf = cbufp = malloc(ALLOC_QUANTA*(sizeof *cbuf));
65 	word = malloc(WORD_ALLOC_QUANTA*(sizeof *word));
66 	if(word == 0 || cbuf == 0)
67 		error("out of memory");
68 	if(argc == 1)
69 		readbuf(0);
70 	else{
71 		for(i = 1; i < argc; i++){
72 			if((ifd = open(*++argv, OREAD)) == -1)
73 				fprint(2, "mc: can't open %s (%r)\n", *argv);
74 			else{
75 				readbuf(ifd);
76 				Bflush(&bin);
77 				close(ifd);
78 			}
79 		}
80 	}
81 	columnate();
82 	exits(0);
83 }
84 void
85 error(char *s)
86 {
87 	fprint(2, "mc: %s\n", s);
88 	exits(s);
89 }
90 void
91 readbuf(int fd)
92 {
93 	int lastwascolon = 0;
94 	long c;
95 	int linesiz = 0;
96 
97 	Binit(&bin, fd, OREAD);
98 	do{
99 		if(nchars++ >= nalloc)
100 			morechars();
101 		*cbufp++ = c = Bgetrune(&bin);
102 		linesiz++;
103 		if(c == '\t') {
104 			cbufp[-1] = L' ';
105 			while(linesiz%TAB != 0) {
106 				if(nchars++ >= nalloc)
107 					morechars();
108 				*cbufp++ = L' ';
109 				linesiz++;
110 			}
111 		}
112 		if(colonflag && c == ':')
113 			lastwascolon++;
114 		else if(lastwascolon){
115 			if(c == '\n'){
116 				--nchars; 	/* skip newline */
117 				*cbufp = L'\0';
118 				while(nchars > 0 && cbuf[--nchars] != '\n')
119 					;
120 				if(nchars)
121 					nchars++;
122 				columnate();
123 				if (nchars)
124 					Bputc(&bout, '\n');
125 				Bprint(&bout, "%S", cbuf+nchars);
126 				nchars = 0;
127 				cbufp = cbuf;
128 			}
129 			lastwascolon = 0;
130 		}
131 		if(c == '\n')
132 			linesiz = 0;
133 	}while(c >= 0);
134 }
135 void
136 scanwords(void)
137 {
138 	Rune *p, *q;
139 	int i;
140 
141 	nwords=0;
142 	maxwidth=0;
143 	for(p = q = cbuf, i = 0; i < nchars; i++){
144 		if(*p++ == L'\n'){
145 			if(nwords >= nwalloc){
146 				nwalloc += WORD_ALLOC_QUANTA;
147 				if((word = realloc(word, nwalloc*sizeof(*word)))==0)
148 					error("out of memory");
149 			}
150 			word[nwords++] = q;
151 			p[-1] = L'\0';
152 			if(p-q > maxwidth)
153 				maxwidth = p-q;
154 			q = p;
155 		}
156 	}
157 }
158 
159 void
160 columnate(void)
161 {
162 	int i, j;
163 	int words_per_line;
164 	int nlines;
165 	int col;
166 	int endcol;
167 
168 
169 	scanwords();
170 	if(nwords==0)
171 		return;
172 	words_per_line = linewidth/maxwidth;
173 	if(words_per_line <= 0)
174 		words_per_line = 1;
175 	nlines=(nwords+words_per_line-1)/words_per_line;
176 	for(i = 0; i < nlines; i++){
177 		col = endcol = 0;
178 		for(j = i; j < nwords; j += nlines){
179 			endcol += maxwidth;
180 			Bprint(&bout, "%S", word[j]);
181 			col += word[j+1]-word[j]-1;
182 			if(j+nlines < nwords){
183 				if(tabflag) {
184 					int tabcol = (col|(TAB-1))+1;
185 					while(tabcol <= endcol){
186 						Bputc(&bout, '\t');
187 						col = tabcol;
188 						tabcol += TAB;
189 					}
190 				}
191 				while(col < endcol){
192 					Bputc(&bout, ' ');
193 					col++;
194 				}
195 			}
196 		}
197 		Bputc(&bout, '\n');
198 	}
199 }
200 
201 void
202 morechars(void)
203 {
204 	nalloc += ALLOC_QUANTA;
205 	if((cbuf = realloc(cbuf, nalloc*sizeof(*cbuf))) == 0)
206 		error("out of memory");
207 	cbufp = cbuf+nchars-1;
208 }
209 
210 /*
211  * These routines discover the width of the display.
212  * It takes some work.  If we do the easy calls to the
213  * draw library, the screen flashes due to repainting
214  * when mc exits.
215  */
216 
217 jmp_buf	drawjmp;
218 
219 void
220 terror(Display*, char*)
221 {
222 	longjmp(drawjmp, 1);
223 }
224 
225 Image*
226 window(void)
227 {
228 	int n, fd;
229 	char buf[128];
230 
231 	/* under acme, don't want to read whole screen width */
232 	if(access("/dev/acme", OREAD) == 0)
233 		return nil;
234 	display = initdisplay("/dev", "/dev", terror);
235 	if(display == nil)
236 		return nil;
237 	snprint(buf, sizeof buf, "%s/winname", display->windir);
238 	fd = open(buf, OREAD);
239 	if(fd<0 || (n=read(fd, buf, 64))<=0){
240 		return nil;
241 	}
242 	close(fd);
243 	buf[n] = 0;
244 	return namedimage(display, buf);
245 }
246 
247 void
248 getwidth(void)
249 {
250 	Image *w;
251 	int width, fd, n;
252 	char fontname[256];
253 
254 	if(setjmp(drawjmp))
255 		return;
256 
257 	w = window();
258 	if(w == nil)
259 		return;
260 	fd = open("/env/font", OREAD);
261 	if(fd < 0)
262 		return;
263 	n = read(fd, fontname, sizeof(fontname)-1);
264 	close(fd);
265 	if(n < 0)
266 		return;
267 	fontname[n] = 0;
268 	if(fontname[0] == 0)
269 		return;
270 	font = openfont(display, fontname);
271 	if(font == nil)
272 		return;
273 	width = stringwidth(font, " ");
274 	/* window stucture:
275 		4 bit left edge
276 		1 bit gap
277 		12 bit scrollbar
278 		4 bit gap
279 		text
280 		4 bit right edge
281 	*/
282 	linewidth = (Dx(w->r)-(4+1+12+4+4))/width;
283 }
284