/* * 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. */ #ifndef lint /*static char sccsid[] = "from: @(#)printcap.c 5.7 (Berkeley) 3/4/91";*/ static char rcsid[] = "$Id: bsdcap.c,v 1.10 1997/11/04 05:22:00 itz Exp $"; #endif /* not lint */ #include #include #include /* malloc */ #include /* strtok */ static FILE *bsdcap_fp = NULL; /* data base file pointer */ static int bsdcap_dbidx = 0; /* which file in data base to read next */ /* set by cgetset(), checked by cgetent() and cgetnext() before anything else */ static char *bsdcap_set_entry = 0; #define SAFE_BUFFER_SIZE 2048 /* bsdcap_nchktc: 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 entries to say * "like an HP2621 but doesn't turn on the labels". Note that this * works because of the left to right scan. */ static int bsdcap_nchktc(bp, dbarray, tbuf, tsize) char **bp; char **dbarray; char *tbuf; int tsize; { register char *p, *q; char *tcname; /* name of similar terminal */ char *tcbuf; int poff, lnew; static char bad_entry[] = "Bad entry\n"; p = strrchr(tbuf, ':'); while (0 != p && p - tbuf >= strlen(tbuf) - 2) { *p = 0; p = strrchr(tbuf, ':'); } /* while */ if (0 == p) { write(2, bad_entry, sizeof (bad_entry) - 1); free(tbuf); return (-2); } /* if */ p++; /* p now points to beginning of last field */ if (p[0] != 't' || p[1] != 'c' || p[2] != '=') { *bp = tbuf; return (0); } /* if */ poff = p - tbuf; tcname = p + 3; q = strchr(tcname, ':'); if (0 != q) { *q = 0; } /* if */ if (0 > cgetent(&tcbuf, dbarray, tcname)) { free(tbuf); return (-2); } /* if */ q = strchr(tcbuf, ':'); if (0 == q) { write(2, bad_entry, sizeof (bad_entry) - 1); free(tcbuf); free(tbuf); return (-2); } /* if */ lnew = strlen(q + 1); if (poff + lnew > tsize - 1) { char *newbuf = realloc(tbuf, poff + lnew + 1); if (0 == newbuf) { free(tbuf); free(tcbuf); return (-2); } /* if */ tsize = poff + lnew + 1; tbuf = newbuf; p = tbuf + poff; } /* if */ strcpy(p, q + 1); free(tcbuf); *bp = tbuf; return (0); } /* bsdcap_namatch deals with name matching. The first field of the 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 bsdcap_namatch(np, buf) char *np, *buf; { char holdchar; char *p, *q; int found = 0; /* take care of comments */ if ('#' == buf[0]) { return (0); } /* if */ for (p = buf, q = strpbrk(buf, ":|"); 0 != q; p = q + 1, q = strpbrk(p, ":|")) { holdchar = *q; *q = 0; if (0 == strcmp(np, p)) { found = 1; } /* if */ *q = holdchar; if (':' == holdchar || found) { break; } /* if */ } /* for */ return found; } /* * 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 file in octal. */ static char * bsdcap_skip(bp, sep) register char *bp; int sep; { char *p; p = strchr(bp, sep); return (0 != p ? p + 1 : bp + strlen(bp)); } /* * bsdcap_decode does the grung work to decode the * string capability escapes. */ static int bsdcap_decode(str, area) register char *str; char **area; { register char *cp; register int c; register char *dp; int i; char *mybuf; int mysize; mybuf = malloc(128); if (0 == mybuf) { return -1; } /* if */ mysize = 128; cp = mybuf; 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; if (cp >= mybuf + mysize) { char *newbuf = realloc(mybuf, mysize + 128); if (0 == newbuf) { free(mybuf); return -1; } /* if */ mybuf = newbuf; mysize += 128; } /* if */ } *cp++ = 0; *area = mybuf; return 0; } typedef enum { no_fail = 0, fopen_fail = 1, malloc_fail = 2, read_fail = 4, } cap_error_type; /* * Get an entry for terminal name in buffer bp, * from the file. Parse is very rudimentary; * we just notice escaped newlines. */ int cgetent(bp, dbarray, name) char **bp, *name, **dbarray; { char **dbfile; FILE *tf; char *mybuf; int mysize; int found = 0; cap_error_type errflag = no_fail; char *myline; int mylinesize; if (0 != bsdcap_set_entry && bsdcap_namatch(name, bsdcap_set_entry)) { *bp = bsdcap_set_entry; return 0; } /* if */ myline = malloc(mylinesize = 256); if (0 == myline) { return -2; } /* if */ mybuf = malloc(SAFE_BUFFER_SIZE); if (0 == mybuf) { free(myline); return -2; } mysize = SAFE_BUFFER_SIZE; for (dbfile = dbarray; !(errflag & malloc_fail) && !found && 0 != *dbfile; ++dbfile) { tf = fopen(*dbfile, "r"); if (0 == tf) { errflag |= fopen_fail; continue; /* back to for() */ } /* if */ errflag &= ~read_fail; while (!(errflag & read_fail) && !feof(tf)) { int count = 0; int entry_complete = 0; mybuf[0] = 0; while (!entry_complete) { #if 0 int read_chars = getdelim(&myline, &mylinesize, '\n', tf); #else int read_chars = fgets(myline, 256, tf) ? strlen(myline) : (-1); #endif if (-1 == read_chars) { if (!feof(tf)) { errflag |= read_fail; } /* if */ break; } /* if */ if (count + read_chars + 1 > mysize) { char *newbuf = realloc(mybuf, count + read_chars + 1); if (0 == newbuf) { errflag |= malloc_fail; break; } /* if */ mybuf = newbuf; mysize = count + read_chars + 1; } /* if */ memcpy(mybuf + count, myline, read_chars); count += read_chars; /* is a continuation line required? */ if (count >= 2 && mybuf[count - 2] == '\\' && mybuf[count - 1] == '\n') { count -= 2; } else { entry_complete = 1; mybuf[count] = 0; } /* if */ } /* while */ if (errflag & (malloc_fail | read_fail) || feof(tf)) { break; /* out of outer while */ } else if (bsdcap_namatch(name, mybuf)) { found = 1; break; } /* if */ } /* while */ fclose(tf); } /* for */ if (errflag & (malloc_fail | read_fail)) { free(mybuf); free(myline); return -2; } else if (found) { free(myline); return bsdcap_nchktc(bp, dbarray, mybuf, mysize); } else if (errflag & fopen_fail) { free(mybuf); free(myline); return -2; } else { free(mybuf); free(myline); return -1; } /* if */ } /* * 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 cgetnum(bp, id, res) char *bp; char *id; long *res; { register int base; for (;;) { bp = bsdcap_skip(bp, ':'); if (*bp == 0) return (-1); if (*bp++ != id[0] || *bp == 0 || *bp++ != id[1]) continue; if (*bp == '@') return (-1); if (*bp != '#') continue; *res = strtoul(++bp, 0, 0); return (0); } } /* * 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 cgetcap(bp, id, sep) char *bp; char *id; int sep; { for (;;) { bp = bsdcap_skip(bp, sep); if (!*bp) return (0); if (*bp++ == id[0] && *bp != 0 && *bp++ == id[1]) { if (!*bp || *bp == sep) 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. */ int cgetstr(bp, id, area) char *bp; char *id; char **area; { for (;;) { bp = bsdcap_skip(bp, ':'); if (!*bp) return (-1); if (*bp++ != id[0] || *bp == 0 || *bp++ != id[1]) continue; if (*bp == '@') return (-1); if (*bp != '=') continue; bp++; return (bsdcap_decode(bp, area)); } } /* * Similar to bsdcap_getent except it returns the next enrty instead of * doing a lookup. */ int cgetnext(bp, dbarray) char **bp; char **dbarray; { char *mybuf; int mysize; int found = 0; cap_error_type errflag = no_fail; char *myline; int mylinesize; myline = malloc(mylinesize = 256); if (0 == myline) { return -2; } /* if */ mybuf = malloc(SAFE_BUFFER_SIZE); if (0 == mybuf) { free(myline); return -2; } mysize = SAFE_BUFFER_SIZE; while (!(errflag & malloc_fail) && !found && 0 != dbarray[bsdcap_dbidx]) { if (0 == bsdcap_fp) { bsdcap_fp = fopen(dbarray[bsdcap_dbidx], "r"); } /* if */ if (0 == bsdcap_fp) { errflag |= fopen_fail; continue; /* back to for() */ } /* if */ errflag &= ~read_fail; while (!(errflag & read_fail) && !feof(bsdcap_fp)) { int count = 0; int entry_complete = 0; mybuf[0] = 0; while (!entry_complete) { #if 0 int read_chars = getdelim(&myline, &mylinesize, '\n', bsdcap_fp); #else int read_chars = fgets(myline, 256, bsdcap_fp) ? strlen(myline) : (-1); #endif if (-1 == read_chars) { if (!feof(bsdcap_fp)) { errflag |= read_fail; } /* if */ break; } /* if */ if (count + read_chars + 1 > mysize) { char *newbuf = realloc(mybuf, count + read_chars + 1); if (0 == newbuf) { errflag |= malloc_fail; break; } /* if */ mybuf = newbuf; mysize = count + read_chars + 1; } /* if */ memcpy(mybuf + count, myline, read_chars); count += read_chars; /* is a continuation line required? */ if (count >= 2 && mybuf[count - 2] == '\\' && mybuf[count - 1] == '\n') { count -= 2; } else { entry_complete = 1; mybuf[count] = 0; } /* if */ } /* while */ if (errflag & (malloc_fail | read_fail) || feof(bsdcap_fp)) { break; /* out of outer while */ } else if ('#' != mybuf[0]) { found = 1; break; } /* if */ } /* while */ if (feof(bsdcap_fp) || (errflag & read_fail)) { fclose(bsdcap_fp); bsdcap_fp = 0; ++bsdcap_dbidx; } /* if */ } /* while */ if (errflag & (read_fail | fopen_fail)) { bsdcap_dbidx = 0; } /* if */ if (errflag & (malloc_fail | read_fail)) { free(mybuf); free(myline); return -2; } else if (found) { int status; free(myline); status = bsdcap_nchktc(bp, dbarray, mybuf, mysize); return (0 > status ? status : 1); } else if (errflag & fopen_fail) { free(mybuf); free(myline); return -2; } else { free(mybuf); free(myline); return -1; } /* if */ } void cgetclose() { if (0 != bsdcap_fp) { fclose(bsdcap_fp); bsdcap_fp = 0; } /* if */ bsdcap_dbidx = 0; } void cgetset(bp) char *bp; { bsdcap_set_entry = bp; }