1 /* $OpenBSD: ul.c,v 1.6 2001/07/12 05:17:27 deraadt Exp $ */ 2 /* $NetBSD: ul.c,v 1.3 1994/12/07 00:28:24 jtc Exp $ */ 3 4 /* 5 * Copyright (c) 1980, 1993 6 * The Regents of the University of California. All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 3. All advertising materials mentioning features or use of this software 17 * must display the following acknowledgement: 18 * This product includes software developed by the University of 19 * California, Berkeley and its contributors. 20 * 4. Neither the name of the University nor the names of its contributors 21 * may be used to endorse or promote products derived from this software 22 * without specific prior written permission. 23 * 24 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 25 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 26 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 27 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 28 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 29 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 30 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 31 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 32 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 33 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 34 * SUCH DAMAGE. 35 */ 36 37 #ifndef lint 38 static char copyright[] = 39 "@(#) Copyright (c) 1980, 1993\n\ 40 The Regents of the University of California. All rights reserved.\n"; 41 #endif /* not lint */ 42 43 #ifndef lint 44 #if 0 45 static char sccsid[] = "@(#)ul.c 8.1 (Berkeley) 6/6/93"; 46 #endif 47 static char rcsid[] = "$OpenBSD: ul.c,v 1.6 2001/07/12 05:17:27 deraadt Exp $"; 48 #endif /* not lint */ 49 50 #include <stdio.h> 51 #include <stdlib.h> 52 #include <string.h> 53 #include <unistd.h> 54 #include <stdlib.h> 55 #include <curses.h> 56 #include <term.h> 57 58 #define IESC '\033' 59 #define SO '\016' 60 #define SI '\017' 61 #define HFWD '9' 62 #define HREV '8' 63 #define FREV '7' 64 #define MAXBUF 512 65 66 #define NORMAL 000 67 #define ALTSET 001 /* Reverse */ 68 #define SUPERSC 002 /* Dim */ 69 #define SUBSC 004 /* Dim | Ul */ 70 #define UNDERL 010 /* Ul */ 71 #define BOLD 020 /* Bold */ 72 73 int must_use_uc, must_overstrike; 74 char *CURS_UP, *CURS_RIGHT, *CURS_LEFT, 75 *ENTER_STANDOUT, *EXIT_STANDOUT, *ENTER_UNDERLINE, *EXIT_UNDERLINE, 76 *ENTER_DIM, *ENTER_BOLD, *ENTER_REVERSE, *UNDER_CHAR, *EXIT_ATTRIBUTES; 77 78 struct CHAR { 79 char c_mode; 80 char c_char; 81 } ; 82 83 struct CHAR obuf[MAXBUF]; 84 int col, maxcol; 85 int mode; 86 int halfpos; 87 int upln; 88 int iflag; 89 90 int outchar(int); 91 void initcap(void); 92 void initbuf(void); 93 void mfilter(FILE *); 94 void reverse(void); 95 void fwd(void); 96 void flushln(void); 97 void msetmode(int); 98 void outc(int); 99 void overstrike(void); 100 void iattr(void); 101 102 #define PRINT(s) \ 103 do { \ 104 if (s) \ 105 tputs(s, 1, outchar); \ 106 } while (0) 107 108 int 109 main(argc, argv) 110 int argc; 111 char **argv; 112 { 113 extern int optind; 114 extern char *optarg; 115 int c; 116 char *termtype; 117 FILE *f; 118 char termcap[1024]; 119 120 termtype = getenv("TERM"); 121 if (termtype == NULL || (argv[0][0] == 'c' && !isatty(1))) 122 termtype = "lpr"; 123 while ((c=getopt(argc, argv, "it:T:")) != -1) 124 switch(c) { 125 126 case 't': 127 case 'T': /* for nroff compatibility */ 128 termtype = optarg; 129 break; 130 case 'i': 131 iflag = 1; 132 break; 133 134 default: 135 fprintf(stderr, 136 "usage: %s [ -i ] [ -tTerm ] file...\n", 137 argv[0]); 138 exit(1); 139 } 140 141 switch(tgetent(termcap, termtype)) { 142 143 case 1: 144 break; 145 146 default: 147 fprintf(stderr,"trouble reading termcap"); 148 /* fall through to ... */ 149 150 case 0: 151 /* No such terminal type - assume dumb */ 152 (void)strcpy(termcap, "dumb:os:col#80:cr=^M:sf=^J:am:"); 153 break; 154 } 155 initcap(); 156 if ( (tgetflag("os") && ENTER_BOLD==NULL ) || 157 (tgetflag("ul") && ENTER_UNDERLINE==NULL && UNDER_CHAR==NULL)) 158 must_overstrike = 1; 159 initbuf(); 160 if (optind == argc) 161 mfilter(stdin); 162 else for (; optind<argc; optind++) { 163 f = fopen(argv[optind],"r"); 164 if (f == NULL) { 165 perror(argv[optind]); 166 exit(1); 167 } 168 169 mfilter(f); 170 fclose(f); 171 } 172 exit(0); 173 } 174 175 void 176 mfilter(f) 177 FILE *f; 178 { 179 int c; 180 181 while ((c = getc(f)) != EOF && col < MAXBUF) switch(c) { 182 183 case '\b': 184 if (col > 0) 185 col--; 186 continue; 187 188 case '\t': 189 col = (col+8) & ~07; 190 if (col > maxcol) 191 maxcol = col; 192 continue; 193 194 case '\r': 195 col = 0; 196 continue; 197 198 case SO: 199 mode |= ALTSET; 200 continue; 201 202 case SI: 203 mode &= ~ALTSET; 204 continue; 205 206 case IESC: 207 switch (c = getc(f)) { 208 209 case HREV: 210 if (halfpos == 0) { 211 mode |= SUPERSC; 212 halfpos--; 213 } else if (halfpos > 0) { 214 mode &= ~SUBSC; 215 halfpos--; 216 } else { 217 halfpos = 0; 218 reverse(); 219 } 220 continue; 221 222 case HFWD: 223 if (halfpos == 0) { 224 mode |= SUBSC; 225 halfpos++; 226 } else if (halfpos < 0) { 227 mode &= ~SUPERSC; 228 halfpos++; 229 } else { 230 halfpos = 0; 231 fwd(); 232 } 233 continue; 234 235 case FREV: 236 reverse(); 237 continue; 238 239 default: 240 fprintf(stderr, 241 "Unknown escape sequence in input: %o, %o\n", 242 IESC, c); 243 exit(1); 244 } 245 continue; 246 247 case '_': 248 if (obuf[col].c_char) 249 obuf[col].c_mode |= UNDERL | mode; 250 else 251 obuf[col].c_char = '_'; 252 case ' ': 253 col++; 254 if (col > maxcol) 255 maxcol = col; 256 continue; 257 258 case '\n': 259 flushln(); 260 continue; 261 262 case '\f': 263 flushln(); 264 putchar('\f'); 265 continue; 266 267 default: 268 if (c < ' ') /* non printing */ 269 continue; 270 if (obuf[col].c_char == '\0') { 271 obuf[col].c_char = c; 272 obuf[col].c_mode = mode; 273 } else if (obuf[col].c_char == '_') { 274 obuf[col].c_char = c; 275 obuf[col].c_mode |= UNDERL|mode; 276 } else if (obuf[col].c_char == c) 277 obuf[col].c_mode |= BOLD|mode; 278 else 279 obuf[col].c_mode = mode; 280 col++; 281 if (col > maxcol) 282 maxcol = col; 283 continue; 284 } 285 if (maxcol) 286 flushln(); 287 } 288 289 void 290 flushln() 291 { 292 int lastmode, i; 293 int hadmodes = 0; 294 295 lastmode = NORMAL; 296 for (i=0; i<maxcol; i++) { 297 if (obuf[i].c_mode != lastmode) { 298 hadmodes++; 299 msetmode(obuf[i].c_mode); 300 lastmode = obuf[i].c_mode; 301 } 302 if (obuf[i].c_char == '\0') { 303 if (upln) 304 PRINT(CURS_RIGHT); 305 else 306 outc(' '); 307 } else 308 outc(obuf[i].c_char); 309 } 310 if (lastmode != NORMAL) { 311 msetmode(0); 312 } 313 if (must_overstrike && hadmodes) 314 overstrike(); 315 putchar('\n'); 316 if (iflag && hadmodes) 317 iattr(); 318 (void)fflush(stdout); 319 if (upln) 320 upln--; 321 initbuf(); 322 } 323 324 /* 325 * For terminals that can overstrike, overstrike underlines and bolds. 326 * We don't do anything with halfline ups and downs, or Greek. 327 */ 328 void 329 overstrike() 330 { 331 register int i; 332 char lbuf[256]; 333 register char *cp = lbuf; 334 int hadbold=0; 335 336 /* Set up overstrike buffer */ 337 for (i=0; i<maxcol; i++) 338 switch (obuf[i].c_mode) { 339 case NORMAL: 340 default: 341 *cp++ = ' '; 342 break; 343 case UNDERL: 344 *cp++ = '_'; 345 break; 346 case BOLD: 347 *cp++ = obuf[i].c_char; 348 hadbold=1; 349 break; 350 } 351 putchar('\r'); 352 for (*cp=' '; *cp==' '; cp--) 353 *cp = 0; 354 for (cp=lbuf; *cp; cp++) 355 putchar(*cp); 356 if (hadbold) { 357 putchar('\r'); 358 for (cp=lbuf; *cp; cp++) 359 putchar(*cp=='_' ? ' ' : *cp); 360 putchar('\r'); 361 for (cp=lbuf; *cp; cp++) 362 putchar(*cp=='_' ? ' ' : *cp); 363 } 364 } 365 366 void 367 iattr() 368 { 369 register int i; 370 char lbuf[256]; 371 register char *cp = lbuf; 372 373 for (i=0; i<maxcol; i++) 374 switch (obuf[i].c_mode) { 375 case NORMAL: *cp++ = ' '; break; 376 case ALTSET: *cp++ = 'g'; break; 377 case SUPERSC: *cp++ = '^'; break; 378 case SUBSC: *cp++ = 'v'; break; 379 case UNDERL: *cp++ = '_'; break; 380 case BOLD: *cp++ = '!'; break; 381 default: *cp++ = 'X'; break; 382 } 383 for (*cp=' '; *cp==' '; cp--) 384 *cp = 0; 385 for (cp=lbuf; *cp; cp++) 386 putchar(*cp); 387 putchar('\n'); 388 } 389 390 void 391 initbuf() 392 { 393 394 bzero((char *)obuf, sizeof (obuf)); /* depends on NORMAL == 0 */ 395 col = 0; 396 maxcol = 0; 397 mode &= ALTSET; 398 } 399 400 void 401 fwd() 402 { 403 int oldcol, oldmax; 404 405 oldcol = col; 406 oldmax = maxcol; 407 flushln(); 408 col = oldcol; 409 maxcol = oldmax; 410 } 411 412 void 413 reverse() 414 { 415 upln++; 416 fwd(); 417 PRINT(CURS_UP); 418 PRINT(CURS_UP); 419 upln++; 420 } 421 422 void 423 initcap() 424 { 425 static char tcapbuf[512]; 426 char *bp = tcapbuf; 427 char *getenv(), *tgetstr(); 428 429 /* This nonsense attempts to work with both old and new termcap */ 430 CURS_UP = tgetstr("up", &bp); 431 CURS_RIGHT = tgetstr("ri", &bp); 432 if (CURS_RIGHT == NULL) 433 CURS_RIGHT = tgetstr("nd", &bp); 434 CURS_LEFT = tgetstr("le", &bp); 435 if (CURS_LEFT == NULL) 436 CURS_LEFT = tgetstr("bc", &bp); 437 if (CURS_LEFT == NULL && tgetflag("bs")) 438 CURS_LEFT = "\b"; 439 440 ENTER_STANDOUT = tgetstr("so", &bp); 441 EXIT_STANDOUT = tgetstr("se", &bp); 442 ENTER_UNDERLINE = tgetstr("us", &bp); 443 EXIT_UNDERLINE = tgetstr("ue", &bp); 444 ENTER_DIM = tgetstr("mh", &bp); 445 ENTER_BOLD = tgetstr("md", &bp); 446 ENTER_REVERSE = tgetstr("mr", &bp); 447 EXIT_ATTRIBUTES = tgetstr("me", &bp); 448 449 if (!ENTER_BOLD && ENTER_REVERSE) 450 ENTER_BOLD = ENTER_REVERSE; 451 if (!ENTER_BOLD && ENTER_STANDOUT) 452 ENTER_BOLD = ENTER_STANDOUT; 453 if (!ENTER_UNDERLINE && ENTER_STANDOUT) { 454 ENTER_UNDERLINE = ENTER_STANDOUT; 455 EXIT_UNDERLINE = EXIT_STANDOUT; 456 } 457 if (!ENTER_DIM && ENTER_STANDOUT) 458 ENTER_DIM = ENTER_STANDOUT; 459 if (!ENTER_REVERSE && ENTER_STANDOUT) 460 ENTER_REVERSE = ENTER_STANDOUT; 461 if (!EXIT_ATTRIBUTES && EXIT_STANDOUT) 462 EXIT_ATTRIBUTES = EXIT_STANDOUT; 463 464 /* 465 * Note that we use REVERSE for the alternate character set, 466 * not the as/ae capabilities. This is because we are modelling 467 * the model 37 teletype (since that's what nroff outputs) and 468 * the typical as/ae is more of a graphics set, not the greek 469 * letters the 37 has. 470 */ 471 472 UNDER_CHAR = tgetstr("uc", &bp); 473 must_use_uc = (UNDER_CHAR && !ENTER_UNDERLINE); 474 } 475 476 int 477 outchar(c) 478 int c; 479 { 480 putchar(c & 0177); 481 return (0); 482 } 483 484 static int curmode = 0; 485 486 void 487 outc(c) 488 int c; 489 { 490 putchar(c); 491 if (must_use_uc && (curmode&UNDERL)) { 492 PRINT(CURS_LEFT); 493 PRINT(UNDER_CHAR); 494 } 495 } 496 497 void 498 msetmode(newmode) 499 int newmode; 500 { 501 if (!iflag) { 502 if (curmode != NORMAL && newmode != NORMAL) 503 msetmode(NORMAL); 504 switch (newmode) { 505 case NORMAL: 506 switch(curmode) { 507 case NORMAL: 508 break; 509 case UNDERL: 510 PRINT(EXIT_UNDERLINE); 511 break; 512 default: 513 /* This includes standout */ 514 PRINT(EXIT_ATTRIBUTES); 515 break; 516 } 517 break; 518 case ALTSET: 519 PRINT(ENTER_REVERSE); 520 break; 521 case SUPERSC: 522 /* 523 * This only works on a few terminals. 524 * It should be fixed. 525 */ 526 PRINT(ENTER_UNDERLINE); 527 PRINT(ENTER_DIM); 528 break; 529 case SUBSC: 530 PRINT(ENTER_DIM); 531 break; 532 case UNDERL: 533 PRINT(ENTER_UNDERLINE); 534 break; 535 case BOLD: 536 PRINT(ENTER_BOLD); 537 break; 538 default: 539 /* 540 * We should have some provision here for multiple modes 541 * on at once. This will have to come later. 542 */ 543 PRINT(ENTER_STANDOUT); 544 break; 545 } 546 } 547 curmode = newmode; 548 } 549