make empty fields work, get rid of pw_class keywords
[unix-history] / usr / src / usr.bin / tip / remcap.c
... / ...
CommitLineData
1/*
2 * Copyright (c) 1983 The Regents of the University of California.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms are permitted
6 * provided that the above copyright notice and this paragraph are
7 * duplicated in all such forms and that any documentation,
8 * advertising materials, and other materials related to such
9 * distribution and use acknowledge that the software was developed
10 * by the University of California, Berkeley. The name of the
11 * University may not be used to endorse or promote products derived
12 * from this software without specific prior written permission.
13 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
14 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
15 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
16 */
17
18#ifndef lint
19static char sccsid[] = "@(#)remcap.c 5.2 (Berkeley) %G%";
20#endif /* not lint */
21
22/*
23 * remcap - routines for dealing with the remote host data base
24 *
25 * derived from termcap
26 */
27#include <sys/file.h>
28#include <ctype.h>
29
30#ifndef BUFSIZ
31#define BUFSIZ 1024
32#endif
33#define MAXHOP 32 /* max number of tc= indirections */
34#define SYSREMOTE "/etc/remote" /* system remote file */
35
36#define tgetent rgetent
37#define tnchktc rnchktc
38#define tnamatch rnamatch
39#define tgetnum rgetnum
40#define tgetflag rgetflag
41#define tgetstr rgetstr
42#define E_TERMCAP RM = SYSREMOTE
43#define V_TERMCAP "REMOTE"
44#define V_TERM "HOST"
45
46char *RM;
47
48/*
49 * termcap - routines for dealing with the terminal capability data base
50 *
51 * BUG: Should use a "last" pointer in tbuf, so that searching
52 * for capabilities alphabetically would not be a n**2/2
53 * process when large numbers of capabilities are given.
54 * Note: If we add a last pointer now we will screw up the
55 * tc capability. We really should compile termcap.
56 *
57 * Essentially all the work here is scanning and decoding escapes
58 * in string capabilities. We don't use stdio because the editor
59 * doesn't, and because living w/o it is not hard.
60 */
61
62static char *tbuf;
63static int hopcount; /* detect infinite loops in termcap, init 0 */
64char *tskip();
65char *tgetstr();
66char *tdecode();
67char *getenv();
68static char *remotefile;
69
70/*
71 * Get an entry for terminal name in buffer bp,
72 * from the termcap file. Parse is very rudimentary;
73 * we just notice escaped newlines.
74 */
75tgetent(bp, name)
76 char *bp, *name;
77{
78 char lbuf[BUFSIZ], *cp, *p;
79 int rc1, rc2;
80
81 remotefile = cp = getenv(V_TERMCAP);
82 if (cp == (char *)0 || strcmp(cp, SYSREMOTE) == 0) {
83 remotefile = cp = SYSREMOTE;
84 return (getent(bp, name, cp));
85 } else {
86 if ((rc1 = getent(bp, name, cp)) != 1)
87 *bp = '\0';
88 remotefile = cp = SYSREMOTE;
89 rc2 = getent(lbuf, name, cp);
90 if (rc1 != 1 && rc2 != 1)
91 return (rc2);
92 if (rc2 == 1) {
93 p = lbuf;
94 if (rc1 == 1)
95 while (*p++ != ':')
96 ;
97 if (strlen(bp) + strlen(p) > BUFSIZ) {
98 write(2, "Remcap entry too long\n", 23);
99 return (-1);
100 }
101 strcat(bp, p);
102 }
103 tbuf = bp;
104 return (1);
105 }
106}
107
108getent(bp, name, cp)
109 char *bp, *name, *cp;
110{
111 register int c;
112 register int i = 0, cnt = 0;
113 char ibuf[BUFSIZ], *cp2;
114 int tf;
115
116 tbuf = bp;
117 tf = 0;
118 /*
119 * TERMCAP can have one of two things in it. It can be the
120 * name of a file to use instead of /etc/termcap. In this
121 * case it better start with a "/". Or it can be an entry to
122 * use so we don't have to read the file. In this case it
123 * has to already have the newlines crunched out.
124 */
125 if (cp && *cp) {
126 if (*cp!='/') {
127 cp2 = getenv(V_TERM);
128 if (cp2 == (char *)0 || strcmp(name,cp2) == 0) {
129 strcpy(bp,cp);
130 return (tnchktc());
131 } else
132 tf = open(E_TERMCAP, O_RDONLY);
133 } else
134 tf = open(RM = cp, O_RDONLY);
135 }
136 if (tf == 0)
137 tf = open(E_TERMCAP, O_RDONLY);
138 if (tf < 0)
139 return (-1);
140 for (;;) {
141 cp = bp;
142 for (;;) {
143 if (i == cnt) {
144 cnt = read(tf, ibuf, BUFSIZ);
145 if (cnt <= 0) {
146 close(tf);
147 return (0);
148 }
149 i = 0;
150 }
151 c = ibuf[i++];
152 if (c == '\n') {
153 if (cp > bp && cp[-1] == '\\') {
154 cp--;
155 continue;
156 }
157 break;
158 }
159 if (cp >= bp+BUFSIZ) {
160 write(2,"Remcap entry too long\n", 23);
161 break;
162 } else
163 *cp++ = c;
164 }
165 *cp = 0;
166
167 /*
168 * The real work for the match.
169 */
170 if (tnamatch(name)) {
171 close(tf);
172 return (tnchktc());
173 }
174 }
175}
176
177/*
178 * tnchktc: check the last entry, see if it's tc=xxx. If so,
179 * recursively find xxx and append that entry (minus the names)
180 * to take the place of the tc=xxx entry. This allows termcap
181 * entries to say "like an HP2621 but doesn't turn on the labels".
182 * Note that this works because of the left to right scan.
183 */
184tnchktc()
185{
186 register char *p, *q;
187 char tcname[16]; /* name of similar terminal */
188 char tcbuf[BUFSIZ];
189 char *holdtbuf = tbuf;
190 int l;
191 char *cp;
192
193 p = tbuf + strlen(tbuf) - 2; /* before the last colon */
194 while (*--p != ':')
195 if (p<tbuf) {
196 write(2, "Bad remcap entry\n", 18);
197 return (0);
198 }
199 p++;
200 /* p now points to beginning of last field */
201 if (p[0] != 't' || p[1] != 'c')
202 return (1);
203 strcpy(tcname, p+3);
204 q = tcname;
205 while (*q && *q != ':')
206 q++;
207 *q = 0;
208 if (++hopcount > MAXHOP) {
209 write(2, "Infinite tc= loop\n", 18);
210 return (0);
211 }
212 if (getent(tcbuf, tcname, remotefile) != 1) {
213 if (strcmp(remotefile, SYSREMOTE) == 0)
214 return (0);
215 else if (getent(tcbuf, tcname, SYSREMOTE) != 1)
216 return (0);
217 }
218 for (q = tcbuf; *q++ != ':'; )
219 ;
220 l = p - holdtbuf + strlen(q);
221 if (l > BUFSIZ) {
222 write(2, "Remcap entry too long\n", 23);
223 q[BUFSIZ - (p-holdtbuf)] = 0;
224 }
225 strcpy(p, q);
226 tbuf = holdtbuf;
227 return (1);
228}
229
230/*
231 * Tnamatch deals with name matching. The first field of the termcap
232 * entry is a sequence of names separated by |'s, so we compare
233 * against each such name. The normal : terminator after the last
234 * name (before the first field) stops us.
235 */
236tnamatch(np)
237 char *np;
238{
239 register char *Np, *Bp;
240
241 Bp = tbuf;
242 if (*Bp == '#')
243 return (0);
244 for (;;) {
245 for (Np = np; *Np && *Bp == *Np; Bp++, Np++)
246 continue;
247 if (*Np == 0 && (*Bp == '|' || *Bp == ':' || *Bp == 0))
248 return (1);
249 while (*Bp && *Bp != ':' && *Bp != '|')
250 Bp++;
251 if (*Bp == 0 || *Bp == ':')
252 return (0);
253 Bp++;
254 }
255}
256
257/*
258 * Skip to the next field. Notice that this is very dumb, not
259 * knowing about \: escapes or any such. If necessary, :'s can be put
260 * into the termcap file in octal.
261 */
262static char *
263tskip(bp)
264 register char *bp;
265{
266
267 while (*bp && *bp != ':')
268 bp++;
269 if (*bp == ':')
270 bp++;
271 return (bp);
272}
273
274/*
275 * Return the (numeric) option id.
276 * Numeric options look like
277 * li#80
278 * i.e. the option string is separated from the numeric value by
279 * a # character. If the option is not found we return -1.
280 * Note that we handle octal numbers beginning with 0.
281 */
282tgetnum(id)
283 char *id;
284{
285 register int i, base;
286 register char *bp = tbuf;
287
288 for (;;) {
289 bp = tskip(bp);
290 if (*bp == 0)
291 return (-1);
292 if (*bp++ != id[0] || *bp == 0 || *bp++ != id[1])
293 continue;
294 if (*bp == '@')
295 return (-1);
296 if (*bp != '#')
297 continue;
298 bp++;
299 base = 10;
300 if (*bp == '0')
301 base = 8;
302 i = 0;
303 while (isdigit(*bp))
304 i *= base, i += *bp++ - '0';
305 return (i);
306 }
307}
308
309/*
310 * Handle a flag option.
311 * Flag options are given "naked", i.e. followed by a : or the end
312 * of the buffer. Return 1 if we find the option, or 0 if it is
313 * not given.
314 */
315tgetflag(id)
316 char *id;
317{
318 register char *bp = tbuf;
319
320 for (;;) {
321 bp = tskip(bp);
322 if (!*bp)
323 return (0);
324 if (*bp++ == id[0] && *bp != 0 && *bp++ == id[1]) {
325 if (!*bp || *bp == ':')
326 return (1);
327 else if (*bp == '@')
328 return (0);
329 }
330 }
331}
332
333/*
334 * Get a string valued option.
335 * These are given as
336 * cl=^Z
337 * Much decoding is done on the strings, and the strings are
338 * placed in area, which is a ref parameter which is updated.
339 * No checking on area overflow.
340 */
341char *
342tgetstr(id, area)
343 char *id, **area;
344{
345 register char *bp = tbuf;
346
347 for (;;) {
348 bp = tskip(bp);
349 if (!*bp)
350 return (0);
351 if (*bp++ != id[0] || *bp == 0 || *bp++ != id[1])
352 continue;
353 if (*bp == '@')
354 return (0);
355 if (*bp != '=')
356 continue;
357 bp++;
358 return (tdecode(bp, area));
359 }
360}
361
362/*
363 * Tdecode does the grung work to decode the
364 * string capability escapes.
365 */
366static char *
367tdecode(str, area)
368 register char *str;
369 char **area;
370{
371 register char *cp;
372 register int c;
373 register char *dp;
374 int i;
375
376 cp = *area;
377 while ((c = *str++) && c != ':') {
378 switch (c) {
379
380 case '^':
381 c = *str++ & 037;
382 break;
383
384 case '\\':
385 dp = "E\033^^\\\\::n\nr\rt\tb\bf\f";
386 c = *str++;
387nextc:
388 if (*dp++ == c) {
389 c = *dp++;
390 break;
391 }
392 dp++;
393 if (*dp)
394 goto nextc;
395 if (isdigit(c)) {
396 c -= '0', i = 2;
397 do
398 c <<= 3, c |= *str++ - '0';
399 while (--i && isdigit(*str));
400 }
401 break;
402 }
403 *cp++ = c;
404 }
405 *cp++ = 0;
406 str = *area;
407 *area = cp;
408 return (str);
409}