/* 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);
}