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