📄 argp-parse.c
字号:
/* Hierarchial argument parsing, layered over getopt Copyright (C) 1995-2000, 2002, 2003, 2004 Free Software Foundation, Inc. This file is part of the GNU C Library. Written by Miles Bader <miles@gnu.ai.mit.edu>. This program 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 3 of the License, or (at your option) any later version. This program 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, see <http://www.gnu.org/licenses/>. */#ifdef HAVE_CONFIG_H# include <config.h>#endif#include <alloca.h>#include <stddef.h>#include <stdlib.h>#include <string.h>#include <unistd.h>#include <limits.h>#include <getopt.h>#include <getopt_int.h>#ifdef _LIBC# include <libintl.h># undef dgettext# define dgettext(domain, msgid) \ INTUSE(__dcgettext) (domain, msgid, LC_MESSAGES)#else# include "gettext.h"#endif#define N_(msgid) msgid#include "argp.h"#include "argp-namefrob.h"#define alignof(type) offsetof (struct { char c; type x; }, x)#define alignto(n, d) ((((n) + (d) - 1) / (d)) * (d))/* Getopt return values. */#define KEY_END (-1) /* The end of the options. */#define KEY_ARG 1 /* A non-option argument. */#define KEY_ERR '?' /* An error parsing the options. *//* The meta-argument used to prevent any further arguments being interpreted as options. */#define QUOTE "--"/* The number of bits we steal in a long-option value for our own use. */#define GROUP_BITS CHAR_BIT/* The number of bits available for the user value. */#define USER_BITS ((sizeof ((struct option *)0)->val * CHAR_BIT) - GROUP_BITS)#define USER_MASK ((1 << USER_BITS) - 1)/* EZ alias for ARGP_ERR_UNKNOWN. */#define EBADKEY ARGP_ERR_UNKNOWN/* Default options. *//* When argp is given the --HANG switch, _ARGP_HANG is set and argp will sleep for one second intervals, decrementing _ARGP_HANG until it's zero. Thus you can force the program to continue by attaching a debugger and setting it to 0 yourself. */static volatile int _argp_hang;#define OPT_PROGNAME -2#define OPT_USAGE -3#define OPT_HANG -4static const struct argp_option argp_default_options[] ={ {"help", '?', 0, 0, N_("give this help list"), -1}, {"usage", OPT_USAGE, 0, 0, N_("give a short usage message"), 0}, {"program-name",OPT_PROGNAME,N_("NAME"), OPTION_HIDDEN, N_("set the program name"), 0}, {"HANG", OPT_HANG, N_("SECS"), OPTION_ARG_OPTIONAL | OPTION_HIDDEN, N_("hang for SECS seconds (default 3600)"), 0}, {NULL, 0, 0, 0, NULL, 0}};static error_targp_default_parser (int key, char *arg, struct argp_state *state){ switch (key) { case '?': __argp_state_help (state, state->out_stream, ARGP_HELP_STD_HELP); break; case OPT_USAGE: __argp_state_help (state, state->out_stream, ARGP_HELP_USAGE | ARGP_HELP_EXIT_OK); break; case OPT_PROGNAME: /* Set the program name. */#if defined _LIBC || HAVE_DECL_PROGRAM_INVOCATION_NAME program_invocation_name = arg;#endif /* [Note that some systems only have PROGRAM_INVOCATION_SHORT_NAME (aka __PROGNAME), in which case, PROGRAM_INVOCATION_NAME is just defined to be that, so we have to be a bit careful here.] */ /* Update what we use for messages. */ state->name = __argp_base_name (arg);#if defined _LIBC || HAVE_DECL_PROGRAM_INVOCATION_SHORT_NAME program_invocation_short_name = state->name;#endif if ((state->flags & (ARGP_PARSE_ARGV0 | ARGP_NO_ERRS)) == ARGP_PARSE_ARGV0) /* Update what getopt uses too. */ state->argv[0] = arg; break; case OPT_HANG: _argp_hang = atoi (arg ? arg : "3600"); while (_argp_hang-- > 0) __sleep (1); break; default: return EBADKEY; } return 0;}static const struct argp argp_default_argp = {argp_default_options, &argp_default_parser, NULL, NULL, NULL, NULL, "libc"};static const struct argp_option argp_version_options[] ={ {"version", 'V', 0, 0, N_("print program version"), -1}, {NULL, 0, 0, 0, NULL, 0}};static error_targp_version_parser (int key, char *arg, struct argp_state *state){ switch (key) { case 'V': if (argp_program_version_hook) (*argp_program_version_hook) (state->out_stream, state); else if (argp_program_version) fprintf (state->out_stream, "%s\n", argp_program_version); else __argp_error (state, dgettext (state->root_argp->argp_domain, "(PROGRAM ERROR) No version known!?")); if (! (state->flags & ARGP_NO_EXIT)) exit (0); break; default: return EBADKEY; } return 0;}static const struct argp argp_version_argp = {argp_version_options, &argp_version_parser, NULL, NULL, NULL, NULL, "libc"};/* Returns the offset into the getopt long options array LONG_OPTIONS of a long option with called NAME, or -1 if none is found. Passing NULL as NAME will return the number of options. */static intfind_long_option (struct option *long_options, const char *name){ struct option *l = long_options; while (l->name != NULL) if (name != NULL && strcmp (l->name, name) == 0) return l - long_options; else l++; if (name == NULL) return l - long_options; else return -1;}/* The state of a `group' during parsing. Each group corresponds to a particular argp structure from the tree of such descending from the top level argp passed to argp_parse. */struct group{ /* This group's parsing function. */ argp_parser_t parser; /* Which argp this group is from. */ const struct argp *argp; /* Points to the point in SHORT_OPTS corresponding to the end of the short options for this group. We use it to determine from which group a particular short options is from. */ char *short_end; /* The number of non-option args sucessfully handled by this parser. */ unsigned args_processed; /* This group's parser's parent's group. */ struct group *parent; unsigned parent_index; /* And the our position in the parent. */ /* These fields are swapped into and out of the state structure when calling this group's parser. */ void *input, **child_inputs; void *hook;};/* Call GROUP's parser with KEY and ARG, swapping any group-specific info from STATE before calling, and back into state afterwards. If GROUP has no parser, EBADKEY is returned. */static error_tgroup_parse (struct group *group, struct argp_state *state, int key, char *arg){ if (group->parser) { error_t err; state->hook = group->hook; state->input = group->input; state->child_inputs = group->child_inputs; state->arg_num = group->args_processed; err = (*group->parser)(key, arg, state); group->hook = state->hook; return err; } else return EBADKEY;}struct parser{ const struct argp *argp; /* SHORT_OPTS is the getopt short options string for the union of all the groups of options. */ char *short_opts; /* LONG_OPTS is the array of getop long option structures for the union of all the groups of options. */ struct option *long_opts; /* OPT_DATA is the getopt data used for the re-entrant getopt. */ struct _getopt_data opt_data; /* States of the various parsing groups. */ struct group *groups; /* The end of the GROUPS array. */ struct group *egroup; /* An vector containing storage for the CHILD_INPUTS field in all groups. */ void **child_inputs; /* True if we think using getopt is still useful; if false, then remaining arguments are just passed verbatim with ARGP_KEY_ARG. This is cleared whenever getopt returns KEY_END, but may be set again if the user moves the next argument pointer backwards. */ int try_getopt; /* State block supplied to parsing routines. */ struct argp_state state; /* Memory used by this parser. */ void *storage;};/* The next usable entries in the various parser tables being filled in by convert_options. */struct parser_convert_state{ struct parser *parser; char *short_end; struct option *long_end; void **child_inputs_end;};/* Converts all options in ARGP (which is put in GROUP) and ancestors into getopt options stored in SHORT_OPTS and LONG_OPTS; SHORT_END and CVT->LONG_END are the points at which new options are added. Returns the next unused group entry. CVT holds state used during the conversion. */static struct group *convert_options (const struct argp *argp, struct group *parent, unsigned parent_index, struct group *group, struct parser_convert_state *cvt){ /* REAL is the most recent non-alias value of OPT. */ const struct argp_option *real = argp->options; const struct argp_child *children = argp->children; if (real || argp->parser) { const struct argp_option *opt; if (real) for (opt = real; !__option_is_end (opt); opt++) { if (! (opt->flags & OPTION_ALIAS)) /* OPT isn't an alias, so we can use values from it. */ real = opt; if (! (real->flags & OPTION_DOC)) /* A real option (not just documentation). */ { if (__option_is_short (opt)) /* OPT can be used as a short option. */ { *cvt->short_end++ = opt->key; if (real->arg) { *cvt->short_end++ = ':'; if (real->flags & OPTION_ARG_OPTIONAL) *cvt->short_end++ = ':'; } *cvt->short_end = '\0'; /* keep 0 terminated */ } if (opt->name && find_long_option (cvt->parser->long_opts, opt->name) < 0) /* OPT can be used as a long option. */ { cvt->long_end->name = opt->name; cvt->long_end->has_arg = (real->arg ? (real->flags & OPTION_ARG_OPTIONAL ? optional_argument : required_argument) : no_argument); cvt->long_end->flag = 0; /* we add a disambiguating code to all the user's values (which is removed before we actually call the function to parse the value); this means that the user loses use of the high 8 bits in all his values (the sign of the lower bits is preserved however)... */ cvt->long_end->val = ((opt->key | real->key) & USER_MASK) + (((group - cvt->parser->groups) + 1) << USER_BITS); /* Keep the LONG_OPTS list terminated. */ (++cvt->long_end)->name = NULL; } } } group->parser = argp->parser; group->argp = argp; group->short_end = cvt->short_end; group->args_processed = 0; group->parent = parent; group->parent_index = parent_index; group->input = 0; group->hook = 0; group->child_inputs = 0; if (children) /* Assign GROUP's CHILD_INPUTS field some space from CVT->child_inputs_end.*/ { unsigned num_children = 0; while (children[num_children].argp) num_children++; group->child_inputs = cvt->child_inputs_end; cvt->child_inputs_end += num_children; } parent = group++; } else parent = 0; if (children) { unsigned index = 0; while (children->argp) group = convert_options (children++->argp, parent, index++, group, cvt); } return group;}/* Find the merged set of getopt options, with keys appropiately prefixed. */static voidparser_convert (struct parser *parser, const struct argp *argp, int flags){ struct parser_convert_state cvt; cvt.parser = parser; cvt.short_end = parser->short_opts; cvt.long_end = parser->long_opts; cvt.child_inputs_end = parser->child_inputs; if (flags & ARGP_IN_ORDER) *cvt.short_end++ = '-'; else if (flags & ARGP_NO_ARGS) *cvt.short_end++ = '+'; *cvt.short_end = '\0'; cvt.long_end->name = NULL; parser->argp = argp; if (argp) parser->egroup = convert_options (argp, 0, 0, parser->groups, &cvt); else parser->egroup = parser->groups; /* No parsers at all! */}/* Lengths of various parser fields which we will allocated. */struct parser_sizes{ size_t short_len; /* Getopt short options string. */ size_t long_len; /* Getopt long options vector. */ size_t num_groups; /* Group structures we allocate. */ size_t num_child_inputs; /* Child input slots. */};/* For ARGP, increments the NUM_GROUPS field in SZS by the total number of argp structures descended from it, and the SHORT_LEN & LONG_LEN fields by the maximum lengths of the resulting merged getopt short options string and long-options array, respectively. */static voidcalc_sizes (const struct argp *argp, struct parser_sizes *szs){ const struct argp_child *child = argp->children; const struct argp_option *opt = argp->options; if (opt || argp->parser) { szs->num_groups++; if (opt) { int num_opts = 0; while (!__option_is_end (opt++)) num_opts++; szs->short_len += num_opts * 3; /* opt + up to 2 `:'s */ szs->long_len += num_opts; } } if (child) while (child->argp) { calc_sizes ((child++)->argp, szs); szs->num_child_inputs++; }}/* Initializes PARSER to parse ARGP in a manner described by FLAGS. */static error_tparser_init (struct parser *parser, const struct argp *argp, int argc, char **argv, int flags, void *input){ error_t err = 0; struct group *group; struct parser_sizes szs; struct _getopt_data opt_data = _GETOPT_DATA_INITIALIZER; char *storage; size_t glen, gsum; size_t clen, csum; size_t llen, lsum; size_t slen, ssum; szs.short_len = (flags & ARGP_NO_ARGS) ? 0 : 1; szs.long_len = 0; szs.num_groups = 0; szs.num_child_inputs = 0; if (argp) calc_sizes (argp, &szs);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -