1 /* Copyright  (C) 2010-2018 The RetroArch team
2  *
3  * ---------------------------------------------------------------------------------------
4  * The following license statement only applies to this file (compat_getopt.c).
5  * ---------------------------------------------------------------------------------------
6  *
7  * Permission is hereby granted, free of charge,
8  * to any person obtaining a copy of this software and associated documentation files (the "Software"),
9  * to deal in the Software without restriction, including without limitation the rights to
10  * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
11  * and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
12  *
13  * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
14  *
15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
16  * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
18  * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
19  * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21  */
22 
23 #include <stdio.h>
24 #include <ctype.h>
25 
26 #include <string.h>
27 #include <boolean.h>
28 #include <stddef.h>
29 #include <stdlib.h>
30 
31 #include <retro_miscellaneous.h>
32 
33 #include <compat/getopt.h>
34 #include <compat/strl.h>
35 #include <compat/strcasestr.h>
36 #include <compat/posix_string.h>
37 
38 #include <retro_assert.h>
39 
40 char *optarg;
41 int optind, opterr, optopt;
42 
is_short_option(const char * str)43 static bool is_short_option(const char *str)
44 {
45    return str[0] == '-' && str[1] != '-';
46 }
47 
is_long_option(const char * str)48 static bool is_long_option(const char *str)
49 {
50    return str[0] == '-' && str[1] == '-';
51 }
52 
find_short_index(char * const * argv)53 static int find_short_index(char * const *argv)
54 {
55    int idx;
56    for (idx = 0; argv[idx]; idx++)
57    {
58       if (is_short_option(argv[idx]))
59          return idx;
60    }
61 
62    return -1;
63 }
64 
find_long_index(char * const * argv)65 static int find_long_index(char * const *argv)
66 {
67    int idx;
68    for (idx = 0; argv[idx]; idx++)
69    {
70       if (is_long_option(argv[idx]))
71          return idx;
72    }
73 
74    return -1;
75 }
76 
parse_short(const char * optstring,char * const * argv)77 static int parse_short(const char *optstring, char * const *argv)
78 {
79    bool extra_opt, takes_arg, embedded_arg;
80    const char *opt = NULL;
81    char        arg = argv[0][1];
82 
83    if (arg == ':')
84       return '?';
85 
86    opt = strchr(optstring, arg);
87    if (!opt)
88       return '?';
89 
90    extra_opt = argv[0][2];
91    takes_arg = opt[1] == ':';
92 
93    /* If we take an argument, and we see additional characters,
94     * this is in fact the argument (i.e. -cfoo is same as -c foo). */
95    embedded_arg = extra_opt && takes_arg;
96 
97    if (takes_arg)
98    {
99       if (embedded_arg)
100       {
101          optarg = argv[0] + 2;
102          optind++;
103       }
104       else
105       {
106          optarg = argv[1];
107          optind += 2;
108       }
109 
110       return optarg ? opt[0] : '?';
111    }
112 
113    if (embedded_arg)
114    {
115       /* If we see additional characters,
116        * and they don't take arguments, this
117        * means we have multiple flags in one. */
118       memmove(&argv[0][1], &argv[0][2], strlen(&argv[0][2]) + 1);
119       return opt[0];
120    }
121 
122    optind++;
123    return opt[0];
124 }
125 
parse_long(const struct option * longopts,char * const * argv)126 static int parse_long(const struct option *longopts, char * const *argv)
127 {
128    size_t indice;
129    const struct option *opt = NULL;
130    for (indice = 0; longopts[indice].name; indice++)
131    {
132       if (!strcmp(longopts[indice].name, &argv[0][2]))
133       {
134          opt = &longopts[indice];
135          break;
136       }
137    }
138 
139    if (!opt)
140       return '?';
141 
142    /* getopt_long has an "optional" arg, but we don't bother with that. */
143    if (opt->has_arg && !argv[1])
144       return '?';
145 
146    if (opt->has_arg)
147    {
148       optarg = argv[1];
149       optind += 2;
150    }
151    else
152       optind++;
153 
154    if (opt->flag)
155    {
156       *opt->flag = opt->val;
157       return 0;
158    }
159 
160    return opt->val;
161 }
162 
shuffle_block(char ** begin,char ** last,char ** end)163 static void shuffle_block(char **begin, char **last, char **end)
164 {
165    ptrdiff_t    len = last - begin;
166    const char **tmp = (const char**)calloc(len, sizeof(const char*));
167 
168    retro_assert(tmp);
169 
170    memcpy((void*)tmp, begin, len * sizeof(const char*));
171    memmove(begin, last, (end - last) * sizeof(const char*));
172    memcpy(end - len, tmp, len * sizeof(const char*));
173 
174    free((void*)tmp);
175 }
176 
getopt_long(int argc,char * argv[],const char * optstring,const struct option * longopts,int * longindex)177 int getopt_long(int argc, char *argv[],
178       const char *optstring, const struct option *longopts, int *longindex)
179 {
180    int short_index, long_index;
181 
182    (void)longindex;
183 
184    if (optind == 0)
185       optind = 1;
186 
187    if (argc < 2)
188       return -1;
189 
190    short_index = find_short_index(&argv[optind]);
191    long_index  = find_long_index(&argv[optind]);
192 
193    /* We're done here. */
194    if (short_index == -1 && long_index == -1)
195       return -1;
196 
197    /* Reorder argv so that non-options come last.
198     * Non-POSIXy, but that's what getopt does by default. */
199    if ((short_index > 0) && ((short_index < long_index) || (long_index == -1)))
200    {
201       shuffle_block(&argv[optind], &argv[optind + short_index], &argv[argc]);
202       short_index = 0;
203    }
204    else if ((long_index > 0) && ((long_index < short_index)
205             || (short_index == -1)))
206    {
207       shuffle_block(&argv[optind], &argv[optind + long_index], &argv[argc]);
208       long_index = 0;
209    }
210 
211    retro_assert(short_index == 0 || long_index == 0);
212 
213    if (short_index == 0)
214       return parse_short(optstring, &argv[optind]);
215    if (long_index == 0)
216       return parse_long(longopts, &argv[optind]);
217 
218    return '?';
219 }
220