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