1 /* $NetBSD: ul.c,v 1.19 2016/06/23 03:58:13 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.19 2016/06/23 03:58:13 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 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 case ' ': 226 col++; 227 if (col > maxcol) 228 maxcol = col; 229 if (col >= obuf_size) 230 alloc_buf(&obuf, &obuf_size); 231 continue; 232 233 case '\n': 234 flushln(obuf, obuf_size); 235 continue; 236 237 case '\f': 238 flushln(obuf, obuf_size); 239 putchar('\f'); 240 continue; 241 242 default: 243 if (c < ' ') /* non printing */ 244 continue; 245 if (obuf[col].c_char == '\0') { 246 obuf[col].c_char = c; 247 obuf[col].c_mode = mode; 248 } else if (obuf[col].c_char == '_') { 249 obuf[col].c_char = c; 250 obuf[col].c_mode |= UNDERL|mode; 251 } else if (obuf[col].c_char == c) 252 obuf[col].c_mode |= BOLD|mode; 253 else 254 obuf[col].c_mode = mode; 255 col++; 256 if (col > maxcol) 257 maxcol = col; 258 if (col >= obuf_size) 259 alloc_buf(&obuf, &obuf_size); 260 continue; 261 } 262 if (maxcol) 263 flushln(obuf, obuf_size); 264 265 free(obuf); 266 } 267 268 static void 269 flushln(struct CHAR *obuf, size_t obuf_size) 270 { 271 int lastmode; 272 size_t i; 273 int hadmodes = 0; 274 275 lastmode = NORMAL; 276 for (i=0; i<maxcol; i++) { 277 if (obuf[i].c_mode != lastmode) { 278 hadmodes++; 279 setulmode(obuf[i].c_mode); 280 lastmode = obuf[i].c_mode; 281 } 282 if (obuf[i].c_char == '\0') { 283 if (upln) { 284 PRINT(cursor_right); 285 } 286 else { 287 outc(' '); 288 } 289 } else 290 outc(obuf[i].c_char); 291 } 292 if (lastmode != NORMAL) { 293 setulmode(0); 294 } 295 if (must_overstrike && hadmodes) 296 overstrike(obuf); 297 putchar('\n'); 298 if (iflag && hadmodes) 299 iattr(obuf); 300 (void)fflush(stdout); 301 if (upln) 302 upln--; 303 initbuf(obuf, obuf_size); 304 } 305 306 /* 307 * For terminals that can overstrike, overstrike underlines and bolds. 308 * We don't do anything with halfline ups and downs, or Greek. 309 */ 310 static void 311 overstrike(struct CHAR *obuf) 312 { 313 size_t i; 314 char lbuf[256]; 315 char *cp = lbuf; 316 int hadbold=0; 317 318 /* Set up overstrike buffer */ 319 for (i=0; i<maxcol; i++) 320 switch (obuf[i].c_mode) { 321 case NORMAL: 322 default: 323 *cp++ = ' '; 324 break; 325 case UNDERL: 326 *cp++ = '_'; 327 break; 328 case BOLD: 329 *cp++ = obuf[i].c_char; 330 hadbold=1; 331 break; 332 } 333 putchar('\r'); 334 for (*cp=' '; *cp==' '; cp--) 335 *cp = 0; 336 for (cp=lbuf; *cp; cp++) 337 putchar(*cp); 338 if (hadbold) { 339 putchar('\r'); 340 for (cp=lbuf; *cp; cp++) 341 putchar(*cp=='_' ? ' ' : *cp); 342 putchar('\r'); 343 for (cp=lbuf; *cp; cp++) 344 putchar(*cp=='_' ? ' ' : *cp); 345 } 346 } 347 348 static void 349 iattr(struct CHAR *obuf) 350 { 351 size_t i; 352 char lbuf[256]; 353 char *cp = lbuf; 354 355 for (i=0; i<maxcol; i++) 356 switch (obuf[i].c_mode) { 357 case NORMAL: *cp++ = ' '; break; 358 case ALTSET: *cp++ = 'g'; break; 359 case SUPERSC: *cp++ = '^'; break; 360 case SUBSC: *cp++ = 'v'; break; 361 case UNDERL: *cp++ = '_'; break; 362 case BOLD: *cp++ = '!'; break; 363 default: *cp++ = 'X'; break; 364 } 365 for (*cp=' '; *cp==' '; cp--) 366 *cp = 0; 367 for (cp=lbuf; *cp; cp++) 368 putchar(*cp); 369 putchar('\n'); 370 } 371 372 static void 373 initbuf(struct CHAR *obuf, size_t obuf_size) 374 { 375 376 memset(obuf, 0, obuf_size * sizeof(*obuf)); /* depends on NORMAL == 0 */ 377 col = 0; 378 maxcol = 0; 379 set_mode(); 380 } 381 382 static void 383 set_mode(void) 384 { 385 mode &= ALTSET; 386 } 387 388 static void 389 fwd(struct CHAR *obuf, size_t obuf_size) 390 { 391 int oldcol, oldmax; 392 393 oldcol = col; 394 oldmax = maxcol; 395 flushln(obuf, obuf_size); 396 col = oldcol; 397 maxcol = oldmax; 398 } 399 400 static void 401 reverse(struct CHAR *obuf, size_t obuf_size) 402 { 403 upln++; 404 fwd(obuf, obuf_size); 405 PRINT(cursor_up); 406 PRINT(cursor_up); 407 upln++; 408 } 409 410 static int 411 outchar(int c) 412 { 413 return (putchar(c & 0177)); 414 } 415 416 static int curmode = 0; 417 418 static void 419 outc(int c) 420 { 421 putchar(c); 422 if (underline_char && !enter_underline_mode && (curmode & UNDERL)) { 423 if (cursor_left) 424 PRINT(cursor_left); 425 else 426 putchar('\b'); 427 PRINT(underline_char); 428 } 429 } 430 431 static void 432 setulmode(int newmode) 433 { 434 if (!iflag) { 435 if (curmode != NORMAL && newmode != NORMAL) 436 setulmode(NORMAL); 437 switch (newmode) { 438 case NORMAL: 439 switch(curmode) { 440 case NORMAL: 441 break; 442 case UNDERL: 443 if (enter_underline_mode) 444 PRINT(exit_underline_mode); 445 else 446 PRINT(exit_standout_mode); 447 break; 448 default: 449 /* This includes standout */ 450 if (exit_attribute_mode) 451 PRINT(exit_attribute_mode); 452 else 453 PRINT(exit_standout_mode); 454 break; 455 } 456 break; 457 case ALTSET: 458 if (enter_reverse_mode) 459 PRINT(enter_reverse_mode); 460 else 461 PRINT(enter_standout_mode); 462 break; 463 case SUPERSC: 464 /* 465 * This only works on a few terminals. 466 * It should be fixed. 467 */ 468 PRINT(enter_underline_mode); 469 PRINT(enter_dim_mode); 470 break; 471 case SUBSC: 472 if (enter_dim_mode) 473 PRINT(enter_dim_mode); 474 else 475 PRINT(enter_standout_mode); 476 break; 477 case UNDERL: 478 if (enter_underline_mode) 479 PRINT(enter_underline_mode); 480 else 481 PRINT(enter_standout_mode); 482 break; 483 case BOLD: 484 if (enter_bold_mode) 485 PRINT(enter_bold_mode); 486 else 487 PRINT(enter_reverse_mode); 488 break; 489 default: 490 /* 491 * We should have some provision here for multiple modes 492 * on at once. This will have to come later. 493 */ 494 PRINT(enter_standout_mode); 495 break; 496 } 497 } 498 curmode = newmode; 499 } 500 501 /* 502 * Reallocates the buffer pointed to by *buf and sets 503 * the newly allocated set of bytes to 0. 504 */ 505 static void 506 alloc_buf(struct CHAR **buf, size_t *size) 507 { 508 size_t osize = *size; 509 *size += MAXBUF; 510 ereallocarr(buf, *size, sizeof(**buf)); 511 memset(*buf + osize, 0, (*size - osize) * sizeof(**buf)); 512 } 513