]>
Commit | Line | Data |
---|---|---|
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 |
34 | DEFINE_MTYPE_STATIC(LIB, CMD_TOKENS, "Command desc") |
35 | ||
36 | #define MAXDEPTH 64 | |
37 | ||
97c45dae | 38 | /** headers **/ |
1ab84bf3 | 39 | void |
1eb5e8dc | 40 | grammar_sandbox_init (void); |
1ab84bf3 | 41 | void |
fa133e00 | 42 | pretty_print_graph (struct vty *vty, struct graph_node *, int, int, struct graph_node **, size_t); |
818a5168 DL |
43 | static void |
44 | pretty_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 | 47 | void |
fa133e00 | 48 | init_cmdgraph (struct vty *, struct graph **); |
1ab84bf3 | 49 | |
97c45dae | 50 | /** shim interface commands **/ |
af2567b6 | 51 | struct graph *nodegraph = NULL, *nodegraph_free = NULL; |
340a2b4a | 52 | |
9d0662e0 QY |
53 | DEFUN (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 |
81 | DEFUN (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 |
139 | DEFUN (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 | */ | |
205 | DEFUN (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 | */ | |
239 | DEFUN (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 |
255 | DEFUN (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 |
285 | struct cmd_permute_item |
286 | { | |
287 | char *cmd; | |
288 | struct cmd_element *el; | |
289 | }; | |
290 | ||
291 | static void | |
292 | cmd_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 | ||
299 | static int | |
300 | cmd_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 | ||
306 | static void | |
307 | cmd_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 | ||
348 | static struct list * | |
349 | cmd_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 | ||
362 | extern vector cmdvec; | |
363 | ||
364 | DEFUN (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 |
438 | DEFUN (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 |
452 | DEFUN (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 |
478 | void 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 } |
494 | struct 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 | 517 | void |
fa133e00 | 518 | pretty_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 |
575 | static void |
576 | pretty_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 | 646 | void |
fa133e00 | 647 | init_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 | } |