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