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