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