1 /* $NetBSD: advcap.c,v 1.16 2015/11/11 07:48:41 ozaki-r 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 #include "prog_ops.h" 50 51 #ifndef __UNCONST 52 #define __UNCONST(a) ((void *)(unsigned long)(const void *)(a)) 53 #endif 54 55 #ifndef BUFSIZ 56 #define BUFSIZ 1024 57 #endif 58 #define MAXHOP 32 /* max number of tc= indirections */ 59 60 #define tgetent agetent 61 #define tnchktc anchktc 62 #define tnamatch anamatch 63 #define tgetnum agetnum 64 #define tgetflag agetflag 65 #define tgetstr agetstr 66 67 #if 0 68 #define V_TERMCAP "REMOTE" 69 #define V_TERM "HOST" 70 #endif 71 72 char *RM; 73 74 /* 75 * termcap - routines for dealing with the terminal capability data base 76 * 77 * BUG: Should use a "last" pointer in tbuf, so that searching 78 * for capabilities alphabetically would not be a n**2/2 79 * process when large numbers of capabilities are given. 80 * Note: If we add a last pointer now we will screw up the 81 * tc capability. We really should compile termcap. 82 * 83 * Essentially all the work here is scanning and decoding escapes 84 * in string capabilities. We don't use stdio because the editor 85 * doesn't, and because living w/o it is not hard. 86 */ 87 88 static char *tbuf; 89 static int hopcount; /* detect infinite loops in termcap, init 0 */ 90 91 static char *remotefile; 92 93 extern char *conffile; 94 95 int tgetent(char *, char *); 96 int getent(char *, char *, char *); 97 int tnchktc(void); 98 int tnamatch(char *); 99 static char *tskip(char *); 100 int64_t tgetnum(char *); 101 int tgetflag(char *); 102 char *tgetstr(char *, char **); 103 static char *tdecode(char *, char **); 104 105 /* 106 * Get an entry for terminal name in buffer bp, 107 * from the termcap file. Parse is very rudimentary; 108 * we just notice escaped newlines. 109 */ 110 int 111 tgetent(char *bp, char *name) 112 { 113 char *cp; 114 115 remotefile = cp = conffile ? conffile : __UNCONST(_PATH_RTADVDCONF); 116 return (getent(bp, name, cp)); 117 } 118 119 int 120 getent(char *bp, char *name, char *cp) 121 { 122 int c; 123 int i = 0, cnt = 0; 124 char ibuf[BUFSIZ]; 125 int tf; 126 127 tbuf = bp; 128 tf = 0; 129 /* 130 * TERMCAP can have one of two things in it. It can be the 131 * name of a file to use instead of /etc/termcap. In this 132 * case it better start with a "/". Or it can be an entry to 133 * use so we don't have to read the file. In this case it 134 * has to already have the newlines crunched out. 135 */ 136 if (cp && *cp) { 137 tf = open(RM = cp, O_RDONLY); 138 } 139 if (tf < 0) { 140 syslog(LOG_INFO, "<%s> open: %m", __func__); 141 return (-2); 142 } 143 for (;;) { 144 cp = bp; 145 for (;;) { 146 if (i == cnt) { 147 cnt = read(tf, ibuf, BUFSIZ); 148 if (cnt <= 0) { 149 close(tf); 150 return (0); 151 } 152 i = 0; 153 } 154 c = ibuf[i++]; 155 if (c == '\n') { 156 if (cp > bp && cp[-1] == '\\') { 157 cp--; 158 continue; 159 } 160 break; 161 } 162 if (cp >= bp + BUFSIZ) { 163 prog_write(2,"Remcap entry too long\n", 23); 164 break; 165 } else 166 *cp++ = c; 167 } 168 *cp = 0; 169 170 /* 171 * The real work for the match. 172 */ 173 if (tnamatch(name)) { 174 close(tf); 175 return (tnchktc()); 176 } 177 } 178 } 179 180 /* 181 * tnchktc: check the last entry, see if it's tc=xxx. If so, 182 * recursively find xxx and append that entry (minus the names) 183 * to take the place of the tc=xxx entry. This allows termcap 184 * entries to say "like an HP2621 but doesn't turn on the labels". 185 * Note that this works because of the left to right scan. 186 */ 187 int 188 tnchktc(void) 189 { 190 char *p, *q; 191 char tcname[16]; /* name of similar terminal */ 192 char tcbuf[BUFSIZ]; 193 char *holdtbuf = tbuf; 194 int l; 195 196 p = tbuf + strlen(tbuf) - 2; /* before the last colon */ 197 while (*--p != ':') 198 if (p < tbuf) { 199 prog_write(2, "Bad remcap entry\n", 18); 200 return (0); 201 } 202 p++; 203 /* p now points to beginning of last field */ 204 if (p[0] != 't' || p[1] != 'c') 205 return (1); 206 strlcpy(tcname, p + 3, sizeof tcname); 207 q = tcname; 208 while (*q && *q != ':') 209 q++; 210 *q = 0; 211 if (++hopcount > MAXHOP) { 212 prog_write(2, "Infinite tc= loop\n", 18); 213 return (0); 214 } 215 if (getent(tcbuf, tcname, remotefile) != 1) { 216 return (0); 217 } 218 for (q = tcbuf; *q++ != ':'; ) 219 ; 220 l = p - holdtbuf + strlen(q); 221 if (l > BUFSIZ) { 222 prog_write(2, "Remcap entry too long\n", 23); 223 q[BUFSIZ - (p-holdtbuf)] = 0; 224 } 225 strcpy(p, q); 226 tbuf = holdtbuf; 227 return (1); 228 } 229 230 /* 231 * Tnamatch deals with name matching. The first field of the termcap 232 * entry is a sequence of names separated by |'s, so we compare 233 * against each such name. The normal : terminator after the last 234 * name (before the first field) stops us. 235 */ 236 int 237 tnamatch(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(char *bp) 264 { 265 int dquote; 266 267 dquote = 0; 268 while (*bp) { 269 switch (*bp) { 270 case ':': 271 if (!dquote) 272 goto breakbreak; 273 else 274 bp++; 275 break; 276 case '\\': 277 bp++; 278 if (isdigit((unsigned char)*bp)) { 279 while (isdigit((unsigned char)*bp++)) 280 ; 281 } else 282 bp++; 283 case '"': 284 dquote = (dquote ? 0 : 1); 285 bp++; 286 break; 287 default: 288 bp++; 289 break; 290 } 291 } 292 breakbreak: 293 if (*bp == ':') 294 bp++; 295 return (bp); 296 } 297 298 /* 299 * Return the (numeric) option id. 300 * Numeric options look like 301 * li#80 302 * i.e. the option string is separated from the numeric value by 303 * a # character. If the option is not found we return -1. 304 * Note that we handle octal numbers beginning with 0. 305 */ 306 int64_t 307 tgetnum(char *id) 308 { 309 int64_t i; 310 int base; 311 char *bp = tbuf; 312 313 for (;;) { 314 bp = tskip(bp); 315 if (*bp == 0) 316 return (-1); 317 if (strncmp(bp, id, strlen(id)) != 0) 318 continue; 319 bp += strlen(id); 320 if (*bp == '@') 321 return (-1); 322 if (*bp != '#') 323 continue; 324 bp++; 325 base = 10; 326 if (*bp == '0') 327 base = 8; 328 i = 0; 329 while (isdigit((unsigned char)*bp)) 330 i *= base, i += *bp++ - '0'; 331 return (i); 332 } 333 } 334 335 /* 336 * Handle a flag option. 337 * Flag options are given "naked", i.e. followed by a : or the end 338 * of the buffer. Return 1 if we find the option, or 0 if it is 339 * not given. 340 */ 341 int 342 tgetflag(char *id) 343 { 344 char *bp = tbuf; 345 346 for (;;) { 347 bp = tskip(bp); 348 if (!*bp) 349 return (0); 350 if (strncmp(bp, id, strlen(id)) == 0) { 351 bp += strlen(id); 352 if (!*bp || *bp == ':') 353 return (1); 354 else if (*bp == '@') 355 return (0); 356 } 357 } 358 } 359 360 /* 361 * Get a string valued option. 362 * These are given as 363 * cl=^Z 364 * Much decoding is done on the strings, and the strings are 365 * placed in area, which is a ref parameter which is updated. 366 * No checking on area overflow. 367 */ 368 char * 369 tgetstr(char *id, char **area) 370 { 371 char *bp = tbuf; 372 373 for (;;) { 374 bp = tskip(bp); 375 if (!*bp) 376 return (0); 377 if (strncmp(bp, id, strlen(id)) != 0) 378 continue; 379 bp += strlen(id); 380 if (*bp == '@') 381 return (0); 382 if (*bp != '=') 383 continue; 384 bp++; 385 return (tdecode(bp, area)); 386 } 387 } 388 389 /* 390 * Tdecode does the grung work to decode the 391 * string capability escapes. 392 */ 393 static char * 394 tdecode(char *str, char **area) 395 { 396 char *cp; 397 int c; 398 const char *dps = "E\033^^\\\\::n\nr\rt\tb\bf\f\"\"", *dp; 399 int i; 400 char term; 401 402 term = ':'; 403 cp = *area; 404 again: 405 if (*str == '"') { 406 term = '"'; 407 str++; 408 } 409 while ((c = *str++) && c != term) { 410 switch (c) { 411 412 case '^': 413 c = *str++ & 037; 414 break; 415 416 case '\\': 417 dp = dps; 418 c = *str++; 419 nextc: 420 if (*dp++ == c) { 421 c = *dp++; 422 break; 423 } 424 dp++; 425 if (*dp) 426 goto nextc; 427 if (isdigit((unsigned char)c)) { 428 c -= '0', i = 2; 429 do 430 c <<= 3, c |= *str++ - '0'; 431 while (--i && isdigit((unsigned char)*str)); 432 } 433 break; 434 } 435 *cp++ = c; 436 } 437 if (c == term && term != ':') { 438 term = ':'; 439 goto again; 440 } 441 *cp++ = 0; 442 str = *area; 443 *area = cp; 444 return (str); 445 } 446