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