root/usr/src/cmd/lp/lib/printers/getprinter.c
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License (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 2006 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

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

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

#include "stdio.h"
#include "string.h"
#include "errno.h"
#include "sys/types.h"
#include "stdlib.h"
#include <syslog.h>

#include "lp.h"
#include "printers.h"

extern struct {
        char                    *v;
        short                   len,
                                okremote;
}                       prtrheadings[];

/**
 ** getprinter() - EXTRACT PRINTER STRUCTURE FROM DISK FILE
 **/

PRINTER *
getprinter(char *name)
{
        static long             lastdir         = -1;

        PRINTER         *prp;

        char                    buf[BUFSIZ];

        short                   daisy;

        int                     fld;

        int fd;

        FALERT                  *pa;

        register char *         p;
        register char **        pp;
        register char ***       ppp;
        register char *         path;
        int                     isNameAll;



        if (!name || !*name) {
                errno = EINVAL;
                return (0);
        }

        syslog(LOG_DEBUG, "getprinter(%s)", name ? name : "");
        /*
         * Getting ``all''? If so, jump into the directory
         * wherever we left off.
         */
        isNameAll = STREQU(NAME_ALL, name);
        for (; ; ) {
                /* fix for bug 1117241
                 * occasionally when a printer is removed, a printer directory
                 * is left behind, but the CONFIGFILE is removed.  In this
                 * case this directory terminates the search for additional
                 * printers as we have been returning 0 in this case.
                 * Now, we loop back and try the next directory until
                 * we have no more directories or we find a directory with
                 * a CONFIGFILE
                 */
                if (isNameAll) {
                        if (!(name = next_dir(Lp_A_Printers, &lastdir)))
                                return (0);
                } else
                        lastdir = -1;

                /*
                 * Get the printer configuration information.
                 */

                path = getprinterfile(name, CONFIGFILE);
                if (!path) {
                        if (isNameAll)
                                Free(name);
                        return (0);
                }

                if ((fd = open_locked(path, "r", 0)) < 0) {
                        Free(path);     /*
                                         * go around to loop again for
                                         * NAME_ALL case
                                         */

                        if (!isNameAll) /* fix for bug 1117241 */
                                return(0);
                        else
                                Free(name);
                }
                else
                        break;
        }
        Free (path);

        /*
         * Initialize the entire structure, to ensure no random
         * values get in it. However, make sure some values won't
         * be null or empty. Do the latter here as opposed to
         * after reading the file, because sometimes the file
         * contains an empty header to FORCE a null/empty value.
         */
        prp = calloc(sizeof (*prp), 1);
        prp->name = Strdup(name);
        if (isNameAll)
                Free(name);
        prp->printer_types = getlist(NAME_UNKNOWN, LP_WS, LP_SEP);
        prp->input_types = getlist(NAME_SIMPLE, LP_WS, LP_SEP);
#if     defined(CAN_DO_MODULES)
        prp->modules = getlist(NAME_DEFAULT, LP_WS, LP_SEP);
#endif

        /*
         * Read the file.
         */
        errno = 0;
        while (fdgets(buf, BUFSIZ, fd) != NULL) {

                buf[strlen(buf) - 1] = 0;

                for (fld = 0; fld < PR_MAX; fld++)
                        if (
                                prtrheadings[fld].v
                             && prtrheadings[fld].len
                             && STRNEQU(
                                        buf,
                                        prtrheadings[fld].v,
                                        prtrheadings[fld].len
                                )
                        ) {
                                p = buf + prtrheadings[fld].len;
                                while (*p && *p == ' ')
                                        p++;
                                break;
                        }

                /*
                 * To allow future extensions to not impact applications
                 * using old versions of this routine, ignore strange
                 * fields.
                 */
                if (fld >= PR_MAX)
                        continue;

                switch (fld) {

                case PR_BAN:
                        if ((pp = getlist(p, LP_WS, ":"))) {
                                if (pp[0] != NULL) {
                                        if (strcmp(pp[0], NAME_OPTIONAL) == 0)
                                                prp->banner = BAN_OPTIONAL;
                                        else if (strcmp(pp[0], NAME_OFF) == 0)
                                                prp->banner = BAN_NEVER;
                                        else if (strcmp(pp[0], NAME_ON) == 0)
                                                prp->banner = BAN_ALWAYS;
                                        else /* default to the LP default */
                                                prp->banner = BAN_ALWAYS;
                                }
                                if (pp[1] && CS_STREQU(pp[1], NAME_ALWAYS))
                                        prp->banner |= BAN_ALWAYS;
                                freelist (pp);
                        }
                        break;

                case PR_LOGIN:
                        prp->login = LOG_IN;
                        break;

                case PR_CPI:
                        prp->cpi = getcpi(p);
                        break;

                case PR_LPI:
                        prp->lpi = getsdn(p);
                        break;

                case PR_LEN:
                        prp->plen = getsdn(p);
                        break;

                case PR_WIDTH:
                        prp->pwid = getsdn(p);
                        break;

                case PR_CS:
                        ppp = &(prp->char_sets);
                        goto CharStarStar;

                case PR_ITYPES:
                        ppp = &(prp->input_types);
CharStarStar:           if (*ppp)
                                freelist (*ppp);
                        *ppp = getlist(p, LP_WS, LP_SEP);
                        break;

                case PR_DEV:
                        pp = &(prp->device);
                        goto CharStar;

                case PR_DIAL:
                        pp = &(prp->dial_info);
                        goto CharStar;

                case PR_RECOV:
                        pp = &(prp->fault_rec);
                        goto CharStar;

                case PR_INTFC:
                        pp = &(prp->interface);
                        goto CharStar;

                case PR_PTYPE:
                        ppp = &(prp->printer_types);
                        goto CharStarStar;

                case PR_REMOTE:
                        pp = &(prp->remote);
                        goto CharStar;

                case PR_SPEED:
                        pp = &(prp->speed);
                        goto CharStar;

                case PR_STTY:
                        pp = &(prp->stty);
CharStar:               if (*pp)
                                Free (*pp);
                        *pp = Strdup(p);
                        break;

#if     defined(CAN_DO_MODULES)
                case PR_MODULES:
                        ppp = &(prp->modules);
                        goto CharStarStar;
#endif

                case PR_OPTIONS:
                        ppp = &(prp->options);
                        goto CharStarStar;
                        break;

                case PR_PPD:
                {
                        pp = &(prp->ppd);
                        goto CharStar;
                }
                }

        }
        if (errno != 0) {
                int                     save_errno = errno;

                freeprinter (prp);
                close(fd);
                errno = save_errno;
                return (0);
        }
        close(fd);

        /*
         * Get the printer description (if it exists).
         */
        if (!(path = getprinterfile(prp->name, COMMENTFILE)))
                return (0);
        if (!(prp->description = loadstring(path)) && errno != ENOENT) {
                Free (path);
                freeprinter (prp);
                return (0);
        }
        Free (path);

        /*
         * Get the information for the alert. Don't fail if we can't
         * read it because of access permission UNLESS we're "root"
         * or "lp"
         */
        if (!(pa = getalert(Lp_A_Printers, prp->name))) {
                if (
                        errno != ENOENT
                     && (
                                errno != EACCES
                             || !getpid()                 /* we be root */
                             || STREQU(getname(), LPUSER) /* we be lp   */
                        )
                ) {
                        freeprinter (prp);
                        return (0);
                }
        } else
                prp->fault_alert = *pa;

        /*
         * Now go through the structure and see if we have
         * anything strange.
         */
        if (!okprinter(prp->name, prp, 0)) {
                freeprinter (prp);
                errno = EBADF;
                return (0);
        }

        /*
         * Just in case somebody tried to pull a fast one
         * by giving a printer type header by itself....
         */
        if (!prp->printer_types)
                prp->printer_types = getlist(NAME_UNKNOWN, LP_WS, LP_SEP);

        /*
         * If there are more than one printer type, then we can't
         * have any input types, except perhaps ``simple''.
         */
        if (
                lenlist(prp->printer_types) > 1
             && prp->input_types
             && (
                        lenlist(prp->input_types) > 1
                     || !STREQU(NAME_SIMPLE, *prp->input_types)
                )
        ) {
                freeprinter (prp);
                badprinter = BAD_ITYPES;
                errno = EBADF;
                return (0);
        }

        /*
         * If there are more than one printer types, none can
         * be ``unknown''.
         */
        if (
                lenlist(prp->printer_types) > 1
             && searchlist(NAME_UNKNOWN, prp->printer_types)
        ) {
                freeprinter (prp);
                badprinter = BAD_PTYPES;
                errno = EBADF;
                return (0);
        }

        /*
         * All the printer types had better agree on whether the
         * printer takes print wheels!
         */
        prp->daisy = -1;
        for (pp = prp->printer_types; *pp; pp++) {
                tidbit (*pp, "daisy", &daisy);
                if (daisy == -1)
                        daisy = 0;
                if (prp->daisy == -1)
                        prp->daisy = daisy;
                else if (prp->daisy != daisy) {
                        freeprinter (prp);
                        badprinter = BAD_DAISY;
                        errno = EBADF;
                        return (0);
                }
        }

        /*
         * Help out those who are still using the obsolete
         * "printer_type" member.
         */
        prp->printer_type = Strdup(*prp->printer_types);

        return (prp);
}