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