/* [argparse.c wk 17.06.97] Argument Parser for option handling * Copyright (C) 1998, 1999, 2000, 2001, 2006 * 2007 Free Software Foundation, Inc. * * This file is part of JNLIB. * * 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 3 of * the License, or (at your option) any later version. * * 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. * * You should have received a copy of the GNU Lesser General Public * License along with this program; if not, see . */ #include #include #include #include #include #include "libjnlib-config.h" #include "mischelp.h" #include "stringhelp.h" #include "logging.h" #ifdef JNLIB_NEED_UTF8CONV #include "utf8conv.h" #endif #include "argparse.h" /********************************* * @Summary arg_parse * #include * * 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 idx; * const char *last; * void *aliases; * } 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 * Bit 6 : ignore --version * 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. * 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/-7 to indicate an unknown option/command. * @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"); * */ 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 const char *(*strusage_handler)( int ) = NULL; 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 void initialize( ARGPARSE_ARGS *arg, const char *filename, unsigned *lineno ) { if( !(arg->flags & (1<<15)) ) { /* initialize this instance */ arg->internal.idx = 0; arg->internal.last = NULL; arg->internal.inarg = 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 ) jnlib_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 = "argument not expected\n"; else if( arg->r_opt == -5 ) s = "read error\n"; else if( arg->r_opt == -4 ) s = "keyword too long\n"; else if( arg->r_opt == -3 ) s = "missing argument\n"; else if( arg->r_opt == -7 ) s = "invalid command\n"; else if( arg->r_opt == -10 ) s = "invalid alias definition\n"; else s = "invalid option\n"; jnlib_log_error("%s:%u: %s\n", filename, *lineno, s); } else { s = arg->internal.last? arg->internal.last:"[??]"; if( arg->r_opt == -3 ) jnlib_log_error ("Missing argument for option \"%.50s\"\n", s); else if( arg->r_opt == -6 ) jnlib_log_error ("Option \"%.50s\" does not expect an argument\n", s ); else if( arg->r_opt == -7 ) jnlib_log_error ("Invalid command \"%.50s\"\n", s); else if( arg->r_opt == -8 ) jnlib_log_error ("Option \"%.50s\" is ambiguous\n", s); else if( arg->r_opt == -9 ) jnlib_log_error ("Command \"%.50s\" is ambiguous\n",s ); else jnlib_log_error ("Invalid option \"%.50s\"\n", s); } if( arg->err != 1 ) 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 = jnlib_xmalloc( 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" * 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 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 ); initialize( arg, filename, lineno ); /* find the next keyword */ state = i = 0; for(;;) { c=getc(fp); if( c == '\n' || c== EOF ) { if( c != EOF ) ++*lineno; if( state == -1 ) break; else if( state == 2 ) { keyword[i] = 0; for(i=0; opts[i].short_opt; i++ ) if( opts[i].long_opt && !strcmp( opts[i].long_opt, keyword) ) break; 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[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[idx].flags & 7) ) /* does not take an arg */ arg->r_type = 0; /* okay */ 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( 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 ) { jnlib_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 = jnlib_xstrdup(keyword); } else buffer[i] = 0; 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); } break; } else if( c == EOF ) { if( ferror(fp) ) arg->r_opt = -5; /* read error */ else arg->r_opt = 0; /* eof */ break; } state = 0; i = 0; } else if( state == -1 ) ; /* skip */ else if( !state && isspace(c) ) ; /* skip leading white space */ else if( !state && c == '#' ) state = 1; /* start of a comment */ else if( state == 1 ) ; /* skip comments */ else if( state == 2 && isspace(c) ) { keyword[i] = 0; for(i=0; opts[i].short_opt; i++ ) if( opts[i].long_opt && !strcmp( opts[i].long_opt, keyword) ) break; 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; } else if( state == 3 ) { /* skip leading spaces of the argument */ if( !isspace(c) ) { i = 0; keyword[i++] = c; state = 4; } } else if( state == 4 ) { /* collect the argument */ if( buffer ) { if( i < buflen-1 ) buffer[i++] = c; else { buflen += 50; buffer = jnlib_xrealloc(buffer, buflen); buffer[i++] = c; } } else if( i < DIM(keyword)-1 ) keyword[i++] = c; else { buflen = DIM(keyword)+50; buffer = jnlib_xmalloc(buflen); memcpy(buffer, keyword, i); buffer[i++] = c; } } else if( i >= DIM(keyword)-1 ) { arg->r_opt = -4; /* keyword to long */ state = -1; /* skip rest of line and leave */ } else { keyword[i++] = c; state = 2; } } return arg->r_opt; } 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 idx; int argc; char **argv; char *s, *s2; int i; initialize( arg, NULL, NULL ); argc = *arg->argc; argv = *arg->argv; idx = arg->internal.idx; if( !idx && argc && !(arg->flags & (1<<4)) ) { /* skip the first entry */ argc--; argv++; idx++; } 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++; 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++; idx++; goto next_one; } argpos = strchr( s+2, '=' ); if( argpos ) *argpos = 0; i = find_long_option( arg, opts, s+2 ); if( argpos ) *argpos = '='; if( i < 0 && !strcmp( "help", s+2) ) 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); } 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) ) { 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( !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 */ arg->r_type = 0; } else { set_opt_arg(arg, opts[i].flags, s2); if( !argpos ) { argc--; argv++; idx++; /* skip one */ } } } else { /* does not take an argument */ if( argpos ) arg->r_type = -6; /* argument not expected */ else arg->r_type = 0; } argc--; argv++; idx++; /* 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' || *s == '?' ) ) show_help(opts, arg->flags); arg->r_opt = opts[i].short_opt; if( !opts[i].short_opt ) { arg->r_opt = (opts[i].flags & 256)? -7:-2; 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++; idx++; /* 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++; 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++; idx++; /* set to next one */ } else { arg->internal.stopped = 1; /* stop option processing */ goto next_one; } leave: *arg->argc = argc; *arg->argv = argv; arg->internal.idx = idx; return arg->r_opt; } static int 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); return 0; case 3: /* takes long argument */ arg->r.ret_long= strtol(s,NULL,base); return 0; case 4: /* takes ulong argument */ arg->r.ret_ulong= strtoul(s,NULL,base); return 0; case 2: /* takes string argument */ default: arg->r.ret_str = s; return 1; } } static size_t long_opt_strlen( ARGPARSE_OPTS *o ) { size_t n = strlen (o->long_opt); 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++; /* For a (mostly) correct length calculation we exclude continuation bytes (10xxxxxx) if we are on a native utf8 terminal. */ for (; *s && *s != '|'; s++ ) #ifdef JNLIB_NEED_UTF8CONV if ( is_utf8 && (*s&0xc0) != 0x80 ) #endif 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; 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 != '@' ) if( (j=long_opt_strlen(opts+i)) > indent && j < 35 ) indent = j; } /* example: " -v, --verbose Viele Sachen ausgeben" */ indent += 10; if( *opts[0].description != '@' ) puts("Options:"); for(i=0; opts[i].short_opt; i++ ) { s = _( opts[i].description ); if( s && *s== '@' && !s[1] ) /* hide this line */ continue; if( s && *s == '@' ) { /* unindented comment only line */ for(s++; *s; s++ ) { if( *s == '\n' ) { if( s[1] ) putchar('\n'); } else putchar(*s); } putchar('\n'); continue; } 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); 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] ) { 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)"); } 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); fputs (PACKAGE_BUGREPORT, stdout); s2 += 7; if (*s2) fputs (s2, stdout); } else fputs(s, stdout); } fflush(stdout); exit(0); } static void show_version() { const char *s; 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 ); /* Licence string. */ if( (s=strusage (10)) ) 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 (s, stdout); fflush(stdout); } void usage (int level) { if (!level) { fprintf(stderr,"%s %s; %s\n", strusage(11), strusage(13), strusage (14)); fflush (stderr); } else if (level == 1) { fputs (strusage (40), stderr); exit (2); } else if (level == 2) { puts (strusage(41)); exit (0); } } /* Level * 0: Print copyright string to stderr * 1: Print a short usage hint to stderr and terminate * 2: Print a long usage hint to stdout and terminate * 10: Return license info string * 11: Return the name of the program * 12: Return 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 * strusage( int level ) { const char *p = strusage_handler? strusage_handler(level) : NULL; if( p ) return p; switch( level ) { case 10: p = ("License GPLv3+: GNU GPL version 3 or later " ""); break; case 11: p = "foo"; break; case 13: p = "0.0"; break; case 14: p = "Copyright (C) 2007 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"; 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 3 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 software. If not, see .\n"; break; case 40: /* short and long usage */ case 41: p = ""; break; } return p; } void set_strusage( const char *(*f)( int ) ) { strusage_handler = f; } #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 ein" " gegeben haben")}, { 'd', "debug", 0 , "Debug\nfalls mal etwas\nschief geht"}, { 'o', "output", 2 }, { 'c', "cross-ref", 2|8, "cross-reference erzeugen\n" }, /* Note that on a non-utf8 terminal the ß might garble the output. */ { 's', "street", 0, "|Straße|set the name of the street to Straße" }, { 'm', "my-option", 1|8 }, { 500, "a-long-option", 0 }, {0} }; ARGPARSE_ARGS pargs = { &argc, &argv, 2|4|32 }; int i; while( arg_parse ( &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 ****/