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