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