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