diff options
author | Timo Teräs <timo.teras@iki.fi> | 2014-04-24 15:58:16 +0300 |
---|---|---|
committer | Timo Teräs <timo.teras@iki.fi> | 2014-04-24 15:59:11 +0300 |
commit | ef1d7c477d98b42ecc7b83aed7d950d3088e2a02 (patch) | |
tree | 5a1c089b46b38ba03c93e4a414282c158c580384 /main | |
parent | 4999b4b369545730001c4bfc749d5515780416ba (diff) | |
download | aports-ef1d7c477d98b42ecc7b83aed7d950d3088e2a02.tar.bz2 aports-ef1d7c477d98b42ecc7b83aed7d950d3088e2a02.tar.xz |
main/quagga: cherry-pick upstream commits
fixes an annoying bug, and contains a required change for the
bgpd-implement-next-hop-self-all patch to work.
Diffstat (limited to 'main')
-rw-r--r-- | main/quagga/0001-vtysh-don-t-append-superflous-spaces-BZ-750.patch | 50 | ||||
-rw-r--r-- | main/quagga/0002-bgpd-ospfd-zebra-fix-some-DEFUN-definitions.patch | 362 | ||||
-rw-r--r-- | main/quagga/0003-lib-rewrite-command-matching.patch | 2979 | ||||
-rw-r--r-- | main/quagga/1001-bgpd-implement-next-hop-self-all.patch (renamed from main/quagga/bgpd-implement-next-hop-self-all.patch) | 0 | ||||
-rw-r--r-- | main/quagga/APKBUILD | 28 | ||||
-rw-r--r-- | main/quagga/quagga-readline-6.3.patch | 6 |
6 files changed, 3414 insertions, 11 deletions
diff --git a/main/quagga/0001-vtysh-don-t-append-superflous-spaces-BZ-750.patch b/main/quagga/0001-vtysh-don-t-append-superflous-spaces-BZ-750.patch new file mode 100644 index 0000000000..610e96aacb --- /dev/null +++ b/main/quagga/0001-vtysh-don-t-append-superflous-spaces-BZ-750.patch @@ -0,0 +1,50 @@ +From 67e7a2127c05a8c7dfddd7ffc6378edf6b666d55 Mon Sep 17 00:00:00 2001 +From: Christian Franke <chris@opensourcerouting.org> +Date: Mon, 4 Mar 2013 09:23:30 +0000 +Subject: [PATCH] vtysh: don't append superflous spaces (BZ#750) + +rl_completion_append_character is reset to space every time the completion +function is entered. So we would have to set it to '\0' every time +new_completion() is called. We can make this conditional and avoid using +rl_pending_input. + +This code path is most relevant when there are multiple completion +matches with the same prefix, e.g. in router bgp context: "neighbor 1.2.3.4 +pa"<ssive|ssword> would have been completed to "neighbor 1.2.3.4 pass " +instead of "neighbor 1.2.3.4 pass". + +Signed-off-by: Christian Franke <chris@opensourcerouting.org> +Signed-off-by: David Lamparter <equinox@opensourcerouting.org> +--- + vtysh/vtysh.c | 8 +++----- + 1 file changed, 3 insertions(+), 5 deletions(-) + +diff --git a/vtysh/vtysh.c b/vtysh/vtysh.c +index e3709e0..c575902 100644 +--- a/vtysh/vtysh.c ++++ b/vtysh/vtysh.c +@@ -677,8 +677,9 @@ new_completion (char *text, int start, int end) + if (matches) + { + rl_point = rl_end; +- if (complete_status == CMD_COMPLETE_FULL_MATCH) +- rl_pending_input = ' '; ++ if (complete_status != CMD_COMPLETE_FULL_MATCH) ++ /* only append a space on full match */ ++ rl_completion_append_character = '\0'; + } + + return matches; +@@ -2214,9 +2215,6 @@ vtysh_readline_init (void) + rl_bind_key ('?', (Function *) vtysh_rl_describe); + rl_completion_entry_function = vtysh_completion_entry_function; + rl_attempted_completion_function = (CPPFunction *)new_completion; +- /* do not append space after completion. It will be appended +- * in new_completion() function explicitly. */ +- rl_completion_append_character = '\0'; + } + + char * +-- +1.9.2 + diff --git a/main/quagga/0002-bgpd-ospfd-zebra-fix-some-DEFUN-definitions.patch b/main/quagga/0002-bgpd-ospfd-zebra-fix-some-DEFUN-definitions.patch new file mode 100644 index 0000000000..f326fbed5c --- /dev/null +++ b/main/quagga/0002-bgpd-ospfd-zebra-fix-some-DEFUN-definitions.patch @@ -0,0 +1,362 @@ +From 2b00515a9b639fd1e057f3ebf10ded2dde920764 Mon Sep 17 00:00:00 2001 +From: Christian Franke <chris@opensourcerouting.org> +Date: Mon, 30 Sep 2013 12:27:49 +0000 +Subject: [PATCH] bgpd, ospfd, zebra: fix some DEFUN definitions + +Fixup some DEFUNS with incorrect command strings or mixed up helpstrings. + +Signed-off-by: Christian Franke <chris@opensourcerouting.org> +Signed-off-by: David Lamparter <equinox@opensourcerouting.org> +--- + bgpd/bgp_route.c | 58 +++++++++++++++++++++++++++---------------------------- + ospfd/ospf_vty.c | 2 ++ + zebra/debug.c | 6 +++--- + zebra/rtadv.c | 2 +- + zebra/zebra_vty.c | 8 ++++---- + 5 files changed, 39 insertions(+), 37 deletions(-) + +diff --git a/bgpd/bgp_route.c b/bgpd/bgp_route.c +index fb35fab..335543e 100644 +--- a/bgpd/bgp_route.c ++++ b/bgpd/bgp_route.c +@@ -4375,7 +4375,7 @@ ALIAS_DEPRECATED (bgp_network_mask_natural, + "AS-Pathlimit TTL, in number of AS-Path hops\n") + ALIAS_DEPRECATED (bgp_network_mask_natural_backdoor, + bgp_network_mask_natural_backdoor_ttl_cmd, +- "network A.B.C.D backdoor pathlimit (1-255>", ++ "network A.B.C.D backdoor pathlimit <1-255>", + "Specify a network to announce via BGP\n" + "Network number\n" + "Specify a BGP backdoor route\n" +@@ -6796,7 +6796,7 @@ DEFUN (show_ip_bgp_view, + IP_STR + BGP_STR + "BGP view\n" +- "BGP view name\n") ++ "View name\n") + { + struct bgp *bgp; + +@@ -6818,7 +6818,7 @@ DEFUN (show_ip_bgp_view_route, + IP_STR + BGP_STR + "BGP view\n" +- "BGP view name\n" ++ "View name\n" + "Network in the BGP routing table to display\n") + { + return bgp_show_route (vty, argv[0], argv[1], AFI_IP, SAFI_UNICAST, NULL, 0); +@@ -6831,7 +6831,7 @@ DEFUN (show_ip_bgp_view_prefix, + IP_STR + BGP_STR + "BGP view\n" +- "BGP view name\n" ++ "View name\n" + "IP prefix <network>/<length>, e.g., 35.0.0.0/8\n") + { + return bgp_show_route (vty, argv[0], argv[1], AFI_IP, SAFI_UNICAST, NULL, 1); +@@ -7904,14 +7904,14 @@ DEFUN (show_bgp_view_afi_safi_community_all, + SHOW_STR + BGP_STR + "BGP view\n" +- "BGP view name\n" ++ "View name\n" + "Address family\n" + #ifdef HAVE_IPV6 + "Address family\n" + #endif + "Address Family modifier\n" + "Address Family modifier\n" +- "Display routes containing communities\n") ++ "Display routes matching the communities\n") + { + int afi; + int safi; +@@ -7945,7 +7945,7 @@ DEFUN (show_bgp_view_afi_safi_community, + SHOW_STR + BGP_STR + "BGP view\n" +- "BGP view name\n" ++ "View name\n" + "Address family\n" + #ifdef HAVE_IPV6 + "Address family\n" +@@ -7982,7 +7982,7 @@ ALIAS (show_bgp_view_afi_safi_community, + SHOW_STR + BGP_STR + "BGP view\n" +- "BGP view name\n" ++ "View name\n" + "Address family\n" + #ifdef HAVE_IPV6 + "Address family\n" +@@ -8009,7 +8009,7 @@ ALIAS (show_bgp_view_afi_safi_community, + SHOW_STR + BGP_STR + "BGP view\n" +- "BGP view name\n" ++ "View name\n" + "Address family\n" + #ifdef HAVE_IPV6 + "Address family\n" +@@ -8040,7 +8040,7 @@ ALIAS (show_bgp_view_afi_safi_community, + SHOW_STR + BGP_STR + "BGP view\n" +- "BGP view name\n" ++ "View name\n" + "Address family\n" + #ifdef HAVE_IPV6 + "Address family\n" +@@ -10198,7 +10198,7 @@ DEFUN (show_bgp_view_afi_safi_neighbor_adv_recd_routes, + SHOW_STR + BGP_STR + "BGP view\n" +- "BGP view name\n" ++ "View name\n" + "Address family\n" + #ifdef HAVE_IPV6 + "Address family\n" +@@ -10613,7 +10613,7 @@ DEFUN (show_ip_bgp_view_rsclient, + IP_STR + BGP_STR + "BGP view\n" +- "BGP view name\n" ++ "View name\n" + "Information about Route Server Client\n" + NEIGHBOR_ADDR_STR) + { +@@ -10663,7 +10663,7 @@ DEFUN (show_bgp_view_ipv4_safi_rsclient, + SHOW_STR + BGP_STR + "BGP view\n" +- "BGP view name\n" ++ "View name\n" + "Address family\n" + "Address Family modifier\n" + "Address Family modifier\n" +@@ -10723,7 +10723,7 @@ DEFUN (show_ip_bgp_view_rsclient_route, + IP_STR + BGP_STR + "BGP view\n" +- "BGP view name\n" ++ "View name\n" + "Information about Route Server Client\n" + NEIGHBOR_ADDR_STR + "Network in the BGP routing table to display\n") +@@ -10795,7 +10795,7 @@ DEFUN (show_bgp_view_ipv4_safi_rsclient_route, + SHOW_STR + BGP_STR + "BGP view\n" +- "BGP view name\n" ++ "View name\n" + "Address family\n" + "Address Family modifier\n" + "Address Family modifier\n" +@@ -10877,7 +10877,7 @@ DEFUN (show_ip_bgp_view_rsclient_prefix, + IP_STR + BGP_STR + "BGP view\n" +- "BGP view name\n" ++ "View name\n" + "Information about Route Server Client\n" + NEIGHBOR_ADDR_STR + "IP prefix <network>/<length>, e.g., 35.0.0.0/8\n") +@@ -10949,7 +10949,7 @@ DEFUN (show_bgp_view_ipv4_safi_rsclient_prefix, + SHOW_STR + BGP_STR + "BGP view\n" +- "BGP view name\n" ++ "View name\n" + "Address family\n" + "Address Family modifier\n" + "Address Family modifier\n" +@@ -11031,7 +11031,7 @@ DEFUN (show_bgp_view_neighbor_routes, + SHOW_STR + BGP_STR + "BGP view\n" +- "BGP view name\n" ++ "View name\n" + "Detailed information on TCP and BGP neighbor connections\n" + "Neighbor to display information about\n" + "Neighbor to display information about\n" +@@ -11057,7 +11057,7 @@ ALIAS (show_bgp_view_neighbor_routes, + SHOW_STR + BGP_STR + "BGP view\n" +- "BGP view name\n" ++ "View name\n" + "Address family\n" + "Detailed information on TCP and BGP neighbor connections\n" + "Neighbor to display information about\n" +@@ -11070,7 +11070,7 @@ DEFUN (show_bgp_view_neighbor_damp, + SHOW_STR + BGP_STR + "BGP view\n" +- "BGP view name\n" ++ "View name\n" + "Detailed information on TCP and BGP neighbor connections\n" + "Neighbor to display information about\n" + "Neighbor to display information about\n" +@@ -11096,7 +11096,7 @@ ALIAS (show_bgp_view_neighbor_damp, + SHOW_STR + BGP_STR + "BGP view\n" +- "BGP view name\n" ++ "View name\n" + "Address family\n" + "Detailed information on TCP and BGP neighbor connections\n" + "Neighbor to display information about\n" +@@ -11109,7 +11109,7 @@ DEFUN (show_bgp_view_neighbor_flap, + SHOW_STR + BGP_STR + "BGP view\n" +- "BGP view name\n" ++ "View name\n" + "Detailed information on TCP and BGP neighbor connections\n" + "Neighbor to display information about\n" + "Neighbor to display information about\n" +@@ -11135,7 +11135,7 @@ ALIAS (show_bgp_view_neighbor_flap, + SHOW_STR + BGP_STR + "BGP view\n" +- "BGP view name\n" ++ "View name\n" + "Address family\n" + "Detailed information on TCP and BGP neighbor connections\n" + "Neighbor to display information about\n" +@@ -11246,7 +11246,7 @@ DEFUN (show_bgp_view_rsclient, + SHOW_STR + BGP_STR + "BGP view\n" +- "BGP view name\n" ++ "View name\n" + "Information about Route Server Client\n" + NEIGHBOR_ADDR_STR) + { +@@ -11295,7 +11295,7 @@ DEFUN (show_bgp_view_ipv6_safi_rsclient, + SHOW_STR + BGP_STR + "BGP view\n" +- "BGP view name\n" ++ "View name\n" + "Address family\n" + "Address Family modifier\n" + "Address Family modifier\n" +@@ -11354,7 +11354,7 @@ DEFUN (show_bgp_view_rsclient_route, + SHOW_STR + BGP_STR + "BGP view\n" +- "BGP view name\n" ++ "View name\n" + "Information about Route Server Client\n" + NEIGHBOR_ADDR_STR + "Network in the BGP routing table to display\n") +@@ -11425,7 +11425,7 @@ DEFUN (show_bgp_view_ipv6_safi_rsclient_route, + SHOW_STR + BGP_STR + "BGP view\n" +- "BGP view name\n" ++ "View name\n" + "Address family\n" + "Address Family modifier\n" + "Address Family modifier\n" +@@ -11506,7 +11506,7 @@ DEFUN (show_bgp_view_rsclient_prefix, + SHOW_STR + BGP_STR + "BGP view\n" +- "BGP view name\n" ++ "View name\n" + "Information about Route Server Client\n" + NEIGHBOR_ADDR_STR + "IPv6 prefix <network>/<length>, e.g., 3ffe::/16\n") +@@ -11577,7 +11577,7 @@ DEFUN (show_bgp_view_ipv6_safi_rsclient_prefix, + SHOW_STR + BGP_STR + "BGP view\n" +- "BGP view name\n" ++ "View name\n" + "Address family\n" + "Address Family modifier\n" + "Address Family modifier\n" +diff --git a/ospfd/ospf_vty.c b/ospfd/ospf_vty.c +index 2ba8188..5e5a0b0 100644 +--- a/ospfd/ospf_vty.c ++++ b/ospfd/ospf_vty.c +@@ -1976,6 +1976,8 @@ DEFUN (ospf_area_authentication_message_digest, + ospf_area_authentication_message_digest_cmd, + "area (A.B.C.D|<0-4294967295>) authentication message-digest", + "OSPF area parameters\n" ++ "OSPF area ID in IP address format\n" ++ "OSPF area ID as a decimal value\n" + "Enable authentication\n" + "Use message-digest authentication\n") + { +diff --git a/zebra/debug.c b/zebra/debug.c +index 7bfdb77..c3b00e0 100644 +--- a/zebra/debug.c ++++ b/zebra/debug.c +@@ -35,8 +35,8 @@ DEFUN (show_debugging_zebra, + show_debugging_zebra_cmd, + "show debugging zebra", + SHOW_STR +- "Zebra configuration\n" +- "Debugging information\n") ++ "Debugging information\n" ++ "Zebra configuration\n") + { + vty_out (vty, "Zebra debugging status:%s", VTY_NEWLINE); + +@@ -128,7 +128,7 @@ DEFUN (debug_zebra_packet_detail, + "Debug option set for zebra packet\n" + "Debug option set for receive packet\n" + "Debug option set for send packet\n" +- "Debug option set detaied information\n") ++ "Debug option set detailed information\n") + { + zebra_debug_packet = ZEBRA_DEBUG_PACKET; + if (strncmp ("send", argv[0], strlen (argv[0])) == 0) +diff --git a/zebra/rtadv.c b/zebra/rtadv.c +index ae5c5a1..91d7b32 100644 +--- a/zebra/rtadv.c ++++ b/zebra/rtadv.c +@@ -1483,7 +1483,7 @@ DEFUN (no_ipv6_nd_router_preference, + + ALIAS (no_ipv6_nd_router_preference, + no_ipv6_nd_router_preference_val_cmd, +- "no ipv6 nd router-preference (high|medium|low", ++ "no ipv6 nd router-preference (high|medium|low)", + NO_STR + "Interface IPv6 config commands\n" + "Neighbor discovery\n" +diff --git a/zebra/zebra_vty.c b/zebra/zebra_vty.c +index 45928e9..f4946f4 100644 +--- a/zebra/zebra_vty.c ++++ b/zebra/zebra_vty.c +@@ -280,9 +280,9 @@ DEFUN (ip_route_mask_flags_distance, + "IP destination prefix mask\n" + "IP gateway address\n" + "IP gateway interface name\n" +- "Distance value for this route\n" + "Emit an ICMP unreachable when matched\n" +- "Silently discard pkts when matched\n") ++ "Silently discard pkts when matched\n" ++ "Distance value for this route\n") + { + return zebra_static_ipv4 (vty, 1, argv[0], argv[1], argv[2], argv[3], argv[4]); + } +@@ -294,9 +294,9 @@ DEFUN (ip_route_mask_flags_distance2, + "Establish static routes\n" + "IP destination prefix\n" + "IP destination prefix mask\n" +- "Distance value for this route\n" + "Emit an ICMP unreachable when matched\n" +- "Silently discard pkts when matched\n") ++ "Silently discard pkts when matched\n" ++ "Distance value for this route\n") + { + return zebra_static_ipv4 (vty, 1, argv[0], argv[1], NULL, argv[2], argv[3]); + } +-- +1.9.2 + diff --git a/main/quagga/0003-lib-rewrite-command-matching.patch b/main/quagga/0003-lib-rewrite-command-matching.patch new file mode 100644 index 0000000000..9cd21ddb38 --- /dev/null +++ b/main/quagga/0003-lib-rewrite-command-matching.patch @@ -0,0 +1,2979 @@ +From cd40b329a2e4da882bcad0431c048c876bbeafbd Mon Sep 17 00:00:00 2001 +From: Christian Franke <chris@opensourcerouting.org> +Date: Mon, 30 Sep 2013 12:27:51 +0000 +Subject: [PATCH] lib/command.c: rewrite command matching/parsing + +Add support for keyword commands. + +Includes new documentation for DEFUN() in lib/command.h, for preexisting +features as well as new keyword specification. + +Signed-off-by: Christian Franke <chris@opensourcerouting.org> +Signed-off-by: David Lamparter <equinox@opensourcerouting.org> +--- + babeld/babel_main.c | 3 - + bgpd/bgp_main.c | 3 - + isisd/isis_main.c | 2 - + lib/command.c | 1977 +++++++++++++++++++++++++++++++------------------ + lib/command.h | 193 ++++- + lib/memtypes.c | 2 +- + lib/vty.c | 54 +- + ospf6d/ospf6_main.c | 3 - + ospfd/ospf_main.c | 2 - + ripd/rip_main.c | 3 - + ripngd/ripng_main.c | 3 - + tests/main.c | 2 - + vtysh/vtysh.c | 22 +- + vtysh/vtysh_main.c | 2 - + zebra/main.c | 3 - + zebra/test_main.c | 3 - + 17 files changed, 1495 insertions(+), 788 deletions(-) + +diff --git a/babeld/babel_main.c b/babeld/babel_main.c +index 7b1d879..529c3f2 100644 +--- a/babeld/babel_main.c ++++ b/babeld/babel_main.c +@@ -277,9 +277,6 @@ babel_init(int argc, char **argv) + /* this replace kernel_setup && kernel_setup_socket */ + babelz_zebra_init (); + +- /* Sort all installed commands. */ +- sort_node (); +- + /* Get zebra configuration file. */ + zlog_set_level (NULL, ZLOG_DEST_STDOUT, ZLOG_DISABLED); + vty_read_config (babel_config_file, babel_config_default); +diff --git a/bgpd/bgp_main.c b/bgpd/bgp_main.c +index 1ff1ac9..1be6504 100644 +--- a/bgpd/bgp_main.c ++++ b/bgpd/bgp_main.c +@@ -431,9 +431,6 @@ main (int argc, char **argv) + /* BGP related initialization. */ + bgp_init (); + +- /* Sort CLI commands. */ +- sort_node (); +- + /* Parse config file. */ + vty_read_config (config_file, config_default); + +diff --git a/isisd/isis_main.c b/isisd/isis_main.c +index 96816eb..283b7ea 100644 +--- a/isisd/isis_main.c ++++ b/isisd/isis_main.c +@@ -339,8 +339,6 @@ main (int argc, char **argv, char **envp) + + isis_zebra_init (); + +- sort_node (); +- + /* parse config file */ + /* this is needed three times! because we have interfaces before the areas */ + vty_read_config (config_file, config_default); +diff --git a/lib/command.c b/lib/command.c +index 3b3fada..a237364 100644 +--- a/lib/command.c ++++ b/lib/command.c +@@ -1,6 +1,8 @@ + /* + Command interpreter routine for virtual terminal [aka TeletYpe] + Copyright (C) 1997, 98, 99 Kunihiro Ishiguro ++ Copyright (C) 2013 by Open Source Routing. ++ Copyright (C) 2013 by Internet Systems Consortium, Inc. ("ISC") + + This file is part of GNU Zebra. + +@@ -35,9 +37,32 @@ Boston, MA 02111-1307, USA. */ + each daemon maintains each own cmdvec. */ + vector cmdvec = NULL; + +-struct desc desc_cr; ++struct cmd_token token_cr; + char *command_cr = NULL; + ++enum filter_type ++{ ++ FILTER_RELAXED, ++ FILTER_STRICT ++}; ++ ++enum matcher_rv ++{ ++ MATCHER_OK, ++ MATCHER_COMPLETE, ++ MATCHER_INCOMPLETE, ++ MATCHER_NO_MATCH, ++ MATCHER_AMBIGUOUS, ++ MATCHER_EXCEED_ARGC_MAX ++}; ++ ++#define MATCHER_ERROR(matcher_rv) \ ++ ( (matcher_rv) == MATCHER_INCOMPLETE \ ++ || (matcher_rv) == MATCHER_NO_MATCH \ ++ || (matcher_rv) == MATCHER_AMBIGUOUS \ ++ || (matcher_rv) == MATCHER_EXCEED_ARGC_MAX \ ++ ) ++ + /* Host information structure. */ + struct host host; + +@@ -196,53 +221,6 @@ install_node (struct cmd_node *node, + node->cmd_vector = vector_init (VECTOR_MIN_SIZE); + } + +-/* Compare two command's string. Used in sort_node (). */ +-static int +-cmp_node (const void *p, const void *q) +-{ +- const struct cmd_element *a = *(struct cmd_element * const *)p; +- const struct cmd_element *b = *(struct cmd_element * const *)q; +- +- return strcmp (a->string, b->string); +-} +- +-static int +-cmp_desc (const void *p, const void *q) +-{ +- const struct desc *a = *(struct desc * const *)p; +- const struct desc *b = *(struct desc * const *)q; +- +- return strcmp (a->cmd, b->cmd); +-} +- +-/* Sort each node's command element according to command string. */ +-void +-sort_node () +-{ +- unsigned int i, j; +- struct cmd_node *cnode; +- vector descvec; +- struct cmd_element *cmd_element; +- +- for (i = 0; i < vector_active (cmdvec); i++) +- if ((cnode = vector_slot (cmdvec, i)) != NULL) +- { +- vector cmd_vector = cnode->cmd_vector; +- qsort (cmd_vector->index, vector_active (cmd_vector), +- sizeof (void *), cmp_node); +- +- for (j = 0; j < vector_active (cmd_vector); j++) +- if ((cmd_element = vector_slot (cmd_vector, j)) != NULL +- && vector_active (cmd_element->strvec)) +- { +- descvec = vector_slot (cmd_element->strvec, +- vector_active (cmd_element->strvec) - 1); +- qsort (descvec->index, vector_active (descvec), +- sizeof (void *), cmp_desc); +- } +- } +-} +- + /* Breaking up string into each command piece. I assume given + character is separated by a space character. Return value is a + vector which includes char ** data element. */ +@@ -312,15 +290,46 @@ cmd_free_strvec (vector v) + vector_free (v); + } + +-/* Fetch next description. Used in cmd_make_descvec(). */ ++struct format_parser_state ++{ ++ vector topvect; /* Top level vector */ ++ vector intvect; /* Intermediate level vector, used when there's ++ * a multiple in a keyword. */ ++ vector curvect; /* current vector where read tokens should be ++ appended. */ ++ ++ const char *string; /* pointer to command string, not modified */ ++ const char *cp; /* pointer in command string, moved along while ++ parsing */ ++ const char *dp; /* pointer in description string, moved along while ++ parsing */ ++ ++ int in_keyword; /* flag to remember if we are in a keyword group */ ++ int in_multiple; /* flag to remember if we are in a multiple group */ ++ int just_read_word; /* flag to remember if the last thing we red was a ++ * real word and not some abstract token */ ++}; ++ ++static void ++format_parser_error(struct format_parser_state *state, const char *message) ++{ ++ int offset = state->cp - state->string + 1; ++ ++ fprintf(stderr, "\nError parsing command: \"%s\"\n", state->string); ++ fprintf(stderr, " %*c\n", offset, '^'); ++ fprintf(stderr, "%s at offset %d.\n", message, offset); ++ fprintf(stderr, "This is a programming error. Check your DEFUNs etc.\n"); ++ exit(1); ++} ++ + static char * +-cmd_desc_str (const char **string) ++format_parser_desc_str(struct format_parser_state *state) + { + const char *cp, *start; + char *token; + int strlen; +- +- cp = *string; ++ ++ cp = state->dp; + + if (cp == NULL) + return NULL; +@@ -339,132 +348,226 @@ cmd_desc_str (const char **string) + cp++; + + strlen = cp - start; +- token = XMALLOC (MTYPE_STRVEC, strlen + 1); ++ token = XMALLOC (MTYPE_CMD_TOKENS, strlen + 1); + memcpy (token, start, strlen); + *(token + strlen) = '\0'; + +- *string = cp; ++ state->dp = cp; + + return token; + } + +-/* New string vector. */ +-static vector +-cmd_make_descvec (const char *string, const char *descstr) ++static void ++format_parser_begin_keyword(struct format_parser_state *state) + { +- int multiple = 0; +- const char *sp; +- char *token; +- int len; +- const char *cp; +- const char *dp; +- vector allvec; +- vector strvec = NULL; +- struct desc *desc; ++ struct cmd_token *token; ++ vector keyword_vect; + +- cp = string; +- dp = descstr; ++ if (state->in_keyword ++ || state->in_multiple) ++ format_parser_error(state, "Unexpected '{'"); + +- if (cp == NULL) +- return NULL; ++ state->cp++; ++ state->in_keyword = 1; + +- allvec = vector_init (VECTOR_MIN_SIZE); ++ token = XCALLOC(MTYPE_CMD_TOKENS, sizeof(*token)); ++ token->type = TOKEN_KEYWORD; ++ token->keyword = vector_init(VECTOR_MIN_SIZE); + +- while (1) +- { +- while (isspace ((int) *cp) && *cp != '\0') +- cp++; ++ keyword_vect = vector_init(VECTOR_MIN_SIZE); ++ vector_set(token->keyword, keyword_vect); + +- if (*cp == '(') +- { +- multiple = 1; +- cp++; +- } +- if (*cp == ')') +- { +- multiple = 0; +- cp++; +- } +- if (*cp == '|') +- { +- if (! multiple) +- { +- fprintf (stderr, "Command parse error!: %s\n", string); +- exit (1); +- } +- cp++; +- } +- +- while (isspace ((int) *cp) && *cp != '\0') +- cp++; ++ vector_set(state->curvect, token); ++ state->curvect = keyword_vect; ++} + +- if (*cp == '(') +- { +- multiple = 1; +- cp++; +- } ++static void ++format_parser_begin_multiple(struct format_parser_state *state) ++{ ++ struct cmd_token *token; + +- if (*cp == '\0') +- return allvec; ++ if (state->in_keyword == 1) ++ format_parser_error(state, "Keyword starting with '('"); + +- sp = cp; ++ if (state->in_multiple) ++ format_parser_error(state, "Nested group"); + +- while (! (isspace ((int) *cp) || *cp == '\r' || *cp == '\n' || *cp == ')' || *cp == '|') && *cp != '\0') +- cp++; ++ state->cp++; ++ state->in_multiple = 1; ++ state->just_read_word = 0; ++ ++ token = XCALLOC(MTYPE_CMD_TOKENS, sizeof(*token)); ++ token->type = TOKEN_MULTIPLE; ++ token->multiple = vector_init(VECTOR_MIN_SIZE); + +- len = cp - sp; ++ vector_set(state->curvect, token); ++ if (state->curvect != state->topvect) ++ state->intvect = state->curvect; ++ state->curvect = token->multiple; ++} + +- token = XMALLOC (MTYPE_STRVEC, len + 1); +- memcpy (token, sp, len); +- *(token + len) = '\0'; ++static void ++format_parser_end_keyword(struct format_parser_state *state) ++{ ++ if (state->in_multiple ++ || !state->in_keyword) ++ format_parser_error(state, "Unexpected '}'"); + +- desc = XCALLOC (MTYPE_DESC, sizeof (struct desc)); +- desc->cmd = token; +- desc->str = cmd_desc_str (&dp); ++ if (state->in_keyword == 1) ++ format_parser_error(state, "Empty keyword group"); + +- if (multiple) +- { +- if (multiple == 1) +- { +- strvec = vector_init (VECTOR_MIN_SIZE); +- vector_set (allvec, strvec); +- } +- multiple++; +- } +- else +- { +- strvec = vector_init (VECTOR_MIN_SIZE); +- vector_set (allvec, strvec); +- } +- vector_set (strvec, desc); ++ state->cp++; ++ state->in_keyword = 0; ++ state->curvect = state->topvect; ++} ++ ++static void ++format_parser_end_multiple(struct format_parser_state *state) ++{ ++ char *dummy; ++ ++ if (!state->in_multiple) ++ format_parser_error(state, "Unepexted ')'"); ++ ++ if (vector_active(state->curvect) == 0) ++ format_parser_error(state, "Empty multiple section"); ++ ++ if (!state->just_read_word) ++ { ++ /* There are constructions like ++ * 'show ip ospf database ... (self-originate|)' ++ * in use. ++ * The old parser reads a description string for the ++ * word '' between |) which will never match. ++ * Simulate this behvaior by dropping the next desc ++ * string in such a case. */ ++ ++ dummy = format_parser_desc_str(state); ++ XFREE(MTYPE_CMD_TOKENS, dummy); + } ++ ++ state->cp++; ++ state->in_multiple = 0; ++ ++ if (state->intvect) ++ state->curvect = state->intvect; ++ else ++ state->curvect = state->topvect; + } + +-/* Count mandantory string vector size. This is to determine inputed +- command has enough command length. */ +-static int +-cmd_cmdsize (vector strvec) ++static void ++format_parser_handle_pipe(struct format_parser_state *state) + { +- unsigned int i; +- int size = 0; +- vector descvec; +- struct desc *desc; ++ struct cmd_token *keyword_token; ++ vector keyword_vect; + +- for (i = 0; i < vector_active (strvec); i++) +- if ((descvec = vector_slot (strvec, i)) != NULL) ++ if (state->in_multiple) + { +- if ((vector_active (descvec)) == 1 +- && (desc = vector_slot (descvec, 0)) != NULL) +- { +- if (desc->cmd == NULL || CMD_OPTION (desc->cmd)) +- return size; +- else +- size++; +- } +- else +- size++; ++ state->just_read_word = 0; ++ state->cp++; ++ } ++ else if (state->in_keyword) ++ { ++ state->in_keyword = 1; ++ state->cp++; ++ ++ keyword_token = vector_slot(state->topvect, ++ vector_active(state->topvect) - 1); ++ keyword_vect = vector_init(VECTOR_MIN_SIZE); ++ vector_set(keyword_token->keyword, keyword_vect); ++ state->curvect = keyword_vect; ++ } ++ else ++ { ++ format_parser_error(state, "Unexpected '|'"); ++ } ++} ++ ++static void ++format_parser_read_word(struct format_parser_state *state) ++{ ++ const char *start; ++ int len; ++ char *cmd; ++ struct cmd_token *token; ++ ++ start = state->cp; ++ ++ while (state->cp[0] != '\0' ++ && !strchr("\r\n(){}|", state->cp[0]) ++ && !isspace((int)state->cp[0])) ++ state->cp++; ++ ++ len = state->cp - start; ++ cmd = XMALLOC(MTYPE_CMD_TOKENS, len + 1); ++ memcpy(cmd, start, len); ++ cmd[len] = '\0'; ++ ++ token = XCALLOC(MTYPE_CMD_TOKENS, sizeof(*token)); ++ token->type = TOKEN_TERMINAL; ++ token->cmd = cmd; ++ token->desc = format_parser_desc_str(state); ++ vector_set(state->curvect, token); ++ ++ if (state->in_keyword == 1) ++ state->in_keyword = 2; ++ ++ state->just_read_word = 1; ++} ++ ++/** ++ * Parse a given command format string and build a tree of tokens from ++ * it that is suitable to be used by the command subsystem. ++ * ++ * @param string Command format string. ++ * @param descstr Description string. ++ * @return A vector of struct cmd_token representing the given command, ++ * or NULL on error. ++ */ ++static vector ++cmd_parse_format(const char *string, const char *descstr) ++{ ++ struct format_parser_state state; ++ ++ if (string == NULL) ++ return NULL; ++ ++ memset(&state, 0, sizeof(state)); ++ state.topvect = state.curvect = vector_init(VECTOR_MIN_SIZE); ++ state.cp = state.string = string; ++ state.dp = descstr; ++ ++ while (1) ++ { ++ while (isspace((int)state.cp[0]) && state.cp[0] != '\0') ++ state.cp++; ++ ++ switch (state.cp[0]) ++ { ++ case '\0': ++ if (state.in_keyword ++ || state.in_multiple) ++ format_parser_error(&state, "Unclosed group/keyword"); ++ return state.topvect; ++ case '{': ++ format_parser_begin_keyword(&state); ++ break; ++ case '(': ++ format_parser_begin_multiple(&state); ++ break; ++ case '}': ++ format_parser_end_keyword(&state); ++ break; ++ case ')': ++ format_parser_end_multiple(&state); ++ break; ++ case '|': ++ format_parser_handle_pipe(&state); ++ break; ++ default: ++ format_parser_read_word(&state); ++ } + } +- return size; + } + + /* Return prompt character of specified node. */ +@@ -497,11 +600,8 @@ install_element (enum node_type ntype, struct cmd_element *cmd) + } + + vector_set (cnode->cmd_vector, cmd); +- +- if (cmd->strvec == NULL) +- cmd->strvec = cmd_make_descvec (cmd->string, cmd->doc); +- +- cmd->cmdsize = cmd_cmdsize (cmd->strvec); ++ if (cmd->tokens == NULL) ++ cmd->tokens = cmd_parse_format(cmd->string, cmd->doc); + } + + static const unsigned char itoa64[] = +@@ -847,9 +947,6 @@ cmd_ipv4_prefix_match (const char *str) + static enum match_type + cmd_ipv6_match (const char *str) + { +- int state = STATE_START; +- int colons = 0, nums = 0, double_colon = 0; +- const char *sp = NULL; + struct sockaddr_in6 sin6_dummy; + int ret; + +@@ -1056,254 +1153,700 @@ cmd_range_match (const char *range, const char *str) + return 1; + } + +-/* Make completion match and return match type flag. */ + static enum match_type +-cmd_filter_by_completion (char *command, vector v, unsigned int index) ++cmd_word_match(struct cmd_token *token, ++ enum filter_type filter, ++ const char *word) + { +- unsigned int i; + const char *str; +- struct cmd_element *cmd_element; + enum match_type match_type; +- vector descvec; +- struct desc *desc; + +- match_type = no_match; ++ str = token->cmd; + +- /* If command and cmd_element string does not match set NULL to vector */ +- for (i = 0; i < vector_active (v); i++) +- if ((cmd_element = vector_slot (v, i)) != NULL) +- { +- if (index >= vector_active (cmd_element->strvec)) +- vector_slot (v, i) = NULL; +- else +- { +- unsigned int j; +- int matched = 0; ++ if (filter == FILTER_RELAXED) ++ if (!word || !strlen(word)) ++ return partly_match; + +- descvec = vector_slot (cmd_element->strvec, index); +- +- for (j = 0; j < vector_active (descvec); j++) +- if ((desc = vector_slot (descvec, j))) +- { +- str = desc->cmd; +- +- if (CMD_VARARG (str)) +- { +- if (match_type < vararg_match) +- match_type = vararg_match; +- matched++; +- } +- else if (CMD_RANGE (str)) +- { +- if (cmd_range_match (str, command)) +- { +- if (match_type < range_match) +- match_type = range_match; ++ if (!word) ++ return no_match; + +- matched++; +- } +- } ++ if (CMD_VARARG(str)) ++ { ++ return vararg_match; ++ } ++ else if (CMD_RANGE(str)) ++ { ++ if (cmd_range_match(str, word)) ++ return range_match; ++ } + #ifdef HAVE_IPV6 +- else if (CMD_IPV6 (str)) +- { +- if (cmd_ipv6_match (command)) +- { +- if (match_type < ipv6_match) +- match_type = ipv6_match; ++ else if (CMD_IPV6(str)) ++ { ++ match_type = cmd_ipv6_match(word); ++ if ((filter == FILTER_RELAXED && match_type != no_match) ++ || (filter == FILTER_STRICT && match_type == exact_match)) ++ return ipv6_match; ++ } ++ else if (CMD_IPV6_PREFIX(str)) ++ { ++ match_type = cmd_ipv6_prefix_match(word); ++ if ((filter == FILTER_RELAXED && match_type != no_match) ++ || (filter == FILTER_STRICT && match_type == exact_match)) ++ return ipv6_prefix_match; ++ } ++#endif /* HAVE_IPV6 */ ++ else if (CMD_IPV4(str)) ++ { ++ match_type = cmd_ipv4_match(word); ++ if ((filter == FILTER_RELAXED && match_type != no_match) ++ || (filter == FILTER_STRICT && match_type == exact_match)) ++ return ipv4_match; ++ } ++ else if (CMD_IPV4_PREFIX(str)) ++ { ++ match_type = cmd_ipv4_prefix_match(word); ++ if ((filter == FILTER_RELAXED && match_type != no_match) ++ || (filter == FILTER_STRICT && match_type == exact_match)) ++ return ipv4_prefix_match; ++ } ++ else if (CMD_OPTION(str) || CMD_VARIABLE(str)) ++ { ++ return extend_match; ++ } ++ else ++ { ++ if (filter == FILTER_RELAXED && !strncmp(str, word, strlen(word))) ++ { ++ if (!strcmp(str, word)) ++ return exact_match; ++ return partly_match; ++ } ++ if (filter == FILTER_STRICT && !strcmp(str, word)) ++ return exact_match; ++ } + +- matched++; +- } +- } +- else if (CMD_IPV6_PREFIX (str)) +- { +- if (cmd_ipv6_prefix_match (command)) +- { +- if (match_type < ipv6_prefix_match) +- match_type = ipv6_prefix_match; ++ return no_match; ++} + +- matched++; +- } +- } +-#endif /* HAVE_IPV6 */ +- else if (CMD_IPV4 (str)) +- { +- if (cmd_ipv4_match (command)) +- { +- if (match_type < ipv4_match) +- match_type = ipv4_match; ++struct cmd_matcher ++{ ++ struct cmd_element *cmd; /* The command element the matcher is using */ ++ enum filter_type filter; /* Whether to use strict or relaxed matching */ ++ vector vline; /* The tokenized commandline which is to be matched */ ++ unsigned int index; /* The index up to which matching should be done */ + +- matched++; +- } +- } +- else if (CMD_IPV4_PREFIX (str)) +- { +- if (cmd_ipv4_prefix_match (command)) +- { +- if (match_type < ipv4_prefix_match) +- match_type = ipv4_prefix_match; +- matched++; +- } +- } +- else +- /* Check is this point's argument optional ? */ +- if (CMD_OPTION (str) || CMD_VARIABLE (str)) +- { +- if (match_type < extend_match) +- match_type = extend_match; +- matched++; +- } +- else if (strncmp (command, str, strlen (command)) == 0) +- { +- if (strcmp (command, str) == 0) +- match_type = exact_match; +- else +- { +- if (match_type < partly_match) +- match_type = partly_match; +- } +- matched++; +- } +- } +- if (!matched) +- vector_slot (v, i) = NULL; +- } ++ /* If set, construct a list of matches at the position given by index */ ++ enum match_type *match_type; ++ vector *match; ++ ++ unsigned int word_index; /* iterating over vline */ ++}; ++ ++static int ++push_argument(int *argc, const char **argv, const char *arg) ++{ ++ if (!arg || !strlen(arg)) ++ arg = NULL; ++ ++ if (!argc || !argv) ++ return 0; ++ ++ if (*argc >= CMD_ARGC_MAX) ++ return -1; ++ ++ argv[(*argc)++] = arg; ++ return 0; ++} ++ ++static void ++cmd_matcher_record_match(struct cmd_matcher *matcher, ++ enum match_type match_type, ++ struct cmd_token *token) ++{ ++ if (matcher->word_index != matcher->index) ++ return; ++ ++ if (matcher->match) ++ { ++ if (!*matcher->match) ++ *matcher->match = vector_init(VECTOR_MIN_SIZE); ++ vector_set(*matcher->match, token); ++ } ++ ++ if (matcher->match_type) ++ { ++ if (match_type > *matcher->match_type) ++ *matcher->match_type = match_type; ++ } ++} ++ ++static int ++cmd_matcher_words_left(struct cmd_matcher *matcher) ++{ ++ return matcher->word_index < vector_active(matcher->vline); ++} ++ ++static const char* ++cmd_matcher_get_word(struct cmd_matcher *matcher) ++{ ++ assert(cmd_matcher_words_left(matcher)); ++ ++ return vector_slot(matcher->vline, matcher->word_index); ++} ++ ++static enum matcher_rv ++cmd_matcher_match_terminal(struct cmd_matcher *matcher, ++ struct cmd_token *token, ++ int *argc, const char **argv) ++{ ++ const char *word; ++ enum match_type word_match; ++ ++ assert(token->type == TOKEN_TERMINAL); ++ ++ if (!cmd_matcher_words_left(matcher)) ++ { ++ if (CMD_OPTION(token->cmd)) ++ return MATCHER_OK; /* missing optional args are NOT pushed as NULL */ ++ else ++ return MATCHER_INCOMPLETE; ++ } ++ ++ word = cmd_matcher_get_word(matcher); ++ word_match = cmd_word_match(token, matcher->filter, word); ++ if (word_match == no_match) ++ return MATCHER_NO_MATCH; ++ ++ /* We have to record the input word as argument if it matched ++ * against a variable. */ ++ if (CMD_VARARG(token->cmd) ++ || CMD_VARIABLE(token->cmd) ++ || CMD_OPTION(token->cmd)) ++ { ++ if (push_argument(argc, argv, word)) ++ return MATCHER_EXCEED_ARGC_MAX; ++ } ++ ++ cmd_matcher_record_match(matcher, word_match, token); ++ ++ matcher->word_index++; ++ ++ /* A vararg token should consume all left over words as arguments */ ++ if (CMD_VARARG(token->cmd)) ++ while (cmd_matcher_words_left(matcher)) ++ { ++ word = cmd_matcher_get_word(matcher); ++ if (word && strlen(word)) ++ push_argument(argc, argv, word); ++ matcher->word_index++; + } +- return match_type; ++ ++ return MATCHER_OK; + } + +-/* Filter vector by command character with index. */ +-static enum match_type +-cmd_filter_by_string (char *command, vector v, unsigned int index) ++static enum matcher_rv ++cmd_matcher_match_multiple(struct cmd_matcher *matcher, ++ struct cmd_token *token, ++ int *argc, const char **argv) ++{ ++ enum match_type multiple_match; ++ unsigned int multiple_index; ++ const char *word; ++ const char *arg; ++ struct cmd_token *word_token; ++ enum match_type word_match; ++ ++ assert(token->type == TOKEN_MULTIPLE); ++ ++ multiple_match = no_match; ++ ++ if (!cmd_matcher_words_left(matcher)) ++ return MATCHER_INCOMPLETE; ++ ++ word = cmd_matcher_get_word(matcher); ++ for (multiple_index = 0; ++ multiple_index < vector_active(token->multiple); ++ multiple_index++) ++ { ++ word_token = vector_slot(token->multiple, multiple_index); ++ ++ word_match = cmd_word_match(word_token, matcher->filter, word); ++ if (word_match == no_match) ++ continue; ++ ++ cmd_matcher_record_match(matcher, word_match, word_token); ++ ++ if (word_match > multiple_match) ++ { ++ multiple_match = word_match; ++ arg = word; ++ } ++ /* To mimic the behavior of the old command implementation, we ++ * tolerate any ambiguities here :/ */ ++ } ++ ++ matcher->word_index++; ++ ++ if (multiple_match == no_match) ++ return MATCHER_NO_MATCH; ++ ++ if (push_argument(argc, argv, arg)) ++ return MATCHER_EXCEED_ARGC_MAX; ++ ++ return MATCHER_OK; ++} ++ ++static enum matcher_rv ++cmd_matcher_read_keywords(struct cmd_matcher *matcher, ++ struct cmd_token *token, ++ vector args_vector) + { + unsigned int i; +- const char *str; +- struct cmd_element *cmd_element; +- enum match_type match_type; +- vector descvec; +- struct desc *desc; ++ unsigned long keyword_mask; ++ unsigned int keyword_found; ++ enum match_type keyword_match; ++ enum match_type word_match; ++ vector keyword_vector; ++ struct cmd_token *word_token; ++ const char *word; ++ int keyword_argc; ++ const char **keyword_argv; ++ enum matcher_rv rv; ++ ++ keyword_mask = 0; ++ while (1) ++ { ++ if (!cmd_matcher_words_left(matcher)) ++ return MATCHER_OK; + +- match_type = no_match; ++ word = cmd_matcher_get_word(matcher); + +- /* If command and cmd_element string does not match set NULL to vector */ +- for (i = 0; i < vector_active (v); i++) +- if ((cmd_element = vector_slot (v, i)) != NULL) +- { +- /* If given index is bigger than max string vector of command, +- set NULL */ +- if (index >= vector_active (cmd_element->strvec)) +- vector_slot (v, i) = NULL; +- else +- { +- unsigned int j; +- int matched = 0; ++ keyword_found = -1; ++ keyword_match = no_match; ++ for (i = 0; i < vector_active(token->keyword); i++) ++ { ++ if (keyword_mask & (1 << i)) ++ continue; ++ ++ keyword_vector = vector_slot(token->keyword, i); ++ word_token = vector_slot(keyword_vector, 0); ++ ++ word_match = cmd_word_match(word_token, matcher->filter, word); ++ if (word_match == no_match) ++ continue; ++ ++ cmd_matcher_record_match(matcher, word_match, word_token); ++ ++ if (word_match > keyword_match) ++ { ++ keyword_match = word_match; ++ keyword_found = i; ++ } ++ else if (word_match == keyword_match) ++ { ++ if (matcher->word_index != matcher->index || args_vector) ++ return MATCHER_AMBIGUOUS; ++ } ++ } + +- descvec = vector_slot (cmd_element->strvec, index); ++ if (keyword_found == (unsigned int)-1) ++ return MATCHER_NO_MATCH; + +- for (j = 0; j < vector_active (descvec); j++) +- if ((desc = vector_slot (descvec, j))) +- { +- str = desc->cmd; ++ matcher->word_index++; + +- if (CMD_VARARG (str)) +- { +- if (match_type < vararg_match) +- match_type = vararg_match; +- matched++; +- } +- else if (CMD_RANGE (str)) +- { +- if (cmd_range_match (str, command)) +- { +- if (match_type < range_match) +- match_type = range_match; +- matched++; +- } +- } +-#ifdef HAVE_IPV6 +- else if (CMD_IPV6 (str)) +- { +- if (cmd_ipv6_match (command) == exact_match) +- { +- if (match_type < ipv6_match) +- match_type = ipv6_match; +- matched++; +- } +- } +- else if (CMD_IPV6_PREFIX (str)) +- { +- if (cmd_ipv6_prefix_match (command) == exact_match) +- { +- if (match_type < ipv6_prefix_match) +- match_type = ipv6_prefix_match; +- matched++; +- } +- } +-#endif /* HAVE_IPV6 */ +- else if (CMD_IPV4 (str)) +- { +- if (cmd_ipv4_match (command) == exact_match) +- { +- if (match_type < ipv4_match) +- match_type = ipv4_match; +- matched++; +- } +- } +- else if (CMD_IPV4_PREFIX (str)) +- { +- if (cmd_ipv4_prefix_match (command) == exact_match) +- { +- if (match_type < ipv4_prefix_match) +- match_type = ipv4_prefix_match; +- matched++; +- } +- } +- else if (CMD_OPTION (str) || CMD_VARIABLE (str)) +- { +- if (match_type < extend_match) +- match_type = extend_match; +- matched++; +- } +- else +- { +- if (strcmp (command, str) == 0) +- { +- match_type = exact_match; +- matched++; +- } +- } +- } +- if (!matched) +- vector_slot (v, i) = NULL; +- } ++ if (matcher->word_index > matcher->index) ++ return MATCHER_OK; ++ ++ keyword_mask |= (1 << keyword_found); ++ ++ if (args_vector) ++ { ++ keyword_argc = 0; ++ keyword_argv = XMALLOC(MTYPE_TMP, (CMD_ARGC_MAX + 1) * sizeof(char*)); ++ /* We use -1 as a marker for unused fields as NULL might be a valid value */ ++ for (i = 0; i < CMD_ARGC_MAX + 1; i++) ++ keyword_argv[i] = (void*)-1; ++ vector_set_index(args_vector, keyword_found, keyword_argv); ++ } ++ else ++ { ++ keyword_argv = NULL; ++ } ++ ++ keyword_vector = vector_slot(token->keyword, keyword_found); ++ /* the keyword itself is at 0. We are only interested in the arguments, ++ * so start counting at 1. */ ++ for (i = 1; i < vector_active(keyword_vector); i++) ++ { ++ word_token = vector_slot(keyword_vector, i); ++ ++ switch (word_token->type) ++ { ++ case TOKEN_TERMINAL: ++ rv = cmd_matcher_match_terminal(matcher, word_token, ++ &keyword_argc, keyword_argv); ++ break; ++ case TOKEN_MULTIPLE: ++ rv = cmd_matcher_match_multiple(matcher, word_token, ++ &keyword_argc, keyword_argv); ++ break; ++ case TOKEN_KEYWORD: ++ assert(!"Keywords should never be nested."); ++ break; ++ } ++ ++ if (MATCHER_ERROR(rv)) ++ return rv; ++ ++ if (matcher->word_index > matcher->index) ++ return MATCHER_OK; ++ } ++ } ++ /* not reached */ ++} ++ ++static enum matcher_rv ++cmd_matcher_build_keyword_args(struct cmd_matcher *matcher, ++ struct cmd_token *token, ++ int *argc, const char **argv, ++ vector keyword_args_vector) ++{ ++ unsigned int i, j; ++ const char **keyword_args; ++ vector keyword_vector; ++ struct cmd_token *word_token; ++ const char *arg; ++ enum matcher_rv rv; ++ ++ rv = MATCHER_OK; ++ ++ if (keyword_args_vector == NULL) ++ return rv; ++ ++ for (i = 0; i < vector_active(token->keyword); i++) ++ { ++ keyword_vector = vector_slot(token->keyword, i); ++ keyword_args = vector_lookup(keyword_args_vector, i); ++ ++ if (vector_active(keyword_vector) == 1) ++ { ++ /* this is a keyword without arguments */ ++ if (keyword_args) ++ { ++ word_token = vector_slot(keyword_vector, 0); ++ arg = word_token->cmd; ++ } ++ else ++ { ++ arg = NULL; ++ } ++ ++ if (push_argument(argc, argv, arg)) ++ rv = MATCHER_EXCEED_ARGC_MAX; ++ } ++ else ++ { ++ /* this is a keyword with arguments */ ++ if (keyword_args) ++ { ++ /* the keyword was present, so just fill in the arguments */ ++ for (j = 0; keyword_args[j] != (void*)-1; j++) ++ if (push_argument(argc, argv, keyword_args[j])) ++ rv = MATCHER_EXCEED_ARGC_MAX; ++ XFREE(MTYPE_TMP, keyword_args); ++ } ++ else ++ { ++ /* the keyword was not present, insert NULL for the arguments ++ * the keyword would have taken. */ ++ for (j = 1; j < vector_active(keyword_vector); j++) ++ { ++ word_token = vector_slot(keyword_vector, j); ++ if ((word_token->type == TOKEN_TERMINAL ++ && (CMD_VARARG(word_token->cmd) ++ || CMD_VARIABLE(word_token->cmd) ++ || CMD_OPTION(word_token->cmd))) ++ || word_token->type == TOKEN_MULTIPLE) ++ { ++ if (push_argument(argc, argv, NULL)) ++ rv = MATCHER_EXCEED_ARGC_MAX; ++ } ++ } ++ } ++ } ++ } ++ vector_free(keyword_args_vector); ++ return rv; ++} ++ ++static enum matcher_rv ++cmd_matcher_match_keyword(struct cmd_matcher *matcher, ++ struct cmd_token *token, ++ int *argc, const char **argv) ++{ ++ vector keyword_args_vector; ++ enum matcher_rv reader_rv; ++ enum matcher_rv builder_rv; ++ ++ assert(token->type == TOKEN_KEYWORD); ++ ++ if (argc && argv) ++ keyword_args_vector = vector_init(VECTOR_MIN_SIZE); ++ else ++ keyword_args_vector = NULL; ++ ++ reader_rv = cmd_matcher_read_keywords(matcher, token, keyword_args_vector); ++ builder_rv = cmd_matcher_build_keyword_args(matcher, token, argc, ++ argv, keyword_args_vector); ++ /* keyword_args_vector is consumed by cmd_matcher_build_keyword_args */ ++ ++ if (!MATCHER_ERROR(reader_rv) && MATCHER_ERROR(builder_rv)) ++ return builder_rv; ++ ++ return reader_rv; ++} ++ ++static void ++cmd_matcher_init(struct cmd_matcher *matcher, ++ struct cmd_element *cmd, ++ enum filter_type filter, ++ vector vline, ++ unsigned int index, ++ enum match_type *match_type, ++ vector *match) ++{ ++ memset(matcher, 0, sizeof(*matcher)); ++ ++ matcher->cmd = cmd; ++ matcher->filter = filter; ++ matcher->vline = vline; ++ matcher->index = index; ++ ++ matcher->match_type = match_type; ++ if (matcher->match_type) ++ *matcher->match_type = no_match; ++ matcher->match = match; ++ ++ matcher->word_index = 0; ++} ++ ++static enum matcher_rv ++cmd_element_match(struct cmd_element *cmd_element, ++ enum filter_type filter, ++ vector vline, ++ unsigned int index, ++ enum match_type *match_type, ++ vector *match, ++ int *argc, ++ const char **argv) ++{ ++ struct cmd_matcher matcher; ++ unsigned int token_index; ++ enum matcher_rv rv; ++ ++ cmd_matcher_init(&matcher, cmd_element, filter, ++ vline, index, match_type, match); ++ ++ if (argc != NULL) ++ *argc = 0; ++ ++ for (token_index = 0; ++ token_index < vector_active(cmd_element->tokens); ++ token_index++) ++ { ++ struct cmd_token *token = vector_slot(cmd_element->tokens, token_index); ++ ++ switch (token->type) ++ { ++ case TOKEN_TERMINAL: ++ rv = cmd_matcher_match_terminal(&matcher, token, argc, argv); ++ break; ++ case TOKEN_MULTIPLE: ++ rv = cmd_matcher_match_multiple(&matcher, token, argc, argv); ++ break; ++ case TOKEN_KEYWORD: ++ rv = cmd_matcher_match_keyword(&matcher, token, argc, argv); ++ } ++ ++ if (MATCHER_ERROR(rv)) ++ return rv; ++ ++ if (matcher.word_index > index) ++ return MATCHER_OK; ++ } ++ ++ /* return MATCHER_COMPLETE if all words were consumed */ ++ if (matcher.word_index >= vector_active(vline)) ++ return MATCHER_COMPLETE; ++ ++ /* return MATCHER_COMPLETE also if only an empty word is left. */ ++ if (matcher.word_index == vector_active(vline) - 1 ++ && (!vector_slot(vline, matcher.word_index) ++ || !strlen((char*)vector_slot(vline, matcher.word_index)))) ++ return MATCHER_COMPLETE; ++ ++ return MATCHER_NO_MATCH; /* command is too long to match */ ++} ++ ++/** ++ * Filter a given vector of commands against a given commandline and ++ * calculate possible completions. ++ * ++ * @param commands A vector of struct cmd_element*. Commands that don't ++ * match against the given command line will be overwritten ++ * with NULL in that vector. ++ * @param filter Either FILTER_RELAXED or FILTER_STRICT. This basically ++ * determines how incomplete commands are handled, compare with ++ * cmd_word_match for details. ++ * @param vline A vector of char* containing the tokenized commandline. ++ * @param index Only match up to the given token of the commandline. ++ * @param match_type Record the type of the best match here. ++ * @param matches Record the matches here. For each cmd_element in the commands ++ * vector, a match vector will be created in the matches vector. ++ * That vector will contain all struct command_token* of the ++ * cmd_element which matched against the given vline at the given ++ * index. ++ * @return A code specifying if an error occured. If all went right, it's ++ * CMD_SUCCESS. ++ */ ++static int ++cmd_vector_filter(vector commands, ++ enum filter_type filter, ++ vector vline, ++ unsigned int index, ++ enum match_type *match_type, ++ vector *matches) ++{ ++ unsigned int i; ++ struct cmd_element *cmd_element; ++ enum match_type best_match; ++ enum match_type element_match; ++ enum matcher_rv matcher_rv; ++ ++ best_match = no_match; ++ *matches = vector_init(VECTOR_MIN_SIZE); ++ ++ for (i = 0; i < vector_active (commands); i++) ++ if ((cmd_element = vector_slot (commands, i)) != NULL) ++ { ++ vector_set_index(*matches, i, NULL); ++ matcher_rv = cmd_element_match(cmd_element, filter, ++ vline, index, ++ &element_match, ++ (vector*)&vector_slot(*matches, i), ++ NULL, NULL); ++ if (MATCHER_ERROR(matcher_rv)) ++ { ++ vector_slot(commands, i) = NULL; ++ if (matcher_rv == MATCHER_AMBIGUOUS) ++ return CMD_ERR_AMBIGUOUS; ++ if (matcher_rv == MATCHER_EXCEED_ARGC_MAX) ++ return CMD_ERR_EXEED_ARGC_MAX; ++ } ++ else if (element_match > best_match) ++ { ++ best_match = element_match; ++ } + } +- return match_type; ++ *match_type = best_match; ++ return CMD_SUCCESS; ++} ++ ++/** ++ * Check whether a given commandline is complete if used for a specific ++ * cmd_element. ++ * ++ * @param cmd_element A cmd_element against which the commandline should be ++ * checked. ++ * @param vline The tokenized commandline. ++ * @return 1 if the given commandline is complete, 0 otherwise. ++ */ ++static int ++cmd_is_complete(struct cmd_element *cmd_element, ++ vector vline) ++{ ++ enum matcher_rv rv; ++ ++ rv = cmd_element_match(cmd_element, ++ FILTER_RELAXED, ++ vline, -1, ++ NULL, NULL, ++ NULL, NULL); ++ return (rv == MATCHER_COMPLETE); ++} ++ ++/** ++ * Parse a given commandline and construct a list of arguments for the ++ * given command_element. ++ * ++ * @param cmd_element The cmd_element for which we want to construct arguments. ++ * @param vline The tokenized commandline. ++ * @param argc Where to store the argument count. ++ * @param argv Where to store the argument list. Should be at least ++ * CMD_ARGC_MAX elements long. ++ * @return CMD_SUCCESS if everything went alright, an error otherwise. ++ */ ++static int ++cmd_parse(struct cmd_element *cmd_element, ++ vector vline, ++ int *argc, const char **argv) ++{ ++ enum matcher_rv rv = cmd_element_match(cmd_element, ++ FILTER_RELAXED, ++ vline, -1, ++ NULL, NULL, ++ argc, argv); ++ switch (rv) ++ { ++ case MATCHER_COMPLETE: ++ return CMD_SUCCESS; ++ ++ case MATCHER_NO_MATCH: ++ return CMD_ERR_NO_MATCH; ++ ++ case MATCHER_AMBIGUOUS: ++ return CMD_ERR_AMBIGUOUS; ++ ++ case MATCHER_EXCEED_ARGC_MAX: ++ return CMD_ERR_EXEED_ARGC_MAX; ++ ++ default: ++ return CMD_ERR_INCOMPLETE; ++ } + } + + /* Check ambiguous match */ + static int +-is_cmd_ambiguous (char *command, vector v, int index, enum match_type type) ++is_cmd_ambiguous (vector cmd_vector, ++ const char *command, ++ vector matches, ++ enum match_type type) + { + unsigned int i; + unsigned int j; + const char *str = NULL; +- struct cmd_element *cmd_element; + const char *matched = NULL; +- vector descvec; +- struct desc *desc; ++ vector match_vector; ++ struct cmd_token *cmd_token; + +- for (i = 0; i < vector_active (v); i++) +- if ((cmd_element = vector_slot (v, i)) != NULL) ++ if (command == NULL) ++ command = ""; ++ ++ for (i = 0; i < vector_active (matches); i++) ++ if ((match_vector = vector_slot (matches, i)) != NULL) + { + int match = 0; + +- descvec = vector_slot (cmd_element->strvec, index); +- +- for (j = 0; j < vector_active (descvec); j++) +- if ((desc = vector_slot (descvec, j))) ++ for (j = 0; j < vector_active (match_vector); j++) ++ if ((cmd_token = vector_slot (match_vector, j)) != NULL) + { + enum match_type ret; +- +- str = desc->cmd; ++ ++ assert(cmd_token->type == TOKEN_TERMINAL); ++ if (cmd_token->type != TOKEN_TERMINAL) ++ continue; ++ ++ str = cmd_token->cmd; + + switch (type) + { +@@ -1371,7 +1914,7 @@ is_cmd_ambiguous (char *command, vector v, int index, enum match_type type) + } + } + if (!match) +- vector_slot (v, i) = NULL; ++ vector_slot (cmd_vector, i) = NULL; + } + return 0; + } +@@ -1461,8 +2004,12 @@ cmd_entry_function_desc (const char *src, const char *dst) + return NULL; + } + +-/* Check same string element existence. If it isn't there return +- 1. */ ++/** ++ * Check whether a string is already present in a vector of strings. ++ * @param v A vector of char*. ++ * @param str A char*. ++ * @return 0 if str is already present in the vector, 1 otherwise. ++ */ + static int + cmd_unique_string (vector v, const char *str) + { +@@ -1476,19 +2023,25 @@ cmd_unique_string (vector v, const char *str) + return 1; + } + +-/* Compare string to description vector. If there is same string +- return 1 else return 0. */ ++/** ++ * Check whether a struct cmd_token matching a given string is already ++ * present in a vector of struct cmd_token. ++ * @param v A vector of struct cmd_token*. ++ * @param str A char* which should be searched for. ++ * @return 0 if there is a struct cmd_token* with its cmd matching str, ++ * 1 otherwise. ++ */ + static int + desc_unique_string (vector v, const char *str) + { + unsigned int i; +- struct desc *desc; ++ struct cmd_token *token; + + for (i = 0; i < vector_active (v); i++) +- if ((desc = vector_slot (v, i)) != NULL) +- if (strcmp (desc->cmd, str) == 0) +- return 1; +- return 0; ++ if ((token = vector_slot (v, i)) != NULL) ++ if (strcmp (token->cmd, str) == 0) ++ return 0; ++ return 1; + } + + static int +@@ -1504,6 +2057,35 @@ cmd_try_do_shortcut (enum node_type node, char* first_word) { + return 0; + } + ++static void ++cmd_matches_free(vector *matches) ++{ ++ unsigned int i; ++ vector cmd_matches; ++ ++ for (i = 0; i < vector_active(*matches); i++) ++ if ((cmd_matches = vector_slot(*matches, i)) != NULL) ++ vector_free(cmd_matches); ++ vector_free(*matches); ++ *matches = NULL; ++} ++ ++static int ++cmd_describe_cmp(const void *a, const void *b) ++{ ++ const struct cmd_token *first = *(struct cmd_token * const *)a; ++ const struct cmd_token *second = *(struct cmd_token * const *)b; ++ ++ return strcmp(first->cmd, second->cmd); ++} ++ ++static void ++cmd_describe_sort(vector matchvec) ++{ ++ qsort(matchvec->index, vector_active(matchvec), ++ sizeof(void*), cmd_describe_cmp); ++} ++ + /* '?' describe command support. */ + static vector + cmd_describe_command_real (vector vline, struct vty *vty, int *status) +@@ -1517,6 +2099,8 @@ cmd_describe_command_real (vector vline, struct vty *vty, int *status) + int ret; + enum match_type match; + char *command; ++ vector matches = NULL; ++ vector match_vector; + + /* Set index. */ + if (vector_active (vline) == 0) +@@ -1524,111 +2108,121 @@ cmd_describe_command_real (vector vline, struct vty *vty, int *status) + *status = CMD_ERR_NO_MATCH; + return NULL; + } +- else +- index = vector_active (vline) - 1; +- ++ ++ index = vector_active (vline) - 1; ++ + /* Make copy vector of current node's command vector. */ + cmd_vector = vector_copy (cmd_node_vector (cmdvec, vty->node)); + + /* Prepare match vector */ + matchvec = vector_init (INIT_MATCHVEC_SIZE); + +- /* Filter commands. */ +- /* Only words precedes current word will be checked in this loop. */ +- for (i = 0; i < index; i++) +- if ((command = vector_slot (vline, i))) +- { +- match = cmd_filter_by_completion (command, cmd_vector, i); +- +- if (match == vararg_match) +- { +- struct cmd_element *cmd_element; +- vector descvec; +- unsigned int j, k; ++ /* Filter commands and build a list how they could possibly continue. */ ++ for (i = 0; i <= index; i++) ++ { ++ command = vector_slot (vline, i); + +- for (j = 0; j < vector_active (cmd_vector); j++) +- if ((cmd_element = vector_slot (cmd_vector, j)) != NULL +- && (vector_active (cmd_element->strvec))) +- { +- descvec = vector_slot (cmd_element->strvec, +- vector_active (cmd_element->strvec) - 1); +- for (k = 0; k < vector_active (descvec); k++) +- { +- struct desc *desc = vector_slot (descvec, k); +- vector_set (matchvec, desc); +- } +- } +- +- vector_set (matchvec, &desc_cr); +- vector_free (cmd_vector); ++ if (matches) ++ cmd_matches_free(&matches); + +- return matchvec; +- } ++ ret = cmd_vector_filter(cmd_vector, ++ FILTER_RELAXED, ++ vline, i, ++ &match, ++ &matches); + +- if ((ret = is_cmd_ambiguous (command, cmd_vector, i, match)) == 1) +- { +- vector_free (cmd_vector); +- vector_free (matchvec); +- *status = CMD_ERR_AMBIGUOUS; +- return NULL; +- } +- else if (ret == 2) +- { +- vector_free (cmd_vector); +- vector_free (matchvec); +- *status = CMD_ERR_NO_MATCH; +- return NULL; +- } +- } ++ if (ret != CMD_SUCCESS) ++ { ++ vector_free (cmd_vector); ++ vector_free (matchvec); ++ cmd_matches_free(&matches); ++ *status = ret; ++ return NULL; ++ } + +- /* Prepare match vector */ +- /* matchvec = vector_init (INIT_MATCHVEC_SIZE); */ ++ /* The last match may well be ambigious, so break here */ ++ if (i == index) ++ break; ++ ++ if (match == vararg_match) ++ { ++ /* We found a vararg match - so we can throw out the current matches here ++ * and don't need to continue checking the command input */ ++ unsigned int j, k; ++ ++ for (j = 0; j < vector_active (matches); j++) ++ if ((match_vector = vector_slot (matches, j)) != NULL) ++ for (k = 0; k < vector_active (match_vector); k++) ++ { ++ struct cmd_token *token = vector_slot (match_vector, k); ++ vector_set (matchvec, token); ++ } ++ ++ *status = CMD_SUCCESS; ++ vector_set(matchvec, &token_cr); ++ vector_free (cmd_vector); ++ cmd_matches_free(&matches); ++ cmd_describe_sort(matchvec); ++ return matchvec; ++ } + +- /* Make sure that cmd_vector is filtered based on current word */ +- command = vector_slot (vline, index); +- if (command) +- match = cmd_filter_by_completion (command, cmd_vector, index); ++ ret = is_cmd_ambiguous(cmd_vector, command, matches, match); ++ if (ret == 1) ++ { ++ vector_free (cmd_vector); ++ vector_free (matchvec); ++ cmd_matches_free(&matches); ++ *status = CMD_ERR_AMBIGUOUS; ++ return NULL; ++ } ++ else if (ret == 2) ++ { ++ vector_free (cmd_vector); ++ vector_free (matchvec); ++ cmd_matches_free(&matches); ++ *status = CMD_ERR_NO_MATCH; ++ return NULL; ++ } ++ } + + /* Make description vector. */ +- for (i = 0; i < vector_active (cmd_vector); i++) ++ for (i = 0; i < vector_active (matches); i++) + if ((cmd_element = vector_slot (cmd_vector, i)) != NULL) + { +- vector strvec = cmd_element->strvec; ++ unsigned int j; ++ const char *last_word; ++ vector vline_trimmed; + +- /* if command is NULL, index may be equal to vector_active */ +- if (command && index >= vector_active (strvec)) +- vector_slot (cmd_vector, i) = NULL; +- else +- { +- /* Check if command is completed. */ +- if (command == NULL && index == vector_active (strvec)) +- { +- if (!desc_unique_string (matchvec, command_cr)) +- vector_set (matchvec, &desc_cr); +- } +- else +- { +- unsigned int j; +- vector descvec = vector_slot (strvec, index); +- struct desc *desc; +- +- for (j = 0; j < vector_active (descvec); j++) +- if ((desc = vector_slot (descvec, j))) +- { +- const char *string; +- +- string = cmd_entry_function_desc (command, desc->cmd); +- if (string) +- { +- /* Uniqueness check */ +- if (!desc_unique_string (matchvec, string)) +- vector_set (matchvec, desc); +- } +- } +- } +- } ++ last_word = vector_slot(vline, vector_active(vline) - 1); ++ if (last_word == NULL || !strlen(last_word)) ++ { ++ vline_trimmed = vector_copy(vline); ++ vector_unset(vline_trimmed, vector_active(vline_trimmed) - 1); ++ ++ if (cmd_is_complete(cmd_element, vline_trimmed) ++ && desc_unique_string(matchvec, command_cr)) ++ { ++ if (match != vararg_match) ++ vector_set(matchvec, &token_cr); ++ } ++ ++ vector_free(vline_trimmed); ++ } ++ ++ match_vector = vector_slot (matches, i); ++ if (match_vector) ++ for (j = 0; j < vector_active(match_vector); j++) ++ { ++ struct cmd_token *token = vector_slot(match_vector, j); ++ const char *string; ++ ++ string = cmd_entry_function_desc(command, token->cmd); ++ if (string && desc_unique_string(matchvec, string)) ++ vector_set(matchvec, token); ++ } + } + vector_free (cmd_vector); ++ cmd_matches_free(&matches); + + if (vector_slot (matchvec, 0) == NULL) + { +@@ -1638,6 +2232,7 @@ cmd_describe_command_real (vector vline, struct vty *vty, int *status) + } + + *status = CMD_SUCCESS; ++ cmd_describe_sort(matchvec); + return matchvec; + } + +@@ -1708,6 +2303,31 @@ cmd_lcd (char **matched) + return lcd; + } + ++static int ++cmd_complete_cmp(const void *a, const void *b) ++{ ++ const char *first = *(char * const *)a; ++ const char *second = *(char * const *)b; ++ ++ if (!first) ++ { ++ if (!second) ++ return 0; ++ return 1; ++ } ++ if (!second) ++ return -1; ++ ++ return strcmp(first, second); ++} ++ ++static void ++cmd_complete_sort(vector matchvec) ++{ ++ qsort(matchvec->index, vector_active(matchvec), ++ sizeof(void*), cmd_complete_cmp); ++} ++ + /* Command line completion support. */ + static char ** + cmd_complete_command_real (vector vline, struct vty *vty, int *status) +@@ -1716,13 +2336,13 @@ cmd_complete_command_real (vector vline, struct vty *vty, int *status) + vector cmd_vector = vector_copy (cmd_node_vector (cmdvec, vty->node)); + #define INIT_MATCHVEC_SIZE 10 + vector matchvec; +- struct cmd_element *cmd_element; + unsigned int index; + char **match_str; +- struct desc *desc; +- vector descvec; ++ struct cmd_token *token; + char *command; + int lcd; ++ vector matches = NULL; ++ vector match_vector; + + if (vector_active (vline) == 0) + { +@@ -1733,66 +2353,80 @@ cmd_complete_command_real (vector vline, struct vty *vty, int *status) + else + index = vector_active (vline) - 1; + +- /* First, filter by preceeding command string */ +- for (i = 0; i < index; i++) +- if ((command = vector_slot (vline, i))) +- { +- enum match_type match; +- int ret; ++ /* First, filter by command string */ ++ for (i = 0; i <= index; i++) ++ { ++ command = vector_slot (vline, i); ++ enum match_type match; ++ int ret; + +- /* First try completion match, if there is exactly match return 1 */ +- match = cmd_filter_by_completion (command, cmd_vector, i); ++ if (matches) ++ cmd_matches_free(&matches); + +- /* If there is exact match then filter ambiguous match else check +- ambiguousness. */ +- if ((ret = is_cmd_ambiguous (command, cmd_vector, i, match)) == 1) +- { +- vector_free (cmd_vector); +- *status = CMD_ERR_AMBIGUOUS; +- return NULL; +- } +- /* ++ /* First try completion match, if there is exactly match return 1 */ ++ ret = cmd_vector_filter(cmd_vector, ++ FILTER_RELAXED, ++ vline, i, ++ &match, ++ &matches); ++ ++ if (ret != CMD_SUCCESS) ++ { ++ vector_free(cmd_vector); ++ cmd_matches_free(&matches); ++ *status = ret; ++ return NULL; ++ } ++ ++ /* Break here - the completion mustn't be checked to be non-ambiguous */ ++ if (i == index) ++ break; ++ ++ /* If there is exact match then filter ambiguous match else check ++ ambiguousness. */ ++ ret = is_cmd_ambiguous (cmd_vector, command, matches, match); ++ if (ret == 1) ++ { ++ vector_free (cmd_vector); ++ cmd_matches_free(&matches); ++ *status = CMD_ERR_AMBIGUOUS; ++ return NULL; ++ } ++ /* + else if (ret == 2) + { + vector_free (cmd_vector); ++ cmd_matches_free(&matches); + *status = CMD_ERR_NO_MATCH; + return NULL; + } + */ +- } ++ } + + /* Prepare match vector. */ + matchvec = vector_init (INIT_MATCHVEC_SIZE); + +- /* Now we got into completion */ +- for (i = 0; i < vector_active (cmd_vector); i++) +- if ((cmd_element = vector_slot (cmd_vector, i))) ++ /* Build the possible list of continuations into a list of completions */ ++ for (i = 0; i < vector_active (matches); i++) ++ if ((match_vector = vector_slot (matches, i))) + { + const char *string; +- vector strvec = cmd_element->strvec; +- +- /* Check field length */ +- if (index >= vector_active (strvec)) +- vector_slot (cmd_vector, i) = NULL; +- else +- { +- unsigned int j; ++ unsigned int j; + +- descvec = vector_slot (strvec, index); +- for (j = 0; j < vector_active (descvec); j++) +- if ((desc = vector_slot (descvec, j))) ++ for (j = 0; j < vector_active (match_vector); j++) ++ if ((token = vector_slot (match_vector, j))) + { + if ((string = + cmd_entry_function (vector_slot (vline, index), +- desc->cmd))) ++ token->cmd))) + if (cmd_unique_string (matchvec, string)) + vector_set (matchvec, XSTRDUP (MTYPE_TMP, string)); + } +- } + } + + /* We don't need cmd_vector any more. */ + vector_free (cmd_vector); ++ cmd_matches_free(&matches); + + /* No matched command */ + if (vector_slot (matchvec, 0) == NULL) +@@ -1832,7 +2466,7 @@ cmd_complete_command_real (vector vline, struct vty *vty, int *status) + { + char *lcdstr; + +- lcdstr = XMALLOC (MTYPE_STRVEC, lcd + 1); ++ lcdstr = XMALLOC (MTYPE_TMP, lcd + 1); + memcpy (lcdstr, matchvec->index[0], lcd); + lcdstr[lcd] = '\0'; + +@@ -1842,7 +2476,7 @@ cmd_complete_command_real (vector vline, struct vty *vty, int *status) + for (i = 0; i < vector_active (matchvec); i++) + { + if (vector_slot (matchvec, i)) +- XFREE (MTYPE_STRVEC, vector_slot (matchvec, i)); ++ XFREE (MTYPE_TMP, vector_slot (matchvec, i)); + } + vector_free (matchvec); + +@@ -1859,6 +2493,7 @@ cmd_complete_command_real (vector vline, struct vty *vty, int *status) + } + + match_str = (char **) matchvec->index; ++ cmd_complete_sort(matchvec); + vector_only_wrapper_free (matchvec); + *status = CMD_COMPLETE_LIST_MATCH; + return match_str; +@@ -1927,7 +2562,9 @@ node_parent ( enum node_type node ) + + /* Execute command by argument vline vector. */ + static int +-cmd_execute_command_real (vector vline, struct vty *vty, ++cmd_execute_command_real (vector vline, ++ enum filter_type filter, ++ struct vty *vty, + struct cmd_element **cmd) + { + unsigned int i; +@@ -1939,35 +2576,48 @@ cmd_execute_command_real (vector vline, struct vty *vty, + int argc; + const char *argv[CMD_ARGC_MAX]; + enum match_type match = 0; +- int varflag; + char *command; ++ int ret; ++ vector matches; + + /* Make copy of command elements. */ + cmd_vector = vector_copy (cmd_node_vector (cmdvec, vty->node)); + + for (index = 0; index < vector_active (vline); index++) +- if ((command = vector_slot (vline, index))) +- { +- int ret; +- +- match = cmd_filter_by_completion (command, cmd_vector, index); ++ { ++ command = vector_slot (vline, index); ++ ret = cmd_vector_filter(cmd_vector, ++ filter, ++ vline, index, ++ &match, ++ &matches); ++ ++ if (ret != CMD_SUCCESS) ++ { ++ cmd_matches_free(&matches); ++ return ret; ++ } + +- if (match == vararg_match) ++ if (match == vararg_match) ++ { ++ cmd_matches_free(&matches); + break; +- +- ret = is_cmd_ambiguous (command, cmd_vector, index, match); ++ } + +- if (ret == 1) +- { +- vector_free (cmd_vector); +- return CMD_ERR_AMBIGUOUS; +- } +- else if (ret == 2) +- { +- vector_free (cmd_vector); +- return CMD_ERR_NO_MATCH; +- } +- } ++ ret = is_cmd_ambiguous (cmd_vector, command, matches, match); ++ cmd_matches_free(&matches); ++ ++ if (ret == 1) ++ { ++ vector_free(cmd_vector); ++ return CMD_ERR_AMBIGUOUS; ++ } ++ else if (ret == 2) ++ { ++ vector_free(cmd_vector); ++ return CMD_ERR_NO_MATCH; ++ } ++ } + + /* Check matched count. */ + matched_element = NULL; +@@ -1977,12 +2627,9 @@ cmd_execute_command_real (vector vline, struct vty *vty, + for (i = 0; i < vector_active (cmd_vector); i++) + if ((cmd_element = vector_slot (cmd_vector, i))) + { +- if (match == vararg_match || index >= cmd_element->cmdsize) ++ if (cmd_is_complete(cmd_element, vline)) + { + matched_element = cmd_element; +-#if 0 +- printf ("DEBUG: %s\n", cmd_element->string); +-#endif + matched_count++; + } + else +@@ -2006,35 +2653,9 @@ cmd_execute_command_real (vector vline, struct vty *vty, + if (matched_count > 1) + return CMD_ERR_AMBIGUOUS; + +- /* Argument treatment */ +- varflag = 0; +- argc = 0; +- +- for (i = 0; i < vector_active (vline); i++) +- { +- if (varflag) +- argv[argc++] = vector_slot (vline, i); +- else +- { +- vector descvec = vector_slot (matched_element->strvec, i); +- +- if (vector_active (descvec) == 1) +- { +- struct desc *desc = vector_slot (descvec, 0); +- +- if (CMD_VARARG (desc->cmd)) +- varflag = 1; +- +- if (varflag || CMD_VARIABLE (desc->cmd) || CMD_OPTION (desc->cmd)) +- argv[argc++] = vector_slot (vline, i); +- } +- else +- argv[argc++] = vector_slot (vline, i); +- } +- +- if (argc >= CMD_ARGC_MAX) +- return CMD_ERR_EXEED_ARGC_MAX; +- } ++ ret = cmd_parse(matched_element, vline, &argc, argv); ++ if (ret != CMD_SUCCESS) ++ return ret; + + /* For vtysh execution. */ + if (cmd) +@@ -2047,6 +2668,21 @@ cmd_execute_command_real (vector vline, struct vty *vty, + return (*matched_element->func) (matched_element, vty, argc, argv); + } + ++/** ++ * Execute a given command, handling things like "do ..." and checking ++ * whether the given command might apply at a parent node if doesn't ++ * apply for the current node. ++ * ++ * @param vline Command line input, vector of char* where each element is ++ * one input token. ++ * @param vty The vty context in which the command should be executed. ++ * @param cmd Pointer where the struct cmd_element of the matched command ++ * will be stored, if any. May be set to NULL if this info is ++ * not needed. ++ * @param vtysh If set != 0, don't lookup the command at parent nodes. ++ * @return The status of the command that has been executed or an error code ++ * as to why no command could be executed. ++ */ + int + cmd_execute_command (vector vline, struct vty *vty, struct cmd_element **cmd, + int vtysh) { +@@ -2070,7 +2706,7 @@ cmd_execute_command (vector vline, struct vty *vty, struct cmd_element **cmd, + vector_set_index (shifted_vline, index-1, vector_lookup(vline, index)); + } + +- ret = cmd_execute_command_real (shifted_vline, vty, cmd); ++ ret = cmd_execute_command_real (shifted_vline, FILTER_RELAXED, vty, cmd); + + vector_free(shifted_vline); + vty->node = onode; +@@ -2078,7 +2714,7 @@ cmd_execute_command (vector vline, struct vty *vty, struct cmd_element **cmd, + } + + +- saved_ret = ret = cmd_execute_command_real (vline, vty, cmd); ++ saved_ret = ret = cmd_execute_command_real (vline, FILTER_RELAXED, vty, cmd); + + if (vtysh) + return saved_ret; +@@ -2089,7 +2725,7 @@ cmd_execute_command (vector vline, struct vty *vty, struct cmd_element **cmd, + { + try_node = node_parent(try_node); + vty->node = try_node; +- ret = cmd_execute_command_real (vline, vty, cmd); ++ ret = cmd_execute_command_real (vline, FILTER_RELAXED, vty, cmd); + tried = 1; + if (ret == CMD_SUCCESS || ret == CMD_WARNING) + { +@@ -2104,123 +2740,24 @@ cmd_execute_command (vector vline, struct vty *vty, struct cmd_element **cmd, + return saved_ret; + } + +-/* Execute command by argument readline. */ ++/** ++ * Execute a given command, matching it strictly against the current node. ++ * This mode is used when reading config files. ++ * ++ * @param vline Command line input, vector of char* where each element is ++ * one input token. ++ * @param vty The vty context in which the command should be executed. ++ * @param cmd Pointer where the struct cmd_element* of the matched command ++ * will be stored, if any. May be set to NULL if this info is ++ * not needed. ++ * @return The status of the command that has been executed or an error code ++ * as to why no command could be executed. ++ */ + int + cmd_execute_command_strict (vector vline, struct vty *vty, + struct cmd_element **cmd) + { +- unsigned int i; +- unsigned int index; +- vector cmd_vector; +- struct cmd_element *cmd_element; +- struct cmd_element *matched_element; +- unsigned int matched_count, incomplete_count; +- int argc; +- const char *argv[CMD_ARGC_MAX]; +- int varflag; +- enum match_type match = 0; +- char *command; +- +- /* Make copy of command element */ +- cmd_vector = vector_copy (cmd_node_vector (cmdvec, vty->node)); +- +- for (index = 0; index < vector_active (vline); index++) +- if ((command = vector_slot (vline, index))) +- { +- int ret; +- +- match = cmd_filter_by_string (vector_slot (vline, index), +- cmd_vector, index); +- +- /* If command meets '.VARARG' then finish matching. */ +- if (match == vararg_match) +- break; +- +- ret = is_cmd_ambiguous (command, cmd_vector, index, match); +- if (ret == 1) +- { +- vector_free (cmd_vector); +- return CMD_ERR_AMBIGUOUS; +- } +- if (ret == 2) +- { +- vector_free (cmd_vector); +- return CMD_ERR_NO_MATCH; +- } +- } +- +- /* Check matched count. */ +- matched_element = NULL; +- matched_count = 0; +- incomplete_count = 0; +- for (i = 0; i < vector_active (cmd_vector); i++) +- if (vector_slot (cmd_vector, i) != NULL) +- { +- cmd_element = vector_slot (cmd_vector, i); +- +- if (match == vararg_match || index >= cmd_element->cmdsize) +- { +- matched_element = cmd_element; +- matched_count++; +- } +- else +- incomplete_count++; +- } +- +- /* Finish of using cmd_vector. */ +- vector_free (cmd_vector); +- +- /* To execute command, matched_count must be 1. */ +- if (matched_count == 0) +- { +- if (incomplete_count) +- return CMD_ERR_INCOMPLETE; +- else +- return CMD_ERR_NO_MATCH; +- } +- +- if (matched_count > 1) +- return CMD_ERR_AMBIGUOUS; +- +- /* Argument treatment */ +- varflag = 0; +- argc = 0; +- +- for (i = 0; i < vector_active (vline); i++) +- { +- if (varflag) +- argv[argc++] = vector_slot (vline, i); +- else +- { +- vector descvec = vector_slot (matched_element->strvec, i); +- +- if (vector_active (descvec) == 1) +- { +- struct desc *desc = vector_slot (descvec, 0); +- +- if (CMD_VARARG (desc->cmd)) +- varflag = 1; +- +- if (varflag || CMD_VARIABLE (desc->cmd) || CMD_OPTION (desc->cmd)) +- argv[argc++] = vector_slot (vline, i); +- } +- else +- argv[argc++] = vector_slot (vline, i); +- } +- +- if (argc >= CMD_ARGC_MAX) +- return CMD_ERR_EXEED_ARGC_MAX; +- } +- +- /* For vtysh execution. */ +- if (cmd) +- *cmd = matched_element; +- +- if (matched_element->daemon) +- return CMD_SUCCESS_DAEMON; +- +- /* Now execute matched command */ +- return (*matched_element->func) (matched_element, vty, argc, argv); ++ return cmd_execute_command_real(vline, FILTER_STRICT, vty, cmd); + } + + /* Configration make from file. */ +@@ -3461,9 +3998,10 @@ install_default (enum node_type node) + void + cmd_init (int terminal) + { +- command_cr = XSTRDUP(MTYPE_STRVEC, "<cr>"); +- desc_cr.cmd = command_cr; +- desc_cr.str = XSTRDUP(MTYPE_STRVEC, ""); ++ command_cr = XSTRDUP(MTYPE_CMD_TOKENS, "<cr>"); ++ token_cr.type = TOKEN_TERMINAL; ++ token_cr.cmd = command_cr; ++ token_cr.desc = XSTRDUP(MTYPE_CMD_TOKENS, ""); + + /* Allocate initial top vector of commands. */ + cmdvec = vector_init (VECTOR_MIN_SIZE); +@@ -3584,14 +4122,61 @@ cmd_init (int terminal) + srand(time(NULL)); + } + ++static void ++cmd_terminate_token(struct cmd_token *token) ++{ ++ unsigned int i, j; ++ vector keyword_vect; ++ ++ if (token->multiple) ++ { ++ for (i = 0; i < vector_active(token->multiple); i++) ++ cmd_terminate_token(vector_slot(token->multiple, i)); ++ vector_free(token->multiple); ++ token->multiple = NULL; ++ } ++ ++ if (token->keyword) ++ { ++ for (i = 0; i < vector_active(token->keyword); i++) ++ { ++ keyword_vect = vector_slot(token->keyword, i); ++ for (j = 0; j < vector_active(keyword_vect); j++) ++ cmd_terminate_token(vector_slot(keyword_vect, j)); ++ vector_free(keyword_vect); ++ } ++ vector_free(token->keyword); ++ token->keyword = NULL; ++ } ++ ++ XFREE(MTYPE_CMD_TOKENS, token->cmd); ++ XFREE(MTYPE_CMD_TOKENS, token->desc); ++ ++ XFREE(MTYPE_CMD_TOKENS, token); ++} ++ ++static void ++cmd_terminate_element(struct cmd_element *cmd) ++{ ++ unsigned int i; ++ ++ if (cmd->tokens == NULL) ++ return; ++ ++ for (i = 0; i < vector_active(cmd->tokens); i++) ++ cmd_terminate_token(vector_slot(cmd->tokens, i)); ++ ++ vector_free(cmd->tokens); ++ cmd->tokens = NULL; ++} ++ + void + cmd_terminate () + { +- unsigned int i, j, k, l; ++ unsigned int i, j; + struct cmd_node *cmd_node; + struct cmd_element *cmd_element; +- struct desc *desc; +- vector cmd_node_v, cmd_element_v, desc_v; ++ vector cmd_node_v; + + if (cmdvec) + { +@@ -3601,30 +4186,8 @@ cmd_terminate () + cmd_node_v = cmd_node->cmd_vector; + + for (j = 0; j < vector_active (cmd_node_v); j++) +- if ((cmd_element = vector_slot (cmd_node_v, j)) != NULL && +- cmd_element->strvec != NULL) +- { +- cmd_element_v = cmd_element->strvec; +- +- for (k = 0; k < vector_active (cmd_element_v); k++) +- if ((desc_v = vector_slot (cmd_element_v, k)) != NULL) +- { +- for (l = 0; l < vector_active (desc_v); l++) +- if ((desc = vector_slot (desc_v, l)) != NULL) +- { +- if (desc->cmd) +- XFREE (MTYPE_STRVEC, desc->cmd); +- if (desc->str) +- XFREE (MTYPE_STRVEC, desc->str); +- +- XFREE (MTYPE_DESC, desc); +- } +- vector_free (desc_v); +- } +- +- cmd_element->strvec = NULL; +- vector_free (cmd_element_v); +- } ++ if ((cmd_element = vector_slot (cmd_node_v, j)) != NULL) ++ cmd_terminate_element(cmd_element); + + vector_free (cmd_node_v); + } +@@ -3634,9 +4197,9 @@ cmd_terminate () + } + + if (command_cr) +- XFREE(MTYPE_STRVEC, command_cr); +- if (desc_cr.str) +- XFREE(MTYPE_STRVEC, desc_cr.str); ++ XFREE(MTYPE_CMD_TOKENS, command_cr); ++ if (token_cr.desc) ++ XFREE(MTYPE_CMD_TOKENS, token_cr.desc); + if (host.name) + XFREE (MTYPE_HOST, host.name); + if (host.password) +diff --git a/lib/command.h b/lib/command.h +index 2d708d8..e47c425 100644 +--- a/lib/command.h ++++ b/lib/command.h +@@ -138,18 +138,32 @@ struct cmd_element + int (*func) (struct cmd_element *, struct vty *, int, const char *[]); + const char *doc; /* Documentation of this command. */ + int daemon; /* Daemon to which this command belong. */ +- vector strvec; /* Pointing out each description vector. */ +- unsigned int cmdsize; /* Command index count. */ +- char *config; /* Configuration string */ +- vector subconfig; /* Sub configuration string */ ++ vector tokens; /* Vector of cmd_tokens */ + u_char attr; /* Command attributes */ + }; + ++ ++enum cmd_token_type ++{ ++ TOKEN_TERMINAL = 0, ++ TOKEN_MULTIPLE, ++ TOKEN_KEYWORD, ++}; ++ + /* Command description structure. */ +-struct desc ++struct cmd_token + { ++ enum cmd_token_type type; ++ ++ /* Used for type == MULTIPLE */ ++ vector multiple; /* vector of cmd_token, type == FINAL */ ++ ++ /* Used for type == KEYWORD */ ++ vector keyword; /* vector of vector of cmd_tokens */ ++ ++ /* Used for type == TERMINAL */ + char *cmd; /* Command string. */ +- char *str; /* Command's description. */ ++ char *desc; /* Command's description. */ + }; + + /* Return value of the commands. */ +@@ -192,7 +206,170 @@ struct desc + int argc __attribute__ ((unused)), \ + const char *argv[] __attribute__ ((unused)) ) + +-/* DEFUN for vty command interafce. Little bit hacky ;-). */ ++/* DEFUN for vty command interafce. Little bit hacky ;-). ++ * ++ * DEFUN(funcname, cmdname, cmdstr, helpstr) ++ * ++ * funcname ++ * ======== ++ * ++ * Name of the function that will be defined. ++ * ++ * cmdname ++ * ======= ++ * ++ * Name of the struct that will be defined for the command. ++ * ++ * cmdstr ++ * ====== ++ * ++ * The cmdstr defines the command syntax. It is used by the vty subsystem ++ * and vtysh to perform matching and completion in the cli. So you have to take ++ * care to construct it adhering to the following grammar. The names used ++ * for the production rules losely represent the names used in lib/command.c ++ * ++ * cmdstr = cmd_token , { " " , cmd_token } ; ++ * ++ * cmd_token = cmd_terminal ++ * | cmd_multiple ++ * | cmd_keyword ; ++ * ++ * cmd_terminal_fixed = fixed_string ++ * | variable ++ * | range ++ * | ipv4 ++ * | ipv4_prefix ++ * | ipv6 ++ * | ipv6_prefix ; ++ * ++ * cmd_terminal = cmd_terminal_fixed ++ * | option ++ * | vararg ; ++ * ++ * multiple_part = cmd_terminal_fixed ; ++ * cmd_multiple = "(" , multiple_part , ( "|" | { "|" , multiple_part } ) , ")" ; ++ * ++ * keyword_part = fixed_string , { " " , ( cmd_terminal_fixed | cmd_multiple ) } ; ++ * cmd_keyword = "{" , keyword_part , { "|" , keyword_part } , "}" ; ++ * ++ * lowercase = "a" | ... | "z" ; ++ * uppercase = "A" | ... | "Z" ; ++ * digit = "0" | ... | "9" ; ++ * number = digit , { digit } ; ++ * ++ * fixed_string = (lowercase | digit) , { lowercase | digit | uppercase | "-" | "_" } ; ++ * variable = uppercase , { uppercase | "_" } ; ++ * range = "<" , number , "-" , number , ">" ; ++ * ipv4 = "A.B.C.D" ; ++ * ipv4_prefix = "A.B.C.D/M" ; ++ * ipv6 = "X:X::X:X" ; ++ * ipv6_prefix = "X:X::X:X/M" ; ++ * option = "[" , variable , "]" ; ++ * vararg = "." , variable ; ++ * ++ * To put that all in a textual description: A cmdstr is a sequence of tokens, ++ * separated by spaces. ++ * ++ * Terminal Tokens: ++ * ++ * A very simple cmdstring would be something like: "show ip bgp". It consists ++ * of three Terminal Tokens, each containing a fixed string. When this command ++ * is called, no arguments will be passed down to the function implementing it, ++ * as it only consists of fixed strings. ++ * ++ * Apart from fixed strings, Terminal Tokens can also contain variables: ++ * An example would be "show ip bgp A.B.C.D". This command expects an IPv4 ++ * as argument. As this is a variable, the IP address entered by the user will ++ * be passed down as an argument. Apart from two exceptions, the other options ++ * for Terminal Tokens behave exactly as we just discussed and only make a ++ * difference for the CLI. The two exceptions will be discussed in the next ++ * paragraphs. ++ * ++ * A Terminal Token can contain a so called option match. This is a simple ++ * string variable that the user may omit. An example would be: ++ * "show interface [IFNAME]". If the user calls this without an interface as ++ * argument, no arguments will be passed down to the function implementing ++ * this command. Otherwise, the interface name will be provided to the function ++ * as a regular argument. ++ ++ * Also, a Terminal Token can contain a so called vararg. This is used e.g. in ++ * "show ip bgp regexp .LINE". The last token is a vararg match and will ++ * consume all the arguments the user inputs on the command line and append ++ * those to the list of arguments passed down to the function implementing this ++ * command. (Therefore, it doesn't make much sense to have any tokens after a ++ * vararg because the vararg will already consume all the words the user entered ++ * in the CLI) ++ * ++ * Multiple Tokens: ++ * ++ * The Multiple Token type can be used if there are multiple possibilities what ++ * arguments may be used for a command, but it should map to the same function ++ * nonetheless. An example would be "ip route A.B.C.D/M (reject|blackhole)" ++ * In that case both "reject" and "blackhole" would be acceptable as last ++ * arguments. The words matched by Multiple Tokens are always added to the ++ * argument list, even if they are matched by fixed strings. Such a Multiple ++ * Token can contain almost any type of token that would also be acceptable ++ * for a Terminal Token, the exception are optional variables and varag. ++ * ++ * There is one special case that is used in some places of Quagga that should be ++ * pointed out here shortly. An example would be "password (8|) WORD". This ++ * construct is used to have fixed strings communicated as arguments. (The "8" ++ * will be passed down as an argument in this case) It does not mean that ++ * the "8" is optional. Another historic and possibly surprising property of ++ * this construct is that it consumes two parts of helpstr. (Help ++ * strings will be explained later) ++ * ++ * Keyword Tokens: ++ * ++ * There are commands that take a lot of different and possibly optional arguments. ++ * An example from ospf would be the "default-information originate" command. This ++ * command takes a lot of optional arguments that may be provided in any order. ++ * To accomodate such commands, the Keyword Token has been implemented. ++ * Using the keyword token, the "default-information originate" command and all ++ * its possible options can be represented using this single cmdstr: ++ * "default-information originate \ ++ * {always|metric <0-16777214>|metric-type (1|2)|route-map WORD}" ++ * ++ * Keywords always start with a fixed string and may be followed by arguments. ++ * Except optional variables and vararg, everything is permitted here. ++ * ++ * For the special case of a keyword without arguments, either NULL or the ++ * keyword itself will be pushed as an argument, depending on whether the ++ * keyword is present. ++ * For the other keywords, arguments will be only pushed for ++ * variables/Multiple Tokens. If the keyword is not present, the arguments that ++ * would have been pushed will be substituted by NULL. ++ * ++ * A few examples: ++ * "default information originate metric-type 1 metric 1000" ++ * would yield the following arguments: ++ * { NULL, "1000", "1", NULL } ++ * ++ * "default information originate always route-map RMAP-DEFAULT" ++ * would yield the following arguments: ++ * { "always", NULL, NULL, "RMAP-DEFAULT" } ++ * ++ * helpstr ++ * ======= ++ * ++ * The helpstr is used to show a short explantion for the commands that ++ * are available when the user presses '?' on the CLI. It is the concatenation ++ * of the helpstrings for all the tokens that make up the command. ++ * ++ * There should be one helpstring for each token in the cmdstr except those ++ * containing other tokens, like Multiple or Keyword Tokens. For those, there ++ * will only be the helpstrings of the contained tokens. ++ * ++ * The individual helpstrings are expected to be in the same order as their ++ * respective Tokens appear in the cmdstr. They should each be terminated with ++ * a linefeed. The last helpstring should be terminated with a linefeed as well. ++ * ++ * Care should also be taken to avoid having similar tokens with different ++ * helpstrings. Imagine e.g. the commands "show ip ospf" and "show ip bgp". ++ * they both contain a helpstring for "show", but only one will be displayed ++ * when the user enters "sh?". If those two helpstrings differ, it is not ++ * defined which one will be shown and the behavior is therefore unpredictable. ++ */ + #define DEFUN(funcname, cmdname, cmdstr, helpstr) \ + DEFUN_CMD_FUNC_DECL(funcname) \ + DEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, 0, 0) \ +@@ -330,7 +507,6 @@ struct desc + extern void install_node (struct cmd_node *, int (*) (struct vty *)); + extern void install_default (enum node_type); + extern void install_element (enum node_type, struct cmd_element *); +-extern void sort_node (void); + + /* Concatenates argv[shift] through argv[argc-1] into a single NUL-terminated + string with a space between each element (allocated using +@@ -346,7 +522,6 @@ extern int config_from_file (struct vty *, FILE *); + extern enum node_type node_parent (enum node_type); + extern int cmd_execute_command (vector, struct vty *, struct cmd_element **, int); + extern int cmd_execute_command_strict (vector, struct vty *, struct cmd_element **); +-extern void config_replace_string (struct cmd_element *, char *, ...); + extern void cmd_init (int); + extern void cmd_terminate (void); + +diff --git a/lib/memtypes.c b/lib/memtypes.c +index 50b6fa4..47a3438 100644 +--- a/lib/memtypes.c ++++ b/lib/memtypes.c +@@ -54,7 +54,7 @@ struct memory_list memory_list_lib[] = + { MTYPE_ROUTE_MAP_RULE, "Route map rule" }, + { MTYPE_ROUTE_MAP_RULE_STR, "Route map rule str" }, + { MTYPE_ROUTE_MAP_COMPILED, "Route map compiled" }, +- { MTYPE_DESC, "Command desc" }, ++ { MTYPE_CMD_TOKENS, "Command desc" }, + { MTYPE_KEY, "Key" }, + { MTYPE_KEYCHAIN, "Key chain" }, + { MTYPE_IF_RMAP, "Interface route map" }, +diff --git a/lib/vty.c b/lib/vty.c +index 96cb1e4..9908b02 100644 +--- a/lib/vty.c ++++ b/lib/vty.c +@@ -931,23 +931,23 @@ vty_complete_command (struct vty *vty) + + static void + vty_describe_fold (struct vty *vty, int cmd_width, +- unsigned int desc_width, struct desc *desc) ++ unsigned int desc_width, struct cmd_token *token) + { + char *buf; + const char *cmd, *p; + int pos; + +- cmd = desc->cmd[0] == '.' ? desc->cmd + 1 : desc->cmd; ++ cmd = token->cmd[0] == '.' ? token->cmd + 1 : token->cmd; + + if (desc_width <= 0) + { +- vty_out (vty, " %-*s %s%s", cmd_width, cmd, desc->str, VTY_NEWLINE); ++ vty_out (vty, " %-*s %s%s", cmd_width, cmd, token->desc, VTY_NEWLINE); + return; + } + +- buf = XCALLOC (MTYPE_TMP, strlen (desc->str) + 1); ++ buf = XCALLOC (MTYPE_TMP, strlen (token->desc) + 1); + +- for (p = desc->str; strlen (p) > desc_width; p += pos + 1) ++ for (p = token->desc; strlen (p) > desc_width; p += pos + 1) + { + for (pos = desc_width; pos > 0; pos--) + if (*(p + pos) == ' ') +@@ -976,7 +976,7 @@ vty_describe_command (struct vty *vty) + vector vline; + vector describe; + unsigned int i, width, desc_width; +- struct desc *desc, *desc_cr = NULL; ++ struct cmd_token *token, *token_cr = NULL; + + vline = cmd_make_strvec (vty->buf); + +@@ -1010,15 +1010,15 @@ vty_describe_command (struct vty *vty) + /* Get width of command string. */ + width = 0; + for (i = 0; i < vector_active (describe); i++) +- if ((desc = vector_slot (describe, i)) != NULL) ++ if ((token = vector_slot (describe, i)) != NULL) + { + unsigned int len; + +- if (desc->cmd[0] == '\0') ++ if (token->cmd[0] == '\0') + continue; + +- len = strlen (desc->cmd); +- if (desc->cmd[0] == '.') ++ len = strlen (token->cmd); ++ if (token->cmd[0] == '.') + len--; + + if (width < len) +@@ -1030,27 +1030,27 @@ vty_describe_command (struct vty *vty) + + /* Print out description. */ + for (i = 0; i < vector_active (describe); i++) +- if ((desc = vector_slot (describe, i)) != NULL) ++ if ((token = vector_slot (describe, i)) != NULL) + { +- if (desc->cmd[0] == '\0') ++ if (token->cmd[0] == '\0') + continue; + +- if (strcmp (desc->cmd, command_cr) == 0) ++ if (strcmp (token->cmd, command_cr) == 0) + { +- desc_cr = desc; ++ token_cr = token; + continue; + } + +- if (!desc->str) ++ if (!token->desc) + vty_out (vty, " %-s%s", +- desc->cmd[0] == '.' ? desc->cmd + 1 : desc->cmd, ++ token->cmd[0] == '.' ? token->cmd + 1 : token->cmd, + VTY_NEWLINE); +- else if (desc_width >= strlen (desc->str)) ++ else if (desc_width >= strlen (token->desc)) + vty_out (vty, " %-*s %s%s", width, +- desc->cmd[0] == '.' ? desc->cmd + 1 : desc->cmd, +- desc->str, VTY_NEWLINE); ++ token->cmd[0] == '.' ? token->cmd + 1 : token->cmd, ++ token->desc, VTY_NEWLINE); + else +- vty_describe_fold (vty, width, desc_width, desc); ++ vty_describe_fold (vty, width, desc_width, token); + + #if 0 + vty_out (vty, " %-*s %s%s", width +@@ -1059,18 +1059,18 @@ vty_describe_command (struct vty *vty) + #endif /* 0 */ + } + +- if ((desc = desc_cr)) ++ if ((token = token_cr)) + { +- if (!desc->str) ++ if (!token->desc) + vty_out (vty, " %-s%s", +- desc->cmd[0] == '.' ? desc->cmd + 1 : desc->cmd, ++ token->cmd[0] == '.' ? token->cmd + 1 : token->cmd, + VTY_NEWLINE); +- else if (desc_width >= strlen (desc->str)) ++ else if (desc_width >= strlen (token->desc)) + vty_out (vty, " %-*s %s%s", width, +- desc->cmd[0] == '.' ? desc->cmd + 1 : desc->cmd, +- desc->str, VTY_NEWLINE); ++ token->cmd[0] == '.' ? token->cmd + 1 : token->cmd, ++ token->desc, VTY_NEWLINE); + else +- vty_describe_fold (vty, width, desc_width, desc); ++ vty_describe_fold (vty, width, desc_width, token); + } + + out: +diff --git a/ospf6d/ospf6_main.c b/ospf6d/ospf6_main.c +index e991971..4f6d9e5 100644 +--- a/ospf6d/ospf6_main.c ++++ b/ospf6d/ospf6_main.c +@@ -325,9 +325,6 @@ main (int argc, char *argv[], char *envp[]) + /* initialize ospf6 */ + ospf6_init (); + +- /* sort command vector */ +- sort_node (); +- + /* parse config file */ + vty_read_config (config_file, config_default); + +diff --git a/ospfd/ospf_main.c b/ospfd/ospf_main.c +index 6d58b4e..c68aa4d 100644 +--- a/ospfd/ospf_main.c ++++ b/ospfd/ospf_main.c +@@ -310,8 +310,6 @@ main (int argc, char **argv) + ospf_opaque_init (); + #endif /* HAVE_OPAQUE_LSA */ + +- sort_node (); +- + /* Get configuration file. */ + vty_read_config (config_file, config_default); + +diff --git a/ripd/rip_main.c b/ripd/rip_main.c +index 6a9fa71..a512fbc 100644 +--- a/ripd/rip_main.c ++++ b/ripd/rip_main.c +@@ -287,9 +287,6 @@ main (int argc, char **argv) + rip_zclient_init (); + rip_peer_init (); + +- /* Sort all installed commands. */ +- sort_node (); +- + /* Get configuration file. */ + vty_read_config (config_file, config_default); + +diff --git a/ripngd/ripng_main.c b/ripngd/ripng_main.c +index 20225b7..7525a26 100644 +--- a/ripngd/ripng_main.c ++++ b/ripngd/ripng_main.c +@@ -282,9 +282,6 @@ main (int argc, char **argv) + zebra_init (); + ripng_peer_init (); + +- /* Sort all installed commands. */ +- sort_node (); +- + /* Get configuration file. */ + vty_read_config (config_file, config_default); + +diff --git a/tests/main.c b/tests/main.c +index e0fbb4d..2d8cb0c 100644 +--- a/tests/main.c ++++ b/tests/main.c +@@ -171,8 +171,6 @@ main (int argc, char **argv) + /* OSPF vty inits. */ + test_vty_init (); + +- sort_node (); +- + /* Change to the daemon program. */ + if (daemon_mode && daemon (0, 0) < 0) + { +diff --git a/vtysh/vtysh.c b/vtysh/vtysh.c +index c575902..34c3bd6 100644 +--- a/vtysh/vtysh.c ++++ b/vtysh/vtysh.c +@@ -554,7 +554,7 @@ vtysh_rl_describe (void) + vector vline; + vector describe; + int width; +- struct desc *desc; ++ struct cmd_token *token; + + vline = cmd_make_strvec (rl_line_buffer); + +@@ -592,15 +592,15 @@ vtysh_rl_describe (void) + /* Get width of command string. */ + width = 0; + for (i = 0; i < vector_active (describe); i++) +- if ((desc = vector_slot (describe, i)) != NULL) ++ if ((token = vector_slot (describe, i)) != NULL) + { + int len; + +- if (desc->cmd[0] == '\0') ++ if (token->cmd[0] == '\0') + continue; + +- len = strlen (desc->cmd); +- if (desc->cmd[0] == '.') ++ len = strlen (token->cmd); ++ if (token->cmd[0] == '.') + len--; + + if (width < len) +@@ -608,19 +608,19 @@ vtysh_rl_describe (void) + } + + for (i = 0; i < vector_active (describe); i++) +- if ((desc = vector_slot (describe, i)) != NULL) ++ if ((token = vector_slot (describe, i)) != NULL) + { +- if (desc->cmd[0] == '\0') ++ if (token->cmd[0] == '\0') + continue; + +- if (! desc->str) ++ if (! token->desc) + fprintf (stdout," %-s\n", +- desc->cmd[0] == '.' ? desc->cmd + 1 : desc->cmd); ++ token->cmd[0] == '.' ? token->cmd + 1 : token->cmd); + else + fprintf (stdout," %-*s %s\n", + width, +- desc->cmd[0] == '.' ? desc->cmd + 1 : desc->cmd, +- desc->str); ++ token->cmd[0] == '.' ? token->cmd + 1 : token->cmd, ++ token->desc); + } + + cmd_free_strvec (vline); +diff --git a/vtysh/vtysh_main.c b/vtysh/vtysh_main.c +index 4a315a5..48958f0 100644 +--- a/vtysh/vtysh_main.c ++++ b/vtysh/vtysh_main.c +@@ -299,8 +299,6 @@ main (int argc, char **argv, char **env) + + vty_init_vtysh (); + +- sort_node (); +- + /* Read vtysh configuration file before connecting to daemons. */ + vtysh_read_config (config_default); + +diff --git a/zebra/main.c b/zebra/main.c +index 742e029..523b191 100644 +--- a/zebra/main.c ++++ b/zebra/main.c +@@ -343,9 +343,6 @@ main (int argc, char **argv) + interface_list (); + route_read (); + +- /* Sort VTY commands. */ +- sort_node (); +- + #ifdef HAVE_SNMP + zebra_snmp_init (); + #endif /* HAVE_SNMP */ +diff --git a/zebra/test_main.c b/zebra/test_main.c +index a951863..c695172 100644 +--- a/zebra/test_main.c ++++ b/zebra/test_main.c +@@ -298,9 +298,6 @@ main (int argc, char **argv) + route_read (); + zebra_vty_init(); + +- /* Sort VTY commands. */ +- sort_node (); +- + /* Configuration file read*/ + vty_read_config (config_file, config_default); + +-- +1.9.2 + diff --git a/main/quagga/bgpd-implement-next-hop-self-all.patch b/main/quagga/1001-bgpd-implement-next-hop-self-all.patch index d8efde4980..d8efde4980 100644 --- a/main/quagga/bgpd-implement-next-hop-self-all.patch +++ b/main/quagga/1001-bgpd-implement-next-hop-self-all.patch diff --git a/main/quagga/APKBUILD b/main/quagga/APKBUILD index 6b9f64a25d..7546e6f906 100644 --- a/main/quagga/APKBUILD +++ b/main/quagga/APKBUILD @@ -1,7 +1,7 @@ # Maintainer: Natanael Copa <ncopa@alpinelinux.org> pkgname=quagga pkgver=0.99.22.4 -pkgrel=5 +pkgrel=6 pkgdesc="A free routing daemon replacing Zebra supporting RIP, OSPF and BGP." url="http://quagga.net/" arch="all" @@ -13,7 +13,10 @@ subpackages="$pkgname-dev $pkgname-doc" pkgusers="quagga" pkggroups="quagga" source="http://download.savannah.gnu.org/releases/quagga/quagga-$pkgver.tar.xz - bgpd-implement-next-hop-self-all.patch + 0001-vtysh-don-t-append-superflous-spaces-BZ-750.patch + 0002-bgpd-ospfd-zebra-fix-some-DEFUN-definitions.patch + 0003-lib-rewrite-command-matching.patch + 1001-bgpd-implement-next-hop-self-all.patch musl-fix-headers.patch quagga-readline-6.3.patch bgpd.initd @@ -73,23 +76,32 @@ package() { install -o quagga -g quagga -d -m755 "$pkgdir"/etc/quagga } md5sums="03ef24a448be47beba80efa2152f8a28 quagga-0.99.22.4.tar.xz -2e78b3ea20041f94ff99798d37e1456e bgpd-implement-next-hop-self-all.patch +e943bdde8ba1a2896672b634d554f504 0001-vtysh-don-t-append-superflous-spaces-BZ-750.patch +1a5c657062304c2b1764fef0882ca0ca 0002-bgpd-ospfd-zebra-fix-some-DEFUN-definitions.patch +22612b753bd4842ffc63a96b87beb67e 0003-lib-rewrite-command-matching.patch +2e78b3ea20041f94ff99798d37e1456e 1001-bgpd-implement-next-hop-self-all.patch 27812f0aaaf8507c4338371aca1bd08d musl-fix-headers.patch -46e4020d7f3e1e8790f4b6e0592481b4 quagga-readline-6.3.patch +67bea3d72089733c031885cfa843b972 quagga-readline-6.3.patch e80a3df594eba8b09e19aa28d9283698 bgpd.initd 33d0e34f11460881161ab930d3d3b987 zebra.initd 34e06a1d2bc602ce691abc9ed169dd15 zebra.confd" sha256sums="5e12056692e2dbc272a929f96d0e98d9873f4e7f7ffcca62434b58f6660a6386 quagga-0.99.22.4.tar.xz -979ed4f7a3e3b604a2cd3c717df467e253a4b75160f870343e6d96af0c9687ec bgpd-implement-next-hop-self-all.patch +29f35175dd1e22ba5f3c18c8ef4addaa5894bf4b942bcd84875c4512a0999139 0001-vtysh-don-t-append-superflous-spaces-BZ-750.patch +1d0a162da4b09472d677d6edb13c32c84fecbfbc9b4c043846199979aa67fe0b 0002-bgpd-ospfd-zebra-fix-some-DEFUN-definitions.patch +6a71d3d48de7cf8f9c4b554e1b455e9734ee71cb36d770a001843ed79411af38 0003-lib-rewrite-command-matching.patch +979ed4f7a3e3b604a2cd3c717df467e253a4b75160f870343e6d96af0c9687ec 1001-bgpd-implement-next-hop-self-all.patch 19068f58f034891c07b8a1324d0645e0f078e3dc98d1360d51c677cecaae8c9e musl-fix-headers.patch -40264c6f00afc45a88ac1dd488081abf8fdf8864b70e7a7ab2db55dc3e388cda quagga-readline-6.3.patch +e4f6c38b3124df2ae328f37bcc2fbc43eeb10b6d8887ca2e38e19e5536aae0d1 quagga-readline-6.3.patch 41471bfda120cb57bc0f40e87ec23a4f150d2b97c97ececdda6c408eab7cf9a3 bgpd.initd d6cc9280df63859ba711ad2071b38b9ce317d718c34840a2b101debef3fa7b56 zebra.initd f7a52d383f60270a5a8fee5d4ac522c5c0ec2b7c4b5252cff54e260f32d9b323 zebra.confd" sha512sums="bcf429e71073e4fc71efc364a21a6c8b70871a17c66bd1328bc57b57962c2c15182436183b2e363c9c1c0e85d84c49d304d4049d40129272f52d6140db330b8a quagga-0.99.22.4.tar.xz -44677f3852b31f2f2776507b0da004431d12253b5897336d49525114a87945283a21d6dfe6162a73ff1f006fc235e31a753ac591aa70e6b8f4fbb3adb75e00f9 bgpd-implement-next-hop-self-all.patch +b34db681155ade1e6dab4df733acb512cdb9adf42fd7ce06bd70fead1572c68d0845c0de5a2411257242c6ad9ce04bc428c4bb0fc2ab7192415fd6a957755039 0001-vtysh-don-t-append-superflous-spaces-BZ-750.patch +e6eee34f13850205e4494c607ccd6bcc7f66b6456ecb986a83910e6c5f4f3f388e1cd7974f28cb591429b0a63e61ab2e21dfb3b1787f8844bfccebb4a1f3276e 0002-bgpd-ospfd-zebra-fix-some-DEFUN-definitions.patch +d1274367fb9dcf8ead73b003569291b462c4d5eab26a8f85c98eb7539c12a9c64063b487bb88aa8fe55469da607f09ea55f02691931cdc77e18d5b622b19d5c3 0003-lib-rewrite-command-matching.patch +44677f3852b31f2f2776507b0da004431d12253b5897336d49525114a87945283a21d6dfe6162a73ff1f006fc235e31a753ac591aa70e6b8f4fbb3adb75e00f9 1001-bgpd-implement-next-hop-self-all.patch 28a1695bd77991790df3b1b5a2c8ced929e6413e9acb87c4a8286569d05b20e65cba466e118fc16a11c264ba11dd1581e4143031ce3bbb478177e900ea68cb07 musl-fix-headers.patch -db69f04b7894871cb0a6ca2722a99ea2cf42e8a8c88af07b29034d9a1093fb1954767874226df1753c0c8f900e3fe2870f408167a48e7884a1f4c1fb961232f2 quagga-readline-6.3.patch +587c5597fad4f54fb2ed21bc06c86b1e78ed9543a363b6b39ac688112dc08b2264ea59caa3a209718a7f07abf47bf3dea8055afb3da139c6197cfe19fa8503bf quagga-readline-6.3.patch d2bf7e8f2da49d0b039e72e76a77860b5b49d41a80550d6dc84791bbdec1d52e579393c5d42b45aa615991742421fef53ec1b92a5e740779b6060e20f5dd0413 bgpd.initd a4955fe54729ec8cb17b72f3d2205d0a4ba814a51a5eb3635a85339de9a2d2342e4814ef8b1e011803fa1dc3c6f9a23b178848e0812576876343104854feb723 zebra.initd 900972c6f98e561dfacf384111251db262326e8764b8c763a5ef639fa11c7949c03eef5e3bce324a4b1964fe45416d2db74ae1b6bc967f7d4ba48c2eeda017c4 zebra.confd" diff --git a/main/quagga/quagga-readline-6.3.patch b/main/quagga/quagga-readline-6.3.patch index 41ba831ac9..c75b430f6b 100644 --- a/main/quagga/quagga-readline-6.3.patch +++ b/main/quagga/quagga-readline-6.3.patch @@ -9,6 +9,6 @@ rl_completion_entry_function = vtysh_completion_entry_function; - rl_attempted_completion_function = (CPPFunction *)new_completion; + rl_attempted_completion_function = (rl_completion_func_t *) new_completion; - /* do not append space after completion. It will be appended - * in new_completion() function explicitly. */ - rl_completion_append_character = '\0'; + } + + char * |