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