advcap.c revision 330897
1179100Syongari/*	$FreeBSD: stable/11/usr.sbin/rtadvd/advcap.c 330897 2018-03-14 03:19:51Z eadler $	*/
2179100Syongari/*	$KAME: advcap.c,v 1.11 2003/05/19 09:46:50 keiichi Exp $	*/
3179100Syongari
4179100Syongari/*-
5179100Syongari * SPDX-License-Identifier: BSD-3-Clause
6179100Syongari *
7179100Syongari * Copyright (c) 1983 The Regents of the University of California.
8179100Syongari * All rights reserved.
9179100Syongari *
10179100Syongari * Redistribution and use in source and binary forms, with or without
11179100Syongari * modification, are permitted provided that the following conditions
12179100Syongari * are met:
13179100Syongari * 1. Redistributions of source code must retain the above copyright
14179100Syongari *    notice, this list of conditions and the following disclaimer.
15179100Syongari * 2. Redistributions in binary form must reproduce the above copyright
16179100Syongari *    notice, this list of conditions and the following disclaimer in the
17179100Syongari *    documentation and/or other materials provided with the distribution.
18179100Syongari * 4. Neither the name of the University nor the names of its contributors
19179100Syongari *    may be used to endorse or promote products derived from this software
20179100Syongari *    without specific prior written permission.
21179100Syongari *
22179100Syongari * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
23179100Syongari * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24179100Syongari * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25179100Syongari * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
26179100Syongari * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27179100Syongari * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28179100Syongari * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29179100Syongari * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30179100Syongari * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31179100Syongari * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32179100Syongari * SUCH DAMAGE.
33179100Syongari */
34179100Syongari
35179100Syongari/*
36179100Syongari * remcap - routines for dealing with the remote host data base
37179100Syongari *
38179100Syongari * derived from termcap
39179100Syongari */
40179100Syongari#include <sys/types.h>
41179100Syongari#include <sys/uio.h>
42179100Syongari#include <unistd.h>
43179100Syongari#include <fcntl.h>
44179100Syongari#include <ctype.h>
45179100Syongari#include <stdlib.h>
46179100Syongari#include <stdio.h>
47179100Syongari#include <syslog.h>
48179100Syongari#include <errno.h>
49179100Syongari#include <string.h>
50179100Syongari#include "pathnames.h"
51179100Syongari
52179100Syongari#ifndef BUFSIZ
53179100Syongari#define	BUFSIZ		1024
54179100Syongari#endif
55179100Syongari#define MAXHOP		32		/* max number of tc= indirections */
56179100Syongari
57179100Syongari#define	tgetent		agetent
58179100Syongari#define	tnchktc		anchktc
59179100Syongari#define	tnamatch	anamatch
60179100Syongari#define	tgetnum		agetnum
61179100Syongari#define	tgetflag	agetflag
62179100Syongari#define	tgetstr		agetstr
63179100Syongari
64179100Syongari#if 0
65179100Syongari#define V_TERMCAP	"REMOTE"
66179100Syongari#define V_TERM		"HOST"
67179100Syongari#endif
68179100Syongari
69179100Syongari/*
70179100Syongari * termcap - routines for dealing with the terminal capability data base
71179100Syongari *
72179100Syongari * BUG:		Should use a "last" pointer in tbuf, so that searching
73179100Syongari *		for capabilities alphabetically would not be a n**2/2
74179100Syongari *		process when large numbers of capabilities are given.
75179100Syongari * Note:	If we add a last pointer now we will screw up the
76179100Syongari *		tc capability. We really should compile termcap.
77179100Syongari *
78179100Syongari * Essentially all the work here is scanning and decoding escapes
79179100Syongari * in string capabilities.  We don't use stdio because the editor
80179100Syongari * doesn't, and because living w/o it is not hard.
81179100Syongari */
82179100Syongari
83179100Syongaristatic	char *tbuf;
84179100Syongaristatic	int hopcount;	/* detect infinite loops in termcap, init 0 */
85179100Syongari
86179100Syongariextern const char *conffile;
87179100Syongari
88179100Syongariint tgetent(char *, char *);
89179100Syongariint getent(char *, char *, const char *);
90179100Syongariint tnchktc(void);
91179100Syongariint tnamatch(char *);
92179100Syongaristatic char *tskip(char *);
93179100Syongariint64_t tgetnum(char *);
94179100Syongariint tgetflag(char *);
95179100Syongarichar *tgetstr(char *, char **);
96179100Syongaristatic char *tdecode(char *, char **);
97179100Syongari
98179100Syongari/*
99179100Syongari * Get an entry for terminal name in buffer bp,
100179100Syongari * from the termcap file.  Parse is very rudimentary;
101179100Syongari * we just notice escaped newlines.
102179100Syongari */
103179100Syongariint
104179100Syongaritgetent(char *bp, char *name)
105179100Syongari{
106179100Syongari	return (getent(bp, name, conffile));
107179100Syongari}
108179100Syongari
109179100Syongariint
110179100Syongarigetent(char *bp, char *name, const char *cfile)
111179100Syongari{
112179100Syongari	int c;
113179100Syongari	int i = 0, cnt = 0;
114179100Syongari	char ibuf[BUFSIZ];
115179100Syongari	char *cp;
116179100Syongari	int tf;
117179100Syongari
118179100Syongari	tbuf = bp;
119179100Syongari	tf = 0;
120179100Syongari	/*
121179100Syongari	 * TERMCAP can have one of two things in it. It can be the
122179100Syongari	 * name of a file to use instead of /etc/termcap. In this
123179100Syongari	 * case it better start with a "/". Or it can be an entry to
124179100Syongari	 * use so we don't have to read the file. In this case it
125179100Syongari	 * has to already have the newlines crunched out.
126179100Syongari	 */
127179100Syongari	if (cfile && *cfile)
128179100Syongari		tf = open(cfile, O_RDONLY);
129179100Syongari
130179100Syongari	if (tf < 0) {
131179100Syongari		syslog(LOG_INFO,
132179100Syongari		       "<%s> open: %s", __func__, strerror(errno));
133179100Syongari		return (-2);
134179100Syongari	}
135179100Syongari	for (;;) {
136179100Syongari		cp = bp;
137179100Syongari		for (;;) {
138179100Syongari			if (i == cnt) {
139179100Syongari				cnt = read(tf, ibuf, BUFSIZ);
140179100Syongari				if (cnt <= 0) {
141179100Syongari					close(tf);
142179100Syongari					return (0);
143179100Syongari				}
144179100Syongari				i = 0;
145179100Syongari			}
146179100Syongari			c = ibuf[i++];
147179100Syongari			if (c == '\n') {
148179100Syongari				if (cp > bp && cp[-1] == '\\') {
149179100Syongari					cp--;
150179100Syongari					continue;
151179100Syongari				}
152179100Syongari				break;
153179100Syongari			}
154179100Syongari			if (cp >= bp + BUFSIZ - 1) {
155179100Syongari				write(STDERR_FILENO, "Remcap entry too long\n",
156179100Syongari				    22);
157179100Syongari				break;
158179100Syongari			} else
159179100Syongari				*cp++ = c;
160179100Syongari		}
161179100Syongari		*cp = 0;
162179100Syongari
163179100Syongari		/*
164179100Syongari		 * The real work for the match.
165179100Syongari		 */
166179100Syongari		if (tnamatch(name)) {
167179100Syongari			close(tf);
168179100Syongari			return (tnchktc());
169179100Syongari		}
170179100Syongari	}
171179100Syongari}
172179100Syongari
173179100Syongari/*
174179100Syongari * tnchktc: check the last entry, see if it's tc=xxx. If so,
175179100Syongari * recursively find xxx and append that entry (minus the names)
176179100Syongari * to take the place of the tc=xxx entry. This allows termcap
177179100Syongari * entries to say "like an HP2621 but doesn't turn on the labels".
178179100Syongari * Note that this works because of the left to right scan.
179179100Syongari */
180179100Syongariint
181179100Syongaritnchktc(void)
182179100Syongari{
183179100Syongari	char *p, *q;
184179100Syongari	char tcname[16];	/* name of similar terminal */
185179100Syongari	char tcbuf[BUFSIZ];
186179100Syongari	char *holdtbuf = tbuf;
187179100Syongari	int l;
188179100Syongari
189179100Syongari	p = tbuf + strlen(tbuf) - 2;	/* before the last colon */
190179100Syongari	while (*--p != ':')
191179100Syongari		if (p < tbuf) {
192179100Syongari			write(STDERR_FILENO, "Bad remcap entry\n", 18);
193179100Syongari			return (0);
194179100Syongari		}
195179100Syongari	p++;
196179100Syongari	/* p now points to beginning of last field */
197179100Syongari	if (p[0] != 't' || p[1] != 'c')
198179100Syongari		return (1);
199179100Syongari	strlcpy(tcname, p + 3, sizeof tcname);
200179100Syongari	q = tcname;
201179100Syongari	while (*q && *q != ':')
202179100Syongari		q++;
203179100Syongari	*q = 0;
204179100Syongari	if (++hopcount > MAXHOP) {
205179100Syongari		write(STDERR_FILENO, "Infinite tc= loop\n", 18);
206179100Syongari		return (0);
207179100Syongari	}
208179100Syongari	if (getent(tcbuf, tcname, conffile) != 1) {
209179100Syongari		return (0);
210179100Syongari	}
211179100Syongari	for (q = tcbuf; *q++ != ':'; )
212179100Syongari		;
213179100Syongari	l = p - holdtbuf + strlen(q);
214179100Syongari	if (l > BUFSIZ) {
215179100Syongari		write(STDERR_FILENO, "Remcap entry too long\n", 23);
216179100Syongari		q[BUFSIZ - (p-holdtbuf)] = 0;
217179100Syongari	}
218179100Syongari	strcpy(p, q);
219179100Syongari	tbuf = holdtbuf;
220179100Syongari	return (1);
221179100Syongari}
222179100Syongari
223179100Syongari/*
224179100Syongari * Tnamatch deals with name matching.  The first field of the termcap
225179100Syongari * entry is a sequence of names separated by |'s, so we compare
226179100Syongari * against each such name.  The normal : terminator after the last
227179100Syongari * name (before the first field) stops us.
228179100Syongari */
229179100Syongariint
230179100Syongaritnamatch(char *np)
231179100Syongari{
232179100Syongari	char *Np, *Bp;
233179100Syongari
234179100Syongari	Bp = tbuf;
235179100Syongari	if (*Bp == '#')
236179100Syongari		return (0);
237179100Syongari	for (;;) {
238179100Syongari		for (Np = np; *Np && *Bp == *Np; Bp++, Np++)
239179100Syongari			continue;
240179100Syongari		if (*Np == 0 && (*Bp == '|' || *Bp == ':' || *Bp == 0))
241179100Syongari			return (1);
242179100Syongari		while (*Bp && *Bp != ':' && *Bp != '|')
243179100Syongari			Bp++;
244179100Syongari		if (*Bp == 0 || *Bp == ':')
245179100Syongari			return (0);
246179100Syongari		Bp++;
247179100Syongari	}
248179100Syongari}
249179100Syongari
250179100Syongari/*
251179100Syongari * Skip to the next field.  Notice that this is very dumb, not
252179100Syongari * knowing about \: escapes or any such.  If necessary, :'s can be put
253179100Syongari * into the termcap file in octal.
254179100Syongari */
255179100Syongaristatic char *
256179100Syongaritskip(char *bp)
257179100Syongari{
258179100Syongari	int dquote;
259179100Syongari
260179100Syongari	dquote = 0;
261179100Syongari	while (*bp) {
262179100Syongari		switch (*bp) {
263179100Syongari		case ':':
264179100Syongari			if (!dquote)
265179100Syongari				goto breakbreak;
266179100Syongari			else
267179100Syongari				bp++;
268179100Syongari			break;
269179100Syongari		case '\\':
270179100Syongari			bp++;
271179100Syongari			if (isdigit(*bp)) {
272179100Syongari				while (isdigit(*bp++))
273179100Syongari					;
274179100Syongari			} else
275179100Syongari				bp++;
276179100Syongari		case '"':
277179100Syongari			dquote = (dquote ? 1 : 0);
278179100Syongari			bp++;
279179100Syongari			break;
280179100Syongari		default:
281179100Syongari			bp++;
282179100Syongari			break;
283179100Syongari		}
284179100Syongari	}
285179100Syongaribreakbreak:
286179100Syongari	if (*bp == ':')
287179100Syongari		bp++;
288179100Syongari	return (bp);
289179100Syongari}
290179100Syongari
291179100Syongari/*
292179100Syongari * Return the (numeric) option id.
293179100Syongari * Numeric options look like
294179100Syongari *	li#80
295179100Syongari * i.e. the option string is separated from the numeric value by
296179100Syongari * a # character.  If the option is not found we return -1.
297179100Syongari * Note that we handle octal numbers beginning with 0.
298179100Syongari */
299179100Syongariint64_t
300179100Syongaritgetnum(char *id)
301179100Syongari{
302179100Syongari	int64_t i;
303179100Syongari	int base;
304179100Syongari	char *bp = tbuf;
305179100Syongari
306179100Syongari	for (;;) {
307179100Syongari		bp = tskip(bp);
308179100Syongari		if (*bp == 0)
309179100Syongari			return (-1);
310179100Syongari		if (strncmp(bp, id, strlen(id)) != 0)
311179100Syongari			continue;
312179100Syongari		bp += strlen(id);
313179100Syongari		if (*bp == '@')
314179100Syongari			return (-1);
315179100Syongari		if (*bp != '#')
316179100Syongari			continue;
317179100Syongari		bp++;
318179100Syongari		base = 10;
319179100Syongari		if (*bp == '0')
320179100Syongari			base = 8;
321179100Syongari		i = 0;
322179100Syongari		while (isdigit(*bp))
323179100Syongari			i *= base, i += *bp++ - '0';
324179100Syongari		return (i);
325179100Syongari	}
326179100Syongari}
327179100Syongari
328179100Syongari/*
329179100Syongari * Handle a flag option.
330179100Syongari * Flag options are given "naked", i.e. followed by a : or the end
331179100Syongari * of the buffer.  Return 1 if we find the option, or 0 if it is
332179100Syongari * not given.
333179100Syongari */
334179100Syongariint
335179100Syongaritgetflag(char *id)
336179100Syongari{
337179100Syongari	char *bp = tbuf;
338179100Syongari
339179100Syongari	for (;;) {
340179100Syongari		bp = tskip(bp);
341179100Syongari		if (!*bp)
342179100Syongari			return (0);
343179100Syongari		if (strncmp(bp, id, strlen(id)) == 0) {
344179100Syongari			bp += strlen(id);
345179100Syongari			if (!*bp || *bp == ':')
346179100Syongari				return (1);
347179100Syongari			else if (*bp == '@')
348179100Syongari				return (0);
349179100Syongari		}
350179100Syongari	}
351179100Syongari}
352179100Syongari
353179100Syongari/*
354179100Syongari * Get a string valued option.
355179100Syongari * These are given as
356179100Syongari *	cl=^Z
357179100Syongari * Much decoding is done on the strings, and the strings are
358179100Syongari * placed in area, which is a ref parameter which is updated.
359179100Syongari * No checking on area overflow.
360179100Syongari */
361179100Syongarichar *
362179100Syongaritgetstr(char *id, char **area)
363179100Syongari{
364179100Syongari	char *bp = tbuf;
365179100Syongari
366179100Syongari	for (;;) {
367179100Syongari		bp = tskip(bp);
368179100Syongari		if (!*bp)
369179100Syongari			return (0);
370179100Syongari		if (strncmp(bp, id, strlen(id)) != 0)
371179100Syongari			continue;
372179100Syongari		bp += strlen(id);
373179100Syongari		if (*bp == '@')
374179100Syongari			return (0);
375179100Syongari		if (*bp != '=')
376179100Syongari			continue;
377179100Syongari		bp++;
378179100Syongari		return (tdecode(bp, area));
379179100Syongari	}
380179100Syongari}
381179100Syongari
382179100Syongari/*
383179100Syongari * Tdecode does the grung work to decode the
384179100Syongari * string capability escapes.
385179100Syongari */
386179100Syongaristatic char *
387179100Syongaritdecode(char *str, char **area)
388179100Syongari{
389179100Syongari	char *cp;
390179100Syongari	int c;
391179100Syongari	const char *dp;
392179100Syongari	int i;
393179100Syongari	char term;
394179100Syongari
395179100Syongari	term = ':';
396179100Syongari	cp = *area;
397179100Syongariagain:
398179100Syongari	if (*str == '"') {
399179100Syongari		term = '"';
400179100Syongari		str++;
401179100Syongari	}
402179100Syongari	while ((c = *str++) && c != term) {
403179100Syongari		switch (c) {
404179100Syongari
405179100Syongari		case '^':
406179100Syongari			c = *str++ & 037;
407179100Syongari			break;
408179100Syongari
409179100Syongari		case '\\':
410179100Syongari			dp = "E\033^^\\\\::n\nr\rt\tb\bf\f\"\"";
411179100Syongari			c = *str++;
412179100Syongarinextc:
413179100Syongari			if (*dp++ == c) {
414179100Syongari				c = *dp++;
415179100Syongari				break;
416179100Syongari			}
417179100Syongari			dp++;
418179100Syongari			if (*dp)
419179100Syongari				goto nextc;
420179100Syongari			if (isdigit(c)) {
421179100Syongari				c -= '0', i = 2;
422179100Syongari				do
423179100Syongari					c <<= 3, c |= *str++ - '0';
424179100Syongari				while (--i && isdigit(*str));
425179100Syongari			}
426179100Syongari			break;
427179100Syongari		}
428179100Syongari		*cp++ = c;
429179100Syongari	}
430179100Syongari	if (c == term && term != ':') {
431179100Syongari		term = ':';
432179100Syongari		goto again;
433179100Syongari	}
434179100Syongari	*cp++ = 0;
435179100Syongari	str = *area;
436179100Syongari	*area = cp;
437179100Syongari	return (str);
438179100Syongari}
439179100Syongari