root/usr/src/cmd/lp/cmd/lpadmin/options.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 2005 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

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

#include "ctype.h"
#include "stdio.h"
#include "string.h"
#include "stdlib.h"
#include <libintl.h>

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

#define WHO_AM_I        I_AM_LPADMIN
#include "oam.h"

#include "lpadmin.h"

#ifdef LP_USE_PAPI_ATTR
#if     defined(CAN_DO_MODULES)
#define OPT_LIST "A:ac:d:D:e:f:F:H:hi:I:lm:Mn:o:p:Q:r:S:s:T:u:U:v:W:x:t:P:"
#else
#define OPT_LIST "A:ac:d:D:e:f:F:hi:I:lm:Mn:o:p:Q:r:S:s:T:u:U:v:W:x:t:P:"
#endif

#else
#if     defined(CAN_DO_MODULES)
#define OPT_LIST        "A:ac:d:D:e:f:F:H:hi:I:lm:Mo:p:Q:r:S:s:T:u:U:v:W:x:t:P:"
#else
#define OPT_LIST        "A:ac:d:D:e:f:F:hi:I:lm:Mo:p:Q:r:S:s:T:u:U:v:W:x:t:P:"
#endif
#endif

#define MALLOC(pointer) \
        if (!(pointer = strdup(optarg))) { \
                LP_ERRMSG (ERROR, E_LP_MALLOC); \
                done (1); \
        } else

#define REALLOC(pointer) \
        if (!(pointer = realloc(pointer, (unsigned) (strlen(pointer) + 1 + strlen(optarg) + 1)))) { \
                LP_ERRMSG (ERROR, E_LP_MALLOC); \
                done (1); \
        } else if (strcat(pointer, " ")) \
                (void)strcat (pointer, optarg); \
        else

extern char             *optarg;

extern int              optind,
                        opterr,
                        optopt;

extern double           strtod();

extern long             strtol();

int                     a       = 0,    /* alignment needed for mount */
                        banner  = -1,   /* allow/don't-allow nobanner */
#if     defined(DIRECT_ACCESS)
                        C       = 0,    /* direct a.o.t. normal access */
#endif
                filebreak       = 0,
                h       = 0,    /* hardwired terminal */
                j       = 0,    /* do -F just for current job */
                l       = 0,    /* login terminal */
                M       = 0,    /* do mount */
                t       = 0,    /* tray number*/
                o       = 0,    /* some -o options given */
                Q       = -1,   /* queue threshold for alert */
                W       = -1;   /* alert interval */

char            *A      = 0,    /* alert type */
                *c      = 0,    /* class name */
                *cpi    = 0,    /* string value of -o cpi= */
                *d      = 0,    /* default destination */
                *D      = 0,    /* description */
                *e      = 0,    /* copy existing interface */
                *f      = 0,    /* forms list - allow/deny */
                *P      = 0,    /* paper list  */
                *F      = 0,    /* fault recovery */
                **H     = 0,    /* list of modules to push */
                *i      = 0,    /* interface pathname */
                **I     = 0,    /* content-type-list */
                *length = 0,    /* string value of -o length= */
                *lpi    = 0,    /* string value of -o lpi= */
                *m      = 0,    /* model name */
                modifications[128], /* list of mods to make */
#ifdef LP_USE_PAPI_ATTR
                *n_opt  = NULL, /* PPD file name */
#endif
                *p      = 0,    /* printer name */
                *r      = 0,    /* class to remove printer from */
                *s      = 0,    /* system printer is on */
                *stty_opt= 0,   /* string value of -o stty= */
                **o_options = 0,/* undefined lpadmin -o options */
                **S     = 0,    /* -set/print-wheel list */
                **T     = 0,    /* terminfo names */
                *u      = 0,    /* user allow/deny list */
                *U      = 0,    /* dialer_info */
                *v      = 0,    /* device pathname */
                *width  = 0,    /* string value of -o width= */
                *x      = 0;    /* destination to be deleted */

SCALED          cpi_sdn = { 0, 0 },
                length_sdn = { 0, 0 },
                lpi_sdn = { 0, 0 },
                width_sdn = { 0, 0 };

static char     *modp   = modifications;

static void     oparse();

static char *   empty_list[] = { 0 };

/**
 ** options() - PARSE COMMAND LINE ARGUMENTS INTO OPTIONS
 **/

void                    options (argc, argv)
        int                     argc;
        char                    *argv[];
{
        int             optsw,
                        ac,
                        Aflag = 0;

        char            *cp,
                        *rest,
                        **av;
        char            stroptsw[] = "-X";

#if     defined(__STDC__)
        typedef char * const *  stupid; /* dumb-ass ANSI C */
#else
        typedef char **         stupid;
#endif


        /*
         * Add a fake value to the end of the "argv" list, to
         * catch the case that a valued-option comes last.
         */
        av = malloc((argc + 2) * sizeof(char *));
        for (ac = 0; ac < argc; ac++)
                av[ac] = argv[ac];
        av[ac++] = "--";

        opterr = 0;
        while ((optsw = getopt(ac, (stupid)av, OPT_LIST)) != EOF) {

                switch (optsw) {

                /*
                 * These options MAY take a value. Check the value;
                 * if it begins with a '-', assume it's really the next
                 * option.
                 */
                case 'd':
                case 'p':       /* MR bl87-27863 */
                case 'I':
#if     defined(CAN_DO_MODULES)
                case 'H':
#endif
                        if (*optarg == '-') {
                                /*
                                 * This will work if we were given
                                 *
                                 *      -x -foo
                                 *
                                 * but would fail if we were given
                                 *
                                 *      -x-foo
                                 */
                                optind--;
                                switch (optsw) {
                                case 'd':
#if     defined(CAN_DO_MODULES)
                                case 'H':
#endif
                                        optarg = NAME_NONE;
                                        break;
                                case 'p':
                                        optarg = NAME_ALL;
                                        break;
                                case 'I':
                                        optarg = 0;
                                        break;
                                }
                        }
                        break;

                /*
                 * These options MUST have a value. Check the value;
                 * if it begins with a dash or is null, complain.
                 */
                case 'Q':
                case 'W':
                case 't':
                        /*
                         * These options take numeric values, which might
                         * be negative. Negative values are handled later,
                         * but here we just screen them.
                         */
                        (void)strtol(optarg, &rest, 10);
                        if (!rest || !*rest)
                                break;
                        /*FALLTHROUGH*/
                case 'A':
                case 'c':
                case 'e':
                case 'f':
                case 'P':
                case 'F':
                case 'i':
                case 'm':
#ifdef LP_USE_PAPI_ATTR
                case 'n':
#endif
                case 'o':
/*              case 'p': */    /* MR bl87-27863 */
                case 'r':
                case 'S':
                case 's':
                case 'T':
                case 'u':
                case 'U':
                case 'v':
                case 'x':
                        /*
                         * These options also must have non-null args.
                         */
                        if (!*optarg) {
                                stroptsw[1] = optsw;
                                LP_ERRMSG1 (ERROR, E_LP_NULLARG, stroptsw);
                                done (1);
                        }
                        if (*optarg == '-') {
                                stroptsw[1] = optsw;
                                LP_ERRMSG1 (ERROR, E_LP_OPTARG, stroptsw);
                                done (1);
                        }
                        if (optsw == 'A')
                                Aflag++;
                        break;
                case 'D':
                        /*
                         * These options can have a null arg.
                         */
                        if (*optarg == '-') {
                                stroptsw[1] = optsw;
                                LP_ERRMSG1 (ERROR, E_LP_OPTARG, stroptsw);
                                done (1);
                        }
                        break;
                }

                switch (optsw) {

                case 'a':       /* alignment pattern needed for mount */
                        a = 1;
                        break;

                case 'A':       /* alert type */
                        if (A)
                                LP_ERRMSG1 (WARNING, E_LP_2MANY, 'A');
                        MALLOC(A);
                        Aflag++;
                        if (!STREQU(A, NAME_QUIET) && !STREQU(A, NAME_LIST))
                                *modp++ = 'A';
                        break;

                case 'c':       /* class to insert printer p */
                        if (c)
                                LP_ERRMSG1 (WARNING, E_LP_2MANY, 'c');
                        MALLOC(c);
                break;

#if     defined(DIRECT_ACCESS)
                case 'C':
                        C = 1;
                        break;
#endif

                case 'd':       /* system default destination */
                        if (d)
                                LP_ERRMSG1 (WARNING, E_LP_2MANY, 'd');
                        MALLOC(d);
                        break;

                case 'D':       /* description */
                        if (D)
                                LP_ERRMSG1 (WARNING, E_LP_2MANY, 'D');
                        MALLOC(D);
                        *modp++ = 'D';
                        break;

                case 'e':       /* existing printer interface */
                        if (e)
                                LP_ERRMSG1 (WARNING, E_LP_2MANY, 'e');
                        MALLOC(e);
                        *modp++ = 'e';
                        break;

                case 'f':       /* set up forms allow/deny */
                        if (f)
                                LP_ERRMSG1 (WARNING, E_LP_2MANY, 'f');
                        MALLOC(f);
                        break;

                case 'P':       /* set up forms allow/deny */
                        if (P)
                                LP_ERRMSG1 (WARNING, E_LP_2MANY, 'P');
                        MALLOC(P);
                        break;

                case 'F':       /* fault recovery */
                        if (F)
                                LP_ERRMSG1 (WARNING, E_LP_2MANY, 'F');
                        MALLOC(F);
                        *modp++ = 'F';
                        break;

#if     defined(CAN_DO_MODULES)
                case 'H':
                        if (H)
                                LP_ERRMSG1 (WARNING, E_LP_2MANY, 'H');
                        if (!optarg || !*optarg || STREQU(NAME_NONE, optarg))
                                H = empty_list;
                        if (!(H = getlist(optarg, LP_WS, LP_SEP))) {
                                LP_ERRMSG (ERROR, E_LP_MALLOC);
                                done(1);
                        }
                        *modp++ = 'H';
                        break;
#endif

                case 'h':       /* hardwired terminal */
                        h = 1;
                        *modp++ = 'h';
                        break;

                case 'i':       /* interface pathname */
                        if (i)
                                LP_ERRMSG1 (WARNING, E_LP_2MANY, 'i');
                        MALLOC(i);
                        *modp++ = 'i';
                        break;

                case 'I':       /* content-type-list */
                        if (I)
                                LP_ERRMSG1 (WARNING, E_LP_2MANY, 'I');
                        if (!optarg || !*optarg || STREQU(NAME_NONE, optarg))
                                I = empty_list;
                        else if (!(I = getlist(optarg, LP_WS, LP_SEP))) {
                                LP_ERRMSG (ERROR, E_LP_MALLOC);
                                done (1);
                        }
                        *modp++ = 'I';
                        break;

#if     defined(J_OPTION)
                case 'j':       /* fault recovery just for current job */
                        j = 1;
(void) printf (gettext("Sorry, the -j option is currently broken\n"));
                        break;
#endif

                case 'l':       /* login terminal */
                        l = 1;
                        *modp++ = 'l';
                        break;

                case 'm':       /* model interface */
                        if (m)
                                LP_ERRMSG1 (WARNING, E_LP_2MANY, 'm');
                        MALLOC(m);
                        *modp++ = 'm';
                        break;

#ifdef LP_USE_PAPI_ATTR
                case 'n':       /* PPD file */
                        if (n_opt)
                                LP_ERRMSG1 (WARNING, E_LP_2MANY, 'n');
                        MALLOC(n_opt);
                        *modp++ = 'n';
                        break;
#endif

                case 'M':       /* a mount request */
                        M = 1;
                        break;

                case 'o':       /* several different options */
                        oparse (optarg);
                        o = 1;
                        break;

                case 'p':       /* printer name */
                        if (p)
                                LP_ERRMSG1 (WARNING, E_LP_2MANY, 'p');
                        MALLOC(p);
                        break;

                case 'Q':
                        if (Q != -1)
                                LP_ERRMSG1 (WARNING, E_LP_2MANY, 'Q');
                        if (STREQU(NAME_ANY, optarg))
                                Q = 1;
                        else {
                                Q = strtol(optarg, &rest, 10);
                                if (Q < 0) {
                                        LP_ERRMSG1 (ERROR, E_LP_NEGARG, 'Q');
                                        done (1);
                                }
                                if (rest && *rest) {
                                        LP_ERRMSG1 (ERROR, E_LP_GARBNMB, 'Q');
                                        done (1);
                                }
                                if (Q == 0) {
                                        LP_ERRMSG1 (ERROR, E_ADM_ZEROARG, 'Q');
                                        done (1);
                                }
                        }
                        *modp++ = 'Q';
                        break;

                case 'r':       /* class to remove p from */
                        if (r)
                                LP_ERRMSG1 (WARNING, E_LP_2MANY, 'r');
                        MALLOC(r);
                        break;

                case 'S':       /* char_set/print-wheels */
                        if (S)
                                LP_ERRMSG1 (WARNING, E_LP_2MANY, 'S');
                        if (!(S = getlist(optarg, LP_WS, LP_SEP))) {
                                LP_ERRMSG (ERROR, E_LP_MALLOC);
                                done (1);
                        }
                        *modp++ = 'S';
                        break;

                case 's':
                        if (s)
                                LP_ERRMSG1 (WARNING, E_LP_2MANY, 's');

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

                        if ((STREQU(optarg, NAME_NONE)) ||
                                (STREQU(optarg, "localhost")))

                                s = Local_System;
                        else if (STREQU(optarg, Local_System)) {
                                if (cp) {
                                        LP_ERRMSG (ERROR, E_ADM_NAMEONLOCAL);
                                        done(1);
                                } else
                                        s = Local_System;
                        } else {
                                if (cp)
                                    *cp = '!';

                                MALLOC(s);
                        }

                        /* 's' already used for stty 'R' for remote? */
                        *modp++ = 'R';
                        break;

                case 't':       /* tray number*/
                        if (t != 0) LP_ERRMSG1 (WARNING, E_LP_2MANY, 't');
                        t = strtol(optarg, &rest, 10);
                        if (t <= 0) {
                                LP_ERRMSG1 (ERROR, E_LP_NEGARG, 't');
                                done (1);
                        }
                        if (rest && *rest) {
                                LP_ERRMSG1 (ERROR, E_LP_GARBNMB, 't');
                                done (1);
                        }
                        break;

                case 'T':       /* terminfo names for p */
                        if (T)
                                LP_ERRMSG1 (WARNING, E_LP_2MANY, 'T');
                        if (!(T = getlist(optarg, LP_WS, LP_SEP))) {
                                LP_ERRMSG (ERROR, E_LP_MALLOC);
                                done (1);
                        }
                        *modp++ = 'T';
                        break;

                case 'u':       /* user allow/deny list */
                        if (u)
                                LP_ERRMSG1 (WARNING, E_LP_2MANY, 'u');
                        MALLOC(u);
                        break;

                case 'U':       /* dialer_info */
                        if (U)
                                LP_ERRMSG1 (WARNING, E_LP_2MANY, 'U');
                        MALLOC(U);
                        *modp++ = 'U';
                        break;

                case 'v':       /* device pathname */
                        if (v)
                                LP_ERRMSG1 (WARNING, E_LP_2MANY, 'v');
                        MALLOC(v);
                        *modp++ = 'v';
                        break;

                case 'W':       /* alert interval */
                        if (W != -1)
                                LP_ERRMSG1 (WARNING, E_LP_2MANY, 'W');
                        if (STREQU(NAME_ONCE, optarg))
                                W = 0;
                        else {
                                W = strtol(optarg, &rest, 10);
                                if (W < 0) {
                                        LP_ERRMSG1 (ERROR, E_LP_NEGARG, 'W');
                                        done (1);
                                }
                                if (rest && *rest) {
                                        LP_ERRMSG1 (ERROR, E_LP_GARBNMB, 'W');
                                        done (1);
                                }
                        }
                        *modp++ = 'W';
                        break;

                case 'x':       /* destination to be deleted */
                        if (x)
                                LP_ERRMSG1 (WARNING, E_LP_2MANY, 'x');
                        MALLOC(x);
                        break;

                default:
                        if (optopt == '?') {
                                usage ();
                                done (0);

                        } else {
                                stroptsw[1] = optsw;

                                if (strchr(OPT_LIST, optopt))
                                        LP_ERRMSG1 (ERROR, E_LP_OPTARG,
                                            stroptsw);
                                else
                                        LP_ERRMSG1 (ERROR, E_LP_OPTION,
                                            stroptsw);
                                done (1);
                        }
                }
        }

        if (optind < argc)
                LP_ERRMSG1 (WARNING, E_LP_EXTRA, argv[optind]);

        if ((v) && (!Aflag)) {
                if (!(A = strdup("write"))) {
                        LP_ERRMSG (ERROR, E_LP_MALLOC);
                        done (1);
                }
                *modp++ = 'A';
        }

        return;
}

/**
 ** oparse() - PARSE -o OPTION
 **/

static void             oparse (optarg)
        char                    *optarg;
{
        register char           **list  = dashos(optarg);


        if (!list)
                return;

        for ( ; (optarg = *list); list++)

                if (STREQU(optarg, "banner")) {
                        if (banner != -1)
                                LP_ERRMSG1 (
                                        WARNING,
                                        E_ADM_2MANY,
                                        "banner/nobanner"
                                );
                        banner = BAN_ALWAYS;
                        *modp++ = 'b';

                } else if (STREQU(optarg, "nobanner")) {
                        if (banner != -1)
                                LP_ERRMSG1 (
                                        WARNING,
                                        E_ADM_2MANY,
                                        "banner/nobanner"
                                );
                        banner = BAN_OPTIONAL;
                        *modp++ = 'b';

                /* handle banner=(always|optional|never) */
                } else if (STRNEQU(optarg, "banner=", 7)) {
                        char *ptr;

                        ptr = (optarg += 7);
                        if (banner != -1)
                                LP_ERRMSG1 ( WARNING, E_ADM_2MANY,
                                "banner/nobanner/banner=(always|optional|never)"
                                );

                        /* like "banner", always print a banner */
                        if (strcasecmp(ptr, "always") == 0)
                                banner = BAN_ALWAYS;
                        /* like "nobanner", print a banner unless requested */
                        if (strcasecmp(ptr, "optional") == 0)
                                banner = BAN_OPTIONAL;
                        /* never print a banner */
                        if (strcasecmp(ptr, "never") == 0)
                                banner = BAN_NEVER;
                        *modp++ = 'b';

                } else if (STRNEQU(optarg, "length=", 7)) {
                        if (length)
                                LP_ERRMSG1 (
                                        WARNING,
                                        E_ADM_2MANY,
                                        "length="
                                );
                        length = (optarg += 7);

                        if (!*optarg) {
                                length_sdn.val = 0;
                                length_sdn.sc = 0;

                        } else {
                                length_sdn = _getsdn(optarg, &optarg, 0);
                                if (errno == EINVAL) {
                                        LP_ERRMSG (ERROR, E_LP_BADSCALE);
                                        done (1);
                                }
                        }
                        *modp++ = 'L';

                } else if (STRNEQU(optarg, "width=", 6)) {
                        if (width)
                                LP_ERRMSG1 (
                                        WARNING,
                                        E_ADM_2MANY,
                                        "width="
                                );
                        width = (optarg += 6);

                        if (!*optarg) {
                                width_sdn.val = 0;
                                width_sdn.sc = 0;

                        } else {
                                width_sdn = _getsdn(optarg, &optarg, 0);
                                if (errno == EINVAL) {
                                        LP_ERRMSG (ERROR, E_LP_BADSCALE);
                                        done (1);
                                }
                        }
                        *modp++ = 'w';

                } else if (STRNEQU(optarg, "cpi=", 4)) {
                        if (cpi)
                                LP_ERRMSG1 (WARNING, E_ADM_2MANY, "cpi=");

                        cpi = (optarg += 4);

                        if (!*optarg) {
                                cpi_sdn.val = 0;
                                cpi_sdn.sc = 0;

                        } else {
                                cpi_sdn = _getsdn(optarg, &optarg, 1);
                                if (errno == EINVAL) {
                                        LP_ERRMSG (ERROR, E_LP_BADSCALE);
                                        done (1);
                                }
                        }
                        *modp++ = 'c';

                } else if (STRNEQU(optarg, "lpi=", 4)) {
                        if (lpi)
                                LP_ERRMSG1 (WARNING, E_ADM_2MANY, "lpi=");
                        lpi = (optarg += 4);

                        if (!*optarg) {
                                lpi_sdn.val = 0;
                                lpi_sdn.sc = 0;

                        } else {
                                lpi_sdn = _getsdn(optarg, &optarg, 0);
                                if (errno == EINVAL) {
                                        LP_ERRMSG (ERROR, E_LP_BADSCALE);
                                        done (1);
                                }
                        }
                        *modp++ = 'M';

                } else if (STRNEQU(optarg, "stty=", 5)) {

                        optarg += 5;
                        if (!*optarg)
                                stty_opt = 0;

                        else {
                                if (strchr(LP_QUOTES, *optarg)) {
                                        register int            len
                                                        = strlen(optarg);

                                        if (optarg[len - 1] == *optarg)
                                                optarg[len - 1] = 0;
                                        optarg++;
                                }
                                if (stty_opt)
                                        REALLOC (stty_opt);
                                else
                                        MALLOC (stty_opt);
                        }
                        *modp++ = 's';

                } else if (STREQU(optarg, "filebreak")) {
                        filebreak = 1;

                } else if (STREQU(optarg, "nofilebreak")) {
                        filebreak = 0;

                /* added support for using -o to pass any key=value pair */
                } else if (*optarg) {

                        if ((addlist(&o_options, optarg)) != 0) {
                                fprintf(stderr, gettext("System Error %d\n"), errno);
                        }

                        *modp++ = 'o';
                        optarg++;
                }

        return;
}