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