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