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