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