1 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ 2 /* All Rights Reserved */ 3 4 5 /* 6 * Copyright (c) 1980 Regents of the University of California. 7 * All rights reserved. The Berkeley software License Agreement 8 * specifies the terms and conditions for redistribution. 9 */ 10 11 /* 12 * Copyright (c) 1983, 1984 1985, 1986, 1987, 1988, 2000 Sun Microsystems, Inc. 13 * All Rights Reserved. 14 */ 15 16 #ident "%Z%%M% %I% %E% SMI" /* SVr4.0 1.1 */ 17 18 #include <stdio.h> 19 #include <locale.h> 20 #include <wctype.h> 21 #include <widec.h> 22 #include <euc.h> 23 #include <limits.h> 24 #include <stdlib.h> 25 26 #define IESC L'\033' 27 #define SO L'\016' 28 #define SI L'\017' 29 #define HFWD L'9' 30 #define HREV L'8' 31 #define FREV L'7' 32 #define CDUMMY -1 33 34 #define NORMAL 000 35 #define ALTSET 001 /* Reverse */ 36 #define SUPERSC 002 /* Dim */ 37 #define SUBSC 004 /* Dim | Ul */ 38 #define UNDERL 010 /* Ul */ 39 #define BOLD 020 /* Bold */ 40 41 #define MEMFCT 16 42 /* 43 * MEMFCT is a number that is likely to be large enough as a factor for 44 * allocating more memory and to be small enough so as not wasting memory 45 */ 46 47 int must_use_uc, must_overstrike; 48 char *CURS_UP, *CURS_RIGHT, *CURS_LEFT, 49 *ENTER_STANDOUT, *EXIT_STANDOUT, *ENTER_UNDERLINE, *EXIT_UNDERLINE, 50 *ENTER_DIM, *ENTER_BOLD, *ENTER_REVERSE, *UNDER_CHAR, *EXIT_ATTRIBUTES; 51 52 struct CHAR { 53 char c_mode; 54 wchar_t c_char; 55 }; 56 57 struct CHAR obuf[LINE_MAX]; 58 int col, maxcol; 59 int mode; 60 int halfpos; 61 int upln; 62 int iflag; 63 64 eucwidth_t wp; 65 int scrw[4]; 66 67 main(argc, argv) 68 int argc; 69 char **argv; 70 { 71 int c; 72 char *cp, *termtype; 73 FILE *f; 74 char termcap[1024]; 75 char *getenv(); 76 extern int optind; 77 extern char *optarg; 78 79 (void) setlocale(LC_ALL, ""); 80 #if !defined(TEXT_DOMAIN) 81 #define TEXT_DOMAIN "SYS_TEST" 82 #endif 83 (void) textdomain(TEXT_DOMAIN); 84 85 getwidth(&wp); 86 scrw[0] = 1; 87 scrw[1] = wp._scrw1; 88 scrw[2] = wp._scrw2; 89 scrw[3] = wp._scrw3; 90 91 termtype = getenv("TERM"); 92 if (termtype == NULL || (argv[0][0] == 'c' && !isatty(1))) 93 termtype = "lpr"; 94 while ((c=getopt(argc, argv, "it:T:")) != EOF) 95 switch (c) { 96 97 case 't': 98 case 'T': /* for nroff compatibility */ 99 termtype = optarg; 100 break; 101 case 'i': 102 iflag = 1; 103 break; 104 105 default: 106 fprintf(stderr, 107 gettext("\ 108 Usage: %s [ -i ] [ -t terminal ] [ filename...]\n"), 109 argv[0]); 110 exit(1); 111 } 112 113 switch (tgetent(termcap, termtype)) { 114 115 case 1: 116 break; 117 118 default: 119 fprintf(stderr, gettext("trouble reading termcap")); 120 /* fall through to ... */ 121 122 case 0: 123 /* No such terminal type - assume dumb */ 124 strcpy(termcap, "dumb:os:col#80:cr=^M:sf=^J:am:"); 125 break; 126 } 127 initcap(); 128 if ((tgetflag("os") && ENTER_BOLD==NULL) || 129 (tgetflag("ul") && ENTER_UNDERLINE==NULL && UNDER_CHAR==NULL)) 130 must_overstrike = 1; 131 initbuf(); 132 if (optind == argc) 133 filter(stdin); 134 else for (; optind<argc; optind++) { 135 f = fopen(argv[optind], "r"); 136 if (f == NULL) { 137 perror(argv[optind]); 138 exit(1); 139 } else 140 filter(f); 141 } 142 exit(0); 143 } 144 145 filter(f) 146 FILE *f; 147 { 148 register wchar_t c; 149 register i; 150 151 while ((c = getwc(f)) != EOF) { 152 if (maxcol >= LINE_MAX) 153 { 154 fprintf(stderr, 155 gettext("Input line longer than %d characters\n"), LINE_MAX); 156 exit(1); 157 } 158 switch (c) { 159 160 case L'\b': 161 if (col > 0) 162 col--; 163 continue; 164 165 case L'\t': 166 col = (col+8) & ~07; 167 if (col > maxcol) 168 maxcol = col; 169 continue; 170 171 case L'\r': 172 col = 0; 173 continue; 174 175 case SO: 176 mode |= ALTSET; 177 continue; 178 179 case SI: 180 mode &= ~ALTSET; 181 continue; 182 183 case IESC: 184 switch (c = getwc(f)) { 185 case HREV: 186 if (halfpos == 0) { 187 mode |= SUPERSC; 188 halfpos--; 189 } else if (halfpos > 0) { 190 mode &= ~SUBSC; 191 halfpos--; 192 } else { 193 halfpos = 0; 194 reverse(); 195 } 196 continue; 197 198 case HFWD: 199 if (halfpos == 0) { 200 mode |= SUBSC; 201 halfpos++; 202 } else if (halfpos < 0) { 203 mode &= ~SUPERSC; 204 halfpos++; 205 } else { 206 halfpos = 0; 207 fwd(); 208 } 209 continue; 210 case FREV: 211 reverse(); 212 continue; 213 214 default: 215 fprintf(stderr, 216 gettext("Unknown escape sequence in input: %o, %o\n"), 217 IESC, c); 218 exit(1); 219 } 220 continue; 221 222 case L'_': 223 if (obuf[col].c_char) 224 obuf[col].c_mode |= UNDERL | mode; 225 else 226 obuf[col].c_char = '_'; 227 case L' ': 228 col++; 229 if (col > maxcol) 230 maxcol = col; 231 continue; 232 233 case L'\n': 234 flushln(); 235 continue; 236 237 default: 238 if (c < L' ') /* non printing */ 239 continue; 240 if (obuf[col].c_char == L'\0') { 241 obuf[col].c_char = c; 242 obuf[col].c_mode = mode; 243 i = scrw[wcsetno(c)]; 244 while (--i > 0) 245 obuf[++col].c_char = CDUMMY; 246 } else if (obuf[col].c_char == L'_') { 247 obuf[col].c_char = c; 248 obuf[col].c_mode |= UNDERL|mode; 249 i = scrw[wcsetno(c)]; 250 while (--i > 0) 251 obuf[++col].c_char = CDUMMY; 252 } else if (obuf[col].c_char == c) 253 obuf[col].c_mode |= BOLD|mode; 254 else { 255 obuf[col].c_char = c; 256 obuf[col].c_mode = mode; 257 } 258 col++; 259 if (col > maxcol) 260 maxcol = col; 261 continue; 262 } 263 } 264 if (maxcol) 265 flushln(); 266 } 267 268 flushln() 269 { 270 register lastmode; 271 register i; 272 int hadmodes = 0; 273 274 lastmode = NORMAL; 275 for (i=0; i<maxcol; i++) { 276 if (obuf[i].c_mode != lastmode) { 277 hadmodes++; 278 setmode(obuf[i].c_mode); 279 lastmode = obuf[i].c_mode; 280 } 281 if (obuf[i].c_char == L'\0') { 282 if (upln) { 283 puts(CURS_RIGHT); 284 } else 285 outc(L' '); 286 } else 287 outc(obuf[i].c_char); 288 } 289 if (lastmode != NORMAL) { 290 setmode(0); 291 } 292 if (must_overstrike && hadmodes) 293 overstrike(); 294 putwchar(L'\n'); 295 if (iflag && hadmodes) 296 iattr(); 297 if (upln) 298 upln--; 299 initbuf(); 300 } 301 302 /* 303 * For terminals that can overstrike, overstrike underlines and bolds. 304 * We don't do anything with halfline ups and downs, or Greek. 305 */ 306 overstrike() 307 { 308 register int i, n; 309 wchar_t *cp, *scp; 310 size_t szbf = 256, tszbf; 311 int hadbold=0; 312 313 scp = (wchar_t *)malloc(sizeof (wchar_t) * szbf); 314 if (!scp) { 315 /* this kind of message need not to be gettext'ed */ 316 (void) fprintf(stderr, "malloc failed\n"); 317 exit(1); 318 } 319 cp = scp; 320 tszbf = szbf; 321 #ifdef DEBUG 322 /* 323 * to allocate a memory after the chunk of the current scp 324 * and to make sure the following realloc() allocates 325 * memory from different chunks. 326 */ 327 (void) malloc(1024 * 1024); 328 #endif 329 330 /* Set up overstrike buffer */ 331 for (i=0; i<maxcol; i++) { 332 n = scrw[wcsetno(obuf[i].c_char)]; 333 if (tszbf <= n) { 334 /* may not enough buffer for this char */ 335 size_t pos; 336 337 /* obtain the offset of cp */ 338 pos = cp - scp; 339 /* reallocate another (n * MEMFCT) * sizeof (wchar_t) */ 340 scp = (wchar_t *)realloc(scp, 341 sizeof (wchar_t) * (szbf + (n * MEMFCT))); 342 if (!scp) { 343 (void) fprintf(stderr, "malloc failed\n"); 344 exit(1); 345 } 346 /* get the new address of cp */ 347 cp = scp + pos; 348 szbf += n * MEMFCT; 349 tszbf += n * MEMFCT; 350 } 351 switch (obuf[i].c_mode) { 352 case NORMAL: 353 default: 354 tszbf -= n; 355 *cp++ = L' '; 356 while (--n > 0) { 357 *cp++ = L' '; 358 i++; 359 } 360 break; 361 case UNDERL: 362 tszbf -= n; 363 *cp++ = L'_'; 364 while (--n > 0) { 365 *cp++ = L'_'; 366 i++; 367 } 368 break; 369 case BOLD: 370 tszbf--; 371 *cp++ = obuf[i].c_char; 372 hadbold=1; 373 break; 374 } 375 } 376 putwchar(L'\r'); 377 for (*cp=L' '; *cp==L' '; cp--) 378 *cp = L'\0'; 379 for (cp=scp; *cp; cp++) 380 putwchar(*cp); 381 if (hadbold) { 382 putwchar(L'\r'); 383 for (cp=scp; *cp; cp++) 384 putwchar(*cp==L'_' ? L' ' : *cp); 385 putwchar(L'\r'); 386 for (cp=scp; *cp; cp++) 387 putwchar(*cp==L'_' ? L' ' : *cp); 388 } 389 free(scp); 390 } 391 392 iattr() 393 { 394 register int i, n; 395 wchar_t *cp, *scp; 396 register wchar_t cx; 397 size_t szbf = 256, tszbf; 398 399 scp = (wchar_t *)malloc(sizeof (wchar_t) * szbf); 400 if (!scp) { 401 /* this kind of message need not to be gettext'ed */ 402 (void) fprintf(stderr, "malloc failed\n"); 403 exit(1); 404 } 405 cp = scp; 406 tszbf = szbf; 407 #ifdef DEBUG 408 /* 409 * to allocate a memory after the chunk of the current scp 410 * and to make sure the following realloc() allocates 411 * memory from different chunks. 412 */ 413 (void) malloc(1024 * 1024); 414 #endif 415 for (i=0; i<maxcol; i++) { 416 switch (obuf[i].c_mode) { 417 case NORMAL: cx = ' '; break; 418 case ALTSET: cx = 'g'; break; 419 case SUPERSC: cx = '^'; break; 420 case SUBSC: cx = 'v'; break; 421 case UNDERL: cx = '_'; break; 422 case BOLD: cx = '!'; break; 423 default: cx = 'X'; break; 424 } 425 n = scrw[wcsetno(obuf[i].c_char)]; 426 if (tszbf <= n) { 427 /* may not enough buffer for this char */ 428 size_t pos; 429 430 /* obtain the offset of cp */ 431 pos = cp - scp; 432 /* reallocate another (n * MEMFCT) * sizeof (wchar_t) */ 433 scp = (wchar_t *)realloc(scp, 434 sizeof (wchar_t) * (szbf + (n * MEMFCT))); 435 if (!scp) { 436 (void) fprintf(stderr, "malloc failed\n"); 437 exit(1); 438 } 439 /* get the new address of cp */ 440 cp = scp + pos; 441 szbf += n * MEMFCT; 442 tszbf += n * MEMFCT; 443 } 444 tszbf -= n; 445 *cp++ = cx; 446 while (--n > 0) { 447 *cp++ = cx; 448 i++; 449 } 450 } 451 for (*cp=L' '; *cp==L' '; cp--) 452 *cp = L'\0'; 453 for (cp=scp; *cp; cp++) 454 putwchar(*cp); 455 putwchar(L'\n'); 456 free(scp); 457 } 458 459 initbuf() 460 { 461 register i; 462 463 /* following depends on NORMAL == 000 */ 464 for (i = 0; i < LINE_MAX; i++) 465 obuf[i].c_char = obuf[i].c_mode = 0; 466 467 col = 0; 468 maxcol = 0; 469 mode &= ALTSET; 470 } 471 472 fwd() 473 { 474 register oldcol, oldmax; 475 476 oldcol = col; 477 oldmax = maxcol; 478 flushln(); 479 col = oldcol; 480 maxcol = oldmax; 481 } 482 483 reverse() 484 { 485 upln++; 486 fwd(); 487 puts(CURS_UP); 488 puts(CURS_UP); 489 upln++; 490 } 491 492 initcap() 493 { 494 static char tcapbuf[512]; 495 char *termtype; 496 char *bp = tcapbuf; 497 char *getenv(), *tgetstr(); 498 499 /* This nonsense attempts to work with both old and new termcap */ 500 CURS_UP = tgetstr("up", &bp); 501 CURS_RIGHT = tgetstr("ri", &bp); 502 if (CURS_RIGHT == NULL) 503 CURS_RIGHT = tgetstr("nd", &bp); 504 CURS_LEFT = tgetstr("le", &bp); 505 if (CURS_LEFT == NULL) 506 CURS_LEFT = tgetstr("bc", &bp); 507 if (CURS_LEFT == NULL && tgetflag("bs")) 508 CURS_LEFT = "\b"; 509 510 ENTER_STANDOUT = tgetstr("so", &bp); 511 EXIT_STANDOUT = tgetstr("se", &bp); 512 ENTER_UNDERLINE = tgetstr("us", &bp); 513 EXIT_UNDERLINE = tgetstr("ue", &bp); 514 ENTER_DIM = tgetstr("mh", &bp); 515 ENTER_BOLD = tgetstr("md", &bp); 516 ENTER_REVERSE = tgetstr("mr", &bp); 517 EXIT_ATTRIBUTES = tgetstr("me", &bp); 518 519 if (!ENTER_BOLD && ENTER_REVERSE) 520 ENTER_BOLD = ENTER_REVERSE; 521 if (!ENTER_BOLD && ENTER_STANDOUT) 522 ENTER_BOLD = ENTER_STANDOUT; 523 if (!ENTER_UNDERLINE && ENTER_STANDOUT) { 524 ENTER_UNDERLINE = ENTER_STANDOUT; 525 EXIT_UNDERLINE = EXIT_STANDOUT; 526 } 527 if (!ENTER_DIM && ENTER_STANDOUT) 528 ENTER_DIM = ENTER_STANDOUT; 529 if (!ENTER_REVERSE && ENTER_STANDOUT) 530 ENTER_REVERSE = ENTER_STANDOUT; 531 if (!EXIT_ATTRIBUTES && EXIT_STANDOUT) 532 EXIT_ATTRIBUTES = EXIT_STANDOUT; 533 534 /* 535 * Note that we use REVERSE for the alternate character set, 536 * not the as/ae capabilities. This is because we are modelling 537 * the model 37 teletype (since that's what nroff outputs) and 538 * the typical as/ae is more of a graphics set, not the greek 539 * letters the 37 has. 540 */ 541 542 #ifdef notdef 543 printf("so %s se %s us %s ue %s me %s\n", 544 ENTER_STANDOUT, EXIT_STANDOUT, ENTER_UNDERLINE, 545 EXIT_UNDERLINE, EXIT_ATTRIBUTES); 546 #endif 547 UNDER_CHAR = tgetstr("uc", &bp); 548 must_use_uc = (UNDER_CHAR && !ENTER_UNDERLINE); 549 } 550 551 outchar(c) 552 char c; 553 { 554 putchar(c&0177); 555 } 556 557 puts(str) 558 const char *str; 559 { 560 if (str) 561 tputs(str, 1, outchar); 562 } 563 564 static curmode = 0; 565 outc(c) 566 wchar_t c; 567 { 568 register int m, n; 569 570 if (c == CDUMMY) 571 return; 572 putwchar(c); 573 if (must_use_uc && (curmode & UNDERL)) { 574 m = n = scrw[wcsetno(c)]; 575 puts(CURS_LEFT); 576 while (--m > 0) 577 puts(CURS_LEFT); 578 puts(UNDER_CHAR); 579 while (--n > 0) 580 puts(UNDER_CHAR); 581 } 582 } 583 584 setmode(newmode) 585 int newmode; 586 { 587 if (!iflag) 588 { 589 if (curmode != NORMAL && newmode != NORMAL) 590 setmode(NORMAL); 591 switch (newmode) { 592 case NORMAL: 593 switch (curmode) { 594 case NORMAL: 595 break; 596 case UNDERL: 597 puts(EXIT_UNDERLINE); 598 break; 599 default: 600 /* This includes standout */ 601 puts(EXIT_ATTRIBUTES); 602 break; 603 } 604 break; 605 case ALTSET: 606 puts(ENTER_REVERSE); 607 break; 608 case SUPERSC: 609 /* 610 * This only works on a few terminals. 611 * It should be fixed. 612 */ 613 puts(ENTER_UNDERLINE); 614 puts(ENTER_DIM); 615 break; 616 case SUBSC: 617 puts(ENTER_DIM); 618 break; 619 case UNDERL: 620 puts(ENTER_UNDERLINE); 621 break; 622 case BOLD: 623 puts(ENTER_BOLD); 624 break; 625 default: 626 /* 627 * We should have some provision here for multiple modes 628 * on at once. This will have to come later. 629 */ 630 puts(ENTER_STANDOUT); 631 break; 632 } 633 } 634 curmode = newmode; 635 } 636