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(int argc, char **argv) 105 { 106 int c; 107 const char *termtype; 108 FILE *f; 109 char termcap[1024]; 110 111 termtype = getenv("TERM"); 112 if (termtype == NULL || (argv[0][0] == 'c' && !isatty(1))) 113 termtype = "lpr"; 114 while ((c=getopt(argc, argv, "it:T:")) != -1) 115 switch(c) { 116 117 case 't': 118 case 'T': /* for nroff compatibility */ 119 termtype = optarg; 120 break; 121 case 'i': 122 iflag = 1; 123 break; 124 default: 125 usage(); 126 } 127 128 switch(tgetent(termcap, termtype)) { 129 130 case 1: 131 break; 132 133 default: 134 warnx("trouble reading termcap"); 135 /* FALLTHROUGH */ 136 137 case 0: 138 /* No such terminal type - assume dumb */ 139 (void)strcpy(termcap, "dumb:os:col#80:cr=^M:sf=^J:am:"); 140 break; 141 } 142 initcap(); 143 if ( (tgetflag("os") && ENTER_BOLD==NULL ) || 144 (tgetflag("ul") && ENTER_UNDERLINE==NULL && UNDER_CHAR==NULL)) 145 must_overstrike = 1; 146 initbuf(); 147 if (optind == argc) 148 filter(stdin); 149 else for (; optind<argc; optind++) { 150 f = fopen(argv[optind],"r"); 151 if (f == NULL) 152 err(1, "%s", argv[optind]); 153 else 154 filter(f); 155 } 156 exit(0); 157 } 158 159 static void 160 usage(void) 161 { 162 fprintf(stderr, "usage: ul [-i] [-t terminal] file...\n"); 163 exit(1); 164 } 165 166 void 167 filter(FILE *f) 168 { 169 int c; 170 171 while ((c = getc(f)) != EOF && col < MAXBUF) switch(c) { 172 173 case '\b': 174 if (col > 0) 175 col--; 176 continue; 177 178 case '\t': 179 col = (col+8) & ~07; 180 if (col > maxcol) 181 maxcol = col; 182 continue; 183 184 case '\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 = getc(f)) { 198 199 case HREV: 200 if (halfpos == 0) { 201 mode |= SUPERSC; 202 halfpos--; 203 } else if (halfpos > 0) { 204 mode &= ~SUBSC; 205 halfpos--; 206 } else { 207 halfpos = 0; 208 reverse(); 209 } 210 continue; 211 212 case HFWD: 213 if (halfpos == 0) { 214 mode |= SUBSC; 215 halfpos++; 216 } else if (halfpos < 0) { 217 mode &= ~SUPERSC; 218 halfpos++; 219 } else { 220 halfpos = 0; 221 fwd(); 222 } 223 continue; 224 225 case FREV: 226 reverse(); 227 continue; 228 229 default: 230 errx(1, "unknown escape sequence in input: %o, %o", IESC, c); 231 } 232 continue; 233 234 case '_': 235 if (obuf[col].c_char) 236 obuf[col].c_mode |= UNDERL | mode; 237 else 238 obuf[col].c_char = '_'; 239 case ' ': 240 col++; 241 if (col > maxcol) 242 maxcol = col; 243 continue; 244 245 case '\n': 246 flushln(); 247 continue; 248 249 case '\f': 250 flushln(); 251 putchar('\f'); 252 continue; 253 254 default: 255 if (c < ' ') /* non printing */ 256 continue; 257 if (obuf[col].c_char == '\0') { 258 obuf[col].c_char = c; 259 obuf[col].c_mode = mode; 260 } else if (obuf[col].c_char == '_') { 261 obuf[col].c_char = c; 262 obuf[col].c_mode |= UNDERL|mode; 263 } else if (obuf[col].c_char == c) 264 obuf[col].c_mode |= BOLD|mode; 265 else 266 obuf[col].c_mode = mode; 267 col++; 268 if (col > maxcol) 269 maxcol = col; 270 continue; 271 } 272 if (maxcol) 273 flushln(); 274 } 275 276 void 277 flushln(void) 278 { 279 int lastmode; 280 int i; 281 int hadmodes = 0; 282 283 lastmode = NORMAL; 284 for (i=0; i<maxcol; i++) { 285 if (obuf[i].c_mode != lastmode) { 286 hadmodes++; 287 setnewmode(obuf[i].c_mode); 288 lastmode = obuf[i].c_mode; 289 } 290 if (obuf[i].c_char == '\0') { 291 if (upln) 292 PRINT(CURS_RIGHT); 293 else 294 outc(' '); 295 } else 296 outc(obuf[i].c_char); 297 } 298 if (lastmode != NORMAL) { 299 setnewmode(0); 300 } 301 if (must_overstrike && hadmodes) 302 overstrike(); 303 putchar('\n'); 304 if (iflag && hadmodes) 305 iattr(); 306 (void)fflush(stdout); 307 if (upln) 308 upln--; 309 initbuf(); 310 } 311 312 /* 313 * For terminals that can overstrike, overstrike underlines and bolds. 314 * We don't do anything with halfline ups and downs, or Greek. 315 */ 316 void 317 overstrike(void) 318 { 319 int i; 320 char lbuf[256]; 321 char *cp = lbuf; 322 int hadbold=0; 323 324 /* Set up overstrike buffer */ 325 for (i=0; i<maxcol; i++) 326 switch (obuf[i].c_mode) { 327 case NORMAL: 328 default: 329 *cp++ = ' '; 330 break; 331 case UNDERL: 332 *cp++ = '_'; 333 break; 334 case BOLD: 335 *cp++ = obuf[i].c_char; 336 hadbold=1; 337 break; 338 } 339 putchar('\r'); 340 for (*cp=' '; *cp==' '; cp--) 341 *cp = 0; 342 for (cp=lbuf; *cp; cp++) 343 putchar(*cp); 344 if (hadbold) { 345 putchar('\r'); 346 for (cp=lbuf; *cp; cp++) 347 putchar(*cp=='_' ? ' ' : *cp); 348 putchar('\r'); 349 for (cp=lbuf; *cp; cp++) 350 putchar(*cp=='_' ? ' ' : *cp); 351 } 352 } 353 354 void 355 iattr(void) 356 { 357 int i; 358 char lbuf[256]; 359 char *cp = lbuf; 360 361 for (i=0; i<maxcol; i++) 362 switch (obuf[i].c_mode) { 363 case NORMAL: *cp++ = ' '; break; 364 case ALTSET: *cp++ = 'g'; break; 365 case SUPERSC: *cp++ = '^'; break; 366 case SUBSC: *cp++ = 'v'; break; 367 case UNDERL: *cp++ = '_'; break; 368 case BOLD: *cp++ = '!'; break; 369 default: *cp++ = 'X'; break; 370 } 371 for (*cp=' '; *cp==' '; cp--) 372 *cp = 0; 373 for (cp=lbuf; *cp; cp++) 374 putchar(*cp); 375 putchar('\n'); 376 } 377 378 void 379 initbuf(void) 380 { 381 382 bzero((char *)obuf, sizeof (obuf)); /* depends on NORMAL == 0 */ 383 col = 0; 384 maxcol = 0; 385 mode &= ALTSET; 386 } 387 388 void 389 fwd(void) 390 { 391 int oldcol, oldmax; 392 393 oldcol = col; 394 oldmax = maxcol; 395 flushln(); 396 col = oldcol; 397 maxcol = oldmax; 398 } 399 400 void 401 reverse(void) 402 { 403 upln++; 404 fwd(); 405 PRINT(CURS_UP); 406 PRINT(CURS_UP); 407 upln++; 408 } 409 410 void 411 initcap(void) 412 { 413 static char tcapbuf[512]; 414 char *bp = tcapbuf; 415 416 /* This nonsense attempts to work with both old and new termcap */ 417 CURS_UP = tgetstr("up", &bp); 418 CURS_RIGHT = tgetstr("ri", &bp); 419 if (CURS_RIGHT == NULL) 420 CURS_RIGHT = tgetstr("nd", &bp); 421 CURS_LEFT = tgetstr("le", &bp); 422 if (CURS_LEFT == NULL) 423 CURS_LEFT = tgetstr("bc", &bp); 424 if (CURS_LEFT == NULL && tgetflag("bs")) 425 CURS_LEFT = "\b"; 426 427 ENTER_STANDOUT = tgetstr("so", &bp); 428 EXIT_STANDOUT = tgetstr("se", &bp); 429 ENTER_UNDERLINE = tgetstr("us", &bp); 430 EXIT_UNDERLINE = tgetstr("ue", &bp); 431 ENTER_DIM = tgetstr("mh", &bp); 432 ENTER_BOLD = tgetstr("md", &bp); 433 ENTER_REVERSE = tgetstr("mr", &bp); 434 EXIT_ATTRIBUTES = tgetstr("me", &bp); 435 436 if (!ENTER_BOLD && ENTER_REVERSE) 437 ENTER_BOLD = ENTER_REVERSE; 438 if (!ENTER_BOLD && ENTER_STANDOUT) 439 ENTER_BOLD = ENTER_STANDOUT; 440 if (!ENTER_UNDERLINE && ENTER_STANDOUT) { 441 ENTER_UNDERLINE = ENTER_STANDOUT; 442 EXIT_UNDERLINE = EXIT_STANDOUT; 443 } 444 if (!ENTER_DIM && ENTER_STANDOUT) 445 ENTER_DIM = ENTER_STANDOUT; 446 if (!ENTER_REVERSE && ENTER_STANDOUT) 447 ENTER_REVERSE = ENTER_STANDOUT; 448 if (!EXIT_ATTRIBUTES && EXIT_STANDOUT) 449 EXIT_ATTRIBUTES = EXIT_STANDOUT; 450 451 /* 452 * Note that we use REVERSE for the alternate character set, 453 * not the as/ae capabilities. This is because we are modelling 454 * the model 37 teletype (since that's what nroff outputs) and 455 * the typical as/ae is more of a graphics set, not the greek 456 * letters the 37 has. 457 */ 458 459 UNDER_CHAR = tgetstr("uc", &bp); 460 must_use_uc = (UNDER_CHAR && !ENTER_UNDERLINE); 461 } 462 463 int 464 outchar(int c) 465 { 466 return(putchar(c & 0177)); 467 } 468 469 static int curmode = 0; 470 471 void 472 outc(int c) 473 { 474 putchar(c); 475 if (must_use_uc && (curmode&UNDERL)) { 476 PRINT(CURS_LEFT); 477 PRINT(UNDER_CHAR); 478 } 479 } 480 481 void 482 setnewmode(int newmode) 483 { 484 if (!iflag) { 485 if (curmode != NORMAL && newmode != NORMAL) 486 setnewmode(NORMAL); 487 switch (newmode) { 488 case NORMAL: 489 switch(curmode) { 490 case NORMAL: 491 break; 492 case UNDERL: 493 PRINT(EXIT_UNDERLINE); 494 break; 495 default: 496 /* This includes standout */ 497 PRINT(EXIT_ATTRIBUTES); 498 break; 499 } 500 break; 501 case ALTSET: 502 PRINT(ENTER_REVERSE); 503 break; 504 case SUPERSC: 505 /* 506 * This only works on a few terminals. 507 * It should be fixed. 508 */ 509 PRINT(ENTER_UNDERLINE); 510 PRINT(ENTER_DIM); 511 break; 512 case SUBSC: 513 PRINT(ENTER_DIM); 514 break; 515 case UNDERL: 516 PRINT(ENTER_UNDERLINE); 517 break; 518 case BOLD: 519 PRINT(ENTER_BOLD); 520 break; 521 default: 522 /* 523 * We should have some provision here for multiple modes 524 * on at once. This will have to come later. 525 */ 526 PRINT(ENTER_STANDOUT); 527 break; 528 } 529 } 530 curmode = newmode; 531 } 532