diff options
-rw-r--r-- | lib/command.c | 3 | ||||
-rw-r--r-- | lib/command.h | 1 | ||||
-rw-r--r-- | lib/command_common.h | 3 | ||||
-rw-r--r-- | lib/command_parse.c | 286 | ||||
-rw-r--r-- | lib/command_parse.h | 38 | ||||
-rw-r--r-- | lib/elstring.c | 51 | ||||
-rw-r--r-- | lib/elstring.h | 10 | ||||
-rw-r--r-- | lib/list_util.c | 62 | ||||
-rw-r--r-- | lib/list_util.h | 213 | ||||
-rw-r--r-- | lib/qstring.h | 6 | ||||
-rw-r--r-- | lib/vty.c | 10 | ||||
-rw-r--r-- | lib/vty_cli.c | 70 | ||||
-rw-r--r-- | lib/vty_io_file.c | 2 | ||||
-rw-r--r-- | lib/vty_io_term.c | 4 |
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)) ; } ; /*------------------------------------------------------------------------------ @@ -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 ; } ; |