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