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