1 /* $NetBSD: ul.c,v 1.16 2012/03/20 20:34:59 matt 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. Neither the name of the University nor the names of its contributors 16 * may be used to endorse or promote products derived from this software 17 * without specific prior written permission. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29 * SUCH DAMAGE. 30 */ 31 32 #include <sys/cdefs.h> 33 #ifndef lint 34 __COPYRIGHT("@(#) Copyright (c) 1980, 1993\ 35 The Regents of the University of California. All rights reserved."); 36 #endif /* not lint */ 37 38 #ifndef lint 39 #if 0 40 static char sccsid[] = "@(#)ul.c 8.1 (Berkeley) 6/6/93"; 41 #endif 42 __RCSID("$NetBSD: ul.c,v 1.16 2012/03/20 20:34:59 matt Exp $"); 43 #endif /* not lint */ 44 45 #include <stdio.h> 46 #include <stdlib.h> 47 #include <string.h> 48 #include <term.h> 49 #include <unistd.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 struct tinfo *info; 67 int must_overstrike; 68 const char *CURS_UP, *CURS_RIGHT, *CURS_LEFT, 69 *ENTER_STANDOUT, *EXIT_STANDOUT, *ENTER_UNDERLINE, *EXIT_UNDERLINE, 70 *ENTER_DIM, *ENTER_BOLD, *ENTER_REVERSE, *UNDER_CHAR, *EXIT_ATTRIBUTES; 71 72 struct CHAR { 73 char c_mode; 74 char c_char; 75 } ; 76 77 struct CHAR obuf[MAXBUF]; 78 int col, maxcol; 79 int mode; 80 int halfpos; 81 int upln; 82 int iflag; 83 84 int main __P((int, char **)); 85 void filter __P((FILE *)); 86 void flushln __P((void)); 87 void fwd __P((void)); 88 void iattr __P((void)); 89 void initbuf __P((void)); 90 void initcap __P((void)); 91 void outc __P((int)); 92 int outchar __P((int)); 93 void overstrike __P((void)); 94 void reverse __P((void)); 95 void setulmode __P((int)); 96 97 98 #define PRINT(s) if (s == NULL) /* void */; else tputs(s, 1, outchar) 99 100 int 101 main(int argc, char **argv) 102 { 103 int c; 104 const char *termtype; 105 FILE *f; 106 107 termtype = getenv("TERM"); 108 if (termtype == NULL || (argv[0][0] == 'c' && !isatty(1))) 109 termtype = "lpr"; 110 while ((c=getopt(argc, argv, "it:T:")) != -1) 111 switch(c) { 112 113 case 't': 114 case 'T': /* for nroff compatibility */ 115 termtype = optarg; 116 break; 117 case 'i': 118 iflag = 1; 119 break; 120 121 default: 122 fprintf(stderr, 123 "usage: %s [ -i ] [ -tTerm ] file...\n", 124 argv[0]); 125 exit(1); 126 } 127 128 setupterm(termtype, 0, NULL); 129 if ((over_strike && enter_bold_mode == NULL) || 130 (transparent_underline && enter_underline_mode == NULL && 131 underline_char == NULL)) 132 initbuf(); 133 if (optind == argc) 134 filter(stdin); 135 else for (; optind<argc; optind++) { 136 f = fopen(argv[optind],"r"); 137 if (f == NULL) { 138 perror(argv[optind]); 139 exit(1); 140 } 141 142 filter(f); 143 fclose(f); 144 } 145 exit(0); 146 } 147 148 void 149 filter(FILE *f) 150 { 151 int c; 152 153 while ((c = getc(f)) != EOF) switch(c) { 154 155 case '\b': 156 if (col > 0) 157 col--; 158 continue; 159 160 case '\t': 161 col = (col+8) & ~07; 162 if (col > maxcol) 163 maxcol = col; 164 continue; 165 166 case '\r': 167 col = 0; 168 continue; 169 170 case SO: 171 mode |= ALTSET; 172 continue; 173 174 case SI: 175 mode &= ~ALTSET; 176 continue; 177 178 case IESC: 179 switch (c = getc(f)) { 180 181 case HREV: 182 if (halfpos == 0) { 183 mode |= SUPERSC; 184 halfpos--; 185 } else if (halfpos > 0) { 186 mode &= ~SUBSC; 187 halfpos--; 188 } else { 189 halfpos = 0; 190 reverse(); 191 } 192 continue; 193 194 case HFWD: 195 if (halfpos == 0) { 196 mode |= SUBSC; 197 halfpos++; 198 } else if (halfpos < 0) { 199 mode &= ~SUPERSC; 200 halfpos++; 201 } else { 202 halfpos = 0; 203 fwd(); 204 } 205 continue; 206 207 case FREV: 208 reverse(); 209 continue; 210 211 default: 212 fprintf(stderr, 213 "Unknown escape sequence in input: %o, %o\n", 214 IESC, c); 215 exit(1); 216 } 217 continue; 218 219 case '_': 220 if (obuf[col].c_char) 221 obuf[col].c_mode |= UNDERL | mode; 222 else 223 obuf[col].c_char = '_'; 224 case ' ': 225 col++; 226 if (col > maxcol) 227 maxcol = col; 228 continue; 229 230 case '\n': 231 flushln(); 232 continue; 233 234 case '\f': 235 flushln(); 236 putchar('\f'); 237 continue; 238 239 default: 240 if (c < ' ') /* non printing */ 241 continue; 242 if (obuf[col].c_char == '\0') { 243 obuf[col].c_char = c; 244 obuf[col].c_mode = mode; 245 } else if (obuf[col].c_char == '_') { 246 obuf[col].c_char = c; 247 obuf[col].c_mode |= UNDERL|mode; 248 } else if (obuf[col].c_char == c) 249 obuf[col].c_mode |= BOLD|mode; 250 else 251 obuf[col].c_mode = mode; 252 col++; 253 if (col > maxcol) 254 maxcol = col; 255 continue; 256 } 257 if (maxcol) 258 flushln(); 259 } 260 261 void 262 flushln(void) 263 { 264 int lastmode; 265 int i; 266 int hadmodes = 0; 267 268 lastmode = NORMAL; 269 for (i=0; i<maxcol; i++) { 270 if (obuf[i].c_mode != lastmode) { 271 hadmodes++; 272 setulmode(obuf[i].c_mode); 273 lastmode = obuf[i].c_mode; 274 } 275 if (obuf[i].c_char == '\0') { 276 if (upln) { 277 PRINT(cursor_right); 278 } 279 else { 280 outc(' '); 281 } 282 } else 283 outc(obuf[i].c_char); 284 } 285 if (lastmode != NORMAL) { 286 setulmode(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 void 304 overstrike(void) 305 { 306 int i; 307 char lbuf[256]; 308 char *cp = lbuf; 309 int hadbold=0; 310 311 /* Set up overstrike buffer */ 312 for (i=0; i<maxcol; i++) 313 switch (obuf[i].c_mode) { 314 case NORMAL: 315 default: 316 *cp++ = ' '; 317 break; 318 case UNDERL: 319 *cp++ = '_'; 320 break; 321 case BOLD: 322 *cp++ = obuf[i].c_char; 323 hadbold=1; 324 break; 325 } 326 putchar('\r'); 327 for (*cp=' '; *cp==' '; cp--) 328 *cp = 0; 329 for (cp=lbuf; *cp; cp++) 330 putchar(*cp); 331 if (hadbold) { 332 putchar('\r'); 333 for (cp=lbuf; *cp; cp++) 334 putchar(*cp=='_' ? ' ' : *cp); 335 putchar('\r'); 336 for (cp=lbuf; *cp; cp++) 337 putchar(*cp=='_' ? ' ' : *cp); 338 } 339 } 340 341 void 342 iattr(void) 343 { 344 int i; 345 char lbuf[256]; 346 char *cp = lbuf; 347 348 for (i=0; i<maxcol; i++) 349 switch (obuf[i].c_mode) { 350 case NORMAL: *cp++ = ' '; break; 351 case ALTSET: *cp++ = 'g'; break; 352 case SUPERSC: *cp++ = '^'; break; 353 case SUBSC: *cp++ = 'v'; break; 354 case UNDERL: *cp++ = '_'; break; 355 case BOLD: *cp++ = '!'; break; 356 default: *cp++ = 'X'; break; 357 } 358 for (*cp=' '; *cp==' '; cp--) 359 *cp = 0; 360 for (cp=lbuf; *cp; cp++) 361 putchar(*cp); 362 putchar('\n'); 363 } 364 365 void 366 initbuf(void) 367 { 368 369 memset((char *)obuf, 0, sizeof (obuf)); /* depends on NORMAL == 0 */ 370 col = 0; 371 maxcol = 0; 372 mode &= ALTSET; 373 } 374 375 void 376 fwd(void) 377 { 378 int oldcol, oldmax; 379 380 oldcol = col; 381 oldmax = maxcol; 382 flushln(); 383 col = oldcol; 384 maxcol = oldmax; 385 } 386 387 void 388 reverse(void) 389 { 390 upln++; 391 fwd(); 392 PRINT(cursor_up); 393 PRINT(cursor_up); 394 upln++; 395 } 396 397 int 398 outchar(int c) 399 { 400 return (putchar(c & 0177)); 401 } 402 403 static int curmode = 0; 404 405 void 406 outc(int c) 407 { 408 putchar(c); 409 if (underline_char && !enter_underline_mode && (curmode & UNDERL)) { 410 if (cursor_left) 411 PRINT(cursor_left); 412 else 413 putchar('\b'); 414 PRINT(underline_char); 415 } 416 } 417 418 void 419 setulmode(int newmode) 420 { 421 if (!iflag) { 422 if (curmode != NORMAL && newmode != NORMAL) 423 setulmode(NORMAL); 424 switch (newmode) { 425 case NORMAL: 426 switch(curmode) { 427 case NORMAL: 428 break; 429 case UNDERL: 430 if (enter_underline_mode) 431 PRINT(exit_underline_mode); 432 else 433 PRINT(exit_standout_mode); 434 break; 435 default: 436 /* This includes standout */ 437 if (exit_attribute_mode) 438 PRINT(exit_attribute_mode); 439 else 440 PRINT(exit_standout_mode); 441 break; 442 } 443 break; 444 case ALTSET: 445 if (enter_reverse_mode) 446 PRINT(enter_reverse_mode); 447 else 448 PRINT(enter_standout_mode); 449 break; 450 case SUPERSC: 451 /* 452 * This only works on a few terminals. 453 * It should be fixed. 454 */ 455 PRINT(enter_underline_mode); 456 PRINT(enter_dim_mode); 457 break; 458 case SUBSC: 459 if (enter_dim_mode) 460 PRINT(enter_dim_mode); 461 else 462 PRINT(enter_standout_mode); 463 break; 464 case UNDERL: 465 if (enter_underline_mode) 466 PRINT(enter_underline_mode); 467 else 468 PRINT(enter_standout_mode); 469 break; 470 case BOLD: 471 if (enter_bold_mode) 472 PRINT(enter_bold_mode); 473 else 474 PRINT(enter_reverse_mode); 475 break; 476 default: 477 /* 478 * We should have some provision here for multiple modes 479 * on at once. This will have to come later. 480 */ 481 PRINT(enter_standout_mode); 482 break; 483 } 484 } 485 curmode = newmode; 486 } 487