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