]> git.proxmox.com Git - mirror_frr.git/blame - lib/grammar_sandbox.c
Merge pull request #929 from opensourcerouting/hooks-doc-irdp
[mirror_frr.git] / lib / grammar_sandbox.c
CommitLineData
1ab84bf3
QY
1/*
2 * Testing shim and API examples for the new CLI backend.
3 *
4 * This unit defines a number of commands in the old engine that can
5 * be used to test and interact with the new engine.
1ab84bf3
QY
6 * --
7 * Copyright (C) 2016 Cumulus Networks, Inc.
8 *
9 * This file is part of GNU Zebra.
10 *
11 * GNU Zebra is free software; you can redistribute it and/or modify it
12 * under the terms of the GNU General Public License as published by the
13 * Free Software Foundation; either version 2, or (at your option) any
14 * later version.
15 *
16 * GNU Zebra is distributed in the hope that it will be useful, but
17 * WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19 * General Public License for more details.
20 *
896014f4
DL
21 * You should have received a copy of the GNU General Public License along
22 * with this program; see the file COPYING; if not, write to the Free Software
23 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
1ab84bf3
QY
24 */
25
51e156b3 26#include "command.h"
fa133e00 27#include "memory_vty.h"
1eb5e8dc 28#include "graph.h"
0a22b979 29#include "linklist.h"
9d0662e0 30#include "command_match.h"
51e156b3
QY
31
32#define GRAMMAR_STR "CLI grammar sandbox\n"
33
fa133e00
DL
34DEFINE_MTYPE_STATIC(LIB, CMD_TOKENS, "Command desc")
35
36#define MAXDEPTH 64
37
97c45dae 38/** headers **/
d62a17ae 39void grammar_sandbox_init(void);
40void pretty_print_graph(struct vty *vty, struct graph_node *, int, int,
41 struct graph_node **, size_t);
42static void pretty_print_dot(FILE *ofd, unsigned opts, struct graph_node *start,
43 struct graph_node **stack, size_t stackpos,
44 struct graph_node **visited, size_t *visitpos);
45void init_cmdgraph(struct vty *, struct graph **);
1ab84bf3 46
97c45dae 47/** shim interface commands **/
af2567b6 48struct graph *nodegraph = NULL, *nodegraph_free = NULL;
340a2b4a 49
d62a17ae 50#define check_nodegraph() \
51 do { \
52 if (!nodegraph) { \
53 vty_out(vty, "nodegraph not initialized\n"); \
54 return CMD_WARNING; \
55 } \
56 } while (0)
45082064 57
9d0662e0
QY
58DEFUN (grammar_test,
59 grammar_test_cmd,
fa133e00 60 "grammar parse LINE...",
9d0662e0 61 GRAMMAR_STR
fa133e00 62 "parse a command\n"
9d0662e0
QY
63 "command to pass to new parser\n")
64{
d62a17ae 65 check_nodegraph();
66
67 int idx_command = 2;
68 // make a string from tokenized command line
69 char *command = argv_concat(argv, argc, idx_command);
70
71 // create cmd_element for parser
72 struct cmd_element *cmd =
73 XCALLOC(MTYPE_CMD_TOKENS, sizeof(struct cmd_element));
74 cmd->string = command;
75 cmd->doc =
76 "0\n1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n11\n12\n13\n14\n15\n16\n17\n18\n19\n";
77 cmd->func = NULL;
78
79 // parse the command and install it into the command graph
80 struct graph *graph = graph_new();
81 struct cmd_token *token =
82 cmd_token_new(START_TKN, CMD_ATTR_NORMAL, NULL, NULL);
83 graph_new_node(graph, token, (void (*)(void *)) & cmd_token_del);
84
85 cmd_graph_parse(graph, cmd);
86 cmd_graph_merge(nodegraph, graph, +1);
87
88 return CMD_SUCCESS;
9d0662e0
QY
89}
90
eceb1066
QY
91DEFUN (grammar_test_complete,
92 grammar_test_complete_cmd,
fa133e00 93 "grammar complete COMMAND...",
9d0662e0 94 GRAMMAR_STR
eceb1066 95 "attempt to complete input on DFA\n"
fa133e00 96 "command to complete\n")
9d0662e0 97{
d62a17ae 98 check_nodegraph();
99
100 int idx_command = 2;
101 char *cmdstr = argv_concat(argv, argc, idx_command);
102 if (!cmdstr)
103 return CMD_SUCCESS;
104
105 vector command = cmd_make_strvec(cmdstr);
106 if (!command) {
107 XFREE(MTYPE_TMP, cmdstr);
108 return CMD_SUCCESS;
109 }
110
111 // generate completions of user input
112 struct list *completions;
113 enum matcher_rv result =
114 command_complete(nodegraph, command, &completions);
115
116 // print completions or relevant error message
117 if (!MATCHER_ERROR(result)) {
118 vector comps = completions_to_vec(completions);
119 struct cmd_token *tkn;
120
121 // calculate length of longest tkn->text in completions
122 unsigned int width = 0, i = 0;
123 for (i = 0; i < vector_active(comps); i++) {
124 tkn = vector_slot(comps, i);
125 unsigned int len = strlen(tkn->text);
126 width = len > width ? len : width;
127 }
128
129 // print completions
130 for (i = 0; i < vector_active(comps); i++) {
131 tkn = vector_slot(comps, i);
132 vty_out(vty, " %-*s %s\n", width, tkn->text,
133 tkn->desc);
134 }
135
136 for (i = 0; i < vector_active(comps); i++)
137 cmd_token_del(
138 (struct cmd_token *)vector_slot(comps, i));
139 vector_free(comps);
140 } else
141 vty_out(vty, "%% No match\n");
142
143 // free resources
144 list_delete(completions);
145 cmd_free_strvec(command);
146 XFREE(MTYPE_TMP, cmdstr);
147
148 return CMD_SUCCESS;
340a2b4a
QY
149}
150
eceb1066
QY
151DEFUN (grammar_test_match,
152 grammar_test_match_cmd,
fa133e00 153 "grammar match COMMAND...",
eceb1066
QY
154 GRAMMAR_STR
155 "attempt to match input on DFA\n"
fa133e00 156 "command to match\n")
eceb1066 157{
d62a17ae 158 check_nodegraph();
159
160 int idx_command = 2;
161 if (argv[2]->arg[0] == '#')
162 return CMD_SUCCESS;
163
164 char *cmdstr = argv_concat(argv, argc, idx_command);
165 if (!cmdstr)
166 return CMD_SUCCESS;
167 vector command = cmd_make_strvec(cmdstr);
168 if (!command) {
169 XFREE(MTYPE_TMP, cmdstr);
170 return CMD_SUCCESS;
171 }
172
173 struct list *argvv = NULL;
174 const struct cmd_element *element = NULL;
175 enum matcher_rv result =
176 command_match(nodegraph, command, &argvv, &element);
177
178 // print completions or relevant error message
179 if (element) {
180 vty_out(vty, "Matched: %s\n", element->string);
181 struct listnode *ln;
182 struct cmd_token *token;
183 for (ALL_LIST_ELEMENTS_RO(argvv, ln, token))
184 vty_out(vty, "%s -- %s\n", token->text, token->arg);
185
186 vty_out(vty, "func: %p\n", element->func);
187
188 list_delete(argvv);
189 } else {
190 assert(MATCHER_ERROR(result));
191 switch (result) {
192 case MATCHER_NO_MATCH:
193 vty_out(vty, "%% Unknown command\n");
194 break;
195 case MATCHER_INCOMPLETE:
196 vty_out(vty, "%% Incomplete command\n");
197 break;
198 case MATCHER_AMBIGUOUS:
199 vty_out(vty, "%% Ambiguous command\n");
200 break;
201 default:
202 vty_out(vty, "%% Unknown error\n");
203 break;
204 }
205 }
206
207 // free resources
208 cmd_free_strvec(command);
209 XFREE(MTYPE_TMP, cmdstr);
210
211 return CMD_SUCCESS;
eceb1066
QY
212}
213
1ab84bf3
QY
214/**
215 * Testing shim to test docstrings
216 */
217DEFUN (grammar_test_doc,
218 grammar_test_doc_cmd,
219 "grammar test docstring",
220 GRAMMAR_STR
221 "Test function for docstring\n"
222 "Command end\n")
223{
d62a17ae 224 check_nodegraph();
225
226 // create cmd_element with docstring
227 struct cmd_element *cmd =
228 XCALLOC(MTYPE_CMD_TOKENS, sizeof(struct cmd_element));
229 cmd->string = XSTRDUP(
230 MTYPE_CMD_TOKENS,
231 "test docstring <example|selector follow> (1-255) end VARIABLE [OPTION|set lol] . VARARG");
232 cmd->doc = XSTRDUP(MTYPE_CMD_TOKENS,
233 "Test stuff\n"
234 "docstring thing\n"
235 "first example\n"
236 "second example\n"
237 "follow\n"
238 "random range\n"
239 "end thingy\n"
240 "variable\n"
241 "optional variable\n"
242 "optional set\n"
243 "optional lol\n"
244 "vararg!\n");
245 cmd->func = NULL;
246
247 // parse element
248 cmd_graph_parse(nodegraph, cmd);
249
250 return CMD_SUCCESS;
1ab84bf3
QY
251}
252
253/**
254 * Debugging command to print command graph
255 */
256DEFUN (grammar_test_show,
257 grammar_test_show_cmd,
fa133e00 258 "grammar show [doc]",
1ab84bf3 259 GRAMMAR_STR
fa133e00
DL
260 "print current accumulated DFA\n"
261 "include docstrings\n")
1ab84bf3 262{
d62a17ae 263 check_nodegraph();
fa133e00 264
d62a17ae 265 struct graph_node *stack[MAXDEPTH];
266 pretty_print_graph(vty, vector_slot(nodegraph->nodes, 0), 0, argc >= 3,
267 stack, 0);
268 return CMD_SUCCESS;
1ab84bf3 269}
51e156b3 270
818a5168
DL
271DEFUN (grammar_test_dot,
272 grammar_test_dot_cmd,
273 "grammar dotfile OUTNAME",
274 GRAMMAR_STR
275 "print current graph for dot\n"
276 ".dot filename\n")
277{
d62a17ae 278 check_nodegraph();
279
280 struct graph_node *stack[MAXDEPTH];
281 struct graph_node *visited[MAXDEPTH * MAXDEPTH];
282 size_t vpos = 0;
283
284 FILE *ofd = fopen(argv[2]->arg, "w");
285 if (!ofd) {
286 vty_out(vty, "%s: %s\r\n", argv[2]->arg, strerror(errno));
287 return CMD_SUCCESS;
288 }
289
290 fprintf(ofd,
291 "digraph {\n graph [ rankdir = LR ];\n node [ fontname = \"Fira Mono\", fontsize = 9 ];\n\n");
292 pretty_print_dot(ofd, 0, vector_slot(nodegraph->nodes, 0), stack, 0,
293 visited, &vpos);
294 fprintf(ofd, "}\n");
295 fclose(ofd);
296 return CMD_SUCCESS;
818a5168
DL
297}
298
d62a17ae 299struct cmd_permute_item {
300 char *cmd;
301 struct cmd_element *el;
0a22b979
DL
302};
303
d62a17ae 304static void cmd_permute_free(void *arg)
0a22b979 305{
d62a17ae 306 struct cmd_permute_item *i = arg;
307 XFREE(MTYPE_TMP, i->cmd);
308 XFREE(MTYPE_TMP, i);
0a22b979
DL
309}
310
d62a17ae 311static int cmd_permute_cmp(void *a, void *b)
0a22b979 312{
d62a17ae 313 struct cmd_permute_item *aa = a, *bb = b;
314 return strcmp(aa->cmd, bb->cmd);
0a22b979
DL
315}
316
d62a17ae 317static void cmd_graph_permute(struct list *out, struct graph_node **stack,
318 size_t stackpos, char *cmd)
0a22b979 319{
d62a17ae 320 struct graph_node *gn = stack[stackpos];
321 struct cmd_token *tok = gn->data;
322 char *appendp = cmd + strlen(cmd);
323 size_t i, j;
324
325 if (tok->type < SPECIAL_TKN) {
326 sprintf(appendp, "%s ", tok->text);
327 appendp += strlen(appendp);
328 } else if (tok->type == END_TKN) {
329 struct cmd_permute_item *i = XMALLOC(MTYPE_TMP, sizeof(*i));
330 i->el = ((struct graph_node *)vector_slot(gn->to, 0))->data;
331 i->cmd = XSTRDUP(MTYPE_TMP, cmd);
332 i->cmd[strlen(cmd) - 1] = '\0';
333 listnode_add_sort(out, i);
334 return;
335 }
336
337 if (++stackpos == MAXDEPTH)
338 return;
339
340 for (i = 0; i < vector_active(gn->to); i++) {
341 struct graph_node *gnext = vector_slot(gn->to, i);
342 for (j = 0; j < stackpos; j++)
343 if (stack[j] == gnext)
344 break;
345 if (j != stackpos)
346 continue;
347
348 stack[stackpos] = gnext;
349 *appendp = '\0';
350 cmd_graph_permute(out, stack, stackpos, cmd);
351 }
0a22b979
DL
352}
353
d62a17ae 354static struct list *cmd_graph_permutations(struct graph *graph)
0a22b979 355{
d62a17ae 356 char accumulate[2048] = "";
357 struct graph_node *stack[MAXDEPTH];
358
359 struct list *rv = list_new();
360 rv->cmp = cmd_permute_cmp;
361 rv->del = cmd_permute_free;
362 stack[0] = vector_slot(graph->nodes, 0);
363 cmd_graph_permute(rv, stack, 0, accumulate);
364 return rv;
0a22b979
DL
365}
366
367extern vector cmdvec;
368
369DEFUN (grammar_findambig,
370 grammar_findambig_cmd,
371 "grammar find-ambiguous [{printall|nodescan}]",
372 GRAMMAR_STR
373 "Find ambiguous commands\n"
374 "Print all permutations\n"
375 "Scan all nodes\n")
376{
d62a17ae 377 struct list *commands;
378 struct cmd_permute_item *prev = NULL, *cur = NULL;
379 struct listnode *ln;
380 int i, printall, scan, scannode = 0;
381 int ambig = 0;
382
383 i = 0;
384 printall = argv_find(argv, argc, "printall", &i);
385 i = 0;
386 scan = argv_find(argv, argc, "nodescan", &i);
387
388 if (scan && nodegraph_free) {
389 graph_delete_graph(nodegraph_free);
390 nodegraph_free = NULL;
391 }
392
393 if (!scan && !nodegraph) {
394 vty_out(vty, "nodegraph uninitialized\r\n");
395 return CMD_WARNING_CONFIG_FAILED;
396 }
397
398 do {
399 if (scan) {
400 struct cmd_node *cnode =
401 vector_slot(cmdvec, scannode++);
402 if (!cnode)
403 continue;
404 nodegraph = cnode->cmdgraph;
405 if (!nodegraph)
406 continue;
407 vty_out(vty, "scanning node %d\n", scannode - 1);
408 }
409
410 commands = cmd_graph_permutations(nodegraph);
411 prev = NULL;
412 for (ALL_LIST_ELEMENTS_RO(commands, ln, cur)) {
413 int same = prev && !strcmp(prev->cmd, cur->cmd);
414 if (printall && !same)
415 vty_out(vty, "'%s' [%x]\n", cur->cmd,
416 cur->el->daemon);
417 if (same) {
418 vty_out(vty, "'%s' AMBIGUOUS:\n", cur->cmd);
419 vty_out(vty, " %s\n '%s'\n", prev->el->name,
420 prev->el->string);
421 vty_out(vty, " %s\n '%s'\n", cur->el->name,
422 cur->el->string);
423 vty_out(vty, "\n");
424 ambig++;
425 }
426 prev = cur;
427 }
428 list_delete(commands);
429
430 vty_out(vty, "\n");
431 } while (scan && scannode < LINK_PARAMS_NODE);
432
433 vty_out(vty, "%d ambiguous commands found.\n", ambig);
434
435 if (scan)
436 nodegraph = NULL;
437 return ambig == 0 ? CMD_SUCCESS : CMD_WARNING_CONFIG_FAILED;
0a22b979
DL
438}
439
500b1a5b
QY
440DEFUN (grammar_init_graph,
441 grammar_init_graph_cmd,
fa133e00 442 "grammar init",
500b1a5b
QY
443 GRAMMAR_STR
444 "(re)initialize graph\n")
445{
d62a17ae 446 if (nodegraph_free)
447 graph_delete_graph(nodegraph_free);
448 nodegraph_free = NULL;
af2567b6 449
d62a17ae 450 init_cmdgraph(vty, &nodegraph);
451 return CMD_SUCCESS;
500b1a5b
QY
452}
453
af2567b6
DL
454DEFUN (grammar_access,
455 grammar_access_cmd,
456 "grammar access (0-65535)",
457 GRAMMAR_STR
458 "access node graph\n"
459 "node number\n")
460{
d62a17ae 461 if (nodegraph_free)
462 graph_delete_graph(nodegraph_free);
463 nodegraph_free = NULL;
464
465 struct cmd_node *cnode;
466
467 cnode = vector_slot(cmdvec, atoi(argv[2]->arg));
468 if (!cnode) {
469 vty_out(vty, "%% no such node\n");
470 return CMD_WARNING_CONFIG_FAILED;
471 }
472
473 vty_out(vty, "node %d\n", (int)cnode->node);
474 nodegraph = cnode->cmdgraph;
475 return CMD_SUCCESS;
af2567b6
DL
476}
477
1ab84bf3 478/* this is called in vtysh.c to set up the testing shim */
d62a17ae 479void grammar_sandbox_init(void)
480{
481 // install all enable elements
482 install_element(ENABLE_NODE, &grammar_test_cmd);
483 install_element(ENABLE_NODE, &grammar_test_show_cmd);
484 install_element(ENABLE_NODE, &grammar_test_dot_cmd);
485 install_element(ENABLE_NODE, &grammar_test_match_cmd);
486 install_element(ENABLE_NODE, &grammar_test_complete_cmd);
487 install_element(ENABLE_NODE, &grammar_test_doc_cmd);
488 install_element(ENABLE_NODE, &grammar_findambig_cmd);
489 install_element(ENABLE_NODE, &grammar_init_graph_cmd);
490 install_element(ENABLE_NODE, &grammar_access_cmd);
51e156b3 491}
1ab84bf3 492
fa133e00 493#define item(x) { x, #x }
d62a17ae 494struct message tokennames[] = {item(WORD_TKN), // words
495 item(VARIABLE_TKN), // almost anything
496 item(RANGE_TKN), // integer range
497 item(IPV4_TKN), // IPV4 addresses
498 item(IPV4_PREFIX_TKN), // IPV4 network prefixes
499 item(IPV6_TKN), // IPV6 prefixes
500 item(IPV6_PREFIX_TKN), // IPV6 network prefixes
9779e3f1
QY
501 item(MAC_TKN), // MAC address
502 item(MAC_PREFIX_TKN), // MAC address w/ mask
d62a17ae 503
504 /* plumbing types */
505 item(FORK_TKN),
506 item(JOIN_TKN),
507 item(START_TKN), // first token in line
508 item(END_TKN), // last token in line
509 {0}};
500b1a5b 510
97f13f08
QY
511/**
512 * Pretty-prints a graph, assuming it is a tree.
513 *
514 * @param start the node to take as the root
515 * @param level indent level for recursive calls, always pass 0
516 */
d62a17ae 517void pretty_print_graph(struct vty *vty, struct graph_node *start, int level,
518 int desc, struct graph_node **stack, size_t stackpos)
1ab84bf3 519{
d62a17ae 520 // print this node
521 char tokennum[32];
522 struct cmd_token *tok = start->data;
523
524 snprintf(tokennum, sizeof(tokennum), "%d?", tok->type);
525 vty_out(vty, "%s", lookup_msg(tokennames, tok->type, NULL));
526 if (tok->text)
527 vty_out(vty, ":\"%s\"", tok->text);
528 if (tok->varname)
529 vty_out(vty, " => %s", tok->varname);
530 if (desc)
531 vty_out(vty, " ?'%s'", tok->desc);
532 vty_out(vty, " ");
533
534 if (stackpos == MAXDEPTH) {
535 vty_out(vty, " -aborting! (depth limit)\n");
536 return;
537 }
538 stack[stackpos++] = start;
539
540 int numto = desc ? 2 : vector_active(start->to);
541 if (numto) {
542 if (numto > 1)
543 vty_out(vty, "\n");
544 for (unsigned int i = 0; i < vector_active(start->to); i++) {
545 struct graph_node *adj = vector_slot(start->to, i);
546 // if we're listing multiple children, indent!
547 if (numto > 1)
548 for (int j = 0; j < level + 1; j++)
549 vty_out(vty, " ");
550 // if this node is a vararg, just print *
551 if (adj == start)
552 vty_out(vty, "*");
553 else if (((struct cmd_token *)adj->data)->type
554 == END_TKN)
555 vty_out(vty, "--END\n");
556 else {
557 size_t k;
558 for (k = 0; k < stackpos; k++)
559 if (stack[k] == adj) {
560 vty_out(vty, "<<loop@%zu \n",
561 k);
562 break;
563 }
564 if (k == stackpos)
565 pretty_print_graph(
566 vty, adj,
567 numto > 1 ? level + 1 : level,
568 desc, stack, stackpos);
569 }
570 }
571 } else
572 vty_out(vty, "\n");
1eb5e8dc
QY
573}
574
d62a17ae 575static void pretty_print_dot(FILE *ofd, unsigned opts, struct graph_node *start,
576 struct graph_node **stack, size_t stackpos,
577 struct graph_node **visited, size_t *visitpos)
818a5168 578{
d62a17ae 579 // print this node
580 char tokennum[32];
581 struct cmd_token *tok = start->data;
582 const char *color;
583
584 for (size_t i = 0; i < (*visitpos); i++)
585 if (visited[i] == start)
586 return;
587 visited[(*visitpos)++] = start;
588 if ((*visitpos) == MAXDEPTH * MAXDEPTH)
589 return;
590
591 snprintf(tokennum, sizeof(tokennum), "%d?", tok->type);
592 fprintf(ofd, " n%p [ shape=box, label=<", start);
593
594 fprintf(ofd, "<b>%s</b>", lookup_msg(tokennames, tok->type, NULL));
595 if (tok->attr == CMD_ATTR_DEPRECATED)
596 fprintf(ofd, " (d)");
597 else if (tok->attr == CMD_ATTR_HIDDEN)
598 fprintf(ofd, " (h)");
599 if (tok->text) {
600 if (tok->type == WORD_TKN)
601 fprintf(ofd,
602 "<br/>\"<font color=\"#0055ff\" point-size=\"11\"><b>%s</b></font>\"",
603 tok->text);
604 else
605 fprintf(ofd, "<br/>%s", tok->text);
606 }
607 /* if (desc)
608 fprintf(ofd, " ?'%s'", tok->desc); */
609 switch (tok->type) {
610 case START_TKN:
611 color = "#ccffcc";
612 break;
613 case FORK_TKN:
614 color = "#aaddff";
615 break;
616 case JOIN_TKN:
617 color = "#ddaaff";
618 break;
619 case WORD_TKN:
620 color = "#ffffff";
621 break;
622 default:
623 color = "#ffffff";
624 break;
625 }
626 fprintf(ofd, ">, style = filled, fillcolor = \"%s\" ];\n", color);
627
628 if (stackpos == MAXDEPTH)
629 return;
630 stack[stackpos++] = start;
631
632 for (unsigned int i = 0; i < vector_active(start->to); i++) {
633 struct graph_node *adj = vector_slot(start->to, i);
634 // if this node is a vararg, just print *
635 if (adj == start) {
636 fprintf(ofd, " n%p -> n%p;\n", start, start);
637 } else if (((struct cmd_token *)adj->data)->type == END_TKN) {
638 // struct cmd_token *et = adj->data;
639 fprintf(ofd, " n%p -> end%p;\n", start, adj);
640 fprintf(ofd,
641 " end%p [ shape=box, label=<end>, style = filled, fillcolor = \"#ffddaa\" ];\n",
642 adj);
643 } else {
644 fprintf(ofd, " n%p -> n%p;\n", start, adj);
645 size_t k;
646 for (k = 0; k < stackpos; k++)
647 if (stack[k] == adj)
648 break;
649 if (k == stackpos) {
650 pretty_print_dot(ofd, opts, adj, stack,
651 stackpos, visited, visitpos);
652 }
653 }
654 }
818a5168
DL
655}
656
657
1eb5e8dc 658/** stuff that should go in command.c + command.h */
d62a17ae 659void init_cmdgraph(struct vty *vty, struct graph **graph)
97c45dae 660{
d62a17ae 661 // initialize graph, add start noe
662 *graph = graph_new();
663 nodegraph_free = *graph;
664 struct cmd_token *token = cmd_token_new(START_TKN, 0, NULL, NULL);
665 graph_new_node(*graph, token, (void (*)(void *)) & cmd_token_del);
666 if (vty)
667 vty_out(vty, "initialized graph\n");
97c45dae 668}