1 /**************************************************************************** 2 * Copyright (c) 1998 Free Software Foundation, Inc. * 3 * * 4 * Permission is hereby granted, free of charge, to any person obtaining a * 5 * copy of this software and associated documentation files (the * 6 * "Software"), to deal in the Software without restriction, including * 7 * without limitation the rights to use, copy, modify, merge, publish, * 8 * distribute, distribute with modifications, sublicense, and/or sell * 9 * copies of the Software, and to permit persons to whom the Software is * 10 * furnished to do so, subject to the following conditions: * 11 * * 12 * The above copyright notice and this permission notice shall be included * 13 * in all copies or substantial portions of the Software. * 14 * * 15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * 16 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * 17 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * 18 * IN NO EVENT SHALL THE ABOVE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, * 19 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR * 20 * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR * 21 * THE USE OR OTHER DEALINGS IN THE SOFTWARE. * 22 * * 23 * Except as contained in this notice, the name(s) of the above copyright * 24 * holders shall not be used in advertising or otherwise to promote the * 25 * sale, use or other dealings in this Software without prior written * 26 * authorization. * 27 ****************************************************************************/ 28 29 /**************************************************************************** 30 * Author: Zeyd M. Ben-Halim <zmbenhal@netcom.com> 1992,1995 * 31 * and: Eric S. Raymond <esr@snark.thyrsus.com> * 32 ****************************************************************************/ 33 34 35 /* 36 * Termcap compatibility support 37 * 38 * If your OS integrator didn't install a terminfo database, you can call 39 * _nc_read_termcap_entry() to support reading and translating capabilities 40 * from the system termcap file. This is a kludge; it will bulk up and slow 41 * down every program that uses ncurses, and translated termcap entries cannot 42 * use full terminfo capabilities. Don't use it unless you absolutely have to; 43 * instead, get your system people to run tic(1) from root on the terminfo 44 * master included with ncurses to translate it into a terminfo database. 45 * 46 * If USE_GETCAP is enabled, we use what is effectively a copy of the 4.4BSD 47 * getcap code to fetch entries. There are disadvantages to this; mainly that 48 * getcap(3) does its own resolution, meaning that entries read in in this way 49 * can't reference the terminfo tree. The only thing it buys is faster startup 50 * time, getcap(3) is much faster than our tic parser. 51 */ 52 53 #include <curses.priv.h> 54 55 #include <ctype.h> 56 #include <tic.h> 57 #include <term_entry.h> 58 59 #if HAVE_FCNTL_H 60 #include <fcntl.h> 61 #endif 62 63 MODULE_ID("$Id: read_termcap.c,v 1.43 1999/04/10 20:52:52 tom Exp $") 64 65 #ifndef PURE_TERMINFO 66 67 #ifdef __EMX__ 68 #define is_pathname(s) ((((s) != 0) && ((s)[0] == '/')) \ 69 || (((s)[0] != 0) && ((s)[1] == ':'))) 70 #else 71 #define is_pathname(s) ((s) != 0 && (s)[0] == '/') 72 #endif 73 74 #define TC_SUCCESS 0 75 #define TC_UNRESOLVED -1 76 #define TC_NOT_FOUND -2 77 #define TC_SYS_ERR -3 78 #define TC_REF_LOOP -4 79 80 #if USE_GETCAP 81 82 #if HAVE_BSD_CGETENT 83 #define _nc_cgetcap cgetcap 84 #define _nc_cgetent(buf, oline, db_array, name) cgetent(buf, db_array, name) 85 #define _nc_cgetmatch cgetmatch 86 #define _nc_cgetset cgetset 87 #else 88 static int _nc_cgetmatch(char *, const char *); 89 static int _nc_getent(char **, unsigned int *, int *, int, char **, int, const char *, int, char *); 90 static int _nc_nfcmp(const char *, char *); 91 92 /*- 93 * Copyright (c) 1992, 1993 94 * The Regents of the University of California. All rights reserved. 95 * 96 * This code is derived from software contributed to Berkeley by 97 * Casey Leedom of Lawrence Livermore National Laboratory. 98 * 99 * Redistribution and use in source and binary forms, with or without 100 * modification, are permitted provided that the following conditions 101 * are met: 102 * 1. Redistributions of source code must retain the above copyright 103 * notice, this list of conditions and the following disclaimer. 104 * 2. Redistributions in binary form must reproduce the above copyright 105 * notice, this list of conditions and the following disclaimer in the 106 * documentation and/or other materials provided with the distribution. 107 * 3. All advertising materials mentioning features or use of this software 108 * must display the following acknowledgment: 109 * This product includes software developed by the University of 110 * California, Berkeley and its contributors. 111 * 4. Neither the name of the University nor the names of its contributors 112 * may be used to endorse or promote products derived from this software 113 * without specific prior written permission. 114 * 115 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 116 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 117 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 118 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 119 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 120 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 121 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 122 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 123 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 124 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 125 * SUCH DAMAGE. 126 */ 127 128 /* static char sccsid[] = "@(#)getcap.c 8.3 (Berkeley) 3/25/94"; */ 129 130 #define BFRAG 1024 131 #define BSIZE 1024 132 #define ESC ('[' & 037) /* ASCII ESC */ 133 #define MAX_RECURSION 32 /* maximum getent recursion */ 134 #define SFRAG 100 /* cgetstr mallocs in SFRAG chunks */ 135 136 #define RECOK (char)0 137 #define TCERR (char)1 138 #define SHADOW (char)2 139 140 static size_t topreclen; /* toprec length */ 141 static char *toprec; /* Additional record specified by cgetset() */ 142 static int gottoprec; /* Flag indicating retrieval of toprecord */ 143 144 /* 145 * Cgetset() allows the addition of a user specified buffer to be added to the 146 * database array, in effect "pushing" the buffer on top of the virtual 147 * database. 0 is returned on success, -1 on failure. 148 */ 149 static int 150 _nc_cgetset(const char *ent) 151 { 152 if (ent == 0) { 153 FreeIfNeeded(toprec); 154 toprec = 0; 155 topreclen = 0; 156 return (0); 157 } 158 topreclen = strlen(ent); 159 if ((toprec = typeMalloc(char, topreclen + 1)) == 0) { 160 errno = ENOMEM; 161 return (-1); 162 } 163 gottoprec = 0; 164 (void)strcpy(toprec, ent); 165 return (0); 166 } 167 168 /* 169 * Cgetcap searches the capability record buf for the capability cap with type 170 * `type'. A pointer to the value of cap is returned on success, 0 if the 171 * requested capability couldn't be found. 172 * 173 * Specifying a type of ':' means that nothing should follow cap (:cap:). In 174 * this case a pointer to the terminating ':' or NUL will be returned if cap is 175 * found. 176 * 177 * If (cap, '@') or (cap, terminator, '@') is found before (cap, terminator) 178 * return 0. 179 */ 180 static char * 181 _nc_cgetcap(char *buf, const char *cap, int type) 182 { 183 register const char *cp; 184 register char *bp; 185 186 bp = buf; 187 for (;;) { 188 /* 189 * Skip past the current capability field - it's either the 190 * name field if this is the first time through the loop, or 191 * the remainder of a field whose name failed to match cap. 192 */ 193 for (;;) { 194 if (*bp == '\0') 195 return (0); 196 else if (*bp++ == ':') 197 break; 198 } 199 200 /* 201 * Try to match (cap, type) in buf. 202 */ 203 for (cp = cap; *cp == *bp && *bp != '\0'; cp++, bp++) 204 continue; 205 if (*cp != '\0') 206 continue; 207 if (*bp == '@') 208 return (0); 209 if (type == ':') { 210 if (*bp != '\0' && *bp != ':') 211 continue; 212 return(bp); 213 } 214 if (*bp != type) 215 continue; 216 bp++; 217 return (*bp == '@' ? 0 : bp); 218 } 219 /* NOTREACHED */ 220 } 221 222 /* 223 * Cgetent extracts the capability record name from the NULL terminated file 224 * array db_array and returns a pointer to a malloc'd copy of it in buf. Buf 225 * must be retained through all subsequent calls to cgetcap, cgetnum, cgetflag, 226 * and cgetstr, but may then be freed. 227 * 228 * Returns: 229 * 230 * positive # on success (i.e., the index in db_array) 231 * TC_UNRESOLVED if we had too many recurrences to resolve 232 * TC_NOT_FOUND if the requested record couldn't be found 233 * TC_SYS_ERR if a system error was encountered (e.g.,couldn't open a file) 234 * TC_REF_LOOP if a potential reference loop is detected 235 */ 236 static int 237 _nc_cgetent(char **buf, int *oline, char **db_array, const char *name) 238 { 239 unsigned int dummy; 240 241 return (_nc_getent(buf, &dummy, oline, 0, db_array, -1, name, 0, 0)); 242 } 243 244 /* 245 * Getent implements the functions of cgetent. If fd is non-negative, 246 * *db_array has already been opened and fd is the open file descriptor. We 247 * do this to save time and avoid using up file descriptors for tc= 248 * recursions. 249 * 250 * Getent returns the same success/failure codes as cgetent. On success, a 251 * pointer to a malloc'd capability record with all tc= capabilities fully 252 * expanded and its length (not including trailing ASCII NUL) are left in 253 * *cap and *len. 254 * 255 * Basic algorithm: 256 * + Allocate memory incrementally as needed in chunks of size BFRAG 257 * for capability buffer. 258 * + Recurse for each tc=name and interpolate result. Stop when all 259 * names interpolated, a name can't be found, or depth exceeds 260 * MAX_RECURSION. 261 */ 262 #define DOALLOC(size) typeRealloc(char, size, record) 263 static int 264 _nc_getent( 265 char **cap, /* termcap-content */ 266 unsigned int *len, /* length, needed for recursion */ 267 int *beginning, /* line-number at match */ 268 int in_array, /* index in 'db_array[] */ 269 char **db_array, /* list of files to search */ 270 int fd, 271 const char *name, 272 int depth, 273 char *nfield) 274 { 275 register char *r_end, *rp; 276 int myfd = FALSE; 277 char *record = 0; 278 int tc_not_resolved; 279 int current; 280 int lineno; 281 282 /* 283 * Return with ``loop detected'' error if we've recurred more than 284 * MAX_RECURSION times. 285 */ 286 if (depth > MAX_RECURSION) 287 return (TC_REF_LOOP); 288 289 /* 290 * Check if we have a top record from cgetset(). 291 */ 292 if (depth == 0 && toprec != 0 && _nc_cgetmatch(toprec, name) == 0) { 293 if ((record = DOALLOC(topreclen + BFRAG)) == 0) { 294 errno = ENOMEM; 295 return (TC_SYS_ERR); 296 } 297 (void)strcpy(record, toprec); 298 rp = record + topreclen + 1; 299 r_end = rp + BFRAG; 300 current = in_array; 301 } else { 302 int foundit; 303 304 /* 305 * Allocate first chunk of memory. 306 */ 307 if ((record = DOALLOC(BFRAG)) == 0) { 308 errno = ENOMEM; 309 return (TC_SYS_ERR); 310 } 311 rp = r_end = record + BFRAG; 312 foundit = FALSE; 313 314 /* 315 * Loop through database array until finding the record. 316 */ 317 for (current = in_array; db_array[current] != 0; current++) { 318 int eof = FALSE; 319 320 /* 321 * Open database if not already open. 322 */ 323 if (fd >= 0) { 324 (void)lseek(fd, (off_t)0, SEEK_SET); 325 } else if ((_nc_access(db_array[current], R_OK) < 0) 326 || (fd = open(db_array[current], O_RDONLY, 0)) < 0) { 327 /* No error on unfound file. */ 328 if (errno == ENOENT) 329 continue; 330 free(record); 331 return (TC_SYS_ERR); 332 } else { 333 myfd = TRUE; 334 } 335 lineno = 0; 336 337 /* 338 * Find the requested capability record ... 339 */ 340 { 341 char buf[2048]; 342 register char *b_end = buf; 343 register char *bp = buf; 344 register int c; 345 346 /* 347 * Loop invariants: 348 * There is always room for one more character in record. 349 * R_end always points just past end of record. 350 * Rp always points just past last character in record. 351 * B_end always points just past last character in buf. 352 * Bp always points at next character in buf. 353 */ 354 355 for (;;) { 356 int first = lineno + 1; 357 358 /* 359 * Read in a line implementing (\, newline) 360 * line continuation. 361 */ 362 rp = record; 363 for (;;) { 364 if (bp >= b_end) { 365 int n; 366 367 n = read(fd, buf, sizeof(buf)); 368 if (n <= 0) { 369 if (myfd) 370 (void)close(fd); 371 if (n < 0) { 372 free(record); 373 return (TC_SYS_ERR); 374 } 375 fd = -1; 376 eof = TRUE; 377 break; 378 } 379 b_end = buf+n; 380 bp = buf; 381 } 382 383 c = *bp++; 384 if (c == '\n') { 385 lineno++; 386 if (rp == record || *(rp-1) != '\\') 387 break; 388 } 389 *rp++ = c; 390 391 /* 392 * Enforce loop invariant: if no room 393 * left in record buffer, try to get 394 * some more. 395 */ 396 if (rp >= r_end) { 397 unsigned int pos; 398 size_t newsize; 399 400 pos = rp - record; 401 newsize = r_end - record + BFRAG; 402 record = DOALLOC(newsize); 403 if (record == 0) { 404 if (myfd) 405 (void)close(fd); 406 errno = ENOMEM; 407 return (TC_SYS_ERR); 408 } 409 r_end = record + newsize; 410 rp = record + pos; 411 } 412 } 413 /* loop invariant lets us do this */ 414 *rp++ = '\0'; 415 416 /* 417 * If encountered eof check next file. 418 */ 419 if (eof) 420 break; 421 422 /* 423 * Toss blank lines and comments. 424 */ 425 if (*record == '\0' || *record == '#') 426 continue; 427 428 /* 429 * See if this is the record we want ... 430 */ 431 if (_nc_cgetmatch(record, name) == 0 432 && (nfield == 0 433 || !_nc_nfcmp(nfield, record))) { 434 foundit = TRUE; 435 *beginning = first; 436 break; /* found it! */ 437 } 438 } 439 } 440 if (foundit) 441 break; 442 } 443 444 if (!foundit) 445 return (TC_NOT_FOUND); 446 } 447 448 /* 449 * Got the capability record, but now we have to expand all tc=name 450 * references in it ... 451 */ 452 { 453 register char *newicap, *s; 454 register int newilen; 455 unsigned int ilen; 456 int diff, iret, tclen, oline; 457 char *icap, *scan, *tc, *tcstart, *tcend; 458 459 /* 460 * Loop invariants: 461 * There is room for one more character in record. 462 * R_end points just past end of record. 463 * Rp points just past last character in record. 464 * Scan points at remainder of record that needs to be 465 * scanned for tc=name constructs. 466 */ 467 scan = record; 468 tc_not_resolved = FALSE; 469 for (;;) { 470 if ((tc = _nc_cgetcap(scan, "tc", '=')) == 0) 471 break; 472 473 /* 474 * Find end of tc=name and stomp on the trailing `:' 475 * (if present) so we can use it to call ourselves. 476 */ 477 s = tc; 478 while (*s != '\0') { 479 if (*s++ == ':') { 480 *(s - 1) = '\0'; 481 break; 482 } 483 } 484 tcstart = tc - 3; 485 tclen = s - tcstart; 486 tcend = s; 487 488 iret = _nc_getent(&icap, &ilen, &oline, current, db_array, fd, tc, depth+1, 0); 489 newicap = icap; /* Put into a register. */ 490 newilen = ilen; 491 if (iret != TC_SUCCESS) { 492 /* an error */ 493 if (iret < TC_NOT_FOUND) { 494 if (myfd) 495 (void)close(fd); 496 free(record); 497 return (iret); 498 } 499 if (iret == TC_UNRESOLVED) 500 tc_not_resolved = TRUE; 501 /* couldn't resolve tc */ 502 if (iret == TC_NOT_FOUND) { 503 *(s - 1) = ':'; 504 scan = s - 1; 505 tc_not_resolved = TRUE; 506 continue; 507 } 508 } 509 510 /* not interested in name field of tc'ed record */ 511 s = newicap; 512 while (*s != '\0' && *s++ != ':') 513 ; 514 newilen -= s - newicap; 515 newicap = s; 516 517 /* make sure interpolated record is `:'-terminated */ 518 s += newilen; 519 if (*(s-1) != ':') { 520 *s = ':'; /* overwrite NUL with : */ 521 newilen++; 522 } 523 524 /* 525 * Make sure there's enough room to insert the 526 * new record. 527 */ 528 diff = newilen - tclen; 529 if (diff >= r_end - rp) { 530 unsigned int pos, tcpos, tcposend; 531 size_t newsize; 532 533 pos = rp - record; 534 newsize = r_end - record + diff + BFRAG; 535 tcpos = tcstart - record; 536 tcposend = tcend - record; 537 record = DOALLOC(newsize); 538 if (record == 0) { 539 if (myfd) 540 (void)close(fd); 541 free(icap); 542 errno = ENOMEM; 543 return (TC_SYS_ERR); 544 } 545 r_end = record + newsize; 546 rp = record + pos; 547 tcstart = record + tcpos; 548 tcend = record + tcposend; 549 } 550 551 /* 552 * Insert tc'ed record into our record. 553 */ 554 s = tcstart + newilen; 555 memmove(s, tcend, (size_t)(rp - tcend)); 556 memmove(tcstart, newicap, (size_t)newilen); 557 rp += diff; 558 free(icap); 559 560 /* 561 * Start scan on `:' so next cgetcap works properly 562 * (cgetcap always skips first field). 563 */ 564 scan = s-1; 565 } 566 } 567 568 /* 569 * Close file (if we opened it), give back any extra memory, and 570 * return capability, length and success. 571 */ 572 if (myfd) 573 (void)close(fd); 574 *len = rp - record - 1; /* don't count NUL */ 575 if (r_end > rp) { 576 if ((record = DOALLOC((size_t)(rp - record))) == 0) { 577 errno = ENOMEM; 578 return (TC_SYS_ERR); 579 } 580 } 581 582 *cap = record; 583 if (tc_not_resolved) 584 return (TC_UNRESOLVED); 585 return (current); 586 } 587 588 /* 589 * Cgetmatch will return 0 if name is one of the names of the capability 590 * record buf, -1 if not. 591 */ 592 static int 593 _nc_cgetmatch(char *buf, const char *name) 594 { 595 register const char *np; 596 register char *bp; 597 598 /* 599 * Start search at beginning of record. 600 */ 601 bp = buf; 602 for (;;) { 603 /* 604 * Try to match a record name. 605 */ 606 np = name; 607 for (;;) { 608 if (*np == '\0') { 609 if (*bp == '|' || *bp == ':' || *bp == '\0') 610 return (0); 611 else 612 break; 613 } else if (*bp++ != *np++) { 614 break; 615 } 616 } 617 618 /* 619 * Match failed, skip to next name in record. 620 */ 621 bp--; /* a '|' or ':' may have stopped the match */ 622 for (;;) { 623 if (*bp == '\0' || *bp == ':') 624 return (-1); /* match failed totally */ 625 else if (*bp++ == '|') 626 break; /* found next name */ 627 } 628 } 629 } 630 631 /* 632 * Compare name field of record. 633 */ 634 static int 635 _nc_nfcmp(const char *nf, char *rec) 636 { 637 char *cp, tmp; 638 int ret; 639 640 for (cp = rec; *cp != ':'; cp++) 641 ; 642 643 tmp = *(cp + 1); 644 *(cp + 1) = '\0'; 645 ret = strcmp(nf, rec); 646 *(cp + 1) = tmp; 647 648 return (ret); 649 } 650 #endif /* HAVE_BSD_CGETENT */ 651 652 /* 653 * Since ncurses provides its own 'tgetent()', we cannot use the native one. 654 * So we reproduce the logic to get down to cgetent() -- or our cut-down 655 * version of that -- to circumvent the problem of configuring against the 656 * termcap library. 657 */ 658 #define USE_BSD_TGETENT 1 659 660 #if USE_BSD_TGETENT 661 /* 662 * Copyright (c) 1980, 1993 663 * The Regents of the University of California. All rights reserved. 664 * 665 * Redistribution and use in source and binary forms, with or without 666 * modification, are permitted provided that the following conditions 667 * are met: 668 * 1. Redistributions of source code must retain the above copyright 669 * notice, this list of conditions and the following disclaimer. 670 * 2. Redistributions in binary form must reproduce the above copyright 671 * notice, this list of conditions and the following disclaimer in the 672 * documentation and/or other materials provided with the distribution. 673 * 3. All advertising materials mentioning features or use of this software 674 * must display the following acknowledgment: 675 * This product includes software developed by the University of 676 * California, Berkeley and its contributors. 677 * 4. Neither the name of the University nor the names of its contributors 678 * may be used to endorse or promote products derived from this software 679 * without specific prior written permission. 680 * 681 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 682 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 683 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 684 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 685 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 686 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 687 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 688 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 689 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 690 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 691 * SUCH DAMAGE. 692 */ 693 694 /* static char sccsid[] = "@(#)termcap.c 8.1 (Berkeley) 6/4/93" */ 695 696 #define PBUFSIZ 512 /* max length of filename path */ 697 #define PVECSIZ 32 /* max number of names in path */ 698 #define TBUFSIZ (2048*2) 699 700 static char *tbuf; 701 702 /* 703 * On entry, srcp points to a non ':' character which is the beginning of the 704 * token, if any. We'll try to return a string that doesn't end with a ':'. 705 */ 706 static char * 707 get_tc_token(char **srcp, int *endp) 708 { 709 int ch; 710 bool found = FALSE; 711 char *s, *base; 712 char *tok = 0; 713 714 *endp = TRUE; 715 for (s = base = *srcp; *s != '\0'; ) { 716 ch = *s++; 717 if (ch == '\\') { 718 if (*s == '\0') { 719 break; 720 } else if (*s++ == '\n') { 721 while (isspace(*s)) 722 s++; 723 } else { 724 found = TRUE; 725 } 726 } else if (ch == ':') { 727 if (found) { 728 tok = base; 729 s[-1] = '\0'; 730 *srcp = s; 731 *endp = FALSE; 732 break; 733 } 734 base = s; 735 } else if (isgraph(ch)) { 736 found = TRUE; 737 } 738 } 739 740 /* malformed entry may end without a ':' */ 741 if (tok == 0 && found) { 742 tok = base; 743 } 744 745 return tok; 746 } 747 748 static char * 749 copy_tc_token(char *dst, const char *src, size_t len) 750 { 751 int ch; 752 753 while ((ch = *src++) != '\0') { 754 if (ch == '\\' && *src == '\n') { 755 while (isspace(*src)) 756 src++; 757 continue; 758 } 759 if (--len == 0) { 760 dst = 0; 761 break; 762 } 763 *dst++ = ch; 764 } 765 return dst; 766 } 767 768 /* 769 * Get an entry for terminal name in buffer bp from the termcap file. 770 */ 771 static int 772 _nc_tgetent(char *bp, char **sourcename, int *lineno, const char *name) 773 { 774 static char *the_source; 775 776 register char *p; 777 register char *cp; 778 char *dummy; 779 char **fname; 780 char *home; 781 int i; 782 char pathbuf[PBUFSIZ]; /* holds raw path of filenames */ 783 char *pathvec[PVECSIZ]; /* to point to names in pathbuf */ 784 char **pvec; /* holds usable tail of path vector */ 785 char *termpath; 786 787 fname = pathvec; 788 pvec = pathvec; 789 tbuf = bp; 790 p = pathbuf; 791 cp = getenv("TERMCAP"); 792 793 /* 794 * TERMCAP can have one of two things in it. It can be the name of a 795 * file to use instead of /etc/termcap. In this case it better start 796 * with a "/". Or it can be an entry to use so we don't have to read 797 * the file. In this case it has to already have the newlines crunched 798 * out. If TERMCAP does not hold a file name then a path of names is 799 * searched instead. The path is found in the TERMPATH variable, or 800 * becomes "$HOME/.termcap /etc/termcap" if no TERMPATH exists. 801 */ 802 if (!is_pathname(cp)) { /* no TERMCAP or it holds an entry */ 803 if ((termpath = getenv("TERMPATH")) != 0) { 804 strncpy(pathbuf, termpath, PBUFSIZ - 1); 805 } else { 806 if ((home = getenv("HOME")) != 0 && 807 strlen(home) < PBUFSIZ) { /* setup path */ 808 p += strlen(home); /* path, looking in */ 809 strcpy(pathbuf, home); /* $HOME first */ 810 *p++ = '/'; 811 } /* if no $HOME look in current directory */ 812 #define MY_PATH_DEF ".termcap /etc/termcap /usr/share/misc/termcap" 813 strncpy(p, MY_PATH_DEF, (size_t)(PBUFSIZ - (p - pathbuf) - 1)); 814 } 815 } 816 else /* user-defined name in TERMCAP */ 817 strncpy(pathbuf, cp, PBUFSIZ - 1); /* still can be tokenized */ 818 pathbuf[PBUFSIZ - 1] = '\0'; 819 820 *fname++ = pathbuf; /* tokenize path into vector of names */ 821 while (*++p) { 822 if (*p == ' ' || *p == ':') { 823 *p = '\0'; 824 while (*++p) 825 if (*p != ' ' && *p != ':') 826 break; 827 if (*p == '\0') 828 break; 829 *fname++ = p; 830 if (fname >= pathvec + PVECSIZ) { 831 fname--; 832 break; 833 } 834 } 835 } 836 *fname = 0; /* mark end of vector */ 837 if (is_pathname(cp)) { 838 if (_nc_cgetset(cp) < 0) { 839 return(TC_SYS_ERR); 840 } 841 } 842 843 i = _nc_cgetent(&dummy, lineno, pathvec, name); 844 845 /* ncurses' termcap-parsing routines cannot handle multiple adjacent 846 * empty fields, and mistakenly use the last valid cap entry instead of 847 * the first (breaks tc= includes) 848 */ 849 if (i >= 0) { 850 char *pd, *ps, *tok; 851 int endflag = FALSE; 852 char *list[1023]; 853 size_t n, count = 0; 854 855 pd = bp; 856 ps = dummy; 857 while (!endflag && (tok = get_tc_token(&ps, &endflag)) != 0) { 858 bool ignore = FALSE; 859 860 for (n = 1; n < count; n++) { 861 char *s = list[n]; 862 if (s[0] == tok[0] 863 && s[1] == tok[1]) { 864 ignore = TRUE; 865 break; 866 } 867 } 868 if (ignore != TRUE) { 869 list[count++] = tok; 870 pd = copy_tc_token(pd, tok, TBUFSIZ - (2+pd-bp)); 871 if (pd == 0) { 872 i = -1; 873 break; 874 } 875 *pd++ = ':'; 876 *pd = '\0'; 877 } 878 } 879 } 880 881 FreeIfNeeded(dummy); 882 FreeIfNeeded(the_source); 883 the_source = 0; 884 885 /* This is not related to the BSD cgetent(), but to fake up a suitable 886 * filename for ncurses' error reporting. (If we are not using BSD 887 * cgetent, then it is the actual filename). 888 */ 889 if (i >= 0) { 890 if ((the_source = strdup(pathvec[i])) != 0) 891 *sourcename = the_source; 892 } 893 894 return(i); 895 } 896 #endif /* USE_BSD_TGETENT */ 897 #endif /* USE_GETCAP */ 898 899 #define MAXPATHS 32 900 901 /* 902 * Add a filename to the list in 'termpaths[]', checking that we really have 903 * a right to open the file. 904 */ 905 #if !USE_GETCAP 906 static int add_tc(char *termpaths[], char *path, int count) 907 { 908 if (count < MAXPATHS 909 && _nc_access(path, R_OK) == 0) 910 termpaths[count++] = path; 911 termpaths[count] = 0; 912 return count; 913 } 914 #define ADD_TC(path, count) filecount = add_tc(termpaths, path, count) 915 #endif /* !USE_GETCAP */ 916 917 int _nc_read_termcap_entry(const char *const tn, TERMTYPE *const tp) 918 { 919 int found = FALSE; 920 ENTRY *ep; 921 #if USE_GETCAP_CACHE 922 char cwd_buf[PATH_MAX]; 923 #endif 924 #if USE_GETCAP 925 char tc[TBUFSIZ]; 926 static char *source; 927 static int lineno; 928 929 /* we're using getcap(3) */ 930 if (_nc_tgetent(tc, &source, &lineno, tn) < 0) 931 return (ERR); 932 933 _nc_curr_line = lineno; 934 _nc_set_source(source); 935 _nc_read_entry_source((FILE *)0, tc, FALSE, FALSE, NULLHOOK); 936 #else 937 /* 938 * Here is what the 4.4BSD termcap(3) page prescribes: 939 * 940 * It will look in the environment for a TERMCAP variable. If found, 941 * and the value does not begin with a slash, and the terminal type 942 * name is the same as the environment string TERM, the TERMCAP string 943 * is used instead of reading a termcap file. If it does begin with a 944 * slash, the string is used as a path name of the termcap file to 945 * search. If TERMCAP does not begin with a slash and name is 946 * different from TERM, tgetent() searches the files $HOME/.termcap and 947 * /usr/share/misc/termcap, in that order, unless the environment 948 * variable TERMPATH exists, in which case it specifies a list of file 949 * pathnames (separated by spaces or colons) to be searched instead. 950 * 951 * It goes on to state: 952 * 953 * Whenever multiple files are searched and a tc field occurs in the 954 * requested entry, the entry it names must be found in the same file 955 * or one of the succeeding files. 956 * 957 * However, this restriction is relaxed in ncurses; tc references to 958 * previous files are permitted. 959 * 960 * This routine returns 1 if an entry is found, 0 if not found, and -1 961 * if the database is not accessible. 962 */ 963 FILE *fp; 964 char *tc, *termpaths[MAXPATHS]; 965 int filecount = 0; 966 bool use_buffer = FALSE; 967 char tc_buf[1024]; 968 char pathbuf[PATH_MAX]; 969 970 termpaths[filecount] = 0; 971 if ((tc = getenv("TERMCAP")) != 0) 972 { 973 if (is_pathname(tc)) /* interpret as a filename */ 974 { 975 ADD_TC(tc, 0); 976 } 977 else if (_nc_name_match(tc, tn, "|:")) /* treat as a capability file */ 978 { 979 use_buffer = TRUE; 980 (void) sprintf(tc_buf, "%.*s\n", (int)sizeof(tc_buf)-2, tc); 981 } 982 else if ((tc = getenv("TERMPATH")) != 0) 983 { 984 char *cp; 985 986 for (cp = tc; *cp; cp++) 987 { 988 if (*cp == ':') 989 *cp = '\0'; 990 else if (cp == tc || cp[-1] == '\0') 991 { 992 ADD_TC(cp, filecount); 993 } 994 } 995 } 996 } 997 else /* normal case */ 998 { 999 char envhome[PATH_MAX], *h; 1000 1001 filecount = 0; 1002 1003 /* 1004 * Probably /etc/termcap is a symlink to /usr/share/misc/termcap. 1005 * Avoid reading the same file twice. 1006 */ 1007 if (_nc_access("/etc/termcap", F_OK) == 0) 1008 ADD_TC("/etc/termcap", filecount); 1009 else 1010 ADD_TC("/usr/share/misc/termcap", filecount); 1011 1012 #define PRIVATE_CAP "%s/.termcap" 1013 1014 if ((h = getenv("HOME")) != NULL 1015 && (strlen(h) + sizeof(PRIVATE_CAP)) < PATH_MAX) 1016 { 1017 /* user's .termcap, if any, should override it */ 1018 (void) strcpy(envhome, h); 1019 (void) sprintf(pathbuf, PRIVATE_CAP, envhome); 1020 ADD_TC(pathbuf, filecount); 1021 } 1022 } 1023 1024 /* parse the sources */ 1025 if (use_buffer) 1026 { 1027 _nc_set_source("TERMCAP"); 1028 1029 /* 1030 * We don't suppress warning messages here. The presumption is 1031 * that since it's just a single entry, they won't be a pain. 1032 */ 1033 _nc_read_entry_source((FILE *)0, tc_buf, FALSE, FALSE, NULLHOOK); 1034 } else { 1035 int i; 1036 1037 for (i = 0; i < filecount; i++) { 1038 1039 T(("Looking for %s in %s", tn, termpaths[i])); 1040 if ((fp = fopen(termpaths[i], "r")) != (FILE *)0) 1041 { 1042 _nc_set_source(termpaths[i]); 1043 1044 /* 1045 * Suppress warning messages. Otherwise you 1046 * get 400 lines of crap from archaic termcap 1047 * files as ncurses complains about all the 1048 * obsolete capabilities. 1049 */ 1050 _nc_read_entry_source(fp, (char*)0, FALSE, TRUE, NULLHOOK); 1051 1052 (void) fclose(fp); 1053 } 1054 } 1055 } 1056 #endif /* USE_GETCAP */ 1057 1058 if (_nc_head == 0) 1059 return(ERR); 1060 1061 /* resolve all use references */ 1062 _nc_resolve_uses(); 1063 1064 /* find a terminal matching tn, if we can */ 1065 #if USE_GETCAP_CACHE 1066 if (getcwd(cwd_buf, sizeof(cwd_buf)) != 0) 1067 { 1068 _nc_set_writedir((char *)0); /* note: this does a chdir */ 1069 #endif 1070 for_entry_list(ep) { 1071 if (_nc_name_match(ep->tterm.term_names, tn, "|:")) 1072 { 1073 /* 1074 * Make a local copy of the terminal 1075 * capabilities. Free all entry storage except 1076 * the string table for the loaded type (which 1077 * we disconnected from the list by NULLing out 1078 * ep->tterm.str_table above). 1079 */ 1080 *tp = ep->tterm; 1081 ep->tterm.str_table = (char *)0; 1082 1083 /* 1084 * OK, now try to write the type to user's 1085 * terminfo directory. Next time he loads 1086 * this, it will come through terminfo. 1087 * 1088 * Advantage: Second and subsequent fetches of 1089 * this entry will be very fast. 1090 * 1091 * Disadvantage: After the first time a 1092 * termcap type is loaded by its user, editing 1093 * it in the /etc/termcap file, or in TERMCAP, 1094 * or in a local ~/.termcap, will be 1095 * ineffective unless the terminfo entry is 1096 * explicitly removed. 1097 */ 1098 #if USE_GETCAP_CACHE 1099 (void) _nc_write_entry(tp); 1100 #endif 1101 found = TRUE; 1102 break; 1103 } 1104 } 1105 #if USE_GETCAP_CACHE 1106 chdir(cwd_buf); 1107 } 1108 #endif 1109 1110 _nc_free_entries(_nc_head); 1111 return(found); 1112 } 1113 #else 1114 extern void _nc_read_termcap(void); 1115 void _nc_read_termcap(void) { } 1116 #endif /* PURE_TERMINFO */ 1117