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