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.1 (Berkeley) 5/31/85"; 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: int c; 56: char *cp, *termtype; 57: FILE *f; 58: char termcap[1024]; 59: char *getenv(); 60: extern int optind; 61: extern char *optarg; 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: 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: default: 203: if (c < ' ') /* non printing */ 204: continue; 205: if (obuf[col].c_char == '\0') { 206: obuf[col].c_char = c; 207: obuf[col].c_mode = mode; 208: } else if (obuf[col].c_char == '_') { 209: obuf[col].c_char = c; 210: obuf[col].c_mode |= UNDERL|mode; 211: } else if (obuf[col].c_char == c) 212: obuf[col].c_mode |= BOLD|mode; 213: else { 214: obuf[col].c_mode = c; 215: obuf[col].c_mode = mode; 216: } 217: col++; 218: if (col > maxcol) 219: maxcol = col; 220: continue; 221: } 222: if (maxcol) 223: flushln(); 224: } 225: 226: flushln() 227: { 228: register lastmode; 229: register i; 230: int hadmodes = 0; 231: 232: lastmode = NORMAL; 233: for (i=0; i<maxcol; i++) { 234: if (obuf[i].c_mode != lastmode) { 235: hadmodes++; 236: setmode(obuf[i].c_mode); 237: lastmode = obuf[i].c_mode; 238: } 239: if (obuf[i].c_char == '\0') { 240: if (upln) { 241: puts(CURS_RIGHT); 242: } else 243: outc(' '); 244: } else 245: outc(obuf[i].c_char); 246: } 247: if (lastmode != NORMAL) { 248: setmode(0); 249: } 250: if (must_overstrike && hadmodes) 251: overstrike(); 252: putchar('\n'); 253: if (iflag && hadmodes) 254: iattr(); 255: fflush(stdout); 256: if (upln) 257: upln--; 258: initbuf(); 259: } 260: 261: /* 262: * For terminals that can overstrike, overstrike underlines and bolds. 263: * We don't do anything with halfline ups and downs, or Greek. 264: */ 265: overstrike() 266: { 267: register int i; 268: char lbuf[256]; 269: register char *cp = lbuf; 270: int hadbold=0; 271: 272: /* Set up overstrike buffer */ 273: for (i=0; i<maxcol; i++) 274: switch (obuf[i].c_mode) { 275: case NORMAL: 276: default: 277: *cp++ = ' '; 278: break; 279: case UNDERL: 280: *cp++ = '_'; 281: break; 282: case BOLD: 283: *cp++ = obuf[i].c_char; 284: hadbold=1; 285: break; 286: } 287: putchar('\r'); 288: for (*cp=' '; *cp==' '; cp--) 289: *cp = 0; 290: for (cp=lbuf; *cp; cp++) 291: putchar(*cp); 292: if (hadbold) { 293: putchar('\r'); 294: for (cp=lbuf; *cp; cp++) 295: putchar(*cp=='_' ? ' ' : *cp); 296: putchar('\r'); 297: for (cp=lbuf; *cp; cp++) 298: putchar(*cp=='_' ? ' ' : *cp); 299: } 300: } 301: 302: iattr() 303: { 304: register int i; 305: char lbuf[256]; 306: register char *cp = lbuf; 307: 308: for (i=0; i<maxcol; i++) 309: switch (obuf[i].c_mode) { 310: case NORMAL: *cp++ = ' '; break; 311: case ALTSET: *cp++ = 'g'; break; 312: case SUPERSC: *cp++ = '^'; break; 313: case SUBSC: *cp++ = 'v'; break; 314: case UNDERL: *cp++ = '_'; break; 315: case BOLD: *cp++ = '!'; break; 316: default: *cp++ = 'X'; break; 317: } 318: for (*cp=' '; *cp==' '; cp--) 319: *cp = 0; 320: for (cp=lbuf; *cp; cp++) 321: putchar(*cp); 322: putchar('\n'); 323: } 324: 325: initbuf() 326: { 327: 328: bzero(obuf, sizeof (obuf)); /* depends on NORMAL == 0 */ 329: col = 0; 330: maxcol = 0; 331: mode &= ALTSET; 332: } 333: 334: fwd() 335: { 336: register oldcol, oldmax; 337: 338: oldcol = col; 339: oldmax = maxcol; 340: flushln(); 341: col = oldcol; 342: maxcol = oldmax; 343: } 344: 345: reverse() 346: { 347: upln++; 348: fwd(); 349: puts(CURS_UP); 350: puts(CURS_UP); 351: upln++; 352: } 353: 354: initcap() 355: { 356: static char tcapbuf[512]; 357: char *termtype; 358: char *bp = tcapbuf; 359: char *getenv(), *tgetstr(); 360: 361: /* This nonsense attempts to work with both old and new termcap */ 362: CURS_UP = tgetstr("up", &bp); 363: CURS_RIGHT = tgetstr("ri", &bp); 364: if (CURS_RIGHT == NULL) 365: CURS_RIGHT = tgetstr("nd", &bp); 366: CURS_LEFT = tgetstr("le", &bp); 367: if (CURS_LEFT == NULL) 368: CURS_LEFT = tgetstr("bc", &bp); 369: if (CURS_LEFT == NULL && tgetflag("bs")) 370: CURS_LEFT = "\b"; 371: 372: ENTER_STANDOUT = tgetstr("so", &bp); 373: EXIT_STANDOUT = tgetstr("se", &bp); 374: ENTER_UNDERLINE = tgetstr("us", &bp); 375: EXIT_UNDERLINE = tgetstr("ue", &bp); 376: ENTER_DIM = tgetstr("mh", &bp); 377: ENTER_BOLD = tgetstr("md", &bp); 378: ENTER_REVERSE = tgetstr("mr", &bp); 379: EXIT_ATTRIBUTES = tgetstr("me", &bp); 380: 381: if (!ENTER_BOLD && ENTER_REVERSE) 382: ENTER_BOLD = ENTER_REVERSE; 383: if (!ENTER_BOLD && ENTER_STANDOUT) 384: ENTER_BOLD = ENTER_STANDOUT; 385: if (!ENTER_UNDERLINE && ENTER_STANDOUT) { 386: ENTER_UNDERLINE = ENTER_STANDOUT; 387: EXIT_UNDERLINE = EXIT_STANDOUT; 388: } 389: if (!ENTER_DIM && ENTER_STANDOUT) 390: ENTER_DIM = ENTER_STANDOUT; 391: if (!ENTER_REVERSE && ENTER_STANDOUT) 392: ENTER_REVERSE = ENTER_STANDOUT; 393: if (!EXIT_ATTRIBUTES && EXIT_STANDOUT) 394: EXIT_ATTRIBUTES = EXIT_STANDOUT; 395: 396: /* 397: * Note that we use REVERSE for the alternate character set, 398: * not the as/ae capabilities. This is because we are modelling 399: * the model 37 teletype (since that's what nroff outputs) and 400: * the typical as/ae is more of a graphics set, not the greek 401: * letters the 37 has. 402: */ 403: 404: UNDER_CHAR = tgetstr("uc", &bp); 405: must_use_uc = (UNDER_CHAR && !ENTER_UNDERLINE); 406: } 407: 408: outchar(c) 409: char c; 410: { 411: putchar(c&0177); 412: } 413: 414: puts(str) 415: char *str; 416: { 417: if (str) 418: tputs(str, 1, outchar); 419: } 420: 421: static curmode = 0; 422: outc(c) 423: char c; 424: { 425: putchar(c); 426: if (must_use_uc && (curmode&UNDERL)) { 427: puts(CURS_LEFT); 428: puts(UNDER_CHAR); 429: } 430: } 431: 432: setmode(newmode) 433: int newmode; 434: { 435: if (!iflag) 436: { 437: if (curmode != NORMAL && newmode != NORMAL) 438: setmode(NORMAL); 439: switch (newmode) { 440: case NORMAL: 441: switch(curmode) { 442: case NORMAL: 443: break; 444: case UNDERL: 445: puts(EXIT_UNDERLINE); 446: break; 447: default: 448: /* This includes standout */ 449: puts(EXIT_ATTRIBUTES); 450: break; 451: } 452: break; 453: case ALTSET: 454: puts(ENTER_REVERSE); 455: break; 456: case SUPERSC: 457: /* 458: * This only works on a few terminals. 459: * It should be fixed. 460: */ 461: puts(ENTER_UNDERLINE); 462: puts(ENTER_DIM); 463: break; 464: case SUBSC: 465: puts(ENTER_DIM); 466: break; 467: case UNDERL: 468: puts(ENTER_UNDERLINE); 469: break; 470: case BOLD: 471: puts(ENTER_BOLD); 472: break; 473: default: 474: /* 475: * We should have some provision here for multiple modes 476: * on at once. This will have to come later. 477: */ 478: puts(ENTER_STANDOUT); 479: break; 480: } 481: } 482: curmode = newmode; 483: } 484: /* @(#)getopt.c 3.2 */ 485: #define ERR(s, c) if(opterr){\ 486: fputs(argv[0], stderr);\ 487: fputs(s, stderr);\ 488: fputc(c, stderr);\ 489: fputc('\n', stderr);} 490: 491: int opterr = 1; 492: int optind = 1; 493: char *optarg; 494: char *index(); 495: 496: int 497: getopt (argc, argv, opts) 498: char **argv, *opts; 499: { 500: static int sp = 1; 501: char c; 502: char *cp; 503: 504: if (sp == 1) 505: if (optind >= argc || 506: argv[optind][0] != '-' || argv[optind][1] == '\0') 507: return EOF; 508: else if (strcmp(argv[optind], "--") == NULL) { 509: optind++; 510: return EOF; 511: } 512: else if (strcmp(argv[optind], "-?") == NULL) { 513: optind++; 514: return '?'; 515: } 516: c = argv[optind][sp]; 517: if (c == ':' || (cp=index(opts, c)) == NULL) { 518: ERR (": illegal option -- ", c); 519: if (argv[optind][++sp] == '\0') { 520: optind++; 521: sp = 1; 522: } 523: return '?'; 524: } 525: if (*++cp == ':') { 526: if (argv[optind][2] != '\0') 527: optarg = &argv[optind++][sp+1]; 528: else if (++optind >= argc) { 529: ERR (": option requires an argument -- ", c); 530: sp = 1; 531: return '?'; 532: } else 533: optarg = argv[optind++]; 534: sp = 1; 535: } 536: else if (argv[optind][++sp] == '\0') { 537: sp = 1; 538: optind++; 539: } 540: return c; 541: }