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