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