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