root/usr/src/cmd/lp/cmd/lpadmin/chkopts.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 (c) 1984, 1986, 1987, 1988, 1989 AT&T */
/*        All Rights Reserved   */


/*
 * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

#include "stdio.h"
#include "string.h"
#include "pwd.h"
#include "sys/types.h"
#include "errno.h"

#include "lp.h"
#include "printers.h"
#include "form.h"
#include "class.h"

#define WHO_AM_I        I_AM_LPADMIN
#include "oam.h"

#include "lpadmin.h"

#define PPDZIP  ".gz"


extern PRINTER          *printer_pointer;

extern PWHEEL           *pwheel_pointer;

extern struct passwd    *getpwnam();

void                    chkopts2(),
                        chkopts3();
static void             chksys();

FORM                    formbuf;

char                    **f_allow,
                        **f_deny,
                        **u_allow,
                        **u_deny,
                        **p_add,
                        **p_remove;

PRINTER                 *oldp           = 0;

PWHEEL                  *oldS           = 0;

short                   daisy           = 0;

static int              root_can_write();

static char             *unpack_sdn();

static char **          bad_list;

#if     defined(__STDC__)
static unsigned long    sum_chkprinter ( char ** , char * , char * , char * , char * , char * );
static int isPPD(char *ppd_file);
#else
static unsigned long    sum_chkprinter();
static int isPPD();
#endif

/**
 ** chkopts() -- CHECK LEGALITY OF COMMAND LINE OPTIONS
 **/

void                    chkopts ()
{
        short           isfAuto = 0;

        /*
         * Check -d.
         */
        if (d) {
                if (
                        a || c || f || P || j || m || M || t || p || r || u || x
#if     defined(DIRECT_ACCESS)
                     || C
#endif
#ifdef LP_USE_PAPI_ATTR
                     || n_opt
#endif
                     || strlen(modifications)
                ) {
                        LP_ERRMSG (ERROR, E_ADM_DALONE);
                        done (1);
                }

                if (
                        *d
                     && !STREQU(d, NAME_NONE)
                     && !isprinter(d)
                     && !isclass(d)
                ) {
                        LP_ERRMSG1 (ERROR, E_ADM_NODEST, d);
                        done (1);
                }
                return;
        }

        /*
         * Check -x.
         */
        if (x) {
                if (    /* MR bl88-02718 */
                        A || a || c || f || P || j || m || M || t || p || r || u || d
#if     defined(DIRECT_ACCESS)
                     || C
#endif
#ifdef LP_USE_PAPI_ATTR
                     || n_opt
#endif
                     || strlen(modifications)
                ) {
                        LP_ERRMSG (ERROR, E_ADM_XALONE);
                        done (1);
                }

                if (
                        !STREQU(NAME_ALL, x)
                     && !STREQU(NAME_ANY, x)
                     && !isprinter(x)
                     && !isclass(x)
                ) {
                        LP_ERRMSG1 (ERROR, E_ADM_NODEST, x);
                        done (1);
                }
                return;
        }

        /*
         * Problems common to both -p and -S (-S alone).
         */
        if (A && STREQU(A, NAME_LIST) && (W != -1 || Q != -1)) {
                LP_ERRMSG (ERROR, E_ADM_LISTWQ);
                done (1);
        }


        /*
         * Check -S.
         */
        if (!p && S) {
                if (
                        M || t || a || f || P || c || r || e || i || m || H || h
                     || l || v || I || T || D || F || u || U || j || o
#ifdef LP_USE_PAPI_ATTR
                     || n_opt
#endif
                ) {
                        LP_ERRMSG (ERROR, E_ADM_SALONE);
                        done (1);
                }
                if (!A && W == -1 && Q == -1) {
                        LP_ERRMSG (ERROR, E_ADM_NOAWQ);
                        done (1);
                }
                if (S[0] && S[1])
                        LP_ERRMSG (WARNING, E_ADM_ASINGLES);
                if (!STREQU(NAME_ALL, *S) && !STREQU(NAME_ANY, *S))
                        chkopts3(1);
                return;
        }

        /*
         * At this point we must have a printer (-p option).
         */
        if (!p) {
                LP_ERRMSG (ERROR, E_ADM_NOACT);
                done (1);
        }
        if (STREQU(NAME_NONE, p)) {
                LP_ERRMSG1 (ERROR, E_LP_NULLARG, "p");
                done (1);
        }


        /*
         * Mount but nothing to mount?
         */
        if (M && (!f && !S)) {
                LP_ERRMSG (ERROR, E_ADM_MNTNONE);
                done (1);
        }

        /*
         * -Q isn't allowed with -p.
         */
        if (Q != -1) {
                LP_ERRMSG (ERROR, E_ADM_PNOQ);
                done (1);
        }

        /*
         * Fault recovery.
         */
        if (
                F
             && !STREQU(F, NAME_WAIT)
             && !STREQU(F, NAME_BEGINNING)
             && (
                        !STREQU(F, NAME_CONTINUE)
                     || j
                     && STREQU(F, NAME_CONTINUE)
                )
        ) {
#if     defined(J_OPTION)
                if (j)
                        LP_ERRMSG (ERROR, E_ADM_FBADJ);
                else
#endif
                        LP_ERRMSG (ERROR, E_ADM_FBAD);
                done (1);
        }

#if     defined(J_OPTION)
        /*
         * The -j option is used only with the -F option.
         */
        if (j) {
                if (M || t || a || f || P || c || r || e || i || m || H || h ||
#ifdef LP_USE_PAPI_ATTR
                    n_opt ||
#endif
                    l || v || I || T || D || u || U || o) {
                        LP_ERRMSG (ERROR, E_ADM_JALONE);
                        done (1);
                }
                if (j && !F) {
                        LP_ERRMSG (ERROR, E_ADM_JNOF);
                        done (1);
                }
                return;
        }
#endif

#if     defined(DIRECT_ACCESS)
        /*
         * -C is only used to modify -u
         */
        if (C && !u) {
                LP_ERRMSG (ERROR, E_ADM_CNOU);
                done (1);
        }
#endif

        /*
         * The -a option needs the -M and -f options,
         * Also, -ofilebreak is used only with -a.
         */
        if (a && (!M || !f)) {
                LP_ERRMSG (ERROR, E_ADM_MALIGN);
                done (1);
        }
        if (filebreak && !a)
                LP_ERRMSG (WARNING, E_ADM_FILEBREAK);

        /*
         * The "-p all" case is restricted to certain options.
         */
        if (
                (STREQU(NAME_ANY, p) || STREQU(NAME_ALL, p))
             && (
                        a || h || l || M || t || D || e || f || P || H || s
#ifdef LP_USE_PAPI_ATTR
                      || n_opt
#endif
                     || i || I || m || S || T || u || U || v || banner != -1
                     || cpi || lpi || width || length || stty_opt
                )
        ) {
                LP_ERRMSG (ERROR, E_ADM_ANYALLNONE);
                done (1);

        }

        /*
         * Allow giving -v or -U option as way of making
         * remote printer into local printer.
         * Note: "!s" here means the user has not given the -s;
         * later it means the user gave -s local-system.
         */
        if (!s && (v || U))
                s = Local_System;

        /*
         * Be careful about checking "s" before getting here.
         * We want "s == 0" to mean this is a local printer; however,
         * if the user wants to change a remote printer to a local
         * printer, we have to have "s == Local_System" long enough
         * to get into "chkopts2()" where a special check is made.
         * After "chkopts2()", "s == 0" means local.
         */
        if (!STREQU(NAME_ALL, p) && !STREQU(NAME_ANY, p))
                /*
                 * If old printer, make sure it exists. If new printer,
                 * check that the name is okay, and that enough is given.
                 * (This stuff has been moved to "chkopts2()".)
                 */
                chkopts2(1);

        if (!s) {

                /*
                 * Only one of -i, -m, -e.
                 */
                if ((i && e) || (m && e) || (i && m)) {
                        LP_ERRMSG (ERROR, E_ADM_INTCONF);
                        done (1);
                }

                /*
                 * Check -e arg.
                 */
                if (e) {
                        if (!isprinter(e)) {
                                LP_ERRMSG1 (ERROR, E_ADM_NOPR, e);
                                done (1);
                        }
                        if (strcmp(e, p) == 0) {
                                LP_ERRMSG (ERROR, E_ADM_SAMEPE);
                                done (1);
                        }
                }

                /*
                 * Check -m arg.
                 */
                if (m && !ismodel(m)) {
                        LP_ERRMSG1 (ERROR, E_ADM_NOMODEL, m);
                        done (1);
                }

#ifdef LP_USE_PAPI_ATTR
                /*
                 * Check -n arg. The ppd file exists.
                 */
                if ((n_opt != NULL) && !isPPD(n_opt)) {
                        LP_ERRMSG1 (ERROR, E_ADM_NOPPD, n_opt);
                        done (1);
                }
#endif

                /*
                 * Need exactly one of -h or -l (but will default -h).
                 */
                if (h && l) {
                        LP_ERRMSG2 (ERROR, E_ADM_CONFLICT, 'h', 'l');
                        done (1);
                }
                if (!h && !l)
                        h = 1;

                /*
                 * Check -c and -r.
                 */
                if (c && r && strcmp(c, r) == 0) {
                        LP_ERRMSG (ERROR, E_ADM_SAMECR);
                        done (1);
                }


                /*
                 * Are we creating a class with the same name as a printer?
                 */
                if (c) {
                        if (STREQU(c, p)) {
                                LP_ERRMSG1 (ERROR, E_ADM_CLNPR, c);
                                done (1);
                        }
                        if (isprinter(c)) {
                                LP_ERRMSG1 (ERROR, E_ADM_CLPR, c);
                                done (1);
                        }
                }

                if (v && (is_printer_uri(v) < 0)) {
                        /*
                         * The device must be writeable by root.
                         */
                        if (v && root_can_write(v) == -1)
                                done (1);
                }

                /*
                 * Can't have both device and dial-out.
                 */
                if (v && U) {
                        LP_ERRMSG (ERROR, E_ADM_BOTHUV);
                        done (1);
                }

        } else
                if (
                        A || a || e || H || h || i || l || m || ( t && !M) || ( M && !t)
                     || o || U || v || Q != -1 || W != -1
#ifdef LP_USE_PAPI_ATTR
                     || n_opt
#endif
                ) {
                        LP_ERRMSG (ERROR, E_ADM_NOTLOCAL);
                        done(1);
                }


        /*
         * We need the printer type for some things, and the boolean
         * "daisy" (from Terminfo) for other things.
         */
        if (!T && oldp)
                T = oldp->printer_types;
        if (T) {
                short                   a_daisy;

                char **                 pt;


                if (lenlist(T) > 1 && searchlist(NAME_UNKNOWN, T)) {
                        LP_ERRMSG (ERROR, E_ADM_MUNKNOWN);
                        done (1);
                }

                for (pt = T; *pt; pt++)
                        if (tidbit(*pt, (char *)0) == -1) {
                                LP_ERRMSG1 (ERROR, E_ADM_BADTYPE, *pt);
                                done (1);
                        }

                /*
                 * All the printer types had better agree on whether the
                 * printer takes print wheels!
                 */
                daisy = a_daisy = -1;
                for (pt = T; *pt; pt++) {
                        tidbit (*pt, "daisy", &daisy);
                        if (daisy == -1)
                                daisy = 0;
                        if (a_daisy == -1)
                                a_daisy = daisy;
                        else if (a_daisy != daisy) {
                                LP_ERRMSG (ERROR, E_ADM_MIXEDTYPES);
                                done (1);
                        }
                }
        }
        if (cpi || lpi || length || width || S || f || filebreak)
                if (!T) {
                        LP_ERRMSG (ERROR, E_ADM_TOPT);
                        done (1);

                }

        /*
         * Check -o cpi=, -o lpi=, -o length=, -o width=
         */
        if (cpi || lpi || length || width) {
                unsigned        long    rc;

                if ((rc = sum_chkprinter(T, cpi, lpi, length, width, NULL)) == 0) {
                        if (bad_list)
                                LP_ERRMSG1 (
                                        INFO,
                                        E_ADM_NBADCAPS,
                                        sprintlist(bad_list)
                                );

                } else {
                        if ((rc & PCK_CPI) && cpi)
                                LP_ERRMSG1 (ERROR, E_ADM_BADCAP, "cpi=");

                        if ((rc & PCK_LPI) && lpi)
                                LP_ERRMSG1 (ERROR, E_ADM_BADCAP, "lpi=");

                        if ((rc & PCK_WIDTH) && width)
                                LP_ERRMSG1 (ERROR, E_ADM_BADCAP, "width=");

                        if ((rc & PCK_LENGTH) && length)
                                LP_ERRMSG1 (ERROR, E_ADM_BADCAP, "length=");

                        LP_ERRMSG (ERROR, E_ADM_BADCAPS);
                        done(1);
                }
        }

        /*
         * Check -I (old or new):
         */
        if (T && lenlist(T) > 1) {

#define BADILIST(X) (lenlist(X) > 1 || X && *X && !STREQU(NAME_SIMPLE, *X))
                if (
                        I && BADILIST(I)
                     || !I && oldp && BADILIST(oldp->input_types)
                ) {
                        LP_ERRMSG (ERROR, E_ADM_ONLYSIMPLE);
                        done (1);
                }
        }

        /*
         * MOUNT:
         * Only one print wheel can be mounted at a time.
         */
        if (M && S && S[0] && S[1])
                LP_ERRMSG (WARNING, E_ADM_MSINGLES);

        /*
         * NO MOUNT:
         * If the printer takes print wheels, the -S argument
         * should be a simple list; otherwise, it must be a
         * mapping list. (EXCEPT: In either case, "none" alone
         * means delete the existing list.)
         */
        if (S && !M) {
                register char           **item,
                                        *cp;

                /*
                 * For us to be here, "daisy" must have been set.
                 * (-S requires knowing printer type (T), and knowing
                 * T caused call to "tidbit()" to set "daisy").
                 */
                if (!STREQU(S[0], NAME_NONE) || S[1])
                    if (daisy) {
                        for (item = S; *item; item++) {
                                if (strchr(*item, '=')) {
                                        LP_ERRMSG (ERROR, E_ADM_PWHEELS);
                                        done (1);
                                }
                                if (!syn_name(*item)) {
                                        LP_ERRMSG1 (ERROR, E_LP_NOTNAME, *item);
                                        done (1);
                                }
                        }
                    } else {
                        register int            die = 0;

                        for (item = S; *item; item++) {
                                if (!(cp = strchr(*item, '='))) {
                                        LP_ERRMSG (ERROR, E_ADM_CHARSETS);
                                        done (1);
                                }

                                *cp = 0;
                                if (!syn_name(*item)) {
                                        LP_ERRMSG1 (ERROR, E_LP_NOTNAME, *item);
                                        done (1);
                                }
                                if (PCK_CHARSET & sum_chkprinter(T, (char *)0, (char *)0, (char *)0, (char *)0, *item)) {
                                        LP_ERRMSG1 (ERROR, E_ADM_BADSET, *item);
                                        die = 1;
                                } else {
                                        if (bad_list)
                                                LP_ERRMSG2 (
                                                        INFO,
                                                        E_ADM_NBADSET,
                                                        *item,
                                                        sprintlist(bad_list)
                                                );
                                }
                                *cp++ = '=';
                                if (!syn_name(cp)) {
                                        LP_ERRMSG1 (ERROR, E_LP_NOTNAME, cp);
                                        done (1);
                                }
                        }
                        if (die) {
                                LP_ERRMSG (ERROR, E_ADM_BADSETS);
                                done (1);
                        }
                }
        }

        if (P) {
                int createForm = 0;
                char **plist;

                if (getform(P, &formbuf, (FALERT *)0, (FILE **)0) != -1) {
                        if ((!formbuf.paper) || (!STREQU(formbuf.paper,P)) ) {
                                LP_ERRMSG (ERROR, E_ADM_ALSO_SEP_FORM);
                                done (1);
                        }
                } else
                        createForm = 1;

                if (*P == '~') { /* removing types of papers */
                        P++;
                        p_remove = getlist(P, LP_WS, LP_SEP);
                        p_add = NULL;
                } else  { /* adding types of papers */
                        p_add = getlist(P, LP_WS, LP_SEP);
                        p_remove = NULL;
                        if (createForm) {
                                char cmdBuf[200];

                                for (plist = p_add; *plist; plist++) {
                                        snprintf(cmdBuf, sizeof (cmdBuf),
                                            "lpforms -f %s -d\n", *plist);
                                        system(cmdBuf);
                                }
                        }
                }

                if (!f && !M) {  /* make paper allowed on printer too */
                        f = Malloc(strlen(P) + strlen(NAME_ALLOW) +
                            strlen(": "));
                        sprintf(f, "%s:%s", NAME_ALLOW, P);
                        isfAuto = 1;
                }
        }
        /*
         * NO MOUNT:
         * The -f option restricts the forms that can be used with
         * the printer.
         *      - construct the allow/deny lists
         *      - check each allowed form to see if it'll work
         *        on the printer
         */
        if (f && !M) {
                register char           *type   = strtok(f, ":"),
                                        *str    = strtok((char *)0, ":"),
                                        **pf;

                register int            die     = 0;


                if (STREQU(type, NAME_ALLOW) && str) {
                        if ((pf = f_allow = getlist(str, LP_WS, LP_SEP)) != NULL) {
                                while (*pf) {
                                        if ((!isfAuto) &&
                                                !STREQU(*pf, NAME_NONE)
                                             && verify_form(*pf) < 0
                                        )
                                                die = 1;
                                        pf++;
                                }
                                if (die) {
                                        LP_ERRMSG (ERROR, E_ADM_FORMCAPS);
                                        done (1);
                                }

                        } else
                                LP_ERRMSG1 (WARNING, E_ADM_MISSING, NAME_ALLOW);

                } else if (STREQU(type, NAME_DENY) && str) {
                        if ((pf = f_deny = getlist(str, LP_WS, LP_SEP)) != NULL) {
                                if (!STREQU(*pf, NAME_ALL)) {
                                        while (*pf) {
                                                if ((!isfAuto) &&
                                                  !STREQU(*pf, NAME_NONE) &&
                                                  getform(*pf, &formbuf,
                                                  (FALERT *)0, (FILE **)0) < 0
                                                ) {
                                                   LP_ERRMSG2(WARNING,
                                                        E_ADM_ICKFORM, *pf, p);
                                                   die = 1;
                                                }
                                                pf++;
                                        }
                                }
                                if (die) {
                                        done (1);
                                }

                        } else
                                LP_ERRMSG1 (WARNING, E_ADM_MISSING, NAME_DENY);

                } else {
                        LP_ERRMSG (ERROR, E_ADM_FALLOWDENY);
                        done (1);
                }
        }

        /*
         * The -u option is setting use restrictions on printers.
         *      - construct the allow/deny lists
         */
        if (u) {
                register char           *type   = strtok(u, ":"),
                                        *str    = strtok((char *)0, ":");

                if (STREQU(type, NAME_ALLOW) && str) {
                        if (!(u_allow = getlist(str, LP_WS, LP_SEP)))
                                LP_ERRMSG1 (WARNING, E_ADM_MISSING, NAME_ALLOW);

                } else if (STREQU(type, NAME_DENY) && str) {
                        if (!(u_deny = getlist(str, LP_WS, LP_SEP)))
                                LP_ERRMSG1 (WARNING, E_ADM_MISSING, NAME_DENY);

                } else {
                        LP_ERRMSG (ERROR, E_LP_UALLOWDENY);
                        done (1);
                }
        }

        return;
}

/**
 ** root_can_write() - CHECK THAT "root" CAN SENSIBLY WRITE TO PATH
 **/

static int              root_can_write (path)
        char                    *path;
{
        static int              lp_uid          = -1;

        struct passwd           *ppw;

        struct stat             statbuf;


        if (lstat(path, &statbuf) == -1) {
                LP_ERRMSG1 (ERROR, E_ADM_NOENT, v);
                return (-1);
        }
        /*
         * If the device is a symlink (and it is not a root owned symlink),
         * verify that the owner matches the destination owner.
         */
        if (S_ISLNK(statbuf.st_mode) && statbuf.st_uid != 0) {
                uid_t uid = statbuf.st_uid;

                if (Stat(path, &statbuf) == -1) {
                        LP_ERRMSG1 (ERROR, E_ADM_NOENT, v);
                        return (-1);
                }

                if (statbuf.st_uid != uid) {
                        LP_ERRMSG1 (ERROR, E_ADM_ISMISMATCH, v);
                        done(1);
                }

                LP_ERRMSG1(WARNING, E_ADM_ISNOTROOTOWNED, v);
        }

        if ((statbuf.st_mode & S_IFMT) == S_IFDIR) {
                LP_ERRMSG1 (WARNING, E_ADM_ISDIR, v);
        } else if ((statbuf.st_mode & S_IFMT) == S_IFBLK)
                LP_ERRMSG1 (WARNING, E_ADM_ISBLK, v);

        if (lp_uid == -1) {
                if (!(ppw = getpwnam(LPUSER)))
                        ppw = getpwnam(ROOTUSER);
                endpwent ();
                if (ppw)
                        lp_uid = ppw->pw_uid;
                else
                        lp_uid = 0;
        }
        if (!STREQU(v, "/dev/null"))
            if ((statbuf.st_uid && statbuf.st_uid != lp_uid)
                || (statbuf.st_mode & (S_IWGRP|S_IRGRP|S_IWOTH|S_IROTH)))
                LP_ERRMSG1 (WARNING, E_ADM_DEVACCESS, v);

        return (0);
}

/**
 ** unpack_sdn() - TURN SCALED TYPE INTO char* TYPE
 **/

static char             *unpack_sdn (sdn)
        SCALED                  sdn;
{
        register char           *cp;
        extern char             *malloc();

        if (sdn.val <= 0 || 99999 < sdn.val)
                cp = 0;

        else if (sdn.val == N_COMPRESSED)
                cp = strdup(NAME_COMPRESSED);

        else if ((cp = malloc(sizeof("99999.999x"))))
                (void) sprintf(cp, "%.3f%c", sdn.val, sdn.sc);

        return (cp);
}

/**
 ** verify_form() - SEE IF PRINTER CAN HANDLE FORM
 **/

int                     verify_form (form)
        char                    *form;
{
        register char           *cpi_f,
                                *lpi_f,
                                *width_f,
                                *length_f,
                                *chset;

        register int            rc      = 0;
        char                    **paperAllowed = NULL;
        char                    **paperDenied = NULL;

        register unsigned long  checks;


        if (STREQU(form, NAME_ANY))
                form = NAME_ALL;

        while (getform(form, &formbuf, (FALERT *)0, (FILE **)0) != -1) {
                if (formbuf.paper) {
                        if (!paperAllowed) {
                                load_paperprinter_access(p, &paperAllowed,
                                        &paperDenied);
                                freelist(paperDenied);
                        }
                        if (!allowed(formbuf.paper,paperAllowed,NULL)) {
                                LP_ERRMSG1 (INFO, E_ADM_BADCAP,
                                gettext("printer doesn't support paper type"));
                                rc = -1;
                        }
                }
                else {

                cpi_f = unpack_sdn(formbuf.cpi);
                lpi_f = unpack_sdn(formbuf.lpi);
                width_f = unpack_sdn(formbuf.pwid);
                length_f = unpack_sdn(formbuf.plen);

                if (
                        formbuf.mandatory
                     && !daisy
                     && !search_cslist(
                                formbuf.chset,
                                (S && !M? S : (oldp? oldp->char_sets : (char **)0))
                        )
                )
                        chset = formbuf.chset;
                else
                        chset = 0;

                if ((checks = sum_chkprinter(
                        T,
                        cpi_f,
                        lpi_f,
                        length_f,
                        width_f,
                        chset
                ))) {
                        rc = -1;
                        if ((checks & PCK_CPI) && cpi_f)
                                LP_ERRMSG1 (INFO, E_ADM_BADCAP, "cpi");

                        if ((checks & PCK_LPI) && lpi_f)
                                LP_ERRMSG1 (INFO, E_ADM_BADCAP, "lpi");

                        if ((checks & PCK_WIDTH) && width_f)
                                LP_ERRMSG1 (INFO, E_ADM_BADCAP, "width");

                        if ((checks & PCK_LENGTH) && length_f)
                                LP_ERRMSG1 (INFO, E_ADM_BADCAP, "length");

                        if ((checks & PCK_CHARSET) && formbuf.chset) {
                                LP_ERRMSG1 (INFO, E_ADM_BADSET, formbuf.chset);
                                rc = -2;
                        }
                        LP_ERRMSG1 (INFO, E_ADM_FORMCAP, formbuf.name);
                } else {
                        if (bad_list)
                                LP_ERRMSG2 (
                                        INFO,
                                        E_ADM_NBADMOUNT,
                                        formbuf.name,
                                        sprintlist(bad_list)
                                );
                }
                }

                if (!STREQU(form, NAME_ALL)) {
                        if (paperAllowed)
                                freelist(paperAllowed);
                        return (rc);
                }

        }
        if (paperAllowed)
                freelist(paperAllowed);

        if (!STREQU(form, NAME_ALL)) {
                LP_ERRMSG1 (ERROR, E_LP_NOFORM, form);
                done (1);
        }

        return (rc);
}

/*
        Second phase of parsing for -p option.
        In a seperate routine so we can call it from other
        routines. This is used when any or all are used as
        a printer name. main() loops over each printer, and
        must call this function for each printer found.
*/
void
chkopts2(called_from_chkopts)
int     called_from_chkopts;
{
        /*
                Only do the getprinter() if we are not being called
                from lpadmin.c. Otherwise we mess up our arena for
                "all" processing.
        */
        if (!called_from_chkopts)
                oldp = printer_pointer;
        else if (!(oldp = getprinter(p)) && errno != ENOENT) {
                LP_ERRMSG2 (ERROR, E_LP_GETPRINTER, p, PERROR);
                done(1);
        }

        if (oldp) {
                if (
                        !c && !d && !f && !P && !M && !t && !r && !u && !x && !A
                        && !strlen(modifications)
                ) {
                        LP_ERRMSG (ERROR, E_ADM_PLONELY);
                        done (1);
                }

                /*
                 * For the case "-s local-system", we need to keep
                 * "s != 0" long enough to get here, where it keeps
                 * us from taking the old value. After this, we make
                 * "s == 0" to indicate this is a local printer.
                 */
                if (s && s != Local_System)
                        chksys(s);
                if (!s && oldp->remote && *(oldp->remote))
                        s = strdup(oldp->remote);
                if (s == Local_System)
                        s = 0;

                /*
                 * A remote printer converted to a local printer
                 * requires device or dial info.
                 */
                if (!s && oldp->remote && !v && !U) {
                        LP_ERRMSG (ERROR, E_ADM_NOUV);
                        done (1);
                }


        } else {
                if (getclass(p)) {
                        LP_ERRMSG1 (ERROR, E_ADM_PRCL, p);
                        done (1);
                }

                if (!syn_name(p)) {
                        LP_ERRMSG1 (ERROR, E_LP_NOTNAME, p);
                        done (1);
                }

                if (s == Local_System)
                        s = 0;
                if (s)
                        chksys(s);

#ifdef LP_USE_PAPI_ATTR
                /*
                 * New printer - if no model and a PPD file is defined then
                 *               use 'standard_foomatic' otherwise use
                 *               the 'standard' model.
                 */
                if (!(e || i || m) && !s) {
                        if (n_opt != NULL) {
                                m = STANDARD_FOOMATIC;
                        } else {
                                m = STANDARD;
                        }
                }
#else
                /*
                 * New printer - if no model, use standard
                 */
                if (!(e || i || m) && !s)
                        m = STANDARD;
#endif

                /*
                 * A new printer requires device or dial info.
                 */
                if (!v && !U && !s) {
                        LP_ERRMSG (ERROR, E_ADM_NOUV);
                        done (1);
                }

                /*
                 * Can't quiet a new printer,
                 * can't list the alerting for a new printer.
                 */
                if (
                        A
                     && (STREQU(A, NAME_QUIET) || STREQU(A, NAME_LIST))
                ) {
                        LP_ERRMSG1 (ERROR, E_ADM_BADQUIETORLIST, p);
                        done (1);
                }

                /*
                 * New printer - if no input types given, assume "simple".
                 */
                if (!I) {
                        I = getlist(NAME_SIMPLE, LP_WS, LP_SEP);
                        strcat (modifications, "I");
                }
        }
}

/*
        Second phase of parsing for -S option.
        In a seperate routine so we can call it from other
        routines. This is used when any or all are used as
        a print wheel name. main() loops over each print wheel,
        and must call this function for each print wheel found.
*/
void
chkopts3(called_from_chkopts)
int     called_from_chkopts;
{
        /*
                Only do the getpwheel() if we are not being called
                from lpadmin.c. Otherwise we mess up our arena for
                "all" processing.
        */
        if (!called_from_chkopts)
                oldS = pwheel_pointer;
        else
                oldS = getpwheel(*S);

        if (!oldS) {
                if (!syn_name(*S)) {
                        LP_ERRMSG1 (ERROR, E_LP_NOTNAME, *S);
                        done (1);
                }

                /*
                 * Can't quiet a new print wheel,
                 * can't list the alerting for a new print wheel.
                 */
                if (
                        A
                     && (STREQU(A, NAME_QUIET) || STREQU(A, NAME_LIST))
                ) {
                        LP_ERRMSG1 (ERROR, E_ADM_BADQUIETORLIST, *S);
                        done (1);
                }
        }
}

static void
chksys(s)
char    *s;
{
        char    *cp;

        if (STREQU(s, NAME_ALL) || STREQU(s, NAME_ANY)) {
                LP_ERRMSG (ERROR, E_ADM_ANYALLSYS);
                done(1);
        }

        if ((cp = strchr(s, '!')) != NULL)
                *cp = '\0';

        if (cp)
                *cp = '!';

        return;
}

/**
 ** sum_chkprinter() - CHECK TERMINFO STUFF FOR A LIST OF PRINTER TYPES
 **/

#include "lp.set.h"

static unsigned long
#if     defined(__STDC__)
sum_chkprinter (
        char **                 types,
        char *                  cpi,
        char *                  lpi,
        char *                  len,
        char *                  wid,
        char *                  cs
)
#else
sum_chkprinter (types, cpi, lpi, len, wid, cs)
        char **                 types;
        char *                  cpi;
        char *                  lpi;
        char *                  len;
        char *                  wid;
        char *                  cs;
#endif
{
        char **                 pt;

        unsigned long           worst   = 0;
        unsigned long           this    = 0;


        /*
         * Check each printer type, to see if any won't work with
         * the attributes requested. However, return ``success''
         * if at least one type works. Keep a list of the failed
         * types for the caller to report.
         */
        bad_list = 0;
        for (pt = types; *pt; pt++) {
                this = chkprinter(*pt, cpi, lpi, len, wid, cs);
                if (this != 0)
                        addlist (&bad_list, *pt);
                worst |= this;
        }
        if (lenlist(types) == lenlist(bad_list))
                return (worst);
        else
                return (0);
}

/*
 * Function:    isPPD()
 *
 * Description: Check that the given PPD file exists. The argument given can
 *              either be a relative path or a full path to the file.
 *
 * Returns:     1 = PPD file found
 *              0 = PPD file not found
 */

static int
isPPD(char *ppd_file)
{
        int result = 0;
        char *ppd = NULL;

        if (ppd_file != NULL) {
                if (*ppd_file == '/') {
                        ppd = strdup(ppd_file);
                } else {
                        ppd = makepath(Lp_Model, "ppd", ppd_file, (char *)0);
                }

                /*
                 * now check the file exists
                 */
                if ((ppd != NULL) && (Access(ppd, 04) != -1)) {
                        result = 1;
                } else {
                        /*
                         * files does not exist so append .gz and check if
                         * that exist
                         */
                        ppd = Realloc(ppd, strlen(ppd)+ strlen(PPDZIP)+2);
                        if (ppd != NULL) {
                                ppd = strcat(ppd, PPDZIP);
                                if (Access(ppd, 04) != -1) {
                                        result = 1;
                                }
                        }
                }

                if (ppd != NULL) {
                        free(ppd);
                }
        }
        return (result);
} /* isPPD() */