1/* Work-alike for termcap, plus extra features. 2 Copyright (C) 1985, 1986, 1993, 1994, 1995, 2000, 2001, 2002, 2003, 3 2004, 2005, 2006, 2007 Free Software Foundation, Inc. 4 5This program is free software; you can redistribute it and/or modify 6it under the terms of the GNU General Public License as published by 7the Free Software Foundation; either version 2, or (at your option) 8any later version. 9 10This program is distributed in the hope that it will be useful, 11but WITHOUT ANY WARRANTY; without even the implied warranty of 12MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13GNU General Public License for more details. 14 15You should have received a copy of the GNU General Public License 16along with this program; see the file COPYING. If not, write to 17the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 18Boston, MA 02110-1301, USA. */ 19 20/* Emacs config.h may rename various library functions such as malloc. */ 21#ifdef HAVE_CONFIG_H 22#include <config.h> 23#endif 24 25#ifdef emacs 26 27#include <lisp.h> /* xmalloc is here */ 28/* Get the O_* definitions for open et al. */ 29#include <sys/file.h> 30#ifdef HAVE_FCNTL_H 31#include <fcntl.h> 32#endif 33#ifdef HAVE_UNISTD_H 34#include <unistd.h> 35#endif 36 37#else /* not emacs */ 38 39#ifdef STDC_HEADERS 40#include <stdlib.h> 41#include <string.h> 42#else 43char *getenv (); 44char *malloc (); 45char *realloc (); 46#endif 47 48/* Do this after the include, in case string.h prototypes bcopy. */ 49#if (defined(HAVE_STRING_H) || defined(STDC_HEADERS)) && !defined(bcopy) 50#define bcopy(s, d, n) memcpy ((d), (s), (n)) 51#endif 52 53#ifdef HAVE_UNISTD_H 54#include <unistd.h> 55#endif 56#ifdef HAVE_FCNTL_H 57#include <fcntl.h> 58#endif 59 60#endif /* not emacs */ 61 62#ifndef NULL 63#define NULL (char *) 0 64#endif 65 66#ifndef O_RDONLY 67#define O_RDONLY 0 68#endif 69 70/* BUFSIZE is the initial size allocated for the buffer 71 for reading the termcap file. 72 It is not a limit. 73 Make it large normally for speed. 74 Make it variable when debugging, so can exercise 75 increasing the space dynamically. */ 76 77#ifndef BUFSIZE 78#ifdef DEBUG 79#define BUFSIZE bufsize 80 81int bufsize = 128; 82#else 83#define BUFSIZE 2048 84#endif 85#endif 86 87#ifndef TERMCAP_FILE 88#define TERMCAP_FILE "/etc/termcap" 89#endif 90 91#ifndef emacs 92static void 93memory_out () 94{ 95 write (2, "virtual memory exhausted\n", 25); 96 exit (1); 97} 98 99static char * 100xmalloc (size) 101 unsigned size; 102{ 103 register char *tem = malloc (size); 104 105 if (!tem) 106 memory_out (); 107 return tem; 108} 109 110static char * 111xrealloc (ptr, size) 112 char *ptr; 113 unsigned size; 114{ 115 register char *tem = realloc (ptr, size); 116 117 if (!tem) 118 memory_out (); 119 return tem; 120} 121#endif /* not emacs */ 122 123/* Looking up capabilities in the entry already found. */ 124 125/* The pointer to the data made by tgetent is left here 126 for tgetnum, tgetflag and tgetstr to find. */ 127static char *term_entry; 128 129static char *tgetst1 (); 130 131/* Search entry BP for capability CAP. 132 Return a pointer to the capability (in BP) if found, 133 0 if not found. */ 134 135static char * 136find_capability (bp, cap) 137 register char *bp, *cap; 138{ 139 for (; *bp; bp++) 140 if (bp[0] == ':' 141 && bp[1] == cap[0] 142 && bp[2] == cap[1]) 143 return &bp[4]; 144 return NULL; 145} 146 147/* These are already defined in the System framework in Mac OS X and 148 cause prebinding to fail. */ 149#ifndef MAC_OSX 150int 151tgetnum (cap) 152 char *cap; 153{ 154 register char *ptr = find_capability (term_entry, cap); 155 if (!ptr || ptr[-1] != '#') 156 return -1; 157 return atoi (ptr); 158} 159 160int 161tgetflag (cap) 162 char *cap; 163{ 164 register char *ptr = find_capability (term_entry, cap); 165 return ptr && ptr[-1] == ':'; 166} 167 168/* Look up a string-valued capability CAP. 169 If AREA is non-null, it points to a pointer to a block in which 170 to store the string. That pointer is advanced over the space used. 171 If AREA is null, space is allocated with `malloc'. */ 172 173char * 174tgetstr (cap, area) 175 char *cap; 176 char **area; 177{ 178 register char *ptr = find_capability (term_entry, cap); 179 if (!ptr || (ptr[-1] != '=' && ptr[-1] != '~')) 180 return NULL; 181 return tgetst1 (ptr, area); 182} 183#endif /* MAC_OSX */ 184 185#ifdef IS_EBCDIC_HOST 186/* Table, indexed by a character in range 0200 to 0300 with 0200 subtracted, 187 gives meaning of character following \, or a space if no special meaning. 188 Sixteen characters per line within the string. */ 189 190static char esctab[] 191 = " \057\026 \047\014 \ 192 \025 \015 \ 193 \005 \013 \ 194 "; 195#else 196/* Table, indexed by a character in range 0100 to 0140 with 0100 subtracted, 197 gives meaning of character following \, or a space if no special meaning. 198 Eight characters per line within the string. */ 199 200static char esctab[] 201 = " \007\010 \033\014 \ 202 \012 \ 203 \015 \011 \013 \ 204 "; 205#endif 206 207/* PTR points to a string value inside a termcap entry. 208 Copy that value, processing \ and ^ abbreviations, 209 into the block that *AREA points to, 210 or to newly allocated storage if AREA is NULL. 211 Return the address to which we copied the value, 212 or NULL if PTR is NULL. */ 213 214static char * 215tgetst1 (ptr, area) 216 char *ptr; 217 char **area; 218{ 219 register char *p, *r; 220 register int c; 221 register int size; 222 char *ret; 223 register int c1; 224 225 if (!ptr) 226 return NULL; 227 228 /* `ret' gets address of where to store the string. */ 229 if (!area) 230 { 231 /* Compute size of block needed (may overestimate). */ 232 p = ptr; 233 while ((c = *p++) && c != ':' && c != '\n') 234 ; 235 ret = (char *) xmalloc (p - ptr + 1); 236 } 237 else 238 ret = *area; 239 240 /* Copy the string value, stopping at null or colon. 241 Also process ^ and \ abbreviations. */ 242 p = ptr; 243 r = ret; 244 while ((c = *p++) && c != ':' && c != '\n') 245 { 246 if (c == '^') 247 { 248 c = *p++; 249 if (c == '?') 250 c = 0177; 251 else 252 c &= 037; 253 } 254 else if (c == '\\') 255 { 256 c = *p++; 257 if (c >= '0' && c <= '7') 258 { 259 c -= '0'; 260 size = 0; 261 262 while (++size < 3 && (c1 = *p) >= '0' && c1 <= '7') 263 { 264 c *= 8; 265 c += c1 - '0'; 266 p++; 267 } 268 } 269#ifdef IS_EBCDIC_HOST 270 else if (c >= 0200 && c < 0360) 271 { 272 c1 = esctab[(c & ~0100) - 0200]; 273 if (c1 != ' ') 274 c = c1; 275 } 276#else 277 else if (c >= 0100 && c < 0200) 278 { 279 c1 = esctab[(c & ~040) - 0100]; 280 if (c1 != ' ') 281 c = c1; 282 } 283#endif 284 } 285 *r++ = c; 286 } 287 288 /* Sometimes entries have "%pN" which means use parameter N in the 289 next %-substitution. If all such N are continuous in the range 290 [1,9] we can remove each "%pN" because they are redundant, thus 291 reducing bandwidth requirements. True, Emacs is well beyond the 292 days of 150baud teletypes, but some of its users aren't much so. 293 294 This pass could probably be integrated into the one above but 295 abbreviation expansion makes that effort a little more hairy than 296 its worth; this is cleaner. */ 297 { 298 register int last_p_param = 0; 299 int remove_p_params = 1; 300 struct { char *beg; int len; } cut[11]; 301 302 for (cut[0].beg = p = ret; p < r - 3; p++) 303 { 304 if (!remove_p_params) 305 break; 306 if (*p == '%' && *(p + 1) == 'p') 307 { 308 if (*(p + 2) - '0' == 1 + last_p_param) 309 { 310 cut[last_p_param].len = p - cut[last_p_param].beg; 311 last_p_param++; 312 p += 3; 313 cut[last_p_param].beg = p; 314 } 315 else /* not continuous: bail */ 316 remove_p_params = 0; 317 if (last_p_param > 10) /* too many: bail */ 318 remove_p_params = 0; 319 } 320 } 321 if (remove_p_params && last_p_param) 322 { 323 register int i; 324 char *wp; 325 326 cut[last_p_param].len = r - cut[last_p_param].beg; 327 for (i = 0, wp = ret; i <= last_p_param; wp += cut[i++].len) 328 bcopy (cut[i].beg, wp, cut[i].len); 329 r = wp; 330 } 331 } 332 333 *r = '\0'; 334 /* Update *AREA. */ 335 if (area) 336 *area = r + 1; 337 return ret; 338} 339 340/* Outputting a string with padding. */ 341 342#ifndef emacs 343short ospeed; 344/* If OSPEED is 0, we use this as the actual baud rate. */ 345int tputs_baud_rate; 346#endif 347 348/* Already defined in the System framework in Mac OS X and causes 349 prebinding to fail. */ 350#ifndef MAC_OSX 351char PC; 352#endif /* MAC_OSX */ 353 354#ifndef emacs 355/* Actual baud rate if positive; 356 - baud rate / 100 if negative. */ 357 358static int speeds[] = 359 { 360#ifdef VMS 361 0, 50, 75, 110, 134, 150, -3, -6, -12, -18, 362 -20, -24, -36, -48, -72, -96, -192 363#else /* not VMS */ 364 0, 50, 75, 110, 135, 150, -2, -3, -6, -12, 365 -18, -24, -48, -96, -192, -288, -384, -576, -1152 366#endif /* not VMS */ 367 }; 368 369#endif /* not emacs */ 370 371/* Already defined in the System framework in Mac OS X and causes 372 prebinding to fail. */ 373#ifndef MAC_OSX 374void 375tputs (str, nlines, outfun) 376 register char *str; 377 int nlines; 378 register int (*outfun) (); 379{ 380 register int padcount = 0; 381 register int speed; 382 383#ifdef emacs 384 extern EMACS_INT baud_rate; 385 speed = baud_rate; 386 /* For quite high speeds, convert to the smaller 387 units to avoid overflow. */ 388 if (speed > 10000) 389 speed = - speed / 100; 390#else 391 if (ospeed == 0) 392 speed = tputs_baud_rate; 393 else 394 speed = speeds[ospeed]; 395#endif 396 397 if (!str) 398 return; 399 400 while (*str >= '0' && *str <= '9') 401 { 402 padcount += *str++ - '0'; 403 padcount *= 10; 404 } 405 if (*str == '.') 406 { 407 str++; 408 padcount += *str++ - '0'; 409 } 410 if (*str == '*') 411 { 412 str++; 413 padcount *= nlines; 414 } 415 while (*str) 416 (*outfun) (*str++); 417 418 /* PADCOUNT is now in units of tenths of msec. 419 SPEED is measured in characters per 10 seconds 420 or in characters per .1 seconds (if negative). 421 We use the smaller units for larger speeds to avoid overflow. */ 422 padcount *= speed; 423 padcount += 500; 424 padcount /= 1000; 425 if (speed < 0) 426 padcount = -padcount; 427 else 428 { 429 padcount += 50; 430 padcount /= 100; 431 } 432 433 while (padcount-- > 0) 434 (*outfun) (PC); 435} 436#endif /* MAC_OSX */ 437 438/* Finding the termcap entry in the termcap data base. */ 439 440struct termcap_buffer 441 { 442 char *beg; 443 int size; 444 char *ptr; 445 int ateof; 446 int full; 447 }; 448 449/* Forward declarations of static functions. */ 450 451static int scan_file (); 452static char *gobble_line (); 453static int compare_contin (); 454static int name_match (); 455 456#ifdef VMS 457 458#include <rmsdef.h> 459#include <fab.h> 460#include <nam.h> 461#include <starlet.h> 462 463static int 464valid_filename_p (fn) 465 char *fn; 466{ 467 struct FAB fab = cc$rms_fab; 468 struct NAM nam = cc$rms_nam; 469 char esa[NAM$C_MAXRSS]; 470 471 fab.fab$l_fna = fn; 472 fab.fab$b_fns = strlen(fn); 473 fab.fab$l_nam = &nam; 474 fab.fab$l_fop = FAB$M_NAM; 475 476 nam.nam$l_esa = esa; 477 nam.nam$b_ess = sizeof esa; 478 479 return SYS$PARSE(&fab, 0, 0) == RMS$_NORMAL; 480} 481 482#else /* !VMS */ 483 484#ifdef MSDOS /* MW, May 1993 */ 485static int 486valid_filename_p (fn) 487 char *fn; 488{ 489 return *fn == '/' || fn[1] == ':'; 490} 491#else 492#define valid_filename_p(fn) (*(fn) == '/') 493#endif 494 495#endif /* !VMS */ 496 497/* Find the termcap entry data for terminal type NAME 498 and store it in the block that BP points to. 499 Record its address for future use. 500 501 If BP is null, space is dynamically allocated. 502 503 Return -1 if there is some difficulty accessing the data base 504 of terminal types, 505 0 if the data base is accessible but the type NAME is not defined 506 in it, and some other value otherwise. */ 507 508/* Already defined in the System framework in Mac OS X and causes 509 prebinding to fail. */ 510#ifndef MAC_OSX 511int 512tgetent (bp, name) 513 char *bp, *name; 514{ 515 register char *termcap_name; 516 register int fd; 517 struct termcap_buffer buf; 518 register char *bp1; 519 char *tc_search_point; 520 char *term; 521 int malloc_size = 0; 522 register int c; 523 char *tcenv = NULL; /* TERMCAP value, if it contains :tc=. */ 524 char *indirect = NULL; /* Terminal type in :tc= in TERMCAP value. */ 525 int filep; 526 527#ifdef INTERNAL_TERMINAL 528 /* For the internal terminal we don't want to read any termcap file, 529 so fake it. */ 530 if (!strcmp (name, "internal")) 531 { 532 term = INTERNAL_TERMINAL; 533 if (!bp) 534 { 535 malloc_size = 1 + strlen (term); 536 bp = (char *) xmalloc (malloc_size); 537 } 538 strcpy (bp, term); 539 goto ret; 540 } 541#endif /* INTERNAL_TERMINAL */ 542 543 /* For compatibility with programs like `less' that want to 544 put data in the termcap buffer themselves as a fallback. */ 545 if (bp) 546 term_entry = bp; 547 548 termcap_name = getenv ("TERMCAP"); 549 if (termcap_name && *termcap_name == '\0') 550 termcap_name = NULL; 551#if defined (MSDOS) && !defined (TEST) 552 if (termcap_name && (*termcap_name == '\\' 553 || *termcap_name == '/' 554 || termcap_name[1] == ':')) 555 dostounix_filename(termcap_name); 556#endif 557 558 filep = termcap_name && valid_filename_p (termcap_name); 559 560 /* If termcap_name is non-null and starts with / (in the un*x case, that is), 561 it is a file name to use instead of /etc/termcap. 562 If it is non-null and does not start with /, 563 it is the entry itself, but only if 564 the name the caller requested matches the TERM variable. */ 565 566 if (termcap_name && !filep && !strcmp (name, getenv ("TERM"))) 567 { 568 indirect = tgetst1 (find_capability (termcap_name, "tc"), (char **) 0); 569 if (!indirect) 570 { 571 if (!bp) 572 bp = termcap_name; 573 else 574 strcpy (bp, termcap_name); 575 goto ret; 576 } 577 else 578 { /* It has tc=. Need to read /etc/termcap. */ 579 tcenv = termcap_name; 580 termcap_name = NULL; 581 } 582 } 583 584 if (!termcap_name || !filep) 585 termcap_name = TERMCAP_FILE; 586 587 /* Here we know we must search a file and termcap_name has its name. */ 588 589#ifdef MSDOS 590 fd = open (termcap_name, O_RDONLY|O_TEXT, 0); 591#else 592 fd = open (termcap_name, O_RDONLY, 0); 593#endif 594 if (fd < 0) 595 return -1; 596 597 buf.size = BUFSIZE; 598 /* Add 1 to size to ensure room for terminating null. */ 599 buf.beg = (char *) xmalloc (buf.size + 1); 600 term = indirect ? indirect : name; 601 602 if (!bp) 603 { 604 malloc_size = indirect ? strlen (tcenv) + 1 : buf.size; 605 bp = (char *) xmalloc (malloc_size); 606 } 607 tc_search_point = bp1 = bp; 608 609 if (indirect) 610 /* Copy the data from the environment variable. */ 611 { 612 strcpy (bp, tcenv); 613 bp1 += strlen (tcenv); 614 } 615 616 while (term) 617 { 618 /* Scan the file, reading it via buf, till find start of main entry. */ 619 if (scan_file (term, fd, &buf) == 0) 620 { 621 close (fd); 622 free (buf.beg); 623 if (malloc_size) 624 free (bp); 625 return 0; 626 } 627 628 /* Free old `term' if appropriate. */ 629 if (term != name) 630 free (term); 631 632 /* If BP is malloc'd by us, make sure it is big enough. */ 633 if (malloc_size) 634 { 635 int offset1 = bp1 - bp, offset2 = tc_search_point - bp; 636 malloc_size = offset1 + buf.size; 637 bp = termcap_name = (char *) xrealloc (bp, malloc_size); 638 bp1 = termcap_name + offset1; 639 tc_search_point = termcap_name + offset2; 640 } 641 642 /* Copy the line of the entry from buf into bp. */ 643 termcap_name = buf.ptr; 644 while ((*bp1++ = c = *termcap_name++) && c != '\n') 645 /* Drop out any \ newline sequence. */ 646 if (c == '\\' && *termcap_name == '\n') 647 { 648 bp1--; 649 termcap_name++; 650 } 651 *bp1 = '\0'; 652 653 /* Does this entry refer to another terminal type's entry? 654 If something is found, copy it into heap and null-terminate it. */ 655 tc_search_point = find_capability (tc_search_point, "tc"); 656 term = tgetst1 (tc_search_point, (char **) 0); 657 } 658 659 close (fd); 660 free (buf.beg); 661 662 if (malloc_size) 663 bp = (char *) xrealloc (bp, bp1 - bp + 1); 664 665 ret: 666 term_entry = bp; 667 return 1; 668} 669#endif /* MAC_OSX */ 670 671/* Given file open on FD and buffer BUFP, 672 scan the file from the beginning until a line is found 673 that starts the entry for terminal type STR. 674 Return 1 if successful, with that line in BUFP, 675 or 0 if no entry is found in the file. */ 676 677static int 678scan_file (str, fd, bufp) 679 char *str; 680 int fd; 681 register struct termcap_buffer *bufp; 682{ 683 register char *end; 684 685 bufp->ptr = bufp->beg; 686 bufp->full = 0; 687 bufp->ateof = 0; 688 *bufp->ptr = '\0'; 689 690 lseek (fd, 0L, 0); 691 692 while (!bufp->ateof) 693 { 694 /* Read a line into the buffer. */ 695 end = NULL; 696 do 697 { 698 /* if it is continued, append another line to it, 699 until a non-continued line ends. */ 700 end = gobble_line (fd, bufp, end); 701 } 702 while (!bufp->ateof && end[-2] == '\\'); 703 704 if (*bufp->ptr != '#' 705 && name_match (bufp->ptr, str)) 706 return 1; 707 708 /* Discard the line just processed. */ 709 bufp->ptr = end; 710 } 711 return 0; 712} 713 714/* Return nonzero if NAME is one of the names specified 715 by termcap entry LINE. */ 716 717static int 718name_match (line, name) 719 char *line, *name; 720{ 721 register char *tem; 722 723 if (!compare_contin (line, name)) 724 return 1; 725 /* This line starts an entry. Is it the right one? */ 726 for (tem = line; *tem && *tem != '\n' && *tem != ':'; tem++) 727 if (*tem == '|' && !compare_contin (tem + 1, name)) 728 return 1; 729 730 return 0; 731} 732 733static int 734compare_contin (str1, str2) 735 register char *str1, *str2; 736{ 737 register int c1, c2; 738 while (1) 739 { 740 c1 = *str1++; 741 c2 = *str2++; 742 while (c1 == '\\' && *str1 == '\n') 743 { 744 str1++; 745 while ((c1 = *str1++) == ' ' || c1 == '\t'); 746 } 747 if (c2 == '\0') 748 { 749 /* End of type being looked up. */ 750 if (c1 == '|' || c1 == ':') 751 /* If end of name in data base, we win. */ 752 return 0; 753 else 754 return 1; 755 } 756 else if (c1 != c2) 757 return 1; 758 } 759} 760 761/* Make sure that the buffer <- BUFP contains a full line 762 of the file open on FD, starting at the place BUFP->ptr 763 points to. Can read more of the file, discard stuff before 764 BUFP->ptr, or make the buffer bigger. 765 766 Return the pointer to after the newline ending the line, 767 or to the end of the file, if there is no newline to end it. 768 769 Can also merge on continuation lines. If APPEND_END is 770 non-null, it points past the newline of a line that is 771 continued; we add another line onto it and regard the whole 772 thing as one line. The caller decides when a line is continued. */ 773 774static char * 775gobble_line (fd, bufp, append_end) 776 int fd; 777 register struct termcap_buffer *bufp; 778 char *append_end; 779{ 780 register char *end; 781 register int nread; 782 register char *buf = bufp->beg; 783 register char *tem; 784 785 if (!append_end) 786 append_end = bufp->ptr; 787 788 while (1) 789 { 790 end = append_end; 791 while (*end && *end != '\n') end++; 792 if (*end) 793 break; 794 if (bufp->ateof) 795 return buf + bufp->full; 796 if (bufp->ptr == buf) 797 { 798 if (bufp->full == bufp->size) 799 { 800 bufp->size *= 2; 801 /* Add 1 to size to ensure room for terminating null. */ 802 tem = (char *) xrealloc (buf, bufp->size + 1); 803 bufp->ptr = (bufp->ptr - buf) + tem; 804 append_end = (append_end - buf) + tem; 805 bufp->beg = buf = tem; 806 } 807 } 808 else 809 { 810 append_end -= bufp->ptr - buf; 811 bcopy (bufp->ptr, buf, bufp->full -= bufp->ptr - buf); 812 bufp->ptr = buf; 813 } 814 if (!(nread = read (fd, buf + bufp->full, bufp->size - bufp->full))) 815 bufp->ateof = 1; 816 bufp->full += nread; 817 buf[bufp->full] = '\0'; 818 } 819 return end + 1; 820} 821 822#ifdef TEST 823 824#ifdef NULL 825#undef NULL 826#endif 827 828#include <stdio.h> 829 830main (argc, argv) 831 int argc; 832 char **argv; 833{ 834 char *term; 835 char *buf; 836 837 term = argv[1]; 838 printf ("TERM: %s\n", term); 839 840 buf = (char *) tgetent (0, term); 841 if ((int) buf <= 0) 842 { 843 printf ("No entry.\n"); 844 return 0; 845 } 846 847 printf ("Entry: %s\n", buf); 848 849 tprint ("cm"); 850 tprint ("AL"); 851 852 printf ("co: %d\n", tgetnum ("co")); 853 printf ("am: %d\n", tgetflag ("am")); 854} 855 856tprint (cap) 857 char *cap; 858{ 859 char *x = tgetstr (cap, 0); 860 register char *y; 861 862 printf ("%s: ", cap); 863 if (x) 864 { 865 for (y = x; *y; y++) 866 if (*y <= ' ' || *y == 0177) 867 printf ("\\%0o", *y); 868 else 869 putchar (*y); 870 free (x); 871 } 872 else 873 printf ("none"); 874 putchar ('\n'); 875} 876 877#endif /* TEST */ 878 879/* arch-tag: c2e8d427-2271-4fac-95fe-411857238b80 880 (do not change this comment) */ 881