xref: /netbsd-src/usr.bin/ul/ul.c (revision b1c86f5f087524e68db12794ee9c3e3da1ab17a0)
1 /*	$NetBSD: ul.c,v 1.15 2010/02/03 15:34:46 roy 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. Neither the name of the University nor the names of its contributors
16  *    may be used to endorse or promote products derived from this software
17  *    without specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
20  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
23  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29  * SUCH DAMAGE.
30  */
31 
32 #include <sys/cdefs.h>
33 #ifndef lint
34 __COPYRIGHT("@(#) Copyright (c) 1980, 1993\
35  The Regents of the University of California.  All rights reserved.");
36 #endif /* not lint */
37 
38 #ifndef lint
39 #if 0
40 static char sccsid[] = "@(#)ul.c	8.1 (Berkeley) 6/6/93";
41 #endif
42 __RCSID("$NetBSD: ul.c,v 1.15 2010/02/03 15:34:46 roy Exp $");
43 #endif /* not lint */
44 
45 #include <stdio.h>
46 #include <stdlib.h>
47 #include <string.h>
48 #include <term.h>
49 #include <unistd.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 struct tinfo *info;
67 int	must_overstrike;
68 const char *CURS_UP, *CURS_RIGHT, *CURS_LEFT,
69 	*ENTER_STANDOUT, *EXIT_STANDOUT, *ENTER_UNDERLINE, *EXIT_UNDERLINE,
70 	*ENTER_DIM, *ENTER_BOLD, *ENTER_REVERSE, *UNDER_CHAR, *EXIT_ATTRIBUTES;
71 
72 struct	CHAR	{
73 	char	c_mode;
74 	char	c_char;
75 } ;
76 
77 struct	CHAR	obuf[MAXBUF];
78 int	col, maxcol;
79 int	mode;
80 int	halfpos;
81 int	upln;
82 int	iflag;
83 
84 int	main __P((int, char **));
85 void	filter __P((FILE *));
86 void	flushln __P((void));
87 void	fwd __P((void));
88 void	iattr __P((void));
89 void	initbuf __P((void));
90 void	initcap __P((void));
91 void	outc __P((int));
92 int	outchar __P((int));
93 void	overstrike __P((void));
94 void	reverse __P((void));
95 void	setulmode __P((int));
96 
97 
98 #define	PRINT(s)	if (s == NULL) /* void */; else tputs(s, 1, outchar)
99 
100 int
101 main(argc, argv)
102 	int argc;
103 	char **argv;
104 {
105 	int c;
106 	const char *termtype;
107 	FILE *f;
108 
109 	termtype = getenv("TERM");
110 	if (termtype == NULL || (argv[0][0] == 'c' && !isatty(1)))
111 		termtype = "lpr";
112 	while ((c=getopt(argc, argv, "it:T:")) != -1)
113 		switch(c) {
114 
115 		case 't':
116 		case 'T': /* for nroff compatibility */
117 				termtype = optarg;
118 			break;
119 		case 'i':
120 			iflag = 1;
121 			break;
122 
123 		default:
124 			fprintf(stderr,
125 				"usage: %s [ -i ] [ -tTerm ] file...\n",
126 				argv[0]);
127 			exit(1);
128 		}
129 
130 	setupterm(termtype, 0, NULL);
131 	if ((over_strike && enter_bold_mode == NULL) ||
132 	    (transparent_underline && enter_underline_mode == NULL &&
133 	     underline_char == NULL))
134 	initbuf();
135 	if (optind == argc)
136 		filter(stdin);
137 	else for (; optind<argc; optind++) {
138 		f = fopen(argv[optind],"r");
139 		if (f == NULL) {
140 			perror(argv[optind]);
141 			exit(1);
142 		}
143 
144 		filter(f);
145 		fclose(f);
146 	}
147 	exit(0);
148 }
149 
150 void
151 filter(f)
152 	FILE *f;
153 {
154 	int 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 void
265 flushln()
266 {
267 	int lastmode;
268 	int i;
269 	int hadmodes = 0;
270 
271 	lastmode = NORMAL;
272 	for (i=0; i<maxcol; i++) {
273 		if (obuf[i].c_mode != lastmode) {
274 			hadmodes++;
275 			setulmode(obuf[i].c_mode);
276 			lastmode = obuf[i].c_mode;
277 		}
278 		if (obuf[i].c_char == '\0') {
279 			if (upln) {
280 				PRINT(cursor_right);
281 			}
282 			else {
283 				outc(' ');
284 			}
285 		} else
286 			outc(obuf[i].c_char);
287 	}
288 	if (lastmode != NORMAL) {
289 		setulmode(0);
290 	}
291 	if (must_overstrike && hadmodes)
292 		overstrike();
293 	putchar('\n');
294 	if (iflag && hadmodes)
295 		iattr();
296 	(void)fflush(stdout);
297 	if (upln)
298 		upln--;
299 	initbuf();
300 }
301 
302 /*
303  * For terminals that can overstrike, overstrike underlines and bolds.
304  * We don't do anything with halfline ups and downs, or Greek.
305  */
306 void
307 overstrike()
308 {
309 	int i;
310 	char lbuf[256];
311 	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 void
345 iattr()
346 {
347 	int i;
348 	char lbuf[256];
349 	char *cp = lbuf;
350 
351 	for (i=0; i<maxcol; i++)
352 		switch (obuf[i].c_mode) {
353 		case NORMAL:	*cp++ = ' '; break;
354 		case ALTSET:	*cp++ = 'g'; break;
355 		case SUPERSC:	*cp++ = '^'; break;
356 		case SUBSC:	*cp++ = 'v'; break;
357 		case UNDERL:	*cp++ = '_'; break;
358 		case BOLD:	*cp++ = '!'; break;
359 		default:	*cp++ = 'X'; break;
360 		}
361 	for (*cp=' '; *cp==' '; cp--)
362 		*cp = 0;
363 	for (cp=lbuf; *cp; cp++)
364 		putchar(*cp);
365 	putchar('\n');
366 }
367 
368 void
369 initbuf()
370 {
371 
372 	memset((char *)obuf, 0, sizeof (obuf));	/* depends on NORMAL == 0 */
373 	col = 0;
374 	maxcol = 0;
375 	mode &= ALTSET;
376 }
377 
378 void
379 fwd()
380 {
381 	int oldcol, oldmax;
382 
383 	oldcol = col;
384 	oldmax = maxcol;
385 	flushln();
386 	col = oldcol;
387 	maxcol = oldmax;
388 }
389 
390 void
391 reverse()
392 {
393 	upln++;
394 	fwd();
395 	PRINT(cursor_up);
396 	PRINT(cursor_up);
397 	upln++;
398 }
399 
400 int
401 outchar(c)
402 	int c;
403 {
404 	return (putchar(c & 0177));
405 }
406 
407 static int curmode = 0;
408 
409 void
410 outc(c)
411 	int c;
412 {
413 	putchar(c);
414 	if (underline_char && !enter_underline_mode && (curmode & UNDERL)) {
415 		if (cursor_left)
416 			PRINT(cursor_left);
417 		else
418 			putchar('\b');
419 		PRINT(underline_char);
420 	}
421 }
422 
423 void
424 setulmode(newmode)
425 	int newmode;
426 {
427 	if (!iflag) {
428 		if (curmode != NORMAL && newmode != NORMAL)
429 			setulmode(NORMAL);
430 		switch (newmode) {
431 		case NORMAL:
432 			switch(curmode) {
433 			case NORMAL:
434 				break;
435 			case UNDERL:
436 				if (enter_underline_mode)
437 					PRINT(exit_underline_mode);
438 				else
439 					PRINT(exit_standout_mode);
440 				break;
441 			default:
442 				/* This includes standout */
443 				if (exit_attribute_mode)
444 					PRINT(exit_attribute_mode);
445 				else
446 					PRINT(exit_standout_mode);
447 				break;
448 			}
449 			break;
450 		case ALTSET:
451 			if (enter_reverse_mode)
452 				PRINT(enter_reverse_mode);
453 			else
454 				PRINT(enter_standout_mode);
455 			break;
456 		case SUPERSC:
457 			/*
458 			 * This only works on a few terminals.
459 			 * It should be fixed.
460 			 */
461 			PRINT(enter_underline_mode);
462 			PRINT(enter_dim_mode);
463 			break;
464 		case SUBSC:
465 			if (enter_dim_mode)
466 				PRINT(enter_dim_mode);
467 			else
468 				PRINT(enter_standout_mode);
469 			break;
470 		case UNDERL:
471 			if (enter_underline_mode)
472 				PRINT(enter_underline_mode);
473 			else
474 				PRINT(enter_standout_mode);
475 			break;
476 		case BOLD:
477 			if (enter_bold_mode)
478 				PRINT(enter_bold_mode);
479 			else
480 				PRINT(enter_reverse_mode);
481 			break;
482 		default:
483 			/*
484 			 * We should have some provision here for multiple modes
485 			 * on at once.  This will have to come later.
486 			 */
487 			PRINT(enter_standout_mode);
488 			break;
489 		}
490 	}
491 	curmode = newmode;
492 }
493