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