xref: /illumos-gate/usr/src/cmd/lp/lib/filters/insfilter.c (revision 2a8bcb4efb45d99ac41c94a75c396b362c414f7f)
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 /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
23 /*	  All Rights Reserved  	*/
24 
25 
26 /*
27  * Copyright (c) 1998-1999 by Sun Microsystems, Inc.
28  * All rights reserved.
29  */
30 
31 /* EMACS_MODES: !fill, lnumb, !overwrite, !nodelete, !picture */
32 
33 #include "assert.h"
34 #include "string.h"
35 #include "errno.h"
36 #include "stdlib.h"
37 
38 #include "lp.h"
39 #include "filters.h"
40 
41 #include "regex.h"
42 
43 
44 #define	MATCH(PT, PM) (STREQU((PT)->pattern, PATT_STAR) || \
45 					match((PT)->re, *((PM)->pvalue)))
46 
47 
48 typedef struct PARM {
49 	char			*keyword;
50 	unsigned short		flags;
51 	char			**pvalue;
52 }			PARM;
53 
54 #define	X_MUST	0x0800	/* Pipeline MUST use this parm */
55 #define	X_FIRST	0x1000	/* Use parm only in 1st cmd of pipeline */
56 #define	X_FIXED	0x2000	/* Get value from elsewhere, not parms */
57 #define	X_MANY	0x4000	/* Several values allowed for parm */
58 #define	X_USED	0x8000	/* Used already, don't use again */
59 
60 static struct S {
61 	TYPE			input_type;
62 	TYPE			output_type;
63 	TYPE			printer_type;
64 	char			*printer;
65 	PARM			*parms;
66 } S;
67 
68 #if	defined(__STDC__)
69 
70 static int		searchlist_t(TYPE *, TYPE *);
71 static int		instantiate(_FILTER **, TYPE *, TYPE *,
72 							int (*)(), void *);
73 static int		check_pipeline(_FILTER *, PARM *);
74 static char		*build_pipe(_FILTER *, PARM *, unsigned short *);
75 #else
76 
77 static int		searchlist_t();
78 static int		instantiate();
79 static int		check_pipeline();
80 static char		*build_pipe();
81 
82 #endif
83 
84 /*
85  * Table of recognized keywords, with info. about them.
86  */
87 
88 #define	NFIXED 4
89 
90 static PARM		parmtable[] = {
91 
92 /* These must be the first NFIXED, and in this order */
93 PARM_INPUT,	X_FIXED,			&S.input_type.name,
94 PARM_OUTPUT,	X_FIXED,			&S.output_type.name,
95 PARM_TERM,	X_FIXED,			&S.printer_type.name,
96 PARM_PRINTER,	X_FIXED,			&S.printer,
97 
98 PARM_CPI,	FPARM_CPI,			0,
99 PARM_LPI,	FPARM_LPI,			0,
100 PARM_LENGTH,	FPARM_LENGTH,			0,
101 PARM_WIDTH,	FPARM_WIDTH,			0,
102 PARM_PAGES,	FPARM_PAGES | X_FIRST | X_MUST,	0,
103 PARM_CHARSET,	FPARM_CHARSET,			0,
104 PARM_FORM,	FPARM_FORM,			0,
105 PARM_COPIES,	FPARM_COPIES | X_FIRST,		0,
106 PARM_MODES,	FPARM_MODES | X_MANY | X_MUST,	0,
107 0,		0,				0,
108 };
109 
110 /*
111  * insfilter()
112  */
113 
114 FILTERTYPE
115 #if	defined(__STDC__)
insfilter(char ** pipes,char * input_type,char * output_type,char * printer_type,char * printer,char ** parms,unsigned short * flagsp)116 insfilter(
117 	char			**pipes,
118 	char			*input_type,
119 	char			*output_type,
120 	char			*printer_type,
121 	char			*printer,
122 	char			**parms,
123 	unsigned short		*flagsp
124 )
125 #else
126 insfilter(pipes, input_type, output_type, printer_type, printer, parms, flagsp)
127 	char			**pipes,
128 				*input_type,
129 				*output_type,
130 				*printer_type,
131 				*printer,
132 				**parms;
133 	unsigned short		*flagsp;
134 #endif
135 {
136 	_FILTER			*pipeline;
137 
138 	FILTERTYPE		ret;
139 
140 
141 	S.input_type.name = input_type;
142 	S.input_type.info = isterminfo(input_type);
143 	S.output_type.name = output_type;
144 	S.output_type.info = isterminfo(output_type);
145 	S.printer_type.name = printer_type;
146 	S.printer_type.info = isterminfo(printer_type);
147 	S.printer = printer;
148 
149 	/*
150 	 * If the filters have't been loaded yet, do so now.
151 	 * We'll load the standard table, but the caller can override
152 	 * this by first calling "loadfilters()" with the appropriate
153 	 * filter table name.
154 	 */
155 	if (!filters && loadfilters((char *)0) == -1)
156 		return (fl_none);
157 
158 	/*
159 	 * Allocate and initialize space to hold additional
160 	 * information about each item in "parms".
161 	 * THIS SPACE MUST BE FREED BEFORE EXITING THIS ROUTINE!
162 	 */
163 	{
164 		register int		n;
165 
166 		register PARM *		pp;
167 		register PARM *		ppt;
168 
169 		register char **	p;
170 
171 
172 
173 		for (n = 0, p = parms; *p; n++, p++)
174 			;
175 		n /= 2;
176 		n += NFIXED; /* for fixed parms (in/out/printer types) */
177 
178 		if (!(S.parms = (PARM *)Malloc((n + 1) * sizeof (PARM)))) {
179 			errno = ENOMEM;
180 			return (fl_none);
181 		}
182 
183 		for (ppt = parmtable; ppt->keyword; ppt++)
184 			ppt->flags &= ~X_USED;
185 
186 		/*
187 		 * Load the parameter list with the fixed ``type''
188 		 * parameters. Mark them as used (if appropriate)
189 		 * so we don't pick them up from the callers list.
190 		 */
191 		pp = S.parms;
192 		for (ppt = parmtable; ppt < parmtable + NFIXED; ppt++) {
193 			pp->keyword = ppt->keyword;
194 			pp->flags = ppt->flags;
195 			if (ppt->flags & X_FIXED)
196 				pp->pvalue = ppt->pvalue;
197 			else
198 				pp->pvalue = parms + 1;
199 			if (!(ppt->flags & X_MANY))
200 				ppt->flags |= X_USED;
201 			pp++;
202 		}
203 
204 		/*
205 		 * Copy each parameter from the caller supplied list
206 		 * to another list, adding information gathered from
207 		 * the keyword table. Note that some keywords should
208 		 * be given only once; additional occurrances in the
209 		 * caller's list will be ignored.
210 		 */
211 		for (p = parms; *p; p += 2)
212 			for (ppt = parmtable; ppt->keyword; ppt++)
213 				if (STREQU(*p, ppt->keyword) &&
214 						!(ppt->flags & X_USED)) {
215 
216 					pp->keyword = ppt->keyword;
217 					pp->flags = ppt->flags;
218 					if (ppt->flags & X_FIXED)
219 						pp->pvalue = ppt->pvalue;
220 					else
221 						pp->pvalue = p + 1;
222 
223 					if (!(ppt->flags & X_MANY))
224 						ppt->flags |= X_USED;
225 
226 					pp++;
227 					break;
228 
229 				}
230 
231 		pp->keyword = 0;
232 
233 	}
234 
235 	/*
236 	 * Preview the list of filters, to rule out those that
237 	 * can't possibly work.
238 	 */
239 	{
240 		register _FILTER *	pf;
241 
242 		for (pf = filters; pf->name; pf++) {
243 
244 			pf->mark = FL_CLEAR;
245 
246 			if (printer && !searchlist(printer, pf->printers))
247 				pf->mark = FL_SKIP;
248 
249 			else if (printer_type &&
250 					!searchlist_t(&(S.printer_type),
251 							pf->printer_types))
252 				pf->mark = FL_SKIP;
253 
254 		}
255 	}
256 
257 	/*
258 	 * Find a pipeline that will convert the input-type to the
259 	 * output-type and map the parameters as well.
260 	 */
261 	if (!instantiate(&pipeline, &S.input_type, &S.output_type,
262 			check_pipeline, S.parms)) {
263 		ret = fl_none;
264 		goto Return;
265 	}
266 
267 	if (!pipes) {
268 		ret = fl_both;
269 		goto Return;
270 
271 	} else {
272 		register _FILTER *	pf;
273 		register _FILTER *	pfastf; /* first in fast pipe */
274 		register _FILTER *	pslowf; /* last in slow pipe */
275 
276 		/*
277 		 * Found a pipeline, so now build it.
278 		 */
279 
280 		/*
281 		 * Split pipeline after last slow filter.
282 		 * "pipeline" will point to first filter in slow
283 		 * pipe, "pfastf" will point to first filter in
284 		 * fast pipe.
285 		 */
286 		for (pf = pfastf = pipeline, pslowf = 0; pf; pf = pf->next)
287 			if (pf->type == fl_slow) {
288 				pslowf = pf;
289 				pfastf = pf->next;
290 			}
291 
292 		if (pslowf) {
293 			assert(pslowf != pfastf);
294 			pslowf->next = 0;
295 			pipes[0] = build_pipe(pipeline, S.parms, flagsp);
296 			ret = fl_slow;
297 		} else
298 			pipes[0] = 0;
299 
300 		if (pfastf) {
301 			pipes[1] = build_pipe(pfastf, S.parms, flagsp);
302 			ret = fl_fast;
303 		} else
304 			pipes[1] = 0;
305 
306 		if (pslowf && pfastf)
307 			ret = fl_both;
308 
309 		/*
310 		 * Check for the oops case.
311 		 */
312 		if (pslowf && !pipes[0] || pfastf && !pipes[1])
313 			ret = fl_none;
314 
315 	}
316 
317 Return:	Free((char *)S.parms);
318 
319 	return (ret);
320 }
321 
322 /*
323  * searchlist_t() - SEARCH (TYPE *) LIST FOR ITEM
324  */
325 
326 static int
327 #if	defined(__STDC__)
typematch(TYPE * type1,TYPE * type2)328 typematch(
329 	TYPE			*type1,
330 	TYPE			*type2
331 )
332 #else
333 typematch(type1, type2)
334 	TYPE			*type1, *type2;
335 #endif
336 {
337 	if (STREQU(type1->name, NAME_ANY) || STREQU(type2->name, NAME_ANY) ||
338 			STREQU(type1->name, type2->name) ||
339 			(STREQU(type1->name, NAME_TERMINFO) && type2->info) ||
340 			(STREQU(type2->name, NAME_TERMINFO) && type1->info))
341 		return (1);
342 	else
343 		return (0);
344 }
345 
346 static int
347 #if	defined(__STDC__)
searchlist_t(TYPE * itemp,TYPE * list)348 searchlist_t(
349 	TYPE			*itemp,
350 	TYPE			*list
351 )
352 #else
353 searchlist_t(itemp, list)
354 	TYPE			*itemp;
355 	register TYPE		*list;
356 #endif
357 {
358 	if (!list || !list->name)
359 		return (0);
360 
361 	/*
362 	 * This is a linear search--we believe that the lists
363 	 * will be short.
364 	 */
365 	while (list->name) {
366 		if (typematch(itemp, list))
367 			return (1);
368 		list++;
369 	}
370 	return (0);
371 }
372 
373 /*
374  * instantiate() - CREATE FILTER-PIPELINE KNOWING INPUT/OUTPUT TYPES
375  */
376 
377 /*
378  *	The "instantiate()" routine is the meat of the "insfilter()"
379  *	algorithm. It is given an input-type and output-type and finds a
380  *	filter-pipline that will convert the input-type into the
381  *	output-type. Since the filter-pipeline must meet other criteria,
382  *	a function "verify" is also given, along with the set of criteria;
383  *	these are used by "instantiate()" to verify a filter-pipeline.
384  *
385  *	The filter-pipeline is built up and returned in "pipeline".
386  *	Conceptually this is just a list of filters, with the pipeline to
387  *	be constructed by simply concatenating the filter simple-commmands
388  *	(after filling in option templates) in the order found in the
389  *	list. What is used in the routine, though, is a pair of linked
390  *	lists, one list forming the ``right-half'' of the pipeline, the
391  *	other forming the ``left-half''. The pipeline is then the two
392  *	lists taken together.
393  *
394  *	The "instantiate()" routine looks for a single filter that matches
395  *	the input-type and output-type and satisfies the criteria. If one
396  *	is found, it is added to the end of the ``left-half'' list (it
397  *	could be added to the beginning of the ``right-half'' list with no
398  *	problem). The two lists are linked together to form one linked
399  *	list, which is passed, along with the set of criteria, to the
400  *	"verify" routine to check the filter-pipeline. If it passes the
401  *	check, the work is done.
402  *
403  *	If a single filter is not found, "instantiate()" examines all
404  *	pairs of filters where one in the pair can accept the input-type
405  *	and the other can produce the output-type. For each of these, it
406  *	calls itself again to find a filter that can join the pair
407  *	together--one that accepts as input the output-type of the first
408  *	in the pair, and produces as output the input-type of the second
409  *	in the pair.  This joining filter may be a single filter or may
410  *	be a filter-pipeline. "instantiate()" checks for the trivial case
411  *	where the input-type is the output-type; with trivial cases it
412  *	links the two lists without adding a filter and checks it with
413  *	"verify".
414  */
415 
416 /*
417  * instantiate()
418  */
419 
420 /*
421  * A PIPELIST is what is passed to each recursive call to "instantiate()".
422  * It contains a pointer to the end of the ``left-list'', a pointer to the
423  * head of the ``right-list'', and a pointer to the head of the left-list.
424  * The latter is passed to "verify". The end of the right-list (and thus
425  * the end of the entire list when left and right are joined) is the
426  * filter with a null ``next'' pointer.
427  */
428 typedef struct PIPELIST {
429 	_FILTER *		lhead;
430 	_FILTER *		ltail;
431 	_FILTER *		rhead;
432 }			PIPELIST;
433 
434 #if	defined(__STDC__)
435 static int		_instantiate(PIPELIST *, TYPE *, TYPE *,
436 					int (*)(_FILTER *, void *), void *);
437 #else
438 static int		_instantiate();
439 #endif
440 
441 static int		peg;
442 
443 static int
444 #if	defined(__STDC__)
instantiate(_FILTER ** pline,TYPE * input,TYPE * output,int (* verify)(_FILTER *,void *),void * criteria)445 instantiate(
446 	_FILTER			**pline,
447 	TYPE			*input,
448 	TYPE			*output,
449 	int			(*verify)(_FILTER *, void *),
450 	void			*criteria
451 )
452 #else
453 instantiate(pline, input, output, verify, criteria)
454 	_FILTER			**pline;
455 	TYPE			*input,
456 				*output;
457 	int			(*verify)();
458 	char			*criteria;
459 #endif
460 {
461 	PIPELIST		p;
462 	int			ret;
463 
464 	peg = 0;
465 	p.lhead = p.ltail = p.rhead = 0;
466 	ret = _instantiate(&p, input, output, verify, criteria);
467 	*pline = p.lhead;
468 	return (ret);
469 }
470 
471 #define	ENTER()		int our_tag; our_tag = ++peg;
472 
473 #define	LEAVE(Y)	if (!Y) { \
474 				register _FILTER *f; \
475 				for (f = filters; f->name; f++) \
476 					CLEAR(f); \
477 				return (0); \
478 			} else \
479 				return (1)
480 
481 #define	MARK(F, M)	(((F)->mark |= M), (F)->level = our_tag)
482 
483 #define	CLEAR(F)	if ((F)->level == our_tag) \
484 				(F)->level = 0, (F)->mark = FL_CLEAR
485 
486 #define	CHECK(F, M)	(((F)->mark & M) && (F)->level == our_tag)
487 
488 #define	USED(F)		((F)->mark)
489 
490 static int
491 #if	defined(__STDC__)
_instantiate(PIPELIST * pp,TYPE * inputp,TYPE * outputp,int (* verify)(_FILTER *,void *),void * criteria)492 _instantiate(
493 	PIPELIST		*pp,
494 	TYPE			*inputp,
495 	TYPE			*outputp,
496 	int			(*verify)(_FILTER *, void *),
497 	void			*criteria
498 )
499 #else
500 _instantiate(pp, inputp, outputp, verify, criteria)
501 	PIPELIST		*pp;
502 	TYPE			*inputp,
503 				*outputp;
504 	int			(*verify)();
505 	char			*criteria;
506 #endif
507 {
508 	register _FILTER	*prev_lhead;
509 	register _FILTER	*prev_ltail;
510 
511 
512 	/*
513 	 * Must be first ``statement'' after declarations.
514 	 */
515 	ENTER();
516 
517 	/*
518 	 * We're done when we've added filters on the left and right
519 	 * that let us connect the left and right directly; i.e. when
520 	 * the output of the left is the same type as the input of the
521 	 * right. HOWEVER, there must be at least one filter involved,
522 	 * to allow the filter feature to be used for handling modes,
523 	 * pages, copies, etc. not just FILTERING data.
524 	 */
525 	if (typematch(inputp, outputp) && pp->lhead) {
526 
527 		/*
528 		 * Getting here means that we must have a left and right
529 		 * pipeline. Why? For "pp->lhead" to be non-zero it
530 		 * must have been set below. The first place below
531 		 * doesn't set the right pipeline, but it also doesn't
532 		 * get us here (at least not directly). The only
533 		 * place we can get to here again is the second place
534 		 * "pp->phead" is set, and THAT sets the right pipeline.
535 		 */
536 		pp->ltail->next = pp->rhead;
537 		if ((*verify)(pp->lhead, criteria))
538 			LEAVE(1);
539 		else
540 			LEAVE(0);
541 
542 	}
543 
544 	/*
545 	 * Each time we search the list of filters, we examine
546 	 * them in the order given and stop searching when a filter
547 	 * that meets the needs is found. If the list is ordered with
548 	 * fast filters before slow filters, then fast filters will
549 	 * be chosen over otherwise-equal filters.
550 	 */
551 
552 	/*
553 	 * See if there's a single filter that will work.
554 	 * Just in case we can't find one, mark those that
555 	 * will work as left- or right-filters, to save time
556 	 * later.
557 	 *
558 	 * Also, record exactly *which* input/output
559 	 * type would be needed if the filter was used.
560 	 * This record will be complete (both input and output
561 	 * recorded) IF the single filter works. Otherwise,
562 	 * only the input, for the left possible filters,
563 	 * and the output, for the right possible filters,
564 	 * will be recorded. Thus, we'll have to record the
565 	 * missing types later.
566 	 */
567 	{
568 		register _FILTER *		pf;
569 
570 
571 		for (pf = filters; pf->name; pf++) {
572 
573 			if (USED(pf))
574 				continue;
575 
576 			if (searchlist_t(inputp, pf->input_types)) {
577 				MARK(pf, FL_LEFT);
578 				pf->inputp = inputp;
579 			}
580 			if (searchlist_t(outputp, pf->output_types)) {
581 				MARK(pf, FL_RIGHT);
582 				pf->outputp = outputp;
583 			}
584 
585 			if (CHECK(pf, FL_LEFT) && CHECK(pf, FL_RIGHT)) {
586 				prev_lhead = pp->lhead;
587 				prev_ltail = pp->ltail;
588 
589 				if (!pp->lhead)
590 					pp->lhead = pf;
591 				else
592 					pp->ltail->next = pf;
593 				(pp->ltail = pf)->next = pp->rhead;
594 
595 				if ((*verify)(pp->lhead, criteria))
596 					LEAVE(1);
597 
598 				if ((pp->ltail = prev_ltail))
599 					pp->ltail->next = 0;
600 				pp->lhead = prev_lhead;
601 
602 			}
603 
604 		}
605 	}
606 
607 	/*
608 	 * Try all DISJOINT pairs of left- and right-filters; recursively
609 	 * call this function to find a filter that will connect
610 	 * them (it might be a ``null'' filter).
611 	 */
612 	{
613 		register _FILTER *	pfl;
614 		register _FILTER *	pfr;
615 
616 		register TYPE *		llist;
617 		register TYPE *		rlist;
618 
619 
620 		for (pfl = filters; pfl->name; pfl++) {
621 
622 			if (!CHECK(pfl, FL_LEFT))
623 				continue;
624 
625 			for (pfr = filters; pfr->name; pfr++) {
626 
627 				if (pfr == pfl || !CHECK(pfr, FL_RIGHT))
628 					continue;
629 
630 				prev_lhead = pp->lhead;
631 				prev_ltail = pp->ltail;
632 
633 				if (!pp->lhead)
634 					pp->lhead = pfl;
635 				else
636 					pp->ltail->next = pfl;
637 				(pp->ltail = pfl)->next = 0;
638 
639 				pfr->next = pp->rhead;
640 				pp->rhead = pfr;
641 
642 				/*
643 				 * Try all the possible output types of
644 				 * the left filter with all the possible
645 				 * input types of the right filter. If
646 				 * we find a combo. that works, record
647 				 * the output and input types for the
648 				 * respective filters.
649 				 */
650 				for (llist = pfl->output_types; llist->name;
651 								llist++)
652 					for (rlist = pfr->input_types;
653 							rlist->name; rlist++)
654 						if (_instantiate(pp, llist,
655 								rlist, verify,
656 								criteria)) {
657 							pfl->outputp = llist;
658 							pfr->inputp = rlist;
659 							LEAVE(1);
660 						}
661 				pp->rhead = pfr->next;
662 				if ((pp->ltail = prev_ltail))
663 					pp->ltail->next = 0;
664 				pp->lhead = prev_lhead;
665 
666 			}
667 
668 		}
669 	}
670 
671 	LEAVE(0);
672 }
673 
674 /*
675  * check_pipeline() - CHECK THAT PIPELINE HANDLES MODES, PAGE-LIST
676  */
677 
678 static int
679 #if	defined(__STDC__)
check_pipeline(_FILTER * pipeline,PARM * parms)680 check_pipeline(
681 	_FILTER			*pipeline,
682 	PARM			*parms
683 )
684 #else
685 check_pipeline(pipeline, parms)
686 	_FILTER			*pipeline;
687 	PARM			*parms;
688 #endif
689 {
690 	register PARM		*pm;
691 
692 	register _FILTER	*pf;
693 
694 	register TEMPLATE	*pt;
695 
696 	register int		fail;
697 
698 
699 	for (fail = 0, pm = parms; !fail && pm->keyword; pm++) {
700 
701 		if (!(pm->flags & X_MUST))
702 			continue;
703 
704 		for (pf = pipeline; pf; pf = pf->next) {
705 
706 			if (!(pt = pf->templates))
707 				continue;
708 
709 			for (; pt->keyword; pt++)
710 				if (STREQU(pt->keyword, pm->keyword) &&
711 						pt->result && MATCH(pt, pm))
712 					goto Okay;
713 
714 		}
715 		fail = 1;
716 		continue;
717 
718 Okay:;
719 
720 	}
721 
722 	return (fail? 0 : 1);
723 }
724 
725 /*
726  * build_filter() - CONSTRUCT PIPELINE FROM LINKED LIST OF FILTERS
727  */
728 
729 #if	defined(__STDC__)
730 static size_t		build_simple_cmd(char **, _FILTER *, PARM *,
731 							unsigned short *);
732 #else
733 static size_t		build_simple_cmd();
734 #endif
735 
736 static char *
737 #if	defined(__STDC__)
build_pipe(_FILTER * pipeline,PARM * parms,unsigned short * fp)738 build_pipe(
739 	_FILTER			*pipeline,
740 	PARM			*parms,
741 	unsigned short		*fp
742 )
743 #else
744 build_pipe(pipeline, parms, fp)
745 	_FILTER			*pipeline;
746 	PARM			*parms;
747 	unsigned short		*fp;
748 #endif
749 {
750 	register _FILTER	*pf;
751 
752 	register size_t		nchars;
753 	register size_t		n;
754 
755 	char			*p;	/* NOT register */
756 	char			*ret;
757 
758 
759 	/*
760 	 * This is a two-pass routine. In the first pass we add
761 	 * up how much space is needed for the pipeline, in the second
762 	 * pass we allocate the space and construct the pipeline.
763 	 */
764 
765 	for (nchars = 0, pf = pipeline; pf; pf = pf->next)
766 		if ((n = build_simple_cmd((char **)0, pf, parms, fp)) > 0)
767 			nchars += n + 1;   /* +1 for '|' or ending null */
768 
769 	if (!(ret = p = Malloc(nchars))) {
770 		errno = ENOMEM;
771 		return (0);
772 	}
773 
774 	for (pf = pipeline; pf; pf = pf->next, *p++ = (pf? '|' : 0))
775 		(void) build_simple_cmd(&p, pf, parms, fp);
776 
777 	return (ret);
778 }
779 
780 /*
781  * build_simple_cmd()
782  */
783 
784 static size_t
785 #if	defined(__STDC__)
build_simple_cmd(char ** pp,_FILTER * pf,PARM * parms,unsigned short * flagsp)786 build_simple_cmd(
787 	char			**pp,
788 	_FILTER			*pf,
789 	PARM			*parms,
790 	unsigned short		*flagsp
791 )
792 #else
793 build_simple_cmd(pp, pf, parms, flagsp)
794 	char			**pp;
795 	_FILTER			*pf;
796 	PARM			*parms;
797 	unsigned short		*flagsp;
798 #endif
799 {
800 	register size_t		ncount;
801 
802 	register TEMPLATE	*pt;
803 
804 	register PARM		*pm;
805 
806 
807 	if (pf->command) {
808 		ncount = strlen(pf->command);
809 		if (pp) {
810 			strcpy (*pp, pf->command);
811 			*pp += ncount;
812 		}
813 	} else
814 		ncount = 0;
815 
816 	if (!pf->templates)
817 		return (ncount);
818 
819 	for (pm = parms; pm->keyword; pm++) {
820 
821 		if ((pm->flags & X_USED) || !*(pm->pvalue))
822 			continue;
823 
824 		for (pt = pf->templates; pt->keyword; pt++) {
825 
826 			if (!STREQU(pt->keyword, pm->keyword) || !pt->result)
827 				continue;
828 
829 			/*
830 			 * INPUT and OUTPUT are those for *this* filter,
831 			 * not for the entire pipeline.
832 			 */
833 			if (STREQU(pt->keyword, PARM_INPUT))
834 				pm->pvalue = &(pf->inputp->name);
835 			else if (STREQU(pt->keyword, PARM_OUTPUT))
836 				pm->pvalue = &(pf->outputp->name);
837 
838 			if (MATCH(pt, pm)) {
839 				if (pp)
840 					*(*pp)++ = ' ';
841 				ncount++;
842 
843 				ncount += replace(pp, pt->result,
844 						*(pm->pvalue), pt->nbra);
845 
846 				/*
847 				 * Difficulty here due to the two pass
848 				 * nature of this code. The first pass
849 				 * just counts the number of bytes; if
850 				 * we mark the once-only parms as being
851 				 * used, then we don't pick them up the
852 				 * second time through. We could get
853 				 * difficult and mark them temporarily,
854 				 * but that's hard. So on the first pass
855 				 * we don't mark the flags. The only
856 				 * problem is an estimate too high.
857 				 */
858 				if (pp && pm->flags & X_FIRST)
859 					pm->flags |= X_USED;
860 
861 				*flagsp |= pm->flags;
862 
863 			}
864 		}
865 	}
866 
867 	return (ncount);
868 }
869