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, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * Copyright 2004 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 #pragma ident "%Z%%M% %I% %E% SMI" 32 33 34 /* 35 * tput - print terminal attribute 36 * 37 * return-codes - command line arguments: 38 * 0: ok if boolean capname -> TRUE 39 * 1: for boolean capname -> FALSE 40 * 41 * return-codes - standard input arguments: 42 * 0: ok; tput for all lines was successful 43 * 44 * return-codes - both cases: 45 * 2 usage error 46 * 3 bad terminal type given or no terminfo database 47 * 4 unknown capname 48 * -1 capname is a numeric variable that is not specified in the 49 * terminfo database(E.g. tpu -T450 lines). 50 * 51 * tput printfs a value if an INT capname was given; e.g. cols. 52 * putp's a string if a STRING capname was given; e.g. clear. and 53 * for BOOLEAN capnames, e.g. hard-copy, just returns the boolean value. 54 */ 55 56 #include <curses.h> 57 #include <term.h> 58 #include <fcntl.h> 59 #include <ctype.h> 60 #include <stdlib.h> 61 #include <string.h> 62 #include <sys/types.h> 63 #include <unistd.h> 64 #include <locale.h> 65 66 /* externs from libcurses */ 67 extern int tigetnum(); 68 69 static int outputcap(char *cap, int argc, char **argv); 70 static int allnumeric(char *string); 71 static int getpad(char *cap); 72 static void setdelay(); 73 static void settabs(); 74 static void cat(char *file); 75 static void initterm(); 76 static void reset_term(); 77 78 static char *progname; /* argv[0] */ 79 static int CurrentBaudRate; /* current baud rate */ 80 static int reset = 0; /* called as reset_term */ 81 static int fildes = 1; 82 83 int 84 main(int argc, char **argv) 85 { 86 int i, std_argc; 87 char *term = getenv("TERM"); 88 char *cap, std_input = FALSE; 89 int setuperr; 90 91 (void) setlocale(LC_ALL, ""); 92 #if !defined(TEXT_DOMAIN) 93 #define TEXT_DOMAIN "SYS_TEST" 94 #endif 95 (void) textdomain(TEXT_DOMAIN); 96 97 progname = argv[0]; 98 99 while ((i = getopt(argc, argv, "ST:")) != EOF) { 100 switch (i) { 101 case 'T': 102 fildes = -1; 103 (void) putenv("LINES="); 104 (void) putenv("COLUMNS="); 105 term = optarg; 106 break; 107 108 case 'S': 109 std_input = TRUE; 110 break; 111 112 case '?': /* FALLTHROUGH */ 113 usage: /* FALLTHROUGH */ 114 default: 115 (void) fprintf(stderr, gettext( 116 "usage:\t%s [-T [term]] capname [parm argument...]\n"), 117 progname); 118 (void) fprintf(stderr, gettext("OR:\t%s -S <<\n"), 119 progname); 120 exit(2); 121 } 122 } 123 124 if (!term || !*term) { 125 (void) fprintf(stderr, 126 gettext("%s: No value for $TERM and no -T specified\n"), 127 progname); 128 exit(2); 129 } 130 131 (void) setupterm(term, fildes, &setuperr); 132 133 switch (setuperr) { 134 case -2: 135 (void) fprintf(stderr, 136 gettext("%s: unreadable terminal descriptor \"%s\"\n"), 137 progname, term); 138 exit(3); 139 break; 140 141 case -1: 142 (void) fprintf(stderr, 143 gettext("%s: no terminfo database\n"), progname); 144 exit(3); 145 break; 146 147 case 0: 148 (void) fprintf(stderr, 149 gettext("%s: unknown terminal \"%s\"\n"), 150 progname, term); 151 exit(3); 152 } 153 154 reset_shell_mode(); 155 156 /* command line arguments */ 157 if (!std_input) { 158 if (argc == optind) 159 goto usage; 160 161 cap = argv[optind++]; 162 163 if (strcmp(cap, "init") == 0) 164 initterm(); 165 else if (strcmp(cap, "reset") == 0) 166 reset_term(); 167 else if (strcmp(cap, "longname") == 0) 168 (void) printf("%s\n", longname()); 169 else 170 exit(outputcap(cap, argc, argv)); 171 return (0); 172 } else { /* standard input argumets */ 173 char buff[128]; 174 char **v; 175 176 /* allocate storage for the 'faked' argv[] array */ 177 v = (char **)malloc(10 * sizeof (char *)); 178 for (i = 0; i < 10; i++) 179 v[i] = (char *)malloc(32 * sizeof (char)); 180 181 while (gets(buff) != NULL) { 182 /* read standard input line; skip over empty lines */ 183 if ((std_argc = 184 sscanf(buff, "%s %s %s %s %s %s %s %s %s %s", 185 v[0], v[1], v[2], v[3], v[4], v[5], v[6], v[7], 186 v[8], v[9])) < 1) { 187 continue; 188 } 189 190 cap = v[0]; 191 optind = 1; 192 193 if (strcmp(cap, "init") == 0) { 194 initterm(); 195 } else if (strcmp(cap, "reset") == 0) { 196 reset_term(); 197 } else if (strcmp(cap, "longname") == 0) { 198 (void) printf("%s\n", longname()); 199 } else { 200 (void) outputcap(cap, std_argc, v); 201 } 202 (void) fflush(stdout); 203 } 204 205 return (0); 206 } 207 } 208 209 static long parm[9] = { 210 0, 0, 0, 0, 0, 0, 0, 0, 0 211 }; 212 213 static int 214 outputcap(char *cap, int argc, char **argv) 215 { 216 int parmset = 0; 217 char *thisstr; 218 int i; 219 220 if ((i = tigetflag(cap)) >= 0) 221 return (1 - i); 222 223 if ((i = tigetnum(cap)) >= -1) { 224 (void) printf("%d\n", i); 225 return (0); 226 } 227 228 if ((thisstr = tigetstr(cap)) != (char *)-1) { 229 if (!thisstr) { 230 return (1); 231 } 232 for (parmset = 0; optind < argc; optind++, parmset++) 233 if (allnumeric(argv[optind])) 234 parm[parmset] = atoi(argv[optind]); 235 else 236 parm[parmset] = (int)argv[optind]; 237 238 if (parmset) 239 putp(tparm(thisstr, 240 parm[0], parm[1], parm[2], parm[3], parm[4], parm[5], 241 parm[6], parm[7], parm[8])); 242 else 243 putp(thisstr); 244 return (0); 245 } 246 247 (void) fprintf(stderr, 248 gettext("%s: unknown terminfo capability '%s'\n"), progname, cap); 249 250 exit(4); 251 /* NOTREACHED */ 252 } 253 254 /* 255 * The decision as to whether an argument is a number or not is to simply 256 * look at whether there are any non-digits in the string. 257 */ 258 static int 259 allnumeric(char *string) 260 { 261 if (*string) { 262 while (*string) { 263 if (!isdigit(*string++)) { 264 return (0); 265 } 266 } 267 return (1); 268 } else { 269 return (0); 270 } 271 } 272 273 /* 274 * SYSTEM DEPENDENT TERMINAL DELAY TABLES 275 * 276 * These tables maintain the correspondence between the delays 277 * defined in terminfo and the delay algorithms in the tty driver 278 * on the particular systems. For each type of delay, the bits used 279 * for that delay must be specified, in XXbits, and a table 280 * must be defined giving correspondences between delays and 281 * algorithms. Algorithms which are not fixed delays, such 282 * as dependent on current column or line number, must be 283 * kludged in some way at this time. 284 * 285 * Some of this was taken from tset(1). 286 */ 287 288 struct delay 289 { 290 int d_delay; 291 int d_bits; 292 }; 293 294 /* The appropriate speeds for various termio settings. */ 295 static int speeds[] = { 296 0, /* B0, */ 297 50, /* B50, */ 298 75, /* B75, */ 299 110, /* B110, */ 300 134, /* B134, */ 301 150, /* B150, */ 302 200, /* B200, */ 303 300, /* B300, */ 304 600, /* B600, */ 305 1200, /* B1200, */ 306 1800, /* B1800, */ 307 2400, /* B2400, */ 308 4800, /* B4800, */ 309 9600, /* B9600, */ 310 19200, /* EXTA, */ 311 38400, /* EXTB, */ 312 57600, /* B57600, */ 313 76800, /* B76800, */ 314 115200, /* B115200, */ 315 153600, /* B153600, */ 316 230400, /* B230400, */ 317 307200, /* B307200, */ 318 460800, /* B460800, */ 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), CRdelay, CRbits, &OFLAG(termmodes)); 641 setdelay(getpad(scroll_forward), NLdelay, NLbits, &OFLAG(termmodes)); 642 setdelay(getpad(cursor_left), BSdelay, BSbits, &OFLAG(termmodes)); 643 setdelay(getpad(form_feed), FFdelay, FFbits, &OFLAG(termmodes)); 644 setdelay(getpad(tab), TBdelay, TBbits, &OFLAG(termmodes)); 645 #else /* SYSV */ 646 setdelay(getpad(carriage_return), CRdelay, CRbits, &OFLAG(termmode)); 647 setdelay(getpad(scroll_forward), NLdelay, NLbits, &OFLAG(termmode)); 648 setdelay(getpad(cursor_left), BSdelay, BSbits, &OFLAG(termmode)); 649 setdelay(getpad(form_feed), FFdelay, FFbits, &OFLAG(termmode)); 650 setdelay(getpad(tab), TBdelay, TBbits, &OFLAG(termmode)); 651 #endif /* SYSV */ 652 } 653 654 /* If tabs can be sent to the tty, turn off their expansion. */ 655 if (tab && set_tab || init_tabs == 8) { 656 #ifdef SYSV 657 OFLAG(termmodes) &= ~(TAB3); 658 #else /* SYSV */ 659 OFLAG(termmode) &= ~(TAB3); 660 #endif /* SYSV */ 661 } else { 662 #ifdef SYSV 663 OFLAG(termmodes) |= TAB3; 664 #else /* SYSV */ 665 OFLAG(termmode) |= TAB3; 666 #endif /* SYSV */ 667 } 668 669 /* Do the changes to the terminal settings */ 670 #ifdef SYSV 671 if (istermios < 0) { 672 int i; 673 674 termmode.c_lflag = termmodes.c_lflag; 675 termmode.c_oflag = termmodes.c_oflag; 676 termmode.c_iflag = termmodes.c_iflag; 677 termmode.c_cflag = termmodes.c_cflag; 678 for (i = 0; i < NCC; i++) 679 termmode.c_cc[i] = termmodes.c_cc[i]; 680 (void) STTY(filedes, &termmode); 681 } else 682 (void) STTYS(filedes, &termmodes); 683 684 #else /* SYSV */ 685 (void) STTY(filedes, &termmode); 686 #endif /* SYSV */ 687 688 /* Send first initialization strings. */ 689 if (init_prog) 690 (void) system(init_prog); 691 692 if (reset && reset_1string) { 693 putp(reset_1string); 694 } else if (init_1string) { 695 putp(init_1string); 696 } 697 698 if (reset && reset_2string) { 699 putp(reset_2string); 700 } else if (init_2string) { 701 putp(init_2string); 702 } 703 704 /* Set up the tabs stops. */ 705 settabs(); 706 707 /* Send out initializing file. */ 708 if (reset && reset_file) { 709 cat(reset_file); 710 } else if (init_file) { 711 cat(init_file); 712 } 713 714 /* Send final initialization strings. */ 715 if (reset && reset_3string) { 716 putp(reset_3string); 717 } else if (init_3string) { 718 putp(init_3string); 719 } 720 721 if (carriage_return) { 722 putp(carriage_return); 723 } else { 724 (void) putchar('\r'); 725 } 726 727 /* Send color initialization strings */ 728 729 if (orig_colors) 730 putp(orig_colors); 731 732 if (orig_pair) 733 putp(orig_pair); 734 735 /* Let the terminal settle down. */ 736 (void) fflush(stdout); 737 (void) sleep(1); 738 } 739 740 static void 741 reset_term() 742 { 743 reset++; 744 initterm(); 745 } 746