1
0
mirror of git://git.gnupg.org/gnupg.git synced 2024-06-13 00:09:51 +02:00
gnupg/util/argparse.c

654 lines
17 KiB
C
Raw Normal View History

1997-11-18 15:06:00 +01:00
/* [argparse.c wk 17.06.97] Argument Parser for option handling
* Copyright (c) 1997 by Werner Koch (dd9jn)
* This file is part of WkLib.
*
* WkLib 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,
* 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 copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
*
*
* Note: This is an independent version of the one in WkLib
*/
#include <config.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "util.h"
#ifdef DOCUMENTATION
@Summary arg_parse
#include <wk/lib.h>
typedef struct {
char *argc; /* pointer to argc (value subject to change) */
char ***argv; /* pointer to argv (value subject to change) */
unsigned flags; /* Global flags (DO NOT CHANGE) */
int err; /* print error about last option */
/* 1 = warning, 2 = abort */
int r_opt; /* return option */
int r_type; /* type of return value (0 = no argument found)*/
union {
int ret_int;
long ret_long
ulong ret_ulong;
char *ret_str;
} r; /* Return values */
struct {
int index;
const char *last;
} internal; /* DO NOT CHANGE */
} ARGPARSE_ARGS;
typedef struct {
int short_opt;
const char *long_opt;
unsigned flags;
} ARGPARSE_OPTS;
int arg_parse( ARGPARSE_ARGS *arg, ARGPARSE_OPTS *opts );
@Description
This is my replacement for getopt(). See the example for a typical usage.
Global flags are:
Bit 0 : Do not remove options form argv
Bit 1 : Do not stop at last option but return other args
with r_opt set to -1.
Bit 2 : Assume options and real args are mixed.
Bit 3 : Do not use -- to stop option processing.
Bit 4 : Do not skip the first arg.
Bit 5 : allow usage of long option with only one dash
all other bits must be set to zero, this value is modified by the function
so assume this is write only.
Local flags (for each option):
Bit 2-0 : 0 = does not take an argument
1 = takes int argument
2 = takes string argument
3 = takes long argument
4 = takes ulong argument
Bit 3 : argument is optional (r_type will the be set to 0)
Bit 4 : allow 0x etc. prefixed values.
If can 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.
@See Also
ArgExpand
@Notes
You do not need to process the options 'h', '--help' or '--version'
because this function includes standard help processing; but if you
specify '-h', '--help' or '--version' you have to do it yourself.
The option '--' stops argument processing; if bit 1 is set the function
continues to return normal arguments.
To process float args or unsigned args you must use a string args and do
the conversion yourself.
@Example
ARGPARSE_OPTS opts[] = {
{ 'v', "verbose", 0 },
{ 'd', "debug", 0 },
{ 'o', "output", 2 },
{ 'c', "cross-ref", 2|8 },
{ 'm', "my-option", 1|8 },
{ 500, "have-no-short-option-for-this-long-option", 0 },
{0} };
ARGPARSE_ARGS pargs = { &argc, &argv, 0 }
while( ArgParse( &pargs, &opts) ) {
switch( pargs.r_opt ) {
case 'v': opt.verbose++; break;
case 'd': opt.debug++; break;
case 'o': opt.outfile = pargs.r.ret_str; break;
case 'c': opt.crf = pargs.r_type? pargs.r.ret_str:"a.crf"; break;
case 'm': opt.myopt = pargs.r_type? pargs.r.ret_int : 1; break;
case 500: opt.a_long_one++; break
default : pargs.err = 1; break; /* force warning output */
}
}
if( argc > 1 )
log_fatal( "Too many args");
#endif /*DOCUMENTATION*/
static void set_opt_arg(ARGPARSE_ARGS *arg, unsigned flags, char *s);
static void show_help(ARGPARSE_OPTS *opts, unsigned flags);
static void show_version(void);
int
arg_parse( ARGPARSE_ARGS *arg, ARGPARSE_OPTS *opts)
{
int index;
int argc;
char **argv;
char *s, *s2;
int i;
if( !(arg->flags & (1<<15)) ) { /* initialize this instance */
arg->internal.index = 0;
arg->internal.last = NULL;
arg->internal.inarg = 0;
arg->internal.stopped= 0;
arg->err = 0;
arg->flags |= 1<<15; /* mark initialized */
if( *arg->argc < 0 )
log_bug("Invalid argument for ArgParse\n");
}
argc = *arg->argc;
argv = *arg->argv;
index = arg->internal.index;
if( arg->err ) { /* last option was erroneous */
if( arg->r_opt == -3 )
s = "Missing argument for option \"%.50s\"\n";
else
s = "Invalid option \"%.50s\"\n";
log_error(s, arg->internal.last? arg->internal.last:"[??]" );
if( arg->err != 1 )
exit(2);
arg->err = 0;
}
if( !index && argc && !(arg->flags & (1<<4)) ) { /* skip the first entry */
argc--; argv++; index++;
}
next_one:
if( !argc ) { /* no more args */
arg->r_opt = 0;
goto leave; /* ready */
}
s = *argv;
arg->internal.last = s;
if( arg->internal.stopped && (arg->flags & (1<<1)) ) {
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 */
}
else if( arg->internal.stopped ) { /* ready */
arg->r_opt = 0;
goto leave;
}
else if( *s == '-' && s[1] == '-' ) { /* long option */
arg->internal.inarg = 0;
if( !s[2] && !(arg->flags & (1<<3)) ) { /* stop option processing */
arg->internal.stopped = 1;
argc--; argv++; index++;
goto next_one;
}
for(i=0; opts[i].short_opt; i++ )
if( opts[i].long_opt && !strcmp( opts[i].long_opt, s+2) )
break;
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) );
exit(0);
}
arg->r_opt = opts[i].short_opt;
if( !opts[i].short_opt ) {
arg->r_opt = -2; /* unknown option */
arg->r.ret_str = s+2;
}
else if( (opts[i].flags & 7) ) {
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) ) {
/* 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 = 0;
}
else {
set_opt_arg(arg, opts[i].flags, s2);
argc--; argv++; index++; /* skip one */
}
}
else { /* does not take an argument */
arg->r_type = 0;
}
argc--; argv++; index++; /* set to next one */
}
else if( (*s == '-' && s[1]) || arg->internal.inarg ) { /* short option */
int dash_kludge = 0;
i = 0;
if( !arg->internal.inarg ) {
arg->internal.inarg++;
if( arg->flags & (1<<5) ) {
for(i=0; opts[i].short_opt; i++ )
if( opts[i].long_opt && !strcmp( opts[i].long_opt, s+1)) {
dash_kludge=1;
break;
}
}
}
s += arg->internal.inarg;
if( !dash_kludge ) {
for(i=0; opts[i].short_opt; i++ )
if( opts[i].short_opt == *s )
break;
}
if( !opts[i].short_opt && *s == 'h' )
show_help(opts, arg->flags);
arg->r_opt = opts[i].short_opt;
if( !opts[i].short_opt ) {
arg->r_opt = -2; /* unknown option */
arg->internal.inarg++; /* point to the next arg */
arg->r.ret_str = s;
}
else if( (opts[i].flags & 7) ) {
if( s[1] && !dash_kludge ) {
s2 = s+1;
set_opt_arg(arg, opts[i].flags, s2);
}
else {
s2 = argv[1];
if( !s2 && (opts[i].flags & 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 == '-' && s2[1] && (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 */
arg->r_type = 0;
}
else {
set_opt_arg(arg, opts[i].flags, s2);
argc--; argv++; index++; /* skip one */
}
}
s = "x"; /* so that !s[1] yields false */
}
else { /* does not take an argument */
arg->r_type = 0;
arg->internal.inarg++; /* point to the next arg */
}
if( !s[1] || dash_kludge ) { /* no more concatenated short options */
arg->internal.inarg = 0;
argc--; argv++; index++;
}
}
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 */
}
else {
arg->internal.stopped = 1; /* stop option processing */
goto next_one;
}
leave:
*arg->argc = argc;
*arg->argv = argv;
arg->internal.index = index;
return arg->r_opt;
}
static void
set_opt_arg(ARGPARSE_ARGS *arg, unsigned flags, char *s)
{
int base = (flags & 16)? 0 : 10;
switch( arg->r_type = (flags & 7) ) {
case 1: /* takes int argument */
arg->r.ret_int = (int)strtol(s,NULL,base);
break;
default:
case 2: /* takes string argument */
arg->r.ret_str = s;
break;
case 3: /* takes long argument */
arg->r.ret_long= strtol(s,NULL,base);
break;
case 4: /* takes ulong argument */
arg->r.ret_ulong= strtoul(s,NULL,base);
break;
}
}
static void
show_help( ARGPARSE_OPTS *opts, unsigned flags )
{
const char *s;
puts( strusage(10) );
s = strusage(12);
if( *s == '\n' )
s++;
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( (j=strlen(opts[i].long_opt)) > indent && j < 35 )
indent = j;
}
/* example: " -v, --verbose Viele Sachen ausgeben" */
indent += 10;
puts("Options:");
for(i=0; opts[i].short_opt; i++ ) {
if( opts[i].short_opt < 256 )
printf(" -%c", opts[i].short_opt );
else
fputs(" ", stdout);
j = 3;
if( opts[i].long_opt )
j += printf("%c --%s ", opts[i].short_opt < 256?',':' ',
opts[i].long_opt );
for(;j < indent; j++ )
putchar(' ');
if( (s = opts[i].description) ) {
for(; *s; s++ ) {
if( *s == '\n' ) {
if( s[1] ) {
putchar('\n');
for(j=0;j < indent; j++ )
putchar(' ');
}
}
else
putchar(*s);
}
}
putchar('\n');
}
if( flags & 32 )
puts("\n(A single dash may be used instead of the double ones)");
}
fflush(stdout);
exit(0);
}
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
}
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
fflush(stderr);
}
else if( level == 1 ) {
fputs(strusage(level),stderr);putc('\n',stderr);
exit(2);}
else if( level == 2 ) {
puts(strusage(level)); exit(0);}
sentinel--;
}
const char *
default_strusage( int level )
{
const char *p;
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 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"
"WkLib 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 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 = "?";
}
return p;
}
#ifdef TEST
static struct {
int verbose;
int debug;
char *outfile;
char *crf;
int myopt;
int echo;
int a_long_one;
}opt;
int
main(int argc, char **argv)
{
ARGPARSE_OPTS opts[] = {
{ 'v', "verbose", 0 , "Laut sein"},
{ 'e', "echo" , 0 , "Zeile ausgeben, damit wir sehen, was wir einegegeben haben"},
{ 'd', "debug", 0 , "Debug\nfalls mal etasws\nSchief geht"},
{ 'o', "output", 2 },
{ 'c', "cross-ref", 2|8, "cross-reference erzeugen\n" },
{ 'm', "my-option", 1|8 },
{ 500, "a-long-option", 0 },
{0} };
ARGPARSE_ARGS pargs = { &argc, &argv, 2|4|32 };
int i;
while( ArgParse( &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 = 1; break; /* force warning output */
}
}
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
/**** bottom of file ****/