int Clp_Next(Clp_Parser *clp) /* Gets and parses the next argument from the argument list. If there are no more arguments, returns Clp_Done. If the next argument isn't an option, returns Clp_NotOption; the argument is stored in clp->arg. If the next argument is an option, returns that option's option_id. If the next argument is an unrecognizable or ambiguous option, an error message is given and Clp_BadOption is returned. If an option has an argument, that argument is stored in clp->arg and clp->have_arg is set to 1. Furthermore, that argument's parsed value (according to its type) is stored in the clp->val union. If an option needs an argument but isn't given one; if it doesn't need an argument but IS given one; or if the argument is the wrong type, an error message is given and Clp_BadOption is returned. */ { Clp_Internal *cli = clp->internal; Clp_Option *opt; Clp_ParserState clpsave; int complain; /** Set up clp **/ cli->current_option = 0; cli->ambiguous = 0; /** Get the next argument or option **/ if (!next_argument(clp, cli->option_processing ? 0 : 2)) return clp->have_arg ? Clp_NotOption : Clp_Done; clp->negated = cli->whole_negated; if (cli->is_short) opt = find_short(clp, cli->text[0]); else opt = find_long(clp, cli->text); /** If there's ambiguity between long & short options, and we couldn't find a long option, look for a short option **/ if (!opt && cli->could_be_short) { switch_to_short_argument(clp); opt = find_short(clp, cli->text[0]); } /** If we didn't find an option... **/ if (!opt || (clp->negated && !TEST(opt, Clp_Negate))) { /* default processing for the "--" option: turn off option processing and return the next argument */ if (strcmp(cli->argv[0], "--") == 0) { Clp_SetOptionProcessing(clp, 0); return Clp_Next(clp); } /* otherwise, report some error or other */ if (cli->ambiguous) ambiguity_error(clp, cli->ambiguous, cli->ambiguous_values, cli->opt, cli->option_chars, "option `%s%s' is ambiguous", cli->option_chars, cli->text); else if (cli->is_short && !cli->could_be_short) Clp_OptionError(clp, "unrecognized option `%s%c'", cli->option_chars, cli->text[0]); else Clp_OptionError(clp, "unrecognized option `%s%s'", cli->option_chars, cli->text); return Clp_BadOption; } /** Set the current option **/ cli->current_option = opt; cli->current_short = cli->is_short; cli->negated_by_no = clp->negated && !cli->whole_negated; /** The no-argument (or should-have-no-argument) case **/ if (clp->negated || !TEST(opt, Clp_AnyArgument)) { if (clp->have_arg) { Clp_OptionError(clp, "`%O' can't take an argument"); return Clp_BadOption; } else return opt->option_id; } /** Get an argument if we need one, or if it's optional **/ /* Sanity-check the argument type. */ if (opt->arg_type <= 0 || opt->arg_type >= cli->nargtype || cli->argtype[ opt->arg_type ].func == 0) return Clp_Error; /* complain == 1 only if the argument was explicitly given, or it is mandatory. */ complain = (clp->have_arg != 0) || TEST(opt, Clp_Mandatory); Clp_SaveParser(clp, &clpsave); if (TEST(opt, Clp_Mandatory) && !clp->have_arg) { /* Mandatory argument case */ /* Allow arguments to options to start with a dash, but only if the argument type allows it by not setting Clp_DisallowOptions */ int disallow = TEST(&cli->argtype[opt->arg_type], Clp_DisallowOptions); next_argument(clp, disallow ? 1 : 2); if (!clp->have_arg) { int got_option = cli->text != 0; Clp_RestoreParser(clp, &clpsave); if (got_option) Clp_OptionError(clp, "`%O' requires a non-option argument"); else Clp_OptionError(clp, "`%O' requires an argument"); return Clp_BadOption; } } else if (cli->is_short && !clp->have_arg && cli->text[1] != 0) /* The -[option]argument case: Assume that the rest of the current string is the argument. */ next_argument(clp, 1); /** Parse the argument **/ if (clp->have_arg) { Clp_ArgType *atr = &cli->argtype[ opt->arg_type ]; if (atr->func(clp, clp->arg, complain, atr->thunk) <= 0) { /* parser failed */ clp->have_arg = 0; if (TEST(opt, Clp_Mandatory)) return Clp_BadOption; else Clp_RestoreParser(clp, &clpsave); } } return opt->option_id; }
int CLIF_parse_cmdline (int argc, char *argv[], CLIF_option *option_list, CLIF_argument *argument_list, unsigned int parse_flags) { int i, j; CLIF_option *optn; CLIF_argument *argm; int num_args = 0; int num_argm = 0, strict_beg = 0, strict_end = 0; _CLIF_index arg_n[MAX_ARGC_NUMBER]; unsigned int dirty_flags = 0; int dirty_plus = 0; int exclusive_cnt = 0; int posix = getenv ("POSIXLY_CORRECT") != NULL || (parse_flags & CLIF_POSIX); curr.argc = argc; curr.argv = argv; curr.option_list = option_list; curr.argument_list = argument_list; curr.parse_flags = parse_flags; if (argc <= 1 && (parse_flags & CLIF_HELP_EMPTY)) { CLIF_current_help (); exit (0); } /* Scan argument_list for check and some info. */ if (argument_list) { enum stages { STRICT_BEG, OPTIONAL, STRICT_END }; int stage = STRICT_BEG; for (argm = argument_list; argm->name; argm++) { if (argm->flags & CLIF_STRICT) { if (stage == STRICT_BEG) strict_beg++; else if (stage == OPTIONAL) { stage = STRICT_END; strict_end++; } else if (stage == STRICT_END) strict_end++; } else { if (stage == STRICT_BEG) stage = OPTIONAL; else if (stage == STRICT_END) { err_report ("Incorrect argument list set in program " "source: more than one optional area."); return -1; } } num_argm++; } } /* Scan option_list for some info. */ if (option_list) { dirty_flags = parse_flags; for (optn = option_list; optn->short_opt || optn->long_opt; optn++ ) { dirty_flags |= optn->flags; if (optn->function_plus) dirty_plus = 1; } } if (dirty_flags & CLIF_EXCL) exclusive_cnt = 1; /* only one is allowed... */ /* Go ! Store arguments, parse options. */ for (i = 1; i < argc; i++) { char *arg = argv[i]; char *opt_arg = NULL; char sym = '-'; if (!option_list) goto handle_arg; if (*arg == '+' && dirty_plus) sym = '+'; if (*arg != sym) { /* argument or keyword */ if (dirty_flags & CLIF_MAY_KEYWORD) { optn = find_long (arg, &opt_arg, CLIF_MAY_KEYWORD, 0); if (optn) goto long_found; } if (num_args == 0 && (parse_flags & CLIF_FIRST_GROUP)) { /* ugly... */ parse_flags &= ~CLIF_FIRST_GROUP; dirty_flags &= ~CLIF_FIRST_GROUP; /* to be correct */ goto handle_short; } /* else it is an argument */ goto handle_arg; } else if (*++arg == sym) { /* `--' - long option */ arg++; if (*arg == sym || /* `---' - let it be not option... */ (parse_flags & (_CLIF_STRICT_KEYWORD|_CLIF_STRICT_ONEDASH)) ) { arg -= 2; goto handle_arg; /* not option anyway */ } optn = find_long (arg, &opt_arg, 0, _CLIF_STRICT_KEYWORD | _CLIF_STRICT_ONEDASH); if (optn) goto long_found; /* XXX: May be allow only for `--', not `++' too... */ if (!*arg && sym == '-') { /* `--' and no empty longoption */ option_list = NULL; /* POSIX way... */ continue; } /* XXX: or treat as an argument sometimes??? */ err_bad_opt (argv[i], 0, i); return -1; } else { /* short option, or several short options... */ if (dirty_flags & CLIF_MAY_ONEDASH) { optn = find_long (arg, &opt_arg, CLIF_MAY_ONEDASH, 0); if (optn) goto long_found; } if (!*arg) { /* POSIX say: only "stdout specification"... */ arg--; goto handle_arg; } goto handle_short; } long_found: if (check_sym (optn, sym) < 0) { /* Oops... */ err_bad_opt (argv[i], 0, i); return -1; } if (optn->flags & CLIF_EXCL) { if (!exclusive_cnt) { err_bad_excl (optn, 0, i); return -1; } exclusive_cnt--; } if (optn->arg_name && !opt_arg) { unsigned int flags = optn->flags | parse_flags; if (++i >= argc || !(flags & CLIF_MAY_NOEQUAL) ) { /* missing opt arg */ i--; if (!(flags & CLIF_OPTARG)) { err_bad_arg (optn, 0, i); return -1; } opt_arg = NULL; } else opt_arg = argv[i]; } if (call_function (optn, opt_arg, sym) < 0) { err_bad_res (optn, 0, opt_arg, i); return -1; } if (optn->flags & CLIF_EXIT) exit (0); continue; handle_arg: if (argument_list) { if (i < MAX_ARGC_NUMBER) /* XXX: ugly, better report */ arg_n[num_args++] = i; } else { err_report ("`%s' (argc %d): arguments are not allowed", argv[i], i); return -1; } /* POSIX say: No more options after args... */ if (posix) option_list = NULL; /* geniously... */ continue; handle_short: opt_arg = NULL; do { for (optn = option_list; optn->short_opt || optn->long_opt; optn++ ) { if (optn->short_opt && optn->short_opt[0] == *arg) break; } if (!optn->short_opt || check_sym (optn, sym) < 0 ) { err_bad_opt (argv[i], *arg, i); return -1; } if (optn->flags & CLIF_EXCL) { if (!exclusive_cnt) { err_bad_excl (optn, *arg, i); return -1; } exclusive_cnt--; } if (optn->arg_name) { unsigned int flags = parse_flags | optn->flags; if (arg[1] == '\0') { /* a last one */ /* POSIX say: an option with arg cannot be grouped. */ if (posix && arg != argv[i] && arg[-1] != sym) { err_bad_arg (optn, *arg, i); /* good way? */ return -1; } if (++i >= argc || (flags & _CLIF_STRICT_JOIN_ARG) ) { i--; if (!(flags & CLIF_OPTARG)) { err_bad_arg (optn, *arg, i); return -1; } opt_arg = NULL; } else opt_arg = argv[i]; } else if ((arg == argv[i] || arg[-1] == sym) && (flags & CLIF_MAY_JOIN_ARG) ) { opt_arg = ++arg; } else { /* inside a group... */ if (!(flags & CLIF_OPTARG) || (flags & CLIF_MAY_JOIN_ARG) ) { err_bad_arg (optn, *arg, i); return -1; } opt_arg = NULL; } } if (call_function (optn, opt_arg, sym) < 0) { err_bad_res (optn, optn->short_opt[0], opt_arg, i); return -1; } if (optn->flags & CLIF_EXIT) exit (0); } while (!opt_arg && *++arg); } /* for ( ... ) */ if ((parse_flags & CLIF_STRICT_EXCL) && exclusive_cnt != 0) { err_report ("One of these must be specified:\n %s\n", show_excl (option_list, 0)); return -1; } /* Now, after *ALL* options, handle arguments, if any. */ if (num_args < strict_beg + strict_end) { /* Missing some needed arguments. */ if (num_args < strict_beg) argm = argument_list + num_args; else argm = argument_list + ((num_args - strict_beg) + (num_argm - strict_end)); if (num_args == strict_beg + strict_end - 1) err_report ("Specify \"%s\" missing argument.", argm->name); else err_report ("Specify \"%s\" and other missing arguments.", argm->name); return -1; } if (num_args > 0) { _CLIF_index argm_index[MAX_ARGC_NUMBER]; /* assing argm (by index) for each arg... */ for (i = 0, j = 0; i < strict_beg; i++, j++) argm_index[i] = j; for (i = num_args - strict_end, j = num_argm - strict_end; i < num_args; i++, j++ ) argm_index[i] = j; for (i = strict_beg, j = strict_beg; i < num_args - strict_end && j < num_argm - strict_end; i++ ) { argm_index[i] = j; if (!(argument_list[j].flags & CLIF_MORE)) j++; } if (i < num_args - strict_end) { /* there are extra args... */ err_report ("Extra arg `%s' (position %d, argc %d)", argv[arg_n[i]], i + 1, arg_n[i]); return -1; } if (j < num_argm - strict_end && !(argument_list[j].flags & CLIF_MORE) && /* ...i.e, there are some missing optional args... */ (argument_list[j].flags & CLIF_ACC_PREV) ) { if (j == 0) err_report ("Incorrect argument list set: first arg " "cannot be `accompanied with previous'."); else err_report ("Arg \"%s\" must be specified because " "\"%s\" `%s' is used.", argument_list[j].name, argument_list[j - 1].name, argv[arg_n[i - 1]]); return -1; } if (argm_index[--i] == j && /* above is true only after OPTIONAL area scan and when `j' is stopped on CLIF_MORE */ ++j < num_argm - strict_end /* i.e: there is a *last* one (after CLIF_MORE) in the OPTIONAL area */ ) argm_index[i] = j; /* *last* is better than *more* */ /* ...and work now */ for (i = 0; i < num_args; i++) { argm = argument_list + argm_index[i]; if (argm->function && argm->function (argm, argv[arg_n[i]], i) < 0 ) { err_report ("Cannot handle \"%s\" cmdline arg `%s' " "on position %d (argc %d)", argm->name, argv[arg_n[i]], i + 1, arg_n[i]); return -1; } } /* That`s all. */ } return 0; }
UpoptStatus upopt_next(UpoptContext* context, int* constant, const char** value, char** error) { const char* arg; const char* val = NULL; const UpoptOptionInfo* info = NULL; if (context->index >= context->argc) return UPOPT_STATUS_DONE; arg = context->argv[context->index++]; if (!context->only_normal) { if (is_long(arg)) { if (!arg[2]) { context->only_normal = 1; return upopt_next(context, constant, value, error); } else { char* equal = strchr(arg, '='); if (equal) { *equal = '\0'; val = equal + 1; } info = find_long(context->options, arg+2); if (equal) { *equal = '='; } if (!info) { *error = format("Unrecognized option: %s\n", arg); return UPOPT_STATUS_ERROR; } } } else if (is_short(arg)) { if (arg[2]) { val = arg+2; } info = find_short(context->options, arg[1]); if (!info) { *error = format("Unrecognized option: %s\n", arg); return UPOPT_STATUS_ERROR; } } } if (info) { if (val && !info->argument) { if (is_long(arg)) { *error = format("Did not expect an argument after --%s\n", info->longname); } else { *error = format("Did not expect an argument after -%c\n", info->shortname); } return UPOPT_STATUS_ERROR; } else if (!val && info->argument) { if ((context->index >= context->argc || (!context->only_normal && is_option(context->argv[context->index])))) { *error = format("Expected argument after %s\n", arg); return UPOPT_STATUS_ERROR; } val = context->argv[context->index++]; } *constant = info->constant; *value = val; *error = NULL; return UPOPT_STATUS_NORMAL; } else { *constant = UPOPT_ARG_NORMAL; *value = arg; *error = NULL; return UPOPT_STATUS_NORMAL; } }