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