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__) 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__) 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__) 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__) 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__) 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__) 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__) 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__) 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