xref: /plan9/sys/src/cmd/mc.c (revision 219b2ee8daee37f4aad58d63f21287faa8e4ffdc)
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	<libg.h>
13 #include	<bio.h>
14 
15 #define WIDTH			80
16 #define WORD_ALLOC_QUANTA	1024
17 #define ALLOC_QUANTA		4096
18 
19 int linewidth=WIDTH;
20 int colonflag=0;
21 int tabflag=0;	/* -t flag turned off forever */
22 Rune *cbuf, *cbufp;
23 Rune **word;
24 int maxwidth=0;
25 int nalloc=ALLOC_QUANTA;
26 int nwalloc=WORD_ALLOC_QUANTA;
27 int nchars=0;
28 int nwords=0;
29 Biobuf	bin;
30 Biobuf	bout;
31 
32 void getwidth(void), readbuf(int), error(char *);
33 void scanwords(void), columnate(void), morechars(void);
34 
35 void
36 main(int argc, char *argv[])
37 {
38 	int i;
39 	int lineset;
40 	int ifd;
41 
42 	lineset = 0;
43 	Binit(&bout, 1, OWRITE);
44 	while(argc > 1 && argv[1][0] == '-'){
45 		--argc; argv++;
46 		switch(argv[0][1]){
47 		case '\0':
48 			colonflag = 1;
49 			break;
50 		case 't':
51 			tabflag = 0;
52 			break;
53 		default:
54 			linewidth = atoi(&argv[0][1]);
55 			if(linewidth <= 1)
56 				linewidth = WIDTH;
57 			lineset = 1;
58 			break;
59 		}
60 	}
61 	if(lineset == 0)
62 		getwidth();
63 	cbuf = cbufp = malloc(ALLOC_QUANTA*(sizeof *cbuf));
64 	word = malloc(WORD_ALLOC_QUANTA*(sizeof *word));
65 	if(word == 0 || cbuf == 0)
66 		error("out of memory");
67 	if(argc == 1)
68 		readbuf(0);
69 	else{
70 		for(i = 1; i < argc; i++){
71 			if((ifd = open(*++argv, OREAD)) == -1)
72 				fprint(2, "mc: can't open %s (%r)\n", *argv);
73 			else{
74 				readbuf(ifd);
75 				Bflush(&bin);
76 				close(ifd);
77 			}
78 		}
79 	}
80 	columnate();
81 	exits(0);
82 }
83 void
84 error(char *s)
85 {
86 	fprint(2, "mc: %s\n", s);
87 	exits(s);
88 }
89 void
90 readbuf(int fd)
91 {
92 	int lastwascolon = 0;
93 	long c;
94 	int linesiz = 0;
95 
96 	Binit(&bin, fd, OREAD);
97 	do{
98 		if(nchars++ >= nalloc)
99 			morechars();
100 		*cbufp++ = c = Bgetrune(&bin);
101 		linesiz++;
102 		if(c == '\t') {
103 			cbufp[-1] = L' ';
104 			while(linesiz%8 != 0) {
105 				if(nchars++ >= nalloc)
106 					morechars();
107 				*cbufp++ = L' ';
108 				linesiz++;
109 			}
110 		}
111 		if(colonflag && c == ':')
112 			lastwascolon++;
113 		else if(lastwascolon){
114 			if(c == '\n'){
115 				--nchars; 	/* skip newline */
116 				*cbufp = L'\0';
117 				while(nchars > 0 && cbuf[--nchars] != '\n')
118 					;
119 				if(nchars)
120 					nchars++;
121 				columnate();
122 				if (nchars)
123 					BPUTC(&bout, '\n');
124 				Bprint(&bout, "%S", cbuf+nchars);
125 				nchars = 0;
126 				cbufp = cbuf;
127 			}
128 			lastwascolon = 0;
129 		}
130 		if(c == '\n')
131 			linesiz = 0;
132 	}while(c >= 0);
133 }
134 void
135 scanwords(void)
136 {
137 	Rune *p, *q;
138 	int i;
139 
140 	nwords=0;
141 	maxwidth=0;
142 	for(p = q = cbuf, i = 0; i < nchars; i++){
143 		if(*p++ == L'\n'){
144 			if(nwords >= nwalloc){
145 				nwalloc += WORD_ALLOC_QUANTA;
146 				if((word = realloc(word, nwalloc*sizeof(*word)))==0)
147 					error("out of memory");
148 			}
149 			word[nwords++] = q;
150 			p[-1] = L'\0';
151 			if(p-q > maxwidth)
152 				maxwidth = p-q;
153 			q = p;
154 		}
155 	}
156 }
157 
158 void
159 columnate(void)
160 {
161 	int i, j;
162 	int words_per_line;
163 	int nlines;
164 	int col;
165 	int endcol;
166 
167 
168 	scanwords();
169 	if(nwords==0)
170 		return;
171 	words_per_line = linewidth/maxwidth;
172 	if(words_per_line <= 0)
173 		words_per_line = 1;
174 	nlines=(nwords+words_per_line-1)/words_per_line;
175 	for(i = 0; i < nlines; i++){
176 		col = endcol = 0;
177 		for(j = i; j < nwords; j += nlines){
178 			endcol += maxwidth;
179 			Bprint(&bout, "%S", word[j]);
180 			col += word[j+1]-word[j]-1;
181 			if(j+nlines < nwords){
182 				if(tabflag) {
183 					int tabcol = (col|07)+1;
184 					while(tabcol <= endcol){
185 						BPUTC(&bout, '\t');
186 						col = tabcol;
187 						tabcol += 8;
188 					}
189 				}
190 				while(col < endcol){
191 					BPUTC(&bout, ' ');
192 					col++;
193 				}
194 			}
195 		}
196 		BPUTC(&bout, '\n');
197 	}
198 }
199 
200 void
201 morechars(void)
202 {
203 	nalloc += ALLOC_QUANTA;
204 	if((cbuf = realloc(cbuf, nalloc*sizeof(*cbuf))) == 0)
205 		error("out of memory");
206 	cbufp = cbuf+nchars-1;
207 }
208 
209 void
210 getwidth(void)
211 {
212 	Rectangle r;
213 	char buf[5*12];
214 	int fd, width;
215 
216 		/* window stucture:
217 			4 bit left edge
218 			1 bit gap
219 			12 bit scrollbar
220 			4 bit gap
221 			text
222 			4 bit right edge
223 		*/
224 	fd = open("/dev/window", OREAD);
225 	if(fd < 0)
226 		fd = open("/dev/screen", OREAD);
227 	if(fd < 0)
228 		return;
229 	if(read(fd, buf, sizeof buf) != sizeof buf){
230 		close(fd);
231 		return;
232 	}
233 	close(fd);
234 	r.min.x = atoi(buf+1*12);
235 	r.min.y = atoi(buf+2*12);
236 	r.max.x = atoi(buf+3*12);
237 	r.max.y = atoi(buf+4*12);
238 	fd = open("/dev/bitblt", ORDWR);
239 	if(fd < 0)
240 		width = 9;
241 	else{
242 		close(fd);
243 		binit(0, 0, 0);
244 		width = charwidth(font, ' ');
245 		bclose();
246 	}
247 	linewidth = (r.max.x-r.min.x-(4+1+12+4+4))/width;
248 }
249