1 /* $FreeBSD$ */ 2 /* $KAME: advcap.c,v 1.11 2003/05/19 09:46:50 keiichi 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 int64_t 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 int c; 124 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", __func__, 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(STDERR_FILENO, "Remcap entry too long\n", 166 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 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(STDERR_FILENO, "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 strlcpy(tcname, p + 3, sizeof tcname); 210 q = tcname; 211 while (*q && *q != ':') 212 q++; 213 *q = 0; 214 if (++hopcount > MAXHOP) { 215 write(STDERR_FILENO, "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(STDERR_FILENO, "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 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 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 int64_t 312 tgetnum(id) 313 char *id; 314 { 315 int64_t i; 316 int base; 317 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 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 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 char *str; 404 char **area; 405 { 406 char *cp; 407 int c; 408 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