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