]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - usr.sbin/rtadvd/advcap.c
bhnd(9): Fix a few mandoc related issues
[FreeBSD/FreeBSD.git] / usr.sbin / rtadvd / advcap.c
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 }