Annotation of src/usr.bin/ul/ul.c, Revision 1.8
1.8 ! deraadt 1: /* $OpenBSD: ul.c,v 1.7 2001/11/19 19:02:17 mpech 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.
16: * 3. All advertising materials mentioning features or use of this software
17: * must display the following acknowledgement:
18: * This product includes software developed by the University of
19: * California, Berkeley and its contributors.
20: * 4. Neither the name of the University nor the names of its contributors
21: * may be used to endorse or promote products derived from this software
22: * without specific prior written permission.
23: *
24: * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25: * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27: * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28: * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29: * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30: * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31: * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32: * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33: * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34: * SUCH DAMAGE.
35: */
36:
37: #ifndef lint
38: static char copyright[] =
39: "@(#) Copyright (c) 1980, 1993\n\
40: The Regents of the University of California. All rights reserved.\n";
41: #endif /* not lint */
42:
43: #ifndef lint
44: #if 0
45: static char sccsid[] = "@(#)ul.c 8.1 (Berkeley) 6/6/93";
46: #endif
1.8 ! deraadt 47: static char rcsid[] = "$OpenBSD: ul.c,v 1.7 2001/11/19 19:02:17 mpech Exp $";
1.1 deraadt 48: #endif /* not lint */
49:
50: #include <stdio.h>
1.2 deraadt 51: #include <stdlib.h>
52: #include <string.h>
1.6 deraadt 53: #include <unistd.h>
54: #include <stdlib.h>
55: #include <curses.h>
56: #include <term.h>
1.1 deraadt 57:
58: #define IESC '\033'
59: #define SO '\016'
60: #define SI '\017'
61: #define HFWD '9'
62: #define HREV '8'
63: #define FREV '7'
64: #define MAXBUF 512
65:
66: #define NORMAL 000
67: #define ALTSET 001 /* Reverse */
68: #define SUPERSC 002 /* Dim */
69: #define SUBSC 004 /* Dim | Ul */
70: #define UNDERL 010 /* Ul */
71: #define BOLD 020 /* Bold */
72:
73: int must_use_uc, must_overstrike;
74: char *CURS_UP, *CURS_RIGHT, *CURS_LEFT,
75: *ENTER_STANDOUT, *EXIT_STANDOUT, *ENTER_UNDERLINE, *EXIT_UNDERLINE,
76: *ENTER_DIM, *ENTER_BOLD, *ENTER_REVERSE, *UNDER_CHAR, *EXIT_ATTRIBUTES;
77:
78: struct CHAR {
79: char c_mode;
80: char c_char;
81: } ;
82:
83: struct CHAR obuf[MAXBUF];
84: int col, maxcol;
85: int mode;
86: int halfpos;
87: int upln;
88: int iflag;
89:
1.6 deraadt 90: int outchar(int);
91: void initcap(void);
92: void initbuf(void);
93: void mfilter(FILE *);
94: void reverse(void);
95: void fwd(void);
96: void flushln(void);
97: void msetmode(int);
98: void outc(int);
99: void overstrike(void);
100: void iattr(void);
101:
102: #define PRINT(s) \
103: do { \
104: if (s) \
105: tputs(s, 1, outchar); \
106: } while (0)
1.1 deraadt 107:
1.6 deraadt 108: int
1.1 deraadt 109: main(argc, argv)
110: int argc;
111: char **argv;
112: {
113: extern int optind;
114: extern char *optarg;
115: int c;
116: char *termtype;
117: FILE *f;
118: char termcap[1024];
119:
120: termtype = getenv("TERM");
121: if (termtype == NULL || (argv[0][0] == 'c' && !isatty(1)))
122: termtype = "lpr";
1.4 millert 123: while ((c=getopt(argc, argv, "it:T:")) != -1)
1.1 deraadt 124: switch(c) {
125:
126: case 't':
127: case 'T': /* for nroff compatibility */
128: termtype = optarg;
129: break;
130: case 'i':
131: iflag = 1;
132: break;
133:
134: default:
135: fprintf(stderr,
136: "usage: %s [ -i ] [ -tTerm ] file...\n",
137: argv[0]);
138: exit(1);
139: }
140:
141: switch(tgetent(termcap, termtype)) {
142:
143: case 1:
144: break;
145:
146: default:
147: fprintf(stderr,"trouble reading termcap");
148: /* fall through to ... */
149:
150: case 0:
151: /* No such terminal type - assume dumb */
1.8 ! deraadt 152: (void)strlcpy(termcap, "dumb:os:col#80:cr=^M:sf=^J:am:",
! 153: sizeof termcap);
1.1 deraadt 154: break;
155: }
156: initcap();
157: if ( (tgetflag("os") && ENTER_BOLD==NULL ) ||
158: (tgetflag("ul") && ENTER_UNDERLINE==NULL && UNDER_CHAR==NULL))
159: must_overstrike = 1;
160: initbuf();
161: if (optind == argc)
1.6 deraadt 162: mfilter(stdin);
1.1 deraadt 163: else for (; optind<argc; optind++) {
164: f = fopen(argv[optind],"r");
165: if (f == NULL) {
166: perror(argv[optind]);
167: exit(1);
1.2 deraadt 168: }
169:
1.6 deraadt 170: mfilter(f);
1.2 deraadt 171: fclose(f);
1.1 deraadt 172: }
173: exit(0);
174: }
175:
1.6 deraadt 176: void
177: mfilter(f)
1.1 deraadt 178: FILE *f;
179: {
1.6 deraadt 180: int c;
1.1 deraadt 181:
1.5 markus 182: while ((c = getc(f)) != EOF && col < MAXBUF) switch(c) {
1.1 deraadt 183:
184: case '\b':
185: if (col > 0)
186: col--;
187: continue;
188:
189: case '\t':
190: col = (col+8) & ~07;
191: if (col > maxcol)
192: maxcol = col;
193: continue;
194:
195: case '\r':
196: col = 0;
197: continue;
198:
199: case SO:
200: mode |= ALTSET;
201: continue;
202:
203: case SI:
204: mode &= ~ALTSET;
205: continue;
206:
207: case IESC:
208: switch (c = getc(f)) {
209:
210: case HREV:
211: if (halfpos == 0) {
212: mode |= SUPERSC;
213: halfpos--;
214: } else if (halfpos > 0) {
215: mode &= ~SUBSC;
216: halfpos--;
217: } else {
218: halfpos = 0;
219: reverse();
220: }
221: continue;
222:
223: case HFWD:
224: if (halfpos == 0) {
225: mode |= SUBSC;
226: halfpos++;
227: } else if (halfpos < 0) {
228: mode &= ~SUPERSC;
229: halfpos++;
230: } else {
231: halfpos = 0;
232: fwd();
233: }
234: continue;
235:
236: case FREV:
237: reverse();
238: continue;
239:
240: default:
241: fprintf(stderr,
242: "Unknown escape sequence in input: %o, %o\n",
243: IESC, c);
244: exit(1);
245: }
246: continue;
247:
248: case '_':
249: if (obuf[col].c_char)
250: obuf[col].c_mode |= UNDERL | mode;
251: else
252: obuf[col].c_char = '_';
253: case ' ':
254: col++;
255: if (col > maxcol)
256: maxcol = col;
257: continue;
258:
259: case '\n':
260: flushln();
261: continue;
262:
263: case '\f':
264: flushln();
265: putchar('\f');
266: continue;
267:
268: default:
269: if (c < ' ') /* non printing */
270: continue;
271: if (obuf[col].c_char == '\0') {
272: obuf[col].c_char = c;
273: obuf[col].c_mode = mode;
274: } else if (obuf[col].c_char == '_') {
275: obuf[col].c_char = c;
276: obuf[col].c_mode |= UNDERL|mode;
277: } else if (obuf[col].c_char == c)
278: obuf[col].c_mode |= BOLD|mode;
279: else
280: obuf[col].c_mode = mode;
281: col++;
282: if (col > maxcol)
283: maxcol = col;
284: continue;
285: }
286: if (maxcol)
287: flushln();
288: }
289:
1.6 deraadt 290: void
1.1 deraadt 291: flushln()
292: {
1.6 deraadt 293: int lastmode, i;
1.1 deraadt 294: int hadmodes = 0;
295:
296: lastmode = NORMAL;
297: for (i=0; i<maxcol; i++) {
298: if (obuf[i].c_mode != lastmode) {
299: hadmodes++;
1.6 deraadt 300: msetmode(obuf[i].c_mode);
1.1 deraadt 301: lastmode = obuf[i].c_mode;
302: }
303: if (obuf[i].c_char == '\0') {
304: if (upln)
305: PRINT(CURS_RIGHT);
306: else
307: outc(' ');
308: } else
309: outc(obuf[i].c_char);
310: }
311: if (lastmode != NORMAL) {
1.6 deraadt 312: msetmode(0);
1.1 deraadt 313: }
314: if (must_overstrike && hadmodes)
315: overstrike();
316: putchar('\n');
317: if (iflag && hadmodes)
318: iattr();
319: (void)fflush(stdout);
320: if (upln)
321: upln--;
322: initbuf();
323: }
324:
325: /*
326: * For terminals that can overstrike, overstrike underlines and bolds.
327: * We don't do anything with halfline ups and downs, or Greek.
328: */
1.6 deraadt 329: void
1.1 deraadt 330: overstrike()
331: {
1.7 mpech 332: int i;
1.1 deraadt 333: char lbuf[256];
1.7 mpech 334: char *cp = lbuf;
1.1 deraadt 335: int hadbold=0;
336:
337: /* Set up overstrike buffer */
338: for (i=0; i<maxcol; i++)
339: switch (obuf[i].c_mode) {
340: case NORMAL:
341: default:
342: *cp++ = ' ';
343: break;
344: case UNDERL:
345: *cp++ = '_';
346: break;
347: case BOLD:
348: *cp++ = obuf[i].c_char;
349: hadbold=1;
350: break;
351: }
352: putchar('\r');
353: for (*cp=' '; *cp==' '; cp--)
354: *cp = 0;
355: for (cp=lbuf; *cp; cp++)
356: putchar(*cp);
357: if (hadbold) {
358: putchar('\r');
359: for (cp=lbuf; *cp; cp++)
360: putchar(*cp=='_' ? ' ' : *cp);
361: putchar('\r');
362: for (cp=lbuf; *cp; cp++)
363: putchar(*cp=='_' ? ' ' : *cp);
364: }
365: }
366:
1.6 deraadt 367: void
1.1 deraadt 368: iattr()
369: {
1.7 mpech 370: int i;
1.1 deraadt 371: char lbuf[256];
1.7 mpech 372: char *cp = lbuf;
1.1 deraadt 373:
374: for (i=0; i<maxcol; i++)
375: switch (obuf[i].c_mode) {
376: case NORMAL: *cp++ = ' '; break;
377: case ALTSET: *cp++ = 'g'; break;
378: case SUPERSC: *cp++ = '^'; break;
379: case SUBSC: *cp++ = 'v'; break;
380: case UNDERL: *cp++ = '_'; break;
381: case BOLD: *cp++ = '!'; break;
382: default: *cp++ = 'X'; break;
383: }
384: for (*cp=' '; *cp==' '; cp--)
385: *cp = 0;
386: for (cp=lbuf; *cp; cp++)
387: putchar(*cp);
388: putchar('\n');
389: }
390:
1.6 deraadt 391: void
1.1 deraadt 392: initbuf()
393: {
394:
395: bzero((char *)obuf, sizeof (obuf)); /* depends on NORMAL == 0 */
396: col = 0;
397: maxcol = 0;
398: mode &= ALTSET;
399: }
400:
1.6 deraadt 401: void
1.1 deraadt 402: fwd()
403: {
1.6 deraadt 404: int oldcol, oldmax;
1.1 deraadt 405:
406: oldcol = col;
407: oldmax = maxcol;
408: flushln();
409: col = oldcol;
410: maxcol = oldmax;
411: }
412:
1.6 deraadt 413: void
1.1 deraadt 414: reverse()
415: {
416: upln++;
417: fwd();
418: PRINT(CURS_UP);
419: PRINT(CURS_UP);
420: upln++;
421: }
422:
1.6 deraadt 423: void
1.1 deraadt 424: initcap()
425: {
426: static char tcapbuf[512];
427: char *bp = tcapbuf;
428: char *getenv(), *tgetstr();
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.1 deraadt 478: outchar(c)
479: int c;
480: {
481: putchar(c & 0177);
1.6 deraadt 482: return (0);
1.1 deraadt 483: }
484:
485: static int curmode = 0;
486:
1.6 deraadt 487: void
1.1 deraadt 488: outc(c)
489: int c;
490: {
491: putchar(c);
492: if (must_use_uc && (curmode&UNDERL)) {
493: PRINT(CURS_LEFT);
494: PRINT(UNDER_CHAR);
495: }
496: }
497:
1.6 deraadt 498: void
499: msetmode(newmode)
1.1 deraadt 500: int newmode;
501: {
502: if (!iflag) {
503: if (curmode != NORMAL && newmode != NORMAL)
1.6 deraadt 504: msetmode(NORMAL);
1.1 deraadt 505: switch (newmode) {
506: case NORMAL:
507: switch(curmode) {
508: case NORMAL:
509: break;
510: case UNDERL:
511: PRINT(EXIT_UNDERLINE);
512: break;
513: default:
514: /* This includes standout */
515: PRINT(EXIT_ATTRIBUTES);
516: break;
517: }
518: break;
519: case ALTSET:
520: PRINT(ENTER_REVERSE);
521: break;
522: case SUPERSC:
523: /*
524: * This only works on a few terminals.
525: * It should be fixed.
526: */
527: PRINT(ENTER_UNDERLINE);
528: PRINT(ENTER_DIM);
529: break;
530: case SUBSC:
531: PRINT(ENTER_DIM);
532: break;
533: case UNDERL:
534: PRINT(ENTER_UNDERLINE);
535: break;
536: case BOLD:
537: PRINT(ENTER_BOLD);
538: break;
539: default:
540: /*
541: * We should have some provision here for multiple modes
542: * on at once. This will have to come later.
543: */
544: PRINT(ENTER_STANDOUT);
545: break;
546: }
547: }
548: curmode = newmode;
549: }