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