Home | History | Annotate | Line # | Download | only in ul
ul.c revision 1.16
      1 /*	$NetBSD: ul.c,v 1.16 2012/03/20 20:34:59 matt Exp $	*/
      2 
      3 /*
      4  * Copyright (c) 1980, 1993
      5  *	The Regents of the University of California.  All rights reserved.
      6  *
      7  * Redistribution and use in source and binary forms, with or without
      8  * modification, are permitted provided that the following conditions
      9  * are met:
     10  * 1. Redistributions of source code must retain the above copyright
     11  *    notice, this list of conditions and the following disclaimer.
     12  * 2. Redistributions in binary form must reproduce the above copyright
     13  *    notice, this list of conditions and the following disclaimer in the
     14  *    documentation and/or other materials provided with the distribution.
     15  * 3. Neither the name of the University nor the names of its contributors
     16  *    may be used to endorse or promote products derived from this software
     17  *    without specific prior written permission.
     18  *
     19  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
     20  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     21  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     22  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
     23  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     24  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
     25  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     26  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     27  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     28  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     29  * SUCH DAMAGE.
     30  */
     31 
     32 #include <sys/cdefs.h>
     33 #ifndef lint
     34 __COPYRIGHT("@(#) Copyright (c) 1980, 1993\
     35  The Regents of the University of California.  All rights reserved.");
     36 #endif /* not lint */
     37 
     38 #ifndef lint
     39 #if 0
     40 static char sccsid[] = "@(#)ul.c	8.1 (Berkeley) 6/6/93";
     41 #endif
     42 __RCSID("$NetBSD: ul.c,v 1.16 2012/03/20 20:34:59 matt Exp $");
     43 #endif /* not lint */
     44 
     45 #include <stdio.h>
     46 #include <stdlib.h>
     47 #include <string.h>
     48 #include <term.h>
     49 #include <unistd.h>
     50 
     51 #define	IESC	'\033'
     52 #define	SO	'\016'
     53 #define	SI	'\017'
     54 #define	HFWD	'9'
     55 #define	HREV	'8'
     56 #define	FREV	'7'
     57 #define	MAXBUF	512
     58 
     59 #define	NORMAL	000
     60 #define	ALTSET	001	/* Reverse */
     61 #define	SUPERSC	002	/* Dim */
     62 #define	SUBSC	004	/* Dim | Ul */
     63 #define	UNDERL	010	/* Ul */
     64 #define	BOLD	020	/* Bold */
     65 
     66 struct tinfo *info;
     67 int	must_overstrike;
     68 const char *CURS_UP, *CURS_RIGHT, *CURS_LEFT,
     69 	*ENTER_STANDOUT, *EXIT_STANDOUT, *ENTER_UNDERLINE, *EXIT_UNDERLINE,
     70 	*ENTER_DIM, *ENTER_BOLD, *ENTER_REVERSE, *UNDER_CHAR, *EXIT_ATTRIBUTES;
     71 
     72 struct	CHAR	{
     73 	char	c_mode;
     74 	char	c_char;
     75 } ;
     76 
     77 struct	CHAR	obuf[MAXBUF];
     78 int	col, maxcol;
     79 int	mode;
     80 int	halfpos;
     81 int	upln;
     82 int	iflag;
     83 
     84 int	main __P((int, char **));
     85 void	filter __P((FILE *));
     86 void	flushln __P((void));
     87 void	fwd __P((void));
     88 void	iattr __P((void));
     89 void	initbuf __P((void));
     90 void	initcap __P((void));
     91 void	outc __P((int));
     92 int	outchar __P((int));
     93 void	overstrike __P((void));
     94 void	reverse __P((void));
     95 void	setulmode __P((int));
     96 
     97 
     98 #define	PRINT(s)	if (s == NULL) /* void */; else tputs(s, 1, outchar)
     99 
    100 int
    101 main(int argc, char **argv)
    102 {
    103 	int c;
    104 	const char *termtype;
    105 	FILE *f;
    106 
    107 	termtype = getenv("TERM");
    108 	if (termtype == NULL || (argv[0][0] == 'c' && !isatty(1)))
    109 		termtype = "lpr";
    110 	while ((c=getopt(argc, argv, "it:T:")) != -1)
    111 		switch(c) {
    112 
    113 		case 't':
    114 		case 'T': /* for nroff compatibility */
    115 				termtype = optarg;
    116 			break;
    117 		case 'i':
    118 			iflag = 1;
    119 			break;
    120 
    121 		default:
    122 			fprintf(stderr,
    123 				"usage: %s [ -i ] [ -tTerm ] file...\n",
    124 				argv[0]);
    125 			exit(1);
    126 		}
    127 
    128 	setupterm(termtype, 0, NULL);
    129 	if ((over_strike && enter_bold_mode == NULL) ||
    130 	    (transparent_underline && enter_underline_mode == NULL &&
    131 	     underline_char == NULL))
    132 	initbuf();
    133 	if (optind == argc)
    134 		filter(stdin);
    135 	else for (; optind<argc; optind++) {
    136 		f = fopen(argv[optind],"r");
    137 		if (f == NULL) {
    138 			perror(argv[optind]);
    139 			exit(1);
    140 		}
    141 
    142 		filter(f);
    143 		fclose(f);
    144 	}
    145 	exit(0);
    146 }
    147 
    148 void
    149 filter(FILE *f)
    150 {
    151 	int c;
    152 
    153 	while ((c = getc(f)) != EOF) switch(c) {
    154 
    155 	case '\b':
    156 		if (col > 0)
    157 			col--;
    158 		continue;
    159 
    160 	case '\t':
    161 		col = (col+8) & ~07;
    162 		if (col > maxcol)
    163 			maxcol = col;
    164 		continue;
    165 
    166 	case '\r':
    167 		col = 0;
    168 		continue;
    169 
    170 	case SO:
    171 		mode |= ALTSET;
    172 		continue;
    173 
    174 	case SI:
    175 		mode &= ~ALTSET;
    176 		continue;
    177 
    178 	case IESC:
    179 		switch (c = getc(f)) {
    180 
    181 		case HREV:
    182 			if (halfpos == 0) {
    183 				mode |= SUPERSC;
    184 				halfpos--;
    185 			} else if (halfpos > 0) {
    186 				mode &= ~SUBSC;
    187 				halfpos--;
    188 			} else {
    189 				halfpos = 0;
    190 				reverse();
    191 			}
    192 			continue;
    193 
    194 		case HFWD:
    195 			if (halfpos == 0) {
    196 				mode |= SUBSC;
    197 				halfpos++;
    198 			} else if (halfpos < 0) {
    199 				mode &= ~SUPERSC;
    200 				halfpos++;
    201 			} else {
    202 				halfpos = 0;
    203 				fwd();
    204 			}
    205 			continue;
    206 
    207 		case FREV:
    208 			reverse();
    209 			continue;
    210 
    211 		default:
    212 			fprintf(stderr,
    213 				"Unknown escape sequence in input: %o, %o\n",
    214 				IESC, c);
    215 			exit(1);
    216 		}
    217 		continue;
    218 
    219 	case '_':
    220 		if (obuf[col].c_char)
    221 			obuf[col].c_mode |= UNDERL | mode;
    222 		else
    223 			obuf[col].c_char = '_';
    224 	case ' ':
    225 		col++;
    226 		if (col > maxcol)
    227 			maxcol = col;
    228 		continue;
    229 
    230 	case '\n':
    231 		flushln();
    232 		continue;
    233 
    234 	case '\f':
    235 		flushln();
    236 		putchar('\f');
    237 		continue;
    238 
    239 	default:
    240 		if (c < ' ')	/* non printing */
    241 			continue;
    242 		if (obuf[col].c_char == '\0') {
    243 			obuf[col].c_char = c;
    244 			obuf[col].c_mode = mode;
    245 		} else if (obuf[col].c_char == '_') {
    246 			obuf[col].c_char = c;
    247 			obuf[col].c_mode |= UNDERL|mode;
    248 		} else if (obuf[col].c_char == c)
    249 			obuf[col].c_mode |= BOLD|mode;
    250 		else
    251 			obuf[col].c_mode = mode;
    252 		col++;
    253 		if (col > maxcol)
    254 			maxcol = col;
    255 		continue;
    256 	}
    257 	if (maxcol)
    258 		flushln();
    259 }
    260 
    261 void
    262 flushln(void)
    263 {
    264 	int lastmode;
    265 	int i;
    266 	int hadmodes = 0;
    267 
    268 	lastmode = NORMAL;
    269 	for (i=0; i<maxcol; i++) {
    270 		if (obuf[i].c_mode != lastmode) {
    271 			hadmodes++;
    272 			setulmode(obuf[i].c_mode);
    273 			lastmode = obuf[i].c_mode;
    274 		}
    275 		if (obuf[i].c_char == '\0') {
    276 			if (upln) {
    277 				PRINT(cursor_right);
    278 			}
    279 			else {
    280 				outc(' ');
    281 			}
    282 		} else
    283 			outc(obuf[i].c_char);
    284 	}
    285 	if (lastmode != NORMAL) {
    286 		setulmode(0);
    287 	}
    288 	if (must_overstrike && hadmodes)
    289 		overstrike();
    290 	putchar('\n');
    291 	if (iflag && hadmodes)
    292 		iattr();
    293 	(void)fflush(stdout);
    294 	if (upln)
    295 		upln--;
    296 	initbuf();
    297 }
    298 
    299 /*
    300  * For terminals that can overstrike, overstrike underlines and bolds.
    301  * We don't do anything with halfline ups and downs, or Greek.
    302  */
    303 void
    304 overstrike(void)
    305 {
    306 	int i;
    307 	char lbuf[256];
    308 	char *cp = lbuf;
    309 	int hadbold=0;
    310 
    311 	/* Set up overstrike buffer */
    312 	for (i=0; i<maxcol; i++)
    313 		switch (obuf[i].c_mode) {
    314 		case NORMAL:
    315 		default:
    316 			*cp++ = ' ';
    317 			break;
    318 		case UNDERL:
    319 			*cp++ = '_';
    320 			break;
    321 		case BOLD:
    322 			*cp++ = obuf[i].c_char;
    323 			hadbold=1;
    324 			break;
    325 		}
    326 	putchar('\r');
    327 	for (*cp=' '; *cp==' '; cp--)
    328 		*cp = 0;
    329 	for (cp=lbuf; *cp; cp++)
    330 		putchar(*cp);
    331 	if (hadbold) {
    332 		putchar('\r');
    333 		for (cp=lbuf; *cp; cp++)
    334 			putchar(*cp=='_' ? ' ' : *cp);
    335 		putchar('\r');
    336 		for (cp=lbuf; *cp; cp++)
    337 			putchar(*cp=='_' ? ' ' : *cp);
    338 	}
    339 }
    340 
    341 void
    342 iattr(void)
    343 {
    344 	int i;
    345 	char lbuf[256];
    346 	char *cp = lbuf;
    347 
    348 	for (i=0; i<maxcol; i++)
    349 		switch (obuf[i].c_mode) {
    350 		case NORMAL:	*cp++ = ' '; break;
    351 		case ALTSET:	*cp++ = 'g'; break;
    352 		case SUPERSC:	*cp++ = '^'; break;
    353 		case SUBSC:	*cp++ = 'v'; break;
    354 		case UNDERL:	*cp++ = '_'; break;
    355 		case BOLD:	*cp++ = '!'; break;
    356 		default:	*cp++ = 'X'; break;
    357 		}
    358 	for (*cp=' '; *cp==' '; cp--)
    359 		*cp = 0;
    360 	for (cp=lbuf; *cp; cp++)
    361 		putchar(*cp);
    362 	putchar('\n');
    363 }
    364 
    365 void
    366 initbuf(void)
    367 {
    368 
    369 	memset((char *)obuf, 0, sizeof (obuf));	/* depends on NORMAL == 0 */
    370 	col = 0;
    371 	maxcol = 0;
    372 	mode &= ALTSET;
    373 }
    374 
    375 void
    376 fwd(void)
    377 {
    378 	int oldcol, oldmax;
    379 
    380 	oldcol = col;
    381 	oldmax = maxcol;
    382 	flushln();
    383 	col = oldcol;
    384 	maxcol = oldmax;
    385 }
    386 
    387 void
    388 reverse(void)
    389 {
    390 	upln++;
    391 	fwd();
    392 	PRINT(cursor_up);
    393 	PRINT(cursor_up);
    394 	upln++;
    395 }
    396 
    397 int
    398 outchar(int c)
    399 {
    400 	return (putchar(c & 0177));
    401 }
    402 
    403 static int curmode = 0;
    404 
    405 void
    406 outc(int c)
    407 {
    408 	putchar(c);
    409 	if (underline_char && !enter_underline_mode && (curmode & UNDERL)) {
    410 		if (cursor_left)
    411 			PRINT(cursor_left);
    412 		else
    413 			putchar('\b');
    414 		PRINT(underline_char);
    415 	}
    416 }
    417 
    418 void
    419 setulmode(int newmode)
    420 {
    421 	if (!iflag) {
    422 		if (curmode != NORMAL && newmode != NORMAL)
    423 			setulmode(NORMAL);
    424 		switch (newmode) {
    425 		case NORMAL:
    426 			switch(curmode) {
    427 			case NORMAL:
    428 				break;
    429 			case UNDERL:
    430 				if (enter_underline_mode)
    431 					PRINT(exit_underline_mode);
    432 				else
    433 					PRINT(exit_standout_mode);
    434 				break;
    435 			default:
    436 				/* This includes standout */
    437 				if (exit_attribute_mode)
    438 					PRINT(exit_attribute_mode);
    439 				else
    440 					PRINT(exit_standout_mode);
    441 				break;
    442 			}
    443 			break;
    444 		case ALTSET:
    445 			if (enter_reverse_mode)
    446 				PRINT(enter_reverse_mode);
    447 			else
    448 				PRINT(enter_standout_mode);
    449 			break;
    450 		case SUPERSC:
    451 			/*
    452 			 * This only works on a few terminals.
    453 			 * It should be fixed.
    454 			 */
    455 			PRINT(enter_underline_mode);
    456 			PRINT(enter_dim_mode);
    457 			break;
    458 		case SUBSC:
    459 			if (enter_dim_mode)
    460 				PRINT(enter_dim_mode);
    461 			else
    462 				PRINT(enter_standout_mode);
    463 			break;
    464 		case UNDERL:
    465 			if (enter_underline_mode)
    466 				PRINT(enter_underline_mode);
    467 			else
    468 				PRINT(enter_standout_mode);
    469 			break;
    470 		case BOLD:
    471 			if (enter_bold_mode)
    472 				PRINT(enter_bold_mode);
    473 			else
    474 				PRINT(enter_reverse_mode);
    475 			break;
    476 		default:
    477 			/*
    478 			 * We should have some provision here for multiple modes
    479 			 * on at once.  This will have to come later.
    480 			 */
    481 			PRINT(enter_standout_mode);
    482 			break;
    483 		}
    484 	}
    485 	curmode = newmode;
    486 }
    487