/* Copyright (C) 2008 Emmanuel Varoquaux This file is part of XOS. This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "getopt.h" #include #include #include /* modes */ enum { /* retourne toutes les options en permutant les arguments */ OM_PERMUTE, /* retourne toutes les options jusqu'au premier argument non-option */ OM_REQUIRE_ORDER, /* retourne tous les arguments comme des options */ OM_PRESERVE_ORDER }; int opterr __attribute__ ((weak, visibility ("default"))); /* non nul pour afficher les messages d'erreur */ /* position dans le tableau des arguments */ int optind __attribute__ ((weak, visibility ("default"))); /* indice de l'argument en cours d'analyse */ static int nextchar; /* indice du prochain caractere a analyser */ char *optarg __attribute__ ((weak, visibility ("default"))); /* argument de l'option */ int optopt __attribute__ ((weak, visibility ("default"))); /* caractere d'option courant */ void __getopt_init() { opterr = 1; optind = 1; nextchar = 0; } static inline int is_option(const char *arg) { return arg[0] == '-' && strcmp(arg, "-") && strcmp(arg, "--"); } static inline int is_long_option(const char *arg) { return arg[1] == '-'; } static void permute(char *argv[], int ind) { char *tmp; int i; tmp = argv[ind]; for (i = ind; i > optind; i--) argv[i] = argv[i - 1]; argv[optind] = tmp; } static const char *search_short_option(char short_opt_char, const char *optstring) { return strchr(optstring, short_opt_char); } static const struct option *search_long_option(const char *long_opt_str, const struct option *longopts) { const struct option *long_opt1, *long_opt2; size_t n; /* recherche d'une option dont l'argument est un prefixe */ long_opt1 = &longopts[0]; n = 0; while (1) { if (!long_opt1->name) return long_opt1; /* fin du tableau, pas d'option trouvee */ if (strncmp(long_opt_str, long_opt1->name, n)) long_opt1++; /* pas un prefixe, option suivante */ else { if (!long_opt_str[n] || long_opt_str[n] == '=') break; /* prefixe */ n++; } } /* recherche d'une option designee par l'argument */ if (!long_opt1->name[n]) return long_opt1; /* option trouvee */ long_opt2 = long_opt1 + 1; while (long_opt2->name) { if (!strncmp(long_opt_str, long_opt2->name, n)) { if (long_opt2->name[n]) return long_opt2; /* option trouvee */ else return NULL; /* ambiguite */ } long_opt2++; } return long_opt1; /* l'argument est une abbreviation de l'option */ } static int getopt_internal(int argc, char *argv[], const char *optstring, const struct option *longopts, int *longindex, int long_only) { int mode; int next_ind; char *arg; char short_opt_char; const char *short_opt; const char *long_opt_str; const struct option *long_opt; char *long_opt_arg; /* Appeler getopt avec optind == 0 peut etre utilise par les programmes pour provoquer la reinitialisation de getopt. */ if (!optind) { optind = 1; nextchar = 0; } if (optstring[0] == '+') { mode = OM_REQUIRE_ORDER; optstring++; } else if (optstring[0] == '-') { mode = OM_PRESERVE_ORDER; optstring++; } else mode = OM_PERMUTE; /* selection de la prochaine option */ next_ind = optind; if (!nextchar) { /* recherche de la prochaine option */ while (1) { if (next_ind == argc) return -1; /* plus d'option */ if (is_option(argv[next_ind])) { /* option trouvee */ if (next_ind != optind) permute(argv, next_ind); break; } if (!strcmp(argv[next_ind], "--")) { /* arret de la recherche d'option */ if (next_ind != optind) permute(argv, next_ind); optind++; return -1; } /* l'argument n'est pas une option - ce qu'il faut faire depend du mode */ switch (mode) { case OM_PERMUTE: next_ind++; continue; case OM_REQUIRE_ORDER: return -1; case OM_PRESERVE_ORDER: short_opt_char = 1; goto short_option_arg; } } if (longopts && is_long_option(argv[optind])) { /* option longue */ arg = argv[optind]; long_opt_str = arg + 2; if (!(long_opt = search_long_option(long_opt_str, longopts))) goto error_ambiguous_long_option; if (!long_opt->name) goto error_unrecognized_long_option; goto long_option; } nextchar = 1; } /* option introduite par "-" en cours d'analyse */ arg = argv[optind]; if (long_only) { /* recherche d'une option longue */ long_opt_str = arg + nextchar; if (!(long_opt = search_long_option(long_opt_str, longopts))) goto error_ambiguous_long_option; if (long_opt->name) goto long_option; /* recherche d'une option courte */ short_opt_char = arg[nextchar]; if ((short_opt = search_short_option(short_opt_char, optstring))) goto short_option; goto error_unrecognized_long_option; } else { /* option courte */ short_opt_char = arg[nextchar]; if (!(short_opt = search_short_option(short_opt_char, optstring))) goto error_illegal_short_option; goto short_option; } short_option: nextchar++; if (!arg[nextchar]) { next_ind++; optind++; nextchar = 0; } if (short_opt[1] == ':') { if (short_opt[2] == ':') { /* argument optionnel */ if (nextchar) { optarg = arg + nextchar; optind++; nextchar = 0; } else optarg = NULL; } else { /* argument requis */ if (nextchar) { optarg = arg + nextchar; optind++; nextchar = 0; } else { if (next_ind == argc) goto error_short_option_requires_argument; if (next_ind != optind) permute(argv, next_ind); short_option_arg: optarg = argv[optind]; optind++; } } } return short_opt_char; long_option: next_ind++; optind++; nextchar = 0; long_opt_arg = strchr(long_opt_str, '='); switch (long_opt->has_arg) { case no_argument: /* pas d'argument */ if (long_opt_arg) goto error_long_option_does_not_allow_argument; break; case required_argument: /* argument requis */ if (long_opt_arg) optarg = long_opt_arg + 1; else { if (next_ind == argc) goto error_long_option_requires_argument; if (next_ind != optind) permute(argv, next_ind); optarg = argv[optind]; optind++; } break; case optional_argument: /* argument optionnel */ if (long_opt_arg) optarg = long_opt_arg + 1; else optarg = NULL; break; } if (longindex) *longindex = long_opt - longopts; if (long_opt->flag) { *long_opt->flag = long_opt->val; return 0; } else return long_opt->val; error_illegal_short_option: nextchar++; if (!arg[nextchar]) { optind++; nextchar = 0; } optopt = short_opt_char; if (opterr) error(0, 0, "invalid option -- %c", short_opt_char); return '?'; error_short_option_requires_argument: optopt = short_opt_char; if (optstring[0] == ':') return ':'; else { if (opterr) error(0, 0, "option requires an argument -- %c", short_opt_char); return '?'; } error_ambiguous_long_option: optind++; nextchar = 0; optopt = 0; if (opterr) error(0, 0, "option `%s' is ambiguous", arg); return '?'; error_unrecognized_long_option: optind++; nextchar = 0; optopt = 0; if (opterr) error(0, 0, "unrecognized option `%s'", arg); return '?'; error_long_option_does_not_allow_argument: optopt = 0; if (opterr) error(0, 0, "option `%s%s' doesn't allow an argument", &"--"[arg[1] == '-' ? 0 : 1], long_opt->name); return '?'; error_long_option_requires_argument: optopt = 0; if (optstring[0] == ':') return ':'; else { if (opterr) error(0, 0, "option `%s' requires an argument", arg); return '?'; } } int __attribute__ ((weak, visibility ("default"))) getopt(int argc, char *argv[], const char *optstring) { return getopt_internal(argc, argv, optstring, NULL, NULL, 0); } int __attribute__ ((weak, visibility ("default"))) getopt_long(int argc, char *argv[], const char *optstring, const struct option *longopts, int *longindex) { return getopt_internal(argc, argv, optstring, longopts, longindex, 0); } int __attribute__ ((weak, visibility ("default"))) getopt_long_only(int argc, char *argv[], const char *optstring, const struct option *longopts, int *longindex) { return getopt_internal(argc, argv, optstring, longopts, longindex, 1); }