summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--lib/command.c3
-rw-r--r--lib/command.h1
-rw-r--r--lib/command_common.h3
-rw-r--r--lib/command_parse.c286
-rw-r--r--lib/command_parse.h38
-rw-r--r--lib/elstring.c51
-rw-r--r--lib/elstring.h10
-rw-r--r--lib/list_util.c62
-rw-r--r--lib/list_util.h213
-rw-r--r--lib/qstring.h6
-rw-r--r--lib/vty.c10
-rw-r--r--lib/vty_cli.c70
-rw-r--r--lib/vty_io_file.c2
-rw-r--r--lib/vty_io_term.c4
14 files changed, 606 insertions, 153 deletions
diff --git a/lib/command.c b/lib/command.c
index 4057bcfc..f9747a33 100644
--- a/lib/command.c
+++ b/lib/command.c
@@ -2159,6 +2159,8 @@ cmd_init (bool terminal)
{
srand(time(NULL)) ;
+ cmd_parser_init() ;
+
if (host.cwd == NULL) /* in case cmd_cwd() not called, yet */
cmd_getcwd() ;
@@ -2333,7 +2335,6 @@ cmd_terminate ()
cmd->items = NULL ; /* gone */
- XFREE (MTYPE_CMD_STRING, cmd->r_string) ; /* sets NULL */
XFREE (MTYPE_CMD_STRING, cmd->r_doc) ;
} ;
}
diff --git a/lib/command.h b/lib/command.h
index 3ad758d5..bb8b0bd9 100644
--- a/lib/command.h
+++ b/lib/command.h
@@ -55,7 +55,6 @@
.nt = 0, \
.nt_max = 0, \
.vararg = NULL, \
- .r_string = NULL, \
.r_doc = NULL, \
} ;
diff --git a/lib/command_common.h b/lib/command_common.h
index 26f2909f..3621a4b6 100644
--- a/lib/command_common.h
+++ b/lib/command_common.h
@@ -278,8 +278,7 @@ struct cmd_command
struct cmd_item* vararg ; /* if there is a vararg item */
- char* r_string ;
- char* r_doc ;
+ char* r_doc ; /* rendered documentation */
//char* config ; /* Configuration string */
//vector subconfig ; /* Sub configuration string */
diff --git a/lib/command_parse.c b/lib/command_parse.c
index ba1b217e..81e4ad74 100644
--- a/lib/command_parse.c
+++ b/lib/command_parse.c
@@ -29,6 +29,8 @@
#include "command_local.h"
#include "command_parse.h"
#include "memory.h"
+#include "list_util.h"
+#include "elstring.h"
/*==============================================================================
* Command Description objects.
@@ -44,13 +46,35 @@ static int cmd_cmp_item(const cmd_item* a, const cmd_item* b) ;
static int cmd_cmp_range_items(const cmd_item a, const cmd_item b) ;
static bool cmd_item_is_option(cmd_item_type_t it) ;
static bool cmd_item_is_vararg(cmd_item_type_t it) ;
+static void cmd_set_str(cmd_item n, const char* str) ;
/*------------------------------------------------------------------------------
- * Dummy eol_item
+ * Table of known "words" -- so that a word can be represented by its
+ * address in this table and its length.
+ *
+ * See: cmd_set_str()
+ */
+enum { word_lump_length = 500 * 8 } ; /* plenty ? */
+enum { command_specification_length = 500 } ; /* plenty !! */
+
+typedef struct word_lump* word_lump ;
+
+struct word_lump
+{
+ word_lump next ;
+
+ char* end ; /* points at the terminating '\0' */
+ char words[word_lump_length] ;
+} ;
+
+static struct dl_base_pair(word_lump) word_lumps = INIT_DL_BASE_PAIR ;
+
+/*------------------------------------------------------------------------------
+ * Dummy eol_item -- completed in cmd_parse_init()
*/
static struct cmd_item eol_item =
{
- .str = "<cr>",
+ .str = ELSTRING_INIT, /* see cmd_parse_init() */
.doc = "",
.next = NULL,
@@ -152,15 +176,18 @@ extern void
cmd_compile(cmd_command cmd)
{
vector multvec ;
+ const char* p ;
char* cp ;
char* qp ;
char* dp ;
bool opt ;
bool vararg ;
+ char spec[command_specification_length + 1] ;
+
/* Initialise the compiled version of the command */
- assert((cmd->r_doc == NULL) && (cmd->r_string == NULL)) ;
+ assert(cmd->r_doc == NULL) ;
cmd->items = vector_init_new(NULL, 10) ; /* plenty ! */
cmd->nt_min = 0 ;
@@ -168,63 +195,68 @@ cmd_compile(cmd_command cmd)
cmd->nt_max = 0 ;
cmd->vararg = NULL ;
cmd->r_doc = XSTRDUP(MTYPE_CMD_STRING, cmd->doc) ; /* NULL => "" */
- cmd->r_string = XSTRDUP(MTYPE_CMD_STRING, cmd->string) ;
+
+ if (strlen(cmd->string) > command_specification_length)
+ cmd_fail_item(cmd, "command specification *too* long") ;
/* Simplify the command line string by replacing TABs by spaces, and barfing
* on control characters. Strip leading and trailing spaces and any spaces
- * between brackets... checking for matching brackets.
+ * between brackets -- checking for matching brackets -- and squeeze out
+ * multiple spaces.
*/
- cp = cmd->r_string ;
- while (*cp != '\0')
+ qp = spec ;
+ p = cmd->string ;
+
+ while ((*p == ' ') || (*p == '\t'))
+ ++p ; /* squeeze out leading spaces */
+
+ while (*p != '\0') /* starts with not ' ' and not '\t' */
{
- if (!iscntrl(*cp))
- ++cp ;
- else if (*cp == '\t')
- *cp++ = ' ' ;
+ if (!iscntrl(*p))
+ *qp = *p ;
+ else if (*p == '\t')
+ *qp = ' ' ;
else
cmd_fail_item(cmd, "improper control character in string") ;
+
+ if ((*qp != ' ') || (*(qp - 1) != ' '))
+ ++qp ; /* squeeze out multiple spaces */
+
+ ++p ;
} ;
- cp = cmd->r_string ;
- while (*cp == ' ')
- ++cp ;
+ while ((qp > spec) && (*(qp - 1) == ' '))
+ --qp ; /* squeeze out trailing spaces */
+
+ *qp = '\0' ;
- qp = cmd->r_string ;
+ cp = spec ;
+ qp = spec ;
while (*cp != '\0')
{
- if (*cp != ' ')
+ if ((*cp == '(') || (*cp == '[') || (*cp == '<') || (*cp == '{'))
{
- if ((*cp == '(') || (*cp == '[') || (*cp == '<') || (*cp == '{'))
+ /* Check for balanced brackets and remove any spaces between.
+ *
+ * Checks for enclosed brackets being balanced as well.
+ *
+ * Leaves cp pointing at the trailing bracket.
+ */
+ char* sp = cp ;
+ cp = cmd_item_brackets(cmd, cp) ;
+ while (sp < cp)
{
- /* Check for balanced brackets and remove any spaces between.
- *
- * Checks for enclosed brackets being balanced as well.
- *
- * Leaves cp pointing at the trailing bracket.
- */
- char* sp = cp ;
- cp = cmd_item_brackets(cmd, cp) ;
- while (sp < cp)
- {
- if (*sp != ' ')
- *qp++ = *sp++ ;
- else
- ++sp ;
- } ;
+ if (*sp != ' ')
+ *qp++ = *sp++ ;
+ else
+ ++sp ;
} ;
- }
- else
- {
- while (*(cp + 1) == ' ')
- ++cp ;
- if (*(cp + 1) == '\0')
- break ;
} ;
*qp++ = *cp++ ;
} ;
- *qp++ = '\0' ; /* terminate reduced string */
+ *qp = '\0' ; /* terminate reduced string */
/* Simplify the documentation string by replacing TABs by spaces, and barfing
* on control characters other than '\n'.
@@ -265,11 +297,11 @@ cmd_compile(cmd_command cmd)
*qp++ = *dp++ ;
} ;
- *qp++ = '\0' ; /* terminate reduced string */
+ *qp = '\0' ; /* terminate reduced string */
/* Processing loop */
- cp = cmd->r_string ;
+ cp = spec ;
dp = cmd->r_doc ;
opt = false ;
@@ -380,7 +412,7 @@ cmd_compile(cmd_command cmd)
if (cmd_item_is_option(n->type))
opt = true ;
else if (opt)
- cmd_fail_item(cmd, "can only have [option] after [option]") ;
+ cmd_fail_item(cmd, "can only have [option] after [option]") ;
/* Check vararg item state -- can only be trailing */
if (vararg)
@@ -434,7 +466,8 @@ cmd_compile(cmd_command cmd)
bool repeat ;
if (n->type == item_keyword)
- repeat = strcmp(n->str, p->str) == 0 ;
+ repeat = (els_body_nn(n->str) == els_body_nn(p->str))
+ && (els_len_nn(n->str) == els_len_nn(p->str)) ;
else if (n->type == item_range)
repeat = cmd_cmp_range_items(n, p) == 0 ;
else
@@ -478,7 +511,6 @@ cmd_compile_check(cmd_command cmd)
if ( (cmd->string == NULL)
|| (cmd->func == NULL)
|| (cmd->items == NULL)
- || (cmd->r_string == NULL)
|| (cmd->r_doc == NULL) )
ok = false ;
@@ -666,7 +698,8 @@ cmd_make_item(cmd_command cmd, char* cp, char* dp)
/* Zeroising has set:
*
- * * cmd = NULL -- set below
+ * * str = NULL -- set below
+ * * str_len = 0 -- set below
* * doc = NULL -- set below
*
* * next = NULL -- set elsewhere if multiple.
@@ -681,7 +714,7 @@ cmd_make_item(cmd_command cmd, char* cp, char* dp)
*/
confirm(item_null == 0) ;
- n->str = cp ;
+ cmd_set_str(n, cp) ;
n->doc = dp ;
/* Worry about option state */
@@ -931,7 +964,7 @@ cmd_cmp_item(const cmd_item* a, const cmd_item* b)
if ((*a)->type == item_range)
return cmd_cmp_range_items(*a, *b) ;
else
- return strcmp ((*a)->str, (*b)->str);
+ return els_cmp((*a)->str, (*b)->str) ;
} ;
} ;
@@ -958,6 +991,59 @@ cmd_cmp_range_items(const cmd_item a, const cmd_item b)
return 0 ;
} ;
+/*------------------------------------------------------------------------------
+ * Set the str and str_len entries for a given item.
+ *
+ * The str entry of a cmd_item is set to point at a pool of known values.
+ * This means that where two str entries are the same, they will have the
+ * same address !
+ */
+static void
+cmd_set_str(cmd_item n, const char* str)
+{
+ uint len ;
+ word_lump lump ;
+ char* word ;
+
+ len = strlen(str) ;
+
+ lump = dsl_head(word_lumps) ;
+ while (1)
+ {
+ if (lump == NULL)
+ {
+ lump = XCALLOC(MTYPE_CMD_STRING, sizeof(struct word_lump)) ;
+ lump->end = lump->words ;
+
+ dsl_append(word_lumps, lump, next) ;
+ } ;
+
+ word = strstr(lump->words, str) ;
+ if (word != NULL)
+ break ;
+
+ if (dsl_next(lump, next) != NULL)
+ {
+ lump = dsl_next(lump, next) ;
+ continue ;
+ } ;
+
+ if (len >= (&lump->words[word_lump_length] - lump->end))
+ {
+ lump = NULL ;
+ continue ;
+ } ;
+
+ word = lump->end ;
+ memcpy(word, str, len) ;
+ lump->end += len ;
+ *lump->end = '\0' ;
+ break ;
+ } ;
+
+ els_set_n_nn(n->str, word, len) ;
+} ;
+
/*==============================================================================
* Token objects
*/
@@ -1062,6 +1148,9 @@ cmd_token_set(token_vector tv, vector_index_t i,
qs_set_alias_n(t->qs, p, len) ;
qs_els_copy_nn(t->ot, t->qs) ;
+
+ t->w_len = 0 ; /* no words matched to, yet */
+ t->seen = 0 ; /* no matches attempted, yet */
} ;
@@ -2249,15 +2338,23 @@ static match_type_t
cmd_ipv4_address_match(cmd_token t)
{
match_strength_t ms ;
+ match_type_t mt ;
+
+ if ((t->seen & item_ipv4_address_bit) != 0)
+ return t->match[item_ipv4_address] ;
ms = cmd_ipv4_match(cmd_token_make_string(t), 0) ;
- if (ms == ms_var_complete)
- return mt_ipv4_address_complete ;
- if (ms == ms_partial)
- return mt_ipv4_address_partial ;
+ if (ms == ms_var_complete)
+ mt = mt_ipv4_address_complete ;
+ else if (ms == ms_partial)
+ mt = mt_ipv4_address_partial ;
+ else
+ mt = mt_no_match ;
- return mt_no_match ;
+ t->seen |= item_ipv4_address_bit ;
+
+ return t->match[item_ipv4_address] = mt ;
} ;
/*------------------------------------------------------------------------------
@@ -2277,15 +2374,23 @@ static match_type_t
cmd_ipv4_prefix_match(cmd_token t)
{
match_strength_t ms ;
+ match_type_t mt ;
+
+ if ((t->seen & item_ipv4_prefix_bit) != 0)
+ return t->match[item_ipv4_prefix] ;
ms = cmd_ipv4_match(cmd_token_make_string(t), 32) ;
- if (ms == ms_var_complete)
- return mt_ipv4_prefix_complete ;
- if (ms == ms_partial)
- return mt_ipv4_prefix_partial ;
+ if (ms == ms_var_complete)
+ mt = mt_ipv4_prefix_complete ;
+ else if (ms == ms_partial)
+ mt = mt_ipv4_prefix_partial ;
+ else
+ mt = mt_no_match ;
+
+ t->seen |= item_ipv4_prefix_bit ;
- return mt_no_match ;
+ return t->match[item_ipv4_prefix] = mt ;
} ;
/*------------------------------------------------------------------------------
@@ -2425,15 +2530,23 @@ static match_type_t
cmd_ipv6_address_match (cmd_token t)
{
match_strength_t ms ;
+ match_type_t mt ;
+
+ if ((t->seen & item_ipv6_address_bit) != 0)
+ return t->match[item_ipv6_address] ;
ms = cmd_ipv6_match(cmd_token_make_string(t), 0) ;
- if (ms == ms_var_complete)
- return mt_ipv6_address_complete ;
- if (ms == ms_partial)
- return mt_ipv6_address_partial ;
+ if (ms == ms_var_complete)
+ mt = mt_ipv6_address_complete ;
+ else if (ms == ms_partial)
+ mt = mt_ipv6_address_partial ;
+ else
+ mt = mt_no_match ;
+
+ t->seen |= item_ipv6_address_bit ;
- return mt_no_match ;
+ return t->match[item_ipv6_address] = mt ;
} ;
/*------------------------------------------------------------------------------
@@ -2452,15 +2565,23 @@ static match_type_t
cmd_ipv6_prefix_match(cmd_token t)
{
match_strength_t ms ;
+ match_type_t mt ;
+
+ if ((t->seen & item_ipv6_prefix_bit) != 0)
+ return t->match[item_ipv6_prefix] ;
ms = cmd_ipv6_match(cmd_token_make_string(t), 128) ;
- if (ms == ms_var_complete)
- return mt_ipv6_prefix_complete ;
- if (ms == ms_partial)
- return mt_ipv6_prefix_partial ;
+ if (ms == ms_var_complete)
+ mt = mt_ipv6_prefix_complete ;
+ else if (ms == ms_partial)
+ mt = mt_ipv6_prefix_partial ;
+ else
+ mt = mt_no_match ;
+
+ t->seen |= item_ipv6_prefix_bit ;
- return mt_no_match ;
+ return t->match[item_ipv6_prefix] = mt ;
} ;
/*------------------------------------------------------------------------------
@@ -2988,7 +3109,7 @@ cmd_item_filter(cmd_parsed parsed, cmd_item item, cmd_token t)
*/
if (t->type == cmd_tok_eol)
{
-// mt = (item->type == item_eol) ? mt_eol : mt_eol_partial ;
+ mt = (item->type == item_eol) ? mt_eol : mt_eol_partial ;
mt = mt_eol_partial ;
}
else
@@ -3003,7 +3124,16 @@ cmd_item_filter(cmd_parsed parsed, cmd_item item, cmd_token t)
break ;
case item_keyword:
- cw = els_cmp_word(t->ot, item->str) ;
+ if ((t->word == els_body_nn(item->str))
+ && (t->w_len == els_len_nn(item->str)))
+ cw = t->cw ;
+ else
+ {
+ cw = els_cmp_word(t->ot, item->str) ;
+ t->word = els_body_nn(item->str) ;
+ t->w_len = els_len_nn(item->str) ;
+ t->cw = cw ;
+ } ;
if (cw > 0) /* nope */
mt = mt_no_match ;
@@ -4738,8 +4868,14 @@ cmd_complete_command (vector tokens, int node, int *status)
return NULL;
} ;
-
-
-
-
-
+/*==============================================================================
+ * Initialise command parsing -- done before first command is installed.
+ *
+ * Complete the (much used) eol_item.
+ */
+extern void
+cmd_parser_init(void)
+{
+ dsl_init(word_lumps) ;
+ cmd_set_str(&eol_item, "<cr>") ;
+} ;
diff --git a/lib/command_parse.h b/lib/command_parse.h
index 5f17d083..d2c53385 100644
--- a/lib/command_parse.h
+++ b/lib/command_parse.h
@@ -30,6 +30,7 @@
#include "vector.h"
#include "vio_fifo.h"
#include "qstring.h"
+#include "elstring.h"
#include "qpath.h"
#include "elstring.h"
#include "memory.h"
@@ -117,6 +118,22 @@ enum {
CONFIRM(LONG_MAX >= item_max_number) ;
+enum cmd_item_type_bit
+{
+ item_null_bit = BIT(item_null),
+ item_eol_bit = BIT(item_eol),
+ item_option_word_bit = BIT(item_option_word),
+ item_vararg_bit = BIT(item_vararg),
+ item_word_bit = BIT(item_word),
+ item_ipv6_prefix_bit = BIT(item_ipv6_prefix),
+ item_ipv6_address_bit = BIT(item_ipv6_address),
+ item_ipv4_prefix_bit = BIT(item_ipv4_prefix),
+ item_ipv4_address_bit = BIT(item_ipv4_address),
+ item_range_bit = BIT(item_range),
+ item_keyword_bit = BIT(item_keyword),
+} ;
+typedef enum cmd_item_type_bit cmd_item_type_bit_t ;
+
/*------------------------------------------------------------------------------
* Sex cmd_item_type and return whether it is an "option" type, or not.
*
@@ -191,15 +208,16 @@ cmd_item_is_vararg(cmd_item_type_t itt)
typedef struct cmd_item* cmd_item ;
struct cmd_item
{
- const char* str ; /* in r_string -- original string form */
- const char* doc ; /* in r_doc -- description text */
+ elstring_t str ; /* in some word_lump */
- cmd_item next ; /* Next possibility (if any) */
+ const char* doc ; /* in r_doc -- description text */
+
+ cmd_item next ; /* Next possibility (if any) */
cmd_item_type_t type ;
- bool arg ; /* include in argv */
+ bool arg ; /* include in argv */
- /* For item_range values */
+ /* For item_range values */
bool range_sign_allowed ;
bool range_sign_required ;
long range_min ;
@@ -522,6 +540,14 @@ struct cmd_token
qstring_t qs ; /* token string */
elstring_t ot ; /* original token in the line */
+
+ const void* word ; /* set when a word match is done */
+ ulen w_len ; /* length of word match */
+ int cw ; /* result of the word match */
+
+ cmd_item_type_bit_t seen ; /* bit set when match done */
+ match_type_t match[item_type_count] ;
+ /* result of match seen */
} ;
typedef struct cmd_token* cmd_token ;
@@ -727,7 +753,7 @@ extern void cmd_complete_keyword(cmd_parsed parsed,
extern void cmd_get_parse_error(vio_fifo ebuf, cmd_parsed parsed, uint indent) ;
-
+extern void cmd_parser_init(void) ;
diff --git a/lib/elstring.c b/lib/elstring.c
index 64756a64..51089cdd 100644
--- a/lib/elstring.c
+++ b/lib/elstring.c
@@ -77,23 +77,13 @@ els_free(elstring els)
*/
/*------------------------------------------------------------------------------
- * Compare two elstrings -- returns the usual -ve, 0, +ve cmp result.
- *
- * NULL elstring is treated as empty.
+ * Basic string/length vs string/length comparison
*/
-extern int
-els_cmp(elstring a, elstring b)
+inline static int
+str_nn_cmp(const uchar* ap, ulen al, const uchar* bp, ulen bl)
{
- const uchar* ap ;
- const uchar* bp ;
- ulen al, bl ;
ulen n ;
- ap = els_body(a) ; /* NULL if a is NULL */
- bp = els_body(b) ;
- al = els_len(a) ; /* zero if a is NULL */
- bl = els_len(b) ;
-
n = (al <= bl) ? al : bl ; /* min(al, bl) */
while (n)
@@ -108,7 +98,29 @@ els_cmp(elstring a, elstring b)
} ;
/*------------------------------------------------------------------------------
- * Compare elstring against word.
+ * Compare two elstrings -- returns the usual -ve, 0, +ve cmp result.
+ *
+ * NULL elstring is treated as empty.
+ */
+extern int
+els_cmp(elstring a, elstring b)
+{
+ return str_nn_cmp(els_body(a), els_len(a), els_body(b), els_len(b)) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Compare two string/length values -- returns the usual -ve, 0, +ve cmp result
+ *
+ * (Not really an elstring function, but the infrastructure is here.)
+ */
+extern int
+els_nn_cmp(const void* ap, ulen al, const void* bp, ulen bl)
+{
+ return str_nn_cmp(ap, al, bp, bl) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Compare elstring against word elstring.
*
* Returns: -1 => elstring and word match to end of elstring
* 0 => elstring and word match completely
@@ -120,13 +132,13 @@ els_cmp(elstring a, elstring b)
* An empty elstring will partly match any non-empty word.
*/
extern int
-els_cmp_word(elstring a, const char* w)
+els_cmp_word(elstring a, elstring w)
{
- const uchar* ap, * ep ;
+ const uchar* ap, * ep, * wp ;
ulen al, wl ;
al = els_len(a) ; /* zero if a is NULL */
- wl = (w != NULL) ? strlen(w) : 0 ; /* zero if w is NULL */
+ wl = els_len(w) ; /* zero if w is NULL */
if (al > wl)
return +1 ; /* no match if longer */
@@ -135,16 +147,17 @@ els_cmp_word(elstring a, const char* w)
return (wl == 0) ? 0 : -1 ; /* exact or partial if empty */
ap = els_body_nn(a) ;
+ wp = els_body_nn(w) ;
/* Neither string is empty */
- if (*ap != *w)
+ if (*ap != *wp)
return +1 ; /* quick out if no match for 1st char */
ep = ap + al - 1 ;
while (ap < ep)
{
- if (*++ap != *++w)
+ if (*++ap != *++wp)
return 1 ;
} ;
diff --git a/lib/elstring.h b/lib/elstring.h
index 8ab1c58d..2b8e77fc 100644
--- a/lib/elstring.h
+++ b/lib/elstring.h
@@ -52,10 +52,9 @@ typedef struct elstring* elstring ;
/* Setting an elstring object to all zeros is enough to initialise it to
* an empty string.
*/
-enum
-{
- ELSTRING_INIT_ALL_ZEROS = true
-} ;
+enum { ELSTRING_INIT_ALL_ZEROS = true } ;
+
+#define ELSTRING_INIT { { { NULL }, 0 } }
/*==============================================================================
* Pointer pair and unsigned pointer pair and const versions.
@@ -177,7 +176,8 @@ extern elstring els_new(void) ;
extern elstring els_free(elstring els) ;
extern int els_cmp(elstring a, elstring b) ;
-extern int els_cmp_word(elstring a, const char* w) ;
+extern int els_nn_cmp(const void* ap, ulen al, const void* bp, ulen bl) ;
+extern int els_cmp_word(elstring a, elstring w) ;
extern int els_cmp_sig(elstring a, elstring b) ;
extern bool els_equal(elstring a, elstring b) ;
extern bool els_substring(elstring a, elstring b) ;
diff --git a/lib/list_util.c b/lib/list_util.c
index 0f7e8a02..09ddb6b5 100644
--- a/lib/list_util.c
+++ b/lib/list_util.c
@@ -53,14 +53,14 @@
* false => item not found on list (or item == NULL)
*/
extern bool
-ssl_del_func(void* p_this, void* item, size_t link_offset)
+ssl_del_func(void** p_this, void* item, size_t link_offset)
{
void* this ;
if (item == NULL)
return false ;
- while ((this = *(void**)p_this) != item)
+ while ((this = *p_this) != item)
{
if (this == NULL)
return false ;
@@ -68,12 +68,66 @@ ssl_del_func(void* p_this, void* item, size_t link_offset)
p_this = _sl_p_next(this, link_offset) ;
} ;
- *(void**)p_this = _sl_next(item, link_offset) ;
+ *p_this = _sl_next(item, link_offset) ;
return true ;
} ;
/*==============================================================================
- * Single Base, Double Link
+ * Double Base, Single Link
*/
+/*------------------------------------------------------------------------------
+ * Deleting item
+ *
+ * Have to chase down list to find item.
+ *
+ * Note that p_this:
+ *
+ * * starts as pointer to the base pointer, so should really be void**,
+ * but that causes all sorts of problems with strict-aliasing.
+ *
+ * So: have to cast to (void**) before dereferencing to get the address
+ * of the first item on the list.
+ *
+ * * as steps along the list p_this points to the "next pointer" in the
+ * previous item.
+ *
+ * The _sl_p_next() macro adds the offset of the "next pointer" to the
+ * address of the this item.
+ *
+ * * at the end, assigns the item's "next pointer" to the "next pointer"
+ * field pointed at by p_this.
+ *
+ * Note again the cast to (void**).
+ *
+ * Returns: true => removed item from list
+ * false => item not found on list (or item == NULL)
+ */
+extern bool
+dsl_del_func(struct dl_void_base_pair* p_base, void* item, size_t link_offset)
+{
+ void* this ;
+ void** p_this ;
+
+ if (item == NULL)
+ return false ;
+
+ p_this = &p_base->head ;
+
+ while ((this = *p_this) != item)
+ {
+ if (this == NULL)
+ return false ;
+
+ p_this = _sl_p_next(this, link_offset) ;
+ } ;
+
+ *p_this = _sl_next(item, link_offset) ;
+
+ if (item == p_base->tail)
+ p_base->tail = *p_this ;
+
+ return true ;
+} ;
+
diff --git a/lib/list_util.h b/lib/list_util.h
index f6a01d93..0ce3e6fa 100644
--- a/lib/list_util.h
+++ b/lib/list_util.h
@@ -109,15 +109,17 @@
#define dl_base_pair(ptr_t) { ptr_t head ; ptr_t tail ; }
-struct dl_void_list_pair list_pair(void*) ;
-struct dl_void_base_pair base_pair(void*) ;
+#define INIT_DL_BASE_PAIR { NULL, NULL }
+
+struct dl_void_list_pair dl_list_pair(void*) ;
+struct dl_void_base_pair dl_base_pair(void*) ;
#define _lu_off(obj, field) ((char*)&((obj)->field) - (char*)(obj))
/*==============================================================================
* Single Base, Single Link
*
- * To delete entry must chase down list to find it.
+ * To delete entry must chase down list to find it. Cannot insert at tail.
*
* Supports:
*
@@ -237,11 +239,11 @@ struct dl_void_base_pair base_pair(void*) ;
(base) = item ; \
} while (0)
-extern bool ssl_del_func(void* p_this, void* obj, size_t link_offset)
+Private bool ssl_del_func(void** p_this, void* obj, size_t link_offset)
__attribute__((noinline)) ;
#define ssl_del(base, item, next) \
- ssl_del_func((void*)(&base), item, _lu_off(item, next))
+ ssl_del_func((void**)(&base), item, _lu_off(item, next))
#define ssl_del_head(base, next) \
do { if ((base) != NULL) \
@@ -261,10 +263,10 @@ extern bool ssl_del_func(void* p_this, void* obj, size_t link_offset)
*/
#define _sl_p_next(item, off) \
- ( (char*)(item) + (off) )
+ ((void**)( (char*)(item) + (off) ))
#define _sl_next(item, off) \
- *(void**)_sl_p_next(item, off)
+ *_sl_p_next(item, off)
/*==============================================================================
* Single Base, Double Link
@@ -721,4 +723,201 @@ extern bool ssl_del_func(void* p_this, void* obj, size_t link_offset)
#define ddl_prev(item, list) \
((item) != NULL ? (item)->list.prev : NULL)
+ /*==============================================================================
+ * Double Base, Single Link
+ *
+ * To delete entry must chase down list to find it. Can insert at tail, but
+ * not remove (except by chasing down list).
+ *
+ * Supports:
+ *
+ * dsl_init(base) -- initialise base
+ *
+ * An empty list has *both* head and tail pointers NULL.
+ *
+ * NB: confusion will arise if only one of these pointers is NULL.
+ *
+ * dsl_push(base, item, next) -- insert at head of list
+ *
+ * Treat as void function. The item may *not* be NULL.
+ *
+ * Undefined if item is already on any list (including this one).
+ *
+ * dsl_append(base, item, next) -- insert at tail of list
+ *
+ * Treat as void function. The item may *not* be NULL.
+ *
+ * Undefined if item is already on any list (including this one).
+ *
+ * dsl_in_after(after, base, item, next) -- insert after
+ *
+ * Treat as void function. The after & item may *not* be NULL.
+ *
+ * Undefined if item is already on any list (including this one), or if
+ * after is not on the list.
+ *
+ * dsl_pop(&dst, base, next) -- pop head of list, if any
+ *
+ * Treat as function returning void*.
+ *
+ * Returns old head in dst and as return from "function".
+ *
+ * Returns NULL and sets dst == NULL if list is empty.
+ *
+ * dsl_del(base, item, next) -- delete from list
+ *
+ * Treat as void function. Does nothing if the item is NULL.
+ *
+ * Undefined if item is not on the list.
+ *
+ * dsl_del_head(base, next) -- delete head of list
+ *
+ * Treat as void function. Does nothing if the list is empty.
+ *
+ * dsl_del_tail(base, next) -- delete tail of list
+ *
+ * Treat as void function. Does nothing if the list is empty.
+ *
+ * dsl_head(base) -- return head of list
+ *
+ * Treat as function returning void*.
+ *
+ * dsl_tail(base) -- return tail of list
+ *
+ * Treat as function returning void*.
+ *
+ * dsl_next(item, next) -- step to next item, if any
+ *
+ * Treat as function returning void*. Returns NULL if the item is NULL.
+ *
+ * Note that dsl_del() and dsl_pop() do NOT affect the item->next pointer.
+ *
+ * Where:
+ *
+ * "base" to be an r-value of type: struct base_pair(struct item*)*
+ *
+ * That is... a variable or field which is a pointer to
+ *
+ * "item" to be an l-value of type struct item*
+ *
+ * "dst" to be an r-value of type struct item*
+ *
+ * "next" to be the name of a field in struct item of type struct item*
+ *
+ *------------------------------------------------------------------------------
+ * For example:
+ *
+ * struct item // definition for list items
+ * {
+ * ...
+ * struct item* bar_next ;
+ * ...
+ * } ;
+ *
+ * static struct base_pair(struct item*) bar_base ;
+ * // declaration of the list base
+ *
+ * // create item and add to list (adds at front)
+ * struct item* q = calloc(1, sizeof(struct item)) ;
+ * dsl_push(bar_base, q, bar_next) ;
+ *
+ * // remove item from list
+ * dsl_del(bar_base, q, bar_next) ;
+ *
+ * // walk a list
+ * struct item* t = dsl_head(bar_base) ;
+ * while (t != NULL)
+ * {
+ * ....
+ * t = dsl_next(t, bar_next) ;
+ * }
+ *
+ * // walk and empty out a list -- removing item before processing
+ * struct item* t ;
+ * while (dsl_pop(&t, bar_base, bar_next) != NULL)
+ * {
+ * .... // t points to old head of list
+ * }
+ *
+ * // walk and empty out a list -- removing after processing
+ * struct item* t ;
+ * while ((t = dsl_head(bar_base) != NULL)
+ * {
+ * ....
+ * dsl_del_head(bar_base, bar_next) ;
+ * }
+ *
+ * And for example:
+ *
+ * struct parent_item // parent structure containing list
+ * {
+ * ....
+ * struct base_pair(struct item*) bar_base ;
+ * ....
+ * }
+ *
+ * void footle(struct parent_item* parent, struct item* item)
+ * {
+ * ....
+ * dsl_push(parent->bar_base, item, bar_next) ;
+ * ....
+ * }
+ */
+
+ #define dsl_init(base) \
+ ((base).head = (base).tail = NULL)
+
+ #define dsl_push(base, item, next) \
+ do { (item)->next = (base).head ; \
+ if ((base).tail == NULL) \
+ (base).tail = (item) ; \
+ (base).head = (item) ; \
+ } while (0)
+
+ #define dsl_append(base, item, next) \
+ do { (item)->next = NULL ; \
+ if ((base).tail != NULL) \
+ (base).tail->next = (item) ; \
+ else \
+ (base).head = (item) ; \
+ (base).tail = (item) ; \
+ } while (0)
+
+ #define dsl_in_after(after, base, item, next) \
+ do { (item)->next = (after)->next ; \
+ (after)->next = (item) ; \
+ if ((base).tail == (after)) \
+ (base).tail = (item) ; \
+ } while (0)
+
+ Private bool dsl_del_func(struct dl_void_base_pair* p_base,
+ void* obj, size_t link_offset)
+ __attribute__((noinline)) ;
+ #define dsl_del(base, item, list) \
+ dsl_del_func((struct dl_void_base_pair*)(&base), item, \
+ _lu_off(item, next))
+
+ #define dsl_del_head(base, list) \
+ do { if ((base).head != NULL) \
+ { \
+ (base).head = (base).head->next ; \
+ if ((base).head == NULL) \
+ (base).tail = NULL ; \
+ } \
+ } while (0)
+
+ #define dsl_pop(dst, base, list) \
+ ((*(dst) = (base).head) != NULL \
+ ? ( ((base).head = (base).head->next) == NULL \
+ ? ( (base).tail = NULL, *(dst) ) \
+ : *(dst) ) \
+ : NULL)
+
+ #define dsl_head(base) ((base).head)
+
+ #define dsl_tail(base) ((base).tail)
+
+ #define dsl_next(item, list) \
+ ((item) != NULL ? (item)->next : NULL)
+
#endif /* _ZEBRA_LIST_UTIL_H */
diff --git a/lib/qstring.h b/lib/qstring.h
index b05c738b..536105d0 100644
--- a/lib/qstring.h
+++ b/lib/qstring.h
@@ -454,7 +454,7 @@ Inline usize qs_delete(qstring qs, usize n)
} ;
Inline int qs_cmp(qstring a, qstring b) ;
-Inline int qs_cmp_word(qstring a, const char* w) ;
+Inline int qs_cmp_word(qstring a, qstring w) ;
Inline int qs_cmp_sig(qstring a, qstring b) ;
Inline bool qs_equal(qstring a, qstring b) ;
Inline bool qs_substring(qstring a, qstring b) ;
@@ -666,9 +666,9 @@ qs_cmp(qstring a, qstring b)
* Compare qstrings to given word -- see els_cmp_word
*/
Inline int
-qs_cmp_word(qstring a, const char* w)
+qs_cmp_word(qstring a, qstring w)
{
- return els_cmp_word(qs_els(a), w) ;
+ return els_cmp_word(qs_els(a), qs_els(w)) ;
} ;
/*------------------------------------------------------------------------------
diff --git a/lib/vty.c b/lib/vty.c
index 173b59e1..2a936170 100644
--- a/lib/vty.c
+++ b/lib/vty.c
@@ -982,17 +982,21 @@ vty_read_config_file (int fd, const char* name, cmd_command first_cmd,
{
cmd_return_code_t ret ;
vty vty ;
+ qtime_t taking ;
vty = vty_config_read_open(fd, name, full_lex) ;
vty_cmd_loop_prepare(vty) ;
- zlog_info("Started reading configuration: %s", name) ;
+ taking = qt_get_monotonic() ;
ret = cmd_read_config(vty, first_cmd, ignore_warnings) ;
- zlog_info("Finished reading configuration%s",
- (ret == CMD_SUCCESS) ? "." : " -- FAILED") ;
+ taking = (qt_get_monotonic() - taking) / (QTIME_SECOND / 1000) ;
+
+ zlog_info("Finished reading configuration '%s' in %d.%d secs%s",
+ name, (int)(taking / 1000), (int)(taking % 1000),
+ (ret == CMD_SUCCESS) ? "." : " -- FAILED") ;
vty_cmd_loop_exit(vty) ;
diff --git a/lib/vty_cli.c b/lib/vty_cli.c
index 311d950c..cf85fb6c 100644
--- a/lib/vty_cli.c
+++ b/lib/vty_cli.c
@@ -2556,12 +2556,12 @@ uty_cli_hist_show(vty_cli cli)
*/
static uint uty_cli_help_parse(vty_cli cli) ;
-static void uty_cli_complete_keyword(vty_cli cli, const char* keyword) ;
+static void uty_cli_complete_keyword(vty_cli cli, elstring keyword) ;
static void uty_cli_complete_list(vty_cli cli, vector item_v) ;
static void uty_cli_describe_list(vty_cli cli, vector item_v) ;
static void uty_cli_describe_line(vty_cli cli, uint str_width, const char* str,
- const char* doc, uint len) ;
+ ulen str_len, const char* doc, ulen doc_len) ;
static uint uty_cli_width_to_use(vty_cli cli) ;
static void uty_cli_help_message(vty_cli cli, const char* msg) ;
@@ -2717,14 +2717,14 @@ uty_cli_help_parse(vty_cli cli)
* Can complete a keyword.
*/
static void
-uty_cli_complete_keyword(vty_cli cli, const char* keyword)
+uty_cli_complete_keyword(vty_cli cli, elstring keyword)
{
int pre, rep, ins, mov ;
cmd_complete_keyword(cli->parsed, &pre, &rep, &ins, &mov) ;
uty_cli_move(cli->cl, pre) ; /* move to start of token */
- uty_cli_replace(cli->cl, rep, keyword, strlen(keyword)) ;
+ uty_cli_replace(cli->cl, rep, els_body_nn(keyword), els_len_nn(keyword)) ;
assert(ins <= 2) ;
if (ins > 0)
@@ -2747,22 +2747,20 @@ uty_cli_complete_keyword(vty_cli cli, const char* keyword)
static void
uty_cli_complete_list(vty_cli cli, vector item_v)
{
- uint i, len, n ;
+ uint i, str_width, n ;
- len = 6 ;
+ str_width = 6 ;
for (i = 0 ; i < vector_length(item_v) ; ++i)
{
cmd_item item ;
- uint sl ;
item = vector_get_item(item_v, i) ;
- sl = strlen(item->str) ;
- if (len < sl)
- len = sl ;
+ if (str_width < els_len_nn(item->str))
+ str_width = els_len_nn(item->str) ;
} ;
- n = uty_cli_width_to_use(cli) / (len + 2) ;
+ n = uty_cli_width_to_use(cli) / (str_width + 2) ;
if (n == 0)
n = 1 ;
@@ -2770,12 +2768,20 @@ uty_cli_complete_list(vty_cli cli, vector item_v)
for (i = 0 ; i < vector_length(item_v) ; ++i)
{
cmd_item item ;
+ ulen str_len ;
+ ulen pad ;
+
item = vector_get_item(item_v, i) ;
if ((i % n) == 0)
uty_cli_out_newline(cli) ; /* clears cli_drawn */
- uty_cli_out(cli, "%-*s ", len, item->str) ;
+ str_len = els_len_nn(item->str) ;
+ uty_cli_write(cli, els_body_nn(item->str), str_len) ;
+
+ pad = (str_len < str_width) ? str_width - str_len : 0 ;
+
+ uty_cli_write_n(cli, telnet_spaces, pad + 2) ;
}
uty_cli_help_newline(cli) ;
} ;
@@ -2811,8 +2817,8 @@ uty_cli_describe_list(vty_cli cli, vector item_v)
item = vector_get_item(item_v, i) ;
- len = strlen(item->str) ;
- if (item->str[0] == '.')
+ len = els_len_nn(item->str) ;
+ if (*((char*)els_body_nn(item->str)) == '.')
len--;
if (len > str_width)
@@ -2844,10 +2850,18 @@ uty_cli_describe_list(vty_cli cli, vector item_v)
{
cmd_item item ;
const char* str, * dp, * ep ;
+ ulen str_len ;
item = vector_get_item(item_v, i) ;
- str = item->str[0] == '.' ? item->str + 1 : item->str;
+ str = els_body_nn(item->str) ;
+ str_len = els_len_nn(item->str) ;
+ if (*str == '.')
+ {
+ ++str ;
+ --str_len ;
+ } ;
+
dp = item->doc ;
ep = dp + strlen(dp) ;
@@ -2866,9 +2880,9 @@ uty_cli_describe_list(vty_cli cli, vector item_v)
if (np == dp) /* if no space... */
np = dp + doc_width ; /* ...force break */
- uty_cli_describe_line(cli, str_width, str, dp, np - dp) ;
+ uty_cli_describe_line(cli, str_width, str, str_len, dp, np - dp) ;
- str = ""; /* for 2nd and subsequent lines */
+ str_len = 0 ; /* for 2nd and subsequent lines */
dp = np ; /* step past what just wrote */
while (*dp == ' ')
@@ -2876,7 +2890,7 @@ uty_cli_describe_list(vty_cli cli, vector item_v)
} ;
} ;
- uty_cli_describe_line(cli, str_width, str, dp, ep - dp) ;
+ uty_cli_describe_line(cli, str_width, str, str_len, dp, ep - dp) ;
} ;
uty_cli_help_newline(cli) ;
@@ -2887,19 +2901,27 @@ uty_cli_describe_list(vty_cli cli, vector item_v)
*/
static void
uty_cli_describe_line(vty_cli cli, uint str_width, const char* str,
- const char* doc, uint len)
+ ulen str_len, const char* doc, ulen doc_len)
{
- if ((*str == '\0') && (len == 0))
+ if ((str_len == 0) && (doc_len == 0))
return ; /* quit if nothing to say */
uty_cli_help_newline(cli) ;
- if (len == 0)
- uty_cli_out(cli, " %s", str) ; /* left justify */
+ if (str_len > 0)
+ {
+ uty_cli_write(cli, " ", 2) ;
+ uty_cli_write(cli, str, str_len) ;
+ }
else
+ str_width += 2 ;
+
+ if (doc_len > 0)
{
- uty_cli_out(cli, " %-*s ", str_width, str) ;
- uty_cli_write(cli, doc, len) ;
+ ulen pad ;
+ pad = (str_len < str_width) ? str_width - str_len : 0 ;
+ uty_cli_write_n(cli, telnet_spaces, pad + 2) ;
+ uty_cli_write(cli, doc, doc_len) ;
} ;
} ;
diff --git a/lib/vty_io_file.c b/lib/vty_io_file.c
index 4b1a8cfa..b98dd694 100644
--- a/lib/vty_io_file.c
+++ b/lib/vty_io_file.c
@@ -700,7 +700,7 @@ uty_file_read_close(vio_vf vf, bool final)
extern cmd_return_code_t
uty_file_write_close(vio_vf vf, bool final, bool base)
{
- VTY_ASSERT_CLI_THREAD_LOCKED() ;
+ VTY_ASSERT_CAN_CLOSE_VF(vf) ;
assert(vf->vout_state == vf_closing) ;
assert((vf->vout_type == VOUT_FILE) || (vf->vout_type == VOUT_CONFIG)) ;
diff --git a/lib/vty_io_term.c b/lib/vty_io_term.c
index 416968bf..857e457a 100644
--- a/lib/vty_io_term.c
+++ b/lib/vty_io_term.c
@@ -515,7 +515,7 @@ uty_term_ready(vio_vf vf)
vio_lc_counter_reset(vf->cli->olc) ; /* do one tranche */
done = uty_term_write(vf) ;
- signal = done == utw_done ;
+ signal = ((done == utw_done) || (done == utw_stopped)) ;
while (done != utw_error)
{
@@ -538,7 +538,7 @@ uty_term_ready(vio_vf vf)
if (done == done_before)
break ; /* quit if no change in response */
- if (done == utw_done)
+ if ((done == utw_done) || (done == utw_stopped))
signal = true ;
} ;