diff options
Diffstat (limited to 'util/argparse.c')
-rw-r--r-- | util/argparse.c | 653 |
1 files changed, 653 insertions, 0 deletions
diff --git a/util/argparse.c b/util/argparse.c new file mode 100644 index 000000000..3d51d014c --- /dev/null +++ b/util/argparse.c @@ -0,0 +1,653 @@ +/* [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 ****/ |