1 /* 2 * Copyright (c) 1983 The Regents of the University of California. 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 3. All advertising materials mentioning features or use of this software 14 * must display the following acknowledgement: 15 * This product includes software developed by the University of 16 * California, Berkeley and its contributors. 17 * 4. 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 * $FreeBSD$ 34 */ 35 36 #ifndef lint 37 static char sccsid[] = "@(#)remcap.c 5.5 (Berkeley) 2/2/91"; 38 #endif /* not lint */ 39 40 /* 41 * remcap - routines for dealing with the remote host data base 42 * 43 * derived from termcap 44 */ 45 #include <sys/types.h> 46 #include <sys/uio.h> 47 #include <unistd.h> 48 #include <fcntl.h> 49 #include <ctype.h> 50 #include <stdlib.h> 51 #include <stdio.h> 52 #include <syslog.h> 53 #include <errno.h> 54 #include <string.h> 55 #include "pathnames.h" 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 char *RM; 70 71 /* 72 * termcap - routines for dealing with the terminal capability data base 73 * 74 * BUG: Should use a "last" pointer in tbuf, so that searching 75 * for capabilities alphabetically would not be a n**2/2 76 * process when large numbers of capabilities are given. 77 * Note: If we add a last pointer now we will screw up the 78 * tc capability. We really should compile termcap. 79 * 80 * Essentially all the work here is scanning and decoding escapes 81 * in string capabilities. We don't use stdio because the editor 82 * doesn't, and because living w/o it is not hard. 83 */ 84 85 static char *tbuf; 86 static int hopcount; /* detect infinite loops in termcap, init 0 */ 87 88 static char *remotefile; 89 90 extern char *conffile; 91 92 int tgetent __P((char *, char *)); 93 int getent __P((char *, char *, char *)); 94 int tnchktc __P((void)); 95 int tnamatch __P((char *)); 96 static char *tskip __P((char *)); 97 int tgetnum __P((char *)); 98 int tgetflag __P((char *)); 99 char *tgetstr __P((char *, char **)); 100 static char *tdecode __P((char *, char **)); 101 102 /* 103 * Get an entry for terminal name in buffer bp, 104 * from the termcap file. Parse is very rudimentary; 105 * we just notice escaped newlines. 106 */ 107 int 108 tgetent(bp, name) 109 char *bp, *name; 110 { 111 char *cp; 112 113 remotefile = cp = conffile ? conffile : _PATH_RTADVDCONF; 114 return (getent(bp, name, cp)); 115 } 116 117 int 118 getent(bp, name, cp) 119 char *bp, *name, *cp; 120 { 121 register int c; 122 register 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_WARNING, 140 "<%s> open: %s", __FUNCTION__, strerror(errno)); 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 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() 189 { 190 register 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 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 strcpy(tcname, p+3); 207 q = tcname; 208 while (*q && *q != ':') 209 q++; 210 *q = 0; 211 if (++hopcount > MAXHOP) { 212 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 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(np) 238 char *np; 239 { 240 register char *Np, *Bp; 241 242 Bp = tbuf; 243 if (*Bp == '#') 244 return (0); 245 for (;;) { 246 for (Np = np; *Np && *Bp == *Np; Bp++, Np++) 247 continue; 248 if (*Np == 0 && (*Bp == '|' || *Bp == ':' || *Bp == 0)) 249 return (1); 250 while (*Bp && *Bp != ':' && *Bp != '|') 251 Bp++; 252 if (*Bp == 0 || *Bp == ':') 253 return (0); 254 Bp++; 255 } 256 } 257 258 /* 259 * Skip to the next field. Notice that this is very dumb, not 260 * knowing about \: escapes or any such. If necessary, :'s can be put 261 * into the termcap file in octal. 262 */ 263 static char * 264 tskip(bp) 265 register 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(*bp)) { 281 while (isdigit(*bp++)) 282 ; 283 } else 284 bp++; 285 case '"': 286 dquote = (dquote ? 1 : 0); 287 bp++; 288 break; 289 default: 290 bp++; 291 break; 292 } 293 } 294 breakbreak: 295 if (*bp == ':') 296 bp++; 297 return (bp); 298 } 299 300 /* 301 * Return the (numeric) option id. 302 * Numeric options look like 303 * li#80 304 * i.e. the option string is separated from the numeric value by 305 * a # character. If the option is not found we return -1. 306 * Note that we handle octal numbers beginning with 0. 307 */ 308 int 309 tgetnum(id) 310 char *id; 311 { 312 register long int i; 313 register int base; 314 register 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(*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 */ 344 int 345 tgetflag(id) 346 char *id; 347 { 348 register char *bp = tbuf; 349 350 for (;;) { 351 bp = tskip(bp); 352 if (!*bp) 353 return (0); 354 if (strncmp(bp, id, strlen(id)) == 0) { 355 bp += strlen(id); 356 if (!*bp || *bp == ':') 357 return (1); 358 else if (*bp == '@') 359 return (0); 360 } 361 } 362 } 363 364 /* 365 * Get a string valued option. 366 * These are given as 367 * cl=^Z 368 * Much decoding is done on the strings, and the strings are 369 * placed in area, which is a ref parameter which is updated. 370 * No checking on area overflow. 371 */ 372 char * 373 tgetstr(id, area) 374 char *id, **area; 375 { 376 register char *bp = tbuf; 377 378 for (;;) { 379 bp = tskip(bp); 380 if (!*bp) 381 return (0); 382 if (strncmp(bp, id, strlen(id)) != 0) 383 continue; 384 bp += strlen(id); 385 if (*bp == '@') 386 return (0); 387 if (*bp != '=') 388 continue; 389 bp++; 390 return (tdecode(bp, area)); 391 } 392 } 393 394 /* 395 * Tdecode does the grung work to decode the 396 * string capability escapes. 397 */ 398 static char * 399 tdecode(str, area) 400 register char *str; 401 char **area; 402 { 403 register char *cp; 404 register int c; 405 register char *dp; 406 int i; 407 char term; 408 409 term = ':'; 410 cp = *area; 411 again: 412 if (*str == '"') { 413 term = '"'; 414 str++; 415 } 416 while ((c = *str++) && c != term) { 417 switch (c) { 418 419 case '^': 420 c = *str++ & 037; 421 break; 422 423 case '\\': 424 dp = "E\033^^\\\\::n\nr\rt\tb\bf\f\"\""; 425 c = *str++; 426 nextc: 427 if (*dp++ == c) { 428 c = *dp++; 429 break; 430 } 431 dp++; 432 if (*dp) 433 goto nextc; 434 if (isdigit(c)) { 435 c -= '0', i = 2; 436 do 437 c <<= 3, c |= *str++ - '0'; 438 while (--i && isdigit(*str)); 439 } 440 break; 441 } 442 *cp++ = c; 443 } 444 if (c == term && term != ':') { 445 term = ':'; 446 goto again; 447 } 448 *cp++ = 0; 449 str = *area; 450 *area = cp; 451 return (str); 452 } 453