]> git.proxmox.com Git - mirror_frr.git/blame - lib/grammar_sandbox.c
*: ditch vty_outln(), part 1 of 2
[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 **/
1ab84bf3 39void
1eb5e8dc 40grammar_sandbox_init (void);
1ab84bf3 41void
fa133e00 42pretty_print_graph (struct vty *vty, struct graph_node *, int, int, struct graph_node **, size_t);
818a5168
DL
43static void
44pretty_print_dot (FILE *ofd, unsigned opts, struct graph_node *start,
b285cf4b
DL
45 struct graph_node **stack, size_t stackpos,
46 struct graph_node **visited, size_t *visitpos);
500b1a5b 47void
fa133e00 48init_cmdgraph (struct vty *, struct graph **);
1ab84bf3 49
97c45dae 50/** shim interface commands **/
af2567b6 51struct graph *nodegraph = NULL, *nodegraph_free = NULL;
340a2b4a 52
9d0662e0
QY
53DEFUN (grammar_test,
54 grammar_test_cmd,
fa133e00 55 "grammar parse LINE...",
9d0662e0 56 GRAMMAR_STR
fa133e00 57 "parse a command\n"
9d0662e0
QY
58 "command to pass to new parser\n")
59{
58749582 60 int idx_command = 2;
ff35126c 61 // make a string from tokenized command line
58749582 62 char *command = argv_concat (argv, argc, idx_command);
1ab84bf3 63
ff35126c 64 // create cmd_element for parser
1eb5e8dc 65 struct cmd_element *cmd = XCALLOC (MTYPE_CMD_TOKENS, sizeof (struct cmd_element));
500b1a5b 66 cmd->string = command;
fa133e00 67 cmd->doc = "0\n1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n11\n12\n13\n14\n15\n16\n17\n18\n19\n";
e648e61a 68 cmd->func = NULL;
51e156b3 69
1ab84bf3 70 // parse the command and install it into the command graph
de8f7a39 71 struct graph *graph = graph_new();
5894e76d
DL
72 struct cmd_token *token = cmd_token_new (START_TKN, CMD_ATTR_NORMAL, NULL, NULL);
73 graph_new_node (graph, token, (void (*)(void *)) &cmd_token_del);
de8f7a39 74
5894e76d
DL
75 cmd_graph_parse (graph, cmd);
76 cmd_graph_merge (nodegraph, graph, +1);
1ab84bf3 77
9d0662e0
QY
78 return CMD_SUCCESS;
79}
80
eceb1066
QY
81DEFUN (grammar_test_complete,
82 grammar_test_complete_cmd,
fa133e00 83 "grammar complete COMMAND...",
9d0662e0 84 GRAMMAR_STR
eceb1066 85 "attempt to complete input on DFA\n"
fa133e00 86 "command to complete\n")
9d0662e0 87{
58749582
DW
88 int idx_command = 2;
89 char *cmdstr = argv_concat (argv, argc, idx_command);
fa133e00
DL
90 if (!cmdstr)
91 return CMD_SUCCESS;
92
1ab84bf3 93 vector command = cmd_make_strvec (cmdstr);
14878121
DL
94 if (!command)
95 {
96 XFREE (MTYPE_TMP, cmdstr);
97 return CMD_SUCCESS;
98 }
1ab84bf3 99
ff35126c 100 // generate completions of user input
795d0278 101 struct list *completions;
ff35126c 102 enum matcher_rv result = command_complete (nodegraph, command, &completions);
18be0e59 103
1ab84bf3 104 // print completions or relevant error message
ca90e051 105 if (!MATCHER_ERROR(result))
1ab84bf3 106 {
ee761cc0 107 vector comps = completions_to_vec (completions);
fa133e00 108 struct cmd_token *tkn;
ff35126c
QY
109
110 // calculate length of longest tkn->text in completions
ee761cc0
QY
111 unsigned int width = 0, i = 0;
112 for (i = 0; i < vector_active (comps); i++) {
113 tkn = vector_slot (comps, i);
114 unsigned int len = strlen (tkn->text);
115 width = len > width ? len : width;
ff35126c
QY
116 }
117
118 // print completions
ee761cc0
QY
119 for (i = 0; i < vector_active (comps); i++) {
120 tkn = vector_slot (comps, i);
5c7571d4 121 vty_out (vty, " %-*s %s\n", width, tkn->text, tkn->desc);
ee761cc0 122 }
795d0278
QY
123
124 for (i = 0; i < vector_active (comps); i++)
5894e76d 125 cmd_token_del ((struct cmd_token *) vector_slot (comps, i));
795d0278 126 vector_free (comps);
1ab84bf3 127 }
18be0e59 128 else
5c7571d4 129 vty_out (vty, "%% No match\n");
1ab84bf3
QY
130
131 // free resources
1eb5e8dc 132 list_delete (completions);
ff35126c 133 cmd_free_strvec (command);
14878121 134 XFREE (MTYPE_TMP, cmdstr);
18be0e59 135
9d0662e0 136 return CMD_SUCCESS;
340a2b4a
QY
137}
138
eceb1066
QY
139DEFUN (grammar_test_match,
140 grammar_test_match_cmd,
fa133e00 141 "grammar match COMMAND...",
eceb1066
QY
142 GRAMMAR_STR
143 "attempt to match input on DFA\n"
fa133e00 144 "command to match\n")
eceb1066 145{
58749582 146 int idx_command = 2;
fa133e00 147 if (argv[2]->arg[0] == '#')
4427e9b3
QY
148 return CMD_SUCCESS;
149
58749582 150 char *cmdstr = argv_concat(argv, argc, idx_command);
14878121
DL
151 if (!cmdstr)
152 return CMD_SUCCESS;
1ab84bf3 153 vector command = cmd_make_strvec (cmdstr);
14878121
DL
154 if (!command)
155 {
156 XFREE (MTYPE_TMP, cmdstr);
157 return CMD_SUCCESS;
158 }
6ce82b63 159
de9d7e4f 160 struct list *argvv = NULL;
fa133e00 161 const struct cmd_element *element = NULL;
55589d30 162 enum matcher_rv result = command_match (nodegraph, command, &argvv, &element);
1ab84bf3
QY
163
164 // print completions or relevant error message
165 if (element)
166 {
5c7571d4 167 vty_out (vty, "Matched: %s\n", element->string);
1ab84bf3 168 struct listnode *ln;
fa133e00 169 struct cmd_token *token;
97f13f08 170 for (ALL_LIST_ELEMENTS_RO(argvv,ln,token))
5c7571d4 171 vty_out (vty, "%s -- %s\n", token->text, token->arg);
1eb5e8dc 172
5c7571d4 173 vty_out (vty, "func: %p\n", element->func);
1ab84bf3
QY
174
175 list_delete (argvv);
176 }
de9d7e4f 177 else {
1ab84bf3 178 assert(MATCHER_ERROR(result));
6ce82b63
QY
179 switch (result) {
180 case MATCHER_NO_MATCH:
5c7571d4 181 vty_out (vty, "%% Unknown command\n");
6ce82b63
QY
182 break;
183 case MATCHER_INCOMPLETE:
5c7571d4 184 vty_out (vty, "%% Incomplete command\n");
6ce82b63
QY
185 break;
186 case MATCHER_AMBIGUOUS:
5c7571d4 187 vty_out (vty, "%% Ambiguous command\n");
6ce82b63
QY
188 break;
189 default:
5c7571d4 190 vty_out (vty, "%% Unknown error\n");
6ce82b63
QY
191 break;
192 }
de9d7e4f
QY
193 }
194
1ab84bf3 195 // free resources
795d0278 196 cmd_free_strvec (command);
14878121 197 XFREE (MTYPE_TMP, cmdstr);
1ab84bf3 198
eceb1066
QY
199 return CMD_SUCCESS;
200}
201
1ab84bf3
QY
202/**
203 * Testing shim to test docstrings
204 */
205DEFUN (grammar_test_doc,
206 grammar_test_doc_cmd,
207 "grammar test docstring",
208 GRAMMAR_STR
209 "Test function for docstring\n"
210 "Command end\n")
211{
212 // create cmd_element with docstring
1eb5e8dc 213 struct cmd_element *cmd = XCALLOC (MTYPE_CMD_TOKENS, sizeof (struct cmd_element));
ff35126c
QY
214 cmd->string = XSTRDUP (MTYPE_CMD_TOKENS, "test docstring <example|selector follow> (1-255) end VARIABLE [OPTION|set lol] . VARARG");
215 cmd->doc = XSTRDUP (MTYPE_CMD_TOKENS,
216 "Test stuff\n"
1ab84bf3
QY
217 "docstring thing\n"
218 "first example\n"
219 "second example\n"
220 "follow\n"
221 "random range\n"
222 "end thingy\n"
223 "variable\n"
224 "optional variable\n"
225 "optional set\n"
226 "optional lol\n"
ff35126c 227 "vararg!\n");
1ab84bf3 228 cmd->func = NULL;
1ab84bf3
QY
229
230 // parse element
5894e76d 231 cmd_graph_parse (nodegraph, cmd);
1ab84bf3
QY
232
233 return CMD_SUCCESS;
234}
235
236/**
237 * Debugging command to print command graph
238 */
239DEFUN (grammar_test_show,
240 grammar_test_show_cmd,
fa133e00 241 "grammar show [doc]",
1ab84bf3 242 GRAMMAR_STR
fa133e00
DL
243 "print current accumulated DFA\n"
244 "include docstrings\n")
1ab84bf3 245{
fa133e00
DL
246 struct graph_node *stack[MAXDEPTH];
247
1ab84bf3 248 if (!nodegraph)
fa133e00 249 vty_out(vty, "nodegraph uninitialized\r\n");
1ab84bf3 250 else
fa133e00 251 pretty_print_graph (vty, vector_slot (nodegraph->nodes, 0), 0, argc >= 3, stack, 0);
1ab84bf3
QY
252 return CMD_SUCCESS;
253}
51e156b3 254
818a5168
DL
255DEFUN (grammar_test_dot,
256 grammar_test_dot_cmd,
257 "grammar dotfile OUTNAME",
258 GRAMMAR_STR
259 "print current graph for dot\n"
260 ".dot filename\n")
261{
262 struct graph_node *stack[MAXDEPTH];
263 struct graph_node *visited[MAXDEPTH*MAXDEPTH];
264 size_t vpos = 0;
265
266 if (!nodegraph) {
267 vty_out(vty, "nodegraph uninitialized\r\n");
268 return CMD_SUCCESS;
269 }
270 FILE *ofd = fopen(argv[2]->arg, "w");
271 if (!ofd) {
272 vty_out(vty, "%s: %s\r\n", argv[2]->arg, strerror(errno));
273 return CMD_SUCCESS;
274 }
275
276 fprintf(ofd, "digraph {\n graph [ rankdir = LR ];\n node [ fontname = \"Fira Mono\", fontsize = 9 ];\n\n");
277 pretty_print_dot (ofd, 0,
b285cf4b
DL
278 vector_slot (nodegraph->nodes, 0),
279 stack, 0, visited, &vpos);
818a5168
DL
280 fprintf(ofd, "}\n");
281 fclose(ofd);
282 return CMD_SUCCESS;
283}
284
0a22b979
DL
285struct cmd_permute_item
286{
287 char *cmd;
288 struct cmd_element *el;
289};
290
291static void
292cmd_permute_free (void *arg)
293{
294 struct cmd_permute_item *i = arg;
295 XFREE (MTYPE_TMP, i->cmd);
296 XFREE (MTYPE_TMP, i);
297}
298
299static int
300cmd_permute_cmp (void *a, void *b)
301{
302 struct cmd_permute_item *aa = a, *bb = b;
303 return strcmp (aa->cmd, bb->cmd);
304}
305
306static void
307cmd_graph_permute (struct list *out, struct graph_node **stack,
308 size_t stackpos, char *cmd)
309{
310 struct graph_node *gn = stack[stackpos];
311 struct cmd_token *tok = gn->data;
312 char *appendp = cmd + strlen(cmd);
313 size_t i, j;
314
315 if (tok->type < SPECIAL_TKN)
316 {
317 sprintf (appendp, "%s ", tok->text);
318 appendp += strlen (appendp);
319 }
320 else if (tok->type == END_TKN)
321 {
322 struct cmd_permute_item *i = XMALLOC (MTYPE_TMP, sizeof (*i));
323 i->el = ((struct graph_node *)vector_slot (gn->to, 0))->data;
324 i->cmd = XSTRDUP (MTYPE_TMP, cmd);
325 i->cmd[strlen(cmd) - 1] = '\0';
326 listnode_add_sort (out, i);
327 return;
328 }
329
330 if (++stackpos == MAXDEPTH)
331 return;
332
333 for (i = 0; i < vector_active (gn->to); i++)
334 {
335 struct graph_node *gnext = vector_slot (gn->to, i);
336 for (j = 0; j < stackpos; j++)
337 if (stack[j] == gnext)
338 break;
339 if (j != stackpos)
340 continue;
341
342 stack[stackpos] = gnext;
343 *appendp = '\0';
344 cmd_graph_permute (out, stack, stackpos, cmd);
345 }
346}
347
348static struct list *
349cmd_graph_permutations (struct graph *graph)
350{
351 char accumulate[2048] = "";
352 struct graph_node *stack[MAXDEPTH];
353
354 struct list *rv = list_new ();
355 rv->cmp = cmd_permute_cmp;
356 rv->del = cmd_permute_free;
357 stack[0] = vector_slot (graph->nodes, 0);
358 cmd_graph_permute (rv, stack, 0, accumulate);
359 return rv;
360}
361
362extern vector cmdvec;
363
364DEFUN (grammar_findambig,
365 grammar_findambig_cmd,
366 "grammar find-ambiguous [{printall|nodescan}]",
367 GRAMMAR_STR
368 "Find ambiguous commands\n"
369 "Print all permutations\n"
370 "Scan all nodes\n")
371{
372 struct list *commands;
373 struct cmd_permute_item *prev = NULL, *cur = NULL;
374 struct listnode *ln;
375 int i, printall, scan, scannode = 0;
37f9f36e 376 int ambig = 0;
0a22b979
DL
377
378 i = 0;
379 printall = argv_find (argv, argc, "printall", &i);
380 i = 0;
381 scan = argv_find (argv, argc, "nodescan", &i);
382
383 if (scan && nodegraph_free)
384 {
385 graph_delete_graph (nodegraph_free);
386 nodegraph_free = NULL;
387 }
388
389 if (!scan && !nodegraph)
390 {
391 vty_out(vty, "nodegraph uninitialized\r\n");
392 return CMD_WARNING;
393 }
394
395 do {
396 if (scan)
397 {
398 struct cmd_node *cnode = vector_slot (cmdvec, scannode++);
399 if (!cnode)
400 continue;
401 nodegraph = cnode->cmdgraph;
402 if (!nodegraph)
403 continue;
5c7571d4 404 vty_out (vty, "scanning node %d\n", scannode - 1);
0a22b979
DL
405 }
406
407 commands = cmd_graph_permutations (nodegraph);
408 prev = NULL;
409 for (ALL_LIST_ELEMENTS_RO (commands, ln, cur))
410 {
411 int same = prev && !strcmp (prev->cmd, cur->cmd);
412 if (printall && !same)
5c7571d4 413 vty_out (vty, "'%s' [%x]\n", cur->cmd, cur->el->daemon);
0a22b979
DL
414 if (same)
415 {
5c7571d4
DL
416 vty_out (vty, "'%s' AMBIGUOUS:\n", cur->cmd);
417 vty_out (vty, " %s%s '%s'\n", prev->el->name, VTYNL,
96ade3ed 418 prev->el->string);
5c7571d4 419 vty_out (vty, " %s%s '%s'\n", cur->el->name, VTYNL,
96ade3ed 420 cur->el->string);
e31b6333 421 vty_out (vty, VTYNL);
37f9f36e 422 ambig++;
0a22b979
DL
423 }
424 prev = cur;
425 }
426 list_delete (commands);
37f9f36e 427
e31b6333 428 vty_out (vty, VTYNL);
0a22b979
DL
429 } while (scan && scannode < LINK_PARAMS_NODE);
430
5c7571d4 431 vty_out (vty, "%d ambiguous commands found.\n", ambig);
37f9f36e 432
0a22b979
DL
433 if (scan)
434 nodegraph = NULL;
37f9f36e 435 return ambig == 0 ? CMD_SUCCESS : CMD_WARNING;
0a22b979
DL
436}
437
500b1a5b
QY
438DEFUN (grammar_init_graph,
439 grammar_init_graph_cmd,
fa133e00 440 "grammar init",
500b1a5b
QY
441 GRAMMAR_STR
442 "(re)initialize graph\n")
443{
af2567b6
DL
444 if (nodegraph_free)
445 graph_delete_graph (nodegraph_free);
446 nodegraph_free = NULL;
447
fa133e00 448 init_cmdgraph (vty, &nodegraph);
500b1a5b
QY
449 return CMD_SUCCESS;
450}
451
af2567b6
DL
452DEFUN (grammar_access,
453 grammar_access_cmd,
454 "grammar access (0-65535)",
455 GRAMMAR_STR
456 "access node graph\n"
457 "node number\n")
458{
459 if (nodegraph_free)
460 graph_delete_graph (nodegraph_free);
461 nodegraph_free = NULL;
462
463 struct cmd_node *cnode;
464
465 cnode = vector_slot (cmdvec, atoi (argv[2]->arg));
466 if (!cnode)
467 {
5c7571d4 468 vty_out (vty, "%% no such node\n");
af2567b6
DL
469 return CMD_WARNING;
470 }
471
5c7571d4 472 vty_out (vty, "node %d\n", (int)cnode->node);
af2567b6
DL
473 nodegraph = cnode->cmdgraph;
474 return CMD_SUCCESS;
475}
476
1ab84bf3 477/* this is called in vtysh.c to set up the testing shim */
fa133e00
DL
478void grammar_sandbox_init(void) {
479 init_cmdgraph (NULL, &nodegraph);
1eb5e8dc
QY
480
481 // install all enable elements
478bdaeb 482 install_element (ENABLE_NODE, &grammar_test_cmd);
340a2b4a 483 install_element (ENABLE_NODE, &grammar_test_show_cmd);
818a5168 484 install_element (ENABLE_NODE, &grammar_test_dot_cmd);
9d0662e0 485 install_element (ENABLE_NODE, &grammar_test_match_cmd);
eceb1066 486 install_element (ENABLE_NODE, &grammar_test_complete_cmd);
0aa2c2ff 487 install_element (ENABLE_NODE, &grammar_test_doc_cmd);
0a22b979 488 install_element (ENABLE_NODE, &grammar_findambig_cmd);
500b1a5b 489 install_element (ENABLE_NODE, &grammar_init_graph_cmd);
af2567b6 490 install_element (ENABLE_NODE, &grammar_access_cmd);
51e156b3 491}
1ab84bf3 492
fa133e00
DL
493#define item(x) { x, #x }
494struct message tokennames[] = {
495 item(WORD_TKN), // words
496 item(VARIABLE_TKN), // almost anything
497 item(RANGE_TKN), // integer range
498 item(IPV4_TKN), // IPV4 addresses
499 item(IPV4_PREFIX_TKN), // IPV4 network prefixes
500 item(IPV6_TKN), // IPV6 prefixes
501 item(IPV6_PREFIX_TKN), // IPV6 network prefixes
502
503 /* plumbing types */
0bf5b1cb
DL
504 item(FORK_TKN),
505 item(JOIN_TKN),
fa133e00
DL
506 item(START_TKN), // first token in line
507 item(END_TKN), // last token in line
56b40679 508 { 0 }
fa133e00 509};
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);
56b40679 526 vty_out(vty, "%s", lookup_msg(tokennames, tok->type, NULL));
fa133e00
DL
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 {
5c7571d4 537 vty_out (vty, " -aborting! (depth limit)\n");
fa133e00
DL
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)
e31b6333 546 vty_out (vty, VTYNL);
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)
5c7571d4 558 vty_out (vty, "--END\n");
fa133e00
DL
559 else {
560 size_t k;
561 for (k = 0; k < stackpos; k++)
562 if (stack[k] == adj) {
5c7571d4 563 vty_out (vty, "<<loop@%zu \n", k);
fa133e00
DL
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
e31b6333 572 vty_out (vty, VTYNL);
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 594
56b40679 595 fprintf(ofd, "<b>%s</b>", lookup_msg(tokennames, tok->type, NULL));
818a5168
DL
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 654 if (vty)
5c7571d4 655 vty_out (vty, "initialized graph\n");
97c45dae 656}