1 /*
2 * Copyright (c) 1983, 1993
3 * The Regents of the University of California. All rights reserved.
4 *
5 * %sccs.include.redist.c%
6 */
7
8 #ifndef lint
9 static char sccsid[] = "@(#)printcap.c 8.2 (Berkeley) 04/28/95";
10 #endif /* not lint */
11
12 #include <sys/param.h>
13
14 #include <fcntl.h>
15 #include <dirent.h>
16 #include <unistd.h>
17 #include <stdio.h>
18 #include <ctype.h>
19 #include <string.h>
20 #include "lp.h"
21 #include "pathnames.h"
22
23 #ifndef BUFSIZ
24 #define BUFSIZ 1024
25 #endif
26 #define MAXHOP 32 /* max number of tc= indirections */
27
28 /*
29 * getcap-style interface for the old printcap routines.
30 *
31 * !!!USE THIS INTERFACE ONLY IF YOU DON'T HAVE THE REAL GETCAP!!!
32 */
33
34 static char *pbp; /* pointer into pbuf for pgetstr() */
35 static char pbuf[BUFSIZ]; /* buffer for capability strings */
36 extern char line[]; /* buffer for printcap entries */
37
38 int
cgetnext(bp,db_array)39 cgetnext(bp, db_array)
40 register char **bp;
41 char **db_array;
42 {
43 int ret;
44 char *strdup();
45
46 pbp = pbuf;
47 ret = getprent(line);
48 *bp = strdup(line);
49 return (ret);
50 }
51
52 int
cgetent(bp,db_array,name)53 cgetent(bp, db_array, name)
54 char **bp, **db_array, *name;
55 {
56 int i;
57
58 *bp = line;
59 pbp = pbuf;
60 i = pgetent(*bp, name);
61 if (i < 0)
62 return (-2);
63 else if (i == 0)
64 return (-1);
65 else
66 return (0);
67 }
68
69 char *
cgetcap(buf,cap,type)70 cgetcap(buf, cap, type)
71 char *buf, *cap;
72 int type;
73 {
74 return ((char *) pgetflag(cap));
75 }
76
77 int
cgetstr(buf,cap,str)78 cgetstr(buf, cap, str)
79 char *buf, *cap;
80 char **str;
81 {
82 char *pgetstr __P((char *, char **));
83
84 if (pbp >= pbuf+BUFSIZ) {
85 write(2, "Capability string buffer overflow\n", 34);
86 return (-1);
87 }
88 return ((*str = pgetstr(cap, &pbp)) == NULL ? -1 : 0);
89 }
90
91 int
cgetnum(buf,cap,num)92 cgetnum(buf, cap, num)
93 char *buf, *cap;
94 long *num;
95 {
96 return ((*num = pgetnum(cap)) < 0 ? -1 : 0);
97 }
98
99 int
cgetclose()100 cgetclose()
101 {
102 void endprent __P((void));
103
104 endprent();
105 return (0);
106 }
107
108
109 /*
110 * termcap - routines for dealing with the terminal capability data base
111 *
112 * BUG: Should use a "last" pointer in tbuf, so that searching
113 * for capabilities alphabetically would not be a n**2/2
114 * process when large numbers of capabilities are given.
115 * Note: If we add a last pointer now we will screw up the
116 * tc capability. We really should compile termcap.
117 *
118 * Essentially all the work here is scanning and decoding escapes
119 * in string capabilities. We don't use stdio because the editor
120 * doesn't, and because living w/o it is not hard.
121 */
122
123 #define PRINTCAP
124
125 #ifdef PRINTCAP
126 #define tgetent pgetent
127 #define tskip pskip
128 #define tgetstr pgetstr
129 #define tdecode pdecode
130 #define tgetnum pgetnum
131 #define tgetflag pgetflag
132 #define tdecode pdecode
133 #define tnchktc pnchktc
134 #define tnamatch pnamatch
135 #define V6
136 #endif
137
138 static FILE *pfp = NULL; /* printcap data base file pointer */
139 static char *tbuf;
140 static int hopcount; /* detect infinite loops in termcap, init 0 */
141 static int tf;
142
143 char *tgetstr __P((char *, char **));
144 static char *tskip __P((char *));
145 static char *tdecode __P((char *, char **));
146
147 /*
148 * Similar to tgetent except it returns the next enrty instead of
149 * doing a lookup.
150 */
151 int
getprent(bp)152 getprent(bp)
153 register char *bp;
154 {
155 register int c, skip = 0;
156
157 if (pfp == NULL && (pfp = fopen(_PATH_PRINTCAP, "r")) == NULL)
158 return(-1);
159 tbuf = bp;
160 for (;;) {
161 switch (c = getc(pfp)) {
162 case EOF:
163 fclose(pfp);
164 pfp = NULL;
165 return(0);
166 case '\n':
167 if (bp == tbuf) {
168 skip = 0;
169 continue;
170 }
171 if (bp[-1] == '\\') {
172 bp--;
173 continue;
174 }
175 *bp = '\0';
176 return(1);
177 case '#':
178 if (bp == tbuf)
179 skip++;
180 default:
181 if (skip)
182 continue;
183 if (bp >= tbuf+BUFSIZ) {
184 write(2, "Termcap entry too long\n", 23);
185 *bp = '\0';
186 return(1);
187 }
188 *bp++ = c;
189 }
190 }
191 }
192
193 void
endprent()194 endprent()
195 {
196 if (pfp != NULL) {
197 /*
198 * Can't use fclose here because on POSIX-compliant
199 * systems, fclose() causes the file pointer of the
200 * underlying file descriptor (which is possibly shared
201 * with a parent process) to be adjusted, and this
202 * reeks havoc in the parent because it doesn't know
203 * the file pointer has changed.
204 */
205 (void) close(fileno(pfp));
206 pfp = NULL;
207 }
208 }
209
210 /*
211 * Get an entry for terminal name in buffer bp,
212 * from the termcap file. Parse is very rudimentary;
213 * we just notice escaped newlines.
214 */
215 int
tgetent(bp,name)216 tgetent(bp, name)
217 char *bp, *name;
218 {
219 register char *cp;
220 register int c;
221 register int i = 0, cnt = 0;
222 char ibuf[BUFSIZ];
223
224 tbuf = bp;
225 #ifndef V6
226 cp = getenv("TERMCAP");
227 /*
228 * TERMCAP can have one of two things in it. It can be the
229 * name of a file to use instead of /etc/termcap. In this
230 * case it better start with a "/". Or it can be an entry to
231 * use so we don't have to read the file. In this case it
232 * has to already have the newlines crunched out.
233 */
234 if (cp && *cp) {
235 if (*cp!='/') {
236 cp2 = getenv("TERM");
237 if (cp2==(char *) 0 || strcmp(name,cp2)==0) {
238 strcpy(bp,cp);
239 return(tnchktc());
240 } else {
241 tf = open(_PATH_PRINTCAP, 0);
242 }
243 } else
244 tf = open(cp, 0);
245 }
246 #endif
247 if (tf==0)
248 tf = open(_PATH_PRINTCAP, 0);
249 if (tf < 0)
250 return (-1);
251 for (;;) {
252 cp = bp;
253 for (;;) {
254 if (i == cnt) {
255 cnt = read(tf, ibuf, BUFSIZ);
256 if (cnt <= 0) {
257 close(tf);
258 tf = 0;
259 return (0);
260 }
261 i = 0;
262 }
263 c = ibuf[i++];
264 if (c == '\n') {
265 if (cp > bp && cp[-1] == '\\'){
266 cp--;
267 continue;
268 }
269 break;
270 }
271 if (cp >= bp+BUFSIZ) {
272 write(2,"Termcap entry too long\n", 23);
273 break;
274 } else
275 *cp++ = c;
276 }
277 *cp = 0;
278
279 /*
280 * The real work for the match.
281 */
282 if (tnamatch(name)) {
283 lseek(tf, 0L, 0);
284 i = tnchktc();
285 if (tf) {
286 close(tf);
287 tf = 0;
288 }
289 return(i);
290 }
291 }
292 }
293
294 /*
295 * tnchktc: check the last entry, see if it's tc=xxx. If so,
296 * recursively find xxx and append that entry (minus the names)
297 * to take the place of the tc=xxx entry. This allows termcap
298 * entries to say "like an HP2621 but doesn't turn on the labels".
299 * Note that this works because of the left to right scan.
300 */
301 int
tnchktc()302 tnchktc()
303 {
304 register char *p, *q;
305 char tcname[16]; /* name of similar terminal */
306 char tcbuf[BUFSIZ];
307 char *holdtbuf = tbuf;
308 int l;
309
310 p = tbuf + strlen(tbuf) - 2; /* before the last colon */
311 while (*--p != ':')
312 if (p<tbuf) {
313 write(2, "Bad termcap entry\n", 18);
314 return (0);
315 }
316 p++;
317 /* p now points to beginning of last field */
318 if (p[0] != 't' || p[1] != 'c')
319 return(1);
320 strcpy(tcname,p+3);
321 q = tcname;
322 while (q && *q != ':')
323 q++;
324 *q = 0;
325 if (++hopcount > MAXHOP) {
326 write(2, "Infinite tc= loop\n", 18);
327 return (0);
328 }
329 if (tgetent(tcbuf, tcname) != 1)
330 return(0);
331 for (q=tcbuf; *q != ':'; q++)
332 ;
333 l = p - holdtbuf + strlen(q);
334 if (l > BUFSIZ) {
335 write(2, "Termcap entry too long\n", 23);
336 q[BUFSIZ - (p-tbuf)] = 0;
337 }
338 strcpy(p, q+1);
339 tbuf = holdtbuf;
340 return(1);
341 }
342
343 /*
344 * Tnamatch deals with name matching. The first field of the termcap
345 * entry is a sequence of names separated by |'s, so we compare
346 * against each such name. The normal : terminator after the last
347 * name (before the first field) stops us.
348 */
349 int
tnamatch(np)350 tnamatch(np)
351 char *np;
352 {
353 register char *Np, *Bp;
354
355 Bp = tbuf;
356 if (*Bp == '#')
357 return(0);
358 for (;;) {
359 for (Np = np; *Np && *Bp == *Np; Bp++, Np++)
360 continue;
361 if (*Np == 0 && (*Bp == '|' || *Bp == ':' || *Bp == 0))
362 return (1);
363 while (*Bp && *Bp != ':' && *Bp != '|')
364 Bp++;
365 if (*Bp == 0 || *Bp == ':')
366 return (0);
367 Bp++;
368 }
369 }
370
371 /*
372 * Skip to the next field. Notice that this is very dumb, not
373 * knowing about \: escapes or any such. If necessary, :'s can be put
374 * into the termcap file in octal.
375 */
376 static char *
tskip(bp)377 tskip(bp)
378 register char *bp;
379 {
380
381 while (*bp && *bp != ':')
382 bp++;
383 if (*bp == ':')
384 bp++;
385 return (bp);
386 }
387
388 /*
389 * Return the (numeric) option id.
390 * Numeric options look like
391 * li#80
392 * i.e. the option string is separated from the numeric value by
393 * a # character. If the option is not found we return -1.
394 * Note that we handle octal numbers beginning with 0.
395 */
396 int
tgetnum(id)397 tgetnum(id)
398 char *id;
399 {
400 register int i, base;
401 register char *bp = tbuf;
402
403 for (;;) {
404 bp = tskip(bp);
405 if (*bp == 0)
406 return (-1);
407 if (*bp++ != id[0] || *bp == 0 || *bp++ != id[1])
408 continue;
409 if (*bp == '@')
410 return(-1);
411 if (*bp != '#')
412 continue;
413 bp++;
414 base = 10;
415 if (*bp == '0')
416 base = 8;
417 i = 0;
418 while (isdigit(*bp))
419 i *= base, i += *bp++ - '0';
420 return (i);
421 }
422 }
423
424 /*
425 * Handle a flag option.
426 * Flag options are given "naked", i.e. followed by a : or the end
427 * of the buffer. Return 1 if we find the option, or 0 if it is
428 * not given.
429 */
430 int
tgetflag(id)431 tgetflag(id)
432 char *id;
433 {
434 register char *bp = tbuf;
435
436 for (;;) {
437 bp = tskip(bp);
438 if (!*bp)
439 return (0);
440 if (*bp++ == id[0] && *bp != 0 && *bp++ == id[1]) {
441 if (!*bp || *bp == ':')
442 return (1);
443 else if (*bp == '@')
444 return(0);
445 }
446 }
447 }
448
449 /*
450 * Get a string valued option.
451 * These are given as
452 * cl=^Z
453 * Much decoding is done on the strings, and the strings are
454 * placed in area, which is a ref parameter which is updated.
455 * No checking on area overflow.
456 */
457 char *
tgetstr(id,area)458 tgetstr(id, area)
459 char *id, **area;
460 {
461 register char *bp = tbuf;
462
463 for (;;) {
464 bp = tskip(bp);
465 if (!*bp)
466 return (0);
467 if (*bp++ != id[0] || *bp == 0 || *bp++ != id[1])
468 continue;
469 if (*bp == '@')
470 return(0);
471 if (*bp != '=')
472 continue;
473 bp++;
474 return (tdecode(bp, area));
475 }
476 }
477
478 /*
479 * Tdecode does the grung work to decode the
480 * string capability escapes.
481 */
482 static char *
tdecode(str,area)483 tdecode(str, area)
484 register char *str;
485 char **area;
486 {
487 register char *cp;
488 register int c;
489 register char *dp;
490 int i;
491
492 cp = *area;
493 while ((c = *str++) && c != ':') {
494 switch (c) {
495
496 case '^':
497 c = *str++ & 037;
498 break;
499
500 case '\\':
501 dp = "E\033^^\\\\::n\nr\rt\tb\bf\f";
502 c = *str++;
503 nextc:
504 if (*dp++ == c) {
505 c = *dp++;
506 break;
507 }
508 dp++;
509 if (*dp)
510 goto nextc;
511 if (isdigit(c)) {
512 c -= '0', i = 2;
513 do
514 c <<= 3, c |= *str++ - '0';
515 while (--i && isdigit(*str));
516 }
517 break;
518 }
519 *cp++ = c;
520 }
521 *cp++ = 0;
522 str = *area;
523 *area = cp;
524 return (str);
525 }
526