1 /* printcap.c 1.3 81/06/01 */ 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 "local/uparm.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 char *tbuf; 40 static int hopcount; /* detect infinite loops in termcap, init 0 */ 41 char *tskip(); 42 char *tgetstr(); 43 char *tdecode(); 44 char *getenv(); 45 46 /* 47 * Get an entry for terminal name in buffer bp, 48 * from the termcap file. Parse is very rudimentary; 49 * we just notice escaped newlines. 50 */ 51 tgetent(bp, name) 52 char *bp, *name; 53 { 54 register char *cp; 55 register int c; 56 register int i = 0, cnt = 0; 57 char ibuf[BUFSIZ]; 58 char *cp2; 59 int tf; 60 61 tbuf = bp; 62 tf = 0; 63 #ifndef V6 64 cp = getenv("TERMCAP"); 65 /* 66 * TERMCAP can have one of two things in it. It can be the 67 * name of a file to use instead of /etc/termcap. In this 68 * case it better start with a "/". Or it can be an entry to 69 * use so we don't have to read the file. In this case it 70 * has to already have the newlines crunched out. 71 */ 72 if (cp && *cp) { 73 if (*cp!='/') { 74 cp2 = getenv("TERM"); 75 if (cp2==(char *) 0 || strcmp(name,cp2)==0) { 76 strcpy(bp,cp); 77 return(tnchktc()); 78 } else { 79 tf = open(E_TERMCAP, 0); 80 } 81 } else 82 tf = open(cp, 0); 83 } 84 if (tf==0) 85 tf = open(E_TERMCAP, 0); 86 #else 87 tf = open(E_TERMCAP, 0); 88 #endif 89 if (tf < 0) 90 return (-1); 91 for (;;) { 92 cp = bp; 93 for (;;) { 94 if (i == cnt) { 95 cnt = read(tf, ibuf, BUFSIZ); 96 if (cnt <= 0) { 97 close(tf); 98 return (0); 99 } 100 i = 0; 101 } 102 c = ibuf[i++]; 103 if (c == '\n') { 104 if (cp > bp && cp[-1] == '\\'){ 105 cp--; 106 continue; 107 } 108 break; 109 } 110 if (cp >= bp+BUFSIZ) { 111 write(2,"Termcap entry too long\n", 23); 112 break; 113 } else 114 *cp++ = c; 115 } 116 *cp = 0; 117 118 /* 119 * The real work for the match. 120 */ 121 if (tnamatch(name)) { 122 close(tf); 123 return(tnchktc()); 124 } 125 } 126 } 127 128 /* 129 * tnchktc: check the last entry, see if it's tc=xxx. If so, 130 * recursively find xxx and append that entry (minus the names) 131 * to take the place of the tc=xxx entry. This allows termcap 132 * entries to say "like an HP2621 but doesn't turn on the labels". 133 * Note that this works because of the left to right scan. 134 */ 135 tnchktc() 136 { 137 register char *p, *q; 138 char tcname[16]; /* name of similar terminal */ 139 char tcbuf[BUFSIZ]; 140 char *holdtbuf = tbuf; 141 int l; 142 143 p = tbuf + strlen(tbuf) - 2; /* before the last colon */ 144 while (*--p != ':') 145 if (p<tbuf) { 146 write(2, "Bad termcap entry\n", 18); 147 return (0); 148 } 149 p++; 150 /* p now points to beginning of last field */ 151 if (p[0] != 't' || p[1] != 'c') 152 return(1); 153 strcpy(tcname,p+3); 154 q = tcname; 155 while (q && *q != ':') 156 q++; 157 *q = 0; 158 if (++hopcount > MAXHOP) { 159 write(2, "Infinite tc= loop\n", 18); 160 return (0); 161 } 162 if (tgetent(tcbuf, tcname) != 1) 163 return(0); 164 for (q=tcbuf; *q != ':'; q++) 165 ; 166 l = p - holdtbuf + strlen(q); 167 if (l > BUFSIZ) { 168 write(2, "Termcap entry too long\n", 23); 169 q[BUFSIZ - (p-tbuf)] = 0; 170 } 171 strcpy(p, q+1); 172 tbuf = holdtbuf; 173 return(1); 174 } 175 176 /* 177 * Tnamatch deals with name matching. The first field of the termcap 178 * entry is a sequence of names separated by |'s, so we compare 179 * against each such name. The normal : terminator after the last 180 * name (before the first field) stops us. 181 */ 182 tnamatch(np) 183 char *np; 184 { 185 register char *Np, *Bp; 186 187 Bp = tbuf; 188 if (*Bp == '#') 189 return(0); 190 for (;;) { 191 for (Np = np; *Np && *Bp == *Np; Bp++, Np++) 192 continue; 193 if (*Np == 0 && (*Bp == '|' || *Bp == ':' || *Bp == 0)) 194 return (1); 195 while (*Bp && *Bp != ':' && *Bp != '|') 196 Bp++; 197 if (*Bp == 0 || *Bp == ':') 198 return (0); 199 Bp++; 200 } 201 } 202 203 /* 204 * Skip to the next field. Notice that this is very dumb, not 205 * knowing about \: escapes or any such. If necessary, :'s can be put 206 * into the termcap file in octal. 207 */ 208 static char * 209 tskip(bp) 210 register char *bp; 211 { 212 213 while (*bp && *bp != ':') 214 bp++; 215 if (*bp == ':') 216 bp++; 217 return (bp); 218 } 219 220 /* 221 * Return the (numeric) option id. 222 * Numeric options look like 223 * li#80 224 * i.e. the option string is separated from the numeric value by 225 * a # character. If the option is not found we return -1. 226 * Note that we handle octal numbers beginning with 0. 227 */ 228 tgetnum(id) 229 char *id; 230 { 231 register int i, base; 232 register char *bp = tbuf; 233 234 for (;;) { 235 bp = tskip(bp); 236 if (*bp == 0) 237 return (-1); 238 if (*bp++ != id[0] || *bp == 0 || *bp++ != id[1]) 239 continue; 240 if (*bp == '@') 241 return(-1); 242 if (*bp != '#') 243 continue; 244 bp++; 245 base = 10; 246 if (*bp == '0') 247 base = 8; 248 i = 0; 249 while (isdigit(*bp)) 250 i *= base, i += *bp++ - '0'; 251 return (i); 252 } 253 } 254 255 /* 256 * Handle a flag option. 257 * Flag options are given "naked", i.e. followed by a : or the end 258 * of the buffer. Return 1 if we find the option, or 0 if it is 259 * not given. 260 */ 261 tgetflag(id) 262 char *id; 263 { 264 register char *bp = tbuf; 265 266 for (;;) { 267 bp = tskip(bp); 268 if (!*bp) 269 return (0); 270 if (*bp++ == id[0] && *bp != 0 && *bp++ == id[1]) { 271 if (!*bp || *bp == ':') 272 return (1); 273 else if (*bp == '@') 274 return(0); 275 } 276 } 277 } 278 279 /* 280 * Get a string valued option. 281 * These are given as 282 * cl=^Z 283 * Much decoding is done on the strings, and the strings are 284 * placed in area, which is a ref parameter which is updated. 285 * No checking on area overflow. 286 */ 287 char * 288 tgetstr(id, area) 289 char *id, **area; 290 { 291 register char *bp = tbuf; 292 293 for (;;) { 294 bp = tskip(bp); 295 if (!*bp) 296 return (0); 297 if (*bp++ != id[0] || *bp == 0 || *bp++ != id[1]) 298 continue; 299 if (*bp == '@') 300 return(0); 301 if (*bp != '=') 302 continue; 303 bp++; 304 return (tdecode(bp, area)); 305 } 306 } 307 308 /* 309 * Tdecode does the grung work to decode the 310 * string capability escapes. 311 */ 312 static char * 313 tdecode(str, area) 314 register char *str; 315 char **area; 316 { 317 register char *cp; 318 register int c; 319 register char *dp; 320 int i; 321 322 cp = *area; 323 while ((c = *str++) && c != ':') { 324 switch (c) { 325 326 case '^': 327 c = *str++ & 037; 328 break; 329 330 case '\\': 331 dp = "E\033^^\\\\::n\nr\rt\tb\bf\f"; 332 c = *str++; 333 nextc: 334 if (*dp++ == c) { 335 c = *dp++; 336 break; 337 } 338 dp++; 339 if (*dp) 340 goto nextc; 341 if (isdigit(c)) { 342 c -= '0', i = 2; 343 do 344 c <<= 3, c |= *str++ - '0'; 345 while (--i && isdigit(*str)); 346 } 347 break; 348 } 349 *cp++ = c; 350 } 351 *cp++ = 0; 352 str = *area; 353 *area = cp; 354 return (str); 355 } 356