doc: Describe filter expressions.
[gnupg.git] / common / argparse.c
index 844c170..240fdce 100644 (file)
@@ -1,11 +1,10 @@
 /* [argparse.c wk 17.06.97] Argument Parser for option handling
- * Copyright (C) 1998, 1999, 2000, 2001, 2006
- *               2007, 2008, 2012  Free Software Foundation, Inc.
- * Copyright (C) 1997, 2013 Werner Koch
+ * Copyright (C) 1998-2001, 2006-2008, 2012 Free Software Foundation, Inc.
+ * Copyright (C) 1997-2001, 2006-2008, 2013-2016 Werner Koch
  *
- * This file is part of JNLIB, which is a subsystem of GnuPG.
+ * This file is part of GnuPG.
  *
- * JNLIB is free software; you can redistribute it and/or modify it
+ * 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
  *
  * or both in parallel, as here.
  *
- * JNLIB is distributed in the hope that it will be useful, but
+ * 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/>.
+ * if not, see <https://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 <limits.h>
 #include <errno.h>
 
-#include "libjnlib-config.h"
-#include "mischelp.h"
-#include "stringhelp.h"
-#include "logging.h"
-#ifdef JNLIB_NEED_UTF8CONV
-#include "utf8conv.h"
-#endif
+#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) 2016 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 <wk/lib.h>
+ *  #include "argparse.h"
  *
  *  typedef struct {
  *     char *argc;               pointer to argc (value subject to change)
@@ -245,7 +350,7 @@ initialize( ARGPARSE_ARGS *arg, const char *filename, unsigned *lineno )
       arg->err = 0;
       arg->flags |= 1<<15; /* Mark as initialized.  */
       if ( *arg->argc < 0 )
-        jnlib_log_bug ("invalid argument for arg_parse\n");
+        log_bug ("invalid argument for arg_parse\n");
     }
 
 
@@ -274,29 +379,28 @@ initialize( ARGPARSE_ARGS *arg, const char *filename, unsigned *lineno )
             s = _("out of core");
           else
             s = _("invalid option");
-          jnlib_log_error ("%s:%u: %s\n", filename, *lineno, s);
+          log_error ("%s:%u: %s\n", filename, *lineno, s);
        }
       else
         {
           s = arg->internal.last? arg->internal.last:"[??]";
 
           if ( arg->r_opt == ARGPARSE_MISSING_ARG )
-            jnlib_log_error (_("missing argument for option \"%.50s\"\n"), s);
+            log_error (_("missing argument for option \"%.50s\"\n"), s);
           else if ( arg->r_opt == ARGPARSE_INVALID_ARG )
-            jnlib_log_error (_("invalid argument for option \"%.50s\"\n"), s);
+            log_error (_("invalid argument for option \"%.50s\"\n"), s);
           else if ( arg->r_opt == ARGPARSE_UNEXPECTED_ARG )
-            jnlib_log_error (_("option \"%.50s\" does not expect an "
-                               "argument\n"), s );
+            log_error (_("option \"%.50s\" does not expect an argument\n"), s);
           else if ( arg->r_opt == ARGPARSE_INVALID_COMMAND )
-            jnlib_log_error (_("invalid command \"%.50s\"\n"), s);
+            log_error (_("invalid command \"%.50s\"\n"), s);
           else if ( arg->r_opt == ARGPARSE_AMBIGUOUS_OPTION )
-            jnlib_log_error (_("option \"%.50s\" is ambiguous\n"), s);
-          else if ( arg->r_opt == ARGPARSE_AMBIGUOUS_OPTION )
-            jnlib_log_error (_("command \"%.50s\" is ambiguous\n"),s );
+            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 )
-            jnlib_log_error ("%s\n", _("out of core\n"));
+            log_error ("%s\n", _("out of core\n"));
           else
-            jnlib_log_error (_("invalid option \"%.50s\"\n"), s);
+            log_error (_("invalid option \"%.50s\"\n"), s);
        }
       if (arg->err != ARGPARSE_PRINT_WARNING)
         exit (2);
@@ -320,7 +424,7 @@ store_alias( ARGPARSE_ARGS *arg, char *name, char *value )
   (void)name;
   (void)value;
 #if 0
-    ALIAS_DEF a = jnlib_xmalloc( sizeof *a );
+    ALIAS_DEF a = xmalloc( sizeof *a );
     a->name = name;
     a->value = value;
     a->next = (ALIAS_DEF)arg->internal.aliases;
@@ -402,7 +506,7 @@ ignore_invalid_option_add (ARGPARSE_ARGS *arg, FILE *fp)
           name[namelen] = 0;
           if (!ignore_invalid_option_p (arg, name))
             {
-              item = jnlib_malloc (sizeof *item + namelen);
+              item = xtrymalloc (sizeof *item + namelen);
               if (!item)
                 return 1;
               strcpy (item->name, name);
@@ -426,7 +530,7 @@ ignore_invalid_option_clear (ARGPARSE_ARGS *arg)
   for (item = arg->internal.iio_list; item; item = tmpitem)
     {
       tmpitem = item->next;
-      jnlib_free (item);
+      xfree (item);
     }
   arg->internal.iio_list = NULL;
 }
@@ -557,7 +661,7 @@ optfile_parse (FILE *fp, const char *filename, unsigned *lineno,
                        }
                       if (!p || !*p)
                         {
-                          jnlib_free (buffer);
+                          xfree (buffer);
                           arg->r_opt = ARGPARSE_INVALID_ALIAS;
                         }
                       else
@@ -575,7 +679,7 @@ optfile_parse (FILE *fp, const char *filename, unsigned *lineno,
                   if (!buffer)
                     {
                       keyword[i] = 0;
-                      buffer = jnlib_strdup (keyword);
+                      buffer = xtrystrdup (keyword);
                       if (!buffer)
                         arg->r_opt = ARGPARSE_OUT_OF_CORE;
                    }
@@ -594,7 +698,9 @@ optfile_parse (FILE *fp, const char *filename, unsigned *lineno,
                             p[strlen(p)-1] = 0;
                         }
                       if (!set_opt_arg (arg, opts[idx].flags, p))
-                        jnlib_free(buffer);
+                        xfree (buffer);
+                      else
+                        gpgrt_annotate_leaked_object (buffer);
                     }
                 }
               break;
@@ -684,7 +790,7 @@ optfile_parse (FILE *fp, const char *filename, unsigned *lineno,
                   char *tmp;
                   size_t tmplen = buflen + 50;
 
-                  tmp = jnlib_realloc (buffer, tmplen);
+                  tmp = xtryrealloc (buffer, tmplen);
                   if (tmp)
                     {
                       buflen = tmplen;
@@ -693,7 +799,7 @@ optfile_parse (FILE *fp, const char *filename, unsigned *lineno,
                     }
                   else
                     {
-                      jnlib_free (buffer);
+                      xfree (buffer);
                       arg->r_opt = ARGPARSE_OUT_OF_CORE;
                       break;
                     }
@@ -704,7 +810,7 @@ optfile_parse (FILE *fp, const char *filename, unsigned *lineno,
           else
             {
               size_t tmplen = DIM(keyword) + 50;
-              buffer = jnlib_malloc (tmplen);
+              buffer = xtrymalloc (tmplen);
               if (buffer)
                 {
                   buflen = tmplen;
@@ -747,7 +853,7 @@ find_long_option( ARGPARSE_ARGS *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 */
+       lookup table when this function is first invoked */
     if( !*keyword )
        return -1;
     for(i=0; opts[i].short_opt; i++ )
@@ -1102,9 +1208,7 @@ long_opt_strlen( ARGPARSE_OPTS *o )
   if ( o->description && *o->description == '|' )
     {
       const char *s;
-#ifdef JNLIB_NEED_UTF8CONV
       int is_utf8 = is_native_utf8 ();
-#endif
 
       s=o->description+1;
       if ( *s != '=' )
@@ -1113,9 +1217,7 @@ long_opt_strlen( ARGPARSE_OPTS *o )
          continuation bytes (10xxxxxx) if we are on a native utf8
          terminal. */
       for (; *s && *s != '|'; s++ )
-#ifdef JNLIB_NEED_UTF8CONV
         if ( is_utf8 && (*s&0xc0) != 0x80 )
-#endif
           n++;
     }
   return n;
@@ -1141,6 +1243,14 @@ show_help (ARGPARSE_OPTS *opts, unsigned int flags)
 
   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 )
@@ -1334,6 +1444,14 @@ usage (int level)
     }
   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);
     }
@@ -1357,6 +1475,10 @@ usage (int level)
  *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 )
@@ -1368,12 +1490,19 @@ strusage( int level )
 
   switch ( level )
     {
-    case 10: p = ("License GPLv3+: GNU GPL version 3 or later "
-                  "<http://gnu.org/licenses/gpl.html>");
+
+    case 10:
+#if ARGPARSE_GPL_VERSION == 3
+      p = ("License GPLv3+: GNU GPL version 3 or later "
+           "<https://gnu.org/licenses/gpl.html>");
+#else
+      p = ("License GPLv2+: GNU GPL version 2 or later "
+           "<https://gnu.org/licenses/>");
+#endif
       break;
     case 11: p = "foo"; break;
     case 13: p = "0.0"; break;
-    case 14: p = "Copyright (C) 2014 Free Software Foundation, Inc."; 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";
@@ -1381,14 +1510,16 @@ strusage( int level )
     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 3 of the License, or\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";
+"along with this software.  If not, see <https://gnu.org/licenses/>.\n";
       break;
     case 40: /* short and long usage */
     case 41: p = ""; break;
@@ -1415,7 +1546,7 @@ static struct {
     int myopt;
     int echo;
     int a_long_one;
-}opt;
+} opt;
 
 int
 main(int argc, char **argv)
@@ -1423,7 +1554,7 @@ 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 ein gegeben haben")),
+                                  "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" ),
@@ -1431,43 +1562,50 @@ main(int argc, char **argv)
     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_end()
   };
-  ARGPARSE_ARGS pargs = { &argc, &argv, 2|4|32 };
-    int i;
+  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 -1 : 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;
+  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;
+  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
+#endif /*TEST*/
 
 /**** bottom of file ****/