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