source: branches/libc-0.6/src/emx/bsd/libterm/termcap.c

Last change on this file was 272, checked in by bird, 22 years ago

Initial revision

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