Sat, 20 May 2000 17:43:36 +0200
trunk: changeset 14
Added dlist.h for doubly linked lists.
| 0 | 1 | /* |
| 2 | * libtu/optparser.c | |
| 3 | * | |
| 9 | 4 | * Copyright (c) Tuomo Valkonen 1999-2000. |
| 0 | 5 | * See the included file LICENSE for details. |
| 6 | */ | |
| 7 | ||
| 8 | #include <string.h> | |
| 9 | #include <stdlib.h> | |
| 10 | ||
| 5 | 11 | #include <libtu/util.h> |
| 12 | #include <libtu/misc.h> | |
| 13 | #include <libtu/optparser.h> | |
| 14 | #include <libtu/output.h> | |
| 0 | 15 | |
| 16 | ||
| 17 | #define O_ARGS(o) (o->flags&OPT_OPT_ARG) | |
| 18 | #define O_ARG(o) (o->flasg&OPT_ARG) | |
| 19 | #define O_OPT_ARG(o) (O_ARGS(o)==OPT_OPT_ARG) | |
| 20 | #define O_ID(o) (o->optid) | |
| 21 | ||
| 22 | ||
| 23 | static const OptParserOpt *o_opts=NULL; | |
| 24 | static char *const *o_current=NULL; | |
| 25 | static int o_left=0; | |
| 26 | static const char* o_chain_ptr=NULL; | |
| 27 | static int o_args_left=0; | |
| 28 | static const char*o_tmp=NULL; | |
| 29 | static int o_error=0; | |
| 3 | 30 | static int o_mode=OPTP_CHAIN; |
| 0 | 31 | |
| 32 | ||
| 33 | /* */ | |
| 34 | ||
| 35 | ||
| 3 | 36 | void optparser_init(int argc, char *const argv[], int mode, |
| 37 | const OptParserOpt *opts) | |
| 0 | 38 | { |
| 3 | 39 | o_mode=mode; |
| 0 | 40 | o_opts=opts; |
| 41 | o_current=argv+1; | |
| 42 | o_left=argc-1; | |
| 43 | o_chain_ptr=NULL; | |
| 44 | o_args_left=0; | |
| 45 | o_tmp=NULL; | |
| 46 | } | |
| 47 | ||
| 48 | ||
| 49 | /* */ | |
| 50 | ||
| 51 | ||
| 52 | static const OptParserOpt *find_chain_opt(char p, const OptParserOpt *o) | |
| 53 | { | |
| 54 | for(;O_ID(o);o++){ | |
| 3 | 55 | if(O_ID(o)==p) |
| 0 | 56 | return o; |
| 57 | } | |
| 58 | return NULL; | |
| 59 | } | |
| 60 | ||
| 61 | ||
| 62 | static bool is_option(const char *p) | |
| 63 | { | |
| 64 | if(p==NULL) | |
| 65 | return FALSE; | |
| 66 | if(*p++!='-') | |
| 67 | return FALSE; | |
| 68 | if(*p++!='-') | |
| 69 | return FALSE; | |
| 70 | if(*p=='\0') | |
| 71 | return FALSE; | |
| 72 | return TRUE; | |
| 73 | } | |
| 74 | ||
| 75 | ||
| 76 | /* */ | |
| 77 | ||
| 3 | 78 | enum{ |
| 79 | SHORT, MIDLONG, LONG | |
| 80 | }; | |
| 81 | ||
| 0 | 82 | |
| 83 | int optparser_get_opt() | |
| 84 | { | |
| 85 | #define RET(X) return o_tmp=p, o_error=X | |
| 86 | const char *p, *p2=NULL; | |
| 3 | 87 | bool dash=TRUE; |
| 88 | int type=SHORT; | |
| 0 | 89 | const OptParserOpt *o; |
| 90 | int l; | |
| 91 | ||
| 92 | while(o_args_left) | |
| 93 | optparser_get_arg(); | |
| 94 | ||
| 95 | o_tmp=NULL; | |
| 96 | ||
| 3 | 97 | /* Are we doing a chain (i.e. opt. of style 'tar xzf')? */ |
| 0 | 98 | if(o_chain_ptr!=NULL){ |
| 99 | p=o_chain_ptr++; | |
| 100 | if(!*o_chain_ptr) | |
| 101 | o_chain_ptr=NULL; | |
| 102 | ||
| 103 | o=find_chain_opt(*p, o_opts); | |
| 104 | ||
| 105 | if(o==NULL) | |
| 106 | RET(E_OPT_INVALID_CHAIN_OPTION); | |
| 107 | ||
| 108 | goto do_arg; | |
| 109 | } | |
| 110 | ||
| 111 | if(o_left<1) | |
| 112 | return OPT_ID_END; | |
| 113 | ||
| 114 | o_left--; | |
| 115 | p=*o_current++; | |
| 116 | ||
| 117 | if(*p!='-'){ | |
| 118 | dash=FALSE; | |
| 3 | 119 | if(o_mode!=OPTP_NO_DASH) |
| 120 | RET(OPT_ID_ARGUMENT); | |
| 0 | 121 | }else if(*(p+1)=='-'){ |
| 122 | /* --foo */ | |
| 123 | if(*(p+2)=='\0'){ | |
| 3 | 124 | /* -- arguments */ |
| 0 | 125 | o_args_left=o_left; |
| 126 | RET(OPT_ID_ARGUMENT); | |
| 127 | } | |
| 3 | 128 | type=LONG; |
| 0 | 129 | p2=p+2; |
| 130 | }else{ | |
| 131 | /* -foo */ | |
| 132 | if(*(p+1)=='\0'){ | |
| 133 | /* - */ | |
| 134 | o_args_left=1; | |
| 135 | RET(OPT_ID_ARGUMENT); | |
| 136 | } | |
| 3 | 137 | if(*(p+2)!='\0' && o_mode==OPTP_MIDLONG) |
| 138 | type=MIDLONG; | |
| 139 | ||
| 0 | 140 | p2=p+1; |
| 141 | } | |
| 142 | ||
| 143 | o=o_opts; | |
| 144 | ||
| 145 | again: | |
| 146 | for(;O_ID(o);o++){ | |
| 3 | 147 | if(type==LONG){ |
| 0 | 148 | /* Do long option (--foo=bar) */ |
| 149 | if(o->longopt==NULL) | |
| 150 | continue; | |
| 151 | l=strlen(o->longopt); | |
| 152 | if(strncmp(p2, o->longopt, l)!=0) | |
| 153 | continue; | |
| 154 | ||
| 155 | if(p2[l]=='\0'){ | |
| 156 | if(O_ARGS(o)==OPT_ARG) | |
| 157 | RET(E_OPT_MISSING_ARGUMENT); | |
| 158 | return O_ID(o); | |
| 159 | }else if(p2[l]=='='){ | |
| 160 | if(!O_ARGS(o)) | |
| 161 | RET(E_OPT_UNEXPECTED_ARGUMENT); | |
| 162 | if(p2[l+1]=='\0') | |
| 163 | RET(E_OPT_MISSING_ARGUMENT); | |
| 164 | o_tmp=p2+l+1; | |
| 165 | o_args_left=1; | |
| 166 | return O_ID(o); | |
| 167 | } | |
| 3 | 168 | continue; |
| 169 | }else if(type==MIDLONG){ | |
| 170 | if(o->longopt==NULL) | |
| 171 | continue; | |
| 172 | ||
| 173 | if(strcmp(p2, o->longopt)!=0) | |
| 174 | continue; | |
| 175 | }else{ /* type==SHORT */ | |
| 176 | if(*p2!=O_ID(o)) | |
| 0 | 177 | continue; |
| 178 | ||
| 179 | if(*(p2+1)!='\0'){ | |
| 3 | 180 | if(o_mode==OPTP_CHAIN || o_mode==OPTP_NO_DASH){ |
| 181 | /*valid_chain(p2+1, o_opts)*/ | |
| 182 | o_chain_ptr=p2+1; | |
| 183 | p++; | |
| 184 | }else if(o_mode==OPTP_IMMEDIATE){ | |
| 185 | if(!O_ARGS(o)){ | |
| 186 | if(*(p2+1)!='\0') | |
| 187 | RET(E_OPT_UNEXPECTED_ARGUMENT); | |
| 188 | }else{ | |
| 189 | if(*(p2+1)=='\0') | |
| 190 | RET(E_OPT_MISSING_ARGUMENT); | |
| 191 | o_tmp=p2+1; | |
| 192 | o_args_left=1; | |
| 193 | } | |
| 194 | return O_ID(o); | |
| 0 | 195 | }else{ |
| 3 | 196 | RET(E_OPT_SYNTAX_ERROR); |
| 0 | 197 | } |
| 198 | } | |
| 3 | 199 | } |
| 200 | ||
| 201 | do_arg: | |
| 202 | ||
| 203 | if(!O_ARGS(o)) | |
| 204 | return O_ID(o); | |
| 205 | ||
| 206 | if(!o_left || is_option(*o_current)){ | |
| 207 | if(O_ARGS(o)==OPT_OPT_ARG) | |
| 0 | 208 | return O_ID(o); |
| 3 | 209 | RET(E_OPT_MISSING_ARGUMENT); |
| 210 | } | |
| 0 | 211 | |
| 3 | 212 | o_args_left=1; |
| 213 | return O_ID(o); | |
| 0 | 214 | } |
| 215 | ||
| 216 | if(dash) | |
| 217 | RET(E_OPT_INVALID_OPTION); | |
| 218 | ||
| 219 | RET(OPT_ID_ARGUMENT); | |
| 220 | #undef RET | |
| 221 | } | |
| 222 | ||
| 223 | ||
| 224 | /* */ | |
| 225 | ||
| 226 | ||
| 227 | const char* optparser_get_arg() | |
| 228 | { | |
| 229 | const char *p; | |
| 230 | ||
| 231 | if(o_tmp!=NULL){ | |
| 232 | /* If o_args_left==0, then were returning an invalid option | |
| 233 | * otherwise an immediate argument (e.g. -funsigned-char | |
| 234 | * where '-f' is the option and 'unsigned-char' the argument) | |
| 235 | */ | |
| 236 | if(o_args_left>0) | |
| 237 | o_args_left--; | |
| 238 | p=o_tmp; | |
| 239 | o_tmp=NULL; | |
| 240 | return p; | |
| 241 | } | |
| 242 | ||
| 243 | if(o_args_left<1 || o_left<1) | |
| 244 | return NULL; | |
| 245 | ||
| 246 | o_left--; | |
| 247 | o_args_left--; | |
| 248 | return *o_current++; | |
| 249 | } | |
| 250 | ||
| 251 | ||
| 252 | /* */ | |
| 253 | ||
| 254 | static void warn_arg(const char *e) | |
| 255 | { | |
| 3 | 256 | const char *p=optparser_get_arg(); |
| 0 | 257 | |
| 258 | if(p==NULL) | |
| 259 | warn("%s (null)", e); | |
| 260 | else | |
| 261 | warn("%s \'%s\'", e, p); | |
| 262 | } | |
| 263 | ||
| 264 | ||
| 265 | static void warn_opt(const char *e) | |
| 266 | { | |
| 3 | 267 | if(o_tmp!=NULL && o_chain_ptr!=NULL) |
| 0 | 268 | warn("%s \'-%c\'", e, *o_tmp); |
| 269 | else | |
| 270 | warn_arg(e); | |
| 271 | } | |
| 272 | ||
| 273 | ||
| 274 | void optparser_print_error() | |
| 275 | { | |
| 276 | switch(o_error){ | |
| 277 | case E_OPT_INVALID_OPTION: | |
| 3 | 278 | case E_OPT_INVALID_CHAIN_OPTION: |
| 0 | 279 | warn_opt(TR("Invalid option")); |
| 280 | break; | |
| 281 | ||
| 282 | case E_OPT_SYNTAX_ERROR: | |
| 283 | warn_arg(TR("Syntax error while parsing")); | |
| 284 | break; | |
| 285 | ||
| 286 | case E_OPT_MISSING_ARGUMENT: | |
| 287 | warn_opt(TR("Missing argument to")); | |
| 288 | break; | |
| 289 | ||
| 290 | case E_OPT_UNEXPECTED_ARGUMENT: | |
| 3 | 291 | warn_opt(TR("No argument expected:")); |
| 0 | 292 | break; |
| 293 | ||
| 294 | case OPT_ID_ARGUMENT: | |
| 295 | warn(TR("Unexpected argument")); | |
| 296 | break; | |
| 297 | ||
| 298 | default: | |
| 299 | warn(TR("(unknown error)")); | |
| 300 | } | |
| 301 | ||
| 302 | o_tmp=NULL; | |
| 303 | o_error=0; | |
| 304 | } |