/* Quagga command line parsing -- header * Copyright (C) 1997, 98 Kunihiro Ishiguro * * Recast and extended: Copyright (C) 2010 Chris Hall (GMCH), Highwayman * * This file is part of GNU Zebra. * * GNU Zebra 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 2, or (at your * option) any later version. * * GNU Zebra 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 GNU Zebra; see the file COPYING. If not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */ #ifndef _ZEBRA_COMMAND_PARSE_H #define _ZEBRA_COMMAND_PARSE_H #include "misc.h" #include "command_common.h" #include "vector.h" #include "vio_fifo.h" #include "qstring.h" #include "elstring.h" #include "qpath.h" #include "elstring.h" #include "memory.h" #if 0 /*============================================================================== * Parsing of tokens */ enum cmd_token_spec { cmd_ts_simple = 0, cmd_ts_keyword, cmd_ts_number, cmd_ts_word, cmd_ts_option, cmd_ts_ipv4_addr, cmd_ts_ipv4_prefix, cmd_ts_ipv6_addr, cmd_ts_ipv6_prefix, cmd_ts_vararg, } ; typedef enum cmd_token_spec cmd_token_spec_t ; #endif /*============================================================================== * Sexing of token types in command descriptions */ #define CMD_OPTION(S) ((S[0]) == '[') #define CMD_VARIABLE(S) (((S[0]) >= 'A' && (S[0]) <= 'Z') || ((S[0]) == '<')) #define CMD_VARARG(S) ((S[0]) == '.') #define CMD_RANGE(S) ((S[0] == '<')) #define CMD_IPV4(S) ((strcmp ((S), "A.B.C.D") == 0)) #define CMD_IPV4_PREFIX(S) ((strcmp ((S), "A.B.C.D/M") == 0)) #define CMD_IPV6(S) ((strcmp ((S), "X:X::X:X") == 0)) #define CMD_IPV6_PREFIX(S) ((strcmp ((S), "X:X::X:X/M") == 0)) /*============================================================================== * Command Items. * * A command is compiled into a vector of lists of command items -- the * cmd_command->items (so called). * */ /* Command item types * * NB: the command items sort according to this -- highest first. * * NB: this is in the same order as the match_type -- and should remain so. * See match_type below for further discussion of the hierarchy. */ enum cmd_item_type { item_null, item_eol, item_option_word, item_vararg, /* rest of the line */ item_word, item_ipv6_prefix, item_ipv6_address, item_ipv4_prefix, item_ipv4_address, item_range, item_keyword, item_type_count, /* number of types */ } ; typedef enum cmd_item_type cmd_item_type_t ; enum { item_max_number = 0xFFFFFFFF /* can be +/- this */ } ; CONFIRM(LONG_MAX >= item_max_number) ; enum cmd_item_type_bit { item_null_bit = BIT(item_null), item_eol_bit = BIT(item_eol), item_option_word_bit = BIT(item_option_word), item_vararg_bit = BIT(item_vararg), item_word_bit = BIT(item_word), item_ipv6_prefix_bit = BIT(item_ipv6_prefix), item_ipv6_address_bit = BIT(item_ipv6_address), item_ipv4_prefix_bit = BIT(item_ipv4_prefix), item_ipv4_address_bit = BIT(item_ipv4_address), item_range_bit = BIT(item_range), item_keyword_bit = BIT(item_keyword), } ; typedef enum cmd_item_type_bit cmd_item_type_bit_t ; /*------------------------------------------------------------------------------ * Sex cmd_item_type and return whether it is an "option" type, or not. * * NB: tolerates item_null -- others may well not. */ Inline bool cmd_item_is_option(cmd_item_type_t itt) { static const bool is_option[item_type_count] = { [item_null] = false, [item_eol] = false, [item_option_word] = true, [item_vararg] = false, [item_word] = false, [item_ipv6_prefix] = false, [item_ipv6_address] = false, [item_ipv4_prefix] = false, [item_ipv4_address] = false, [item_range] = false, [item_keyword] = false, } ; assert((itt >= 0) && (itt < item_type_count)) ; return is_option[itt] ; } ; /*------------------------------------------------------------------------------ * Sex cmd_item_type and return whether it is an "vararg" type, or not. * * NB: tolerates item_null -- others may well not. */ Inline bool cmd_item_is_vararg(cmd_item_type_t itt) { static const bool is_vararg[item_type_count] = { [item_null] = false, [item_eol] = false, [item_option_word] = false, [item_vararg] = true, [item_word] = false, [item_ipv6_prefix] = false, [item_ipv6_address] = false, [item_ipv4_prefix] = false, [item_ipv4_address] = false, [item_range] = false, [item_keyword] = false, } ; assert((itt >= 0) && (itt < item_type_count)) ; return is_vararg[itt] ; } ; /*------------------------------------------------------------------------------ * The command item structure. */ struct cmd_item { elstring_t str ; /* in some word_lump */ const char* doc ; /* in r_doc -- description text */ cmd_item next ; /* Next possibility (if any) */ cmd_item_type_t type ; bool arg ; /* include in argv */ /* For item_range values */ bool range_sign_allowed ; bool range_sign_required ; long range_min ; long range_max ; } ; /*============================================================================== * Match strengths types and filter settings. * * When matching a token, may have a number of competing items, possibly of * different types. * * For execution a token must match completely -- or, for keyword items, * match partially at most one possible keyword. */ /* Match strength * * When matching a token against an item, the following are the possible * results. */ enum match_strength { ms_no_match = 0, /* match failed: token is definitely NOT * the item in question. */ ms_min_parse = ms_no_match + 1, /* for parsing must match somehow ! */ ms_partial, /* match OK up to the end of the token, but * more is required to complete it. * This is used by the variable matches. */ ms_min_execute = ms_partial + 1, /* for execution must be at least this */ ms_anything, /* matches because will match anything. * This is used for WORD and such like items. */ ms_kwd_incomplete, /* keyword match OK, but not complete. */ ms_var_complete, /* match succeeded: token is a complete and * valid instance of the item in question */ ms_kwd_complete /* match succeeded: token is a complete and * valid instance of the item in question */ } ; typedef enum match_strength match_strength_t ; /* The match type indicates what has been matched and the strength of the * match. * * NB: the order of these is significant, higher numbered match types are * preferred over lower numbered ones. * * NB: this is in the same order as the cmd_item_type -- and should remain so. * * During the command filtering, when a token and an item match, and the * match type is better than the match type to date, then all previous matches * are discarded. * * The hierarchy means that, for example, '22' will match an IPv4 prefix * partially, but that will be forgotten if it later matches a WORD, and that * will be forgotten if it later matches a number range. * * The IPv6 items have a lower priority so that a simple decimal will prefer * the simpler IPv4 address form. As soon as there is an 'a'..'f' or a ':', * the IPv4 will no longer match. * * The partial keyword match is preferred over a partial anything else, but * cannot mask a complete value ! * * The relative ranking of: mt_option_word_match, mt_vararg_match, and * mt_word_match, is more or less arbitrary -- if these are ever candidates * at the same time, the commands are ambiguous. * */ enum match_type { mt_no_match, mt_eol_partial, /* a partial match ! */ mt_ipv6_address_partial, mt_ipv6_prefix_partial, mt_ipv4_address_partial, mt_ipv4_prefix_partial, mt_range_partial, mt_eol, /* an ms_anything match */ mt_option_word_match, /* anything can match a [WORD] */ mt_vararg_match, /* anything can match a .vararg */ mt_word_match, /* anything can match a WORD */ mt_keyword_incomplete, mt_ipv6_prefix_complete, mt_ipv6_address_complete, mt_ipv4_prefix_complete, mt_ipv4_address_complete, mt_range_complete, mt_keyword_complete, match_type_count /* Number of match types */ } ; typedef enum match_type match_type_t ; /*------------------------------------------------------------------------------ * Map match_type -> cmd_item_type * * From a match type extract the type of item which has been match, partially * or completely. */ Inline cmd_item_type_t match_item_type(match_type_t mt) { static cmd_item_type_t match_item_type[match_type_count] = { [mt_no_match] = item_null, [mt_eol_partial] = item_eol, [mt_ipv4_prefix_partial] = item_ipv4_prefix, [mt_ipv4_address_partial] = item_ipv4_address, [mt_ipv6_prefix_partial] = item_ipv6_prefix, [mt_ipv6_address_partial] = item_ipv6_address, [mt_range_partial] = item_range, [mt_eol] = item_eol, [mt_option_word_match] = item_option_word, [mt_vararg_match] = item_vararg, [mt_word_match] = item_word, [mt_keyword_incomplete] = item_keyword, [mt_ipv6_prefix_complete] = item_ipv6_prefix, [mt_ipv6_address_complete] = item_ipv6_address, [mt_ipv4_prefix_complete] = item_ipv4_prefix, [mt_ipv4_address_complete] = item_ipv4_address, [mt_range_complete] = item_range, [mt_keyword_complete] = item_keyword, } ; assert((mt >= 0) && (mt < match_type_count)) ; return match_item_type[mt] ; } ; /*------------------------------------------------------------------------------ * Map match_type -> match_strength. * * From a match type extract the strength of the match. */ Inline match_strength_t match_match_strength(match_type_t mt) { static const match_strength_t match_match_strength[match_type_count] = { [mt_no_match] = ms_no_match, [mt_eol_partial] = ms_partial, [mt_ipv6_prefix_partial] = ms_partial, [mt_ipv6_address_partial] = ms_partial, [mt_ipv4_prefix_partial] = ms_partial, [mt_ipv4_address_partial] = ms_partial, [mt_range_partial] = ms_partial, [mt_eol] = ms_anything, [mt_option_word_match] = ms_anything, [mt_vararg_match] = ms_anything, [mt_word_match] = ms_anything, [mt_keyword_incomplete] = ms_kwd_incomplete, [mt_ipv6_prefix_complete] = ms_var_complete, [mt_ipv6_address_complete] = ms_var_complete, [mt_ipv4_prefix_complete] = ms_var_complete, [mt_ipv4_address_complete] = ms_var_complete, [mt_range_complete] = ms_var_complete, [mt_keyword_complete] = ms_kwd_complete, } ; assert((mt >= 0) && (mt < match_type_count)) ; return match_match_strength[mt] ; } ; /*------------------------------------------------------------------------------ * Map cmd_item_type -> best possible match type. * * This gives the most optimistic outcome of an attempt to match to the given * type of item. * * NB: tolerates item_null -- others may not */ Inline match_type_t item_best_match(cmd_item_type_t it) { static const match_type_t item_best_match[item_type_count] = { [item_null] = mt_no_match, [item_eol] = mt_eol, [item_option_word] = mt_option_word_match, [item_vararg] = mt_vararg_match, [item_word] = mt_word_match, [item_ipv6_prefix] = mt_ipv6_prefix_complete, [item_ipv6_address] = mt_ipv6_address_complete, [item_ipv4_prefix] = mt_ipv4_prefix_complete, [item_ipv4_address] = mt_ipv4_address_complete, [item_range] = mt_range_complete, [item_keyword] = mt_keyword_complete, } ; assert((it >= 0) && (it < item_type_count)) ; return item_best_match[it] ; } ; /*============================================================================== * */ /* Pipe types */ enum cmd_pipe_type /* bit significant */ { cmd_pipe_none = 0, cmd_pipe_file = BIT(0), cmd_pipe_shell = BIT(1), cmd_pipe_dev_null = BIT(2), /* out pipe only -- black hole */ /* For in file and shell pipes */ cmd_pipe_strict = BIT( 3), /* << type */ cmd_pipe_reflect_enable = BIT( 4), /* + option */ cmd_pipe_reflect_disable = BIT( 5), /* - option */ cmd_pipe_warn_continue = BIT( 6), /* % option */ cmd_pipe_warn_stop = BIT( 7), /* ! option */ cmd_pipe_out_enable = BIT( 8), /* = option */ cmd_pipe_out_disable = BIT( 9), /* # option */ cmd_pipe_quiet = BIT(10), /* * option */ /* For out file pipes */ cmd_pipe_append = BIT( 4), /* >> */ /* For out shell pipes */ cmd_pipe_shell_cmd = BIT( 4), /* | at start of line */ cmd_pipe_stderr_enable = BIT( 6), /* & option */ cmd_pipe_stderr_disable = BIT( 7), /* % option */ cmd_pipe_stdout_enable = BIT( 8), /* = option */ cmd_pipe_stdout_disable = BIT( 9), /* # option */ cmd_pipe_std_quiet = BIT(10), /* * option */ } ; CONFIRM(cmd_pipe_std_quiet == cmd_pipe_quiet) ; typedef enum cmd_pipe_type cmd_pipe_type_t ; /* Parsed parts */ enum cmd_parts { cmd_parts_none = 0, /* bit significant */ cmd_part_command = BIT(0), /* command part exists */ cmd_part_do = BIT(1), /* command has leading "do" */ cmd_part_meta = BIT(2), /* command has meta prefix */ cmd_part_in_pipe = BIT(4), /* in pipe part exists */ cmd_part_out_pipe = BIT(5), /* out pipe part exists */ cmd_parts_pipe = cmd_part_in_pipe | cmd_part_out_pipe, cmd_parts_execute = cmd_part_command | cmd_parts_pipe, cmd_part_comment = BIT(7), /* comment part exists */ } ; typedef enum cmd_parts cmd_parts_t ; /*------------------------------------------------------------------------------ * Token object -- a qstring and some other properties. */ enum cmd_token_type /* *bit* significant */ { cmd_tok_eol = 0, /* all lines have one */ /* Simple token may contain quotes and escapes. */ cmd_tok_simple = BIT( 0), cmd_tok_sq = BIT( 4), cmd_tok_dq = BIT( 5), /* '\\' within "..." are not registered separately. */ cmd_tok_esc = BIT( 6), cmd_tok_incomplete = (cmd_tok_sq | cmd_tok_dq | cmd_tok_esc), /* These tokens will not contain quotes or escapes. */ cmd_tok_in_pipe = BIT( 8), /* token starting '<' */ cmd_tok_out_pipe = BIT( 9), /* token starting '>' */ cmd_tok_out_shell = BIT(10), /* token starting '|' */ cmd_tok_meta_prefix = BIT(13), /* prefix of a meta command */ cmd_tok_comment = BIT(14), /* token starting '!' or '#" */ } ; typedef enum cmd_token_type cmd_token_type_t ; struct cmd_token { cmd_token_type_t type ; usize tp ; /* location of token in the line */ bool term ; /* token has been '\0' terminated */ qstring_t qs ; /* token string */ elstring_t ot ; /* original token in the line */ const void* word ; /* set when a word match is done */ ulen w_len ; /* length of word match */ int cw ; /* result of the word match */ cmd_item_type_bit_t seen ; /* bit set when match done */ match_type_t match[item_type_count] ; /* result of match seen */ } ; typedef struct cmd_token* cmd_token ; /*------------------------------------------------------------------------------ * Token vector -- a vector of token objects */ struct token_vector { vector_t body ; } ; typedef struct token_vector token_vector_t[1] ; typedef struct token_vector* token_vector ; enum { TOKEN_VECTOR_INIT_ALL_ZEROS = VECTOR_INIT_ALL_ZEROS } ; /*------------------------------------------------------------------------------ * Argument vector -- a vector of const char* */ struct arg_vector { vector_t body ; } ; typedef struct arg_vector arg_vector_t[1] ; typedef struct arg_vector* arg_vector ; enum { ARG_VECTOR_INIT_ALL_ZEROS = VECTOR_INIT_ALL_ZEROS } ; /*------------------------------------------------------------------------------ * Parsed command line * * The current command line is only valid while command is being parsed and * executed -- in between it is nonsense. The pointer can be overwritten at * any time -- responsibility for the memory lies elsewhere. * * The args vector is a set of pointers to the strings in the relevant tokens. * This vector is only valid while command is being parsed and executed -- in * between it too is nonsense. The pointers can be overwritten or discarded at * any time -- responsibility for the memory lies elsewhere. * * The token vectors contain the tokens for the current command being parsed * and executed. After the command has completed these vectors can be * emptied -- see cmd_empty_parsed_tokens() -- but the next command to be * parsed will tidy up before proceeding. */ struct cmd_parsed { /* The following are filled in when a command line is tokenised */ cmd_token_type_t tok_total ; /* What token types are present */ uint num_tokens ; /* number of tokens parsed */ token_vector tokens ; /* vector of token objects */ /* The following is used (and reused) by cmd_tokens_concat() */ qstring concat ; /* The following are filled in when a tokenised command line is parsed. */ cmd_parts_t parts ; /* What parts are present */ /* NB: the following are significant only if there is a command part. */ struct cmd_command *cmd ; /* NULL if empty command or fails to parse */ node_type_t cnode ; /* node command is in */ node_type_t xnode ; /* node command is to execute in */ node_type_t nnode ; /* node to set if command succeeds */ arg_vector args ; /* vector of arguments */ /* NB: the following are significant only if an error is returned */ qstring emess ; /* parse error */ ssize eloc ; /* error location */ /* NB: the following are significant only if respective part is * present. * * If there is a "do" or "#__" (meta) prefix then "first_action" is * implicitly the number of prefix items. */ cmd_pipe_type_t in_pipe ; /* if any */ cmd_pipe_type_t out_pipe ; /* if any */ uint first_action ; /* command or in pipe */ uint num_action ; uint first_out_pipe ; uint num_out_pipe ; uint first_comment ; uint num_comment ; /* The following are significant after cmd_token_position() */ uint cti ; /* cursor token index -- may be eol token */ uint ctl ; /* cursor token length -- 0 <=> eol token */ int rp ; /* cursor relative to start of cursor token */ /* The following are used while filtering commands */ vector cmd_v ; /* working vector */ vector item_v ; /* working vector */ match_strength_t strongest ; match_type_t best_complete ; match_strength_t min_strength ; /* for execution */ bool strict ; /* for strict keyword match */ } ; enum { CMD_PARSED_INIT_ALL_ZEROS = (cmd_pipe_none == 0) } ; typedef struct cmd_parsed cmd_parsed_t[1] ; typedef struct cmd_parsed* cmd_parsed ; /*============================================================================== * Prototypes */ extern void cmd_compile(cmd_command cmd) ; extern void cmd_compile_check(cmd_command cmd) ; extern cmd_parsed cmd_parsed_new(void) ; extern cmd_parsed cmd_parsed_free(cmd_parsed parsed) ; extern bool cmd_is_empty(qstring line) ; extern void cmd_tokenize(cmd_parsed parsed, qstring line, bool full_lex) ; extern qstring cmd_tokens_concat(cmd_parsed parsed, uint ti, uint nt) ; extern cmd_ret_t cmd_parse_command(cmd_parsed parsed, cmd_context context) ; extern bool cmd_token_position(cmd_parsed parsed, qstring line) ; extern const char* cmd_help_preflight(cmd_parsed parsed) ; extern cmd_ret_t cmd_completion(cmd_parsed parsed, cmd_context context); extern void cmd_complete_keyword(cmd_parsed parsed, int* pre, int* rep, int* ins, int* mov) ; extern bool cmd_part_complete(cmd_parsed parsed, elstring els) ; extern void cmd_get_parse_error(vio_fifo ebuf, cmd_parsed parsed, uint indent) ; extern void cmd_parser_init(void) ; extern void cmd_parser_finish(void) ; extern vector cmd_describe_command (const char* line, node_type_t node, cmd_ret_t* status) ; extern vector cmd_complete_command (vector, int, int *status); /*============================================================================== * Inlines */ /*------------------------------------------------------------------------------ * Get the body of the argument vector. */ Inline const char * const* cmd_arg_vector_argv(cmd_parsed parsed) { return (const char* const*)vector_body(parsed->args->body) ; } ; /*------------------------------------------------------------------------------ * Get length of the body of the argument vector. */ Inline unsigned cmd_arg_vector_argc(cmd_parsed parsed) { return vector_length(parsed->args->body) ; } ; /*------------------------------------------------------------------------------ * Get i'th token from given token vector -- zero origin */ Inline cmd_token cmd_token_get(token_vector tv, vector_index_t i) { return vector_get_item(tv->body, i) ; } ; #endif /* _ZEBRA_COMMAND_PARSE_H */