Back to index

tetex-bin  3.0
read_termcap.c
Go to the documentation of this file.
00001 /****************************************************************************
00002  * Copyright (c) 1998-2003,2004 Free Software Foundation, Inc.              *
00003  *                                                                          *
00004  * Permission is hereby granted, free of charge, to any person obtaining a  *
00005  * copy of this software and associated documentation files (the            *
00006  * "Software"), to deal in the Software without restriction, including      *
00007  * without limitation the rights to use, copy, modify, merge, publish,      *
00008  * distribute, distribute with modifications, sublicense, and/or sell       *
00009  * copies of the Software, and to permit persons to whom the Software is    *
00010  * furnished to do so, subject to the following conditions:                 *
00011  *                                                                          *
00012  * The above copyright notice and this permission notice shall be included  *
00013  * in all copies or substantial portions of the Software.                   *
00014  *                                                                          *
00015  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS  *
00016  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF               *
00017  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.   *
00018  * IN NO EVENT SHALL THE ABOVE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,   *
00019  * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR    *
00020  * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR    *
00021  * THE USE OR OTHER DEALINGS IN THE SOFTWARE.                               *
00022  *                                                                          *
00023  * Except as contained in this notice, the name(s) of the above copyright   *
00024  * holders shall not be used in advertising or otherwise to promote the     *
00025  * sale, use or other dealings in this Software without prior written       *
00026  * authorization.                                                           *
00027  ****************************************************************************/
00028 
00029 /****************************************************************************
00030  *  Author: Zeyd M. Ben-Halim <zmbenhal@netcom.com> 1992,1995               *
00031  *     and: Eric S. Raymond <esr@snark.thyrsus.com>                         *
00032  ****************************************************************************/
00033 
00034 /*
00035  * Termcap compatibility support
00036  *
00037  * If your OS integrator didn't install a terminfo database, you can call
00038  * _nc_read_termcap_entry() to support reading and translating capabilities
00039  * from the system termcap file.  This is a kludge; it will bulk up and slow
00040  * down every program that uses ncurses, and translated termcap entries cannot
00041  * use full terminfo capabilities.  Don't use it unless you absolutely have to;
00042  * instead, get your system people to run tic(1) from root on the terminfo
00043  * master included with ncurses to translate it into a terminfo database.
00044  *
00045  * If USE_GETCAP is enabled, we use what is effectively a copy of the 4.4BSD
00046  * getcap code to fetch entries.  There are disadvantages to this; mainly that
00047  * getcap(3) does its own resolution, meaning that entries read in in this way
00048  * can't reference the terminfo tree.  The only thing it buys is faster startup
00049  * time, getcap(3) is much faster than our tic parser.
00050  */
00051 
00052 #include <curses.priv.h>
00053 
00054 #include <ctype.h>
00055 #include <sys/types.h>
00056 #include <sys/stat.h>
00057 #include <tic.h>
00058 #include <term_entry.h>
00059 
00060 MODULE_ID("$Id: read_termcap.c,v 1.66 2004/07/05 12:55:23 tom Exp $")
00061 
00062 #if !PURE_TERMINFO
00063 
00064 #if defined(__EMX__) || defined(__DJGPP__)
00065 #define is_pathname(s) ((((s) != 0) && ((s)[0] == '/')) \
00066                 || (((s)[0] != 0) && ((s)[1] == ':')))
00067 #else
00068 #define is_pathname(s) ((s) != 0 && (s)[0] == '/')
00069 #endif
00070 
00071 #define TC_SUCCESS     0
00072 #define TC_UNRESOLVED -1
00073 #define TC_NOT_FOUND  -2
00074 #define TC_SYS_ERR    -3
00075 #define TC_REF_LOOP   -4
00076 
00077 static NCURSES_CONST char *
00078 get_termpath(void)
00079 {
00080     NCURSES_CONST char *result;
00081 
00082     if (!use_terminfo_vars() || (result = getenv("TERMPATH")) == 0)
00083        result = TERMPATH;
00084     T(("TERMPATH is %s", result));
00085     return result;
00086 }
00087 
00088 #if USE_GETCAP
00089 
00090 #if HAVE_BSD_CGETENT
00091 #define _nc_cgetcap   cgetcap
00092 #define _nc_cgetent(buf, oline, db_array, name) cgetent(buf, db_array, name)
00093 #define _nc_cgetmatch cgetmatch
00094 #define _nc_cgetset   cgetset
00095 #else
00096 static int _nc_cgetmatch(char *, const char *);
00097 static int _nc_getent(char **, unsigned *, int *, int, char **, int, const char
00098                     *, int, char *);
00099 static int _nc_nfcmp(const char *, char *);
00100 
00101 /*-
00102  * Copyright (c) 1992, 1993
00103  *     The Regents of the University of California.  All rights reserved.
00104  *
00105  * This code is derived from software contributed to Berkeley by
00106  * Casey Leedom of Lawrence Livermore National Laboratory.
00107  *
00108  * Redistribution and use in source and binary forms, with or without
00109  * modification, are permitted provided that the following conditions
00110  * are met:
00111  * 1. Redistributions of source code must retain the above copyright
00112  *    notice, this list of conditions and the following disclaimer.
00113  * 2. Redistributions in binary form must reproduce the above copyright
00114  *    notice, this list of conditions and the following disclaimer in the
00115  *    documentation and/or other materials provided with the distribution.
00116  * 3. All advertising materials mentioning features or use of this software
00117  *    must display the following acknowledgment:
00118  *     This product includes software developed by the University of
00119  *     California, Berkeley and its contributors.
00120  * 4. Neither the name of the University nor the names of its contributors
00121  *    may be used to endorse or promote products derived from this software
00122  *    without specific prior written permission.
00123  *
00124  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
00125  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
00126  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
00127  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
00128  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
00129  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
00130  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
00131  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
00132  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
00133  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
00134  * SUCH DAMAGE.
00135  */
00136 
00137 /* static char sccsid[] = "@(#)getcap.c   8.3 (Berkeley) 3/25/94"; */
00138 
00139 #define       BFRAG         1024
00140 #define       BSIZE         1024
00141 #define       MAX_RECURSION 32     /* maximum getent recursion */
00142 
00143 static size_t topreclen;    /* toprec length */
00144 static char *toprec;        /* Additional record specified by cgetset() */
00145 static int gottoprec;              /* Flag indicating retrieval of toprecord */
00146 
00147 /*
00148  * Cgetset() allows the addition of a user specified buffer to be added to the
00149  * database array, in effect "pushing" the buffer on top of the virtual
00150  * database.  0 is returned on success, -1 on failure.
00151  */
00152 static int
00153 _nc_cgetset(const char *ent)
00154 {
00155     if (ent == 0) {
00156        FreeIfNeeded(toprec);
00157        toprec = 0;
00158        topreclen = 0;
00159        return (0);
00160     }
00161     topreclen = strlen(ent);
00162     if ((toprec = typeMalloc(char, topreclen + 1)) == 0) {
00163        errno = ENOMEM;
00164        return (-1);
00165     }
00166     gottoprec = 0;
00167     (void) strcpy(toprec, ent);
00168     return (0);
00169 }
00170 
00171 /*
00172  * Cgetcap searches the capability record buf for the capability cap with type
00173  * `type'.  A pointer to the value of cap is returned on success, 0 if the
00174  * requested capability couldn't be found.
00175  *
00176  * Specifying a type of ':' means that nothing should follow cap (:cap:).  In
00177  * this case a pointer to the terminating ':' or NUL will be returned if cap is
00178  * found.
00179  *
00180  * If (cap, '@') or (cap, terminator, '@') is found before (cap, terminator)
00181  * return 0.
00182  */
00183 static char *
00184 _nc_cgetcap(char *buf, const char *cap, int type)
00185 {
00186     register const char *cp;
00187     register char *bp;
00188 
00189     bp = buf;
00190     for (;;) {
00191        /*
00192         * Skip past the current capability field - it's either the
00193         * name field if this is the first time through the loop, or
00194         * the remainder of a field whose name failed to match cap.
00195         */
00196        for (;;) {
00197            if (*bp == '\0')
00198               return (0);
00199            else if (*bp++ == ':')
00200               break;
00201        }
00202 
00203        /*
00204         * Try to match (cap, type) in buf.
00205         */
00206        for (cp = cap; *cp == *bp && *bp != '\0'; cp++, bp++)
00207            continue;
00208        if (*cp != '\0')
00209            continue;
00210        if (*bp == '@')
00211            return (0);
00212        if (type == ':') {
00213            if (*bp != '\0' && *bp != ':')
00214               continue;
00215            return (bp);
00216        }
00217        if (*bp != type)
00218            continue;
00219        bp++;
00220        return (*bp == '@' ? 0 : bp);
00221     }
00222     /* NOTREACHED */
00223 }
00224 
00225 /*
00226  * Cgetent extracts the capability record name from the NULL terminated file
00227  * array db_array and returns a pointer to a malloc'd copy of it in buf.  Buf
00228  * must be retained through all subsequent calls to cgetcap, cgetnum, cgetflag,
00229  * and cgetstr, but may then be freed.
00230  *
00231  * Returns:
00232  *
00233  * positive #    on success (i.e., the index in db_array)
00234  * TC_UNRESOLVED if we had too many recurrences to resolve
00235  * TC_NOT_FOUND  if the requested record couldn't be found
00236  * TC_SYS_ERR    if a system error was encountered (e.g.,couldn't open a file)
00237  * TC_REF_LOOP   if a potential reference loop is detected
00238  */
00239 static int
00240 _nc_cgetent(char **buf, int *oline, char **db_array, const char *name)
00241 {
00242     unsigned dummy;
00243 
00244     return (_nc_getent(buf, &dummy, oline, 0, db_array, -1, name, 0, 0));
00245 }
00246 
00247 /*
00248  * Getent implements the functions of cgetent.  If fd is non-negative,
00249  * *db_array has already been opened and fd is the open file descriptor.  We
00250  * do this to save time and avoid using up file descriptors for tc=
00251  * recursions.
00252  *
00253  * Getent returns the same success/failure codes as cgetent.  On success, a
00254  * pointer to a malloc'd capability record with all tc= capabilities fully
00255  * expanded and its length (not including trailing ASCII NUL) are left in
00256  * *cap and *len.
00257  *
00258  * Basic algorithm:
00259  *     + Allocate memory incrementally as needed in chunks of size BFRAG
00260  *       for capability buffer.
00261  *     + Recurse for each tc=name and interpolate result.  Stop when all
00262  *       names interpolated, a name can't be found, or depth exceeds
00263  *       MAX_RECURSION.
00264  */
00265 #define DOALLOC(size) typeRealloc(char, size, record)
00266 static int
00267 _nc_getent(
00268              char **cap,    /* termcap-content */
00269              unsigned *len, /* length, needed for recursion */
00270              int *beginning,       /* line-number at match */
00271              int in_array,  /* index in 'db_array[] */
00272              char **db_array,      /* list of files to search */
00273              int fd,
00274              const char *name,
00275              int depth,
00276              char *nfield)
00277 {
00278     register char *r_end, *rp;
00279     int myfd = FALSE;
00280     char *record = 0;
00281     int tc_not_resolved;
00282     int current;
00283     int lineno;
00284 
00285     /*
00286      * Return with ``loop detected'' error if we've recurred more than
00287      * MAX_RECURSION times.
00288      */
00289     if (depth > MAX_RECURSION)
00290        return (TC_REF_LOOP);
00291 
00292     /*
00293      * Check if we have a top record from cgetset().
00294      */
00295     if (depth == 0 && toprec != 0 && _nc_cgetmatch(toprec, name) == 0) {
00296        if ((record = DOALLOC(topreclen + BFRAG)) == 0) {
00297            errno = ENOMEM;
00298            return (TC_SYS_ERR);
00299        }
00300        (void) strcpy(record, toprec);
00301        rp = record + topreclen + 1;
00302        r_end = rp + BFRAG;
00303        current = in_array;
00304     } else {
00305        int foundit;
00306 
00307        /*
00308         * Allocate first chunk of memory.
00309         */
00310        if ((record = DOALLOC(BFRAG)) == 0) {
00311            errno = ENOMEM;
00312            return (TC_SYS_ERR);
00313        }
00314        rp = r_end = record + BFRAG;
00315        foundit = FALSE;
00316 
00317        /*
00318         * Loop through database array until finding the record.
00319         */
00320        for (current = in_array; db_array[current] != 0; current++) {
00321            int eof = FALSE;
00322 
00323            /*
00324             * Open database if not already open.
00325             */
00326            if (fd >= 0) {
00327               (void) lseek(fd, (off_t) 0, SEEK_SET);
00328            } else if ((_nc_access(db_array[current], R_OK) < 0)
00329                      || (fd = open(db_array[current], O_RDONLY, 0)) < 0) {
00330               /* No error on unfound file. */
00331               if (errno == ENOENT)
00332                   continue;
00333               free(record);
00334               return (TC_SYS_ERR);
00335            } else {
00336               myfd = TRUE;
00337            }
00338            lineno = 0;
00339 
00340            /*
00341             * Find the requested capability record ...
00342             */
00343            {
00344               char buf[2048];
00345               register char *b_end = buf;
00346               register char *bp = buf;
00347               register int c;
00348 
00349               /*
00350                * Loop invariants:
00351                *      There is always room for one more character in record.
00352                *      R_end always points just past end of record.
00353                *      Rp always points just past last character in record.
00354                *      B_end always points just past last character in buf.
00355                *      Bp always points at next character in buf.
00356                */
00357 
00358               for (;;) {
00359                   int first = lineno + 1;
00360 
00361                   /*
00362                    * Read in a line implementing (\, newline)
00363                    * line continuation.
00364                    */
00365                   rp = record;
00366                   for (;;) {
00367                      if (bp >= b_end) {
00368                          int n;
00369 
00370                          n = read(fd, buf, sizeof(buf));
00371                          if (n <= 0) {
00372                             if (myfd)
00373                                 (void) close(fd);
00374                             if (n < 0) {
00375                                 free(record);
00376                                 return (TC_SYS_ERR);
00377                             }
00378                             fd = -1;
00379                             eof = TRUE;
00380                             break;
00381                          }
00382                          b_end = buf + n;
00383                          bp = buf;
00384                      }
00385 
00386                      c = *bp++;
00387                      if (c == '\n') {
00388                          lineno++;
00389                          if (rp == record || *(rp - 1) != '\\')
00390                             break;
00391                      }
00392                      *rp++ = c;
00393 
00394                      /*
00395                       * Enforce loop invariant: if no room
00396                       * left in record buffer, try to get
00397                       * some more.
00398                       */
00399                      if (rp >= r_end) {
00400                          unsigned pos;
00401                          size_t newsize;
00402 
00403                          pos = rp - record;
00404                          newsize = r_end - record + BFRAG;
00405                          record = DOALLOC(newsize);
00406                          if (record == 0) {
00407                             if (myfd)
00408                                 (void) close(fd);
00409                             errno = ENOMEM;
00410                             return (TC_SYS_ERR);
00411                          }
00412                          r_end = record + newsize;
00413                          rp = record + pos;
00414                      }
00415                   }
00416                   /* loop invariant lets us do this */
00417                   *rp++ = '\0';
00418 
00419                   /*
00420                    * If encountered eof check next file.
00421                    */
00422                   if (eof)
00423                      break;
00424 
00425                   /*
00426                    * Toss blank lines and comments.
00427                    */
00428                   if (*record == '\0' || *record == '#')
00429                      continue;
00430 
00431                   /*
00432                    * See if this is the record we want ...
00433                    */
00434                   if (_nc_cgetmatch(record, name) == 0
00435                      && (nfield == 0
00436                          || !_nc_nfcmp(nfield, record))) {
00437                      foundit = TRUE;
00438                      *beginning = first;
00439                      break; /* found it! */
00440                   }
00441               }
00442            }
00443            if (foundit)
00444               break;
00445        }
00446 
00447        if (!foundit)
00448            return (TC_NOT_FOUND);
00449     }
00450 
00451     /*
00452      * Got the capability record, but now we have to expand all tc=name
00453      * references in it ...
00454      */
00455     {
00456        register char *newicap, *s;
00457        register int newilen;
00458        unsigned ilen;
00459        int diff, iret, tclen, oline;
00460        char *icap, *scan, *tc, *tcstart, *tcend;
00461 
00462        /*
00463         * Loop invariants:
00464         *      There is room for one more character in record.
00465         *      R_end points just past end of record.
00466         *      Rp points just past last character in record.
00467         *      Scan points at remainder of record that needs to be
00468         *      scanned for tc=name constructs.
00469         */
00470        scan = record;
00471        tc_not_resolved = FALSE;
00472        for (;;) {
00473            if ((tc = _nc_cgetcap(scan, "tc", '=')) == 0)
00474               break;
00475 
00476            /*
00477             * Find end of tc=name and stomp on the trailing `:'
00478             * (if present) so we can use it to call ourselves.
00479             */
00480            s = tc;
00481            while (*s != '\0') {
00482               if (*s++ == ':') {
00483                   *(s - 1) = '\0';
00484                   break;
00485               }
00486            }
00487            tcstart = tc - 3;
00488            tclen = s - tcstart;
00489            tcend = s;
00490 
00491            iret = _nc_getent(&icap, &ilen, &oline, current, db_array, fd,
00492                            tc, depth + 1, 0);
00493            newicap = icap;  /* Put into a register. */
00494            newilen = ilen;
00495            if (iret != TC_SUCCESS) {
00496               /* an error */
00497               if (iret < TC_NOT_FOUND) {
00498                   if (myfd)
00499                      (void) close(fd);
00500                   free(record);
00501                   return (iret);
00502               }
00503               if (iret == TC_UNRESOLVED)
00504                   tc_not_resolved = TRUE;
00505               /* couldn't resolve tc */
00506               if (iret == TC_NOT_FOUND) {
00507                   *(s - 1) = ':';
00508                   scan = s - 1;
00509                   tc_not_resolved = TRUE;
00510                   continue;
00511               }
00512            }
00513 
00514            /* not interested in name field of tc'ed record */
00515            s = newicap;
00516            while (*s != '\0' && *s++ != ':') ;
00517            newilen -= s - newicap;
00518            newicap = s;
00519 
00520            /* make sure interpolated record is `:'-terminated */
00521            s += newilen;
00522            if (*(s - 1) != ':') {
00523               *s = ':';     /* overwrite NUL with : */
00524               newilen++;
00525            }
00526 
00527            /*
00528             * Make sure there's enough room to insert the
00529             * new record.
00530             */
00531            diff = newilen - tclen;
00532            if (diff >= r_end - rp) {
00533               unsigned pos, tcpos, tcposend;
00534               size_t newsize;
00535 
00536               pos = rp - record;
00537               newsize = r_end - record + diff + BFRAG;
00538               tcpos = tcstart - record;
00539               tcposend = tcend - record;
00540               record = DOALLOC(newsize);
00541               if (record == 0) {
00542                   if (myfd)
00543                      (void) close(fd);
00544                   free(icap);
00545                   errno = ENOMEM;
00546                   return (TC_SYS_ERR);
00547               }
00548               r_end = record + newsize;
00549               rp = record + pos;
00550               tcstart = record + tcpos;
00551               tcend = record + tcposend;
00552            }
00553 
00554            /*
00555             * Insert tc'ed record into our record.
00556             */
00557            s = tcstart + newilen;
00558            memmove(s, tcend, (size_t) (rp - tcend));
00559            memmove(tcstart, newicap, (size_t) newilen);
00560            rp += diff;
00561            free(icap);
00562 
00563            /*
00564             * Start scan on `:' so next cgetcap works properly
00565             * (cgetcap always skips first field).
00566             */
00567            scan = s - 1;
00568        }
00569     }
00570 
00571     /*
00572      * Close file (if we opened it), give back any extra memory, and
00573      * return capability, length and success.
00574      */
00575     if (myfd)
00576        (void) close(fd);
00577     *len = rp - record - 1; /* don't count NUL */
00578     if (r_end > rp) {
00579        if ((record = DOALLOC((size_t) (rp - record))) == 0) {
00580            errno = ENOMEM;
00581            return (TC_SYS_ERR);
00582        }
00583     }
00584 
00585     *cap = record;
00586     if (tc_not_resolved)
00587        return (TC_UNRESOLVED);
00588     return (current);
00589 }
00590 
00591 /*
00592  * Cgetmatch will return 0 if name is one of the names of the capability
00593  * record buf, -1 if not.
00594  */
00595 static int
00596 _nc_cgetmatch(char *buf, const char *name)
00597 {
00598     register const char *np;
00599     register char *bp;
00600 
00601     /*
00602      * Start search at beginning of record.
00603      */
00604     bp = buf;
00605     for (;;) {
00606        /*
00607         * Try to match a record name.
00608         */
00609        np = name;
00610        for (;;) {
00611            if (*np == '\0') {
00612               if (*bp == '|' || *bp == ':' || *bp == '\0')
00613                   return (0);
00614               else
00615                   break;
00616            } else if (*bp++ != *np++) {
00617               break;
00618            }
00619        }
00620 
00621        /*
00622         * Match failed, skip to next name in record.
00623         */
00624        bp--;                /* a '|' or ':' may have stopped the match */
00625        for (;;) {
00626            if (*bp == '\0' || *bp == ':')
00627               return (-1);  /* match failed totally */
00628            else if (*bp++ == '|')
00629               break;        /* found next name */
00630        }
00631     }
00632 }
00633 
00634 /*
00635  * Compare name field of record.
00636  */
00637 static int
00638 _nc_nfcmp(const char *nf, char *rec)
00639 {
00640     char *cp, tmp;
00641     int ret;
00642 
00643     for (cp = rec; *cp != ':'; cp++) ;
00644 
00645     tmp = *(cp + 1);
00646     *(cp + 1) = '\0';
00647     ret = strcmp(nf, rec);
00648     *(cp + 1) = tmp;
00649 
00650     return (ret);
00651 }
00652 #endif /* HAVE_BSD_CGETENT */
00653 
00654 /*
00655  * Since ncurses provides its own 'tgetent()', we cannot use the native one.
00656  * So we reproduce the logic to get down to cgetent() -- or our cut-down
00657  * version of that -- to circumvent the problem of configuring against the
00658  * termcap library.
00659  */
00660 #define USE_BSD_TGETENT 1
00661 
00662 #if USE_BSD_TGETENT
00663 /*
00664  * Copyright (c) 1980, 1993
00665  *     The Regents of the University of California.  All rights reserved.
00666  *
00667  * Redistribution and use in source and binary forms, with or without
00668  * modification, are permitted provided that the following conditions
00669  * are met:
00670  * 1. Redistributions of source code must retain the above copyright
00671  *    notice, this list of conditions and the following disclaimer.
00672  * 2. Redistributions in binary form must reproduce the above copyright
00673  *    notice, this list of conditions and the following disclaimer in the
00674  *    documentation and/or other materials provided with the distribution.
00675  * 3. All advertising materials mentioning features or use of this software
00676  *    must display the following acknowledgment:
00677  *     This product includes software developed by the University of
00678  *     California, Berkeley and its contributors.
00679  * 4. Neither the name of the University nor the names of its contributors
00680  *    may be used to endorse or promote products derived from this software
00681  *    without specific prior written permission.
00682  *
00683  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
00684  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
00685  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
00686  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
00687  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
00688  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
00689  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
00690  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
00691  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
00692  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
00693  * SUCH DAMAGE.
00694  */
00695 
00696 /* static char sccsid[] = "@(#)termcap.c  8.1 (Berkeley) 6/4/93" */
00697 
00698 #define       PBUFSIZ              512    /* max length of filename path */
00699 #define       PVECSIZ              32     /* max number of names in path */
00700 #define TBUFSIZ (2048*2)
00701 
00702 static char *tbuf;
00703 
00704 /*
00705  * On entry, srcp points to a non ':' character which is the beginning of the
00706  * token, if any.  We'll try to return a string that doesn't end with a ':'.
00707  */
00708 static char *
00709 get_tc_token(char **srcp, int *endp)
00710 {
00711     int ch;
00712     bool found = FALSE;
00713     char *s, *base;
00714     char *tok = 0;
00715 
00716     *endp = TRUE;
00717     for (s = base = *srcp; *s != '\0';) {
00718        ch = *s++;
00719        if (ch == '\\') {
00720            if (*s == '\0') {
00721               break;
00722            } else if (*s++ == '\n') {
00723               while (isspace(UChar(*s)))
00724                   s++;
00725            } else {
00726               found = TRUE;
00727            }
00728        } else if (ch == ':') {
00729            if (found) {
00730               tok = base;
00731               s[-1] = '\0';
00732               *srcp = s;
00733               *endp = FALSE;
00734               break;
00735            }
00736            base = s;
00737        } else if (isgraph(UChar(ch))) {
00738            found = TRUE;
00739        }
00740     }
00741 
00742     /* malformed entry may end without a ':' */
00743     if (tok == 0 && found) {
00744        tok = base;
00745     }
00746 
00747     return tok;
00748 }
00749 
00750 static char *
00751 copy_tc_token(char *dst, const char *src, size_t len)
00752 {
00753     int ch;
00754 
00755     while ((ch = *src++) != '\0') {
00756        if (ch == '\\' && *src == '\n') {
00757            while (isspace(UChar(*src)))
00758               src++;
00759            continue;
00760        }
00761        if (--len == 0) {
00762            dst = 0;
00763            break;
00764        }
00765        *dst++ = ch;
00766     }
00767     return dst;
00768 }
00769 
00770 /*
00771  * Get an entry for terminal name in buffer bp from the termcap file.
00772  */
00773 static int
00774 _nc_tgetent(char *bp, char **sourcename, int *lineno, const char *name)
00775 {
00776     static char *the_source;
00777 
00778     register char *p;
00779     register char *cp;
00780     char *dummy = NULL;
00781     char **fname;
00782     char *home;
00783     int i;
00784     char pathbuf[PBUFSIZ];  /* holds raw path of filenames */
00785     char *pathvec[PVECSIZ]; /* to point to names in pathbuf */
00786     char **pvec;            /* holds usable tail of path vector */
00787     NCURSES_CONST char *termpath;
00788     string_desc desc;
00789 
00790     fname = pathvec;
00791     pvec = pathvec;
00792     tbuf = bp;
00793     p = pathbuf;
00794     cp = use_terminfo_vars()? getenv("TERMCAP") : NULL;
00795 
00796     /*
00797      * TERMCAP can have one of two things in it.  It can be the name of a file
00798      * to use instead of /etc/termcap.  In this case it better start with a
00799      * "/".  Or it can be an entry to use so we don't have to read the file. 
00800      * In this case it has to already have the newlines crunched out.  If
00801      * TERMCAP does not hold a file name then a path of names is searched
00802      * instead.  The path is found in the TERMPATH variable, or becomes
00803      * "$HOME/.termcap /etc/termcap" if no TERMPATH exists.
00804      */
00805     _nc_str_init(&desc, pathbuf, sizeof(pathbuf));
00806     if (cp == NULL) {
00807        _nc_safe_strcpy(&desc, get_termpath());
00808     } else if (!is_pathname(cp)) { /* TERMCAP holds an entry */
00809        if ((termpath = get_termpath()) != 0) {
00810            _nc_safe_strcat(&desc, termpath);
00811        } else {
00812            char temp[PBUFSIZ];
00813            temp[0] = 0;
00814            if ((home = getenv("HOME")) != 0 && *home != '\0'
00815               && strchr(home, ' ') == 0
00816               && strlen(home) < sizeof(temp) - 10) {    /* setup path */
00817               sprintf(temp, "%s/", home); /* $HOME first */
00818            }
00819            /* if no $HOME look in current directory */
00820            strcat(temp, ".termcap");
00821            _nc_safe_strcat(&desc, temp);
00822            _nc_safe_strcat(&desc, " ");
00823            _nc_safe_strcat(&desc, get_termpath());
00824        }
00825     } else {                /* user-defined name in TERMCAP */
00826        _nc_safe_strcat(&desc, cp); /* still can be tokenized */
00827     }
00828 
00829     *fname++ = pathbuf;            /* tokenize path into vector of names */
00830     while (*++p) {
00831        if (*p == ' ' || *p == NCURSES_PATHSEP) {
00832            *p = '\0';
00833            while (*++p)
00834               if (*p != ' ' && *p != NCURSES_PATHSEP)
00835                   break;
00836            if (*p == '\0')
00837               break;
00838            *fname++ = p;
00839            if (fname >= pathvec + PVECSIZ) {
00840               fname--;
00841               break;
00842            }
00843        }
00844     }
00845     *fname = 0;                    /* mark end of vector */
00846     if (is_pathname(cp)) {
00847        if (_nc_cgetset(cp) < 0) {
00848            return (TC_SYS_ERR);
00849        }
00850     }
00851 
00852     i = _nc_cgetent(&dummy, lineno, pathvec, name);
00853 
00854     /* ncurses' termcap-parsing routines cannot handle multiple adjacent
00855      * empty fields, and mistakenly use the last valid cap entry instead of
00856      * the first (breaks tc= includes)
00857      */
00858     if (i >= 0) {
00859        char *pd, *ps, *tok;
00860        int endflag = FALSE;
00861        char *list[1023];
00862        size_t n, count = 0;
00863 
00864        pd = bp;
00865        ps = dummy;
00866        while (!endflag && (tok = get_tc_token(&ps, &endflag)) != 0) {
00867            bool ignore = FALSE;
00868 
00869            for (n = 1; n < count; n++) {
00870               char *s = list[n];
00871               if (s[0] == tok[0]
00872                   && s[1] == tok[1]) {
00873                   ignore = TRUE;
00874                   break;
00875               }
00876            }
00877            if (ignore != TRUE) {
00878               list[count++] = tok;
00879               pd = copy_tc_token(pd, tok, TBUFSIZ - (2 + pd - bp));
00880               if (pd == 0) {
00881                   i = -1;
00882                   break;
00883               }
00884               *pd++ = ':';
00885               *pd = '\0';
00886            }
00887        }
00888     }
00889 
00890     FreeIfNeeded(dummy);
00891     FreeIfNeeded(the_source);
00892     the_source = 0;
00893 
00894     /* This is not related to the BSD cgetent(), but to fake up a suitable
00895      * filename for ncurses' error reporting.  (If we are not using BSD
00896      * cgetent, then it is the actual filename).
00897      */
00898     if (i >= 0) {
00899        if ((the_source = strdup(pathvec[i])) != 0)
00900            *sourcename = the_source;
00901     }
00902 
00903     return (i);
00904 }
00905 #endif /* USE_BSD_TGETENT */
00906 #endif /* USE_GETCAP */
00907 
00908 #define MAXPATHS     32
00909 
00910 /*
00911  * Add a filename to the list in 'termpaths[]', checking that we really have
00912  * a right to open the file.
00913  */
00914 #if !USE_GETCAP
00915 static int
00916 add_tc(char *termpaths[], char *path, int count)
00917 {
00918     char *save = strchr(path, NCURSES_PATHSEP);
00919     if (save != 0)
00920        *save = '\0';
00921     if (count < MAXPATHS
00922        && _nc_access(path, R_OK) == 0) {
00923        termpaths[count++] = path;
00924        T(("Adding termpath %s", path));
00925     }
00926     termpaths[count] = 0;
00927     if (save != 0)
00928        *save = NCURSES_PATHSEP;
00929     return count;
00930 }
00931 #define ADD_TC(path, count) filecount = add_tc(termpaths, path, count)
00932 #endif /* !USE_GETCAP */
00933 
00934 NCURSES_EXPORT(int)
00935 _nc_read_termcap_entry(const char *const tn, TERMTYPE *const tp)
00936 {
00937     int found = FALSE;
00938     ENTRY *ep;
00939 #if USE_GETCAP_CACHE
00940     char cwd_buf[PATH_MAX];
00941 #endif
00942 #if USE_GETCAP
00943     char *p, tc[TBUFSIZ];
00944     static char *source;
00945     static int lineno;
00946 
00947     T(("read termcap entry for %s", tn));
00948 
00949     if (strlen(tn) == 0
00950        || strcmp(tn, ".") == 0
00951        || strcmp(tn, "..") == 0
00952        || _nc_pathlast(tn) != 0) {
00953        T(("illegal or missing entry name '%s'", tn));
00954        return 0;
00955     }
00956 
00957     if (use_terminfo_vars() && (p = getenv("TERMCAP")) != 0
00958        && !is_pathname(p) && _nc_name_match(p, tn, "|:")) {
00959        /* TERMCAP holds a termcap entry */
00960        strncpy(tc, p, sizeof(tc) - 1);
00961        tc[sizeof(tc) - 1] = '\0';
00962        _nc_set_source("TERMCAP");
00963     } else {
00964        /* we're using getcap(3) */
00965        if (_nc_tgetent(tc, &source, &lineno, tn) < 0)
00966            return (ERR);
00967 
00968        _nc_curr_line = lineno;
00969        _nc_set_source(source);
00970     }
00971     _nc_read_entry_source((FILE *) 0, tc, FALSE, FALSE, NULLHOOK);
00972 #else
00973     /*
00974      * Here is what the 4.4BSD termcap(3) page prescribes:
00975      *
00976      * It will look in the environment for a TERMCAP variable.  If found, and
00977      * the value does not begin with a slash, and the terminal type name is the
00978      * same as the environment string TERM, the TERMCAP string is used instead
00979      * of reading a termcap file.  If it does begin with a slash, the string is
00980      * used as a path name of the termcap file to search.  If TERMCAP does not
00981      * begin with a slash and name is different from TERM, tgetent() searches
00982      * the files $HOME/.termcap and /usr/share/misc/termcap, in that order,
00983      * unless the environment variable TERMPATH exists, in which case it
00984      * specifies a list of file pathnames (separated by spaces or colons) to be
00985      * searched instead.
00986      *
00987      * It goes on to state:
00988      *
00989      * Whenever multiple files are searched and a tc field occurs in the
00990      * requested entry, the entry it names must be found in the same file or
00991      * one of the succeeding files.
00992      *
00993      * However, this restriction is relaxed in ncurses; tc references to
00994      * previous files are permitted.
00995      *
00996      * This routine returns 1 if an entry is found, 0 if not found, and -1 if
00997      * the database is not accessible.
00998      */
00999     FILE *fp;
01000     char *tc, *termpaths[MAXPATHS];
01001     int filecount = 0;
01002     int j, k;
01003     bool use_buffer = FALSE;
01004     bool normal = TRUE;
01005     char tc_buf[1024];
01006     char pathbuf[PATH_MAX];
01007     char *copied = 0;
01008     char *cp;
01009     struct stat test_stat[MAXPATHS];
01010 
01011     termpaths[filecount] = 0;
01012     if (use_terminfo_vars() && (tc = getenv("TERMCAP")) != 0) {
01013        if (is_pathname(tc)) {      /* interpret as a filename */
01014            ADD_TC(tc, 0);
01015            normal = FALSE;
01016        } else if (_nc_name_match(tc, tn, "|:")) {       /* treat as a capability file */
01017            use_buffer = TRUE;
01018            (void) sprintf(tc_buf, "%.*s\n", (int) sizeof(tc_buf) - 2, tc);
01019            normal = FALSE;
01020        }
01021     }
01022 
01023     if (normal) {           /* normal case */
01024        char envhome[PATH_MAX], *h;
01025 
01026        copied = strdup(get_termpath());
01027        for (cp = copied; *cp; cp++) {
01028            if (*cp == NCURSES_PATHSEP)
01029               *cp = '\0';
01030            else if (cp == copied || cp[-1] == '\0') {
01031               ADD_TC(cp, filecount);
01032            }
01033        }
01034 
01035 #define PRIVATE_CAP "%s/.termcap"
01036 
01037        if (use_terminfo_vars() && (h = getenv("HOME")) != NULL && *h != '\0'
01038            && (strlen(h) + sizeof(PRIVATE_CAP)) < PATH_MAX) {
01039            /* user's .termcap, if any, should override it */
01040            (void) strcpy(envhome, h);
01041            (void) sprintf(pathbuf, PRIVATE_CAP, envhome);
01042            ADD_TC(pathbuf, filecount);
01043        }
01044     }
01045 
01046     /*
01047      * Probably /etc/termcap is a symlink to /usr/share/misc/termcap.
01048      * Avoid reading the same file twice.
01049      */
01050 #if HAVE_LINK
01051     for (j = 0; j < filecount; j++) {
01052        bool omit = FALSE;
01053        if (stat(termpaths[j], &test_stat[j]) != 0
01054            || (test_stat[j].st_mode & S_IFMT) != S_IFREG) {
01055            omit = TRUE;
01056        } else {
01057            for (k = 0; k < j; k++) {
01058               if (test_stat[k].st_dev == test_stat[j].st_dev
01059                   && test_stat[k].st_ino == test_stat[j].st_ino) {
01060                   omit = TRUE;
01061                   break;
01062               }
01063            }
01064        }
01065        if (omit) {
01066            T(("Path %s is a duplicate", termpaths[j]));
01067            for (k = j + 1; k < filecount; k++) {
01068               termpaths[k - 1] = termpaths[k];
01069               test_stat[k - 1] = test_stat[k];
01070            }
01071            --filecount;
01072            --j;
01073        }
01074     }
01075 #endif
01076 
01077     /* parse the sources */
01078     if (use_buffer) {
01079        _nc_set_source("TERMCAP");
01080 
01081        /*
01082         * We don't suppress warning messages here.  The presumption is
01083         * that since it's just a single entry, they won't be a pain.
01084         */
01085        _nc_read_entry_source((FILE *) 0, tc_buf, FALSE, FALSE, NULLHOOK);
01086     } else {
01087        int i;
01088 
01089        for (i = 0; i < filecount; i++) {
01090 
01091            T(("Looking for %s in %s", tn, termpaths[i]));
01092            if (_nc_access(termpaths[i], R_OK) == 0
01093               && (fp = fopen(termpaths[i], "r")) != (FILE *) 0) {
01094               _nc_set_source(termpaths[i]);
01095 
01096               /*
01097                * Suppress warning messages.  Otherwise you get 400 lines of
01098                * crap from archaic termcap files as ncurses complains about
01099                * all the obsolete capabilities.
01100                */
01101               _nc_read_entry_source(fp, (char *) 0, FALSE, TRUE, NULLHOOK);
01102 
01103               (void) fclose(fp);
01104            }
01105        }
01106     }
01107     if (copied != 0)
01108        free(copied);
01109 #endif /* USE_GETCAP */
01110 
01111     if (_nc_head == 0)
01112        return (ERR);
01113 
01114     /* resolve all use references */
01115     _nc_resolve_uses2(TRUE, FALSE);
01116 
01117     /* find a terminal matching tn, if we can */
01118 #if USE_GETCAP_CACHE
01119     if (getcwd(cwd_buf, sizeof(cwd_buf)) != 0) {
01120        _nc_set_writedir((char *) 0);      /* note: this does a chdir */
01121 #endif
01122        for_entry_list(ep) {
01123            if (_nc_name_match(ep->tterm.term_names, tn, "|:")) {
01124               /*
01125                * Make a local copy of the terminal capabilities.  Free all
01126                * entry storage except the string table for the loaded type
01127                * (which we disconnected from the list by NULLing out
01128                * ep->tterm.str_table above).
01129                */
01130               *tp = ep->tterm;
01131               ep->tterm.str_table = (char *) 0;
01132 
01133               /*
01134                * OK, now try to write the type to user's terminfo directory. 
01135                * Next time he loads this, it will come through terminfo.
01136                *
01137                * Advantage:  Second and subsequent fetches of this entry will
01138                * be very fast.
01139                *
01140                * Disadvantage:  After the first time a termcap type is loaded
01141                * by its user, editing it in the /etc/termcap file, or in
01142                * TERMCAP, or in a local ~/.termcap, will be ineffective
01143                * unless the terminfo entry is explicitly removed.
01144                */
01145 #if USE_GETCAP_CACHE
01146               (void) _nc_write_entry(tp);
01147 #endif
01148               found = TRUE;
01149               break;
01150            }
01151        }
01152 #if USE_GETCAP_CACHE
01153        chdir(cwd_buf);
01154     }
01155 #endif
01156 
01157     _nc_free_entries(_nc_head);
01158     return (found);
01159 }
01160 #else
01161 extern
01162 NCURSES_EXPORT(void)
01163 _nc_read_termcap(void);
01164 NCURSES_EXPORT(void)
01165 _nc_read_termcap(void)
01166 {
01167 }
01168 #endif /* PURE_TERMINFO */