Home | History | Annotate | Line # | Download | only in ul
ul.c revision 1.7
      1 /*	$NetBSD: ul.c,v 1.7 1998/12/19 23:21:24 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.7 1998/12/19 23:21:24 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 int	must_use_uc, must_overstrike;
     71 char	*CURS_UP, *CURS_RIGHT, *CURS_LEFT,
     72 	*ENTER_STANDOUT, *EXIT_STANDOUT, *ENTER_UNDERLINE, *EXIT_UNDERLINE,
     73 	*ENTER_DIM, *ENTER_BOLD, *ENTER_REVERSE, *UNDER_CHAR, *EXIT_ATTRIBUTES;
     74 
     75 struct	CHAR	{
     76 	char	c_mode;
     77 	char	c_char;
     78 } ;
     79 
     80 struct	CHAR	obuf[MAXBUF];
     81 int	col, maxcol;
     82 int	mode;
     83 int	halfpos;
     84 int	upln;
     85 int	iflag;
     86 
     87 int	main __P((int, char **));
     88 void	filter __P((FILE *));
     89 void	flushln __P((void));
     90 void	fwd __P((void));
     91 void	iattr __P((void));
     92 void	initbuf __P((void));
     93 void	initcap __P((void));
     94 void	outc __P((int));
     95 void	outchar __P((int));
     96 void	overstrike __P((void));
     97 void	reverse __P((void));
     98 void	setulmode __P((int));
     99 
    100 
    101 #define	PRINT(s)	if (s == NULL) /* void */; else tputs(s, 1, outchar)
    102 
    103 int
    104 main(argc, argv)
    105 	int argc;
    106 	char **argv;
    107 {
    108 	extern int optind;
    109 	extern char *optarg;
    110 	int c;
    111 	char *termtype;
    112 	FILE *f;
    113 	char termcap[1024];
    114 
    115 	termtype = getenv("TERM");
    116 	if (termtype == NULL || (argv[0][0] == 'c' && !isatty(1)))
    117 		termtype = "lpr";
    118 	while ((c=getopt(argc, argv, "it:T:")) != -1)
    119 		switch(c) {
    120 
    121 		case 't':
    122 		case 'T': /* for nroff compatibility */
    123 				termtype = optarg;
    124 			break;
    125 		case 'i':
    126 			iflag = 1;
    127 			break;
    128 
    129 		default:
    130 			fprintf(stderr,
    131 				"usage: %s [ -i ] [ -tTerm ] file...\n",
    132 				argv[0]);
    133 			exit(1);
    134 		}
    135 
    136 	switch(tgetent(termcap, termtype)) {
    137 
    138 	case 1:
    139 		break;
    140 
    141 	default:
    142 		fprintf(stderr,"trouble reading termcap");
    143 		/* fall through to ... */
    144 
    145 	case 0:
    146 		/* No such terminal type - assume dumb */
    147 		(void)strcpy(termcap, "dumb:os:col#80:cr=^M:sf=^J:am:");
    148 		break;
    149 	}
    150 	initcap();
    151 	if (    (tgetflag("os") && ENTER_BOLD==NULL ) ||
    152 		(tgetflag("ul") && ENTER_UNDERLINE==NULL && UNDER_CHAR==NULL))
    153 			must_overstrike = 1;
    154 	initbuf();
    155 	if (optind == argc)
    156 		filter(stdin);
    157 	else for (; optind<argc; optind++) {
    158 		f = fopen(argv[optind],"r");
    159 		if (f == NULL) {
    160 			perror(argv[optind]);
    161 			exit(1);
    162 		}
    163 
    164 		filter(f);
    165 		fclose(f);
    166 	}
    167 	exit(0);
    168 }
    169 
    170 void
    171 filter(f)
    172 	FILE *f;
    173 {
    174 	int c;
    175 
    176 	while ((c = getc(f)) != EOF) switch(c) {
    177 
    178 	case '\b':
    179 		if (col > 0)
    180 			col--;
    181 		continue;
    182 
    183 	case '\t':
    184 		col = (col+8) & ~07;
    185 		if (col > maxcol)
    186 			maxcol = col;
    187 		continue;
    188 
    189 	case '\r':
    190 		col = 0;
    191 		continue;
    192 
    193 	case SO:
    194 		mode |= ALTSET;
    195 		continue;
    196 
    197 	case SI:
    198 		mode &= ~ALTSET;
    199 		continue;
    200 
    201 	case IESC:
    202 		switch (c = getc(f)) {
    203 
    204 		case HREV:
    205 			if (halfpos == 0) {
    206 				mode |= SUPERSC;
    207 				halfpos--;
    208 			} else if (halfpos > 0) {
    209 				mode &= ~SUBSC;
    210 				halfpos--;
    211 			} else {
    212 				halfpos = 0;
    213 				reverse();
    214 			}
    215 			continue;
    216 
    217 		case HFWD:
    218 			if (halfpos == 0) {
    219 				mode |= SUBSC;
    220 				halfpos++;
    221 			} else if (halfpos < 0) {
    222 				mode &= ~SUPERSC;
    223 				halfpos++;
    224 			} else {
    225 				halfpos = 0;
    226 				fwd();
    227 			}
    228 			continue;
    229 
    230 		case FREV:
    231 			reverse();
    232 			continue;
    233 
    234 		default:
    235 			fprintf(stderr,
    236 				"Unknown escape sequence in input: %o, %o\n",
    237 				IESC, c);
    238 			exit(1);
    239 		}
    240 		continue;
    241 
    242 	case '_':
    243 		if (obuf[col].c_char)
    244 			obuf[col].c_mode |= UNDERL | mode;
    245 		else
    246 			obuf[col].c_char = '_';
    247 	case ' ':
    248 		col++;
    249 		if (col > maxcol)
    250 			maxcol = col;
    251 		continue;
    252 
    253 	case '\n':
    254 		flushln();
    255 		continue;
    256 
    257 	case '\f':
    258 		flushln();
    259 		putchar('\f');
    260 		continue;
    261 
    262 	default:
    263 		if (c < ' ')	/* non printing */
    264 			continue;
    265 		if (obuf[col].c_char == '\0') {
    266 			obuf[col].c_char = c;
    267 			obuf[col].c_mode = mode;
    268 		} else if (obuf[col].c_char == '_') {
    269 			obuf[col].c_char = c;
    270 			obuf[col].c_mode |= UNDERL|mode;
    271 		} else if (obuf[col].c_char == c)
    272 			obuf[col].c_mode |= BOLD|mode;
    273 		else
    274 			obuf[col].c_mode = mode;
    275 		col++;
    276 		if (col > maxcol)
    277 			maxcol = col;
    278 		continue;
    279 	}
    280 	if (maxcol)
    281 		flushln();
    282 }
    283 
    284 void
    285 flushln()
    286 {
    287 	int lastmode;
    288 	int i;
    289 	int hadmodes = 0;
    290 
    291 	lastmode = NORMAL;
    292 	for (i=0; i<maxcol; i++) {
    293 		if (obuf[i].c_mode != lastmode) {
    294 			hadmodes++;
    295 			setulmode(obuf[i].c_mode);
    296 			lastmode = obuf[i].c_mode;
    297 		}
    298 		if (obuf[i].c_char == '\0') {
    299 			if (upln) {
    300 				PRINT(CURS_RIGHT);
    301 			}
    302 			else {
    303 				outc(' ');
    304 			}
    305 		} else
    306 			outc(obuf[i].c_char);
    307 	}
    308 	if (lastmode != NORMAL) {
    309 		setulmode(0);
    310 	}
    311 	if (must_overstrike && hadmodes)
    312 		overstrike();
    313 	putchar('\n');
    314 	if (iflag && hadmodes)
    315 		iattr();
    316 	(void)fflush(stdout);
    317 	if (upln)
    318 		upln--;
    319 	initbuf();
    320 }
    321 
    322 /*
    323  * For terminals that can overstrike, overstrike underlines and bolds.
    324  * We don't do anything with halfline ups and downs, or Greek.
    325  */
    326 void
    327 overstrike()
    328 {
    329 	int i;
    330 	char lbuf[256];
    331 	char *cp = lbuf;
    332 	int hadbold=0;
    333 
    334 	/* Set up overstrike buffer */
    335 	for (i=0; i<maxcol; i++)
    336 		switch (obuf[i].c_mode) {
    337 		case NORMAL:
    338 		default:
    339 			*cp++ = ' ';
    340 			break;
    341 		case UNDERL:
    342 			*cp++ = '_';
    343 			break;
    344 		case BOLD:
    345 			*cp++ = obuf[i].c_char;
    346 			hadbold=1;
    347 			break;
    348 		}
    349 	putchar('\r');
    350 	for (*cp=' '; *cp==' '; cp--)
    351 		*cp = 0;
    352 	for (cp=lbuf; *cp; cp++)
    353 		putchar(*cp);
    354 	if (hadbold) {
    355 		putchar('\r');
    356 		for (cp=lbuf; *cp; cp++)
    357 			putchar(*cp=='_' ? ' ' : *cp);
    358 		putchar('\r');
    359 		for (cp=lbuf; *cp; cp++)
    360 			putchar(*cp=='_' ? ' ' : *cp);
    361 	}
    362 }
    363 
    364 void
    365 iattr()
    366 {
    367 	int i;
    368 	char lbuf[256];
    369 	char *cp = lbuf;
    370 
    371 	for (i=0; i<maxcol; i++)
    372 		switch (obuf[i].c_mode) {
    373 		case NORMAL:	*cp++ = ' '; break;
    374 		case ALTSET:	*cp++ = 'g'; break;
    375 		case SUPERSC:	*cp++ = '^'; break;
    376 		case SUBSC:	*cp++ = 'v'; break;
    377 		case UNDERL:	*cp++ = '_'; break;
    378 		case BOLD:	*cp++ = '!'; break;
    379 		default:	*cp++ = 'X'; break;
    380 		}
    381 	for (*cp=' '; *cp==' '; cp--)
    382 		*cp = 0;
    383 	for (cp=lbuf; *cp; cp++)
    384 		putchar(*cp);
    385 	putchar('\n');
    386 }
    387 
    388 void
    389 initbuf()
    390 {
    391 
    392 	memset((char *)obuf, 0, sizeof (obuf));	/* depends on NORMAL == 0 */
    393 	col = 0;
    394 	maxcol = 0;
    395 	mode &= ALTSET;
    396 }
    397 
    398 void
    399 fwd()
    400 {
    401 	int oldcol, oldmax;
    402 
    403 	oldcol = col;
    404 	oldmax = maxcol;
    405 	flushln();
    406 	col = oldcol;
    407 	maxcol = oldmax;
    408 }
    409 
    410 void
    411 reverse()
    412 {
    413 	upln++;
    414 	fwd();
    415 	PRINT(CURS_UP);
    416 	PRINT(CURS_UP);
    417 	upln++;
    418 }
    419 
    420 void
    421 initcap()
    422 {
    423 	static char tcapbuf[512];
    424 	char *bp = tcapbuf;
    425 
    426 	/* This nonsense attempts to work with both old and new termcap */
    427 	CURS_UP =		tgetstr("up", &bp);
    428 	CURS_RIGHT =		tgetstr("ri", &bp);
    429 	if (CURS_RIGHT == NULL)
    430 		CURS_RIGHT =	tgetstr("nd", &bp);
    431 	CURS_LEFT =		tgetstr("le", &bp);
    432 	if (CURS_LEFT == NULL)
    433 		CURS_LEFT =	tgetstr("bc", &bp);
    434 	if (CURS_LEFT == NULL && tgetflag("bs"))
    435 		CURS_LEFT =	"\b";
    436 
    437 	ENTER_STANDOUT =	tgetstr("so", &bp);
    438 	EXIT_STANDOUT =		tgetstr("se", &bp);
    439 	ENTER_UNDERLINE =	tgetstr("us", &bp);
    440 	EXIT_UNDERLINE =	tgetstr("ue", &bp);
    441 	ENTER_DIM =		tgetstr("mh", &bp);
    442 	ENTER_BOLD =		tgetstr("md", &bp);
    443 	ENTER_REVERSE =		tgetstr("mr", &bp);
    444 	EXIT_ATTRIBUTES =	tgetstr("me", &bp);
    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 =		tgetstr("uc", &bp);
    470 	must_use_uc = (UNDER_CHAR && !ENTER_UNDERLINE);
    471 }
    472 
    473 void
    474 outchar(c)
    475 	int c;
    476 {
    477 	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