Logo Search packages:      
Sourcecode: texfam version File versions  Download package

read_termcap.c

/****************************************************************************
 * Copyright (c) 1998 Free Software Foundation, Inc.                        *
 *                                                                          *
 * Permission is hereby granted, free of charge, to any person obtaining a  *
 * copy of this software and associated documentation files (the            *
 * "Software"), to deal in the Software without restriction, including      *
 * without limitation the rights to use, copy, modify, merge, publish,      *
 * distribute, distribute with modifications, sublicense, and/or sell       *
 * copies of the Software, and to permit persons to whom the Software is    *
 * furnished to do so, subject to the following conditions:                 *
 *                                                                          *
 * The above copyright notice and this permission notice shall be included  *
 * in all copies or substantial portions of the Software.                   *
 *                                                                          *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS  *
 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF               *
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.   *
 * IN NO EVENT SHALL THE ABOVE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,   *
 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR    *
 * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR    *
 * THE USE OR OTHER DEALINGS IN THE SOFTWARE.                               *
 *                                                                          *
 * Except as contained in this notice, the name(s) of the above copyright   *
 * holders shall not be used in advertising or otherwise to promote the     *
 * sale, use or other dealings in this Software without prior written       *
 * authorization.                                                           *
 ****************************************************************************/

/****************************************************************************
 *  Author: Zeyd M. Ben-Halim <zmbenhal@netcom.com> 1992,1995               *
 *     and: Eric S. Raymond <esr@snark.thyrsus.com>                         *
 ****************************************************************************/


/*
 * Termcap compatibility support
 *
 * If your OS integrator didn't install a terminfo database, you can call
 * _nc_read_termcap_entry() to support reading and translating capabilities
 * from the system termcap file.  This is a kludge; it will bulk up and slow
 * down every program that uses ncurses, and translated termcap entries cannot
 * use full terminfo capabilities.  Don't use it unless you absolutely have to;
 * instead, get your system people to run tic(1) from root on the terminfo
 * master included with ncurses to translate it into a terminfo database.
 *
 * If USE_GETCAP is enabled, we use what is effectively a copy of the 4.4BSD
 * getcap code to fetch entries.  There are disadvantages to this; mainly that
 * getcap(3) does its own resolution, meaning that entries read in in this way
 * can't reference the terminfo tree.  The only thing it buys is faster startup
 * time, getcap(3) is much faster than our tic parser.
 */

#include <curses.priv.h>

#include <ctype.h>
#include <term.h>
#include <tic.h>
#include <term_entry.h>

#if HAVE_FCNTL_H
#include <fcntl.h>
#endif

MODULE_ID("$Id: read_termcap.c,v 1.27 1998/02/11 12:13:58 tom Exp $")

#ifdef __EMX__
#define is_pathname(s) ((((s) != 0) && ((s)[0] == '/')) \
              || (((s)[0] != 0) && ((s)[1] == ':')))
#else
#define is_pathname(s) ((s) != 0 && (s)[0] == '/')
#endif

#define TC_SUCCESS     0
#define TC_UNRESOLVED -1
#define TC_NOT_FOUND  -2
#define TC_SYS_ERR    -3
#define TC_REF_LOOP   -4

#if USE_GETCAP

#if HAVE_BSD_CGETENT
#define _nc_cgetcap   cgetcap
#define _nc_cgetent(buf, oline, db_array, name) cgetent(buf, db_array, name)
#define _nc_cgetmatch cgetmatch
#define _nc_cgetset   cgetset
#else
static int _nc_cgetmatch(char *, const char *);
static int _nc_getent(char **, unsigned int *, int *, int, char **, int, const char *, int, char *);
static int _nc_nfcmp(const char *, char *);

/*-
 * Copyright (c) 1992, 1993
 *    The Regents of the University of California.  All rights reserved.
 *
 * This code is derived from software contributed to Berkeley by
 * Casey Leedom of Lawrence Livermore National Laboratory.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. All advertising materials mentioning features or use of this software
 *    must display the following acknowledgment:
 *    This product includes software developed by the University of
 *    California, Berkeley and its contributors.
 * 4. Neither the name of the University nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */

/* static char sccsid[] = "@(#)getcap.c   8.3 (Berkeley) 3/25/94"; */

#define     BFRAG       1024
#define     BSIZE       1024
#define     ESC         ('[' & 037) /* ASCII ESC */
#define     MAX_RECURSION     32          /* maximum getent recursion */
#define     SFRAG       100         /* cgetstr mallocs in SFRAG chunks */

#define RECOK     (char)0
#define TCERR     (char)1
#define     SHADOW      (char)2

static size_t      topreclen; /* toprec length */
static char *toprec;    /* Additional record specified by cgetset() */
static int   gottoprec; /* Flag indicating retrieval of toprecord */

/*
 * Cgetset() allows the addition of a user specified buffer to be added to the
 * database array, in effect "pushing" the buffer on top of the virtual
 * database.  0 is returned on success, -1 on failure.
 */
static int
_nc_cgetset(const char *ent)
{
      if (ent == 0) {
            FreeIfNeeded(toprec);
            toprec = 0;
            topreclen = 0;
            return (0);
      }
      topreclen = strlen(ent);
      if ((toprec = malloc (topreclen + 1)) == 0) {
            errno = ENOMEM;
            return (-1);
      }
      gottoprec = 0;
      (void)strcpy(toprec, ent);
      return (0);
}

/*
 * Cgetcap searches the capability record buf for the capability cap with type
 * `type'.  A pointer to the value of cap is returned on success, 0 if the
 * requested capability couldn't be found.
 *
 * Specifying a type of ':' means that nothing should follow cap (:cap:).  In
 * this case a pointer to the terminating ':' or NUL will be returned if cap is
 * found.
 *
 * If (cap, '@') or (cap, terminator, '@') is found before (cap, terminator)
 * return 0.
 */
static char *
_nc_cgetcap(char *buf, const char *cap, int type)
{
      register const char *cp;
      register char *bp;

      bp = buf;
      for (;;) {
            /*
             * Skip past the current capability field - it's either the
             * name field if this is the first time through the loop, or
             * the remainder of a field whose name failed to match cap.
             */
            for (;;) {
                  if (*bp == '\0')
                        return (0);
                  else if (*bp++ == ':')
                        break;
            }

            /*
             * Try to match (cap, type) in buf.
             */
            for (cp = cap; *cp == *bp && *bp != '\0'; cp++, bp++)
                  continue;
            if (*cp != '\0')
                  continue;
            if (*bp == '@')
                  return (0);
            if (type == ':') {
                  if (*bp != '\0' && *bp != ':')
                        continue;
                  return(bp);
            }
            if (*bp != type)
                  continue;
            bp++;
            return (*bp == '@' ? 0 : bp);
      }
      /* NOTREACHED */
}

/*
 * Cgetent extracts the capability record name from the NULL terminated file
 * array db_array and returns a pointer to a malloc'd copy of it in buf.  Buf
 * must be retained through all subsequent calls to cgetcap, cgetnum, cgetflag,
 * and cgetstr, but may then be freed.
 *
 * Returns:
 *
 * positive #    on success (i.e., the index in db_array)
 * TC_UNRESOLVED if we had too many recurrences to resolve
 * TC_NOT_FOUND  if the requested record couldn't be found
 * TC_SYS_ERR    if a system error was encountered (e.g.,couldn't open a file)
 * TC_REF_LOOP   if a potential reference loop is detected
 */
static int
_nc_cgetent(char **buf, int *oline, char **db_array, const char *name)
{
      unsigned int dummy;

      return (_nc_getent(buf, &dummy, oline, 0, db_array, -1, name, 0, 0));
}

/*
 * Getent implements the functions of cgetent.  If fd is non-negative,
 * *db_array has already been opened and fd is the open file descriptor.  We
 * do this to save time and avoid using up file descriptors for tc=
 * recursions.
 *
 * Getent returns the same success/failure codes as cgetent.  On success, a
 * pointer to a malloc'd capability record with all tc= capabilities fully
 * expanded and its length (not including trailing ASCII NUL) are left in
 * *cap and *len.
 *
 * Basic algorithm:
 *    + Allocate memory incrementally as needed in chunks of size BFRAG
 *      for capability buffer.
 *    + Recurse for each tc=name and interpolate result.  Stop when all
 *      names interpolated, a name can't be found, or depth exceeds
 *      MAX_RECURSION.
 */
static int
_nc_getent(
      char **cap,         /* termcap-content */
      unsigned int *len,  /* length, needed for recursion */
      int *beginning,     /* line-number at match */
      int in_array,       /* index in 'db_array[] */
      char **db_array,    /* list of files to search */
      int fd,
      const char *name,
      int depth,
      char *nfield)
{
      register char *r_end, *rp;
      int myfd = FALSE;
      char *record;
      int tc_not_resolved;
      int current;
      int lineno;

      /*
       * Return with ``loop detected'' error if we've recurred more than
       * MAX_RECURSION times.
       */
      if (depth > MAX_RECURSION)
            return (TC_REF_LOOP);

      /*
       * Check if we have a top record from cgetset().
       */
      if (depth == 0 && toprec != 0 && _nc_cgetmatch(toprec, name) == 0) {
            if ((record = malloc (topreclen + BFRAG)) == 0) {
                  errno = ENOMEM;
                  return (TC_SYS_ERR);
            }
            (void)strcpy(record, toprec);
            rp = record + topreclen + 1;
            r_end = rp + BFRAG;
            current = in_array;
      } else {
            int foundit;

            /*
             * Allocate first chunk of memory.
             */
            if ((record = malloc(BFRAG)) == 0) {
                  errno = ENOMEM;
                  return (TC_SYS_ERR);
            }
            rp = r_end = record + BFRAG;
            foundit = FALSE;

            /*
             * Loop through database array until finding the record.
             */
            for (current = in_array; db_array[current] != 0; current++) {
                  int eof = FALSE;

                  /*
                   * Open database if not already open.
                   */
                  if (fd >= 0) {
                        (void)lseek(fd, (off_t)0, SEEK_SET);
                  } else {
                        fd = open(db_array[current], O_RDONLY, 0);
                        if (fd < 0) {
                              /* No error on unfound file. */
                              if (errno == ENOENT)
                                    continue;
                              free(record);
                              return (TC_SYS_ERR);
                        }
                        myfd = TRUE;
                  }
                  lineno = 0;

                  /*
                   * Find the requested capability record ...
                   */
                  {
                        char buf[2048];
                        register char *b_end = buf;
                        register char *bp = buf;
                        register int c;

                        /*
                         * Loop invariants:
                         *    There is always room for one more character in record.
                         *    R_end always points just past end of record.
                         *    Rp always points just past last character in record.
                         *    B_end always points just past last character in buf.
                         *    Bp always points at next character in buf.
                         */

                        for (;;) {
                              int first = lineno + 1;

                              /*
                               * Read in a line implementing (\, newline)
                               * line continuation.
                               */
                              rp = record;
                              for (;;) {
                                    if (bp >= b_end) {
                                          int n;

                                          n = read(fd, buf, sizeof(buf));
                                          if (n <= 0) {
                                                if (myfd)
                                                      (void)close(fd);
                                                if (n < 0) {
                                                      free(record);
                                                      return (TC_SYS_ERR);
                                                }
                                                fd = -1;
                                                eof = TRUE;
                                                break;
                                          }
                                          b_end = buf+n;
                                          bp = buf;
                                    }

                                    c = *bp++;
                                    if (c == '\n') {
                                          lineno++;
                                          if (rp == record || *(rp-1) != '\\')
                                                break;
                                    }
                                    *rp++ = c;

                                    /*
                                     * Enforce loop invariant: if no room
                                     * left in record buffer, try to get
                                     * some more.
                                     */
                                    if (rp >= r_end) {
                                          unsigned int pos;
                                          size_t newsize;

                                          pos = rp - record;
                                          newsize = r_end - record + BFRAG;
                                          record = realloc(record, newsize);
                                          if (record == 0) {
                                                errno = ENOMEM;
                                                if (myfd)
                                                      (void)close(fd);
                                                return (TC_SYS_ERR);
                                          }
                                          r_end = record + newsize;
                                          rp = record + pos;
                                    }
                              }
                              /* loop invariant lets us do this */
                              *rp++ = '\0';

                              /*
                               * If encountered eof check next file.
                               */
                              if (eof)
                                    break;

                              /*
                               * Toss blank lines and comments.
                               */
                              if (*record == '\0' || *record == '#')
                                    continue;

                              /*
                               * See if this is the record we want ...
                               */
                              if (_nc_cgetmatch(record, name) == 0
                               && (nfield == 0
                                || !_nc_nfcmp(nfield, record))) {
                                    foundit = TRUE;
                                    *beginning = first;
                                    break;      /* found it! */
                              }
                        }
                  }
                  if (foundit)
                        break;
            }

            if (!foundit)
                  return (TC_NOT_FOUND);
      }

      /*
       * Got the capability record, but now we have to expand all tc=name
       * references in it ...
       */
      {
            register char *newicap, *s;
            register int newilen;
            unsigned int ilen;
            int diff, iret, tclen, oline;
            char *icap, *scan, *tc, *tcstart, *tcend;

            /*
             * Loop invariants:
             *    There is room for one more character in record.
             *    R_end points just past end of record.
             *    Rp points just past last character in record.
             *    Scan points at remainder of record that needs to be
             *    scanned for tc=name constructs.
             */
            scan = record;
            tc_not_resolved = FALSE;
            for (;;) {
                  if ((tc = _nc_cgetcap(scan, "tc", '=')) == 0)
                        break;

                  /*
                   * Find end of tc=name and stomp on the trailing `:'
                   * (if present) so we can use it to call ourselves.
                   */
                  s = tc;
                  while (*s != '\0') {
                        if (*s++ == ':') {
                              *(s - 1) = '\0';
                              break;
                        }
                  }
                  tcstart = tc - 3;
                  tclen = s - tcstart;
                  tcend = s;

                  iret = _nc_getent(&icap, &ilen, &oline, current, db_array, fd, tc, depth+1, 0);
                  newicap = icap;         /* Put into a register. */
                  newilen = ilen;
                  if (iret != TC_SUCCESS) {
                        /* an error */
                        if (iret < TC_NOT_FOUND) {
                              if (myfd)
                                    (void)close(fd);
                              free(record);
                              return (iret);
                        }
                        if (iret == TC_UNRESOLVED)
                              tc_not_resolved = TRUE;
                        /* couldn't resolve tc */
                        if (iret == TC_NOT_FOUND) {
                              *(s - 1) = ':';
                              scan = s - 1;
                              tc_not_resolved = TRUE;
                              continue;
                        }
                  }

                  /* not interested in name field of tc'ed record */
                  s = newicap;
                  while (*s != '\0' && *s++ != ':')
                        ;
                  newilen -= s - newicap;
                  newicap = s;

                  /* make sure interpolated record is `:'-terminated */
                  s += newilen;
                  if (*(s-1) != ':') {
                        *s = ':';   /* overwrite NUL with : */
                        newilen++;
                  }

                  /*
                   * Make sure there's enough room to insert the
                   * new record.
                   */
                  diff = newilen - tclen;
                  if (diff >= r_end - rp) {
                        unsigned int pos, tcpos, tcposend;
                        size_t newsize;

                        pos = rp - record;
                        newsize = r_end - record + diff + BFRAG;
                        tcpos = tcstart - record;
                        tcposend = tcend - record;
                        record = realloc(record, newsize);
                        if (record == 0) {
                              errno = ENOMEM;
                              if (myfd)
                                    (void)close(fd);
                              free(icap);
                              return (TC_SYS_ERR);
                        }
                        r_end = record + newsize;
                        rp = record + pos;
                        tcstart = record + tcpos;
                        tcend = record + tcposend;
                  }

                  /*
                   * Insert tc'ed record into our record.
                   */
                  s = tcstart + newilen;
                  memmove(s, tcend, (size_t)(rp - tcend));
                  memmove(tcstart, newicap, (size_t)newilen);
                  rp += diff;
                  free(icap);

                  /*
                   * Start scan on `:' so next cgetcap works properly
                   * (cgetcap always skips first field).
                   */
                  scan = s-1;
            }
      }

      /*
       * Close file (if we opened it), give back any extra memory, and
       * return capability, length and success.
       */
      if (myfd)
            (void)close(fd);
      *len = rp - record - 1; /* don't count NUL */
      if (r_end > rp) {
            if ((record = realloc(record, (size_t)(rp - record))) == 0) {
                  errno = ENOMEM;
                  return (TC_SYS_ERR);
            }
      }

      *cap = record;
      if (tc_not_resolved)
            return (TC_UNRESOLVED);
      return (current);
}

/*
 * Cgetmatch will return 0 if name is one of the names of the capability
 * record buf, -1 if not.
 */
static int
_nc_cgetmatch(char *buf, const char *name)
{
      register const char *np;
      register char *bp;

      /*
       * Start search at beginning of record.
       */
      bp = buf;
      for (;;) {
            /*
             * Try to match a record name.
             */
            np = name;
            for (;;) {
                  if (*np == '\0') {
                        if (*bp == '|' || *bp == ':' || *bp == '\0')
                              return (0);
                        else
                              break;
                  } else if (*bp++ != *np++) {
                        break;
                  }
            }

            /*
             * Match failed, skip to next name in record.
             */
            bp--; /* a '|' or ':' may have stopped the match */
            for (;;) {
                  if (*bp == '\0' || *bp == ':')
                        return (-1);      /* match failed totally */
                  else if (*bp++ == '|')
                        break;      /* found next name */
            }
      }
}

/*
 * Compare name field of record.
 */
static int
_nc_nfcmp(const char *nf, char *rec)
{
      char *cp, tmp;
      int ret;

      for (cp = rec; *cp != ':'; cp++)
            ;

      tmp = *(cp + 1);
      *(cp + 1) = '\0';
      ret = strcmp(nf, rec);
      *(cp + 1) = tmp;

      return (ret);
}
#endif /* HAVE_BSD_CGETENT */

/*
 * Since ncurses provides its own 'tgetent()', we cannot use the native one.
 * So we reproduce the logic to get down to cgetent() -- or our cut-down
 * version of that -- to circumvent the problem of configuring against the
 * termcap library.
 */
#define USE_BSD_TGETENT 1

#if USE_BSD_TGETENT
/*
 * Copyright (c) 1980, 1993
 *    The Regents of the University of California.  All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. All advertising materials mentioning features or use of this software
 *    must display the following acknowledgment:
 *    This product includes software developed by the University of
 *    California, Berkeley and its contributors.
 * 4. Neither the name of the University nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */

/* static char sccsid[] = "@(#)termcap.c  8.1 (Berkeley) 6/4/93" */

#define     PBUFSIZ           512   /* max length of filename path */
#define     PVECSIZ           32    /* max number of names in path */
#define TBUFSIZ (2048*2)

static char *tbuf;

/*
 * On entry, srcp points to a non ':' character which is the beginning of the
 * token, if any.  We'll try to return a string that doesn't end with a ':'.
 */
static char *
get_tc_token(char **srcp, int *endp)
{
      int ch;
      bool found = FALSE;
      char *s, *base;
      char *tok = 0;

      *endp = TRUE;
      for (s = base = *srcp; *s != '\0'; ) {
            ch = *s++;
            if (ch == '\\') {
                  if (*s == '\0') {
                        break;
                  } else if (*s++ == '\n') {
                        while (isspace(*s))
                              s++;
                  } else {
                        found = TRUE;
                  }
            } else if (ch == ':') {
                  if (found) {
                        tok = base;
                        s[-1] = '\0';
                        *srcp = s;
                        *endp = FALSE;
                        break;
                  }
                  base = s;
            } else if (isgraph(ch)) {
                  found = TRUE;
            }
      }

      /* malformed entry may end without a ':' */
      if (tok == 0 && found) {
            tok = base;
      }

      return tok;
}

static char *
copy_tc_token(char *dst, const char *src, size_t len)
{
      int ch;

      while ((ch = *src++) != '\0') {
            if (ch == '\\' && *src == '\n') {
                  while (isspace(*src))
                        src++;
                  continue;
            }
            if (--len == 0) {
                  dst = 0;
                  break;
            }
            *dst++ = ch;
      }
      return dst;
}

/*
 * Get an entry for terminal name in buffer bp from the termcap file.
 */
static int
_nc_tgetent(char *bp, char **sourcename, int *lineno, const char *name)
{
      static char *the_source;

      register char *p;
      register char *cp;
      char  *dummy;
      char **fname;
      char  *home;
      int    i;
      char   pathbuf[PBUFSIZ];      /* holds raw path of filenames */
      char  *pathvec[PVECSIZ];      /* to point to names in pathbuf */
      char **pvec;                  /* holds usable tail of path vector */
      char  *termpath;

      fname = pathvec;
      pvec = pathvec;
      tbuf = bp;
      p = pathbuf;
      cp = getenv("TERMCAP");

      /*
       * TERMCAP can have one of two things in it.  It can be the name of a
       * file to use instead of /etc/termcap.  In this case it better start
       * with a "/".  Or it can be an entry to use so we don't have to read
       * the file.  In this case it has to already have the newlines crunched
       * out.  If TERMCAP does not hold a file name then a path of names is
       * searched instead.  The path is found in the TERMPATH variable, or
       * becomes "$HOME/.termcap /etc/termcap" if no TERMPATH exists.
       */
      if (!is_pathname(cp)) { /* no TERMCAP or it holds an entry */
            if ((termpath = getenv("TERMPATH")) != 0) {
                  strncpy(pathbuf, termpath, sizeof(pathbuf)-1);
            } else {
                  if ((home = getenv("HOME")) != 0) { /* setup path */
                        p += strlen(home);      /* path, looking in */
                        strcpy(pathbuf, home);  /* $HOME first */
                        *p++ = '/';
                  }     /* if no $HOME look in current directory */
#define     MY_PATH_DEF ".termcap /etc/termcap /usr/share/misc/termcap"
                  strncpy(p, MY_PATH_DEF, (size_t)(PBUFSIZ - (p - pathbuf)));
            }
      }
      else                    /* user-defined name in TERMCAP */
            strncpy(pathbuf, cp, PBUFSIZ);      /* still can be tokenized */

      *fname++ = pathbuf;     /* tokenize path into vector of names */
      while (*++p) {
            if (*p == ' ' || *p == ':') {
                  *p = '\0';
                  while (*++p)
                        if (*p != ' ' && *p != ':')
                              break;
                  if (*p == '\0')
                        break;
                  *fname++ = p;
                  if (fname >= pathvec + PVECSIZ) {
                        fname--;
                        break;
                  }
            }
      }
      *fname = 0;             /* mark end of vector */
      if (is_pathname(cp)) {
            if (_nc_cgetset(cp) < 0) {
                  return(TC_SYS_ERR);
            }
      }

      i = _nc_cgetent(&dummy, lineno, pathvec, name);

      /* ncurses' termcap-parsing routines cannot handle multiple adjacent
       * empty fields, and mistakenly use the last valid cap entry instead of
       * the first (breaks tc= includes)
       */
      if (i >= 0) {
            char *pd, *ps, *tok;
            int endflag = FALSE;
            char *list[1023];
            size_t n, count = 0;

            pd = bp;
            ps = dummy;
            while (!endflag && (tok = get_tc_token(&ps, &endflag)) != 0) {
                  bool ignore = FALSE;

                  for (n = 1; n < count; n++) {
                        char *s = list[n];
                        if (s[0] == tok[0]
                         && s[1] == tok[1]) {
                              ignore = TRUE;
                              break;
                        }
                  }
                  if (ignore != TRUE) {
                        list[count++] = tok;
                        pd = copy_tc_token(pd, tok, TBUFSIZ - (2+pd-bp));
                        if (pd == 0) {
                              i = -1;
                              break;
                        }
                        *pd++ = ':';
                        *pd = '\0';
                  }
            }
      }

      FreeIfNeeded(dummy);
      FreeIfNeeded(the_source);
      the_source = 0;

      /* This is not related to the BSD cgetent(), but to fake up a suitable
       * filename for ncurses' error reporting.  (If we are not using BSD
       * cgetent, then it is the actual filename).
       */
      if (i >= 0) {
            the_source = malloc(strlen(pathvec[i]) + 1);
            if (the_source != 0)
                  *sourcename = strcpy(the_source, pathvec[i]);
      }

      return(i);
}
#endif /* USE_BSD_TGETENT */
#endif /* USE_GETCAP */

int _nc_read_termcap_entry(const char *const tn, TERMTYPE *const tp)
{
      int found = FALSE;
      ENTRY *ep;
#if USE_GETCAP_CACHE
      char  cwd_buf[PATH_MAX];
#endif
#if USE_GETCAP
      char  tc[TBUFSIZ];
      static char *source;
      static int lineno;

      /* we're using getcap(3) */
      if (_nc_tgetent(tc, &source, &lineno, tn) < 0)
            return (ERR);

      _nc_curr_line = lineno;
      _nc_set_source(source);
      _nc_read_entry_source((FILE *)0, tc, FALSE, FALSE, NULLHOOK);
#else
      /*
       * Here is what the 4.4BSD termcap(3) page prescribes:
       *
       * It will look in the environment for a TERMCAP variable.  If found,
       * and the value does not begin with a slash, and the terminal type
       * name is the same as the environment string TERM, the TERMCAP string
       * is used instead of reading a termcap file.  If it does begin with a
       * slash, the string is used as a path name of the termcap file to
       * search.  If TERMCAP does not begin with a slash and name is
       * different from TERM, tgetent() searches the files $HOME/.termcap and
       * /usr/share/misc/termcap, in that order, unless the environment
       * variable TERMPATH exists, in which case it specifies a list of file
       * pathnames (separated by spaces or colons) to be searched instead.
       *
       * It goes on to state:
       *
       * Whenever multiple files are searched and a tc field occurs in the
       * requested entry, the entry it names must be found in the same file
       * or one of the succeeding files.
       *
       * However, this restriction is relaxed in ncurses; tc references to
       * previous files are permitted.
       *
       * This routine returns 1 if an entry is found, 0 if not found, and -1
       * if the database is not accessible.
       */
      FILE  *fp;
#define MAXPATHS  32
      char  *tc, *termpaths[MAXPATHS];
      int   filecount = 0;
      bool  use_buffer = FALSE;
      char  tc_buf[1024];
      char  pathbuf[PATH_MAX];

      if ((tc = getenv("TERMCAP")) != 0)
      {
            if (is_pathname(tc))    /* interpret as a filename */
            {
                  termpaths[0] = tc;
                  termpaths[filecount = 1] = 0;
            }
            else if (_nc_name_match(tc, tn, "|:")) /* treat as a capability file */
            {
                  use_buffer = TRUE;
                  (void) sprintf(tc_buf, "%.*s\n", (int)sizeof(tc_buf)-2, tc);
            }
            else if ((tc = getenv("TERMPATH")) != 0)
            {
                  char    *cp;

                  for (cp = tc; *cp; cp++)
                  {
                        if (*cp == ':')
                              *cp = '\0';
                        else if (cp == tc || cp[-1] == '\0')
                        {
                              if (filecount >= MAXPATHS - 1)
                                    return(-1);

                              termpaths[filecount++] = cp;
                        }
                  }
                  termpaths[filecount] = 0;
            }
      }
      else  /* normal case */
      {
            char  envhome[PATH_MAX], *h;

            filecount = 0;

            /*
             * Probably /etc/termcap is a symlink to /usr/share/misc/termcap.
             * Avoid reading the same file twice.
             */
            if (access("/etc/termcap", R_OK) == 0)
                  termpaths[filecount++] = "/etc/termcap";
            else if (access("/usr/share/misc/termcap", R_OK) == 0)
                  termpaths[filecount++] = "/usr/share/misc/termcap";

            if ((h = getenv("HOME")) != (char *)NULL)
            {
            /* user's .termcap, if any, should override it */
                (void) strncpy(envhome, h, PATH_MAX - 10);
            envhome[PATH_MAX - 10] = '\0';
            (void) sprintf(pathbuf, "%s/.termcap", envhome);
            termpaths[filecount++] = pathbuf;
            }

            termpaths[filecount] = 0;
      }

      /* parse the sources */
      if (use_buffer)
      {
            _nc_set_source("TERMCAP");

            /*
             * We don't suppress warning messages here.  The presumption is
             * that since it's just a single entry, they won't be a pain.
             */
            _nc_read_entry_source((FILE *)0, tc_buf, FALSE, FALSE, NULLHOOK);
      } else {
            int   i;

            for (i = 0; i < filecount; i++) {

                  T(("Looking for %s in %s", tn, termpaths[i]));
                  if ((fp = fopen(termpaths[i], "r")) != (FILE *)0)
                  {
                        _nc_set_source(termpaths[i]);

                        /*
                         * Suppress warning messages.  Otherwise you
                         * get 400 lines of crap from archaic termcap
                         * files as ncurses complains about all the
                         * obsolete capabilities.
                         */
                        _nc_read_entry_source(fp, (char*)0, FALSE, TRUE, NULLHOOK);

                        (void) fclose(fp);
                  }
            }
      }
#endif /* USE_GETCAP */

      if (_nc_head == 0)
            return(ERR);

      /* resolve all use references */
      _nc_resolve_uses();

      /* find a terminal matching tn, if we can */
#if USE_GETCAP_CACHE
      if (getcwd(cwd_buf, sizeof(cwd_buf)) != 0)
      {
            _nc_set_writedir((char *)0); /* note: this does a chdir */
#endif
            for_entry_list(ep) {
                  if (_nc_name_match(ep->tterm.term_names, tn, "|:"))
                  {
                        /*
                         * Make a local copy of the terminal
                         * capabilities.  Free all entry storage except
                         * the string table for the loaded type (which
                         * we disconnected from the list by NULLing out
                         * ep->tterm.str_table above).
                         */
                        memcpy(tp, &ep->tterm, sizeof(TERMTYPE));
                        ep->tterm.str_table = (char *)0;

                        /*
                         * OK, now try to write the type to user's
                         * terminfo directory.  Next time he loads
                         * this, it will come through terminfo.
                         *
                         * Advantage:  Second and subsequent fetches of
                         * this entry will be very fast.
                         *
                         * Disadvantage:  After the first time a
                         * termcap type is loaded by its user, editing
                         * it in the /etc/termcap file, or in TERMCAP,
                         * or in a local ~/.termcap, will be
                         * ineffective unless the terminfo entry is
                         * explicitly removed.
                         */
#if USE_GETCAP_CACHE
                        (void) _nc_write_entry(tp);
#endif
                        found = TRUE;
                        break;
                  }
            }
#if USE_GETCAP_CACHE
            chdir(cwd_buf);
      }
#endif

      _nc_free_entries(_nc_head);
      return(found);
}

Generated by  Doxygen 1.6.0   Back to index