Typo fixes.
[gnupg.git] / common / argparse.c
index 28b4007..0a36a9e 100644 (file)
@@ -1,21 +1,32 @@
 /* [argparse.c wk 17.06.97] Argument Parser for option handling
- * Copyright (C) 1998, 1999, 2000, 2001, 2006
- *               2007, 2008  Free Software Foundation, Inc.
+ * Copyright (C) 1998-2001, 2006-2008, 2012  Free Software Foundation, Inc.
+ * Copyright (C) 1997-2001, 2006-2008, 2013 Werner Koch
  *
- * This file is part of JNLIB.
+ * This file is part of JNLIB, which is a subsystem of GnuPG.
  *
  * JNLIB is free software; you can redistribute it and/or modify it
- * under the terms of the GNU Lesser General Public License as
- * published by the Free Software Foundation; either version 2.1 of
- * the License, or (at your option) any later version.
+ * 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.
  *
  * JNLIB 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
- * Lesser General Public License for more details.
+ * General Public License for more details.
  *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this program; if not, see <http://www.gnu.org/licenses/>.
+ * 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/>.
  */
 
 #ifdef HAVE_CONFIG_H
@@ -26,6 +37,9 @@
 #include <stdlib.h>
 #include <ctype.h>
 #include <string.h>
+#include <stdarg.h>
+#include <limits.h>
+#include <errno.h>
 
 #include "libjnlib-config.h"
 #include "mischelp.h"
  *              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 7 : this is a command and not an option
+ *     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
  *     { '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 }
@@ -143,18 +159,80 @@ struct alias_def_s {
     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)) ) 
-    { 
+  if( !(arg->flags & (1<<15)) )
+    {
       /* Initialize this instance. */
       arg->internal.idx = 0;
       arg->internal.last = NULL;
@@ -162,18 +240,19 @@ initialize( ARGPARSE_ARGS *arg, const char *filename, unsigned *lineno )
       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 )
-        jnlib_log_bug ("invalid argument for arg_parsee\n");
+        jnlib_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 )
@@ -184,6 +263,8 @@ initialize( ARGPARSE_ARGS *arg, const char *filename, unsigned *lineno )
             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 )
@@ -194,12 +275,14 @@ initialize( ARGPARSE_ARGS *arg, const char *filename, unsigned *lineno )
             s = _("invalid option");
           jnlib_log_error ("%s:%u: %s\n", filename, *lineno, s);
        }
-      else 
+      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);
+          else if ( arg->r_opt == ARGPARSE_INVALID_ARG )
+            jnlib_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 );
@@ -214,7 +297,7 @@ initialize( ARGPARSE_ARGS *arg, const char *filename, unsigned *lineno )
           else
             jnlib_log_error (_("invalid option \"%.50s\"\n"), s);
        }
-      if ( arg->err != 1 )
+      if (arg->err != ARGPARSE_PRINT_WARNING)
         exit (2);
       arg->err = 0;
     }
@@ -244,6 +327,111 @@ store_alias( ARGPARSE_ARGS *arg, char *name, char *value )
 #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 = jnlib_malloc (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;
+      jnlib_free (item);
+    }
+  arg->internal.iio_list = NULL;
+}
+
+
+
 /****************
  * Get options from a file.
  * Lines starting with '#' are comment lines.
@@ -253,6 +441,10 @@ store_alias( ARGPARSE_ARGS *arg, char *name, char *value )
  * 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.
  *
@@ -271,10 +463,10 @@ optfile_parse (FILE *fp, const char *filename, unsigned *lineno,
   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.  */
@@ -298,13 +490,32 @@ optfile_parse (FILE *fp, const char *filename, unsigned *lineno,
                 }
               idx = i;
               arg->r_opt = opts[idx].short_opt;
-              if (!opts[idx].short_opt )
-                arg->r_opt = ((opts[idx].flags & ARGPARSE_OPT_COMMAND)
-                              ? ARGPARSE_INVALID_COMMAND
-                              : ARGPARSE_INVALID_OPTION);
-              else if (!(opts[idx].flags & 7)) 
+              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 & 8) )  
+              else if ((opts[idx].flags & ARGPARSE_OPT_OPTIONAL) )
                 arg->r_type = 0; /* Arg is optional.  */
               else
                 arg->r_opt = ARGPARSE_MISSING_ARG;
@@ -312,13 +523,13 @@ optfile_parse (FILE *fp, const char *filename, unsigned *lineno,
               break;
            }
           else if (state == 3)
-            {  
+            {
               /* No argument found.  */
               if (in_alias)
                 arg->r_opt = ARGPARSE_MISSING_ARG;
-              else if (!(opts[idx].flags & 7)) 
+              else if (!(opts[idx].flags & ARGPARSE_TYPE_MASK))
                 arg->r_type = 0; /* Does not take an arg. */
-              else if ((opts[idx].flags & 8))  
+              else if ((opts[idx].flags & ARGPARSE_OPT_OPTIONAL))
                 arg->r_type = 0; /* No optional argument. */
               else
                 arg->r_opt = ARGPARSE_MISSING_ARG;
@@ -328,14 +539,14 @@ optfile_parse (FILE *fp, const char *filename, unsigned *lineno,
           else if (state == 4)
             {
               /* Has an argument. */
-              if (in_alias) 
+              if (in_alias)
                 {
                   if (!buffer)
                     arg->r_opt = ARGPARSE_UNEXPECTED_ARG;
-                  else 
+                  else
                     {
                       char *p;
-                      
+
                       buffer[i] = 0;
                       p = strpbrk (buffer, " \t");
                       if (p)
@@ -354,7 +565,7 @@ optfile_parse (FILE *fp, const char *filename, unsigned *lineno,
                         }
                    }
                }
-              else if (!(opts[idx].flags & 7))
+              else if (!(opts[idx].flags & ARGPARSE_TYPE_MASK))
                 arg->r_opt = ARGPARSE_UNEXPECTED_ARG;
               else
                 {
@@ -369,26 +580,27 @@ optfile_parse (FILE *fp, const char *filename, unsigned *lineno,
                    }
                   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))
-                       jnlib_free(buffer);
+                        jnlib_free(buffer);
                     }
                 }
               break;
             }
           else if (c == EOF)
             {
+              ignore_invalid_option_clear (arg);
               if (ferror (fp))
                 arg->r_opt = ARGPARSE_READ_ERROR;
               else
@@ -415,14 +627,30 @@ optfile_parse (FILE *fp, const char *filename, unsigned *lineno,
               break;
           idx = i;
           arg->r_opt = opts[idx].short_opt;
-          if (!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 
+              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
@@ -444,13 +672,13 @@ optfile_parse (FILE *fp, const char *filename, unsigned *lineno,
             }
         }
       else if (state == 4)
-        { 
+        {
           /* Collect the argument. */
           if (buffer)
             {
               if (i < buflen-1)
                 buffer[i++] = c;
-              else 
+              else
                 {
                   char *tmp;
                   size_t tmplen = buflen + 50;
@@ -472,7 +700,7 @@ optfile_parse (FILE *fp, const char *filename, unsigned *lineno,
             }
           else if (i < DIM(keyword)-1)
             keyword[i++] = c;
-          else 
+          else
             {
               size_t tmplen = DIM(keyword) + 50;
               buffer = jnlib_malloc (tmplen);
@@ -494,13 +722,13 @@ optfile_parse (FILE *fp, const char *filename, unsigned *lineno,
           arg->r_opt = ARGPARSE_KEYWORD_TOO_LONG;
           state = -1; /* Skip rest of line and leave.  */
         }
-      else 
+      else
         {
           keyword[i++] = c;
           state = 2;
         }
     }
-  
+
   return arg->r_opt;
 }
 
@@ -551,7 +779,7 @@ find_long_option( ARGPARSE_ARGS *arg,
            return i;
        }
     }
-    return -1;
+    return -1;  /* Not found.  */
 }
 
 int
@@ -562,7 +790,7 @@ arg_parse( ARGPARSE_ARGS *arg, ARGPARSE_OPTS *opts)
   char **argv;
   char *s, *s2;
   int i;
-  
+
   initialize( arg, NULL, NULL );
   argc = *arg->argc;
   argv = *arg->argv;
@@ -573,10 +801,10 @@ arg_parse( ARGPARSE_ARGS *arg, ARGPARSE_OPTS *opts)
       /* Skip the first argument.  */
       argc--; argv++; idx++;
     }
-  
+
  next_one:
-  if (!argc) 
-    { 
+  if (!argc)
+    {
       /* No more args.  */
       arg->r_opt = 0;
       goto leave; /* Ready. */
@@ -585,14 +813,14 @@ arg_parse( ARGPARSE_ARGS *arg, ARGPARSE_OPTS *opts)
   s = *argv;
   arg->internal.last = s;
 
-  if (arg->internal.stopped && (arg->flags & ARGPARSE_FLAG_ALL)) 
+  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 ) 
+  else if( arg->internal.stopped )
     {
       arg->r_opt = 0;
       goto leave; /* Ready.  */
@@ -601,12 +829,13 @@ arg_parse( ARGPARSE_ARGS *arg, ARGPARSE_OPTS *opts)
     {
       /* 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;
        }
@@ -630,20 +859,21 @@ arg_parse( ARGPARSE_ARGS *arg, ARGPARSE_OPTS *opts)
        }
       else if ( i < 0 && !strcmp( "warranty", s+2))
         {
-          puts ( strusage (16) );
+          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 )
-                printf ("--%s\n", opts[i].long_opt);
+              if (opts[i].long_opt && !(opts[i].flags & ARGPARSE_OPT_IGNORE))
+                writestrings (0, "--", opts[i].long_opt, "\n", NULL);
            }
-          fputs ("--dump-options\n--help\n--version\n--warranty\n", stdout);
+          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 )
@@ -655,7 +885,7 @@ arg_parse( ARGPARSE_ARGS *arg, ARGPARSE_OPTS *opts)
         arg->r_opt = opts[i].short_opt;
       if ( i < 0 )
         ;
-      else if ( (opts[i].flags & 0x07) )
+      else if ( (opts[i].flags & ARGPARSE_TYPE_MASK) )
         {
           if ( argpos )
             {
@@ -673,40 +903,40 @@ arg_parse( ARGPARSE_ARGS *arg, ARGPARSE_OPTS *opts)
             {
               arg->r_opt = ARGPARSE_MISSING_ARG;
            }
-          else if ( !argpos && *s2 == '-' 
-                    && (opts[i].flags & ARGPARSE_OPT_OPTIONAL) ) 
+          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 
+          else
             {
               set_opt_arg (arg, opts[i].flags, s2);
-              if ( !argpos ) 
+              if ( !argpos )
                 {
                   argc--; argv++; idx++; /* Skip one.  */
                }
            }
        }
       else
-        { 
+        {
           /* Does not take an argument. */
           if ( argpos )
-            arg->r_type = ARGPARSE_UNEXPECTED_ARG; 
+            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 ) 
+    else if ( (*s == '-' && s[1]) || arg->internal.inarg )
       {
         /* Short option.  */
        int dash_kludge = 0;
 
        i = 0;
-       if ( !arg->internal.inarg ) 
+       if ( !arg->internal.inarg )
           {
            arg->internal.inarg++;
            if ( (arg->flags & ARGPARSE_FLAG_ONEDASH) )
@@ -727,7 +957,7 @@ arg_parse( ARGPARSE_ARGS *arg, ARGPARSE_OPTS *opts)
               if ( opts[i].short_opt == *s )
                 break;
           }
-        
+
        if ( !opts[i].short_opt && ( *s == 'h' || *s == '?' ) )
           show_help (opts, arg->flags);
 
@@ -739,7 +969,7 @@ arg_parse( ARGPARSE_ARGS *arg, ARGPARSE_OPTS *opts)
            arg->internal.inarg++; /* Point to the next arg.  */
            arg->r.ret_str = s;
           }
-       else if ( (opts[i].flags & 7) )
+       else if ( (opts[i].flags & ARGPARSE_TYPE_MASK) )
           {
            if ( s[1] && !dash_kludge )
               {
@@ -807,23 +1037,54 @@ arg_parse( ARGPARSE_ARGS *arg, ARGPARSE_OPTS *opts)
 }
 
 
-
+/* 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)
+set_opt_arg (ARGPARSE_ARGS *arg, unsigned flags, char *s)
 {
-  int base = (flags & 16)? 0 : 10;
+  int base = (flags & ARGPARSE_OPT_PREFIX)? 0 : 10;
+  long l;
 
-  switch ( (arg->r_type = (flags & 7)) )
+  switch ( (arg->r_type = (flags & ARGPARSE_TYPE_MASK)) )
     {
-    case ARGPARSE_TYPE_INT:
-      arg->r.ret_int = (int)strtol(s,NULL,base);
-      return 0;
     case ARGPARSE_TYPE_LONG:
-      arg->r.ret_long= strtol(s,NULL,base);
+    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:
-      arg->r.ret_ulong= strtoul(s,NULL,base);
+      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;
@@ -837,13 +1098,13 @@ long_opt_strlen( ARGPARSE_OPTS *o )
 {
   size_t n = strlen (o->long_opt);
 
-  if ( o->description && *o->description == '|' ) 
+  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 != '=' )
         n++;
@@ -875,11 +1136,12 @@ static void
 show_help (ARGPARSE_OPTS *opts, unsigned int flags)
 {
   const char *s;
-  
+  char tmp[2];
+
   show_version ();
-  putchar ('\n');
+  writestrings (0, "\n", NULL);
   s = strusage(41);
-  puts (s);
+  writestrings (0, s, "\n", NULL);
   if ( opts[0].description )
     {
       /* Auto format the option description.  */
@@ -897,119 +1159,121 @@ show_help (ARGPARSE_OPTS *opts, unsigned int flags)
       /* Example: " -v, --verbose   Viele Sachen ausgeben" */
       indent += 10;
       if ( *opts[0].description != '@' )
-        puts ("Options:");
+        writestrings (0, "Options:", "\n", NULL);
       for (i=0; opts[i].short_opt; i++ )
         {
-          s = _( opts[i].description );
+          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++ ) 
+            {
+              for (s++; *s; s++ )
                 {
                   if ( *s == '\n' )
                     {
                       if( s[1] )
-                        putchar('\n');
+                        writestrings (0, "\n", NULL);
                    }
                   else
-                    putchar(*s);
-               }
-              putchar('\n');
+                    {
+                      tmp[0] = *s;
+                      tmp[1] = 0;
+                      writestrings (0, tmp, NULL);
+                    }
+                }
+              writestrings (0, "\n", NULL);
               continue;
            }
 
           j = 3;
           if ( opts[i].short_opt < 256 )
             {
-              printf (" -%c", opts[i].short_opt);
-              if ( !opts[i].long_opt ) 
+              tmp[0] = opts[i].short_opt;
+              tmp[1] = 0;
+              writestrings (0, " -", tmp, NULL );
+              if ( !opts[i].long_opt )
                 {
-                  if (s && *s == '|' ) 
+                  if (s && *s == '|' )
                     {
-                      putchar (' '); j++;
+                      writestrings (0, " ", NULL); j++;
                       for (s++ ; *s && *s != '|'; s++, j++ )
-                        putchar (*s);
+                        {
+                          tmp[0] = *s;
+                          tmp[1] = 0;
+                          writestrings (0, tmp, NULL);
+                        }
                       if ( *s )
                         s++;
                    }
                }
            }
           else
-            fputs("   ", stdout);
-          if ( opts[i].long_opt ) 
+            writestrings (0, "   ", NULL);
+          if ( opts[i].long_opt )
             {
-              j += printf ("%c --%s", opts[i].short_opt < 256?',':' ',
-                           opts[i].long_opt );
-              if (s && *s == '|' ) 
+              tmp[0] = opts[i].short_opt < 256?',':' ';
+              tmp[1] = 0;
+              j += writestrings (0, tmp, " --", opts[i].long_opt, NULL);
+              if (s && *s == '|' )
                 {
                   if ( *++s != '=' )
                     {
-                      putchar(' ');
+                      writestrings (0, " ", NULL);
                       j++;
                    }
                   for ( ; *s && *s != '|'; s++, j++ )
-                    putchar(*s);
+                    {
+                      tmp[0] = *s;
+                      tmp[1] = 0;
+                      writestrings (0, tmp, NULL);
+                    }
                   if ( *s )
                     s++;
                }
-              fputs ("   ", stdout);
+              writestrings (0, "   ", NULL);
               j += 3;
            }
           for (;j < indent; j++ )
-            putchar(' ');
+            writestrings (0, " ", NULL);
           if ( s )
             {
               if ( *s && j > indent )
                 {
-                  putchar('\n');
+                  writestrings (0, "\n", NULL);
                   for (j=0;j < indent; j++ )
-                    putchar (' ');
+                    writestrings (0, " ", NULL);
                }
               for (; *s; s++ )
                 {
                   if ( *s == '\n' )
                     {
-                      if ( s[1] ) 
+                      if ( s[1] )
                         {
-                          putchar ('\n');
+                          writestrings (0, "\n", NULL);
                           for (j=0; j < indent; j++ )
-                            putchar (' ');
+                            writestrings (0, " ", NULL);
                        }
                    }
                   else
-                    putchar (*s);
+                    {
+                      tmp[0] = *s;
+                      tmp[1] = 0;
+                      writestrings (0, tmp, NULL);
+                    }
                }
            }
-          putchar ('\n');
+          writestrings (0, "\n", NULL);
        }
        if ( (flags & ARGPARSE_FLAG_ONEDASH) )
-           puts ("\n(A single dash may be used instead of the double ones)");
+          writestrings (0, "\n(A single dash may be used "
+                        "instead of the double ones)\n", NULL);
     }
   if ( (s=strusage(19)) )
-    { 
-      /* bug reports to ... */
-      char *s2;
-      
-      putchar('\n');
-      s2 = strstr (s, "@EMAIL@");
-      if (s2)
-        {
-          if (s2-s)
-            fwrite (s, s2-s, 1, stdout);
-#ifdef PACKAGE_BUGREPORT
-          fputs (PACKAGE_BUGREPORT, stdout);
-#else
-          fputs ("bug@example.org", stdout);
-#endif
-          s2 += 7;
-          if (*s2)
-            fputs (s2, stdout);
-        }
-      else
-        fputs(s, stdout);
+    {
+      writestrings (0, "\n", NULL);
+      writestrings (0, s, NULL);
     }
-  fflush(stdout);
+  flushstrings (0);
   exit(0);
 }
 
@@ -1020,31 +1284,31 @@ show_version ()
   int i;
 
   /* Version line.  */
-  fputs (strusage (11), stdout);
+  writestrings (0, strusage (11), NULL);
   if ((s=strusage (12)))
-    printf (" (%s)", s );
-  printf (" %s\n", strusage (13) );
+    writestrings (0, " (", s, ")", NULL);
+  writestrings (0, " ", strusage (13), "\n", NULL);
   /* Additional version lines. */
   for (i=20; i < 30; i++)
     if ((s=strusage (i)))
-      printf ("%s\n", s );
+      writestrings (0, s, "\n", NULL);
   /* Copyright string.  */
-  if( (s=strusage (14)) )
-    printf("%s\n", s );
+  if ((s=strusage (14)))
+    writestrings (0, s, "\n", NULL);
   /* Licence string.  */
   if( (s=strusage (10)) )
-    printf("%s\n", s );
+    writestrings (0, s, "\n", NULL);
   /* Copying conditions. */
   if ( (s=strusage(15)) )
-    fputs (s, stdout);
+    writestrings (0, s, NULL);
   /* Thanks. */
   if ((s=strusage(18)))
-    fputs (s, stdout);
+    writestrings (0, s, NULL);
   /* Additional program info. */
   for (i=30; i < 40; i++ )
     if ( (s=strusage (i)) )
-      fputs (s, stdout);
-  fflush (stdout);
+      writestrings (0, s, NULL);
+  flushstrings (0);
 }
 
 
@@ -1055,20 +1319,21 @@ usage (int level)
 
   if (!level)
     {
-      fprintf(stderr,"%s %s; %s\n", strusage(11), strusage(13), strusage (14));
-      fflush (stderr);
+      writestrings (1, strusage(11), " ", strusage(13), "; ",
+                    strusage (14), "\n", NULL);
+      flushstrings (1);
     }
   else if (level == 1)
     {
       p = strusage (40);
-      fputs (p, stderr);
+      writestrings (1, p, NULL);
       if (*p && p[strlen(p)] != '\n')
-        putc ('\n', stderr);
+        writestrings (1, "\n", NULL);
       exit (2);
     }
-  else if (level == 2) 
+  else if (level == 2)
     {
-      puts (strusage(41));
+      writestrings (0, strusage(41), "\n", NULL);
       exit (0);
     }
 }
@@ -1096,10 +1361,10 @@ const char *
 strusage( int level )
 {
   const char *p = strusage_handler? strusage_handler(level) : NULL;
-  
+
   if ( p )
-    return p;
-  
+    return map_static_macro_string (p);
+
   switch ( level )
     {
     case 10: p = ("License GPLv3+: GNU GPL version 3 or later "
@@ -1107,7 +1372,7 @@ strusage( int level )
       break;
     case 11: p = "foo"; break;
     case 13: p = "0.0"; break;
-    case 14: p = "Copyright (C) 2010 Free Software Foundation, Inc."; break;
+    case 14: p = "Copyright (C) 2014 Free Software Foundation, Inc."; 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";
@@ -1127,7 +1392,7 @@ strusage( int level )
     case 40: /* short and long usage */
     case 41: p = ""; break;
     }
-  
+
   return p;
 }
 
@@ -1172,7 +1437,7 @@ main(int argc, char **argv)
 
     while( arg_parse ( &pargs, opts) ) {
        switch( pargs.r_opt ) {
-         case -1 : printf( "arg=`%s'\n", pargs.r.ret_str); break;
+         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;
@@ -1180,7 +1445,7 @@ main(int argc, char **argv)
          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; 
+         default : pargs.err = ARGPARSE_PRINT_WARNING; break;
        }
     }
     for(i=0; i < argc; i++ )
@@ -1191,9 +1456,9 @@ main(int argc, char **argv)
     if( opt.debug )
        printf("  debug=%d\n", opt.debug );
     if( opt.outfile )
-       printf("  outfile=`%s'\n", opt.outfile );
+       printf("  outfile='%s'\n", opt.outfile );
     if( opt.crf )
-       printf("  crffile=`%s'\n", opt.crf );
+       printf("  crffile='%s'\n", opt.crf );
     if( opt.myopt )
        printf("  myopt=%d\n", opt.myopt );
     if( opt.a_long_one )