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