--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/optparser.c Tue Feb 15 18:57:52 2005 +0100 @@ -0,0 +1,308 @@ +/* + * libtu/optparser.c + * + * Copyright (c) Tuomo Valkonen 1999-2000. + * + * This file is distributed under the terms of the "Artistic License". + * See the included file LICENSE for details. + */ + +#include <string.h> +#include <stdlib.h> + +#include "include/util.h" +#include "include/misc.h" +#include "include/optparser.h" +#include "include/output.h" + + +#define O_CHAINABLE(o) (o->flags&OPT_CHAINABLE) +#define O_IMM_ARG(o) (o->flags&OPT__IMM_ARG) +#define O_NO_DASH(o) (o->flags&OPT_NO_DASH) +#define O_MIDLONG(o) (o->flags&OPT_MIDLONG) +#define O_NO_LONG(o) (o->flags&OPT_NO_LONG) +#define O_ARGS(o) (o->flags&OPT_OPT_ARG) +#define O_ARG(o) (o->flasg&OPT_ARG) +#define O_OPT_ARG(o) (O_ARGS(o)==OPT_OPT_ARG) +#define O_ID(o) (o->optid) + + +static const OptParserOpt *o_opts=NULL; +static char *const *o_current=NULL; +static int o_left=0; +static const char* o_chain_ptr=NULL; +static int o_args_left=0; +static const char*o_tmp=NULL; +static int o_error=0; + + +/* */ + + +void optparser_init(int argc, char *const argv[], const OptParserOpt *opts) +{ + o_opts=opts; + o_current=argv+1; + o_left=argc-1; + o_chain_ptr=NULL; + o_args_left=0; + o_tmp=NULL; +} + + +/* */ + + +static const OptParserOpt *find_chain_opt(char p, const OptParserOpt *o) +{ + for(;O_ID(o);o++){ + if(O_CHAINABLE(o) && O_ID(o)==p) + return o; + } + return NULL; +} + + +static bool valid_chain(const char *p, const OptParserOpt *o) +{ + while(*p!='\0'){ + if(!find_chain_opt(*p, o)) + return FALSE; + p++; + } + return TRUE; +} + + +static bool is_option(const char *p) +{ + if(p==NULL) + return FALSE; + if(*p++!='-') + return FALSE; + if(*p++!='-') + return FALSE; + if(*p=='\0') + return FALSE; + return TRUE; +} + + +/* */ + + +int optparser_get_opt() +{ +#define RET(X) return o_tmp=p, o_error=X + const char *p, *p2=NULL; + bool lo=FALSE, dash=TRUE; + const OptParserOpt *o; + int l; + + while(o_args_left) + optparser_get_arg(); + + o_tmp=NULL; + + /* Are we doing a chain (i.e. opt. of style 'tar xzf') ? */ + if(o_chain_ptr!=NULL){ + p=o_chain_ptr++; + if(!*o_chain_ptr) + o_chain_ptr=NULL; + + o=find_chain_opt(*p, o_opts); + + if(o==NULL) + RET(E_OPT_INVALID_CHAIN_OPTION); + + goto do_arg; + } + + if(o_left<1) + return OPT_ID_END; + + o_left--; + p=*o_current++; + + if(*p!='-'){ + /* foo */ + dash=FALSE; + }else if(*(p+1)=='-'){ + /* --foo */ + if(*(p+2)=='\0'){ + /* -- */ + o_args_left=o_left; + RET(OPT_ID_ARGUMENT); + } + lo=TRUE; + p2=p+2; + }else{ + /* -foo */ + if(*(p+1)=='\0'){ + /* - */ + o_args_left=1; + RET(OPT_ID_ARGUMENT); + } + p2=p+1; + } + + o=o_opts; + +again: + for(;O_ID(o);o++){ + if(lo){ + /* Do long option (--foo=bar) */ + if(o->longopt==NULL) + continue; + l=strlen(o->longopt); + if(strncmp(p2, o->longopt, l)!=0) + continue; + + if(O_NO_LONG(o)) + RET(E_OPT_INVALID_OPTION); + + if(p2[l]=='\0'){ + if(O_ARGS(o)==OPT_ARG) + RET(E_OPT_MISSING_ARGUMENT); + return O_ID(o); + }else if(p2[l]=='='){ + if(!O_ARGS(o)) + RET(E_OPT_UNEXPECTED_ARGUMENT); + if(p2[l+1]=='\0') + RET(E_OPT_MISSING_ARGUMENT); + o_tmp=p2+l+1; + o_args_left=1; + return O_ID(o); + } + }else{ + /* Do midlong option (-foobar) */ + if(dash && o->longopt!=NULL && strcmp(p2, o->longopt)==0){ + if(!O_MIDLONG(o)) + RET(E_OPT_INVALID_OPTION); + goto do_arg; + } + + /* Do short option (-f) */ + if((!dash && !O_NO_DASH(o)) || *p2!=O_ID(o)) + continue; + + if(*(p2+1)!='\0'){ + if(O_CHAINABLE(o) && valid_chain(p2+1, o_opts)){ + o_chain_ptr=p2+1; + }else if(O_IMM_ARG(o)){ + o_tmp=p2+1; + o_args_left=1; + return O_ID(o); + }else{ + continue; + } + } +do_arg: + if(!O_ARGS(o)) + return O_ID(o); + + if(O_ARGS(o)==OPT_ARG){ + if(o_left==0) + RET(E_OPT_MISSING_ARGUMENT); + }else{ + if(!o_left || is_option(*o_current)) + return O_ID(o); + } + + o_args_left=1; + return O_ID(o); + } + } + + if(dash) + RET(E_OPT_INVALID_OPTION); + + RET(OPT_ID_ARGUMENT); +#undef RET +} + + +/* */ + + +const char* optparser_get_arg() +{ + const char *p; + + if(o_tmp!=NULL){ + /* If o_args_left==0, then were returning an invalid option + * otherwise an immediate argument (e.g. -funsigned-char + * where '-f' is the option and 'unsigned-char' the argument) + */ + if(o_args_left>0) + o_args_left--; + p=o_tmp; + o_tmp=NULL; + return p; + } + + if(o_args_left<1 || o_left<1) + return NULL; + + o_left--; + o_args_left--; + return *o_current++; +} + + +/* */ + +static void warn_arg(const char *e) +{ + const char*p=optparser_get_arg(); + + if(p==NULL) + warn("%s (null)", e); + else + warn("%s \'%s\'", e, p); +} + + +static void warn_opt(const char *e) +{ + if(o_chain_ptr!=NULL && o_tmp!=NULL) + warn("%s \'-%c\'", e, *o_tmp); + else + warn_arg(e); +} + + +void optparser_print_error() +{ + switch(o_error){ + case E_OPT_INVALID_OPTION: + warn_opt(TR("Invalid option")); + break; + + case E_OPT_INVALID_CHAIN_OPTION: + warn_opt(TR("Invalid option in chain (this cannot happen)")); + break; + + case E_OPT_SYNTAX_ERROR: + warn_arg(TR("Syntax error while parsing")); + break; + + case E_OPT_MISSING_ARGUMENT: + warn_opt(TR("Missing argument to")); + break; + + case E_OPT_UNEXPECTED_ARGUMENT: + warn_opt(TR("No argument expected to")); + break; + + case OPT_ID_ARGUMENT: + warn(TR("Unexpected argument")); + break; + + default: + warn(TR("(unknown error)")); + } + + o_tmp=NULL; + o_error=0; +}