xref: /freebsd/usr.sbin/rtadvd/advcap.c (revision 23f282aa31e9b6fceacd449020e936e98d6f2298)
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