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