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