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