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