1 /* $NetBSD: ul.c,v 1.15 2010/02/03 15:34:46 roy 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.15 2010/02/03 15:34:46 roy 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(argc, argv) 102 int argc; 103 char **argv; 104 { 105 int c; 106 const char *termtype; 107 FILE *f; 108 109 termtype = getenv("TERM"); 110 if (termtype == NULL || (argv[0][0] == 'c' && !isatty(1))) 111 termtype = "lpr"; 112 while ((c=getopt(argc, argv, "it:T:")) != -1) 113 switch(c) { 114 115 case 't': 116 case 'T': /* for nroff compatibility */ 117 termtype = optarg; 118 break; 119 case 'i': 120 iflag = 1; 121 break; 122 123 default: 124 fprintf(stderr, 125 "usage: %s [ -i ] [ -tTerm ] file...\n", 126 argv[0]); 127 exit(1); 128 } 129 130 setupterm(termtype, 0, NULL); 131 if ((over_strike && enter_bold_mode == NULL) || 132 (transparent_underline && enter_underline_mode == NULL && 133 underline_char == NULL)) 134 initbuf(); 135 if (optind == argc) 136 filter(stdin); 137 else for (; optind<argc; optind++) { 138 f = fopen(argv[optind],"r"); 139 if (f == NULL) { 140 perror(argv[optind]); 141 exit(1); 142 } 143 144 filter(f); 145 fclose(f); 146 } 147 exit(0); 148 } 149 150 void 151 filter(f) 152 FILE *f; 153 { 154 int 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 void 265 flushln() 266 { 267 int lastmode; 268 int i; 269 int hadmodes = 0; 270 271 lastmode = NORMAL; 272 for (i=0; i<maxcol; i++) { 273 if (obuf[i].c_mode != lastmode) { 274 hadmodes++; 275 setulmode(obuf[i].c_mode); 276 lastmode = obuf[i].c_mode; 277 } 278 if (obuf[i].c_char == '\0') { 279 if (upln) { 280 PRINT(cursor_right); 281 } 282 else { 283 outc(' '); 284 } 285 } else 286 outc(obuf[i].c_char); 287 } 288 if (lastmode != NORMAL) { 289 setulmode(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() 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() 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() 370 { 371 372 memset((char *)obuf, 0, sizeof (obuf)); /* depends on NORMAL == 0 */ 373 col = 0; 374 maxcol = 0; 375 mode &= ALTSET; 376 } 377 378 void 379 fwd() 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() 392 { 393 upln++; 394 fwd(); 395 PRINT(cursor_up); 396 PRINT(cursor_up); 397 upln++; 398 } 399 400 int 401 outchar(c) 402 int c; 403 { 404 return (putchar(c & 0177)); 405 } 406 407 static int curmode = 0; 408 409 void 410 outc(c) 411 int c; 412 { 413 putchar(c); 414 if (underline_char && !enter_underline_mode && (curmode & UNDERL)) { 415 if (cursor_left) 416 PRINT(cursor_left); 417 else 418 putchar('\b'); 419 PRINT(underline_char); 420 } 421 } 422 423 void 424 setulmode(newmode) 425 int newmode; 426 { 427 if (!iflag) { 428 if (curmode != NORMAL && newmode != NORMAL) 429 setulmode(NORMAL); 430 switch (newmode) { 431 case NORMAL: 432 switch(curmode) { 433 case NORMAL: 434 break; 435 case UNDERL: 436 if (enter_underline_mode) 437 PRINT(exit_underline_mode); 438 else 439 PRINT(exit_standout_mode); 440 break; 441 default: 442 /* This includes standout */ 443 if (exit_attribute_mode) 444 PRINT(exit_attribute_mode); 445 else 446 PRINT(exit_standout_mode); 447 break; 448 } 449 break; 450 case ALTSET: 451 if (enter_reverse_mode) 452 PRINT(enter_reverse_mode); 453 else 454 PRINT(enter_standout_mode); 455 break; 456 case SUPERSC: 457 /* 458 * This only works on a few terminals. 459 * It should be fixed. 460 */ 461 PRINT(enter_underline_mode); 462 PRINT(enter_dim_mode); 463 break; 464 case SUBSC: 465 if (enter_dim_mode) 466 PRINT(enter_dim_mode); 467 else 468 PRINT(enter_standout_mode); 469 break; 470 case UNDERL: 471 if (enter_underline_mode) 472 PRINT(enter_underline_mode); 473 else 474 PRINT(enter_standout_mode); 475 break; 476 case BOLD: 477 if (enter_bold_mode) 478 PRINT(enter_bold_mode); 479 else 480 PRINT(enter_reverse_mode); 481 break; 482 default: 483 /* 484 * We should have some provision here for multiple modes 485 * on at once. This will have to come later. 486 */ 487 PRINT(enter_standout_mode); 488 break; 489 } 490 } 491 curmode = newmode; 492 } 493