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