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