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 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 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 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 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 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 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 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 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 759 reset_term() 760 { 761 reset++; 762 initterm(); 763 } 764