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