xref: /illumos-gate/usr/src/cmd/lp/lib/lp/tidbit.c (revision bfed486ad8de8b8ebc6345a8e10accae08bf2f45)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 /*
22  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
27 /*	  All Rights Reserved  	*/
28 
29 
30 #pragma ident	"%Z%%M%	%I%	%E% SMI"	/* SVr4.0 1.12	*/
31 
32 #include "errno.h"
33 #include "string.h"
34 #include "sys/types.h"
35 #include "sys/stat.h"
36 
37 #if	defined(__STDC__)
38 #include "stdarg.h"
39 #else
40 #include "varargs.h"
41 #endif
42 
43 #include "lp.h"
44 
45 extern char		*boolnames[],
46 			*numnames[],
47 			*strnames[];
48 
49 extern char		*getenv();
50 
51 ushort_t		tidbit_boolean	= 0;
52 
53 short			tidbit_number	= 0;
54 
55 char			*tidbit_string	= 0;
56 
57 #if	defined(__STDC__)
58 static int		open_terminfo_file(char *, char *);
59 #else
60 static int		open_terminfo_file();
61 #endif
62 
63 /*
64  * _Getsh() - GET TWO-BYTE SHORT FROM "char *" POINTER PORTABLY
65  */
66 
67 /*
68  * "function" to get a short from a pointer.  The short is in a standard
69  * format: two bytes, the first is the low order byte, the second is
70  * the high order byte (base 256).  The only negative number allowed is
71  * -1, which is represented as 255, 255.  This format happens to be the
72  * same as the hardware on the pdp-11 and vax, making it fast and
73  * convenient and small to do this on a pdp-11.
74  */
75 
76 #if	vax || pdp11 || i386
77 #define	_Getsh(ip)	(*((short *)((char *)(ip))))
78 #endif	/* vax || pdp11 || i386 */
79 
80 /*
81  * The following macro is partly due to Mike Laman, laman@sdcsvax
82  *	NCR @ Torrey Pines.		- Tony Hansen
83  */
84 #if	u3b || u3b15 || u3b2 || m68000 || sparc
85 #define	_Getsh(ip)	((short)(*((unsigned char *) ip) | (*(ip+1) << 8)))
86 #endif	/* u3b || u3b15 || u3b2 || m68000 || sparc */
87 
88 #ifndef	_Getsh
89 /*
90  * Here is a more portable version, which does not assume byte ordering
91  * in shorts, sign extension, etc. It does assume that the C preprocessor
92  * does sign-extension the same as on the machine being compiled for.
93  * When ANSI C comes along, this should be changed to check <limits.h>
94  * to see if the low character value is negative.
95  */
96 
97 static int
98 #if	defined(__STDC__)
99 _Getsh(
100 	register char		*p
101 )
102 #else
103 _Getsh(p)
104 	register char		*p;
105 #endif
106 {
107 	register int		rv,
108 				rv2;
109 
110 #if	-1 == '\377'			/* sign extension occurs */
111 	rv = (*p++) & 0377;
112 	rv2 = (*p) & 0377;
113 #else	/* -1 == '\377' */			/* no sign extension */
114 	rv = *p++;
115 	rv2 = *p;
116 #endif	/* -1 == '\377' */
117 	if ((rv2 == 0377) && ((rv == 0377) || (rv == 0376)))
118 		return (-1);
119 	return (rv + (rv2 * 256));
120 }
121 #endif	/* _Getsh */
122 
123 #define	MAX_TIDBS	32
124 
125 static struct tidb	{
126 
127 	int			snames,
128 				nbools,
129 				nints,
130 				nstrs;
131 
132 	char			*term,
133 				*tiebuf,
134 				*boolean_offset,
135 				*number_offset,
136 				*string_offset,
137 				*string_table;
138 
139 }			tidbs[MAX_TIDBS + 1];	/* one for last ditch */
140 
141 /*
142  * tidbit() - TERMINFO DATABASE LOOKUP
143  */
144 
145 /*
146  * Four forms of calling:
147  *
148  *	tidbit ("term-type", "boolean-cap-name", &ushort)
149  *	tidbit ("term-type", "numeric-cap-name", &short)
150  *	tidbit ("term-type", "string-cap-name", &charstar)
151  *	tidbit ("term-type", "any-cap-name", (char *)0)
152  *
153  * The last one is chancy, because of the pointer alignment
154  * problem, but hey--what the heck. Anyway, the last one
155  * causes the value to be stored in one of
156  *
157  *	ushort  tidbit_boolean;
158  *	short   tidbit_number;
159  *	char   *tidbit_string;
160  *
161  * as appropriate, and returns one of 1, 2, or 3 as the type
162  * of the capability is boolean, numeric, or string.
163  *
164  * For example, to extract the size of the screen for a 5410:
165  *
166  *	short cols, lines;
167  *
168  *	tidbit ("5410", "cols", &cols);
169  *	tidbit ("5410", "lines", &lines);
170  *
171  * Note that for the lines and columns, this does NOT check
172  * the LINES and COLUMNS environment variables nor the window
173  * size, if running on a windowing terminal. That can be done
174  * by the caller.
175  *
176  * If first argument is (char *)0, "tidbit()" uses the same TERM
177  * used in the last call, or the TERM environment variable if this
178  * is the first call.
179  * If second argument is (char *)0, no lookup just verification
180  * of terminal type.
181  *
182  * Return is 0 (or 1, 2, 3 as above) if successful, otherwise -1
183  * with "errno" set:
184  *
185  *	ENOENT		can't open Terminfo file for terminal type
186  *	EBADF		Terminfo file is corrupted
187  *	ENOMEM		malloc failed
188  */
189 
190 /*VARARGS2*/
191 int
192 #if	defined(__STDC__)
193 tidbit(
194 	char			*term,
195 	char			*cap,
196 	...
197 )
198 #else
199 tidbit(term, cap, va_alist)
200 	char			*term,
201 				*cap;
202 	va_dcl
203 #endif
204 {
205 	va_list			ap;
206 
207 	int			rc;
208 
209 	register int		i;
210 
211 	register char		**pp;
212 
213 	register struct tidb	*pt;
214 
215 	static char		*last_term;
216 
217 
218 	if (!term)
219 		if (last_term)
220 			term = last_term;
221 		else {
222 			term = getenv("TERM");
223 			if (!term || !*term)
224 				term = NAME_UNKNOWN;
225 		}
226 	if (term != last_term) {
227 		if (last_term)
228 			Free(last_term);
229 		last_term = Strdup(term);
230 	}
231 
232 	for (i = 0; i < MAX_TIDBS; i++)
233 		if (tidbs[i].term && STREQU(tidbs[i].term, term)) {
234 			pt = &tidbs[i];
235 			break;
236 		}
237 
238 	/*
239 	 * Not cached, so read the file and cache it.
240 	 */
241 	if (i >= MAX_TIDBS) {
242 
243 		register int		n,
244 					tfd;
245 
246 		register char		*terminfo;
247 
248 		struct stat		statbuf;
249 
250 
251 		/*
252 		 * If no empty spot can be found, "i" will index the
253 		 * last spot, a spare reserved to avoid problems with
254 		 * a full cache.
255 		 */
256 		for (i = 0; i < MAX_TIDBS; i++)
257 			if (!tidbs[i].term)
258 				break;
259 		pt = &tidbs[i];
260 
261 		tfd = -1;
262 		if ((terminfo = getenv("TERMINFO")) && *terminfo)
263 			tfd = open_terminfo_file(terminfo, term);
264 #if	defined(TERMINFO)
265 		if (tfd < 0)
266 			tfd = open_terminfo_file(TERMINFO, term);
267 #endif
268 		if (tfd >= 0)
269 			(void) Fstat(tfd, &statbuf);
270 
271 		if (tfd < 0 || !statbuf.st_size) {
272 			errno = ENOENT;
273 			return (-1);
274 		}
275 
276 		if (pt->tiebuf)
277 			Free(pt->tiebuf);
278 		if (!(pt->tiebuf = Malloc(statbuf.st_size))) {
279 			errno = ENOMEM;
280 			return (-1);
281 		}
282 
283 		n = Read(tfd, pt->tiebuf, statbuf.st_size);
284 		(void) Close(tfd);
285 		if (n <= 0 || n >= 4096 || _Getsh(pt->tiebuf) != 0432) {
286 			Free(pt->tiebuf);
287 			pt->tiebuf = 0;
288 			errno = EBADF;
289 			return (-1);
290 		}
291 
292 		if (pt->term)
293 			Free(pt->term);
294 		if (!(pt->term = Strdup(term))) {
295 			Free(pt->tiebuf);
296 			pt->tiebuf = 0;
297 			errno = ENOMEM;
298 			return (-1);
299 		}
300 
301 		pt->snames = _Getsh(pt->tiebuf + 2);
302 		pt->nbools = _Getsh(pt->tiebuf + 4);
303 		pt->nints = _Getsh(pt->tiebuf + 6);
304 		pt->nstrs = _Getsh(pt->tiebuf + 8);
305 
306 		pt->boolean_offset = pt->tiebuf + 6 * 2 + pt->snames;
307 
308 		pt->number_offset = pt->boolean_offset + pt->nbools;
309 		if ((unsigned int)pt->number_offset & 1)
310 			pt->number_offset++;
311 
312 		pt->string_offset = pt->number_offset + pt->nints * 2;
313 
314 		pt->string_table = pt->string_offset + pt->nstrs * 2;
315 
316 	}
317 
318 	rc = 0;
319 
320 #if	defined(__STDC__)
321 	va_start(ap, cap);
322 #else
323 	va_start(ap);
324 #endif
325 
326 	if (!cap || !*cap)
327 		;
328 
329 	else if ((pp = wherelist(cap, boolnames))) {
330 		register ushort_t	*ushort_p;
331 
332 		register char		*ip;
333 
334 		register int		index	= pp - boolnames;
335 
336 		if (!(ushort_p = va_arg(ap, ushort_t *))) {
337 			ushort_p = &tidbit_boolean;
338 			rc = 1;
339 		}
340 
341 		if (index >= pt->nbools)
342 			*ushort_p = 0;
343 		else {
344 			ip = pt->boolean_offset + index;
345 			*ushort_p = (*ip & 01);
346 		}
347 
348 	} else if ((pp = wherelist(cap, numnames))) {
349 		register short		*short_p;
350 
351 		register char		*ip;
352 
353 		register int		index	= pp - numnames;
354 
355 		if (!(short_p = va_arg(ap, short *))) {
356 			short_p = &tidbit_number;
357 			rc = 2;
358 		}
359 
360 		if (index >= pt->nints)
361 			*short_p = -1;
362 		else {
363 			ip = pt->number_offset + index * 2;
364 			*short_p = _Getsh(ip);
365 			if (*short_p == -2)
366 				*short_p = -1;
367 		}
368 
369 	} else if ((pp = wherelist(cap, strnames))) {
370 		register char		**charstar_p;
371 
372 		register char		*ip;
373 
374 		register int		index	= pp - strnames;
375 
376 		register short		sindex;
377 
378 
379 		if (!(charstar_p = va_arg(ap, char **))) {
380 			charstar_p = &tidbit_string;
381 			rc = 3;
382 		}
383 
384 		if (index >= pt->nstrs)
385 			*charstar_p = 0;
386 		else {
387 			ip = pt->string_offset + index * 2;
388 			if ((sindex = _Getsh(ip)) >= 0)
389 				*charstar_p = pt->string_table + sindex;
390 			else
391 				*charstar_p = 0;
392 		}
393 	}
394 
395 	va_end(ap);
396 	return (rc);
397 }
398 
399 /*
400  * untidbit() - FREE SPACE ASSOCIATED WITH A TERMINFO ENTRY
401  */
402 
403 void
404 #if	defined(__STDC__)
405 untidbit(
406 	char			*term
407 )
408 #else
409 untidbit(term)
410 	char			*term;
411 #endif
412 {
413 	register int		i;
414 
415 
416 	for (i = 0; i < MAX_TIDBS; i++)
417 		if (tidbs[i].term && STREQU(tidbs[i].term, term)) {
418 			if (tidbs[i].tiebuf) {
419 				Free(tidbs[i].tiebuf);
420 				tidbs[i].tiebuf = 0;
421 			}
422 			Free(tidbs[i].term);
423 			tidbs[i].term = 0;
424 			break;
425 		}
426 }
427 
428 /*
429  * open_terminfo_file() - OPEN FILE FOR TERM ENTRY
430  */
431 
432 static int
433 #if	defined(__STDC__)
434 open_terminfo_file(
435 	char			*terminfo,
436 	char			*term
437 )
438 #else
439 open_terminfo_file(terminfo, term)
440 	char			*terminfo,
441 				*term;
442 #endif
443 {
444 	char			*first_letter	= "X",
445 				*path;
446 
447 	int			fd;
448 
449 	first_letter[0] = term[0];
450 	path = makepath(terminfo, first_letter, term, (char *)0);
451 
452 	/* start fix for bugid 1109709	*/
453 	if (path == NULL) {
454 		return (-1);
455 	}
456 	/* end fix for bugid 1109709	*/
457 
458 	fd = Open(path, 0);
459 	Free(path);
460 	return (fd);
461 }
462