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