back to topotato report
topotato coverage report
Current view: top level - lib - command_graph.c (source / functions) Hit Total Coverage
Test: test_ospf6_vlink.py::VirtualLinkBasic Lines: 212 295 71.9 %
Date: 2023-02-16 02:06:43 Functions: 26 30 86.7 %

          Line data    Source code
       1             : /*
       2             :  * CLI graph handling
       3             :  *
       4             :  * --
       5             :  * Copyright (C) 2016 Cumulus Networks, Inc.
       6             :  * Copyright (C) 1997, 98, 99 Kunihiro Ishiguro
       7             :  * Copyright (C) 2013 by Open Source Routing.
       8             :  * Copyright (C) 2013 by Internet Systems Consortium, Inc. ("ISC")
       9             :  *
      10             :  * This program is free software; you can redistribute it and/or modify it
      11             :  * under the terms of the GNU General Public License as published by the Free
      12             :  * Software Foundation; either version 2 of the License, or (at your option)
      13             :  * any later version.
      14             :  *
      15             :  * This program is distributed in the hope that it will be useful, but WITHOUT
      16             :  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
      17             :  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
      18             :  * more details.
      19             :  *
      20             :  * You should have received a copy of the GNU General Public License along
      21             :  * with this program; see the file COPYING; if not, write to the Free Software
      22             :  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
      23             :  */
      24             : 
      25             : #include <zebra.h>
      26             : 
      27             : #include "command_graph.h"
      28             : 
      29          48 : DEFINE_MTYPE_STATIC(LIB, CMD_TOKENS, "Command Tokens");
      30          48 : DEFINE_MTYPE_STATIC(LIB, CMD_DESC, "Command Token Text");
      31          48 : DEFINE_MTYPE_STATIC(LIB, CMD_TEXT, "Command Token Help");
      32          48 : DEFINE_MTYPE(LIB, CMD_ARG, "Command Argument");
      33          48 : DEFINE_MTYPE_STATIC(LIB, CMD_VAR, "Command Argument Name");
      34             : 
      35      111454 : struct cmd_token *cmd_token_new(enum cmd_token_type type, uint8_t attr,
      36             :                                 const char *text, const char *desc)
      37             : {
      38      111454 :         struct cmd_token *token =
      39      111454 :                 XCALLOC(MTYPE_CMD_TOKENS, sizeof(struct cmd_token));
      40      111454 :         token->type = type;
      41      111454 :         token->attr = attr;
      42      111454 :         token->text = text ? XSTRDUP(MTYPE_CMD_TEXT, text) : NULL;
      43      111454 :         token->desc = desc ? XSTRDUP(MTYPE_CMD_DESC, desc) : NULL;
      44      111454 :         token->refcnt = 1;
      45      111454 :         token->arg = NULL;
      46      111454 :         token->allowrepeat = false;
      47      111454 :         token->varname = NULL;
      48             : 
      49      111454 :         return token;
      50             : }
      51             : 
      52      111454 : void cmd_token_del(struct cmd_token *token)
      53             : {
      54      111454 :         if (!token)
      55             :                 return;
      56             : 
      57      111454 :         XFREE(MTYPE_CMD_TEXT, token->text);
      58      111454 :         XFREE(MTYPE_CMD_DESC, token->desc);
      59      111454 :         XFREE(MTYPE_CMD_ARG, token->arg);
      60      111454 :         XFREE(MTYPE_CMD_VAR, token->varname);
      61             : 
      62      111454 :         XFREE(MTYPE_CMD_TOKENS, token);
      63             : }
      64             : 
      65        1750 : struct cmd_token *cmd_token_dup(struct cmd_token *token)
      66             : {
      67        1750 :         struct cmd_token *copy =
      68        1750 :                 cmd_token_new(token->type, token->attr, NULL, NULL);
      69        1750 :         copy->max = token->max;
      70        1750 :         copy->min = token->min;
      71        1750 :         copy->text = token->text ? XSTRDUP(MTYPE_CMD_TEXT, token->text) : NULL;
      72        1750 :         copy->desc = token->desc ? XSTRDUP(MTYPE_CMD_DESC, token->desc) : NULL;
      73        1750 :         copy->arg = token->arg ? XSTRDUP(MTYPE_CMD_ARG, token->arg) : NULL;
      74        3500 :         copy->varname =
      75        1750 :                 token->varname ? XSTRDUP(MTYPE_CMD_VAR, token->varname) : NULL;
      76             : 
      77        1750 :         return copy;
      78             : }
      79             : 
      80       20704 : static void cmd_token_varname_do(struct cmd_token *token, const char *varname,
      81             :                                  uint8_t varname_src)
      82             : {
      83       20704 :         if (token->varname_src >= varname_src)
      84             :                 return;
      85             : 
      86       17568 :         XFREE(MTYPE_CMD_VAR, token->varname);
      87             : 
      88       17568 :         size_t len = strlen(varname), i;
      89       17568 :         token->varname = XMALLOC(MTYPE_CMD_VAR, len + 1);
      90       17568 :         token->varname_src = varname_src;
      91             : 
      92      121752 :         for (i = 0; i < len; i++)
      93      104184 :                 switch (varname[i]) {
      94        1808 :                 case '-':
      95             :                 case '+':
      96             :                 case '*':
      97             :                 case ':':
      98        1808 :                         token->varname[i] = '_';
      99        1808 :                         break;
     100             :                 default:
     101      102376 :                         token->varname[i] = tolower((unsigned char)varname[i]);
     102             :                 }
     103       17568 :         token->varname[len] = '\0';
     104             : }
     105             : 
     106       61240 : void cmd_token_varname_set(struct cmd_token *token, const char *varname)
     107             : {
     108       61240 :         if (varname) {
     109        5512 :                 cmd_token_varname_do(token, varname, VARNAME_EXPLICIT);
     110        5512 :                 return;
     111             :         }
     112       55728 :         if (token->type == VARIABLE_TKN) {
     113        3688 :                 if (strcmp(token->text, "WORD") && strcmp(token->text, "NAME"))
     114        2384 :                         cmd_token_varname_do(token, token->text, VARNAME_TEXT);
     115             :         }
     116             : }
     117             : 
     118        7064 : static void cmd_token_varname_fork(struct graph_node *node,
     119             :                                    struct cmd_token *prevtoken)
     120             : {
     121       27200 :         for (size_t i = 0; i < vector_active(node->to); i++) {
     122       20136 :                 struct graph_node *next = vector_slot(node->to, i);
     123       20136 :                 struct cmd_token *nexttoken = next->data;
     124             : 
     125       20136 :                 if (nexttoken->type == FORK_TKN) {
     126         952 :                         cmd_token_varname_fork(next, prevtoken);
     127         952 :                         continue;
     128             :                 }
     129       19184 :                 if (nexttoken->varname)
     130        5408 :                         continue;
     131       13776 :                 if (!IS_VARYING_TOKEN(nexttoken->type))
     132       11864 :                         continue;
     133             : 
     134        1912 :                 cmd_token_varname_do(nexttoken, prevtoken->text, VARNAME_TEXT);
     135             :         }
     136        7064 : }
     137             : 
     138       12128 : void cmd_token_varname_join(struct graph_node *join, const char *varname)
     139             : {
     140       12128 :         if (!varname)
     141             :                 return;
     142             : 
     143        5576 :         for (size_t i = 0; i < vector_active(join->from); i++) {
     144        4800 :                 struct graph_node *prev = vector_slot(join->from, i);
     145        4800 :                 struct cmd_token *token = prev->data;
     146             : 
     147        4800 :                 if (token->type == JOIN_TKN)
     148           0 :                         cmd_token_varname_join(prev, varname);
     149        4800 :                 else if (token->type < SPECIAL_TKN)
     150        4768 :                         cmd_token_varname_do(token, varname, VARNAME_EXPLICIT);
     151             :         }
     152             : }
     153             : 
     154       49208 : void cmd_token_varname_seqappend(struct graph_node *node)
     155             : {
     156       49208 :         struct graph_node *prevnode = node;
     157       49208 :         struct cmd_token *token = node->data;
     158       49208 :         struct cmd_token *prevtoken;
     159             : 
     160       49208 :         if (token->type == WORD_TKN)
     161             :                 return;
     162             : 
     163       17288 :         do {
     164       17288 :                 if (vector_active(prevnode->from) != 1)
     165             :                         return;
     166             : 
     167       17280 :                 prevnode = vector_slot(prevnode->from, 0);
     168       17280 :                 prevtoken = prevnode->data;
     169       17280 :         } while (prevtoken->type == FORK_TKN);
     170             : 
     171       17280 :         if (prevtoken->type != WORD_TKN)
     172             :                 return;
     173             : 
     174       11376 :         if (token->type == FORK_TKN)
     175        6112 :                 cmd_token_varname_fork(node, prevtoken);
     176             :         else
     177        5264 :                 cmd_token_varname_do(token, prevtoken->text, VARNAME_TEXT);
     178             : }
     179             : 
     180       79040 : static bool cmd_nodes_link(struct graph_node *from, struct graph_node *to)
     181             : {
     182      284968 :         for (size_t i = 0; i < vector_active(from->to); i++)
     183      209192 :                 if (vector_slot(from->to, i) == to)
     184             :                         return true;
     185             :         return false;
     186             : }
     187             : 
     188             : static bool cmd_nodes_equal(struct graph_node *ga, struct graph_node *gb);
     189             : 
     190             : /* returns a single node to be excluded as "next" from iteration
     191             :  * - for JOIN_TKN, never continue back to the FORK_TKN
     192             :  * - in all other cases, don't try the node itself (in case of "...")
     193             :  */
     194      924672 : static inline struct graph_node *cmd_loopstop(struct graph_node *gn)
     195             : {
     196      924672 :         struct cmd_token *tok = gn->data;
     197      924672 :         if (tok->type == JOIN_TKN)
     198      334256 :                 return tok->forkjoin;
     199             :         else
     200             :                 return gn;
     201             : }
     202             : 
     203        9608 : static bool cmd_subgraph_equal(struct graph_node *ga, struct graph_node *gb,
     204             :                                struct graph_node *a_join)
     205             : {
     206        9608 :         size_t i, j;
     207        9608 :         struct graph_node *a_fork, *b_fork;
     208        9608 :         a_fork = cmd_loopstop(ga);
     209        9608 :         b_fork = cmd_loopstop(gb);
     210             : 
     211        9608 :         if (vector_active(ga->to) != vector_active(gb->to))
     212             :                 return false;
     213       22040 :         for (i = 0; i < vector_active(ga->to); i++) {
     214       13152 :                 struct graph_node *cga = vector_slot(ga->to, i);
     215             : 
     216       14176 :                 for (j = 0; j < vector_active(gb->to); j++) {
     217       13488 :                         struct graph_node *cgb = vector_slot(gb->to, i);
     218             : 
     219       13488 :                         if (cga == a_fork && cgb != b_fork)
     220           0 :                                 continue;
     221       13488 :                         if (cga == a_fork && cgb == b_fork)
     222             :                                 break;
     223             : 
     224       13488 :                         if (cmd_nodes_equal(cga, cgb)) {
     225       12848 :                                 if (cga == a_join)
     226             :                                         break;
     227        7024 :                                 if (cmd_subgraph_equal(cga, cgb, a_join))
     228             :                                         break;
     229             :                         }
     230             :                 }
     231       13152 :                 if (j == vector_active(gb->to))
     232             :                         return false;
     233             :         }
     234             :         return true;
     235             : }
     236             : 
     237             : /* deep compare -- for FORK_TKN, the entire subgraph is compared.
     238             :  * this is what's needed since we're not currently trying to partially
     239             :  * merge subgraphs */
     240      235184 : static bool cmd_nodes_equal(struct graph_node *ga, struct graph_node *gb)
     241             : {
     242      235184 :         struct cmd_token *a = ga->data, *b = gb->data;
     243             : 
     244      235184 :         if (a->type != b->type || a->allowrepeat != b->allowrepeat)
     245             :                 return false;
     246      213608 :         if (a->type < SPECIAL_TKN && strcmp(a->text, b->text))
     247             :                 return false;
     248             :         /* one a ..., the other not. */
     249       33776 :         if (cmd_nodes_link(ga, ga) != cmd_nodes_link(gb, gb))
     250             :                 return false;
     251       33776 :         if (!a->varname != !b->varname)
     252             :                 return false;
     253       33680 :         if (a->varname && strcmp(a->varname, b->varname))
     254             :                 return false;
     255             : 
     256       33648 :         switch (a->type) {
     257         528 :         case RANGE_TKN:
     258         528 :                 return a->min == b->min && a->max == b->max;
     259             : 
     260        2872 :         case FORK_TKN:
     261             :                 /* one is keywords, the other just option or selector ... */
     262        5744 :                 if (cmd_nodes_link(a->forkjoin, ga)
     263        2872 :                     != cmd_nodes_link(b->forkjoin, gb))
     264             :                         return false;
     265        5744 :                 if (cmd_nodes_link(ga, a->forkjoin)
     266        2872 :                     != cmd_nodes_link(gb, b->forkjoin))
     267             :                         return false;
     268        2584 :                 return cmd_subgraph_equal(ga, gb, a->forkjoin);
     269             : 
     270             :         case VARIABLE_TKN:
     271             :         case IPV4_TKN:
     272             :         case IPV4_PREFIX_TKN:
     273             :         case IPV6_PREFIX_TKN:
     274             :         case IPV6_TKN:
     275             :         case MAC_TKN:
     276             :         case MAC_PREFIX_TKN:
     277             :         case JOIN_TKN:
     278             :         case START_TKN:
     279             :         case END_TKN:
     280             :         case NEG_ONLY_TKN:
     281             :         case WORD_TKN:
     282             :                 return true;
     283             :         }
     284             : 
     285           0 :         assert(!"Reached end of function we should never hit");
     286             : }
     287             : 
     288          48 : static void cmd_fork_bump_attr(struct graph_node *gn, struct graph_node *join,
     289             :                                uint8_t attr)
     290             : {
     291          48 :         size_t i;
     292          48 :         struct cmd_token *tok = gn->data;
     293          48 :         struct graph_node *stop = cmd_loopstop(gn);
     294             : 
     295          48 :         tok->attr = attr;
     296         112 :         for (i = 0; i < vector_active(gn->to); i++) {
     297          64 :                 struct graph_node *next = vector_slot(gn->to, i);
     298          64 :                 if (next == stop || next == join)
     299          32 :                         continue;
     300          32 :                 cmd_fork_bump_attr(next, join, attr);
     301             :         }
     302          48 : }
     303             : 
     304             : /* move an entire subtree from the temporary graph resulting from
     305             :  * parse() into the permanent graph for the command node.
     306             :  *
     307             :  * this touches rather deeply into the graph code unfortunately.
     308             :  */
     309      841264 : static void cmd_reparent_tree(struct graph *fromgraph, struct graph *tograph,
     310             :                               struct graph_node *node)
     311             : {
     312      841264 :         struct graph_node *stop = cmd_loopstop(node);
     313      841264 :         size_t i;
     314             : 
     315    47666512 :         for (i = 0; i < vector_active(fromgraph->nodes); i++)
     316    46907696 :                 if (vector_slot(fromgraph->nodes, i) == node) {
     317             :                         /* agressive iteration punching through subgraphs - may
     318             :                          * hit some
     319             :                          * nodes twice.  reparent only if found on old graph */
     320       82448 :                         vector_unset(fromgraph->nodes, i);
     321       82448 :                         vector_set(tograph->nodes, node);
     322       82448 :                         break;
     323             :                 }
     324             : 
     325     1674520 :         for (i = 0; i < vector_active(node->to); i++) {
     326      833256 :                 struct graph_node *next = vector_slot(node->to, i);
     327      833256 :                 if (next != stop)
     328      829368 :                         cmd_reparent_tree(fromgraph, tograph, next);
     329             :         }
     330      841264 : }
     331             : 
     332           0 : static void cmd_free_recur(struct graph *graph, struct graph_node *node,
     333             :                            struct graph_node *stop)
     334             : {
     335           0 :         struct graph_node *next, *nstop;
     336             : 
     337           0 :         for (size_t i = vector_active(node->to); i; i--) {
     338           0 :                 next = vector_slot(node->to, i - 1);
     339           0 :                 if (next == stop)
     340           0 :                         continue;
     341           0 :                 nstop = cmd_loopstop(next);
     342           0 :                 if (nstop != next)
     343           0 :                         cmd_free_recur(graph, next, nstop);
     344           0 :                 cmd_free_recur(graph, nstop, stop);
     345             :         }
     346           0 :         graph_delete_node(graph, node);
     347           0 : }
     348             : 
     349           0 : static void cmd_free_node(struct graph *graph, struct graph_node *node)
     350             : {
     351           0 :         struct cmd_token *tok = node->data;
     352           0 :         if (tok->type == JOIN_TKN)
     353           0 :                 cmd_free_recur(graph, tok->forkjoin, node);
     354           0 :         graph_delete_node(graph, node);
     355           0 : }
     356             : 
     357             : /* recursive graph merge.  call with
     358             :  *   old ~= new
     359             :  * (which holds true for old == START_TKN, new == START_TKN)
     360             :  */
     361       32072 : static void cmd_merge_nodes(struct graph *oldgraph, struct graph *newgraph,
     362             :                             struct graph_node *old, struct graph_node *new,
     363             :                             int direction)
     364             : {
     365       32072 :         struct cmd_token *tok;
     366       32072 :         struct graph_node *old_skip, *new_skip;
     367       32072 :         old_skip = cmd_loopstop(old);
     368       32072 :         new_skip = cmd_loopstop(new);
     369             : 
     370       32072 :         assert(direction == 1 || direction == -1);
     371             : 
     372       32072 :         tok = old->data;
     373       32072 :         tok->refcnt += direction;
     374             : 
     375       32072 :         size_t j, i;
     376       64144 :         for (j = 0; j < vector_active(new->to); j++) {
     377       32072 :                 struct graph_node *cnew = vector_slot(new->to, j);
     378       32072 :                 if (cnew == new_skip)
     379           0 :                         continue;
     380             : 
     381      233592 :                 for (i = 0; i < vector_active(old->to); i++) {
     382      221696 :                         struct graph_node *cold = vector_slot(old->to, i);
     383      221696 :                         if (cold == old_skip)
     384           0 :                                 continue;
     385             : 
     386      221696 :                         if (cmd_nodes_equal(cold, cnew)) {
     387       20176 :                                 struct cmd_token *told = cold->data,
     388       20176 :                                                  *tnew = cnew->data;
     389             : 
     390       20176 :                                 if (told->type == END_TKN) {
     391           0 :                                         if (direction < 0) {
     392           0 :                                                 graph_delete_node(
     393             :                                                         oldgraph,
     394           0 :                                                         vector_slot(cold->to,
     395             :                                                                     0));
     396           0 :                                                 graph_delete_node(oldgraph,
     397             :                                                                   cold);
     398             :                                         } else
     399             :                                                 /* force no-match handling to
     400             :                                                  * install END_TKN */
     401           0 :                                                 i = vector_active(old->to);
     402             :                                         break;
     403             :                                 }
     404             : 
     405             :                                 /* the entire fork compared as equal, we
     406             :                                  * continue after it. */
     407       20176 :                                 if (told->type == FORK_TKN) {
     408        1688 :                                         if (tnew->attr < told->attr
     409          16 :                                             && direction > 0)
     410          16 :                                                 cmd_fork_bump_attr(
     411             :                                                         cold, told->forkjoin,
     412             :                                                         tnew->attr);
     413             :                                         /* XXX: no reverse bump on uninstall */
     414        1688 :                                         told = (cold = told->forkjoin)->data;
     415        1688 :                                         tnew = (cnew = tnew->forkjoin)->data;
     416             :                                 }
     417       20176 :                                 if (tnew->attr < told->attr)
     418         576 :                                         told->attr = tnew->attr;
     419             : 
     420       20176 :                                 cmd_merge_nodes(oldgraph, newgraph, cold, cnew,
     421             :                                                 direction);
     422       20176 :                                 break;
     423             :                         }
     424             :                 }
     425             :                 /* nothing found => add new to old */
     426       32072 :                 if (i == vector_active(old->to) && direction > 0) {
     427       11896 :                         graph_remove_edge(new, cnew);
     428             : 
     429       11896 :                         cmd_reparent_tree(newgraph, oldgraph, cnew);
     430             : 
     431       11896 :                         graph_add_edge(old, cnew);
     432             :                 }
     433             :         }
     434             : 
     435       32072 :         if (!tok->refcnt)
     436           0 :                 cmd_free_node(oldgraph, old);
     437       32072 : }
     438             : 
     439       11896 : void cmd_graph_merge(struct graph *old, struct graph *new, int direction)
     440             : {
     441       11896 :         assert(vector_active(old->nodes) >= 1);
     442       11896 :         assert(vector_active(new->nodes) >= 1);
     443             : 
     444       11896 :         cmd_merge_nodes(old, new, vector_slot(old->nodes, 0),
     445       11896 :                         vector_slot(new->nodes, 0), direction);
     446       11896 : }
     447             : 
     448       11896 : void cmd_graph_names(struct graph *graph)
     449             : {
     450       11896 :         struct graph_node *start;
     451             : 
     452       11896 :         assert(vector_active(graph->nodes) >= 1);
     453       11896 :         start = vector_slot(graph->nodes, 0);
     454             : 
     455             :         /* apply varname on initial "[no]" */
     456       23792 :         do {
     457       11896 :                 if (vector_active(start->to) != 1)
     458             :                         break;
     459             : 
     460       11896 :                 struct graph_node *first = vector_slot(start->to, 0);
     461       11896 :                 struct cmd_token *tok = first->data;
     462             :                 /* looking for an option with 2 choices, nothing or "no" */
     463       11896 :                 if (tok->type != FORK_TKN || vector_active(first->to) != 2)
     464             :                         break;
     465             : 
     466         864 :                 struct graph_node *next0 = vector_slot(first->to, 0);
     467         864 :                 struct graph_node *next1 = vector_slot(first->to, 1);
     468             :                 /* one needs to be empty */
     469         864 :                 if (next0 != tok->forkjoin && next1 != tok->forkjoin)
     470             :                         break;
     471             : 
     472         864 :                 struct cmd_token *tok0 = next0->data;
     473         864 :                 struct cmd_token *tok1 = next1->data;
     474             :                 /* the other one needs to be "no" (only one will match here) */
     475         864 :                 if ((tok0->type == WORD_TKN && !strcmp(tok0->text, "no")))
     476         864 :                         cmd_token_varname_do(tok0, "no", VARNAME_AUTO);
     477         864 :                 if ((tok1->type == WORD_TKN && !strcmp(tok1->text, "no")))
     478           0 :                         cmd_token_varname_do(tok1, "no", VARNAME_AUTO);
     479             :         } while (0);
     480       11896 : }
     481             : 
     482             : #ifndef BUILDING_CLIPPY
     483             : 
     484             : #include "command.h"
     485             : #include "log.h"
     486             : 
     487           0 : void cmd_graph_node_print_cb(struct graph_node *gn, struct buffer *buf)
     488             : {
     489           0 :         static bool wasend;
     490             : 
     491           0 :         char nbuf[512];
     492           0 :         struct cmd_token *tok = gn->data;
     493           0 :         const char *color = NULL;
     494             : 
     495           0 :         if (wasend) {
     496           0 :                 wasend = false;
     497           0 :                 return;
     498             :         }
     499             : 
     500           0 :         if (tok->type == END_TKN) {
     501           0 :                 wasend = true;
     502           0 :                 return;
     503             :         }
     504             : 
     505           0 :         snprintf(nbuf, sizeof(nbuf), "  n%p [ shape=box, label=<", gn);
     506           0 :         buffer_putstr(buf, nbuf);
     507           0 :         snprintf(nbuf, sizeof(nbuf), "<b>%s</b>",
     508           0 :                  lookup_msg(tokennames, tok->type, NULL));
     509           0 :         buffer_putstr(buf, nbuf);
     510           0 :         if (tok->attr & CMD_ATTR_DEPRECATED)
     511           0 :                 buffer_putstr(buf, " (d)");
     512             :         /* DEPRECATED implies HIDDEN, don't print both */
     513           0 :         else if (tok->attr & CMD_ATTR_HIDDEN)
     514           0 :                 buffer_putstr(buf, " (h)");
     515           0 :         if (tok->text) {
     516           0 :                 if (tok->type == WORD_TKN)
     517           0 :                         snprintf(
     518             :                                 nbuf, sizeof(nbuf),
     519             :                                 "<br/>\"<font color=\"#0055ff\" point-size=\"11\"><b>%s</b></font>\"",
     520             :                                 tok->text);
     521             :                 else
     522           0 :                         snprintf(nbuf, sizeof(nbuf), "<br/>%s", tok->text);
     523           0 :                 buffer_putstr(buf, nbuf);
     524             :         }
     525             : 
     526           0 :         switch (tok->type) {
     527           0 :         case START_TKN:
     528           0 :                 color = "#ccffcc";
     529           0 :                 break;
     530           0 :         case FORK_TKN:
     531           0 :                 color = "#aaddff";
     532           0 :                 break;
     533           0 :         case JOIN_TKN:
     534           0 :                 color = "#ddaaff";
     535           0 :                 break;
     536           0 :         case NEG_ONLY_TKN:
     537           0 :                 color = "#ffddaa";
     538           0 :                 break;
     539             :         case WORD_TKN:
     540           0 :                 color = "#ffffff";
     541             :                 break;
     542             :         case RANGE_TKN:
     543             :         case IPV4_TKN:
     544             :         case IPV4_PREFIX_TKN:
     545             :         case IPV6_TKN:
     546             :         case IPV6_PREFIX_TKN:
     547             :         case MAC_TKN:
     548             :         case MAC_PREFIX_TKN:
     549             :         case END_TKN:
     550             :         case VARIABLE_TKN:
     551           0 :                 color = "#ffffff";
     552             :                 break;
     553             :         }
     554             : 
     555             :         /*
     556             :          * Some compilers have the mistaken belief that we can
     557             :          * get here without initializing color.
     558             :          */
     559           0 :         snprintf(nbuf, sizeof(nbuf),
     560             :                  ">, style = filled, fillcolor = \"%s\" ];\n", color);
     561           0 :         buffer_putstr(buf, nbuf);
     562             : 
     563           0 :         for (unsigned int i = 0; i < vector_active(gn->to); i++) {
     564           0 :                 struct graph_node *adj = vector_slot(gn->to, i);
     565             : 
     566           0 :                 if (((struct cmd_token *)adj->data)->type == END_TKN) {
     567           0 :                         snprintf(nbuf, sizeof(nbuf), "  n%p -> end%p;\n", gn,
     568             :                                  adj);
     569           0 :                         buffer_putstr(buf, nbuf);
     570           0 :                         snprintf(
     571             :                                 nbuf, sizeof(nbuf),
     572             :                                 "  end%p [ shape=box, label=<end>, style = filled, fillcolor = \"#ffddaa\" ];\n",
     573             :                                 adj);
     574             :                 } else
     575           0 :                         snprintf(nbuf, sizeof(nbuf), "  n%p -> n%p;\n", gn,
     576             :                                  adj);
     577             : 
     578           0 :                 buffer_putstr(buf, nbuf);
     579             :         }
     580             : }
     581             : 
     582           0 : char *cmd_graph_dump_dot(struct graph *cmdgraph)
     583             : {
     584           0 :         struct graph_node *start = vector_slot(cmdgraph->nodes, 0);
     585             : 
     586           0 :         return graph_dump_dot(cmdgraph, start, cmd_graph_node_print_cb);
     587             : }
     588             : 
     589             : #endif /* BUILDING_CLIPPY */

Generated by: LCOV version v1.16-topotato