1 1.20 mrg /* $NetBSD: ul.c,v 1.20 2019/02/03 03:19:30 mrg Exp $ */ 2 1.3 jtc 3 1.1 cgd /* 4 1.3 jtc * Copyright (c) 1980, 1993 5 1.3 jtc * The Regents of the University of California. All rights reserved. 6 1.1 cgd * 7 1.1 cgd * Redistribution and use in source and binary forms, with or without 8 1.1 cgd * modification, are permitted provided that the following conditions 9 1.1 cgd * are met: 10 1.1 cgd * 1. Redistributions of source code must retain the above copyright 11 1.1 cgd * notice, this list of conditions and the following disclaimer. 12 1.1 cgd * 2. Redistributions in binary form must reproduce the above copyright 13 1.1 cgd * notice, this list of conditions and the following disclaimer in the 14 1.1 cgd * documentation and/or other materials provided with the distribution. 15 1.12 agc * 3. Neither the name of the University nor the names of its contributors 16 1.1 cgd * may be used to endorse or promote products derived from this software 17 1.1 cgd * without specific prior written permission. 18 1.1 cgd * 19 1.1 cgd * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 20 1.1 cgd * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 1.1 cgd * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 1.1 cgd * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 23 1.1 cgd * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 1.1 cgd * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 25 1.1 cgd * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26 1.1 cgd * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27 1.1 cgd * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28 1.1 cgd * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29 1.1 cgd * SUCH DAMAGE. 30 1.1 cgd */ 31 1.1 cgd 32 1.5 lukem #include <sys/cdefs.h> 33 1.1 cgd #ifndef lint 34 1.13 lukem __COPYRIGHT("@(#) Copyright (c) 1980, 1993\ 35 1.13 lukem The Regents of the University of California. All rights reserved."); 36 1.1 cgd #endif /* not lint */ 37 1.1 cgd 38 1.1 cgd #ifndef lint 39 1.3 jtc #if 0 40 1.3 jtc static char sccsid[] = "@(#)ul.c 8.1 (Berkeley) 6/6/93"; 41 1.3 jtc #endif 42 1.20 mrg __RCSID("$NetBSD: ul.c,v 1.20 2019/02/03 03:19:30 mrg Exp $"); 43 1.1 cgd #endif /* not lint */ 44 1.1 cgd 45 1.17 abhinav #include <err.h> 46 1.1 cgd #include <stdio.h> 47 1.4 jtc #include <stdlib.h> 48 1.4 jtc #include <string.h> 49 1.15 roy #include <term.h> 50 1.5 lukem #include <unistd.h> 51 1.17 abhinav #include <util.h> 52 1.1 cgd 53 1.1 cgd #define IESC '\033' 54 1.1 cgd #define SO '\016' 55 1.1 cgd #define SI '\017' 56 1.1 cgd #define HFWD '9' 57 1.1 cgd #define HREV '8' 58 1.1 cgd #define FREV '7' 59 1.1 cgd #define MAXBUF 512 60 1.1 cgd 61 1.1 cgd #define NORMAL 000 62 1.1 cgd #define ALTSET 001 /* Reverse */ 63 1.1 cgd #define SUPERSC 002 /* Dim */ 64 1.1 cgd #define SUBSC 004 /* Dim | Ul */ 65 1.1 cgd #define UNDERL 010 /* Ul */ 66 1.1 cgd #define BOLD 020 /* Bold */ 67 1.1 cgd 68 1.19 abhinav static int must_overstrike; 69 1.1 cgd 70 1.1 cgd struct CHAR { 71 1.1 cgd char c_mode; 72 1.1 cgd char c_char; 73 1.1 cgd } ; 74 1.1 cgd 75 1.19 abhinav static size_t col, maxcol; 76 1.19 abhinav static int mode; 77 1.19 abhinav static int halfpos; 78 1.19 abhinav static int upln; 79 1.19 abhinav static int iflag; 80 1.19 abhinav 81 1.19 abhinav static void filter(FILE *); 82 1.19 abhinav static void flushln(struct CHAR *, size_t); 83 1.19 abhinav static void fwd(struct CHAR *, size_t); 84 1.19 abhinav static void iattr(struct CHAR *); 85 1.19 abhinav static void initbuf(struct CHAR *, size_t); 86 1.19 abhinav static void outc(int); 87 1.19 abhinav static int outchar(int); 88 1.19 abhinav static void overstrike(struct CHAR *); 89 1.19 abhinav static void reverse(struct CHAR *, size_t); 90 1.19 abhinav static void setulmode(int); 91 1.17 abhinav static void alloc_buf(struct CHAR **, size_t *); 92 1.17 abhinav static void set_mode(void); 93 1.5 lukem 94 1.5 lukem 95 1.1 cgd #define PRINT(s) if (s == NULL) /* void */; else tputs(s, 1, outchar) 96 1.1 cgd 97 1.5 lukem int 98 1.16 matt main(int argc, char **argv) 99 1.1 cgd { 100 1.1 cgd int c; 101 1.14 lukem const char *termtype; 102 1.1 cgd FILE *f; 103 1.1 cgd 104 1.1 cgd termtype = getenv("TERM"); 105 1.1 cgd if (termtype == NULL || (argv[0][0] == 'c' && !isatty(1))) 106 1.1 cgd termtype = "lpr"; 107 1.6 lukem while ((c=getopt(argc, argv, "it:T:")) != -1) 108 1.1 cgd switch(c) { 109 1.1 cgd 110 1.1 cgd case 't': 111 1.1 cgd case 'T': /* for nroff compatibility */ 112 1.1 cgd termtype = optarg; 113 1.1 cgd break; 114 1.1 cgd case 'i': 115 1.1 cgd iflag = 1; 116 1.1 cgd break; 117 1.1 cgd 118 1.1 cgd default: 119 1.1 cgd fprintf(stderr, 120 1.1 cgd "usage: %s [ -i ] [ -tTerm ] file...\n", 121 1.1 cgd argv[0]); 122 1.1 cgd exit(1); 123 1.1 cgd } 124 1.1 cgd 125 1.15 roy setupterm(termtype, 0, NULL); 126 1.15 roy if ((over_strike && enter_bold_mode == NULL) || 127 1.15 roy (transparent_underline && enter_underline_mode == NULL && 128 1.17 abhinav underline_char == NULL)) { 129 1.17 abhinav set_mode(); 130 1.17 abhinav } 131 1.1 cgd if (optind == argc) 132 1.1 cgd filter(stdin); 133 1.17 abhinav else { 134 1.17 abhinav for (; optind < argc; optind++) { 135 1.17 abhinav f = fopen(argv[optind], "r"); 136 1.17 abhinav if (f == NULL) 137 1.17 abhinav err(EXIT_FAILURE, "Failed to open `%s'", argv[optind]); 138 1.17 abhinav filter(f); 139 1.17 abhinav fclose(f); 140 1.4 jtc } 141 1.1 cgd } 142 1.1 cgd exit(0); 143 1.1 cgd } 144 1.1 cgd 145 1.19 abhinav static void 146 1.16 matt filter(FILE *f) 147 1.1 cgd { 148 1.5 lukem int c; 149 1.17 abhinav struct CHAR *obuf = NULL; 150 1.17 abhinav size_t obuf_size = 0; 151 1.17 abhinav alloc_buf(&obuf, &obuf_size); 152 1.1 cgd 153 1.1 cgd while ((c = getc(f)) != EOF) switch(c) { 154 1.1 cgd 155 1.1 cgd case '\b': 156 1.1 cgd if (col > 0) 157 1.1 cgd col--; 158 1.1 cgd continue; 159 1.1 cgd 160 1.1 cgd case '\t': 161 1.1 cgd col = (col+8) & ~07; 162 1.1 cgd if (col > maxcol) 163 1.1 cgd maxcol = col; 164 1.17 abhinav if (col >= obuf_size) 165 1.17 abhinav alloc_buf(&obuf, &obuf_size); 166 1.1 cgd continue; 167 1.1 cgd 168 1.1 cgd case '\r': 169 1.1 cgd col = 0; 170 1.1 cgd continue; 171 1.1 cgd 172 1.1 cgd case SO: 173 1.1 cgd mode |= ALTSET; 174 1.1 cgd continue; 175 1.1 cgd 176 1.1 cgd case SI: 177 1.1 cgd mode &= ~ALTSET; 178 1.1 cgd continue; 179 1.1 cgd 180 1.1 cgd case IESC: 181 1.1 cgd switch (c = getc(f)) { 182 1.1 cgd 183 1.1 cgd case HREV: 184 1.1 cgd if (halfpos == 0) { 185 1.1 cgd mode |= SUPERSC; 186 1.1 cgd halfpos--; 187 1.1 cgd } else if (halfpos > 0) { 188 1.1 cgd mode &= ~SUBSC; 189 1.1 cgd halfpos--; 190 1.1 cgd } else { 191 1.1 cgd halfpos = 0; 192 1.17 abhinav reverse(obuf, obuf_size); 193 1.1 cgd } 194 1.1 cgd continue; 195 1.1 cgd 196 1.1 cgd case HFWD: 197 1.1 cgd if (halfpos == 0) { 198 1.1 cgd mode |= SUBSC; 199 1.1 cgd halfpos++; 200 1.1 cgd } else if (halfpos < 0) { 201 1.1 cgd mode &= ~SUPERSC; 202 1.1 cgd halfpos++; 203 1.1 cgd } else { 204 1.1 cgd halfpos = 0; 205 1.17 abhinav fwd(obuf, obuf_size); 206 1.1 cgd } 207 1.1 cgd continue; 208 1.1 cgd 209 1.1 cgd case FREV: 210 1.17 abhinav reverse(obuf, obuf_size); 211 1.1 cgd continue; 212 1.1 cgd 213 1.1 cgd default: 214 1.1 cgd fprintf(stderr, 215 1.1 cgd "Unknown escape sequence in input: %o, %o\n", 216 1.1 cgd IESC, c); 217 1.1 cgd exit(1); 218 1.1 cgd } 219 1.1 cgd 220 1.1 cgd case '_': 221 1.1 cgd if (obuf[col].c_char) 222 1.1 cgd obuf[col].c_mode |= UNDERL | mode; 223 1.1 cgd else 224 1.1 cgd obuf[col].c_char = '_'; 225 1.20 mrg /* FALLTHROUGH */ 226 1.1 cgd case ' ': 227 1.1 cgd col++; 228 1.1 cgd if (col > maxcol) 229 1.1 cgd maxcol = col; 230 1.17 abhinav if (col >= obuf_size) 231 1.17 abhinav alloc_buf(&obuf, &obuf_size); 232 1.1 cgd continue; 233 1.1 cgd 234 1.1 cgd case '\n': 235 1.17 abhinav flushln(obuf, obuf_size); 236 1.1 cgd continue; 237 1.1 cgd 238 1.1 cgd case '\f': 239 1.17 abhinav flushln(obuf, obuf_size); 240 1.1 cgd putchar('\f'); 241 1.1 cgd continue; 242 1.1 cgd 243 1.1 cgd default: 244 1.1 cgd if (c < ' ') /* non printing */ 245 1.1 cgd continue; 246 1.1 cgd if (obuf[col].c_char == '\0') { 247 1.1 cgd obuf[col].c_char = c; 248 1.1 cgd obuf[col].c_mode = mode; 249 1.1 cgd } else if (obuf[col].c_char == '_') { 250 1.1 cgd obuf[col].c_char = c; 251 1.1 cgd obuf[col].c_mode |= UNDERL|mode; 252 1.1 cgd } else if (obuf[col].c_char == c) 253 1.1 cgd obuf[col].c_mode |= BOLD|mode; 254 1.1 cgd else 255 1.1 cgd obuf[col].c_mode = mode; 256 1.1 cgd col++; 257 1.1 cgd if (col > maxcol) 258 1.1 cgd maxcol = col; 259 1.17 abhinav if (col >= obuf_size) 260 1.17 abhinav alloc_buf(&obuf, &obuf_size); 261 1.1 cgd continue; 262 1.1 cgd } 263 1.1 cgd if (maxcol) 264 1.17 abhinav flushln(obuf, obuf_size); 265 1.17 abhinav 266 1.17 abhinav free(obuf); 267 1.1 cgd } 268 1.1 cgd 269 1.19 abhinav static void 270 1.17 abhinav flushln(struct CHAR *obuf, size_t obuf_size) 271 1.1 cgd { 272 1.5 lukem int lastmode; 273 1.17 abhinav size_t i; 274 1.1 cgd int hadmodes = 0; 275 1.1 cgd 276 1.1 cgd lastmode = NORMAL; 277 1.1 cgd for (i=0; i<maxcol; i++) { 278 1.1 cgd if (obuf[i].c_mode != lastmode) { 279 1.1 cgd hadmodes++; 280 1.5 lukem setulmode(obuf[i].c_mode); 281 1.1 cgd lastmode = obuf[i].c_mode; 282 1.1 cgd } 283 1.1 cgd if (obuf[i].c_char == '\0') { 284 1.7 christos if (upln) { 285 1.15 roy PRINT(cursor_right); 286 1.7 christos } 287 1.7 christos else { 288 1.1 cgd outc(' '); 289 1.7 christos } 290 1.1 cgd } else 291 1.1 cgd outc(obuf[i].c_char); 292 1.1 cgd } 293 1.1 cgd if (lastmode != NORMAL) { 294 1.5 lukem setulmode(0); 295 1.1 cgd } 296 1.1 cgd if (must_overstrike && hadmodes) 297 1.17 abhinav overstrike(obuf); 298 1.1 cgd putchar('\n'); 299 1.1 cgd if (iflag && hadmodes) 300 1.17 abhinav iattr(obuf); 301 1.1 cgd (void)fflush(stdout); 302 1.1 cgd if (upln) 303 1.1 cgd upln--; 304 1.17 abhinav initbuf(obuf, obuf_size); 305 1.1 cgd } 306 1.1 cgd 307 1.1 cgd /* 308 1.1 cgd * For terminals that can overstrike, overstrike underlines and bolds. 309 1.1 cgd * We don't do anything with halfline ups and downs, or Greek. 310 1.1 cgd */ 311 1.19 abhinav static void 312 1.17 abhinav overstrike(struct CHAR *obuf) 313 1.1 cgd { 314 1.17 abhinav size_t i; 315 1.1 cgd char lbuf[256]; 316 1.5 lukem char *cp = lbuf; 317 1.1 cgd int hadbold=0; 318 1.1 cgd 319 1.1 cgd /* Set up overstrike buffer */ 320 1.1 cgd for (i=0; i<maxcol; i++) 321 1.1 cgd switch (obuf[i].c_mode) { 322 1.1 cgd case NORMAL: 323 1.1 cgd default: 324 1.1 cgd *cp++ = ' '; 325 1.1 cgd break; 326 1.1 cgd case UNDERL: 327 1.1 cgd *cp++ = '_'; 328 1.1 cgd break; 329 1.1 cgd case BOLD: 330 1.1 cgd *cp++ = obuf[i].c_char; 331 1.1 cgd hadbold=1; 332 1.1 cgd break; 333 1.1 cgd } 334 1.1 cgd putchar('\r'); 335 1.1 cgd for (*cp=' '; *cp==' '; cp--) 336 1.1 cgd *cp = 0; 337 1.1 cgd for (cp=lbuf; *cp; cp++) 338 1.1 cgd putchar(*cp); 339 1.1 cgd if (hadbold) { 340 1.1 cgd putchar('\r'); 341 1.1 cgd for (cp=lbuf; *cp; cp++) 342 1.1 cgd putchar(*cp=='_' ? ' ' : *cp); 343 1.1 cgd putchar('\r'); 344 1.1 cgd for (cp=lbuf; *cp; cp++) 345 1.1 cgd putchar(*cp=='_' ? ' ' : *cp); 346 1.1 cgd } 347 1.1 cgd } 348 1.1 cgd 349 1.19 abhinav static void 350 1.17 abhinav iattr(struct CHAR *obuf) 351 1.1 cgd { 352 1.17 abhinav size_t i; 353 1.1 cgd char lbuf[256]; 354 1.5 lukem char *cp = lbuf; 355 1.1 cgd 356 1.1 cgd for (i=0; i<maxcol; i++) 357 1.1 cgd switch (obuf[i].c_mode) { 358 1.1 cgd case NORMAL: *cp++ = ' '; break; 359 1.1 cgd case ALTSET: *cp++ = 'g'; break; 360 1.1 cgd case SUPERSC: *cp++ = '^'; break; 361 1.1 cgd case SUBSC: *cp++ = 'v'; break; 362 1.1 cgd case UNDERL: *cp++ = '_'; break; 363 1.1 cgd case BOLD: *cp++ = '!'; break; 364 1.1 cgd default: *cp++ = 'X'; break; 365 1.1 cgd } 366 1.1 cgd for (*cp=' '; *cp==' '; cp--) 367 1.1 cgd *cp = 0; 368 1.1 cgd for (cp=lbuf; *cp; cp++) 369 1.1 cgd putchar(*cp); 370 1.1 cgd putchar('\n'); 371 1.1 cgd } 372 1.1 cgd 373 1.19 abhinav static void 374 1.17 abhinav initbuf(struct CHAR *obuf, size_t obuf_size) 375 1.1 cgd { 376 1.1 cgd 377 1.17 abhinav memset(obuf, 0, obuf_size * sizeof(*obuf)); /* depends on NORMAL == 0 */ 378 1.1 cgd col = 0; 379 1.1 cgd maxcol = 0; 380 1.17 abhinav set_mode(); 381 1.17 abhinav } 382 1.17 abhinav 383 1.17 abhinav static void 384 1.17 abhinav set_mode(void) 385 1.17 abhinav { 386 1.1 cgd mode &= ALTSET; 387 1.1 cgd } 388 1.1 cgd 389 1.19 abhinav static void 390 1.17 abhinav fwd(struct CHAR *obuf, size_t obuf_size) 391 1.1 cgd { 392 1.5 lukem int oldcol, oldmax; 393 1.1 cgd 394 1.1 cgd oldcol = col; 395 1.1 cgd oldmax = maxcol; 396 1.17 abhinav flushln(obuf, obuf_size); 397 1.1 cgd col = oldcol; 398 1.1 cgd maxcol = oldmax; 399 1.1 cgd } 400 1.1 cgd 401 1.19 abhinav static void 402 1.17 abhinav reverse(struct CHAR *obuf, size_t obuf_size) 403 1.1 cgd { 404 1.1 cgd upln++; 405 1.17 abhinav fwd(obuf, obuf_size); 406 1.15 roy PRINT(cursor_up); 407 1.15 roy PRINT(cursor_up); 408 1.1 cgd upln++; 409 1.1 cgd } 410 1.1 cgd 411 1.19 abhinav static int 412 1.16 matt outchar(int c) 413 1.1 cgd { 414 1.8 lukem return (putchar(c & 0177)); 415 1.1 cgd } 416 1.1 cgd 417 1.1 cgd static int curmode = 0; 418 1.1 cgd 419 1.19 abhinav static void 420 1.16 matt outc(int c) 421 1.1 cgd { 422 1.1 cgd putchar(c); 423 1.15 roy if (underline_char && !enter_underline_mode && (curmode & UNDERL)) { 424 1.15 roy if (cursor_left) 425 1.15 roy PRINT(cursor_left); 426 1.15 roy else 427 1.15 roy putchar('\b'); 428 1.15 roy PRINT(underline_char); 429 1.1 cgd } 430 1.1 cgd } 431 1.1 cgd 432 1.19 abhinav static void 433 1.16 matt setulmode(int newmode) 434 1.1 cgd { 435 1.1 cgd if (!iflag) { 436 1.1 cgd if (curmode != NORMAL && newmode != NORMAL) 437 1.5 lukem setulmode(NORMAL); 438 1.1 cgd switch (newmode) { 439 1.1 cgd case NORMAL: 440 1.1 cgd switch(curmode) { 441 1.1 cgd case NORMAL: 442 1.1 cgd break; 443 1.1 cgd case UNDERL: 444 1.15 roy if (enter_underline_mode) 445 1.15 roy PRINT(exit_underline_mode); 446 1.15 roy else 447 1.15 roy PRINT(exit_standout_mode); 448 1.1 cgd break; 449 1.1 cgd default: 450 1.1 cgd /* This includes standout */ 451 1.15 roy if (exit_attribute_mode) 452 1.15 roy PRINT(exit_attribute_mode); 453 1.15 roy else 454 1.15 roy PRINT(exit_standout_mode); 455 1.1 cgd break; 456 1.1 cgd } 457 1.1 cgd break; 458 1.1 cgd case ALTSET: 459 1.15 roy if (enter_reverse_mode) 460 1.15 roy PRINT(enter_reverse_mode); 461 1.15 roy else 462 1.15 roy PRINT(enter_standout_mode); 463 1.1 cgd break; 464 1.1 cgd case SUPERSC: 465 1.1 cgd /* 466 1.1 cgd * This only works on a few terminals. 467 1.1 cgd * It should be fixed. 468 1.1 cgd */ 469 1.15 roy PRINT(enter_underline_mode); 470 1.15 roy PRINT(enter_dim_mode); 471 1.1 cgd break; 472 1.1 cgd case SUBSC: 473 1.15 roy if (enter_dim_mode) 474 1.15 roy PRINT(enter_dim_mode); 475 1.15 roy else 476 1.15 roy PRINT(enter_standout_mode); 477 1.1 cgd break; 478 1.1 cgd case UNDERL: 479 1.15 roy if (enter_underline_mode) 480 1.15 roy PRINT(enter_underline_mode); 481 1.15 roy else 482 1.15 roy PRINT(enter_standout_mode); 483 1.1 cgd break; 484 1.1 cgd case BOLD: 485 1.15 roy if (enter_bold_mode) 486 1.15 roy PRINT(enter_bold_mode); 487 1.15 roy else 488 1.15 roy PRINT(enter_reverse_mode); 489 1.1 cgd break; 490 1.1 cgd default: 491 1.1 cgd /* 492 1.1 cgd * We should have some provision here for multiple modes 493 1.1 cgd * on at once. This will have to come later. 494 1.1 cgd */ 495 1.15 roy PRINT(enter_standout_mode); 496 1.1 cgd break; 497 1.1 cgd } 498 1.1 cgd } 499 1.1 cgd curmode = newmode; 500 1.1 cgd } 501 1.17 abhinav 502 1.17 abhinav /* 503 1.17 abhinav * Reallocates the buffer pointed to by *buf and sets 504 1.17 abhinav * the newly allocated set of bytes to 0. 505 1.17 abhinav */ 506 1.18 abhinav static void 507 1.17 abhinav alloc_buf(struct CHAR **buf, size_t *size) 508 1.17 abhinav { 509 1.17 abhinav size_t osize = *size; 510 1.17 abhinav *size += MAXBUF; 511 1.17 abhinav ereallocarr(buf, *size, sizeof(**buf)); 512 1.17 abhinav memset(*buf + osize, 0, (*size - osize) * sizeof(**buf)); 513 1.17 abhinav } 514