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 } 753