new copyright notice
[unix-history] / usr / src / usr.sbin / lpr / common_source / printcap.c
CommitLineData
d0aeaf5a
DF
1/*
2 * Copyright (c) 1983 Regents of the University of California.
9d85c861
KB
3 * All rights reserved.
4 *
417f7a11 5 * %sccs.include.redist.c%
d0aeaf5a
DF
6 */
7
5f84f8f0 8#ifndef lint
417f7a11 9static char sccsid[] = "@(#)printcap.c 5.5 (Berkeley) %G%";
9d85c861 10#endif /* not lint */
5f84f8f0 11
60d01856
SL
12#define BUFSIZ 1024
13#define MAXHOP 32 /* max number of tc= indirections */
2fd90732
BJ
14
15#include <ctype.h>
4d4caa50 16#include <stdio.h>
7abf8d65
KB
17#include "pathnames.h"
18
2fd90732 19/*
60d01856 20 * termcap - routines for dealing with the terminal capability data base
2fd90732
BJ
21 *
22 * BUG: Should use a "last" pointer in tbuf, so that searching
23 * for capabilities alphabetically would not be a n**2/2
24 * process when large numbers of capabilities are given.
60d01856
SL
25 * Note: If we add a last pointer now we will screw up the
26 * tc capability. We really should compile termcap.
2fd90732
BJ
27 *
28 * Essentially all the work here is scanning and decoding escapes
29 * in string capabilities. We don't use stdio because the editor
30 * doesn't, and because living w/o it is not hard.
31 */
32
60d01856
SL
33#define PRINTCAP
34
35#ifdef PRINTCAP
36#define tgetent pgetent
37#define tskip pskip
38#define tgetstr pgetstr
39#define tdecode pdecode
40#define tgetnum pgetnum
41#define tgetflag pgetflag
42#define tdecode pdecode
43#define tnchktc pnchktc
44#define tnamatch pnamatch
60d01856
SL
45#define V6
46#endif
47
4d4caa50 48static FILE *pfp = NULL; /* printcap data base file pointer */
60d01856 49static char *tbuf;
4d4caa50 50static int hopcount; /* detect infinite loops in termcap, init 0 */
60d01856
SL
51char *tskip();
52char *tgetstr();
53char *tdecode();
54char *getenv();
2fd90732 55
4d4caa50
RC
56/*
57 * Similar to tgetent except it returns the next enrty instead of
58 * doing a lookup.
59 */
60getprent(bp)
61 register char *bp;
62{
63 register int c, skip = 0;
64
7abf8d65 65 if (pfp == NULL && (pfp = fopen(_PATH_PRINTCAP, "r")) == NULL)
4d4caa50
RC
66 return(-1);
67 tbuf = bp;
68 for (;;) {
69 switch (c = getc(pfp)) {
70 case EOF:
71 fclose(pfp);
72 pfp = NULL;
73 return(0);
74 case '\n':
75 if (bp == tbuf) {
76 skip = 0;
77 continue;
78 }
79 if (bp[-1] == '\\') {
80 bp--;
81 continue;
82 }
83 *bp = '\0';
84 return(1);
85 case '#':
86 if (bp == tbuf)
87 skip++;
88 default:
89 if (skip)
90 continue;
91 if (bp >= tbuf+BUFSIZ) {
92 write(2, "Termcap entry too long\n", 23);
93 *bp = '\0';
94 return(1);
95 }
96 *bp++ = c;
97 }
98 }
99}
100
101endprent()
102{
103 if (pfp != NULL)
104 fclose(pfp);
105}
106
2fd90732 107/*
60d01856
SL
108 * Get an entry for terminal name in buffer bp,
109 * from the termcap file. Parse is very rudimentary;
2fd90732
BJ
110 * we just notice escaped newlines.
111 */
60d01856 112tgetent(bp, name)
2fd90732
BJ
113 char *bp, *name;
114{
115 register char *cp;
116 register int c;
117 register int i = 0, cnt = 0;
118 char ibuf[BUFSIZ];
60d01856 119 char *cp2;
2fd90732
BJ
120 int tf;
121
60d01856
SL
122 tbuf = bp;
123 tf = 0;
124#ifndef V6
125 cp = getenv("TERMCAP");
126 /*
127 * TERMCAP can have one of two things in it. It can be the
128 * name of a file to use instead of /etc/termcap. In this
129 * case it better start with a "/". Or it can be an entry to
130 * use so we don't have to read the file. In this case it
131 * has to already have the newlines crunched out.
132 */
133 if (cp && *cp) {
134 if (*cp!='/') {
135 cp2 = getenv("TERM");
136 if (cp2==(char *) 0 || strcmp(name,cp2)==0) {
137 strcpy(bp,cp);
138 return(tnchktc());
139 } else {
7abf8d65 140 tf = open(_PATH_PRINTCAP, 0);
60d01856
SL
141 }
142 } else
143 tf = open(cp, 0);
144 }
145 if (tf==0)
7abf8d65 146 tf = open(_PATH_PRINTCAP, 0);
60d01856 147#else
7abf8d65 148 tf = open(_PATH_PRINTCAP, 0);
60d01856 149#endif
2fd90732
BJ
150 if (tf < 0)
151 return (-1);
152 for (;;) {
153 cp = bp;
154 for (;;) {
155 if (i == cnt) {
156 cnt = read(tf, ibuf, BUFSIZ);
157 if (cnt <= 0) {
158 close(tf);
159 return (0);
160 }
161 i = 0;
162 }
163 c = ibuf[i++];
164 if (c == '\n') {
165 if (cp > bp && cp[-1] == '\\'){
166 cp--;
167 continue;
168 }
169 break;
170 }
60d01856
SL
171 if (cp >= bp+BUFSIZ) {
172 write(2,"Termcap entry too long\n", 23);
173 break;
174 } else
175 *cp++ = c;
2fd90732
BJ
176 }
177 *cp = 0;
178
179 /*
180 * The real work for the match.
181 */
60d01856 182 if (tnamatch(name)) {
2fd90732 183 close(tf);
60d01856
SL
184 return(tnchktc());
185 }
186 }
187}
188
189/*
190 * tnchktc: check the last entry, see if it's tc=xxx. If so,
191 * recursively find xxx and append that entry (minus the names)
192 * to take the place of the tc=xxx entry. This allows termcap
193 * entries to say "like an HP2621 but doesn't turn on the labels".
194 * Note that this works because of the left to right scan.
195 */
196tnchktc()
197{
198 register char *p, *q;
199 char tcname[16]; /* name of similar terminal */
200 char tcbuf[BUFSIZ];
201 char *holdtbuf = tbuf;
202 int l;
203
204 p = tbuf + strlen(tbuf) - 2; /* before the last colon */
205 while (*--p != ':')
206 if (p<tbuf) {
207 write(2, "Bad termcap entry\n", 18);
208 return (0);
2fd90732 209 }
60d01856
SL
210 p++;
211 /* p now points to beginning of last field */
212 if (p[0] != 't' || p[1] != 'c')
213 return(1);
214 strcpy(tcname,p+3);
215 q = tcname;
216 while (q && *q != ':')
217 q++;
218 *q = 0;
219 if (++hopcount > MAXHOP) {
220 write(2, "Infinite tc= loop\n", 18);
221 return (0);
222 }
223 if (tgetent(tcbuf, tcname) != 1)
224 return(0);
225 for (q=tcbuf; *q != ':'; q++)
226 ;
227 l = p - holdtbuf + strlen(q);
228 if (l > BUFSIZ) {
229 write(2, "Termcap entry too long\n", 23);
230 q[BUFSIZ - (p-tbuf)] = 0;
2fd90732 231 }
60d01856
SL
232 strcpy(p, q+1);
233 tbuf = holdtbuf;
234 return(1);
2fd90732
BJ
235}
236
237/*
60d01856 238 * Tnamatch deals with name matching. The first field of the termcap
2fd90732
BJ
239 * entry is a sequence of names separated by |'s, so we compare
240 * against each such name. The normal : terminator after the last
241 * name (before the first field) stops us.
242 */
60d01856 243tnamatch(np)
2fd90732
BJ
244 char *np;
245{
246 register char *Np, *Bp;
247
60d01856
SL
248 Bp = tbuf;
249 if (*Bp == '#')
250 return(0);
2fd90732
BJ
251 for (;;) {
252 for (Np = np; *Np && *Bp == *Np; Bp++, Np++)
253 continue;
254 if (*Np == 0 && (*Bp == '|' || *Bp == ':' || *Bp == 0))
255 return (1);
256 while (*Bp && *Bp != ':' && *Bp != '|')
257 Bp++;
258 if (*Bp == 0 || *Bp == ':')
259 return (0);
260 Bp++;
261 }
262}
263
264/*
265 * Skip to the next field. Notice that this is very dumb, not
266 * knowing about \: escapes or any such. If necessary, :'s can be put
60d01856 267 * into the termcap file in octal.
2fd90732
BJ
268 */
269static char *
60d01856 270tskip(bp)
2fd90732
BJ
271 register char *bp;
272{
273
274 while (*bp && *bp != ':')
275 bp++;
276 if (*bp == ':')
277 bp++;
278 return (bp);
279}
280
281/*
282 * Return the (numeric) option id.
283 * Numeric options look like
284 * li#80
285 * i.e. the option string is separated from the numeric value by
286 * a # character. If the option is not found we return -1.
287 * Note that we handle octal numbers beginning with 0.
288 */
60d01856 289tgetnum(id)
2fd90732
BJ
290 char *id;
291{
292 register int i, base;
60d01856 293 register char *bp = tbuf;
2fd90732
BJ
294
295 for (;;) {
60d01856 296 bp = tskip(bp);
2fd90732
BJ
297 if (*bp == 0)
298 return (-1);
299 if (*bp++ != id[0] || *bp == 0 || *bp++ != id[1])
300 continue;
60d01856
SL
301 if (*bp == '@')
302 return(-1);
2fd90732
BJ
303 if (*bp != '#')
304 continue;
305 bp++;
306 base = 10;
307 if (*bp == '0')
308 base = 8;
309 i = 0;
310 while (isdigit(*bp))
311 i *= base, i += *bp++ - '0';
312 return (i);
313 }
314}
315
316/*
317 * Handle a flag option.
318 * Flag options are given "naked", i.e. followed by a : or the end
319 * of the buffer. Return 1 if we find the option, or 0 if it is
320 * not given.
321 */
60d01856 322tgetflag(id)
2fd90732
BJ
323 char *id;
324{
60d01856 325 register char *bp = tbuf;
2fd90732
BJ
326
327 for (;;) {
60d01856 328 bp = tskip(bp);
2fd90732
BJ
329 if (!*bp)
330 return (0);
60d01856
SL
331 if (*bp++ == id[0] && *bp != 0 && *bp++ == id[1]) {
332 if (!*bp || *bp == ':')
333 return (1);
334 else if (*bp == '@')
335 return(0);
336 }
2fd90732
BJ
337 }
338}
339
340/*
341 * Get a string valued option.
342 * These are given as
343 * cl=^Z
344 * Much decoding is done on the strings, and the strings are
345 * placed in area, which is a ref parameter which is updated.
346 * No checking on area overflow.
347 */
348char *
60d01856 349tgetstr(id, area)
2fd90732
BJ
350 char *id, **area;
351{
60d01856 352 register char *bp = tbuf;
2fd90732
BJ
353
354 for (;;) {
60d01856 355 bp = tskip(bp);
2fd90732
BJ
356 if (!*bp)
357 return (0);
358 if (*bp++ != id[0] || *bp == 0 || *bp++ != id[1])
359 continue;
60d01856
SL
360 if (*bp == '@')
361 return(0);
2fd90732
BJ
362 if (*bp != '=')
363 continue;
364 bp++;
60d01856 365 return (tdecode(bp, area));
2fd90732
BJ
366 }
367}
368
369/*
370 * Tdecode does the grung work to decode the
371 * string capability escapes.
372 */
373static char *
60d01856 374tdecode(str, area)
2fd90732
BJ
375 register char *str;
376 char **area;
377{
378 register char *cp;
379 register int c;
380 register char *dp;
381 int i;
382
383 cp = *area;
384 while ((c = *str++) && c != ':') {
385 switch (c) {
386
387 case '^':
388 c = *str++ & 037;
389 break;
390
391 case '\\':
392 dp = "E\033^^\\\\::n\nr\rt\tb\bf\f";
393 c = *str++;
394nextc:
395 if (*dp++ == c) {
396 c = *dp++;
397 break;
398 }
399 dp++;
400 if (*dp)
401 goto nextc;
402 if (isdigit(c)) {
403 c -= '0', i = 2;
404 do
405 c <<= 3, c |= *str++ - '0';
406 while (--i && isdigit(*str));
407 }
408 break;
409 }
410 *cp++ = c;
411 }
412 *cp++ = 0;
413 str = *area;
414 *area = cp;
415 return (str);
416}