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