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