xref: /netbsd/usr.sbin/rtadvd/advcap.c (revision bf9ec67e)
1 /*	$NetBSD: advcap.c,v 1.6 2001/02/04 06:19:40 itojun Exp $	*/
2 /*	$KAME: advcap.c,v 1.5 2001/02/01 09:12:08 jinmei 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. All advertising materials mentioning features or use of this software
17  *    must display the following acknowledgement:
18  *	This product includes software developed by the University of
19  *	California, Berkeley and its contributors.
20  * 4. Neither the name of the University nor the names of its contributors
21  *    may be used to endorse or promote products derived from this software
22  *    without specific prior written permission.
23  *
24  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34  * SUCH DAMAGE.
35  */
36 
37 /*
38  * remcap - routines for dealing with the remote host data base
39  *
40  * derived from termcap
41  */
42 #include <sys/types.h>
43 #include <sys/uio.h>
44 #include <unistd.h>
45 #include <fcntl.h>
46 #include <ctype.h>
47 #include <stdlib.h>
48 #include <stdio.h>
49 #include <syslog.h>
50 #include <errno.h>
51 #include <string.h>
52 #include "pathnames.h"
53 
54 #ifndef BUFSIZ
55 #define	BUFSIZ		1024
56 #endif
57 #define MAXHOP		32		/* max number of tc= indirections */
58 
59 #define	tgetent		agetent
60 #define	tnchktc		anchktc
61 #define	tnamatch	anamatch
62 #define	tgetnum		agetnum
63 #define	tgetflag	agetflag
64 #define	tgetstr		agetstr
65 
66 #if 0
67 #define V_TERMCAP	"REMOTE"
68 #define V_TERM		"HOST"
69 #endif
70 
71 char	*RM;
72 
73 /*
74  * termcap - routines for dealing with the terminal capability data base
75  *
76  * BUG:		Should use a "last" pointer in tbuf, so that searching
77  *		for capabilities alphabetically would not be a n**2/2
78  *		process when large numbers of capabilities are given.
79  * Note:	If we add a last pointer now we will screw up the
80  *		tc capability. We really should compile termcap.
81  *
82  * Essentially all the work here is scanning and decoding escapes
83  * in string capabilities.  We don't use stdio because the editor
84  * doesn't, and because living w/o it is not hard.
85  */
86 
87 static	char *tbuf;
88 static	int hopcount;	/* detect infinite loops in termcap, init 0 */
89 
90 static	char *remotefile;
91 
92 extern char *conffile;
93 
94 int tgetent __P((char *, char *));
95 int getent __P((char *, char *, char *));
96 int tnchktc __P((void));
97 int tnamatch __P((char *));
98 static char *tskip __P((char *));
99 long long tgetnum __P((char *));
100 int tgetflag __P((char *));
101 char *tgetstr __P((char *, char **));
102 static char *tdecode __P((char *, char **));
103 
104 /*
105  * Get an entry for terminal name in buffer bp,
106  * from the termcap file.  Parse is very rudimentary;
107  * we just notice escaped newlines.
108  */
109 int
110 tgetent(bp, name)
111 	char *bp, *name;
112 {
113 	char *cp;
114 
115 	remotefile = cp = conffile ? conffile : _PATH_RTADVDCONF;
116 	return (getent(bp, name, cp));
117 }
118 
119 int
120 getent(bp, name, cp)
121 	char *bp, *name, *cp;
122 {
123 	register int c;
124 	register int i = 0, cnt = 0;
125 	char ibuf[BUFSIZ];
126 	int tf;
127 
128 	tbuf = bp;
129 	tf = 0;
130 	/*
131 	 * TERMCAP can have one of two things in it. It can be the
132 	 * name of a file to use instead of /etc/termcap. In this
133 	 * case it better start with a "/". Or it can be an entry to
134 	 * use so we don't have to read the file. In this case it
135 	 * has to already have the newlines crunched out.
136 	 */
137 	if (cp && *cp) {
138 		tf = open(RM = cp, O_RDONLY);
139 	}
140 	if (tf < 0) {
141 		syslog(LOG_INFO,
142 		       "<%s> open: %s", __FUNCTION__, strerror(errno));
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 				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  */
189 int
190 tnchktc()
191 {
192 	register 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 			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 	strcpy(tcname, p+3);
209 	q = tcname;
210 	while (*q && *q != ':')
211 		q++;
212 	*q = 0;
213 	if (++hopcount > MAXHOP) {
214 		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 		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  */
238 int
239 tnamatch(np)
240 	char *np;
241 {
242 	register char *Np, *Bp;
243 
244 	Bp = tbuf;
245 	if (*Bp == '#')
246 		return (0);
247 	for (;;) {
248 		for (Np = np; *Np && *Bp == *Np; Bp++, Np++)
249 			continue;
250 		if (*Np == 0 && (*Bp == '|' || *Bp == ':' || *Bp == 0))
251 			return (1);
252 		while (*Bp && *Bp != ':' && *Bp != '|')
253 			Bp++;
254 		if (*Bp == 0 || *Bp == ':')
255 			return (0);
256 		Bp++;
257 	}
258 }
259 
260 /*
261  * Skip to the next field.  Notice that this is very dumb, not
262  * knowing about \: escapes or any such.  If necessary, :'s can be put
263  * into the termcap file in octal.
264  */
265 static char *
266 tskip(bp)
267 	register char *bp;
268 {
269 	int dquote;
270 
271 	dquote = 0;
272 	while (*bp) {
273 		switch (*bp) {
274 		case ':':
275 			if (!dquote)
276 				goto breakbreak;
277 			else
278 				bp++;
279 			break;
280 		case '\\':
281 			bp++;
282 			if (isdigit(*bp)) {
283 				while (isdigit(*bp++))
284 					;
285 			} else
286 				bp++;
287 		case '"':
288 			dquote = (dquote ? 1 : 0);
289 			bp++;
290 			break;
291 		default:
292 			bp++;
293 			break;
294 		}
295 	}
296 breakbreak:
297 	if (*bp == ':')
298 		bp++;
299 	return (bp);
300 }
301 
302 /*
303  * Return the (numeric) option id.
304  * Numeric options look like
305  *	li#80
306  * i.e. the option string is separated from the numeric value by
307  * a # character.  If the option is not found we return -1.
308  * Note that we handle octal numbers beginning with 0.
309  */
310 long long
311 tgetnum(id)
312 	char *id;
313 {
314 	register long long i;
315 	register int base;
316 	register char *bp = tbuf;
317 
318 	for (;;) {
319 		bp = tskip(bp);
320 		if (*bp == 0)
321 			return (-1);
322 		if (strncmp(bp, id, strlen(id)) != 0)
323 			continue;
324 		bp += strlen(id);
325 		if (*bp == '@')
326 			return (-1);
327 		if (*bp != '#')
328 			continue;
329 		bp++;
330 		base = 10;
331 		if (*bp == '0')
332 			base = 8;
333 		i = 0;
334 		while (isdigit(*bp))
335 			i *= base, i += *bp++ - '0';
336 		return (i);
337 	}
338 }
339 
340 /*
341  * Handle a flag option.
342  * Flag options are given "naked", i.e. followed by a : or the end
343  * of the buffer.  Return 1 if we find the option, or 0 if it is
344  * not given.
345  */
346 int
347 tgetflag(id)
348 	char *id;
349 {
350 	register char *bp = tbuf;
351 
352 	for (;;) {
353 		bp = tskip(bp);
354 		if (!*bp)
355 			return (0);
356 		if (strncmp(bp, id, strlen(id)) == 0) {
357 			bp += strlen(id);
358 			if (!*bp || *bp == ':')
359 				return (1);
360 			else if (*bp == '@')
361 				return (0);
362 		}
363 	}
364 }
365 
366 /*
367  * Get a string valued option.
368  * These are given as
369  *	cl=^Z
370  * Much decoding is done on the strings, and the strings are
371  * placed in area, which is a ref parameter which is updated.
372  * No checking on area overflow.
373  */
374 char *
375 tgetstr(id, area)
376 	char *id, **area;
377 {
378 	register char *bp = tbuf;
379 
380 	for (;;) {
381 		bp = tskip(bp);
382 		if (!*bp)
383 			return (0);
384 		if (strncmp(bp, id, strlen(id)) != 0)
385 			continue;
386 		bp += strlen(id);
387 		if (*bp == '@')
388 			return (0);
389 		if (*bp != '=')
390 			continue;
391 		bp++;
392 		return (tdecode(bp, area));
393 	}
394 }
395 
396 /*
397  * Tdecode does the grung work to decode the
398  * string capability escapes.
399  */
400 static char *
401 tdecode(str, area)
402 	register char *str;
403 	char **area;
404 {
405 	register char *cp;
406 	register int c;
407 	register char *dp;
408 	int i;
409 	char term;
410 
411 	term = ':';
412 	cp = *area;
413 again:
414 	if (*str == '"') {
415 		term = '"';
416 		str++;
417 	}
418 	while ((c = *str++) && c != term) {
419 		switch (c) {
420 
421 		case '^':
422 			c = *str++ & 037;
423 			break;
424 
425 		case '\\':
426 			dp = "E\033^^\\\\::n\nr\rt\tb\bf\f\"\"";
427 			c = *str++;
428 nextc:
429 			if (*dp++ == c) {
430 				c = *dp++;
431 				break;
432 			}
433 			dp++;
434 			if (*dp)
435 				goto nextc;
436 			if (isdigit(c)) {
437 				c -= '0', i = 2;
438 				do
439 					c <<= 3, c |= *str++ - '0';
440 				while (--i && isdigit(*str));
441 			}
442 			break;
443 		}
444 		*cp++ = c;
445 	}
446 	if (c == term && term != ':') {
447 		term = ':';
448 		goto again;
449 	}
450 	*cp++ = 0;
451 	str = *area;
452 	*area = cp;
453 	return (str);
454 }
455