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