View of xos/usr/lib/libc/getopt.c


XOS | Parent Directory | View | Download

/* 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 <http://www.gnu.org/licenses/>. */
 
#include "getopt.h"
 
#include <error.h>
#include <string.h>
#include <stdio.h>
 
/* 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);
}