1/* $NetBSD: getopt.c,v 1.1 2009/03/22 22:33:13 joerg Exp $*/
2/*  Modified by David Anderson to work with GNU/Linux and freebsd.
3    Added {} for clarity.
4    Switched to standard dwarfdump formatting.
5    Treatment of : modified so that :: gets dwoptarg NULL
6    if space follows the letter
7    (the dwoptarg is set to null).
8    renamed to make it clear this is a private version.
9    Oct 17 2017: Created dwgetopt_long(). See dwgetopt.h
10*/
11/*
12* Copyright (c) 1987, 1993, 1994
13* The Regents of the University of California.  All rights reserved.
14*
15* Redistribution and use in source and binary forms, with or without
16* modification, are permitted provided that the following conditions
17* are met:
18* 1. Redistributions of source code must retain the above copyright
19*    notice, this list of conditions and the following disclaimer.
20* 2. Redistributions in binary form must reproduce the above copyright
21*    notice, this list of conditions and the following disclaimer in the
22*    documentation and/or other materials provided with the distribution.
23* 3. Neither the name of the University nor the names of its contributors
24*    may be used to endorse or promote products derived from this software
25*    without specific prior written permission.
26*
27* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
28* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
29* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
30* ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
31* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
32* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
33* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
34* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
35* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
36* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
37* SUCH DAMAGE.
38*/
39
40/*  This does not presently handle the option string
41    leading + or leading - features. Such are not used
42    by by libdwarfdump.  Nor does it understand the
43    GNU Env var POSIXLY_CORRECT .
44    It does know of the leading ":" in the option string.
45    See BADCH below.
46    */
47
48#include <stdio.h>
49#ifdef HAVE_STDLIB_H
50#include <stdlib.h>
51#endif /* HAVE_STDLIB_H */
52#include <string.h> /* For strchr */
53#include "dwgetopt.h"
54
55#define STRIP_OFF_CONSTNESS(a)  ((void *)(size_t)(const void *)(a))
56
57int dwopterr = 1,    /* if error message should be printed */
58    dwoptind = 1,    /* index into parent argv vector */
59    dwoptopt,        /* character checked for validity */
60    dwoptreset;      /* reset getopt */
61char *dwoptarg;      /* argument associated with option */
62
63#define BADCH   (int)'?'
64#define BADARG  (int)':'
65#define EMSG    ""
66
67#define TRUE 1
68#define FALSE 0
69
70#if 0 /* FOR DEBUGGING ONLY */
71/*  Use for testing dwgetopt only.
72    Not a standard function. */
73void
74dwgetoptresetfortestingonly(void)
75{
76   dwopterr   = 1;
77   dwoptind   = 1;
78   dwoptopt   = 0;
79   dwoptreset = 0;
80   dwoptarg   = 0;
81}
82#endif /* FOR DEBUGGING ONLY */
83
84
85static const char *place = EMSG;/* option letter processing */
86
87
88/*  Post Condition:
89    if return FALSE then *argerr is set false. */
90static int
91dwoptnamematches(
92    const struct dwoption *dwlopt,
93    const char *iplace,
94    const char **argloc,
95    int *argerr)
96{
97
98    const char *eq = 0;
99    size_t namelen = 0;
100    size_t arglen = 0;
101    int d = 0;
102
103    for(eq = iplace; *eq; ++eq) {
104        if (*eq != '=') {
105            continue;
106        }
107        /* Found  =, arg should follow */
108        namelen = (eq - iplace);
109        if (namelen != (unsigned)strlen(dwlopt->name)) {
110            return FALSE;
111        }
112        eq++;
113        arglen = strlen(eq);
114        break;
115    }
116    if (namelen) {
117        d = strncmp(iplace,dwlopt->name,namelen);
118        if (d) {
119            return FALSE;
120        }
121        if (dwlopt->has_arg == 0) {
122            *argerr = TRUE;
123            return TRUE;
124        }
125        if (arglen) {
126            /*  Discarding const, avoiding warning.
127                Data is in user space, so this is ok. */
128            dwoptarg = (char *)eq;
129            *argloc = (const char *)eq;
130        } else {
131            /* Has arg = but arg is empty. */
132            dwoptarg = 0;
133        }
134        return TRUE;
135    } else {
136        d = strcmp(iplace,dwlopt->name);
137        if (d) {
138            return FALSE;
139        }
140        if (dwlopt->has_arg == 1) {
141            *argerr = TRUE;
142            return TRUE;
143        }
144        dwoptarg = 0;
145        return TRUE;
146    }
147}
148
149
150
151/*  dwgetopt_long
152    A reimplemenation of  a portion of
153    the getopt(3) GNU/Linux  getopt_long().
154    See dwgetopt.h for more details.
155*/
156int dwgetopt_long(int nargc, char *const nargv[],
157    const char *ostr,
158    const struct dwoption* longopts,
159    int *longindex)
160{
161    char *lplace = 0;
162    if (dwoptreset) {
163        /*  Not really supported. */
164        place = EMSG;
165        return (-1);
166    }
167    if (*place) {
168        int v = dwgetopt(nargc,nargv,ostr);
169        return v;
170    }
171    /*  Use local lplace in case we need to call getopt()
172        just below. */
173    lplace = nargv[dwoptind];
174    if (dwoptind >= nargc || *lplace++ != '-') {
175        /* Argument is absent or is not an option */
176        place = EMSG;
177        return (-1);
178    }
179    if  (*lplace  != '-') {
180        /* Notice place not  disturbed. */
181        int v = dwgetopt(nargc,nargv,ostr);
182        return v;
183    }
184    /*  Starts with two dashes.
185        Now we set the global place */
186    place = lplace+1;
187    if (!*place) {
188        /* "--" => end of options */
189        ++dwoptind;
190        place = EMSG;
191        return (-1);
192    }
193
194    /* We think this is a longopt. */
195    {
196    int lo_num = 0;
197
198    for(;;lo_num++) {
199        const struct dwoption *dwlopt = longopts +lo_num;
200        const char * argloc = 0;
201        int argerr = 0;
202        int resmatch = 0;
203
204        if (!dwlopt->name) {
205            dwoptind++;
206            (void)fprintf(stderr,
207                "%s: invalid long option '--%s'\n",
208                nargv[0]?nargv[0]:"",
209                place);
210            /* Leave longindex unchanged. */
211            place = EMSG;
212            return (BADCH);
213        }
214        resmatch= dwoptnamematches(dwlopt,place,
215            &argloc,&argerr);
216        if (resmatch) {
217            dwoptarg = 0;
218            if (argloc) {
219                /* Must drop const here. Ugh. */
220                dwoptarg = (char *)argloc;
221            }
222        }
223        if (argerr) {
224            /*  resmatch  == TRUE
225
226                arg option  missing if required, present
227                but not allowed.
228                GNU Behavior not well documented.
229                Had to experiment.
230
231                if argument-not-allowed, and we have one,
232                do ???
233
234                If argument-required,
235                then here GNU
236                would take the next argv as the argument.
237                we are not currently doing that. */
238            /**longindex = lo_num; */
239            if (dwlopt->has_arg) {
240                /*  Missing required arg, this does not
241                    match GNU getopt_long behavior
242                    of taking next argv as the arg value.
243                    and thus making getopt_long succeed. */
244                (void)fprintf(stderr,
245                    "%s: missing required long option argument '--%s'\n",
246                    nargv[0]?nargv[0]:"",
247                    place);
248            } else {
249                /* has arg but should not */
250                (void)fprintf(stderr,
251                    "%s: option '--%s' does not allow an argument\n",
252                    nargv[0]?nargv[0]:"",
253                    dwlopt->name);
254            }
255            dwoptind++;
256            place = EMSG;
257            return (BADCH);
258        }
259        if (resmatch) {
260            *longindex = lo_num;
261            place = EMSG;
262            dwoptind++;
263            return dwlopt->val;
264        }
265    }
266    /* Can never get here */
267    place = EMSG;
268    dwoptind++;
269    return (-1);
270    }
271}
272
273/*
274    * getopt --
275    * Parse argc/argv argument vector.
276    * a: means
277    *     -afoo
278    *     -a foo
279    *     and 'foo' is returned in dwoptarg
280    *  b:: means
281    *     -b
282    *        and dwoptarg is null
283    *     -bother
284    *        and dwoptarg is 'other'
285    */
286int
287dwgetopt(int nargc, char * const nargv[], const char *ostr)
288{
289    char *oli;                      /* option letter list index */
290
291    if (dwoptreset || *place == 0) { /* update scanning pointer */
292        dwoptreset = 0;
293        place = nargv[dwoptind];
294
295        if (dwoptind >= nargc || *place++ != '-') {
296            /* Argument is absent or is not an option */
297            place = EMSG;
298            return (-1);
299        }
300        dwoptopt = *place++;
301        if (dwoptopt == '-' && *place == 0) {
302            /* "--" => end of options */
303            ++dwoptind;
304            place = EMSG;
305            return (-1);
306        }
307        if (dwoptopt == 0) {
308            /* Solitary '-', treat as a '-' option
309                if the program (eg su) is looking for it. */
310            place = EMSG;
311            if (strchr(ostr, '-') == NULL) {
312                return -1;
313            }
314            dwoptopt = '-';
315        }
316    } else {
317        dwoptopt = *place++;
318    }
319    /* See if option letter is one the caller wanted... */
320    if (dwoptopt == ':' || (oli = strchr(ostr, dwoptopt)) == NULL) {
321        if (*place == 0) {
322            ++dwoptind;
323        }
324        if (dwopterr && *ostr != ':') {
325            (void)fprintf(stderr,
326                "%s: invalid option -- '%c'\n",
327                nargv[0]?nargv[0]:"",
328                dwoptopt);
329        }
330        return (BADCH);
331    }
332
333    /* Does this option need an argument? */
334    if (oli[1] != ':') {
335        /* don't need argument */
336        dwoptarg = NULL;
337        if (*place == 0) {
338            ++dwoptind;
339        }
340    } else {
341        int reqnextarg = 1;
342        if (oli[1] && (oli[2] == ':')) {
343            /* Pair of :: means special treatment of dwoptarg */
344            reqnextarg = 0;
345        }
346        /* Option-argument is either the rest of this argument or the
347        entire next argument. */
348        if (*place ) {
349            /* Whether : or :: */
350            dwoptarg = STRIP_OFF_CONSTNESS(place);
351        } else if (reqnextarg) {
352            /* ! *place */
353            if (nargc > (++dwoptind)) {
354                dwoptarg = nargv[dwoptind];
355            } else {
356                place=EMSG;
357                /*  Next arg required, but is missing */
358                if (*ostr == ':') {
359                    /* Leading : in ostr calls for BADARG return. */
360                    return (BADARG);
361                }
362                if (dwopterr) {
363                    (void)fprintf(stderr,
364                        "%s: option requires an argument. -- '%c'\n",
365                        nargv[0]?nargv[0]:"",
366                        dwoptopt);
367                }
368                return (BADCH);
369            }
370        } else {
371            /* ! *place */
372            /* The key part of :: treatment. */
373            dwoptarg = NULL;
374        }
375        place = EMSG;
376        ++dwoptind;
377    }
378    return (dwoptopt);  /* return option letter */
379}
380