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