1 /* $FreeBSD$ */ 2 /* $KAME: advcap.c,v 1.5 2001/02/01 09:12:08 jinmei Exp $ */ 3 4 /* 5 * Copyright (c) 1983 The Regents of the University of California. 6 * All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 3. All advertising materials mentioning features or use of this software 17 * must display the following acknowledgement: 18 * This product includes software developed by the University of 19 * California, Berkeley and its contributors. 20 * 4. Neither the name of the University nor the names of its contributors 21 * may be used to endorse or promote products derived from this software 22 * without specific prior written permission. 23 * 24 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 25 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 26 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 27 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 28 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 29 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 30 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 31 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 32 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 33 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 34 * SUCH DAMAGE. 35 */ 36 37 /* 38 * remcap - routines for dealing with the remote host data base 39 * 40 * derived from termcap 41 */ 42 #include <sys/types.h> 43 #include <sys/uio.h> 44 #include <unistd.h> 45 #include <fcntl.h> 46 #include <ctype.h> 47 #include <stdlib.h> 48 #include <stdio.h> 49 #include <syslog.h> 50 #include <errno.h> 51 #include <string.h> 52 #include "pathnames.h" 53 54 #ifndef BUFSIZ 55 #define BUFSIZ 1024 56 #endif 57 #define MAXHOP 32 /* max number of tc= indirections */ 58 59 #define tgetent agetent 60 #define tnchktc anchktc 61 #define tnamatch anamatch 62 #define tgetnum agetnum 63 #define tgetflag agetflag 64 #define tgetstr agetstr 65 66 #if 0 67 #define V_TERMCAP "REMOTE" 68 #define V_TERM "HOST" 69 #endif 70 71 char *RM; 72 73 /* 74 * termcap - routines for dealing with the terminal capability data base 75 * 76 * BUG: Should use a "last" pointer in tbuf, so that searching 77 * for capabilities alphabetically would not be a n**2/2 78 * process when large numbers of capabilities are given. 79 * Note: If we add a last pointer now we will screw up the 80 * tc capability. We really should compile termcap. 81 * 82 * Essentially all the work here is scanning and decoding escapes 83 * in string capabilities. We don't use stdio because the editor 84 * doesn't, and because living w/o it is not hard. 85 */ 86 87 static char *tbuf; 88 static int hopcount; /* detect infinite loops in termcap, init 0 */ 89 90 static char *remotefile; 91 92 extern char *conffile; 93 94 int tgetent __P((char *, char *)); 95 int getent __P((char *, char *, char *)); 96 int tnchktc __P((void)); 97 int tnamatch __P((char *)); 98 static char *tskip __P((char *)); 99 long long tgetnum __P((char *)); 100 int tgetflag __P((char *)); 101 char *tgetstr __P((char *, char **)); 102 static char *tdecode __P((char *, char **)); 103 104 /* 105 * Get an entry for terminal name in buffer bp, 106 * from the termcap file. Parse is very rudimentary; 107 * we just notice escaped newlines. 108 */ 109 int 110 tgetent(bp, name) 111 char *bp, *name; 112 { 113 char *cp; 114 115 remotefile = cp = conffile ? conffile : _PATH_RTADVDCONF; 116 return (getent(bp, name, cp)); 117 } 118 119 int 120 getent(bp, name, cp) 121 char *bp, *name, *cp; 122 { 123 register int c; 124 register int i = 0, cnt = 0; 125 char ibuf[BUFSIZ]; 126 int tf; 127 128 tbuf = bp; 129 tf = 0; 130 /* 131 * TERMCAP can have one of two things in it. It can be the 132 * name of a file to use instead of /etc/termcap. In this 133 * case it better start with a "/". Or it can be an entry to 134 * use so we don't have to read the file. In this case it 135 * has to already have the newlines crunched out. 136 */ 137 if (cp && *cp) { 138 tf = open(RM = cp, O_RDONLY); 139 } 140 if (tf < 0) { 141 syslog(LOG_INFO, 142 "<%s> open: %s", __FUNCTION__, strerror(errno)); 143 return (-2); 144 } 145 for (;;) { 146 cp = bp; 147 for (;;) { 148 if (i == cnt) { 149 cnt = read(tf, ibuf, BUFSIZ); 150 if (cnt <= 0) { 151 close(tf); 152 return (0); 153 } 154 i = 0; 155 } 156 c = ibuf[i++]; 157 if (c == '\n') { 158 if (cp > bp && cp[-1] == '\\') { 159 cp--; 160 continue; 161 } 162 break; 163 } 164 if (cp >= bp+BUFSIZ) { 165 write(2,"Remcap entry too long\n", 23); 166 break; 167 } else 168 *cp++ = c; 169 } 170 *cp = 0; 171 172 /* 173 * The real work for the match. 174 */ 175 if (tnamatch(name)) { 176 close(tf); 177 return (tnchktc()); 178 } 179 } 180 } 181 182 /* 183 * tnchktc: check the last entry, see if it's tc=xxx. If so, 184 * recursively find xxx and append that entry (minus the names) 185 * to take the place of the tc=xxx entry. This allows termcap 186 * entries to say "like an HP2621 but doesn't turn on the labels". 187 * Note that this works because of the left to right scan. 188 */ 189 int 190 tnchktc() 191 { 192 register char *p, *q; 193 char tcname[16]; /* name of similar terminal */ 194 char tcbuf[BUFSIZ]; 195 char *holdtbuf = tbuf; 196 int l; 197 198 p = tbuf + strlen(tbuf) - 2; /* before the last colon */ 199 while (*--p != ':') 200 if (p<tbuf) { 201 write(2, "Bad remcap entry\n", 18); 202 return (0); 203 } 204 p++; 205 /* p now points to beginning of last field */ 206 if (p[0] != 't' || p[1] != 'c') 207 return (1); 208 strcpy(tcname, p+3); 209 q = tcname; 210 while (*q && *q != ':') 211 q++; 212 *q = 0; 213 if (++hopcount > MAXHOP) { 214 write(2, "Infinite tc= loop\n", 18); 215 return (0); 216 } 217 if (getent(tcbuf, tcname, remotefile) != 1) { 218 return (0); 219 } 220 for (q = tcbuf; *q++ != ':'; ) 221 ; 222 l = p - holdtbuf + strlen(q); 223 if (l > BUFSIZ) { 224 write(2, "Remcap entry too long\n", 23); 225 q[BUFSIZ - (p-holdtbuf)] = 0; 226 } 227 strcpy(p, q); 228 tbuf = holdtbuf; 229 return (1); 230 } 231 232 /* 233 * Tnamatch deals with name matching. The first field of the termcap 234 * entry is a sequence of names separated by |'s, so we compare 235 * against each such name. The normal : terminator after the last 236 * name (before the first field) stops us. 237 */ 238 int 239 tnamatch(np) 240 char *np; 241 { 242 register char *Np, *Bp; 243 244 Bp = tbuf; 245 if (*Bp == '#') 246 return (0); 247 for (;;) { 248 for (Np = np; *Np && *Bp == *Np; Bp++, Np++) 249 continue; 250 if (*Np == 0 && (*Bp == '|' || *Bp == ':' || *Bp == 0)) 251 return (1); 252 while (*Bp && *Bp != ':' && *Bp != '|') 253 Bp++; 254 if (*Bp == 0 || *Bp == ':') 255 return (0); 256 Bp++; 257 } 258 } 259 260 /* 261 * Skip to the next field. Notice that this is very dumb, not 262 * knowing about \: escapes or any such. If necessary, :'s can be put 263 * into the termcap file in octal. 264 */ 265 static char * 266 tskip(bp) 267 register char *bp; 268 { 269 int dquote; 270 271 dquote = 0; 272 while (*bp) { 273 switch (*bp) { 274 case ':': 275 if (!dquote) 276 goto breakbreak; 277 else 278 bp++; 279 break; 280 case '\\': 281 bp++; 282 if (isdigit(*bp)) { 283 while (isdigit(*bp++)) 284 ; 285 } else 286 bp++; 287 case '"': 288 dquote = (dquote ? 1 : 0); 289 bp++; 290 break; 291 default: 292 bp++; 293 break; 294 } 295 } 296 breakbreak: 297 if (*bp == ':') 298 bp++; 299 return (bp); 300 } 301 302 /* 303 * Return the (numeric) option id. 304 * Numeric options look like 305 * li#80 306 * i.e. the option string is separated from the numeric value by 307 * a # character. If the option is not found we return -1. 308 * Note that we handle octal numbers beginning with 0. 309 */ 310 long long 311 tgetnum(id) 312 char *id; 313 { 314 register long long i; 315 register int base; 316 register char *bp = tbuf; 317 318 for (;;) { 319 bp = tskip(bp); 320 if (*bp == 0) 321 return (-1); 322 if (strncmp(bp, id, strlen(id)) != 0) 323 continue; 324 bp += strlen(id); 325 if (*bp == '@') 326 return (-1); 327 if (*bp != '#') 328 continue; 329 bp++; 330 base = 10; 331 if (*bp == '0') 332 base = 8; 333 i = 0; 334 while (isdigit(*bp)) 335 i *= base, i += *bp++ - '0'; 336 return (i); 337 } 338 } 339 340 /* 341 * Handle a flag option. 342 * Flag options are given "naked", i.e. followed by a : or the end 343 * of the buffer. Return 1 if we find the option, or 0 if it is 344 * not given. 345 */ 346 int 347 tgetflag(id) 348 char *id; 349 { 350 register char *bp = tbuf; 351 352 for (;;) { 353 bp = tskip(bp); 354 if (!*bp) 355 return (0); 356 if (strncmp(bp, id, strlen(id)) == 0) { 357 bp += strlen(id); 358 if (!*bp || *bp == ':') 359 return (1); 360 else if (*bp == '@') 361 return (0); 362 } 363 } 364 } 365 366 /* 367 * Get a string valued option. 368 * These are given as 369 * cl=^Z 370 * Much decoding is done on the strings, and the strings are 371 * placed in area, which is a ref parameter which is updated. 372 * No checking on area overflow. 373 */ 374 char * 375 tgetstr(id, area) 376 char *id, **area; 377 { 378 register char *bp = tbuf; 379 380 for (;;) { 381 bp = tskip(bp); 382 if (!*bp) 383 return (0); 384 if (strncmp(bp, id, strlen(id)) != 0) 385 continue; 386 bp += strlen(id); 387 if (*bp == '@') 388 return (0); 389 if (*bp != '=') 390 continue; 391 bp++; 392 return (tdecode(bp, area)); 393 } 394 } 395 396 /* 397 * Tdecode does the grung work to decode the 398 * string capability escapes. 399 */ 400 static char * 401 tdecode(str, area) 402 register char *str; 403 char **area; 404 { 405 register char *cp; 406 register int c; 407 register char *dp; 408 int i; 409 char term; 410 411 term = ':'; 412 cp = *area; 413 again: 414 if (*str == '"') { 415 term = '"'; 416 str++; 417 } 418 while ((c = *str++) && c != term) { 419 switch (c) { 420 421 case '^': 422 c = *str++ & 037; 423 break; 424 425 case '\\': 426 dp = "E\033^^\\\\::n\nr\rt\tb\bf\f\"\""; 427 c = *str++; 428 nextc: 429 if (*dp++ == c) { 430 c = *dp++; 431 break; 432 } 433 dp++; 434 if (*dp) 435 goto nextc; 436 if (isdigit(c)) { 437 c -= '0', i = 2; 438 do 439 c <<= 3, c |= *str++ - '0'; 440 while (--i && isdigit(*str)); 441 } 442 break; 443 } 444 *cp++ = c; 445 } 446 if (c == term && term != ':') { 447 term = ':'; 448 goto again; 449 } 450 *cp++ = 0; 451 str = *area; 452 *area = cp; 453 return (str); 454 } 455