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