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

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