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