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