summaryrefslogtreecommitdiffstats
path: root/lib
diff options
context:
space:
mode:
authorChris Hall (GMCH) <chris.hall@highwayman.com>2009-12-10 21:40:17 +0000
committerChris Hall (GMCH) <chris.hall@highwayman.com>2009-12-10 21:40:17 +0000
commit122e52d3c6f844aceddf1b3b35885d0feae6650a (patch)
treec2a5a03b3b39f4adfbc472b2c2a8d9c8fd4dd444 /lib
parent2c2397059d4d4177ed4636c08aa476a138425dc8 (diff)
parent16899228d96d10853ff46cac2e24ab311b44e574 (diff)
downloadquagga-122e52d3c6f844aceddf1b3b35885d0feae6650a.tar.bz2
quagga-122e52d3c6f844aceddf1b3b35885d0feae6650a.tar.xz
Merge branch 'master' of /git/quagga.euro-ix
Diffstat (limited to 'lib')
-rw-r--r--lib/Makefile.am4
-rw-r--r--lib/command.c182
-rw-r--r--lib/memory.c40
-rw-r--r--lib/memory.h6
-rw-r--r--lib/memtypes.c12
-rw-r--r--lib/plist.c2364
-rw-r--r--lib/plist.h45
-rw-r--r--lib/prefix.c92
-rw-r--r--lib/symtab.c1186
-rw-r--r--lib/symtab.h318
-rw-r--r--lib/vector.c1192
-rw-r--r--lib/vector.h276
-rw-r--r--lib/vty.c147
-rw-r--r--lib/vty.h15
14 files changed, 4476 insertions, 1403 deletions
diff --git a/lib/Makefile.am b/lib/Makefile.am
index 315e919b..f655ac39 100644
--- a/lib/Makefile.am
+++ b/lib/Makefile.am
@@ -12,7 +12,7 @@ libzebra_la_SOURCES = \
sockunion.c prefix.c thread.c if.c memory.c buffer.c table.c hash.c \
filter.c routemap.c distribute.c stream.c str.c log.c plist.c \
zclient.c sockopt.c smux.c md5.c if_rmap.c keychain.c privs.c \
- sigevent.c pqueue.c jhash.c memtypes.c workqueue.c
+ sigevent.c pqueue.c jhash.c memtypes.c workqueue.c symtab.c
BUILT_SOURCES = memtypes.h route_types.h
@@ -27,7 +27,7 @@ pkginclude_HEADERS = \
str.h stream.h table.h thread.h vector.h version.h vty.h zebra.h \
plist.h zclient.h sockopt.h smux.h md5.h if_rmap.h keychain.h \
privs.h sigevent.h pqueue.h jhash.h zassert.h memtypes.h \
- workqueue.h route_types.h
+ workqueue.h route_types.h symtab.h
EXTRA_DIST = regex.c regex-gnu.h memtypes.awk route_types.awk route_types.txt
diff --git a/lib/command.c b/lib/command.c
index 31c067a3..60880f49 100644
--- a/lib/command.c
+++ b/lib/command.c
@@ -1,11 +1,11 @@
/*
$Id$
-
+
Command interpreter routine for virtual terminal [aka TeletYpe]
Copyright (C) 1997, 98, 99 Kunihiro Ishiguro
This file is part of GNU Zebra.
-
+
GNU Zebra is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published
by the Free Software Foundation; either version 2, or (at your
@@ -93,7 +93,7 @@ static struct facility_map {
int facility;
const char *name;
size_t match;
-} syslog_facilities[] =
+} syslog_facilities[] =
{
{ LOG_KERN, "kern", 1 },
{ LOG_USER, "user", 2 },
@@ -145,7 +145,7 @@ static int
level_match(const char *s)
{
int level ;
-
+
for ( level = 0 ; zlog_priority [level] != NULL ; level ++ )
if (!strncmp (s, zlog_priority[level], 2))
return level;
@@ -160,7 +160,7 @@ print_version (const char *progname)
printf ("%s\n", QUAGGA_COPYRIGHT);
}
-
+
/* Utility function to concatenate argv argument into a single string
with inserting ' ' character between each argument. */
char *
@@ -190,30 +190,24 @@ argv_concat (const char **argv, int argc, int shift)
/* Install top node of command vector. */
void
-install_node (struct cmd_node *node,
+install_node (struct cmd_node *node,
int (*func) (struct vty *))
{
vector_set_index (cmdvec, node->node, node);
node->func = func;
- node->cmd_vector = vector_init (VECTOR_MIN_SIZE);
+ node->cmd_vector = vector_init (0);
}
/* Compare two command's string. Used in sort_node (). */
static int
-cmp_node (const void *p, const void *q)
+cmp_node (const struct cmd_element *a, const struct cmd_element *b)
{
- 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)
+cmp_desc (const struct desc *a, const struct desc *b)
{
- const struct desc *a = *(struct desc * const *)p;
- const struct desc *b = *(struct desc * const *)q;
-
return strcmp (a->cmd, b->cmd);
}
@@ -223,24 +217,21 @@ 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);
+ vector_sort(cmd_vector, (vector_sort_cmp*)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 descvec = vector_slot (cmd_element->strvec,
vector_active (cmd_element->strvec) - 1);
- qsort (descvec->index, vector_active (descvec),
- sizeof (void *), cmp_desc);
+ vector_sort(descvec, (vector_sort_cmp*)cmp_desc) ;
}
}
}
@@ -255,10 +246,10 @@ cmd_make_strvec (const char *string)
char *token;
int strlen;
vector strvec;
-
+
if (string == NULL)
return NULL;
-
+
cp = string;
/* Skip white spaces. */
@@ -273,10 +264,10 @@ cmd_make_strvec (const char *string)
return NULL;
/* Prepare return vector. */
- strvec = vector_init (VECTOR_MIN_SIZE);
+ strvec = vector_init (0);
/* Copy each command piece and set into vector. */
- while (1)
+ while (1)
{
start = cp;
while (!(isspace ((int) *cp) || *cp == '\r' || *cp == '\n') &&
@@ -321,7 +312,7 @@ cmd_desc_str (const char **string)
const char *cp, *start;
char *token;
int strlen;
-
+
cp = *string;
if (cp == NULL)
@@ -370,7 +361,7 @@ cmd_make_descvec (const char *string, const char *descstr)
if (cp == NULL)
return NULL;
- allvec = vector_init (VECTOR_MIN_SIZE);
+ allvec = vector_init (0);
while (1)
{
@@ -396,7 +387,7 @@ cmd_make_descvec (const char *string, const char *descstr)
}
cp++;
}
-
+
while (isspace ((int) *cp) && *cp != '\0')
cp++;
@@ -406,7 +397,7 @@ cmd_make_descvec (const char *string, const char *descstr)
cp++;
}
- if (*cp == '\0')
+ if (*cp == '\0')
return allvec;
sp = cp;
@@ -428,14 +419,14 @@ cmd_make_descvec (const char *string, const char *descstr)
{
if (multiple == 1)
{
- strvec = vector_init (VECTOR_MIN_SIZE);
+ strvec = vector_init (0);
vector_set (allvec, strvec);
}
multiple++;
}
else
{
- strvec = vector_init (VECTOR_MIN_SIZE);
+ strvec = vector_init (0);
vector_set (allvec, strvec);
}
vector_set (strvec, desc);
@@ -484,14 +475,14 @@ void
install_element (enum node_type ntype, struct cmd_element *cmd)
{
struct cmd_node *cnode;
-
+
/* cmd_init hasn't been called */
if (!cmdvec)
return;
-
+
cnode = vector_slot (cmdvec, ntype);
- if (cnode == NULL)
+ if (cnode == NULL)
{
fprintf (stderr, "Command node %d doesn't exist, please check it\n",
ntype);
@@ -506,13 +497,13 @@ install_element (enum node_type ntype, struct cmd_element *cmd)
cmd->cmdsize = cmd_cmdsize (cmd->strvec);
}
-static unsigned char itoa64[] =
+static unsigned char itoa64[] =
"./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
static void
to64(char *s, long v, int n)
{
- while (--n >= 0)
+ while (--n >= 0)
{
*s++ = itoa64[v&0x3f];
v >>= 6;
@@ -527,7 +518,7 @@ zencrypt (const char *passwd)
char *crypt (const char *, const char *);
gettimeofday(&tv,0);
-
+
to64(&salt[0], random(), 3);
to64(&salt[3], tv.tv_usec, 3);
salt[5] = '\0';
@@ -545,9 +536,9 @@ config_write_host (struct vty *vty)
if (host.encrypt)
{
if (host.password_encrypt)
- vty_out (vty, "password 8 %s%s", host.password_encrypt, VTY_NEWLINE);
+ vty_out (vty, "password 8 %s%s", host.password_encrypt, VTY_NEWLINE);
if (host.enable_encrypt)
- vty_out (vty, "enable password 8 %s%s", host.enable_encrypt, VTY_NEWLINE);
+ vty_out (vty, "enable password 8 %s%s", host.enable_encrypt, VTY_NEWLINE);
}
else
{
@@ -684,7 +675,7 @@ cmd_filter_by_symbol (char *command, char *symbol)
#endif
/* Completion match types. */
-enum match_type
+enum match_type
{
no_match,
extend_match,
@@ -695,7 +686,7 @@ enum match_type
range_match,
vararg_match,
partly_match,
- exact_match
+ exact_match
};
static enum match_type
@@ -866,7 +857,7 @@ cmd_ipv6_match (const char *str)
* ::1.2.3.4
*/
ret = inet_pton(AF_INET6, str, &sin6_dummy.sin6_addr);
-
+
if (ret == 1)
return exact_match;
@@ -1071,7 +1062,7 @@ cmd_ipv6_prefix_match (const char *str)
if (mask < 0 || mask > 128)
return no_match;
-
+
/* I don't know why mask < 13 makes command match partly.
Forgive me to make this comments. I Want to set static default route
because of lack of function to originate default in ospf6d; sorry
@@ -1162,7 +1153,7 @@ cmd_filter_by_completion (char *command, vector v, unsigned int index)
if ((desc = vector_slot (descvec, j)))
{
str = desc->cmd;
-
+
if (CMD_VARARG (str))
{
if (match_type < vararg_match)
@@ -1378,7 +1369,7 @@ is_cmd_ambiguous (char *command, vector v, int index, enum match_type type)
if ((desc = vector_slot (descvec, j)))
{
enum match_type ret;
-
+
str = desc->cmd;
switch (type)
@@ -1567,7 +1558,7 @@ desc_unique_string (vector v, const char *str)
return 0;
}
-static int
+static int
cmd_try_do_shortcut (enum node_type node, char* first_word) {
if ( first_word != NULL &&
node != AUTH_NODE &&
@@ -1602,7 +1593,7 @@ cmd_describe_command_real (vector vline, struct vty *vty, int *status)
}
else
index = vector_active (vline) - 1;
-
+
/* Make copy vector of current node's command vector. */
cmd_vector = vector_copy (cmd_node_vector (cmdvec, vty->node));
@@ -1615,7 +1606,7 @@ cmd_describe_command_real (vector vline, struct vty *vty, int *status)
if ((command = vector_slot (vline, i)))
{
match = cmd_filter_by_completion (command, cmd_vector, i);
-
+
if (match == vararg_match)
{
struct cmd_element *cmd_element;
@@ -1634,7 +1625,7 @@ cmd_describe_command_real (vector vline, struct vty *vty, int *status)
vector_set (matchvec, desc);
}
}
-
+
vector_set (matchvec, &desc_cr);
vector_free (cmd_vector);
@@ -1734,7 +1725,7 @@ cmd_describe_command (vector vline, struct vty *vty, int *status)
shifted_vline = vector_init (vector_count(vline));
/* use memcpy? */
- for (index = 1; index < vector_active (vline); index++)
+ for (index = 1; index < vector_active (vline); index++)
{
vector_set_index (shifted_vline, index-1, vector_lookup(vline, index));
}
@@ -1836,7 +1827,7 @@ cmd_complete_command_real (vector vline, struct vty *vty, int *status)
}
*/
}
-
+
/* Prepare match vector. */
matchvec = vector_init (INIT_MATCHVEC_SIZE);
@@ -1858,7 +1849,7 @@ cmd_complete_command_real (vector vline, struct vty *vty, int *status)
for (j = 0; j < vector_active (descvec); j++)
if ((desc = vector_slot (descvec, j)))
{
- if ((string =
+ if ((string =
cmd_entry_function (vector_slot (vline, index),
desc->cmd)))
if (cmd_unique_string (matchvec, string))
@@ -1884,10 +1875,11 @@ cmd_complete_command_real (vector vline, struct vty *vty, int *status)
return NULL;
}
+/* XXX: TODO: stop poking around inside vector */
/* Only one matched */
if (vector_slot (matchvec, 1) == NULL)
{
- match_str = (char **) matchvec->index;
+ match_str = (char **) matchvec->VECTOR_INDEX;
vector_only_wrapper_free (matchvec);
*status = CMD_COMPLETE_FULL_MATCH;
return match_str;
@@ -1898,7 +1890,7 @@ cmd_complete_command_real (vector vline, struct vty *vty, int *status)
/* Check LCD of matched strings. */
if (vector_slot (vline, index) != NULL)
{
- lcd = cmd_lcd ((char **) matchvec->index);
+ lcd = cmd_lcd ((char **) matchvec->VECTOR_INDEX);
if (lcd)
{
@@ -1909,7 +1901,7 @@ cmd_complete_command_real (vector vline, struct vty *vty, int *status)
char *lcdstr;
lcdstr = XMALLOC (MTYPE_STRVEC, lcd + 1);
- memcpy (lcdstr, matchvec->index[0], lcd);
+ memcpy (lcdstr, matchvec->VECTOR_INDEX[0], lcd);
lcdstr[lcd] = '\0';
/* match_str = (char **) &lcdstr; */
@@ -1925,7 +1917,7 @@ cmd_complete_command_real (vector vline, struct vty *vty, int *status)
/* Make new matchvec. */
matchvec = vector_init (INIT_MATCHVEC_SIZE);
vector_set (matchvec, lcdstr);
- match_str = (char **) matchvec->index;
+ match_str = (char **) matchvec->VECTOR_INDEX;
vector_only_wrapper_free (matchvec);
*status = CMD_COMPLETE_MATCH;
@@ -1934,7 +1926,7 @@ cmd_complete_command_real (vector vline, struct vty *vty, int *status)
}
}
- match_str = (char **) matchvec->index;
+ match_str = (char **) matchvec->VECTOR_INDEX;
vector_only_wrapper_free (matchvec);
*status = CMD_COMPLETE_LIST_MATCH;
return match_str;
@@ -1957,7 +1949,7 @@ cmd_complete_command (vector vline, struct vty *vty, int *status)
shifted_vline = vector_init (vector_count(vline));
/* use memcpy? */
- for (index = 1; index < vector_active (vline); index++)
+ for (index = 1; index < vector_active (vline); index++)
{
vector_set_index (shifted_vline, index-1, vector_lookup(vline, index));
}
@@ -2030,7 +2022,7 @@ cmd_execute_command_real (vector vline, struct vty *vty,
if (match == vararg_match)
break;
-
+
ret = is_cmd_ambiguous (command, cmd_vector, index, match);
if (ret == 1)
@@ -2141,7 +2133,7 @@ cmd_execute_command (vector vline, struct vty *vty, struct cmd_element **cmd,
shifted_vline = vector_init (vector_count(vline));
/* use memcpy? */
- for (index = 1; index < vector_active (vline); index++)
+ for (index = 1; index < vector_active (vline); index++)
{
vector_set_index (shifted_vline, index-1, vector_lookup(vline, index));
}
@@ -2160,7 +2152,7 @@ cmd_execute_command (vector vline, struct vty *vty, struct cmd_element **cmd,
return saved_ret;
/* This assumes all nodes above CONFIG_NODE are childs of CONFIG_NODE */
- while ( ret != CMD_SUCCESS && ret != CMD_WARNING
+ while ( ret != CMD_SUCCESS && ret != CMD_WARNING
&& vty->node > CONFIG_NODE )
{
try_node = node_parent(try_node);
@@ -2204,14 +2196,14 @@ cmd_execute_command_strict (vector vline, struct vty *vty,
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)
{
@@ -2351,7 +2343,7 @@ DEFUN (config_terminal,
}
/* Enable command */
-DEFUN (enable,
+DEFUN (enable,
config_enable_cmd,
"enable",
"Turn on privileged mode command\n")
@@ -2367,7 +2359,7 @@ DEFUN (enable,
}
/* Disable command */
-DEFUN (disable,
+DEFUN (disable,
config_disable_cmd,
"disable",
"Turn off privileged mode command\n")
@@ -2432,7 +2424,7 @@ ALIAS (config_exit,
config_quit_cmd,
"quit",
"Exit current mode and down to previous mode\n")
-
+
/* End of configuration. */
DEFUN (config_end,
config_end_cmd,
@@ -2494,7 +2486,7 @@ DEFUN (config_help,
"help",
"Description of the interactive help system\n")
{
- vty_out (vty,
+ vty_out (vty,
"Quagga VTY provides advanced help feature. When you need help,%s\
anytime at the command line please press '?'.%s\
%s\
@@ -2532,9 +2524,9 @@ DEFUN (config_list,
}
/* Write current configuration into file. */
-DEFUN (config_write_file,
+DEFUN (config_write_file,
config_write_file_cmd,
- "write file",
+ "write file",
"Write running configuration to memory, network, or terminal\n"
"Write to configuration file\n")
{
@@ -2557,7 +2549,7 @@ DEFUN (config_write_file,
/* Get filename. */
config_file = host.config;
-
+
config_file_sav =
XMALLOC (MTYPE_TMP, strlen (config_file) + strlen (CONF_BACKUP_EXT) + 1);
strcpy (config_file_sav, config_file);
@@ -2566,7 +2558,7 @@ DEFUN (config_write_file,
config_file_tmp = XMALLOC (MTYPE_TMP, strlen (config_file) + 8);
sprintf (config_file_tmp, "%s.XXXXXX", config_file);
-
+
/* Open file to configuration write. */
fd = mkstemp (config_file_tmp);
if (fd < 0)
@@ -2575,7 +2567,7 @@ DEFUN (config_write_file,
VTY_NEWLINE);
goto finished;
}
-
+
/* Make vty for configuration file. */
file_vty = vty_new ();
file_vty->fd = fd;
@@ -2621,10 +2613,10 @@ DEFUN (config_write_file,
goto finished;
}
sync ();
-
+
if (chmod (config_file, CONFIGFILE_MASK) != 0)
{
- vty_out (vty, "Can't chmod configuration file %s: %s (%d).%s",
+ vty_out (vty, "Can't chmod configuration file %s: %s (%d).%s",
config_file, safe_strerror(errno), errno, VTY_NEWLINE);
goto finished;
}
@@ -2640,20 +2632,20 @@ finished:
return ret;
}
-ALIAS (config_write_file,
+ALIAS (config_write_file,
config_write_cmd,
- "write",
+ "write",
"Write running configuration to memory, network, or terminal\n")
-ALIAS (config_write_file,
+ALIAS (config_write_file,
config_write_memory_cmd,
- "write memory",
+ "write memory",
"Write running configuration to memory, network, or terminal\n"
"Write configuration to the file (same as write file)\n")
-ALIAS (config_write_file,
+ALIAS (config_write_file,
copy_runningconfig_startupconfig_cmd,
- "copy running-config startup-config",
+ "copy running-config startup-config",
"Copy configuration\n"
"Copy running config to... \n"
"Copy running config to startup config (same as write file)\n")
@@ -2736,7 +2728,7 @@ DEFUN (show_startup_config,
}
/* Hostname configuration */
-DEFUN (config_hostname,
+DEFUN (config_hostname,
hostname_cmd,
"hostname WORD",
"Set system's network name\n"
@@ -2750,12 +2742,12 @@ DEFUN (config_hostname,
if (host.name)
XFREE (MTYPE_HOST, host.name);
-
+
host.name = XSTRDUP (MTYPE_HOST, argv[0]);
return CMD_SUCCESS;
}
-DEFUN (config_no_hostname,
+DEFUN (config_no_hostname,
no_hostname_cmd,
"no hostname [HOSTNAME]",
NO_STR
@@ -2804,7 +2796,7 @@ DEFUN (config_password, password_cmd,
if (!isalnum ((int) *argv[0]))
{
- vty_out (vty,
+ vty_out (vty,
"Please specify string starting with alphanumeric%s", VTY_NEWLINE);
return CMD_WARNING;
}
@@ -2870,7 +2862,7 @@ DEFUN (config_enable_password, enable_password_cmd,
if (!isalnum ((int) *argv[0]))
{
- vty_out (vty,
+ vty_out (vty,
"Please specify string starting with alphanumeric%s", VTY_NEWLINE);
return CMD_WARNING;
}
@@ -2916,7 +2908,7 @@ DEFUN (no_config_enable_password, no_enable_password_cmd,
return CMD_SUCCESS;
}
-
+
DEFUN (service_password_encrypt,
service_password_encrypt_cmd,
"service password-encryption",
@@ -3195,19 +3187,19 @@ set_log_file(struct vty *vty, const char *fname, int loglevel)
int ret;
char *p = NULL;
const char *fullpath;
-
+
/* Path detection. */
if (! IS_DIRECTORY_SEP (*fname))
{
char cwd[MAXPATHLEN+1];
cwd[MAXPATHLEN] = '\0';
-
+
if (getcwd (cwd, MAXPATHLEN) == NULL)
{
zlog_err ("config_log_file: Unable to alloc mem!");
return CMD_WARNING;
}
-
+
if ( (p = XMALLOC (MTYPE_TMP, strlen (cwd) + strlen (fname) + 2))
== NULL)
{
@@ -3391,7 +3383,7 @@ DEFUN_DEPRECATED (config_log_trap,
{
int new_level ;
int i;
-
+
if ((new_level = level_match(argv[0])) == ZLOG_DISABLED)
return CMD_ERR_NO_MATCH;
@@ -3500,7 +3492,7 @@ DEFUN (no_banner_motd,
"Strings for motd\n")
{
host.motd = NULL;
- if (host.motdfile)
+ if (host.motdfile)
XFREE (MTYPE_HOST, host.motdfile);
host.motdfile = NULL;
return CMD_SUCCESS;
@@ -3540,7 +3532,7 @@ cmd_init (int terminal)
desc_cr.str = XSTRDUP(MTYPE_STRVEC, "");
/* Allocate initial top vector of commands. */
- cmdvec = vector_init (VECTOR_MIN_SIZE);
+ cmdvec = vector_init (0);
/* Default host value settings. */
host.name = NULL;
@@ -3604,7 +3596,7 @@ cmd_init (int terminal)
install_default (CONFIG_NODE);
}
-
+
install_element (CONFIG_NODE, &hostname_cmd);
install_element (CONFIG_NODE, &no_hostname_cmd);
@@ -3667,7 +3659,7 @@ cmd_terminate ()
if (cmdvec)
{
- for (i = 0; i < vector_active (cmdvec); i++)
+ for (i = 0; i < vector_active (cmdvec); i++)
if ((cmd_node = vector_slot (cmdvec, i)) != NULL)
{
cmd_node_v = cmd_node->cmd_vector;
diff --git a/lib/memory.c b/lib/memory.c
index dc09d8a6..333f59ad 100644
--- a/lib/memory.c
+++ b/lib/memory.c
@@ -17,7 +17,7 @@
* You should have received a copy of the GNU General Public License
* along with GNU Zebra; see the file COPYING. If not, write to the Free
* Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
- * 02111-1307, USA.
+ * 02111-1307, USA.
*/
#include <zebra.h>
@@ -32,22 +32,22 @@
static void alloc_inc (int);
static void alloc_dec (int);
static void log_memstats(int log_priority);
-
+
static const struct message mstr [] =
{
{ MTYPE_THREAD, "thread" },
{ MTYPE_THREAD_MASTER, "thread_master" },
{ MTYPE_VECTOR, "vector" },
- { MTYPE_VECTOR_INDEX, "vector_index" },
+ { MTYPE_VECTOR_BODY, "vector_index" },
{ MTYPE_IF, "interface" },
{ 0, NULL },
};
-
+
/* Fatal memory allocation error occured. */
static void __attribute__ ((noreturn))
zerror (const char *fname, int type, size_t size)
{
- zlog_err ("%s : can't allocate memory for `%s' size %d: %s\n",
+ zlog_err ("%s : can't allocate memory for `%s' size %d: %s\n",
fname, lookup (mstr, type), (int) size, safe_strerror(errno));
log_memstats(LOG_WARNING);
/* N.B. It might be preferable to call zlog_backtrace_sigsafe here, since
@@ -122,9 +122,9 @@ zstrdup (int type, const char *str)
alloc_inc (type);
return dup;
}
-
+
#ifdef MEMORY_LOG
-static struct
+static struct
{
const char *name;
long alloc;
@@ -187,7 +187,7 @@ mtype_zrealloc (const char *file, int line, int type, void *ptr, size_t size)
}
/* Important function. */
-void
+void
mtype_zfree (const char *file, int line, int type, void *ptr)
{
mstat[type].t_free++;
@@ -205,13 +205,13 @@ mtype_zstrdup (const char *file, int line, int type, const char *str)
mstat[type].c_strdup++;
memory = zstrdup (type, str);
-
+
mtype_log ("xstrdup", memory, file, line, type);
return memory;
}
#else
-static struct
+static struct
{
char *name;
long alloc;
@@ -231,7 +231,7 @@ alloc_dec (int type)
{
mstat[type].alloc--;
}
-
+
/* Looking up memory status from vty interface. */
#include "vector.h"
#include "vty.h"
@@ -329,7 +329,7 @@ show_memory_mallinfo (struct vty *vty)
{
struct mallinfo minfo = mallinfo();
char buf[MTYPE_MEMSTR_LEN];
-
+
vty_out (vty, "System allocator statistics:%s", VTY_NEWLINE);
vty_out (vty, " Total heap allocated: %s%s",
mtype_memstr (buf, MTYPE_MEMSTR_LEN, minfo.arena),
@@ -373,11 +373,11 @@ DEFUN (show_memory_all,
{
struct mlist *ml;
int needsep = 0;
-
+
#ifdef HAVE_MALLINFO
needsep = show_memory_mallinfo (vty);
#endif /* HAVE_MALLINFO */
-
+
for (ml = mlists; ml->list; ml++)
{
if (needsep)
@@ -516,7 +516,7 @@ memory_init (void)
install_element (ENABLE_NODE, &show_memory_ospf6_cmd);
install_element (ENABLE_NODE, &show_memory_isis_cmd);
}
-
+
/* Stats querying from users */
/* Return a pointer to a human friendly string describing
* the byte count passed in. E.g:
@@ -529,13 +529,13 @@ const char *
mtype_memstr (char *buf, size_t len, unsigned long bytes)
{
unsigned int t, g, m, k;
-
+
/* easy cases */
if (!bytes)
return "0 bytes";
if (bytes == 1)
return "1 byte";
-
+
if (sizeof (unsigned long) >= 8)
/* Hacked to make it not warn on ILP32 machines
* Shift will always be 40 at runtime. See below too */
@@ -545,11 +545,11 @@ mtype_memstr (char *buf, size_t len, unsigned long bytes)
g = bytes >> 30;
m = bytes >> 20;
k = bytes >> 10;
-
+
if (t > 10)
{
/* The shift will always be 39 at runtime.
- * Just hacked to make it not warn on 'smaller' machines.
+ * Just hacked to make it not warn on 'smaller' machines.
* Static compiler analysis should mean no extra code
*/
if (bytes & (1UL << (sizeof (unsigned long) >= 8 ? 39 : 0)))
@@ -576,7 +576,7 @@ mtype_memstr (char *buf, size_t len, unsigned long bytes)
}
else
snprintf (buf, len, "%ld bytes", bytes);
-
+
return buf;
}
diff --git a/lib/memory.h b/lib/memory.h
index 42eb5cae..037efef2 100644
--- a/lib/memory.h
+++ b/lib/memory.h
@@ -32,7 +32,7 @@ struct mlist {
struct memory_list *list;
const char *name;
};
-
+
#include "lib/memtypes.h"
extern struct mlist mlists[];
@@ -60,6 +60,8 @@ extern struct mlist mlists[];
#define XSTRDUP(mtype, str) zstrdup ((mtype), (str))
#endif /* MEMORY_LOG */
+#define SIZE(t,n) (sizeof(t) * (n))
+
/* Prototypes of memory function. */
extern void *zmalloc (int type, size_t size);
extern void *zcalloc (int type, size_t size);
@@ -69,7 +71,7 @@ extern char *zstrdup (int type, const char *str);
extern void *mtype_zmalloc (const char *file, int line, int type, size_t size);
-extern void *mtype_zcalloc (const char *file, int line, int type,
+extern void *mtype_zcalloc (const char *file, int line, int type,
size_t num, size_t size);
extern void *mtype_zrealloc (const char *file, int line, int type, void *ptr,
diff --git a/lib/memtypes.c b/lib/memtypes.c
index 05d93225..197fb88c 100644
--- a/lib/memtypes.c
+++ b/lib/memtypes.c
@@ -1,6 +1,6 @@
/*
* Memory type definitions. This file is parsed by memtypes.awk to extract
- * MTYPE_ and memory_list_.. information in order to autogenerate
+ * MTYPE_ and memory_list_.. information in order to autogenerate
* memtypes.h.
*
* The script is sensitive to the format (though not whitespace), see
@@ -16,8 +16,12 @@ struct memory_list memory_list_lib[] =
{
{ MTYPE_TMP, "Temporary memory" },
{ MTYPE_STRVEC, "String vector" },
- { MTYPE_VECTOR, "Vector" },
- { MTYPE_VECTOR_INDEX, "Vector index" },
+ { MTYPE_VECTOR, "Vector structure" },
+ { MTYPE_VECTOR_BODY, "Vector body" },
+ { MTYPE_SYMBOL_TABLE, "Symbol Table structure" },
+ { MTYPE_SYMBOL_BASES, "Symbol Table chain bases" },
+ { MTYPE_SYMBOL, "Symbol" },
+ { MTYPE_SYMBOL_REF, "Symbol Reference" },
{ MTYPE_LINK_LIST, "Link List" },
{ MTYPE_LINK_NODE, "Link Node" },
{ MTYPE_THREAD, "Thread" },
@@ -75,7 +79,7 @@ struct memory_list memory_list_lib[] =
{ -1, NULL },
};
-struct memory_list memory_list_zebra[] =
+struct memory_list memory_list_zebra[] =
{
{ MTYPE_RTADV_PREFIX, "Router Advertisement Prefix" },
{ MTYPE_VRF, "VRF" },
diff --git a/lib/plist.c b/lib/plist.c
index 0f802a83..10d5e31c 100644
--- a/lib/plist.c
+++ b/lib/plist.c
@@ -1,6 +1,10 @@
/* Prefix list functions.
* Copyright (C) 1999 Kunihiro Ishiguro
*
+ * 24-Nov-2009 -- substantially re-cast to speed up the handling of very
+ * large prefix-lists (10,000+ entries).
+ * Copyright (C) 2009 Chris Hall (GMCH), Highwayman
+ *
* This file is part of GNU Zebra.
*
* GNU Zebra is free software; you can redistribute it and/or modify
@@ -29,494 +33,949 @@
#include "buffer.h"
#include "stream.h"
#include "log.h"
+#include "symtab.h"
+#include "vector.h"
-/* Each prefix-list's entry. */
-struct prefix_list_entry
+/* This implements ip prefix-list functions.
+ *
+ * A prefix-list is referred to by name, where a name is an arbitrary string,
+ * case-sensitive. When showing prefix-lists the names are sorted
+ * "alphabetically", except for any digit sections, which sort numerically.
+ * Note that leading zeros are significant... "01" is not the same as "1",
+ * and sorts after it.
+*/
+
+enum prefix_flags {
+ PREFIX_ANY = 0x01, /* prefix declared as 'any' */
+ PREFIX_LE = 0x02, /* explicit 'le' */
+ PREFIX_GE = 0x04, /* explicit 'ge' */
+ PREFIX_SEQ = 0x10, /* explicit sequence number */
+} ;
+
+struct prefix_list ;
+struct prefix_list_entry ;
+
+/* Master structure of prefix_list.
+ *
+ * Each address family has it's own distinct set of prefix lists. (Including
+ * the fake AFI_ORF_PREFIX "family".)
+ *
+ * This means that a prefix_list name is local to an address family, but
+ * global wrt router instances.
+ * */
+struct prefix_master
{
- int seq;
+ struct symbol_table table ; /* table of prefix_list by name. */
- int le;
- int ge;
-
- enum prefix_list_type type;
+ int seqnum_flag ; /* ip prefix-list sequence-number state. */
- int any;
- struct prefix prefix;
+ struct prefix_list *recent ; /* the latest update. */
- unsigned long refcnt;
- unsigned long hitcnt;
+ struct prefix_list *cache_owner ;
+ /* prefix_list that owns the dup_cache */
+ struct vector dup_cache ; /* the dup_cache vector */
- struct prefix_list_entry *next;
- struct prefix_list_entry *prev;
+ void (*add_hook) (struct prefix_list *);
+ /* executed when new prefix_list is added. */
+ void (*delete_hook) (struct prefix_list *);
+ /* executed when a prefix_list is deleted. */
};
-/* List of struct prefix_list. */
-struct prefix_list_list
+/* Each prefix_list is described by one of these.*/
+struct prefix_list
{
- struct prefix_list *head;
- struct prefix_list *tail;
-};
+ struct prefix_master *master ; /* parent table: scope of this list. */
+ struct symbol* sym ; /* symbol in parent's symbol table */
-/* Master structure of prefix_list. */
-struct prefix_master
+ char *desc ; /* ip prefix-list description */
+
+ afi_t afi ; /* address family for all prefixes */
+ /* this is the *real* address family, so */
+ /* not "AFI_ORF_PREFIX" or similar. */
+
+ struct vector list ; /* the actual list of prefix matches */
+
+ int (*cmp)(struct prefix_list_entry** p_a, struct prefix_list_entry** p_b) ;
+ /* used when searching for duplicates */
+
+ int rangecount; /* XXX TODO: discover what this is for ?? */
+ /* Is not changed anywhere !! */
+} ;
+
+/* Each prefix-list's entry. */
+struct prefix_list_entry
{
- /* List of prefix_list which name is number. */
- struct prefix_list_list num;
+ int seq;
- /* List of prefix_list which name is string. */
- struct prefix_list_list str;
+ enum prefix_list_type type;
- /* Whether sequential number is used. */
- int seqnum;
+ int flags ; /* zero or more of PREFIX_ANY, PREFIX_LE and PREFIX_GE */
+ int le ; /* for exact match, set to prefix length */
+ int ge ; /* ditto */
- /* The latest update. */
- struct prefix_list *recent;
+ struct prefix prefix;
- /* Hook function which is executed when new prefix_list is added. */
- void (*add_hook) (struct prefix_list *);
+ u_int32_t mask ; /* for IPv4 -- host order. */
+ /* for last significant word of IPv6 -- network order */
+ int last ; /* for IPv4 -- not used. */
+ /* for IPv6 -- word to apply mask to. */
- /* Hook function which is executed when prefix_list is deleted. */
- void (*delete_hook) (struct prefix_list *);
+ unsigned long refcnt;
+ unsigned long hitcnt;
};
/* Static structure of IPv4 prefix_list's master. */
-static struct prefix_master prefix_master_ipv4 =
-{
- {NULL, NULL},
- {NULL, NULL},
- 1,
- NULL,
- NULL,
-};
+static struct prefix_master prefix_master_ipv4 ;
#ifdef HAVE_IPV6
/* Static structure of IPv6 prefix-list's master. */
-static struct prefix_master prefix_master_ipv6 =
-{
- {NULL, NULL},
- {NULL, NULL},
- 1,
- NULL,
- NULL,
-};
+static struct prefix_master prefix_master_ipv6 ;
#endif /* HAVE_IPV6*/
/* Static structure of BGP ORF prefix_list's master. */
-static struct prefix_master prefix_master_orf =
-{
- {NULL, NULL},
- {NULL, NULL},
- 1,
- NULL,
- NULL,
-};
-
-static struct prefix_master *
-prefix_master_get (afi_t afi)
+static struct prefix_master prefix_master_orf ;
+
+/* For real afi, the choice is strictly limited, and includes IPv6
+ * only if HAVE_IPV6 ! */
+#ifdef HAVE_IPV6
+#define assert_afi_real(a) assert(((a) == AFI_IP) || ((a) == AFI_IP6))
+#else
+#define assert_afi_real(a) assert((a) == AFI_IP)
+#endif
+
+/* Map afi to prefix_master.
+ *
+ * Note: there is no ipv6 master if not HAVE_IPV6.
+ *
+ * Returns address of prefix_master, or NULL if unknown afi.
+ */
+static inline struct prefix_master *
+prefix_master_get(afi_t afi)
{
- if (afi == AFI_IP)
- return &prefix_master_ipv4;
+ switch (afi)
+ {
+ case AFI_IP:
+ return &prefix_master_ipv4;
#ifdef HAVE_IPV6
- else if (afi == AFI_IP6)
- return &prefix_master_ipv6;
+ case AFI_IP6:
+ return &prefix_master_ipv6;
#endif /* HAVE_IPV6 */
- else if (afi == AFI_ORF_PREFIX)
- return &prefix_master_orf;
- return NULL;
-}
+ case AFI_ORF_PREFIX:
+ return &prefix_master_orf;
+ default:
+ return NULL;
+ } ;
+} ;
-/* Lookup prefix_list from list of prefix_list by name. */
-struct prefix_list *
-prefix_list_lookup (afi_t afi, const char *name)
+/* Map afi to maximum prefix length. Implied assert_afi_real(). */
+static int
+prefix_max_length(afi_t afi)
{
- struct prefix_list *plist;
- struct prefix_master *master;
+ switch (afi)
+ {
+ case AFI_IP:
+ return IPV4_MAX_BITLEN ;
+#ifdef HAVE_IPV6
+ case AFI_IP6:
+ return IPV6_MAX_BITLEN ;
+#endif
+ default:
+ assert(0) ; /* Should not get here ! */
+ return 0 ;
+ } ;
+} ;
- if (name == NULL)
- return NULL;
+/* Compare prefix list entries, taking into account:
+ *
+ * -- prefix value -- assumes is masked down correctly
+ * -- prefix length
+ * -- ge
+ * -- le
+ * -- type
+ *
+ * ... everything *except* the sequence number.
+ */
- master = prefix_master_get (afi);
- if (master == NULL)
- return NULL;
+#define PREFIX_LIST_CMP_RET(f) \
+ if ((*p_a)->f != (*p_b)->f) \
+ return ( (*p_a)->f < (*p_b)->f) ? -1 : +1
+#define PREFIX_LIST_CMP_RET_NL(f) \
+ if ((*p_a)->f != (*p_b)->f) \
+ return (ntohl((*p_a)->f) < ntohl((*p_b)->f)) ? -1 : +1
+#define PREFIX_LIST_CMP_REST \
+PREFIX_LIST_CMP_RET(prefix.prefixlen) ; \
+PREFIX_LIST_CMP_RET(ge) ; \
+PREFIX_LIST_CMP_RET(le) ; \
+PREFIX_LIST_CMP_RET(type)
- for (plist = master->num.head; plist; plist = plist->next)
- if (strcmp (plist->name, name) == 0)
- return plist;
+static int
+prefix_list_ipv4_cmp(struct prefix_list_entry** p_a,
+ struct prefix_list_entry** p_b)
+{
+ PREFIX_LIST_CMP_RET_NL(prefix.u.prefix4.s_addr) ;
+ PREFIX_LIST_CMP_REST ;
+ return 0 ;
+} ;
- for (plist = master->str.head; plist; plist = plist->next)
- if (strcmp (plist->name, name) == 0)
- return plist;
+#ifdef HAVE_IPV6 /*----------------------------------------------------*/
+static int
+prefix_list_ipv6_cmp(struct prefix_list_entry** p_a,
+ struct prefix_list_entry** p_b)
+{
+#ifdef s6_addr32
+ PREFIX_LIST_CMP_RET_NL(prefix.u.prefix6.s6_addr32[0]) ;
+ PREFIX_LIST_CMP_RET_NL(prefix.u.prefix6.s6_addr32[1]) ;
+ PREFIX_LIST_CMP_RET_NL(prefix.u.prefix6.s6_addr32[2]) ;
+ PREFIX_LIST_CMP_RET_NL(prefix.u.prefix6.s6_addr32[3]) ;
+#else
+ int c ;
+ if ((c = memcmp(&(*p_a)->prefix.u.prefix6.s6_addr,
+ &(*p_b)->prefix.u.prefix6.s6_addr, 16)) != 0)
+ return c ;
+#endif
+ PREFIX_LIST_CMP_REST ;
+ return 0 ;
+} ;
+#endif /*----------------------------------------------------*/
+
+/*==============================================================================
+ * Operations on prefix_master.
+ */
- return NULL;
-}
+static void prefix_list_delete (struct prefix_list* plist) ;
+static void prefix_dup_cache_free(struct prefix_master* pm) ;
-static struct prefix_list *
-prefix_list_new (void)
+/* Initialise given prefix_master. */
+static void
+prefix_master_init(struct prefix_master * pm)
{
- struct prefix_list *new;
+ memset(pm, 0, sizeof(struct prefix_master)) ;
- new = XCALLOC (MTYPE_PREFIX_LIST, sizeof (struct prefix_list));
- return new;
-}
+ symbol_table_init_new(&pm->table, pm, 20, 200, NULL, NULL) ;
+ pm->seqnum_flag = 1 ; /* Default is to generate sequence numbers. */
+} ;
+/* Reset given prefix_master.
+ *
+ * Frees all prefix lists and empties the symbol table. Any references to
+ * prefix lists are the responsibility of the reference owners.
+ *
+ * Resets to the default sequence numbering state.
+ *
+ * Retains current add_hook and delete_hook functions.
+ */
static void
-prefix_list_free (struct prefix_list *plist)
+prefix_master_reset(struct prefix_master * pm)
{
- XFREE (MTYPE_PREFIX_LIST, plist);
-}
+ struct prefix_list* plist ;
+ while ((plist = symbol_table_ream_keep(&(pm->table))))
+ prefix_list_delete(plist) ;
-static struct prefix_list_entry *
-prefix_list_entry_new (void)
-{
- struct prefix_list_entry *new;
+ pm->seqnum_flag = 1 ; /* Default is to generate sequence numbers. */
- new = XCALLOC (MTYPE_PREFIX_LIST_ENTRY, sizeof (struct prefix_list_entry));
- return new;
+ pm->recent = NULL ;
+ prefix_dup_cache_free(pm) ;
+} ;
+
+/* Add hook function. */
+void
+prefix_list_add_hook (void (*func)(struct prefix_list *plist))
+{
+ prefix_master_ipv4.add_hook = func;
+#ifdef HAVE_IPV6
+ prefix_master_ipv6.add_hook = func;
+#endif /* HAVE_IPV6 */
}
-static void
-prefix_list_entry_free (struct prefix_list_entry *pentry)
+/* Delete hook function. */
+void
+prefix_list_delete_hook (void (*func)(struct prefix_list *plist))
{
- XFREE (MTYPE_PREFIX_LIST_ENTRY, pentry);
+ prefix_master_ipv4.delete_hook = func;
+#ifdef HAVE_IPV6
+ prefix_master_ipv6.delete_hook = func;
+#endif /* HAVE_IPVt6 */
}
-/* Insert new prefix list to list of prefix_list. Each prefix_list
- is sorted by the name. */
-static struct prefix_list *
-prefix_list_insert (afi_t afi, const char *name)
+/*==============================================================================
+ * Prefix List Use.
+ *
+ * The prefix_list_ref type is a struct symbol_ref*, so we can operate on
+ * prefix_list_ref* arguments, keeping the stored reference values up to date.
+ */
+
+const char*
+prefix_list_get_name(struct prefix_list* plist)
{
- unsigned int i;
- long number;
- struct prefix_list *plist;
- struct prefix_list *point;
- struct prefix_list_list *list;
- struct prefix_master *master;
+ return symbol_get_name(plist->sym) ;
+} ;
- master = prefix_master_get (afi);
- if (master == NULL)
- return NULL;
+/* Set reference to prefix_list, by name.
+ * Replaces any existing reference.
+ *
+ * Returns the new value of the prefix_list_ref.
+ *
+ * NB: if reference existed, the parent and tag fields are preserved.
+ * Otherwise they are set to 0.
+ */
+prefix_list_ref
+prefix_list_set_ref(prefix_list_ref* p_ref, afi_t afi, const char* name)
+{
+ struct prefix_master* pm = prefix_master_get(afi) ;
- /* Allocate new prefix_list and copy given name. */
- plist = prefix_list_new ();
- plist->name = XSTRDUP (MTYPE_PREFIX_LIST_STR, name);
- plist->master = master;
+ if (pm == NULL)
+ return NULL ;
- /* If name is made by all digit character. We treat it as
- number. */
- for (number = 0, i = 0; i < strlen (name); i++)
- {
- if (isdigit ((int) name[i]))
- number = (number * 10) + (name[i] - '0');
- else
- break;
- }
+ return *p_ref = symbol_set_ref(*p_ref, symbol_find(&(pm->table), name)) ;
+} ;
- /* In case of name is all digit character */
- if (i == strlen (name))
- {
- plist->type = PREFIX_TYPE_NUMBER;
+/* Copy reference to prefix_list.
+ * Replaces any existing reference (by NULL if reference is NULL).
+ *
+ * Returns the new value of the prefix_list_ref.
+ *
+ * NB: if reference existed, the parent and tag fields are preserved.
+ * Otherwise they are set to 0.
+ */
+prefix_list_ref
+prefix_list_copy_ref(prefix_list_ref* p_dst, prefix_list_ref src)
+{
+ return *p_dst = symbol_set_ref(*p_dst, sym_ref_symbol(src)) ;
+} ;
- /* Set prefix_list to number list. */
- list = &master->num;
+/* Unset reference to prefix_list (does nothing if reference is NULL).
+ *
+ * Returns the new value of the prefix_list_ref -- ie NULL.
+ */
+prefix_list_ref
+prefix_list_unset_ref(prefix_list_ref* p_ref)
+{
+ return *p_ref = symbol_unset_ref(*p_ref, 1) ;
+} ;
- for (point = list->head; point; point = point->next)
- if (atol (point->name) >= number)
- break;
- }
- else
- {
- plist->type = PREFIX_TYPE_STRING;
-
- /* Set prefix_list to string list. */
- list = &master->str;
-
- /* Set point to insertion point. */
- for (point = list->head; point; point = point->next)
- if (strcmp (point->name, name) >= 0)
- break;
- }
+/* Get name of prefix_list to which given reference (if any) refers.
+ *
+ * Returns NULL if the reference is NULL.
+ */
+const char*
+prefix_list_ref_name(prefix_list_ref ref)
+{
+ return sym_ref_name(ref) ;
+} ;
- /* In case of this is the first element of master. */
- if (list->head == NULL)
- {
- list->head = list->tail = plist;
- return plist;
- }
+/* Return "identity" of prefix_list referred to by the given reference.
+ * Will be NULL if the reference is NULL.
+ *
+ * Two references to the same prefix_list will have the same "identity".
+ */
+void* prefix_list_ref_ident(prefix_list_ref ref)
+{
+ return (void*)sym_ref_symbol(ref) ;
+} ;
- /* In case of insertion is made at the tail of access_list. */
- if (point == NULL)
- {
- plist->prev = list->tail;
- list->tail->next = plist;
- list->tail = plist;
- return plist;
- }
+/* Return prefix_list referred to by the given reference.
+ * Will be NULL If the reference is NULL *OR* if the prefix_list is undefined.
+ */
+struct prefix_list* prefix_list_ref_plist(prefix_list_ref ref)
+{
+ return (struct prefix_list*)sym_ref_value(ref) ;
+} ;
- /* In case of insertion is made at the head of access_list. */
- if (point == list->head)
- {
- plist->next = list->head;
- list->head->prev = plist;
- list->head = plist;
- return plist;
- }
- /* Insertion is made at middle of the access_list. */
- plist->next = point;
- plist->prev = point->prev;
+/* Apply a prefix_list to the given prefix. */
+enum prefix_list_type
+prefix_list_apply (struct prefix_list *plist, void *object)
+{
+ struct prefix *p ;
+ int plen ;
+ struct prefix_list_entry* pe ;
+ vector_index i ;
+
+ in_addr_t ip ;
+#ifdef s6_addr32
+ u_int32_t* pp ;
+ u_int32_t* pep ;
+#else
+ unsigned char* pp ;
+ unsigned char* pep ;
+#endif
+ /* Deny if prefix_list is undefined or empty */
+ if ((plist == NULL) || (vector_end(&plist->list) == 0))
+ return PREFIX_DENY;
- if (point->prev)
- point->prev->next = plist;
- point->prev = plist;
+ p = object ;
+ plen = p->prefixlen ;
- return plist;
-}
+ /* For maximum performance we have separate loops for IPv4 and IPv6 */
+ assert_afi_real(plist->afi) ;
-static struct prefix_list *
-prefix_list_get (afi_t afi, const char *name)
-{
- struct prefix_list *plist;
+ switch (plist->afi)
+ {
+ case AFI_IP:
+ ip = p->u.prefix4.s_addr ;
+ for (VECTOR_ITEMS(&plist->list, pe, i))
+ {
+ ++pe->refcnt ;
+ if ((((pe->prefix.u.prefix4.s_addr ^ ip) & pe->mask) == 0)
+ && (plen >= pe->ge) && (plen <= pe->le))
+ {
+ ++pe->hitcnt;
+ return pe->type ;
+ } ;
+ }
+ break ;
+
+ case AFI_IP6:
+#ifdef s6_addr32
+ pp = p->u.prefix6.s6_addr32 ;
+#else
+ pp = p->u.prefix6.s6_addr ;
+#endif
+ for (VECTOR_ITEMS(&plist->list, pe, i))
+ {
+ int l = pe->last ;
+ int j ;
+ ++pe->refcnt ;
+#ifdef s6_addr32
+ pep = pe->prefix.u.prefix6.s6_addr32 ;
+#else
+ pep = pe->prefix.u.prefix6.s6_addr ;
+#endif
+ for (j = 0 ; j < l ; j++)
+ if (pep[j] != pp[j])
+ goto NEXT ;
+ if ((((pep[l] ^ pp[l]) & pe->mask) == 0)
+ && (plen >= pe->ge) && (plen <= pe->le))
+ {
+ ++pe->hitcnt;
+ return pe->type ;
+ } ;
+ NEXT: ;
+ }
+ break ;
- plist = prefix_list_lookup (afi, name);
+ default:
+ assert(0) ;
+ } ;
- if (plist == NULL)
- plist = prefix_list_insert (afi, name);
- return plist;
+ return PREFIX_DENY;
}
+/*==============================================================================
+ * Basic constructors and destructors.
+ */
-/* Delete prefix-list from prefix_list_master and free it. */
-static void
-prefix_list_delete (struct prefix_list *plist)
+/* Construct a new prefix_list and set the the associated symbol's value.
+ *
+ * Implied assert_afi_real().
+ */
+static struct prefix_list *
+prefix_list_new(struct prefix_master* pm, struct symbol* sym, afi_t afi)
{
- struct prefix_list_list *list;
- struct prefix_master *master;
- struct prefix_list_entry *pentry;
- struct prefix_list_entry *next;
+ struct prefix_list* new ;
- /* If prefix-list contain prefix_list_entry free all of it. */
- for (pentry = plist->head; pentry; pentry = next)
- {
- next = pentry->next;
- prefix_list_entry_free (pentry);
- plist->count--;
- }
-
- master = plist->master;
-
- if (plist->type == PREFIX_TYPE_NUMBER)
- list = &master->num;
- else
- list = &master->str;
+ new = XCALLOC (MTYPE_PREFIX_LIST, sizeof (struct prefix_list));
+ /* NB: implicitly sets the list empty. */
- if (plist->next)
- plist->next->prev = plist->prev;
- else
- list->tail = plist->prev;
+ new->sym = symbol_inc_ref(sym) ;
+ new->master = pm ;
- if (plist->prev)
- plist->prev->next = plist->next;
- else
- list->head = plist->next;
+ new->afi = afi ;
+ switch (afi)
+ {
+ case AFI_IP:
+ new->cmp = prefix_list_ipv4_cmp ;
+ break ;
+#ifdef HAVE_IPV6
+ case AFI_IP6:
+ new->cmp = prefix_list_ipv6_cmp ;
+ break ;
+#endif
+ default:
+ assert(0) ; /* Should not get here ! */
+ } ;
- if (plist->desc)
- XFREE (MTYPE_TMP, plist->desc);
+ symbol_set_value(sym, new) ;
- /* Make sure master's recent changed prefix-list information is
- cleared. */
- master->recent = NULL;
+ return new ;
+} ;
- if (plist->name)
- XFREE (MTYPE_PREFIX_LIST_STR, plist->name);
-
- prefix_list_free (plist);
-
- if (master->delete_hook)
- (*master->delete_hook) (NULL);
+/* Initialise prefix_list entry -- cleared to zeros. */
+static struct prefix_list_entry *
+prefix_list_entry_init(struct prefix_list_entry * pe)
+{
+ return memset(pe, 0, sizeof(struct prefix_list_entry));
}
+/* Allocate new prefix_list entry -- cleared to zeros. */
static struct prefix_list_entry *
-prefix_list_entry_make (struct prefix *prefix, enum prefix_list_type type,
- int seq, int le, int ge, int any)
+prefix_list_entry_new (void)
{
- struct prefix_list_entry *pentry;
-
- pentry = prefix_list_entry_new ();
-
- if (any)
- pentry->any = 1;
-
- prefix_copy (&pentry->prefix, prefix);
- pentry->type = type;
- pentry->seq = seq;
- pentry->le = le;
- pentry->ge = ge;
-
- return pentry;
+ return XCALLOC (MTYPE_PREFIX_LIST_ENTRY, sizeof (struct prefix_list_entry));
}
-/* Add hook function. */
-void
-prefix_list_add_hook (void (*func) (struct prefix_list *plist))
+/* Free given prefix list entry. */
+static void
+prefix_list_entry_free (struct prefix_list_entry* pe)
{
- prefix_master_ipv4.add_hook = func;
-#ifdef HAVE_IPV6
- prefix_master_ipv6.add_hook = func;
-#endif /* HAVE_IPV6 */
+ XFREE (MTYPE_PREFIX_LIST_ENTRY, pe);
}
-/* Delete hook function. */
-void
-prefix_list_delete_hook (void (*func) (struct prefix_list *plist))
+/* Set cache owned by nobody, and free the contents of the cache (if any). */
+static void
+prefix_dup_cache_free(struct prefix_master* pm)
{
- prefix_master_ipv4.delete_hook = func;
-#ifdef HAVE_IPV6
- prefix_master_ipv6.delete_hook = func;
-#endif /* HAVE_IPVt6 */
+ pm->cache_owner = NULL ;
+ vector_reset(&pm->dup_cache, 0) ;
}
-/* Calculate new sequential number. */
-static int
-prefix_new_seq_get (struct prefix_list *plist)
+/* Delete prefix_list from prefix_list_master and free it and its contents. */
+/* The prefix_list's symbol is set undefined. */
+static void
+prefix_list_delete (struct prefix_list* plist)
{
- int maxseq;
- int newseq;
- struct prefix_list_entry *pentry;
+ struct prefix_master* pm = plist->master ;
+ unsigned int i ;
+ struct prefix_list_entry* pe ;
- maxseq = newseq = 0;
+ /* Free all the prefix_list_entries, then free the vector they live in. */
+ for (VECTOR_ITEMS(&plist->list, pe, i))
+ prefix_list_entry_free(pe) ;
+ vector_reset(&plist->list, 0) ;
- for (pentry = plist->head; pentry; pentry = pentry->next)
- {
- if (maxseq < pentry->seq)
- maxseq = pentry->seq;
- }
+ /* If there is a description, release that now. */
+ if (plist->desc)
+ XFREE (MTYPE_TMP, plist->desc);
- newseq = ((maxseq / 5) * 5) + 5;
-
- return newseq;
-}
+ /* Can no longer own the dup_cache. */
+ if (pm->cache_owner == plist)
+ prefix_dup_cache_free(pm) ;
-/* Return prefix list entry which has same seq number. */
-static struct prefix_list_entry *
-prefix_seq_check (struct prefix_list *plist, int seq)
-{
- struct prefix_list_entry *pentry;
+ /* Symbol no longer has a value & drop reference. */
+ symbol_unset_value(plist->sym) ;
+ plist->sym = symbol_dec_ref(plist->sym) ;
- for (pentry = plist->head; pentry; pentry = pentry->next)
- if (pentry->seq == seq)
- return pentry;
- return NULL;
-}
+ /* Finally, release the prefix_list structure. */
+ XFREE (MTYPE_PREFIX_LIST, plist) ;
-static struct prefix_list_entry *
-prefix_list_entry_lookup (struct prefix_list *plist, struct prefix *prefix,
- enum prefix_list_type type, int seq, int le, int ge)
-{
- struct prefix_list_entry *pentry;
+ /* No longer have a recently changed prefix-list */
+ pm->recent = NULL ;
- for (pentry = plist->head; pentry; pentry = pentry->next)
- if (prefix_same (&pentry->prefix, prefix) && pentry->type == type)
- {
- if (seq >= 0 && pentry->seq != seq)
- continue;
+ /* Tell the world. */
+ if (pm->delete_hook)
+ (*pm->delete_hook) (NULL);
+} ;
- if (pentry->le != le)
- continue;
- if (pentry->ge != ge)
- continue;
+/*==============================================================================
+ * Operations on prefix_lists
+ */
- return pentry;
- }
+/* Seek prefix_list by name in give prefix master. Does NOT create. */
+static struct prefix_list *
+prefix_list_seek (struct prefix_master* pm, const char *name)
+{
+ return symbol_get_value(symbol_seek(&(pm->table), name)) ;
+} ;
- return NULL;
-}
+/* Lookup prefix_list by afi and name -- if afi is known, and name not NULL.
+ *
+ * Returns NULL if no prefix_list by that afi and name. Tolerates unknown afi
+ * and allows "fake" afi (eg. AFI_ORF_PREFIX).
+ */
+struct prefix_list *
+prefix_list_lookup (afi_t afi, const char *name)
+{
+ struct prefix_master* pm = prefix_master_get(afi) ;
-static void
-prefix_list_entry_delete (struct prefix_list *plist,
- struct prefix_list_entry *pentry,
- int update_list)
+ if ((name == NULL) || (pm == NULL))
+ return NULL;
+
+ return prefix_list_seek(pm, name) ;
+} ;
+
+/* Get prefix_list -- creating empty one if required. */
+static struct prefix_list *
+prefix_list_get (struct prefix_master* pm, const char *name, afi_t afi)
{
- if (plist == NULL || pentry == NULL)
- return;
- if (pentry->prev)
- pentry->prev->next = pentry->next;
- else
- plist->head = pentry->next;
- if (pentry->next)
- pentry->next->prev = pentry->prev;
- else
- plist->tail = pentry->prev;
+ struct symbol* sym ;
+ struct prefix_list* plist ;
- prefix_list_entry_free (pentry);
+ assert((pm != NULL) && (name != NULL)) ;
- plist->count--;
+ sym = symbol_find(&(pm->table), name) ; /* creates if required */
+ plist = symbol_get_value(sym) ;
- if (update_list)
- {
- if (plist->master->delete_hook)
- (*plist->master->delete_hook) (plist);
+ return plist ? plist : prefix_list_new(pm, sym, afi) ;
+} ;
- if (plist->head == NULL && plist->tail == NULL && plist->desc == NULL)
- prefix_list_delete (plist);
- else
- plist->master->recent = plist;
- }
-}
+/*==============================================================================
+ * Operations on prefix_list_entry
+ */
-static void
-prefix_list_entry_add (struct prefix_list *plist,
- struct prefix_list_entry *pentry)
+/* Sequence comparison function -- used in prefix_list_entry_lookup_seq */
+static int
+prefix_seq_cmp(const int** seq, const struct prefix_list_entry** pe)
{
- struct prefix_list_entry *replace;
- struct prefix_list_entry *point;
+ if (**seq != (*pe)->seq)
+ return (**seq < (*pe)->seq) ? -1 : + 1 ;
+ return 0 ;
+} ;
+
+/* Check any ge and le settings -- set defaults as required.
+ *
+ * -- sets the implied le for "any" or ge, if no explicit le set
+ *
+ * -- checks le & ge and updates as required the filter.
+ *
+ * Note that filter requires le = ge = prefix-length for an exact match.
+ *
+ * Returns: CMD_SUCCESS -- it's OK
+ * CMD_WARNING -- something amiss with the ge and/or le setting
+ *
+ * If ge: must be <= maximum prefix length and > actual prefix length
+ * else: set to prefix length
+ *
+ * If le: must be <= maximum prefix length and > actual prefix length
+ * else: if ge or any set to maximum prefix length
+ * else: set to prefix length
+ *
+ * If both ge and le: must have le > ge
+ *
+ * XXX TODO: see if we can allow explicit ge == prefix_length
+ * and explicit le == ge
+ */
+static int
+prefix_list_entry_ge_le_check(struct prefix_list_entry* pe, afi_t afi)
+{
+ int pl_max = prefix_max_length(afi) ;
+ int pl = pe->prefix.prefixlen ;
+
+ /* If we had ge, check in range, otherwise set to prefixlen. */
+ if (pe->flags & PREFIX_GE)
+ {
+ if (!(pe->ge <= pl_max) || !(pe->ge > pl))
+ return CMD_WARNING ;
+ }
+ else
+ pe->ge = pl ;
- /* Automatic asignment of seq no. */
- if (pentry->seq == -1)
- pentry->seq = prefix_new_seq_get (plist);
+ /* If we had le, check in range, otherwise set as required. */
+ if (pe->flags & PREFIX_LE)
+ {
+ if (!(pe->le <= pl_max) || !(pe->le > pl))
+ return CMD_WARNING ;
+ }
+ else
+ pe->le = (pe->flags & (PREFIX_ANY | PREFIX_GE)) ? pl_max : pl ;
- /* Is there any same seq prefix list entry? */
- replace = prefix_seq_check (plist, pentry->seq);
- if (replace)
- prefix_list_entry_delete (plist, replace, 0);
+ /* If had both ge and le, check that le > ge. */
+ if ((pe->flags & (PREFIX_GE | PREFIX_LE)) == (PREFIX_GE | PREFIX_LE))
+ if (pe->le <= pe->ge)
+ return CMD_WARNING ;
- /* Check insert point. */
- for (point = plist->head; point; point = point->next)
- if (point->seq >= pentry->seq)
- break;
+ return CMD_SUCCESS ;
+} ;
- /* In case of this is the first element of the list. */
- pentry->next = point;
+/* Lookup prefix_list_entry by its sequence number. Returns index of an entry
+ * in the prefix_list, and sets:
+ *
+ * result < 0 -- not found. index returned is of first entry in the
+ * prefix list, and this sequence number comes
+ * before it. (Or list is empty.)
+ * result == 0 -- found. index is of the entry found.
+ * result > 0 -- not found. index returned is of the entry with the largest
+ * sequence number smaller than the given one.
+ */
+static vector_index
+prefix_list_entry_lookup_seq(struct prefix_list *plist, int seq, int* result)
+{
+ return vector_bsearch(&plist->list, (vector_bsearch_cmp*)prefix_seq_cmp,
+ &seq, result) ;
+} ;
- if (point)
+/* Lookup prefix_list_entry by its contents.
+ *
+ * For large prefix_lists this uses the "dup_cache", which is an auxiliary
+ * vector, sorted by prefix value. Each prefix_master has a dup_cache,
+ * which is co-opted by the last prefix_list to use it.
+ *
+ * Returns index of an entry in the prefix_list or the dup_cache, and sets:
+ *
+ * cache -- NULL if not using dup_cache for this prefix_list.
+ * The index and result values refer to the main prefix_list.
+ *
+ * -- address of the cache (a vector).
+ * The index and result values refer to the cache. << NB
+ *
+ * result < 0 -- not found. index returned is of first entry in the
+ * prefix list, and this sequence number comes
+ * before it. (Or list is empty.)
+ * result == 0 -- found. index is of the entry found.
+ * result > 0 -- not found. index returned is of the entry with the largest
+ * sequence number smaller than the given one.
+ */
+static vector_index
+prefix_list_entry_lookup_val(struct prefix_list *plist,
+ struct prefix_list_entry* temp,
+ vector* cache,
+ int* result)
+{
+ /* See if we already have an entry like this one. */
+ if (vector_end(&plist->list) > 10)
{
- if (point->prev)
- point->prev->next = pentry;
- else
- plist->head = pentry;
-
- pentry->prev = point->prev;
- point->prev = pentry;
+ struct prefix_master* pm = plist->master ;
+
+ if (pm->cache_owner != plist)
+ {
+ /* Create new cache by copying vector. Releases any old cache. */
+ vector_copy_here(&pm->dup_cache, &plist->list) ;
+ /* Sort the result so can binary chop it. */
+ vector_sort(&pm->dup_cache, (vector_sort_cmp*)plist->cmp) ;
+ /* Now we own the cache. */
+ pm->cache_owner = plist ;
+ } ;
+
+ *cache = &pm->dup_cache ;
+ return vector_bsearch(*cache, (vector_bsearch_cmp*)plist->cmp, temp,
+ result) ;
}
else
{
- if (plist->tail)
- plist->tail->next = pentry;
- else
- plist->head = pentry;
+ struct prefix_list_entry* pe ;
+ vector_index i ;
+ *cache = NULL ; /* Not found in cache. */
+ *result = 0 ; /* Assume found ! */
+ for (VECTOR_ITEMS(&plist->list, pe, i))
+ {
+ if (plist->cmp(&pe, &temp) == 0)
+ return i ; /* Found ! */
+ } ;
+ *result = 1 ; /* Not found. */
+ return i ;
+ } ;
+} ;
+
+/* Look up prefix_list_entry looking for an exact match.
+ *
+ * If we have an explicit sequence number, then we look that up and then
+ * see if that prefix_list_entry is identical.
+ *
+ * Otherwise, look for entry whose value is identical, if any.
+ *
+ * Returns an index of a prefix_list entry and sets:
+ *
+ * -- result == 0 found -- index is of entry found
+ * -- result != 0 not found -- index is immaterial
+ *
+ * */
+static vector_index
+prefix_list_entry_lookup (struct prefix_list* plist,
+ struct prefix_list_entry* pe_seek, int* result)
+{
+ struct prefix_list_entry* pe_found ;
+ vector_index i ;
- pentry->prev = plist->tail;
- plist->tail = pentry;
+ if (pe_seek->flags & PREFIX_SEQ)
+ {
+ /* Lookup by sequence number. */
+ i = prefix_list_entry_lookup_seq(plist, pe_seek->seq, result) ;
+ if (*result == 0)
+ {
+ /* found by sequence number, now see if value matches. */
+ pe_found = vector_get_item(&plist->list, i) ;
+ *result = plist->cmp(&pe_seek, &pe_found) ;
+ } ;
+ }
+ else
+ {
+ /* Lookup by value. */
+ vector cache ;
+ i = prefix_list_entry_lookup_val(plist, pe_seek, &cache, result) ;
+ if ((*result == 0) && cache)
+ {
+ /* Found in the cache. We need it's position in prefix_list */
+ pe_found = vector_get_item(cache, i) ;
+ i = prefix_list_entry_lookup_seq(plist, pe_found->seq, result) ;
+ assert(*result == 0) ; /* MUST Find it !! */
+ } ;
}
- /* Increment count. */
- plist->count++;
+ return i ;
+} ;
+
+/* Insert prefix_list_entry or replace an existing one, if we can.
+ *
+ * May NOT insert or replace if an entry already exists with the same value,
+ * (where the value excludes the sequence number).
+ *
+ * Except that, if a sequence number is given, it is (trivially) possible to
+ * "replace" an entry with the same sequence number and the same value.
+ *
+ * Then, if no sequence number is given, one is allocated. The allocation
+ * rounds the last sequence number up to a multiple of 5 and adds 5.
+ *
+ * The prefix_list_entry is then put in the list by sequence number, replacing
+ * any existing entry.
+ *
+ * Returns: CMD_SUCCESS -- OK
+ * CMD_WARNING -- Nope, and NB: temp->seq set to sequence number
+ * of existing prefix_list_entry.
+ */
+static int
+prefix_list_entry_insert(struct prefix_list *plist,
+ struct prefix_list_entry *temp)
+{
+ struct prefix_list_entry* pe ;
+ vector cache ;
+ vector_index i, ic ;
+ int ret, retc ;
+ u_int32_t mask ;
+ int pl ;
+
+ /* See if we have an entry like this one, if we do:
+ *
+ * OK if sequence number is the same too -- nothing more to do !
+ * Fail if sequence number differs or was not set.
+ *
+ * If not found, and we own the cache, ic and retc tell us where in the
+ * cache the new entry belongs.
+ */
+ ic = prefix_list_entry_lookup_val(plist, temp, &cache, &retc) ;
+ if (retc == 0)
+ {
+ pe = vector_get_item(cache ? cache : &plist->list, ic) ;
+ if ((temp->flags & PREFIX_SEQ) && (pe->seq == temp->seq))
+ return CMD_SUCCESS ;
+ temp->seq = pe->seq ; /* capture clashing sequence number */
+ return CMD_WARNING ;
+ } ;
+
+ /* Now we need to find where to insert in the list. */
+ /* If required, we set implied sequence number, and insert at end. */
+ if (temp->flags & PREFIX_SEQ)
+ i = prefix_list_entry_lookup_seq(plist, temp->seq, &ret) ;
+ else
+ {
+ int last_seq = 0 ;
+ i = vector_end(&plist->list) ;
+ if (i != 0)
+ {
+ --i ; /* step back to last entry */
+ pe = vector_get_item(&plist->list, i) ;
+ last_seq = pe->seq ;
+ } ;
+ temp->seq = (((last_seq + 5 - 1) / 5) * 5) + 5 ;
+ ret = 1 ; /* insert after last entry (if any) */
+ } ;
+
+ /* If we found it with same sequence number, then replace entry,
+ * otherwise we need to create a new entry and insert it. i & ret show
+ * where we need to put the value in the list.
+ *
+ * If a dup cache exists, that must be kept up to date. ic & retc show
+ * where need to put the new value in the cache.
+ */
+ if (ret == 0)
+ {
+ /* We are going to replace an existing list entry. */
+ pe = vector_get_item(&plist->list, i) ; /* address of current entry */
+ /* If we have a cache, need to move the entry to it's new place */
+ if (cache)
+ {
+ /* We need to know where the old value was. */
+ vector_index io ;
+ int reto ;
+ io = vector_bsearch(cache, (vector_bsearch_cmp*)plist->cmp, pe,
+ &reto) ;
+ assert(reto == 0) ; /* MUST find it !! */
+ vector_move_item_here(cache, ic, retc, io) ;
+ } ;
+ }
+ else
+ {
+ /* We are going to insert a new list entry item. */
+ pe = prefix_list_entry_new() ;
+ vector_insert_item_here(&plist->list, i, ret, pe) ;
+ /* If we have a cache, need to insert the entry to it's place */
+ if (cache)
+ vector_insert_item_here(cache, ic, retc, pe) ;
+ } ;
+
+ /* Now we can set the value of the entry. */
+ *pe = *temp ;
+
+ /* Set mask and last ready to apply the filter. */
+ /* Note: if we don't have s6_addr32, we must handle IPv6 byte-wise ! */
+ pl = pe->prefix.prefixlen ;
+ mask = htonl((0xFFFFFFFF >> (pl & 0x1F)) ^ 0xFFFFFFFF) ;
+ switch (plist->afi)
+ {
+ case AFI_IP:
+ pe->mask = mask ;
+ break ;
+ case AFI_IP6:
+#ifdef s6_addr32
+ pe->mask = mask ;
+ pe->last = (pl == 0) ? 0 : (pl - 1) >> 5 ;
+#else
+ pe->mask = (0xFF >> (pl & 0x07)) ^ 0xFF ;
+ pe->last = (pl == 0) ? 0 : (pl - 1) >> 3 ;
+#endif
+ break ;
+ default:
+ assert(0) ;
+ } ;
/* Run hook function. */
if (plist->master->add_hook)
(*plist->master->add_hook) (plist);
plist->master->recent = plist;
+
+ return CMD_SUCCESS ;
}
+/* Delete prefix_list_entry, if we can.
+ *
+ * To delete an entry the caller must specify the exact value of an existing
+ * entry. If a sequence number is specified, that entry must exist, and its
+ * value must exactly match the given value. If no sequence number is
+ * specified, an entry must exist with exactly the given value.
+ *
+ * Returns: CMD_SUCCESS -- OK
+ * CMD_WARNING -- entry not found.
+ */
+static int
+prefix_list_entry_delete (struct prefix_list *plist,
+ struct prefix_list_entry *pe_seek)
+{
+ struct prefix_list_entry* pe ;
+ vector_index i ;
+ int ret ;
+
+ i = prefix_list_entry_lookup (plist, pe_seek, &ret) ;
+ if (ret)
+ return CMD_WARNING ;
+
+ pe = vector_delete_item(&plist->list, i) ;
+ assert(pe != NULL) ;
+
+ prefix_list_entry_free(pe) ; /* now release memory */
+
+ if (plist->master->delete_hook)
+ (*plist->master->delete_hook) (plist);
+
+ if ((vector_end(&plist->list) == 0) && (plist->desc == NULL))
+ prefix_list_delete (plist);
+ else
+ plist->master->recent = plist;
+
+ return CMD_SUCCESS ;
+} ;
+
+/*==============================================================================
+ * Common printing operations
+ */
+
/* Return string of prefix_list_type. */
static const char *
prefix_list_type_str (struct prefix_list_entry *pentry)
@@ -532,179 +991,192 @@ prefix_list_type_str (struct prefix_list_entry *pentry)
}
}
-static int
-prefix_list_entry_match (struct prefix_list_entry *pentry, struct prefix *p)
+/* Map afi to name of same: "ip" or "ipv6". Implied assert_afi_real(). */
+static const char*
+prefix_afi_name_str(afi_t afi)
{
- int ret;
+ switch (afi)
+ {
+ case AFI_IP:
+ return "ip" ;
+#ifdef HAVE_IPV6
+ case AFI_IP6:
+ return "ipv6" ;
+#endif
+ default:
+ assert(0) ; /* Should not get here ! */
+ return "?" ;
+ } ;
+} ;
- ret = prefix_match (&pentry->prefix, p);
- if (! ret)
- return 0;
-
- /* In case of le nor ge is specified, exact match is performed. */
- if (! pentry->le && ! pentry->ge)
- {
- if (pentry->prefix.prefixlen != p->prefixlen)
- return 0;
- }
- else
- {
- if (pentry->le)
- if (p->prefixlen > pentry->le)
- return 0;
-
- if (pentry->ge)
- if (p->prefixlen < pentry->ge)
- return 0;
- }
- return 1;
-}
+/* Print: "(ip|ipv6) prefix-list NAME" <post> */
+static void
+vty_prefix_list_name_print(struct vty* vty, struct prefix_list* plist,
+ const char* post)
+{
+ vty_out(vty, "%s prefix-list %s%s", prefix_afi_name_str(plist->afi),
+ (const char*)symbol_get_name(plist->sym), post) ;
+} ;
-enum prefix_list_type
-prefix_list_apply (struct prefix_list *plist, void *object)
+/* Print: "(ip|ipv6) prefix-list NAME: 99 entries" <post> */
+static void
+vty_prefix_list_name_count_print(struct vty* vty, struct prefix_list* plist,
+ const char* post)
{
- struct prefix_list_entry *pentry;
- struct prefix *p;
+ vty_prefix_list_name_print(vty, plist, "") ;
+ vty_out(vty, ": %d entries%s", vector_end(&plist->list), post);
+} ;
- p = (struct prefix *) object;
+/* Print: "(ip|ipv6) prefix-list NAME" UNDEFINED<post> */
+static void
+vty_prefix_list_undefined_print(struct vty* vty, afi_t afi, struct symbol* sym,
+ const char* post)
+{
+ vty_out(vty, "%s prefix-list %s UNDEFINED%s", prefix_afi_name_str(afi),
+ (const char*)symbol_get_name(sym), post) ;
+} ;
- if (plist == NULL)
- return PREFIX_DENY;
+/* Print: <indent>"Description: xxxx"<post>, if there is a description */
+static void
+vty_prefix_list_desc_print(struct vty* vty, struct prefix_list* plist,
+ int indent, const char* post)
+{
+ if (plist->desc)
+ vty_out (vty, "%sDescription: %s%s", VTY_SPACES(indent), plist->desc,
+ VTY_NEWLINE) ;
+}
+
+/* Size of buffer to hold either IPv4 or IPv6 string. */
+#ifndef INETX_ADDRSTRLEN
+# if INET_ADDRSTRLEN < INET6_ADDRSTRLEN
+# define INETX_ADDRSTRLEN INET6_ADDRSTRLEN
+# else
+# define INETX_ADDRSTRLEN INET_ADDRSTLEN
+# endif
+#endif
+
+/* Print value of given prefix_list_entry:
+ *
+ * "[seq 999 ](permit|deny) (any|XXXXX/999)[ ge 99][ le 99]"
+ * "[ '('hit count: 999, refcount: 999')']" <post>
+ *
+ * where: sequence number is included if "with_seq" specified
+ * ge and/or le are included if explicitly set
+ * the hit count and refcount are included if "with_stats" specified
+ */
+static void
+vty_prefix_list_value_print(struct vty* vty, struct prefix_list_entry* pe,
+ const char* post, int with_seq, int with_stats)
+{
+ if (with_seq)
+ vty_out(vty, "seq %d ", pe->seq) ;
- if (plist->count == 0)
- return PREFIX_PERMIT;
+ vty_out(vty, "%s ", prefix_list_type_str(pe)) ;
- for (pentry = plist->head; pentry; pentry = pentry->next)
+ if (pe->flags & PREFIX_ANY)
+ vty_puts(vty, "any");
+ else
{
- pentry->refcnt++;
- if (prefix_list_entry_match (pentry, p))
- {
- pentry->hitcnt++;
- return pentry->type;
- }
- }
+ struct prefix *p = &pe->prefix ;
+ char buf[INETX_ADDRSTRLEN];
+ vty_out(vty, "%s/%d",
+ inet_ntop (p->family, &p->u.prefix, buf, INETX_ADDRSTRLEN),
+ p->prefixlen);
+ } ;
- return PREFIX_DENY;
+ if (pe->flags & PREFIX_GE)
+ vty_out(vty, " ge %d", pe->ge);
+ if (pe->flags & PREFIX_LE)
+ vty_out(vty, " le %d", pe->le);
+
+ if (with_stats)
+ vty_out (vty, " (hit count: %lu, refcount: %lu)", pe->hitcnt, pe->refcnt);
+
+ vty_puts(vty, post) ;
}
static void __attribute__ ((unused))
prefix_list_print (struct prefix_list *plist)
{
- struct prefix_list_entry *pentry;
+ struct prefix_list_entry* pe ;
+ vector_index i ;
+ struct vty* vty = NULL ;
if (plist == NULL)
return;
- printf ("ip prefix-list %s: %d entries\n", plist->name, plist->count);
+ vty_prefix_list_name_count_print(vty, plist, VTY_NEWLINE) ;
- for (pentry = plist->head; pentry; pentry = pentry->next)
+ for (VECTOR_ITEMS(&plist->list, pe, i))
{
- if (pentry->any)
- printf ("any %s\n", prefix_list_type_str (pentry));
- else
- {
- struct prefix *p;
- char buf[BUFSIZ];
-
- p = &pentry->prefix;
-
- printf (" seq %d %s %s/%d",
- pentry->seq,
- prefix_list_type_str (pentry),
- inet_ntop (p->family, &p->u.prefix, buf, BUFSIZ),
- p->prefixlen);
- if (pentry->ge)
- printf (" ge %d", pentry->ge);
- if (pentry->le)
- printf (" le %d", pentry->le);
- printf ("\n");
- }
+ vty_out_indent(vty, 2) ;
+ vty_prefix_list_value_print(vty, pe, VTY_NEWLINE, 1, 1) ;
}
}
-
-/* Retrun 1 when plist already include pentry policy. */
-static struct prefix_list_entry *
-prefix_entry_dup_check (struct prefix_list *plist,
- struct prefix_list_entry *new)
-{
- struct prefix_list_entry *pentry;
- int seq = 0;
-
- if (new->seq == -1)
- seq = prefix_new_seq_get (plist);
- else
- seq = new->seq;
-
- for (pentry = plist->head; pentry; pentry = pentry->next)
- {
- if (prefix_same (&pentry->prefix, &new->prefix)
- && pentry->type == new->type
- && pentry->le == new->le
- && pentry->ge == new->ge
- && pentry->seq != seq)
- return pentry;
- }
- return NULL;
-}
+/*==============================================================================
+ * vty prefix_list operations.
+ */
-static int
-vty_invalid_prefix_range (struct vty *vty, const char *prefix)
+/* Look up given prefix_list -- complain if not found. */
+static struct prefix_list*
+vty_prefix_list_lookup(struct vty *vty, afi_t afi, const char* name)
{
- vty_out (vty, "%% Invalid prefix range for %s, make sure: len < ge-value <= le-value%s",
- prefix, VTY_NEWLINE);
- return CMD_WARNING;
-}
+ struct prefix_list* plist = prefix_list_lookup(afi, name);
+ if (plist == NULL)
+ vty_out (vty, "%% Can't find specified prefix-list%s", VTY_NEWLINE);
+ return plist ;
+} ;
+/* Process parameters for (ip|ipv6) prefix-list and no (ip|ipv6) prefix-list
+ *
+ * Fills in the given prefix_list_entry structure, ready for looking up,
+ * inserting or deleting prefix_list_entry.
+ *
+ * Checks parameters for validity/legality.
+ *
+ * Returns a CMD_xxxx return code. CMD_SUCCESS => OK !
+ */
static int
-vty_prefix_list_install (struct vty *vty, afi_t afi, const char *name,
- const char *seq, const char *typestr,
- const char *prefix, const char *ge, const char *le)
+vty_prefix_list_process(struct vty *vty, struct prefix_list_entry* pe,
+ struct prefix_list* plist,
+ afi_t afi, const char *seq_str, const char *type_str,
+ const char *prefix_str,
+ const char *ge_str, const char *le_str)
{
- int ret;
- enum prefix_list_type type;
- struct prefix_list *plist;
- struct prefix_list_entry *pentry;
- struct prefix_list_entry *dup;
- struct prefix p;
- int any = 0;
- int seqnum = -1;
- int lenum = 0;
- int genum = 0;
+ int ret ;
- /* Sequential number. */
- if (seq)
- seqnum = atoi (seq);
+ assert_afi_real(afi) ; /* require real (and supported) afi */
- /* ge and le number */
- if (ge)
- genum = atoi (ge);
- if (le)
- lenum = atoi (le);
+ prefix_list_entry_init(pe) ; /* clears everything, including flags */
+
+ /* Sequence number. */
+ if (seq_str)
+ {
+ pe->flags |= PREFIX_SEQ ;
+ pe->seq = atoi(seq_str) ;
+ } ;
/* Check filter type. */
- if (strncmp ("permit", typestr, 1) == 0)
- type = PREFIX_PERMIT;
- else if (strncmp ("deny", typestr, 1) == 0)
- type = PREFIX_DENY;
+ if (strncmp ("permit", type_str, 1) == 0)
+ pe->type = PREFIX_PERMIT;
+ else if (strncmp ("deny", type_str, 1) == 0)
+ pe->type = PREFIX_DENY;
else
{
vty_out (vty, "%% prefix type must be permit or deny%s", VTY_NEWLINE);
return CMD_WARNING;
}
- /* "any" is special token for matching any IPv4 addresses. */
+ /* Watch out for "any" */
+ if (strncmp ("any", prefix_str, strlen (prefix_str)) == 0)
+ pe->flags |= PREFIX_ANY ;
+
+ /* Process the prefix. */
if (afi == AFI_IP)
{
- if (strncmp ("any", prefix, strlen (prefix)) == 0)
- {
- ret = str2prefix_ipv4 ("0.0.0.0/0", (struct prefix_ipv4 *) &p);
- genum = 0;
- lenum = IPV4_MAX_BITLEN;
- any = 1;
- }
- else
- ret = str2prefix_ipv4 (prefix, (struct prefix_ipv4 *) &p);
-
+ if (pe->flags & PREFIX_ANY)
+ prefix_str = "0.0.0.0/0" ;
+ ret = str2prefix_ipv4 (prefix_str, (struct prefix_ipv4 *)&pe->prefix);
if (ret <= 0)
{
vty_out (vty, "%% Malformed IPv4 prefix%s", VTY_NEWLINE);
@@ -714,16 +1186,9 @@ vty_prefix_list_install (struct vty *vty, afi_t afi, const char *name,
#ifdef HAVE_IPV6
else if (afi == AFI_IP6)
{
- if (strncmp ("any", prefix, strlen (prefix)) == 0)
- {
- ret = str2prefix_ipv6 ("::/0", (struct prefix_ipv6 *) &p);
- genum = 0;
- lenum = IPV6_MAX_BITLEN;
- any = 1;
- }
- else
- ret = str2prefix_ipv6 (prefix, (struct prefix_ipv6 *) &p);
-
+ if (pe->flags & PREFIX_ANY)
+ prefix_str = "::/0" ;
+ ret = str2prefix_ipv6 (prefix_str, (struct prefix_ipv6 *)&pe->prefix);
if (ret <= 0)
{
vty_out (vty, "%% Malformed IPv6 prefix%s", VTY_NEWLINE);
@@ -732,155 +1197,147 @@ vty_prefix_list_install (struct vty *vty, afi_t afi, const char *name,
}
#endif /* HAVE_IPV6 */
- /* ge and le check. */
- if (genum && genum <= p.prefixlen)
- return vty_invalid_prefix_range (vty, prefix);
+ /* ge and le number */
+ if (ge_str)
+ {
+ pe->ge = atoi (ge_str) ;
+ pe->flags |= PREFIX_GE ;
+ }
+
+ if (le_str)
+ {
+ pe->le = atoi (le_str);
+ pe->flags |= PREFIX_LE ;
+ } ;
+
+ /* Complete the entry we've constructed, and check ge and le. */
+ ret = prefix_list_entry_ge_le_check(pe, afi) ;
+
+ if (ret != CMD_SUCCESS)
+ vty_out (vty, "%% Invalid prefix range for %s, make sure: "
+ "len < ge-value <= le-value%s",
+ prefix_str, VTY_NEWLINE);
- if (lenum && lenum <= p.prefixlen)
- return vty_invalid_prefix_range (vty, prefix);
+ return ret ;
+} ;
+
+/* Install a prefix_list_entry.
+ *
+ * Deals with all of ip prefix-list and ipv6 prefix-list commands.
+ *
+ * Note:
+ *
+ */
+
+static int
+vty_prefix_list_install (struct vty *vty, afi_t afi, const char *name,
+ const char *seq_str, const char *type_str,
+ const char *prefix_str,
+ const char *ge_str, const char *le_str)
+{
+ struct prefix_master* pm ;
+ struct prefix_list *plist;
+ struct prefix_list_entry temp ;
+ int ret;
- if (lenum && genum > lenum)
- return vty_invalid_prefix_range (vty, prefix);
+ assert_afi_real(afi) ; /* UI stuff should ensure this */
+ pm = prefix_master_get(afi) ;
- if (genum && lenum == (afi == AFI_IP ? 32 : 128))
- lenum = 0;
+ /* Get prefix_list with name. Make new list if required. */
+ plist = prefix_list_get(pm, name, afi) ;
- /* Get prefix_list with name. */
- plist = prefix_list_get (afi, name);
+ /* Do the grunt work on the parameters.
+ * Completely fill in the temp prefix_list_entry structure.
+ */
+ ret = vty_prefix_list_process(vty, &temp, plist, afi, seq_str, type_str,
+ prefix_str, ge_str, le_str) ;
+ if (ret != CMD_SUCCESS)
+ return ret ;
- /* Make prefix entry. */
- pentry = prefix_list_entry_make (&p, type, seqnum, lenum, genum, any);
-
- /* Check same policy. */
- dup = prefix_entry_dup_check (plist, pentry);
+ /* Insert into the list, unless list contains an entry which is the same
+ * apart from the sequence number.
+ * If fails, sets the sequence no. in temp to the sequence number found.
+ */
+ ret = prefix_list_entry_insert(plist, &temp);
- if (dup)
+ if (ret != CMD_SUCCESS)
{
- prefix_list_entry_free (pentry);
vty_out (vty, "%% Insertion failed - prefix-list entry exists:%s",
VTY_NEWLINE);
- vty_out (vty, " seq %d %s %s", dup->seq, typestr, prefix);
- if (! any && genum)
- vty_out (vty, " ge %d", genum);
- if (! any && lenum)
- vty_out (vty, " le %d", lenum);
- vty_out (vty, "%s", VTY_NEWLINE);
- return CMD_WARNING;
+ vty_out_indent(vty, 2) ;
+ vty_prefix_list_value_print(vty, &temp, VTY_NEWLINE, 1, 0) ;
}
- /* Install new filter to the access_list. */
- prefix_list_entry_add (plist, pentry);
-
return CMD_SUCCESS;
}
+/* Remove a prefix_list_entry. */
static int
-vty_prefix_list_uninstall (struct vty *vty, afi_t afi, const char *name,
- const char *seq, const char *typestr,
- const char *prefix, const char *ge, const char *le)
+vty_prefix_list_uninstall(struct vty *vty, afi_t afi, const char *name,
+ const char *seq_str, const char *type_str,
+ const char *prefix_str,
+ const char *ge_str, const char *le_str)
{
- int ret;
- enum prefix_list_type type;
struct prefix_list *plist;
- struct prefix_list_entry *pentry;
- struct prefix p;
- int seqnum = -1;
- int lenum = 0;
- int genum = 0;
+ struct prefix_list_entry temp ;
+ int ret;
- /* Check prefix list name. */
- plist = prefix_list_lookup (afi, name);
- if (! plist)
- {
- vty_out (vty, "%% Can't find specified prefix-list%s", VTY_NEWLINE);
- return CMD_WARNING;
- }
+ assert_afi_real(afi) ; /* UI should guarantee this. */
+
+ /* Seek prefix_list with name -- error if not found. */
+ plist = vty_prefix_list_lookup(vty, afi, name);
+ if (plist == NULL)
+ return CMD_WARNING ;
/* Only prefix-list name specified, delete the entire prefix-list. */
- if (seq == NULL && typestr == NULL && prefix == NULL &&
- ge == NULL && le == NULL)
+ if (seq_str == NULL && type_str == NULL && prefix_str == NULL &&
+ ge_str == NULL && le_str == NULL)
{
prefix_list_delete (plist);
return CMD_SUCCESS;
}
/* We must have, at a minimum, both the type and prefix here */
- if ((typestr == NULL) || (prefix == NULL))
+ if ((type_str == NULL) || (prefix_str == NULL))
{
vty_out (vty, "%% Both prefix and type required%s", VTY_NEWLINE);
return CMD_WARNING;
}
- /* Check sequence number. */
- if (seq)
- seqnum = atoi (seq);
+ /* Do the grunt work on the parameters.
+ * Completely fill in the temp prefix_list_entry structure.
+ */
+ ret = vty_prefix_list_process(vty, &temp, plist, afi, seq_str, type_str,
+ prefix_str, ge_str, le_str) ;
+ if (ret != CMD_SUCCESS)
+ return ret ;
- /* ge and le number */
- if (ge)
- genum = atoi (ge);
- if (le)
- lenum = atoi (le);
-
- /* Check of filter type. */
- if (strncmp ("permit", typestr, 1) == 0)
- type = PREFIX_PERMIT;
- else if (strncmp ("deny", typestr, 1) == 0)
- type = PREFIX_DENY;
- else
- {
- vty_out (vty, "%% prefix type must be permit or deny%s", VTY_NEWLINE);
- return CMD_WARNING;
- }
+ /* Remove prefix_list_entry if we can. */
+ ret = prefix_list_entry_delete (plist, &temp);
- /* "any" is special token for matching any IPv4 addresses. */
- if (afi == AFI_IP)
- {
- if (strncmp ("any", prefix, strlen (prefix)) == 0)
- {
- ret = str2prefix_ipv4 ("0.0.0.0/0", (struct prefix_ipv4 *) &p);
- genum = 0;
- lenum = IPV4_MAX_BITLEN;
- }
- else
- ret = str2prefix_ipv4 (prefix, (struct prefix_ipv4 *) &p);
+ if (ret != CMD_SUCCESS)
+ vty_out (vty, "%% Can't find specified prefix-list%s", VTY_NEWLINE);
- if (ret <= 0)
- {
- vty_out (vty, "%% Malformed IPv4 prefix%s", VTY_NEWLINE);
- return CMD_WARNING;
- }
- }
-#ifdef HAVE_IPV6
- else if (afi == AFI_IP6)
- {
- if (strncmp ("any", prefix, strlen (prefix)) == 0)
- {
- ret = str2prefix_ipv6 ("::/0", (struct prefix_ipv6 *) &p);
- genum = 0;
- lenum = IPV6_MAX_BITLEN;
- }
- else
- ret = str2prefix_ipv6 (prefix, (struct prefix_ipv6 *) &p);
+ return ret;
+}
- if (ret <= 0)
- {
- vty_out (vty, "%% Malformed IPv6 prefix%s", VTY_NEWLINE);
- return CMD_WARNING;
- }
- }
-#endif /* HAVE_IPV6 */
+static int
+vty_prefix_list_desc_set (struct vty *vty, afi_t afi, const char *name,
+ char* desc)
+{
+ struct prefix_master* pm ;
+ struct prefix_list *plist;
- /* Lookup prefix entry. */
- pentry = prefix_list_entry_lookup(plist, &p, type, seqnum, lenum, genum);
+ assert_afi_real(afi) ; /* UI stuff should ensure this */
+ pm = prefix_master_get(afi) ;
- if (pentry == NULL)
- {
- vty_out (vty, "%% Can't find specified prefix-list%s", VTY_NEWLINE);
- return CMD_WARNING;
- }
+ /* Get prefix_list with name. Make new list if required. */
+ plist = prefix_list_get(pm, name, afi) ;
+
+ if (plist->desc)
+ XFREE (MTYPE_TMP, plist->desc) ; /* Discard any existing value */
- /* Install new filter to the access_list. */
- prefix_list_entry_delete (plist, pentry, 1);
+ plist->desc = desc ;
return CMD_SUCCESS;
}
@@ -890,21 +1347,15 @@ vty_prefix_list_desc_unset (struct vty *vty, afi_t afi, const char *name)
{
struct prefix_list *plist;
- plist = prefix_list_lookup (afi, name);
- if (! plist)
- {
- vty_out (vty, "%% Can't find specified prefix-list%s", VTY_NEWLINE);
- return CMD_WARNING;
- }
+ plist = vty_prefix_list_lookup(vty, afi, name);
+ if (plist == NULL)
+ return CMD_WARNING ;
if (plist->desc)
- {
- XFREE (MTYPE_TMP, plist->desc);
- plist->desc = NULL;
- }
+ XFREE (MTYPE_TMP, plist->desc) ; /* sets plist->dec to NULL */
- if (plist->head == NULL && plist->tail == NULL && plist->desc == NULL)
- prefix_list_delete (plist);
+ if (vector_end(&plist->list) == 0)
+ prefix_list_delete(plist) ; /* delete list if all gone now */
return CMD_SUCCESS;
}
@@ -916,143 +1367,135 @@ enum display_type
detail_display,
sequential_display,
longer_display,
- first_match_display
+ first_match_display,
};
+/* Show given prefix_list */
static void
-vty_show_prefix_entry (struct vty *vty, afi_t afi, struct prefix_list *plist,
- struct prefix_master *master, enum display_type dtype,
+vty_show_prefix_entry (struct vty *vty, struct prefix_list *plist,
+ struct prefix_master* pm, enum display_type dtype,
int seqnum)
{
- struct prefix_list_entry *pentry;
-
/* Print the name of the protocol */
if (zlog_default)
vty_out (vty, "%s: ", zlog_proto_names[zlog_default->protocol]);
-
+
if (dtype == normal_display)
{
- vty_out (vty, "ip%s prefix-list %s: %d entries%s",
- afi == AFI_IP ? "" : "v6",
- plist->name, plist->count, VTY_NEWLINE);
- if (plist->desc)
- vty_out (vty, " Description: %s%s", plist->desc, VTY_NEWLINE);
+ vty_prefix_list_name_count_print(vty, plist, VTY_NEWLINE) ;
+ vty_prefix_list_desc_print(vty, plist, 3, VTY_NEWLINE) ;
}
else if (dtype == summary_display || dtype == detail_display)
{
- vty_out (vty, "ip%s prefix-list %s:%s",
- afi == AFI_IP ? "" : "v6", plist->name, VTY_NEWLINE);
+ struct prefix_list_entry* p_f = vector_get_first_item(&plist->list) ;
+ struct prefix_list_entry* p_l = vector_get_last_item(&plist->list) ;
+
+ vty_prefix_list_name_print(vty, plist, ":") ;
+ vty_out_newline(vty) ;
- if (plist->desc)
- vty_out (vty, " Description: %s%s", plist->desc, VTY_NEWLINE);
+ vty_prefix_list_desc_print(vty, plist, 3, VTY_NEWLINE) ;
vty_out (vty, " count: %d, range entries: %d, sequences: %d - %d%s",
- plist->count, plist->rangecount,
- plist->head ? plist->head->seq : 0,
- plist->tail ? plist->tail->seq : 0,
+ vector_end(&plist->list), plist->rangecount,
+ p_f ? p_f->seq : 0,
+ p_l ? p_l->seq : 0,
VTY_NEWLINE);
- }
+ } ;
if (dtype != summary_display)
{
- for (pentry = plist->head; pentry; pentry = pentry->next)
+ struct prefix_list_entry* pe ;
+ vector_index i ;
+ int with_seq = pm->seqnum_flag ;
+ int with_stats = (dtype == detail_display)
+ ||(dtype == sequential_display) ;
+
+ for (VECTOR_ITEMS(&plist->list, pe, i))
{
- if (dtype == sequential_display && pentry->seq != seqnum)
+ if ((dtype == sequential_display) && (pe->seq != seqnum))
continue;
-
- vty_out (vty, " ");
-
- if (master->seqnum)
- vty_out (vty, "seq %d ", pentry->seq);
- vty_out (vty, "%s ", prefix_list_type_str (pentry));
-
- if (pentry->any)
- vty_out (vty, "any");
- else
- {
- struct prefix *p = &pentry->prefix;
- char buf[BUFSIZ];
-
- vty_out (vty, "%s/%d",
- inet_ntop (p->family, &p->u.prefix, buf, BUFSIZ),
- p->prefixlen);
-
- if (pentry->ge)
- vty_out (vty, " ge %d", pentry->ge);
- if (pentry->le)
- vty_out (vty, " le %d", pentry->le);
- }
-
- if (dtype == detail_display || dtype == sequential_display)
- vty_out (vty, " (hit count: %ld, refcount: %ld)",
- pentry->hitcnt, pentry->refcnt);
-
- vty_out (vty, "%s", VTY_NEWLINE);
+ vty_out_indent(vty, 3);
+ vty_prefix_list_value_print(vty, pe, VTY_NEWLINE,
+ with_seq, with_stats) ;
}
}
}
+/* Show given prefix list in given afi, or all prefix lists in given afi. */
+
static int
vty_show_prefix_list (struct vty *vty, afi_t afi, const char *name,
- const char *seq, enum display_type dtype)
+ const char *seq_str, enum display_type dtype)
{
struct prefix_list *plist;
- struct prefix_master *master;
- int seqnum = 0;
+ struct prefix_master *pm;
+ int seq = 0;
- master = prefix_master_get (afi);
- if (master == NULL)
+ pm = prefix_master_get(afi) ;
+ if (pm == NULL)
return CMD_WARNING;
- if (seq)
- seqnum = atoi (seq);
+ if (seq_str)
+ seq = atoi (seq_str);
if (name)
{
- plist = prefix_list_lookup (afi, name);
- if (! plist)
- {
- vty_out (vty, "%% Can't find specified prefix-list%s", VTY_NEWLINE);
- return CMD_WARNING;
- }
- vty_show_prefix_entry (vty, afi, plist, master, dtype, seqnum);
+ /* Note that asking after an undefined prefix_list is an error. */
+ /* Does not mention references to an undefined prefix_list. */
+ plist = vty_prefix_list_lookup(vty, afi, name);
+ if (plist == NULL)
+ return CMD_WARNING;
+ vty_show_prefix_entry (vty, plist, pm, dtype, seq);
}
else
{
+ vector extract ;
+ vector_index i ;
+ struct symbol* sym ;
+
if (dtype == detail_display || dtype == summary_display)
{
- if (master->recent)
+ if (pm->recent)
vty_out (vty, "Prefix-list with the last deletion/insertion: %s%s",
- master->recent->name, VTY_NEWLINE);
+ (const char*)symbol_get_name(pm->recent->sym), VTY_NEWLINE) ;
}
- for (plist = master->num.head; plist; plist = plist->next)
- vty_show_prefix_entry (vty, afi, plist, master, dtype, seqnum);
+ /* Extract a vector of all prefix_list symbols, in name order. */
+ extract = symbol_table_extract(&pm->table, NULL, NULL, 0,
+ symbol_mixed_name_cmp) ;
+
+ for (VECTOR_ITEMS(extract, sym, i))
+ {
+ plist = symbol_get_value(sym) ;
+ if (plist != NULL)
+ vty_show_prefix_entry(vty, plist, pm, dtype, seq);
+ else
+ vty_prefix_list_undefined_print(vty, afi, sym, VTY_NEWLINE) ;
+ }
- for (plist = master->str.head; plist; plist = plist->next)
- vty_show_prefix_entry (vty, afi, plist, master, dtype, seqnum);
+ vector_free(extract) ; /* throw away temporary vector */
}
return CMD_SUCCESS;
}
static int
-vty_show_prefix_list_prefix (struct vty *vty, afi_t afi, const char *name,
+vty_show_prefix_list_prefix (struct vty *vty, afi_t afi, const char *name,
const char *prefix, enum display_type type)
{
struct prefix_list *plist;
- struct prefix_list_entry *pentry;
+ struct prefix_list_entry* pe ;
+ vector_index i ;
struct prefix p;
int ret;
int match;
+ int with_stats ;
- plist = prefix_list_lookup (afi, name);
- if (! plist)
- {
- vty_out (vty, "%% Can't find specified prefix-list%s", VTY_NEWLINE);
- return CMD_WARNING;
- }
+ /* Error if cannot find prefix list. */
+ plist = vty_prefix_list_lookup(vty, afi, name);
+ if (plist == NULL)
+ return CMD_WARNING;
ret = str2prefix (prefix, &p);
if (ret <= 0)
@@ -1061,88 +1504,66 @@ vty_show_prefix_list_prefix (struct vty *vty, afi_t afi, const char *name,
return CMD_WARNING;
}
- for (pentry = plist->head; pentry; pentry = pentry->next)
+ with_stats = (type == normal_display) || (type == first_match_display) ;
+
+ for (VECTOR_ITEMS(&plist->list, pe, i))
{
match = 0;
- if (type == normal_display || type == first_match_display)
- if (prefix_same (&p, &pentry->prefix))
- match = 1;
-
- if (type == longer_display)
- if (prefix_match (&p, &pentry->prefix))
- match = 1;
+ if ((type == normal_display || type == first_match_display))
+ match = prefix_same(&p, &pe->prefix) ;
+ else if (type == longer_display)
+ match = (prefix_match (&p, &pe->prefix)) ;
if (match)
{
- vty_out (vty, " seq %d %s ",
- pentry->seq,
- prefix_list_type_str (pentry));
-
- if (pentry->any)
- vty_out (vty, "any");
- else
- {
- struct prefix *p = &pentry->prefix;
- char buf[BUFSIZ];
-
- vty_out (vty, "%s/%d",
- inet_ntop (p->family, &p->u.prefix, buf, BUFSIZ),
- p->prefixlen);
-
- if (pentry->ge)
- vty_out (vty, " ge %d", pentry->ge);
- if (pentry->le)
- vty_out (vty, " le %d", pentry->le);
- }
-
- if (type == normal_display || type == first_match_display)
- vty_out (vty, " (hit count: %ld, refcount: %ld)",
- pentry->hitcnt, pentry->refcnt);
-
- vty_out (vty, "%s", VTY_NEWLINE);
+ vty_out_indent(vty, 3);
+ vty_prefix_list_value_print(vty, pe, VTY_NEWLINE, 1, with_stats) ;
if (type == first_match_display)
- return CMD_SUCCESS;
+ break ;
}
}
return CMD_SUCCESS;
}
+/* Clear hit counters in all prefix_list_entries:
+ *
+ * a) in all prefix_lists -- name NULL
+ * b) in given prefix list -- prefix NULL
+ * c) that match given prefix, in given prefix_list
+ */
static int
-vty_clear_prefix_list (struct vty *vty, afi_t afi, const char *name,
+vty_clear_prefix_list (struct vty *vty, afi_t afi, const char *name,
const char *prefix)
{
- struct prefix_master *master;
+ struct prefix_master *pm;
struct prefix_list *plist;
- struct prefix_list_entry *pentry;
int ret;
struct prefix p;
+ struct prefix_list_entry* pe ;
+ vector_index i ;
- master = prefix_master_get (afi);
- if (master == NULL)
+ pm = prefix_master_get (afi);
+ if (pm == NULL)
return CMD_WARNING;
- if (name == NULL && prefix == NULL)
+ if (name == NULL)
{
- for (plist = master->num.head; plist; plist = plist->next)
- for (pentry = plist->head; pentry; pentry = pentry->next)
- pentry->hitcnt = 0;
-
- for (plist = master->str.head; plist; plist = plist->next)
- for (pentry = plist->head; pentry; pentry = pentry->next)
- pentry->hitcnt = 0;
+ struct symbol_walker walker ;
+ symbol_walk_start(&pm->table, &walker) ;
+ while ((plist = symbol_get_value(symbol_walk_next(&walker))))
+ for (VECTOR_ITEMS(&plist->list, pe, i))
+ pe->hitcnt = 0 ;
}
else
{
- plist = prefix_list_lookup (afi, name);
- if (! plist)
- {
- vty_out (vty, "%% Can't find specified prefix-list%s", VTY_NEWLINE);
- return CMD_WARNING;
- }
+ /* Error if cannot find prefix list. */
+ plist = vty_prefix_list_lookup(vty, afi, name);
+ if (plist == NULL)
+ return CMD_WARNING;
- if (prefix)
+ if (prefix != NULL)
{
ret = str2prefix (prefix, &p);
if (ret <= 0)
@@ -1152,20 +1573,13 @@ vty_clear_prefix_list (struct vty *vty, afi_t afi, const char *name,
}
}
- for (pentry = plist->head; pentry; pentry = pentry->next)
- {
- if (prefix)
- {
- if (prefix_match (&pentry->prefix, &p))
- pentry->hitcnt = 0;
- }
- else
- pentry->hitcnt = 0;
- }
- }
- return CMD_SUCCESS;
+ for (VECTOR_ITEMS(&plist->list, pe, i))
+ if ((prefix == NULL) || prefix_match(&pe->prefix, &p))
+ pe->hitcnt = 0;
+ } ;
+return CMD_SUCCESS;
}
-
+
DEFUN (ip_prefix_list,
ip_prefix_list_cmd,
"ip prefix-list WORD (deny|permit) (A.B.C.D/M|any)",
@@ -1177,7 +1591,7 @@ DEFUN (ip_prefix_list,
"IP prefix <network>/<length>, e.g., 35.0.0.0/8\n"
"Any prefix match. Same as \"0.0.0.0/0 le 32\"\n")
{
- return vty_prefix_list_install (vty, AFI_IP, argv[0], NULL,
+ return vty_prefix_list_install (vty, AFI_IP, argv[0], NULL,
argv[1], argv[2], NULL, NULL);
}
@@ -1193,7 +1607,7 @@ DEFUN (ip_prefix_list_ge,
"Minimum prefix length to be matched\n"
"Minimum prefix length\n")
{
- return vty_prefix_list_install (vty, AFI_IP, argv[0], NULL, argv[1],
+ return vty_prefix_list_install (vty, AFI_IP, argv[0], NULL, argv[1],
argv[2], argv[3], NULL);
}
@@ -1211,7 +1625,7 @@ DEFUN (ip_prefix_list_ge_le,
"Maximum prefix length to be matched\n"
"Maximum prefix length\n")
{
- return vty_prefix_list_install (vty, AFI_IP, argv[0], NULL, argv[1],
+ return vty_prefix_list_install (vty, AFI_IP, argv[0], NULL, argv[1],
argv[2], argv[3], argv[4]);
}
@@ -1547,7 +1961,7 @@ DEFUN (ip_prefix_list_sequence_number,
PREFIX_LIST_STR
"Include/exclude sequence numbers in NVGEN\n")
{
- prefix_master_ipv4.seqnum = 1;
+ prefix_master_ipv4.seqnum_flag = 1;
return CMD_SUCCESS;
}
@@ -1559,7 +1973,7 @@ DEFUN (no_ip_prefix_list_sequence_number,
PREFIX_LIST_STR
"Include/exclude sequence numbers in NVGEN\n")
{
- prefix_master_ipv4.seqnum = 0;
+ prefix_master_ipv4.seqnum_flag = 0;
return CMD_SUCCESS;
}
@@ -1572,19 +1986,9 @@ DEFUN (ip_prefix_list_description,
"Prefix-list specific description\n"
"Up to 80 characters describing this prefix-list\n")
{
- struct prefix_list *plist;
-
- plist = prefix_list_get (AFI_IP, argv[0]);
-
- if (plist->desc)
- {
- XFREE (MTYPE_TMP, plist->desc);
- plist->desc = NULL;
- }
- plist->desc = argv_concat(argv, argc, 1);
-
- return CMD_SUCCESS;
-}
+ return vty_prefix_list_desc_set (vty, AFI_IP, argv[0],
+ argv_concat(argv, argc, 1));
+} ;
DEFUN (no_ip_prefix_list_description,
no_ip_prefix_list_description_cmd,
@@ -1759,7 +2163,7 @@ DEFUN (clear_ip_prefix_list_name_prefix,
{
return vty_clear_prefix_list (vty, AFI_IP, argv[0], argv[1]);
}
-
+
#ifdef HAVE_IPV6
DEFUN (ipv6_prefix_list,
ipv6_prefix_list_cmd,
@@ -1772,7 +2176,7 @@ DEFUN (ipv6_prefix_list,
"IPv6 prefix <network>/<length>, e.g., 3ffe::/16\n"
"Any prefix match. Same as \"::0/0 le 128\"\n")
{
- return vty_prefix_list_install (vty, AFI_IP6, argv[0], NULL,
+ return vty_prefix_list_install (vty, AFI_IP6, argv[0], NULL,
argv[1], argv[2], NULL, NULL);
}
@@ -1788,7 +2192,7 @@ DEFUN (ipv6_prefix_list_ge,
"Minimum prefix length to be matched\n"
"Minimum prefix length\n")
{
- return vty_prefix_list_install (vty, AFI_IP6, argv[0], NULL, argv[1],
+ return vty_prefix_list_install (vty, AFI_IP6, argv[0], NULL, argv[1],
argv[2], argv[3], NULL);
}
@@ -1807,7 +2211,7 @@ DEFUN (ipv6_prefix_list_ge_le,
"Maximum prefix length\n")
{
- return vty_prefix_list_install (vty, AFI_IP6, argv[0], NULL, argv[1],
+ return vty_prefix_list_install (vty, AFI_IP6, argv[0], NULL, argv[1],
argv[2], argv[3], argv[4]);
}
@@ -2143,7 +2547,7 @@ DEFUN (ipv6_prefix_list_sequence_number,
PREFIX_LIST_STR
"Include/exclude sequence numbers in NVGEN\n")
{
- prefix_master_ipv6.seqnum = 1;
+ prefix_master_ipv6.seqnum_flag = 1;
return CMD_SUCCESS;
}
@@ -2155,7 +2559,7 @@ DEFUN (no_ipv6_prefix_list_sequence_number,
PREFIX_LIST_STR
"Include/exclude sequence numbers in NVGEN\n")
{
- prefix_master_ipv6.seqnum = 0;
+ prefix_master_ipv6.seqnum_flag = 0;
return CMD_SUCCESS;
}
@@ -2168,19 +2572,9 @@ DEFUN (ipv6_prefix_list_description,
"Prefix-list specific description\n"
"Up to 80 characters describing this prefix-list\n")
{
- struct prefix_list *plist;
-
- plist = prefix_list_get (AFI_IP6, argv[0]);
-
- if (plist->desc)
- {
- XFREE (MTYPE_TMP, plist->desc);
- plist->desc = NULL;
- }
- plist->desc = argv_concat(argv, argc, 1);
-
- return CMD_SUCCESS;
-}
+ return vty_prefix_list_desc_set (vty, AFI_IP6, argv[0],
+ argv_concat(argv, argc, 1));
+}
DEFUN (no_ipv6_prefix_list_description,
no_ipv6_prefix_list_description_cmd,
@@ -2355,190 +2749,138 @@ DEFUN (clear_ipv6_prefix_list_name_prefix,
return vty_clear_prefix_list (vty, AFI_IP6, argv[0], argv[1]);
}
#endif /* HAVE_IPV6 */
-
+
/* Configuration write function. */
static int
config_write_prefix_afi (afi_t afi, struct vty *vty)
{
struct prefix_list *plist;
- struct prefix_list_entry *pentry;
- struct prefix_master *master;
+ struct prefix_list_entry *pe;
+ struct prefix_master *pm;
int write = 0;
+ vector extract ;
+ vector_index i, ipe ;
+ struct symbol* sym ;
- master = prefix_master_get (afi);
- if (master == NULL)
+ pm = prefix_master_get (afi);
+ if (pm == NULL)
return 0;
- if (! master->seqnum)
+ if (! pm->seqnum_flag)
{
- vty_out (vty, "no ip%s prefix-list sequence-number%s",
+ vty_out (vty, "no ip%s prefix-list sequence-number%s",
afi == AFI_IP ? "" : "v6", VTY_NEWLINE);
vty_out (vty, "!%s", VTY_NEWLINE);
}
- for (plist = master->num.head; plist; plist = plist->next)
+ /* Extract a vector of all prefix_list symbols, in name order. */
+ extract = symbol_table_extract(&pm->table, NULL, NULL, 0,
+ symbol_mixed_name_cmp) ;
+ for (VECTOR_ITEMS(extract, sym, i))
{
- if (plist->desc)
- {
- vty_out (vty, "ip%s prefix-list %s description %s%s",
- afi == AFI_IP ? "" : "v6",
- plist->name, plist->desc, VTY_NEWLINE);
- write++;
- }
-
- for (pentry = plist->head; pentry; pentry = pentry->next)
+ plist = symbol_get_value(sym) ;
+ if (plist)
{
- vty_out (vty, "ip%s prefix-list %s ",
- afi == AFI_IP ? "" : "v6",
- plist->name);
-
- if (master->seqnum)
- vty_out (vty, "seq %d ", pentry->seq);
-
- vty_out (vty, "%s ", prefix_list_type_str (pentry));
-
- if (pentry->any)
- vty_out (vty, "any");
- else
+ if (plist->desc)
{
- struct prefix *p = &pentry->prefix;
- char buf[BUFSIZ];
-
- vty_out (vty, "%s/%d",
- inet_ntop (p->family, &p->u.prefix, buf, BUFSIZ),
- p->prefixlen);
-
- if (pentry->ge)
- vty_out (vty, " ge %d", pentry->ge);
- if (pentry->le)
- vty_out (vty, " le %d", pentry->le);
+ vty_prefix_list_name_print(vty, plist, "") ;
+ vty_out (vty, " description %s%s", plist->desc, VTY_NEWLINE) ;
+ write++ ;
}
- vty_out (vty, "%s", VTY_NEWLINE);
- write++;
- }
- /* vty_out (vty, "!%s", VTY_NEWLINE); */
- }
- for (plist = master->str.head; plist; plist = plist->next)
- {
- if (plist->desc)
- {
- vty_out (vty, "ip%s prefix-list %s description %s%s",
- afi == AFI_IP ? "" : "v6",
- plist->name, plist->desc, VTY_NEWLINE);
- write++;
+ for (VECTOR_ITEMS(&plist->list, pe, ipe))
+ {
+ vty_prefix_list_name_print(vty, plist, " ") ;
+ vty_prefix_list_value_print(vty, pe, VTY_NEWLINE,
+ pm->seqnum_flag, 0) ;
+ write++ ;
+ }
}
-
- for (pentry = plist->head; pentry; pentry = pentry->next)
+ else
{
- vty_out (vty, "ip%s prefix-list %s ",
- afi == AFI_IP ? "" : "v6",
- plist->name);
-
- if (master->seqnum)
- vty_out (vty, "seq %d ", pentry->seq);
-
- vty_out (vty, "%s", prefix_list_type_str (pentry));
-
- if (pentry->any)
- vty_out (vty, " any");
- else
- {
- struct prefix *p = &pentry->prefix;
- char buf[BUFSIZ];
+ vty_puts(vty, "!! ") ;
+ vty_prefix_list_undefined_print(vty, afi, sym, VTY_NEWLINE) ;
+ write++ ;
+ } ;
+ } ;
- vty_out (vty, " %s/%d",
- inet_ntop (p->family, &p->u.prefix, buf, BUFSIZ),
- p->prefixlen);
+ vector_free(extract) ; /* discard temporary vector */
- if (pentry->ge)
- vty_out (vty, " ge %d", pentry->ge);
- if (pentry->le)
- vty_out (vty, " le %d", pentry->le);
- }
- vty_out (vty, "%s", VTY_NEWLINE);
- write++;
- }
- }
-
return write;
}
struct stream *
-prefix_bgp_orf_entry (struct stream *s, struct prefix_list *plist,
+prefix_bgp_orf_entry (struct stream *s, prefix_list_ref ref,
u_char init_flag, u_char permit_flag, u_char deny_flag)
{
- struct prefix_list_entry *pentry;
+ struct prefix_list_entry *pe;
+ vector_index i ;
+
+ struct prefix_list *plist = prefix_list_ref_plist(ref) ;
if (! plist)
return s;
- for (pentry = plist->head; pentry; pentry = pentry->next)
+ for (VECTOR_ITEMS(&plist->list, pe, i))
{
- u_char flag = init_flag;
- struct prefix *p = &pentry->prefix;
-
- flag |= (pentry->type == PREFIX_PERMIT ?
- permit_flag : deny_flag);
- stream_putc (s, flag);
- stream_putl (s, (u_int32_t)pentry->seq);
- stream_putc (s, (u_char)pentry->ge);
- stream_putc (s, (u_char)pentry->le);
- stream_put_prefix (s, p);
+ stream_putc (s, init_flag | (pe->type == PREFIX_PERMIT ? permit_flag
+ : deny_flag));
+ stream_putl (s, (u_int32_t)pe->seq);
+ stream_putc (s, (u_char)pe->ge);
+ stream_putc (s, (u_char)pe->le);
+ stream_put_prefix (s, &pe->prefix);
}
return s;
}
+/* Set or Unset a BGP ORF entry. */
int
prefix_bgp_orf_set (char *name, afi_t afi, struct orf_prefix *orfp,
int permit, int set)
{
- struct prefix_list *plist;
- struct prefix_list_entry *pentry;
+ struct prefix_list *plist ;
+ struct prefix_list_entry temp ;
+ int ret ;
- /* ge and le value check */
- if (orfp->ge && orfp->ge <= orfp->p.prefixlen)
- return CMD_WARNING;
- if (orfp->le && orfp->le <= orfp->p.prefixlen)
- return CMD_WARNING;
- if (orfp->le && orfp->ge > orfp->le)
- return CMD_WARNING;
+ assert_afi_real(afi) ;
- if (orfp->ge && orfp->le == (afi == AFI_IP ? 32 : 128))
- orfp->le = 0;
-
- plist = prefix_list_get (AFI_ORF_PREFIX, name);
- if (! plist)
- return CMD_WARNING;
+ /* Transfer the values from the orf_prefix */
+ prefix_list_entry_init(&temp) ;
- if (set)
+ temp.type = permit ? PREFIX_PERMIT : PREFIX_DENY ;
+ temp.prefix = orfp->p ;
+ temp.seq = orfp->seq ; /* NB: U32 and may be zero */
+ if (orfp->ge)
{
- pentry = prefix_list_entry_make (&orfp->p,
- (permit ? PREFIX_PERMIT : PREFIX_DENY),
- orfp->seq, orfp->le, orfp->ge, 0);
+ temp.flags |= PREFIX_GE ;
+ temp.ge = orfp->ge ;
+ }
+ if (orfp->le)
+ {
+ temp.flags |= PREFIX_LE ;
+ temp.le = orfp->le ;
+ }
- if (prefix_entry_dup_check (plist, pentry))
- {
- prefix_list_entry_free (pentry);
- return CMD_WARNING;
- }
+ /* Make sure ge & le are acceptable and set as required */
+ ret = prefix_list_entry_ge_le_check(&temp, afi) ;
+ if (ret != CMD_SUCCESS)
+ return ret ;
- prefix_list_entry_add (plist, pentry);
+ /* Now insert or delete */
+ if (set)
+ {
+ plist = prefix_list_get(&prefix_master_orf, name, afi);
+ return prefix_list_entry_insert(plist, &temp) ;
}
else
{
- pentry = prefix_list_entry_lookup (plist, &orfp->p,
- (permit ? PREFIX_PERMIT : PREFIX_DENY),
- orfp->seq, orfp->le, orfp->ge);
+ plist = prefix_list_seek(&prefix_master_orf, name) ;
+ if (plist == NULL)
+ return CMD_WARNING ;
- if (! pentry)
- return CMD_WARNING;
-
- prefix_list_entry_delete (plist, pentry, 1);
+ return prefix_list_entry_delete(plist, &temp) ;
}
-
- return CMD_SUCCESS;
}
void
@@ -2555,70 +2897,33 @@ prefix_bgp_orf_remove_all (char *name)
int
prefix_bgp_show_prefix_list (struct vty *vty, afi_t afi, char *name)
{
- struct prefix_list *plist;
- struct prefix_list_entry *pentry;
+ struct prefix_list *plist ;
plist = prefix_list_lookup (AFI_ORF_PREFIX, name);
if (! plist)
return 0;
- if (! vty)
- return plist->count;
-
- vty_out (vty, "ip%s prefix-list %s: %d entries%s",
- afi == AFI_IP ? "" : "v6",
- plist->name, plist->count, VTY_NEWLINE);
-
- for (pentry = plist->head; pentry; pentry = pentry->next)
+ if (vty)
{
- struct prefix *p = &pentry->prefix;
- char buf[BUFSIZ];
-
- vty_out (vty, " seq %d %s %s/%d", pentry->seq,
- prefix_list_type_str (pentry),
- inet_ntop (p->family, &p->u.prefix, buf, BUFSIZ),
- p->prefixlen);
+ struct prefix_list_entry* pe ;
+ vector_index i ;
- if (pentry->ge)
- vty_out (vty, " ge %d", pentry->ge);
- if (pentry->le)
- vty_out (vty, " le %d", pentry->le);
+ vty_prefix_list_name_count_print(vty, plist, VTY_NEWLINE) ;
- vty_out (vty, "%s", VTY_NEWLINE);
+ for (VECTOR_ITEMS(&plist->list, pe, i))
+ {
+ vty_out_indent(vty, 3) ;
+ vty_prefix_list_value_print(vty, pe, VTY_NEWLINE, 1, 1) ;
+ }
}
- return plist->count;
+
+ return vector_end(&plist->list);
}
static void
prefix_list_reset_orf (void)
{
- struct prefix_list *plist;
- struct prefix_list *next;
- struct prefix_master *master;
-
- master = prefix_master_get (AFI_ORF_PREFIX);
- if (master == NULL)
- return;
-
- for (plist = master->num.head; plist; plist = next)
- {
- next = plist->next;
- prefix_list_delete (plist);
- }
- for (plist = master->str.head; plist; plist = next)
- {
- next = plist->next;
- prefix_list_delete (plist);
- }
-
- assert (master->num.head == NULL);
- assert (master->num.tail == NULL);
-
- assert (master->str.head == NULL);
- assert (master->str.tail == NULL);
-
- master->seqnum = 1;
- master->recent = NULL;
+ prefix_master_reset(&prefix_master_orf) ;
}
@@ -2639,38 +2944,14 @@ config_write_prefix_ipv4 (struct vty *vty)
static void
prefix_list_reset_ipv4 (void)
{
- struct prefix_list *plist;
- struct prefix_list *next;
- struct prefix_master *master;
-
- master = prefix_master_get (AFI_IP);
- if (master == NULL)
- return;
-
- for (plist = master->num.head; plist; plist = next)
- {
- next = plist->next;
- prefix_list_delete (plist);
- }
- for (plist = master->str.head; plist; plist = next)
- {
- next = plist->next;
- prefix_list_delete (plist);
- }
-
- assert (master->num.head == NULL);
- assert (master->num.tail == NULL);
-
- assert (master->str.head == NULL);
- assert (master->str.tail == NULL);
-
- master->seqnum = 1;
- master->recent = NULL;
+ prefix_master_reset(&prefix_master_ipv4) ;
}
static void
prefix_list_init_ipv4 (void)
{
+ prefix_master_init(&prefix_master_ipv4) ;
+
install_node (&prefix_node, config_write_prefix_ipv4);
install_element (CONFIG_NODE, &ip_prefix_list_cmd);
@@ -2734,10 +3015,10 @@ prefix_list_init_ipv4 (void)
/* Prefix-list node. */
static struct cmd_node prefix_ipv6_node =
{
- PREFIX_IPV6_NODE,
- "", /* Prefix list has no interface. */
- 1
-};
+ PREFIX_IPV6_NODE,
+ "", /* Prefix list has no interface. */
+ 1
+ };
static int
config_write_prefix_ipv6 (struct vty *vty)
@@ -2748,38 +3029,16 @@ config_write_prefix_ipv6 (struct vty *vty)
static void
prefix_list_reset_ipv6 (void)
{
- struct prefix_list *plist;
- struct prefix_list *next;
- struct prefix_master *master;
-
- master = prefix_master_get (AFI_IP6);
- if (master == NULL)
- return;
-
- for (plist = master->num.head; plist; plist = next)
- {
- next = plist->next;
- prefix_list_delete (plist);
- }
- for (plist = master->str.head; plist; plist = next)
- {
- next = plist->next;
- prefix_list_delete (plist);
- }
-
- assert (master->num.head == NULL);
- assert (master->num.tail == NULL);
-
- assert (master->str.head == NULL);
- assert (master->str.tail == NULL);
-
- master->seqnum = 1;
- master->recent = NULL;
-}
+#ifdef HAVE_IPV6
+ prefix_master_reset(&prefix_master_ipv6) ;
+#endif
+} ;
static void
prefix_list_init_ipv6 (void)
{
+ prefix_master_init(&prefix_master_ipv6) ;
+
install_node (&prefix_ipv6_node, config_write_prefix_ipv6);
install_element (CONFIG_NODE, &ipv6_prefix_list_cmd);
@@ -2847,6 +3106,7 @@ prefix_list_init ()
#ifdef HAVE_IPV6
prefix_list_init_ipv6 ();
#endif /* HAVE_IPV6 */
+ prefix_master_init(&prefix_master_orf) ;
}
void
diff --git a/lib/plist.h b/lib/plist.h
index fb3168a6..97dab78e 100644
--- a/lib/plist.h
+++ b/lib/plist.h
@@ -23,38 +23,21 @@
#ifndef _QUAGGA_PLIST_H
#define _QUAGGA_PLIST_H
+#include "prefix.h"
+#include "symtab.h"
+#include "vector.h"
+
#define AFI_ORF_PREFIX 65535
-enum prefix_list_type
+enum prefix_list_type
{
PREFIX_DENY,
PREFIX_PERMIT,
};
-enum prefix_name_type
-{
- PREFIX_TYPE_STRING,
- PREFIX_TYPE_NUMBER
-};
-
-struct prefix_list
-{
- char *name;
- char *desc;
-
- struct prefix_master *master;
-
- enum prefix_name_type type;
+struct prefix_list ;
- int count;
- int rangecount;
-
- struct prefix_list_entry *head;
- struct prefix_list_entry *tail;
-
- struct prefix_list *next;
- struct prefix_list *prev;
-};
+typedef struct symbol_ref* prefix_list_ref ;
struct orf_prefix
{
@@ -73,11 +56,23 @@ extern void prefix_list_delete_hook (void (*func) (struct prefix_list *));
extern struct prefix_list *prefix_list_lookup (afi_t, const char *);
extern enum prefix_list_type prefix_list_apply (struct prefix_list *, void *);
+extern const char* prefix_list_get_name(struct prefix_list* plist) ;
+
extern struct stream * prefix_bgp_orf_entry (struct stream *,
- struct prefix_list *,
+ prefix_list_ref ref,
u_char, u_char, u_char);
extern int prefix_bgp_orf_set (char *, afi_t, struct orf_prefix *, int, int);
extern void prefix_bgp_orf_remove_all (char *);
extern int prefix_bgp_show_prefix_list (struct vty *, afi_t, char *);
+extern prefix_list_ref prefix_list_set_ref(prefix_list_ref* p_ref, afi_t afi,
+ const char* name) ;
+extern prefix_list_ref prefix_list_copy_ref(prefix_list_ref* p_dst,
+ prefix_list_ref src) ;
+extern prefix_list_ref prefix_list_unset_ref(prefix_list_ref* p_ref) ;
+
+extern const char* prefix_list_ref_name(prefix_list_ref ref) ;
+extern void* prefix_list_ref_ident(prefix_list_ref ref) ;
+extern struct prefix_list* prefix_list_ref_plist(prefix_list_ref ref) ;
+
#endif /* _QUAGGA_PLIST_H */
diff --git a/lib/prefix.c b/lib/prefix.c
index 2afaa09c..6399788c 100644
--- a/lib/prefix.c
+++ b/lib/prefix.c
@@ -17,7 +17,7 @@
* You should have received a copy of the GNU General Public License
* along with GNU Zebra; see the file COPYING. If not, write to the Free
* Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
- * 02111-1307, USA.
+ * 02111-1307, USA.
*/
#include <zebra.h>
@@ -27,7 +27,7 @@
#include "sockunion.h"
#include "memory.h"
#include "log.h"
-
+
/* Maskbit. */
static u_char maskbit[] = {0x00, 0x80, 0xc0, 0xe0, 0xf0,
0xf8, 0xfc, 0xfe, 0xff};
@@ -85,7 +85,7 @@ prefix_match (const struct prefix *n, const struct prefix *p)
if (shift)
if (maskbit[shift] & (np[offset] ^ pp[offset]))
return 0;
-
+
while (offset--)
if (np[offset] != pp[offset])
return 0;
@@ -118,7 +118,7 @@ prefix_copy (struct prefix *dest, const struct prefix *src)
}
}
-/*
+/*
* Return 1 if the address/netmask contained in the prefix structure
* is the same, and else return 0. For this routine, 'same' requires
* that not only the prefix length and the network part be the same,
@@ -214,7 +214,16 @@ prefix_ipv4_free (struct prefix_ipv4 *p)
prefix_free((struct prefix *)p);
}
-/* When string format is invalid return 0. */
+/* When string format is valid return 1 otherwise return 0.
+ *
+ * Some callers of this function treat non-0 as OK and 0 as invalid (which is
+ * what inet_aton() is defined to do).
+ *
+ * Some callers treat > 0 as OK and <= 0 as invalid (which is similar to what
+ * inet_pton() is defined to do).
+ *
+ * The actual returns are consistent with both usages.
+ */
int
str2prefix_ipv4 (const char *str, struct prefix_ipv4 *p)
{
@@ -227,7 +236,7 @@ str2prefix_ipv4 (const char *str, struct prefix_ipv4 *p)
pnt = strchr (str, '/');
/* String doesn't contail slash. */
- if (pnt == NULL)
+ if (pnt == NULL)
{
/* Convert string to prefix. */
ret = inet_aton (str, &p->prefix);
@@ -238,7 +247,7 @@ str2prefix_ipv4 (const char *str, struct prefix_ipv4 *p)
p->family = AF_INET;
p->prefixlen = IPV4_MAX_BITLEN;
- return ret;
+ return 1 ;
}
else
{
@@ -257,7 +266,7 @@ str2prefix_ipv4 (const char *str, struct prefix_ipv4 *p)
p->prefixlen = plen;
}
- return ret;
+ return 1 ;
}
/* Convert masklen into IP address's netmask. */
@@ -273,7 +282,7 @@ masklen2ip (int masklen, struct in_addr *netmask)
offset = masklen / 8;
bit = masklen % 8;
-
+
while (offset--)
*pnt++ = 0xff;
@@ -299,7 +308,7 @@ ip_masklen (struct in_addr netmask)
{
len+= 8;
pnt++;
- }
+ }
if (pnt < end)
{
@@ -342,7 +351,7 @@ prefix_ipv4_any (const struct prefix_ipv4 *p)
{
return (p->prefix.s_addr == 0 && p->prefixlen == 0);
}
-
+
#ifdef HAVE_IPV6
/* Allocate a new ip version 6 route */
@@ -365,7 +374,26 @@ prefix_ipv6_free (struct prefix_ipv6 *p)
prefix_free((struct prefix *)p);
}
-/* If given string is valid return pin6 else return NULL */
+/* If given string is valid IPv6 address or prefix return 1 else return 0
+ *
+ * Of inet_pton() POSIX 1003.1, 2004 says:
+ *
+ * The inet_pton() function shall return 1 if the conversion succeeds, with
+ * the address pointed to by dst in network byte order. It shall return 0 if
+ * the input is not either a valid IPv4 dotted-decimal string or a valid IPv6
+ * address string (if IPv6 supported), or return -1 with errno set to
+ * [EAFNOSUPPORT] if the af argument is unknown.
+ *
+ * Any error returned is reported as an invalid address or prefix. So best not
+ * to call this if IPv6 is not supported.
+ *
+ * Some callers treat > 0 as OK and <= 0 as invalid (which is consistent with
+ * what inet_pton() is defined to do).
+ *
+ * Some callers of this function treat non-0 as OK and 0 as invalid.
+ *
+ * The actual returns are consistent with both usages.
+ */
int
str2prefix_ipv6 (const char *str, struct prefix_ipv6 *p)
{
@@ -376,14 +404,14 @@ str2prefix_ipv6 (const char *str, struct prefix_ipv6 *p)
pnt = strchr (str, '/');
/* If string doesn't contain `/' treat it as host route. */
- if (pnt == NULL)
+ if (pnt == NULL)
{
ret = inet_pton (AF_INET6, str, &p->prefix);
- if (ret == 0)
+ if (ret <= 0)
return 0;
p->prefixlen = IPV6_MAX_BITLEN;
}
- else
+ else
{
int plen;
@@ -392,7 +420,7 @@ str2prefix_ipv6 (const char *str, struct prefix_ipv6 *p)
*(cp + (pnt - str)) = '\0';
ret = inet_pton (AF_INET6, cp, &p->prefix);
free (cp);
- if (ret == 0)
+ if (ret <= 0)
return 0;
plen = (u_char) atoi (++pnt);
if (plen > 128)
@@ -401,7 +429,7 @@ str2prefix_ipv6 (const char *str, struct prefix_ipv6 *p)
}
p->family = AF_INET6;
- return ret;
+ return 1 ;
}
/* Convert struct in6_addr netmask into integer.
@@ -412,19 +440,19 @@ ip6_masklen (struct in6_addr netmask)
int len = 0;
unsigned char val;
unsigned char *pnt;
-
+
pnt = (unsigned char *) & netmask;
- while ((*pnt == 0xff) && len < 128)
+ while ((*pnt == 0xff) && len < 128)
{
len += 8;
pnt++;
- }
-
- if (len < 128)
+ }
+
+ if (len < 128)
{
val = *pnt;
- while (val)
+ while (val)
{
len++;
val <<= 1;
@@ -572,7 +600,7 @@ sockunion2hostprefix (const union sockunion *su)
int
prefix_blen (const struct prefix *p)
{
- switch (p->family)
+ switch (p->family)
{
case AF_INET:
return IPV4_MAX_BYTELEN;
@@ -600,7 +628,7 @@ str2prefix (const char *str, struct prefix *p)
#ifdef HAVE_IPV6
/* Next we try to convert string to struct prefix_ipv6. */
ret = str2prefix_ipv6 (str, (struct prefix_ipv6 *) p);
- if (ret)
+ if (ret <= 0)
return ret;
#endif /* HAVE_IPV6 */
@@ -650,22 +678,22 @@ void apply_classful_mask_ipv4 (struct prefix_ipv4 *p)
{
u_int32_t destination;
-
+
destination = ntohl (p->prefix.s_addr);
-
+
if (p->prefixlen == IPV4_MAX_PREFIXLEN);
/* do nothing for host routes */
- else if (IN_CLASSC (destination))
+ else if (IN_CLASSC (destination))
{
p->prefixlen=24;
apply_mask_ipv4(p);
}
- else if (IN_CLASSB(destination))
+ else if (IN_CLASSB(destination))
{
p->prefixlen=16;
apply_mask_ipv4(p);
}
- else
+ else
{
p->prefixlen=8;
apply_mask_ipv4(p);
@@ -694,7 +722,7 @@ ipv4_broadcast_addr (in_addr_t hostaddr, int masklen)
(hostaddr ^ ~mask.s_addr);
}
-/* Utility function to convert ipv4 netmask to prefixes
+/* Utility function to convert ipv4 netmask to prefixes
ex.) "1.1.0.0" "255.255.0.0" => "1.1.0.0/16"
ex.) "1.0.0.0" NULL => "1.0.0.0/8" */
int
@@ -719,7 +747,7 @@ netmask_str2prefix_str (const char *net_str, const char *mask_str,
prefixlen = ip_masklen (mask);
}
- else
+ else
{
destination = ntohl (network.s_addr);
diff --git a/lib/symtab.c b/lib/symtab.c
new file mode 100644
index 00000000..57a49396
--- /dev/null
+++ b/lib/symtab.c
@@ -0,0 +1,1186 @@
+/* Symbol Table data structure -- functions
+ * Copyright (C) 2009 Chris Hall (GMCH), Highwayman
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * GNU Zebra is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GNU Zebra; see the file COPYING. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include <zebra.h>
+#include <stddef.h>
+
+#include "symtab.h"
+#include "memory.h"
+
+/* A symbol table maps symbol "names" to symbol values and, for each symbol,
+ * has two ways of keeping track of references to the symbol.
+ *
+ * The symbol name can be an arbitrary collection of bytes, or a string. All
+ * names in a symbol table are unique.
+ *
+ * The symbol value is a void* -- whose contents are no concern of this code.
+ *
+ * A symbol table comprises:
+ *
+ * * symbol table structure -- containing all "red-tape"
+ * * array of chain-bases -- for the hash table
+ * * symbol entries -- each containing name and value of a symbol
+ *
+ * The symbol table structure may be statically allocated, embedded in another
+ * structure, or allocated dynamically. In any case the symbol table operations
+ * require the address of the symbol table structure -- see typedef for
+ * symbol_table.
+ *
+ * A symbol table may point to its "parent" -- which could be an enclosing
+ * structure, or any other higher level data. The symbol table code does not
+ * use or need this -- it is for the convenience of the caller.
+ *
+ * A symbol table structure which is zeroised is implicitly an empty symbol
+ * table, using the default symbol name type -- a null terminated string.
+ *
+ * Each Symbol Table requires a hash function, which takes a pointer to
+ * whatever and returns a 32-bit hash value and a canonical form of the
+ * symbol "name". When a new symbol is created the canonical form of the name
+ * is copied to the symbol entry.
+ *
+ * The array of chain-bases is dynamically allocated and will grow to maintain
+ * an approximate given maximum number of symbols per chain base. This density
+ * is set when the symbol table is initialised. The array does not
+ * automatically reduce in size.
+ *
+ * The number of chain bases is always odd. The hash function returns a 32-bit
+ * unsigned value, which is mapped to the chain bases modulo their number.
+ * Since that is odd, it is co-prime with the hash, so contributes to the hash
+ * process.
+ *
+ * Each symbol in the table has a dynamically allocated entry, which includes:
+ *
+ * * symbol value -- void*
+ * * symbol name
+ * * count of references
+ * * list of references
+ *
+ * Symbols may have the value NULL. Deleting a symbol is not the same as
+ * setting its value to NULL. If the value is set NULL and later set to
+ * something else, all references to the symbol will see the new value. If the
+ * symbol is deleted, all references will see its value set to NULL, but if a
+ * new symbol with the same name is later created, all references to the old
+ * symbol are unchanged, and continue to see the (orphan) NULL.
+ *
+ * Keeping track of references is important because it ensures that symbols
+ * can be deleted without leaving dangling references. When a symbol is
+ * deleted, it is removed from the symbol table and its value is set to NULL.
+ * If there are no references to the symbol, the symbol entry is released.
+ * If there are references to the symbol, it is preserved (as an orphan) until
+ * all references are unset. The value of an orphaned symbol should not be
+ * changed (but may be unset, since it is already unset).
+ *
+ * There are two, parallel mechanisms for keeping track of references to a
+ * symbol:
+ *
+ * 1. reference count -- this is for when all that is required is a pointer
+ * to the symbol, ie:
+ *
+ * * ptr_to_symbol = symbol_inc_ref(sym) -- set reference & count up
+ * * ptr_to_symbol = symbol_dec_ref(sym) -- unset reference & count down
+ *
+ * 2. list of references -- this is for when it is useful to visit all
+ * references to a given symbol, for example when the value of the symbol
+ * is set and all users of the symbol need to adjust to the new state.
+ *
+ * A symbol reference contains a pointer to the symbol, and lives on the
+ * list of references to the symbol. When the value is set, a call-back
+ * associated with the table is called (if it is set). That call-back may
+ * walk the list of references to the symbol. Each reference contains a
+ * pointer and a pointer/long int value, which may be used to identify the
+ * reference.
+ *
+ * A symbol reference may be statically allocated, embedded in another
+ * structure, or allocated dynamically. In any case the symbol reference
+ * operations require the address of the symbol reference structure -- see
+ * typedef for symbol_ref.
+ *
+ * Symbol references are the responsibility of of the owner of the reference,
+ * who must look after setting, unsetting and (if required) releasing them.
+ *
+ * It may seem profligate to have two mechanisms. However, it is simpler than
+ * having two types of symbol, or two types of symbol table. It may also be
+ * useful to have both simple references and references for dealing with
+ * value changes. Suppose, for instance, that the value of a symbol has a
+ * pointer to the symbol -- so that the value of the symbol can refer back to
+ * its name, for example. This requires only a simple reference, even if
+ * more generally a list of references is required.
+ *
+ * A symbol table can be walked to visit all symbols, and there is support for
+ * extracting a vector of all symbols which satisfy a given criterion.
+ */
+
+static void symbol_extend_bases(symbol_table table) ;
+static void symbol_free(symbol sym) ;
+
+/* Return true iff there are no references to the given symbol */
+inline static int
+symbol_no_references(symbol sym)
+{
+ return (sym->ref_count == 0) && (sym->ref_list == NULL) ;
+} ;
+
+/* If symbol is an orphan with no references/bookmarks, free it.
+ *
+ * NB: if symbol is an orphan, then it implicitly has a NULL value, because
+ * to become an orphan it must have been deleted, which unsets the value.
+ */
+inline static void
+symbol_free_if_redundant(symbol sym)
+{
+ if ((sym->table == NULL) && symbol_no_references(sym))
+ symbol_free(sym) ;
+} ;
+
+/* Return chain base for given hash value. */
+inline static symbol*
+symbol_base(symbol_table table, u_int32_t hash)
+{
+ return &table->bases[hash % table->base_count] ;
+} ;
+
+/* Initialise a new symbol table -- allocate if required.
+ *
+ * table -- address of table to initialise. NULL -> allocate.
+ * NB: if allocated, it is the caller's responsibility to free.
+ *
+ * parent -- address of some parent or other higher level data structure.
+ * This is not used by the symbol table code and may be NULL if
+ * the caller has no use for it.
+ *
+ * bases -- number of list bases to start the symbol table at.
+ * Symbol table grows as required, but can set initial size if
+ * have some expectations and wish to avoid growth steps.
+ *
+ * density -- %-age of entries/bases. 0 => use default.
+ *
+ * hash_function
+ * -- function to fill in symbol_hash from a given "name".
+ * see: description of struct symbol_hash and default function,
+ * symbol_hash_string, below.
+ * NULL => the names in this symbol table are null terminated
+ * strings, so use symbol_hash_string.
+ *
+ * value_call_back
+ * -- function to be called when the symbol value is set or unset.
+ * (Or the symbol is about to be deleted -- which starts by
+ * unsetting it).
+ *
+ * The function is passed the new state of the symbol and its
+ * previous value.
+ *
+ * The value of the symbol is a pointer to some data. If the
+ * data changes symbol_set_value() must be called if its
+ * address changes and may be called even if its address doesn't
+ * change.
+ *
+ * In any event, value_call_back is called when symbol_set_value()
+ * is called -- except where the symbol is being set to NULL and
+ * the value is already NULL.
+ *
+ * During the call-back the symbol may be set again or unset,
+ * which will cause the call-back to be called inside itself.
+ * Also, references may be set or unset.
+ *
+ * In the call-back the reference list may be walked to signal
+ * to all holders of the reference that something has changed.
+ *
+ * NB: A completely zeroized symbol_table structure is a valid, empty
+ * symbol table. The first time it is used it will be set up to default
+ * state -- in particular: symbol names are null terminated strings (a
+ * state which *cannot* then be changed).
+ *
+ * NB: when this is used to re-initialising an existing symbol table structure,
+ * any existing chain base array, symbols and symbol references are simply
+ * discarded -- which will leak memory and is probably a mistake.
+ */
+symbol_table
+symbol_table_init_new(symbol_table table,
+ void* parent,
+ unsigned int base_count,
+ unsigned int density,
+ symbol_hash_function* hash_function,
+ symbol_call_back_function* value_call_back)
+{
+ assert(base_count <= SYMBOL_TABLE_BASES_MAX) ;
+
+ if (table == NULL)
+ table = XCALLOC(MTYPE_SYMBOL_TABLE, sizeof (struct symbol_table)) ;
+
+ table->parent = parent ;
+
+ table->bases = NULL ; /* Allocated when required */
+ table->base_count = base_count ;
+
+ table->entry_count = 0 ;
+ table->extend_thresh = density ; /* Fixed up when required */
+
+ table->hash_function = hash_function ;
+ symbol_table_set_value_call_back(table, value_call_back) ;
+
+ return table ;
+} ;
+
+/* Set "parent" of symbol table. */
+void
+symbol_table_set_parent(symbol_table table, void* parent)
+{
+ table->parent = parent ;
+} ;
+
+/* Get "parent" of symbol table. */
+void*
+symbol_table_get_parent(symbol_table table)
+{
+ return table->parent ;
+} ;
+
+/* Set the value_call_back */
+void
+symbol_table_set_value_call_back(symbol_table table,
+ symbol_call_back_function* value_call_back)
+{
+ table->value_call_back = value_call_back ;
+} ;
+
+/* Create and set new chain bases and threshold for next extension. */
+/* */
+/* Ensures that the base count is at least the minimum and is odd, */
+/* and returns the value set. */
+static unsigned int
+symbol_table_new_bases(symbol_table table,
+ unsigned int new_base_count, float density)
+{
+ if (new_base_count < SYMBOL_TABLE_BASES_MIN)
+ new_base_count = SYMBOL_TABLE_BASES_MIN ;
+ new_base_count |= 1 ;
+
+ table->bases = XCALLOC(MTYPE_SYMBOL_BASES, new_base_count * sizeof(symbol)) ;
+ table->extend_thresh = new_base_count * density ;
+ return table->base_count = new_base_count ;
+} ;
+
+/* Setup symbol table body for use.
+ *
+ * Used for "lazy" allocation of chain bases and allows symbol_lookup
+ * to operate on a completely zeroized symbol_table structure.
+ */
+static void
+symbol_table_setup(symbol_table table)
+{
+ float density ;
+
+ /* If density was set explicitly, extend_thresh entry is a %age. */
+
+ if (table->extend_thresh != 0)
+ density = (float)table->extend_thresh / (float)100 ;
+ else
+ density = (float)2 ; /* Default density */
+
+ /* Initialise the chain bases -- enforces minimum base_count and odd-ness */
+ symbol_table_new_bases(table, table->base_count, density) ;
+
+ /* make default hash_function explicit. */
+ if (table->hash_function == NULL)
+ table->hash_function = (symbol_hash_function*)symbol_hash_string ;
+} ;
+
+/* Reset symbol table.
+ *
+ * Free the symbol table body, and free the symbol table structure or reset it.
+ *
+ * Return NULL if frees symbol table structure, otherwise the address of same.
+ *
+ * NB: must only be done when the table is empty -- see assertion !
+ */
+symbol_table
+symbol_table_reset(symbol_table table, int free_structure)
+{
+ if (table== NULL)
+ return NULL ; /* allow for already freed table */
+
+ assert(table->entry_count == 0) ;
+
+ if (table->bases)
+ XFREE(MTYPE_SYMBOL_BASES, table->bases);
+
+ if (free_structure)
+ {
+ XFREE(MTYPE_VECTOR, table) ;
+ return NULL ;
+ }
+ else
+ return memset(table, 0, sizeof(struct symbol_table)) ;
+} ;
+
+/* Remove symbol from its symbol table (if any). */
+
+static void
+symbol_remove(symbol sym)
+{
+ symbol_table table ;
+ symbol* base ;
+ symbol prev ;
+
+ table = sym->table ;
+ if (table != NULL) /* Deleted symbols have no parent table. */
+ {
+ assert(table->entry_count != 0) ;
+
+ base = symbol_base(table, sym->hash) ;
+ if (*base == sym)
+ *base = sym->next ;
+ else
+ {
+ prev = *base ;
+ while (prev->next != sym)
+ prev = prev->next ;
+ prev->next = sym->next ;
+ } ;
+
+ sym->table = NULL ; /* Symbol is now an orphan. */
+ --table->entry_count ;
+ } ;
+} ;
+
+/* Free symbol, removing it from the symbol table.
+ *
+ * NB: the value and all references MUST already have been unset, because:
+ *
+ * * any value may well need to be released, and have no idea how to do
+ * that here.
+ *
+ * * similarly, references may need to be released and should not, in
+ * any case, be left dangling.
+ */
+static void
+symbol_free(symbol sym)
+{
+ assert((sym->value == NULL) && symbol_no_references(sym)) ;
+
+ symbol_remove(sym) ; /* Remove from table, if any. */
+
+ XFREE(MTYPE_SYMBOL, sym) ;
+} ;
+
+/* Ream out symbols.
+ *
+ * Delete symbols -- but do not invoke the value_call_back.
+ *
+ * When the table is (or becomes) empty, the chain bases are freed, and the
+ * structure freed or reset (depending on the free_structure argument).
+ *
+ * This is intended for use when the symbol table is being destroyed, and all
+ * references have been, or will be unset.
+ *
+ * Returns the value of the next non-NULL symbol (if any). So may be used, for
+ * example:
+ *
+ * xxxx* val ;
+ * ...
+ * while ((val = symbol_table_ream(table, free_structure)) != NULL)
+ * {
+ * ... do what's required to release the value ...
+ * }
+ *
+ * Noting that the symbol may already have been released when its value is
+ * returned. (If the symbol is required when the value is released, then the
+ * value should hold a simple reference to the symbol.)
+ *
+ * Returns NULL when the table is empty.
+ *
+ * Symbols which have one or more references when they are deleted are left as
+ * orphans, which will be freed when all their references are unset.
+ *
+ * NB: do NOT attempt to do anything else with the symbol table once reaming
+ * has started.
+ *
+ * NB: it is the caller's responsibility to unset all references and release
+ * any that need to be released -- either before or after this operation.
+ */
+void*
+symbol_table_ream(symbol_table table, int free_structure)
+{
+ void* value ;
+ symbol sym ;
+ unsigned int i ;
+
+ /* There are no actual bases until they have been allocated. */
+ i = (table->bases != NULL) ? table->base_count : 0 ;
+
+ while (i--)
+ {
+ while ((sym = table->bases[i]) != NULL)
+ {
+ assert(table->entry_count != 0) ;
+
+ /* the following is effectively symbol_delete, but avoids the */
+ /* value_call_back and returns only if the value is not NULL. */
+
+ table->bases[i] = sym->next ; /* remove from table */
+ --table->entry_count ; /* count down */
+
+ sym->table = NULL ; /* orphan symbol */
+ value = sym->value ; /* pick up value. */
+ sym->value = NULL ; /* and set symbol undefined */
+
+ if (symbol_no_references(sym))
+ symbol_free(sym) ; /* not in table, no value, no references */
+
+ if (value != NULL)
+ {
+ table->base_count = i + 1 ; /* where we've got to */
+ return value ; /* <<< RETURN: caller must deal with value */
+ } ;
+ } ;
+ } ;
+
+ symbol_table_reset(table, free_structure) ;
+ /* asserts(table->entry_count == 0) */
+ return NULL ;
+} ;
+
+/* Look-up name in given symbol table. Add if required.
+ *
+ * Returns NULL if not found and not required to add.
+ *
+ * NB: the name argument is passed to the symbol table's hash function. That
+ * function is required to return with a 32-bit hash
+ *
+ * NB: if required to add, the caller cannot distinguish between a symbol
+ * which did not previously exist, and one which did exist but had no
+ * value and no references. Where that distinction matters, it is
+ * necessary to do an extra lookup.
+ */
+symbol
+symbol_lookup(symbol_table table, const void* name, int add)
+{
+ struct symbol* this ;
+ struct symbol** base ;
+ struct symbol_hash hash ;
+
+ assert(table != NULL) ;
+ if (table->bases == NULL)
+ symbol_table_setup(table) ; /* Lazy allocation of chain bases etc. */
+
+ table->hash_function(&hash, name) ;
+
+ base = symbol_base(table, hash.hash) ;
+ this = *base ;
+ while (this)
+ {
+ if ((this->hash == hash.hash)
+ && (this->name_len == hash.name_len)
+ && (memcmp(this->name, hash.name, this->name_len) == 0))
+ return this ;
+ this = this->next ;
+ } ;
+
+ /* Not found -- quit now if not required to add */
+ if (!add) return NULL ;
+
+ /* Adding: first, carve a new, empty symbol entry */
+ this = XCALLOC(MTYPE_SYMBOL, sizeof(struct symbol) + hash.name_copy_len) ;
+
+ this->table = table ;
+ this->value = NULL ;
+ this->ref_list = NULL ;
+ this->ref_count = 0 ;
+ this->hash = hash.hash ;
+ this->name_len = hash.name_len ;
+ memcpy(this->name, hash.name, hash.name_copy_len) ;
+
+ /* Second, if required, extend the array of list bases. We extend if */
+ /* we have a collision *and* we exceed threshold of number of entries. */
+ if ((*base != NULL) && (table->entry_count > table->extend_thresh))
+ {
+ symbol_extend_bases(table) ;
+ base = symbol_base(table, hash.hash) ;
+ } ;
+
+ /* Third, chain in the new entry, count it in and return */
+ this->next = *base ;
+ *base = this ;
+
+ ++table->entry_count ;
+
+ return this ;
+} ;
+
+/* Delete symbol.
+ *
+ * The first effect of this is to set the symbol value to NULL, which may
+ * trigger a value_call_back etc.
+ *
+ * Then the symbol is removed from the table (and the symbol becomes an orphan).
+ *
+ * Then, if there are no (remaining) references the symbol is freed. Otherwise
+ * the symbol entry remains in existence until there are no more references
+ * (at which point it will finally be destroyed).
+ *
+ * Returns the last value of the symbol -- which may itself need to be
+ * destroyed -- noting that the symbol may already have been released. (If the
+ * symbol is required when the value is released, then the value should hold a
+ * simple reference to the symbol.)
+ *
+ * NB: the effect of deleting a symbol is to leave all remaining references
+ * pointing at an NULL value, orphaned symbol.
+ *
+ * If a new symbol is created with the same name, that will be a
+ * completely different symbol -- references to the old symbol will
+ * continue to be to the vestigial NULL value.
+ *
+ * This is different from setting the symbol value to NULL and later
+ * giving it a new value.
+ *
+ * NB: orphan symbols can be deleted. The effect is to free the symbol if
+ * possible.
+ */
+void*
+symbol_delete(symbol sym)
+{
+ void* old_value = symbol_unset_value(sym) ;
+
+ if (symbol_no_references(sym))
+ symbol_free(sym) ; /* free symbol now if no references */
+ else
+ symbol_remove(sym) ; /* else just remove it from the table -- will be */
+ /* freed when all references are unset. */
+ return old_value ;
+} ;
+
+/* The hash functions provided here use CRC32 as a hash.
+ *
+ * CRC32 is not intended as a hash function, and is not a perfect one.
+ * However it is fast -- requiring a few simple operations per byte. Taken
+ * with the secondary effect of using the hash produced modulo an odd number,
+ * experience suggests this is sufficient.
+ */
+
+static u_int32_t crc_table[] ;
+
+/* Standard symbol string hash function. */
+void
+symbol_hash_string(symbol_hash p_hash, const char* string) {
+ u_int32_t h = 0 ;
+ const char* p = string ;
+
+ while (*p != 0)
+ h = crc_table[(h & 0xFF) ^ (u_int8_t)*p++] ^ (h >> 8) ;
+
+ assert((p - string) < 0xFFFF) ;
+
+ p_hash->hash = h ;
+ p_hash->name = string ;
+ p_hash->name_len = (p - string) ;
+ p_hash->name_copy_len = p_hash->name_len + 1 ;
+} ;
+
+/* Standard symbol byte vector hash function. */
+void
+symbol_hash_bytes(symbol_hash p_hash, const void* bytes, size_t len) {
+ assert(len < 0xFFFF) ;
+
+ u_int32_t h = len ; /* So strings of zeros don't CRC the same ! */
+ const u_int8_t* p = bytes ;
+ const u_int8_t* e = p + len ;
+
+ while (p < e)
+ h = crc_table[(h & 0xFF) ^ *p++] ^ (h >> 8) ;
+
+ p_hash->hash = h ;
+ p_hash->name = (const void*)bytes ;
+ p_hash->name_len = len ;
+ p_hash->name_copy_len = len ;
+} ;
+
+/* Extend the array of list bases. */
+static void
+symbol_extend_bases(symbol_table table)
+{
+ symbol this ;
+ symbol next ;
+ symbol* old_bases ;
+ symbol* new_bases ;
+ symbol* base ;
+ unsigned int new_base_count ;
+ unsigned int old_base_count ;
+
+ old_bases = table->bases ;
+ old_base_count = table->base_count ;
+
+ assert((old_bases != NULL) && (old_base_count != 0)) ;
+
+ /* TODO: should look out for overflowing base_count and requiring */
+ /* impossible amounts of memory ?! */
+
+ new_base_count = (table->base_count | 1) - 1 ; /* trim enforced odd-ness */
+
+ if (new_base_count <= SYMBOL_TABLE_BASES_DOUBLE_MAX)
+ new_base_count *= 2 ;
+ else
+ new_base_count += SYMBOL_TABLE_BASES_DOUBLE_MAX ;
+
+ new_base_count = symbol_table_new_bases(table, new_base_count,
+ (float)table->extend_thresh / table->base_count) ;
+
+ /* Rehome everything on the new chain bases. */
+ new_bases = table->bases ;
+ while (old_base_count--)
+ {
+ next = old_bases[old_base_count] ;
+ while (next != NULL)
+ {
+ this = next ;
+ next = this->next ;
+ base = &new_bases[this->hash % new_base_count] ;
+ this->next = *base ;
+ *base = this ;
+ } ;
+ } ;
+
+ /* Release the old chain bases, and we're done. */
+ XFREE(MTYPE_SYMBOL_BASES, old_bases) ;
+} ;
+
+/*==============================================================================
+ * Reference count handling.
+ *
+ * symbol_inc_ref(sym) -- declared Inline
+ * symbol_dec_ref(sym) -- declared Inline
+ */
+
+/* Zeroise the reference count.*/
+
+symbol
+symbol_zero_ref(symbol sym, int force)
+{
+ assert((sym->ref_count == 1) || force) ;
+
+ sym->ref_count = 0 ;
+ symbol_free_if_redundant(sym) ;
+
+ return NULL ;
+} ;
+
+/*==============================================================================
+ * Reference list handling.
+ *
+ * References are added at the head of the list -- which is significant when
+ * adding references during a symbol reference walk.
+ */
+
+/* Insert symbol_ref at head of symbol's list of references. */
+static inline void
+symbol_add_ref(symbol sym, symbol_ref ref)
+{
+ symbol_ref next = sym->ref_list ;
+ sym->ref_list = ref ;
+ if (next)
+ next->prev = ref ;
+ ref->next = next ;
+ ref->prev = (void*)sym ; /* marker for first on list */
+} ;
+
+/* Clip symbol_ref from symbol's list of references.
+ *
+ * If symbol_ref has already been deleted the prev pointer is NULL, and this
+ * function copes -- and does not need the symbol to be valid (sym may be NULL).
+ */
+static inline void
+symbol_del_ref(symbol sym, symbol_ref ref)
+{
+ symbol_ref prev = ref->prev ;
+ symbol_ref next = ref->next ;
+
+ if (prev != NULL)
+ {
+ if (prev == (void*)sym)
+ {
+ assert(sym->ref_list == ref) ;
+ sym->ref_list = next ;
+ }
+ else
+ prev->next = next ;
+
+ if (next != NULL)
+ next->prev = prev ;
+ } ;
+ ref->next = ref->prev = NULL ;
+} ;
+
+/*==============================================================================
+ * The value_call_back handling and symbol reference list walking.
+ *
+ * If there is one, the value_call_back function is called when the value of
+ * a symbol is set -- except when it is set NULL and is already NULL. Note
+ * that setting the same non-NULL value *does* invoke the value_call_back.
+ *
+ * The value_call_back function is passed the current state of the symbol,
+ * complete with new value, and the old value of the symbol.
+ *
+ * During the value_call_back the symbol reference list may be walked, so that
+ * users of the value may be updated.
+ *
+ * During the value_call_back the symbol may be set, unset or deleted, and
+ * references added or taken away. This may cause nested calls of the
+ * call-back. Note that each call-back holds a reference to the symbol, so if
+ * the symbol is deleted it won't be freed until the outermost call-back
+ * returns.
+ *
+ * Procedure for walking the references to a symbol:
+ *
+ * struct symbol_ref walk ;
+ * symbol sym ;
+ * symbol_ref ref ;
+ * symbol_ref_walk_start(sym, &walk) ;
+ * while ((ref = symbol_ref_walk_step(&walk)) != NULL)
+ * .... whatever
+ * symbol_ref_walk_end(&walk) ;
+ *
+ * NB: it is *essential* to call symbol_ref_walk_end() exactly once at some
+ * time after symbol_ref_walk_start.
+ *
+ * The symbol table walk uses a "bookmark" which is a special from of entry in
+ * the symbol's reference list. This mechanism:
+ *
+ * (a) prevents the symbol being freed while the reference walk is in
+ * progress -- that may happen during symbol_ref_walk_end.
+ *
+ * (b) allows for the current and other references to be set or unset.
+ *
+ * Setting a reference inserts it upstream of the bookmark -- so it will
+ * not be visited during the walk.
+ *
+ * Unsetting a reference that has yet to be visited eliminates it from
+ * the walk.
+ *
+ * Note that setting a reference to refer to the symbol it already
+ * refers to has no effect at all.
+ *
+ * (c) allows the symbol to be defined, undefined or redefined during a
+ * symbol reference walk.
+ *
+ * If that triggers another symbol reference walk, then that walk will
+ * proceed until it hits the point reached by the walk it is nested
+ * inside, and then stop.
+ *
+ * Suppose the outer walk was dealing with the value having changed from
+ * 'A' to 'B'. The inner walk will do from 'B' to the latest value 'C'
+ * for the references that have already seen 'A' to 'B'. When the outer
+ * walk resumes, it will deal with the change 'A' to 'C', unaware of the
+ * intermediate step.
+ *
+ * If that does not suit, don't fiddle with symbol values during a
+ * symbol reference walk.
+ */
+
+/* Bookmarks are symbol_ref structures, distinguished from ordinary symbol_ref
+ * structures by setting the sym field to point at the bookmark symbol_ref
+ * itself.
+ *
+ * (It would be nicer to use the parent field for this... but what is put
+ * there in ordinary symbol_ref structures is not guaranteed...)
+ */
+static inline int
+symbol_ref_is_bookmark(symbol_ref ref)
+{
+ return (void*)ref->sym == (void*)ref ;
+} ;
+
+/* Start walk of symbol references */
+void
+symbol_ref_walk_start(symbol sym, symbol_ref walk)
+{
+ symbol_init_ref(walk) ; /* keeping things tidy */
+ walk->sym = (void*)walk ; /* bookmark signature */
+ walk->parent = sym ;
+ symbol_add_ref(sym, walk) ; /* insert bookmark at head of list */
+} ;
+
+/* Step walk and return the next reference (if any). */
+symbol_ref
+symbol_ref_walk_step(symbol_ref walk)
+{
+ symbol_ref next_ref ;
+
+ assert(symbol_ref_is_bookmark(walk)) ; /* must be a bookmark ! */
+
+ /* Pick up reference following the bookmark, before deleting it. */
+ next_ref = walk->next ;
+ symbol_del_ref((symbol)walk->parent, walk) ;
+
+ /* Stop immediately if bookmark was at the end of the list or the next */
+ /* item is a bookmark (for a walk that started earlier). */
+ if ((next_ref == NULL) || symbol_ref_is_bookmark(next_ref))
+ return NULL ;
+
+ /* Now we move the bookmark from where it is now to after next_ref. */
+
+ walk->next = next_ref->next ;
+ next_ref->next = walk ;
+ walk->prev = next_ref ;
+ if (walk->next != NULL)
+ walk->next->prev = walk ;
+
+ /* Return the next real reference to be processed. */
+ return next_ref ;
+} ;
+
+/* End of symbol reference walk.
+ *
+ * NB: if the symbol is not defined and has no references or bookmarks it
+ * will now be freed.
+ */
+void
+symbol_ref_walk_end(symbol_ref walk)
+{
+ assert(symbol_ref_is_bookmark(walk)) ; /* must be a bookmark ! */
+
+ symbol_del_ref((symbol)(walk->parent), walk) ; /* make sure */
+
+ symbol_free_if_redundant((symbol)(walk->parent)) ;
+} ;
+
+/*==============================================================================
+ * Symbol Value handling.
+ */
+
+/* Set symbol value. NB: setting to NULL == symbol_unset_value.
+ * NB: setting same value as currently looks like a change.
+ * (except for setting NULL to NULL !)
+ *
+ * Invokes change call-back, if any -- except when setting to NULL and is
+ * already NULL.
+ *
+ * It is possible for the call-back to set the value again, to unset it, to
+ * change references, etc.
+ *
+ * Returns previous value -- which may require releasing.
+ */
+void*
+symbol_set_value(symbol sym, void* new_value)
+{
+ void* old_value ;
+
+ old_value = sym->value ;
+ sym->value = new_value ;
+
+ if (sym->table == NULL) /* watch out for orphans */
+ {
+ assert((new_value == NULL) && (old_value == NULL)) ;
+ return NULL ;
+ } ;
+
+ /* Invoke value_call_back (if any). */
+ /* Note that the value_call_back may set/unset references and/or */
+ /* define/undefine the value. */
+ if (((sym)->table->value_call_back != NULL)
+ && ( (new_value != NULL) || (old_value != NULL) ))
+ {
+ symbol_inc_ref(sym) ; /* preserve until call-back returns */
+ sym->table->value_call_back(sym, old_value) ;
+ symbol_dec_ref(sym) ; /* may now free if has been deleted */
+ } ;
+
+ return old_value ;
+} ;
+
+/*==============================================================================
+ * Symbol Reference handling.
+ *
+ * Implementation note: the next and prev pointers in the symbol_ref structure
+ * are significant only if the sym pointer is not NULL.
+ */
+
+/* Initialise symbol reference -- allocate if required. */
+symbol_ref
+symbol_init_ref(symbol_ref ref)
+{
+ if (ref == NULL)
+ return XCALLOC(MTYPE_SYMBOL_REF, sizeof(struct symbol_ref)) ;
+ else
+ return memset(ref, 0, sizeof(struct symbol_ref)) ;
+} ;
+
+/* Set symbol reference -- allocate if required (ref == NULL).
+ *
+ * NB: does nothing if reference already set to the given symbol.
+ *
+ * NB: unsets (but does not free) reference if was not NULL (and is not
+ * same as symbol being set to) before setting new reference.
+ *
+ * NB: setting reference to NULL unsets any existing reference, but does NOT
+ * release the reference structure.
+ *
+ * NB: if reference is allocated, the parent is set NULL and the tag is set
+ * NULL/0.
+ *
+ * if reference is not allocated, the parent and tag are unchanged.
+ */
+symbol_ref
+symbol_set_ref(symbol_ref ref, struct symbol* sym)
+{
+ if (ref != NULL)
+ {
+ if (ref->sym == sym)
+ return ref ; /* Nothing more to do if already set to given value */
+ if (ref->sym != NULL)
+ symbol_unset_ref_keep(ref) ;
+ }
+ else
+ ref = symbol_init_ref(NULL) ;
+
+ ref->sym = sym ;
+ if (sym != NULL)
+ symbol_add_ref(sym, ref) ;
+
+ return ref ;
+} ;
+
+/* Unset symbol reference. Free the structure if required.
+ *
+ * NB: does nothing if address of reference is NULL.
+ *
+ * NB: if reference is not freed, the parent and tag are unchanged.
+ *
+ * NB: removing the last reference to an symbol that has been deleted causes
+ * the symbol to be freed.
+ *
+ * NB: copes if the reference is already unset, of course.
+ */
+symbol_ref
+symbol_unset_ref(symbol_ref ref, int free_ref_structure)
+{
+ if (ref == NULL) return ref ;
+
+ if (ref->sym != NULL) /* NULL => reference already unset */
+ {
+ symbol_del_ref(ref->sym, ref) ;
+ symbol_free_if_redundant(ref->sym) ;
+ ref->sym = NULL ;
+ } ;
+
+ if (free_ref_structure)
+ XFREE(MTYPE_SYMBOL_REF, ref) ; /* ref is set to NULL */
+
+ return ref ;
+} ;
+
+/*==============================================================================
+ * Walking a symbol table
+ *
+ * Simple walk: visits all entries in the table, in the order they are hashed
+ * to. Simple iterator.
+ *
+ * Extract: makes vector of pointers to selected entries, and sorts that
+ * vector as required.
+ */
+
+/* Walk the given symbol table. Usage:
+ *
+ * struct symbol_walker walker ;
+ * symbol sym ;
+ * ....
+ * symbol_walk_start(table, &walker) ;
+ * while ((sym = symbol_walk_next(&walker)))
+ * ....
+ *
+ * NB: it is possible to change the current symbol while the walk is in
+ * progress -- up to and including deleting it. Any other changes to
+ * the table must NOT be attempted.
+ */
+void
+symbol_walk_start(symbol_table table, struct symbol_walker* walker)
+{
+ walker->next = NULL ;
+ walker->base = table->bases ;
+ walker->base_count = table->base_count ;
+} ;
+
+symbol
+symbol_walk_next(struct symbol_walker* walker)
+{
+ symbol this = walker->next ;
+
+ while (this == NULL)
+ {
+ if (walker->base_count == 0)
+ return NULL ;
+ --walker->base_count ;
+ this = *(walker->base++) ;
+ } ;
+
+ walker->next = this->next ;
+ return this ;
+} ;
+
+/* Extract Symbols.
+ *
+ * Walk symbol table and select symbols to add to a new vector. Then sort the
+ * vector, if required. Takes:
+ *
+ * -- selector: NULL => select all
+ * -- p_val: pointer is passed to the select function (if any)
+ * -- most: if there is a select function, this flag hints that most of
+ * the symbols will be selected -- so it is worth preallocating
+ * a vector big enough for all symbols.
+ * -- sort: NULL => no sort (!)
+ *
+ * NB: the vector contains pointers to the selected symbols. It is the
+ * caller's responsibility to avoid deleting any symbol whose pointer
+ * in the vector they expect to rely on !
+ */
+vector
+symbol_table_extract(symbol_table table,
+ symbol_select_cmp* selector, const void* p_val, int most,
+ symbol_sort_cmp* sort)
+{
+ vector extract ;
+ symbol* base ;
+ unsigned int count ;
+ symbol sym ;
+
+ extract = vector_init_new(NULL, (most || (selector == NULL))
+ ? table->entry_count : 8) ;
+ base = table->bases ;
+
+ if (base == NULL)
+ return extract ; /* Quit if symbol table is "reset" */
+
+ count = table->base_count ;
+ while (count--)
+ {
+ sym = *base++ ;
+ while (sym != NULL)
+ {
+ if ((selector == NULL) || selector(sym, p_val))
+ vector_push_item(extract, sym) ;
+ sym = sym->next ;
+ } ;
+ } ;
+
+ if (sort != NULL)
+ vector_sort(extract, (vector_sort_cmp*)sort) ;
+
+ return extract ;
+} ;
+
+/*==============================================================================
+ * Some common comparison functions for symbol table extracts.
+ */
+
+/* Comparison function to sort names which are a mixture of digits and other
+ * characters.
+ *
+ * This comparison treats substrings of digits as numbers, so "a10" is > "a1".
+ */
+int
+symbol_mixed_name_cmp(const symbol* p_a,
+ const symbol* p_b)
+{
+ const char* a = symbol_get_name(*p_a) ;
+ const char* b = symbol_get_name(*p_b) ;
+ int la, lb ;
+
+ while (1) {
+ if (isdigit(*a) && isdigit(*b))
+ {
+ char* as ; /* Required to stop the compiler whining */
+ char* bs ;
+ unsigned long int na = strtoul(a, (char** restrict)&as, 10) ;
+ unsigned long int nb = strtoul(b, (char** restrict)&bs, 10) ;
+ if (na != nb)
+ return (na < nb) ? -1 : +1 ;
+ a = as ;
+ b = bs ;
+ }
+ else
+ {
+ if (*a != *b)
+ return (*a < *b) ? -1 : +1 ;
+ if (*a == '\0')
+ break ;
+ ++a ;
+ ++b ;
+ }
+ } ;
+
+ /* Looks like the names are equal.
+ * But may be different lengths if have number part(s) with leading zeros,
+ */
+
+ la = symbol_get_name_len(*p_a) ;
+ lb = symbol_get_name_len(*p_b) ;
+ if (la != lb)
+ return (la < lb) ? -1 : +1 ;
+ return 0 ;
+} ;
+
+/*==============================================================================
+ * Table for generating CRC-32 -- Standard (0x1_04C1_1DB7 0xEDB8_8320)
+ */
+static u_int32_t crc_table[] =
+{
+ 0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA, 0x076DC419, 0x706AF48F,
+ 0xE963A535, 0x9E6495A3, 0x0EDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988,
+ 0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, 0x90BF1D91, 0x1DB71064, 0x6AB020F2,
+ 0xF3B97148, 0x84BE41DE, 0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7,
+ 0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC, 0x14015C4F, 0x63066CD9,
+ 0xFA0F3D63, 0x8D080DF5, 0x3B6E20C8, 0x4C69105E, 0xD56041E4, 0xA2677172,
+ 0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B, 0x35B5A8FA, 0x42B2986C,
+ 0xDBBBC9D6, 0xACBCF940, 0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59,
+ 0x26D930AC, 0x51DE003A, 0xC8D75180, 0xBFD06116, 0x21B4F4B5, 0x56B3C423,
+ 0xCFBA9599, 0xB8BDA50F, 0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924,
+ 0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D, 0x76DC4190, 0x01DB7106,
+ 0x98D220BC, 0xEFD5102A, 0x71B18589, 0x06B6B51F, 0x9FBFE4A5, 0xE8B8D433,
+ 0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818, 0x7F6A0DBB, 0x086D3D2D,
+ 0x91646C97, 0xE6635C01, 0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E,
+ 0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457, 0x65B0D9C6, 0x12B7E950,
+ 0x8BBEB8EA, 0xFCB9887C, 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65,
+ 0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2, 0x4ADFA541, 0x3DD895D7,
+ 0xA4D1C46D, 0xD3D6F4FB, 0x4369E96A, 0x346ED9FC, 0xAD678846, 0xDA60B8D0,
+ 0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9, 0x5005713C, 0x270241AA,
+ 0xBE0B1010, 0xC90C2086, 0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F,
+ 0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4, 0x59B33D17, 0x2EB40D81,
+ 0xB7BD5C3B, 0xC0BA6CAD, 0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A,
+ 0xEAD54739, 0x9DD277AF, 0x04DB2615, 0x73DC1683, 0xE3630B12, 0x94643B84,
+ 0x0D6D6A3E, 0x7A6A5AA8, 0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1,
+ 0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE, 0xF762575D, 0x806567CB,
+ 0x196C3671, 0x6E6B06E7, 0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC,
+ 0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5, 0xD6D6A3E8, 0xA1D1937E,
+ 0x38D8C2C4, 0x4FDFF252, 0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B,
+ 0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60, 0xDF60EFC3, 0xA867DF55,
+ 0x316E8EEF, 0x4669BE79, 0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236,
+ 0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F, 0xC5BA3BBE, 0xB2BD0B28,
+ 0x2BB45A92, 0x5CB36A04, 0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D,
+ 0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A, 0x9C0906A9, 0xEB0E363F,
+ 0x72076785, 0x05005713, 0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38,
+ 0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21, 0x86D3D2D4, 0xF1D4E242,
+ 0x68DDB3F8, 0x1FDA836E, 0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777,
+ 0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C, 0x8F659EFF, 0xF862AE69,
+ 0x616BFFD3, 0x166CCF45, 0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2,
+ 0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB, 0xAED16A4A, 0xD9D65ADC,
+ 0x40DF0B66, 0x37D83BF0, 0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9,
+ 0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6, 0xBAD03605, 0xCDD70693,
+ 0x54DE5729, 0x23D967BF, 0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94,
+ 0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D,
+} ;
diff --git a/lib/symtab.h b/lib/symtab.h
new file mode 100644
index 00000000..7b4e002c
--- /dev/null
+++ b/lib/symtab.h
@@ -0,0 +1,318 @@
+/* Symbol Table data structure -- header
+ * Copyright (C) 2009 Chris Hall (GMCH), Highwayman
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * GNU Zebra is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GNU Zebra; see the file COPYING. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef _ZEBRA_SYMTAB_H
+#define _ZEBRA_SYMTAB_H
+
+#include "vector.h"
+
+/* Macro in case there are particular compiler issues. */
+#ifndef Inline
+ #define Inline static inline
+#endif
+
+/* Maximum number of symbol table bases -- something has gone tragically wrong
+ * if we hit this. Assume can multiply this by 2 and get valid size_t result.
+ */
+#define SYMBOL_TABLE_BASES_MAX (1024 * 1024 * 1024)
+
+/* Minimum number of symbol table bases. */
+#define SYMBOL_TABLE_BASES_MIN 10
+/* Point at which stops doubling the symbol table size (bases) */
+#define SYMBOL_TABLE_BASES_DOUBLE_MAX 2000
+
+/* Structures defined below. */
+struct symbol_table ;
+struct symbol ;
+struct symbol_ref ;
+struct symbol_hash ;
+
+typedef struct symbol_table* symbol_table ;
+typedef struct symbol* symbol ;
+typedef struct symbol_ref* symbol_ref ;
+typedef struct symbol_hash* symbol_hash ;
+
+/* Function types used. */
+typedef void symbol_hash_function(symbol_hash hash, const void* name) ;
+typedef void symbol_call_back_function(symbol sym, void* value) ;
+typedef void symbol_destroy_function(symbol sym) ;
+
+/* Symbol Table.
+ *
+ * Don't fiddle with this directly... see access functions below.
+ */
+
+struct symbol_table
+{
+ void* parent ; /* to identify the table. */
+
+ symbol* bases ; /* ref:array of chain bases */
+ unsigned int base_count ; /* number of chain bases */
+
+ unsigned int entry_count ; /* number of entries in the table */
+ unsigned int extend_thresh ; /* when to extend the hash table */
+
+ symbol_hash_function* hash_function ; /* function to hash given "name" */
+ /* NULL => use default */
+ symbol_call_back_function* value_call_back ;
+ /* called when symbol value is set */
+} ;
+
+/* Symbol Table Entry.
+ *
+ * Don't fiddle with this directly... see access macros/functions below.
+ */
+
+struct symbol
+{
+ symbol_table table ; /* so can go from symbol to enclosing table */
+ /* NULL => orphan symbol, with NULL value. */
+
+ symbol next ; /* assume chains are short and seldom remove */
+ /* symbols -- so single pointer will do. */
+
+ void* value ; /* see: symbol_get_value(sym) etc. */
+
+ symbol_ref ref_list ; /* list of symbol_ref references */
+ unsigned ref_count ; /* count of simple references */
+
+ u_int32_t hash ; /* used in lookup and when extending bases. */
+
+ u_int16_t name_len ; /* see: symbol_get_name_len(sym) */
+ char name[] ; /* see: symbol_get_name(sym) */
+} ;
+
+/* Symbol Reference (or "bookmark").
+ *
+ * Don't fiddle with this directly... see access macros/functions below
+ */
+
+typedef union
+{
+ void* p ;
+ unsigned long u ;
+ signed long i ;
+} symbol_ref_tag_t ;
+
+struct symbol_ref
+{
+ symbol sym ; /* Address of symbol referred to (if any). */
+ /* (In "bookmark" this points to self.) */
+
+ symbol_ref next ; /* fellow references to the symbol ... */
+ symbol_ref prev ; /* ... ignore if sym is NULL. */
+
+ void* parent ; /* see: sym_ref_parent(sym_ref) etc. */
+ symbol_ref_tag_t tag ; /* see: sym_ref_tag(sym_ref) etc. */
+} ;
+
+/* Result of a hash function for a symbol name. */
+struct symbol_hash
+{
+ u_int32_t hash ; /* the hash value ! */
+ const void* name ; /* symbol name as byte vector */
+ u_int16_t name_len ; /* length in chars for comparison purposes */
+ u_int16_t name_copy_len ; /* number of chars to copy to store name. */
+} ;
+
+/* Symbol Walk Iterator */
+struct symbol_walker
+{
+ symbol next ; /* next symbol to return (if any) */
+ symbol* base ; /* next chain base to process (if any) */
+ unsigned base_count ; /* number of chain bases left to process */
+} ;
+
+/* Symbol Table Operations. */
+
+extern symbol_table
+symbol_table_init_new(symbol_table table,
+ void* parent,
+ unsigned bases,
+ unsigned density,
+ symbol_hash_function* hash_function,
+ symbol_call_back_function* value_call_back) ;
+void symbol_table_set_parent(symbol_table table, void* parent) ;
+void* symbol_table_get_parent(symbol_table table) ;
+void* symbol_table_ream(symbol_table table, int free_structure) ;
+#define symbol_table_ream_free(table) symbol_table_ream(table, 1)
+#define symbol_table_ream_keep(table) symbol_table_ream(table, 0)
+symbol_table symbol_table_reset(symbol_table table, int free_structure) ;
+#define symbol_table_reset_free(table) symbol_table_reset(table, 1)
+#define symbol_table_reset_keep(table) symbol_table_reset(table, 0)
+
+extern void symbol_hash_string(struct symbol_hash* p_hash, const char* string) ;
+extern void symbol_hash_bytes(struct symbol_hash* p_hash, const void* bytes,
+ size_t len) ;
+extern void symbol_table_set_value_call_back(symbol_table table,
+ symbol_call_back_function* value_call_back) ;
+
+extern void symbol_table_free(symbol_table) ;
+
+extern symbol symbol_lookup(symbol_table table, const void* name, int add) ;
+
+#define symbol_seek(table, name) symbol_lookup(table, name, 0)
+#define symbol_find(table, name) symbol_lookup(table, name, 1)
+
+extern void* symbol_delete(symbol sym) ;
+
+extern void* symbol_set_value(symbol sym, void* new_value) ;
+#define symbol_unset_value(sym) symbol_set_value(sym, NULL)
+
+void symbol_ref_walk_start(symbol sym, symbol_ref walk) ;
+symbol_ref symbol_ref_walk_step(symbol_ref walk) ;
+void symbol_ref_walk_end(symbol_ref walk) ;
+
+void symbol_walk_start(symbol_table table, struct symbol_walker* walker) ;
+symbol symbol_walk_next(struct symbol_walker* walker) ;
+
+typedef int symbol_select_cmp(const symbol, const void*) ;
+typedef int symbol_sort_cmp(const symbol*, const symbol*) ;
+vector symbol_table_extract(symbol_table table,
+ symbol_select_cmp* select, const void* p_value,
+ int most, symbol_sort_cmp* sort) ;
+
+extern symbol_sort_cmp symbol_mixed_name_cmp ;
+
+/* Access functions -- argument is address of symbol (may be NULL). */
+Inline void*
+symbol_get_value(const symbol sym)
+{
+ return (sym != NULL) ? sym->value : NULL ;
+} ;
+
+Inline const void*
+symbol_get_name(const symbol sym)
+{
+ return (sym != NULL) ? sym->name : NULL ;
+} ;
+
+Inline unsigned
+symbol_get_name_len(const symbol sym)
+{
+ return (sym != NULL) ? sym->name_len : 0 ;
+} ;
+
+Inline struct symbol_table*
+symbol_get_table(const symbol sym)
+{
+ return (sym != NULL) ? sym->table : NULL ;
+} ;
+
+Inline symbol
+symbol_inc_ref(symbol sym)
+{
+ ++sym->ref_count ;
+ return sym ;
+} ;
+
+extern symbol symbol_zero_ref(symbol sym, int force) ;
+
+Inline symbol
+symbol_dec_ref(symbol sym)
+{
+ if (sym->ref_count <= 1)
+ return symbol_zero_ref(sym, 0) ;
+
+ --sym->ref_count ;
+ return sym ;
+} ;
+
+extern symbol_ref
+symbol_init_ref(symbol_ref ref) ;
+
+extern symbol_ref
+symbol_set_ref(symbol_ref ref, symbol sym) ;
+
+extern symbol_ref
+symbol_unset_ref(symbol_ref ref, int free_ref_structure) ;
+
+#define symbol_unset_ref_free(ref) symbol_unset_ref(ref, 1) ;
+#define symbol_unset_ref_keep(ref) symbol_unset_ref(ref, 0) ;
+
+/* Access functions -- argument is address of symbol_ref. */
+/* These cope if address of symbol_ref is null, or reference is undefined. */
+Inline void*
+sym_ref_symbol(symbol_ref ref)
+{
+ return (ref != NULL) ? ref->sym : NULL ;
+}
+Inline void*
+sym_ref_value(symbol_ref ref)
+{
+ return symbol_get_value(sym_ref_symbol(ref)) ;
+}
+Inline const void*
+sym_ref_name(symbol_ref ref)
+{
+ return symbol_get_name(sym_ref_symbol(ref)) ;
+}
+Inline u_int16_t
+sym_ref_name_len(symbol_ref ref)
+{
+ return symbol_get_name_len(sym_ref_symbol(ref)) ;
+}
+
+Inline void*
+sym_ref_parent(symbol_ref ref)
+{
+ return (ref != NULL) ? ref->parent : NULL ;
+}
+Inline void*
+sym_ref_p_tag(symbol_ref ref)
+{
+ return (ref != NULL) ? ref->tag.p : NULL ;
+}
+Inline unsigned long int
+sym_ref_u_tag(symbol_ref ref)
+{
+ return (ref != NULL) ? ref->tag.u : 0 ;
+}
+Inline signed long int
+sym_ref_i_tag(symbol_ref ref)
+{
+ return (ref != NULL) ? ref->tag.i : 0 ;
+}
+
+/* Set properties of reference -- argument is address of symbol_ref, which is */
+/* assumed to NOT be NULL. */
+Inline void
+sym_ref_set_parent(symbol_ref ref, void* pa)
+{
+ ref->parent = pa ;
+}
+Inline void
+sym_ref_set_p_tag(symbol_ref ref, void* p_tag)
+{
+ ref->tag.p = p_tag ;
+}
+Inline void
+sym_ref_set_u_tag(symbol_ref ref, unsigned long int u_tag)
+{
+ ref->tag.u = u_tag ;
+}
+Inline void
+sym_ref_set_i_tag(symbol_ref ref, signed long int i_tag)
+{
+ ref->tag.i = i_tag ;
+}
+
+#endif /* _ZEBRA_SYMTAB_H */
diff --git a/lib/vector.c b/lib/vector.c
index 7c148628..8268c830 100644
--- a/lib/vector.c
+++ b/lib/vector.c
@@ -1,6 +1,9 @@
-/* Generic vector interface routine
+/* Generic vector interface routine -- functions
* Copyright (C) 1997 Kunihiro Ishiguro
*
+ * 24-Nov-2009 -- extended to add a number of new operations on vectors.
+ * Copyright (C) 2009 Chris Hall (GMCH), Highwayman
+ *
* This file is part of GNU Zebra.
*
* GNU Zebra is free software; you can redistribute it and/or modify it
@@ -16,7 +19,7 @@
* You should have received a copy of the GNU General Public License
* along with GNU Zebra; see the file COPYING. If not, write to the Free
* Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
- * 02111-1307, USA.
+ * 02111-1307, USA.
*/
#include <zebra.h>
@@ -24,87 +27,757 @@
#include "vector.h"
#include "memory.h"
-/* Initialize vector : allocate memory and return vector. */
+/* Vectors are implemented as a structure which points to an array of pointers
+ * to vector items. That array -- the body of the vector -- can change size,
+ * and therefore may move around in memory.
+ *
+ * The vector structure may be statically allocated, embedded in another
+ * structure, or allocated dynamically. In any case the vector operations
+ * require the address of the vector structure -- see typedef for vector.
+ *
+ * Vector items are accessed by index, fetching and storing pointers to
+ * those items. Can also push and pop items.
+ *
+ * A vector has a known (logical) end position. Everything beyond the end is
+ * defined to be NULL. When a vector is initialised it is set empty.
+ *
+ * At any given moment the vector body has a limit on the number of items it
+ * can accommodate (the physical end).
+ *
+ * A vector will grow to accommodate what is put into it. Adding items beyond
+ * the (logical) end moves it. Adding items beyond the (physical) limit causes
+ * the body to be extended to suit, and to leave some spare space for future
+ * expansion.
+ *
+ * While the vector is small (see VECTOR_LIMIT_DOUBLE_MAX) the body will grow by
+ * doubling in size. When it is larger, it grows to be multiples of
+ * VECTOR_LIMIT_DOUBLE_MAX.
+ *
+ * Deleting items reduces the (logical) end position, but does NOT release
+ * memory -- the (physical) limit is not changed.
+ *
+ * To release memory: vector_chop will release everything beyond the current
+ * end; vector_decant will create a new body of exactly the current size,
+ * releasing the old body; vector_discard will release everything beyond a
+ * given position.
+ *
+ * NB: you can set a vector item to be NULL. If you set a vector item beyond
+ * the current end, NULL items are inserted in the vector.
+ *
+ * NB: when setting a vector item it is the caller's responsibility to
+ * deallocate any pre-existing value of the item.
+ *
+ * NB: when deleting items it is also the caller's responsibility to deallocate
+ * any values that require it.
+ *
+ * Implementation Notes
+ *
+ * Everything beyond the (logical) end is implicitly NULL.
+ *
+ * Actual memory between (logical) end and (physical) limit is UNDEFINED. So
+ * when advancing the end some care has to be taken to ensure that any new
+ * items in the vector are either set to something or cleared to NULL.
+ *
+ * It would have been possible to ensure that everything between end and limit
+ * is cleared to NULL, but that is more work -- in particular it creates work
+ * when it is not always required.
+ */
+
+#define P_ITEMS_SIZE(n) SIZE(p_vector_item, n)
+/*==============================================================================
+ * Initialisation, allocation, reset etc.
+ */
+
+/* Initialise a brand new vector, setting it empty.
+ *
+ * Allocates vector structure if none given -- that is, if v == NULL.
+ *
+ * If size is given as zero, no body is allocated, otherwise body of exactly
+ * the required size is allocated.
+ *
+ * NB: discards any existing vector body -- so it is the caller's responsibility
+ * to release any existing body, and any items in that body.
+ */
vector
-vector_init (unsigned int size)
+vector_init_new(vector v, unsigned int size)
{
- vector v = XCALLOC (MTYPE_VECTOR, sizeof (struct _vector));
+ if (v == NULL)
+ v = XCALLOC(MTYPE_VECTOR, sizeof(struct vector)) ;
+ else
+ memset(v, 0, sizeof(struct vector)) ;
- /* allocate at least one slot */
- if (size == 0)
- size = 1;
+ if (size != 0)
+ {
+ v->p_items = XMALLOC(MTYPE_VECTOR_BODY, P_ITEMS_SIZE(size)) ;
+ v->limit = size ;
+ } ;
- v->alloced = size;
- v->active = 0;
- v->index = XCALLOC (MTYPE_VECTOR_INDEX, sizeof (void *) * size);
- return v;
-}
+ return v ;
+} ;
+/* Initialize vector : allocate memory and return vector.
+ * allocates body with at least 1 entry.
+ */
+vector
+vector_init (unsigned int size)
+{
+ return vector_init_new(NULL, size ? size : 1) ; /* at least 1 entry */
+} ;
+
+/* Basic: free the given vector structure. NB: Orphans any existing body !! */
void
vector_only_wrapper_free (vector v)
{
XFREE (MTYPE_VECTOR, v);
}
+/* Basic: free the vector body. */
void
-vector_only_index_free (void *index)
+vector_only_index_free (void *body)
{
- XFREE (MTYPE_VECTOR_INDEX, index);
+ XFREE (MTYPE_VECTOR_BODY, body);
}
+/* Basic: free the vector body and the vector structure. */
void
vector_free (vector v)
{
- XFREE (MTYPE_VECTOR_INDEX, v->index);
+ XFREE (MTYPE_VECTOR_BODY, v->p_items);
XFREE (MTYPE_VECTOR, v);
}
+/* Re-Initialize vector (or create new one), setting it empty.
+ *
+ * Allocates vector structure if none given -- that is, if v == NULL.
+ *
+ * If size is given as zero, no body is allocated, but any existing body is
+ * retained. (vector_reset() will discard body.)
+ *
+ * Otherwise ensures existing body is at least the required size, or a body
+ * of exactly the required size is allocated.
+ *
+ * NB: when re-initialising an existing vector it is the caller's
+ * responsibility to release any vector item values *before* doing this.
+ * */
+vector
+vector_re_init(vector v, unsigned int size)
+{
+ if ((v == NULL) || (v->p_items == NULL))
+ return vector_init_new(v, size) ;
+
+ v->end = 0 ;
+
+ if (v->limit < size)
+ {
+ v->p_items = XREALLOC(MTYPE_VECTOR_BODY, v->p_items, P_ITEMS_SIZE(size)) ;
+ v->limit = size ;
+ } ;
+
+ return v ;
+} ;
+
+/* Free the vector body, and free the vector structure or reset it.
+ *
+ * Return NULL if releases vector, otherwise the address of the vector.
+ *
+ * NB: it is the caller's responsibility to release any vector item values
+ * *before* doing this.
+ */
+vector
+vector_reset(vector v, int free_structure)
+{
+ if (v == NULL)
+ return NULL ; /* allow for already freed vector */
+
+ if (v->p_items != NULL)
+ XFREE(MTYPE_VECTOR_BODY, v->p_items) ;
+
+ if (free_structure)
+ {
+ XFREE(MTYPE_VECTOR, v) ;
+ return NULL ;
+ }
+ else
+ return vector_init_new(v, 0) ;
+} ;
+
+/* Pop item from vector, stepping past any NULLs.
+ *
+ * If vector is empty, free the body and, if required, the vector structure.
+ *
+ * Useful for emptying out and discarding a vector:
+ *
+ * while ((p_v = vector_ream_out(v, 1)))
+ * ... do what's required to release the item p_v
+ *
+ * Returns NULL if vector was empty and has now been freed as required.
+ */
+p_vector_item
+vector_ream(vector v, int free_structure)
+{
+ p_vector_item p_v ;
+
+ if (v == NULL)
+ return NULL ;
+
+ while (v->end != 0)
+ {
+ p_v = v->p_items[--v->end] ;
+ if (p_v != NULL)
+ return p_v ; /* return non-NULL item */
+ } ;
+
+ /* vector is empty: free the body, and (if required) the vector structure. */
+ vector_reset(v, free_structure) ;
+
+ return NULL ; /* signals end */
+} ;
+
+/*==============================================================================
+ * Inserting and deleting items.
+ */
+
+/* Insert item in vector, before item at given position
+ * Move items and extend vector as required.
+ */
+void
+vector_insert_item(vector v, vector_index i, void* p_v)
+{
+ if ((i == v->end) && (i < v->limit))
+ ++v->end ;
+ else
+ vector_insert(v, i, 1) ;
+
+ v->p_items[i] = (p_vector_item)p_v ;
+} ;
+
+/* Insert item in vector at given position with rider:
+ *
+ * rider < 0 -- insert before the item at the given position
+ * rider == 0 -- insert at the given position -- REPLACING any existing value
+ * rider > 0 -- insert after the item at the given position
+ *
+ * NB: when an item is replaced, it is the caller's responsibility to release
+ * any memory used by the item, if required.
+ *
+ * Move items and extend vector as required.
+ */
+void
+vector_insert_item_here(vector v, vector_index i, int rider,
+ p_vector_item p_v)
+{
+ if (rider == 0)
+ return vector_set_item(v, i, p_v) ;
+
+ if (rider > 0)
+ ++i ; /* insert before next item */
+ vector_insert_item(v, i, p_v) ;
+} ;
+
+/* Delete item from vector.
+ *
+ * Move items as required. Reduces number of items in the vector (unless
+ * the item in question is beyond the end of the vector !)
+ *
+ * NB: it is the caller's responsibility to release memory used by any
+ * current value of the item, if required.
+ *
+ * NB: does NOT change the size of the vector body.
+ */
+p_vector_item
+vector_delete_item(vector v, vector_index i)
+{
+ p_vector_item p_e ;
+ if (i < v->end)
+ {
+ p_e = v->p_items[i] ; /* pick up the current value */
+ if (i != (v->end - 1))
+ vector_delete(v, i, 1) ;
+ else
+ v->end = i ;
+ return p_e ;
+ }
+ else
+ return NULL ;
+} ;
+
+/*==============================================================================
+ * Moving items within vector.
+ */
+
+/* Move item in vector from source position to destination position.
+ * Moves intervening items up or down as required.
+ * Extends vector to include the destination, if required.
+ * A source item beyond the end of the vector is implicitly NULL.
+ */
+void
+vector_move_item(vector v, vector_index i_dst, vector_index i_src)
+{
+ p_vector_item* pp_s ;
+ p_vector_item* pp_d ;
+ p_vector_item p_e ;
+
+ vector_index old_end = v->end ;
+
+ /* Worry about whether both source and destination exist. */
+ if (i_dst >= old_end)
+ {
+ vector_insert(v, i_dst, 1) ; /* ensure destination exists */
+ if (i_src >= old_end)
+ return ; /* both were beyond the end */
+ }
+ else if (i_src >= old_end)
+ {
+ i_src = old_end ; /* clamp to just beyond last */
+ vector_insert(v, i_src, 1) ; /* create empty entry */
+ } ;
+
+ if (i_dst == i_src) /* avoid work and edge case */
+ return ;
+
+ /* Both src and dst are within the vector and src != dst */
+ pp_s = &v->p_items[i_src] ; /* address of src entry */
+ pp_d = &v->p_items[i_dst] ; /* address of dst entry */
+ p_e = *pp_s ; /* pick up item to move */
+ if (i_src < i_dst)
+ memmove(pp_s, pp_s+1, P_ITEMS_SIZE(i_dst - i_src)) ;
+ else
+ memmove(pp_d+1, pp_d, P_ITEMS_SIZE(i_src - i_dst)) ;
+ *pp_d = p_e ; /* put down the item to move */
+} ;
+
+/* Move item in vector to given position with rider:
+ *
+ * rider < 0 -- move to before the item at the given position
+ * rider == 0 -- move to replace item at the given position
+ * rider > 0 -- insert after the item at the given position
+ *
+ * NB: it is the caller's responsibility to release the any existing value
+ * that will be replaced.
+ *
+ * Move items and extend vector as required.
+ */
+void
+vector_move_item_here(vector v, vector_index i_dst, int rider,
+ vector_index i_src)
+{
+ if (rider != 0)
+ {
+ if (rider > 0)
+ ++i_dst ;
+ vector_move_item(v, i_dst, i_src) ;
+ }
+ else
+ {
+ /* to replace: copy and then delete. */
+ vector_set_item(v, i_dst, vector_get_item(v, i_src)) ;
+ vector_delete_item(v, i_src) ;
+ } ;
+} ;
+
+/* Reverse vector: reverse the order of items in the vector.
+ */
+void
+vector_reverse(vector v)
+{
+ if (v != NULL)
+ vector_part_reverse(v, 0, v->end) ;
+} ;
+
+/* Reverse portion of vector.
+ */
+void
+vector_part_reverse(vector v, vector_index i, unsigned int n)
+{
+ vector_index j ;
+
+ if (v == NULL)
+ return ;
+
+ if ((i + n) > v->limit)
+ vector_extend(v, i + n) ; /* ensure portion exists */
+
+ if (n <= 1)
+ return ;
+
+ j = i + n - 1 ; /* j > i, because n > 1 */
+ do
+ {
+ p_vector_item p_i = v->p_items[i] ;
+ v->p_items[i++] = v->p_items[j] ;
+ v->p_items[j--] = p_i ;
+ } while (j > i) ;
+} ;
+
+/*==============================================================================
+ * Copying, moving and appending entire vectors.
+ */
+
+static void vector_new_limit(vector v, vector_index new_end) ;
+
+/* Shallow vector copy -- copies pointers to item values, not the values. */
+/* Creates a new vector. */
+/* NB: copies whole body, including stuff beyond (logical) end ! */
+/* TODO: is this behaviour required ? */
vector
vector_copy (vector v)
{
- unsigned int size;
- vector new = XCALLOC (MTYPE_VECTOR, sizeof (struct _vector));
+ vector new = vector_init_new(NULL, v->limit) ;
- new->active = v->active;
- new->alloced = v->alloced;
+ new->end = v->end;
- size = sizeof (void *) * (v->alloced);
- new->index = XCALLOC (MTYPE_VECTOR_INDEX, size);
- memcpy (new->index, v->index, size);
+ if (v->limit > 0)
+ memcpy(new->p_items, v->p_items, P_ITEMS_SIZE(v->limit)) ;
return new;
}
-/* Check assigned index, and if it runs short double index pointer */
-void
-vector_ensure (vector v, unsigned int num)
+/* Shallow vector copy -- copies pointers to item values, not the values.
+ * Creates a new vector or re-initialises an existing one.
+ *
+ * NB: creates new vector with same limit as existing one, but copies only
+ * the known items (ie up to end, not up to limit).
+ *
+ * NB: if re-initialising existing vector, it is the caller's responsibility
+ * to release any existing items if that is required.
+ *
+ * NB: if re-initialising existing vector, it is the caller's responsibility
+ * to ensure the vector structure is currently valid.
+ *
+ * NB: do NOT try copying a vector to itself !!
+ */
+vector
+vector_copy_here(vector dst, vector src)
{
- if (v->alloced > num)
- return;
+ assert((src != NULL) && (src != dst)) ;
+
+ dst = vector_re_init(dst, src->limit) ;
- v->index = XREALLOC (MTYPE_VECTOR_INDEX,
- v->index, sizeof (void *) * (v->alloced * 2));
- memset (&v->index[v->alloced], 0, sizeof (void *) * v->alloced);
- v->alloced *= 2;
-
- if (v->alloced <= num)
- vector_ensure (v, num);
+ dst->end = src->end;
+
+ if (src->end > 0)
+ memcpy(dst->p_items, src->p_items, P_ITEMS_SIZE(src->end)) ;
+
+ return dst ;
+} ;
+
+/* Vector move -- moves body of vector.
+ * Creates a new vector or re-initialises an existing one.
+ * Leaves the source vector empty -- does not release the structure.
+ *
+ * NB: if re-initialising existing vector, it is the caller's responsibility
+ * to release any existing items if that is required.
+ *
+ * NB: if re-initialising existing vector, it is the caller's responsibility
+ * to ensure the vector structure is currently valid.
+ *
+ * NB: do NOT try moving a vector to itself !!
+ */
+vector
+vector_move_here(vector dst, vector src)
+{
+ assert((src != NULL) && (src != dst)) ;
+
+ if (dst != NULL)
+ dst = vector_reset(dst, 0) ; /* Reset to deallocate any existing body */
+ else
+ dst = vector_init_new(dst, 0) ; /* Create new structure sans body. */
+
+ *dst = *src ; /* Copy the vector structure */
+
+ vector_init_new(src, 0) ; /* Set empty, forgetting body */
+
+ return dst ;
}
-/* This function only returns next empty slot index. It dose not mean
+/* Shallow vector copy append -- copies pointers to item values, not the values.
+ * Appends copied pointers to the destination vector.
+ * Creates a new destination vector if required.
+ *
+ * NB: Can append to self.
+ */
+vector
+vector_copy_append(vector dst, vector src)
+{
+ vector_index new_end ;
+
+ assert(src != NULL) ;
+
+ if (dst != NULL)
+ {
+ new_end = dst->end + src->end ;
+ if (new_end > dst->limit)
+ vector_new_limit(dst, new_end) ;
+ }
+ else
+ {
+ new_end = src->end ;
+ vector_init_new(dst, new_end) ;
+ } ;
+
+ if (src->end)
+ memcpy(&dst->p_items[dst->end], src->p_items, P_ITEMS_SIZE(src->end)) ;
+
+ dst->end = new_end ; /* Done last, allows for append to self ! */
+ return dst ;
+} ;
+
+/* Vector move append -- moves pointers to item values.
+ * Appends moved pointers to the destination vector.
+ * Creates a new destination vector if required (dst == NULL).
+ * Leaves the source vector empty -- does not release the structure.
+ *
+ * NB: do NOT try moving a vector to itself !!
+ */
+vector
+vector_move_append(vector dst, vector src)
+{
+ assert((src != NULL) && (src != dst)) ;
+
+ if ((dst == NULL) || (dst->end == 0))
+ return vector_move_here(dst, src) ; /* Easy way to do it if dst empty */
+
+ vector_copy_append(dst, src) ; /* Extend dst and copy src */
+ vector_init_new(src, 0) ; /* Set empty, forgetting body */
+
+ return dst ;
+} ;
+
+/*==============================================================================
+ * Portmanteau splice/extract/replace function.
+ *
+ * All take a portion of 'src' vector and:
+ *
+ * splice:
+ *
+ * a) replace a portion of the 'dst' vector by a portion of the 'src' vector
+ * copying the replaced portion of the 'dst' vector to the 'to' vector
+ * b) either: leave 'src' unchanged -- copy
+ * or: remove the stuff copied from 'src' -- move
+ *
+ * Arguments: to_copy -- true
+ * to -- vector, or NULL => allocate new vector
+ * dst -- vector
+ * i_dst -- start of portion to replace
+ * n_dst -- length of portion to replace. 0 => insertion.
+ * src -- vector, or NULL => nothing to copy/move
+ * i_src -- start of portion to copy/move
+ * n_src -- length of portion to copy/move. 0 => nothing.
+ * src_move -- true => move, otherwise copy.
+ *
+ * Returns: the (possibly new) 'to' vector
+ *
+ * NB: 'to', 'dst' and 'src' must be distinct vectors.
+ *
+ * extract:
+ *
+ * a) copy a portion of the 'src' vector to the 'to' vector
+ * c) either: leave 'src' unchanged -- copy
+ * or: remove the stuff copied from 'src' -- move
+ *
+ * Arguments: to_copy -- true
+ * to -- vector, or NULL => allocate new vector
+ * dst -- NULL
+ * i_dst -- ignored
+ * n_dst -- ignored
+ * src -- vector, or NULL => nothing to copy/move
+ * i_src -- start of portion to copy/move
+ * n_src -- length of portion to copy/move. 0 => nothing.
+ * src_move -- true => move, otherwise copy.
+ *
+ * Returns: the (possibly new) 'to' vector
+ *
+ * NB: 'to' and 'src' must be distinct vectors.
+ *
+ * replace:
+ *
+ * a) replace a portion of the 'dst' vector by a portion of the 'src' vector
+ * b) either: leave 'src' unchanged -- copy
+ * or: remove the stuff copied from 'src' -- move
+ *
+ * Arguments: to_copy -- false
+ * to -- ignored
+ * dst -- vector
+ * i_dst -- start of portion to replace
+ * n_dst -- length of portion to replace. 0 => insertion.
+ * src -- vector, or NULL => nothing to copy/move
+ * i_src -- start of portion to copy/move
+ * n_src -- length of portion to copy/move. 0 => nothing.
+ * src_move -- true => move, otherwise copy.
+ *
+ * Returns: original 'to' argument
+ *
+ * NB: 'dst' and 'src' must be distinct vectors.
+ *
+ * All copies are shallow -- pointers to item values are copied, not the values.
+ *
+ * NB: any existing contents of the 'to' vector are discarded.
+ *
+ * NB: it is the caller's responsibility to release memory allocated to any
+ * items which are discarded in these operations.
+ *
+ * NB: for splice and replace, the resulting destination vector will be at
+ * least i_dst + n_src long. (Even if is copying actual or implied NULLs
+ * from the source.)
+ *
+ * NB: where new vectors are created, they will be of exactly the required size.
+ *
+ * NB: where an existing vector is reused, it is the caller's responsibility
+ * to ensure the vector structure is currently valid (by vector_init_new()
+ * or by ensuring it is zeroized).
+ */
+vector
+vector_sak(int to_copy, vector to,
+ vector dst, vector_index i_dst, unsigned int n_dst,
+ vector src, vector_index i_src, unsigned int n_src, int src_move)
+{
+ int dst_replace ; /* true => replace portion of 'dst' */
+
+ vector_index new_dst_end = 0 ; /* new end of dst */
+
+ unsigned int n_dst_nulls ; /* number of implicit NULLs to add */
+ unsigned int n_dst_move ; /* number of items to move up or down */
+ unsigned int n_src_real ; /* number of items to really copy */
+ unsigned int n_src_nulls ; /* number of implicit NULLs to "copy" */
+
+ assert((to == NULL) || (dst == NULL) || (to != dst)) ;
+ assert((src == NULL) || (dst == NULL) || (src != dst)) ;
+
+ /* Worry about how much we really have in the source vector. */
+
+ n_src_real = n_src ; /* assume all real */
+ n_src_nulls = 0 ; /* so no NULLs to "copy" */
+
+ if (n_src != 0)
+ {
+ if ((src == NULL) || (i_src >= src->end))
+ n_src_real = 0 ;
+ else if ((i_src + n_src) > src->end)
+ n_src_real = src->end - i_src ;
+ n_src_nulls = n_src - n_src_real ;
+ } ;
+
+ /* If no 'dst' vector, then this is an extract. */
+
+ n_dst_move = 0 ; /* assume nothing to move */
+ n_dst_nulls = 0 ; /* assume no NULLs to add */
+
+ if (dst == NULL)
+ /* For extract: set up dst, i_dst and n_dst so that can copy to the */
+ /* 'to' vector as if from 'dst'. */
+ {
+ dst_replace = 0 ; /* no replacement operation */
+ dst = src ; /* copy from here */
+ i_dst = i_src ;
+ n_dst = n_src_real ;
+ }
+ else
+ /* Reduce n_dst to the number of actual items to be replaced. */
+ /* */
+ /* Calculate the new end of 'dst'. */
+ {
+ dst_replace = 1 ; /* have replacement to do */
+ if (i_dst >= dst->end)
+ /* If i_dst is beyond the end of 'dst', then there is nothing */
+ /* to replace (so set n_dst == 0). Will be adding n_src items */
+ /* at i_dst -- so new end must be i_dst + n_src. */
+ {
+ n_dst_nulls = i_dst - dst->end ; /* fill from end to i_dst */
+ n_dst = 0 ; /* nothing to replace */
+ new_dst_end = i_dst + n_src ; /* all beyond current end */
+ }
+ else
+ /* If i_dst + n_dst is beyond the end of 'dst', reduce n_dst to */
+ /* number of items up to the end. */
+ /* Will remove n_dst items and insert n_src, so end will move */
+ /* by n_src - n_dst. */
+ {
+ if ((i_dst + n_dst) > dst->end)
+ n_dst = dst->end - i_dst ; /* what we actually replace */
+ else if (n_dst != n_src)
+ n_dst_move = dst->end - (i_dst + n_dst) ;
+ /* what we move up or down */
+
+ new_dst_end = dst->end + n_src - n_dst ;
+ /* end depends on amount added */
+ /* & amount actually replaced */
+ } ;
+ } ;
+
+ /* Copy portion of 'dst' (or of 'src') to 'to', if required. */
+ /* */
+ /* Have arranged: n_dst -- number of items to copy, all existent */
+ /* dst -- vector to copy from -- if n_dst > 0 */
+ /* i_dst -- first item to copy -- if n_dst > 0 */
+
+ if (to_copy)
+ {
+ to = vector_re_init(to, n_dst) ; /* reinitialise or create */
+ to->end = n_dst ;
+ if (n_dst > 0)
+ memcpy(to->p_items, &dst->p_items[i_dst], P_ITEMS_SIZE(n_dst)) ;
+ } ;
+
+ /* Replace portion of 'dst' by portion of 'src', if required. */
+ /* */
+ /* Have arranged: */
+ /* */
+ /* new_dst_end -- end of dst once dust settles */
+ /* n_dst_nulls -- number of NULLs to insert at dst->end to fill up */
+ /* to i_dst (when i_dst is beyond old end.) */
+ /* n_dst_move -- number of items in dst to move up or down to */
+ /* leave n_src item hole at i_dst to fill in. */
+ /* n_src_real -- number of real src items at i_src to copy to dst */
+ /* at i_dst. */
+ /* n_src_nulls -- number of nulls to add to fill to i_dst + n_src. */
+
+ if (dst_replace)
+ {
+ if (new_dst_end > dst->limit) /* extend body if required */
+ vector_new_limit(dst, new_dst_end) ;
+
+ if (n_dst_nulls > 0)
+ memset(&dst->p_items[dst->end], 0, P_ITEMS_SIZE(n_dst_nulls)) ;
+ if (n_dst_move > 0)
+ memmove(&dst->p_items[i_dst + n_dst], &dst->p_items[i_dst + n_src],
+ P_ITEMS_SIZE(n_dst_move)) ;
+ if (n_src_real > 0)
+ memcpy(&dst->p_items[i_dst], &src->p_items[i_src],
+ P_ITEMS_SIZE(n_src_real)) ;
+ if (n_src_nulls > 0)
+ memset(&dst->p_items[i_dst + n_src_real], 0,
+ P_ITEMS_SIZE(n_src_nulls)) ;
+ dst->end = new_dst_end ;
+ } ;
+
+ /* Delete portion of 'src', if required (and have 'src' !) */
+
+ if (src_move && (n_src_real != 0))
+ vector_delete(src, i_src, n_src_real) ;
+
+ /* Done -- return 'to' vector as promised. */
+
+ return to ;
+} ;
+
+/*==============================================================================
+ * Legacy Vector Operations
+ */
+
+/* This function only returns next empty slot index. It does not mean
the slot's index memory is assigned, please call vector_ensure()
- after calling this function. */
+ after calling this function.
+
+ Index returned is <= current (logical) end.
+*/
int
vector_empty_slot (vector v)
{
- unsigned int i;
-
- if (v->active == 0)
- return 0;
+ vector_index i;
- for (i = 0; i < v->active; i++)
- if (v->index[i] == 0)
- return i;
+ for (i = 0; i < v->end; i++)
+ if (v->p_items[i] == NULL)
+ break ;
return i;
}
@@ -113,77 +786,436 @@ vector_empty_slot (vector v)
int
vector_set (vector v, void *val)
{
- unsigned int i;
-
- i = vector_empty_slot (v);
- vector_ensure (v, i);
+ vector_index i;
+ i = vector_empty_slot(v) ; /* NB: i <= v->end */
+ if (i == v->end)
+ i = vector_extend_by_1(v) ;
- v->index[i] = val;
-
- if (v->active <= i)
- v->active = i + 1;
+ v->p_items[i] = val;
return i;
}
/* Set value to specified index slot. */
int
-vector_set_index (vector v, unsigned int i, void *val)
+vector_set_index (vector v, vector_index i, void *val)
{
vector_ensure (v, i);
-
- v->index[i] = val;
-
- if (v->active <= i)
- v->active = i + 1;
-
+ v->p_items[i] = val;
return i;
}
/* Look up vector. */
-void *
-vector_lookup (vector v, unsigned int i)
+p_vector_item
+vector_lookup (vector v, vector_index i)
{
- if (i >= v->active)
+ if (i >= v->end)
return NULL;
- return v->index[i];
+ return v->p_items[i];
}
/* Lookup vector, ensure it. */
-void *
-vector_lookup_ensure (vector v, unsigned int i)
+p_vector_item
+vector_lookup_ensure (vector v, vector_index i)
{
vector_ensure (v, i);
- return v->index[i];
+ return v->p_items[i];
}
/* Unset value at specified index slot. */
void
-vector_unset (vector v, unsigned int i)
+vector_unset (vector v, vector_index i)
{
- if (i >= v->alloced)
- return;
+ vector_index j ;
+ if (i >= v->end)
+ return; /* Everything beyond or at end is implicitly NULL */
- v->index[i] = NULL;
+ v->p_items[i] = NULL;
- if (i + 1 == v->active)
- {
- v->active--;
- while (i && v->index[--i] == NULL && v->active--)
+ /* See if everything ahead of 'i' is also NULL. */
+ j = i ;
+ while (++j < v->end)
+ if (v->p_items[j] != NULL)
+ return ; /* Finished if anything ahead 'i' is not NULL */
+
+ /* Everything from 'i' onwards is NULL.
+ * Step backwards across any NULLs and then set the new end.
+ */
+#if 0
+ v->end--;
+ while (i && v->p_items[--i] == NULL && v->end--)
; /* Is this ugly ? */
- }
+#endif
+ while ((i != 0) && (v->p_items[i - 1] == NULL))
+ --i ;
+
+ v->end = i ;
}
-/* Count the number of not emplty slot. */
-unsigned int
+/* Count the number of not empty slots. */
+vector_index
vector_count (vector v)
{
- unsigned int i;
+ vector_index i;
unsigned count = 0;
- for (i = 0; i < v->active; i++)
- if (v->index[i] != NULL)
+ for (i = 0; i < v->end; i++)
+ if (v->p_items[i] != NULL)
count++;
return count;
}
+
+/*==============================================================================
+ * Sorting and Searching vector.
+ */
+
+/* Sort the given vector.
+ *
+ * NB: the comparison function receives a pointer to the pointer to the
+ * vector item's value.
+ *
+ * NB: if there are NULL items in the vector, the comparison function MUST
+ * be ready for them.
+ */
+void
+vector_sort(vector v, vector_sort_cmp* cmp)
+{
+ if (v->end <= 1)
+ return ; /* Stop dead if 0 or 1 items */
+
+ typedef int qsort_cmp(const void*, const void*) ;
+
+ qsort(v->p_items, v->end, sizeof(p_vector_item), (qsort_cmp*)cmp) ;
+} ;
+
+/* Perform binary search on the given vector.
+ *
+ * The vector MUST be sorted in the order implied by the comparison function
+ * given. The vector need not contain unique values, but this search makes
+ * no effort to select any particular instance of a sequence of equals.
+ *
+ * Returns:
+ *
+ * result == 0: found an equal value.
+ * index returned is of first entry found which is equal to
+ * the value sought. There may be other equal values, before
+ * and/or after this one in the vector.
+ *
+ * result == +1: value not found and vector not empty.
+ * index returned is of the largest entry whose value is less
+ * than the value sought.
+ * (The value sought belongs after this point.)
+ *
+ * result == -1: value is less than everything in the vector, or the
+ * vector is empty.
+ * index returned is 0
+ *
+ * NB: The comparison function takes arguments which are:
+ *
+ * const void** pointer to pointer to value being searched for.
+ * const void** pointer to pointer to vector item to compare with
+ *
+ * The value being searched for need not be in the same form as a vector
+ * item. However, if it is then the same comparison function can be used
+ * to sort and search.
+ *
+ * NB: if there are NULL items in the vector, the comparison function MUST
+ * be ready for them.
+ */
+vector_index
+vector_bsearch(vector v, vector_bsearch_cmp* cmp, const void* p_val,
+ int* result)
+{
+ vector_index il, iv, ih ;
+ int c ;
+
+ if (v->end <= 1)
+ {
+ *result = (v->end == 0) ? -1 : cmp(&p_val, (const void**)&v->p_items[0]) ;
+ return 0 ; /* Stop dead if 0 or 1 items */
+ } ;
+
+ /* We have at least two items. */
+ il = 0 ;
+ ih = v->end - 1 ;
+
+ /* Pick off the edge cases: >= last and <= first. */
+ if ((c = cmp(&p_val, (const void**)&v->p_items[ih])) >= 0)
+ {
+ *result = c ; /* 0 => found. +1 => val > last */
+ return ih ; /* return high index. */
+ } ;
+ if ((c = cmp(&p_val, (const void**)&v->p_items[il])) <= 0)
+ {
+ *result = c ; /* 0 => found. -1 => val < first */
+ return il ; /* return low index. */
+ }
+
+ /* Now binary chop. We know that item[il] < val < item[ih] */
+ /* We also know that il < ih */
+ while (1)
+ {
+ iv = (il + ih) / 2 ;
+ if (iv == il) /* true if (ih == il+1) */
+ {
+ *result = +1 ;
+ return il ; /* return il: item[il] < val < item[il+1] */
+ } ;
+ /* We now know that il < iv < ih */
+ c = cmp(&p_val, (const void**)&v->p_items[iv]) ;
+ if (c == 0)
+ {
+ *result = 0 ;
+ return iv ; /* found !! */
+ }
+ if (c < 0)
+ ih = iv ; /* step down iv > il, so new ih > il */
+ else
+ il = iv ; /* step up iv < ih, so new il < ih */
+ } ;
+} ;
+
+/*==============================================================================
+ * Mechanics for adding/deleting items and managing the vector (logical) end
+ * and (physical) limit.
+ */
+
+/* Extract the LS bit of unsigned integer 'x'. */
+#define lsbit(x) ((x) & ((~(x)) + 1))
+/* Round 'x' up to a multiple of 'm' */
+#define multiple(x, m) ((((x) + (m) - 1) / (m)) * (m))
+
+/* Set new limit to suit new end for given vector.
+ *
+ * The new limit will be at least: VECTOR_LIMIT_MIN.
+ *
+ * While the vector is relatively small, the limit is doubled until there
+ * is at least 1/8 of the new vector free.
+ *
+ * Beyond VECTOR_LIMIT_DOUBLE_MAX, however, the limit is set to the
+ * smallest multiple of VECTOR_LIMIT_DOUBLE_MAX which gives at least
+ * VECTOR_LIMIT_SLACK_MIN free entries beyond the new end.
+ *
+ * This is an attempt to balance the cost of repeated reallocations of
+ * memory against the cost of possible wasted space at the end of the
+ * vector.
+ *
+ * NB: the new_limit depends entirely on the new end position. (Current
+ * end position is ignored.)
+ *
+ * NB: the new limit may be less than the current limit, in which case the
+ * vector body is reduced in size.
+ *
+ * Except for any size set when the vector is initialised, the vector body
+ * size will be a power of 2 or a multiple of VECTOR_LIMIT_DOUBLE_MAX.
+ * (Vectors are regular in their habits, which may help the memory allocator).
+ *
+ * TODO: what to do if calculation of new_limit overflows, or calculation
+ * of P_ITEMS_SIZE will ?
+ */
+static void
+vector_new_limit(vector v, vector_index new_end)
+{
+ vector_index old_limit = v->limit ;
+ vector_index new_limit ;
+
+ if (new_end > ((VECTOR_LIMIT_DOUBLE_MAX * 7) / 8))
+ {
+ new_limit = multiple(new_end + VECTOR_LIMIT_SLACK_MIN,
+ VECTOR_LIMIT_DOUBLE_MAX) ;
+ }
+ else
+ {
+ /* Want the new_limit to be a power of 2. */
+ /* If the old_limit was a power of 2, start from there. */
+ /* Otherwise start from a power of 2 less than new_end: either the */
+ /* minimum value or a value mid way to VECTOR_LIMIT_DOUBLE_MAX. */
+ if ( (old_limit != 0) && (old_limit == lsbit(old_limit))
+ && (new_end >= old_limit) )
+ new_limit = old_limit ;
+ else
+ new_limit = (new_end < VECTOR_LIMIT_MID) ? VECTOR_LIMIT_MIN
+ : VECTOR_LIMIT_MID ;
+ while (new_end > ((new_limit * 7) / 8))
+ new_limit *= 2 ;
+ } ;
+
+ v->p_items =
+ XREALLOC(MTYPE_VECTOR_BODY, v->p_items, P_ITEMS_SIZE(new_limit)) ;
+
+ v->limit = new_limit ;
+} ;
+
+/* Extend vector and set new (logical) end.
+ *
+ * Extends body if required.
+ * Ensures everything between old and new end is set NULL.
+ *
+ * NB: expects new end > old end, but copes with new end <= old end.
+ */
+void
+vector_extend(vector v, vector_index new_end)
+{
+ vector_index old_end = v->end ;
+
+ if (new_end > v->limit)
+ vector_new_limit(v, new_end) ;
+ v->end = new_end ;
+
+ if (new_end > old_end)
+ memset(&v->p_items[old_end], 0, P_ITEMS_SIZE(new_end - old_end)) ;
+} ;
+
+/* Insert entries into vector: insert 'n' NULL entries at location 'i'.
+ *
+ * Updates end (and limit) to be at least 'i' + 'n'.
+ * (So if 'i' < end then end becomes end + 'n', else end becomes 'i' + 'n'.)
+ */
+void
+vector_insert(vector v, vector_index i, unsigned int n)
+{
+ vector_index old_end, new_end ;
+ unsigned int n_above ;
+
+ /* If i < old end, then we are inserting n NULLs, and need
+ * to shuffle at least one item up.
+ * else we are setting new end to i + n and need to NULL
+ * fill from old end to the new end.
+ */
+ old_end = v->end ;
+ if (i < old_end)
+ {
+ if (n == 0)
+ return ; /* give up now if not inserting anything */
+ n_above = old_end - i ; /* number of items to shuffle up.. >= 1 */
+ new_end = old_end + n ;
+ }
+ else
+ {
+ n_above = 0 ; /* nothing to shuffle up. */
+ new_end = i + n ;
+ i = old_end ; /* where to zeroize from */
+ n = new_end - old_end ; /* how much to zeroize */
+ } ;
+
+ /* Now we extend the body if we need to. */
+ if (new_end > v->limit)
+ vector_new_limit(v, new_end) ;
+ v->end = new_end ;
+
+ if (n_above > 0)
+ memmove(&v->p_items[i + n], &v->p_items[i], P_ITEMS_SIZE(n_above)) ;
+
+ memset(&v->p_items[i], 0, P_ITEMS_SIZE(n)) ;
+} ;
+
+/* Delete items from vector: delete 'n' items at location 'i'.
+ *
+ * Does nothing if 'i' is beyond current end of vector or if 'n' == 0.
+ *
+ * Deletes from 'i' to end if less than 'n' items to the end.
+ *
+ * NB: does NOT change the size of the body.
+ *
+ * NB: it is the caller's responsibility to have released any memory allocated
+ * for the items that are being deleted.
+*/
+void
+vector_delete(vector v, vector_index i, unsigned int n)
+{
+ vector_index old_end, new_end ;
+
+ old_end = v->end ;
+
+ if ((i >= old_end) || (n == 0))
+ return ;
+
+ /* If i + n < old_end, we have 1 or more items to keep and move down */
+ if ((i + n) < old_end)
+ {
+ memmove(&v->p_items[i], &v->p_items[i + n],
+ P_ITEMS_SIZE(old_end - (i + n))) ;
+ new_end = old_end - n ;
+ }
+ else
+ {
+ new_end = i ; /* We are keeping nothing above 'i' */
+ /* NB: new_end < old_end */
+ } ;
+
+ v->end = new_end ; /* account for stuff dropped */
+} ;
+
+/* Discard entries from vector: discard entries from location 'i' onwards.
+ *
+ * Releases memory from 'i' onwards.
+ * Releases the body altogether if this sets the vector empty ('i' == 0).
+ * Sets new end of vector iff 'i' < current end.
+ *
+ * Does nothing if 'i' is beyond current limit (physical end) of vector.
+ *
+ * NB: it is the caller's responsibility to have released any memory allocated
+ * for the items that are being discarded.
+*/
+void
+vector_discard(vector v, vector_index i)
+{
+ if (i >= v->limit)
+ return ;
+
+ if (i == 0)
+ vector_reset(v, 0) ; /* reset, without releasing the structure */
+ else
+ {
+ v->limit = i ;
+ if (i < v->end)
+ v->end = i ;
+ v->p_items = XREALLOC(MTYPE_VECTOR_BODY, v->p_items, P_ITEMS_SIZE(i)) ;
+ } ;
+} ;
+
+/* Chop vector at the current (logical) end.
+ *
+ * Releases the body altogether if the vector is currently empty.
+*/
+void
+vector_chop(vector v)
+{
+ vector_index new_limit = v->end ;
+
+ if (new_limit == 0)
+ vector_reset(v, 0) ; /* reset, without releasing the structure */
+ else if (new_limit != v->limit)
+ {
+ v->limit = new_limit ;
+ v->p_items = XREALLOC(MTYPE_VECTOR_BODY, v->p_items, P_ITEMS_SIZE(new_limit)) ;
+ } ;
+} ;
+
+/* Decant vector into a new body allocated to current logical size, and
+ * release the old body.
+ *
+ * Releases the body altogether if the vector is currently empty.
+*/
+void
+vector_decant(vector v)
+{
+ p_vector_item* p_old_body ;
+ vector_index new_limit = v->end ;
+
+ if (new_limit == 0)
+ vector_reset(v, 0) ; /* reset, without releasing the structure */
+ else
+ {
+ p_old_body = v->p_items ;
+
+ vector_init_new(v, new_limit) ; /* initialise with new body */
+
+ memcpy(v->p_items, p_old_body, P_ITEMS_SIZE(new_limit)) ;
+ /* copy the old body across */
+ v->end = new_limit ;
+
+ XFREE(MTYPE_VECTOR_BODY, p_old_body) ;
+ } ;
+} ;
diff --git a/lib/vector.h b/lib/vector.h
index 6b27fd96..3a7e7ca5 100644
--- a/lib/vector.h
+++ b/lib/vector.h
@@ -1,7 +1,9 @@
-/*
- * Generic vector interface header.
+/* Generic vector interface header.
* Copyright (C) 1997, 98 Kunihiro Ishiguro
*
+ * 24-Nov-2009 -- extended to add a number of new operations on vectors.
+ * Copyright (C) 2009 Chris Hall (GMCH), Highwayman
+ *
* This file is part of GNU Zebra.
*
* GNU Zebra is free software; you can redistribute it and/or modify it
@@ -17,47 +19,271 @@
* You should have received a copy of the GNU General Public License
* along with GNU Zebra; see the file COPYING. If not, write to the Free
* Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
- * 02111-1307, USA.
+ * 02111-1307, USA.
*/
#ifndef _ZEBRA_VECTOR_H
#define _ZEBRA_VECTOR_H
-/* struct for vector */
-struct _vector
+/* Macro in case there are particular compiler issues. */
+#ifndef Inline
+ #define Inline static inline
+#endif
+
+/* types and struct for vector */
+/* */
+/* NB: an entirely zero structure represents an entirely empty vector. */
+/* */
+/* TODO: could force vector_index to be 32 bits ? */
+
+typedef void* p_vector_item ;
+typedef unsigned int vector_index ;
+
+struct vector
{
- unsigned int active; /* number of active slots */
- unsigned int alloced; /* number of allocated slot */
- void **index; /* index to data */
+ p_vector_item *p_items ; /* pointer to array of vector item pointers */
+ vector_index end ; /* number of "active" item entries */
+ vector_index limit ; /* number of allocated item entries */
};
-typedef struct _vector *vector;
+typedef struct vector *vector;
+
+/* Values that control the allocation of the vector body. */
+/* NB: these must all be powers of 2. */
+
+/* The body, when allocated, will have at least this many entries. */
+#define VECTOR_LIMIT_MIN 8
+/* When the body grows, it doubles in size, until it is this big. */
+/* After that it grows in units of this much. */
+#define VECTOR_LIMIT_DOUBLE_MAX 2048
+/* "Midway" between VECTOR_LIMIT_MIN and VECTOR_LIMIT_DOUBLE_MAX. */
+#define VECTOR_LIMIT_MID 128
+/* When growing in units of VECTOR_LIMIT_DOUBLE_MAX, this is the */
+/* minimum slack space to leave after the logical end of the vector. */
+#define VECTOR_LIMIT_SLACK_MIN ((VECTOR_LIMIT_DOUBLE_MAX) / 8)
+
+/* (Sometimes) useful macros. */
-#define VECTOR_MIN_SIZE 1
+/* Reference item at given index. */
+/* NB: does not guarantee the item is "active" (that is: within the */
+/* (logical) vector) -- but see vector_ensure. */
+/* Returns address of vector item. */
+/* See: VECTOR_ITEMS() for walking items in a vector. */
+/* See: vector_get_item(), which is preferable. */
+#define vector_slot(V,I) ((V)->p_items[(I)])
+
+/* Number of "active" item entries -- (logical) end of the vector. */
+/* Note that this differs from vector_count() as this count will */
+/* include any NULL items. */
+#define vector_active(V) ((V)->end)
+
+/* TODO: fix where this is used to poke around inside a vector */
+#define VECTOR_INDEX p_items
+
+/* To walk all items in a vector:
+ *
+ * vector_index i ;
+ * xxxxx* p_v ;
+ *
+ * for (VECTOR_ITEMS(v, p_v, i))
+ * {
+ * ... i is index of current item
+ * ... p_v is address of current item value -- may be NULL
+ * } ;
+ *
+ * ... i is number of items in vector (including any NULLs)
+ */
+#define VECTOR_ITEMS(v, p_v, i)\
+ (i) = 0 ;\
+ (i) < (v)->end ? (((p_v) = (void*)(v)->p_items[i]), 1) \
+ : (((p_v) = NULL), 0) ;\
+ ++(i)
-/* (Sometimes) usefull macros. This macro convert index expression to
- array expression. */
-/* Reference slot at given index, caller must ensure slot is active */
-#define vector_slot(V,I) ((V)->index[(I)])
-/* Number of active slots.
- * Note that this differs from vector_count() as it the count returned
- * will include any empty slots
+/*==============================================================================
+ * Prototypes.
*/
-#define vector_active(V) ((V)->active)
-/* Prototypes. */
extern vector vector_init (unsigned int size);
-extern void vector_ensure (vector v, unsigned int num);
+Inline void vector_ensure(vector v, vector_index i) ;
extern int vector_empty_slot (vector v);
extern int vector_set (vector v, void *val);
-extern int vector_set_index (vector v, unsigned int i, void *val);
-extern void vector_unset (vector v, unsigned int i);
-extern unsigned int vector_count (vector v);
+extern int vector_set_index (vector v, vector_index i, void *val);
+extern void vector_unset (vector v, vector_index i);
+extern vector_index vector_count (vector v);
extern void vector_only_wrapper_free (vector v);
extern void vector_only_index_free (void *index);
extern void vector_free (vector v);
extern vector vector_copy (vector v);
-extern void *vector_lookup (vector, unsigned int);
-extern void *vector_lookup_ensure (vector, unsigned int);
+extern void *vector_lookup (vector, vector_index);
+extern void *vector_lookup_ensure (vector, vector_index);
+
+extern vector vector_init_new(vector v, unsigned int size) ;
+extern vector vector_re_init(vector v, unsigned int size) ;
+extern vector vector_reset(vector v, int free_structure) ;
+extern p_vector_item vector_ream(vector v, int free_structure) ;
+
+/* Reset vector and free the vector structure. */
+#define vector_reset_free(v) vector_reset(v, 1)
+/* Reset vector but free the heap structure. */
+#define vector_reset_keep(v) vector_reset(v, 0)
+/* Ream out vector and free the vector structure. */
+#define vector_ream_free(v) vector_ream(v, 1)
+/* Ream out vector but keep the vector structure. */
+#define vector_ream_keep(v) vector_ream(v, 0)
+
+Inline vector_index vector_end(vector v) ;
+Inline int vector_is_empty(vector v) ;
+
+Inline p_vector_item vector_get_item(vector v, vector_index i) ;
+Inline p_vector_item vector_get_first_item(vector v) ;
+Inline p_vector_item vector_get_last_item(vector v) ;
+Inline void vector_set_item(vector v, vector_index i, p_vector_item p_v) ;
+
+extern void vector_insert_item(vector v, vector_index i, p_vector_item p_v) ;
+extern void vector_insert_item_here(vector v, vector_index i, int rider,
+ p_vector_item p_v) ;
+extern void vector_move_item(vector v, vector_index dst, vector_index src) ;
+extern void vector_move_item_here(vector v, vector_index dst, int rider,
+ vector_index src) ;
+extern p_vector_item vector_delete_item(vector v, vector_index i) ;
+extern void vector_reverse(vector v) ;
+extern void vector_part_reverse(vector v, vector_index i, unsigned int n) ;
+
+Inline void vector_push_item(vector v, p_vector_item p_v) ;
+Inline p_vector_item vector_pop_item(vector v) ;
+
+extern void vector_insert(vector v, vector_index i, unsigned int n) ;
+extern void vector_delete(vector v, vector_index i, unsigned int n) ;
+
+typedef int vector_bsearch_cmp(const void** pp_val, const void** item) ;
+vector_index vector_bsearch(vector v, vector_bsearch_cmp* cmp,
+ const void* p_val, int* result) ;
+typedef int vector_sort_cmp(const void** a, const void** b) ;
+void vector_sort(vector v, vector_sort_cmp* cmp) ;
+
+extern vector vector_copy_here(vector dst, vector src) ;
+extern vector vector_move_here(vector dst, vector src) ;
+extern vector vector_copy_append(vector dst, vector src) ;
+extern vector vector_move_append(vector dst, vector src) ;
+
+#define vector_copy_extract(to, src, i_src, n_src) \
+ vector_sak(1, to, NULL, 0, 0, src, i_src, n_src, 0)
+#define vector_move_extract(to, src, i_src, n_src) \
+ vector_sak(1, to, NULL, 0, 0, src, i_src, n_src, 1)
+#define vector_copy_splice(to, dst, i_dst, n_dst, src, i_src, n_src) \
+ vector_sak(1, to, dst, i_dst, n_dst, src, i_src, n_src, 0)
+#define vector_move_splice(to, dst, i_dst, n_dst, src, i_src, n_src) \
+ vector_sak(1, to, dst, i_dst, n_dst, src, i_src, n_src, 1)
+#define vector_copy_replace(dst, i_dst, n_dst, src, i_src, n_src) \
+ vector_sak(0, NULL, dst, i_dst, n_dst, src, i_src, n_src, 0)
+#define vector_move_replace(dst, i_dst, n_dst, src, i_src, n_src) \
+ vector_sak(0, NULL, dst, i_dst, n_dst, src, i_src, n_src, 1)
+
+extern vector vector_sak(int to_copy, vector to,
+ vector dst, vector_index i_dst, unsigned int n_dst,
+ vector src, vector_index i_src, unsigned int n_src, int src_move) ;
+
+extern void vector_discard(vector v, vector_index i) ;
+extern void vector_chop(vector v) ;
+extern void vector_decant(vector v) ;
+
+Inline vector_index vector_extend_by_1(vector v) ;
+extern void vector_extend(vector v, vector_index new_end) ;
+
+/*==============================================================================
+ * The inline functions:
+ */
+
+/* Extend vector by one item at the end, which is about to be set. */
+/* Returns index of new least item in the vector. */
+/* NB: if left unset, the item may be UNDEFINED. */
+Inline vector_index
+vector_extend_by_1(vector v)
+{
+ vector_index i = v->end ;
+
+ if (i < v->limit)
+ return v->end++ ; /* simple if we have room */
+
+ vector_extend(v, i + 1) ; /* the hard way */
+ return i ;
+} ;
+
+/* Ensure given index is "active". */
+/* Adjusts logical and physical end of the vector as required, filling */
+/* with NULLs upto any new logical end. */
+Inline void
+vector_ensure(vector v, vector_index i)
+{
+ if (i < v->end) /* trivial if within vector */
+ return ;
+ if ((i == v->end) && (i < v->limit)) /* simple if end and have room */
+ v->p_items[v->end++] = NULL ; /* set NULL for complete safety */
+ else
+ vector_extend(v, i + 1) ; /* do it the hard way */
+} ;
+
+/* Return index of end of vector (index of last item + 1) */
+Inline vector_index
+vector_end(vector v)
+{
+ return v->end ;
+} ;
+
+/* Returns whether vector is empty or not. */
+Inline int
+vector_is_empty(vector v)
+{
+ return (v->end == 0) ;
+} ;
+
+/* Access functions -- Inline for obvious reasons. */
+
+/* Get pointer to item. Returns NULL if accessing beyond end. */
+Inline p_vector_item
+vector_get_item(vector v, vector_index i)
+{
+ return (i < v->end) ? v->p_items[i] : NULL ;
+} ;
+
+/* Get pointer to first item. Returns NULL if vector empty. */
+Inline p_vector_item
+vector_get_first_item(vector v)
+{
+ return (v->end != 0) ? v->p_items[0] : NULL ;
+} ;
+
+/* Get pointer to last item. Returns NULL if vector empty. */
+Inline p_vector_item
+vector_get_last_item(vector v)
+{
+ return (v->end != 0) ? v->p_items[v->end - 1] : NULL ;
+} ;
+
+/* Set item value in vector. Extend vector if required. */
+/* NB: it is the caller's responsibility to release memory used by any */
+/* current value of the item, if required. */
+Inline void
+vector_set_item(vector v, vector_index i, void* p_v)
+{
+ vector_ensure(v, i) ;
+ v->p_items[i] = (p_vector_item)p_v ;
+} ;
+
+/* Push value onto vector, extending as required. */
+Inline void
+vector_push_item(vector v, void* p_v)
+{
+ vector_index i = vector_extend_by_1(v) ;
+ v->p_items[i] = (p_vector_item)p_v ;
+} ;
+
+/* Pop value from vector. Returns NULL if vector is empty. */
+/* NB: does NOT change the size of the vector body. */
+Inline p_vector_item
+vector_pop_item(vector v)
+{
+ return (v->end > 0) ? v->p_items[--v->end] : NULL ;
+} ;
#endif /* _ZEBRA_VECTOR_H */
diff --git a/lib/vty.c b/lib/vty.c
index e4818eb6..680286c0 100644
--- a/lib/vty.c
+++ b/lib/vty.c
@@ -17,7 +17,7 @@
* You should have received a copy of the GNU General Public License
* along with GNU Zebra; see the file COPYING. If not, write to the Free
* Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
- * 02111-1307, USA.
+ * 02111-1307, USA.
*/
#include <zebra.h>
@@ -40,7 +40,7 @@
#include <arpa/telnet.h>
/* Vty events */
-enum event
+enum event
{
VTY_SERV,
VTY_READ,
@@ -57,7 +57,7 @@ static void vty_event (enum event, int, struct vty *);
/* Extern host structure from command.c */
extern struct host host;
-
+
/* Vector which store each vty structure. */
static vector vtyvec;
@@ -89,8 +89,8 @@ static u_char restricted_mode = 0;
/* Integrated configuration file path */
char integrate_default[] = SYSCONFDIR INTEGRATE_DEFAULT_CONFIG;
-
-/* VTY standard output function. */
+
+/* VTY standard output function. vty == NULL or VTY_SHELL => stdout */
int
vty_out (struct vty *vty, const char *format, ...)
{
@@ -151,6 +151,27 @@ vty_out (struct vty *vty, const char *format, ...)
return len;
}
+int
+vty_puts(struct vty *vty, const char* str)
+{
+ return vty_out(vty, "%s", str) ;
+}
+
+int
+vty_out_newline(struct vty *vty)
+{
+ return vty_out(vty, "%s", VTY_NEWLINE) ;
+}
+
+/* 123456789012345678901234 */
+const char* vty_spaces_string = " " ;
+
+int
+vty_out_indent(struct vty *vty, int indent)
+{
+ return vty_puts(vty, VTY_SPACES(indent)) ;
+}
+
static int
vty_log_out (struct vty *vty, const char *level, const char *proto_str,
const char *format, struct timestamp_control *ctl, va_list va)
@@ -209,7 +230,7 @@ void
vty_time_print (struct vty *vty, int cr)
{
char buf [25];
-
+
if (quagga_timestamp(0, buf, sizeof(buf)) == 0)
{
zlog (NULL, LOG_INFO, "quagga_timestamp error");
@@ -382,7 +403,7 @@ vty_auth (struct vty *vty, char *buf)
vty_out (vty, "%% Bad passwords, too many failures!%s", VTY_NEWLINE);
vty->status = VTY_CLOSE;
}
- else
+ else
{
/* AUTH_ENABLE_NODE */
vty->fail = 0;
@@ -423,7 +444,7 @@ vty_command (struct vty *vty, char *buf)
protocolname = zlog_proto_names[zlog_default->protocol];
else
protocolname = zlog_proto_names[ZLOG_NONE];
-
+
#ifdef CONSUMED_TIME_CHECK
GETRUSAGE(&after);
if ((realtime = thread_consumed_time(&after, &before, &cputime)) >
@@ -455,7 +476,7 @@ vty_command (struct vty *vty, char *buf)
return ret;
}
-
+
static const char telnet_backward_char = 0x08;
static const char telnet_space_char = ' ';
@@ -646,7 +667,7 @@ vty_forward_word (struct vty *vty)
{
while (vty->cp != vty->length && vty->buf[vty->cp] != ' ')
vty_forward_char (vty);
-
+
while (vty->cp != vty->length && vty->buf[vty->cp] == ' ')
vty_forward_char (vty);
}
@@ -746,7 +767,7 @@ vty_delete_char (struct vty *vty)
vty->length--;
memmove (&vty->buf[vty->cp], &vty->buf[vty->cp + 1], size - 1);
vty->buf[vty->length] = '\0';
-
+
if (vty->node == AUTH_NODE || vty->node == AUTH_ENABLE_NODE)
return;
@@ -776,7 +797,7 @@ vty_kill_line (struct vty *vty)
int size;
size = vty->length - vty->cp;
-
+
if (size == 0)
return;
@@ -871,7 +892,7 @@ vty_complete_command (struct vty *vty)
vector_set (vline, '\0');
matched = cmd_complete_command (vline, vty, &ret);
-
+
cmd_free_strvec (vline);
vty_out (vty, "%s", VTY_NEWLINE);
@@ -985,7 +1006,7 @@ vty_describe_command (struct vty *vty)
vline = vector_init (1);
vector_set (vline, '\0');
}
- else
+ else
if (isspace ((int) vty->buf[vty->length - 1]))
vector_set (vline, '\0');
@@ -1004,7 +1025,7 @@ vty_describe_command (struct vty *vty)
vty_out (vty, "%% There is no matched command.%s", VTY_NEWLINE);
goto out;
break;
- }
+ }
/* Get width of command string. */
width = 0;
@@ -1033,7 +1054,7 @@ vty_describe_command (struct vty *vty)
{
if (desc->cmd[0] == '\0')
continue;
-
+
if (strcmp (desc->cmd, command_cr) == 0)
{
desc_cr = desc;
@@ -1220,7 +1241,7 @@ vty_telnet_option (struct vty *vty, unsigned char *buf, int nbytes)
vty->iac_sb_in_progress = 1;
return 0;
break;
- case SE:
+ case SE:
{
if (!vty->iac_sb_in_progress)
return 0;
@@ -1364,7 +1385,7 @@ vty_read (struct thread *thread)
vty->status = VTY_CLOSE;
}
- for (i = 0; i < nbytes; i++)
+ for (i = 0; i < nbytes; i++)
{
if (buf[i] == IAC)
{
@@ -1378,7 +1399,7 @@ vty_read (struct thread *thread)
vty->iac = 0;
}
}
-
+
if (vty->iac_sb_in_progress && !vty->iac)
{
if (vty->sb_len < sizeof(vty->sb_buf))
@@ -1396,7 +1417,7 @@ vty_read (struct thread *thread)
i += ret;
continue;
}
-
+
if (vty->status == VTY_MORE)
{
@@ -1723,10 +1744,10 @@ vty_accept (struct thread *thread)
(buf = sockunion_su2str (&su)));
free (buf);
close (vty_sock);
-
+
/* continue accepting connections */
vty_event (VTY_SERV, accept_sock, NULL);
-
+
prefix_free (p);
return 0;
@@ -1745,24 +1766,24 @@ vty_accept (struct thread *thread)
(buf = sockunion_su2str (&su)));
free (buf);
close (vty_sock);
-
+
/* continue accepting connections */
vty_event (VTY_SERV, accept_sock, NULL);
-
+
prefix_free (p);
return 0;
}
}
#endif /* HAVE_IPV6 */
-
+
prefix_free (p);
on = 1;
- ret = setsockopt (vty_sock, IPPROTO_TCP, TCP_NODELAY,
+ ret = setsockopt (vty_sock, IPPROTO_TCP, TCP_NODELAY,
(char *) &on, sizeof (on));
if (ret < 0)
- zlog (NULL, LOG_INFO, "can't set sockopt to vty_sock : %s",
+ zlog (NULL, LOG_INFO, "can't set sockopt to vty_sock : %s",
safe_strerror (errno));
zlog (NULL, LOG_INFO, "Vty connection from %s",
@@ -1827,7 +1848,7 @@ vty_serv_sock_addrinfo (const char *hostname, unsigned short port)
}
ret = listen (sock, 3);
- if (ret < 0)
+ if (ret < 0)
{
close (sock); /* Avoid sd leak. */
continue;
@@ -1860,7 +1881,7 @@ vty_serv_sock_family (const char* addr, unsigned short port, int family)
#ifdef HAVE_IPV6
case AF_INET6:
naddr=&su.sin6.sin6_addr;
-#endif
+#endif
}
if(naddr)
@@ -1895,7 +1916,7 @@ vty_serv_sock_family (const char* addr, unsigned short port, int family)
/* Listen socket under queue 3. */
ret = listen (accept_sock, 3);
- if (ret < 0)
+ if (ret < 0)
{
zlog (NULL, LOG_WARNING, "can't listen socket");
close (accept_sock); /* Avoid sd leak. */
@@ -1919,7 +1940,7 @@ vty_serv_un (const char *path)
struct sockaddr_un serv;
mode_t old_mask;
struct zprivs_ids_t ids;
-
+
/* First of all, unlink existing socket */
unlink (path);
@@ -1963,7 +1984,7 @@ vty_serv_un (const char *path)
umask (old_mask);
zprivs_get_ids(&ids);
-
+
if (ids.gid_vty > 0)
{
/* set group of socket */
@@ -1987,7 +2008,7 @@ vtysh_accept (struct thread *thread)
int client_len;
struct sockaddr_un client;
struct vty *vty;
-
+
accept_sock = THREAD_FD (thread);
vty_event (VTYSH_SERV, accept_sock, NULL);
@@ -2011,7 +2032,7 @@ vtysh_accept (struct thread *thread)
close (sock);
return -1;
}
-
+
#ifdef VTYSH_DEBUG
printf ("VTY shell accept\n");
#endif /* VTYSH_DEBUG */
@@ -2234,11 +2255,11 @@ vty_read_file (FILE *confp)
vty->fd = 0; /* stdout */
vty->type = VTY_TERM;
vty->node = CONFIG_NODE;
-
+
/* Execute configuration file */
ret = config_from_file (vty, confp);
- if ( !((ret == CMD_SUCCESS) || (ret == CMD_ERR_NOTHING_TODO)) )
+ if ( !((ret == CMD_SUCCESS) || (ret == CMD_ERR_NOTHING_TODO)) )
{
switch (ret)
{
@@ -2249,7 +2270,7 @@ vty_read_file (FILE *confp)
fprintf (stderr, "There is no such command.\n");
break;
}
- fprintf (stderr, "Error occured during reading below line.\n%s\n",
+ fprintf (stderr, "Error occured during reading below line.\n%s\n",
vty->buf);
vty_close (vty);
exit (1);
@@ -2267,7 +2288,7 @@ vty_use_backup_config (char *fullpath)
int tmp, sav;
int c;
char buffer[512];
-
+
fullpath_sav = malloc (strlen (fullpath) + strlen (CONF_BACKUP_EXT) + 1);
strcpy (fullpath_sav, fullpath);
strcat (fullpath_sav, CONF_BACKUP_EXT);
@@ -2279,7 +2300,7 @@ vty_use_backup_config (char *fullpath)
fullpath_tmp = malloc (strlen (fullpath) + 8);
sprintf (fullpath_tmp, "%s.XXXXXX", fullpath);
-
+
/* Open file to configuration write. */
tmp = mkstemp (fullpath_tmp);
if (tmp < 0)
@@ -2297,13 +2318,13 @@ vty_use_backup_config (char *fullpath)
free (fullpath_tmp);
return NULL;
}
-
+
while((c = read (sav, buffer, 512)) > 0)
write (tmp, buffer, c);
-
+
close (sav);
close (tmp);
-
+
if (chmod(fullpath_tmp, CONFIGFILE_MASK) != 0)
{
unlink (fullpath_tmp);
@@ -2311,12 +2332,12 @@ vty_use_backup_config (char *fullpath)
free (fullpath_tmp);
return NULL;
}
-
+
if (link (fullpath_tmp, fullpath) == 0)
ret = fopen (fullpath, "r");
unlink (fullpath_tmp);
-
+
free (fullpath_sav);
free (fullpath_tmp);
return ret;
@@ -2338,7 +2359,7 @@ vty_read_config (char *config_file,
if (! IS_DIRECTORY_SEP (config_file[0]))
{
getcwd (cwd, MAXPATHLEN);
- tmp = XMALLOC (MTYPE_TMP,
+ tmp = XMALLOC (MTYPE_TMP,
strlen (cwd) + strlen (config_file) + 2);
sprintf (tmp, "%s/%s", cwd, config_file);
fullpath = tmp;
@@ -2352,13 +2373,13 @@ vty_read_config (char *config_file,
{
fprintf (stderr, "%s: failed to open configuration file %s: %s\n",
__func__, fullpath, safe_strerror (errno));
-
+
confp = vty_use_backup_config (fullpath);
if (confp)
fprintf (stderr, "WARNING: using backup configuration file!\n");
else
{
- fprintf (stderr, "can't open configuration file [%s]\n",
+ fprintf (stderr, "can't open configuration file [%s]\n",
config_file);
exit(1);
}
@@ -2397,7 +2418,7 @@ vty_read_config (char *config_file,
{
fprintf (stderr, "%s: failed to open configuration file %s: %s\n",
__func__, config_default_dir, safe_strerror (errno));
-
+
confp = vty_use_backup_config (config_default_dir);
if (confp)
{
@@ -2410,7 +2431,7 @@ vty_read_config (char *config_file,
config_default_dir);
exit (1);
}
- }
+ }
else
fullpath = config_default_dir;
}
@@ -2420,7 +2441,7 @@ vty_read_config (char *config_file,
fclose (confp);
host_config_set (fullpath);
-
+
if (tmp)
XFREE (MTYPE_TMP, fullpath);
}
@@ -2432,7 +2453,7 @@ vty_log (const char *level, const char *proto_str,
{
unsigned int i;
struct vty *vty;
-
+
if (!vtyvec)
return;
@@ -2457,7 +2478,7 @@ vty_log_fixed (const char *buf, size_t len)
/* vty may not have been initialised */
if (!vtyvec)
return;
-
+
iov[0].iov_base = (void *)buf;
iov[0].iov_len = len;
iov[1].iov_base = (void *)"\r\n";
@@ -2494,7 +2515,7 @@ vty_config_unlock (struct vty *vty)
}
return vty->config;
}
-
+
/* Master of the threads. */
static struct thread_master *master;
@@ -2528,7 +2549,7 @@ vty_event (enum event event, int sock, struct vty *vty)
{
if (vty->t_timeout)
thread_cancel (vty->t_timeout);
- vty->t_timeout =
+ vty->t_timeout =
thread_add_timer (master, vty_timeout, vty, vty->v_timeout);
}
break;
@@ -2544,13 +2565,13 @@ vty_event (enum event event, int sock, struct vty *vty)
}
if (vty->v_timeout)
{
- vty->t_timeout =
+ vty->t_timeout =
thread_add_timer (master, vty_timeout, vty, vty->v_timeout);
}
break;
}
}
-
+
DEFUN (config_who,
config_who_cmd,
"who",
@@ -2839,14 +2860,14 @@ vty_config_write (struct vty *vty)
/* exec-timeout */
if (vty_timeout_val != VTY_TIMEOUT_DEFAULT)
- vty_out (vty, " exec-timeout %ld %ld%s",
+ vty_out (vty, " exec-timeout %ld %ld%s",
vty_timeout_val / 60,
vty_timeout_val % 60, VTY_NEWLINE);
/* login */
if (no_password_check)
vty_out (vty, " no login%s", VTY_NEWLINE);
-
+
if (restricted_mode != restricted_mode_default)
{
if (restricted_mode_default)
@@ -2854,7 +2875,7 @@ vty_config_write (struct vty *vty)
else
vty_out (vty, " anonymous restricted%s", VTY_NEWLINE);
}
-
+
vty_out (vty, "!%s", VTY_NEWLINE);
return CMD_SUCCESS;
@@ -2933,7 +2954,7 @@ vty_get_cwd ()
int
vty_shell (struct vty *vty)
{
- return vty->type == VTY_SHELL ? 1 : 0;
+ return ((vty == NULL) || (vty->type == VTY_SHELL)) ? 1 : 0;
}
int
@@ -2945,7 +2966,7 @@ vty_shell_serv (struct vty *vty)
void
vty_init_vtysh ()
{
- vtyvec = vector_init (VECTOR_MIN_SIZE);
+ vtyvec = vector_init (0);
}
/* Install vty's own commands like `who' command. */
@@ -2955,12 +2976,12 @@ vty_init (struct thread_master *master_thread)
/* For further configuration read, preserve current directory. */
vty_save_cwd ();
- vtyvec = vector_init (VECTOR_MIN_SIZE);
+ vtyvec = vector_init (0);
master = master_thread;
/* Initilize server thread vector. */
- Vvty_serv_thread = vector_init (VECTOR_MIN_SIZE);
+ Vvty_serv_thread = vector_init (0);
/* Install bgp top node. */
install_node (&vty_node, vty_config_write);
diff --git a/lib/vty.h b/lib/vty.h
index 7df04b5f..1e7f1261 100644
--- a/lib/vty.h
+++ b/lib/vty.h
@@ -28,7 +28,7 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
#define VTY_MAXHIST 20
/* VTY struct. */
-struct vty
+struct vty
{
/* File descripter of this vty. */
int fd;
@@ -124,7 +124,13 @@ struct vty
#define INTEGRATE_DEFAULT_CONFIG "Quagga.conf"
/* Small macro to determine newline is newline only or linefeed needed. */
-#define VTY_NEWLINE ((vty->type == VTY_TERM) ? "\r\n" : "\n")
+#define VTY_NEWLINE (((vty != NULL) && (vty->type == VTY_TERM)) ? "\r\n" : "\n")
+
+/* For indenting, mostly. */
+extern const char* vty_spaces_string ;
+#define VTY_MAX_SPACES 24
+#define VTY_SPACES(n) (vty_spaces_string + ((n) < VTY_MAX_SPACES \
+ ? VTY_MAX_SPACES - (n) : 0))
/* Default time out value */
#define VTY_TIMEOUT_DEFAULT 600
@@ -207,12 +213,15 @@ extern void vty_terminate (void);
extern void vty_reset (void);
extern struct vty *vty_new (void);
extern int vty_out (struct vty *, const char *, ...) PRINTF_ATTRIBUTE(2, 3);
+extern int vty_puts(struct vty* vty, const char* str) ;
+extern int vty_out_newline(struct vty *vty) ;
+extern int vty_out_indent(struct vty *vty, int indent) ;
extern void vty_read_config (char *, char *);
extern void vty_time_print (struct vty *, int);
extern void vty_serv_sock (const char *, unsigned short, const char *);
extern void vty_close (struct vty *);
extern char *vty_get_cwd (void);
-extern void vty_log (const char *level, const char *proto,
+extern void vty_log (const char *level, const char *proto,
const char *fmt, struct timestamp_control *, va_list);
extern int vty_config_lock (struct vty *);
extern int vty_config_unlock (struct vty *);