/* * Copyright (c) 2015 Cisco and/or its affiliates. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #define PARSE_DEBUG 0 u16 word_type_index, number_type_index, eof_type_index, rule_eof_type_index, plus_type_index, minus_type_index, star_type_index, slash_type_index, lpar_type_index, rpar_type_index; u8 * format_vlib_parse_value (u8 * s, va_list * args) { vlib_parse_main_t *pm = va_arg (*args, vlib_parse_main_t *); vlib_parse_type_t *type; vlib_parse_value_t *v; u16 type_index; s = format (s, "%d items:\n", vec_len (pm->parse_value)); vec_foreach (v, pm->parse_value) { type_index = v->type; type = pool_elt_at_index (pm->parse_types, type_index); if (type->format_value) s = format (s, "[%d]: %U\n", v - pm->parse_value, type->format_value, v); else s = format (s, "[%d]: (nofun)\n", v - pm->parse_value); } return s; } static u8 * format_vlib_parse_match (u8 * s, va_list * args) { vlib_parse_match_t m = va_arg (*args, vlib_parse_match_t); char *t = 0; switch (m) { #define _(a) case VLIB_PARSE_##a: t = #a; break; foreach_parse_match_type #undef _ default: t = 0; break; } if (t) return format (s, "%s", t); else return format (s, "unknown 0x%x", m); } static u8 * format_vlib_parse_item (u8 * s, va_list * args) { vlib_parse_main_t *pm = va_arg (*args, vlib_parse_main_t *); vlib_parse_item_t *item = va_arg (*args, vlib_parse_item_t *); vlib_parse_type_t *type = pool_elt_at_index (pm->parse_types, item->type); if (item->type == word_type_index) s = format (s, "%s", item->value.as_pointer); else s = format (s, "<%s>", type->name); return s; } static u8 * format_vlib_parse_graph (u8 * s, va_list * args) { vlib_parse_main_t *pm = va_arg (*args, vlib_parse_main_t *); vlib_parse_graph_t *node = va_arg (*args, vlib_parse_graph_t *); vlib_parse_item_t *item; vlib_parse_type_t *type; /* $$$ hash table */ /* *INDENT-OFF* */ pool_foreach (type, pm->parse_types, ({ if (type->rule_index == node - pm->parse_graph) s = format (s, "\n<%s>\n", type->name); })); /* *INDENT-ON* */ if (pm->root_index == (node - pm->parse_graph)) s = format (s, "\n\n"); item = pool_elt_at_index (pm->parse_items, node->item); s = format (s, "[%d] %U ", node - pm->parse_graph, format_vlib_parse_item, pm, item); if (node->peer == (u32) ~ 0) s = format (s, "peer nil "); else s = format (s, "peer %4u ", node->peer); if (node->deeper == (u32) ~ 0) s = format (s, "deeper nil "); else s = format (s, "deeper %4u ", node->deeper); return s; } void dump_parse_graph (void) { vlib_parse_main_t *pm = &vlib_parse_main; vlib_parse_graph_t *node; /* *INDENT-OFF* */ pool_foreach (node, pm->parse_graph, ({ fformat(stdout, "%U\n", format_vlib_parse_graph, pm, node); })); /* *INDENT-ON* */ } always_inline void parse_cleanup_value (vlib_parse_main_t * pm, vlib_parse_value_t * pv) { vlib_parse_type_t *type = pool_elt_at_index (pm->parse_types, pv->type); if (type->value_cleanup_function) type->value_cleanup_function (pv); } static void parse_reset (vlib_parse_main_t * pm, u8 * input) { vlib_lex_token_t *t; vlib_parse_value_t *pv; vlib_lex_reset (pm->lex_main, input); vec_foreach (t, pm->tokens) vlib_lex_cleanup_token (t); vec_foreach (pv, pm->parse_value) parse_cleanup_value (pm, pv); _vec_len (pm->parse_value) = 0; _vec_len (pm->tokens) = 0; pm->current_token_index = 0; } static void parse_help (vlib_parse_main_t * pm, u32 index) { vlib_parse_graph_t *node; vlib_parse_item_t *item; vlib_parse_type_t *type; vlib_main_t *vm = pm->vlib_main; u8 *help_input; int i; help_input = vec_dup (pm->lex_main->input_vector); for (i = vec_len (help_input) - 1; i >= 0; i--) if (help_input[i] == '?') { help_input[i] = 0; _vec_len (help_input) = i; break; } for (i = vec_len (help_input) - 1; i >= 0; i--) { if (help_input[i] != ' ' && help_input[i] != '\t') break; help_input[i] = 0; break; } _vec_len (help_input) = i + 1; while (index != (u32) ~ 0) { node = pool_elt_at_index (pm->parse_graph, index); item = pool_elt_at_index (pm->parse_items, node->item); type = pool_elt_at_index (pm->parse_types, item->type); if (item->type == eof_type_index && vec_len (pm->match_items) == 0) /* do nothing */ ; else if (item->type == word_type_index) vlib_cli_output (vm, "%s %s\n", help_input, item->value.as_pointer); else vlib_cli_output (vm, "%s <%s>\n", help_input, type->name); index = node->peer; } vec_free (help_input); } static vlib_parse_match_t parse_eval_internal (vlib_parse_main_t * pm, u32 index) { vlib_parse_graph_t *node; vlib_parse_item_t *item; vlib_parse_type_t *type; vlib_parse_value_t value, *pv; vlib_parse_match_t rv; u32 *partial_matches = 0; vlib_lex_token_t *t; u32 save_token_index = (u32) ~ 0, save_match_items = 0; int had_value = 0; if (pm->current_token_index >= vec_len (pm->tokens)) return VLIB_PARSE_MATCH_FAIL; /* current token */ t = vec_elt_at_index (pm->tokens, pm->current_token_index); /* Help ? */ if (PREDICT_FALSE (t->token == VLIB_LEX_qmark)) { parse_help (pm, index); _vec_len (pm->match_items) = 0; return VLIB_PARSE_MATCH_DONE; } /* Across all peers at this level of the parse graph */ while (index != (u32) ~ 0) { node = pool_elt_at_index (pm->parse_graph, index); item = pool_elt_at_index (pm->parse_items, node->item); type = pool_elt_at_index (pm->parse_types, item->type); /* * Save the token index. We may have to back up several * trie plies. Type-specific match functions can consume * multiple tokens, and they may not be optimally careful */ save_token_index = pm->current_token_index; save_match_items = vec_len (pm->match_items); vec_add1 (pm->match_items, node->item); if (PARSE_DEBUG > 1) clib_warning ("Try to match token %U against node %d", format_vlib_lex_token, pm->lex_main, t, index); /* Call the type-specific match function */ rv = type->match_function (pm, type, t, &value); if (PARSE_DEBUG > 1) clib_warning ("returned %U", format_vlib_parse_match, rv); switch (rv) { case VLIB_PARSE_MATCH_VALUE: /* * Matched, and returned a value to append to the * set of args passed to the action function */ value.type = item->type; vec_add1 (pm->parse_value, value); had_value = 1; /* fallthrough */ case VLIB_PARSE_MATCH_FULL: unambiguous_partial_match: /* Consume the matched token */ pm->current_token_index++; /* continue matching along this path */ rv = parse_eval_internal (pm, node->deeper); /* this is not the right path */ if (rv == VLIB_PARSE_MATCH_FAIL) { if (had_value) { /* Delete the value */ value = pm->parse_value[vec_len (pm->parse_value) - 1]; parse_cleanup_value (pm, &value); _vec_len (pm->parse_value) -= 1; } /* Continue with the next sibling */ pm->current_token_index = save_token_index; _vec_len (pm->match_items) = save_match_items; index = node->peer; break; } return rv; case VLIB_PARSE_MATCH_PARTIAL: /* Partial (substring) match, remember it but keep going */ vec_add1 (partial_matches, node - pm->parse_graph); index = node->peer; break; case VLIB_PARSE_MATCH_FAIL: /* Continue with the next sibling */ index = node->peer; _vec_len (pm->match_items) = save_match_items; break; case VLIB_PARSE_MATCH_DONE: /* Parse complete, invoke the action function */ if (PARSE_DEBUG > 0) clib_warning ("parse_value: %U", format_vlib_parse_value, pm); { vlib_parse_eval_function_t *f = item->value.as_pointer; if (f) rv = f (pm, item, pm->parse_value); } vec_foreach (pv, pm->parse_value) parse_cleanup_value (pm, pv); _vec_len (pm->parse_value) = 0; _vec_len (pm->match_items) = 0; return rv; case VLIB_PARSE_MATCH_AMBIGUOUS: case VLIB_PARSE_MATCH_EVAL_FAIL: case VLIB_PARSE_MATCH_RULE: _vec_len (pm->match_items) = save_match_items; return rv; } } /* * Out of siblings. If we have exactly one partial match * we win */ if (vec_len (partial_matches) == 1) { index = partial_matches[0]; node = pool_elt_at_index (pm->parse_graph, index); vec_free (partial_matches); goto unambiguous_partial_match; } /* Ordinary loser */ rv = VLIB_PARSE_MATCH_FAIL; /* Ambiguous loser */ if (vec_len (partial_matches) > 1) { vec_free (partial_matches); rv = VLIB_PARSE_MATCH_AMBIGUOUS; } _vec_len (pm->match_items) = save_match_items; return rv; } vlib_parse_match_t rule_match (vlib_parse_main_t * pm, vlib_parse_type_t * type, vlib_lex_token_t * t, vlib_parse_value_t * valuep) { vlib_parse_match_t rv; static int recursion_level; if (PARSE_DEBUG > 1) clib_warning ("[%d]: try to match type %s graph index %d", recursion_level, type->name, type->rule_index); recursion_level++; rv = parse_eval_internal (pm, type->rule_index); recursion_level--; /* Break the recusive unwind here... */ if (rv == VLIB_PARSE_MATCH_RULE) { if (PARSE_DEBUG > 1) clib_warning ("[%d]: type %s matched", recursion_level, type->name); return VLIB_PARSE_MATCH_FULL; } else { if (PARSE_DEBUG > 1) clib_warning ("[%d]: type %s returns %U", recursion_level, type->name, format_vlib_parse_match, rv); } return rv; } static int parse_eval (vlib_parse_main_t * pm, u8 * input) { vlib_lex_token_t *t; parse_reset (pm, input); /* Tokenize the entire input vector */ do { vec_add2 (pm->tokens, t, 1); vlib_lex_get_token (pm->lex_main, t); } while (t->token != VLIB_LEX_eof); /* Feed it to the parser */ return parse_eval_internal (pm, pm->root_index); } /* Temporary vlib stub */ vlib_parse_match_t vlib_parse_eval (u8 * input) { return parse_eval (&vlib_parse_main, input); } u16 parse_type_find_or_create (vlib_parse_main_t * pm, vlib_parse_type_t * t) { uword *p; vlib_parse_type_t *n; u8 *name_copy; p = hash_get_mem (pm->parse_type_by_name_hash, t->name); if (p) return p[0]; pool_get (pm->parse_types, n); *n = *t; n->rule_index = (u32) ~ 0; name_copy = format (0, "%s%c", n->name, 0); hash_set_mem (pm->parse_type_by_name_hash, name_copy, n - pm->parse_types); return n - pm->parse_types; } u16 parse_type_find_by_name (vlib_parse_main_t * pm, char *name) { uword *p; p = hash_get_mem (pm->parse_type_by_name_hash, name); if (p) return p[0]; return (u16) ~ 0; } u32 parse_item_find_or_create (vlib_parse_main_t * pm, vlib_parse_item_t * item) { uword *p; vlib_parse_item_t *i; /* Exact match the entire item */ p = mhash_get (&pm->parse_item_hash, item); if (p) return p[0]; pool_get (pm->parse_items, i); *i = *item; mhash_set (&pm->parse_item_hash, i, i - pm->parse_items, 0); return i - pm->parse_items; } static void parse_type_and_graph_init (vlib_parse_main_t * pm) { u32 eof_index; vlib_parse_type_t type; vlib_parse_item_t item; memset (&type, 0, sizeof (type)); #define foreach_token_type \ _ (eof) \ _ (rule_eof) \ _ (word) \ _ (number) \ _ (plus) \ _ (minus) \ _ (star) \ _ (slash) \ _ (lpar) \ _ (rpar) #define _(a) a##_type_index = parse_type_find_by_name (pm, #a); foreach_token_type #undef _ memset (&item, 0, sizeof (item)); item.type = eof_type_index; eof_index = parse_item_find_or_create (pm, &item); pm->root_index = (u32) ~ 0; #if 0 pool_get (pm->parse_graph, g); memset (g, 0xff, sizeof (*g)); g->item = eof_index; pm->root_index = 0; #endif } static void tokenize (vlib_parse_main_t * pm, parse_registration_t * pr) { vlib_lex_token_t *t; pm->register_input = format (pm->register_input, "%s%c", pr->initializer, 0); parse_reset (pm, pm->register_input); do { vec_add2 (pm->tokens, t, 1); vlib_lex_get_token (pm->lex_main, t); } while (t->token != VLIB_LEX_eof); _vec_len (pm->register_input) = 0; } static int is_typed_rule (vlib_parse_main_t * pm) { vlib_lex_token_t *t = vec_elt_at_index (pm->tokens, 0); /* = blah blah blah */ if (vec_len (pm->tokens) >= 4 && t[0].token == VLIB_LEX_lt && t[1].token == VLIB_LEX_word && t[2].token == VLIB_LEX_gt && t[3].token == VLIB_LEX_equals) return 1; return 0; } static int token_matches_graph_node (vlib_parse_main_t * pm, vlib_lex_token_t * t, vlib_parse_graph_t * node, vlib_parse_item_t * item, vlib_parse_type_t * type, u32 * token_increment) { /* EOFs don't match */ if (t->token == VLIB_LEX_eof) return 0; /* New chain element is a word */ if (t->token == VLIB_LEX_word) { /* but the item in hand is not a word */ if (item->type != word_type_index) return 0; /* Or it's not this particular word */ if (strcmp (t->value.as_pointer, item->value.as_pointer)) return 0; *token_increment = 1; return 1; } /* New chain element is a type-name: < TYPE-NAME > */ if (t->token == VLIB_LEX_lt) { u16 token_type_index; /* < TYPE > */ if (t[1].token != VLIB_LEX_word || t[2].token != VLIB_LEX_gt) { clib_warning (0, "broken type name in '%s'", pm->register_input); return 0; } token_type_index = parse_type_find_by_name (pm, t[1].value.as_pointer); if (token_type_index == (u16) ~ 0) { clib_warning (0, "unknown type '%s'", t[1].value.as_pointer); return 0; } /* Its a known type but does not match. */ if (item->type != token_type_index) return 0; *token_increment = 3; return 1; } clib_warning ("BUG: t->token = %d", t->token); return 0; } u32 generate_subgraph_from_tokens (vlib_parse_main_t * pm, vlib_lex_token_t * t, u32 * new_subgraph_depth, parse_registration_t * pr, int not_a_rule) { vlib_parse_graph_t *g, *last_g; vlib_parse_item_t new_item; u32 rv = (u32) ~ 0, new_item_index, last_index = (u32) ~ 0; u16 token_type_index; u32 depth = 0; while (t < pm->tokens + vec_len (pm->tokens)) { memset (&new_item, 0, sizeof (new_item)); if (t->token == VLIB_LEX_word) { new_item.type = word_type_index; new_item.value.as_pointer = vec_dup ((u8 *) t->value.as_pointer); new_item_index = parse_item_find_or_create (pm, &new_item); t++; } else if (t->token == VLIB_LEX_lt) { if (t[1].token != VLIB_LEX_word || t[2].token != VLIB_LEX_gt) { clib_warning ("broken type name in '%s'", pm->register_input); goto screwed; } token_type_index = parse_type_find_by_name (pm, t[1].value.as_pointer); if (token_type_index == (u16) ~ 0) { clib_warning ("unknown type 2 '%s'", t[1].value.as_pointer); goto screwed; } new_item.type = token_type_index; new_item.value.as_pointer = 0; new_item_index = parse_item_find_or_create (pm, &new_item); t += 3; /* skip < and > */ } else if (t->token == VLIB_LEX_eof) { screwed: new_item.type = not_a_rule ? eof_type_index : rule_eof_type_index; new_item.value.as_pointer = pr->eof_match; new_item_index = parse_item_find_or_create (pm, &new_item); t++; } else { clib_warning ("unexpected token %U index %d in '%s'", format_vlib_lex_token, pm->lex_main, t, t - pm->tokens, pm->register_input); goto screwed; } pool_get (pm->parse_graph, g); memset (g, 0xff, sizeof (*g)); g->item = new_item_index; depth++; if (rv == (u32) ~ 0) { rv = g - pm->parse_graph; last_index = rv; } else { last_g = pool_elt_at_index (pm->parse_graph, last_index); last_index = last_g->deeper = g - pm->parse_graph; } } *new_subgraph_depth = depth; return rv; } static u32 measure_depth (vlib_parse_main_t * pm, u32 index) { vlib_parse_graph_t *node; vlib_parse_item_t *item; u32 max = 0; u32 depth; if (index == (u32) ~ 0) return 0; node = pool_elt_at_index (pm->parse_graph, index); item = pool_elt_at_index (pm->parse_items, node->item); if (item->type == eof_type_index) return 1; while (index != (u32) ~ 0) { node = pool_elt_at_index (pm->parse_graph, index); depth = measure_depth (pm, node->deeper); if (max < depth) max = depth; index = node->peer; } return max + 1; } static void add_subgraph_to_graph (vlib_parse_main_t * pm, u32 last_matching_index, u32 graph_root_index, u32 new_subgraph_index, u32 new_subgraph_depth) { vlib_parse_graph_t *parent_node; int new_subgraph_longest = 1; u32 current_peer_index; u32 current_depth; vlib_parse_graph_t *current_peer = 0; vlib_parse_graph_t *new_subgraph_node = pool_elt_at_index (pm->parse_graph, new_subgraph_index); /* * Case 1: top-level peer. Splice into the top-level * peer chain according to rule depth */ if (last_matching_index == (u32) ~ 0) { u32 index = graph_root_index; while (1) { current_peer = pool_elt_at_index (pm->parse_graph, index); current_depth = measure_depth (pm, index); if (current_depth < new_subgraph_depth || current_peer->peer == (u32) ~ 0) break; index = current_peer->peer; } new_subgraph_node->peer = current_peer->peer; current_peer->peer = new_subgraph_index; return; } parent_node = pool_elt_at_index (pm->parse_graph, last_matching_index); current_peer_index = parent_node->deeper; while (current_peer_index != (u32) ~ 0) { current_peer = pool_elt_at_index (pm->parse_graph, current_peer_index); current_depth = measure_depth (pm, current_peer_index); if (current_depth < new_subgraph_depth) break; new_subgraph_longest = 0; current_peer_index = current_peer->peer; } ASSERT (current_peer); if (new_subgraph_longest) { new_subgraph_node->peer = parent_node->deeper; parent_node->deeper = new_subgraph_index; } else { new_subgraph_node->peer = current_peer->peer; current_peer->peer = new_subgraph_index; } } static clib_error_t * parse_register_one (vlib_parse_main_t * pm, parse_registration_t * pr) { u32 graph_root_index; u16 subgraph_type_index = (u16) ~ 0; vlib_parse_type_t *subgraph_type = 0; vlib_lex_token_t *t; vlib_parse_graph_t *node; u32 node_index, last_index, token_increment, new_subgraph_index; u32 new_subgraph_depth, last_matching_index; vlib_parse_item_t *item; vlib_parse_type_t *type; int use_main_graph = 1; tokenize (pm, pr); /* A typed rule? */ if (is_typed_rule (pm)) { /* Get the type and its current subgraph root, if any */ t = vec_elt_at_index (pm->tokens, 1); subgraph_type_index = parse_type_find_by_name (pm, t->value.as_pointer); if (subgraph_type_index == (u16) ~ 0) return clib_error_return (0, "undeclared type '%s'", t->value.as_pointer); subgraph_type = pool_elt_at_index (pm->parse_types, subgraph_type_index); graph_root_index = subgraph_type->rule_index; /* Skip "mytype> = */ t += 3; use_main_graph = 0; } else { /* top-level graph */ graph_root_index = pm->root_index; t = vec_elt_at_index (pm->tokens, 0); } last_matching_index = (u32) ~ 0; last_index = node_index = graph_root_index; /* Find the first token which isn't already being parsed */ while (t < pm->tokens + vec_len (pm->tokens) && node_index != (u32) ~ 0) { node = pool_elt_at_index (pm->parse_graph, node_index); item = pool_elt_at_index (pm->parse_items, node->item); type = pool_elt_at_index (pm->parse_types, item->type); last_index = node_index; if (token_matches_graph_node (pm, t, node, item, type, &token_increment)) { t += token_increment; last_matching_index = node_index; node_index = node->deeper; } else node_index = node->peer; } new_subgraph_index = generate_subgraph_from_tokens (pm, t, &new_subgraph_depth, pr, use_main_graph); /* trivial cases: first graph node or first type rule */ if (graph_root_index == (u32) ~ 0) { if (use_main_graph) pm->root_index = new_subgraph_index; else subgraph_type->rule_index = new_subgraph_index; return 0; } add_subgraph_to_graph (pm, last_matching_index, graph_root_index, new_subgraph_index, new_subgraph_depth); return 0; } static clib_error_t * parse_register (vlib_main_t * vm, parse_registration_t * lo, parse_registration_t * hi, vlib_parse_main_t * pm) { parse_registration_t *pr; for (pr = lo; pr < hi; pr = vlib_elf_section_data_next (pr, 0)) vec_add1 (pm->parse_registrations, pr); return 0; } static clib_error_t * parse_register_one_type (vlib_parse_main_t * pm, vlib_parse_type_t * rp) { (void) parse_type_find_or_create (pm, (vlib_parse_type_t *) rp); return 0; } static clib_error_t * parse_type_register (vlib_main_t * vm, vlib_parse_type_t * lo, vlib_parse_type_t * hi, vlib_parse_main_t * pm) { clib_error_t *error = 0; vlib_parse_type_t *ptr; for (ptr = lo; ptr < hi; ptr = vlib_elf_section_data_next (ptr, 0)) { error = parse_register_one_type (pm, ptr); if (error) goto done; } done: return error; } clib_error_t *vlib_stdlex_init (vlib_main_t * vm) __attribute__ ((weak)); clib_error_t * vlib_stdlex_init (vlib_main_t * vm) { (void) vlib_lex_add_table ("ignore_everything"); return 0; } static int compute_rule_length (parse_registration_t * r) { int length, i; vlib_parse_main_t *pm = &vlib_parse_main; if (r->rule_length) return r->rule_length; length = 0; tokenize (pm, r); length = vec_len (pm->tokens); /* Account for " = " in " = bar" etc. */ if (is_typed_rule (pm)) length -= 2; for (i = 0; i < vec_len (pm->tokens); i++) { switch (pm->tokens[i].token) { case VLIB_LEX_lt: case VLIB_LEX_gt: length -= 1; default: break; } } ASSERT (length > 0); r->rule_length = length; return length; } static int rule_length_compare (parse_registration_t * r1, parse_registration_t * r2) { compute_rule_length (r1); compute_rule_length (r2); /* Descending sort */ return r2->rule_length - r1->rule_length; } static clib_error_t * parse_init (vlib_main_t * vm) { vlib_parse_main_t *pm = &vlib_parse_main; vlib_lex_main_t *lm = &vlib_lex_main; vlib_elf_section_bounds_t *b, *bounds; clib_error_t *error = 0; parse_registration_t *rule; int i; if ((error = vlib_call_init_function (vm, lex_onetime_init))) return error; if ((error = vlib_stdlex_init (vm))) return error; if ((error = vlib_call_init_function (vm, parse_builtin_init))) return error; pm->vlib_main = vm; pm->lex_main = lm; mhash_init (&pm->parse_item_hash, sizeof (u32), sizeof (vlib_parse_item_t)); pm->parse_type_by_name_hash = hash_create_string (0, sizeof (u32)); vec_validate (pm->parse_value, 16); vec_validate (pm->tokens, 16); vec_validate (pm->register_input, 32); vec_validate (pm->match_items, 16); _vec_len (pm->parse_value) = 0; _vec_len (pm->tokens) = 0; _vec_len (pm->register_input) = 0; _vec_len (pm->match_items) = 0; bounds = vlib_get_elf_section_bounds (vm, "parse_type_registrations"); vec_foreach (b, bounds) { error = parse_type_register (vm, b->lo, b->hi, pm); if (error) break; } vec_free (bounds); parse_type_and_graph_init (pm); bounds = vlib_get_elf_section_bounds (vm, "parse_registrations"); vec_foreach (b, bounds) { error = parse_register (vm, b->lo, b->hi, pm); if (error) break; } vec_free (bounds); vec_sort_with_function (pm->parse_registrations, rule_length_compare); for (i = 0; i < vec_len (pm->parse_registrations); i++) { rule = pm->parse_registrations[i]; parse_register_one (pm, rule); } return error; } VLIB_INIT_FUNCTION (parse_init); /* * fd.io coding-style-patch-verification: ON * * Local Variables: * eval: (c-set-style "gnu") * End: */