Generally use NULL instead of explicitly casting 0 to some pointer type.
[dragonfly.git] / usr.bin / tip / tip / remcap.c
1 /*
2  * Copyright (c) 1983, 1993
3  *      The Regents of the University of California.  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  * @(#)remcap.c 8.1 (Berkeley) 6/6/93
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 <fcntl.h>
43 #include <ctype.h>
44 #include <stdlib.h>
45 #include "pathnames.h"
46
47 #ifndef BUFSIZ
48 #define BUFSIZ          1024
49 #endif
50 #define MAXHOP          32              /* max number of tc= indirections */
51
52 #define tgetent         rgetent
53 #define tnchktc         rnchktc
54 #define tnamatch        rnamatch
55 #define tgetnum         rgetnum
56 #define tgetflag        rgetflag
57 #define tgetstr         rgetstr
58 #define E_TERMCAP       RM = _PATH_REMOTE
59 #define V_TERMCAP       "REMOTE"
60 #define V_TERM          "HOST"
61
62 char    *RM;
63
64 /*
65  * termcap - routines for dealing with the terminal capability data base
66  *
67  * BUG:         Should use a "last" pointer in tbuf, so that searching
68  *              for capabilities alphabetically would not be a n**2/2
69  *              process when large numbers of capabilities are given.
70  * Note:        If we add a last pointer now we will screw up the
71  *              tc capability. We really should compile termcap.
72  *
73  * Essentially all the work here is scanning and decoding escapes
74  * in string capabilities.  We don't use stdio because the editor
75  * doesn't, and because living w/o it is not hard.
76  */
77
78 static  char *tbuf;
79 static  int hopcount;   /* detect infinite loops in termcap, init 0 */
80 static  char *tskip();
81 char    *tgetstr();
82 static  char *tdecode();
83 static  char *remotefile;
84
85 /*
86  * Get an entry for terminal name in buffer bp,
87  * from the termcap file.  Parse is very rudimentary;
88  * we just notice escaped newlines.
89  */
90 tgetent(bp, name)
91         char *bp, *name;
92 {
93         char lbuf[BUFSIZ], *cp, *p;
94         int rc1, rc2;
95
96         remotefile = cp = getenv(V_TERMCAP);
97         if (cp == NULL || strcmp(cp, _PATH_REMOTE) == 0) {
98                 remotefile = cp = _PATH_REMOTE;
99                 return (getent(bp, name, cp));
100         } else {
101                 if ((rc1 = getent(bp, name, cp)) != 1)
102                         *bp = '\0';
103                 remotefile = cp = _PATH_REMOTE;
104                 rc2 = getent(lbuf, name, cp);
105                 if (rc1 != 1 && rc2 != 1)
106                         return (rc2);
107                 if (rc2 == 1) {
108                         p = lbuf;
109                         if (rc1 == 1)
110                                 while (*p++ != ':')
111                                         ;
112                         if (strlen(bp) + strlen(p) > BUFSIZ) {
113                                 write(2, "Remcap entry too long\n", 23);
114                                 return (-1);
115                         }
116                         strcat(bp, p);
117                 }
118                 tbuf = bp;
119                 return (1);
120         }
121 }
122
123 getent(bp, name, cp)
124         char *bp, *name, *cp;
125 {
126         register int c;
127         register int i = 0, cnt = 0;
128         char ibuf[BUFSIZ], *cp2;
129         int tf;
130
131         tbuf = bp;
132         tf = 0;
133         /*
134          * TERMCAP can have one of two things in it. It can be the
135          * name of a file to use instead of /etc/termcap. In this
136          * case it better start with a "/". Or it can be an entry to
137          * use so we don't have to read the file. In this case it
138          * has to already have the newlines crunched out.
139          */
140         if (cp && *cp) {
141                 if (*cp!='/') {
142                         cp2 = getenv(V_TERM);
143                         if (cp2 == NULL || strcmp(name,cp2) == 0) {
144                                 strcpy(bp,cp);
145                                 return (tnchktc());
146                         } else
147                                 tf = open(E_TERMCAP, O_RDONLY);
148                 } else
149                         tf = open(RM = cp, O_RDONLY);
150         }
151         if (tf == 0)
152                 tf = open(E_TERMCAP, O_RDONLY);
153         if (tf < 0)
154                 return (-1);
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 tnchktc()
200 {
201         register char *p, *q;
202         char tcname[16];        /* name of similar terminal */
203         char tcbuf[BUFSIZ];
204         char *holdtbuf = tbuf;
205         int l;
206         char *cp;
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                 if (strcmp(remotefile, _PATH_REMOTE) == 0)
229                         return (0);
230                 else if (getent(tcbuf, tcname, _PATH_REMOTE) != 1)
231                         return (0);
232         }
233         for (q = tcbuf; *q++ != ':'; )
234                 ;
235         l = p - holdtbuf + strlen(q);
236         if (l > BUFSIZ) {
237                 write(2, "Remcap entry too long\n", 23);
238                 q[BUFSIZ - (p-holdtbuf)] = 0;
239         }
240         strcpy(p, q);
241         tbuf = holdtbuf;
242         return (1);
243 }
244
245 /*
246  * Tnamatch deals with name matching.  The first field of the termcap
247  * entry is a sequence of names separated by |'s, so we compare
248  * against each such name.  The normal : terminator after the last
249  * name (before the first field) stops us.
250  */
251 tnamatch(np)
252         char *np;
253 {
254         register char *Np, *Bp;
255
256         Bp = tbuf;
257         if (*Bp == '#')
258                 return (0);
259         for (;;) {
260                 for (Np = np; *Np && *Bp == *Np; Bp++, Np++)
261                         continue;
262                 if (*Np == 0 && (*Bp == '|' || *Bp == ':' || *Bp == 0))
263                         return (1);
264                 while (*Bp && *Bp != ':' && *Bp != '|')
265                         Bp++;
266                 if (*Bp == 0 || *Bp == ':')
267                         return (0);
268                 Bp++;
269         }
270 }
271
272 /*
273  * Skip to the next field.  Notice that this is very dumb, not
274  * knowing about \: escapes or any such.  If necessary, :'s can be put
275  * into the termcap file in octal.
276  */
277 static char *
278 tskip(bp)
279         register char *bp;
280 {
281
282         while (*bp && *bp != ':')
283                 bp++;
284         if (*bp == ':')
285                 bp++;
286         return (bp);
287 }
288
289 /*
290  * Return the (numeric) option id.
291  * Numeric options look like
292  *      li#80
293  * i.e. the option string is separated from the numeric value by
294  * a # character.  If the option is not found we return -1.
295  * Note that we handle octal numbers beginning with 0.
296  */
297 tgetnum(id)
298         char *id;
299 {
300         register int i, base;
301         register char *bp = tbuf;
302
303         for (;;) {
304                 bp = tskip(bp);
305                 if (*bp == 0)
306                         return (-1);
307                 if (*bp++ != id[0] || *bp == 0 || *bp++ != id[1])
308                         continue;
309                 if (*bp == '@')
310                         return (-1);
311                 if (*bp != '#')
312                         continue;
313                 bp++;
314                 base = 10;
315                 if (*bp == '0')
316                         base = 8;
317                 i = 0;
318                 while (isdigit(*bp))
319                         i *= base, i += *bp++ - '0';
320                 return (i);
321         }
322 }
323
324 /*
325  * Handle a flag option.
326  * Flag options are given "naked", i.e. followed by a : or the end
327  * of the buffer.  Return 1 if we find the option, or 0 if it is
328  * not given.
329  */
330 tgetflag(id)
331         char *id;
332 {
333         register char *bp = tbuf;
334
335         for (;;) {
336                 bp = tskip(bp);
337                 if (!*bp)
338                         return (0);
339                 if (*bp++ == id[0] && *bp != 0 && *bp++ == id[1]) {
340                         if (!*bp || *bp == ':')
341                                 return (1);
342                         else if (*bp == '@')
343                                 return (0);
344                 }
345         }
346 }
347
348 /*
349  * Get a string valued option.
350  * These are given as
351  *      cl=^Z
352  * Much decoding is done on the strings, and the strings are
353  * placed in area, which is a ref parameter which is updated.
354  * No checking on area overflow.
355  */
356 char *
357 tgetstr(id, area)
358         char *id, **area;
359 {
360         register char *bp = tbuf;
361
362         for (;;) {
363                 bp = tskip(bp);
364                 if (!*bp)
365                         return (0);
366                 if (*bp++ != id[0] || *bp == 0 || *bp++ != id[1])
367                         continue;
368                 if (*bp == '@')
369                         return (0);
370                 if (*bp != '=')
371                         continue;
372                 bp++;
373                 return (tdecode(bp, area));
374         }
375 }
376
377 /*
378  * Tdecode does the grung work to decode the
379  * string capability escapes.
380  */
381 static char *
382 tdecode(str, area)
383         register char *str;
384         char **area;
385 {
386         register char *cp;
387         register int c;
388         register char *dp;
389         int i;
390
391         cp = *area;
392         while ((c = *str++) && c != ':') {
393                 switch (c) {
394
395                 case '^':
396                         c = *str++ & 037;
397                         break;
398
399                 case '\\':
400                         dp = "E\033^^\\\\::n\nr\rt\tb\bf\f";
401                         c = *str++;
402 nextc:
403                         if (*dp++ == c) {
404                                 c = *dp++;
405                                 break;
406                         }
407                         dp++;
408                         if (*dp)
409                                 goto nextc;
410                         if (isdigit(c)) {
411                                 c -= '0', i = 2;
412                                 do
413                                         c <<= 3, c |= *str++ - '0';
414                                 while (--i && isdigit(*str));
415                         }
416                         break;
417                 }
418                 *cp++ = c;
419         }
420         *cp++ = 0;
421         str = *area;
422         *area = cp;
423         return (str);
424 }