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