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