Annotation of src/usr.bin/ul/ul.c, Revision 1.19
1.19 ! deraadt 1: /* $OpenBSD: ul.c,v 1.18 2015/02/08 23:40:34 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.1 deraadt 35: #include <stdio.h>
1.13 jaredy 36: #include <stdlib.h>
1.2 deraadt 37: #include <string.h>
1.13 jaredy 38: #include <term.h>
1.6 deraadt 39: #include <unistd.h>
1.1 deraadt 40:
41: #define IESC '\033'
42: #define SO '\016'
43: #define SI '\017'
44: #define HFWD '9'
45: #define HREV '8'
46: #define FREV '7'
47: #define MAXBUF 512
48:
49: #define NORMAL 000
50: #define ALTSET 001 /* Reverse */
51: #define SUPERSC 002 /* Dim */
52: #define SUBSC 004 /* Dim | Ul */
53: #define UNDERL 010 /* Ul */
54: #define BOLD 020 /* Bold */
55:
56: int must_use_uc, must_overstrike;
57: char *CURS_UP, *CURS_RIGHT, *CURS_LEFT,
58: *ENTER_STANDOUT, *EXIT_STANDOUT, *ENTER_UNDERLINE, *EXIT_UNDERLINE,
59: *ENTER_DIM, *ENTER_BOLD, *ENTER_REVERSE, *UNDER_CHAR, *EXIT_ATTRIBUTES;
60:
61: struct CHAR {
62: char c_mode;
63: char c_char;
64: } ;
65:
66: struct CHAR obuf[MAXBUF];
67: int col, maxcol;
68: int mode;
69: int halfpos;
70: int upln;
71: int iflag;
72:
1.6 deraadt 73: int outchar(int);
74: void initcap(void);
75: void initbuf(void);
76: void mfilter(FILE *);
77: void reverse(void);
78: void fwd(void);
79: void flushln(void);
80: void msetmode(int);
81: void outc(int);
82: void overstrike(void);
83: void iattr(void);
84:
85: #define PRINT(s) \
86: do { \
87: if (s) \
88: tputs(s, 1, outchar); \
89: } while (0)
1.1 deraadt 90:
1.6 deraadt 91: int
1.10 deraadt 92: main(int argc, char *argv[])
1.1 deraadt 93: {
94: extern int optind;
95: extern char *optarg;
96: int c;
97: char *termtype;
98: FILE *f;
99: char termcap[1024];
1.19 ! deraadt 100:
! 101: if (pledge("stdio rpath tty", NULL) == -1)
! 102: err(1, "pledge");
1.1 deraadt 103:
104: termtype = getenv("TERM");
105: if (termtype == NULL || (argv[0][0] == 'c' && !isatty(1)))
106: termtype = "lpr";
1.17 tedu 107: while ((c = getopt(argc, argv, "it:T:")) != -1)
108: switch (c) {
1.1 deraadt 109: case 't':
110: case 'T': /* for nroff compatibility */
1.18 deraadt 111: termtype = optarg;
1.1 deraadt 112: break;
113: case 'i':
114: iflag = 1;
115: break;
116:
117: default:
118: fprintf(stderr,
1.14 sobrado 119: "usage: %s [-i] [-t terminal] [file ...]\n",
120: argv[0]);
1.1 deraadt 121: exit(1);
122: }
123:
1.17 tedu 124: switch (tgetent(termcap, termtype)) {
1.1 deraadt 125: case 1:
126: break;
127: default:
1.13 jaredy 128: warnx("trouble reading termcap");
129: /* FALLTHROUGH */
1.1 deraadt 130: case 0:
131: /* No such terminal type - assume dumb */
1.8 deraadt 132: (void)strlcpy(termcap, "dumb:os:col#80:cr=^M:sf=^J:am:",
133: sizeof termcap);
1.1 deraadt 134: break;
135: }
136: initcap();
1.17 tedu 137: if ((tgetflag("os") && ENTER_BOLD == NULL ) ||
138: (tgetflag("ul") && ENTER_UNDERLINE == NULL && UNDER_CHAR == NULL))
139: must_overstrike = 1;
1.1 deraadt 140: initbuf();
141: if (optind == argc)
1.6 deraadt 142: mfilter(stdin);
1.1 deraadt 143: else for (; optind<argc; optind++) {
144: f = fopen(argv[optind],"r");
1.13 jaredy 145: if (f == NULL)
146: err(1, "%s", argv[optind]);
1.2 deraadt 147:
1.6 deraadt 148: mfilter(f);
1.2 deraadt 149: fclose(f);
1.1 deraadt 150: }
151: exit(0);
152: }
153:
1.6 deraadt 154: void
1.10 deraadt 155: mfilter(FILE *f)
1.1 deraadt 156: {
1.6 deraadt 157: int c;
1.1 deraadt 158:
1.5 markus 159: while ((c = getc(f)) != EOF && col < MAXBUF) switch(c) {
1.1 deraadt 160: case '\b':
161: if (col > 0)
162: col--;
163: continue;
164: case '\t':
165: col = (col+8) & ~07;
166: if (col > maxcol)
167: maxcol = col;
168: continue;
169: case '\r':
170: col = 0;
171: continue;
172: case SO:
173: mode |= ALTSET;
174: continue;
175: case SI:
176: mode &= ~ALTSET;
177: continue;
178: case IESC:
179: switch (c = getc(f)) {
180: case HREV:
181: if (halfpos == 0) {
182: mode |= SUPERSC;
183: halfpos--;
184: } else if (halfpos > 0) {
185: mode &= ~SUBSC;
186: halfpos--;
187: } else {
188: halfpos = 0;
189: reverse();
190: }
191: continue;
192: case HFWD:
193: if (halfpos == 0) {
194: mode |= SUBSC;
195: halfpos++;
196: } else if (halfpos < 0) {
197: mode &= ~SUPERSC;
198: halfpos++;
199: } else {
200: halfpos = 0;
201: fwd();
202: }
203: continue;
204: case FREV:
205: reverse();
206: continue;
207: default:
1.13 jaredy 208: errx(1, "0%o: unknown escape sequence", c);
209: /* NOTREACHED */
1.1 deraadt 210: }
211: continue;
212:
213: case '_':
214: if (obuf[col].c_char)
215: obuf[col].c_mode |= UNDERL | mode;
216: else
217: obuf[col].c_char = '_';
1.13 jaredy 218: /* FALLTHROUGH */
1.1 deraadt 219: case ' ':
220: col++;
221: if (col > maxcol)
222: maxcol = col;
223: continue;
224: case '\n':
225: flushln();
226: continue;
227: case '\f':
228: flushln();
229: putchar('\f');
230: continue;
231: default:
232: if (c < ' ') /* non printing */
233: continue;
234: if (obuf[col].c_char == '\0') {
235: obuf[col].c_char = c;
236: obuf[col].c_mode = mode;
237: } else if (obuf[col].c_char == '_') {
238: obuf[col].c_char = c;
239: obuf[col].c_mode |= UNDERL|mode;
240: } else if (obuf[col].c_char == c)
241: obuf[col].c_mode |= BOLD|mode;
242: else
243: obuf[col].c_mode = mode;
244: col++;
245: if (col > maxcol)
246: maxcol = col;
247: continue;
248: }
249: if (maxcol)
250: flushln();
251: }
252:
1.6 deraadt 253: void
1.10 deraadt 254: flushln(void)
1.1 deraadt 255: {
1.6 deraadt 256: int lastmode, i;
1.1 deraadt 257: int hadmodes = 0;
258:
259: lastmode = NORMAL;
1.17 tedu 260: for (i=0; i < maxcol; i++) {
1.1 deraadt 261: if (obuf[i].c_mode != lastmode) {
262: hadmodes++;
1.6 deraadt 263: msetmode(obuf[i].c_mode);
1.1 deraadt 264: lastmode = obuf[i].c_mode;
265: }
266: if (obuf[i].c_char == '\0') {
267: if (upln)
268: PRINT(CURS_RIGHT);
269: else
270: outc(' ');
271: } else
272: outc(obuf[i].c_char);
273: }
274: if (lastmode != NORMAL) {
1.6 deraadt 275: msetmode(0);
1.1 deraadt 276: }
277: if (must_overstrike && hadmodes)
278: overstrike();
279: putchar('\n');
280: if (iflag && hadmodes)
281: iattr();
282: (void)fflush(stdout);
283: if (upln)
284: upln--;
285: initbuf();
286: }
287:
288: /*
289: * For terminals that can overstrike, overstrike underlines and bolds.
290: * We don't do anything with halfline ups and downs, or Greek.
291: */
1.6 deraadt 292: void
1.10 deraadt 293: overstrike(void)
1.1 deraadt 294: {
1.7 mpech 295: int i;
1.13 jaredy 296: char *buf, *cp;
1.17 tedu 297: int hadbold = 0;
1.1 deraadt 298:
1.13 jaredy 299: if ((buf = malloc(maxcol + 1)) == NULL)
300: err(1, NULL);
301: cp = buf;
302:
1.1 deraadt 303: /* Set up overstrike buffer */
1.17 tedu 304: for (i = 0; i < maxcol; i++)
1.1 deraadt 305: switch (obuf[i].c_mode) {
306: case NORMAL:
307: default:
308: *cp++ = ' ';
309: break;
310: case UNDERL:
311: *cp++ = '_';
312: break;
313: case BOLD:
314: *cp++ = obuf[i].c_char;
315: hadbold=1;
316: break;
317: }
318: putchar('\r');
1.13 jaredy 319: while (cp > buf && *(cp - 1) == ' ')
320: cp--;
321: *cp = '\0';
322: for (cp = buf; *cp != '\0'; cp++)
1.1 deraadt 323: putchar(*cp);
324: if (hadbold) {
325: putchar('\r');
1.13 jaredy 326: for (cp = buf; *cp != '\0'; cp++)
1.1 deraadt 327: putchar(*cp=='_' ? ' ' : *cp);
328: putchar('\r');
1.13 jaredy 329: for (cp = buf; *cp != '\0'; cp++)
1.1 deraadt 330: putchar(*cp=='_' ? ' ' : *cp);
331: }
1.13 jaredy 332: free(buf);
1.1 deraadt 333: }
334:
1.6 deraadt 335: void
1.10 deraadt 336: iattr(void)
1.1 deraadt 337: {
1.7 mpech 338: int i;
1.13 jaredy 339: char *buf, *cp;
340:
341: if ((buf = malloc(maxcol + 1)) == NULL)
342: err(1, NULL);
343: cp = buf;
1.1 deraadt 344:
1.17 tedu 345: for (i=0; i < maxcol; i++)
1.1 deraadt 346: switch (obuf[i].c_mode) {
347: case NORMAL: *cp++ = ' '; break;
348: case ALTSET: *cp++ = 'g'; break;
349: case SUPERSC: *cp++ = '^'; break;
350: case SUBSC: *cp++ = 'v'; break;
351: case UNDERL: *cp++ = '_'; break;
352: case BOLD: *cp++ = '!'; break;
353: default: *cp++ = 'X'; break;
354: }
1.13 jaredy 355: while (cp > buf && *(cp - 1) == ' ')
356: cp--;
357: *cp = '\0';
358: for (cp = buf; *cp != '\0'; cp++)
1.1 deraadt 359: putchar(*cp);
1.13 jaredy 360: free(buf);
1.1 deraadt 361: putchar('\n');
362: }
363:
1.6 deraadt 364: void
1.10 deraadt 365: initbuf(void)
1.1 deraadt 366: {
1.17 tedu 367: bzero(obuf, sizeof (obuf)); /* depends on NORMAL == 0 */
1.1 deraadt 368: col = 0;
369: maxcol = 0;
370: mode &= ALTSET;
371: }
372:
1.6 deraadt 373: void
1.10 deraadt 374: fwd(void)
1.1 deraadt 375: {
1.6 deraadt 376: int oldcol, oldmax;
1.1 deraadt 377:
378: oldcol = col;
379: oldmax = maxcol;
380: flushln();
381: col = oldcol;
382: maxcol = oldmax;
383: }
384:
1.6 deraadt 385: void
1.10 deraadt 386: reverse(void)
1.1 deraadt 387: {
388: upln++;
389: fwd();
390: PRINT(CURS_UP);
391: PRINT(CURS_UP);
392: upln++;
393: }
394:
1.6 deraadt 395: void
1.10 deraadt 396: initcap(void)
1.1 deraadt 397: {
398: static char tcapbuf[512];
399: char *bp = tcapbuf;
400:
401: /* This nonsense attempts to work with both old and new termcap */
402: CURS_UP = tgetstr("up", &bp);
403: CURS_RIGHT = tgetstr("ri", &bp);
404: if (CURS_RIGHT == NULL)
405: CURS_RIGHT = tgetstr("nd", &bp);
406: CURS_LEFT = tgetstr("le", &bp);
407: if (CURS_LEFT == NULL)
408: CURS_LEFT = tgetstr("bc", &bp);
409: if (CURS_LEFT == NULL && tgetflag("bs"))
410: CURS_LEFT = "\b";
411:
412: ENTER_STANDOUT = tgetstr("so", &bp);
413: EXIT_STANDOUT = tgetstr("se", &bp);
414: ENTER_UNDERLINE = tgetstr("us", &bp);
415: EXIT_UNDERLINE = tgetstr("ue", &bp);
416: ENTER_DIM = tgetstr("mh", &bp);
417: ENTER_BOLD = tgetstr("md", &bp);
418: ENTER_REVERSE = tgetstr("mr", &bp);
419: EXIT_ATTRIBUTES = tgetstr("me", &bp);
420:
421: if (!ENTER_BOLD && ENTER_REVERSE)
422: ENTER_BOLD = ENTER_REVERSE;
423: if (!ENTER_BOLD && ENTER_STANDOUT)
424: ENTER_BOLD = ENTER_STANDOUT;
425: if (!ENTER_UNDERLINE && ENTER_STANDOUT) {
426: ENTER_UNDERLINE = ENTER_STANDOUT;
427: EXIT_UNDERLINE = EXIT_STANDOUT;
428: }
429: if (!ENTER_DIM && ENTER_STANDOUT)
430: ENTER_DIM = ENTER_STANDOUT;
431: if (!ENTER_REVERSE && ENTER_STANDOUT)
432: ENTER_REVERSE = ENTER_STANDOUT;
433: if (!EXIT_ATTRIBUTES && EXIT_STANDOUT)
434: EXIT_ATTRIBUTES = EXIT_STANDOUT;
435:
436: /*
437: * Note that we use REVERSE for the alternate character set,
438: * not the as/ae capabilities. This is because we are modelling
439: * the model 37 teletype (since that's what nroff outputs) and
440: * the typical as/ae is more of a graphics set, not the greek
441: * letters the 37 has.
442: */
443:
444: UNDER_CHAR = tgetstr("uc", &bp);
445: must_use_uc = (UNDER_CHAR && !ENTER_UNDERLINE);
446: }
447:
1.6 deraadt 448: int
1.10 deraadt 449: outchar(int c)
1.1 deraadt 450: {
451: putchar(c & 0177);
1.6 deraadt 452: return (0);
1.1 deraadt 453: }
454:
455: static int curmode = 0;
456:
1.6 deraadt 457: void
1.10 deraadt 458: outc(int c)
1.1 deraadt 459: {
460: putchar(c);
461: if (must_use_uc && (curmode&UNDERL)) {
462: PRINT(CURS_LEFT);
463: PRINT(UNDER_CHAR);
464: }
465: }
466:
1.6 deraadt 467: void
1.10 deraadt 468: msetmode(int newmode)
1.1 deraadt 469: {
470: if (!iflag) {
471: if (curmode != NORMAL && newmode != NORMAL)
1.6 deraadt 472: msetmode(NORMAL);
1.1 deraadt 473: switch (newmode) {
474: case NORMAL:
475: switch(curmode) {
476: case NORMAL:
477: break;
478: case UNDERL:
479: PRINT(EXIT_UNDERLINE);
480: break;
481: default:
482: /* This includes standout */
483: PRINT(EXIT_ATTRIBUTES);
484: break;
485: }
486: break;
487: case ALTSET:
488: PRINT(ENTER_REVERSE);
489: break;
490: case SUPERSC:
491: /*
492: * This only works on a few terminals.
493: * It should be fixed.
494: */
495: PRINT(ENTER_UNDERLINE);
496: PRINT(ENTER_DIM);
497: break;
498: case SUBSC:
499: PRINT(ENTER_DIM);
500: break;
501: case UNDERL:
502: PRINT(ENTER_UNDERLINE);
503: break;
504: case BOLD:
505: PRINT(ENTER_BOLD);
506: break;
507: default:
508: /*
509: * We should have some provision here for multiple modes
510: * on at once. This will have to come later.
511: */
512: PRINT(ENTER_STANDOUT);
513: break;
514: }
515: }
516: curmode = newmode;
517: }