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