source: trunk/athena/bin/lpr/printcap.c @ 7990

Revision 7990, 11.4 KB checked in by cfields, 29 years ago (diff)
close() before fclose() the child's file descriptor. Ain't POSIX great? See comments in code.
Line 
1/*
2 * Copyright (c) 1983 Regents of the University of California.
3 * All rights reserved.  The Berkeley software License Agreement
4 * specifies the terms and conditions for redistribution.
5 */
6
7#ifndef lint
8static char sccsid[] = "@(#)printcap.c  5.1 (Berkeley) 6/6/85";
9#endif not lint
10
11#define MAXHOP  32      /* max number of tc= indirections */
12
13#include <ctype.h>
14#include <stdio.h>
15#include <string.h>
16#ifdef HESIOD
17#include <hesiod.h>
18#include "lp.local.h"
19#endif
20#ifndef BUFSIZ
21#define BUFSIZ  1024
22#endif
23
24/*
25 * termcap - routines for dealing with the terminal capability data base
26 *
27 * BUG:         Should use a "last" pointer in tbuf, so that searching
28 *              for capabilities alphabetically would not be a n**2/2
29 *              process when large numbers of capabilities are given.
30 * Note:        If we add a last pointer now we will screw up the
31 *              tc capability. We really should compile termcap.
32 *
33 * Essentially all the work here is scanning and decoding escapes
34 * in string capabilities.  We don't use stdio because the editor
35 * doesn't, and because living w/o it is not hard.
36 *
37 * 4/13/87      Modified to use Hesiod name server iff HESIOD
38 * SDyer        is defined.  If no printer capability is found in the
39 * Athena       /etc/printcap file, Hesiod is queried for an alias for
40 *              the given printer name and its capability.  In looking
41 *              up the alias, Hesiod may also be queried for the host's
42 *              cluster name.
43 */
44
45#define PRINTCAP
46
47#ifdef PRINTCAP
48#define tgetent pgetent
49#define tskip   pskip
50#define tgetstr pgetstr
51#define tdecode pdecode
52#define tgetnum pgetnum
53#define tgetflag pgetflag
54#define tdecode pdecode
55#define tnchktc pnchktc
56#define tnamatch pnamatch
57#ifdef E_TERMCAP
58#undef E_TERMCAP
59#endif
60#define E_TERMCAP "/etc/printcap"
61#define V6
62#endif
63
64static  FILE *pfp = NULL;       /* printcap data base file pointer */
65static  char *tbuf;
66static  int hopcount;           /* detect infinite loops in termcap, init 0 */
67char    *tskip();
68char    *tgetstr();
69char    *tdecode();
70char    *getenv();
71
72#ifdef HESIOD
73/* Ask Hesiod if printer name is an alias.  If so,
74 * put new name in "name" and return 1.  If not, return 0.
75 * format:  -Pprinter[.cluster]
76 */
77
78int pralias(buf, name)
79        char buf[];             /* buffer to put printer alias in */
80        char *name;             /* name of printer to look up */
81{
82        char *e, temp[BUFSIZ/2];
83#ifdef OLD_DEFAULT_ALIAS
84        char **hv;
85#endif
86        char *getclus();
87       
88        strcpy(temp, name);
89        /* If printer name == "default" then lookup based on LPR cluster info*/
90        if(!strcmp(name, DEFLP)) {
91            if ((e = getclus()) != NULL) {
92                strcpy(buf, e);
93                return(1);
94            }
95        }
96
97#ifdef OLD_DEFAULT_ALIAS
98        /* if not in "printer.cluster" format, look up cluster and append */
99        if (strchr(name, '.') == NULL)
100                if ((e = getenv("LPR")) != NULL) {
101                        strcat(temp, ".");
102                        strcat(temp, e);
103                } else if ((e = getclus()) != NULL) {
104                        strcat(temp, ".");
105                        strcat(temp, e);
106                }
107        if ((hv = hes_resolve(temp, "lpralias")) != NULL &&
108            strlen(*hv) < BUFSIZ/2) {
109                strcpy(buf, *hv);
110                return(1);
111        }
112#endif
113        return (0);
114}
115
116/* Find this host's cluster name */
117char *
118getclus()
119{
120        static char cluster[BUFSIZ/2];
121        char host[32];
122        char **hv;
123        int len = 4;  /* length of string "lpr " */
124
125        gethostname(host, sizeof (host));
126        if ((hv = hes_resolve(host, "cluster")) == NULL)
127                return NULL;
128        while (*hv) {
129                if (strncmp(*hv, "lpr ", len) == 0) {
130                        strcpy(cluster, *hv + len);
131                        return cluster;
132                        }
133                ++hv;
134        }
135        return NULL;
136}
137       
138/*
139 * Look for printer capability in Hesiod.
140 * If eventually found, copy into "line" and return 1, otherwise
141 * return 0.
142 */
143
144hpgetent(line, print)
145        char *line;
146        char *print;
147{
148        register char **hv;
149        char **hes_resolve();
150
151        tbuf = line;
152        if ( (hv = hes_resolve(print, "pcap")) != NULL) {
153                strcpy(line, *hv);
154                return(tnchktc());
155        }
156        return 0;
157}
158
159#endif HESIOD
160
161/*
162 * Similar to tgetent except it returns the next entry instead of
163 * doing a lookup.
164 */
165getprent(bp)
166        register char *bp;
167{
168        register int c, skip = 0;
169
170        if (pfp == NULL && (pfp = fopen(E_TERMCAP, "r")) == NULL)
171                return(-1);
172        tbuf = bp;
173        for (;;) {
174                switch (c = getc(pfp)) {
175                case EOF:
176                        fclose(pfp);
177                        pfp = NULL;
178                        return(0);
179                case '\n':
180                        if (bp == tbuf) {
181                                skip = 0;
182                                continue;
183                        }
184                        if (bp[-1] == '\\') {
185                                bp--;
186                                continue;
187                        }
188                        *bp = '\0';
189                        return(1);
190                case '#':
191                        if (bp == tbuf)
192                                skip++;
193                default:
194                        if (skip)
195                                continue;
196                        if (bp >= tbuf+BUFSIZ) {
197                                write(2, "Termcap entry too long\n", 23);
198                                *bp = '\0';
199                                return(1);
200                        }
201                        *bp++ = c;
202                }
203        }
204}
205
206/*
207 * Why do we close() before fclose()?
208 *
209 * endprent is called only by forked children, and with forked
210 * children calling it under POSIX semantics there is a problem.
211 *
212 * When the forked child calls fclose(), fclose() (under POSIX) seeks
213 * the file (and it's sharing the file descriptor with the parent) to
214 * the byte after the last one read (as recorded in the FILE
215 * structure) before close()ing. At the time of fork(), that pointer
216 * is set to X. After the fork happens, the parent may get to run for
217 * a while before the child does, and so it merrily continues reading
218 * through the file, getting the pointer set to X+delta. But then the
219 * child gets its turn, and seeks the pointer back to X. So the
220 * parent's loop pointer has just been decremented, and may read
221 * through the file potentially forever.
222 *
223 * Clearly we do not want the seek. So we close() the descriptor so
224 * that fclose() (or exit() calling fclose()) can't muck with it. We
225 * then do the fclose() anyway in the hope that it will free the memory
226 * it was using.
227 *
228 * See Solaris stdio(3S) and fclose(3S) more details.
229 */
230endprent()
231{
232        if (pfp != NULL) {
233                close(fileno(pfp));
234                fclose(pfp);
235        }
236        pfp = NULL;
237}
238
239/*
240 * Get an entry for terminal name in buffer bp,
241 * from the termcap file.  Parse is very rudimentary;
242 * we just notice escaped newlines.
243 */
244
245tgetent(bp, name)
246        char *bp, *name;
247{
248        register char *cp;
249        register int c;
250        register int i = 0, cnt = 0;
251        char ibuf[BUFSIZ];
252#ifndef V6
253        char *cp2;
254#endif
255        int tf;
256
257        tbuf = bp;
258        tf = 0;
259#ifndef V6
260        cp = getenv("TERMCAP");
261        /*
262         * TERMCAP can have one of two things in it. It can be the
263         * name of a file to use instead of /etc/termcap. In this
264         * case it better start with a "/". Or it can be an entry to
265         * use so we don't have to read the file. In this case it
266         * has to already have the newlines crunched out.
267         */
268        if (cp && *cp) {
269                if (*cp!='/') {
270                        cp2 = getenv("TERM");
271                        if (cp2==(char *) 0 || strcmp(name,cp2)==0) {
272                                strcpy(bp,cp);
273                                return(tnchktc());
274                        } else {
275                                tf = open(E_TERMCAP, 0);
276                        }
277                } else
278                        tf = open(cp, 0);
279        }
280        if (tf==0)
281                tf = open(E_TERMCAP, 0);
282#else
283        tf = open(E_TERMCAP, 0);
284#endif
285        if (tf < 0)
286                return (-1);
287        for (;;) {
288                cp = bp;
289                for (;;) {
290                        if (i == cnt) {
291                                cnt = read(tf, ibuf, BUFSIZ);
292                                if (cnt <= 0) {
293                                        close(tf);
294                                        return (0);
295                                }
296                                i = 0;
297                        }
298                        c = ibuf[i++];
299                        if (c == '\n') {
300                                if (cp > bp && cp[-1] == '\\'){
301                                        cp--;
302                                        continue;
303                                }
304                                break;
305                        }
306                        if (cp >= bp+BUFSIZ) {
307                                write(2,"Termcap entry too long\n", 23);
308                                break;
309                        } else
310                                *cp++ = c;
311                }
312                *cp = 0;
313
314                /*
315                 * The real work for the match.
316                 */
317                if (tnamatch(name)) {
318                        close(tf);
319                        return(tnchktc());
320                }
321        }
322}
323
324/*
325 * tnchktc: check the last entry, see if it's tc=xxx. If so,
326 * recursively find xxx and append that entry (minus the names)
327 * to take the place of the tc=xxx entry. This allows termcap
328 * entries to say "like an HP2621 but doesn't turn on the labels".
329 * Note that this works because of the left to right scan.
330 */
331tnchktc()
332{
333        register char *p, *q;
334        char tcname[16];        /* name of similar terminal */
335        char tcbuf[BUFSIZ];
336        char *holdtbuf = tbuf;
337        int l;
338
339        p = tbuf + strlen(tbuf) - 2;    /* before the last colon */
340        while (*--p != ':')
341                if (p<tbuf) {
342                        write(2, "Bad termcap entry\n", 18);
343                        return (0);
344                }
345        p++;
346        /* p now points to beginning of last field */
347        if (p[0] != 't' || p[1] != 'c')
348                return(1);
349        strcpy(tcname,p+3);
350        q = tcname;
351        while (*q && *q != ':')/* was while (q && ... *//* SPD Athena 4/15/87 */
352                q++;
353        *q = 0;
354        if (++hopcount > MAXHOP) {
355                write(2, "Infinite tc= loop\n", 18);
356                return (0);
357        }
358#ifdef HESIOD
359        if (hpgetent(tcbuf, tcname) != 1)
360#else
361        if (tgetent(tcbuf, tcname) != 1)
362#endif
363                return(0);
364        for (q=tcbuf; *q != ':'; q++)
365                ;
366        l = p - holdtbuf + strlen(q);
367        if (l > BUFSIZ) {
368                write(2, "Termcap entry too long\n", 23);
369                q[BUFSIZ - (p-tbuf)] = 0;
370        }
371        strcpy(p, q+1);
372        tbuf = holdtbuf;
373        return(1);
374}
375
376/*
377 * Tnamatch deals with name matching.  The first field of the termcap
378 * entry is a sequence of names separated by |'s, so we compare
379 * against each such name.  The normal : terminator after the last
380 * name (before the first field) stops us.
381 */
382tnamatch(np)
383        char *np;
384{
385        register char *Np, *Bp;
386
387        Bp = tbuf;
388        if (*Bp == '#')
389                return(0);
390        for (;;) {
391                for (Np = np; *Np && *Bp == *Np; Bp++, Np++)
392                        continue;
393                if (*Np == 0 && (*Bp == '|' || *Bp == ':' || *Bp == 0))
394                        return (1);
395                while (*Bp && *Bp != ':' && *Bp != '|')
396                        Bp++;
397                if (*Bp == 0 || *Bp == ':')
398                        return (0);
399                Bp++;
400        }
401}
402
403/*
404 * Skip to the next field.  Notice that this is very dumb, not
405 * knowing about \: escapes or any such.  If necessary, :'s can be put
406 * into the termcap file in octal.
407 */
408static char *
409tskip(bp)
410        register char *bp;
411{
412
413        while (*bp && *bp != ':')
414                bp++;
415        if (*bp == ':')
416                bp++;
417        return (bp);
418}
419
420/*
421 * Return the (numeric) option id.
422 * Numeric options look like
423 *      li#80
424 * i.e. the option string is separated from the numeric value by
425 * a # character.  If the option is not found we return -1.
426 * Note that we handle octal numbers beginning with 0.
427 */
428tgetnum(id)
429        char *id;
430{
431        register int i, base;
432        register char *bp = tbuf;
433
434        for (;;) {
435                bp = tskip(bp);
436                if (*bp == 0)
437                        return (-1);
438                if (*bp++ != id[0] || *bp == 0 || *bp++ != id[1])
439                        continue;
440                if (*bp == '@')
441                        return(-1);
442                if (*bp != '#')
443                        continue;
444                bp++;
445                base = 10;
446                if (*bp == '0')
447                        base = 8;
448                i = 0;
449                while (isdigit(*bp))
450                        i *= base, i += *bp++ - '0';
451                return (i);
452        }
453}
454
455/*
456 * Handle a flag option.
457 * Flag options are given "naked", i.e. followed by a : or the end
458 * of the buffer.  Return 1 if we find the option, or 0 if it is
459 * not given.
460 */
461tgetflag(id)
462        char *id;
463{
464        register char *bp = tbuf;
465
466        for (;;) {
467                bp = tskip(bp);
468                if (!*bp)
469                        return (0);
470                if (*bp++ == id[0] && *bp != 0 && *bp++ == id[1]) {
471                        if (!*bp || *bp == ':')
472                                return (1);
473                        else if (*bp == '@')
474                                return(0);
475                }
476        }
477}
478
479/*
480 * Get a string valued option.
481 * These are given as
482 *      cl=^Z
483 * Much decoding is done on the strings, and the strings are
484 * placed in area, which is a ref parameter which is updated.
485 * No checking on area overflow.
486 */
487char *
488tgetstr(id, area)
489        char *id, **area;
490{
491        register char *bp = tbuf;
492       
493        for (;;) {
494                bp = tskip(bp);
495                if (!*bp)
496                        return (0);
497                if (*bp++ != id[0] || *bp == 0 || *bp++ != id[1])
498                        continue;
499                if (*bp == '@')
500                        return(0);
501                if (*bp != '=')
502                        continue;
503                bp++;
504                return (tdecode(bp, area));
505        }
506}
507
508/*
509 * Tdecode does the grung work to decode the
510 * string capability escapes.
511 */
512static char *
513tdecode(str, area)
514        register char *str;
515        char **area;
516{
517        register char *cp;
518        register int c;
519        register char *dp;
520        int i;
521
522        cp = *area;
523        while ((c = *str++) && c != ':') {
524                switch (c) {
525
526                case '^':
527                        c = *str++ & 037;
528                        break;
529
530                case '\\':
531                        dp = "E\033^^\\\\::n\nr\rt\tb\bf\f";
532                        c = *str++;
533nextc:
534                        if (*dp++ == c) {
535                                c = *dp++;
536                                break;
537                        }
538                        dp++;
539                        if (*dp)
540                                goto nextc;
541                        if (isdigit(c)) {
542                                c -= '0', i = 2;
543                                do
544                                        c <<= 3, c |= *str++ - '0';
545                                while (--i && isdigit(*str));
546                        }
547                        break;
548                }
549                *cp++ = c;
550        }
551        *cp++ = 0;
552        str = *area;
553        *area = cp;
554        return (str);
555}
Note: See TracBrowser for help on using the repository browser.