1 /* 2 * Copyright (c) 1983, 1993 3 * The Regents of the University of California. All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 3. All advertising materials mentioning features or use of this software 14 * must display the following acknowledgement: 15 * This product includes software developed by the University of 16 * California, Berkeley and its contributors. 17 * 4. Neither the name of the University nor the names of its contributors 18 * may be used to endorse or promote products derived from this software 19 * without specific prior written permission. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31 * SUCH DAMAGE. 32 */ 33 34 #ifndef lint 35 static char sccsid[] = "@(#)printcap.c 8.1 (Berkeley) 6/6/93"; 36 #endif /* not lint */ 37 38 #include <sys/param.h> 39 40 #include <fcntl.h> 41 #include <dirent.h> 42 #include <unistd.h> 43 #include <stdio.h> 44 #include <ctype.h> 45 #include <string.h> 46 #include "lp.h" 47 #include "pathnames.h" 48 49 #ifndef BUFSIZ 50 #define BUFSIZ 1024 51 #endif 52 #define MAXHOP 32 /* max number of tc= indirections */ 53 54 /* 55 * termcap - routines for dealing with the terminal capability data base 56 * 57 * BUG: Should use a "last" pointer in tbuf, so that searching 58 * for capabilities alphabetically would not be a n**2/2 59 * process when large numbers of capabilities are given. 60 * Note: If we add a last pointer now we will screw up the 61 * tc capability. We really should compile termcap. 62 * 63 * Essentially all the work here is scanning and decoding escapes 64 * in string capabilities. We don't use stdio because the editor 65 * doesn't, and because living w/o it is not hard. 66 */ 67 68 #define PRINTCAP 69 70 #ifdef PRINTCAP 71 #define tgetent pgetent 72 #define tskip pskip 73 #define tgetstr pgetstr 74 #define tdecode pdecode 75 #define tgetnum pgetnum 76 #define tgetflag pgetflag 77 #define tdecode pdecode 78 #define tnchktc pnchktc 79 #define tnamatch pnamatch 80 #define V6 81 #endif 82 83 static FILE *pfp = NULL; /* printcap data base file pointer */ 84 static char *tbuf; 85 static int hopcount; /* detect infinite loops in termcap, init 0 */ 86 87 static char *tskip __P((char *)); 88 static char *tskip __P((char *bp)); 89 static char *tdecode __P((char *, char **)); 90 91 /* 92 * Similar to tgetent except it returns the next enrty instead of 93 * doing a lookup. 94 */ 95 int 96 getprent(bp) 97 register char *bp; 98 { 99 register int c, skip = 0; 100 101 if (pfp == NULL && (pfp = fopen(_PATH_PRINTCAP, "r")) == NULL) 102 return(-1); 103 tbuf = bp; 104 for (;;) { 105 switch (c = getc(pfp)) { 106 case EOF: 107 fclose(pfp); 108 pfp = NULL; 109 return(0); 110 case '\n': 111 if (bp == tbuf) { 112 skip = 0; 113 continue; 114 } 115 if (bp[-1] == '\\') { 116 bp--; 117 continue; 118 } 119 *bp = '\0'; 120 return(1); 121 case '#': 122 if (bp == tbuf) 123 skip++; 124 default: 125 if (skip) 126 continue; 127 if (bp >= tbuf+BUFSIZ) { 128 write(2, "Termcap entry too long\n", 23); 129 *bp = '\0'; 130 return(1); 131 } 132 *bp++ = c; 133 } 134 } 135 } 136 137 void 138 endprent() 139 { 140 if (pfp != NULL) 141 fclose(pfp); 142 } 143 144 /* 145 * Get an entry for terminal name in buffer bp, 146 * from the termcap file. Parse is very rudimentary; 147 * we just notice escaped newlines. 148 */ 149 int 150 tgetent(bp, name) 151 char *bp, *name; 152 { 153 register char *cp; 154 register int c; 155 register int i = 0, cnt = 0; 156 char ibuf[BUFSIZ]; 157 int tf; 158 159 tbuf = bp; 160 tf = 0; 161 #ifndef V6 162 cp = getenv("TERMCAP"); 163 /* 164 * TERMCAP can have one of two things in it. It can be the 165 * name of a file to use instead of /etc/termcap. In this 166 * case it better start with a "/". Or it can be an entry to 167 * use so we don't have to read the file. In this case it 168 * has to already have the newlines crunched out. 169 */ 170 if (cp && *cp) { 171 if (*cp!='/') { 172 cp2 = getenv("TERM"); 173 if (cp2==(char *) 0 || strcmp(name,cp2)==0) { 174 strcpy(bp,cp); 175 return(tnchktc()); 176 } else { 177 tf = open(_PATH_PRINTCAP, 0); 178 } 179 } else 180 tf = open(cp, 0); 181 } 182 if (tf==0) 183 tf = open(_PATH_PRINTCAP, 0); 184 #else 185 tf = open(_PATH_PRINTCAP, 0); 186 #endif 187 if (tf < 0) 188 return (-1); 189 for (;;) { 190 cp = bp; 191 for (;;) { 192 if (i == cnt) { 193 cnt = read(tf, ibuf, BUFSIZ); 194 if (cnt <= 0) { 195 close(tf); 196 return (0); 197 } 198 i = 0; 199 } 200 c = ibuf[i++]; 201 if (c == '\n') { 202 if (cp > bp && cp[-1] == '\\'){ 203 cp--; 204 continue; 205 } 206 break; 207 } 208 if (cp >= bp+BUFSIZ) { 209 write(2,"Termcap entry too long\n", 23); 210 break; 211 } else 212 *cp++ = c; 213 } 214 *cp = 0; 215 216 /* 217 * The real work for the match. 218 */ 219 if (tnamatch(name)) { 220 close(tf); 221 return(tnchktc()); 222 } 223 } 224 } 225 226 /* 227 * tnchktc: check the last entry, see if it's tc=xxx. If so, 228 * recursively find xxx and append that entry (minus the names) 229 * to take the place of the tc=xxx entry. This allows termcap 230 * entries to say "like an HP2621 but doesn't turn on the labels". 231 * Note that this works because of the left to right scan. 232 */ 233 int 234 tnchktc() 235 { 236 register char *p, *q; 237 char tcname[16]; /* name of similar terminal */ 238 char tcbuf[BUFSIZ]; 239 char *holdtbuf = tbuf; 240 int l; 241 242 p = tbuf + strlen(tbuf) - 2; /* before the last colon */ 243 while (*--p != ':') 244 if (p<tbuf) { 245 write(2, "Bad termcap entry\n", 18); 246 return (0); 247 } 248 p++; 249 /* p now points to beginning of last field */ 250 if (p[0] != 't' || p[1] != 'c') 251 return(1); 252 strcpy(tcname,p+3); 253 q = tcname; 254 while (q && *q != ':') 255 q++; 256 *q = 0; 257 if (++hopcount > MAXHOP) { 258 write(2, "Infinite tc= loop\n", 18); 259 return (0); 260 } 261 if (tgetent(tcbuf, tcname) != 1) 262 return(0); 263 for (q=tcbuf; *q != ':'; q++) 264 ; 265 l = p - holdtbuf + strlen(q); 266 if (l > BUFSIZ) { 267 write(2, "Termcap entry too long\n", 23); 268 q[BUFSIZ - (p-tbuf)] = 0; 269 } 270 strcpy(p, q+1); 271 tbuf = holdtbuf; 272 return(1); 273 } 274 275 /* 276 * Tnamatch deals with name matching. The first field of the termcap 277 * entry is a sequence of names separated by |'s, so we compare 278 * against each such name. The normal : terminator after the last 279 * name (before the first field) stops us. 280 */ 281 int 282 tnamatch(np) 283 char *np; 284 { 285 register char *Np, *Bp; 286 287 Bp = tbuf; 288 if (*Bp == '#') 289 return(0); 290 for (;;) { 291 for (Np = np; *Np && *Bp == *Np; Bp++, Np++) 292 continue; 293 if (*Np == 0 && (*Bp == '|' || *Bp == ':' || *Bp == 0)) 294 return (1); 295 while (*Bp && *Bp != ':' && *Bp != '|') 296 Bp++; 297 if (*Bp == 0 || *Bp == ':') 298 return (0); 299 Bp++; 300 } 301 } 302 303 /* 304 * Skip to the next field. Notice that this is very dumb, not 305 * knowing about \: escapes or any such. If necessary, :'s can be put 306 * into the termcap file in octal. 307 */ 308 static char * 309 tskip(bp) 310 register char *bp; 311 { 312 313 while (*bp && *bp != ':') 314 bp++; 315 if (*bp == ':') 316 bp++; 317 return (bp); 318 } 319 320 /* 321 * Return the (numeric) option id. 322 * Numeric options look like 323 * li#80 324 * i.e. the option string is separated from the numeric value by 325 * a # character. If the option is not found we return -1. 326 * Note that we handle octal numbers beginning with 0. 327 */ 328 int 329 tgetnum(id) 330 char *id; 331 { 332 register int i, base; 333 register char *bp = tbuf; 334 335 for (;;) { 336 bp = tskip(bp); 337 if (*bp == 0) 338 return (-1); 339 if (*bp++ != id[0] || *bp == 0 || *bp++ != id[1]) 340 continue; 341 if (*bp == '@') 342 return(-1); 343 if (*bp != '#') 344 continue; 345 bp++; 346 base = 10; 347 if (*bp == '0') 348 base = 8; 349 i = 0; 350 while (isdigit(*bp)) 351 i *= base, i += *bp++ - '0'; 352 return (i); 353 } 354 } 355 356 /* 357 * Handle a flag option. 358 * Flag options are given "naked", i.e. followed by a : or the end 359 * of the buffer. Return 1 if we find the option, or 0 if it is 360 * not given. 361 */ 362 int 363 tgetflag(id) 364 char *id; 365 { 366 register char *bp = tbuf; 367 368 for (;;) { 369 bp = tskip(bp); 370 if (!*bp) 371 return (0); 372 if (*bp++ == id[0] && *bp != 0 && *bp++ == id[1]) { 373 if (!*bp || *bp == ':') 374 return (1); 375 else if (*bp == '@') 376 return(0); 377 } 378 } 379 } 380 381 /* 382 * Get a string valued option. 383 * These are given as 384 * cl=^Z 385 * Much decoding is done on the strings, and the strings are 386 * placed in area, which is a ref parameter which is updated. 387 * No checking on area overflow. 388 */ 389 char * 390 tgetstr(id, area) 391 char *id, **area; 392 { 393 register char *bp = tbuf; 394 395 for (;;) { 396 bp = tskip(bp); 397 if (!*bp) 398 return (0); 399 if (*bp++ != id[0] || *bp == 0 || *bp++ != id[1]) 400 continue; 401 if (*bp == '@') 402 return(0); 403 if (*bp != '=') 404 continue; 405 bp++; 406 return (tdecode(bp, area)); 407 } 408 } 409 410 /* 411 * Tdecode does the grung work to decode the 412 * string capability escapes. 413 */ 414 static char * 415 tdecode(str, area) 416 register char *str; 417 char **area; 418 { 419 register char *cp; 420 register int c; 421 register char *dp; 422 int i; 423 424 cp = *area; 425 while ((c = *str++) && c != ':') { 426 switch (c) { 427 428 case '^': 429 c = *str++ & 037; 430 break; 431 432 case '\\': 433 dp = "E\033^^\\\\::n\nr\rt\tb\bf\f"; 434 c = *str++; 435 nextc: 436 if (*dp++ == c) { 437 c = *dp++; 438 break; 439 } 440 dp++; 441 if (*dp) 442 goto nextc; 443 if (isdigit(c)) { 444 c -= '0', i = 2; 445 do 446 c <<= 3, c |= *str++ - '0'; 447 while (--i && isdigit(*str)); 448 } 449 break; 450 } 451 *cp++ = c; 452 } 453 *cp++ = 0; 454 str = *area; 455 *area = cp; 456 return (str); 457 } 458 459