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