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 
57 int 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 */
61 char *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. */
73 void
74 dwgetoptresetfortestingonly(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 
85 static const char *place = EMSG;/* option letter processing */
86 
87 
88 /*  Post Condition:
89     if return FALSE then *argerr is set false. */
90 static int
dwoptnamematches(const struct dwoption * dwlopt,const char * iplace,const char ** argloc,int * argerr)91 dwoptnamematches(
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 */
dwgetopt_long(int nargc,char * const nargv[],const char * ostr,const struct dwoption * longopts,int * longindex)156 int 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     */
286 int
dwgetopt(int nargc,char * const nargv[],const char * ostr)287 dwgetopt(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