* mkdtemp.c: New (moved from g10/), setenv.c: New, unsetenv.c: New.
[gnupg.git] / util / argparse.c
index 620211e..30b403f 100644 (file)
@@ -1,13 +1,15 @@
 /* [argparse.c wk 17.06.97] Argument Parser for option handling
- *     Copyright (c) 1997 by Werner Koch (dd9jn)
- *  This file is part of WkLib.
+ *  Copyright (C) 1998, 1999, 2000, 2001, 2003
+ *                2004 Free Software Foundation, Inc.
  *
- *  WkLib is free software; you can redistribute it and/or modify
+ *  This file is part of GnuPG.
+ *
+ *  GnuPG is free software; you can redistribute it and/or modify
  *  it under the terms of 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.
  *
- *  WkLib is distributed in the hope that it will be useful,
+ *  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.
@@ -49,8 +51,9 @@
  *         char *ret_str;
  *     } r;                      Return values
  *     struct {
- *         int index;
+ *         int idx;
  *         const char *last;
+ *         void *aliases;
  *     } internal;               DO NOT CHANGE
  *  } ARGPARSE_ARGS;
  *
@@ -72,8 +75,9 @@
  *     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
- *     all other bits must be set to zero, this value is modified by the function
- *     so assume this is write only.
+ *     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):
  *     Bit 2-0 : 0 = does not take an argument
  *              1 = takes int 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.
- *  If can stop the option processing by setting opts to NULL, the function will
+ *     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 to indicate an unknown option.
+ *   r_opt may be -2/-7 to indicate an unknown option/command.
  * @See Also
  *   ArgExpand
  * @Notes
  *
  */
 
+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 */
+};
 
 static int  set_opt_arg(ARGPARSE_ARGS *arg, unsigned flags, char *s);
 static void show_help(ARGPARSE_OPTS *opts, unsigned flags);
@@ -134,67 +145,112 @@ 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;
+       arg->internal.stopped = 0;
+       arg->internal.aliases = NULL;
+       arg->internal.cur_alias = NULL;
        arg->err = 0;
        arg->flags |= 1<<15; /* mark initialized */
        if( *arg->argc < 0 )
            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 )
+             log_error("%s:%u: invalid command\n", filename, *lineno );
+           else if( arg->r_opt == -10 )
+             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 )
+             log_error("Option \"%.50s\" does not expect an argument\n",
+                       arg->internal.last? arg->internal.last:"[??]" );
+           else if( arg->r_opt == -7 )
+             log_error("Invalid command \"%.50s\"\n",
+                       arg->internal.last? arg->internal.last:"[??]" );
+           else if( arg->r_opt == -8 )
+             log_error("Option \"%.50s\" is ambiguous\n",
+                       arg->internal.last? arg->internal.last:"[??]" );
+           else if( arg->r_opt == -9 )
+             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
+}
 
 /****************
  * 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
+ * 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.
  * 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 index=0;
+    int idx=0;
     char keyword[100];
     char *buffer = NULL;
     size_t buflen = 0;
     int inverse=0;
+    int in_alias=0;
 
     if( !fp ) /* same as arg_parse() in this case */
        return arg_parse( arg, opts );
@@ -215,31 +271,57 @@ 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( inverse )
+               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 )
-                   arg->r_opt = -2;           /* unknown option */
-               else if( (opts[index].flags & 8) ) /* no argument */
-                   arg->r_opt = -3;           /* error */
-               else                           /* no or optiona 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( !(opts[index].flags & 7) ) /* does not take an argument */
+               if( in_alias )
+                   arg->r_opt = -3;           /* error */
+               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 */
                break;
            }
            else if( state == 4 ) {     /* have an argument */
-               if( !(opts[index].flags & 7) )  /* does not take an argument */
+               if( in_alias ) {
+                   if( !buffer )
+                       arg->r_opt = -6;
+                   else {
+                       char *p;
+
+                       buffer[i] = 0;
+                       p = strpbrk( buffer, " \t" );
+                       if( p ) {
+                           *p++ = 0;
+                           trim_spaces( p );
+                       }
+                       if( !p || !*p ) {
+                           m_free( buffer );
+                           arg->r_opt = -10;
+                       }
+                       else {
+                           store_alias( arg, buffer, p );
+                       }
+                   }
+               }
+               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);
@@ -247,7 +329,23 @@ optfile_parse( FILE *fp, const char *filename, unsigned *lineno,
                    else
                        buffer[i] = 0;
 
-                   if( !set_opt_arg(arg, opts[index].flags, buffer) )
+                   trim_spaces( 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;
@@ -275,11 +373,17 @@ 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 ) {
-               arg->r_opt = -2;   /* unknown option */
-               state = -1;        /* skip rest of line and leave */
+           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[idx].flags & 256)? -7:-2;
+                   state = -1;        /* skip rest of line and leave */
+               }
            }
            else
                state = 3;
@@ -325,10 +429,56 @@ optfile_parse( FILE *fp, const char *filename, unsigned *lineno,
 
 
 
+static int
+find_long_option( ARGPARSE_ARGS *arg,
+                 ARGPARSE_OPTS *opts, const char *keyword )
+{
+    int i;
+    size_t n;
+
+    /* 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;
+}
+
 int
 arg_parse( ARGPARSE_ARGS *arg, ARGPARSE_OPTS *opts)
 {
-    int index;
+    int idx;
     int argc;
     char **argv;
     char *s, *s2;
@@ -337,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:
@@ -356,48 +506,78 @@ 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;
        goto leave;
     }
     else if( *s == '-' && s[1] == '-' ) { /* long option */
+       char *argpos;
+
        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;
        }
 
-       for(i=0; opts[i].short_opt; i++ )
-           if( opts[i].long_opt && !strcmp( opts[i].long_opt, s+2) )
-               break;
+       argpos = strchr( s+2, '=' );
+       if( argpos )
+           *argpos = 0;
+       i = find_long_option( arg, opts, s+2 );
+       if( argpos )
+           *argpos = '=';
 
-       if( !opts[i].short_opt && !strcmp( "help", s+2) )
-           show_help(opts, arg->flags);
-       else if( !opts[i].short_opt && !strcmp( "version", s+2) )
-           show_version();
-       else if( !opts[i].short_opt && !strcmp( "warranty", s+2) ) {
-           puts( strusage(10) );
-           puts( strusage(31) );
+       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();
+               exit(0);
+           }
+       }
+       else if( i < 0 && !strcmp( "warranty", s+2) ) {
+           puts( strusage(16) );
+           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 );
+           }
+           fputs("--dump-options\n--help\n--version\n--warranty\n", stdout );
            exit(0);
        }
 
-       arg->r_opt = opts[i].short_opt;
-       if( !opts[i].short_opt ) {
-           arg->r_opt = -2; /* unknown option */
+       if( i == -2 ) /* ambiguous option */
+           arg->r_opt = -8;
+       else if( i == -1 ) {
+           arg->r_opt = -2;
            arg->r.ret_str = s+2;
        }
+       else
+           arg->r_opt = opts[i].short_opt;
+       if( i < 0 )
+           ;
        else if( (opts[i].flags & 7) ) {
-           s2 = argv[1];
+           if( argpos ) {
+               s2 = argpos+1;
+               if( !*s2 )
+                   s2 = NULL;
+           }
+           else
+               s2 = argv[1];
            if( !s2 && (opts[i].flags & 8) ) { /* no argument but it is okay*/
                arg->r_type = 0;               /* because it is optional */
            }
            else if( !s2 ) {
                arg->r_opt = -3; /* missing argument */
            }
-           else if( *s2 == '-' && (opts[i].flags & 8) ) {
+           else if( !argpos && *s2 == '-' && (opts[i].flags & 8) ) {
                /* the argument is optional and the next seems to be
                 * an option. We do not check this possible option
                 * but assume no argument */
@@ -405,13 +585,18 @@ arg_parse( ARGPARSE_ARGS *arg, ARGPARSE_OPTS *opts)
            }
            else {
                set_opt_arg(arg, opts[i].flags, s2);
-               argc--; argv++; index++; /* skip one */
+               if( !argpos ) {
+                   argc--; argv++; idx++; /* skip one */
+               }
            }
        }
        else { /* does not take an argument */
-           arg->r_type = 0;
+           if( argpos )
+               arg->r_type = -6; /* argument not expected */
+           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;
@@ -434,12 +619,15 @@ arg_parse( ARGPARSE_ARGS *arg, ARGPARSE_OPTS *opts)
                    break;
        }
 
-       if( !opts[i].short_opt && *s == 'h' )
-           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 ) {
-           arg->r_opt = -2; /* unknown option */
+           arg->r_opt = (opts[i].flags & 256)? -7:-2;
            arg->internal.inarg++; /* point to the next arg */
            arg->r.ret_str = s;
        }
@@ -464,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 */
@@ -475,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 */
@@ -492,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;
 }
 
@@ -520,37 +708,62 @@ set_opt_arg(ARGPARSE_ARGS *arg, unsigned flags, char *s)
     }
 }
 
+
+static size_t
+long_opt_strlen( ARGPARSE_OPTS *o )
+{
+    size_t n = strlen(o->long_opt);
+
+    if( o->description && *o->description == '|' ) {
+       const char *s;
+
+       s=o->description+1;
+       if( *s != '=' )
+           n++;
+       for(; *s && *s != '|'; s++ )
+           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 flags )
 {
     const char *s;
 
-    s = strusage(10);
-    fputs( s, stdout );
-    if( *s && s[strlen(s)-1] != '\n' )
-       putchar( '\n' );
-    s = strusage(12);
-    if( *s == '\n' )
-       s++;
+    show_version();
+    putchar('\n');
+    s = strusage(41);
     puts(s);
     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 != '\v' )
-                   if( (j=strlen(opts[i].long_opt)) > indent && j < 35 )
+               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 != '\v' )
+       if( *opts[0].description != '@' )
            puts("Options:");
        for(i=0; opts[i].short_opt; i++ ) {
            s = _( opts[i].description );
-           if( s && *s== '\r' ) /* hide this line */
+           if( s && *s== '@' && !s[1] ) /* hide this line */
                continue;
-           if( s && *s == '\v' ) { /* unindented comment only line */
+           if( s && *s == '@' ) { /* unindented comment only line */
                for(s++; *s; s++ ) {
                    if( *s == '\n' ) {
                        if( s[1] )
@@ -563,17 +776,45 @@ show_help( ARGPARSE_OPTS *opts, unsigned flags )
                continue;
            }
 
-           if( opts[i].short_opt < 256 )
+           j = 3;
+           if( opts[i].short_opt < 256 ) {
                printf(" -%c", opts[i].short_opt );
+               if( !opts[i].long_opt ) {
+                   if(s && *s == '|' ) {
+                       putchar(' '); j++;
+                       for(s++ ; *s && *s != '|'; s++, j++ )
+                           putchar(*s);
+                       if( *s )
+                           s++;
+                   }
+               }
+           }
            else
                fputs("   ", stdout);
-           j = 3;
-           if( opts[i].long_opt )
-               j += printf("%c --%s   ", opts[i].short_opt < 256?',':' ',
-                                         opts[i].long_opt );
+           if( opts[i].long_opt ) {
+               j += printf("%c --%s", opts[i].short_opt < 256?',':' ',
+                                      opts[i].long_opt );
+               if(s && *s == '|' ) {
+                   if( *++s != '=' ) {
+                       putchar(' ');
+                       j++;
+                   }
+                   for( ; *s && *s != '|'; s++, j++ )
+                       putchar(*s);
+                   if( *s )
+                       s++;
+               }
+               fputs("   ", stdout);
+               j += 3;
+           }
            for(;j < indent; j++ )
                putchar(' ');
            if( s ) {
+               if( *s && j > indent ) {
+                   putchar('\n');
+                   for(j=0;j < indent; j++ )
+                       putchar(' ');
+               }
                for(; *s; s++ ) {
                    if( *s == '\n' ) {
                        if( s[1] ) {
@@ -591,11 +832,7 @@ show_help( ARGPARSE_OPTS *opts, unsigned flags )
        if( flags & 32 )
            puts("\n(A single dash may be used instead of the double ones)");
     }
-    if( *(s=strusage(26)) ) {  /* bug reports to ... */
-       putchar('\n');
-       fputs(s, stdout);
-    }
-    if( *(s=strusage(30)) ) {  /* special notes */
+    if( (s=strusage(19)) ) {  /* bug reports to ... */
        putchar('\n');
        fputs(s, stdout);
     }
@@ -607,189 +844,96 @@ static void
 show_version()
 {
     const char *s;
-    printf("%s version %s (%s", strusage(13), strusage(14), strusage(45) );
-    if( (s = strusage(24)) && *s ) {
-      #ifdef DEBUG
-       printf(", %s, dbg)\n", s);
-      #else
-       printf(", %s)\n", s);
-      #endif
-    }
-    else {
-      #ifdef DEBUG
-       printf(", dbg)\n");
-      #else
-       printf(")\n");
-      #endif
-    }
+    int i;
+    /* version line */
+    fputs(strusage(11), stdout);
+    if( (s=strusage(12)) )
+       printf(" (%s)", s );
+    printf(" %s\n", strusage(13) );
+    /* additional version lines */
+    for(i=20; i < 30; i++ )
+       if( (s=strusage(i)) )
+           printf("%s\n", s );
+    /* copyright string */
+    if( (s=strusage(14)) )
+       printf("%s\n", s );
+    /* copying conditions */
+    if( (s=strusage(15)) )
+       fputs(s, stdout);
+    /* thanks */
+    if( (s=strusage(18)) )
+       fputs(s, stdout);
+    /* additional program info */
+    for(i=30; i < 40; i++ )
+       if( (s=strusage(i)) )
+           fputs( (const byte*)s, stdout);
     fflush(stdout);
-    exit(0);
 }
 
 
-
 void
 usage( int level )
 {
-    static int sentinel=0;
-
-    if( sentinel )
-       return;
-
-    sentinel++;
     if( !level ) {
-       fputs( strusage(level), stderr ); putc( '\n', stderr );
-       fputs( strusage(31), stderr);
-      #if DEBUG
-       fprintf(stderr, "%s (%s - Debug)\n", strusage(32), strusage(24) );
-      #else
-       fprintf(stderr, "%s (%s)\n", strusage(32), strusage(24) );
-      #endif
+       fprintf(stderr,"%s %s; %s\n", strusage(11), strusage(13),
+                                                    strusage(14) );
        fflush(stderr);
     }
     else if( level == 1 ) {
-       fputs(strusage(level),stderr);putc('\n',stderr);
-       exit(2);}
+       fputs(strusage(40),stderr);
+       exit(2);
+    }
     else if( level == 2 ) {
-       puts(strusage(level)); exit(0);}
-    sentinel--;
+       puts(strusage(41));
+       exit(0);
+    }
 }
 
-
+/* Level
+ *     0: Copyright String auf stderr ausgeben
+ *     1: Kurzusage auf stderr ausgeben und beenden
+ *     2: Langusage auf stdout ausgeben und beenden
+ *    11: name of program
+ *    12: 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)
+ */
 const char *
 default_strusage( int level )
 {
-    const char *p;
+    const char *p = NULL;
     switch( level ) {
-      case  0: p = strusage(10); break;
-      case  1: p = strusage(11); break;
-      case  2: p = strusage(12); break;
-      case 10: p = "WkLib"
-                 #if DOS386 && __WATCOMC__
-                   " (DOS4G)"
-                 #elif DOS386
-                   " (DOSX)"
-                 #elif DOS16RM
-                   " (DOS16RM)"
-                 #elif M_I86VM
-                   " (VCM)"
-                 #elif UNIX || POSIX
-                   " (Posix)"
-                 #elif OS2
-                   " (OS/2)"
-                 #elif WINNT && __CYGWIN32__
-                   " (CygWin)"
-                 #elif WINNT
-                   " (WinNT)"
-                 #elif NETWARE
-                   " (Netware)"
-                 #elif VMS
-                   " (VMS)"
-                 #endif
-                   "; Copyright (c) 1997 by Werner Koch (dd9jn)" ; break;
-      case 11: p = "usage: ?"; break;
-      case 16:
-      case 15: p = "[Untitled]"; break;
-      case 23: p = "[unknown]"; break;
-      case 24: p = ""; break;
-      case 26: p = ""; break;
-      case 12: 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 2 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 program; if not, write to the Free Software\n"
-   "Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,"
-                                                                 " USA.\n" ;
+      case 11: p = "foo"; break;
+      case 13: p = "0.0"; 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"
+"under certain conditions. See the file COPYING for details.\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 2 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 program; if not, write to the Free Software\n"
+"Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.\n";
        break;
-      case 22:
-         #if MSDOS
-           #if USE_EMS
-             p = "MSDOS+EMS";
-           #else
-             p = "MSDOS";
-           #endif
-         #elif OS2
-           p = "OS/2";
-         #elif WINNT && __CYGWIN32__
-           p = "CygWin";
-         #elif WINNT
-           p = "WinNT";
-         #elif DOS386
-           p = "DOS386";
-         #elif EMX
-           p = "EMX";
-         #elif DOS16RM
-           p = "DOS16RM";
-         #elif NETWARE
-           p = "Netware";
-         #elif __linux__
-           p = "Linux";
-         #elif UNIX || M_UNIX || M_XENIX
-           p = "UNIX";
-         #elif VMS
-           p = "VMS";
-         #else
-           p = "UnknownOS";
-         #endif
-           break;
-      case 30: p = ""; break;
-      case 31: p =
-    "This program comes with ABSOLUTELY NO WARRANTY.\n"
-    "This is free software, and you are welcome to redistribute it\n"
-    "under certain conditions. See the file COPYING for details.\n";
-           break;
-      case 32: p = "["
-         #if MSDOS
-             "MSDOS Version"
-         #elif DOS386 && __ZTC__
-           "32-Bit MSDOS Version (Zortech's DOSX)"
-         #elif DOS386
-           "32-Bit MSDOS Version"
-         #elif OS20 && EMX
-           "OS/2 2.x EMX Version"
-         #elif OS20
-           "OS/2 2.x Version"
-         #elif OS2
-           "OS/2 1.x Version"
-         #elif WINNT && __CYGWIN32__
-           "Cygnus WinAPI Version"
-         #elif WINNT
-           "Windoze NT Version"
-         #elif EMX
-           "EMX Version"
-         #elif NETWARE
-           "NLM Version"
-         #elif DOS16RM
-           "DOS16RM Version"
-         #elif __linux__
-           "Linux Version"
-         #elif VMS
-           "OpenVMS Version"
-         #elif POSIX
-           "POSIX Version"
-         #elif M_UNIX || M_XENIX
-           "*IX Version"
-         #endif
-           "]";
-           break;
-      case 33: p =
-         #ifdef MULTI_THREADED
-           "mt"
-         #else
-           ""
-         #endif
-           ; break;
-      case 42:
-      case 43:
-      case 44:
-      case 45: p = ""; break;
-      default: p = "?";
+      case 40: /* short and long usage */
+      case 41: p = ""; break;
     }
 
     return p;
@@ -825,7 +969,7 @@ main(int argc, char **argv)
 
     while( ArgParse( &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;
@@ -844,9 +988,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 )