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