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