Logo Search packages:      
Sourcecode: netatalk version File versions

printcap.c

/*
 * $Id: printcap.c,v 1.9.6.1 2004/06/18 07:56:04 bfernhomberg Exp $
 *
 * Copyright (c) 1990,1994 Regents of The University of Michigan.
 * All Rights Reserved.  See COPYRIGHT.
 *
 * Copyright (c) 1983 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 acknowledgement:
 *    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.
 */

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif /* HAVE_CONFIG_H */

#include <ctype.h>
#include <stdio.h>
#include <string.h>
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif /* HAVE_UNISTD_H */
#include <sys/types.h>
#include <sys/stat.h>
#ifdef HAVE_FCNTL_H
#include <fcntl.h>
#endif /* HAVE_FCNTL_H */
#include <atalk/paths.h>

#include "printcap.h"

#ifndef BUFSIZ
#define     BUFSIZ      1024
#endif /* ! BUFSIZ */
#define MAXHOP    32    /* max number of tc= indirections */

/*
 * termcap - routines for dealing with the terminal capability data base
 *
 * BUG:           Should use a "last" pointer in tbuf, so that searching
 *          for capabilities alphabetically would not be a n**2/2
 *          process when large numbers of capabilities are given.
 * Note:    If we add a last pointer now we will screw up the
 *          tc capability. We really should compile termcap.
 *
 * Essentially all the work here is scanning and decoding escapes
 * in string capabilities.  We don't use stdio because the editor
 * doesn't, and because living w/o it is not hard.
 */

#define PRINTCAP

#ifdef PRINTCAP
#define tgetent   pgetent
#define tskip     pskip
#define tgetstr   pgetstr
#define tdecode pdecode
#define tgetnum   pgetnum
#define     tgetflag pgetflag
#define tdecode pdecode
#define tnchktc   pnchktc
#define     tnamatch pnamatch
#define V6
#endif /* PRINTCAP */

static      FILE *pfp = NULL; /* printcap data base file pointer */
static      char *tbuf;
static      int hopcount;           /* detect infinite loops in termcap, init 0 */
static char *tskip();
char  *tgetstr();
static char *tdecode();
char  *getenv();

/*
 * Similar to tgetent except it returns the next entry instead of
 * doing a lookup.
 *
 * Added a "cap" parameter, so we can use these calls for printcap
 * and papd.conf.
 */
int getprent( cap, bp, bufsize )
      register char *cap;
      register char *bp;
      register int bufsize;
{
      register int c, skip = 0, i;

      if (pfp == NULL && (pfp = fopen( cap, "r")) == NULL)
            return(-1);
      tbuf = bp;
      i = 0;
      for (;;) {
            switch (c = getc(pfp)) {
            case EOF:
                        if (bp != tbuf) {
                        *bp = '\0';
                        return(1);
                  }
                  fclose(pfp);
                  pfp = NULL;
                  return(0);
            case '\n':
                  if (bp == tbuf) {
                        skip = 0;
                        continue;
                  }
                  if (bp[-1] == '\\') {
                        bp--;
                        continue;
                  }
                  *bp = '\0';
                  return(1);
            case '#':
                  if (bp == tbuf)
                        skip++;
            default:
                  if (skip)
                        continue;
                  if (bp >= tbuf+BUFSIZ) {
                        write(2, "Termcap entry too long\n", 23);
                        *bp = '\0';
                        return(1);
                  }
                  *bp++ = c;
                  if (++i >= bufsize) {
                        write(2, "config file too large\n", 22);
                        fclose(pfp);
                        pfp = NULL;
                        *bp = '\0';
                        return(1);
                  }
            }
      }
}

void endprent()
{
      if (pfp != NULL)
            fclose(pfp);
}

/*
 * Get an entry for terminal name in buffer bp,
 * from the termcap file.  Parse is very rudimentary;
 * we just notice escaped newlines.
 *
 * Added a "cap" parameter, so we can use these calls for printcap
 * and papd.conf.
 */
int tgetent( cap, bp, name)
      char *cap, *bp, *name;
{
      register char *cp;
      register int c;
      register int i = 0, cnt = 0;
      char ibuf[BUFSIZ];
      int tf;
      int skip;

      hopcount = 0;
      tbuf = bp;
      tf = 0;
#ifndef V6
      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 (cp && *cp) {
            if (*cp!='/') {
                  cp2 = getenv("TERM");
                  if (cp2==(char *) 0 || strcmp(name,cp2)==0) {
                        strcpy(bp,cp);
                        return(tnchktc(cap));
                  } else {
                        tf = open(cap, 0);
                  }
            } else
                  tf = open(cp, 0);
      }
      if (tf==0)
            tf = open(cap, 0);
#else /* V6 */
      tf = open(cap, 0);
#endif /* V6 */
      if (tf < 0)
            return (-1);
      for (;;) {
            cp = bp;
            skip = 0;
            for (;;) {
                  if (i == cnt) {
                        cnt = read(tf, ibuf, BUFSIZ);
                        if (cnt <= 0) {
                              close(tf);
                              return (0);
                        }
                        i = 0;
                  }
                  c = ibuf[i++];
                  if (c == '\n') {
                        if (!skip && cp > bp && cp[-1] == '\\') {
                              cp--;
                              continue;
                        }
                        skip = 0;
                        if (cp == bp)
                              continue;
                        else
                              break;
                  }
                  if (c == '#' && cp == bp)
                        skip++;
                  if (skip)
                        continue;
                  if (cp >= bp+BUFSIZ) {
                        write(2,"Termcap entry too long\n", 23);
                        break;
                  } else
                        *cp++ = c;
            }
            *cp = 0;

            /*
             * The real work for the match.
             */
            if (tnamatch(name)) {
                  close(tf);
                  return(tnchktc(cap));
            }
      }
}

/*
 * tnchktc: check the last entry, see if it's tc=xxx. If so,
 * recursively find xxx and append that entry (minus the names)
 * to take the place of the tc=xxx entry. This allows termcap
 * entries to say "like an HP2621 but doesn't turn on the labels".
 * Note that this works because of the left to right scan.
 *
 * Added a "cap" parameter, so we can use these calls for printcap
 * and papd.conf.
 */
int tnchktc( cap )
    char *cap;
{
      register char *p, *q;
      char tcname[16];  /* name of similar terminal */
      char tcbuf[BUFSIZ];
      char *holdtbuf = tbuf;
      int l;

      p = tbuf + strlen(tbuf) - 2;  /* before the last colon */
      while (*--p != ':')
            if (p<tbuf) {
                  write(2, "Bad termcap entry\n", 18);
                  return (0);
            }
      p++;
      /* p now points to beginning of last field */
      if (p[0] != 't' || p[1] != 'c')
            return(1);
      strcpy(tcname,p+3);
      q = tcname;
      while (q && *q != ':')
            q++;
      *q = 0;
      if (++hopcount > MAXHOP) {
            write(2, "Infinite tc= loop\n", 18);
            return (0);
      }
      if (tgetent( cap, tcbuf, tcname) != 1)
            return(0);
      for (q=tcbuf; *q != ':'; q++)
            ;
      l = p - holdtbuf + strlen(q);
      if (l > BUFSIZ) {
            write(2, "Termcap entry too long\n", 23);
            q[BUFSIZ - (p-tbuf)] = 0;
      }
      strcpy(p, q+1);
      tbuf = holdtbuf;
      return(1);
}

/*
 * Tnamatch deals with name matching.  The first field of the termcap
 * entry is a sequence of names separated by |'s, so we compare
 * against each such name.  The normal : terminator after the last
 * name (before the first field) stops us.
 */
int tnamatch(np)
      char *np;
{
      register char *Np, *Bp;

      Bp = tbuf;
      if (*Bp == '#')
            return(0);
      for (;;) {
            for (Np = np; *Np && *Bp == *Np; Bp++, Np++)
                  continue;
            if (*Np == 0 && (*Bp == '|' || *Bp == ':' || *Bp == 0))
                  return (1);
            while (*Bp && *Bp != ':' && *Bp != '|')
                  Bp++;
            if (*Bp == 0 || *Bp == ':')
                  return (0);
            Bp++;
      }
}

/*
 * Skip to the next field.  Notice that this is very dumb, not
 * knowing about \: escapes or any such.  If necessary, :'s can be put
 * into the termcap file in octal.
 */
static char *
tskip(bp)
      register char *bp;
{

      while (*bp && *bp != ':')
            bp++;
      while (*bp && *bp == ':')
            bp++;
      return (bp);
}

/*
 * Return the (numeric) option id.
 * Numeric options look like
 *    li#80
 * i.e. the option string is separated from the numeric value by
 * a # character.  If the option is not found we return -1.
 * Note that we handle octal numbers beginning with 0.
 */
int tgetnum(id)
      char *id;
{
      register int i, base;
      register char *bp = tbuf;

      for (;;) {
            bp = tskip(bp);
            if (*bp == 0)
                  return (-1);
            if (*bp++ != id[0] || *bp == 0 || *bp++ != id[1])
                  continue;
            if (*bp == '@')
                  return(-1);
            if (*bp != '#')
                  continue;
            bp++;
            base = 10;
            if (*bp == '0')
                  base = 8;
            i = 0;
            while (isdigit(*bp))
                  i *= base, i += *bp++ - '0';
            return (i);
      }
}

/*
 * Handle a flag option.
 * Flag options are given "naked", i.e. followed by a : or the end
 * of the buffer.  Return 1 if we find the option, or 0 if it is
 * not given.
 */
int tgetflag(id)
      char *id;
{
      register char *bp = tbuf;

      for (;;) {
            bp = tskip(bp);
            if (!*bp)
                  return (0);
            if (*bp++ == id[0] && *bp != 0 && *bp++ == id[1]) {
                  if (!*bp || *bp == ':')
                        return (1);
                  else if (*bp == '@')
                        return(0);
            }
      }
}

/*
 * Get a string valued option.
 * These are given as
 *    cl=^Z
 * Much decoding is done on the strings, and the strings are
 * placed in area, which is a ref parameter which is updated.
 * No checking on area overflow.
 */
char *
tgetstr(id, area)
      char *id, **area;
{
      register char *bp = tbuf;

      for (;;) {
            bp = tskip(bp);
            if (!*bp)
                  return (0);
            if (*bp++ != id[0] || *bp == 0 || *bp++ != id[1])
                  continue;
            if (*bp == '@')
                  return(0);
            if (*bp != '=')
                  continue;
            bp++;
            return (tdecode(bp, area));
      }
}

/*
 * Tdecode does the grung work to decode the
 * string capability escapes.
 */
static char *
tdecode(str, area)
      register char *str;
      char **area;
{
      register char *cp;
      register int c;
      register char *dp;
      int i;

      cp = *area;
      while ((c = *str++) && c != ':') {
            switch (c) {

            case '^':
                  c = *str++ & 037;
                  break;

            case '\\':
                  dp = "E\033^^\\\\::n\nr\rt\tb\bf\f";
                  c = *str++;
nextc:
                  if (*dp++ == c) {
                        c = *dp++;
                        break;
                  }
                  dp++;
                  if (*dp)
                        goto nextc;
                  if (isdigit(c)) {
                        c -= '0', i = 2;
                        do
                              c <<= 3, c |= *str++ - '0';
                        while (--i && isdigit(*str));
                  }
                  break;
            }
            *cp++ = c;
      }
      *cp++ = 0;
      str = *area;
      *area = cp;
      return (str);
}

static char *
decodename(str, area, bufsize)
      register char *str;
      char **area;
      int bufsize;
{
      register char *cp;
      register int c;
      register char *dp;
      int i;

      cp = *area;
      while ((c = *str++) && --bufsize && c != ':' && c != '|' ) {
            switch (c) {

            case '^':
                  c = *str++ & 037;
                  break;

            case '\\':
                  dp = "E\033^^\\\\::n\nr\rt\tb\bf\f";
                  c = *str++;
nextc:
                  if (*dp++ == c) {
                        c = *dp++;
                        break;
                  }
                  dp++;
                  if (*dp)
                        goto nextc;
                  if (isdigit(c)) {
                        c -= '0', i = 2;
                        do
                              c <<= 3, c |= *str++ - '0';
                        while (--i && isdigit(*str));
                  }
                  break;
            }
            *cp++ = c;
      }
      *cp++ = 0;
      str = *area;
      *area = cp;
      return (str);
}

char *
getpname( area, bufsize )
      char  **area;
      int   bufsize;
{
      return( decodename( tbuf, area, bufsize));
}

Generated by  Doxygen 1.6.0   Back to index