xref: /netbsd-src/usr.bin/ul/ul.c (revision 946379e7b37692fc43f68eb0d1c10daa0a7f3b6c)
1 /*	$NetBSD: ul.c,v 1.16 2012/03/20 20:34:59 matt 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.16 2012/03/20 20:34:59 matt 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(int argc, char **argv)
102 {
103 	int c;
104 	const char *termtype;
105 	FILE *f;
106 
107 	termtype = getenv("TERM");
108 	if (termtype == NULL || (argv[0][0] == 'c' && !isatty(1)))
109 		termtype = "lpr";
110 	while ((c=getopt(argc, argv, "it:T:")) != -1)
111 		switch(c) {
112 
113 		case 't':
114 		case 'T': /* for nroff compatibility */
115 				termtype = optarg;
116 			break;
117 		case 'i':
118 			iflag = 1;
119 			break;
120 
121 		default:
122 			fprintf(stderr,
123 				"usage: %s [ -i ] [ -tTerm ] file...\n",
124 				argv[0]);
125 			exit(1);
126 		}
127 
128 	setupterm(termtype, 0, NULL);
129 	if ((over_strike && enter_bold_mode == NULL) ||
130 	    (transparent_underline && enter_underline_mode == NULL &&
131 	     underline_char == NULL))
132 	initbuf();
133 	if (optind == argc)
134 		filter(stdin);
135 	else for (; optind<argc; optind++) {
136 		f = fopen(argv[optind],"r");
137 		if (f == NULL) {
138 			perror(argv[optind]);
139 			exit(1);
140 		}
141 
142 		filter(f);
143 		fclose(f);
144 	}
145 	exit(0);
146 }
147 
148 void
149 filter(FILE *f)
150 {
151 	int c;
152 
153 	while ((c = getc(f)) != EOF) switch(c) {
154 
155 	case '\b':
156 		if (col > 0)
157 			col--;
158 		continue;
159 
160 	case '\t':
161 		col = (col+8) & ~07;
162 		if (col > maxcol)
163 			maxcol = col;
164 		continue;
165 
166 	case '\r':
167 		col = 0;
168 		continue;
169 
170 	case SO:
171 		mode |= ALTSET;
172 		continue;
173 
174 	case SI:
175 		mode &= ~ALTSET;
176 		continue;
177 
178 	case IESC:
179 		switch (c = getc(f)) {
180 
181 		case HREV:
182 			if (halfpos == 0) {
183 				mode |= SUPERSC;
184 				halfpos--;
185 			} else if (halfpos > 0) {
186 				mode &= ~SUBSC;
187 				halfpos--;
188 			} else {
189 				halfpos = 0;
190 				reverse();
191 			}
192 			continue;
193 
194 		case HFWD:
195 			if (halfpos == 0) {
196 				mode |= SUBSC;
197 				halfpos++;
198 			} else if (halfpos < 0) {
199 				mode &= ~SUPERSC;
200 				halfpos++;
201 			} else {
202 				halfpos = 0;
203 				fwd();
204 			}
205 			continue;
206 
207 		case FREV:
208 			reverse();
209 			continue;
210 
211 		default:
212 			fprintf(stderr,
213 				"Unknown escape sequence in input: %o, %o\n",
214 				IESC, c);
215 			exit(1);
216 		}
217 		continue;
218 
219 	case '_':
220 		if (obuf[col].c_char)
221 			obuf[col].c_mode |= UNDERL | mode;
222 		else
223 			obuf[col].c_char = '_';
224 	case ' ':
225 		col++;
226 		if (col > maxcol)
227 			maxcol = col;
228 		continue;
229 
230 	case '\n':
231 		flushln();
232 		continue;
233 
234 	case '\f':
235 		flushln();
236 		putchar('\f');
237 		continue;
238 
239 	default:
240 		if (c < ' ')	/* non printing */
241 			continue;
242 		if (obuf[col].c_char == '\0') {
243 			obuf[col].c_char = c;
244 			obuf[col].c_mode = mode;
245 		} else if (obuf[col].c_char == '_') {
246 			obuf[col].c_char = c;
247 			obuf[col].c_mode |= UNDERL|mode;
248 		} else if (obuf[col].c_char == c)
249 			obuf[col].c_mode |= BOLD|mode;
250 		else
251 			obuf[col].c_mode = mode;
252 		col++;
253 		if (col > maxcol)
254 			maxcol = col;
255 		continue;
256 	}
257 	if (maxcol)
258 		flushln();
259 }
260 
261 void
262 flushln(void)
263 {
264 	int lastmode;
265 	int i;
266 	int hadmodes = 0;
267 
268 	lastmode = NORMAL;
269 	for (i=0; i<maxcol; i++) {
270 		if (obuf[i].c_mode != lastmode) {
271 			hadmodes++;
272 			setulmode(obuf[i].c_mode);
273 			lastmode = obuf[i].c_mode;
274 		}
275 		if (obuf[i].c_char == '\0') {
276 			if (upln) {
277 				PRINT(cursor_right);
278 			}
279 			else {
280 				outc(' ');
281 			}
282 		} else
283 			outc(obuf[i].c_char);
284 	}
285 	if (lastmode != NORMAL) {
286 		setulmode(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 void
304 overstrike(void)
305 {
306 	int i;
307 	char lbuf[256];
308 	char *cp = lbuf;
309 	int hadbold=0;
310 
311 	/* Set up overstrike buffer */
312 	for (i=0; i<maxcol; i++)
313 		switch (obuf[i].c_mode) {
314 		case NORMAL:
315 		default:
316 			*cp++ = ' ';
317 			break;
318 		case UNDERL:
319 			*cp++ = '_';
320 			break;
321 		case BOLD:
322 			*cp++ = obuf[i].c_char;
323 			hadbold=1;
324 			break;
325 		}
326 	putchar('\r');
327 	for (*cp=' '; *cp==' '; cp--)
328 		*cp = 0;
329 	for (cp=lbuf; *cp; cp++)
330 		putchar(*cp);
331 	if (hadbold) {
332 		putchar('\r');
333 		for (cp=lbuf; *cp; cp++)
334 			putchar(*cp=='_' ? ' ' : *cp);
335 		putchar('\r');
336 		for (cp=lbuf; *cp; cp++)
337 			putchar(*cp=='_' ? ' ' : *cp);
338 	}
339 }
340 
341 void
342 iattr(void)
343 {
344 	int i;
345 	char lbuf[256];
346 	char *cp = lbuf;
347 
348 	for (i=0; i<maxcol; i++)
349 		switch (obuf[i].c_mode) {
350 		case NORMAL:	*cp++ = ' '; break;
351 		case ALTSET:	*cp++ = 'g'; break;
352 		case SUPERSC:	*cp++ = '^'; break;
353 		case SUBSC:	*cp++ = 'v'; break;
354 		case UNDERL:	*cp++ = '_'; break;
355 		case BOLD:	*cp++ = '!'; break;
356 		default:	*cp++ = 'X'; break;
357 		}
358 	for (*cp=' '; *cp==' '; cp--)
359 		*cp = 0;
360 	for (cp=lbuf; *cp; cp++)
361 		putchar(*cp);
362 	putchar('\n');
363 }
364 
365 void
366 initbuf(void)
367 {
368 
369 	memset((char *)obuf, 0, sizeof (obuf));	/* depends on NORMAL == 0 */
370 	col = 0;
371 	maxcol = 0;
372 	mode &= ALTSET;
373 }
374 
375 void
376 fwd(void)
377 {
378 	int oldcol, oldmax;
379 
380 	oldcol = col;
381 	oldmax = maxcol;
382 	flushln();
383 	col = oldcol;
384 	maxcol = oldmax;
385 }
386 
387 void
388 reverse(void)
389 {
390 	upln++;
391 	fwd();
392 	PRINT(cursor_up);
393 	PRINT(cursor_up);
394 	upln++;
395 }
396 
397 int
398 outchar(int c)
399 {
400 	return (putchar(c & 0177));
401 }
402 
403 static int curmode = 0;
404 
405 void
406 outc(int c)
407 {
408 	putchar(c);
409 	if (underline_char && !enter_underline_mode && (curmode & UNDERL)) {
410 		if (cursor_left)
411 			PRINT(cursor_left);
412 		else
413 			putchar('\b');
414 		PRINT(underline_char);
415 	}
416 }
417 
418 void
419 setulmode(int newmode)
420 {
421 	if (!iflag) {
422 		if (curmode != NORMAL && newmode != NORMAL)
423 			setulmode(NORMAL);
424 		switch (newmode) {
425 		case NORMAL:
426 			switch(curmode) {
427 			case NORMAL:
428 				break;
429 			case UNDERL:
430 				if (enter_underline_mode)
431 					PRINT(exit_underline_mode);
432 				else
433 					PRINT(exit_standout_mode);
434 				break;
435 			default:
436 				/* This includes standout */
437 				if (exit_attribute_mode)
438 					PRINT(exit_attribute_mode);
439 				else
440 					PRINT(exit_standout_mode);
441 				break;
442 			}
443 			break;
444 		case ALTSET:
445 			if (enter_reverse_mode)
446 				PRINT(enter_reverse_mode);
447 			else
448 				PRINT(enter_standout_mode);
449 			break;
450 		case SUPERSC:
451 			/*
452 			 * This only works on a few terminals.
453 			 * It should be fixed.
454 			 */
455 			PRINT(enter_underline_mode);
456 			PRINT(enter_dim_mode);
457 			break;
458 		case SUBSC:
459 			if (enter_dim_mode)
460 				PRINT(enter_dim_mode);
461 			else
462 				PRINT(enter_standout_mode);
463 			break;
464 		case UNDERL:
465 			if (enter_underline_mode)
466 				PRINT(enter_underline_mode);
467 			else
468 				PRINT(enter_standout_mode);
469 			break;
470 		case BOLD:
471 			if (enter_bold_mode)
472 				PRINT(enter_bold_mode);
473 			else
474 				PRINT(enter_reverse_mode);
475 			break;
476 		default:
477 			/*
478 			 * We should have some provision here for multiple modes
479 			 * on at once.  This will have to come later.
480 			 */
481 			PRINT(enter_standout_mode);
482 			break;
483 		}
484 	}
485 	curmode = newmode;
486 }
487