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