gpgme-tool: Switch to argparse.c for option parsing.
authorWerner Koch <wk@gnupg.org>
Mon, 31 Aug 2015 18:33:44 +0000 (20:33 +0200)
committerWerner Koch <wk@gnupg.org>
Mon, 31 Aug 2015 18:34:00 +0000 (20:34 +0200)
* src/argparse.c, src/argparse.h: New. Taken from current gnupg.
* src/Makefile.am (gpgme_tool_SOURCES): New.
* src/gpgme-tool.c: Remove all argp.h stuff.
(my_strusage): New.
(main): Change to use argparse.
--

As a GnuPG related tool the user should get the same experience in
option parsing as with gpg et al.  Thus we use the same parser.

Signed-off-by: Werner Koch <wk@gnupg.org>
src/Makefile.am
src/argparse.c [new file with mode: 0644]
src/argparse.h [new file with mode: 0644]
src/gpgme-tool.c

index 58922f9..698c632 100644 (file)
@@ -128,6 +128,7 @@ endif
 AM_CPPFLAGS = @GPG_ERROR_CFLAGS@ @QT4_CORE_CFLAGS@
 AM_CFLAGS = @LIBASSUAN_CFLAGS@ @GLIB_CFLAGS@ @QT4_CORE_CFLAGS@
 
+gpgme_tool_SOURCES = gpgme-tool.c argparse.c argparse.h
 gpgme_tool_LDADD = libgpgme.la @LIBASSUAN_LIBS@
 
 
diff --git a/src/argparse.c b/src/argparse.c
new file mode 100644 (file)
index 0000000..53c20fc
--- /dev/null
@@ -0,0 +1,1609 @@
+/* [argparse.c wk 17.06.97] Argument Parser for option handling
+ * Copyright (C) 1998-2001, 2006-2008, 2012 Free Software Foundation, Inc.
+ * Copyright (C) 1997-2001, 2006-2008, 2013-2015 Werner Koch
+ *
+ * This file is part of GnuPG.
+ *
+ * GnuPG is free software; you can redistribute it and/or modify it
+ * under the terms of either
+ *
+ *   - the GNU Lesser General Public License as published by the Free
+ *     Software Foundation; either version 3 of the License, or (at
+ *     your option) any later version.
+ *
+ * or
+ *
+ *   - the GNU General Public License as published by the Free
+ *     Software Foundation; either version 2 of the License, or (at
+ *     your option) any later version.
+ *
+ * or both in parallel, as here.
+ *
+ * GnuPG 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 copies of the GNU General Public License
+ * and the GNU Lesser General Public License along with this program;
+ * if not, see <http://www.gnu.org/licenses/>.
+ */
+
+/* This file may be used as part of GnuPG or standalone.  A GnuPG
+   build is detected by the presence of the macro GNUPG_MAJOR_VERSION.
+   Some feature are only availalbe in the GnuPG build mode.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <string.h>
+#include <stdarg.h>
+#include <limits.h>
+#include <errno.h>
+
+#ifdef GNUPG_MAJOR_VERSION
+# include "util.h"
+# include "common-defs.h"
+# include "i18n.h"
+# include "mischelp.h"
+# include "stringhelp.h"
+# include "logging.h"
+# include "utf8conv.h"
+#endif /*GNUPG_MAJOR_VERSION*/
+
+#include "argparse.h"
+
+/* GnuPG uses GPLv3+ but a standalone version of this defaults to
+   GPLv2+ because that is the license of this file.  Change this if
+   you include it in a program which uses GPLv3.  If you don't want to
+   set a a copyright string for your usage() you may also hardcode it
+   here.  */
+#ifndef GNUPG_MAJOR_VERSION
+
+# define ARGPARSE_GPL_VERSION      2
+# define ARGPARSE_CRIGHT_STR "Copyright (C) YEAR NAME"
+
+#else /* Used by GnuPG  */
+
+# define ARGPARSE_GPL_VERSION      3
+# define ARGPARSE_CRIGHT_STR "Copyright (C) 2015 Free Software Foundation, Inc."
+
+#endif /*GNUPG_MAJOR_VERSION*/
+
+/* Replacements for standalone builds.  */
+#ifndef GNUPG_MAJOR_VERSION
+# ifndef _
+#  define _(a)  (a)
+# endif
+# ifndef DIM
+#  define DIM(v)           (sizeof(v)/sizeof((v)[0]))
+# endif
+# define xtrymalloc(a)    malloc ((a))
+# define xtryrealloc(a,b) realloc ((a), (b))
+# define xtrystrdup(a)    strdup ((a))
+# define xfree(a)         free ((a))
+# define log_error        my_log_error
+# define log_bug         my_log_bug
+# define trim_spaces(a)   my_trim_spaces ((a))
+# define map_static_macro_string(a)  (a)
+#endif /*!GNUPG_MAJOR_VERSION*/
+
+
+#define ARGPARSE_STR(v) #v
+#define ARGPARSE_STR2(v) ARGPARSE_STR(v)
+
+
+/* Replacements for standalone builds.  */
+#ifndef GNUPG_MAJOR_VERSION
+static void
+my_log_error (const char *fmt, ...)
+{
+  va_list arg_ptr ;
+
+  va_start (arg_ptr, fmt);
+  fprintf (stderr, "%s: ", strusage (11));
+  vfprintf (stderr, fmt, arg_ptr);
+  va_end (arg_ptr);
+}
+
+static void
+my_log_bug (const char *fmt, ...)
+{
+  va_list arg_ptr ;
+
+  va_start (arg_ptr, fmt);
+  fprintf (stderr, "%s: Ohhhh jeeee: ", strusage (11));
+  vfprintf (stderr, fmt, arg_ptr);
+  va_end (arg_ptr);
+  abort ();
+}
+
+/* Return true if the native charset is utf-8.  */
+static int
+is_native_utf8 (void)
+{
+  return 1;
+}
+
+static char *
+my_trim_spaces (char *str)
+{
+  char *string, *p, *mark;
+
+  string = str;
+  /* Find first non space character. */
+  for (p=string; *p && isspace (*(unsigned char*)p) ; p++)
+    ;
+  /* Move characters. */
+  for ((mark = NULL); (*string = *p); string++, p++)
+    if (isspace (*(unsigned char*)p))
+      {
+        if (!mark)
+          mark = string;
+      }
+    else
+      mark = NULL;
+  if (mark)
+    *mark = '\0' ;  /* Remove trailing spaces. */
+
+  return str ;
+}
+
+#endif /*!GNUPG_MAJOR_VERSION*/
+
+
+
+/*********************************
+ * @Summary arg_parse
+ *  #include "argparse.h"
+ *
+ *  typedef struct {
+ *     char *argc;               pointer to argc (value subject to change)
+ *     char ***argv;             pointer to argv (value subject to change)
+ *     unsigned flags;           Global flags (DO NOT CHANGE)
+ *     int err;                  print error about last option
+ *                               1 = warning, 2 = abort
+ *     int r_opt;                return option
+ *     int r_type;               type of return value (0 = no argument found)
+ *     union {
+ *         int   ret_int;
+ *         long  ret_long
+ *         ulong ret_ulong;
+ *         char *ret_str;
+ *     } r;                      Return values
+ *     struct {
+ *         int idx;
+ *         const char *last;
+ *         void *aliases;
+ *     } internal;               DO NOT CHANGE
+ *  } ARGPARSE_ARGS;
+ *
+ *  typedef struct {
+ *     int         short_opt;
+ *     const char *long_opt;
+ *     unsigned flags;
+ *  } ARGPARSE_OPTS;
+ *
+ *  int arg_parse( ARGPARSE_ARGS *arg, ARGPARSE_OPTS *opts );
+ *
+ * @Description
+ *  This is my replacement for getopt(). See the example for a typical usage.
+ *  Global flags are:
+ *     Bit 0 : Do not remove options form argv
+ *     Bit 1 : Do not stop at last option but return other args
+ *            with r_opt set to -1.
+ *     Bit 2 : Assume options and real args are mixed.
+ *     Bit 3 : Do not use -- to stop option processing.
+ *     Bit 4 : Do not skip the first arg.
+ *     Bit 5 : allow usage of long option with only one dash
+ *     Bit 6 : ignore --version
+ *     all other bits must be set to zero, this value is modified by the
+ *     function, so assume this is write only.
+ *  Local flags (for each option):
+ *     Bit 2-0 : 0 = does not take an argument
+ *              1 = takes int argument
+ *              2 = takes string argument
+ *              3 = takes long argument
+ *              4 = takes ulong argument
+ *     Bit 3 : argument is optional (r_type will the be set to 0)
+ *     Bit 4 : allow 0x etc. prefixed values.
+ *     Bit 6 : Ignore this option
+ *     Bit 7 : This is a command and not an option
+ *  You stop the option processing by setting opts to NULL, the function will
+ *  then return 0.
+ * @Return Value
+ *   Returns the args.r_opt or 0 if ready
+ *   r_opt may be -2/-7 to indicate an unknown option/command.
+ * @See Also
+ *   ArgExpand
+ * @Notes
+ *  You do not need to process the options 'h', '--help' or '--version'
+ *  because this function includes standard help processing; but if you
+ *  specify '-h', '--help' or '--version' you have to do it yourself.
+ *  The option '--' stops argument processing; if bit 1 is set the function
+ *  continues to return normal arguments.
+ *  To process float args or unsigned args you must use a string args and do
+ *  the conversion yourself.
+ * @Example
+ *
+ *     ARGPARSE_OPTS opts[] = {
+ *     { 'v', "verbose",   0 },
+ *     { 'd', "debug",     0 },
+ *     { 'o', "output",    2 },
+ *     { 'c', "cross-ref", 2|8 },
+ *     { 'm', "my-option", 1|8 },
+ *     { 300, "ignored-long-option, ARGPARSE_OP_IGNORE},
+ *     { 500, "have-no-short-option-for-this-long-option", 0 },
+ *     {0} };
+ *     ARGPARSE_ARGS pargs = { &argc, &argv, 0 }
+ *
+ *     while( ArgParse( &pargs, &opts) ) {
+ *        switch( pargs.r_opt ) {
+ *          case 'v': opt.verbose++; break;
+ *          case 'd': opt.debug++; break;
+ *          case 'o': opt.outfile = pargs.r.ret_str; break;
+ *          case 'c': opt.crf = pargs.r_type? pargs.r.ret_str:"a.crf"; break;
+ *          case 'm': opt.myopt = pargs.r_type? pargs.r.ret_int : 1; break;
+ *          case 500: opt.a_long_one++;  break
+ *          default : pargs.err = 1; break; -- force warning output --
+ *        }
+ *     }
+ *     if( argc > 1 )
+ *        log_fatal( "Too many args");
+ *
+ */
+
+typedef struct alias_def_s *ALIAS_DEF;
+struct alias_def_s {
+    ALIAS_DEF next;
+    char *name;   /* malloced buffer with name, \0, value */
+    const char *value; /* ptr into name */
+};
+
+
+/* Object to store the names for the --ignore-invalid-option option.
+   This is a simple linked list.  */
+typedef struct iio_item_def_s *IIO_ITEM_DEF;
+struct iio_item_def_s
+{
+  IIO_ITEM_DEF next;
+  char name[1];      /* String with the long option name.  */
+};
+
+static const char *(*strusage_handler)( int ) = NULL;
+static int (*custom_outfnc) (int, const char *);
+
+static int  set_opt_arg(ARGPARSE_ARGS *arg, unsigned flags, char *s);
+static void show_help(ARGPARSE_OPTS *opts, unsigned flags);
+static void show_version(void);
+static int writestrings (int is_error, const char *string, ...)
+#if __GNUC__ >= 4
+  __attribute__ ((sentinel(0)))
+#endif
+  ;
+
+
+void
+argparse_register_outfnc (int (*fnc)(int, const char *))
+{
+  custom_outfnc = fnc;
+}
+
+
+/* Write STRING and all following const char * arguments either to
+   stdout or, if IS_ERROR is set, to stderr.  The list of strings must
+   be terminated by a NULL.  */
+static int
+writestrings (int is_error, const char *string, ...)
+{
+  va_list arg_ptr;
+  const char *s;
+  int count = 0;
+
+  if (string)
+    {
+      s = string;
+      va_start (arg_ptr, string);
+      do
+        {
+          if (custom_outfnc)
+            custom_outfnc (is_error? 2:1, s);
+          else
+            fputs (s, is_error? stderr : stdout);
+          count += strlen (s);
+        }
+      while ((s = va_arg (arg_ptr, const char *)));
+      va_end (arg_ptr);
+    }
+  return count;
+}
+
+
+static void
+flushstrings (int is_error)
+{
+  if (custom_outfnc)
+    custom_outfnc (is_error? 2:1, NULL);
+  else
+    fflush (is_error? stderr : stdout);
+}
+
+
+static void
+initialize( ARGPARSE_ARGS *arg, const char *filename, unsigned *lineno )
+{
+  if( !(arg->flags & (1<<15)) )
+    {
+      /* Initialize this instance. */
+      arg->internal.idx = 0;
+      arg->internal.last = NULL;
+      arg->internal.inarg = 0;
+      arg->internal.stopped = 0;
+      arg->internal.aliases = NULL;
+      arg->internal.cur_alias = NULL;
+      arg->internal.iio_list = NULL;
+      arg->err = 0;
+      arg->flags |= 1<<15; /* Mark as initialized.  */
+      if ( *arg->argc < 0 )
+        log_bug ("invalid argument for arg_parse\n");
+    }
+
+
+  if (arg->err)
+    {
+      /* Last option was erroneous.  */
+      const char *s;
+
+      if (filename)
+        {
+          if ( arg->r_opt == ARGPARSE_UNEXPECTED_ARG )
+            s = _("argument not expected");
+          else if ( arg->r_opt == ARGPARSE_READ_ERROR )
+            s = _("read error");
+          else if ( arg->r_opt == ARGPARSE_KEYWORD_TOO_LONG )
+            s = _("keyword too long");
+          else if ( arg->r_opt == ARGPARSE_MISSING_ARG )
+            s = _("missing argument");
+          else if ( arg->r_opt == ARGPARSE_INVALID_ARG )
+            s = _("invalid argument");
+          else if ( arg->r_opt == ARGPARSE_INVALID_COMMAND )
+            s = _("invalid command");
+          else if ( arg->r_opt == ARGPARSE_INVALID_ALIAS )
+            s = _("invalid alias definition");
+          else if ( arg->r_opt == ARGPARSE_OUT_OF_CORE )
+            s = _("out of core");
+          else
+            s = _("invalid option");
+          log_error ("%s:%u: %s\n", filename, *lineno, s);
+       }
+      else
+        {
+          s = arg->internal.last? arg->internal.last:"[??]";
+
+          if ( arg->r_opt == ARGPARSE_MISSING_ARG )
+            log_error (_("missing argument for option \"%.50s\"\n"), s);
+          else if ( arg->r_opt == ARGPARSE_INVALID_ARG )
+            log_error (_("invalid argument for option \"%.50s\"\n"), s);
+          else if ( arg->r_opt == ARGPARSE_UNEXPECTED_ARG )
+            log_error (_("option \"%.50s\" does not expect an argument\n"), s);
+          else if ( arg->r_opt == ARGPARSE_INVALID_COMMAND )
+            log_error (_("invalid command \"%.50s\"\n"), s);
+          else if ( arg->r_opt == ARGPARSE_AMBIGUOUS_OPTION )
+            log_error (_("option \"%.50s\" is ambiguous\n"), s);
+          else if ( arg->r_opt == ARGPARSE_AMBIGUOUS_COMMAND )
+            log_error (_("command \"%.50s\" is ambiguous\n"),s );
+          else if ( arg->r_opt == ARGPARSE_OUT_OF_CORE )
+            log_error ("%s\n", _("out of core\n"));
+          else
+            log_error (_("invalid option \"%.50s\"\n"), s);
+       }
+      if (arg->err != ARGPARSE_PRINT_WARNING)
+        exit (2);
+      arg->err = 0;
+    }
+
+  /* Zero out the return value union.  */
+  arg->r.ret_str = NULL;
+  arg->r.ret_long = 0;
+}
+
+
+static void
+store_alias( ARGPARSE_ARGS *arg, char *name, char *value )
+{
+    /* TODO: replace this dummy function with a rea one
+     * and fix the probelms IRIX has with (ALIAS_DEV)arg..
+     * used as lvalue
+     */
+  (void)arg;
+  (void)name;
+  (void)value;
+#if 0
+    ALIAS_DEF a = xmalloc( sizeof *a );
+    a->name = name;
+    a->value = value;
+    a->next = (ALIAS_DEF)arg->internal.aliases;
+    (ALIAS_DEF)arg->internal.aliases = a;
+#endif
+}
+
+
+/* Return true if KEYWORD is in the ignore-invalid-option list.  */
+static int
+ignore_invalid_option_p (ARGPARSE_ARGS *arg, const char *keyword)
+{
+  IIO_ITEM_DEF item = arg->internal.iio_list;
+
+  for (; item; item = item->next)
+    if (!strcmp (item->name, keyword))
+      return 1;
+  return 0;
+}
+
+
+/* Add the keywords up to the next LF to the list of to be ignored
+   options.  After returning FP will either be at EOF or the next
+   character read wll be the first of a new line.  The function
+   returns 0 on success or true on malloc failure.  */
+static int
+ignore_invalid_option_add (ARGPARSE_ARGS *arg, FILE *fp)
+{
+  IIO_ITEM_DEF item;
+  int c;
+  char name[100];
+  int namelen = 0;
+  int ready = 0;
+  enum { skipWS, collectNAME, skipNAME, addNAME} state = skipWS;
+
+  while (!ready)
+    {
+      c = getc (fp);
+      if (c == '\n')
+        ready = 1;
+      else if (c == EOF)
+        {
+          c = '\n';
+          ready = 1;
+        }
+    again:
+      switch (state)
+        {
+        case skipWS:
+          if (!isascii (c) || !isspace(c))
+            {
+              namelen = 0;
+              state = collectNAME;
+              goto again;
+            }
+          break;
+
+        case collectNAME:
+          if (isspace (c))
+            {
+              state = addNAME;
+              goto again;
+            }
+          else if (namelen < DIM(name)-1)
+            name[namelen++] = c;
+          else /* Too long.  */
+            state = skipNAME;
+          break;
+
+        case skipNAME:
+          if (isspace (c))
+            {
+              state = skipWS;
+              goto again;
+            }
+          break;
+
+        case addNAME:
+          name[namelen] = 0;
+          if (!ignore_invalid_option_p (arg, name))
+            {
+              item = xtrymalloc (sizeof *item + namelen);
+              if (!item)
+                return 1;
+              strcpy (item->name, name);
+              item->next = (IIO_ITEM_DEF)arg->internal.iio_list;
+              arg->internal.iio_list = item;
+            }
+          state = skipWS;
+          goto again;
+        }
+    }
+  return 0;
+}
+
+
+/* Clear the entire ignore-invalid-option list.  */
+static void
+ignore_invalid_option_clear (ARGPARSE_ARGS *arg)
+{
+  IIO_ITEM_DEF item, tmpitem;
+
+  for (item = arg->internal.iio_list; item; item = tmpitem)
+    {
+      tmpitem = item->next;
+      xfree (item);
+    }
+  arg->internal.iio_list = NULL;
+}
+
+
+
+/****************
+ * Get options from a file.
+ * Lines starting with '#' are comment lines.
+ * Syntax is simply a keyword and the argument.
+ * Valid keywords are all keywords from the long_opt list without
+ * the leading dashes. The special keywords "help", "warranty" and "version"
+ * are not valid here.
+ * The special keyword "alias" may be used to store alias definitions,
+ * which are later expanded like long options.
+ * The option
+ *   ignore-invalid-option OPTIONNAMEs
+ * is recognized and updates a list of option which should be ignored if they
+ * are not defined.
+ * Caller must free returned strings.
+ * If called with FP set to NULL command line args are parse instead.
+ *
+ * Q: Should we allow the syntax
+ *     keyword = value
+ *    and accept for boolean options a value of 1/0, yes/no or true/false?
+ * Note: Abbreviation of options is here not allowed.
+ */
+int
+optfile_parse (FILE *fp, const char *filename, unsigned *lineno,
+              ARGPARSE_ARGS *arg, ARGPARSE_OPTS *opts)
+{
+  int state, i, c;
+  int idx=0;
+  char keyword[100];
+  char *buffer = NULL;
+  size_t buflen = 0;
+  int in_alias=0;
+
+  if (!fp) /* Divert to to arg_parse() in this case.  */
+    return arg_parse (arg, opts);
+
+  initialize (arg, filename, lineno);
+
+  /* Find the next keyword.  */
+  state = i = 0;
+  for (;;)
+    {
+      c = getc (fp);
+      if (c == '\n' || c== EOF )
+        {
+          if ( c != EOF )
+            ++*lineno;
+          if (state == -1)
+            break;
+          else if (state == 2)
+            {
+              keyword[i] = 0;
+              for (i=0; opts[i].short_opt; i++ )
+                {
+                  if (opts[i].long_opt && !strcmp (opts[i].long_opt, keyword))
+                    break;
+                }
+              idx = i;
+              arg->r_opt = opts[idx].short_opt;
+              if ((opts[idx].flags & ARGPARSE_OPT_IGNORE))
+                {
+                  state = i = 0;
+                  continue;
+                }
+              else if (!opts[idx].short_opt )
+                {
+                  if (!strcmp (keyword, "ignore-invalid-option"))
+                    {
+                      /* No argument - ignore this meta option.  */
+                      state = i = 0;
+                      continue;
+                    }
+                  else if (ignore_invalid_option_p (arg, keyword))
+                    {
+                      /* This invalid option is in the iio list.  */
+                      state = i = 0;
+                      continue;
+                    }
+                  arg->r_opt = ((opts[idx].flags & ARGPARSE_OPT_COMMAND)
+                                ? ARGPARSE_INVALID_COMMAND
+                                : ARGPARSE_INVALID_OPTION);
+                }
+              else if (!(opts[idx].flags & ARGPARSE_TYPE_MASK))
+                arg->r_type = 0; /* Does not take an arg. */
+              else if ((opts[idx].flags & ARGPARSE_OPT_OPTIONAL) )
+                arg->r_type = 0; /* Arg is optional.  */
+              else
+                arg->r_opt = ARGPARSE_MISSING_ARG;
+
+              break;
+           }
+          else if (state == 3)
+            {
+              /* No argument found.  */
+              if (in_alias)
+                arg->r_opt = ARGPARSE_MISSING_ARG;
+              else if (!(opts[idx].flags & ARGPARSE_TYPE_MASK))
+                arg->r_type = 0; /* Does not take an arg. */
+              else if ((opts[idx].flags & ARGPARSE_OPT_OPTIONAL))
+                arg->r_type = 0; /* No optional argument. */
+              else
+                arg->r_opt = ARGPARSE_MISSING_ARG;
+
+              break;
+           }
+          else if (state == 4)
+            {
+              /* Has an argument. */
+              if (in_alias)
+                {
+                  if (!buffer)
+                    arg->r_opt = ARGPARSE_UNEXPECTED_ARG;
+                  else
+                    {
+                      char *p;
+
+                      buffer[i] = 0;
+                      p = strpbrk (buffer, " \t");
+                      if (p)
+                        {
+                          *p++ = 0;
+                          trim_spaces (p);
+                       }
+                      if (!p || !*p)
+                        {
+                          xfree (buffer);
+                          arg->r_opt = ARGPARSE_INVALID_ALIAS;
+                        }
+                      else
+                        {
+                          store_alias (arg, buffer, p);
+                        }
+                   }
+               }
+              else if (!(opts[idx].flags & ARGPARSE_TYPE_MASK))
+                arg->r_opt = ARGPARSE_UNEXPECTED_ARG;
+              else
+                {
+                  char *p;
+
+                  if (!buffer)
+                    {
+                      keyword[i] = 0;
+                      buffer = xtrystrdup (keyword);
+                      if (!buffer)
+                        arg->r_opt = ARGPARSE_OUT_OF_CORE;
+                   }
+                  else
+                    buffer[i] = 0;
+
+                  if (buffer)
+                    {
+                      trim_spaces (buffer);
+                      p = buffer;
+                      if (*p == '"')
+                        {
+                          /* Remove quotes. */
+                          p++;
+                          if (*p && p[strlen(p)-1] == '\"' )
+                            p[strlen(p)-1] = 0;
+                        }
+                      if (!set_opt_arg (arg, opts[idx].flags, p))
+                        xfree (buffer);
+                    }
+                }
+              break;
+            }
+          else if (c == EOF)
+            {
+              ignore_invalid_option_clear (arg);
+              if (ferror (fp))
+                arg->r_opt = ARGPARSE_READ_ERROR;
+              else
+                arg->r_opt = 0; /* EOF. */
+              break;
+            }
+          state = 0;
+          i = 0;
+        }
+      else if (state == -1)
+        ; /* Skip. */
+      else if (state == 0 && isascii (c) && isspace(c))
+        ; /* Skip leading white space.  */
+      else if (state == 0 && c == '#' )
+        state = 1;     /* Start of a comment.  */
+      else if (state == 1)
+        ; /* Skip comments. */
+      else if (state == 2 && isascii (c) && isspace(c))
+        {
+          /* Check keyword.  */
+          keyword[i] = 0;
+          for (i=0; opts[i].short_opt; i++ )
+            if (opts[i].long_opt && !strcmp (opts[i].long_opt, keyword))
+              break;
+          idx = i;
+          arg->r_opt = opts[idx].short_opt;
+          if ((opts[idx].flags & ARGPARSE_OPT_IGNORE))
+            {
+              state = 1; /* Process like a comment.  */
+            }
+          else if (!opts[idx].short_opt)
+            {
+              if (!strcmp (keyword, "alias"))
+                {
+                  in_alias = 1;
+                  state = 3;
+                }
+              else if (!strcmp (keyword, "ignore-invalid-option"))
+                {
+                  if (ignore_invalid_option_add (arg, fp))
+                    {
+                      arg->r_opt = ARGPARSE_OUT_OF_CORE;
+                      break;
+                    }
+                  state = i = 0;
+                  ++*lineno;
+                }
+              else if (ignore_invalid_option_p (arg, keyword))
+                state = 1; /* Process like a comment.  */
+              else
+                {
+                  arg->r_opt = ((opts[idx].flags & ARGPARSE_OPT_COMMAND)
+                                ? ARGPARSE_INVALID_COMMAND
+                                : ARGPARSE_INVALID_OPTION);
+                  state = -1; /* Skip rest of line and leave.  */
+                }
+            }
+          else
+            state = 3;
+        }
+      else if (state == 3)
+        {
+          /* Skip leading spaces of the argument.  */
+          if (!isascii (c) || !isspace(c))
+            {
+              i = 0;
+              keyword[i++] = c;
+              state = 4;
+            }
+        }
+      else if (state == 4)
+        {
+          /* Collect the argument. */
+          if (buffer)
+            {
+              if (i < buflen-1)
+                buffer[i++] = c;
+              else
+                {
+                  char *tmp;
+                  size_t tmplen = buflen + 50;
+
+                  tmp = xtryrealloc (buffer, tmplen);
+                  if (tmp)
+                    {
+                      buflen = tmplen;
+                      buffer = tmp;
+                      buffer[i++] = c;
+                    }
+                  else
+                    {
+                      xfree (buffer);
+                      arg->r_opt = ARGPARSE_OUT_OF_CORE;
+                      break;
+                    }
+                }
+            }
+          else if (i < DIM(keyword)-1)
+            keyword[i++] = c;
+          else
+            {
+              size_t tmplen = DIM(keyword) + 50;
+              buffer = xtrymalloc (tmplen);
+              if (buffer)
+                {
+                  buflen = tmplen;
+                  memcpy(buffer, keyword, i);
+                  buffer[i++] = c;
+                }
+              else
+                {
+                  arg->r_opt = ARGPARSE_OUT_OF_CORE;
+                  break;
+                }
+            }
+        }
+      else if (i >= DIM(keyword)-1)
+        {
+          arg->r_opt = ARGPARSE_KEYWORD_TOO_LONG;
+          state = -1; /* Skip rest of line and leave.  */
+        }
+      else
+        {
+          keyword[i++] = c;
+          state = 2;
+        }
+    }
+
+  return arg->r_opt;
+}
+
+
+
+static int
+find_long_option( ARGPARSE_ARGS *arg,
+                 ARGPARSE_OPTS *opts, const char *keyword )
+{
+    int i;
+    size_t n;
+
+    (void)arg;
+
+    /* Would be better if we can do a binary search, but it is not
+       possible to reorder our option table because we would mess
+       up our help strings - What we can do is: Build a nice option
+       lookup table wehn this function is first invoked */
+    if( !*keyword )
+       return -1;
+    for(i=0; opts[i].short_opt; i++ )
+       if( opts[i].long_opt && !strcmp( opts[i].long_opt, keyword) )
+           return i;
+#if 0
+    {
+       ALIAS_DEF a;
+       /* see whether it is an alias */
+       for( a = args->internal.aliases; a; a = a->next ) {
+           if( !strcmp( a->name, keyword) ) {
+               /* todo: must parse the alias here */
+               args->internal.cur_alias = a;
+               return -3; /* alias available */
+           }
+       }
+    }
+#endif
+    /* not found, see whether it is an abbreviation */
+    /* aliases may not be abbreviated */
+    n = strlen( keyword );
+    for(i=0; opts[i].short_opt; i++ ) {
+       if( opts[i].long_opt && !strncmp( opts[i].long_opt, keyword, n ) ) {
+           int j;
+           for(j=i+1; opts[j].short_opt; j++ ) {
+               if( opts[j].long_opt
+                   && !strncmp( opts[j].long_opt, keyword, n ) )
+                   return -2;  /* abbreviation is ambiguous */
+           }
+           return i;
+       }
+    }
+    return -1;  /* Not found.  */
+}
+
+int
+arg_parse( ARGPARSE_ARGS *arg, ARGPARSE_OPTS *opts)
+{
+  int idx;
+  int argc;
+  char **argv;
+  char *s, *s2;
+  int i;
+
+  initialize( arg, NULL, NULL );
+  argc = *arg->argc;
+  argv = *arg->argv;
+  idx = arg->internal.idx;
+
+  if (!idx && argc && !(arg->flags & ARGPARSE_FLAG_ARG0))
+    {
+      /* Skip the first argument.  */
+      argc--; argv++; idx++;
+    }
+
+ next_one:
+  if (!argc)
+    {
+      /* No more args.  */
+      arg->r_opt = 0;
+      goto leave; /* Ready. */
+    }
+
+  s = *argv;
+  arg->internal.last = s;
+
+  if (arg->internal.stopped && (arg->flags & ARGPARSE_FLAG_ALL))
+    {
+      arg->r_opt = ARGPARSE_IS_ARG;  /* Not an option but an argument.  */
+      arg->r_type = 2;
+      arg->r.ret_str = s;
+      argc--; argv++; idx++; /* set to next one */
+    }
+  else if( arg->internal.stopped )
+    {
+      arg->r_opt = 0;
+      goto leave; /* Ready.  */
+    }
+  else if ( *s == '-' && s[1] == '-' )
+    {
+      /* Long option.  */
+      char *argpos;
+
+      arg->internal.inarg = 0;
+      if (!s[2] && !(arg->flags & ARGPARSE_FLAG_NOSTOP))
+        {
+          /* Stop option processing.  */
+          arg->internal.stopped = 1;
+          arg->flags |= ARGPARSE_FLAG_STOP_SEEN;
+          argc--; argv++; idx++;
+          goto next_one;
+       }
+
+      argpos = strchr( s+2, '=' );
+      if ( argpos )
+        *argpos = 0;
+      i = find_long_option ( arg, opts, s+2 );
+      if ( argpos )
+        *argpos = '=';
+
+      if ( i < 0 && !strcmp ( "help", s+2) )
+        show_help (opts, arg->flags);
+      else if ( i < 0 && !strcmp ( "version", s+2) )
+        {
+          if (!(arg->flags & ARGPARSE_FLAG_NOVERSION))
+            {
+              show_version ();
+              exit(0);
+            }
+       }
+      else if ( i < 0 && !strcmp( "warranty", s+2))
+        {
+          writestrings (0, strusage (16), "\n", NULL);
+          exit (0);
+       }
+      else if ( i < 0 && !strcmp( "dump-options", s+2) )
+        {
+          for (i=0; opts[i].short_opt; i++ )
+            {
+              if (opts[i].long_opt && !(opts[i].flags & ARGPARSE_OPT_IGNORE))
+                writestrings (0, "--", opts[i].long_opt, "\n", NULL);
+           }
+          writestrings (0, "--dump-options\n--help\n--version\n--warranty\n",
+                        NULL);
+          exit (0);
+       }
+
+      if ( i == -2 )
+        arg->r_opt = ARGPARSE_AMBIGUOUS_OPTION;
+      else if ( i == -1 )
+        {
+          arg->r_opt = ARGPARSE_INVALID_OPTION;
+          arg->r.ret_str = s+2;
+       }
+      else
+        arg->r_opt = opts[i].short_opt;
+      if ( i < 0 )
+        ;
+      else if ( (opts[i].flags & ARGPARSE_TYPE_MASK) )
+        {
+          if ( argpos )
+            {
+              s2 = argpos+1;
+              if ( !*s2 )
+                s2 = NULL;
+           }
+          else
+            s2 = argv[1];
+          if ( !s2 && (opts[i].flags & ARGPARSE_OPT_OPTIONAL) )
+            {
+              arg->r_type = ARGPARSE_TYPE_NONE; /* Argument is optional.  */
+           }
+          else if ( !s2 )
+            {
+              arg->r_opt = ARGPARSE_MISSING_ARG;
+           }
+          else if ( !argpos && *s2 == '-'
+                    && (opts[i].flags & ARGPARSE_OPT_OPTIONAL) )
+            {
+              /* The argument is optional and the next seems to be an
+                 option.  We do not check this possible option but
+                 assume no argument */
+              arg->r_type = ARGPARSE_TYPE_NONE;
+           }
+          else
+            {
+              set_opt_arg (arg, opts[i].flags, s2);
+              if ( !argpos )
+                {
+                  argc--; argv++; idx++; /* Skip one.  */
+               }
+           }
+       }
+      else
+        {
+          /* Does not take an argument. */
+          if ( argpos )
+            arg->r_type = ARGPARSE_UNEXPECTED_ARG;
+          else
+            arg->r_type = 0;
+       }
+      argc--; argv++; idx++; /* Set to next one.  */
+    }
+    else if ( (*s == '-' && s[1]) || arg->internal.inarg )
+      {
+        /* Short option.  */
+       int dash_kludge = 0;
+
+       i = 0;
+       if ( !arg->internal.inarg )
+          {
+           arg->internal.inarg++;
+           if ( (arg->flags & ARGPARSE_FLAG_ONEDASH) )
+              {
+                for (i=0; opts[i].short_opt; i++ )
+                  if ( opts[i].long_opt && !strcmp (opts[i].long_opt, s+1))
+                    {
+                      dash_kludge = 1;
+                      break;
+                   }
+              }
+          }
+       s += arg->internal.inarg;
+
+       if (!dash_kludge )
+          {
+           for (i=0; opts[i].short_opt; i++ )
+              if ( opts[i].short_opt == *s )
+                break;
+          }
+
+       if ( !opts[i].short_opt && ( *s == 'h' || *s == '?' ) )
+          show_help (opts, arg->flags);
+
+       arg->r_opt = opts[i].short_opt;
+       if (!opts[i].short_opt )
+          {
+           arg->r_opt = (opts[i].flags & ARGPARSE_OPT_COMMAND)?
+              ARGPARSE_INVALID_COMMAND:ARGPARSE_INVALID_OPTION;
+           arg->internal.inarg++; /* Point to the next arg.  */
+           arg->r.ret_str = s;
+          }
+       else if ( (opts[i].flags & ARGPARSE_TYPE_MASK) )
+          {
+           if ( s[1] && !dash_kludge )
+              {
+               s2 = s+1;
+               set_opt_arg (arg, opts[i].flags, s2);
+              }
+           else
+              {
+               s2 = argv[1];
+               if ( !s2 && (opts[i].flags & ARGPARSE_OPT_OPTIONAL) )
+                  {
+                   arg->r_type = ARGPARSE_TYPE_NONE;
+                  }
+               else if ( !s2 )
+                  {
+                   arg->r_opt = ARGPARSE_MISSING_ARG;
+                  }
+               else if ( *s2 == '-' && s2[1]
+                          && (opts[i].flags & ARGPARSE_OPT_OPTIONAL) )
+                  {
+                   /* The argument is optional and the next seems to
+                      be an option.  We do not check this possible
+                      option but assume no argument.  */
+                   arg->r_type = ARGPARSE_TYPE_NONE;
+                  }
+               else
+                  {
+                   set_opt_arg (arg, opts[i].flags, s2);
+                   argc--; argv++; idx++; /* Skip one.  */
+                  }
+              }
+           s = "x"; /* This is so that !s[1] yields false.  */
+          }
+       else
+          {
+            /* Does not take an argument.  */
+           arg->r_type = ARGPARSE_TYPE_NONE;
+           arg->internal.inarg++; /* Point to the next arg.  */
+          }
+       if ( !s[1] || dash_kludge )
+          {
+            /* No more concatenated short options.  */
+           arg->internal.inarg = 0;
+           argc--; argv++; idx++;
+          }
+      }
+  else if ( arg->flags & ARGPARSE_FLAG_MIXED )
+    {
+      arg->r_opt = ARGPARSE_IS_ARG;
+      arg->r_type = 2;
+      arg->r.ret_str = s;
+      argc--; argv++; idx++; /* Set to next one.  */
+    }
+  else
+    {
+      arg->internal.stopped = 1; /* Stop option processing.  */
+      goto next_one;
+    }
+
+ leave:
+  *arg->argc = argc;
+  *arg->argv = argv;
+  arg->internal.idx = idx;
+  return arg->r_opt;
+}
+
+
+/* Returns: -1 on error, 0 for an integer type and 1 for a non integer
+   type argument.  */
+static int
+set_opt_arg (ARGPARSE_ARGS *arg, unsigned flags, char *s)
+{
+  int base = (flags & ARGPARSE_OPT_PREFIX)? 0 : 10;
+  long l;
+
+  switch ( (arg->r_type = (flags & ARGPARSE_TYPE_MASK)) )
+    {
+    case ARGPARSE_TYPE_LONG:
+    case ARGPARSE_TYPE_INT:
+      errno = 0;
+      l = strtol (s, NULL, base);
+      if ((l == LONG_MIN || l == LONG_MAX) && errno == ERANGE)
+        {
+          arg->r_opt = ARGPARSE_INVALID_ARG;
+          return -1;
+        }
+      if (arg->r_type == ARGPARSE_TYPE_LONG)
+        arg->r.ret_long = l;
+      else if ( (l < 0 && l < INT_MIN) || l > INT_MAX )
+        {
+          arg->r_opt = ARGPARSE_INVALID_ARG;
+          return -1;
+        }
+      else
+        arg->r.ret_int = (int)l;
+      return 0;
+
+    case ARGPARSE_TYPE_ULONG:
+      while (isascii (*s) && isspace(*s))
+        s++;
+      if (*s == '-')
+        {
+          arg->r.ret_ulong = 0;
+          arg->r_opt = ARGPARSE_INVALID_ARG;
+          return -1;
+        }
+      errno = 0;
+      arg->r.ret_ulong = strtoul (s, NULL, base);
+      if (arg->r.ret_ulong == ULONG_MAX && errno == ERANGE)
+        {
+          arg->r_opt = ARGPARSE_INVALID_ARG;
+          return -1;
+        }
+      return 0;
+
+    case ARGPARSE_TYPE_STRING:
+    default:
+      arg->r.ret_str = s;
+      return 1;
+    }
+}
+
+
+static size_t
+long_opt_strlen( ARGPARSE_OPTS *o )
+{
+  size_t n = strlen (o->long_opt);
+
+  if ( o->description && *o->description == '|' )
+    {
+      const char *s;
+      int is_utf8 = is_native_utf8 ();
+
+      s=o->description+1;
+      if ( *s != '=' )
+        n++;
+      /* For a (mostly) correct length calculation we exclude
+         continuation bytes (10xxxxxx) if we are on a native utf8
+         terminal. */
+      for (; *s && *s != '|'; s++ )
+        if ( is_utf8 && (*s&0xc0) != 0x80 )
+          n++;
+    }
+  return n;
+}
+
+
+/****************
+ * Print formatted help. The description string has some special
+ * meanings:
+ *  - A description string which is "@" suppresses help output for
+ *    this option
+ *  - a description,ine which starts with a '@' and is followed by
+ *    any other characters is printed as is; this may be used for examples
+ *    ans such.
+ *  - A description which starts with a '|' outputs the string between this
+ *    bar and the next one as arguments of the long option.
+ */
+static void
+show_help (ARGPARSE_OPTS *opts, unsigned int flags)
+{
+  const char *s;
+  char tmp[2];
+
+  show_version ();
+  writestrings (0, "\n", NULL);
+  s = strusage (42);
+  if (s && *s == '1')
+    {
+      s = strusage (40);
+      writestrings (1, s, NULL);
+      if (*s && s[strlen(s)] != '\n')
+        writestrings (1, "\n", NULL);
+    }
+  s = strusage(41);
+  writestrings (0, s, "\n", NULL);
+  if ( opts[0].description )
+    {
+      /* Auto format the option description.  */
+      int i,j, indent;
+
+      /* Get max. length of long options.  */
+      for (i=indent=0; opts[i].short_opt; i++ )
+        {
+          if ( opts[i].long_opt )
+            if ( !opts[i].description || *opts[i].description != '@' )
+              if ( (j=long_opt_strlen(opts+i)) > indent && j < 35 )
+                indent = j;
+       }
+
+      /* Example: " -v, --verbose   Viele Sachen ausgeben" */
+      indent += 10;
+      if ( *opts[0].description != '@' )
+        writestrings (0, "Options:", "\n", NULL);
+      for (i=0; opts[i].short_opt; i++ )
+        {
+          s = map_static_macro_string (_( opts[i].description ));
+          if ( s && *s== '@' && !s[1] ) /* Hide this line.  */
+            continue;
+          if ( s && *s == '@' )  /* Unindented comment only line.  */
+            {
+              for (s++; *s; s++ )
+                {
+                  if ( *s == '\n' )
+                    {
+                      if( s[1] )
+                        writestrings (0, "\n", NULL);
+                   }
+                  else
+                    {
+                      tmp[0] = *s;
+                      tmp[1] = 0;
+                      writestrings (0, tmp, NULL);
+                    }
+                }
+              writestrings (0, "\n", NULL);
+              continue;
+           }
+
+          j = 3;
+          if ( opts[i].short_opt < 256 )
+            {
+              tmp[0] = opts[i].short_opt;
+              tmp[1] = 0;
+              writestrings (0, " -", tmp, NULL );
+              if ( !opts[i].long_opt )
+                {
+                  if (s && *s == '|' )
+                    {
+                      writestrings (0, " ", NULL); j++;
+                      for (s++ ; *s && *s != '|'; s++, j++ )
+                        {
+                          tmp[0] = *s;
+                          tmp[1] = 0;
+                          writestrings (0, tmp, NULL);
+                        }
+                      if ( *s )
+                        s++;
+                   }
+               }
+           }
+          else
+            writestrings (0, "   ", NULL);
+          if ( opts[i].long_opt )
+            {
+              tmp[0] = opts[i].short_opt < 256?',':' ';
+              tmp[1] = 0;
+              j += writestrings (0, tmp, " --", opts[i].long_opt, NULL);
+              if (s && *s == '|' )
+                {
+                  if ( *++s != '=' )
+                    {
+                      writestrings (0, " ", NULL);
+                      j++;
+                   }
+                  for ( ; *s && *s != '|'; s++, j++ )
+                    {
+                      tmp[0] = *s;
+                      tmp[1] = 0;
+                      writestrings (0, tmp, NULL);
+                    }
+                  if ( *s )
+                    s++;
+               }
+              writestrings (0, "   ", NULL);
+              j += 3;
+           }
+          for (;j < indent; j++ )
+            writestrings (0, " ", NULL);
+          if ( s )
+            {
+              if ( *s && j > indent )
+                {
+                  writestrings (0, "\n", NULL);
+                  for (j=0;j < indent; j++ )
+                    writestrings (0, " ", NULL);
+               }
+              for (; *s; s++ )
+                {
+                  if ( *s == '\n' )
+                    {
+                      if ( s[1] )
+                        {
+                          writestrings (0, "\n", NULL);
+                          for (j=0; j < indent; j++ )
+                            writestrings (0, " ", NULL);
+                       }
+                   }
+                  else
+                    {
+                      tmp[0] = *s;
+                      tmp[1] = 0;
+                      writestrings (0, tmp, NULL);
+                    }
+               }
+           }
+          writestrings (0, "\n", NULL);
+       }
+       if ( (flags & ARGPARSE_FLAG_ONEDASH) )
+          writestrings (0, "\n(A single dash may be used "
+                        "instead of the double ones)\n", NULL);
+    }
+  if ( (s=strusage(19)) )
+    {
+      writestrings (0, "\n", NULL);
+      writestrings (0, s, NULL);
+    }
+  flushstrings (0);
+  exit(0);
+}
+
+static void
+show_version ()
+{
+  const char *s;
+  int i;
+
+  /* Version line.  */
+  writestrings (0, strusage (11), NULL);
+  if ((s=strusage (12)))
+    writestrings (0, " (", s, ")", NULL);
+  writestrings (0, " ", strusage (13), "\n", NULL);
+  /* Additional version lines. */
+  for (i=20; i < 30; i++)
+    if ((s=strusage (i)))
+      writestrings (0, s, "\n", NULL);
+  /* Copyright string.  */
+  if ((s=strusage (14)))
+    writestrings (0, s, "\n", NULL);
+  /* Licence string.  */
+  if( (s=strusage (10)) )
+    writestrings (0, s, "\n", NULL);
+  /* Copying conditions. */
+  if ( (s=strusage(15)) )
+    writestrings (0, s, NULL);
+  /* Thanks. */
+  if ((s=strusage(18)))
+    writestrings (0, s, NULL);
+  /* Additional program info. */
+  for (i=30; i < 40; i++ )
+    if ( (s=strusage (i)) )
+      writestrings (0, s, NULL);
+  flushstrings (0);
+}
+
+
+void
+usage (int level)
+{
+  const char *p;
+
+  if (!level)
+    {
+      writestrings (1, strusage(11), " ", strusage(13), "; ",
+                    strusage (14), "\n", NULL);
+      flushstrings (1);
+    }
+  else if (level == 1)
+    {
+      p = strusage (40);
+      writestrings (1, p, NULL);
+      if (*p && p[strlen(p)] != '\n')
+        writestrings (1, "\n", NULL);
+      exit (2);
+    }
+  else if (level == 2)
+    {
+      p = strusage (42);
+      if (p && *p == '1')
+        {
+          p = strusage (40);
+          writestrings (1, p, NULL);
+          if (*p && p[strlen(p)] != '\n')
+            writestrings (1, "\n", NULL);
+        }
+      writestrings (0, strusage(41), "\n", NULL);
+      exit (0);
+    }
+}
+
+/* Level
+ *     0: Print copyright string to stderr
+ *     1: Print a short usage hint to stderr and terminate
+ *     2: Print a long usage hint to stdout and terminate
+ *    10: Return license info string
+ *    11: Return the name of the program
+ *    12: Return optional name of package which includes this program.
+ *    13: version  string
+ *    14: copyright string
+ *    15: Short copying conditions (with LFs)
+ *    16: Long copying conditions (with LFs)
+ *    17: Optional printable OS name
+ *    18: Optional thanks list (with LFs)
+ *    19: Bug report info
+ *20..29: Additional lib version strings.
+ *30..39: Additional program info (with LFs)
+ *    40: short usage note (with LF)
+ *    41: long usage note (with LF)
+ *    42: Flag string:
+ *          First char is '1':
+ *             The short usage notes needs to be printed
+ *             before the long usage note.
+ */
+const char *
+strusage( int level )
+{
+  const char *p = strusage_handler? strusage_handler(level) : NULL;
+
+  if ( p )
+    return map_static_macro_string (p);
+
+  switch ( level )
+    {
+
+    case 10:
+#if ARGPARSE_GPL_VERSION == 3
+      p = ("License GPLv3+: GNU GPL version 3 or later "
+           "<http://gnu.org/licenses/gpl.html>");
+#else
+      p = ("License GPLv2+: GNU GPL version 2 or later "
+           "<http://gnu.org/licenses/>");
+#endif
+      break;
+    case 11: p = "foo"; break;
+    case 13: p = "0.0"; break;
+    case 14: p = ARGPARSE_CRIGHT_STR; break;
+    case 15: p =
+"This is free software: you are free to change and redistribute it.\n"
+"There is NO WARRANTY, to the extent permitted by law.\n";
+      break;
+    case 16: p =
+"This is free software; you can redistribute it and/or modify\n"
+"it under the terms of the GNU General Public License as published by\n"
+"the Free Software Foundation; either version "
+ARGPARSE_STR2(ARGPARSE_GPL_VERSION)
+" of the License, or\n"
+"(at your option) any later version.\n\n"
+"It is distributed in the hope that it will be useful,\n"
+"but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
+"MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n"
+"GNU General Public License for more details.\n\n"
+"You should have received a copy of the GNU General Public License\n"
+"along with this software.  If not, see <http://www.gnu.org/licenses/>.\n";
+      break;
+    case 40: /* short and long usage */
+    case 41: p = ""; break;
+    }
+
+  return p;
+}
+
+
+/* Set the usage handler.  This function is basically a constructor.  */
+void
+set_strusage ( const char *(*f)( int ) )
+{
+  strusage_handler = f;
+}
+
+
+#ifdef TEST
+static struct {
+    int verbose;
+    int debug;
+    char *outfile;
+    char *crf;
+    int myopt;
+    int echo;
+    int a_long_one;
+} opt;
+
+int
+main(int argc, char **argv)
+{
+  ARGPARSE_OPTS opts[] = {
+    ARGPARSE_x('v', "verbose", NONE, 0, "Laut sein"),
+    ARGPARSE_s_n('e', "echo"   , ("Zeile ausgeben, damit wir sehen, "
+                                  "was wir eingegeben haben")),
+    ARGPARSE_s_n('d', "debug", "Debug\nfalls mal etwas\nschief geht"),
+    ARGPARSE_s_s('o', "output", 0 ),
+    ARGPARSE_o_s('c', "cross-ref", "cross-reference erzeugen\n" ),
+    /* Note that on a non-utf8 terminal the ß might garble the output. */
+    ARGPARSE_s_n('s', "street","|Straße|set the name of the street to Straße"),
+    ARGPARSE_o_i('m', "my-option", 0),
+    ARGPARSE_s_n(500, "a-long-option", 0 ),
+    ARGPARSE_end()
+  };
+  ARGPARSE_ARGS pargs = { &argc, &argv, (ARGPARSE_FLAG_ALL
+                                         | ARGPARSE_FLAG_MIXED
+                                         | ARGPARSE_FLAG_ONEDASH) };
+  int i;
+
+  while (arg_parse  (&pargs, opts))
+    {
+      switch (pargs.r_opt)
+        {
+        case ARGPARSE_IS_ARG :
+          printf ("arg='%s'\n", pargs.r.ret_str);
+          break;
+        case 'v': opt.verbose++; break;
+        case 'e': opt.echo++; break;
+        case 'd': opt.debug++; break;
+        case 'o': opt.outfile = pargs.r.ret_str; break;
+        case 'c': opt.crf = pargs.r_type? pargs.r.ret_str:"a.crf"; break;
+        case 'm': opt.myopt = pargs.r_type? pargs.r.ret_int : 1; break;
+        case 500: opt.a_long_one++;  break;
+        default : pargs.err = ARGPARSE_PRINT_WARNING; break;
+       }
+    }
+  for (i=0; i < argc; i++ )
+    printf ("%3d -> (%s)\n", i, argv[i] );
+  puts ("Options:");
+  if (opt.verbose)
+    printf ("  verbose=%d\n", opt.verbose );
+  if (opt.debug)
+    printf ("  debug=%d\n", opt.debug );
+  if (opt.outfile)
+    printf ("  outfile='%s'\n", opt.outfile );
+  if (opt.crf)
+    printf ("  crffile='%s'\n", opt.crf );
+  if (opt.myopt)
+    printf ("  myopt=%d\n", opt.myopt );
+  if (opt.a_long_one)
+    printf ("  a-long-one=%d\n", opt.a_long_one );
+  if (opt.echo)
+    printf ("  echo=%d\n", opt.echo );
+
+  return 0;
+}
+#endif /*TEST*/
+
+/**** bottom of file ****/
diff --git a/src/argparse.h b/src/argparse.h
new file mode 100644 (file)
index 0000000..10b838f
--- /dev/null
@@ -0,0 +1,203 @@
+/* argparse.h - Argument parser for option handling.
+ *     Copyright (C) 1998,1999,2000,2001,2006 Free Software Foundation, Inc.
+ *
+ * This file is part of GnuPG.
+ *
+ * GnuPG is free software; you can redistribute it and/or modify it
+ * under the terms of either
+ *
+ *   - the GNU Lesser General Public License as published by the Free
+ *     Software Foundation; either version 3 of the License, or (at
+ *     your option) any later version.
+ *
+ * or
+ *
+ *   - the GNU General Public License as published by the Free
+ *     Software Foundation; either version 2 of the License, or (at
+ *     your option) any later version.
+ *
+ * or both in parallel, as here.
+ *
+ * GnuPG 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 copies of the GNU General Public License
+ * and the GNU Lesser General Public License along with this program;
+ * if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef GNUPG_COMMON_ARGPARSE_H
+#define GNUPG_COMMON_ARGPARSE_H
+
+#include <stdio.h>
+
+typedef struct
+{
+  int  *argc;        /* Pointer to ARGC (value subject to change). */
+  char ***argv;              /* Pointer to ARGV (value subject to change). */
+  unsigned int flags; /* Global flags.  May be set prior to calling the
+                         parser.  The parser may change the value.  */
+  int err;            /* Print error description for last option.
+                         Either 0,  ARGPARSE_PRINT_WARNING or
+                         ARGPARSE_PRINT_ERROR.  */
+
+  int r_opt;         /* Returns option code. */
+  int r_type;        /* Returns type of option value.  */
+  union {
+    int   ret_int;
+    long  ret_long;
+    unsigned long ret_ulong;
+    char *ret_str;
+  } r;               /* Return values */
+
+  struct {
+    int idx;
+    int inarg;
+    int stopped;
+    const char *last;
+    void *aliases;
+    const void *cur_alias;
+    void *iio_list;
+  } internal;      /* Private - do not change. */
+} ARGPARSE_ARGS;
+
+typedef struct
+{
+  int          short_opt;
+  const char  *long_opt;
+  unsigned int flags;
+  const char  *description; /* Optional option description. */
+} ARGPARSE_OPTS;
+
+
+/* Global flags (ARGPARSE_ARGS).  */
+#define ARGPARSE_FLAG_KEEP       1   /* Do not remove options form argv.     */
+#define ARGPARSE_FLAG_ALL        2   /* Do not stop at last option but return
+                                        remaining args with R_OPT set to -1. */
+#define ARGPARSE_FLAG_MIXED      4   /* Assume options and args are mixed.   */
+#define ARGPARSE_FLAG_NOSTOP     8   /* Do not stop processing at "--".      */
+#define ARGPARSE_FLAG_ARG0      16   /* Do not skip the first arg.           */
+#define ARGPARSE_FLAG_ONEDASH   32   /* Allow long options with one dash.    */
+#define ARGPARSE_FLAG_NOVERSION 64   /* No output for "--version".           */
+
+#define ARGPARSE_FLAG_STOP_SEEN 256  /* Set to true if a "--" has been seen. */
+
+/* Flags for each option (ARGPARSE_OPTS).  The type code may be
+   ORed with the OPT flags.  */
+#define ARGPARSE_TYPE_NONE        0  /* Does not take an argument.        */
+#define ARGPARSE_TYPE_INT         1  /* Takes an int argument.            */
+#define ARGPARSE_TYPE_STRING      2  /* Takes a string argument.          */
+#define ARGPARSE_TYPE_LONG        3  /* Takes a long argument.            */
+#define ARGPARSE_TYPE_ULONG       4  /* Takes an unsigned long argument.  */
+#define ARGPARSE_OPT_OPTIONAL (1<<3) /* Argument is optional.             */
+#define ARGPARSE_OPT_PREFIX   (1<<4) /* Allow 0x etc. prefixed values.    */
+#define ARGPARSE_OPT_IGNORE   (1<<6) /* Ignore command or option.         */
+#define ARGPARSE_OPT_COMMAND  (1<<7) /* The argument is a command.        */
+
+#define ARGPARSE_TYPE_MASK  7  /* Mask for the type values (internal).  */
+
+/* A set of macros to make option definitions easier to read.  */
+#define ARGPARSE_x(s,l,t,f,d) \
+     { (s), (l), ARGPARSE_TYPE_ ## t | (f), (d) }
+
+#define ARGPARSE_s(s,l,t,d) \
+     { (s), (l), ARGPARSE_TYPE_ ## t, (d) }
+#define ARGPARSE_s_n(s,l,d) \
+     { (s), (l), ARGPARSE_TYPE_NONE, (d) }
+#define ARGPARSE_s_i(s,l,d) \
+     { (s), (l), ARGPARSE_TYPE_INT, (d) }
+#define ARGPARSE_s_s(s,l,d) \
+     { (s), (l), ARGPARSE_TYPE_STRING, (d) }
+#define ARGPARSE_s_l(s,l,d) \
+     { (s), (l), ARGPARSE_TYPE_LONG, (d) }
+#define ARGPARSE_s_u(s,l,d) \
+     { (s), (l), ARGPARSE_TYPE_ULONG, (d) }
+
+#define ARGPARSE_o(s,l,t,d) \
+     { (s), (l), (ARGPARSE_TYPE_ ## t  | ARGPARSE_OPT_OPTIONAL), (d) }
+#define ARGPARSE_o_n(s,l,d) \
+     { (s), (l), (ARGPARSE_TYPE_NONE   | ARGPARSE_OPT_OPTIONAL), (d) }
+#define ARGPARSE_o_i(s,l,d) \
+     { (s), (l), (ARGPARSE_TYPE_INT    | ARGPARSE_OPT_OPTIONAL), (d) }
+#define ARGPARSE_o_s(s,l,d) \
+     { (s), (l), (ARGPARSE_TYPE_STRING | ARGPARSE_OPT_OPTIONAL), (d) }
+#define ARGPARSE_o_l(s,l,d) \
+     { (s), (l), (ARGPARSE_TYPE_LONG   | ARGPARSE_OPT_OPTIONAL), (d) }
+#define ARGPARSE_o_u(s,l,d) \
+     { (s), (l), (ARGPARSE_TYPE_ULONG  | ARGPARSE_OPT_OPTIONAL), (d) }
+
+#define ARGPARSE_p(s,l,t,d) \
+     { (s), (l), (ARGPARSE_TYPE_ ## t  | ARGPARSE_OPT_PREFIX), (d) }
+#define ARGPARSE_p_n(s,l,d) \
+     { (s), (l), (ARGPARSE_TYPE_NONE   | ARGPARSE_OPT_PREFIX), (d) }
+#define ARGPARSE_p_i(s,l,d) \
+     { (s), (l), (ARGPARSE_TYPE_INT    | ARGPARSE_OPT_PREFIX), (d) }
+#define ARGPARSE_p_s(s,l,d) \
+     { (s), (l), (ARGPARSE_TYPE_STRING | ARGPARSE_OPT_PREFIX), (d) }
+#define ARGPARSE_p_l(s,l,d) \
+     { (s), (l), (ARGPARSE_TYPE_LONG   | ARGPARSE_OPT_PREFIX), (d) }
+#define ARGPARSE_p_u(s,l,d) \
+     { (s), (l), (ARGPARSE_TYPE_ULONG  | ARGPARSE_OPT_PREFIX), (d) }
+
+#define ARGPARSE_op(s,l,t,d) \
+     { (s), (l), (ARGPARSE_TYPE_ ## t \
+                  | ARGPARSE_OPT_OPTIONAL | ARGPARSE_OPT_PREFIX), (d) }
+#define ARGPARSE_op_n(s,l,d) \
+     { (s), (l), (ARGPARSE_TYPE_NONE \
+                  | ARGPARSE_OPT_OPTIONAL | ARGPARSE_OPT_PREFIX), (d) }
+#define ARGPARSE_op_i(s,l,d) \
+     { (s), (l), (ARGPARSE_TYPE_INT \
+                  | ARGPARSE_OPT_OPTIONAL | ARGPARSE_OPT_PREFIX), (d) }
+#define ARGPARSE_op_s(s,l,d) \
+     { (s), (l), (ARGPARSE_TYPE_STRING \
+                  | ARGPARSE_OPT_OPTIONAL | ARGPARSE_OPT_PREFIX), (d) }
+#define ARGPARSE_op_l(s,l,d) \
+     { (s), (l), (ARGPARSE_TYPE_LONG \
+                  | ARGPARSE_OPT_OPTIONAL | ARGPARSE_OPT_PREFIX), (d) }
+#define ARGPARSE_op_u(s,l,d) \
+     { (s), (l), (ARGPARSE_TYPE_ULONG \
+                  | ARGPARSE_OPT_OPTIONAL | ARGPARSE_OPT_PREFIX), (d) }
+
+#define ARGPARSE_c(s,l,d) \
+     { (s), (l), (ARGPARSE_TYPE_NONE | ARGPARSE_OPT_COMMAND), (d) }
+
+#define ARGPARSE_ignore(s,l) \
+     { (s), (l), (ARGPARSE_OPT_IGNORE), "@" }
+
+#define ARGPARSE_group(s,d) \
+     { (s), NULL, 0, (d) }
+
+#define ARGPARSE_end()  { 0, NULL, 0, NULL }
+
+
+/* Other constants.  */
+#define ARGPARSE_PRINT_WARNING  1
+#define ARGPARSE_PRINT_ERROR    2
+
+
+/* Error values.  */
+#define ARGPARSE_IS_ARG            (-1)
+#define ARGPARSE_INVALID_OPTION    (-2)
+#define ARGPARSE_MISSING_ARG       (-3)
+#define ARGPARSE_KEYWORD_TOO_LONG  (-4)
+#define ARGPARSE_READ_ERROR        (-5)
+#define ARGPARSE_UNEXPECTED_ARG    (-6)
+#define ARGPARSE_INVALID_COMMAND   (-7)
+#define ARGPARSE_AMBIGUOUS_OPTION  (-8)
+#define ARGPARSE_AMBIGUOUS_COMMAND (-9)
+#define ARGPARSE_INVALID_ALIAS     (-10)
+#define ARGPARSE_OUT_OF_CORE       (-11)
+#define ARGPARSE_INVALID_ARG       (-12)
+
+
+int arg_parse (ARGPARSE_ARGS *arg, ARGPARSE_OPTS *opts);
+int optfile_parse (FILE *fp, const char *filename, unsigned *lineno,
+                  ARGPARSE_ARGS *arg, ARGPARSE_OPTS *opts);
+void usage (int level);
+const char *strusage (int level);
+void set_strusage (const char *(*f)( int ));
+void argparse_register_outfnc (int (*fnc)(int, const char *));
+
+#endif /*GNUPG_COMMON_ARGPARSE_H*/
index e5e5707..557ed64 100644 (file)
 #ifdef HAVE_LOCALE_H
 #include <locale.h>
 #endif
-#ifdef HAVE_ARGP_H
-#include <argp.h>
-#endif
 
 #include <assuan.h>
 
+#include "argparse.h"
 #include "gpgme.h"
 
 /* GCC attributes.  */
 
 
 \f
-#ifndef HAVE_ARGP_H
-/* Minimal argp implementation.  */
-
-/* Differences to ARGP:
-   argp_program_version: Required.
-   argp_program_bug_address: Required.
-   argp_program_version_hook: Not supported.
-   argp_err_exit_status: Required.
-   struct argp: Children and help_filter not supported.
-   argp_domain: Not supported.
-   struct argp_option: Group not supported.  Options are printed in
-   order given.  Flags OPTION_ALIAS, OPTION_DOC and OPTION_NO_USAGE
-   are not supported.
-   argp_parse: No flags are supported (ARGP_PARSE_ARGV0, ARGP_NO_ERRS,
-   ARGP_NO_ARGS, ARGP_IN_ORDER, ARGP_NO_HELP, ARGP_NO_EXIT,
-   ARGP_LONG_ONLY, ARGP_SILENT).  ARGP must not be NULL.
-   argp_help: Flag ARGP_HELP_LONG_ONLY not supported.
-   argp_state: argc, argv, next may not be modified and should not be used.  */
-
-extern const char *argp_program_version;
-extern const char *argp_program_bug_address;
-extern error_t argp_err_exit_status;
-
-struct argp_option
-{
-  const char *name;
-  int key;
-  const char *arg;
-#define OPTION_ARG_OPTIONAL 0x1
-#define OPTION_HIDDEN 0x2
-  int flags;
-  const char *doc;
-  int group;
-};
-
-struct argp;
-struct argp_state
-{
-  const struct argp *const root_argp;
-  int argc;
-  char **argv;
-  int next;
-  unsigned flags;
-  unsigned arg_num;
-  int quoted;
-  void *input;
-  void **child_inputs;
-  void *hook;
-  char *name;
-  FILE *err_stream;
-  FILE *out_stream;
-  void *pstate;
-};
-
-#ifdef EDEADLK
-# define ARGP_ERR_UNKNOWN EDEADLK /* POSIX */
-#else
-# define ARGP_ERR_UNKNOWN EDEADLOCK /* *GNU/kFreebsd does not define this) */
-#endif
-#define ARGP_KEY_ARG 0
-#define ARGP_KEY_ARGS 0x1000006
-#define ARGP_KEY_END 0x1000001
-#define ARGP_KEY_NO_ARGS 0x1000002
-#define ARGP_KEY_INIT 0x1000003
-#define ARGP_KEY_FINI 0x1000007
-#define ARGP_KEY_SUCCESS 0x1000004
-#define ARGP_KEY_ERROR 0x1000005
-typedef error_t (*argp_parser_t) (int key, char *arg, struct argp_state *state);
-
-struct argp
-{
-  const struct argp_option *options;
-  argp_parser_t parser;
-  const char *args_doc;
-  const char *doc;
-
-  const struct argp_child *children;
-  char *(*help_filter) (int key, const char *text, void *input);
-  const char *argp_domain;
-};
-
-#define ARGP_HELP_USAGE ARGP_HELP_SHORT_USAGE
-#define ARGP_HELP_SHORT_USAGE 0x02
-#define ARGP_HELP_SEE 0x04
-#define ARGP_HELP_LONG 0x08
-#define ARGP_HELP_PRE_DOC 0x10
-#define ARGP_HELP_POST_DOC 0x20
-#define ARGP_HELP_DOC (ARGP_HELP_PRE_DOC | ARGP_HELP_POST_DOC)
-#define ARGP_HELP_BUG_ADDR 0x40
-#define ARGP_HELP_EXIT_ERR 0x100
-#define ARGP_HELP_EXIT_OK 0x200
-#define ARGP_HELP_STD_ERR (ARGP_HELP_SEE | ARGP_HELP_EXIT_ERR)
-#define ARGP_HELP_STD_USAGE \
-  (ARGP_HELP_SHORT_USAGE | ARGP_HELP_SEE | ARGP_HELP_EXIT_ERR)
-#define ARGP_HELP_STD_HELP \
-  (ARGP_HELP_SHORT_USAGE | ARGP_HELP_LONG | ARGP_HELP_EXIT_OK  \
-   | ARGP_HELP_DOC | ARGP_HELP_BUG_ADDR)
-
-
-void argp_error (const struct argp_state *state,
-                 const char *fmt, ...) GT_GCC_A_PRINTF(2, 3);
-
-
-
-char *
-_argp_pname (char *name)
-{
-  char *pname = name;
-  char *bname = strrchr (pname, '/');
-  if (! bname)
-    bname = strrchr (pname, '\\');
-  if (bname)
-    pname = bname + 1;
-  return pname;
-}
-
-
-void
-_argp_state_help (const struct argp *argp, const struct argp_state *state,
-                 FILE *stream, unsigned flags, char *name)
-{
-  if (state)
-    name = state->name;
-
-  if (flags & ARGP_HELP_SHORT_USAGE)
-    fprintf (stream, "Usage: %s [OPTIONS...] %s\n", name, argp->args_doc);
-  if (flags & ARGP_HELP_SEE)
-    fprintf (stream, "Try `%s --help' or `%s --usage' for more information.\n",
-            name, name);
-  if (flags & ARGP_HELP_PRE_DOC)
-    {
-      char buf[1024];
-      char *end;
-      strncpy (buf, argp->doc, sizeof (buf));
-      buf[sizeof (buf) - 1] = '\0';
-      end = strchr (buf, '\v');
-      if (end)
-       *end = '\0';
-      fprintf (stream, "%s\n%s", buf, buf[0] ? "\n" : "");
-    }
-  if (flags & ARGP_HELP_LONG)
-    {
-      const struct argp_option *opt = argp->options;
-      while (opt->key)
-       {
-         #define NSPACES 29
-         char spaces[NSPACES + 1] = "                              ";
-         int len = 0;
-         fprintf (stream, "  ");
-         len += 2;
-         if (isascii (opt->key))
-           {
-             fprintf (stream, "-%c", opt->key);
-             len += 2;
-             if (opt->name)
-               {
-                 fprintf (stream, ", ");
-                 len += 2;
-               }
-           }
-         if (opt->name)
-           {
-             fprintf (stream, "--%s", opt->name);
-             len += 2 + strlen (opt->name);
-           }
-         if (opt->arg && (opt->flags & OPTION_ARG_OPTIONAL))
-           {
-             fprintf (stream, "[=%s]", opt->arg);
-             len += 3 + strlen (opt->arg);
-           }
-         else if (opt->arg)
-           {
-             fprintf (stream, "=%s", opt->arg);
-             len += 1 + strlen (opt->arg);
-           }
-         if (len >= NSPACES)
-           len = NSPACES - 1;
-         spaces[NSPACES - len] = '\0';
-         fprintf (stream, "%s%s\n", spaces, opt->doc);
-         opt++;
-       }
-      fprintf (stream, "  -?, --help                 Give this help list\n");
-      fprintf (stream, "      --usage                Give a short usage "
-              "message\n");
-    }
-  if (flags & ARGP_HELP_POST_DOC)
-    {
-      char buf[1024];
-      char *end;
-      strncpy (buf, argp->doc, sizeof (buf));
-      buf[sizeof (buf) - 1] = '\0';
-      end = strchr (buf, '\v');
-      if (end)
-       {
-         end++;
-         if (*end)
-           fprintf (stream, "\n%s\n", end);
-       }
-      fprintf (stream, "\nMandatory or optional arguments to long options are also mandatory or optional\n");
-      fprintf (stream, "for any corresponding short options.\n");
-    }
-  if (flags & ARGP_HELP_BUG_ADDR)
-    fprintf (stream, "\nReport bugs to %s.\n", argp_program_bug_address);
-
-  if (flags & ARGP_HELP_EXIT_ERR)
-    exit (argp_err_exit_status);
-  if (flags & ARGP_HELP_EXIT_OK)
-    exit (0);
-}
-
-
-void
-argp_usage (const struct argp_state *state)
-{
-  _argp_state_help (state->root_argp, state, state->err_stream,
-                   ARGP_HELP_STD_USAGE, state->name);
-}
-
-
-void
-argp_state_help (const struct argp_state *state, FILE *stream, unsigned flags)
-{
-  _argp_state_help (state->root_argp, state, stream, flags, state->name);
-}
-
-
-void
-argp_error (const struct argp_state *state, const char *fmt, ...)
-{
-  va_list ap;
-
-  fprintf (state->err_stream, "%s: ", state->name);
-  va_start (ap, fmt);
-  vfprintf (state->err_stream, fmt, ap);
-  va_end (ap);
-  fprintf (state->err_stream, "\n");
-  argp_state_help (state, state->err_stream, ARGP_HELP_STD_ERR);
-  exit (argp_err_exit_status);
-}
-
-
-void
-argp_help (const struct argp *argp, FILE *stream, unsigned flags, char *name)
-{
-  _argp_state_help (argp, NULL, stream, flags, name);
-}
-
-
-error_t
-argp_parse (const struct argp *argp, int argc,
-           char **argv, unsigned flags, int *arg_index, void *input)
-{
-  int rc = 0;
-  struct argp_state state = { argp, argc, argv, 1, flags, 0, 0, input,
-                             NULL, NULL, _argp_pname (argv[0]),
-                             stderr, stdout, NULL };
-  /* All non-option arguments are collected at the beginning of
-     &argv[1] during processing.  This is a counter for their number.  */
-  int non_opt_args = 0;
-
-  rc = argp->parser (ARGP_KEY_INIT, NULL, &state);
-  if (rc && rc != ARGP_ERR_UNKNOWN)
-    goto argperror;
-
-  while (state.next < state.argc - non_opt_args)
-    {
-      int idx = state.next;
-      state.next++;
-
-      if (! strcasecmp (state.argv[idx], "--"))
-       {
-         state.quoted = idx;
-         continue;
-       }
-
-      if (state.quoted || state.argv[idx][0] != '-')
-       {
-         char *arg_saved = state.argv[idx];
-         non_opt_args++;
-         memmove (&state.argv[idx], &state.argv[idx + 1],
-                  (state.argc - 1 - idx) * sizeof (char *));
-         state.argv[argc - 1] = arg_saved;
-         state.next--;
-       }
-      else if (! strcasecmp (state.argv[idx], "--help")
-              || !strcmp (state.argv[idx], "-?"))
-       {
-         argp_state_help (&state, state.out_stream, ARGP_HELP_STD_HELP);
-       }
-      else if (! strcasecmp (state.argv[idx], "--usage"))
-       {
-         argp_state_help (&state, state.out_stream,
-                          ARGP_HELP_USAGE | ARGP_HELP_EXIT_OK);
-       }
-      else if (! strcasecmp (state.argv[idx], "--version")
-              || !strcmp (state.argv[idx], "-V"))
-       {
-         fprintf (state.out_stream, "%s\n", argp_program_version);
-         exit (0);
-       }
-      else
-       {
-         /* Search for option and call parser with its KEY.  */
-         int key = ARGP_KEY_ARG; /* Just some dummy value.  */
-         const struct argp_option *opt = argp->options;
-         char *arg = NULL;
-         int found = 0;
-
-         /* Check for --opt=value syntax.  */
-         arg = strchr (state.argv[idx], '=');
-         if (arg)
-           {
-             *arg = '\0';
-             arg++;
-           }
-
-         if (state.argv[idx][1] != '-')
-           key = state.argv[idx][1];
-
-         while (! found && opt->key)
-           {
-             if (key == opt->key
-                 || (key == ARGP_KEY_ARG
-                     && ! strcasecmp (&state.argv[idx][2], opt->name)))
-               {
-                 if (arg && !opt->arg)
-                   argp_error (&state, "Option %s does not take an argument",
-                               state.argv[idx]);
-                 if (opt->arg && state.next < state.argc
-                     && state.argv[idx + 1][0] != '-')
-                   {
-                     arg = state.argv[idx + 1];
-                     state.next++;
-                   }
-                 if (opt->arg && !(opt->flags & OPTION_ARG_OPTIONAL))
-                   argp_error (&state, "Option %s requires an argument",
-                               state.argv[idx]);
-
-                 rc = argp->parser (opt->key, arg, &state);
-                 if (rc == ARGP_ERR_UNKNOWN)
-                   break;
-                 else if (rc)
-                   goto argperror;
-                 found = 1;
-               }
-             opt++;
-           }
-         if (! found)
-           argp_error (&state, "Unknown option %s", state.argv[idx]);
-       }
-    }
-
-  while (state.next < state.argc)
-    {
-      /* Call parser for all non-option args.  */
-      int idx = state.next;
-      state.next++;
-      rc = argp->parser (ARGP_KEY_ARG, state.argv[idx], &state);
-      if (rc && rc != ARGP_ERR_UNKNOWN)
-       goto argperror;
-      if (rc == ARGP_ERR_UNKNOWN)
-       {
-         int old_next = state.next;
-         rc = argp->parser (ARGP_KEY_ARGS, NULL, &state);
-         if (rc == ARGP_ERR_UNKNOWN)
-           {
-             argp_error (&state, "Too many arguments");
-             goto argperror;
-           }
-         if (! rc && state.next == old_next)
-           {
-             state.arg_num += state.argc - state.next;
-             state.next = state.argc;
-           }
-       }
-      else
-       state.arg_num++;
-    }
-
-  if (state.arg_num == 0)
-    {
-      rc = argp->parser (ARGP_KEY_NO_ARGS, NULL, &state);
-      if (rc && rc != ARGP_ERR_UNKNOWN)
-       goto argperror;
-    }
-  if (state.next == state.argc)
-    {
-      rc = argp->parser (ARGP_KEY_END, NULL, &state);
-      if (rc && rc != ARGP_ERR_UNKNOWN)
-       goto argperror;
-    }
-  rc = argp->parser (ARGP_KEY_FINI, NULL, &state);
-  if (rc && rc != ARGP_ERR_UNKNOWN)
-    goto argperror;
-
-  rc = 0;
-  argp->parser (ARGP_KEY_SUCCESS, NULL, &state);
-
- argperror:
-  if (rc)
-    {
-      argp_error (&state, "unexpected error: %s", strerror (rc));
-      argp->parser (ARGP_KEY_ERROR, NULL, &state);
-    }
-
-  argp->parser (ARGP_KEY_FINI, NULL, &state);
-
-  if (arg_index)
-    *arg_index = state.next - 1;
-
-  return 0;
-}
-#endif
-
-\f
 /* MEMBUF */
 
 /* A simple implementation of a dynamic buffer.  Use init_membuf() to
@@ -3722,85 +3305,51 @@ gpgme_server (gpgme_tool_t gt)
 
 
 \f
-/* MAIN PROGRAM STARTS HERE.  */
-
-const char *argp_program_version = VERSION;
-const char *argp_program_bug_address = "bug-gpgme@gnupg.org";
-error_t argp_err_exit_status = 1;
-
-static char doc[] = "GPGME Tool -- Assuan server exposing GPGME operations";
-static char args_doc[] = "COMMAND [OPTIONS...]";
-
-static struct argp_option options[] = {
-  { "server", 's', 0, 0, "Server mode" },
-  { "gpg-binary", 501, "FILE", 0, "Use FILE for the GPG backend" },
-  { "lib-version", 502, 0, 0, "Show library version" },
-  { 0 }
-};
-
-static error_t parse_options (int key, char *arg, struct argp_state *state);
-static struct argp argp = { options, parse_options, args_doc, doc };
-
-struct args
-{
-  enum { CMD_DEFAULT, CMD_SERVER, CMD_LIBVERSION } cmd;
-  const char *gpg_binary;
-};
-
-void
-args_init (struct args *args)
-{
-  memset (args, '\0', sizeof (*args));
-  args->cmd = CMD_DEFAULT;
-}
-
-
-static error_t
-parse_options (int key, char *arg, struct argp_state *state)
+static const char *
+my_strusage( int level )
 {
-  struct args *args = state->input;
+  const char *p;
 
-  switch (key)
+  switch (level)
     {
-    case 's':
-      args->cmd = CMD_SERVER;
-      break;
-
-    case 501:
-      args->gpg_binary = arg;
-      break;
-
-    case 502:
-      args->cmd = CMD_LIBVERSION;
+    case 11: p = "gpgme-tool"; break;
+    case 13: p = PACKAGE_VERSION; break;
+    case 14: p = "Copyright (C) 2015 g10 Code GmbH"; break;
+    case 19: p = "Please report bugs to <" PACKAGE_BUGREPORT ">.\n"; break;
+    case 1:
+    case 40:
+      p = "Usage: gpgme-tool [OPTIONS] [COMMANDS]";
       break;
-
-#if 0
-    case ARGP_KEY_ARG:
-      if (state->arg_num >= 2)
-       argp_usage (state);
-      printf ("Arg[%i] = %s\n", state->arg_num, arg);
+    case 41:
+      p = "GPGME Tool -- Assuan server exposing GPGME operations\n";
       break;
-    case ARGP_KEY_END:
-      if (state->arg_num < 2)
-       argp_usage (state);
+    case 42:
+      p = "1"; /* Flag print 40 as part of 41. */
       break;
-#endif
-
-    default:
-      return ARGP_ERR_UNKNOWN;
+    default: p = NULL; break;
     }
-  return 0;
+  return p;
 }
 
-\f
+
 int
 main (int argc, char *argv[])
 {
-  struct args args;
+  static ARGPARSE_OPTS opts[] = {
+    ARGPARSE_c  ('s', "server",      "Server mode"),
+    ARGPARSE_s_s(501, "gpg-binary",  "|FILE|Use FILE for the GPG backend"),
+    ARGPARSE_c  (502, "lib-version", "Show library version"),
+    ARGPARSE_end()
+  };
+  ARGPARSE_ARGS pargs = { &argc, &argv, 0 };
+  enum { CMD_DEFAULT, CMD_SERVER, CMD_LIBVERSION } cmd = CMD_DEFAULT;
+  const char *gpg_binary = NULL;
   struct gpgme_tool gt;
   gpg_error_t err;
   int needgt = 1;
 
+  set_strusage (my_strusage);
+
 #ifdef HAVE_SETLOCALE
   setlocale (LC_ALL, "");
 #endif
@@ -3812,30 +3361,40 @@ main (int argc, char *argv[])
   gpgme_set_locale (NULL, LC_MESSAGES, setlocale (LC_MESSAGES, NULL));
 #endif
 
-  args_init (&args);
-
-  argp_parse (&argp, argc, argv, 0, 0, &args);
   log_init ();
 
-  if (args.cmd == CMD_LIBVERSION)
+  while (arg_parse  (&pargs, opts))
+    {
+      switch (pargs.r_opt)
+        {
+        case 's': cmd = CMD_SERVER; break;
+        case 501: gpg_binary = pargs.r.ret_str; break;
+        case 502: cmd = CMD_LIBVERSION; break;
+        default:
+          pargs.err = ARGPARSE_PRINT_WARNING;
+         break;
+        }
+    }
+
+  if (cmd == CMD_LIBVERSION)
     needgt = 0;
 
-  if (needgt && args.gpg_binary)
+  if (needgt && gpg_binary)
     {
-      if (access (args.gpg_binary, X_OK))
+      if (access (gpg_binary, X_OK))
         err = gpg_error_from_syserror ();
       else
         err = gpgme_set_engine_info (GPGME_PROTOCOL_OpenPGP,
-                                     args.gpg_binary, NULL);
+                                     gpg_binary, NULL);
       if (err)
         log_error (1, err, "error witching OpenPGP engine to '%s'",
-                   args.gpg_binary);
+                   gpg_binary);
     }
 
   if (needgt)
     gt_init (&gt);
 
-  switch (args.cmd)
+  switch (cmd)
     {
     case CMD_DEFAULT:
     case CMD_SERVER:
@@ -3860,4 +3419,3 @@ main (int argc, char *argv[])
 
   return 0;
 }
-