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