Home | History | Annotate | Line # | Download | only in ul
ul.c revision 1.15
      1 /*	$NetBSD: ul.c,v 1.15 2010/02/03 15:34:46 roy 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.15 2010/02/03 15:34:46 roy 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(argc, argv)
    102 	int argc;
    103 	char **argv;
    104 {
    105 	int c;
    106 	const char *termtype;
    107 	FILE *f;
    108 
    109 	termtype = getenv("TERM");
    110 	if (termtype == NULL || (argv[0][0] == 'c' && !isatty(1)))
    111 		termtype = "lpr";
    112 	while ((c=getopt(argc, argv, "it:T:")) != -1)
    113 		switch(c) {
    114 
    115 		case 't':
    116 		case 'T': /* for nroff compatibility */
    117 				termtype = optarg;
    118 			break;
    119 		case 'i':
    120 			iflag = 1;
    121 			break;
    122 
    123 		default:
    124 			fprintf(stderr,
    125 				"usage: %s [ -i ] [ -tTerm ] file...\n",
    126 				argv[0]);
    127 			exit(1);
    128 		}
    129 
    130 	setupterm(termtype, 0, NULL);
    131 	if ((over_strike && enter_bold_mode == NULL) ||
    132 	    (transparent_underline && enter_underline_mode == NULL &&
    133 	     underline_char == NULL))
    134 	initbuf();
    135 	if (optind == argc)
    136 		filter(stdin);
    137 	else for (; optind<argc; optind++) {
    138 		f = fopen(argv[optind],"r");
    139 		if (f == NULL) {
    140 			perror(argv[optind]);
    141 			exit(1);
    142 		}
    143 
    144 		filter(f);
    145 		fclose(f);
    146 	}
    147 	exit(0);
    148 }
    149 
    150 void
    151 filter(f)
    152 	FILE *f;
    153 {
    154 	int c;
    155 
    156 	while ((c = getc(f)) != EOF) switch(c) {
    157 
    158 	case '\b':
    159 		if (col > 0)
    160 			col--;
    161 		continue;
    162 
    163 	case '\t':
    164 		col = (col+8) & ~07;
    165 		if (col > maxcol)
    166 			maxcol = col;
    167 		continue;
    168 
    169 	case '\r':
    170 		col = 0;
    171 		continue;
    172 
    173 	case SO:
    174 		mode |= ALTSET;
    175 		continue;
    176 
    177 	case SI:
    178 		mode &= ~ALTSET;
    179 		continue;
    180 
    181 	case IESC:
    182 		switch (c = getc(f)) {
    183 
    184 		case HREV:
    185 			if (halfpos == 0) {
    186 				mode |= SUPERSC;
    187 				halfpos--;
    188 			} else if (halfpos > 0) {
    189 				mode &= ~SUBSC;
    190 				halfpos--;
    191 			} else {
    192 				halfpos = 0;
    193 				reverse();
    194 			}
    195 			continue;
    196 
    197 		case HFWD:
    198 			if (halfpos == 0) {
    199 				mode |= SUBSC;
    200 				halfpos++;
    201 			} else if (halfpos < 0) {
    202 				mode &= ~SUPERSC;
    203 				halfpos++;
    204 			} else {
    205 				halfpos = 0;
    206 				fwd();
    207 			}
    208 			continue;
    209 
    210 		case FREV:
    211 			reverse();
    212 			continue;
    213 
    214 		default:
    215 			fprintf(stderr,
    216 				"Unknown escape sequence in input: %o, %o\n",
    217 				IESC, c);
    218 			exit(1);
    219 		}
    220 		continue;
    221 
    222 	case '_':
    223 		if (obuf[col].c_char)
    224 			obuf[col].c_mode |= UNDERL | mode;
    225 		else
    226 			obuf[col].c_char = '_';
    227 	case ' ':
    228 		col++;
    229 		if (col > maxcol)
    230 			maxcol = col;
    231 		continue;
    232 
    233 	case '\n':
    234 		flushln();
    235 		continue;
    236 
    237 	case '\f':
    238 		flushln();
    239 		putchar('\f');
    240 		continue;
    241 
    242 	default:
    243 		if (c < ' ')	/* non printing */
    244 			continue;
    245 		if (obuf[col].c_char == '\0') {
    246 			obuf[col].c_char = c;
    247 			obuf[col].c_mode = mode;
    248 		} else if (obuf[col].c_char == '_') {
    249 			obuf[col].c_char = c;
    250 			obuf[col].c_mode |= UNDERL|mode;
    251 		} else if (obuf[col].c_char == c)
    252 			obuf[col].c_mode |= BOLD|mode;
    253 		else
    254 			obuf[col].c_mode = mode;
    255 		col++;
    256 		if (col > maxcol)
    257 			maxcol = col;
    258 		continue;
    259 	}
    260 	if (maxcol)
    261 		flushln();
    262 }
    263 
    264 void
    265 flushln()
    266 {
    267 	int lastmode;
    268 	int i;
    269 	int hadmodes = 0;
    270 
    271 	lastmode = NORMAL;
    272 	for (i=0; i<maxcol; i++) {
    273 		if (obuf[i].c_mode != lastmode) {
    274 			hadmodes++;
    275 			setulmode(obuf[i].c_mode);
    276 			lastmode = obuf[i].c_mode;
    277 		}
    278 		if (obuf[i].c_char == '\0') {
    279 			if (upln) {
    280 				PRINT(cursor_right);
    281 			}
    282 			else {
    283 				outc(' ');
    284 			}
    285 		} else
    286 			outc(obuf[i].c_char);
    287 	}
    288 	if (lastmode != NORMAL) {
    289 		setulmode(0);
    290 	}
    291 	if (must_overstrike && hadmodes)
    292 		overstrike();
    293 	putchar('\n');
    294 	if (iflag && hadmodes)
    295 		iattr();
    296 	(void)fflush(stdout);
    297 	if (upln)
    298 		upln--;
    299 	initbuf();
    300 }
    301 
    302 /*
    303  * For terminals that can overstrike, overstrike underlines and bolds.
    304  * We don't do anything with halfline ups and downs, or Greek.
    305  */
    306 void
    307 overstrike()
    308 {
    309 	int i;
    310 	char lbuf[256];
    311 	char *cp = lbuf;
    312 	int hadbold=0;
    313 
    314 	/* Set up overstrike buffer */
    315 	for (i=0; i<maxcol; i++)
    316 		switch (obuf[i].c_mode) {
    317 		case NORMAL:
    318 		default:
    319 			*cp++ = ' ';
    320 			break;
    321 		case UNDERL:
    322 			*cp++ = '_';
    323 			break;
    324 		case BOLD:
    325 			*cp++ = obuf[i].c_char;
    326 			hadbold=1;
    327 			break;
    328 		}
    329 	putchar('\r');
    330 	for (*cp=' '; *cp==' '; cp--)
    331 		*cp = 0;
    332 	for (cp=lbuf; *cp; cp++)
    333 		putchar(*cp);
    334 	if (hadbold) {
    335 		putchar('\r');
    336 		for (cp=lbuf; *cp; cp++)
    337 			putchar(*cp=='_' ? ' ' : *cp);
    338 		putchar('\r');
    339 		for (cp=lbuf; *cp; cp++)
    340 			putchar(*cp=='_' ? ' ' : *cp);
    341 	}
    342 }
    343 
    344 void
    345 iattr()
    346 {
    347 	int i;
    348 	char lbuf[256];
    349 	char *cp = lbuf;
    350 
    351 	for (i=0; i<maxcol; i++)
    352 		switch (obuf[i].c_mode) {
    353 		case NORMAL:	*cp++ = ' '; break;
    354 		case ALTSET:	*cp++ = 'g'; break;
    355 		case SUPERSC:	*cp++ = '^'; break;
    356 		case SUBSC:	*cp++ = 'v'; break;
    357 		case UNDERL:	*cp++ = '_'; break;
    358 		case BOLD:	*cp++ = '!'; break;
    359 		default:	*cp++ = 'X'; break;
    360 		}
    361 	for (*cp=' '; *cp==' '; cp--)
    362 		*cp = 0;
    363 	for (cp=lbuf; *cp; cp++)
    364 		putchar(*cp);
    365 	putchar('\n');
    366 }
    367 
    368 void
    369 initbuf()
    370 {
    371 
    372 	memset((char *)obuf, 0, sizeof (obuf));	/* depends on NORMAL == 0 */
    373 	col = 0;
    374 	maxcol = 0;
    375 	mode &= ALTSET;
    376 }
    377 
    378 void
    379 fwd()
    380 {
    381 	int oldcol, oldmax;
    382 
    383 	oldcol = col;
    384 	oldmax = maxcol;
    385 	flushln();
    386 	col = oldcol;
    387 	maxcol = oldmax;
    388 }
    389 
    390 void
    391 reverse()
    392 {
    393 	upln++;
    394 	fwd();
    395 	PRINT(cursor_up);
    396 	PRINT(cursor_up);
    397 	upln++;
    398 }
    399 
    400 int
    401 outchar(c)
    402 	int c;
    403 {
    404 	return (putchar(c & 0177));
    405 }
    406 
    407 static int curmode = 0;
    408 
    409 void
    410 outc(c)
    411 	int c;
    412 {
    413 	putchar(c);
    414 	if (underline_char && !enter_underline_mode && (curmode & UNDERL)) {
    415 		if (cursor_left)
    416 			PRINT(cursor_left);
    417 		else
    418 			putchar('\b');
    419 		PRINT(underline_char);
    420 	}
    421 }
    422 
    423 void
    424 setulmode(newmode)
    425 	int newmode;
    426 {
    427 	if (!iflag) {
    428 		if (curmode != NORMAL && newmode != NORMAL)
    429 			setulmode(NORMAL);
    430 		switch (newmode) {
    431 		case NORMAL:
    432 			switch(curmode) {
    433 			case NORMAL:
    434 				break;
    435 			case UNDERL:
    436 				if (enter_underline_mode)
    437 					PRINT(exit_underline_mode);
    438 				else
    439 					PRINT(exit_standout_mode);
    440 				break;
    441 			default:
    442 				/* This includes standout */
    443 				if (exit_attribute_mode)
    444 					PRINT(exit_attribute_mode);
    445 				else
    446 					PRINT(exit_standout_mode);
    447 				break;
    448 			}
    449 			break;
    450 		case ALTSET:
    451 			if (enter_reverse_mode)
    452 				PRINT(enter_reverse_mode);
    453 			else
    454 				PRINT(enter_standout_mode);
    455 			break;
    456 		case SUPERSC:
    457 			/*
    458 			 * This only works on a few terminals.
    459 			 * It should be fixed.
    460 			 */
    461 			PRINT(enter_underline_mode);
    462 			PRINT(enter_dim_mode);
    463 			break;
    464 		case SUBSC:
    465 			if (enter_dim_mode)
    466 				PRINT(enter_dim_mode);
    467 			else
    468 				PRINT(enter_standout_mode);
    469 			break;
    470 		case UNDERL:
    471 			if (enter_underline_mode)
    472 				PRINT(enter_underline_mode);
    473 			else
    474 				PRINT(enter_standout_mode);
    475 			break;
    476 		case BOLD:
    477 			if (enter_bold_mode)
    478 				PRINT(enter_bold_mode);
    479 			else
    480 				PRINT(enter_reverse_mode);
    481 			break;
    482 		default:
    483 			/*
    484 			 * We should have some provision here for multiple modes
    485 			 * on at once.  This will have to come later.
    486 			 */
    487 			PRINT(enter_standout_mode);
    488 			break;
    489 		}
    490 	}
    491 	curmode = newmode;
    492 }
    493