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