1 #define _GNU_SOURCE
2 #include <getopt.h>
3 #include <stddef.h>
4 #include <stdio.h>
5 #include <string.h>
6 
7 extern int __optpos, __optreset;
8 
permute(char * const * argv,int dest,int src)9 static void permute(char* const* argv, int dest, int src) {
10     char** av = (char**)argv;
11     char* tmp = av[src];
12     int i;
13     for (i = src; i > dest; i--)
14         av[i] = av[i - 1];
15     av[dest] = tmp;
16 }
17 
18 void __getopt_msg(const char*, const char*, const char*, size_t);
19 
20 static int __getopt_long_core(int argc, char* const* argv, const char* optstring,
21                               const struct option* longopts, int* idx, int longonly);
22 
__getopt_long(int argc,char * const * argv,const char * optstring,const struct option * longopts,int * idx,int longonly)23 static int __getopt_long(int argc, char* const* argv, const char* optstring,
24                          const struct option* longopts, int* idx, int longonly) {
25     int ret, skipped, resumed;
26     if (!optind || __optreset) {
27         __optreset = 0;
28         __optpos = 0;
29         optind = 1;
30     }
31     if (optind >= argc || !argv[optind])
32         return -1;
33     skipped = optind;
34     if (optstring[0] != '+' && optstring[0] != '-') {
35         int i;
36         for (i = optind;; i++) {
37             if (i >= argc || !argv[i])
38                 return -1;
39             if (argv[i][0] == '-' && argv[i][1])
40                 break;
41         }
42         optind = i;
43     }
44     resumed = optind;
45     ret = __getopt_long_core(argc, argv, optstring, longopts, idx, longonly);
46     if (resumed > skipped) {
47         int i, cnt = optind - resumed;
48         for (i = 0; i < cnt; i++)
49             permute(argv, skipped, optind - 1);
50         optind = skipped + cnt;
51     }
52     return ret;
53 }
54 
__getopt_long_core(int argc,char * const * argv,const char * optstring,const struct option * longopts,int * idx,int longonly)55 static int __getopt_long_core(int argc, char* const* argv, const char* optstring,
56                               const struct option* longopts, int* idx, int longonly) {
57     optarg = 0;
58     if (longopts && argv[optind][0] == '-' &&
59         ((longonly && argv[optind][1]) || (argv[optind][1] == '-' && argv[optind][2]))) {
60         int colon = optstring[optstring[0] == '+' || optstring[0] == '-'] == ':';
61         int i, cnt, match;
62         char* opt;
63         for (cnt = i = 0; longopts[i].name; i++) {
64             const char* name = longopts[i].name;
65             opt = argv[optind] + 1;
66             if (*opt == '-')
67                 opt++;
68             for (; *name && *name == *opt; name++, opt++)
69                 ;
70             if (*opt && *opt != '=')
71                 continue;
72             match = i;
73             if (!*name) {
74                 cnt = 1;
75                 break;
76             }
77             cnt++;
78         }
79         if (cnt == 1) {
80             i = match;
81             optind++;
82             optopt = longopts[i].val;
83             if (*opt == '=') {
84                 if (!longopts[i].has_arg) {
85                     if (colon || !opterr)
86                         return '?';
87                     __getopt_msg(argv[0], ": option does not take an argument: ", longopts[i].name,
88                                  strlen(longopts[i].name));
89                     return '?';
90                 }
91                 optarg = opt + 1;
92             } else if (longopts[i].has_arg == required_argument) {
93                 if (!(optarg = argv[optind])) {
94                     if (colon)
95                         return ':';
96                     if (!opterr)
97                         return '?';
98                     __getopt_msg(argv[0], ": option requires an argument: ", longopts[i].name,
99                                  strlen(longopts[i].name));
100                     return '?';
101                 }
102                 optind++;
103             }
104             if (idx)
105                 *idx = i;
106             if (longopts[i].flag) {
107                 *longopts[i].flag = longopts[i].val;
108                 return 0;
109             }
110             return longopts[i].val;
111         }
112         if (argv[optind][1] == '-') {
113             if (!colon && opterr)
114                 __getopt_msg(argv[0], cnt ? ": option is ambiguous: " : ": unrecognized option: ",
115                              argv[optind] + 2, strlen(argv[optind] + 2));
116             optind++;
117             return '?';
118         }
119     }
120     return getopt(argc, argv, optstring);
121 }
122 
getopt_long(int argc,char * const * argv,const char * optstring,const struct option * longopts,int * idx)123 int getopt_long(int argc, char* const* argv, const char* optstring, const struct option* longopts,
124                 int* idx) {
125     return __getopt_long(argc, argv, optstring, longopts, idx, 0);
126 }
127 
getopt_long_only(int argc,char * const * argv,const char * optstring,const struct option * longopts,int * idx)128 int getopt_long_only(int argc, char* const* argv, const char* optstring,
129                      const struct option* longopts, int* idx) {
130     return __getopt_long(argc, argv, optstring, longopts, idx, 1);
131 }
132