xref: /titanic_52/usr/src/cmd/ul/ul.c (revision 7c478bd95313f5f23a4c958a745db2134aa03244)
1 /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
2 /*	  All Rights Reserved  	*/
3 
4 
5 /*
6  * Copyright (c) 1980 Regents of the University of California.
7  * All rights reserved. The Berkeley software License Agreement
8  * specifies the terms and conditions for redistribution.
9  */
10 
11 /*
12  * Copyright (c) 1983, 1984 1985, 1986, 1987, 1988, 2000 Sun Microsystems, Inc.
13  * All Rights Reserved.
14  */
15 
16 #ident	"%Z%%M%	%I%	%E% SMI"	/* SVr4.0 1.1	*/
17 
18 #include <stdio.h>
19 #include <locale.h>
20 #include <wctype.h>
21 #include <widec.h>
22 #include <euc.h>
23 #include <limits.h>
24 #include <stdlib.h>
25 
26 #define	IESC	L'\033'
27 #define	SO	L'\016'
28 #define	SI	L'\017'
29 #define	HFWD	L'9'
30 #define	HREV	L'8'
31 #define	FREV	L'7'
32 #define	CDUMMY	-1
33 
34 #define	NORMAL	000
35 #define	ALTSET	001	/* Reverse */
36 #define	SUPERSC	002	/* Dim */
37 #define	SUBSC	004	/* Dim | Ul */
38 #define	UNDERL	010	/* Ul */
39 #define	BOLD	020	/* Bold */
40 
41 #define	MEMFCT	16
42 /*
43  * MEMFCT is a number that is likely to be large enough as a factor for
44  * allocating more memory and to be small enough so as not wasting memory
45  */
46 
47 int	must_use_uc, must_overstrike;
48 char	*CURS_UP, *CURS_RIGHT, *CURS_LEFT,
49 	*ENTER_STANDOUT, *EXIT_STANDOUT, *ENTER_UNDERLINE, *EXIT_UNDERLINE,
50 	*ENTER_DIM, *ENTER_BOLD, *ENTER_REVERSE, *UNDER_CHAR, *EXIT_ATTRIBUTES;
51 
52 struct	CHAR	{
53 	char	c_mode;
54 	wchar_t	c_char;
55 };
56 
57 struct	CHAR	obuf[LINE_MAX];
58 int	col, maxcol;
59 int	mode;
60 int	halfpos;
61 int	upln;
62 int	iflag;
63 
64 eucwidth_t wp;
65 int scrw[4];
66 
67 main(argc, argv)
68 	int argc;
69 	char **argv;
70 {
71 	int c;
72 	char *cp, *termtype;
73 	FILE *f;
74 	char termcap[1024];
75 	char *getenv();
76 	extern int optind;
77 	extern char *optarg;
78 
79 	(void) setlocale(LC_ALL, "");
80 #if !defined(TEXT_DOMAIN)
81 #define	TEXT_DOMAIN	"SYS_TEST"
82 #endif
83 	(void) textdomain(TEXT_DOMAIN);
84 
85 	getwidth(&wp);
86 	scrw[0] = 1;
87 	scrw[1] = wp._scrw1;
88 	scrw[2] = wp._scrw2;
89 	scrw[3] = wp._scrw3;
90 
91 	termtype = getenv("TERM");
92 	if (termtype == NULL || (argv[0][0] == 'c' && !isatty(1)))
93 		termtype = "lpr";
94 	while ((c=getopt(argc, argv, "it:T:")) != EOF)
95 		switch (c) {
96 
97 		case 't':
98 		case 'T': /* for nroff compatibility */
99 				termtype = optarg;
100 			break;
101 		case 'i':
102 			iflag = 1;
103 			break;
104 
105 		default:
106 			fprintf(stderr,
107 			gettext("\
108 Usage: %s [ -i ] [ -t terminal ] [ filename...]\n"),
109 				argv[0]);
110 			exit(1);
111 		}
112 
113 	switch (tgetent(termcap, termtype)) {
114 
115 	case 1:
116 		break;
117 
118 	default:
119 		fprintf(stderr, gettext("trouble reading termcap"));
120 		/* fall through to ... */
121 
122 	case 0:
123 		/* No such terminal type - assume dumb */
124 		strcpy(termcap, "dumb:os:col#80:cr=^M:sf=^J:am:");
125 		break;
126 	}
127 	initcap();
128 	if ((tgetflag("os") && ENTER_BOLD==NULL) ||
129 		(tgetflag("ul") && ENTER_UNDERLINE==NULL && UNDER_CHAR==NULL))
130 			must_overstrike = 1;
131 	initbuf();
132 	if (optind == argc)
133 		filter(stdin);
134 	else for (; optind<argc; optind++) {
135 		f = fopen(argv[optind], "r");
136 		if (f == NULL) {
137 			perror(argv[optind]);
138 			exit(1);
139 		} else
140 			filter(f);
141 	}
142 	exit(0);
143 }
144 
145 filter(f)
146 FILE *f;
147 {
148 	register wchar_t c;
149 	register i;
150 
151 	while ((c = getwc(f)) != EOF) {
152 		if (maxcol >= LINE_MAX)
153 		{
154 			fprintf(stderr,
155 	gettext("Input line longer than %d characters\n"), LINE_MAX);
156 			exit(1);
157 		}
158 		switch (c) {
159 
160 		case L'\b':
161 			if (col > 0)
162 				col--;
163 			continue;
164 
165 		case L'\t':
166 			col = (col+8) & ~07;
167 			if (col > maxcol)
168 				maxcol = col;
169 			continue;
170 
171 		case L'\r':
172 			col = 0;
173 			continue;
174 
175 		case SO:
176 			mode |= ALTSET;
177 			continue;
178 
179 		case SI:
180 			mode &= ~ALTSET;
181 			continue;
182 
183 		case IESC:
184 			switch (c = getwc(f)) {
185 			case HREV:
186 				if (halfpos == 0) {
187 					mode |= SUPERSC;
188 					halfpos--;
189 				} else if (halfpos > 0) {
190 					mode &= ~SUBSC;
191 					halfpos--;
192 				} else {
193 					halfpos = 0;
194 					reverse();
195 				}
196 				continue;
197 
198 			case HFWD:
199 				if (halfpos == 0) {
200 					mode |= SUBSC;
201 					halfpos++;
202 				} else if (halfpos < 0) {
203 					mode &= ~SUPERSC;
204 					halfpos++;
205 				} else {
206 					halfpos = 0;
207 					fwd();
208 				}
209 				continue;
210 			case FREV:
211 				reverse();
212 				continue;
213 
214 			default:
215 				fprintf(stderr,
216 			gettext("Unknown escape sequence in input: %o, %o\n"),
217 					IESC, c);
218 				exit(1);
219 			}
220 			continue;
221 
222 		case L'_':
223 			if (obuf[col].c_char)
224 				obuf[col].c_mode |= UNDERL | mode;
225 			else
226 				obuf[col].c_char = '_';
227 		case L' ':
228 			col++;
229 			if (col > maxcol)
230 				maxcol = col;
231 			continue;
232 
233 		case L'\n':
234 			flushln();
235 			continue;
236 
237 		default:
238 			if (c < L' ')	/* non printing */
239 				continue;
240 			if (obuf[col].c_char == L'\0') {
241 				obuf[col].c_char = c;
242 				obuf[col].c_mode = mode;
243 				i = scrw[wcsetno(c)];
244 				while (--i > 0)
245 					obuf[++col].c_char = CDUMMY;
246 			} else if (obuf[col].c_char == L'_') {
247 				obuf[col].c_char = c;
248 				obuf[col].c_mode |= UNDERL|mode;
249 				i = scrw[wcsetno(c)];
250 				while (--i > 0)
251 					obuf[++col].c_char = CDUMMY;
252 			} else if (obuf[col].c_char == c)
253 				obuf[col].c_mode |= BOLD|mode;
254 			else {
255 				obuf[col].c_char = c;
256 				obuf[col].c_mode = mode;
257 			}
258 			col++;
259 			if (col > maxcol)
260 				maxcol = col;
261 			continue;
262 		}
263 	}
264 	if (maxcol)
265 		flushln();
266 }
267 
268 flushln()
269 {
270 	register lastmode;
271 	register i;
272 	int hadmodes = 0;
273 
274 	lastmode = NORMAL;
275 	for (i=0; i<maxcol; i++) {
276 		if (obuf[i].c_mode != lastmode) {
277 			hadmodes++;
278 			setmode(obuf[i].c_mode);
279 			lastmode = obuf[i].c_mode;
280 		}
281 		if (obuf[i].c_char == L'\0') {
282 			if (upln) {
283 				puts(CURS_RIGHT);
284 			} else
285 				outc(L' ');
286 		} else
287 			outc(obuf[i].c_char);
288 	}
289 	if (lastmode != NORMAL) {
290 		setmode(0);
291 	}
292 	if (must_overstrike && hadmodes)
293 		overstrike();
294 	putwchar(L'\n');
295 	if (iflag && hadmodes)
296 		iattr();
297 	if (upln)
298 		upln--;
299 	initbuf();
300 }
301 
302 /*
303  * For terminals that can overstrike, overstrike underlines and bolds.
304  * We don't do anything with halfline ups and downs, or Greek.
305  */
306 overstrike()
307 {
308 	register int i, n;
309 	wchar_t *cp, *scp;
310 	size_t  szbf = 256, tszbf;
311 	int hadbold=0;
312 
313 	scp = (wchar_t *)malloc(sizeof (wchar_t) * szbf);
314 	if (!scp) {
315 	/* this kind of message need not to be gettext'ed */
316 		(void) fprintf(stderr, "malloc failed\n");
317 		exit(1);
318 	}
319 	cp = scp;
320 	tszbf = szbf;
321 #ifdef DEBUG
322 	/*
323 	 * to allocate a memory after the chunk of the current scp
324 	 * and to make sure the following realloc() allocates
325 	 * memory from different chunks.
326 	 */
327 	(void) malloc(1024 * 1024);
328 #endif
329 
330 	/* Set up overstrike buffer */
331 	for (i=0; i<maxcol; i++) {
332 		n = scrw[wcsetno(obuf[i].c_char)];
333 		if (tszbf <= n) {
334 		/* may not enough buffer for this char */
335 			size_t  pos;
336 
337 			/* obtain the offset of cp */
338 			pos = cp - scp;
339 			/* reallocate another (n * MEMFCT) * sizeof (wchar_t) */
340 			scp = (wchar_t *)realloc(scp,
341 				sizeof (wchar_t) * (szbf + (n * MEMFCT)));
342 			if (!scp) {
343 				(void) fprintf(stderr, "malloc failed\n");
344 				exit(1);
345 			}
346 			/* get the new address of cp */
347 			cp = scp + pos;
348 			szbf += n * MEMFCT;
349 			tszbf += n * MEMFCT;
350 		}
351 		switch (obuf[i].c_mode) {
352 		case NORMAL:
353 		default:
354 			tszbf -= n;
355 			*cp++ = L' ';
356 			while (--n > 0) {
357 				*cp++ = L' ';
358 				i++;
359 			}
360 			break;
361 		case UNDERL:
362 			tszbf -= n;
363 			*cp++ = L'_';
364 			while (--n > 0) {
365 				*cp++ = L'_';
366 				i++;
367 			}
368 			break;
369 		case BOLD:
370 			tszbf--;
371 			*cp++ = obuf[i].c_char;
372 			hadbold=1;
373 			break;
374 		}
375 	}
376 	putwchar(L'\r');
377 	for (*cp=L' '; *cp==L' '; cp--)
378 		*cp = L'\0';
379 	for (cp=scp; *cp; cp++)
380 		putwchar(*cp);
381 	if (hadbold) {
382 		putwchar(L'\r');
383 		for (cp=scp; *cp; cp++)
384 			putwchar(*cp==L'_' ? L' ' : *cp);
385 		putwchar(L'\r');
386 		for (cp=scp; *cp; cp++)
387 			putwchar(*cp==L'_' ? L' ' : *cp);
388 	}
389 	free(scp);
390 }
391 
392 iattr()
393 {
394 	register int i, n;
395 	wchar_t *cp, *scp;
396 	register wchar_t cx;
397 	size_t  szbf = 256, tszbf;
398 
399 	scp = (wchar_t *)malloc(sizeof (wchar_t) * szbf);
400 	if (!scp) {
401 		/* this kind of message need not to be gettext'ed */
402 		(void) fprintf(stderr, "malloc failed\n");
403 		exit(1);
404 	}
405 	cp = scp;
406 	tszbf = szbf;
407 #ifdef DEBUG
408 	/*
409 	 * to allocate a memory after the chunk of the current scp
410 	 * and to make sure the following realloc() allocates
411 	 * memory from different chunks.
412 	 */
413 	(void) malloc(1024 * 1024);
414 #endif
415 	for (i=0; i<maxcol; i++) {
416 		switch (obuf[i].c_mode) {
417 		case NORMAL:	cx = ' '; break;
418 		case ALTSET:	cx = 'g'; break;
419 		case SUPERSC:	cx = '^'; break;
420 		case SUBSC:	cx = 'v'; break;
421 		case UNDERL:	cx = '_'; break;
422 		case BOLD:	cx = '!'; break;
423 		default:	cx = 'X'; break;
424 		}
425 		n = scrw[wcsetno(obuf[i].c_char)];
426 		if (tszbf <= n) {
427 			/* may not enough buffer for this char */
428 			size_t  pos;
429 
430 			/* obtain the offset of cp */
431 			pos = cp - scp;
432 			/* reallocate another (n * MEMFCT) * sizeof (wchar_t) */
433 			scp = (wchar_t *)realloc(scp,
434 				sizeof (wchar_t) * (szbf + (n * MEMFCT)));
435 			if (!scp) {
436 				(void) fprintf(stderr, "malloc failed\n");
437 				exit(1);
438 			}
439 			/* get the new address of cp */
440 			cp = scp + pos;
441 			szbf += n * MEMFCT;
442 			tszbf += n * MEMFCT;
443 		}
444 		tszbf -= n;
445 		 *cp++ = cx;
446 		while (--n > 0) {
447 			*cp++ = cx;
448 			i++;
449 		}
450 	}
451 	for (*cp=L' '; *cp==L' '; cp--)
452 		*cp = L'\0';
453 	for (cp=scp; *cp; cp++)
454 		putwchar(*cp);
455 	putwchar(L'\n');
456 	free(scp);
457 }
458 
459 initbuf()
460 {
461 	register i;
462 
463 	/* following depends on NORMAL == 000 */
464 	for (i = 0; i < LINE_MAX; i++)
465 		obuf[i].c_char = obuf[i].c_mode = 0;
466 
467 	col = 0;
468 	maxcol = 0;
469 	mode &= ALTSET;
470 }
471 
472 fwd()
473 {
474 	register oldcol, oldmax;
475 
476 	oldcol = col;
477 	oldmax = maxcol;
478 	flushln();
479 	col = oldcol;
480 	maxcol = oldmax;
481 }
482 
483 reverse()
484 {
485 	upln++;
486 	fwd();
487 	puts(CURS_UP);
488 	puts(CURS_UP);
489 	upln++;
490 }
491 
492 initcap()
493 {
494 	static char tcapbuf[512];
495 	char *termtype;
496 	char *bp = tcapbuf;
497 	char *getenv(), *tgetstr();
498 
499 	/* This nonsense attempts to work with both old and new termcap */
500 	CURS_UP =		tgetstr("up", &bp);
501 	CURS_RIGHT =		tgetstr("ri", &bp);
502 	if (CURS_RIGHT == NULL)
503 		CURS_RIGHT =	tgetstr("nd", &bp);
504 	CURS_LEFT =		tgetstr("le", &bp);
505 	if (CURS_LEFT == NULL)
506 		CURS_LEFT =	tgetstr("bc", &bp);
507 	if (CURS_LEFT == NULL && tgetflag("bs"))
508 		CURS_LEFT =	"\b";
509 
510 	ENTER_STANDOUT =	tgetstr("so", &bp);
511 	EXIT_STANDOUT =		tgetstr("se", &bp);
512 	ENTER_UNDERLINE =	tgetstr("us", &bp);
513 	EXIT_UNDERLINE =	tgetstr("ue", &bp);
514 	ENTER_DIM =		tgetstr("mh", &bp);
515 	ENTER_BOLD =		tgetstr("md", &bp);
516 	ENTER_REVERSE =		tgetstr("mr", &bp);
517 	EXIT_ATTRIBUTES =	tgetstr("me", &bp);
518 
519 	if (!ENTER_BOLD && ENTER_REVERSE)
520 		ENTER_BOLD = ENTER_REVERSE;
521 	if (!ENTER_BOLD && ENTER_STANDOUT)
522 		ENTER_BOLD = ENTER_STANDOUT;
523 	if (!ENTER_UNDERLINE && ENTER_STANDOUT) {
524 		ENTER_UNDERLINE = ENTER_STANDOUT;
525 		EXIT_UNDERLINE = EXIT_STANDOUT;
526 	}
527 	if (!ENTER_DIM && ENTER_STANDOUT)
528 		ENTER_DIM = ENTER_STANDOUT;
529 	if (!ENTER_REVERSE && ENTER_STANDOUT)
530 		ENTER_REVERSE = ENTER_STANDOUT;
531 	if (!EXIT_ATTRIBUTES && EXIT_STANDOUT)
532 		EXIT_ATTRIBUTES = EXIT_STANDOUT;
533 
534 	/*
535 	 * Note that we use REVERSE for the alternate character set,
536 	 * not the as/ae capabilities.  This is because we are modelling
537 	 * the model 37 teletype (since that's what nroff outputs) and
538 	 * the typical as/ae is more of a graphics set, not the greek
539 	 * letters the 37 has.
540 	 */
541 
542 #ifdef notdef
543 printf("so %s se %s us %s ue %s me %s\n",
544 	ENTER_STANDOUT, EXIT_STANDOUT, ENTER_UNDERLINE,
545 	EXIT_UNDERLINE, EXIT_ATTRIBUTES);
546 #endif
547 	UNDER_CHAR =		tgetstr("uc", &bp);
548 	must_use_uc = (UNDER_CHAR && !ENTER_UNDERLINE);
549 }
550 
551 outchar(c)
552 char c;
553 {
554 	putchar(c&0177);
555 }
556 
557 puts(str)
558 const char *str;
559 {
560 	if (str)
561 		tputs(str, 1, outchar);
562 }
563 
564 static curmode = 0;
565 outc(c)
566 wchar_t c;
567 {
568 	register int m, n;
569 
570 	if (c == CDUMMY)
571 		return;
572 	putwchar(c);
573 	if (must_use_uc && (curmode & UNDERL)) {
574 		m = n = scrw[wcsetno(c)];
575 		puts(CURS_LEFT);
576 		while (--m > 0)
577 			puts(CURS_LEFT);
578 		puts(UNDER_CHAR);
579 		while (--n > 0)
580 			puts(UNDER_CHAR);
581 	}
582 }
583 
584 setmode(newmode)
585 int newmode;
586 {
587 	if (!iflag)
588 	{
589 		if (curmode != NORMAL && newmode != NORMAL)
590 			setmode(NORMAL);
591 		switch (newmode) {
592 		case NORMAL:
593 			switch (curmode) {
594 			case NORMAL:
595 				break;
596 			case UNDERL:
597 				puts(EXIT_UNDERLINE);
598 				break;
599 			default:
600 				/* This includes standout */
601 				puts(EXIT_ATTRIBUTES);
602 				break;
603 			}
604 			break;
605 		case ALTSET:
606 			puts(ENTER_REVERSE);
607 			break;
608 		case SUPERSC:
609 			/*
610 			 * This only works on a few terminals.
611 			 * It should be fixed.
612 			 */
613 			puts(ENTER_UNDERLINE);
614 			puts(ENTER_DIM);
615 			break;
616 		case SUBSC:
617 			puts(ENTER_DIM);
618 			break;
619 		case UNDERL:
620 			puts(ENTER_UNDERLINE);
621 			break;
622 		case BOLD:
623 			puts(ENTER_BOLD);
624 			break;
625 		default:
626 			/*
627 			 * We should have some provision here for multiple modes
628 			 * on at once.  This will have to come later.
629 			 */
630 			puts(ENTER_STANDOUT);
631 			break;
632 		}
633 	}
634 	curmode = newmode;
635 }
636