]> git.ipfire.org Git - thirdparty/libarchive.git/commitdiff
Use Gregory Pietsch's public domain getopt_long() implementation
authorTim Kientzle <kientzle@gmail.com>
Thu, 5 Jun 2008 22:41:21 +0000 (18:41 -0400)
committerTim Kientzle <kientzle@gmail.com>
Thu, 5 Jun 2008 22:41:21 +0000 (18:41 -0400)
instead of relying on the platform one.

Having the tar program use getopt() on platforms that didn't
support getopt_long() was a nice idea, but it's turning out to
be a real headache. This way, I can just assume long option
support and simplify chunks of the test harness.

SVN-Revision: 107

Makefile.am
tar/Makefile
tar/bsdtar.c
tar/config_freebsd.h
tar/getopt.c [new file with mode: 0644]
tar/getopt.h [new file with mode: 0644]

index f9e1f0bd137b2e8c3c1b086db16e3de382ee2f63..909e60e9ecc477a7dc86a40b483cfd2b242ea649 100644 (file)
@@ -268,6 +268,8 @@ bsdtar_SOURCES=                             \
                tar/bsdtar.h            \
                tar/bsdtar_platform.h   \
                tar/getdate.y           \
+               tar/getopt.c            \
+               tar/getopt.h            \
                tar/matching.c          \
                tar/read.c              \
                tar/siginfo.c           \
index d65b23a9a08f86f318c15d1744ddf5d392c545d1..579282a9e43ecd2fc4d585679b99f2050b39e992 100644 (file)
@@ -2,7 +2,7 @@
 
 PROG=  bsdtar
 BSDTAR_VERSION_STRING=2.5.4b
-SRCS=  bsdtar.c getdate.y matching.c read.c siginfo.c subst.c tree.c util.c write.c
+SRCS=  bsdtar.c getdate.y getopt.c matching.c read.c siginfo.c subst.c tree.c util.c write.c
 WARNS?=        5
 DPADD= ${LIBARCHIVE} ${LIBBZ2} ${LIBZ}
 LDADD= -larchive -lbz2 -lz
index 1c13cc6bfbe76ffd055f70ebdb72e592aface94d..bcf14185c1297e617065491de64055ae10da4401 100644 (file)
@@ -38,18 +38,6 @@ __FBSDID("$FreeBSD: src/usr.bin/tar/bsdtar.c,v 1.91 2008/05/26 17:10:10 kientzle
 #ifdef HAVE_FCNTL_H
 #include <fcntl.h>
 #endif
-#ifdef HAVE_GETOPT_LONG
-#include <getopt.h>
-#else
-struct option {
-       const char *name;
-       int has_arg;
-       int *flag;
-       int val;
-};
-#define        no_argument 0
-#define        required_argument 1
-#endif
 #ifdef HAVE_LANGINFO_H
 #include <langinfo.h>
 #endif
@@ -77,6 +65,7 @@ struct option {
 #endif
 
 #include "bsdtar.h"
+#include "getopt.h"
 
 #if !HAVE_DECL_OPTARG
 extern int optarg;
@@ -799,11 +788,7 @@ usage(struct bsdtar *bsdtar)
        fprintf(stderr, "  List:    %s -tf <archive-filename>\n", p);
        fprintf(stderr, "  Extract: %s -xf <archive-filename>\n", p);
        fprintf(stderr, "  Create:  %s -cf <archive-filename> [filenames...]\n", p);
-#ifdef HAVE_GETOPT_LONG
        fprintf(stderr, "  Help:    %s --help\n", p);
-#else
-       fprintf(stderr, "  Help:    %s -h\n", p);
-#endif
        exit(1);
 }
 
@@ -828,11 +813,7 @@ static const char *long_help_msg =
        "  <file>, <dir>  add these items to archive\n"
        "  -z, -j  Compress archive with gzip/bzip2\n"
        "  --format {ustar|pax|cpio|shar}  Select archive format\n"
-#ifdef HAVE_GETOPT_LONG
        "  --exclude <pattern>  Skip files that match pattern\n"
-#else
-       "  -W exclude=<pattern>  Skip files that match pattern\n"
-#endif
        "  -C <dir>  Change to <dir> before processing remaining files\n"
        "  @<archive>  Add entries from <archive> to output\n"
        "List: %p -t [options] [<patterns>]\n"
@@ -894,14 +875,10 @@ bsdtar_getopt(struct bsdtar *bsdtar, const char *optstring,
        option_index = -1;
        *poption = NULL;
 
-#ifdef HAVE_GETOPT_LONG
        opt = getopt_long(bsdtar->argc, bsdtar->argv, optstring,
            tar_longopts, &option_index);
        if (option_index > -1)
                *poption = tar_longopts + option_index;
-#else
-       opt = getopt(bsdtar->argc, bsdtar->argv, optstring);
-#endif
 
        /* Support long options through -W longopt=value */
        if (opt == 'W') {
index b12b623b1c59bbc17b57b07497e7642e6688e057..5a9a673e1e35606f692f89f08f15e3db04583c73 100644 (file)
@@ -52,7 +52,6 @@
 #define        HAVE_FNMATCH_H 1
 #define        HAVE_FNM_LEADING_DIR 1
 #define        HAVE_FTRUNCATE 1
-#define        HAVE_GETOPT_LONG 1
 #undef HAVE_GETXATTR
 #define        HAVE_GRP_H 1
 #define        HAVE_INTTYPES_H 1
diff --git a/tar/getopt.c b/tar/getopt.c
new file mode 100644 (file)
index 0000000..2f9e551
--- /dev/null
@@ -0,0 +1,428 @@
+/****************************************************************************
+
+getopt.c - Read command line options
+
+AUTHOR: Gregory Pietsch
+CREATED Fri Jan 10 21:13:05 1997
+
+DESCRIPTION:
+
+The getopt() function parses the command line arguments.  Its arguments argc
+and argv are the argument count and array as passed to the main() function
+on program invocation.  The argument optstring is a list of available option
+characters.  If such a character is followed by a colon (`:'), the option
+takes an argument, which is placed in optarg.  If such a character is
+followed by two colons, the option takes an optional argument, which is
+placed in optarg.  If the option does not take an argument, optarg is NULL.
+
+The external variable optind is the index of the next array element of argv
+to be processed; it communicates from one call to the next which element to
+process.
+
+The getopt_long() function works like getopt() except that it also accepts
+long options started by two dashes `--'.  If these take values, it is either
+in the form
+
+--arg=value
+
+ or
+
+--arg value
+
+It takes the additional arguments longopts which is a pointer to the first
+element of an array of type struct option.  The last element of the
+array has to be filled with NULL for the name field.
+
+The longind pointer points to the index of the current long option relative
+to longopts if it is non-NULL.
+
+The getopt() function returns the option character if the option was found
+successfully, `:' if there was a missing parameter for one of the options,
+`?' for an unknown option character, and EOF for the end of the option list.
+
+The getopt_long() function's return value is described in the header file.
+
+The function getopt_long_only() is identical to getopt_long(), except that a
+plus sign `+' can introduce long options as well as `--'.
+
+The following describes how to deal with options that follow non-option
+argv-elements.
+
+If the caller did not specify anything, the default is REQUIRE_ORDER if the
+environment variable POSIXLY_CORRECT is defined, PERMUTE otherwise.
+
+REQUIRE_ORDER means don't recognize them as options; stop option processing
+when the first non-option is seen.  This is what Unix does.  This mode of
+operation is selected by either setting the environment variable
+POSIXLY_CORRECT, or using `+' as the first character of the optstring
+parameter.
+
+PERMUTE is the default.  We permute the contents of ARGV as we scan, so that
+eventually all the non-options are at the end.  This allows options to be
+given in any order, even with programs that were not written to expect this.
+
+RETURN_IN_ORDER is an option available to programs that were written to
+expect options and other argv-elements in any order and that care about the
+ordering of the two.  We describe each non-option argv-element as if it were
+the argument of an option with character code 1.  Using `-' as the first
+character of the optstring parameter selects this mode of operation.
+
+The special argument `--' forces an end of option-scanning regardless of the
+value of ordering.  In the case of RETURN_IN_ORDER, only `--' can cause
+getopt() and friends to return EOF with optind != argc.
+
+COPYRIGHT NOTICE AND DISCLAIMER:
+
+Copyright (C) 1997 Gregory Pietsch
+
+This file and the accompanying getopt.h header file are hereby placed in the
+public domain without restrictions.  Just give the author credit, don't
+claim you wrote it or prevent anyone else from using it.
+
+Gregory Pietsch's current e-mail address:
+gpietsch@comcast.net
+****************************************************************************/
+
+/* For use in bsdtar. */
+#include "bsdtar_platform.h"
+__FBSDID("$FreeBSD$");
+
+/* include files */
+#include <stdio.h>
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#include "getopt.h"
+
+/* macros */
+
+/* types */
+typedef enum GETOPT_ORDERING_T
+{
+  PERMUTE,
+  RETURN_IN_ORDER,
+  REQUIRE_ORDER
+} GETOPT_ORDERING_T;
+
+/* globally-defined variables */
+char *optarg = NULL;
+int optind = 0;
+int opterr = 1;
+int optopt = '?';
+
+/* functions */
+
+/* reverse_argv_elements:  reverses num elements starting at argv */
+static void
+reverse_argv_elements (char **argv, int num)
+{
+  int i;
+  char *tmp;
+
+  for (i = 0; i < (num >> 1); i++)
+    {
+      tmp = argv[i];
+      argv[i] = argv[num - i - 1];
+      argv[num - i - 1] = tmp;
+    }
+}
+
+/* permute: swap two blocks of argv-elements given their lengths */
+static void
+permute (char **argv, int len1, int len2)
+{
+  reverse_argv_elements (argv, len1);
+  reverse_argv_elements (argv, len1 + len2);
+  reverse_argv_elements (argv, len2);
+}
+
+/* is_option: is this argv-element an option or the end of the option list? */
+static int
+is_option (char *argv_element, int only)
+{
+  return ((argv_element == NULL)
+          || (argv_element[0] == '-') || (only && argv_element[0] == '+'));
+}
+
+/* getopt_internal:  the function that does all the dirty work */
+static int
+getopt_internal (int argc, char ** argv, const char *shortopts,
+                 const struct option *longopts, int *longind, int only)
+{
+  GETOPT_ORDERING_T ordering = PERMUTE;
+  static size_t optwhere = 0;
+  size_t permute_from = 0;
+  int num_nonopts = 0;
+  int optindex = 0;
+  size_t match_chars = 0;
+  char *possible_arg = NULL;
+  int longopt_match = -1;
+  int has_arg = -1;
+  char *cp = NULL;
+  int arg_next = 0;
+  enum { NO_ARG, REQUIRED_ARG, OPTIONAL_ARG };
+
+  /* first, deal with silly parameters and easy stuff */
+  if (argc == 0 || argv == NULL || (shortopts == NULL && longopts == NULL))
+    return (optopt = '?');
+  if (optind >= argc || argv[optind] == NULL)
+    return EOF;
+  if (strcmp (argv[optind], "--") == 0)
+    {
+      optind++;
+      return EOF;
+    }
+  /* if this is our first time through */
+  if (optind == 0)
+    optind = optwhere = 1;
+
+  /* define ordering */
+  if (shortopts != NULL && (*shortopts == '-' || *shortopts == '+'))
+    {
+      ordering = (*shortopts == '-') ? RETURN_IN_ORDER : REQUIRE_ORDER;
+      shortopts++;
+    }
+  else
+    ordering = (getenv ("POSIXLY_CORRECT") != NULL) ? REQUIRE_ORDER : PERMUTE;
+
+  /*
+   * based on ordering, find our next option, if we're at the beginning of
+   * one
+   */
+  if (optwhere == 1)
+    {
+      switch (ordering)
+        {
+        case PERMUTE:
+          permute_from = optind;
+          num_nonopts = 0;
+          while (!is_option (argv[optind], only))
+            {
+              optind++;
+              num_nonopts++;
+            }
+          if (argv[optind] == NULL)
+            {
+              /* no more options */
+              optind = permute_from;
+              return EOF;
+            }
+          else if (strcmp (argv[optind], "--") == 0)
+            {
+              /* no more options, but have to get `--' out of the way */
+              permute (argv + permute_from, num_nonopts, 1);
+              optind = permute_from + 1;
+              return EOF;
+            }
+          break;
+        case RETURN_IN_ORDER:
+          if (!is_option (argv[optind], only))
+            {
+              optarg = argv[optind++];
+              return (optopt = 1);
+            }
+          break;
+        case REQUIRE_ORDER:
+          if (!is_option (argv[optind], only))
+            return EOF;
+          break;
+        }
+    }
+  /* we've got an option, so parse it */
+
+  /* first, is it a long option? */
+  if (longopts != NULL
+      && (memcmp (argv[optind], "--", 2) == 0
+          || (only && argv[optind][0] == '+')) && optwhere == 1)
+    {
+      /* handle long options */
+      if (memcmp (argv[optind], "--", 2) == 0)
+        optwhere = 2;
+      longopt_match = -1;
+      possible_arg = strchr (argv[optind] + optwhere, '=');
+      if (possible_arg == NULL)
+        {
+          /* no =, so next argv might be arg */
+          match_chars = strlen (argv[optind]);
+          possible_arg = argv[optind] + match_chars;
+          match_chars = match_chars - optwhere;
+        }
+      else
+        match_chars = (possible_arg - argv[optind]) - optwhere;
+      for (optindex = 0; longopts[optindex].name != NULL; optindex++)
+        {
+          if (memcmp (argv[optind] + optwhere,
+                      longopts[optindex].name, match_chars) == 0)
+            {
+              /* do we have an exact match? */
+              if (match_chars == strlen (longopts[optindex].name))
+                {
+                  longopt_match = optindex;
+                  break;
+                }
+              /* do any characters match? */
+              else
+                {
+                  if (longopt_match < 0)
+                    longopt_match = optindex;
+                  else
+                    {
+                      /* we have ambiguous options */
+                      if (opterr)
+                        fprintf (stderr, "%s: option `%s' is ambiguous "
+                                 "(could be `--%s' or `--%s')\n",
+                                 argv[0],
+                                 argv[optind],
+                                 longopts[longopt_match].name,
+                                 longopts[optindex].name);
+                      return (optopt = '?');
+                    }
+                }
+            }
+        }
+      if (longopt_match >= 0)
+        has_arg = longopts[longopt_match].has_arg;
+    }
+  /* if we didn't find a long option, is it a short option? */
+  if (longopt_match < 0 && shortopts != NULL)
+    {
+      cp = strchr (shortopts, argv[optind][optwhere]);
+      if (cp == NULL)
+        {
+          /* couldn't find option in shortopts */
+          if (opterr)
+            fprintf (stderr,
+                     "%s: invalid option -- `-%c'\n",
+                     argv[0], argv[optind][optwhere]);
+          optwhere++;
+          if (argv[optind][optwhere] == '\0')
+            {
+              optind++;
+              optwhere = 1;
+            }
+          return (optopt = '?');
+        }
+      has_arg = ((cp[1] == ':')
+                 ? ((cp[2] == ':') ? OPTIONAL_ARG : REQUIRED_ARG) : NO_ARG);
+      possible_arg = argv[optind] + optwhere + 1;
+      optopt = *cp;
+    }
+  /* get argument and reset optwhere */
+  arg_next = 0;
+  switch (has_arg)
+    {
+    case OPTIONAL_ARG:
+      if (*possible_arg == '=')
+        possible_arg++;
+      if (*possible_arg != '\0')
+        {
+          optarg = possible_arg;
+          optwhere = 1;
+        }
+      else
+        optarg = NULL;
+      break;
+    case REQUIRED_ARG:
+      if (*possible_arg == '=')
+        possible_arg++;
+      if (*possible_arg != '\0')
+        {
+          optarg = possible_arg;
+          optwhere = 1;
+        }
+      else if (optind + 1 >= argc)
+        {
+          if (opterr)
+            {
+              fprintf (stderr, "%s: argument required for option `", argv[0]);
+              if (longopt_match >= 0)
+                fprintf (stderr, "--%s'\n", longopts[longopt_match].name);
+              else
+                fprintf (stderr, "-%c'\n", *cp);
+            }
+          optind++;
+          return (optopt = ':');
+        }
+      else
+        {
+          optarg = argv[optind + 1];
+          arg_next = 1;
+          optwhere = 1;
+        }
+      break;
+    case NO_ARG:
+      if (longopt_match < 0)
+        {
+          optwhere++;
+          if (argv[optind][optwhere] == '\0')
+            optwhere = 1;
+        }
+      else
+        optwhere = 1;
+      optarg = NULL;
+      break;
+    }
+
+  /* do we have to permute or otherwise modify optind? */
+  if (ordering == PERMUTE && optwhere == 1 && num_nonopts != 0)
+    {
+      permute (argv + permute_from, num_nonopts, 1 + arg_next);
+      optind = permute_from + 1 + arg_next;
+    }
+  else if (optwhere == 1)
+    optind = optind + 1 + arg_next;
+
+  /* finally return */
+  if (longopt_match >= 0)
+    {
+      if (longind != NULL)
+        *longind = longopt_match;
+      if (longopts[longopt_match].flag != NULL)
+        {
+          *(longopts[longopt_match].flag) = longopts[longopt_match].val;
+          return 0;
+        }
+      else
+        return longopts[longopt_match].val;
+    }
+  else
+    return optopt;
+}
+
+int
+getopt (int argc, char *const argv[], const char *optstring)
+{
+  /*
+   * Historically, getopt() did not permute elements, so the
+   * char * const * declaration made sense.  Standards still
+   * require the char * const * declaration, so we need to
+   * cast away a const here.  Yes, it's yucky.
+   */
+  char **a = (char **)(uintptr_t)argv;
+  return getopt_internal (argc, a, optstring, NULL, NULL, 0);
+}
+
+int
+getopt_long (int argc, char *const argv[], const char *shortopts,
+             const struct option *longopts, int *longind)
+{
+  /* See comments above for an explanation of this cast. */
+  char **a = (char **)(uintptr_t)argv;
+  return getopt_internal (argc, a, shortopts, longopts, longind, 0);
+}
+
+int
+getopt_long_only (int argc, char * const argv[], const char *shortopts,
+                  const struct option *longopts, int *longind)
+{
+  /* See comments above for an explanation of this cast. */
+  char **a = (char **)(uintptr_t)argv;
+  return getopt_internal (argc, a, shortopts, longopts, longind, 1);
+}
+
+/* end of file GETOPT.C */
+
diff --git a/tar/getopt.h b/tar/getopt.h
new file mode 100644 (file)
index 0000000..2cadce3
--- /dev/null
@@ -0,0 +1,137 @@
+/****************************************************************************
+
+getopt.h - Read command line options
+
+AUTHOR: Gregory Pietsch
+CREATED Thu Jan 09 22:37:00 1997
+
+DESCRIPTION:
+
+The getopt() function parses the command line arguments.  Its arguments argc
+and argv are the argument count and array as passed to the main() function
+on program invocation.  The argument optstring is a list of available option
+characters.  If such a character is followed by a colon (`:'), the option
+takes an argument, which is placed in optarg.  If such a character is
+followed by two colons, the option takes an optional argument, which is
+placed in optarg.  If the option does not take an argument, optarg is NULL.
+
+The external variable optind is the index of the next array element of argv
+to be processed; it communicates from one call to the next which element to
+process.
+
+The getopt_long() function works like getopt() except that it also accepts
+long options started by two dashes `--'.  If these take values, it is either
+in the form
+
+--arg=value
+
+ or
+
+--arg value
+
+It takes the additional arguments longopts which is a pointer to the first
+element of an array of type GETOPT_LONG_OPTION_T, defined below.  The last
+element of the array has to be filled with NULL for the name field.
+
+The longind pointer points to the index of the current long option relative
+to longopts if it is non-NULL.
+
+The getopt() function returns the option character if the option was found
+successfully, `:' if there was a missing parameter for one of the options,
+`?' for an unknown option character, and EOF for the end of the option list.
+
+The getopt_long() function's return value is described below.
+
+The function getopt_long_only() is identical to getopt_long(), except that a
+plus sign `+' can introduce long options as well as `--'.
+
+Describe how to deal with options that follow non-option ARGV-elements.
+
+If the caller did not specify anything, the default is REQUIRE_ORDER if the
+environment variable POSIXLY_CORRECT is defined, PERMUTE otherwise.
+
+REQUIRE_ORDER means don't recognize them as options; stop option processing
+when the first non-option is seen.  This is what Unix does.  This mode of
+operation is selected by either setting the environment variable
+POSIXLY_CORRECT, or using `+' as the first character of the optstring
+parameter.
+
+PERMUTE is the default.  We permute the contents of ARGV as we scan, so that
+eventually all the non-options are at the end.  This allows options to be
+given in any order, even with programs that were not written to expect this.
+
+RETURN_IN_ORDER is an option available to programs that were written to
+expect options and other ARGV-elements in any order and that care about the
+ordering of the two.  We describe each non-option ARGV-element as if it were
+the argument of an option with character code 1.  Using `-' as the first
+character of the optstring parameter selects this mode of operation.
+
+The special argument `--' forces an end of option-scanning regardless of the
+value of `ordering'.  In the case of RETURN_IN_ORDER, only `--' can cause
+getopt() and friends to return EOF with optind != argc.
+
+COPYRIGHT NOTICE AND DISCLAIMER:
+
+Copyright (C) 1997 Gregory Pietsch
+
+This file and the accompanying getopt.c implementation file are hereby
+placed in the public domain without restrictions.  Just give the author
+credit, don't claim you wrote it or prevent anyone else from using it.
+
+Gregory Pietsch's current e-mail address:
+gpietsch@comcast.net
+****************************************************************************/
+
+#ifndef GETOPT_H
+#define GETOPT_H
+
+/* include files needed by this include file */
+
+/* macros defined by this include file */
+#define no_argument     0
+#define required_argument 1
+#define optional_argument 2
+
+/* types defined by this include file */
+
+/* struct option: The type of long option */
+struct option
+{
+  const char *name;                   /* the name of the long option */
+  int has_arg;                  /* one of the above macros */
+  int *flag;                    /* determines if getopt_long() returns a
+                                 * value for a long option; if it is
+                                 * non-NULL, 0 is returned as a function
+                                 * value and the value of val is stored in
+                                 * the area pointed to by flag.  Otherwise,
+                                 * val is returned. */
+  int val;                      /* determines the value to return if flag is
+                                 * NULL. */
+};
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+  /* externally-defined variables */
+  extern char *optarg;
+  extern int optind;
+  extern int opterr;
+  extern int optopt;
+
+  /* function prototypes */
+  int getopt (int argc, char * const argv[], const char *optstring);
+  int getopt_long (int argc, char * const argv[], const char *shortopts,
+      const struct option * longopts, int *longind);
+  int getopt_long_only (int argc, char * const argv[], const char *shortopts,
+      const struct option * longopts, int *longind);
+
+#ifdef __cplusplus
+};
+
+#endif
+
+#endif /* GETOPT_H */
+
+/* END OF FILE getopt.h */