1 #ifndef lint 2 static char sccsid[] = "@(#)printcap.c 1.6 (Berkeley) 07/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 } 411