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