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