Home | History | Annotate | Line # | Download | only in ul
ul.c revision 1.6
      1 /*	$NetBSD: ul.c,v 1.6 1997/10/20 02:08:29 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. 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.6 1997/10/20 02:08:29 lukem 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 			else
    302 				outc(' ');
    303 		} else
    304 			outc(obuf[i].c_char);
    305 	}
    306 	if (lastmode != NORMAL) {
    307 		setulmode(0);
    308 	}
    309 	if (must_overstrike && hadmodes)
    310 		overstrike();
    311 	putchar('\n');
    312 	if (iflag && hadmodes)
    313 		iattr();
    314 	(void)fflush(stdout);
    315 	if (upln)
    316 		upln--;
    317 	initbuf();
    318 }
    319 
    320 /*
    321  * For terminals that can overstrike, overstrike underlines and bolds.
    322  * We don't do anything with halfline ups and downs, or Greek.
    323  */
    324 void
    325 overstrike()
    326 {
    327 	int i;
    328 	char lbuf[256];
    329 	char *cp = lbuf;
    330 	int hadbold=0;
    331 
    332 	/* Set up overstrike buffer */
    333 	for (i=0; i<maxcol; i++)
    334 		switch (obuf[i].c_mode) {
    335 		case NORMAL:
    336 		default:
    337 			*cp++ = ' ';
    338 			break;
    339 		case UNDERL:
    340 			*cp++ = '_';
    341 			break;
    342 		case BOLD:
    343 			*cp++ = obuf[i].c_char;
    344 			hadbold=1;
    345 			break;
    346 		}
    347 	putchar('\r');
    348 	for (*cp=' '; *cp==' '; cp--)
    349 		*cp = 0;
    350 	for (cp=lbuf; *cp; cp++)
    351 		putchar(*cp);
    352 	if (hadbold) {
    353 		putchar('\r');
    354 		for (cp=lbuf; *cp; cp++)
    355 			putchar(*cp=='_' ? ' ' : *cp);
    356 		putchar('\r');
    357 		for (cp=lbuf; *cp; cp++)
    358 			putchar(*cp=='_' ? ' ' : *cp);
    359 	}
    360 }
    361 
    362 void
    363 iattr()
    364 {
    365 	int i;
    366 	char lbuf[256];
    367 	char *cp = lbuf;
    368 
    369 	for (i=0; i<maxcol; i++)
    370 		switch (obuf[i].c_mode) {
    371 		case NORMAL:	*cp++ = ' '; break;
    372 		case ALTSET:	*cp++ = 'g'; break;
    373 		case SUPERSC:	*cp++ = '^'; break;
    374 		case SUBSC:	*cp++ = 'v'; break;
    375 		case UNDERL:	*cp++ = '_'; break;
    376 		case BOLD:	*cp++ = '!'; break;
    377 		default:	*cp++ = 'X'; break;
    378 		}
    379 	for (*cp=' '; *cp==' '; cp--)
    380 		*cp = 0;
    381 	for (cp=lbuf; *cp; cp++)
    382 		putchar(*cp);
    383 	putchar('\n');
    384 }
    385 
    386 void
    387 initbuf()
    388 {
    389 
    390 	memset((char *)obuf, 0, sizeof (obuf));	/* depends on NORMAL == 0 */
    391 	col = 0;
    392 	maxcol = 0;
    393 	mode &= ALTSET;
    394 }
    395 
    396 void
    397 fwd()
    398 {
    399 	int oldcol, oldmax;
    400 
    401 	oldcol = col;
    402 	oldmax = maxcol;
    403 	flushln();
    404 	col = oldcol;
    405 	maxcol = oldmax;
    406 }
    407 
    408 void
    409 reverse()
    410 {
    411 	upln++;
    412 	fwd();
    413 	PRINT(CURS_UP);
    414 	PRINT(CURS_UP);
    415 	upln++;
    416 }
    417 
    418 void
    419 initcap()
    420 {
    421 	static char tcapbuf[512];
    422 	char *bp = tcapbuf;
    423 
    424 	/* This nonsense attempts to work with both old and new termcap */
    425 	CURS_UP =		tgetstr("up", &bp);
    426 	CURS_RIGHT =		tgetstr("ri", &bp);
    427 	if (CURS_RIGHT == NULL)
    428 		CURS_RIGHT =	tgetstr("nd", &bp);
    429 	CURS_LEFT =		tgetstr("le", &bp);
    430 	if (CURS_LEFT == NULL)
    431 		CURS_LEFT =	tgetstr("bc", &bp);
    432 	if (CURS_LEFT == NULL && tgetflag("bs"))
    433 		CURS_LEFT =	"\b";
    434 
    435 	ENTER_STANDOUT =	tgetstr("so", &bp);
    436 	EXIT_STANDOUT =		tgetstr("se", &bp);
    437 	ENTER_UNDERLINE =	tgetstr("us", &bp);
    438 	EXIT_UNDERLINE =	tgetstr("ue", &bp);
    439 	ENTER_DIM =		tgetstr("mh", &bp);
    440 	ENTER_BOLD =		tgetstr("md", &bp);
    441 	ENTER_REVERSE =		tgetstr("mr", &bp);
    442 	EXIT_ATTRIBUTES =	tgetstr("me", &bp);
    443 
    444 	if (!ENTER_BOLD && ENTER_REVERSE)
    445 		ENTER_BOLD = ENTER_REVERSE;
    446 	if (!ENTER_BOLD && ENTER_STANDOUT)
    447 		ENTER_BOLD = ENTER_STANDOUT;
    448 	if (!ENTER_UNDERLINE && ENTER_STANDOUT) {
    449 		ENTER_UNDERLINE = ENTER_STANDOUT;
    450 		EXIT_UNDERLINE = EXIT_STANDOUT;
    451 	}
    452 	if (!ENTER_DIM && ENTER_STANDOUT)
    453 		ENTER_DIM = ENTER_STANDOUT;
    454 	if (!ENTER_REVERSE && ENTER_STANDOUT)
    455 		ENTER_REVERSE = ENTER_STANDOUT;
    456 	if (!EXIT_ATTRIBUTES && EXIT_STANDOUT)
    457 		EXIT_ATTRIBUTES = EXIT_STANDOUT;
    458 
    459 	/*
    460 	 * Note that we use REVERSE for the alternate character set,
    461 	 * not the as/ae capabilities.  This is because we are modelling
    462 	 * the model 37 teletype (since that's what nroff outputs) and
    463 	 * the typical as/ae is more of a graphics set, not the greek
    464 	 * letters the 37 has.
    465 	 */
    466 
    467 	UNDER_CHAR =		tgetstr("uc", &bp);
    468 	must_use_uc = (UNDER_CHAR && !ENTER_UNDERLINE);
    469 }
    470 
    471 void
    472 outchar(c)
    473 	int c;
    474 {
    475 	putchar(c & 0177);
    476 }
    477 
    478 static int curmode = 0;
    479 
    480 void
    481 outc(c)
    482 	int c;
    483 {
    484 	putchar(c);
    485 	if (must_use_uc && (curmode&UNDERL)) {
    486 		PRINT(CURS_LEFT);
    487 		PRINT(UNDER_CHAR);
    488 	}
    489 }
    490 
    491 void
    492 setulmode(newmode)
    493 	int newmode;
    494 {
    495 	if (!iflag) {
    496 		if (curmode != NORMAL && newmode != NORMAL)
    497 			setulmode(NORMAL);
    498 		switch (newmode) {
    499 		case NORMAL:
    500 			switch(curmode) {
    501 			case NORMAL:
    502 				break;
    503 			case UNDERL:
    504 				PRINT(EXIT_UNDERLINE);
    505 				break;
    506 			default:
    507 				/* This includes standout */
    508 				PRINT(EXIT_ATTRIBUTES);
    509 				break;
    510 			}
    511 			break;
    512 		case ALTSET:
    513 			PRINT(ENTER_REVERSE);
    514 			break;
    515 		case SUPERSC:
    516 			/*
    517 			 * This only works on a few terminals.
    518 			 * It should be fixed.
    519 			 */
    520 			PRINT(ENTER_UNDERLINE);
    521 			PRINT(ENTER_DIM);
    522 			break;
    523 		case SUBSC:
    524 			PRINT(ENTER_DIM);
    525 			break;
    526 		case UNDERL:
    527 			PRINT(ENTER_UNDERLINE);
    528 			break;
    529 		case BOLD:
    530 			PRINT(ENTER_BOLD);
    531 			break;
    532 		default:
    533 			/*
    534 			 * We should have some provision here for multiple modes
    535 			 * on at once.  This will have to come later.
    536 			 */
    537 			PRINT(ENTER_STANDOUT);
    538 			break;
    539 		}
    540 	}
    541 	curmode = newmode;
    542 }
    543