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