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