home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 October / usenetsourcesnewsgroupsinfomagicoctober1994disk2.iso / misc / volume39 / rperf / part02 / termcap.c < prev   
C/C++ Source or Header  |  1993-08-30  |  11KB  |  434 lines

  1. /*-
  2.  * Copyright (c) 1980 The Regents of the University of California.
  3.  * All rights reserved.
  4.  *
  5.  * Redistribution and use in source and binary forms, with or without
  6.  * modification, are permitted provided that the following conditions
  7.  * are met:
  8.  * 1. Redistributions of source code must retain the above copyright
  9.  *    notice, this list of conditions and the following disclaimer.
  10.  * 2. Redistributions in binary form must reproduce the above copyright
  11.  *    notice, this list of conditions and the following disclaimer in the
  12.  *    documentation and/or other materials provided with the distribution.
  13.  * 3. All advertising materials mentioning features or use of this software
  14.  *    must display the following acknowledgement:
  15.  *    This product includes software developed by the University of
  16.  *    California, Berkeley and its contributors.
  17.  * 4. Neither the name of the University nor the names of its contributors
  18.  *    may be used to endorse or promote products derived from this software
  19.  *    without specific prior written permission.
  20.  *
  21.  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
  22.  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  23.  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  24.  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
  25.  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  26.  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
  27.  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  28.  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  29.  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  30.  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  31.  * SUCH DAMAGE.
  32.  */
  33.  
  34. #ifndef lint
  35. static char     sccsid[] = "@(#)termcap.c    5.5 (Berkeley) 6/1/90";
  36. #endif                /* not lint */
  37.  
  38. #define    BUFSIZ        1024
  39. #define MAXHOP        32    /* max number of tc= indirections */
  40. #define    PBUFSIZ        512    /* max length of filename path */
  41. #define    PVECSIZ        32    /* max number of names in path */
  42.  
  43. #include <string.h>
  44. #include <ctype.h>
  45. #include "pathnames.h"
  46.  
  47. /*-
  48.  * termcap - routines for dealing with the terminal capability data base
  49.  *
  50.  * BUG:        Should use a "last" pointer in tbuf, so that searching
  51.  *        for capabilities alphabetically would not be a n**2/2
  52.  *        process when large numbers of capabilities are given.
  53.  * Note:    If we add a last pointer now we will screw up the
  54.  *        tc capability. We really should compile termcap.
  55.  *
  56.  * Essentially all the work here is scanning and decoding escapes
  57.  * in string capabilities.  We don't use stdio because the editor
  58.  * doesn't, and because living w/o it is not hard.
  59.  */
  60.  
  61. static char    *tbuf;
  62. static int      hopcount;    /* detect infinite loops in termcap, init 0 */
  63. static char     pathbuf[PBUFSIZ];    /* holds raw path of filenames */
  64. static char    *pathvec[PVECSIZ];    /* to point to names in pathbuf */
  65. static char   **pvec;        /* holds usable tail of path vector */
  66. char           *tskip();
  67. char           *tgetstr();
  68. char           *tdecode();
  69. char           *getenv();
  70.  
  71. /*
  72.  * Get an entry for terminal name in buffer bp from the termcap file.
  73.  */
  74. tgetent(bp, name)
  75.     char           *bp, *name;
  76. {
  77.     register char  *p;
  78.     register char  *cp;
  79.     register int    c;
  80.     char           *home, *termpath;
  81.     char          **fname = pathvec;
  82.  
  83.     pvec = pathvec;
  84.     tbuf = bp;
  85.     p = pathbuf;
  86.     cp = getenv("TERMCAP");
  87.     /*-
  88.      * TERMCAP can have one of two things in it. It can be the
  89.      * name of a file to use instead of /etc/termcap. In this
  90.      * case it better start with a "/". Or it can be an entry to
  91.      * use so we don't have to read the file. In this case it
  92.      * has to already have the newlines crunched out.  If TERMCAP
  93.      * does not hold a file name then a path of names is searched
  94.      * instead.  The path is found in the TERMPATH variable, or
  95.      * becomes "$HOME/.termcap /etc/termcap" if no TERMPATH exists.
  96.      */
  97.     if (!cp || *cp != '/') {    /* no TERMCAP or it holds an entry */
  98.     if (termpath = getenv("TERMPATH"))
  99.         (void) strncpy(pathbuf, termpath, PBUFSIZ);
  100.     else {
  101.         if (home = getenv("HOME")) {    /* set up default */
  102.         p += strlen(home);    /* path, looking in */
  103.         (void) strcpy(pathbuf, home);    /* $HOME first */
  104.         *p++ = '/';
  105.         }            /* if no $HOME look in current directory */
  106.         (void) strncpy(p, _PATH_DEF, PBUFSIZ - (p - pathbuf));
  107.     }
  108.     } else            /* user-defined name in TERMCAP */
  109.     (void) strncpy(pathbuf, cp, PBUFSIZ);    /* still can be tokenized */
  110.  
  111.     *fname++ = pathbuf;        /* tokenize path into vector of names */
  112.     while (*++p)
  113.     if (*p == ' ' || *p == ':') {
  114.         *p = '\0';
  115.         while (*++p)
  116.         if (*p != ' ' && *p != ':')
  117.             break;
  118.         if (*p == '\0')
  119.         break;
  120.         *fname++ = p;
  121.         if (fname >= pathvec + PVECSIZ) {
  122.         fname--;
  123.         break;
  124.         }
  125.     }
  126.     *fname = (char *) 0;    /* mark end of vector */
  127.     if (cp && *cp && *cp != '/') {
  128.     tbuf = cp;
  129.     c = tnamatch(name);
  130.     tbuf = bp;
  131.     if (c) {
  132.         (void) strcpy(bp, cp);
  133.         return (tnchktc());
  134.     }
  135.     }
  136.     return (tfindent(bp, name));/* find terminal entry in path */
  137. }
  138.  
  139. /*-
  140.  * tfindent - reads through the list of files in pathvec as if they were one
  141.  * continuous file searching for terminal entries along the way.  It will
  142.  * participate in indirect recursion if the call to tnchktc() finds a tc=
  143.  * field, which is only searched for in the current file and files ocurring
  144.  * after it in pathvec.  The usable part of this vector is kept in the global
  145.  * variable pvec.  Terminal entries may not be broken across files.  Parse is
  146.  * very rudimentary; we just notice escaped newlines.
  147.  */
  148. tfindent(bp, name)
  149.     char           *bp, *name;
  150. {
  151.     register char  *cp;
  152.     register int    c;
  153.     register int    i, cnt;
  154.     char            ibuf[BUFSIZ];
  155.     int             opencnt = 0;
  156.     int             tf;
  157.  
  158.     tbuf = bp;
  159. nextfile:
  160.     i = cnt = 0;
  161.     while (*pvec && (tf = open(*pvec, 0)) < 0)
  162.     pvec++;
  163.     if (!*pvec)
  164.     return (opencnt ? 0 : -1);
  165.     opencnt++;
  166.     for (;;) {
  167.     cp = bp;
  168.     for (;;) {
  169.         if (i == cnt) {
  170.         cnt = read(tf, ibuf, BUFSIZ);
  171.         if (cnt <= 0) {
  172.             (void) close(tf);
  173.             pvec++;
  174.             goto nextfile;
  175.         }
  176.         i = 0;
  177.         }
  178.         c = ibuf[i++];
  179.         if (c == '\n') {
  180.         if (cp > bp && cp[-1] == '\\') {
  181.             cp--;
  182.             continue;
  183.         }
  184.         break;
  185.         }
  186.         if (cp >= bp + BUFSIZ) {
  187.         (void) write(2, "Termcap entry too long\n", 23);
  188.         break;
  189.         } else
  190.         *cp++ = c;
  191.     }
  192.     *cp = 0;
  193.  
  194.     /*
  195.      * The real work for the match.
  196.      */
  197.     if (tnamatch(name)) {
  198.         (void) close(tf);
  199.         return (tnchktc());
  200.     }
  201.     }
  202. }
  203.  
  204. /*-
  205.  * tnchktc: check the last entry, see if it's tc=xxx. If so,
  206.  * recursively find xxx and append that entry (minus the names)
  207.  * to take the place of the tc=xxx entry. This allows termcap
  208.  * entries to say "like an HP2621 but doesn't turn on the labels".
  209.  * Note that this works because of the left to right scan.
  210.  */
  211. tnchktc()
  212. {
  213.     register char  *p, *q;
  214.     char            tcname[16];    /* name of similar terminal */
  215.     char            tcbuf[BUFSIZ];
  216.     char           *holdtbuf = tbuf;
  217.     int             l;
  218.  
  219.     p = tbuf + strlen(tbuf) - 2;/* before the last colon */
  220.     while (*--p != ':')
  221.     if (p < tbuf) {
  222.         (void) write(2, "Bad termcap entry\n", 18);
  223.         return (0);
  224.     }
  225.     p++;
  226.     /* p now points to beginning of last field */
  227.     if (p[0] != 't' || p[1] != 'c')
  228.     return (1);
  229.     (void) strcpy(tcname, p + 3);
  230.     q = tcname;
  231.     while (*q && *q != ':')
  232.     q++;
  233.     *q = 0;
  234.     if (++hopcount > MAXHOP) {
  235.     (void) write(2, "Infinite tc= loop\n", 18);
  236.     return (0);
  237.     }
  238.     if (tfindent(tcbuf, tcname) != 1) {
  239.     hopcount = 0;        /* unwind recursion */
  240.     return (0);
  241.     }
  242.     for (q = tcbuf; *q != ':'; q++);
  243.     l = p - holdtbuf + strlen(q);
  244.     if (l > BUFSIZ) {
  245.     (void) write(2, "Termcap entry too long\n", 23);
  246.     q[BUFSIZ - (p - tbuf)] = 0;
  247.     }
  248.     (void) strcpy(p, q + 1);
  249.     tbuf = holdtbuf;
  250.     hopcount = 0;        /* unwind recursion */
  251.     return (1);
  252. }
  253.  
  254. /*-
  255.  * Tnamatch deals with name matching.  The first field of the termcap
  256.  * entry is a sequence of names separated by |'s, so we compare
  257.  * against each such name.  The normal : terminator after the last
  258.  * name (before the first field) stops us.
  259.  */
  260. tnamatch(np)
  261.     char           *np;
  262. {
  263.     register char  *Np, *Bp;
  264.  
  265.     Bp = tbuf;
  266.     if (*Bp == '#')
  267.     return (0);
  268.     for (;;) {
  269.     for (Np = np; *Np && *Bp == *Np; Bp++, Np++)
  270.         continue;
  271.     if (*Np == 0 && (*Bp == '|' || *Bp == ':' || *Bp == 0))
  272.         return (1);
  273.     while (*Bp && *Bp != ':' && *Bp != '|')
  274.         Bp++;
  275.     if (*Bp == 0 || *Bp == ':')
  276.         return (0);
  277.     Bp++;
  278.     }
  279. }
  280.  
  281. /*-
  282.  * Skip to the next field.  Notice that this is very dumb, not
  283.  * knowing about \: escapes or any such.  If necessary, :'s can be put
  284.  * into the termcap file in octal.
  285.  */
  286. static char    *
  287. tskip(bp)
  288.     register char  *bp;
  289. {
  290.  
  291.     while (*bp && *bp != ':')
  292.     bp++;
  293.     if (*bp == ':')
  294.     bp++;
  295.     return (bp);
  296. }
  297.  
  298. /*-
  299.  * Return the (numeric) option id.
  300.  * Numeric options look like
  301.  *    li#80
  302.  * i.e. the option string is separated from the numeric value by
  303.  * a # character.  If the option is not found we return -1.
  304.  * Note that we handle octal numbers beginning with 0.
  305.  */
  306. tgetnum(id)
  307.     char           *id;
  308. {
  309.     register int    i, base;
  310.     register char  *bp = tbuf;
  311.  
  312.     for (;;) {
  313.     bp = tskip(bp);
  314.     if (*bp == 0)
  315.         return (-1);
  316.     if (*bp++ != id[0] || *bp == 0 || *bp++ != id[1])
  317.         continue;
  318.     if (*bp == '@')
  319.         return (-1);
  320.     if (*bp != '#')
  321.         continue;
  322.     bp++;
  323.     base = 10;
  324.     if (*bp == '0')
  325.         base = 8;
  326.     i = 0;
  327.     while (isdigit(*bp))
  328.         i *= base, i += *bp++ - '0';
  329.     return (i);
  330.     }
  331. }
  332.  
  333. /*-
  334.  * Handle a flag option.
  335.  * Flag options are given "naked", i.e. followed by a : or the end
  336.  * of the buffer.  Return 1 if we find the option, or 0 if it is
  337.  * not given.
  338.  */
  339. tgetflag(id)
  340.     char           *id;
  341. {
  342.     register char  *bp = tbuf;
  343.  
  344.     for (;;) {
  345.     bp = tskip(bp);
  346.     if (!*bp)
  347.         return (0);
  348.     if (*bp++ == id[0] && *bp != 0 && *bp++ == id[1]) {
  349.         if (!*bp || *bp == ':')
  350.         return (1);
  351.         else if (*bp == '@')
  352.         return (0);
  353.     }
  354.     }
  355. }
  356.  
  357. /*-
  358.  * Get a string valued option.
  359.  * These are given as
  360.  *    cl=^Z
  361.  * Much decoding is done on the strings, and the strings are
  362.  * placed in area, which is a ref parameter which is updated.
  363.  * No checking on area overflow.
  364.  */
  365. char           *
  366. tgetstr(id, area)
  367.     char           *id, **area;
  368. {
  369.     register char  *bp = tbuf;
  370.  
  371.     for (;;) {
  372.     bp = tskip(bp);
  373.     if (!*bp)
  374.         return (0);
  375.     if (*bp++ != id[0] || *bp == 0 || *bp++ != id[1])
  376.         continue;
  377.     if (*bp == '@')
  378.         return (0);
  379.     if (*bp != '=')
  380.         continue;
  381.     bp++;
  382.     return (tdecode(bp, area));
  383.     }
  384. }
  385.  
  386. /*-
  387.  * Tdecode does the grung work to decode the
  388.  * string capability escapes.
  389.  */
  390. static char    *
  391. tdecode(str, area)
  392.     register char  *str;
  393.     char          **area;
  394. {
  395.     register char  *cp;
  396.     register int    c;
  397.     register char  *dp;
  398.     int             i;
  399.  
  400.     cp = *area;
  401.     while ((c = *str++) && c != ':') {
  402.     switch (c) {
  403.  
  404.     case '^':
  405.         c = *str++ & 037;
  406.         break;
  407.  
  408.     case '\\':
  409.         dp = "E\033^^\\\\::n\nr\rt\tb\bf\f";
  410.         c = *str++;
  411.     nextc:
  412.         if (*dp++ == c) {
  413.         c = *dp++;
  414.         break;
  415.         }
  416.         dp++;
  417.         if (*dp)
  418.         goto nextc;
  419.         if (isdigit(c)) {
  420.         c -= '0', i = 2;
  421.         do
  422.             c <<= 3, c |= *str++ - '0';
  423.         while (--i && isdigit(*str));
  424.         }
  425.         break;
  426.     }
  427.     *cp++ = c;
  428.     }
  429.     *cp++ = 0;
  430.     str = *area;
  431.     *area = cp;
  432.     return (str);
  433. }
  434.