xref: /illumos-gate/usr/src/cmd/col/col.c (revision 2bda830b1b393f809c54b105ec8ab418c3e505a1)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright (c) 1998, 2001 by Sun Microsystems, Inc.
24  * All Rights Reserved
25  */
26 
27 /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
28 /*	  All Rights Reserved  	*/
29 
30 
31 #pragma ident	"%Z%%M%	%I%	%E% SMI"
32 /*
33  *	col - filter reverse carraige motions
34  *
35  */
36 
37 
38 #include <stdio.h>
39 #include <ctype.h>
40 #include <locale.h>
41 #include <limits.h>
42 #include <stdlib.h>
43 #include <wctype.h>
44 
45 #define	PL 256
46 #define	ESC '\033'
47 #define	RLF '\013'
48 #define	SI '\017'
49 #define	SO '\016'
50 #define	GREEK 0200
51 #define	LINELN 4096
52 
53 wchar_t	*page[PL];
54 wchar_t	lbuff[LINELN], *line;
55 wchar_t	ws_blank[2] = {' ', 0};
56 char	esc_chars, underline, temp_off, smart;
57 int	bflag, xflag, fflag, pflag;
58 int	greeked;
59 int	half;
60 int	cp, lp;
61 int	ll, llh, mustwr;
62 int	pcp = 0;
63 char	*pgmname;
64 
65 #define	USAGEMSG	"usage:\tcol [-bfxp]\n"
66 
67 static void	outc(wchar_t);
68 static void	store(int);
69 static void	fetch(int);
70 static void	emit(wchar_t *, int);
71 static void	incr(void);
72 static void	decr(void);
73 static void	wsinsert(wchar_t *, int);
74 static int	wcscrwidth(wchar_t);
75 
76 int
77 main(int argc, char **argv)
78 {
79 	int	i, n;
80 	int	opt;
81 	int	greek;
82 	int	c;
83 	wchar_t	wc;
84 	char	byte;
85 	static char	fbuff[BUFSIZ];
86 
87 	setbuf(stdout, fbuff);
88 	(void) setlocale(LC_ALL, "");
89 #if !defined(TEXT_DOMAIN)
90 #define	TEXT_DOMAIN "SYS_TEST"
91 #endif
92 	(void) textdomain(TEXT_DOMAIN);
93 	pgmname = argv[0];
94 
95 	while ((opt = getopt(argc, argv, "bfxp")) != EOF)
96 		switch (opt) {
97 		case 'b':
98 			bflag++;
99 			break;
100 		case 'x':
101 			xflag++;
102 			break;
103 		case 'f':
104 			fflag++;
105 			break;
106 		case 'p':
107 			pflag++;
108 			break;
109 		case '?':
110 		default:
111 			(void) fprintf(stderr, gettext(USAGEMSG));
112 			exit(2);
113 		}
114 
115 	argc -= optind;
116 	if (argc >= 1) {
117 		(void) fprintf(stderr, gettext(USAGEMSG));
118 		exit(2);
119 	}
120 
121 	for (ll = 0; ll < PL; ll++)
122 		page[ll] = 0;
123 
124 	smart = temp_off = underline = esc_chars = '\0';
125 	cp = 0;
126 	ll = 0;
127 	greek = 0;
128 	mustwr = PL;
129 	line = lbuff;
130 
131 	while ((c = getwchar()) != EOF) {
132 		if (underline && temp_off && c > ' ') {
133 			outc(ESC);
134 			if (*line) line++;
135 			*line++ = 'X';
136 			*line = temp_off = '\0';
137 		}
138 		if (c != '\b')
139 			if (esc_chars)
140 				esc_chars = '\0';
141 		switch (c) {
142 		case '\n':
143 			if (underline && !temp_off) {
144 				if (*line)
145 					line++;
146 				*line++ = ESC;
147 				*line++ = 'Y';
148 				*line = '\0';
149 				temp_off = '1';
150 			}
151 			incr();
152 			incr();
153 			cp = 0;
154 			continue;
155 
156 		case '\0':
157 			continue;
158 
159 		case ESC:
160 			c = getwchar();
161 			switch (c) {
162 			case '7':	/* reverse full line feed */
163 				decr();
164 				decr();
165 				break;
166 
167 			case '8':	/* reverse half line feed */
168 				if (fflag)
169 					decr();
170 				else {
171 					if (--half < -1) {
172 						decr();
173 						decr();
174 						half += 2;
175 					}
176 				}
177 				break;
178 
179 			case '9':	/* forward half line feed */
180 				if (fflag)
181 					incr();
182 				else {
183 					if (++half > 0) {
184 						incr();
185 						incr();
186 						half -= 2;
187 					}
188 				}
189 				break;
190 
191 			default:
192 				if (pflag)	{	/* pass through esc */
193 					outc(ESC);
194 					line++;
195 					*line = c;
196 					line++;
197 					*line = '\0';
198 					esc_chars = 1;
199 					if (c == 'X')
200 						underline = 1;
201 					if (c == 'Y' && underline)
202 						underline =	temp_off = '\0';
203 					if (c == ']')
204 						smart = 1;
205 					if (c == '[')
206 						smart = '\0';
207 					}
208 				break;
209 			}
210 			continue;
211 
212 		case SO:
213 			greek = GREEK;
214 			greeked++;
215 			continue;
216 
217 		case SI:
218 			greek = 0;
219 			continue;
220 
221 		case RLF:
222 			decr();
223 			decr();
224 			continue;
225 
226 		case '\r':
227 			cp = 0;
228 			continue;
229 
230 		case '\t':
231 			cp = (cp + 8) & -8;
232 			continue;
233 
234 		case '\b':
235 			if (esc_chars) {
236 				*line++ = '\b';
237 				*line = '\0';
238 			} else if (cp > 0)
239 				cp--;
240 			continue;
241 
242 		case ' ':
243 			cp++;
244 			continue;
245 
246 		default:
247 			if (iswprint(c)) {	/* if printable */
248 				if (!greek) {
249 					outc((wchar_t)c);
250 					cp += wcscrwidth(c);
251 				}
252 				/*
253 				 * EUC (apply SO only when there can
254 				 * be corresponding character in CS1)
255 				 */
256 				else if (iswascii(c)) {
257 					byte = (c | greek);
258 					n = mbtowc(&wc, &byte, 1);
259 					if (!iswcntrl(c) && !iswspace(c) &&
260 					    n == 1) {
261 						outc(wc);
262 						cp += wcscrwidth(wc);
263 					} else {
264 						outc((wchar_t)c);
265 						cp += wcscrwidth(c);
266 					}
267 				} else {
268 					outc((wchar_t)c);
269 					cp += wcscrwidth(c);
270 				}
271 
272 				if ((cp + 1) > LINELN) {
273 					(void) fprintf(stderr,
274 					    gettext("col: Line too long\n"));
275 					exit(2);
276 				}
277 			}
278 			continue;
279 		}
280 	}
281 
282 	for (i = 0; i < PL; i++)
283 		if (page[(mustwr+i)%PL] != 0)
284 			emit(page[(mustwr+i) % PL], mustwr+i-PL);
285 	emit(ws_blank, (llh + 1) & -2);
286 	return (0);
287 }
288 
289 static void
290 outc(wchar_t c)
291 {
292 	int	n, i;
293 	int	width, widthl, widthc;
294 	wchar_t	*p1;
295 	wchar_t c1;
296 	char esc_chars = '\0';
297 	if (lp > cp) {
298 		line = lbuff;
299 		lp = 0;
300 	}
301 
302 	while (lp < cp) {
303 		if (*line != '\b')
304 			if (esc_chars)
305 				esc_chars = '\0';
306 			switch (*line)	{
307 			case ESC:
308 				line++;
309 				esc_chars = 1;
310 				break;
311 			case '\0':
312 				*line = ' ';
313 				lp++;
314 				break;
315 			case '\b':
316 				/* if ( ! esc_chars ) */
317 					lp--;
318 				break;
319 			default:
320 				lp += wcscrwidth(*line);
321 			}
322 		line++;
323 	}
324 	while (*line == '\b') {
325 		/*
326 		 * EUC (For a multi-column character, backspace characters
327 		 * are assumed to be used like "__^H^HXX", where "XX"
328 		 * represents a two-column character, and a backspace
329 		 * always goes back by one column.)
330 		 */
331 		for (n = 0; *line == '\b'; line++) {
332 			n++;
333 			lp--;
334 		}
335 		while (n > 0 && lp < cp) {
336 			i = *line++;
337 			i = wcscrwidth(i);
338 			n -= i;
339 			lp += i;
340 		}
341 	}
342 	while (*line == ESC)
343 		line += 6;
344 	widthc = wcscrwidth(c);
345 	widthl = wcscrwidth(*line);
346 	if (bflag || (*line == '\0') || *line == ' ') {
347 		if (*line == '\0' || widthl == widthc) {
348 			*line = c;
349 		} else if (widthl > widthc) {
350 			n = widthl - widthc;
351 			wsinsert(line, n);
352 			*line++ = c;
353 			for (i = 0; i < n; i++)
354 				*line++ = ' ';
355 			line = lbuff;
356 			lp = 0;
357 		} else {
358 			n = widthc - widthl;
359 			for (p1 = line+1; n > 0; n -= wcscrwidth(i))
360 				i = *p1++;
361 			*line = c;
362 			(void) wcscpy(line+1, p1);
363 
364 		}
365 	} else {
366 		if (smart && (widthl == 1) && (widthc == 1)) {
367 			wchar_t	c1, c2, c3, c4, c5, c6, c7;
368 			c1 = *++line;
369 			*line++ = ESC;
370 			c2 = *line;
371 			*line++ = '[';
372 			c3 = *line;
373 			*line++ = '\b';
374 			c4 = *line;
375 			*line++ = ESC;
376 			c5 = *line;
377 			*line++ = ']';
378 			c6 = *line;
379 			*line++ = c;
380 			while (c1) {
381 				c7 = *line;
382 				*line++ = c1;
383 				c1 = c2;
384 				c2 = c3;
385 				c3 = c4;
386 				c4 = c5;
387 				c5 = c6;
388 				c6 = c7;
389 			}
390 		} else	{
391 			if ((widthl == 1) && (widthc == 1)) {
392 				wchar_t	c1, c2, c3;
393 				c1 = *++line;
394 				*line++ = '\b';
395 				c2 = *line;
396 				*line++ = c;
397 				while (c1) {
398 					c3 = *line;
399 					*line++ = c1;
400 					c1 = c2;
401 					c2 = c3;
402 				}
403 			} else {
404 				width = (widthc > widthl) ? widthc : widthl;
405 				for (i = 0; i < width; i += wcscrwidth(c1))
406 					c1 = *line++;
407 				wsinsert(line, width + (width - widthc + 1));
408 				for (i = 0; i < width; i++)
409 					*line++ = '\b';
410 				*line++ = c;
411 				for (i = widthc; i < width; i++)
412 					*line++ = ' ';
413 			}
414 		}
415 		lp = 0;
416 		line = lbuff;
417 	}
418 }
419 
420 static void
421 store(int lno)
422 {
423 	lno %= PL;
424 	if (page[lno] != 0)
425 		free((char *)page[lno]);
426 	page[lno] = (wchar_t *)malloc((unsigned)(wcslen(lbuff) + 2)
427 		* sizeof (wchar_t));
428 	if (page[lno] == 0) {
429 		/* fprintf(stderr, "%s: no storage\n", pgmname); */
430 		exit(2);
431 	}
432 	(void) wcscpy(page[lno], lbuff);
433 }
434 
435 static void
436 fetch(int lno)
437 {
438 	wchar_t	*p;
439 
440 	lno %= PL;
441 	p = lbuff;
442 	while (*p)
443 		*p++ = '\0';
444 	line = lbuff;
445 	lp = 0;
446 	if (page[lno])
447 		(void) wcscpy(line, page[lno]);
448 }
449 
450 static void
451 emit(wchar_t *s, int lineno)
452 {
453 	static int	cline = 0;
454 	int	ncp;
455 	wchar_t	*p;
456 	char	cshifted;
457 	char	chr[MB_LEN_MAX + 1];
458 
459 	int	c;
460 	static int	gflag = 0;
461 
462 	if (*s) {
463 		if (gflag) {
464 			(void) putchar(SI);
465 			gflag = 0;
466 		}
467 		while (cline < lineno - 1) {
468 			(void) putchar('\n');
469 			pcp = 0;
470 			cline += 2;
471 		}
472 		if (cline != lineno) {
473 			(void) putchar(ESC);
474 			(void) putchar('9');
475 			cline++;
476 		}
477 		if (pcp)
478 			(void) putchar('\r');
479 		pcp = 0;
480 		p = s;
481 		while (*p) {
482 			ncp = pcp;
483 			while (*p++ == ' ') {
484 				if ((++ncp & 7) == 0 && !xflag) {
485 					pcp = ncp;
486 					(void) putchar('\t');
487 				}
488 			}
489 			if (!*--p)
490 				break;
491 			while (pcp < ncp) {
492 				(void) putchar(' ');
493 				pcp++;
494 			}
495 			if (greeked) {
496 				if (wctomb(chr, *p) == 1) {
497 					if (gflag != (*chr & GREEK) &&
498 					    *p != '\b' &&
499 					    isascii(*chr ^ (gflag ^ GREEK)) &&
500 					    !iscntrl(*chr ^ (gflag ^ GREEK)) &&
501 					    !isspace(*chr ^ (gflag ^ GREEK))) {
502 						if (gflag)
503 							(void) putchar(SI);
504 						else
505 							(void) putchar(SO);
506 						gflag ^= GREEK;
507 					}
508 				}
509 			}
510 			c = *p;
511 			if (greeked) {
512 				if (wctomb(chr, (wchar_t)c) == 1) {
513 					cshifted = (*chr ^ GREEK);
514 					if (isascii(cshifted) &&
515 					    !iscntrl(cshifted) &&
516 					    !isspace(cshifted))
517 						(void) putchar(*chr & ~GREEK);
518 				} else
519 					(void) putwchar(c);
520 			} else
521 				(void) putwchar(c);
522 			if (c == '\b') {
523 				if (*(p-2) && *(p-2) == ESC) {
524 					pcp++;
525 				} else
526 					pcp--;
527 			} else {
528 				pcp += wcscrwidth(c);
529 			}
530 			p++;
531 		}
532 	}
533 }
534 
535 static void
536 incr(void)
537 {
538 	store(ll++);
539 	if (ll > llh)
540 		llh = ll;
541 	if (ll >= mustwr && page[ll%PL]) {
542 		emit(page[ll%PL], ll - PL);
543 		mustwr++;
544 		free((char *)page[ll%PL]);
545 		page[ll%PL] = 0;
546 	}
547 	fetch(ll);
548 }
549 
550 static void
551 decr(void)
552 {
553 	if (ll > mustwr - PL) {
554 		store(ll--);
555 		fetch(ll);
556 	}
557 }
558 
559 static void
560 wsinsert(wchar_t *s, int n)
561 {
562 	wchar_t	*p1, *p2;
563 
564 
565 	p1 = s + wcslen(s);
566 	p2 = p1 + n;
567 	while (p1 >= s)
568 		*p2-- = *p1--;
569 }
570 
571 static int
572 wcscrwidth(wchar_t wc)
573 {
574 	int	nc;
575 
576 	if (wc == 0) {
577 		/*
578 		 * if wc is a null character, needs to
579 		 * return 1 instead of 0.
580 		 */
581 		return (1);
582 	}
583 	nc = wcwidth(wc);
584 	if (nc > 0) {
585 		return (nc);
586 	} else {
587 		return (0);
588 	}
589 }
590