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