Home | History | Annotate | Line # | Download | only in ul
ul.c revision 1.18
      1 /*	$NetBSD: ul.c,v 1.18 2016/06/13 14:58:57 abhinav 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.18 2016/06/13 14:58:57 abhinav Exp $");
     43 #endif /* not lint */
     44 
     45 #include <err.h>
     46 #include <stdio.h>
     47 #include <stdlib.h>
     48 #include <string.h>
     49 #include <term.h>
     50 #include <unistd.h>
     51 #include <util.h>
     52 
     53 #define	IESC	'\033'
     54 #define	SO	'\016'
     55 #define	SI	'\017'
     56 #define	HFWD	'9'
     57 #define	HREV	'8'
     58 #define	FREV	'7'
     59 #define	MAXBUF	512
     60 
     61 #define	NORMAL	000
     62 #define	ALTSET	001	/* Reverse */
     63 #define	SUPERSC	002	/* Dim */
     64 #define	SUBSC	004	/* Dim | Ul */
     65 #define	UNDERL	010	/* Ul */
     66 #define	BOLD	020	/* Bold */
     67 
     68 struct tinfo *info;
     69 int	must_overstrike;
     70 const char *CURS_UP, *CURS_RIGHT, *CURS_LEFT,
     71 	*ENTER_STANDOUT, *EXIT_STANDOUT, *ENTER_UNDERLINE, *EXIT_UNDERLINE,
     72 	*ENTER_DIM, *ENTER_BOLD, *ENTER_REVERSE, *UNDER_CHAR, *EXIT_ATTRIBUTES;
     73 
     74 struct	CHAR	{
     75 	char	c_mode;
     76 	char	c_char;
     77 } ;
     78 
     79 size_t col, maxcol;
     80 int	mode;
     81 int	halfpos;
     82 int	upln;
     83 int	iflag;
     84 
     85 void filter(FILE *);
     86 void flushln(struct CHAR *, size_t);
     87 void fwd(struct CHAR *, size_t);
     88 void iattr(struct CHAR *);
     89 void initbuf(struct CHAR *, size_t);
     90 void initcap(void);
     91 void outc(int);
     92 int outchar(int);
     93 void overstrike(struct CHAR *);
     94 void reverse(struct CHAR *, size_t);
     95 void setulmode(int);
     96 static void alloc_buf(struct CHAR **, size_t *);
     97 static void set_mode(void);
     98 
     99 
    100 #define	PRINT(s)	if (s == NULL) /* void */; else tputs(s, 1, outchar)
    101 
    102 int
    103 main(int argc, 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 		set_mode();
    135 	}
    136 	if (optind == argc)
    137 		filter(stdin);
    138 	else {
    139 		for (; optind < argc; optind++) {
    140 			f = fopen(argv[optind], "r");
    141 			if (f == NULL)
    142 				err(EXIT_FAILURE, "Failed to open `%s'", argv[optind]);
    143 			filter(f);
    144 			fclose(f);
    145 		}
    146 	}
    147 	exit(0);
    148 }
    149 
    150 void
    151 filter(FILE *f)
    152 {
    153 	int c;
    154 	struct	CHAR *obuf = NULL;
    155 	size_t obuf_size = 0;
    156 	alloc_buf(&obuf, &obuf_size);
    157 
    158 	while ((c = getc(f)) != EOF) switch(c) {
    159 
    160 	case '\b':
    161 		if (col > 0)
    162 			col--;
    163 		continue;
    164 
    165 	case '\t':
    166 		col = (col+8) & ~07;
    167 		if (col > maxcol)
    168 			maxcol = col;
    169 		if (col >= obuf_size)
    170 			alloc_buf(&obuf, &obuf_size);
    171 		continue;
    172 
    173 	case '\r':
    174 		col = 0;
    175 		continue;
    176 
    177 	case SO:
    178 		mode |= ALTSET;
    179 		continue;
    180 
    181 	case SI:
    182 		mode &= ~ALTSET;
    183 		continue;
    184 
    185 	case IESC:
    186 		switch (c = getc(f)) {
    187 
    188 		case HREV:
    189 			if (halfpos == 0) {
    190 				mode |= SUPERSC;
    191 				halfpos--;
    192 			} else if (halfpos > 0) {
    193 				mode &= ~SUBSC;
    194 				halfpos--;
    195 			} else {
    196 				halfpos = 0;
    197 				reverse(obuf, obuf_size);
    198 			}
    199 			continue;
    200 
    201 		case HFWD:
    202 			if (halfpos == 0) {
    203 				mode |= SUBSC;
    204 				halfpos++;
    205 			} else if (halfpos < 0) {
    206 				mode &= ~SUPERSC;
    207 				halfpos++;
    208 			} else {
    209 				halfpos = 0;
    210 				fwd(obuf, obuf_size);
    211 			}
    212 			continue;
    213 
    214 		case FREV:
    215 			reverse(obuf, obuf_size);
    216 			continue;
    217 
    218 		default:
    219 			fprintf(stderr,
    220 				"Unknown escape sequence in input: %o, %o\n",
    221 				IESC, c);
    222 			exit(1);
    223 		}
    224 
    225 	case '_':
    226 		if (obuf[col].c_char)
    227 			obuf[col].c_mode |= UNDERL | mode;
    228 		else
    229 			obuf[col].c_char = '_';
    230 	case ' ':
    231 		col++;
    232 		if (col > maxcol)
    233 			maxcol = col;
    234 		if (col >= obuf_size)
    235 			alloc_buf(&obuf, &obuf_size);
    236 		continue;
    237 
    238 	case '\n':
    239 		flushln(obuf, obuf_size);
    240 		continue;
    241 
    242 	case '\f':
    243 		flushln(obuf, obuf_size);
    244 		putchar('\f');
    245 		continue;
    246 
    247 	default:
    248 		if (c < ' ')	/* non printing */
    249 			continue;
    250 		if (obuf[col].c_char == '\0') {
    251 			obuf[col].c_char = c;
    252 			obuf[col].c_mode = mode;
    253 		} else if (obuf[col].c_char == '_') {
    254 			obuf[col].c_char = c;
    255 			obuf[col].c_mode |= UNDERL|mode;
    256 		} else if (obuf[col].c_char == c)
    257 			obuf[col].c_mode |= BOLD|mode;
    258 		else
    259 			obuf[col].c_mode = mode;
    260 		col++;
    261 		if (col > maxcol)
    262 			maxcol = col;
    263 		if (col >= obuf_size)
    264 			alloc_buf(&obuf, &obuf_size);
    265 		continue;
    266 	}
    267 	if (maxcol)
    268 		flushln(obuf, obuf_size);
    269 
    270 	free(obuf);
    271 }
    272 
    273 void
    274 flushln(struct CHAR *obuf, size_t obuf_size)
    275 {
    276 	int lastmode;
    277 	size_t i;
    278 	int hadmodes = 0;
    279 
    280 	lastmode = NORMAL;
    281 	for (i=0; i<maxcol; i++) {
    282 		if (obuf[i].c_mode != lastmode) {
    283 			hadmodes++;
    284 			setulmode(obuf[i].c_mode);
    285 			lastmode = obuf[i].c_mode;
    286 		}
    287 		if (obuf[i].c_char == '\0') {
    288 			if (upln) {
    289 				PRINT(cursor_right);
    290 			}
    291 			else {
    292 				outc(' ');
    293 			}
    294 		} else
    295 			outc(obuf[i].c_char);
    296 	}
    297 	if (lastmode != NORMAL) {
    298 		setulmode(0);
    299 	}
    300 	if (must_overstrike && hadmodes)
    301 		overstrike(obuf);
    302 	putchar('\n');
    303 	if (iflag && hadmodes)
    304 		iattr(obuf);
    305 	(void)fflush(stdout);
    306 	if (upln)
    307 		upln--;
    308 	initbuf(obuf, obuf_size);
    309 }
    310 
    311 /*
    312  * For terminals that can overstrike, overstrike underlines and bolds.
    313  * We don't do anything with halfline ups and downs, or Greek.
    314  */
    315 void
    316 overstrike(struct CHAR *obuf)
    317 {
    318 	size_t i;
    319 	char lbuf[256];
    320 	char *cp = lbuf;
    321 	int hadbold=0;
    322 
    323 	/* Set up overstrike buffer */
    324 	for (i=0; i<maxcol; i++)
    325 		switch (obuf[i].c_mode) {
    326 		case NORMAL:
    327 		default:
    328 			*cp++ = ' ';
    329 			break;
    330 		case UNDERL:
    331 			*cp++ = '_';
    332 			break;
    333 		case BOLD:
    334 			*cp++ = obuf[i].c_char;
    335 			hadbold=1;
    336 			break;
    337 		}
    338 	putchar('\r');
    339 	for (*cp=' '; *cp==' '; cp--)
    340 		*cp = 0;
    341 	for (cp=lbuf; *cp; cp++)
    342 		putchar(*cp);
    343 	if (hadbold) {
    344 		putchar('\r');
    345 		for (cp=lbuf; *cp; cp++)
    346 			putchar(*cp=='_' ? ' ' : *cp);
    347 		putchar('\r');
    348 		for (cp=lbuf; *cp; cp++)
    349 			putchar(*cp=='_' ? ' ' : *cp);
    350 	}
    351 }
    352 
    353 void
    354 iattr(struct CHAR *obuf)
    355 {
    356 	size_t i;
    357 	char lbuf[256];
    358 	char *cp = lbuf;
    359 
    360 	for (i=0; i<maxcol; i++)
    361 		switch (obuf[i].c_mode) {
    362 		case NORMAL:	*cp++ = ' '; break;
    363 		case ALTSET:	*cp++ = 'g'; break;
    364 		case SUPERSC:	*cp++ = '^'; break;
    365 		case SUBSC:	*cp++ = 'v'; break;
    366 		case UNDERL:	*cp++ = '_'; break;
    367 		case BOLD:	*cp++ = '!'; break;
    368 		default:	*cp++ = 'X'; break;
    369 		}
    370 	for (*cp=' '; *cp==' '; cp--)
    371 		*cp = 0;
    372 	for (cp=lbuf; *cp; cp++)
    373 		putchar(*cp);
    374 	putchar('\n');
    375 }
    376 
    377 void
    378 initbuf(struct CHAR *obuf, size_t obuf_size)
    379 {
    380 
    381 	memset(obuf, 0, obuf_size * sizeof(*obuf));	/* depends on NORMAL == 0 */
    382 	col = 0;
    383 	maxcol = 0;
    384 	set_mode();
    385 }
    386 
    387 static void
    388 set_mode(void)
    389 {
    390 	mode &= ALTSET;
    391 }
    392 
    393 void
    394 fwd(struct CHAR *obuf, size_t obuf_size)
    395 {
    396 	int oldcol, oldmax;
    397 
    398 	oldcol = col;
    399 	oldmax = maxcol;
    400 	flushln(obuf, obuf_size);
    401 	col = oldcol;
    402 	maxcol = oldmax;
    403 }
    404 
    405 void
    406 reverse(struct CHAR *obuf, size_t obuf_size)
    407 {
    408 	upln++;
    409 	fwd(obuf, obuf_size);
    410 	PRINT(cursor_up);
    411 	PRINT(cursor_up);
    412 	upln++;
    413 }
    414 
    415 int
    416 outchar(int c)
    417 {
    418 	return (putchar(c & 0177));
    419 }
    420 
    421 static int curmode = 0;
    422 
    423 void
    424 outc(int c)
    425 {
    426 	putchar(c);
    427 	if (underline_char && !enter_underline_mode && (curmode & UNDERL)) {
    428 		if (cursor_left)
    429 			PRINT(cursor_left);
    430 		else
    431 			putchar('\b');
    432 		PRINT(underline_char);
    433 	}
    434 }
    435 
    436 void
    437 setulmode(int newmode)
    438 {
    439 	if (!iflag) {
    440 		if (curmode != NORMAL && newmode != NORMAL)
    441 			setulmode(NORMAL);
    442 		switch (newmode) {
    443 		case NORMAL:
    444 			switch(curmode) {
    445 			case NORMAL:
    446 				break;
    447 			case UNDERL:
    448 				if (enter_underline_mode)
    449 					PRINT(exit_underline_mode);
    450 				else
    451 					PRINT(exit_standout_mode);
    452 				break;
    453 			default:
    454 				/* This includes standout */
    455 				if (exit_attribute_mode)
    456 					PRINT(exit_attribute_mode);
    457 				else
    458 					PRINT(exit_standout_mode);
    459 				break;
    460 			}
    461 			break;
    462 		case ALTSET:
    463 			if (enter_reverse_mode)
    464 				PRINT(enter_reverse_mode);
    465 			else
    466 				PRINT(enter_standout_mode);
    467 			break;
    468 		case SUPERSC:
    469 			/*
    470 			 * This only works on a few terminals.
    471 			 * It should be fixed.
    472 			 */
    473 			PRINT(enter_underline_mode);
    474 			PRINT(enter_dim_mode);
    475 			break;
    476 		case SUBSC:
    477 			if (enter_dim_mode)
    478 				PRINT(enter_dim_mode);
    479 			else
    480 				PRINT(enter_standout_mode);
    481 			break;
    482 		case UNDERL:
    483 			if (enter_underline_mode)
    484 				PRINT(enter_underline_mode);
    485 			else
    486 				PRINT(enter_standout_mode);
    487 			break;
    488 		case BOLD:
    489 			if (enter_bold_mode)
    490 				PRINT(enter_bold_mode);
    491 			else
    492 				PRINT(enter_reverse_mode);
    493 			break;
    494 		default:
    495 			/*
    496 			 * We should have some provision here for multiple modes
    497 			 * on at once.  This will have to come later.
    498 			 */
    499 			PRINT(enter_standout_mode);
    500 			break;
    501 		}
    502 	}
    503 	curmode = newmode;
    504 }
    505 
    506 /*
    507  * Reallocates the buffer pointed to by *buf and sets
    508  * the newly allocated set of bytes to 0.
    509  */
    510 static void
    511 alloc_buf(struct CHAR **buf, size_t *size)
    512 {
    513         size_t osize = *size;
    514         *size += MAXBUF;
    515         ereallocarr(buf, *size, sizeof(**buf));
    516         memset(*buf + osize, 0, (*size - osize) * sizeof(**buf));
    517 }
    518