1/*
2 * Copyright (c) 2010 Apple Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 *
10 * 1.  Redistributions of source code must retain the above copyright
11 *     notice, this list of conditions and the following disclaimer.
12 * 2.  Redistributions in binary form must reproduce the above copyright
13 *     notice, this list of conditions and the following disclaimer in the
14 *     documentation and/or other materials provided with the distribution.
15 * 3.  Neither the name of Apple Inc. ("Apple") nor the names of its
16 *     contributors may be used to endorse or promote products derived from
17 *     this software without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
20 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
21 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
23 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
24 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
25 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
26 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
28 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 *
30 * Portions of this software have been released under the following terms:
31 *
32 * (c) Copyright 1989-1993 OPEN SOFTWARE FOUNDATION, INC.
33 * (c) Copyright 1989-1993 HEWLETT-PACKARD COMPANY
34 * (c) Copyright 1989-1993 DIGITAL EQUIPMENT CORPORATION
35 *
36 * To anyone who acknowledges that this file is provided "AS IS"
37 * without any express or implied warranty:
38 * permission to use, copy, modify, and distribute this file for any
39 * purpose is hereby granted without fee, provided that the above
40 * copyright notices and this notice appears in all source code copies,
41 * and that none of the names of Open Software Foundation, Inc., Hewlett-
42 * Packard Company or Digital Equipment Corporation be used
43 * in advertising or publicity pertaining to distribution of the software
44 * without specific, written prior permission.  Neither Open Software
45 * Foundation, Inc., Hewlett-Packard Company nor Digital
46 * Equipment Corporation makes any representations about the suitability
47 * of this software for any purpose.
48 *
49 * Copyright (c) 2007, Novell, Inc. All rights reserved.
50 * Redistribution and use in source and binary forms, with or without
51 * modification, are permitted provided that the following conditions
52 * are met:
53 *
54 * 1.  Redistributions of source code must retain the above copyright
55 *     notice, this list of conditions and the following disclaimer.
56 * 2.  Redistributions in binary form must reproduce the above copyright
57 *     notice, this list of conditions and the following disclaimer in the
58 *     documentation and/or other materials provided with the distribution.
59 * 3.  Neither the name of Novell Inc. nor the names of its contributors
60 *     may be used to endorse or promote products derived from this
61 *     this software without specific prior written permission.
62 *
63 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
64 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
65 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
66 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY
67 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
68 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
69 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
70 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
71 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
72 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
73 *
74 * @APPLE_LICENSE_HEADER_END@
75 */
76
77/*
78**
79**  NAME
80**
81**      GETFLAGS.C
82**
83**  FACILITY:
84**
85**      Interface Definition Language (IDL) Compiler
86**
87**  ABSTRACT:
88**
89**      Command Line Parser
90**
91**  VERSION: DCE 1.0
92**
93*/
94
95#include <ctype.h>
96
97#include <nidl.h>
98#include <getflags.h>
99#include <command.h>
100#include <driver.h>
101#include <message.h>
102
103#define NFLAGS 128
104static unsigned char option_count[NFLAGS]    = {0};
105static int           other_count             = 0;
106static char          *(other_flags[NFLAGS])  = {0};
107
108char *last_string;           /* Last string parsed, for disambiguating */
109
110/*
111 *  flags_option_count: Returns #occurences of option on command line.
112 */
113int flags_option_count
114(
115    const OPTIONS table[],
116    const char *option
117)
118{
119    int o;
120
121    if (*option == '-')
122        option++;
123    for (o = 0; table[o].option && o < NFLAGS; o++)
124    {
125        if (strcmp(option, table[o].option) == 0)
126            return((int)option_count[o]);
127    }
128    return(-1);
129}
130
131/*
132 *  flags_incr_count: Increments command option count by specified amount.
133 */
134void flags_incr_count
135(
136    const OPTIONS table[],
137    const char *option,
138    int delta
139)
140{
141    int o;
142
143    if (*option == '-')
144        option++;
145    for (o = 0; table[o].option && o < NFLAGS; o++)
146    {
147        if (strlen(option) != strlen(table[o].option))
148            continue;
149        if (strcmp(option, table[o].option) == 0)
150        {
151            option_count[o] += delta;
152            return;
153        }
154    }
155}
156
157/*
158 *  flags_other_count: Returns count of command line parameters that are not
159 *                     part of command line options.
160 */
161int flags_other_count
162(
163    void
164)
165{
166    return(other_count);
167}
168
169/*
170 *  flags_other: Returns the Nth command line parameter that is not an option.
171 */
172char *flags_other
173(
174    int index
175)
176{
177    if (0 <= index && index < other_count)
178        return(other_flags[index]);
179    else
180        return(NULL);
181}
182
183/*
184 *  is_number: Returns true if argument consists only of ASCII "0" thru "9"
185 *             with optional leading "+" or "-".
186 */
187
188boolean is_number
189(
190    char *str
191)
192{
193    if (*str == '+' || *str == '-')
194        str++;
195
196    for ( ; *str != '\0' ; str++)
197        if (!isdigit((int)*str))
198            return false;
199
200    return true;
201}
202
203/*
204 *  getflags: Parses command parameters and options.
205 */
206void getflags
207(
208    int ac,
209    char **av,
210    const OPTIONS table[]
211)
212{
213    int             o;
214    int             optlen;
215    int             nflags, type;
216    int             vflag;
217    boolean         optval;
218    register char   **pstring;
219    register char   *pchar;
220    register int    *pint;
221    register char   *flag = NULL;
222    register long   *plong;
223    register double *pfloat;
224
225    last_string = NULL;
226
227    while (ac > 0)
228    {
229    thisf:
230        for (o = 0;  table[o].option;  o++)
231        {
232            flag = *av;
233            if (flag[0] == '-')
234                flag++;
235
236            if (strlen(flag) != strlen(table[o].option))
237                continue;
238
239            if (strcmp(flag, table[o].option) == 0)
240            {
241                optval = false;     /* This is not OptVal with no white space */
242            matchf:
243                nflags = (table[o].ftype >> 8) & 0xFF;
244                vflag = nflags & VARARGFLAG;
245                nflags &= MULTARGMASK;
246                if (nflags <= 0)
247                    nflags = 1;
248                type = table[o].ftype & 0xFF;
249
250                switch (type)
251                {
252                default:
253                    INTERNAL_ERROR("Illegal option type");
254
255                case INTARG:
256                    pint = (int *)table[o].dest;
257                    if (vflag)
258                        pint += option_count[o];
259                    /*
260                     * Replacing "if (nflags" with "while (nflags--" allows
261                     * lists such as -bug 1 2, but makes parameter determination
262                     * ambiguous.  As it stands, -bug 1 -bug 2 must be used.
263                     */
264                    if (nflags && (ac > 1))
265                    {
266                        if (is_number(av[1]))
267                        {
268                            GETINT(*pint++);
269                        }
270                        else
271                            goto nextf;
272                        if (ac > 0 && vflag && **av == '-') goto thisf;
273                        option_count[o]++;
274                    }
275                    goto nextf;
276
277                case STRARG:
278                    pstring = (char **)table[o].dest;
279
280                    if (vflag)
281                        pstring += option_count[o];
282                    /*
283                     * Do the following statement even if no more values on the
284                     * command line, so caller can later determine, if desired,
285                     * that a required value was not supplied (option_count[o]
286                     * != 0 but option value left at caller's initialization.
287                     */
288                    option_count[o]++;
289                    /*
290                     * Replacing "if (nflags" with "while (nflags--" allows
291                     * lists like -D foo bar, but makes parameter determination
292                     * ambiguous.  As it stands, -D foo -D bar must be used.
293                     */
294                    if (nflags && (ac > 1))
295                    {
296                        GETSTR(*pstring);
297                        if (ac > 0 && vflag && **av == '-')
298                        {
299                            *pstring = NULL;
300                            goto thisf;
301                        }
302                        /** Add pstring++; for while loop version **/
303                    }
304                    goto nextf;
305
306                case OSTRARG:
307                    /* Similar to STRARG, but allows for optional string arg. */
308                    pstring = (char **)table[o].dest;
309
310                    /*
311                     * Allow the string argument to be optional.
312                     */
313                    if (!optval)
314                    {
315                        if (ac == 1 || (ac > 1 && *av[1] == '-'))
316                        {
317                            *pstring = (char*) "";
318                            goto nextf;
319                        }
320                    }
321
322                    if (vflag)
323                        pstring += option_count[o];
324                    /*
325                     * Replacing "if (nflags" with "while (nflags--" allows
326                     * lists like -D foo bar, but makes parameter determination
327                     * ambiguous.  As it stands, -D foo -D bar must be used.
328                     */
329                    if (nflags && (ac > 1))
330                    {
331                        GETSTR(*pstring);
332                        if (ac > 0 && vflag && **av == '-')
333                        {
334                            *pstring = NULL;
335                            goto thisf;
336                        }
337                        /*
338                         * Save pointer to this string, so caller can use to
339                         * disambiguate ambiguous syntax.
340                         */
341                        last_string = *pstring;
342                        /** Add pstring++; for while loop version **/
343                        option_count[o]++;
344                    }
345                    goto nextf;
346
347                case TOGGLEARG:
348                    pchar = (char *)table[o].dest;
349                    *pchar = ~*pchar;
350                    goto nextf;
351
352                case ASSERTARG:
353                    pchar = (char *)table[o].dest;
354                    *pchar = true;
355                    goto nextf;
356
357                case DENYARG:
358                    pchar = (char *)table[o].dest;
359                    *pchar = false;
360                    goto nextf;
361
362                case CHRARG:
363                    pchar = (char *)table[o].dest;
364                    if (vflag)
365                        pchar += option_count[o];
366                    /*
367                     * Replacing "if (nflags" with "while (nflags--" allows
368                     * lists such as -opt a b, but makes parameter determination
369                     * ambiguous.  As it stands, -opt a -opt b must be used.
370                     */
371                    if (nflags && (ac > 1))
372                    {
373                        GETCH(*pchar++);
374                        if (ac > 0 && vflag && **av == '-') goto thisf;
375                        option_count[o]++;
376                    }
377                    goto nextf;
378
379                case FLTARG:
380                    pfloat = (double *)table[o].dest;
381                    if (vflag)
382                        pfloat += option_count[o];
383                    /*
384                     * Replacing "if (nflags" with "while (nflags--" allows
385                     * lists like -f 1.1 2.2, but makes parameter determination
386                     * ambiguous.  As it stands, -f 1.1 -f 2.2 must be used.
387                     */
388                    if (nflags && (ac > 1))
389                    {
390                        GETFLT(*pfloat++);
391                        if (ac > 0 && vflag && **av == '-') goto thisf;
392                        option_count[o]++;
393                    }
394                    goto nextf;
395
396                case LONGARG:
397                    plong = (long *)table[o].dest;
398                    if (vflag)
399                        plong += option_count[o];
400                    /*
401                     * Replacing "if (nflags" with "while (nflags--" allows
402                     * lists such as -bug 1 2, but makes parameter determination
403                     * ambiguous.  As it stands, -bug 1 -bug 2 must be used.
404                     */
405                    if (nflags && (ac > 1))
406                    {
407                        if (is_number(av[1]))
408                        {
409                            GETLONG(*plong++);
410                        }
411                        else
412                            goto nextf;
413                        if (ac > 0 && vflag && **av == '-') goto thisf;
414                        option_count[o]++;
415                    }
416                    goto nextf;
417                }
418            }
419        }
420
421        if (**av == '-')
422        {
423            /*
424             * Check for the case of -OptVal, i.e. where the option name and
425             * its value are not separated by white space.  This code isn't
426             * pretty.  So horrendous code promotes horrendous code!
427             */
428            for (o = 0;  table[o].option;  o++)
429            {
430                optlen = strlen(table[o].option);
431                if (strncmp(flag, table[o].option, optlen) == 0)
432                {
433                    /*
434                     * If an option that's not supposed to take a value, then
435                     * issue error and exit.
436                     */
437                    type = table[o].ftype & 0xFF;
438                    if (type==TOGGLEARG || type==ASSERTARG || type==DENYARG)
439                    {
440                        message_print(NIDL_OPTNOVAL, table[o].option);
441                        CMD_explain_args();
442                        exit(pgm_error);
443                    }
444
445                    /*
446                     * Modify the argv entry to be just Val instead of -OptVal.
447                     */
448                    optval = true;      /* Parsed -OptVal with no white space */
449                    *av += optlen+1;    /* Point argptr past -Opt part */
450                    /*
451                     * Fake out the code above as if -Opt and Val are two
452                     * separate entries.  In reality, Val is now a separate
453                     * entry and -Opt has been destroyed (no longer needed).
454                     */
455                    ac++;
456                    av--;
457                    goto matchf;
458                }
459            }
460
461            /*
462             * Unknown option.
463             */
464            message_print(NIDL_UNKFLAG, *av);
465            CMD_explain_args();
466            exit(pgm_error);
467        }
468        else
469        {
470            other_flags[other_count++] = *av;
471        }
472
473    nextf:
474        ac--;
475        av++;
476    }
477}
478
479/*
480 * printflags: Prints list of command options and values to stderr.
481 */
482
483#define yes_no(x) (x? "Yes" : "No")
484#define no_yes(x) (x? "No" : "Yes")
485
486void printflags
487(
488    const OPTIONS table[]
489)
490{
491    register int    o;
492    register int    nflags;
493    register int    type;
494    int             vflag;
495    int             *pint;
496    char            *pchar;
497    char            **pstring;
498    long            *plong;
499    double          *pdouble;
500    unsigned int             option_len;
501
502    option_len = 0;
503
504    for (o = 0; table[o].option; o++)
505        if (strlen(table[o].option) > option_len)
506            option_len = strlen(table[o].option);
507
508    option_len += 3;
509
510    message_print(NIDL_OPTIONSTABLE);
511    for (o = 0;  table[o].option;  o++)
512    {
513        type = table[o].ftype;
514        if (type & HIDARG) continue;
515        nflags = (type >> 8) & 0xFF;
516        vflag = nflags & VARARGFLAG;
517
518        if (vflag)
519            nflags = option_count[o];
520
521        type &= 255;
522        fprintf(stderr, "    %-*s", option_len, table[o].option);
523
524        if (!vflag && nflags <= 0)
525            nflags = 1;
526
527        switch (type)
528        {
529        default:
530            fprintf(stderr, "\tillegal option in printflags: %d\n",
531                table[o].ftype);
532            exit(pgm_error);
533
534        case INTARG:
535            pint = (int *)table[o].dest;
536            while (nflags-- > 0)
537                fprintf(stderr, "\t%d", *pint++);
538            fprintf(stderr, "\n");
539            break;
540
541        case STRARG:
542        case OSTRARG:
543            pstring = (char **)table[o].dest;
544            while (nflags-- > -0)
545                fprintf(stderr, "\t%s", *pstring++);
546            fprintf(stderr, "\n");
547            break;
548
549        case TOGGLEARG:
550        case ASSERTARG:
551            pchar = (char *)table[o].dest;
552            while (nflags-- > 0)
553                fprintf(stderr, "\t%s", yes_no(*pchar++));
554            fprintf(stderr, "\n");
555            break;
556
557        case DENYARG:
558            pchar = (char *)table[o].dest;
559            while (nflags-- > 0)
560                fprintf(stderr, "\t%s", no_yes(*pchar++));
561            fprintf(stderr, "\n");
562            break;
563
564        case CHRARG:
565            pchar = (char *)table[o].dest;
566            while (nflags-- > 0)
567                fprintf(stderr, "\t%c", *pchar++);
568            fprintf(stderr, "\n");
569            break;
570
571        case FLTARG:
572            pdouble = (double *)table[o].dest;
573            while (nflags-- > 0)
574                fprintf(stderr, "\t%.3f", *pdouble++);
575            fprintf(stderr, "\n");
576            break;
577
578        case LONGARG:
579            plong = (long *)table[o].dest;
580            while (nflags-- > 0)
581                fprintf(stderr, "\t%ld", *plong++);
582            fprintf(stderr, "\n");
583            break;
584        }
585    }
586
587    fprintf(stderr, "\n");
588}
589