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