diff options
author | Timo Teräs <timo.teras@iki.fi> | 2014-06-25 11:54:52 +0300 |
---|---|---|
committer | Timo Teräs <timo.teras@iki.fi> | 2014-06-25 11:56:25 +0300 |
commit | 18ea80fb4ac96d340e2de9b1a3ec730dcea0b0ba (patch) | |
tree | 1d66e8ec8cba4859cf7833d56728df7f3033cea1 | |
parent | 912433c5facc905a7ef866e272eb0f7704186ea7 (diff) | |
download | aports-18ea80fb4ac96d340e2de9b1a3ec730dcea0b0ba.tar.bz2 aports-18ea80fb4ac96d340e2de9b1a3ec730dcea0b0ba.tar.xz |
main/quagga: upgrade to 0.99.23
remove patches present in upstream
-rw-r--r-- | main/quagga/0001-vtysh-don-t-append-superflous-spaces-BZ-750.patch | 50 | ||||
-rw-r--r-- | main/quagga/0002-bgpd-ospfd-zebra-fix-some-DEFUN-definitions.patch | 362 | ||||
-rw-r--r-- | main/quagga/0003-lib-rewrite-command-matching.patch | 2979 | ||||
-rw-r--r-- | main/quagga/APKBUILD | 32 | ||||
-rw-r--r-- | main/quagga/musl-fix-headers.patch | 10 | ||||
-rw-r--r-- | main/quagga/quagga-readline-6.3.patch | 14 |
6 files changed, 8 insertions, 3439 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 deleted file mode 100644 index 610e96aacb..0000000000 --- a/main/quagga/0001-vtysh-don-t-append-superflous-spaces-BZ-750.patch +++ /dev/null @@ -1,50 +0,0 @@ -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 deleted file mode 100644 index f326fbed5c..0000000000 --- a/main/quagga/0002-bgpd-ospfd-zebra-fix-some-DEFUN-definitions.patch +++ /dev/null @@ -1,362 +0,0 @@ -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 deleted file mode 100644 index 9cd21ddb38..0000000000 --- a/main/quagga/0003-lib-rewrite-command-matching.patch +++ /dev/null @@ -1,2979 +0,0 @@ -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/APKBUILD b/main/quagga/APKBUILD index 7546e6f906..f8fbca12ac 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=6 +pkgver=0.99.23 +pkgrel=0 pkgdesc="A free routing daemon replacing Zebra supporting RIP, OSPF and BGP." url="http://quagga.net/" arch="all" @@ -13,12 +13,8 @@ subpackages="$pkgname-dev $pkgname-doc" pkgusers="quagga" pkggroups="quagga" source="http://download.savannah.gnu.org/releases/quagga/quagga-$pkgver.tar.xz - 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 zebra.initd zebra.confd @@ -75,33 +71,21 @@ package() { install -Dm644 "$srcdir/zebra.confd" "$pkgdir"/etc/conf.d/zebra install -o quagga -g quagga -d -m755 "$pkgdir"/etc/quagga } -md5sums="03ef24a448be47beba80efa2152f8a28 quagga-0.99.22.4.tar.xz -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 +md5sums="92dff03272aa9127ac13c6bea9c66187 quagga-0.99.23.tar.xz 2e78b3ea20041f94ff99798d37e1456e 1001-bgpd-implement-next-hop-self-all.patch -27812f0aaaf8507c4338371aca1bd08d musl-fix-headers.patch -67bea3d72089733c031885cfa843b972 quagga-readline-6.3.patch +df62890cccdb7d9c7cc9b96167b9da8c musl-fix-headers.patch e80a3df594eba8b09e19aa28d9283698 bgpd.initd 33d0e34f11460881161ab930d3d3b987 zebra.initd 34e06a1d2bc602ce691abc9ed169dd15 zebra.confd" -sha256sums="5e12056692e2dbc272a929f96d0e98d9873f4e7f7ffcca62434b58f6660a6386 quagga-0.99.22.4.tar.xz -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 +sha256sums="7f374da7bab275b9dd2be864ac6fb4e0552d455d6b15a7f1d7128f5208eadeee quagga-0.99.23.tar.xz 979ed4f7a3e3b604a2cd3c717df467e253a4b75160f870343e6d96af0c9687ec 1001-bgpd-implement-next-hop-self-all.patch -19068f58f034891c07b8a1324d0645e0f078e3dc98d1360d51c677cecaae8c9e musl-fix-headers.patch -e4f6c38b3124df2ae328f37bcc2fbc43eeb10b6d8887ca2e38e19e5536aae0d1 quagga-readline-6.3.patch +f59f1f654e80ae9c80e6ea150e210d82aa799d44624fa361348fc242849d0ebc musl-fix-headers.patch 41471bfda120cb57bc0f40e87ec23a4f150d2b97c97ececdda6c408eab7cf9a3 bgpd.initd d6cc9280df63859ba711ad2071b38b9ce317d718c34840a2b101debef3fa7b56 zebra.initd f7a52d383f60270a5a8fee5d4ac522c5c0ec2b7c4b5252cff54e260f32d9b323 zebra.confd" -sha512sums="bcf429e71073e4fc71efc364a21a6c8b70871a17c66bd1328bc57b57962c2c15182436183b2e363c9c1c0e85d84c49d304d4049d40129272f52d6140db330b8a quagga-0.99.22.4.tar.xz -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 +sha512sums="c8072da8cec96e023ba8a53da7b2bbe6d709d13a7d03204245f6a15b70be81f88106be4864ecd552d84660cd40e89cf52260bc850f948352a3068fb08fd918e0 quagga-0.99.23.tar.xz 44677f3852b31f2f2776507b0da004431d12253b5897336d49525114a87945283a21d6dfe6162a73ff1f006fc235e31a753ac591aa70e6b8f4fbb3adb75e00f9 1001-bgpd-implement-next-hop-self-all.patch -28a1695bd77991790df3b1b5a2c8ced929e6413e9acb87c4a8286569d05b20e65cba466e118fc16a11c264ba11dd1581e4143031ce3bbb478177e900ea68cb07 musl-fix-headers.patch -587c5597fad4f54fb2ed21bc06c86b1e78ed9543a363b6b39ac688112dc08b2264ea59caa3a209718a7f07abf47bf3dea8055afb3da139c6197cfe19fa8503bf quagga-readline-6.3.patch +b0cbef2d1544efa8a194aa4f05bd17225073dff7526bfa84a6068d7ae5806ff045c62c914999e1ecae04c4b251713aed5d5b0a6a98db6c3176ddf122d76894c7 musl-fix-headers.patch d2bf7e8f2da49d0b039e72e76a77860b5b49d41a80550d6dc84791bbdec1d52e579393c5d42b45aa615991742421fef53ec1b92a5e740779b6060e20f5dd0413 bgpd.initd a4955fe54729ec8cb17b72f3d2205d0a4ba814a51a5eb3635a85339de9a2d2342e4814ef8b1e011803fa1dc3c6f9a23b178848e0812576876343104854feb723 zebra.initd 900972c6f98e561dfacf384111251db262326e8764b8c763a5ef639fa11c7949c03eef5e3bce324a4b1964fe45416d2db74ae1b6bc967f7d4ba48c2eeda017c4 zebra.confd" diff --git a/main/quagga/musl-fix-headers.patch b/main/quagga/musl-fix-headers.patch index 12e96ece3d..b3e797c3b4 100644 --- a/main/quagga/musl-fix-headers.patch +++ b/main/quagga/musl-fix-headers.patch @@ -9,16 +9,6 @@ /* * This file defines four types of data structures: singly-linked lists, * singly-linked tail queues, lists and tail queues. ---- quagga-0.99.22.4.orig/lib/zebra.h -+++ quagga-0.99.22.4/lib/zebra.h -@@ -51,7 +51,6 @@ - #ifdef HAVE_STROPTS_H - #include <stropts.h> - #endif /* HAVE_STROPTS_H */ --#include <sys/fcntl.h> - #ifdef HAVE_SYS_SELECT_H - #include <sys/select.h> - #endif /* HAVE_SYS_SELECT_H */ --- quagga-0.99.22.4.orig/isisd/include-netbsd/iso.h +++ quagga-0.99.22.4/isisd/include-netbsd/iso.h @@ -192,7 +192,7 @@ diff --git a/main/quagga/quagga-readline-6.3.patch b/main/quagga/quagga-readline-6.3.patch deleted file mode 100644 index c75b430f6b..0000000000 --- a/main/quagga/quagga-readline-6.3.patch +++ /dev/null @@ -1,14 +0,0 @@ ---- quagga-0.99.22.4.orig/vtysh/vtysh.c -+++ quagga-0.99.22.4/vtysh/vtysh.c -@@ -2211,9 +2211,9 @@ - vtysh_readline_init (void) - { - /* readline related settings. */ -- rl_bind_key ('?', (Function *) vtysh_rl_describe); -+ rl_bind_key ('?', (rl_command_func_t *) vtysh_rl_describe); - 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; - } - - char * |