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