aboutsummaryrefslogtreecommitdiffstats
path: root/main
diff options
context:
space:
mode:
authorTimo Teräs <timo.teras@iki.fi>2014-04-24 15:58:16 +0300
committerTimo Teräs <timo.teras@iki.fi>2014-04-24 15:59:11 +0300
commitef1d7c477d98b42ecc7b83aed7d950d3088e2a02 (patch)
tree5a1c089b46b38ba03c93e4a414282c158c580384 /main
parent4999b4b369545730001c4bfc749d5515780416ba (diff)
downloadaports-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.patch50
-rw-r--r--main/quagga/0002-bgpd-ospfd-zebra-fix-some-DEFUN-definitions.patch362
-rw-r--r--main/quagga/0003-lib-rewrite-command-matching.patch2979
-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/APKBUILD28
-rw-r--r--main/quagga/quagga-readline-6.3.patch6
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 *