root/usr/src/cmd/lp/cmd/lpsched/pickfilter.c
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License, Version 1.0 only
 * (the "License").  You may not use this file except in compliance
 * with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */
/*
 * Copyright 1996 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 * Copyright (c) 2016 by Delphix. All rights reserved.
 * Copyright 2026 OmniOS Community Edition (OmniOSce) Association.
 */

/*      Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
/*        All Rights Reserved   */

/* EMACS_MODES: !fill, lnumb, !overwrite, !nodelete, !picture */

#include "string.h"
#include "limits.h"

#include "lpsched.h"

#include "validate.h"

/*
 * Transform consecutive "LP_SEP" characters to a single comma and n-1 LP_SEP;
 *
 * BUT we'll leave LP_SEP inside single quotes alone!
 *
 * This is to allow the following case (and the like) to work correctly:
 *      prtitle='standard input'
 */

void
transform_WS_to_SEP(char *cp)
{       char *p;
        int inside_quote = 0;
        int done_one_already = 0;

        for (p = cp; *p != '\0'; p++) {
                if (*p == '\'') {
                        inside_quote = (inside_quote + 1) % 2;
                        continue;
                }
                if (inside_quote)
                        continue;
                if (*p == ' ') {
                        if (!done_one_already) {
                                *p = ',';
                                done_one_already = 1;
                        } else {
                                /* multiple LP_WS into one LP_SEP */
                        }
                } else {
                        done_one_already = 0;
                }
        }
}

/**
 ** pickfilter() - SEE IF WE CAN FIND A FILTER FOR THIS REQUEST
 **/

int
pickfilter(RSTATUS *prs, CANDIDATE *pc, FSTATUS *pfs)
{
        register char **        pp;
        register char **        pl;

        register PSTATUS *      pps             = pc->pps;

        char *                  pipes[2]        = { 0 , 0 };
        char *                  cp;
        char *                  output_type;
        char                    copiesbuf[sizeof (BIGGEST_NUMBER_S)];

        char **                 modes           = 0;
        char **                 parms           = 0;
        char **                 valid_printer_types;
        char **                 p_cpi           = 0;
        char **                 p_lpi           = 0;
        char **                 p_pwid          = 0;
        char **                 p_plen          = 0;

        FILTERTYPE              ret             = fl_none;

        int                     got_cpi         = 0;
        int                     got_lpi         = 0;
        int                     got_plen        = 0;
        int                     got_pwid        = 0;
        int                     must_have_filter= 0;

        unsigned long           chk;


        /* fix for bugid 1097387        */
        output_type = (char *) NULL;

        /*
         * The bulk of the code below is building a parameter list "parms"
         * to send to "insfilter()".
         */

        if (prs->request->modes) {
                cp = Strdup(prs->request->modes);
                transform_WS_to_SEP(cp);
                modes = getlist(cp, "", LP_SEP);
                Free (cp);
        }

        pp = parms = (char **)Malloc(
                2 * (NPARM_SPEC + lenlist(modes) + 1) * sizeof(char *)
        );

        /*
         * Add to the parameter list the appropriate cpi/lpi/etc.
         * characteristics (aka ``stuff'') that will be used for
         * this job. The printer defaults are questionable.
         * Take this opportunity to also save the ``stuff'' in
         * the request structure.
         */

        unload_str (&(prs->cpi));
        unload_str (&(prs->lpi));
        unload_str (&(prs->plen));
        unload_str (&(prs->pwid));

        /*
         * If a form is involved, pick up its page size and print
         * spacing requirements.
         */
        if (pfs) {
                if (pfs->cpi) {
                        *pp++ = PARM_CPI;
                        *pp++ = prs->cpi = pfs->cpi;
                        got_cpi = 1;
                }
                if (pfs->lpi) {
                        *pp++ = PARM_LPI;
                        *pp++ = prs->lpi = pfs->lpi;
                        got_lpi = 1;
                }
                if (pfs->plen) {
                        *pp++ = PARM_LENGTH;
                        *pp++ = prs->plen = pfs->plen;
                        got_plen = 1;
                }
                if (pfs->pwid) {
                        *pp++ = PARM_WIDTH;
                        *pp++ = prs->pwid = pfs->pwid;
                        got_pwid = 1;
                }

        /*
         * If no form is involved, pick up whatever page size and print
         * spacing requirements were given by the user.
         */
        } else {
                if (o_cpi) {
                        *pp++ = PARM_CPI;
                        *pp++ = prs->cpi = o_cpi;
                        got_cpi = 1;
                }
                if (o_lpi) {
                        *pp++ = PARM_LPI;
                        *pp++ = prs->lpi = o_lpi;
                        got_lpi = 1;
                }
                if (o_length) {
                        *pp++ = PARM_LENGTH;
                        *pp++ = prs->plen = o_length;
                        got_plen = 1;
                }
                if (o_width) {
                        *pp++ = PARM_WIDTH;
                        *pp++ = prs->pwid = o_width;
                        got_pwid = 1;
                }
        }

        /*
         * Pick up whatever page size and print spacing requirements
         * haven't been specified yet from the printer defaults.
         *
         * Note: The following cpi/lpi/etc are guaranteed to work
         * for at least one type of the printer at hand, but not
         * necessarily all types. Once we pick a type that works
         * we'll verify that the cpi/lpi/etc stuff works, too.
         * The code that does that assumes that we do the following last,
         * after picking up the form and/or user stuff. If this changes,
         * then the later code will have to be changed, too.
         */
        if (!got_cpi && pps->cpi) {
                *pp++ = PARM_CPI;
                *(p_cpi = pp++) = prs->cpi = pps->cpi;
        }
        if (!got_lpi && pps->lpi) {
                *pp++ = PARM_LPI;
                *(p_lpi = pp++) = prs->lpi = pps->lpi;
        }
        if (!got_plen && pps->plen) {
                *pp++ = PARM_LENGTH;
                *(p_plen = pp++) = prs->plen = pps->plen;
        }
        if (!got_pwid && pps->pwid) {
                *pp++ = PARM_WIDTH;
                *(p_pwid = pp++) = prs->pwid = pps->pwid;
        }

        /*
         * Pick up the number of pages, character set (the form's
         * or the user's), the form name, the number of copies,
         * and the modes.
         */

        if (prs->request->pages) {
                *pp++ = PARM_PAGES;
                *pp++ = prs->request->pages;
                must_have_filter = 1;
        }

        if (prs->request->charset) {
                *pp++ = PARM_CHARSET;
                *pp++ = prs->request->charset;

        } else if (pfs && pfs->form->chset) {
                *pp++ = PARM_CHARSET;
                *pp++ = pfs->form->chset;
        }

        if (prs->request->form) {
                *pp++ = PARM_FORM;
                *pp++ = prs->request->form;
        }

        if (prs->request->copies > 1) {
                *pp++ = PARM_COPIES;
                sprintf (copiesbuf, "%d", prs->request->copies);
                *pp++ = copiesbuf;
        }

        if (modes) {
                for (pl = modes; *pl; pl++) {
                        *pp++ = PARM_MODES;
                        *pp++ = *pl;
                }
                must_have_filter = 1;
        }

        *pp = 0;        /* null terminated list! */


        /*
         * If the printer type(s) are not ``unknown'', then include
         * them as possible ``output'' type(s) to match
         * with the user's input type (directly, or through a filter).
         */
        if (!STREQU(*(pps->printer->printer_types), NAME_UNKNOWN))
                valid_printer_types = pc->printer_types;
        else {
                valid_printer_types = 0;
                must_have_filter = 0;
        }

        pc->fast = 0;
        pc->slow = 0;
        pc->output_type = 0;
        pc->flags = 0;
        ret = fl_none;

        /*
         * If we don't really need a filter and the types match,
         * then that's good enough. Some ``broadly defined''
         * filters might match our needs, but if the printer
         * can do what we need, then why pull in a filter?



         * Besides, Section 3.40 in the requirements imply
         * that we don't use a filter if the printer can handle
         * the file.
         */
        if (!must_have_filter ) {

                if (
                        valid_printer_types
                     && searchlist_with_terminfo(
                                prs->request->input_type,
                                valid_printer_types
                        )
                ) {
                        ret = fl_both;  /* not really, but ok */
                        pc->printer_type = Strdup(prs->request->input_type);

                } else if (
                        pps->printer->input_types
                     && searchlist_with_terminfo(
                                prs->request->input_type,
                                pps->printer->input_types
                        )
                ) {
                        ret = fl_both;  /* not really, but ok */

                        /*
                         * (1) There is one printer type, might even
                         *     be ``unknown'';
                         * (2) There are several printer types, but that
                         *     means only one input type, ``simple'',
                         *     which any of the printer types can handle.
                         */
                        pc->printer_type = Strdup(*(pc->printer_types));

                }
        }

        /*
         * Don't try using a filter if the user doesn't want
         * a filter to be used! They would rather see an
         * error message than (heaven forbid!) a filter being
         * used.
         */
        if (ret == fl_none && !(prs->request->actions & ACT_RAW)) {

                /*
                 * For each printer type, and each input type the printer
                 * accepts, see if we have a filter that matches the
                 * request to the printer. Each time we try, save the
                 * output type we use in case of success; we just might
                 * need that value later....
                 */

                for (
                        pl = valid_printer_types;
                        pl && *pl && ret == fl_none;
                        pl++
                )
                        ret = insfilter(
                                pipes,
                                prs->request->input_type,
                                (output_type = *pl),
                                *pl,
                                pps->printer->name,
                                parms,
                                &(pc->flags)
                        );
                if (ret != fl_none)
                        pc->printer_type = Strdup(*pl);

                for (
                        pl = pps->printer->input_types;
                        pl && *pl && ret == fl_none;
                        pl++
                )
                        /*
                         * Don't waste time with check we've already made.
                         */
                        if ((must_have_filter == 1) ||
                                !valid_printer_types
                             || !searchlist(*pl, valid_printer_types)
                        )
                                /*
                                 * Either we have one (or less) printer
                                 * types and many input types, or we have
                                 * one input type, ``simple''; regardless,
                                 * using the first printer type is OK.
                                 */
                                ret = insfilter(
                                        pipes,
                                        prs->request->input_type,
                                        (output_type = *pl),
                                        *(pc->printer_types),
                                        pps->printer->name,
                                        parms,
                                        &(pc->flags)
                                );
                if (ret != fl_none)
                        pc->printer_type = Strdup(*(pc->printer_types));

        }

        /*
         * If we were successful, check that the printer type
         * we picked can handle the PRINTER'S cpi/lpi/etc. defaults.
         * (We know that ALL the printer's types can handle the stuff
         * the user gave or the stuff in the form.)
         * Each printer's default that doesn't pass muster gets dropped.
         * This may mean re-instantiating the filter(s) (if any).
         */
        if (ret != fl_none && (p_cpi || p_lpi || p_pwid || p_plen)) {

#define NZ(X)   ((X)? *(X) : (char *)0)
                chk = chkprinter(
                        pc->printer_type,
                        NZ(p_cpi),
                        NZ(p_lpi),
                        NZ(p_plen),
                        NZ(p_pwid),
                        (char *)0
                );

                if (chk) {
                        register char **        _pp;

                        char *                  hole    = "";


                        /*
                         * Remove the offending printer defaults from the
                         * request list of cpi/lpi/etc. stuff, and punch
                         * (non-null!) holes in the parameter list.
                         */
#define DROP(P,R)       if (P) {P[-1] = P[0] = hole; R = 0;} else/*EMPTY*/
                        if (chk & PCK_CPI)      DROP (p_cpi, prs->cpi);
                        if (chk & PCK_LPI)      DROP (p_lpi, prs->lpi);
                        if (chk & PCK_WIDTH)    DROP (p_pwid, prs->pwid);
                        if (chk & PCK_LENGTH)   DROP (p_plen, prs->plen);

                        /*
                         * If there are filters, we have to re-instantiate
                         * them. (Can't check "ret" here, because it may
                         * be misleading.)
                         */
                        if (pipes[0] || pipes[1]) {

                                /*
                                 * First, close up the gaps we punched in
                                 * the parameter list.
                                 */
                                for (pp = _pp = parms; *pp; pp++)
                                        if (*pp != hole)
                                                *_pp++ = *pp;
                                *_pp = 0;

                                /*
                                 * Re-instantiate the filter(s). This
                                 * CAN'T fail, because it is not mandatory
                                 * that filters handle cpi/lpi/etc. stuff.
                                 */
                                ret = insfilter(
                                        pipes,
                                        prs->request->input_type,
                                        output_type,
                                        pc->printer_type,
                                        pps->printer->name,
                                        parms,
                                        &(pc->flags)
                                );
                        }
                }
        }

        /*
         * Save the filters, if any. Note: although "ret" can be
         * misleading, i.e. set to "fl_both" when there really aren't
         * any filters, the declaration of "pipes" ensured they'd be
         * zero if not set.
         */
        if (ret == fl_both || ret == fl_slow)
                pc->slow = pipes[0];
        if (ret == fl_both || ret == fl_fast)
                pc->fast = pipes[1];

        if (ret != fl_none)
                pc->output_type = Strdup (output_type);

        /*
         * Wait until now to allocate storage for the cpi/etc.
         * stuff, to make life easier above.
         */
        if (prs->cpi)   prs->cpi = Strdup(prs->cpi);
        if (prs->lpi)   prs->lpi = Strdup(prs->lpi);
        if (prs->plen)  prs->plen = Strdup(prs->plen);
        if (prs->pwid)  prs->pwid = Strdup(prs->pwid);


        if (parms)
                Free ((char *)parms);
        if (modes)
                freelist (modes);

        return ((ret != fl_none));
}