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