1 /* 2 * Copyright (c) 1980 The 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 the above copyright notice and this paragraph are 7 * duplicated in all such forms and that any documentation, 8 * advertising materials, and other materials related to such 9 * distribution and use acknowledge that the software was developed 10 * by the University of California, Berkeley. The name of the 11 * University may not be used to endorse or promote products derived 12 * from this software without specific prior written permission. 13 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR 14 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED 15 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. 16 */ 17 18 #ifndef lint 19 static char sccsid[] = "@(#)termcap.c 5.4 (Berkeley) 05/10/89"; 20 #endif /* not lint */ 21 22 #define BUFSIZ 1024 23 #define MAXHOP 32 /* max number of tc= indirections */ 24 #define PBUFSIZ 512 /* max length of filename path */ 25 #define PVECSIZ 32 /* max number of names in path */ 26 27 #include <ctype.h> 28 #include "pathnames.h" 29 30 /* 31 * termcap - routines for dealing with the terminal capability data base 32 * 33 * BUG: Should use a "last" pointer in tbuf, so that searching 34 * for capabilities alphabetically would not be a n**2/2 35 * process when large numbers of capabilities are given. 36 * Note: If we add a last pointer now we will screw up the 37 * tc capability. We really should compile termcap. 38 * 39 * Essentially all the work here is scanning and decoding escapes 40 * in string capabilities. We don't use stdio because the editor 41 * doesn't, and because living w/o it is not hard. 42 */ 43 44 static char *tbuf; 45 static int hopcount; /* detect infinite loops in termcap, init 0 */ 46 static char pathbuf[PBUFSIZ]; /* holds raw path of filenames */ 47 static char *pathvec[PVECSIZ]; /* to point to names in pathbuf */ 48 static char **pvec; /* holds usable tail of path vector */ 49 char *tskip(); 50 char *tgetstr(); 51 char *tdecode(); 52 char *getenv(); 53 54 /* 55 * Get an entry for terminal name in buffer bp from the termcap file. 56 */ 57 tgetent(bp, name) 58 char *bp, *name; 59 { 60 register char *p; 61 register char *cp; 62 register int c; 63 char *term, *home, *termpath; 64 char **fname = pathvec; 65 66 pvec = pathvec; 67 tbuf = bp; 68 p = pathbuf; 69 cp = getenv("TERMCAP"); 70 /* 71 * TERMCAP can have one of two things in it. It can be the 72 * name of a file to use instead of /etc/termcap. In this 73 * case it better start with a "/". Or it can be an entry to 74 * use so we don't have to read the file. In this case it 75 * has to already have the newlines crunched out. If TERMCAP 76 * does not hold a file name then a path of names is searched 77 * instead. The path is found in the TERMPATH variable, or 78 * becomes "$HOME/.termcap /etc/termcap" if no TERMPATH exists. 79 */ 80 if (!cp || *cp != '/') { /* no TERMCAP or it holds an entry */ 81 if (termpath = getenv("TERMPATH")) 82 strncpy(pathbuf, termpath, PBUFSIZ); 83 else { 84 if (home = getenv("HOME")) { /* set up default */ 85 p += strlen(home); /* path, looking in */ 86 strcpy(pathbuf, home); /* $HOME first */ 87 *p++ = '/'; 88 } /* if no $HOME look in current directory */ 89 strncpy(p, _PATH_DEF, PBUFSIZ - (p - pathbuf)); 90 } 91 } 92 else /* user-defined name in TERMCAP */ 93 strncpy(pathbuf, cp, PBUFSIZ); /* still can be tokenized */ 94 95 *fname++ = pathbuf; /* tokenize path into vector of names */ 96 while (*++p) 97 if (*p == ' ' || *p == ':') { 98 *p = '\0'; 99 while (*++p) 100 if (*p != ' ' && *p != ':') 101 break; 102 if (*p == '\0') 103 break; 104 *fname++ = p; 105 if (fname >= pathvec + PVECSIZ) { 106 fname--; 107 break; 108 } 109 } 110 *fname = (char *) 0; /* mark end of vector */ 111 if (cp && *cp && *cp != '/') { 112 tbuf = cp; 113 c = tnamatch(name); 114 tbuf = bp; 115 if (c) { 116 strcpy(bp,cp); 117 return (tnchktc()); 118 } 119 } 120 return (tfindent(bp, name)); /* find terminal entry in path */ 121 } 122 123 /* 124 * tfindent - reads through the list of files in pathvec as if they were one 125 * continuous file searching for terminal entries along the way. It will 126 * participate in indirect recursion if the call to tnchktc() finds a tc= 127 * field, which is only searched for in the current file and files ocurring 128 * after it in pathvec. The usable part of this vector is kept in the global 129 * variable pvec. Terminal entries may not be broken across files. Parse is 130 * very rudimentary; we just notice escaped newlines. 131 */ 132 tfindent(bp, name) 133 char *bp, *name; 134 { 135 register char *cp; 136 register int c; 137 register int i, cnt; 138 char ibuf[BUFSIZ]; 139 int opencnt = 0; 140 int tf; 141 142 tbuf = bp; 143 nextfile: 144 i = cnt = 0; 145 while (*pvec && (tf = open(*pvec, 0)) < 0) 146 pvec++; 147 if (!*pvec) 148 return (opencnt ? 0 : -1); 149 opencnt++; 150 for (;;) { 151 cp = bp; 152 for (;;) { 153 if (i == cnt) { 154 cnt = read(tf, ibuf, BUFSIZ); 155 if (cnt <= 0) { 156 close(tf); 157 pvec++; 158 goto nextfile; 159 } 160 i = 0; 161 } 162 c = ibuf[i++]; 163 if (c == '\n') { 164 if (cp > bp && cp[-1] == '\\'){ 165 cp--; 166 continue; 167 } 168 break; 169 } 170 if (cp >= bp+BUFSIZ) { 171 write(2,"Termcap entry too long\n", 23); 172 break; 173 } else 174 *cp++ = c; 175 } 176 *cp = 0; 177 178 /* 179 * The real work for the match. 180 */ 181 if (tnamatch(name)) { 182 close(tf); 183 return(tnchktc()); 184 } 185 } 186 } 187 188 /* 189 * tnchktc: check the last entry, see if it's tc=xxx. If so, 190 * recursively find xxx and append that entry (minus the names) 191 * to take the place of the tc=xxx entry. This allows termcap 192 * entries to say "like an HP2621 but doesn't turn on the labels". 193 * Note that this works because of the left to right scan. 194 */ 195 tnchktc() 196 { 197 register char *p, *q; 198 char tcname[16]; /* name of similar terminal */ 199 char tcbuf[BUFSIZ]; 200 char *holdtbuf = tbuf; 201 int l; 202 203 p = tbuf + strlen(tbuf) - 2; /* before the last colon */ 204 while (*--p != ':') 205 if (p<tbuf) { 206 write(2, "Bad termcap entry\n", 18); 207 return (0); 208 } 209 p++; 210 /* p now points to beginning of last field */ 211 if (p[0] != 't' || p[1] != 'c') 212 return(1); 213 strcpy(tcname,p+3); 214 q = tcname; 215 while (*q && *q != ':') 216 q++; 217 *q = 0; 218 if (++hopcount > MAXHOP) { 219 write(2, "Infinite tc= loop\n", 18); 220 return (0); 221 } 222 if (tfindent(tcbuf, tcname) != 1) { 223 hopcount = 0; /* unwind recursion */ 224 return(0); 225 } 226 for (q=tcbuf; *q != ':'; q++) 227 ; 228 l = p - holdtbuf + strlen(q); 229 if (l > BUFSIZ) { 230 write(2, "Termcap entry too long\n", 23); 231 q[BUFSIZ - (p-tbuf)] = 0; 232 } 233 strcpy(p, q+1); 234 tbuf = holdtbuf; 235 hopcount = 0; /* unwind recursion */ 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