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