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