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, /* 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 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 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 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 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 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 751 reset_term() 752 { 753 reset++; 754 initterm(); 755 } 756