ul.c revision 92922
1/*
2 * Copyright (c) 1980, 1993
3 *	The Regents of the University of California.  All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 * 3. All advertising materials mentioning features or use of this software
14 *    must display the following acknowledgement:
15 *	This product includes software developed by the University of
16 *	California, Berkeley and its contributors.
17 * 4. Neither the name of the University nor the names of its contributors
18 *    may be used to endorse or promote products derived from this software
19 *    without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 * SUCH DAMAGE.
32 */
33
34#ifndef lint
35static const char copyright[] =
36"@(#) Copyright (c) 1980, 1993\n\
37	The Regents of the University of California.  All rights reserved.\n";
38#endif /* not lint */
39
40#ifndef lint
41#if 0
42static char sccsid[] = "@(#)ul.c	8.1 (Berkeley) 6/6/93";
43#endif
44static const char rcsid[] =
45  "$FreeBSD: head/usr.bin/ul/ul.c 92922 2002-03-22 01:42:45Z imp $";
46#endif /* not lint */
47
48#include <err.h>
49#include <stdio.h>
50#include <stdlib.h>
51#include <string.h>
52#include <termcap.h>
53#include <unistd.h>
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
70int	must_use_uc, must_overstrike;
71const char
72	*CURS_UP, *CURS_RIGHT, *CURS_LEFT,
73	*ENTER_STANDOUT, *EXIT_STANDOUT, *ENTER_UNDERLINE, *EXIT_UNDERLINE,
74	*ENTER_DIM, *ENTER_BOLD, *ENTER_REVERSE, *UNDER_CHAR, *EXIT_ATTRIBUTES;
75
76struct	CHAR	{
77	char	c_mode;
78	char	c_char;
79} ;
80
81struct	CHAR	obuf[MAXBUF];
82int	col, maxcol;
83int	mode;
84int	halfpos;
85int	upln;
86int	iflag;
87
88static void usage(void);
89void setnewmode(int);
90void initcap(void);
91void reverse(void);
92int outchar(int);
93void fwd(void);
94void initbuf(void);
95void iattr(void);
96void overstrike(void);
97void flushln(void);
98void filter(FILE *);
99void outc(int);
100
101#define	PRINT(s)	if (s == NULL) /* void */; else tputs(s, 1, outchar)
102
103int
104main(argc, argv)
105	int argc;
106	char **argv;
107{
108	int c;
109	const char *termtype;
110	FILE *f;
111	char termcap[1024];
112
113	termtype = getenv("TERM");
114	if (termtype == NULL || (argv[0][0] == 'c' && !isatty(1)))
115		termtype = "lpr";
116	while ((c=getopt(argc, argv, "it:T:")) != -1)
117		switch(c) {
118
119		case 't':
120		case 'T': /* for nroff compatibility */
121			termtype = optarg;
122			break;
123		case 'i':
124			iflag = 1;
125			break;
126		default:
127			usage();
128		}
129
130	switch(tgetent(termcap, termtype)) {
131
132	case 1:
133		break;
134
135	default:
136		warnx("trouble reading termcap");
137		/* fall through to ... */
138
139	case 0:
140		/* No such terminal type - assume dumb */
141		(void)strcpy(termcap, "dumb:os:col#80:cr=^M:sf=^J:am:");
142		break;
143	}
144	initcap();
145	if (    (tgetflag("os") && ENTER_BOLD==NULL ) ||
146		(tgetflag("ul") && ENTER_UNDERLINE==NULL && UNDER_CHAR==NULL))
147			must_overstrike = 1;
148	initbuf();
149	if (optind == argc)
150		filter(stdin);
151	else for (; optind<argc; optind++) {
152		f = fopen(argv[optind],"r");
153		if (f == NULL)
154			err(1, "%s", argv[optind]);
155		else
156			filter(f);
157	}
158	exit(0);
159}
160
161static void
162usage()
163{
164	fprintf(stderr, "usage: ul [-i] [-t terminal] file...\n");
165	exit(1);
166}
167
168void
169filter(f)
170	FILE *f;
171{
172	int c;
173
174	while ((c = getc(f)) != EOF && col < MAXBUF) switch(c) {
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:
233			errx(1, "unknown escape sequence in input: %o, %o", IESC, c);
234		}
235		continue;
236
237	case '_':
238		if (obuf[col].c_char)
239			obuf[col].c_mode |= UNDERL | mode;
240		else
241			obuf[col].c_char = '_';
242	case ' ':
243		col++;
244		if (col > maxcol)
245			maxcol = col;
246		continue;
247
248	case '\n':
249		flushln();
250		continue;
251
252	case '\f':
253		flushln();
254		putchar('\f');
255		continue;
256
257	default:
258		if (c < ' ')	/* non printing */
259			continue;
260		if (obuf[col].c_char == '\0') {
261			obuf[col].c_char = c;
262			obuf[col].c_mode = mode;
263		} else if (obuf[col].c_char == '_') {
264			obuf[col].c_char = c;
265			obuf[col].c_mode |= UNDERL|mode;
266		} else if (obuf[col].c_char == c)
267			obuf[col].c_mode |= BOLD|mode;
268		else
269			obuf[col].c_mode = mode;
270		col++;
271		if (col > maxcol)
272			maxcol = col;
273		continue;
274	}
275	if (maxcol)
276		flushln();
277}
278
279void
280flushln()
281{
282	int lastmode;
283	int i;
284	int hadmodes = 0;
285
286	lastmode = NORMAL;
287	for (i=0; i<maxcol; i++) {
288		if (obuf[i].c_mode != lastmode) {
289			hadmodes++;
290			setnewmode(obuf[i].c_mode);
291			lastmode = obuf[i].c_mode;
292		}
293		if (obuf[i].c_char == '\0') {
294			if (upln)
295				PRINT(CURS_RIGHT);
296			else
297				outc(' ');
298		} else
299			outc(obuf[i].c_char);
300	}
301	if (lastmode != NORMAL) {
302		setnewmode(0);
303	}
304	if (must_overstrike && hadmodes)
305		overstrike();
306	putchar('\n');
307	if (iflag && hadmodes)
308		iattr();
309	(void)fflush(stdout);
310	if (upln)
311		upln--;
312	initbuf();
313}
314
315/*
316 * For terminals that can overstrike, overstrike underlines and bolds.
317 * We don't do anything with halfline ups and downs, or Greek.
318 */
319void
320overstrike()
321{
322	register int i;
323	char lbuf[256];
324	register char *cp = lbuf;
325	int hadbold=0;
326
327	/* Set up overstrike buffer */
328	for (i=0; i<maxcol; i++)
329		switch (obuf[i].c_mode) {
330		case NORMAL:
331		default:
332			*cp++ = ' ';
333			break;
334		case UNDERL:
335			*cp++ = '_';
336			break;
337		case BOLD:
338			*cp++ = obuf[i].c_char;
339			hadbold=1;
340			break;
341		}
342	putchar('\r');
343	for (*cp=' '; *cp==' '; cp--)
344		*cp = 0;
345	for (cp=lbuf; *cp; cp++)
346		putchar(*cp);
347	if (hadbold) {
348		putchar('\r');
349		for (cp=lbuf; *cp; cp++)
350			putchar(*cp=='_' ? ' ' : *cp);
351		putchar('\r');
352		for (cp=lbuf; *cp; cp++)
353			putchar(*cp=='_' ? ' ' : *cp);
354	}
355}
356
357void
358iattr()
359{
360	register int i;
361	char lbuf[256];
362	register char *cp = lbuf;
363
364	for (i=0; i<maxcol; i++)
365		switch (obuf[i].c_mode) {
366		case NORMAL:	*cp++ = ' '; break;
367		case ALTSET:	*cp++ = 'g'; break;
368		case SUPERSC:	*cp++ = '^'; break;
369		case SUBSC:	*cp++ = 'v'; break;
370		case UNDERL:	*cp++ = '_'; break;
371		case BOLD:	*cp++ = '!'; break;
372		default:	*cp++ = 'X'; break;
373		}
374	for (*cp=' '; *cp==' '; cp--)
375		*cp = 0;
376	for (cp=lbuf; *cp; cp++)
377		putchar(*cp);
378	putchar('\n');
379}
380
381void
382initbuf()
383{
384
385	bzero((char *)obuf, sizeof (obuf));	/* depends on NORMAL == 0 */
386	col = 0;
387	maxcol = 0;
388	mode &= ALTSET;
389}
390
391void
392fwd()
393{
394	int oldcol, oldmax;
395
396	oldcol = col;
397	oldmax = maxcol;
398	flushln();
399	col = oldcol;
400	maxcol = oldmax;
401}
402
403void
404reverse()
405{
406	upln++;
407	fwd();
408	PRINT(CURS_UP);
409	PRINT(CURS_UP);
410	upln++;
411}
412
413void
414initcap()
415{
416	static char tcapbuf[512];
417	char *bp = tcapbuf;
418
419	/* This nonsense attempts to work with both old and new termcap */
420	CURS_UP =		tgetstr("up", &bp);
421	CURS_RIGHT =		tgetstr("ri", &bp);
422	if (CURS_RIGHT == NULL)
423		CURS_RIGHT =	tgetstr("nd", &bp);
424	CURS_LEFT =		tgetstr("le", &bp);
425	if (CURS_LEFT == NULL)
426		CURS_LEFT =	tgetstr("bc", &bp);
427	if (CURS_LEFT == NULL && tgetflag("bs"))
428		CURS_LEFT =	"\b";
429
430	ENTER_STANDOUT =	tgetstr("so", &bp);
431	EXIT_STANDOUT =		tgetstr("se", &bp);
432	ENTER_UNDERLINE =	tgetstr("us", &bp);
433	EXIT_UNDERLINE =	tgetstr("ue", &bp);
434	ENTER_DIM =		tgetstr("mh", &bp);
435	ENTER_BOLD =		tgetstr("md", &bp);
436	ENTER_REVERSE =		tgetstr("mr", &bp);
437	EXIT_ATTRIBUTES =	tgetstr("me", &bp);
438
439	if (!ENTER_BOLD && ENTER_REVERSE)
440		ENTER_BOLD = ENTER_REVERSE;
441	if (!ENTER_BOLD && ENTER_STANDOUT)
442		ENTER_BOLD = ENTER_STANDOUT;
443	if (!ENTER_UNDERLINE && ENTER_STANDOUT) {
444		ENTER_UNDERLINE = ENTER_STANDOUT;
445		EXIT_UNDERLINE = EXIT_STANDOUT;
446	}
447	if (!ENTER_DIM && ENTER_STANDOUT)
448		ENTER_DIM = ENTER_STANDOUT;
449	if (!ENTER_REVERSE && ENTER_STANDOUT)
450		ENTER_REVERSE = ENTER_STANDOUT;
451	if (!EXIT_ATTRIBUTES && EXIT_STANDOUT)
452		EXIT_ATTRIBUTES = EXIT_STANDOUT;
453
454	/*
455	 * Note that we use REVERSE for the alternate character set,
456	 * not the as/ae capabilities.  This is because we are modelling
457	 * the model 37 teletype (since that's what nroff outputs) and
458	 * the typical as/ae is more of a graphics set, not the greek
459	 * letters the 37 has.
460	 */
461
462	UNDER_CHAR =		tgetstr("uc", &bp);
463	must_use_uc = (UNDER_CHAR && !ENTER_UNDERLINE);
464}
465
466int
467outchar(c)
468	int c;
469{
470	return(putchar(c & 0177));
471}
472
473static int curmode = 0;
474
475void
476outc(c)
477	int c;
478{
479	putchar(c);
480	if (must_use_uc && (curmode&UNDERL)) {
481		PRINT(CURS_LEFT);
482		PRINT(UNDER_CHAR);
483	}
484}
485
486void
487setnewmode(newmode)
488	int newmode;
489{
490	if (!iflag) {
491		if (curmode != NORMAL && newmode != NORMAL)
492			setnewmode(NORMAL);
493		switch (newmode) {
494		case NORMAL:
495			switch(curmode) {
496			case NORMAL:
497				break;
498			case UNDERL:
499				PRINT(EXIT_UNDERLINE);
500				break;
501			default:
502				/* This includes standout */
503				PRINT(EXIT_ATTRIBUTES);
504				break;
505			}
506			break;
507		case ALTSET:
508			PRINT(ENTER_REVERSE);
509			break;
510		case SUPERSC:
511			/*
512			 * This only works on a few terminals.
513			 * It should be fixed.
514			 */
515			PRINT(ENTER_UNDERLINE);
516			PRINT(ENTER_DIM);
517			break;
518		case SUBSC:
519			PRINT(ENTER_DIM);
520			break;
521		case UNDERL:
522			PRINT(ENTER_UNDERLINE);
523			break;
524		case BOLD:
525			PRINT(ENTER_BOLD);
526			break;
527		default:
528			/*
529			 * We should have some provision here for multiple modes
530			 * on at once.  This will have to come later.
531			 */
532			PRINT(ENTER_STANDOUT);
533			break;
534		}
535	}
536	curmode = newmode;
537}
538