1 /* 2 * Copyright (c) 1983, 1993 3 * The Regents of the University of California. All rights reserved. 4 * 5 * %sccs.include.redist.c% 6 */ 7 8 #ifndef lint 9 static char sccsid[] = "@(#)printcap.c 8.2 (Berkeley) 04/28/95"; 10 #endif /* not lint */ 11 12 #include <sys/param.h> 13 14 #include <fcntl.h> 15 #include <dirent.h> 16 #include <unistd.h> 17 #include <stdio.h> 18 #include <ctype.h> 19 #include <string.h> 20 #include "lp.h" 21 #include "pathnames.h" 22 23 #ifndef BUFSIZ 24 #define BUFSIZ 1024 25 #endif 26 #define MAXHOP 32 /* max number of tc= indirections */ 27 28 /* 29 * getcap-style interface for the old printcap routines. 30 * 31 * !!!USE THIS INTERFACE ONLY IF YOU DON'T HAVE THE REAL GETCAP!!! 32 */ 33 34 static char *pbp; /* pointer into pbuf for pgetstr() */ 35 static char pbuf[BUFSIZ]; /* buffer for capability strings */ 36 extern char line[]; /* buffer for printcap entries */ 37 38 int 39 cgetnext(bp, db_array) 40 register char **bp; 41 char **db_array; 42 { 43 int ret; 44 char *strdup(); 45 46 pbp = pbuf; 47 ret = getprent(line); 48 *bp = strdup(line); 49 return (ret); 50 } 51 52 int 53 cgetent(bp, db_array, name) 54 char **bp, **db_array, *name; 55 { 56 int i; 57 58 *bp = line; 59 pbp = pbuf; 60 i = pgetent(*bp, name); 61 if (i < 0) 62 return (-2); 63 else if (i == 0) 64 return (-1); 65 else 66 return (0); 67 } 68 69 char * 70 cgetcap(buf, cap, type) 71 char *buf, *cap; 72 int type; 73 { 74 return ((char *) pgetflag(cap)); 75 } 76 77 int 78 cgetstr(buf, cap, str) 79 char *buf, *cap; 80 char **str; 81 { 82 char *pgetstr __P((char *, char **)); 83 84 if (pbp >= pbuf+BUFSIZ) { 85 write(2, "Capability string buffer overflow\n", 34); 86 return (-1); 87 } 88 return ((*str = pgetstr(cap, &pbp)) == NULL ? -1 : 0); 89 } 90 91 int 92 cgetnum(buf, cap, num) 93 char *buf, *cap; 94 long *num; 95 { 96 return ((*num = pgetnum(cap)) < 0 ? -1 : 0); 97 } 98 99 int 100 cgetclose() 101 { 102 void endprent __P((void)); 103 104 endprent(); 105 return (0); 106 } 107 108 109 /* 110 * termcap - routines for dealing with the terminal capability data base 111 * 112 * BUG: Should use a "last" pointer in tbuf, so that searching 113 * for capabilities alphabetically would not be a n**2/2 114 * process when large numbers of capabilities are given. 115 * Note: If we add a last pointer now we will screw up the 116 * tc capability. We really should compile termcap. 117 * 118 * Essentially all the work here is scanning and decoding escapes 119 * in string capabilities. We don't use stdio because the editor 120 * doesn't, and because living w/o it is not hard. 121 */ 122 123 #define PRINTCAP 124 125 #ifdef PRINTCAP 126 #define tgetent pgetent 127 #define tskip pskip 128 #define tgetstr pgetstr 129 #define tdecode pdecode 130 #define tgetnum pgetnum 131 #define tgetflag pgetflag 132 #define tdecode pdecode 133 #define tnchktc pnchktc 134 #define tnamatch pnamatch 135 #define V6 136 #endif 137 138 static FILE *pfp = NULL; /* printcap data base file pointer */ 139 static char *tbuf; 140 static int hopcount; /* detect infinite loops in termcap, init 0 */ 141 static int tf; 142 143 char *tgetstr __P((char *, char **)); 144 static char *tskip __P((char *)); 145 static char *tdecode __P((char *, char **)); 146 147 /* 148 * Similar to tgetent except it returns the next enrty instead of 149 * doing a lookup. 150 */ 151 int 152 getprent(bp) 153 register char *bp; 154 { 155 register int c, skip = 0; 156 157 if (pfp == NULL && (pfp = fopen(_PATH_PRINTCAP, "r")) == NULL) 158 return(-1); 159 tbuf = bp; 160 for (;;) { 161 switch (c = getc(pfp)) { 162 case EOF: 163 fclose(pfp); 164 pfp = NULL; 165 return(0); 166 case '\n': 167 if (bp == tbuf) { 168 skip = 0; 169 continue; 170 } 171 if (bp[-1] == '\\') { 172 bp--; 173 continue; 174 } 175 *bp = '\0'; 176 return(1); 177 case '#': 178 if (bp == tbuf) 179 skip++; 180 default: 181 if (skip) 182 continue; 183 if (bp >= tbuf+BUFSIZ) { 184 write(2, "Termcap entry too long\n", 23); 185 *bp = '\0'; 186 return(1); 187 } 188 *bp++ = c; 189 } 190 } 191 } 192 193 void 194 endprent() 195 { 196 if (pfp != NULL) { 197 /* 198 * Can't use fclose here because on POSIX-compliant 199 * systems, fclose() causes the file pointer of the 200 * underlying file descriptor (which is possibly shared 201 * with a parent process) to be adjusted, and this 202 * reeks havoc in the parent because it doesn't know 203 * the file pointer has changed. 204 */ 205 (void) close(fileno(pfp)); 206 pfp = NULL; 207 } 208 } 209 210 /* 211 * Get an entry for terminal name in buffer bp, 212 * from the termcap file. Parse is very rudimentary; 213 * we just notice escaped newlines. 214 */ 215 int 216 tgetent(bp, name) 217 char *bp, *name; 218 { 219 register char *cp; 220 register int c; 221 register int i = 0, cnt = 0; 222 char ibuf[BUFSIZ]; 223 224 tbuf = bp; 225 #ifndef V6 226 cp = getenv("TERMCAP"); 227 /* 228 * TERMCAP can have one of two things in it. It can be the 229 * name of a file to use instead of /etc/termcap. In this 230 * case it better start with a "/". Or it can be an entry to 231 * use so we don't have to read the file. In this case it 232 * has to already have the newlines crunched out. 233 */ 234 if (cp && *cp) { 235 if (*cp!='/') { 236 cp2 = getenv("TERM"); 237 if (cp2==(char *) 0 || strcmp(name,cp2)==0) { 238 strcpy(bp,cp); 239 return(tnchktc()); 240 } else { 241 tf = open(_PATH_PRINTCAP, 0); 242 } 243 } else 244 tf = open(cp, 0); 245 } 246 #endif 247 if (tf==0) 248 tf = open(_PATH_PRINTCAP, 0); 249 if (tf < 0) 250 return (-1); 251 for (;;) { 252 cp = bp; 253 for (;;) { 254 if (i == cnt) { 255 cnt = read(tf, ibuf, BUFSIZ); 256 if (cnt <= 0) { 257 close(tf); 258 tf = 0; 259 return (0); 260 } 261 i = 0; 262 } 263 c = ibuf[i++]; 264 if (c == '\n') { 265 if (cp > bp && cp[-1] == '\\'){ 266 cp--; 267 continue; 268 } 269 break; 270 } 271 if (cp >= bp+BUFSIZ) { 272 write(2,"Termcap entry too long\n", 23); 273 break; 274 } else 275 *cp++ = c; 276 } 277 *cp = 0; 278 279 /* 280 * The real work for the match. 281 */ 282 if (tnamatch(name)) { 283 lseek(tf, 0L, 0); 284 i = tnchktc(); 285 if (tf) { 286 close(tf); 287 tf = 0; 288 } 289 return(i); 290 } 291 } 292 } 293 294 /* 295 * tnchktc: check the last entry, see if it's tc=xxx. If so, 296 * recursively find xxx and append that entry (minus the names) 297 * to take the place of the tc=xxx entry. This allows termcap 298 * entries to say "like an HP2621 but doesn't turn on the labels". 299 * Note that this works because of the left to right scan. 300 */ 301 int 302 tnchktc() 303 { 304 register char *p, *q; 305 char tcname[16]; /* name of similar terminal */ 306 char tcbuf[BUFSIZ]; 307 char *holdtbuf = tbuf; 308 int l; 309 310 p = tbuf + strlen(tbuf) - 2; /* before the last colon */ 311 while (*--p != ':') 312 if (p<tbuf) { 313 write(2, "Bad termcap entry\n", 18); 314 return (0); 315 } 316 p++; 317 /* p now points to beginning of last field */ 318 if (p[0] != 't' || p[1] != 'c') 319 return(1); 320 strcpy(tcname,p+3); 321 q = tcname; 322 while (q && *q != ':') 323 q++; 324 *q = 0; 325 if (++hopcount > MAXHOP) { 326 write(2, "Infinite tc= loop\n", 18); 327 return (0); 328 } 329 if (tgetent(tcbuf, tcname) != 1) 330 return(0); 331 for (q=tcbuf; *q != ':'; q++) 332 ; 333 l = p - holdtbuf + strlen(q); 334 if (l > BUFSIZ) { 335 write(2, "Termcap entry too long\n", 23); 336 q[BUFSIZ - (p-tbuf)] = 0; 337 } 338 strcpy(p, q+1); 339 tbuf = holdtbuf; 340 return(1); 341 } 342 343 /* 344 * Tnamatch deals with name matching. The first field of the termcap 345 * entry is a sequence of names separated by |'s, so we compare 346 * against each such name. The normal : terminator after the last 347 * name (before the first field) stops us. 348 */ 349 int 350 tnamatch(np) 351 char *np; 352 { 353 register char *Np, *Bp; 354 355 Bp = tbuf; 356 if (*Bp == '#') 357 return(0); 358 for (;;) { 359 for (Np = np; *Np && *Bp == *Np; Bp++, Np++) 360 continue; 361 if (*Np == 0 && (*Bp == '|' || *Bp == ':' || *Bp == 0)) 362 return (1); 363 while (*Bp && *Bp != ':' && *Bp != '|') 364 Bp++; 365 if (*Bp == 0 || *Bp == ':') 366 return (0); 367 Bp++; 368 } 369 } 370 371 /* 372 * Skip to the next field. Notice that this is very dumb, not 373 * knowing about \: escapes or any such. If necessary, :'s can be put 374 * into the termcap file in octal. 375 */ 376 static char * 377 tskip(bp) 378 register char *bp; 379 { 380 381 while (*bp && *bp != ':') 382 bp++; 383 if (*bp == ':') 384 bp++; 385 return (bp); 386 } 387 388 /* 389 * Return the (numeric) option id. 390 * Numeric options look like 391 * li#80 392 * i.e. the option string is separated from the numeric value by 393 * a # character. If the option is not found we return -1. 394 * Note that we handle octal numbers beginning with 0. 395 */ 396 int 397 tgetnum(id) 398 char *id; 399 { 400 register int i, base; 401 register char *bp = tbuf; 402 403 for (;;) { 404 bp = tskip(bp); 405 if (*bp == 0) 406 return (-1); 407 if (*bp++ != id[0] || *bp == 0 || *bp++ != id[1]) 408 continue; 409 if (*bp == '@') 410 return(-1); 411 if (*bp != '#') 412 continue; 413 bp++; 414 base = 10; 415 if (*bp == '0') 416 base = 8; 417 i = 0; 418 while (isdigit(*bp)) 419 i *= base, i += *bp++ - '0'; 420 return (i); 421 } 422 } 423 424 /* 425 * Handle a flag option. 426 * Flag options are given "naked", i.e. followed by a : or the end 427 * of the buffer. Return 1 if we find the option, or 0 if it is 428 * not given. 429 */ 430 int 431 tgetflag(id) 432 char *id; 433 { 434 register char *bp = tbuf; 435 436 for (;;) { 437 bp = tskip(bp); 438 if (!*bp) 439 return (0); 440 if (*bp++ == id[0] && *bp != 0 && *bp++ == id[1]) { 441 if (!*bp || *bp == ':') 442 return (1); 443 else if (*bp == '@') 444 return(0); 445 } 446 } 447 } 448 449 /* 450 * Get a string valued option. 451 * These are given as 452 * cl=^Z 453 * Much decoding is done on the strings, and the strings are 454 * placed in area, which is a ref parameter which is updated. 455 * No checking on area overflow. 456 */ 457 char * 458 tgetstr(id, area) 459 char *id, **area; 460 { 461 register char *bp = tbuf; 462 463 for (;;) { 464 bp = tskip(bp); 465 if (!*bp) 466 return (0); 467 if (*bp++ != id[0] || *bp == 0 || *bp++ != id[1]) 468 continue; 469 if (*bp == '@') 470 return(0); 471 if (*bp != '=') 472 continue; 473 bp++; 474 return (tdecode(bp, area)); 475 } 476 } 477 478 /* 479 * Tdecode does the grung work to decode the 480 * string capability escapes. 481 */ 482 static char * 483 tdecode(str, area) 484 register char *str; 485 char **area; 486 { 487 register char *cp; 488 register int c; 489 register char *dp; 490 int i; 491 492 cp = *area; 493 while ((c = *str++) && c != ':') { 494 switch (c) { 495 496 case '^': 497 c = *str++ & 037; 498 break; 499 500 case '\\': 501 dp = "E\033^^\\\\::n\nr\rt\tb\bf\f"; 502 c = *str++; 503 nextc: 504 if (*dp++ == c) { 505 c = *dp++; 506 break; 507 } 508 dp++; 509 if (*dp) 510 goto nextc; 511 if (isdigit(c)) { 512 c -= '0', i = 2; 513 do 514 c <<= 3, c |= *str++ - '0'; 515 while (--i && isdigit(*str)); 516 } 517 break; 518 } 519 *cp++ = c; 520 } 521 *cp++ = 0; 522 str = *area; 523 *area = cp; 524 return (str); 525 } 526