xref: /plan9/sys/src/cmd/col.c (revision c038c065b3262a4b30cb86a2f0f52bffd3cc20c0)
1 /* col - eliminate reverse line feeds */
2 #include <u.h>
3 #include <libc.h>
4 #include <ctype.h>
5 #include <bio.h>
6 
7 enum {
8 	ESC	= '\033',
9 	RLF	= '\013',
10 
11 	PL	= 256,
12 	LINELN	= 800,
13 
14 	Tabstop	= 8,		/* must be power of 2 */
15 };
16 
17 static int bflag, xflag, fflag;
18 static int cp, lp;
19 static int half;
20 static int ll, llh, mustwr;
21 static int pcp = 0;
22 
23 static char *page[PL];
24 static char *line;
25 static char lbuff[LINELN];
26 static Biobuf bin, bout;
27 
28 void	emit(char *s, int lineno);
29 void	incr(void), decr(void);
30 void	outc(Rune);
31 
32 static void
usage(void)33 usage(void)
34 {
35 	fprint(2, "usage: %s [-bfx]\n", argv0);
36 	exits("usage");
37 }
38 
39 void
main(int argc,char ** argv)40 main(int argc, char **argv)
41 {
42 	int i, lno;
43 	long ch;
44 	Rune c;
45 
46 	ARGBEGIN{
47 	case 'b':
48 		bflag++;
49 		break;
50 	case 'f':
51 		fflag++;
52 		break;
53 	case 'x':
54 		xflag++;
55 		break;
56 	default:
57 		usage();
58 	}ARGEND;
59 
60 	for (ll=0; ll < PL; ll++)
61 		page[ll] = nil;
62 
63 	cp = 0;
64 	ll = 0;
65 	mustwr = PL;
66 	line = lbuff;
67 
68 	Binit(&bin, 0, OREAD);
69 	Binit(&bout, 1, OWRITE);
70 	while ((ch = Bgetrune(&bin)) != Beof) {
71 		c = ch;
72 		switch (c) {
73 		case '\n':
74 			incr();
75 			incr();
76 			cp = 0;
77 			break;
78 
79 		case '\0':
80 			break;
81 
82 		case ESC:
83 			c = Bgetrune(&bin);
84 			switch (c) {
85 			case '7':	/* reverse full line feed */
86 				decr();
87 				decr();
88 				break;
89 
90 			case '8':	/* reverse half line feed */
91 				if (fflag)
92 					decr();
93 				else
94 					if (--half < -1) {
95 						decr();
96 						decr();
97 						half += 2;
98 					}
99 				break;
100 
101 			case '9':	/* forward half line feed */
102 				if (fflag)
103 					incr();
104 				else
105 					if (++half > 0) {
106 						incr();
107 						incr();
108 						half -= 2;
109 					}
110 				break;
111 			}
112 			break;
113 
114 		case RLF:
115 			decr();
116 			decr();
117 			break;
118 
119 		case '\r':
120 			cp = 0;
121 			break;
122 
123 		case '\t':
124 			cp = (cp + Tabstop) & -Tabstop;
125 			break;
126 
127 		case '\b':
128 			if (cp > 0)
129 				cp--;
130 			break;
131 
132 		case ' ':
133 			cp++;
134 			break;
135 
136 		default:
137 			if (!isascii(c) || isprint(c)) {
138 				outc(c);
139 				cp++;
140 			}
141 			break;
142 		}
143 	}
144 
145 	for (i=0; i < PL; i++) {
146 		lno = (mustwr+i) % PL;
147 		if (page[lno] != 0)
148 			emit(page[lno], mustwr+i-PL);
149 	}
150 	emit(" ", (llh + 1) & -2);
151 	exits(0);
152 }
153 
154 void
outc(Rune c)155 outc(Rune c)
156 {
157 	if (lp > cp) {
158 		line = lbuff;
159 		lp = 0;
160 	}
161 
162 	while (lp < cp) {
163 		switch (*line) {
164 		case '\0':
165 			*line = ' ';
166 			lp++;
167 			break;
168 		case '\b':
169 			lp--;
170 			break;
171 		default:
172 			lp++;
173 			break;
174 		}
175 		line++;
176 	}
177 	while (*line == '\b')
178 		line += 2;
179 	if (bflag || *line == '\0' || *line == ' ')
180 		cp += runetochar(line, &c) - 1;
181 	else {
182 		char c1, c2, c3;
183 
184 		c1 = *++line;
185 		*line++ = '\b';
186 		c2 = *line;
187 		*line++ = c;
188 		while (c1) {
189 			c3 = *line;
190 			*line++ = c1;
191 			c1 = c2;
192 			c2 = c3;
193 		}
194 		lp = 0;
195 		line = lbuff;
196 	}
197 }
198 
199 void
store(int lno)200 store(int lno)
201 {
202 	lno %= PL;
203 	if (page[lno] != nil)
204 		free(page[lno]);
205 	page[lno] = malloc((unsigned)strlen(lbuff) + 2);
206 	if (page[lno] == nil)
207 		sysfatal("out of memory");
208 	strcpy(page[lno], lbuff);
209 }
210 
211 void
fetch(int lno)212 fetch(int lno)
213 {
214 	char *p;
215 
216 	lno %= PL;
217 	p = lbuff;
218 	while (*p)
219 		*p++ = '\0';
220 	line = lbuff;
221 	lp = 0;
222 	if (page[lno])
223 		strcpy(line, page[lno]);
224 }
225 
226 void
emit(char * s,int lineno)227 emit(char *s, int lineno)
228 {
229 	int ncp;
230 	char *p;
231 	static int cline = 0;
232 
233 	if (*s) {
234 		while (cline < lineno - 1) {
235 			Bputc(&bout, '\n');
236 			pcp = 0;
237 			cline += 2;
238 		}
239 		if (cline != lineno) {
240 			Bputc(&bout, ESC);
241 			Bputc(&bout, '9');
242 			cline++;
243 		}
244 		if (pcp)
245 			Bputc(&bout, '\r');
246 		pcp = 0;
247 		p = s;
248 		while (*p) {
249 			ncp = pcp;
250 			while (*p++ == ' ')
251 				if ((++ncp & 7) == 0 && !xflag) {
252 					pcp = ncp;
253 					Bputc(&bout, '\t');
254 				}
255 			if (!*--p)
256 				break;
257 			while (pcp < ncp) {
258 				Bputc(&bout, ' ');
259 				pcp++;
260 			}
261 			Bputc(&bout, *p);
262 			if (*p++ == '\b')
263 				pcp--;
264 			else
265 				pcp++;
266 		}
267 	}
268 }
269 
270 void
incr(void)271 incr(void)
272 {
273 	int lno;
274 
275 	store(ll++);
276 	if (ll > llh)
277 		llh = ll;
278 	lno = ll % PL;
279 	if (ll >= mustwr && page[lno]) {
280 		emit(page[lno], ll - PL);
281 		mustwr++;
282 		free(page[lno]);
283 		page[lno] = nil;
284 	}
285 	fetch(ll);
286 }
287 
288 void
decr(void)289 decr(void)
290 {
291 	if (ll > mustwr - PL) {
292 		store(ll--);
293 		fetch(ll);
294 	}
295 }
296