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