1#define _GNU_SOURCE
2#include <getopt.h>
3#include <stddef.h>
4#include <stdio.h>
5#include <string.h>
6
7extern int __optpos, __optreset;
8
9static 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
18void __getopt_msg(const char*, const char*, const char*, size_t);
19
20static int __getopt_long_core(int argc, char* const* argv, const char* optstring,
21                              const struct option* longopts, int* idx, int longonly);
22
23static 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
55static 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
123int 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
128int 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