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