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