Annotation of src/usr.bin/ul/ul.c, Revision 1.20
1.20 ! schwarze 1: /* $OpenBSD: ul.c,v 1.19 2015/10/10 16:15:03 deraadt Exp $ */
1.1 deraadt 2: /* $NetBSD: ul.c,v 1.3 1994/12/07 00:28:24 jtc Exp $ */
3:
4: /*
5: * Copyright (c) 1980, 1993
6: * The Regents of the University of California. All rights reserved.
7: *
8: * Redistribution and use in source and binary forms, with or without
9: * modification, are permitted provided that the following conditions
10: * are met:
11: * 1. Redistributions of source code must retain the above copyright
12: * notice, this list of conditions and the following disclaimer.
13: * 2. Redistributions in binary form must reproduce the above copyright
14: * notice, this list of conditions and the following disclaimer in the
15: * documentation and/or other materials provided with the distribution.
1.9 millert 16: * 3. Neither the name of the University nor the names of its contributors
1.1 deraadt 17: * may be used to endorse or promote products derived from this software
18: * without specific prior written permission.
19: *
20: * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21: * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23: * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24: * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25: * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26: * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27: * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28: * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29: * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30: * SUCH DAMAGE.
31: */
32:
1.13 jaredy 33: #include <curses.h>
34: #include <err.h>
1.20 ! schwarze 35: #include <errno.h>
! 36: #include <locale.h>
1.1 deraadt 37: #include <stdio.h>
1.13 jaredy 38: #include <stdlib.h>
1.2 deraadt 39: #include <string.h>
1.13 jaredy 40: #include <term.h>
1.6 deraadt 41: #include <unistd.h>
1.20 ! schwarze 42: #include <wchar.h>
1.1 deraadt 43:
1.20 ! schwarze 44: #define IESC L'\033'
! 45: #define SO L'\016'
! 46: #define SI L'\017'
1.1 deraadt 47: #define HFWD '9'
48: #define HREV '8'
49: #define FREV '7'
50: #define MAXBUF 512
51:
52: #define NORMAL 000
53: #define ALTSET 001 /* Reverse */
54: #define SUPERSC 002 /* Dim */
55: #define SUBSC 004 /* Dim | Ul */
56: #define UNDERL 010 /* Ul */
57: #define BOLD 020 /* Bold */
58:
59: int must_use_uc, must_overstrike;
60: char *CURS_UP, *CURS_RIGHT, *CURS_LEFT,
61: *ENTER_STANDOUT, *EXIT_STANDOUT, *ENTER_UNDERLINE, *EXIT_UNDERLINE,
62: *ENTER_DIM, *ENTER_BOLD, *ENTER_REVERSE, *UNDER_CHAR, *EXIT_ATTRIBUTES;
63:
64: struct CHAR {
65: char c_mode;
1.20 ! schwarze 66: wchar_t c_char;
! 67: int c_width;
! 68: int c_pos;
1.1 deraadt 69: } ;
70:
71: struct CHAR obuf[MAXBUF];
72: int col, maxcol;
73: int mode;
74: int halfpos;
75: int upln;
76: int iflag;
77:
1.6 deraadt 78: int outchar(int);
79: void initcap(void);
80: void initbuf(void);
81: void mfilter(FILE *);
82: void reverse(void);
83: void fwd(void);
84: void flushln(void);
85: void msetmode(int);
1.20 ! schwarze 86: void outc(wchar_t, int);
1.6 deraadt 87: void overstrike(void);
88: void iattr(void);
89:
90: #define PRINT(s) \
91: do { \
92: if (s) \
93: tputs(s, 1, outchar); \
94: } while (0)
1.1 deraadt 95:
1.6 deraadt 96: int
1.10 deraadt 97: main(int argc, char *argv[])
1.1 deraadt 98: {
99: extern int optind;
100: extern char *optarg;
101: int c;
102: char *termtype;
103: FILE *f;
104: char termcap[1024];
1.19 deraadt 105:
1.20 ! schwarze 106: setlocale(LC_CTYPE, "");
! 107:
1.19 deraadt 108: if (pledge("stdio rpath tty", NULL) == -1)
109: err(1, "pledge");
1.1 deraadt 110:
111: termtype = getenv("TERM");
112: if (termtype == NULL || (argv[0][0] == 'c' && !isatty(1)))
113: termtype = "lpr";
1.17 tedu 114: while ((c = getopt(argc, argv, "it:T:")) != -1)
115: switch (c) {
1.1 deraadt 116: case 't':
117: case 'T': /* for nroff compatibility */
1.18 deraadt 118: termtype = optarg;
1.1 deraadt 119: break;
120: case 'i':
121: iflag = 1;
122: break;
123:
124: default:
125: fprintf(stderr,
1.14 sobrado 126: "usage: %s [-i] [-t terminal] [file ...]\n",
127: argv[0]);
1.1 deraadt 128: exit(1);
129: }
130:
1.17 tedu 131: switch (tgetent(termcap, termtype)) {
1.1 deraadt 132: case 1:
133: break;
134: default:
1.13 jaredy 135: warnx("trouble reading termcap");
136: /* FALLTHROUGH */
1.1 deraadt 137: case 0:
138: /* No such terminal type - assume dumb */
1.8 deraadt 139: (void)strlcpy(termcap, "dumb:os:col#80:cr=^M:sf=^J:am:",
140: sizeof termcap);
1.1 deraadt 141: break;
142: }
143: initcap();
1.17 tedu 144: if ((tgetflag("os") && ENTER_BOLD == NULL ) ||
145: (tgetflag("ul") && ENTER_UNDERLINE == NULL && UNDER_CHAR == NULL))
146: must_overstrike = 1;
1.1 deraadt 147: initbuf();
148: if (optind == argc)
1.6 deraadt 149: mfilter(stdin);
1.1 deraadt 150: else for (; optind<argc; optind++) {
151: f = fopen(argv[optind],"r");
1.13 jaredy 152: if (f == NULL)
153: err(1, "%s", argv[optind]);
1.2 deraadt 154:
1.6 deraadt 155: mfilter(f);
1.2 deraadt 156: fclose(f);
1.1 deraadt 157: }
158: exit(0);
159: }
160:
1.6 deraadt 161: void
1.10 deraadt 162: mfilter(FILE *f)
1.1 deraadt 163: {
1.20 ! schwarze 164: struct CHAR *cp;
! 165: wint_t c;
! 166: int skip_bs, w, wt;
! 167:
! 168: col = 1;
! 169: skip_bs = 0;
! 170: while (col < MAXBUF) {
! 171: switch (c = fgetwc(f)) {
! 172: case WEOF:
! 173: /* Discard invalid bytes. */
! 174: if (ferror(f)) {
! 175: if (errno != EILSEQ)
! 176: err(1, NULL);
! 177: clearerr(f);
! 178: break;
! 179: }
! 180:
! 181: /* End of file. */
! 182: if (maxcol)
! 183: flushln();
! 184: return;
! 185:
! 186: case L'\b':
! 187: /*
! 188: * Back up one character position, not one
! 189: * display column, but ignore a second
! 190: * backspace after a double-width character.
! 191: */
! 192: if (skip_bs > 0)
! 193: skip_bs--;
! 194: else if (col > 1)
! 195: if (obuf[--col].c_width > 1)
! 196: skip_bs = obuf[col].c_width - 1;
! 197: continue;
! 198:
! 199: case L'\t':
! 200: /* Calculate the target position. */
! 201: wt = (obuf[col - 1].c_pos + 8) & ~7;
! 202:
! 203: /* Advance past known positions. */
! 204: while ((w = obuf[col].c_pos) > 0 && w <= wt)
! 205: col++;
! 206:
! 207: /* Advance beyond the end. */
! 208: if (w == 0) {
! 209: w = obuf[col - 1].c_pos;
! 210: while (w < wt) {
! 211: obuf[col].c_width = 1;
! 212: obuf[col++].c_pos = ++w;
! 213: }
! 214: }
! 215: if (col > maxcol)
! 216: maxcol = col;
! 217: break;
! 218:
! 219: case L'\r':
! 220: col = 1;
! 221: break;
! 222:
! 223: case SO:
! 224: mode |= ALTSET;
! 225: break;
! 226:
! 227: case SI:
! 228: mode &= ~ALTSET;
! 229: break;
1.1 deraadt 230:
1.20 ! schwarze 231: case IESC:
! 232: switch (c = fgetwc(f)) {
! 233: case HREV:
! 234: if (halfpos == 0) {
! 235: mode |= SUPERSC;
! 236: halfpos--;
! 237: } else if (halfpos > 0) {
! 238: mode &= ~SUBSC;
! 239: halfpos--;
! 240: } else {
! 241: halfpos = 0;
! 242: reverse();
! 243: }
! 244: break;
! 245: case HFWD:
! 246: if (halfpos == 0) {
! 247: mode |= SUBSC;
! 248: halfpos++;
! 249: } else if (halfpos < 0) {
! 250: mode &= ~SUPERSC;
! 251: halfpos++;
! 252: } else {
! 253: halfpos = 0;
! 254: fwd();
! 255: }
! 256: break;
! 257: case FREV:
1.1 deraadt 258: reverse();
1.20 ! schwarze 259: break;
! 260: default:
! 261: errx(1, "0%o: unknown escape sequence", c);
1.1 deraadt 262: }
1.20 ! schwarze 263: break;
! 264:
! 265: case L'_':
! 266: if (obuf[col].c_char == L'\0') {
! 267: obuf[col].c_char = L'_';
! 268: obuf[col].c_width = 1;
! 269: } else
! 270: obuf[col].c_mode |= UNDERL | mode;
! 271: /* FALLTHROUGH */
! 272:
! 273: case L' ':
! 274: if (obuf[col].c_pos == 0) {
! 275: obuf[col].c_width = 1;
! 276: obuf[col].c_pos = obuf[col - 1].c_pos + 1;
1.1 deraadt 277: }
1.20 ! schwarze 278: col++;
! 279: if (col > maxcol)
! 280: maxcol = col;
! 281: break;
! 282:
! 283: case L'\n':
! 284: flushln();
! 285: break;
! 286:
! 287: case L'\f':
! 288: flushln();
! 289: putwchar(L'\f');
! 290: break;
! 291:
1.1 deraadt 292: default:
1.20 ! schwarze 293: /* Discard valid, but non-printable characters. */
! 294: if ((w = wcwidth(c)) == -1)
! 295: break;
! 296:
! 297: if (obuf[col].c_char == L'\0') {
! 298: obuf[col].c_char = c;
! 299: obuf[col].c_mode = mode;
! 300: obuf[col].c_width = w;
! 301: obuf[col].c_pos = obuf[col - 1].c_pos + w;
! 302: } else if (obuf[col].c_char == L'_') {
! 303: obuf[col].c_char = c;
! 304: obuf[col].c_mode |= UNDERL|mode;
! 305: obuf[col].c_width = w;
! 306: obuf[col].c_pos = obuf[col - 1].c_pos + w;
! 307: for (cp = obuf + col; cp[1].c_pos > 0; cp++)
! 308: cp[1].c_pos = cp[0].c_pos +
! 309: cp[1].c_width;
! 310: } else if (obuf[col].c_char == c)
! 311: obuf[col].c_mode |= BOLD|mode;
! 312: else
! 313: obuf[col].c_mode = mode;
! 314: col++;
! 315: if (col > maxcol)
! 316: maxcol = col;
! 317: break;
1.1 deraadt 318: }
1.20 ! schwarze 319: skip_bs = 0;
1.1 deraadt 320: }
321: }
322:
1.6 deraadt 323: void
1.10 deraadt 324: flushln(void)
1.1 deraadt 325: {
1.6 deraadt 326: int lastmode, i;
1.1 deraadt 327: int hadmodes = 0;
328:
329: lastmode = NORMAL;
1.20 ! schwarze 330: for (i = 1; i < maxcol; i++) {
1.1 deraadt 331: if (obuf[i].c_mode != lastmode) {
1.20 ! schwarze 332: hadmodes = 1;
1.6 deraadt 333: msetmode(obuf[i].c_mode);
1.1 deraadt 334: lastmode = obuf[i].c_mode;
335: }
1.20 ! schwarze 336: if (obuf[i].c_char == L'\0') {
1.1 deraadt 337: if (upln)
338: PRINT(CURS_RIGHT);
339: else
1.20 ! schwarze 340: outc(L' ', 1);
1.1 deraadt 341: } else
1.20 ! schwarze 342: outc(obuf[i].c_char, obuf[i].c_width);
1.1 deraadt 343: }
1.20 ! schwarze 344: if (lastmode != NORMAL)
1.6 deraadt 345: msetmode(0);
1.1 deraadt 346: if (must_overstrike && hadmodes)
347: overstrike();
1.20 ! schwarze 348: putwchar(L'\n');
1.1 deraadt 349: if (iflag && hadmodes)
350: iattr();
351: (void)fflush(stdout);
352: if (upln)
353: upln--;
354: initbuf();
355: }
356:
357: /*
358: * For terminals that can overstrike, overstrike underlines and bolds.
359: * We don't do anything with halfline ups and downs, or Greek.
360: */
1.6 deraadt 361: void
1.10 deraadt 362: overstrike(void)
1.1 deraadt 363: {
1.20 ! schwarze 364: wchar_t wc;
! 365: int i, j, needspace;
! 366:
! 367: putwchar(L'\r');
! 368: needspace = 0;
! 369: for (i = 1; i < maxcol; i++) {
! 370: if (obuf[i].c_mode != UNDERL && obuf[i].c_mode != BOLD) {
! 371: needspace += obuf[i].c_width;
! 372: continue;
! 373: }
! 374: while (needspace > 0) {
! 375: putwchar(L' ');
! 376: needspace--;
! 377: }
! 378: if (obuf[i].c_mode == BOLD)
! 379: putwchar(obuf[i].c_char);
! 380: else
! 381: for (j = 0; j < obuf[i].c_width; j++)
! 382: putwchar(L'_');
! 383: }
! 384: }
1.1 deraadt 385:
1.20 ! schwarze 386: void
! 387: iattr(void)
! 388: {
! 389: int i, j, needspace;
! 390: char c;
1.13 jaredy 391:
1.20 ! schwarze 392: needspace = 0;
! 393: for (i = 1; i < maxcol; i++) {
1.1 deraadt 394: switch (obuf[i].c_mode) {
395: case NORMAL:
1.20 ! schwarze 396: needspace += obuf[i].c_width;
! 397: continue;
! 398: case ALTSET:
! 399: c = 'g';
! 400: break;
! 401: case SUPERSC:
! 402: c = '^';
! 403: break;
! 404: case SUBSC:
! 405: c = 'v';
1.1 deraadt 406: break;
407: case UNDERL:
1.20 ! schwarze 408: c = '_';
1.1 deraadt 409: break;
410: case BOLD:
1.20 ! schwarze 411: c = '!';
! 412: break;
! 413: default:
! 414: c = 'X';
1.1 deraadt 415: break;
416: }
1.20 ! schwarze 417: while (needspace > 0) {
! 418: putwchar(L' ');
! 419: needspace--;
! 420: }
! 421: for (j = 0; j < obuf[i].c_width; j++)
! 422: putwchar(c);
1.1 deraadt 423: }
1.20 ! schwarze 424: putwchar(L'\n');
1.1 deraadt 425: }
426:
1.6 deraadt 427: void
1.10 deraadt 428: initbuf(void)
1.1 deraadt 429: {
1.17 tedu 430: bzero(obuf, sizeof (obuf)); /* depends on NORMAL == 0 */
1.20 ! schwarze 431: col = 1;
1.1 deraadt 432: maxcol = 0;
433: mode &= ALTSET;
434: }
435:
1.6 deraadt 436: void
1.10 deraadt 437: fwd(void)
1.1 deraadt 438: {
1.6 deraadt 439: int oldcol, oldmax;
1.1 deraadt 440:
441: oldcol = col;
442: oldmax = maxcol;
443: flushln();
444: col = oldcol;
445: maxcol = oldmax;
446: }
447:
1.6 deraadt 448: void
1.10 deraadt 449: reverse(void)
1.1 deraadt 450: {
451: upln++;
452: fwd();
453: PRINT(CURS_UP);
454: PRINT(CURS_UP);
455: upln++;
456: }
457:
1.6 deraadt 458: void
1.10 deraadt 459: initcap(void)
1.1 deraadt 460: {
461: static char tcapbuf[512];
462: char *bp = tcapbuf;
463:
464: /* This nonsense attempts to work with both old and new termcap */
465: CURS_UP = tgetstr("up", &bp);
466: CURS_RIGHT = tgetstr("ri", &bp);
467: if (CURS_RIGHT == NULL)
468: CURS_RIGHT = tgetstr("nd", &bp);
469: CURS_LEFT = tgetstr("le", &bp);
470: if (CURS_LEFT == NULL)
471: CURS_LEFT = tgetstr("bc", &bp);
472: if (CURS_LEFT == NULL && tgetflag("bs"))
473: CURS_LEFT = "\b";
474:
475: ENTER_STANDOUT = tgetstr("so", &bp);
476: EXIT_STANDOUT = tgetstr("se", &bp);
477: ENTER_UNDERLINE = tgetstr("us", &bp);
478: EXIT_UNDERLINE = tgetstr("ue", &bp);
479: ENTER_DIM = tgetstr("mh", &bp);
480: ENTER_BOLD = tgetstr("md", &bp);
481: ENTER_REVERSE = tgetstr("mr", &bp);
482: EXIT_ATTRIBUTES = tgetstr("me", &bp);
483:
484: if (!ENTER_BOLD && ENTER_REVERSE)
485: ENTER_BOLD = ENTER_REVERSE;
486: if (!ENTER_BOLD && ENTER_STANDOUT)
487: ENTER_BOLD = ENTER_STANDOUT;
488: if (!ENTER_UNDERLINE && ENTER_STANDOUT) {
489: ENTER_UNDERLINE = ENTER_STANDOUT;
490: EXIT_UNDERLINE = EXIT_STANDOUT;
491: }
492: if (!ENTER_DIM && ENTER_STANDOUT)
493: ENTER_DIM = ENTER_STANDOUT;
494: if (!ENTER_REVERSE && ENTER_STANDOUT)
495: ENTER_REVERSE = ENTER_STANDOUT;
496: if (!EXIT_ATTRIBUTES && EXIT_STANDOUT)
497: EXIT_ATTRIBUTES = EXIT_STANDOUT;
498:
499: /*
500: * Note that we use REVERSE for the alternate character set,
501: * not the as/ae capabilities. This is because we are modelling
502: * the model 37 teletype (since that's what nroff outputs) and
503: * the typical as/ae is more of a graphics set, not the greek
504: * letters the 37 has.
505: */
506:
507: UNDER_CHAR = tgetstr("uc", &bp);
508: must_use_uc = (UNDER_CHAR && !ENTER_UNDERLINE);
509: }
510:
1.6 deraadt 511: int
1.10 deraadt 512: outchar(int c)
1.1 deraadt 513: {
1.20 ! schwarze 514: return (putwchar(c) != WEOF ? c : EOF);
1.1 deraadt 515: }
516:
517: static int curmode = 0;
518:
1.6 deraadt 519: void
1.20 ! schwarze 520: outc(wchar_t c, int width)
1.1 deraadt 521: {
1.20 ! schwarze 522: int i;
! 523:
! 524: putwchar(c);
1.1 deraadt 525: if (must_use_uc && (curmode&UNDERL)) {
1.20 ! schwarze 526: for (i = 0; i < width; i++)
! 527: PRINT(CURS_LEFT);
! 528: for (i = 0; i < width; i++)
! 529: PRINT(UNDER_CHAR);
1.1 deraadt 530: }
531: }
532:
1.6 deraadt 533: void
1.10 deraadt 534: msetmode(int newmode)
1.1 deraadt 535: {
536: if (!iflag) {
537: if (curmode != NORMAL && newmode != NORMAL)
1.6 deraadt 538: msetmode(NORMAL);
1.1 deraadt 539: switch (newmode) {
540: case NORMAL:
541: switch(curmode) {
542: case NORMAL:
543: break;
544: case UNDERL:
545: PRINT(EXIT_UNDERLINE);
546: break;
547: default:
548: /* This includes standout */
549: PRINT(EXIT_ATTRIBUTES);
550: break;
551: }
552: break;
553: case ALTSET:
554: PRINT(ENTER_REVERSE);
555: break;
556: case SUPERSC:
557: /*
558: * This only works on a few terminals.
559: * It should be fixed.
560: */
561: PRINT(ENTER_UNDERLINE);
562: PRINT(ENTER_DIM);
563: break;
564: case SUBSC:
565: PRINT(ENTER_DIM);
566: break;
567: case UNDERL:
568: PRINT(ENTER_UNDERLINE);
569: break;
570: case BOLD:
571: PRINT(ENTER_BOLD);
572: break;
573: default:
574: /*
575: * We should have some provision here for multiple modes
576: * on at once. This will have to come later.
577: */
578: PRINT(ENTER_STANDOUT);
579: break;
580: }
581: }
582: curmode = newmode;
583: }