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, Version 1.0 only
6 * (the "License").  You may not use this file except in compliance
7 * with the License.
8 *
9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10 * or http://www.opensolaris.org/os/licensing.
11 * See the License for the specific language governing permissions
12 * and limitations under the License.
13 *
14 * When distributing Covered Code, include this CDDL HEADER in each
15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16 * If applicable, add the following below this CDDL HEADER, with the
17 * fields enclosed by brackets "[]" replaced with your own identifying
18 * information: Portions Copyright [yyyy] [name of copyright owner]
19 *
20 * CDDL HEADER END
21 */
22/*
23 * Copyright 1996 Sun Microsystems, Inc.  All rights reserved.
24 * Use is subject to license terms.
25 * Copyright (c) 2016 by Delphix. All rights reserved.
26 */
27
28/*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
29/*	  All Rights Reserved  	*/
30
31
32#pragma ident	"%Z%%M%	%I%	%E% SMI"	/* SVr4.0 1.2.1.5	*/
33/* EMACS_MODES: !fill, lnumb, !overwrite, !nodelete, !picture */
34
35#include "string.h"
36#include "limits.h"
37
38#include "lpsched.h"
39
40#include "validate.h"
41
42/*
43 * Transform consecutive "LP_SEP" characters to a single comma and n-1 LP_SEP;
44 *
45 * BUT we'll leave LP_SEP inside single quotes alone!
46 *
47 * This is to allow the following case (and the like) to work correctly:
48 *	prtitle='standard input'
49 */
50
51void
52transform_WS_to_SEP(char *cp)
53{	char *p;
54	int inside_quote = 0;
55	int done_one_already = 0;
56
57	for (p = cp; *p != '\0'; p++) {
58		if (*p == '\'') {
59			inside_quote = (inside_quote + 1) % 2;
60			continue;
61		}
62		if (inside_quote)
63			continue;
64		if (*p == ' ') {
65			if (!done_one_already) {
66				*p = ',';
67				done_one_already = 1;
68			} else {
69				/* multiple LP_WS into one LP_SEP */
70			}
71		} else {
72			done_one_already = 0;
73		}
74	}
75}
76
77/**
78 ** pickfilter() - SEE IF WE CAN FIND A FILTER FOR THIS REQUEST
79 **/
80
81int
82pickfilter(RSTATUS *prs, CANDIDATE *pc, FSTATUS *pfs)
83{
84	register char **	pp;
85	register char **	pl;
86
87	register PSTATUS *	pps		= pc->pps;
88
89	char *			pipes[2]	= { 0 , 0 };
90	char *			cp;
91	char *			output_type;
92
93	char **			modes		= 0;
94	char **			parms		= 0;
95	char **			valid_printer_types;
96	char **			p_cpi		= 0;
97	char **			p_lpi		= 0;
98	char **			p_pwid		= 0;
99	char **			p_plen		= 0;
100
101	FILTERTYPE		ret		= fl_none;
102
103	int			got_cpi		= 0;
104	int			got_lpi		= 0;
105	int			got_plen	= 0;
106	int			got_pwid	= 0;
107	int			must_have_filter= 0;
108
109	unsigned long		chk;
110
111
112	/* fix for bugid 1097387	*/
113	output_type = (char *) NULL;
114
115	/*
116	 * The bulk of the code below is building a parameter list "parms"
117	 * to send to "insfilter()".
118	 */
119
120	if (prs->request->modes) {
121		cp = Strdup(prs->request->modes);
122		transform_WS_to_SEP(cp);
123		modes = getlist(cp, "", LP_SEP);
124		Free (cp);
125	}
126
127	pp = parms = (char **)Malloc(
128		2 * (NPARM_SPEC + lenlist(modes) + 1) * sizeof(char *)
129	);
130
131	/*
132	 * Add to the parameter list the appropriate cpi/lpi/etc.
133	 * characteristics (aka ``stuff'') that will be used for
134	 * this job. The printer defaults are questionable.
135	 * Take this opportunity to also save the ``stuff'' in
136	 * the request structure.
137	 */
138
139	unload_str (&(prs->cpi));
140	unload_str (&(prs->lpi));
141	unload_str (&(prs->plen));
142	unload_str (&(prs->pwid));
143
144	/*
145	 * If a form is involved, pick up its page size and print
146	 * spacing requirements.
147	 */
148	if (pfs) {
149		if (pfs->cpi) {
150			*pp++ = PARM_CPI;
151			*pp++ = prs->cpi = pfs->cpi;
152			got_cpi = 1;
153		}
154		if (pfs->lpi) {
155			*pp++ = PARM_LPI;
156			*pp++ = prs->lpi = pfs->lpi;
157			got_lpi = 1;
158		}
159		if (pfs->plen) {
160			*pp++ = PARM_LENGTH;
161			*pp++ = prs->plen = pfs->plen;
162			got_plen = 1;
163		}
164		if (pfs->pwid) {
165			*pp++ = PARM_WIDTH;
166			*pp++ = prs->pwid = pfs->pwid;
167			got_pwid = 1;
168		}
169
170	/*
171	 * If no form is involved, pick up whatever page size and print
172	 * spacing requirements were given by the user.
173	 */
174	} else {
175		if (o_cpi) {
176			*pp++ = PARM_CPI;
177			*pp++ = prs->cpi = o_cpi;
178			got_cpi = 1;
179		}
180		if (o_lpi) {
181			*pp++ = PARM_LPI;
182			*pp++ = prs->lpi = o_lpi;
183			got_lpi = 1;
184		}
185		if (o_length) {
186			*pp++ = PARM_LENGTH;
187			*pp++ = prs->plen = o_length;
188			got_plen = 1;
189		}
190		if (o_width) {
191			*pp++ = PARM_WIDTH;
192			*pp++ = prs->pwid = o_width;
193			got_pwid = 1;
194		}
195	}
196
197	/*
198	 * Pick up whatever page size and print spacing requirements
199	 * haven't been specified yet from the printer defaults.
200	 *
201	 * Note: The following cpi/lpi/etc are guaranteed to work
202	 * for at least one type of the printer at hand, but not
203	 * necessarily all types. Once we pick a type that works
204	 * we'll verify that the cpi/lpi/etc stuff works, too.
205	 * The code that does that assumes that we do the following last,
206	 * after picking up the form and/or user stuff. If this changes,
207	 * then the later code will have to be changed, too.
208	 */
209	if (!got_cpi && pps->cpi) {
210		*pp++ = PARM_CPI;
211		*(p_cpi = pp++) = prs->cpi = pps->cpi;
212	}
213	if (!got_lpi && pps->lpi) {
214		*pp++ = PARM_LPI;
215		*(p_lpi = pp++) = prs->lpi = pps->lpi;
216	}
217	if (!got_plen && pps->plen) {
218		*pp++ = PARM_LENGTH;
219		*(p_plen = pp++) = prs->plen = pps->plen;
220	}
221	if (!got_pwid && pps->pwid) {
222		*pp++ = PARM_WIDTH;
223		*(p_pwid = pp++) = prs->pwid = pps->pwid;
224	}
225
226	/*
227	 * Pick up the number of pages, character set (the form's
228	 * or the user's), the form name, the number of copies,
229	 * and the modes.
230	 */
231
232	if (prs->request->pages) {
233		*pp++ = PARM_PAGES;
234		*pp++ = prs->request->pages;
235		must_have_filter = 1;
236	}
237
238	if (prs->request->charset) {
239		*pp++ = PARM_CHARSET;
240		*pp++ = prs->request->charset;
241
242	} else if (pfs && pfs->form->chset) {
243		*pp++ = PARM_CHARSET;
244		*pp++ = pfs->form->chset;
245	}
246
247	if (prs->request->form) {
248		*pp++ = PARM_FORM;
249		*pp++ = prs->request->form;
250	}
251
252	if (prs->request->copies > 1) {
253		*pp++ = PARM_COPIES;
254		sprintf ((*pp++ = BIGGEST_NUMBER_S), "%d", prs->request->copies);
255	}
256
257	if (modes) {
258		for (pl = modes; *pl; pl++) {
259			*pp++ = PARM_MODES;
260			*pp++ = *pl;
261		}
262		must_have_filter = 1;
263	}
264
265	*pp = 0;	/* null terminated list! */
266
267
268	/*
269	 * If the printer type(s) are not ``unknown'', then include
270	 * them as possible ``output'' type(s) to match
271	 * with the user's input type (directly, or through a filter).
272	 */
273	if (!STREQU(*(pps->printer->printer_types), NAME_UNKNOWN))
274		valid_printer_types = pc->printer_types;
275	else {
276		valid_printer_types = 0;
277		must_have_filter = 0;
278	}
279
280	pc->fast = 0;
281	pc->slow = 0;
282	pc->output_type = 0;
283	pc->flags = 0;
284	ret = fl_none;
285
286	/*
287	 * If we don't really need a filter and the types match,
288	 * then that's good enough. Some ``broadly defined''
289	 * filters might match our needs, but if the printer
290	 * can do what we need, then why pull in a filter?
291
292
293
294	 * Besides, Section 3.40 in the requirements imply
295	 * that we don't use a filter if the printer can handle
296	 * the file.
297	 */
298	if (!must_have_filter ) {
299
300		if (
301			valid_printer_types
302		     && searchlist_with_terminfo(
303				prs->request->input_type,
304				valid_printer_types
305			)
306		) {
307			ret = fl_both;	/* not really, but ok */
308			pc->printer_type = Strdup(prs->request->input_type);
309
310		} else if (
311			pps->printer->input_types
312		     && searchlist_with_terminfo(
313				prs->request->input_type,
314				pps->printer->input_types
315			)
316		) {
317			ret = fl_both;	/* not really, but ok */
318
319			/*
320			 * (1) There is one printer type, might even
321			 *     be ``unknown'';
322			 * (2) There are several printer types, but that
323			 *     means only one input type, ``simple'',
324			 *     which any of the printer types can handle.
325			 */
326			pc->printer_type = Strdup(*(pc->printer_types));
327
328		}
329	}
330
331	/*
332	 * Don't try using a filter if the user doesn't want
333	 * a filter to be used! They would rather see an
334	 * error message than (heaven forbid!) a filter being
335	 * used.
336	 */
337	if (ret == fl_none && !(prs->request->actions & ACT_RAW)) {
338
339		/*
340		 * For each printer type, and each input type the printer
341		 * accepts, see if we have a filter that matches the
342		 * request to the printer. Each time we try, save the
343		 * output type we use in case of success; we just might
344		 * need that value later....
345		 */
346
347		for (
348			pl = valid_printer_types;
349			pl && *pl && ret == fl_none;
350			pl++
351		)
352			ret = insfilter(
353				pipes,
354				prs->request->input_type,
355				(output_type = *pl),
356				*pl,
357				pps->printer->name,
358				parms,
359				&(pc->flags)
360			);
361		if (ret != fl_none)
362			pc->printer_type = Strdup(*pl);
363
364		for (
365			pl = pps->printer->input_types;
366			pl && *pl && ret == fl_none;
367			pl++
368		)
369			/*
370			 * Don't waste time with check we've already made.
371			 */
372			if ((must_have_filter == 1) ||
373				!valid_printer_types
374			     || !searchlist(*pl, valid_printer_types)
375			)
376				/*
377				 * Either we have one (or less) printer
378				 * types and many input types, or we have
379				 * one input type, ``simple''; regardless,
380				 * using the first printer type is OK.
381				 */
382				ret = insfilter(
383					pipes,
384					prs->request->input_type,
385					(output_type = *pl),
386					*(pc->printer_types),
387					pps->printer->name,
388					parms,
389					&(pc->flags)
390				);
391		if (ret != fl_none)
392			pc->printer_type = Strdup(*(pc->printer_types));
393
394	}
395
396	/*
397	 * If we were successful, check that the printer type
398	 * we picked can handle the PRINTER'S cpi/lpi/etc. defaults.
399	 * (We know that ALL the printer's types can handle the stuff
400	 * the user gave or the stuff in the form.)
401	 * Each printer's default that doesn't pass muster gets dropped.
402	 * This may mean re-instantiating the filter(s) (if any).
403	 */
404	if (ret != fl_none && (p_cpi || p_lpi || p_pwid || p_plen)) {
405
406#define	NZ(X)	((X)? *(X) : (char *)0)
407		chk = chkprinter(
408			pc->printer_type,
409			NZ(p_cpi),
410			NZ(p_lpi),
411			NZ(p_plen),
412			NZ(p_pwid),
413			(char *)0
414		);
415
416		if (chk) {
417			register char **	_pp;
418
419			char *			hole	= "";
420
421
422			/*
423			 * Remove the offending printer defaults from the
424			 * request list of cpi/lpi/etc. stuff, and punch
425			 * (non-null!) holes in the parameter list.
426			 */
427#define DROP(P,R)	if (P) {P[-1] = P[0] = hole; R = 0;} else/*EMPTY*/
428			if (chk & PCK_CPI)	DROP (p_cpi, prs->cpi);
429			if (chk & PCK_LPI)	DROP (p_lpi, prs->lpi);
430			if (chk & PCK_WIDTH)	DROP (p_pwid, prs->pwid);
431			if (chk & PCK_LENGTH)	DROP (p_plen, prs->plen);
432
433			/*
434			 * If there are filters, we have to re-instantiate
435			 * them. (Can't check "ret" here, because it may
436			 * be misleading.)
437			 */
438			if (pipes[0] || pipes[1]) {
439
440				/*
441				 * First, close up the gaps we punched in
442				 * the parameter list.
443				 */
444				for (pp = _pp = parms; *pp; pp++)
445					if (*pp != hole)
446						*_pp++ = *pp;
447				*_pp = 0;
448
449				/*
450				 * Re-instantiate the filter(s). This
451				 * CAN'T fail, because it is not mandatory
452				 * that filters handle cpi/lpi/etc. stuff.
453				 */
454				ret = insfilter(
455					pipes,
456					prs->request->input_type,
457					output_type,
458					pc->printer_type,
459					pps->printer->name,
460					parms,
461					&(pc->flags)
462				);
463			}
464		}
465	}
466
467	/*
468	 * Save the filters, if any. Note: although "ret" can be
469	 * misleading, i.e. set to "fl_both" when there really aren't
470	 * any filters, the declaration of "pipes" ensured they'd be
471	 * zero if not set.
472	 */
473	if (ret == fl_both || ret == fl_slow)
474		pc->slow = pipes[0];
475	if (ret == fl_both || ret == fl_fast)
476		pc->fast = pipes[1];
477
478	if (ret != fl_none)
479		pc->output_type = Strdup (output_type);
480
481	/*
482	 * Wait until now to allocate storage for the cpi/etc.
483	 * stuff, to make life easier above.
484	 */
485	if (prs->cpi)	prs->cpi = Strdup(prs->cpi);
486	if (prs->lpi)	prs->lpi = Strdup(prs->lpi);
487	if (prs->plen)	prs->plen = Strdup(prs->plen);
488	if (prs->pwid)	prs->pwid = Strdup(prs->pwid);
489
490
491	if (parms)
492		Free ((char *)parms);
493	if (modes)
494		freelist (modes);
495
496	return ((ret != fl_none));
497}
498