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