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, /* B19200 */
310 38400, /* B38400 */
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 1000000, /* B1000000 */
320 1152000, /* B1152000 */
321 1500000, /* B1500000 */
322 2000000, /* B2000000 */
323 2500000, /* B2500000 */
324 3000000, /* B3000000 */
325 3500000, /* B3500000 */
326 4000000, /* B4000000 */
327 0
328 };
329
330 #if defined(SYSV) || defined(USG)
331 /* Unix 3.0 on up */
332
333 /* Carriage Return delays */
334
335 static int CRbits = CRDLY;
336 static struct delay CRdelay[] =
337 {
338 0, CR0,
339 80, CR1,
340 100, CR2,
341 150, CR3,
342 -1
343 };
344
345 /* New Line delays */
346
347 static int NLbits = NLDLY;
348 static struct delay NLdelay[] =
349 {
350 0, NL0,
351 100, NL1,
352 -1
353 };
354
355 /* Back Space delays */
356
357 static int BSbits = BSDLY;
358 static struct delay BSdelay[] =
359 {
360 0, BS0,
361 50, BS1,
362 -1
363 };
364
365 /* TaB delays */
366
367 static int TBbits = TABDLY;
368 static struct delay TBdelay[] =
369 {
370 0, TAB0,
371 11, TAB1, /* special M37 delay */
372 100, TAB2,
373 /* TAB3 is XTABS and not a delay */
374 -1
375 };
376
377 /* Form Feed delays */
378
379 static int FFbits = FFDLY;
380 static struct delay FFdelay[] =
381 {
382 0, FF0,
383 2000, FF1,
384 -1
385 };
386
387 #else /* BSD */
388
389 /* Carriage Return delays */
390
391 int CRbits = CRDELAY;
392 struct delay CRdelay[] =
393 {
394 0, CR0,
395 9, CR3,
396 80, CR1,
397 160, CR2,
398 -1
399 };
400
401 /* New Line delays */
402
403 int NLbits = NLDELAY;
404 struct delay NLdelay[] =
405 {
406 0, NL0,
407 66, NL1, /* special M37 delay */
408 100, NL2,
409 -1
410 };
411
412 /* Tab delays */
413
414 int TBbits = TBDELAY;
415 struct delay TBdelay[] =
416 {
417 0, TAB0,
418 11, TAB1, /* special M37 delay */
419 -1
420 };
421
422 /* Form Feed delays */
423
424 int FFbits = VTDELAY;
425 struct delay FFdelay[] =
426 {
427 0, FF0,
428 2000, FF1,
429 -1
430 };
431 #endif /* BSD */
432
433 /*
434 * Initterm, a.k.a. reset_term, does terminal specific initialization. In
435 * particular, the init_strings from terminfo are output and tabs are
436 * set, if they aren't hardwired in. Much of this stuff was done by
437 * the tset(1) program.
438 */
439
440 /*
441 * Figure out how many milliseconds of padding the capability cap
442 * needs and return that number. Padding is stored in the string as "$<n>",
443 * where n is the number of milliseconds of padding. More than one
444 * padding string is allowed within the string, although this is unlikely.
445 */
446
447 static int
getpad(char * cap)448 getpad(char *cap)
449 {
450 int padding = 0;
451
452 /* No padding needed at speeds below padding_baud_rate */
453 if (padding_baud_rate > CurrentBaudRate || cap == NULL)
454 return (0);
455
456 while (*cap) {
457 if ((cap[0] == '$') && (cap[1] == '<')) {
458 cap++;
459 cap++;
460 padding += atoi(cap);
461 while (isdigit (*cap))
462 cap++;
463 while (*cap == '.' || *cap == '/' || *cap == '*' ||
464 isdigit(*cap))
465 cap++;
466 while (*cap == '>')
467 cap++;
468 } else {
469 cap++;
470 }
471 }
472
473 return (padding);
474 }
475
476 /*
477 * Set the appropriate delay bits in the termio structure for
478 * the given delay.
479 */
480 static void
setdelay(delay,delaytable,bits,flags)481 setdelay(delay, delaytable, bits, flags)
482 register int delay;
483 struct delay delaytable[];
484 int bits;
485 #ifdef SYSV
486 tcflag_t *flags;
487 #else /* SYSV */
488 unsigned short *flags;
489 #endif /* SYSV */
490 {
491 register struct delay *p;
492 register struct delay *lastdelay;
493
494 /* Clear out the bits, replace with new ones */
495 *flags &= ~bits;
496
497 /* Scan the delay table for first entry with adequate delay */
498 for (lastdelay = p = delaytable;
499 (p -> d_delay >= 0) && (p -> d_delay < delay);
500 p++) {
501 lastdelay = p;
502 }
503
504 /* use last entry if none will do */
505 *flags |= lastdelay -> d_bits;
506 }
507
508 /*
509 * Set the hardware tabs on the terminal, using clear_all_tabs,
510 * set_tab, and column_address capabilities. Cursor_address and cursor_right
511 * may also be used, if necessary.
512 * This is done before the init_file and init_3string, so they can patch in
513 * case we blow this.
514 */
515
516 static void
settabs()517 settabs()
518 {
519 register int c;
520
521 /* Do not set tabs if they power up properly. */
522 if (init_tabs == 8)
523 return;
524
525 if (set_tab) {
526 /* Force the cursor to be at the left margin. */
527 if (carriage_return)
528 putp(carriage_return);
529 else
530 (void) putchar('\r');
531
532 /* Clear any current tab settings. */
533 if (clear_all_tabs)
534 putp(clear_all_tabs);
535
536 /* Set the tabs. */
537 for (c = 8; c < columns; c += 8) {
538 /* Get to that column. */
539 (void) fputs(" ", stdout);
540
541 /* Set the tab. */
542 putp(set_tab);
543 }
544
545 /* Get back to the left column. */
546 if (carriage_return)
547 putp(carriage_return);
548 else
549 (void) putchar('\r');
550
551 }
552 }
553
554 /*
555 * Copy "file" onto standard output.
556 */
557
558 static void
cat(file)559 cat(file)
560 char *file; /* File to copy. */
561 {
562 register int fd; /* File descriptor. */
563 register ssize_t i; /* Number characters read. */
564 char buf[BUFSIZ]; /* Buffer to read into. */
565
566 fd = open(file, O_RDONLY);
567
568 if (fd < 0) {
569 perror("Cannot open initialization file");
570 } else {
571 while ((i = read(fd, buf, BUFSIZ)) > (ssize_t)0)
572 (void) write(fileno(stdout), buf, (unsigned)i);
573 (int)close(fd);
574 }
575 }
576
577 /*
578 * Initialize the terminal.
579 * Send the initialization strings to the terminal.
580 */
581
582 static void
initterm()583 initterm()
584 {
585 register int filedes; /* File descriptor for ioctl's. */
586 #if defined(SYSV) || defined(USG)
587 struct termio termmode; /* To hold terminal settings. */
588 struct termios termmodes; /* To hold terminal settings. */
589 int i;
590 int istermios = -1;
591 #define GTTY(fd, mode) ioctl(fd, TCGETA, mode)
592 #define GTTYS(fd, mode) \
593 (istermios = ioctl(fd, TCGETS, mode))
594 #define STTY(fd, mode) ioctl(fd, TCSETAW, mode)
595 #define STTYS(fd, mode) ioctl(fd, TCSETSW, mode)
596 #define SPEED(mode) (mode.c_cflag & CBAUD)
597 #define SPEEDS(mode) (cfgetospeed(&mode))
598 #define OFLAG(mode) mode.c_oflag
599 #else /* BSD */
600 struct sgttyb termmode; /* To hold terminal settings. */
601 #define GTTY(fd, mode) gtty(fd, mode)
602 #define STTY(fd, mode) stty(fd, mode)
603 #define SPEED(mode) (mode.sg_ospeed & 017)
604 #define OFLAG(mode) mode.sg_flags
605 #define TAB3 XTABS
606 #endif
607
608 /* Get the terminal settings. */
609 /* First try standard output, then standard error, */
610 /* then standard input, then /dev/tty. */
611 #ifdef SYSV
612 if ((filedes = 1, GTTYS(filedes, &termmodes) < 0) ||
613 (filedes = 2, GTTYS(filedes, &termmodes) < 0) ||
614 (filedes = 0, GTTYS(filedes, &termmodes) < 0) ||
615 (filedes = open("/dev/tty", O_RDWR),
616 GTTYS(filedes, &termmodes) < 0)) {
617 #endif /* SYSV */
618 if ((filedes = 1, GTTY(filedes, &termmode) == -1) ||
619 (filedes = 2, GTTY(filedes, &termmode) == -1) ||
620 (filedes = 0, GTTY(filedes, &termmode) == -1) ||
621 (filedes = open("/dev/tty", O_RDWR),
622 GTTY(filedes, &termmode) == -1)) {
623 filedes = -1;
624 CurrentBaudRate = speeds[B1200];
625 } else
626 CurrentBaudRate = speeds[SPEED(termmode)];
627 #ifdef SYSV
628 termmodes.c_lflag = termmode.c_lflag;
629 termmodes.c_oflag = termmode.c_oflag;
630 termmodes.c_iflag = termmode.c_iflag;
631 termmodes.c_cflag = termmode.c_cflag;
632 for (i = 0; i < NCC; i++)
633 termmodes.c_cc[i] = termmode.c_cc[i];
634 } else
635 CurrentBaudRate = speeds[SPEEDS(termmodes)];
636 #endif /* SYSV */
637
638 if (xon_xoff) {
639 #ifdef SYSV
640 OFLAG(termmodes) &=
641 ~(NLbits | CRbits | BSbits | FFbits | TBbits);
642 #else /* SYSV */
643 OFLAG(termmode) &=
644 ~(NLbits | CRbits | BSbits | FFbits | TBbits);
645 #endif /* SYSV */
646 } else {
647 #ifdef SYSV
648 setdelay(getpad(carriage_return),
649 CRdelay, CRbits, &OFLAG(termmodes));
650 setdelay(getpad(scroll_forward),
651 NLdelay, NLbits, &OFLAG(termmodes));
652 setdelay(getpad(cursor_left),
653 BSdelay, BSbits, &OFLAG(termmodes));
654 setdelay(getpad(form_feed),
655 FFdelay, FFbits, &OFLAG(termmodes));
656 setdelay(getpad(tab),
657 TBdelay, TBbits, &OFLAG(termmodes));
658 #else /* SYSV */
659 setdelay(getpad(carriage_return),
660 CRdelay, CRbits, &OFLAG(termmode));
661 setdelay(getpad(scroll_forward),
662 NLdelay, NLbits, &OFLAG(termmode));
663 setdelay(getpad(cursor_left),
664 BSdelay, BSbits, &OFLAG(termmode));
665 setdelay(getpad(form_feed),
666 FFdelay, FFbits, &OFLAG(termmode));
667 setdelay(getpad(tab),
668 TBdelay, TBbits, &OFLAG(termmode));
669 #endif /* SYSV */
670 }
671
672 /* If tabs can be sent to the tty, turn off their expansion. */
673 if (tab && set_tab || init_tabs == 8) {
674 #ifdef SYSV
675 OFLAG(termmodes) &= ~(TAB3);
676 #else /* SYSV */
677 OFLAG(termmode) &= ~(TAB3);
678 #endif /* SYSV */
679 } else {
680 #ifdef SYSV
681 OFLAG(termmodes) |= TAB3;
682 #else /* SYSV */
683 OFLAG(termmode) |= TAB3;
684 #endif /* SYSV */
685 }
686
687 /* Do the changes to the terminal settings */
688 #ifdef SYSV
689 if (istermios < 0) {
690 int i;
691
692 termmode.c_lflag = termmodes.c_lflag;
693 termmode.c_oflag = termmodes.c_oflag;
694 termmode.c_iflag = termmodes.c_iflag;
695 termmode.c_cflag = termmodes.c_cflag;
696 for (i = 0; i < NCC; i++)
697 termmode.c_cc[i] = termmodes.c_cc[i];
698 (void) STTY(filedes, &termmode);
699 } else
700 (void) STTYS(filedes, &termmodes);
701
702 #else /* SYSV */
703 (void) STTY(filedes, &termmode);
704 #endif /* SYSV */
705
706 /* Send first initialization strings. */
707 if (init_prog)
708 (void) system(init_prog);
709
710 if (reset && reset_1string) {
711 putp(reset_1string);
712 } else if (init_1string) {
713 putp(init_1string);
714 }
715
716 if (reset && reset_2string) {
717 putp(reset_2string);
718 } else if (init_2string) {
719 putp(init_2string);
720 }
721
722 /* Set up the tabs stops. */
723 settabs();
724
725 /* Send out initializing file. */
726 if (reset && reset_file) {
727 cat(reset_file);
728 } else if (init_file) {
729 cat(init_file);
730 }
731
732 /* Send final initialization strings. */
733 if (reset && reset_3string) {
734 putp(reset_3string);
735 } else if (init_3string) {
736 putp(init_3string);
737 }
738
739 if (carriage_return) {
740 putp(carriage_return);
741 } else {
742 (void) putchar('\r');
743 }
744
745 /* Send color initialization strings */
746
747 if (orig_colors)
748 putp(orig_colors);
749
750 if (orig_pair)
751 putp(orig_pair);
752
753 /* Let the terminal settle down. */
754 (void) fflush(stdout);
755 (void) sleep(1);
756 }
757
758 static void
reset_term()759 reset_term()
760 {
761 reset++;
762 initterm();
763 }
764