| #define _GNU_SOURCE |
| #include <stddef.h> |
| #include <stdlib.h> |
| #include <limits.h> |
| #include <getopt.h> |
| #include <stdio.h> |
| #include <string.h> |
| #include "stdio_impl.h" |
| |
| extern int __optpos, __optreset; |
| |
| int __posix_getopt(int argc, char * const argv[], const char *optstring); |
| |
| static void permute(char *const *argv, int dest, int src) |
| { |
| char **av = (char **)argv; |
| char *tmp = av[src]; |
| int i; |
| for (i=src; i>dest; i--) |
| av[i] = av[i-1]; |
| av[dest] = tmp; |
| } |
| |
| static int __getopt_long_core(int argc, char *const *argv, const char *optstring, const struct option *longopts, int *idx, int longonly); |
| |
| static int __getopt_long(int argc, char *const *argv, const char *optstring, const struct option *longopts, int *idx, int longonly) |
| { |
| int ret, skipped, resumed; |
| if (!optind || __optreset) { |
| __optreset = 0; |
| __optpos = 0; |
| optind = 1; |
| } |
| if (optind >= argc || !argv[optind]) return -1; |
| skipped = optind; |
| if (optstring[0] != '+' && optstring[0] != '-') { |
| int i; |
| for (i=optind; ; i++) { |
| if (i >= argc || !argv[i]) return -1; |
| if (argv[i][0] == '-' && argv[i][1]) break; |
| } |
| optind = i; |
| } |
| resumed = optind; |
| ret = __getopt_long_core(argc, argv, optstring, longopts, idx, longonly); |
| if (resumed > skipped) { |
| int i, cnt = optind-resumed; |
| for (i=0; i<cnt; i++) |
| permute(argv, skipped, optind-1); |
| optind = skipped + cnt; |
| } |
| return ret; |
| } |
| |
| static int __getopt_long_core(int argc, char *const *argv, const char *optstring, const struct option *longopts, int *idx, int longonly) |
| { |
| optarg = 0; |
| if (longopts && argv[optind][0] == '-' && |
| ((longonly && argv[optind][1] && argv[optind][1] != '-') || |
| (argv[optind][1] == '-' && argv[optind][2]))) |
| { |
| int colon = optstring[optstring[0]=='+'||optstring[0]=='-']==':'; |
| int i, cnt, match; |
| char *arg, *opt, *start = argv[optind]+1; |
| for (cnt=i=0; longopts[i].name; i++) { |
| const char *name = longopts[i].name; |
| opt = start; |
| if (*opt == '-') opt++; |
| while (*opt && *opt != '=' && *opt == *name) |
| name++, opt++; |
| if (*opt && *opt != '=') continue; |
| arg = opt; |
| match = i; |
| if (!*name) { |
| cnt = 1; |
| break; |
| } |
| cnt++; |
| } |
| if (cnt==1 && longonly && arg-start == mblen(start, MB_LEN_MAX)) { |
| int l = arg-start; |
| for (i=0; optstring[i]; i++) { |
| int j; |
| for (j=0; j<l && start[j]==optstring[i+j]; j++); |
| if (j==l) { |
| cnt++; |
| break; |
| } |
| } |
| } |
| if (cnt==1) { |
| i = match; |
| opt = arg; |
| optind++; |
| if (*opt == '=') { |
| if (!longopts[i].has_arg) { |
| optopt = longopts[i].val; |
| if (colon || !opterr) |
| return '?'; |
| __getopt_msg(argv[0], |
| ": option does not take an argument: ", |
| longopts[i].name, |
| strlen(longopts[i].name)); |
| return '?'; |
| } |
| optarg = opt+1; |
| } else if (longopts[i].has_arg == required_argument) { |
| if (!(optarg = argv[optind])) { |
| optopt = longopts[i].val; |
| if (colon) return ':'; |
| if (!opterr) return '?'; |
| __getopt_msg(argv[0], |
| ": option requires an argument: ", |
| longopts[i].name, |
| strlen(longopts[i].name)); |
| return '?'; |
| } |
| optind++; |
| } |
| if (idx) *idx = i; |
| if (longopts[i].flag) { |
| *longopts[i].flag = longopts[i].val; |
| return 0; |
| } |
| return longopts[i].val; |
| } |
| if (argv[optind][1] == '-') { |
| optopt = 0; |
| if (!colon && opterr) |
| __getopt_msg(argv[0], cnt ? |
| ": option is ambiguous: " : |
| ": unrecognized option: ", |
| argv[optind]+2, |
| strlen(argv[optind]+2)); |
| optind++; |
| return '?'; |
| } |
| } |
| return __posix_getopt(argc, argv, optstring); |
| } |
| |
| int getopt_long(int argc, char *const *argv, const char *optstring, const struct option *longopts, int *idx) |
| { |
| return __getopt_long(argc, argv, optstring, longopts, idx, 0); |
| } |
| |
| int getopt_long_only(int argc, char *const *argv, const char *optstring, const struct option *longopts, int *idx) |
| { |
| return __getopt_long(argc, argv, optstring, longopts, idx, 1); |
| } |
| |
| /* ANDROID CHANGE: implement getopt via getopt_long to continue parsing options |
| * after the first non-option argument to match the user visible behavior of |
| * glibc. |
| */ |
| int getopt(int argc, char * const argv[], const char *optstring) |
| { |
| static int posixly_correct = -1; |
| |
| if (posixly_correct == -1 || __optreset) |
| posixly_correct = (getenv("POSIXLY_CORRECT") != NULL); |
| |
| if (posixly_correct) |
| return __posix_getopt(argc, argv, optstring); |
| else |
| return __getopt_long(argc, argv, optstring, NULL, NULL, 0); |
| } |