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