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