* mkdtemp.c: New (moved from g10/), setenv.c: New, unsetenv.c: New.
[gnupg.git] / util / argparse.c
index 67c3c8e..30b403f 100644 (file)
@@ -1,5 +1,7 @@
 /* [argparse.c wk 17.06.97] Argument Parser for option handling
- *     Copyright (C) 1998 Free Software Foundation, Inc.
+ *  Copyright (C) 1998, 1999, 2000, 2001, 2003
+ *                2004 Free Software Foundation, Inc.
+ *
  *  This file is part of GnuPG.
  *
  *  GnuPG is free software; you can redistribute it and/or modify
@@ -49,7 +51,7 @@
  *         char *ret_str;
  *     } r;                      Return values
  *     struct {
- *         int index;
+ *         int idx;
  *         const char *last;
  *         void *aliases;
  *     } internal;               DO NOT CHANGE
@@ -73,7 +75,7 @@
  *     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
+ *     Bit 6 : ignore --version and --help
  *     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):
@@ -143,7 +145,7 @@ static void
 initialize( ARGPARSE_ARGS *arg, const char *filename, unsigned *lineno )
 {
     if( !(arg->flags & (1<<15)) ) { /* initialize this instance */
-       arg->internal.index = 0;
+       arg->internal.idx = 0;
        arg->internal.last = NULL;
        arg->internal.inarg = 0;
        arg->internal.stopped = 0;
@@ -155,57 +157,70 @@ initialize( ARGPARSE_ARGS *arg, const char *filename, unsigned *lineno )
            log_bug("Invalid argument for ArgParse\n");
     }
 
+
     if( arg->err ) { /* last option was erroneous */
-       const char *s;
 
        if( filename ) {
            if( arg->r_opt == -6 )
-               s = "%s:%u: argument not expected\n";
+             log_error("%s:%u: argument not expected\n", filename, *lineno );
            else if( arg->r_opt == -5 )
-               s = "%s:%u: read error\n";
+             log_error("%s:%u: read error\n", filename, *lineno );
            else if( arg->r_opt == -4 )
-               s = "%s:%u: keyword too long\n";
+             log_error("%s:%u: keyword too long\n", filename, *lineno );
            else if( arg->r_opt == -3 )
-               s = "%s:%u: missing argument\n";
+             log_error("%s:%u: missing argument\n", filename, *lineno );
            else if( arg->r_opt == -7 )
-               s = "%s:%u: invalid command\n";
+             log_error("%s:%u: invalid command\n", filename, *lineno );
            else if( arg->r_opt == -10 )
-               s = "%s:%u: invalid alias definition\n";
+             log_error("%s:%u: invalid alias definition\n",filename,*lineno);
            else
-               s = "%s:%u: invalid option\n";
-           log_error(s, filename, *lineno );
+             log_error("%s:%u: invalid option\n", filename, *lineno );
        }
        else {
            if( arg->r_opt == -3 )
-               s = "Missing argument for option \"%.50s\"\n";
+             log_error("Missing argument for option \"%.50s\"\n",
+                       arg->internal.last? arg->internal.last:"[??]" );
            else if( arg->r_opt == -6 )
-               s = "Option \"%.50s\" does not expect an argument\n";
+             log_error("Option \"%.50s\" does not expect an argument\n",
+                       arg->internal.last? arg->internal.last:"[??]" );
            else if( arg->r_opt == -7 )
-               s = "Invalid command \"%.50s\"\n";
+             log_error("Invalid command \"%.50s\"\n",
+                       arg->internal.last? arg->internal.last:"[??]" );
            else if( arg->r_opt == -8 )
-               s = "Option \"%.50s\" is ambiguous\n";
+             log_error("Option \"%.50s\" is ambiguous\n",
+                       arg->internal.last? arg->internal.last:"[??]" );
            else if( arg->r_opt == -9 )
-               s = "Command \"%.50s\" is ambiguous\n";
+             log_error("Command \"%.50s\" is ambiguous\n",
+                       arg->internal.last? arg->internal.last:"[??]" );
            else
-               s = "Invalid option \"%.50s\"\n";
-           log_error(s, arg->internal.last? arg->internal.last:"[??]" );
+             log_error("Invalid option \"%.50s\"\n",
+                       arg->internal.last? arg->internal.last:"[??]" );
        }
-       if( arg->err != 1 )
+       if( arg->err != 1 || arg->r_opt == -5 )
            exit(2);
        arg->err = 0;
     }
-}
 
+    /* clearout 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
+     */
+#if 0
     ALIAS_DEF a = m_alloc( sizeof *a );
     a->name = name;
     a->value = value;
     a->next = (ALIAS_DEF)arg->internal.aliases;
     (ALIAS_DEF)arg->internal.aliases = a;
+#endif
 }
 
 /****************
@@ -230,7 +245,7 @@ optfile_parse( FILE *fp, const char *filename, unsigned *lineno,
               ARGPARSE_ARGS *arg, ARGPARSE_OPTS *opts)
 {
     int state, i, c;
-    int index=0;
+    int idx=0;
     char keyword[100];
     char *buffer = NULL;
     size_t buflen = 0;
@@ -256,24 +271,26 @@ optfile_parse( FILE *fp, const char *filename, unsigned *lineno,
                for(i=0; opts[i].short_opt; i++ )
                    if( opts[i].long_opt && !strcmp( opts[i].long_opt, keyword) )
                        break;
-               index = i;
-               arg->r_opt = opts[index].short_opt;
+               idx = i;
+               arg->r_opt = opts[idx].short_opt;
                if( inverse ) /* this does not have an effect, hmmm */
                    arg->r_opt = -arg->r_opt;
-               if( !opts[index].short_opt )   /* unknown command/option */
-                   arg->r_opt = (opts[index].flags & 256)? -7:-2;
-               else if( (opts[index].flags & 8) ) /* no argument */
-                   arg->r_opt = -3;           /* error */
-               else                           /* no or optional argument */
+               if( !opts[idx].short_opt )   /* unknown command/option */
+                   arg->r_opt = (opts[idx].flags & 256)? -7:-2;
+               else if( !(opts[idx].flags & 7) ) /* does not take an arg */
                    arg->r_type = 0;           /* okay */
+               else if( (opts[idx].flags & 8) )  /* argument is optional */
+                    arg->r_type = 0;          /* okay */
+               else                           /* required argument */
+                   arg->r_opt = -3;           /* error */
                break;
            }
            else if( state == 3 ) {            /* no argument found */
                if( in_alias )
                    arg->r_opt = -3;           /* error */
-               else if( !(opts[index].flags & 7) ) /* does not take an arg */
+               else if( !(opts[idx].flags & 7) ) /* does not take an arg */
                    arg->r_type = 0;           /* okay */
-               else if( (opts[index].flags & 8) )  /* no optional argument */
+               else if( (opts[idx].flags & 8) )  /* no optional argument */
                    arg->r_type = 0;           /* okay */
                else                           /* no required argument */
                    arg->r_opt = -3;           /* error */
@@ -301,9 +318,10 @@ optfile_parse( FILE *fp, const char *filename, unsigned *lineno,
                        }
                    }
                }
-               else if( !(opts[index].flags & 7) )  /* does not take an arg */
+               else if( !(opts[idx].flags & 7) )  /* does not take an arg */
                    arg->r_opt = -6;        /* error */
                else {
+                   char *p;
                    if( !buffer ) {
                        keyword[i] = 0;
                        buffer = m_strdup(keyword);
@@ -312,7 +330,22 @@ optfile_parse( FILE *fp, const char *filename, unsigned *lineno,
                        buffer[i] = 0;
 
                    trim_spaces( buffer );
-                   if( !set_opt_arg(arg, opts[index].flags, buffer) )
+                   p = buffer;
+                   /* remove quotes if they totally enclose the
+                       string, and do not occur within the string */
+                   if( *p == '"' && p[strlen(p)-1]=='"') {
+                       char *p2=p;
+
+                       while(*(++p2))
+                         if(*p2=='"')
+                           break;
+
+                       if(*p2=='"' && *(p2+1)=='\0') {
+                         p[strlen(p)-1] = 0;
+                         p++;
+                       }
+                   }
+                   if( !set_opt_arg(arg, opts[idx].flags, p) )
                        m_free(buffer);
                }
                break;
@@ -340,15 +373,15 @@ optfile_parse( FILE *fp, const char *filename, unsigned *lineno,
            for(i=0; opts[i].short_opt; i++ )
                if( opts[i].long_opt && !strcmp( opts[i].long_opt, keyword) )
                    break;
-           index = i;
-           arg->r_opt = opts[index].short_opt;
-           if( !opts[index].short_opt ) {
+           idx = i;
+           arg->r_opt = opts[idx].short_opt;
+           if( !opts[idx].short_opt ) {
                if( !strcmp( keyword, "alias" ) ) {
                    in_alias = 1;
                    state = 3;
                }
                else {
-                   arg->r_opt = (opts[index].flags & 256)? -7:-2;
+                   arg->r_opt = (opts[idx].flags & 256)? -7:-2;
                    state = -1;        /* skip rest of line and leave */
                }
            }
@@ -412,19 +445,19 @@ find_long_option( ARGPARSE_ARGS *arg,
     for(i=0; opts[i].short_opt; i++ )
        if( opts[i].long_opt && !strcmp( opts[i].long_opt, keyword) )
            return i;
-  #if 0
+#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) ) {
-               /* fixme: must parse the alias here */
+               /* todo: must parse the alias here */
                args->internal.cur_alias = a;
                return -3; /* alias available */
            }
        }
     }
-  #endif
+#endif
     /* not found, see whether it is an abbreviation */
     /* aliases may not be abbreviated */
     n = strlen( keyword );
@@ -445,7 +478,7 @@ find_long_option( ARGPARSE_ARGS *arg,
 int
 arg_parse( ARGPARSE_ARGS *arg, ARGPARSE_OPTS *opts)
 {
-    int index;
+    int idx;
     int argc;
     char **argv;
     char *s, *s2;
@@ -454,10 +487,10 @@ arg_parse( ARGPARSE_ARGS *arg, ARGPARSE_OPTS *opts)
     initialize( arg, NULL, NULL );
     argc = *arg->argc;
     argv = *arg->argv;
-    index = arg->internal.index;
+    idx = arg->internal.idx;
 
-    if( !index && argc && !(arg->flags & (1<<4)) ) { /* skip the first entry */
-       argc--; argv++; index++;
+    if( !idx && argc && !(arg->flags & (1<<4)) ) { /* skip the first entry */
+       argc--; argv++; idx++;
     }
 
   next_one:
@@ -473,7 +506,7 @@ arg_parse( ARGPARSE_ARGS *arg, ARGPARSE_OPTS *opts)
        arg->r_opt = -1;  /* not an option but a argument */
        arg->r_type = 2;
        arg->r.ret_str = s;
-       argc--; argv++; index++; /* set to next one */
+       argc--; argv++; idx++; /* set to next one */
     }
     else if( arg->internal.stopped ) { /* ready */
        arg->r_opt = 0;
@@ -485,7 +518,7 @@ arg_parse( ARGPARSE_ARGS *arg, ARGPARSE_OPTS *opts)
        arg->internal.inarg = 0;
        if( !s[2] && !(arg->flags & (1<<3)) ) { /* stop option processing */
            arg->internal.stopped = 1;
-           argc--; argv++; index++;
+           argc--; argv++; idx++;
            goto next_one;
        }
 
@@ -496,8 +529,11 @@ arg_parse( ARGPARSE_ARGS *arg, ARGPARSE_OPTS *opts)
        if( argpos )
            *argpos = '=';
 
-       if( i < 0 && !strcmp( "help", s+2) )
-           show_help(opts, arg->flags);
+       if( i < 0 && !strcmp( "help", s+2) ) {
+           if( !(arg->flags & (1<<6)) ) {
+               show_help(opts, arg->flags);
+           }
+       }
        else if( i < 0 && !strcmp( "version", s+2) ) {
            if( !(arg->flags & (1<<6)) ) {
                show_version();
@@ -509,9 +545,11 @@ arg_parse( ARGPARSE_ARGS *arg, ARGPARSE_OPTS *opts)
            exit(0);
        }
        else if( i < 0 && !strcmp( "dump-options", s+2) ) {
-           for(i=0; opts[i].short_opt; i++ )
+           for(i=0; opts[i].short_opt; i++ ) {
                if( opts[i].long_opt )
                    printf( "--%s\n", opts[i].long_opt );
+           }
+           fputs("--dump-options\n--help\n--version\n--warranty\n", stdout );
            exit(0);
        }
 
@@ -548,7 +586,7 @@ arg_parse( ARGPARSE_ARGS *arg, ARGPARSE_OPTS *opts)
            else {
                set_opt_arg(arg, opts[i].flags, s2);
                if( !argpos ) {
-                   argc--; argv++; index++; /* skip one */
+                   argc--; argv++; idx++; /* skip one */
                }
            }
        }
@@ -558,7 +596,7 @@ arg_parse( ARGPARSE_ARGS *arg, ARGPARSE_OPTS *opts)
            else
                arg->r_type = 0;
        }
-       argc--; argv++; index++; /* set to next one */
+       argc--; argv++; idx++; /* set to next one */
     }
     else if( (*s == '-' && s[1]) || arg->internal.inarg ) { /* short option */
        int dash_kludge = 0;
@@ -581,8 +619,11 @@ arg_parse( ARGPARSE_ARGS *arg, ARGPARSE_OPTS *opts)
                    break;
        }
 
-       if( !opts[i].short_opt && ( *s == 'h' || *s == '?' ) )
-           show_help(opts, arg->flags);
+       if( !opts[i].short_opt && ( *s == 'h' || *s == '?' ) ) {
+           if( !(arg->flags & (1<<6)) ) {
+               show_help(opts, arg->flags);
+           }
+       }
 
        arg->r_opt = opts[i].short_opt;
        if( !opts[i].short_opt ) {
@@ -611,7 +652,7 @@ arg_parse( ARGPARSE_ARGS *arg, ARGPARSE_OPTS *opts)
                }
                else {
                    set_opt_arg(arg, opts[i].flags, s2);
-                   argc--; argv++; index++; /* skip one */
+                   argc--; argv++; idx++; /* skip one */
                }
            }
            s = "x"; /* so that !s[1] yields false */
@@ -622,14 +663,14 @@ arg_parse( ARGPARSE_ARGS *arg, ARGPARSE_OPTS *opts)
        }
        if( !s[1] || dash_kludge ) { /* no more concatenated short options */
            arg->internal.inarg = 0;
-           argc--; argv++; index++;
+           argc--; argv++; idx++;
        }
     }
     else if( arg->flags & (1<<2) ) {
        arg->r_opt = -1;  /* not an option but a argument */
        arg->r_type = 2;
        arg->r.ret_str = s;
-       argc--; argv++; index++; /* set to next one */
+       argc--; argv++; idx++; /* set to next one */
     }
     else {
        arg->internal.stopped = 1; /* stop option processing */
@@ -639,7 +680,7 @@ arg_parse( ARGPARSE_ARGS *arg, ARGPARSE_OPTS *opts)
   leave:
     *arg->argc = argc;
     *arg->argv = argv;
-    arg->internal.index = index;
+    arg->internal.idx = idx;
     return arg->r_opt;
 }
 
@@ -825,7 +866,7 @@ show_version()
     /* additional program info */
     for(i=30; i < 40; i++ )
        if( (s=strusage(i)) )
-           fputs(s, stdout);
+           fputs( (const byte*)s, stdout);
     fflush(stdout);
 }
 
@@ -873,7 +914,7 @@ default_strusage( int level )
     switch( level ) {
       case 11: p = "foo"; break;
       case 13: p = "0.0"; break;
-      case 14: p = "Copyright (C) 1998 Free Software Foundation, Inc."; break;
+      case 14: p = "Copyright (C) 2004 Free Software Foundation, Inc."; break;
       case 15: p =
 "This program comes with ABSOLUTELY NO WARRANTY.\n"
 "This is free software, and you are welcome to redistribute it\n"