Home | History | Annotate | Line # | Download | only in ul
ul.c revision 1.13
      1 /*	$NetBSD: ul.c,v 1.13 2008/07/21 14:19:27 lukem 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.13 2008/07/21 14:19:27 lukem Exp $");
     43 #endif /* not lint */
     44 
     45 #include <stdio.h>
     46 #include <stdlib.h>
     47 #include <string.h>
     48 #include <termcap.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_use_uc, must_overstrike;
     68 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 	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 	switch(t_getent(&info, termtype)) {
    131 
    132 	case 1:
    133 		break;
    134 
    135 	default:
    136 		fprintf(stderr,"trouble reading termcap");
    137 		/* fall through to ... */
    138 
    139 	case 0:
    140 		/* No such terminal type - assume dumb */
    141 		if (t_setinfo(&info, "dumb:os:col#80:cr=^M:sf=^J:am:") < 0) {
    142 			fprintf(stderr, "t_setinfo failed, cannot continue\n");
    143 			exit(1);
    144 		}
    145 
    146 		break;
    147 	}
    148 	initcap();
    149 	if (    (t_getflag(info, "os") && ENTER_BOLD==NULL ) ||
    150 		(t_getflag(info, "ul") && ENTER_UNDERLINE==NULL
    151 		 && UNDER_CHAR==NULL))
    152 			must_overstrike = 1;
    153 	initbuf();
    154 	if (optind == argc)
    155 		filter(stdin);
    156 	else for (; optind<argc; optind++) {
    157 		f = fopen(argv[optind],"r");
    158 		if (f == NULL) {
    159 			perror(argv[optind]);
    160 			exit(1);
    161 		}
    162 
    163 		filter(f);
    164 		fclose(f);
    165 	}
    166 	exit(0);
    167 }
    168 
    169 void
    170 filter(f)
    171 	FILE *f;
    172 {
    173 	int c;
    174 
    175 	while ((c = getc(f)) != EOF) switch(c) {
    176 
    177 	case '\b':
    178 		if (col > 0)
    179 			col--;
    180 		continue;
    181 
    182 	case '\t':
    183 		col = (col+8) & ~07;
    184 		if (col > maxcol)
    185 			maxcol = col;
    186 		continue;
    187 
    188 	case '\r':
    189 		col = 0;
    190 		continue;
    191 
    192 	case SO:
    193 		mode |= ALTSET;
    194 		continue;
    195 
    196 	case SI:
    197 		mode &= ~ALTSET;
    198 		continue;
    199 
    200 	case IESC:
    201 		switch (c = getc(f)) {
    202 
    203 		case HREV:
    204 			if (halfpos == 0) {
    205 				mode |= SUPERSC;
    206 				halfpos--;
    207 			} else if (halfpos > 0) {
    208 				mode &= ~SUBSC;
    209 				halfpos--;
    210 			} else {
    211 				halfpos = 0;
    212 				reverse();
    213 			}
    214 			continue;
    215 
    216 		case HFWD:
    217 			if (halfpos == 0) {
    218 				mode |= SUBSC;
    219 				halfpos++;
    220 			} else if (halfpos < 0) {
    221 				mode &= ~SUPERSC;
    222 				halfpos++;
    223 			} else {
    224 				halfpos = 0;
    225 				fwd();
    226 			}
    227 			continue;
    228 
    229 		case FREV:
    230 			reverse();
    231 			continue;
    232 
    233 		default:
    234 			fprintf(stderr,
    235 				"Unknown escape sequence in input: %o, %o\n",
    236 				IESC, c);
    237 			exit(1);
    238 		}
    239 		continue;
    240 
    241 	case '_':
    242 		if (obuf[col].c_char)
    243 			obuf[col].c_mode |= UNDERL | mode;
    244 		else
    245 			obuf[col].c_char = '_';
    246 	case ' ':
    247 		col++;
    248 		if (col > maxcol)
    249 			maxcol = col;
    250 		continue;
    251 
    252 	case '\n':
    253 		flushln();
    254 		continue;
    255 
    256 	case '\f':
    257 		flushln();
    258 		putchar('\f');
    259 		continue;
    260 
    261 	default:
    262 		if (c < ' ')	/* non printing */
    263 			continue;
    264 		if (obuf[col].c_char == '\0') {
    265 			obuf[col].c_char = c;
    266 			obuf[col].c_mode = mode;
    267 		} else if (obuf[col].c_char == '_') {
    268 			obuf[col].c_char = c;
    269 			obuf[col].c_mode |= UNDERL|mode;
    270 		} else if (obuf[col].c_char == c)
    271 			obuf[col].c_mode |= BOLD|mode;
    272 		else
    273 			obuf[col].c_mode = mode;
    274 		col++;
    275 		if (col > maxcol)
    276 			maxcol = col;
    277 		continue;
    278 	}
    279 	if (maxcol)
    280 		flushln();
    281 }
    282 
    283 void
    284 flushln()
    285 {
    286 	int lastmode;
    287 	int i;
    288 	int hadmodes = 0;
    289 
    290 	lastmode = NORMAL;
    291 	for (i=0; i<maxcol; i++) {
    292 		if (obuf[i].c_mode != lastmode) {
    293 			hadmodes++;
    294 			setulmode(obuf[i].c_mode);
    295 			lastmode = obuf[i].c_mode;
    296 		}
    297 		if (obuf[i].c_char == '\0') {
    298 			if (upln) {
    299 				PRINT(CURS_RIGHT);
    300 			}
    301 			else {
    302 				outc(' ');
    303 			}
    304 		} else
    305 			outc(obuf[i].c_char);
    306 	}
    307 	if (lastmode != NORMAL) {
    308 		setulmode(0);
    309 	}
    310 	if (must_overstrike && hadmodes)
    311 		overstrike();
    312 	putchar('\n');
    313 	if (iflag && hadmodes)
    314 		iattr();
    315 	(void)fflush(stdout);
    316 	if (upln)
    317 		upln--;
    318 	initbuf();
    319 }
    320 
    321 /*
    322  * For terminals that can overstrike, overstrike underlines and bolds.
    323  * We don't do anything with halfline ups and downs, or Greek.
    324  */
    325 void
    326 overstrike()
    327 {
    328 	int i;
    329 	char lbuf[256];
    330 	char *cp = lbuf;
    331 	int hadbold=0;
    332 
    333 	/* Set up overstrike buffer */
    334 	for (i=0; i<maxcol; i++)
    335 		switch (obuf[i].c_mode) {
    336 		case NORMAL:
    337 		default:
    338 			*cp++ = ' ';
    339 			break;
    340 		case UNDERL:
    341 			*cp++ = '_';
    342 			break;
    343 		case BOLD:
    344 			*cp++ = obuf[i].c_char;
    345 			hadbold=1;
    346 			break;
    347 		}
    348 	putchar('\r');
    349 	for (*cp=' '; *cp==' '; cp--)
    350 		*cp = 0;
    351 	for (cp=lbuf; *cp; cp++)
    352 		putchar(*cp);
    353 	if (hadbold) {
    354 		putchar('\r');
    355 		for (cp=lbuf; *cp; cp++)
    356 			putchar(*cp=='_' ? ' ' : *cp);
    357 		putchar('\r');
    358 		for (cp=lbuf; *cp; cp++)
    359 			putchar(*cp=='_' ? ' ' : *cp);
    360 	}
    361 }
    362 
    363 void
    364 iattr()
    365 {
    366 	int i;
    367 	char lbuf[256];
    368 	char *cp = lbuf;
    369 
    370 	for (i=0; i<maxcol; i++)
    371 		switch (obuf[i].c_mode) {
    372 		case NORMAL:	*cp++ = ' '; break;
    373 		case ALTSET:	*cp++ = 'g'; break;
    374 		case SUPERSC:	*cp++ = '^'; break;
    375 		case SUBSC:	*cp++ = 'v'; break;
    376 		case UNDERL:	*cp++ = '_'; break;
    377 		case BOLD:	*cp++ = '!'; break;
    378 		default:	*cp++ = 'X'; break;
    379 		}
    380 	for (*cp=' '; *cp==' '; cp--)
    381 		*cp = 0;
    382 	for (cp=lbuf; *cp; cp++)
    383 		putchar(*cp);
    384 	putchar('\n');
    385 }
    386 
    387 void
    388 initbuf()
    389 {
    390 
    391 	memset((char *)obuf, 0, sizeof (obuf));	/* depends on NORMAL == 0 */
    392 	col = 0;
    393 	maxcol = 0;
    394 	mode &= ALTSET;
    395 }
    396 
    397 void
    398 fwd()
    399 {
    400 	int oldcol, oldmax;
    401 
    402 	oldcol = col;
    403 	oldmax = maxcol;
    404 	flushln();
    405 	col = oldcol;
    406 	maxcol = oldmax;
    407 }
    408 
    409 void
    410 reverse()
    411 {
    412 	upln++;
    413 	fwd();
    414 	PRINT(CURS_UP);
    415 	PRINT(CURS_UP);
    416 	upln++;
    417 }
    418 
    419 void
    420 initcap()
    421 {
    422 	/* This nonsense attempts to work with both old and new termcap */
    423 	CURS_UP =		t_agetstr(info, "up");
    424 	CURS_RIGHT =		t_agetstr(info, "ri");
    425 	if (CURS_RIGHT == NULL)
    426 		CURS_RIGHT =	t_agetstr(info, "nd");
    427 	CURS_LEFT =		t_agetstr(info, "le");
    428 	if (CURS_LEFT == NULL)
    429 		CURS_LEFT =	t_agetstr(info, "bc");
    430 	if (CURS_LEFT == NULL && t_getflag(info, "bs"))
    431 		CURS_LEFT =	"\b";
    432 
    433 	ENTER_STANDOUT =	t_agetstr(info, "so");
    434 	EXIT_STANDOUT =		t_agetstr(info, "se");
    435 	ENTER_UNDERLINE =	t_agetstr(info, "us");
    436 	EXIT_UNDERLINE =	t_agetstr(info, "ue");
    437 	ENTER_DIM =		t_agetstr(info, "mh");
    438 	ENTER_BOLD =		t_agetstr(info, "md");
    439 	ENTER_REVERSE =		t_agetstr(info, "mr");
    440 	EXIT_ATTRIBUTES =	t_agetstr(info, "me");
    441 
    442 	if (!ENTER_BOLD && ENTER_REVERSE)
    443 		ENTER_BOLD = ENTER_REVERSE;
    444 	if (!ENTER_BOLD && ENTER_STANDOUT)
    445 		ENTER_BOLD = ENTER_STANDOUT;
    446 	if (!ENTER_UNDERLINE && ENTER_STANDOUT) {
    447 		ENTER_UNDERLINE = ENTER_STANDOUT;
    448 		EXIT_UNDERLINE = EXIT_STANDOUT;
    449 	}
    450 	if (!ENTER_DIM && ENTER_STANDOUT)
    451 		ENTER_DIM = ENTER_STANDOUT;
    452 	if (!ENTER_REVERSE && ENTER_STANDOUT)
    453 		ENTER_REVERSE = ENTER_STANDOUT;
    454 	if (!EXIT_ATTRIBUTES && EXIT_STANDOUT)
    455 		EXIT_ATTRIBUTES = EXIT_STANDOUT;
    456 
    457 	/*
    458 	 * Note that we use REVERSE for the alternate character set,
    459 	 * not the as/ae capabilities.  This is because we are modelling
    460 	 * the model 37 teletype (since that's what nroff outputs) and
    461 	 * the typical as/ae is more of a graphics set, not the greek
    462 	 * letters the 37 has.
    463 	 */
    464 
    465 	UNDER_CHAR =		t_agetstr(info, "uc");
    466 	must_use_uc = (UNDER_CHAR && !ENTER_UNDERLINE);
    467 }
    468 
    469 int
    470 outchar(c)
    471 	int c;
    472 {
    473 	return (putchar(c & 0177));
    474 }
    475 
    476 static int curmode = 0;
    477 
    478 void
    479 outc(c)
    480 	int c;
    481 {
    482 	putchar(c);
    483 	if (must_use_uc && (curmode&UNDERL)) {
    484 		PRINT(CURS_LEFT);
    485 		PRINT(UNDER_CHAR);
    486 	}
    487 }
    488 
    489 void
    490 setulmode(newmode)
    491 	int newmode;
    492 {
    493 	if (!iflag) {
    494 		if (curmode != NORMAL && newmode != NORMAL)
    495 			setulmode(NORMAL);
    496 		switch (newmode) {
    497 		case NORMAL:
    498 			switch(curmode) {
    499 			case NORMAL:
    500 				break;
    501 			case UNDERL:
    502 				PRINT(EXIT_UNDERLINE);
    503 				break;
    504 			default:
    505 				/* This includes standout */
    506 				PRINT(EXIT_ATTRIBUTES);
    507 				break;
    508 			}
    509 			break;
    510 		case ALTSET:
    511 			PRINT(ENTER_REVERSE);
    512 			break;
    513 		case SUPERSC:
    514 			/*
    515 			 * This only works on a few terminals.
    516 			 * It should be fixed.
    517 			 */
    518 			PRINT(ENTER_UNDERLINE);
    519 			PRINT(ENTER_DIM);
    520 			break;
    521 		case SUBSC:
    522 			PRINT(ENTER_DIM);
    523 			break;
    524 		case UNDERL:
    525 			PRINT(ENTER_UNDERLINE);
    526 			break;
    527 		case BOLD:
    528 			PRINT(ENTER_BOLD);
    529 			break;
    530 		default:
    531 			/*
    532 			 * We should have some provision here for multiple modes
    533 			 * on at once.  This will have to come later.
    534 			 */
    535 			PRINT(ENTER_STANDOUT);
    536 			break;
    537 		}
    538 	}
    539 	curmode = newmode;
    540 }
    541