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