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