1 /* $NetBSD: advcap.c,v 1.13 2011/12/10 19:14:29 roy Exp $ */ 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. 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(char *, char *); 91 int getent(char *, char *, char *); 92 int tnchktc(void); 93 int tnamatch(char *); 94 static char *tskip(char *); 95 int64_t tgetnum(char *); 96 int tgetflag(char *); 97 char *tgetstr(char *, char **); 98 static char *tdecode(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(char *bp, char *name) 107 { 108 char *cp; 109 110 remotefile = cp = conffile ? conffile : __UNCONST(_PATH_RTADVDCONF); 111 return (getent(bp, name, cp)); 112 } 113 114 int 115 getent(char *bp, char *name, char *cp) 116 { 117 int c; 118 int i = 0, cnt = 0; 119 char ibuf[BUFSIZ]; 120 int tf; 121 122 tbuf = bp; 123 tf = 0; 124 /* 125 * TERMCAP can have one of two things in it. It can be the 126 * name of a file to use instead of /etc/termcap. In this 127 * case it better start with a "/". Or it can be an entry to 128 * use so we don't have to read the file. In this case it 129 * has to already have the newlines crunched out. 130 */ 131 if (cp && *cp) { 132 tf = open(RM = cp, O_RDONLY); 133 } 134 if (tf < 0) { 135 syslog(LOG_INFO, 136 "<%s> open: %s", __func__, strerror(errno)); 137 return (-2); 138 } 139 for (;;) { 140 cp = bp; 141 for (;;) { 142 if (i == cnt) { 143 cnt = read(tf, ibuf, BUFSIZ); 144 if (cnt <= 0) { 145 close(tf); 146 return (0); 147 } 148 i = 0; 149 } 150 c = ibuf[i++]; 151 if (c == '\n') { 152 if (cp > bp && cp[-1] == '\\') { 153 cp--; 154 continue; 155 } 156 break; 157 } 158 if (cp >= bp + BUFSIZ) { 159 write(2,"Remcap entry too long\n", 23); 160 break; 161 } else 162 *cp++ = c; 163 } 164 *cp = 0; 165 166 /* 167 * The real work for the match. 168 */ 169 if (tnamatch(name)) { 170 close(tf); 171 return (tnchktc()); 172 } 173 } 174 } 175 176 /* 177 * tnchktc: check the last entry, see if it's tc=xxx. If so, 178 * recursively find xxx and append that entry (minus the names) 179 * to take the place of the tc=xxx entry. This allows termcap 180 * entries to say "like an HP2621 but doesn't turn on the labels". 181 * Note that this works because of the left to right scan. 182 */ 183 int 184 tnchktc(void) 185 { 186 char *p, *q; 187 char tcname[16]; /* name of similar terminal */ 188 char tcbuf[BUFSIZ]; 189 char *holdtbuf = tbuf; 190 int l; 191 192 p = tbuf + strlen(tbuf) - 2; /* before the last colon */ 193 while (*--p != ':') 194 if (p < tbuf) { 195 write(2, "Bad remcap entry\n", 18); 196 return (0); 197 } 198 p++; 199 /* p now points to beginning of last field */ 200 if (p[0] != 't' || p[1] != 'c') 201 return (1); 202 strlcpy(tcname, p + 3, sizeof tcname); 203 q = tcname; 204 while (*q && *q != ':') 205 q++; 206 *q = 0; 207 if (++hopcount > MAXHOP) { 208 write(2, "Infinite tc= loop\n", 18); 209 return (0); 210 } 211 if (getent(tcbuf, tcname, remotefile) != 1) { 212 return (0); 213 } 214 for (q = tcbuf; *q++ != ':'; ) 215 ; 216 l = p - holdtbuf + strlen(q); 217 if (l > BUFSIZ) { 218 write(2, "Remcap entry too long\n", 23); 219 q[BUFSIZ - (p-holdtbuf)] = 0; 220 } 221 strcpy(p, q); 222 tbuf = holdtbuf; 223 return (1); 224 } 225 226 /* 227 * Tnamatch deals with name matching. The first field of the termcap 228 * entry is a sequence of names separated by |'s, so we compare 229 * against each such name. The normal : terminator after the last 230 * name (before the first field) stops us. 231 */ 232 int 233 tnamatch(char *np) 234 { 235 char *Np, *Bp; 236 237 Bp = tbuf; 238 if (*Bp == '#') 239 return (0); 240 for (;;) { 241 for (Np = np; *Np && *Bp == *Np; Bp++, Np++) 242 continue; 243 if (*Np == 0 && (*Bp == '|' || *Bp == ':' || *Bp == 0)) 244 return (1); 245 while (*Bp && *Bp != ':' && *Bp != '|') 246 Bp++; 247 if (*Bp == 0 || *Bp == ':') 248 return (0); 249 Bp++; 250 } 251 } 252 253 /* 254 * Skip to the next field. Notice that this is very dumb, not 255 * knowing about \: escapes or any such. If necessary, :'s can be put 256 * into the termcap file in octal. 257 */ 258 static char * 259 tskip(char *bp) 260 { 261 int dquote; 262 263 dquote = 0; 264 while (*bp) { 265 switch (*bp) { 266 case ':': 267 if (!dquote) 268 goto breakbreak; 269 else 270 bp++; 271 break; 272 case '\\': 273 bp++; 274 if (isdigit((unsigned char)*bp)) { 275 while (isdigit((unsigned char)*bp++)) 276 ; 277 } else 278 bp++; 279 case '"': 280 dquote = (dquote ? 0 : 1); 281 bp++; 282 break; 283 default: 284 bp++; 285 break; 286 } 287 } 288 breakbreak: 289 if (*bp == ':') 290 bp++; 291 return (bp); 292 } 293 294 /* 295 * Return the (numeric) option id. 296 * Numeric options look like 297 * li#80 298 * i.e. the option string is separated from the numeric value by 299 * a # character. If the option is not found we return -1. 300 * Note that we handle octal numbers beginning with 0. 301 */ 302 int64_t 303 tgetnum(char *id) 304 { 305 int64_t i; 306 int base; 307 char *bp = tbuf; 308 309 for (;;) { 310 bp = tskip(bp); 311 if (*bp == 0) 312 return (-1); 313 if (strncmp(bp, id, strlen(id)) != 0) 314 continue; 315 bp += strlen(id); 316 if (*bp == '@') 317 return (-1); 318 if (*bp != '#') 319 continue; 320 bp++; 321 base = 10; 322 if (*bp == '0') 323 base = 8; 324 i = 0; 325 while (isdigit((unsigned char)*bp)) 326 i *= base, i += *bp++ - '0'; 327 return (i); 328 } 329 } 330 331 /* 332 * Handle a flag option. 333 * Flag options are given "naked", i.e. followed by a : or the end 334 * of the buffer. Return 1 if we find the option, or 0 if it is 335 * not given. 336 */ 337 int 338 tgetflag(char *id) 339 { 340 char *bp = tbuf; 341 342 for (;;) { 343 bp = tskip(bp); 344 if (!*bp) 345 return (0); 346 if (strncmp(bp, id, strlen(id)) == 0) { 347 bp += strlen(id); 348 if (!*bp || *bp == ':') 349 return (1); 350 else if (*bp == '@') 351 return (0); 352 } 353 } 354 } 355 356 /* 357 * Get a string valued option. 358 * These are given as 359 * cl=^Z 360 * Much decoding is done on the strings, and the strings are 361 * placed in area, which is a ref parameter which is updated. 362 * No checking on area overflow. 363 */ 364 char * 365 tgetstr(char *id, char **area) 366 { 367 char *bp = tbuf; 368 369 for (;;) { 370 bp = tskip(bp); 371 if (!*bp) 372 return (0); 373 if (strncmp(bp, id, strlen(id)) != 0) 374 continue; 375 bp += strlen(id); 376 if (*bp == '@') 377 return (0); 378 if (*bp != '=') 379 continue; 380 bp++; 381 return (tdecode(bp, area)); 382 } 383 } 384 385 /* 386 * Tdecode does the grung work to decode the 387 * string capability escapes. 388 */ 389 static char * 390 tdecode(char *str, char **area) 391 { 392 char *cp; 393 int c; 394 const char *dps = "E\033^^\\\\::n\nr\rt\tb\bf\f\"\"", *dp; 395 int i; 396 char term; 397 398 term = ':'; 399 cp = *area; 400 again: 401 if (*str == '"') { 402 term = '"'; 403 str++; 404 } 405 while ((c = *str++) && c != term) { 406 switch (c) { 407 408 case '^': 409 c = *str++ & 037; 410 break; 411 412 case '\\': 413 dp = dps; 414 c = *str++; 415 nextc: 416 if (*dp++ == c) { 417 c = *dp++; 418 break; 419 } 420 dp++; 421 if (*dp) 422 goto nextc; 423 if (isdigit((unsigned char)c)) { 424 c -= '0', i = 2; 425 do 426 c <<= 3, c |= *str++ - '0'; 427 while (--i && isdigit((unsigned char)*str)); 428 } 429 break; 430 } 431 *cp++ = c; 432 } 433 if (c == term && term != ':') { 434 term = ':'; 435 goto again; 436 } 437 *cp++ = 0; 438 str = *area; 439 *area = cp; 440 return (str); 441 } 442