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