xref: /titanic_50/usr/src/cmd/tput/tput.c (revision 461686c359e383739b8e0d23c68520a0e2e2c361)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 
22 /*
23  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 /* Copyright 2011 Nexenta Systems, Inc.  All rights reserved. */
27 
28 /*	Copyright (c) 1988 AT&T	*/
29 /*	  All Rights Reserved  	*/
30 
31 /*
32  *	tput - print terminal attribute
33  *
34  *  return-codes - command line arguments:
35  *	0: ok if boolean capname -> TRUE
36  *	1: for boolean capname -> FALSE
37  *
38  *  return-codes - standard input arguments:
39  *	0: ok; tput for all lines was successful
40  *
41  *  return-codes - both cases:
42  *	2	usage error
43  *	3	bad terminal type given or no terminfo database
44  *	4	unknown capname
45  *	-1	capname is a numeric variable that is not specified in the
46  *		terminfo database(E.g. tpu -T450 lines).
47  *
48  *  tput printfs a value if an INT capname was given; e.g. cols.
49  *	putp's a string if a STRING capname was given; e.g. clear. and
50  *  for BOOLEAN capnames, e.g. hard-copy, just returns the boolean value.
51  */
52 
53 #include <curses.h>
54 #include <term.h>
55 #include <fcntl.h>
56 #include <ctype.h>
57 #include <stdlib.h>
58 #include <string.h>
59 #include <sys/types.h>
60 #include <unistd.h>
61 #include <locale.h>
62 #include <err.h>
63 
64 /* externs from libcurses */
65 extern int tigetnum();
66 
67 static int outputcap(char *cap, int argc, char **argv);
68 static int allnumeric(char *string);
69 static int getpad(char *cap);
70 static void setdelay();
71 static void settabs();
72 static void cat(char *file);
73 static void initterm();
74 static void reset_term();
75 
76 static char *progname;		/* argv[0] */
77 static int CurrentBaudRate;	/* current baud rate */
78 static int reset = 0;		/* called as reset_term */
79 static int fildes = 1;
80 
81 int
82 main(int argc, char **argv)
83 {
84 	int i, std_argc;
85 	char *term = getenv("TERM");
86 	char *cap, std_input = FALSE;
87 	int setuperr;
88 
89 	(void) setlocale(LC_ALL, "");
90 #if !defined(TEXT_DOMAIN)
91 #define	TEXT_DOMAIN "SYS_TEST"
92 #endif
93 	(void) textdomain(TEXT_DOMAIN);
94 
95 	progname = argv[0];
96 
97 	while ((i = getopt(argc, argv, "ST:")) != EOF) {
98 		switch (i) {
99 		case 'T':
100 			fildes = -1;
101 			(void) putenv("LINES=");
102 			(void) putenv("COLUMNS=");
103 			term = optarg;
104 			break;
105 
106 		case 'S':
107 			std_input = TRUE;
108 			break;
109 
110 		case '?':			/* FALLTHROUGH		*/
111 		usage:				/* FALLTHROUGH		*/
112 		default:
113 			(void) fprintf(stderr, gettext(
114 			    "usage:\t%s [-T [term]] capname "
115 			    "[parm argument...]\n"), progname);
116 			(void) fprintf(stderr, gettext("OR:\t%s -S <<\n"),
117 			    progname);
118 			exit(2);
119 		}
120 	}
121 
122 	if (!term || !*term) {
123 		(void) fprintf(stderr,
124 		    gettext("%s: No value for $TERM and no -T specified\n"),
125 		    progname);
126 		exit(2);
127 	}
128 
129 	(void) setupterm(term, fildes, &setuperr);
130 
131 	switch (setuperr) {
132 	case -2:
133 		(void) fprintf(stderr,
134 		    gettext("%s: unreadable terminal descriptor \"%s\"\n"),
135 		    progname, term);
136 		exit(3);
137 		break;
138 
139 	case -1:
140 		(void) fprintf(stderr,
141 		    gettext("%s: no terminfo database\n"), progname);
142 		exit(3);
143 		break;
144 
145 	case 0:
146 		(void) fprintf(stderr,
147 		    gettext("%s: unknown terminal \"%s\"\n"),
148 		    progname, term);
149 		exit(3);
150 	}
151 
152 	reset_shell_mode();
153 
154 	/* command line arguments */
155 	if (!std_input) {
156 		if (argc == optind)
157 			goto usage;
158 
159 		cap = argv[optind++];
160 
161 		if (strcmp(cap, "init") == 0)
162 			initterm();
163 		else if (strcmp(cap, "reset") == 0)
164 			reset_term();
165 		else if (strcmp(cap, "longname") == 0)
166 			(void) printf("%s\n", longname());
167 		else
168 			exit(outputcap(cap, argc, argv));
169 		return (0);
170 	} else {			/* standard input argumets	*/
171 		char buff[256];
172 		char **v;
173 
174 		/*
175 		 * allocate storage for the 'faked' argv[] array
176 		 *
177 		 * fixme: The algorithm here is botched. Who or what defines
178 		 * that only 10 arguments with 32 bytes each are passed?
179 		 */
180 		v = (char **)malloc(10 * sizeof (char *));
181 		for (i = 0; i < 10; i++) {
182 			v[i] = (char *)malloc(32);
183 			if (!v[i])
184 				err(EXIT_FAILURE, "no memory for argv[] array");
185 		}
186 
187 		while (fgets(buff, sizeof (buff), stdin) != NULL) {
188 			/* read standard input line; skip over empty lines */
189 			if ((std_argc =
190 			    sscanf(buff, "%s %s %s %s %s %s %s %s %s %s",
191 			    v[0], v[1], v[2], v[3], v[4], v[5], v[6], v[7],
192 			    v[8], v[9])) < 1) {
193 				continue;
194 			}
195 
196 			cap = v[0];
197 			optind = 1;
198 
199 			if (strcmp(cap, "init") == 0) {
200 				initterm();
201 			} else if (strcmp(cap, "reset") == 0) {
202 				reset_term();
203 			} else if (strcmp(cap, "longname") == 0) {
204 				(void) printf("%s\n", longname());
205 			} else {
206 				(void) outputcap(cap, std_argc, v);
207 			}
208 			(void) fflush(stdout);
209 		}
210 
211 		return (0);
212 	}
213 }
214 
215 static long parm[9] = {
216     0, 0, 0, 0, 0, 0, 0, 0, 0
217 };
218 
219 static int
220 outputcap(char *cap, int argc, char **argv)
221 {
222 	int parmset = 0;
223 	char *thisstr;
224 	int i;
225 
226 	if ((i = tigetflag(cap)) >= 0)
227 		return (1 - i);
228 
229 	if ((i = tigetnum(cap)) >= -1) {
230 		(void) printf("%d\n", i);
231 		return (0);
232 	}
233 
234 	if ((thisstr = tigetstr(cap)) != (char *)-1) {
235 		if (!thisstr) {
236 			return (1);
237 		}
238 		for (parmset = 0; optind < argc; optind++, parmset++)
239 			if (allnumeric(argv[optind]))
240 				parm[parmset] = atoi(argv[optind]);
241 			else
242 				parm[parmset] = (int)argv[optind];
243 
244 		if (parmset)
245 			putp(tparm(thisstr,
246 			    parm[0], parm[1], parm[2], parm[3],
247 			    parm[4], parm[5], parm[6], parm[7], parm[8]));
248 		else
249 			putp(thisstr);
250 		return (0);
251 	}
252 
253 	(void) fprintf(stderr,
254 	    gettext("%s: unknown terminfo capability '%s'\n"), progname, cap);
255 
256 	exit(4);
257 	/* NOTREACHED */
258 }
259 
260 /*
261  *  The decision as to whether an argument is a number or not is to simply
262  *  look at whether there are any non-digits in the string.
263  */
264 static int
265 allnumeric(char *string)
266 {
267 	if (*string) {
268 		while (*string) {
269 			if (!isdigit(*string++)) {
270 				return (0);
271 			}
272 		}
273 		return (1);
274 	} else {
275 		return (0);
276 	}
277 }
278 
279 /*
280  *  SYSTEM DEPENDENT TERMINAL DELAY TABLES
281  *
282  *	These tables maintain the correspondence between the delays
283  *	defined in terminfo and the delay algorithms in the tty driver
284  *	on the particular systems. For each type of delay, the bits used
285  *	for that delay must be specified, in XXbits, and a table
286  *	must be defined giving correspondences between delays and
287  *	algorithms. Algorithms which are not fixed delays, such
288  *	as dependent on current column or line number, must be
289  *	kludged in some way at this time.
290  *
291  *	Some of this was taken from tset(1).
292  */
293 
294 struct delay
295 {
296     int d_delay;
297     int d_bits;
298 };
299 
300 /* The appropriate speeds for various termio settings. */
301 static int speeds[] = {
302 		0,	/*  B0,		*/
303 		50,	/*  B50,	*/
304 		75,	/*  B75,	*/
305 		110,	/*  B110,	*/
306 		134,	/*  B134,	*/
307 		150,	/*  B150,	*/
308 		200,	/*  B200,	*/
309 		300,	/*  B300,	*/
310 		600,	/*  B600,	*/
311 		1200,	/*  B1200,	*/
312 		1800,	/*  B1800,	*/
313 		2400,	/*  B2400,	*/
314 		4800,	/*  B4800,	*/
315 		9600,	/*  B9600,	*/
316 		19200,	/*  EXTA,	*/
317 		38400,	/*  EXTB,	*/
318 		57600,	/*  B57600,	*/
319 		76800,	/*  B76800,	*/
320 		115200,	/*  B115200,	*/
321 		153600,	/*  B153600,	*/
322 		230400,	/*  B230400,	*/
323 		307200,	/*  B307200,	*/
324 		460800,	/*  B460800,	*/
325 		921600, /*  B921600,	*/
326 		0,
327 };
328 
329 #if defined(SYSV) || defined(USG)
330 /*	Unix 3.0 on up */
331 
332 /*    Carriage Return delays	*/
333 
334 static int	CRbits = CRDLY;
335 static struct delay	CRdelay[] =
336 {
337 	0,	CR0,
338 	80,	CR1,
339 	100,	CR2,
340 	150,	CR3,
341 	-1
342 };
343 
344 /*	New Line delays	*/
345 
346 static int	NLbits = NLDLY;
347 static struct delay	NLdelay[] =
348 {
349 	0,	NL0,
350 	100,	NL1,
351 	-1
352 };
353 
354 /*	Back Space delays	*/
355 
356 static int	BSbits = BSDLY;
357 static struct delay	BSdelay[] =
358 {
359 	0,	BS0,
360 	50,	BS1,
361 	-1
362 };
363 
364 /*	TaB delays	*/
365 
366 static int	TBbits = TABDLY;
367 static struct delay	TBdelay[] =
368 {
369 	0,	TAB0,
370 	11,	TAB1,		/* special M37 delay */
371 	100,	TAB2,
372 				/* TAB3 is XTABS and not a delay */
373 	-1
374 };
375 
376 /*	Form Feed delays	*/
377 
378 static int	FFbits = FFDLY;
379 static struct delay	FFdelay[] =
380 {
381 	0,	FF0,
382 	2000,	FF1,
383 	-1
384 };
385 
386 #else	/* BSD */
387 
388 /*	Carriage Return delays	*/
389 
390 int	CRbits = CRDELAY;
391 struct delay	CRdelay[] =
392 {
393 	0,	CR0,
394 	9,	CR3,
395 	80,	CR1,
396 	160,	CR2,
397 	-1
398 };
399 
400 /*	New Line delays	*/
401 
402 int	NLbits = NLDELAY;
403 struct delay	NLdelay[] =
404 {
405 	0,	NL0,
406 	66,	NL1,		/* special M37 delay */
407 	100,	NL2,
408 	-1
409 };
410 
411 /*	Tab delays	*/
412 
413 int	TBbits = TBDELAY;
414 struct delay	TBdelay[] =
415 {
416 	0,	TAB0,
417 	11,	TAB1,		/* special M37 delay */
418 	-1
419 };
420 
421 /*	Form Feed delays	*/
422 
423 int	FFbits = VTDELAY;
424 struct delay	FFdelay[] =
425 {
426 	0,	FF0,
427 	2000,	FF1,
428 	-1
429 };
430 #endif	/* BSD */
431 
432 /*
433  *  Initterm, a.k.a. reset_term, does terminal specific initialization. In
434  *  particular, the init_strings from terminfo are output and tabs are
435  *  set, if they aren't hardwired in. Much of this stuff was done by
436  *  the tset(1) program.
437  */
438 
439 /*
440  *  Figure out how many milliseconds of padding the capability cap
441  *  needs and return that number. Padding is stored in the string as "$<n>",
442  *  where n is the number of milliseconds of padding. More than one
443  *  padding string is allowed within the string, although this is unlikely.
444  */
445 
446 static int
447 getpad(char *cap)
448 {
449 	int padding = 0;
450 
451 	/* No padding needed at speeds below padding_baud_rate */
452 	if (padding_baud_rate > CurrentBaudRate || cap == NULL)
453 		return (0);
454 
455 	while (*cap) {
456 		if ((cap[0] == '$') && (cap[1] == '<')) {
457 			cap++;
458 			cap++;
459 			padding += atoi(cap);
460 			while (isdigit (*cap))
461 				cap++;
462 			while (*cap == '.' || *cap == '/' || *cap == '*' ||
463 			    isdigit(*cap))
464 				cap++;
465 			while (*cap == '>')
466 				cap++;
467 		} else {
468 			cap++;
469 		}
470 	}
471 
472 	return (padding);
473 }
474 
475 /*
476  *  Set the appropriate delay bits in the termio structure for
477  *  the given delay.
478  */
479 static void
480 setdelay(delay, delaytable, bits, flags)
481 register int delay;
482 struct delay delaytable[];
483 int bits;
484 #ifdef SYSV
485 tcflag_t *flags;
486 #else	/* SYSV */
487 unsigned short *flags;
488 #endif	/* SYSV */
489 {
490 	register struct delay  *p;
491 	register struct delay  *lastdelay;
492 
493 	/* Clear out the bits, replace with new ones */
494 	*flags &= ~bits;
495 
496 	/* Scan the delay table for first entry with adequate delay */
497 	for (lastdelay = p = delaytable;
498 	    (p -> d_delay >= 0) && (p -> d_delay < delay);
499 	    p++) {
500 		lastdelay = p;
501 	}
502 
503 	/* use last entry if none will do */
504 	*flags |= lastdelay -> d_bits;
505 }
506 
507 /*
508  * Set the hardware tabs on the terminal, using clear_all_tabs,
509  * set_tab, and column_address capabilities. Cursor_address and cursor_right
510  * may also be used, if necessary.
511  * This is done before the init_file and init_3string, so they can patch in
512  * case we blow this.
513  */
514 
515 static void
516 settabs()
517 {
518 	register int c;
519 
520 	/* Do not set tabs if they power up properly. */
521 	if (init_tabs == 8)
522 		return;
523 
524 	if (set_tab) {
525 		/* Force the cursor to be at the left margin. */
526 		if (carriage_return)
527 			putp(carriage_return);
528 		else
529 			(void) putchar('\r');
530 
531 		/* Clear any current tab settings. */
532 		if (clear_all_tabs)
533 			putp(clear_all_tabs);
534 
535 		/* Set the tabs. */
536 		for (c = 8; c < columns; c += 8) {
537 			/* Get to that column. */
538 			(void) fputs("        ", stdout);
539 
540 			/* Set the tab. */
541 			putp(set_tab);
542 		}
543 
544 		/* Get back to the left column. */
545 		if (carriage_return)
546 			putp(carriage_return);
547 		else
548 			(void) putchar('\r');
549 
550 	}
551 }
552 
553 /*
554  *  Copy "file" onto standard output.
555  */
556 
557 static void
558 cat(file)
559 char *file;				/* File to copy. */
560 {
561 	register int fd;			/* File descriptor. */
562 	register ssize_t i;			/* Number characters read. */
563 	char buf[BUFSIZ];			/* Buffer to read into. */
564 
565 	fd = open(file, O_RDONLY);
566 
567 	if (fd < 0) {
568 		perror("Cannot open initialization file");
569 	} else {
570 		while ((i = read(fd, buf, BUFSIZ)) > (ssize_t)0)
571 			(void) write(fileno(stdout), buf, (unsigned)i);
572 		(int)close(fd);
573 	}
574 }
575 
576 /*
577  *  Initialize the terminal.
578  *  Send the initialization strings to the terminal.
579  */
580 
581 static void
582 initterm()
583 {
584 	register int filedes;		/* File descriptor for ioctl's. */
585 #if defined(SYSV) || defined(USG)
586 	struct termio termmode;		/* To hold terminal settings. */
587 	struct termios termmodes;	/* To hold terminal settings. */
588 	int i;
589 	int istermios = -1;
590 #define	GTTY(fd, mode)	ioctl(fd, TCGETA, mode)
591 #define	GTTYS(fd, mode) \
592 	(istermios = ioctl(fd, TCGETS, mode))
593 #define	STTY(fd, mode)	ioctl(fd, TCSETAW, mode)
594 #define	STTYS(fd, mode)	ioctl(fd, TCSETSW, mode)
595 #define	SPEED(mode)	(mode.c_cflag & CBAUD)
596 #define	SPEEDS(mode)	(cfgetospeed(&mode))
597 #define	OFLAG(mode)	mode.c_oflag
598 #else	/* BSD */
599 	struct sgttyb termmode;		/* To hold terminal settings. */
600 #define	GTTY(fd, mode)	gtty(fd, mode)
601 #define	STTY(fd, mode)	stty(fd, mode)
602 #define	SPEED(mode)	(mode.sg_ospeed & 017)
603 #define	OFLAG(mode)	mode.sg_flags
604 #define	TAB3		XTABS
605 #endif
606 
607 	/* Get the terminal settings. */
608 	/* First try standard output, then standard error, */
609 	/* then standard input, then /dev/tty. */
610 #ifdef SYSV
611 	if ((filedes = 1, GTTYS(filedes, &termmodes) < 0) ||
612 	    (filedes = 2, GTTYS(filedes, &termmodes) < 0) ||
613 	    (filedes = 0, GTTYS(filedes, &termmodes) < 0) ||
614 	    (filedes = open("/dev/tty", O_RDWR),
615 	    GTTYS(filedes, &termmodes) < 0)) {
616 #endif	/* SYSV */
617 		if ((filedes = 1, GTTY(filedes, &termmode) == -1) ||
618 		    (filedes = 2, GTTY(filedes, &termmode) == -1) ||
619 		    (filedes = 0, GTTY(filedes, &termmode) == -1) ||
620 		    (filedes = open("/dev/tty", O_RDWR),
621 		    GTTY(filedes, &termmode) == -1)) {
622 			filedes = -1;
623 			CurrentBaudRate = speeds[B1200];
624 		} else
625 			CurrentBaudRate = speeds[SPEED(termmode)];
626 #ifdef SYSV
627 		termmodes.c_lflag = termmode.c_lflag;
628 		termmodes.c_oflag = termmode.c_oflag;
629 		termmodes.c_iflag = termmode.c_iflag;
630 		termmodes.c_cflag = termmode.c_cflag;
631 		for (i = 0; i < NCC; i++)
632 			termmodes.c_cc[i] = termmode.c_cc[i];
633 	} else
634 		CurrentBaudRate = speeds[SPEEDS(termmodes)];
635 #endif	/* SYSV */
636 
637 	if (xon_xoff) {
638 #ifdef SYSV
639 		OFLAG(termmodes) &=
640 		    ~(NLbits | CRbits | BSbits | FFbits | TBbits);
641 #else	/* SYSV */
642 		OFLAG(termmode) &=
643 		    ~(NLbits | CRbits | BSbits | FFbits | TBbits);
644 #endif	/* SYSV */
645 	} else {
646 #ifdef SYSV
647 		setdelay(getpad(carriage_return),
648 		    CRdelay, CRbits, &OFLAG(termmodes));
649 		setdelay(getpad(scroll_forward),
650 		    NLdelay, NLbits, &OFLAG(termmodes));
651 		setdelay(getpad(cursor_left),
652 		    BSdelay, BSbits, &OFLAG(termmodes));
653 		setdelay(getpad(form_feed),
654 		    FFdelay, FFbits, &OFLAG(termmodes));
655 		setdelay(getpad(tab),
656 		    TBdelay, TBbits, &OFLAG(termmodes));
657 #else	/* SYSV */
658 		setdelay(getpad(carriage_return),
659 		    CRdelay, CRbits, &OFLAG(termmode));
660 		setdelay(getpad(scroll_forward),
661 		    NLdelay, NLbits, &OFLAG(termmode));
662 		setdelay(getpad(cursor_left),
663 		    BSdelay, BSbits, &OFLAG(termmode));
664 		setdelay(getpad(form_feed),
665 		    FFdelay, FFbits, &OFLAG(termmode));
666 		setdelay(getpad(tab),
667 		    TBdelay, TBbits, &OFLAG(termmode));
668 #endif	/* SYSV */
669 	}
670 
671 	/* If tabs can be sent to the tty, turn off their expansion. */
672 	if (tab && set_tab || init_tabs == 8) {
673 #ifdef SYSV
674 		OFLAG(termmodes) &= ~(TAB3);
675 #else	/* SYSV */
676 		OFLAG(termmode) &= ~(TAB3);
677 #endif	/* SYSV */
678 	} else {
679 #ifdef SYSV
680 		OFLAG(termmodes) |= TAB3;
681 #else	/* SYSV */
682 		OFLAG(termmode) |= TAB3;
683 #endif	/* SYSV */
684 	}
685 
686 	/* Do the changes to the terminal settings */
687 #ifdef SYSV
688 	if (istermios < 0) {
689 		int i;
690 
691 		termmode.c_lflag = termmodes.c_lflag;
692 		termmode.c_oflag = termmodes.c_oflag;
693 		termmode.c_iflag = termmodes.c_iflag;
694 		termmode.c_cflag = termmodes.c_cflag;
695 		for (i = 0; i < NCC; i++)
696 			termmode.c_cc[i] = termmodes.c_cc[i];
697 		(void) STTY(filedes, &termmode);
698 	} else
699 		(void) STTYS(filedes, &termmodes);
700 
701 #else	/* SYSV */
702 	(void) STTY(filedes, &termmode);
703 #endif	/* SYSV */
704 
705 	/* Send first initialization strings. */
706 	if (init_prog)
707 	(void) system(init_prog);
708 
709 	if (reset && reset_1string) {
710 		putp(reset_1string);
711 	} else if (init_1string) {
712 		putp(init_1string);
713 	}
714 
715 	if (reset && reset_2string) {
716 		putp(reset_2string);
717 	} else if (init_2string) {
718 		putp(init_2string);
719 	}
720 
721 	/* Set up the tabs stops. */
722 	settabs();
723 
724 	/* Send out initializing file. */
725 	if (reset && reset_file) {
726 		cat(reset_file);
727 	} else if (init_file) {
728 		cat(init_file);
729 	}
730 
731 	/* Send final initialization strings. */
732 	if (reset && reset_3string) {
733 		putp(reset_3string);
734 	} else if (init_3string) {
735 		putp(init_3string);
736 	}
737 
738 	if (carriage_return) {
739 		putp(carriage_return);
740 	} else {
741 		(void) putchar('\r');
742 	}
743 
744 	/* Send color initialization strings */
745 
746 	if (orig_colors)
747 		putp(orig_colors);
748 
749 	if (orig_pair)
750 	putp(orig_pair);
751 
752 	/* Let the terminal settle down. */
753 	(void) fflush(stdout);
754 	(void) sleep(1);
755 }
756 
757 static void
758 reset_term()
759 {
760 	reset++;
761 	initterm();
762 }
763