1 /* $NetBSD: advcap.c,v 1.15 2015/06/05 15:41:59 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 __UNCONST 51 #define __UNCONST(a) ((void *)(unsigned long)(const void *)(a)) 52 #endif 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(char *, char *); 95 int getent(char *, char *, char *); 96 int tnchktc(void); 97 int tnamatch(char *); 98 static char *tskip(char *); 99 int64_t tgetnum(char *); 100 int tgetflag(char *); 101 char *tgetstr(char *, char **); 102 static char *tdecode(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(char *bp, char *name) 111 { 112 char *cp; 113 114 remotefile = cp = conffile ? conffile : __UNCONST(_PATH_RTADVDCONF); 115 return (getent(bp, name, cp)); 116 } 117 118 int 119 getent(char *bp, char *name, char *cp) 120 { 121 int c; 122 int i = 0, cnt = 0; 123 char ibuf[BUFSIZ]; 124 int tf; 125 126 tbuf = bp; 127 tf = 0; 128 /* 129 * TERMCAP can have one of two things in it. It can be the 130 * name of a file to use instead of /etc/termcap. In this 131 * case it better start with a "/". Or it can be an entry to 132 * use so we don't have to read the file. In this case it 133 * has to already have the newlines crunched out. 134 */ 135 if (cp && *cp) { 136 tf = open(RM = cp, O_RDONLY); 137 } 138 if (tf < 0) { 139 syslog(LOG_INFO, "<%s> open: %m", __func__); 140 return (-2); 141 } 142 for (;;) { 143 cp = bp; 144 for (;;) { 145 if (i == cnt) { 146 cnt = read(tf, ibuf, BUFSIZ); 147 if (cnt <= 0) { 148 close(tf); 149 return (0); 150 } 151 i = 0; 152 } 153 c = ibuf[i++]; 154 if (c == '\n') { 155 if (cp > bp && cp[-1] == '\\') { 156 cp--; 157 continue; 158 } 159 break; 160 } 161 if (cp >= bp + BUFSIZ) { 162 write(2,"Remcap entry too long\n", 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(void) 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(2, "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(2, "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(2, "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(char *np) 237 { 238 char *Np, *Bp; 239 240 Bp = tbuf; 241 if (*Bp == '#') 242 return (0); 243 for (;;) { 244 for (Np = np; *Np && *Bp == *Np; Bp++, Np++) 245 continue; 246 if (*Np == 0 && (*Bp == '|' || *Bp == ':' || *Bp == 0)) 247 return (1); 248 while (*Bp && *Bp != ':' && *Bp != '|') 249 Bp++; 250 if (*Bp == 0 || *Bp == ':') 251 return (0); 252 Bp++; 253 } 254 } 255 256 /* 257 * Skip to the next field. Notice that this is very dumb, not 258 * knowing about \: escapes or any such. If necessary, :'s can be put 259 * into the termcap file in octal. 260 */ 261 static char * 262 tskip(char *bp) 263 { 264 int dquote; 265 266 dquote = 0; 267 while (*bp) { 268 switch (*bp) { 269 case ':': 270 if (!dquote) 271 goto breakbreak; 272 else 273 bp++; 274 break; 275 case '\\': 276 bp++; 277 if (isdigit((unsigned char)*bp)) { 278 while (isdigit((unsigned char)*bp++)) 279 ; 280 } else 281 bp++; 282 case '"': 283 dquote = (dquote ? 0 : 1); 284 bp++; 285 break; 286 default: 287 bp++; 288 break; 289 } 290 } 291 breakbreak: 292 if (*bp == ':') 293 bp++; 294 return (bp); 295 } 296 297 /* 298 * Return the (numeric) option id. 299 * Numeric options look like 300 * li#80 301 * i.e. the option string is separated from the numeric value by 302 * a # character. If the option is not found we return -1. 303 * Note that we handle octal numbers beginning with 0. 304 */ 305 int64_t 306 tgetnum(char *id) 307 { 308 int64_t i; 309 int base; 310 char *bp = tbuf; 311 312 for (;;) { 313 bp = tskip(bp); 314 if (*bp == 0) 315 return (-1); 316 if (strncmp(bp, id, strlen(id)) != 0) 317 continue; 318 bp += strlen(id); 319 if (*bp == '@') 320 return (-1); 321 if (*bp != '#') 322 continue; 323 bp++; 324 base = 10; 325 if (*bp == '0') 326 base = 8; 327 i = 0; 328 while (isdigit((unsigned char)*bp)) 329 i *= base, i += *bp++ - '0'; 330 return (i); 331 } 332 } 333 334 /* 335 * Handle a flag option. 336 * Flag options are given "naked", i.e. followed by a : or the end 337 * of the buffer. Return 1 if we find the option, or 0 if it is 338 * not given. 339 */ 340 int 341 tgetflag(char *id) 342 { 343 char *bp = tbuf; 344 345 for (;;) { 346 bp = tskip(bp); 347 if (!*bp) 348 return (0); 349 if (strncmp(bp, id, strlen(id)) == 0) { 350 bp += strlen(id); 351 if (!*bp || *bp == ':') 352 return (1); 353 else if (*bp == '@') 354 return (0); 355 } 356 } 357 } 358 359 /* 360 * Get a string valued option. 361 * These are given as 362 * cl=^Z 363 * Much decoding is done on the strings, and the strings are 364 * placed in area, which is a ref parameter which is updated. 365 * No checking on area overflow. 366 */ 367 char * 368 tgetstr(char *id, char **area) 369 { 370 char *bp = tbuf; 371 372 for (;;) { 373 bp = tskip(bp); 374 if (!*bp) 375 return (0); 376 if (strncmp(bp, id, strlen(id)) != 0) 377 continue; 378 bp += strlen(id); 379 if (*bp == '@') 380 return (0); 381 if (*bp != '=') 382 continue; 383 bp++; 384 return (tdecode(bp, area)); 385 } 386 } 387 388 /* 389 * Tdecode does the grung work to decode the 390 * string capability escapes. 391 */ 392 static char * 393 tdecode(char *str, char **area) 394 { 395 char *cp; 396 int c; 397 const char *dps = "E\033^^\\\\::n\nr\rt\tb\bf\f\"\"", *dp; 398 int i; 399 char term; 400 401 term = ':'; 402 cp = *area; 403 again: 404 if (*str == '"') { 405 term = '"'; 406 str++; 407 } 408 while ((c = *str++) && c != term) { 409 switch (c) { 410 411 case '^': 412 c = *str++ & 037; 413 break; 414 415 case '\\': 416 dp = dps; 417 c = *str++; 418 nextc: 419 if (*dp++ == c) { 420 c = *dp++; 421 break; 422 } 423 dp++; 424 if (*dp) 425 goto nextc; 426 if (isdigit((unsigned char)c)) { 427 c -= '0', i = 2; 428 do 429 c <<= 3, c |= *str++ - '0'; 430 while (--i && isdigit((unsigned char)*str)); 431 } 432 break; 433 } 434 *cp++ = c; 435 } 436 if (c == term && term != ':') { 437 term = ':'; 438 goto again; 439 } 440 *cp++ = 0; 441 str = *area; 442 *area = cp; 443 return (str); 444 } 445