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