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