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