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