]> git.proxmox.com Git - mirror_frr.git/blame - lib/grammar_sandbox.c
zebra, lib: fix the ZEBRA_INTERFACE_VRF_UPDATE zapi message
[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
b45ac5f5
DL
26#ifdef HAVE_CONFIG_H
27#include "config.h"
28#endif
29
51e156b3 30#include "command.h"
fa133e00 31#include "memory_vty.h"
1eb5e8dc 32#include "graph.h"
0a22b979 33#include "linklist.h"
9d0662e0 34#include "command_match.h"
51e156b3
QY
35
36#define GRAMMAR_STR "CLI grammar sandbox\n"
37
fa133e00
DL
38DEFINE_MTYPE_STATIC(LIB, CMD_TOKENS, "Command desc")
39
97c45dae 40/** headers **/
d62a17ae 41void grammar_sandbox_init(void);
42void pretty_print_graph(struct vty *vty, struct graph_node *, int, int,
43 struct graph_node **, size_t);
d62a17ae 44void init_cmdgraph(struct vty *, struct graph **);
1ab84bf3 45
97c45dae 46/** shim interface commands **/
af2567b6 47struct graph *nodegraph = NULL, *nodegraph_free = NULL;
340a2b4a 48
d62a17ae 49#define check_nodegraph() \
50 do { \
51 if (!nodegraph) { \
52 vty_out(vty, "nodegraph not initialized\n"); \
53 return CMD_WARNING; \
54 } \
55 } while (0)
45082064 56
9d0662e0
QY
57DEFUN (grammar_test,
58 grammar_test_cmd,
fa133e00 59 "grammar parse LINE...",
9d0662e0 60 GRAMMAR_STR
fa133e00 61 "parse a command\n"
9d0662e0
QY
62 "command to pass to new parser\n")
63{
d62a17ae 64 check_nodegraph();
65
66 int idx_command = 2;
67 // make a string from tokenized command line
68 char *command = argv_concat(argv, argc, idx_command);
69
70 // create cmd_element for parser
71 struct cmd_element *cmd =
72 XCALLOC(MTYPE_CMD_TOKENS, sizeof(struct cmd_element));
73 cmd->string = command;
74 cmd->doc =
75 "0\n1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n11\n12\n13\n14\n15\n16\n17\n18\n19\n";
76 cmd->func = NULL;
77
78 // parse the command and install it into the command graph
79 struct graph *graph = graph_new();
80 struct cmd_token *token =
81 cmd_token_new(START_TKN, CMD_ATTR_NORMAL, NULL, NULL);
82 graph_new_node(graph, token, (void (*)(void *)) & cmd_token_del);
83
84 cmd_graph_parse(graph, cmd);
85 cmd_graph_merge(nodegraph, graph, +1);
86
87 return CMD_SUCCESS;
9d0662e0
QY
88}
89
eceb1066
QY
90DEFUN (grammar_test_complete,
91 grammar_test_complete_cmd,
fa133e00 92 "grammar complete COMMAND...",
9d0662e0 93 GRAMMAR_STR
eceb1066 94 "attempt to complete input on DFA\n"
fa133e00 95 "command to complete\n")
9d0662e0 96{
d62a17ae 97 check_nodegraph();
98
99 int idx_command = 2;
100 char *cmdstr = argv_concat(argv, argc, idx_command);
101 if (!cmdstr)
102 return CMD_SUCCESS;
103
104 vector command = cmd_make_strvec(cmdstr);
105 if (!command) {
106 XFREE(MTYPE_TMP, cmdstr);
107 return CMD_SUCCESS;
108 }
109
110 // generate completions of user input
111 struct list *completions;
112 enum matcher_rv result =
113 command_complete(nodegraph, command, &completions);
114
115 // print completions or relevant error message
116 if (!MATCHER_ERROR(result)) {
117 vector comps = completions_to_vec(completions);
118 struct cmd_token *tkn;
119
120 // calculate length of longest tkn->text in completions
121 unsigned int width = 0, i = 0;
122 for (i = 0; i < vector_active(comps); i++) {
123 tkn = vector_slot(comps, i);
124 unsigned int len = strlen(tkn->text);
125 width = len > width ? len : width;
126 }
127
128 // print completions
129 for (i = 0; i < vector_active(comps); i++) {
130 tkn = vector_slot(comps, i);
131 vty_out(vty, " %-*s %s\n", width, tkn->text,
132 tkn->desc);
133 }
134
135 for (i = 0; i < vector_active(comps); i++)
136 cmd_token_del(
137 (struct cmd_token *)vector_slot(comps, i));
138 vector_free(comps);
139 } else
140 vty_out(vty, "%% No match\n");
141
142 // free resources
6a154c88 143 list_delete(&completions);
d62a17ae 144 cmd_free_strvec(command);
145 XFREE(MTYPE_TMP, cmdstr);
146
147 return CMD_SUCCESS;
340a2b4a
QY
148}
149
eceb1066
QY
150DEFUN (grammar_test_match,
151 grammar_test_match_cmd,
fa133e00 152 "grammar match COMMAND...",
eceb1066
QY
153 GRAMMAR_STR
154 "attempt to match input on DFA\n"
fa133e00 155 "command to match\n")
eceb1066 156{
d62a17ae 157 check_nodegraph();
158
159 int idx_command = 2;
160 if (argv[2]->arg[0] == '#')
161 return CMD_SUCCESS;
162
163 char *cmdstr = argv_concat(argv, argc, idx_command);
164 if (!cmdstr)
165 return CMD_SUCCESS;
166 vector command = cmd_make_strvec(cmdstr);
167 if (!command) {
168 XFREE(MTYPE_TMP, cmdstr);
169 return CMD_SUCCESS;
170 }
171
172 struct list *argvv = NULL;
173 const struct cmd_element *element = NULL;
174 enum matcher_rv result =
175 command_match(nodegraph, command, &argvv, &element);
176
177 // print completions or relevant error message
178 if (element) {
179 vty_out(vty, "Matched: %s\n", element->string);
180 struct listnode *ln;
181 struct cmd_token *token;
182 for (ALL_LIST_ELEMENTS_RO(argvv, ln, token))
183 vty_out(vty, "%s -- %s\n", token->text, token->arg);
184
185 vty_out(vty, "func: %p\n", element->func);
186
6a154c88 187 list_delete(&argvv);
d62a17ae 188 } else {
189 assert(MATCHER_ERROR(result));
190 switch (result) {
191 case MATCHER_NO_MATCH:
192 vty_out(vty, "%% Unknown command\n");
193 break;
194 case MATCHER_INCOMPLETE:
195 vty_out(vty, "%% Incomplete command\n");
196 break;
197 case MATCHER_AMBIGUOUS:
198 vty_out(vty, "%% Ambiguous command\n");
199 break;
200 default:
201 vty_out(vty, "%% Unknown error\n");
202 break;
203 }
204 }
205
206 // free resources
207 cmd_free_strvec(command);
208 XFREE(MTYPE_TMP, cmdstr);
209
210 return CMD_SUCCESS;
eceb1066
QY
211}
212
1ab84bf3
QY
213/**
214 * Testing shim to test docstrings
215 */
216DEFUN (grammar_test_doc,
217 grammar_test_doc_cmd,
218 "grammar test docstring",
219 GRAMMAR_STR
220 "Test function for docstring\n"
221 "Command end\n")
222{
d62a17ae 223 check_nodegraph();
224
225 // create cmd_element with docstring
226 struct cmd_element *cmd =
227 XCALLOC(MTYPE_CMD_TOKENS, sizeof(struct cmd_element));
228 cmd->string = XSTRDUP(
229 MTYPE_CMD_TOKENS,
230 "test docstring <example|selector follow> (1-255) end VARIABLE [OPTION|set lol] . VARARG");
231 cmd->doc = XSTRDUP(MTYPE_CMD_TOKENS,
232 "Test stuff\n"
233 "docstring thing\n"
234 "first example\n"
235 "second example\n"
236 "follow\n"
237 "random range\n"
238 "end thingy\n"
239 "variable\n"
240 "optional variable\n"
241 "optional set\n"
242 "optional lol\n"
243 "vararg!\n");
244 cmd->func = NULL;
245
246 // parse element
247 cmd_graph_parse(nodegraph, cmd);
248
249 return CMD_SUCCESS;
1ab84bf3
QY
250}
251
252/**
253 * Debugging command to print command graph
254 */
255DEFUN (grammar_test_show,
256 grammar_test_show_cmd,
fa133e00 257 "grammar show [doc]",
1ab84bf3 258 GRAMMAR_STR
fa133e00
DL
259 "print current accumulated DFA\n"
260 "include docstrings\n")
1ab84bf3 261{
d62a17ae 262 check_nodegraph();
fa133e00 263
09f6d019 264 struct graph_node *stack[CMD_ARGC_MAX];
d62a17ae 265 pretty_print_graph(vty, vector_slot(nodegraph->nodes, 0), 0, argc >= 3,
266 stack, 0);
267 return CMD_SUCCESS;
1ab84bf3 268}
51e156b3 269
818a5168
DL
270DEFUN (grammar_test_dot,
271 grammar_test_dot_cmd,
272 "grammar dotfile OUTNAME",
273 GRAMMAR_STR
274 "print current graph for dot\n"
275 ".dot filename\n")
276{
d62a17ae 277 check_nodegraph();
d62a17ae 278 FILE *ofd = fopen(argv[2]->arg, "w");
26fbe472 279
d62a17ae 280 if (!ofd) {
281 vty_out(vty, "%s: %s\r\n", argv[2]->arg, strerror(errno));
282 return CMD_SUCCESS;
283 }
284
26fbe472
QY
285 char *dot = cmd_graph_dump_dot(nodegraph);
286
287 fprintf(ofd, "%s", dot);
d62a17ae 288 fclose(ofd);
26fbe472
QY
289 XFREE(MTYPE_TMP, dot);
290
d62a17ae 291 return CMD_SUCCESS;
818a5168
DL
292}
293
d62a17ae 294struct cmd_permute_item {
295 char *cmd;
296 struct cmd_element *el;
0a22b979
DL
297};
298
d62a17ae 299static void cmd_permute_free(void *arg)
0a22b979 300{
d62a17ae 301 struct cmd_permute_item *i = arg;
302 XFREE(MTYPE_TMP, i->cmd);
303 XFREE(MTYPE_TMP, i);
0a22b979
DL
304}
305
d62a17ae 306static int cmd_permute_cmp(void *a, void *b)
0a22b979 307{
d62a17ae 308 struct cmd_permute_item *aa = a, *bb = b;
309 return strcmp(aa->cmd, bb->cmd);
0a22b979
DL
310}
311
d62a17ae 312static void cmd_graph_permute(struct list *out, struct graph_node **stack,
313 size_t stackpos, char *cmd)
0a22b979 314{
d62a17ae 315 struct graph_node *gn = stack[stackpos];
316 struct cmd_token *tok = gn->data;
317 char *appendp = cmd + strlen(cmd);
c683bd44 318 size_t j;
d62a17ae 319
320 if (tok->type < SPECIAL_TKN) {
321 sprintf(appendp, "%s ", tok->text);
322 appendp += strlen(appendp);
323 } else if (tok->type == END_TKN) {
324 struct cmd_permute_item *i = XMALLOC(MTYPE_TMP, sizeof(*i));
325 i->el = ((struct graph_node *)vector_slot(gn->to, 0))->data;
326 i->cmd = XSTRDUP(MTYPE_TMP, cmd);
327 i->cmd[strlen(cmd) - 1] = '\0';
328 listnode_add_sort(out, i);
329 return;
330 }
331
09f6d019 332 if (++stackpos == CMD_ARGC_MAX)
d62a17ae 333 return;
334
c683bd44 335 for (size_t i = 0; i < vector_active(gn->to); i++) {
d62a17ae 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 }
0a22b979
DL
347}
348
d62a17ae 349static struct list *cmd_graph_permutations(struct graph *graph)
0a22b979 350{
d62a17ae 351 char accumulate[2048] = "";
09f6d019 352 struct graph_node *stack[CMD_ARGC_MAX];
d62a17ae 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;
0a22b979
DL
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{
d62a17ae 372 struct list *commands;
373 struct cmd_permute_item *prev = NULL, *cur = NULL;
374 struct listnode *ln;
375 int i, printall, scan, scannode = 0;
376 int ambig = 0;
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 graph_delete_graph(nodegraph_free);
385 nodegraph_free = NULL;
386 }
387
388 if (!scan && !nodegraph) {
389 vty_out(vty, "nodegraph uninitialized\r\n");
390 return CMD_WARNING_CONFIG_FAILED;
391 }
392
393 do {
394 if (scan) {
395 struct cmd_node *cnode =
396 vector_slot(cmdvec, scannode++);
397 if (!cnode)
398 continue;
399 nodegraph = cnode->cmdgraph;
400 if (!nodegraph)
401 continue;
996c9314
LB
402 vty_out(vty, "scanning node %d (%s)\n", scannode - 1,
403 node_names[scannode - 1]);
d62a17ae 404 }
405
406 commands = cmd_graph_permutations(nodegraph);
407 prev = NULL;
408 for (ALL_LIST_ELEMENTS_RO(commands, ln, cur)) {
409 int same = prev && !strcmp(prev->cmd, cur->cmd);
410 if (printall && !same)
411 vty_out(vty, "'%s' [%x]\n", cur->cmd,
412 cur->el->daemon);
413 if (same) {
414 vty_out(vty, "'%s' AMBIGUOUS:\n", cur->cmd);
415 vty_out(vty, " %s\n '%s'\n", prev->el->name,
416 prev->el->string);
417 vty_out(vty, " %s\n '%s'\n", cur->el->name,
418 cur->el->string);
419 vty_out(vty, "\n");
420 ambig++;
421 }
422 prev = cur;
423 }
6a154c88 424 list_delete(&commands);
d62a17ae 425
426 vty_out(vty, "\n");
427 } while (scan && scannode < LINK_PARAMS_NODE);
428
429 vty_out(vty, "%d ambiguous commands found.\n", ambig);
430
431 if (scan)
432 nodegraph = NULL;
433 return ambig == 0 ? CMD_SUCCESS : CMD_WARNING_CONFIG_FAILED;
0a22b979
DL
434}
435
500b1a5b
QY
436DEFUN (grammar_init_graph,
437 grammar_init_graph_cmd,
fa133e00 438 "grammar init",
500b1a5b
QY
439 GRAMMAR_STR
440 "(re)initialize graph\n")
441{
d62a17ae 442 if (nodegraph_free)
443 graph_delete_graph(nodegraph_free);
444 nodegraph_free = NULL;
af2567b6 445
d62a17ae 446 init_cmdgraph(vty, &nodegraph);
447 return CMD_SUCCESS;
500b1a5b
QY
448}
449
af2567b6
DL
450DEFUN (grammar_access,
451 grammar_access_cmd,
452 "grammar access (0-65535)",
453 GRAMMAR_STR
454 "access node graph\n"
455 "node number\n")
456{
d62a17ae 457 if (nodegraph_free)
458 graph_delete_graph(nodegraph_free);
459 nodegraph_free = NULL;
460
461 struct cmd_node *cnode;
462
463 cnode = vector_slot(cmdvec, atoi(argv[2]->arg));
464 if (!cnode) {
465 vty_out(vty, "%% no such node\n");
466 return CMD_WARNING_CONFIG_FAILED;
467 }
468
469 vty_out(vty, "node %d\n", (int)cnode->node);
470 nodegraph = cnode->cmdgraph;
471 return CMD_SUCCESS;
af2567b6
DL
472}
473
1ab84bf3 474/* this is called in vtysh.c to set up the testing shim */
d62a17ae 475void grammar_sandbox_init(void)
476{
477 // install all enable elements
478 install_element(ENABLE_NODE, &grammar_test_cmd);
479 install_element(ENABLE_NODE, &grammar_test_show_cmd);
480 install_element(ENABLE_NODE, &grammar_test_dot_cmd);
481 install_element(ENABLE_NODE, &grammar_test_match_cmd);
482 install_element(ENABLE_NODE, &grammar_test_complete_cmd);
483 install_element(ENABLE_NODE, &grammar_test_doc_cmd);
484 install_element(ENABLE_NODE, &grammar_findambig_cmd);
485 install_element(ENABLE_NODE, &grammar_init_graph_cmd);
486 install_element(ENABLE_NODE, &grammar_access_cmd);
51e156b3 487}
1ab84bf3 488
97f13f08
QY
489/**
490 * Pretty-prints a graph, assuming it is a tree.
491 *
492 * @param start the node to take as the root
493 * @param level indent level for recursive calls, always pass 0
494 */
d62a17ae 495void pretty_print_graph(struct vty *vty, struct graph_node *start, int level,
496 int desc, struct graph_node **stack, size_t stackpos)
1ab84bf3 497{
d62a17ae 498 // print this node
499 char tokennum[32];
500 struct cmd_token *tok = start->data;
501
502 snprintf(tokennum, sizeof(tokennum), "%d?", tok->type);
503 vty_out(vty, "%s", lookup_msg(tokennames, tok->type, NULL));
504 if (tok->text)
505 vty_out(vty, ":\"%s\"", tok->text);
506 if (tok->varname)
507 vty_out(vty, " => %s", tok->varname);
508 if (desc)
509 vty_out(vty, " ?'%s'", tok->desc);
510 vty_out(vty, " ");
511
09f6d019 512 if (stackpos == CMD_ARGC_MAX) {
d62a17ae 513 vty_out(vty, " -aborting! (depth limit)\n");
514 return;
515 }
516 stack[stackpos++] = start;
517
518 int numto = desc ? 2 : vector_active(start->to);
519 if (numto) {
520 if (numto > 1)
521 vty_out(vty, "\n");
522 for (unsigned int i = 0; i < vector_active(start->to); i++) {
523 struct graph_node *adj = vector_slot(start->to, i);
524 // if we're listing multiple children, indent!
525 if (numto > 1)
526 for (int j = 0; j < level + 1; j++)
527 vty_out(vty, " ");
528 // if this node is a vararg, just print *
529 if (adj == start)
530 vty_out(vty, "*");
531 else if (((struct cmd_token *)adj->data)->type
532 == END_TKN)
533 vty_out(vty, "--END\n");
534 else {
535 size_t k;
536 for (k = 0; k < stackpos; k++)
537 if (stack[k] == adj) {
538 vty_out(vty, "<<loop@%zu \n",
539 k);
540 break;
541 }
542 if (k == stackpos)
543 pretty_print_graph(
544 vty, adj,
545 numto > 1 ? level + 1 : level,
546 desc, stack, stackpos);
547 }
548 }
549 } else
550 vty_out(vty, "\n");
1eb5e8dc
QY
551}
552
553/** stuff that should go in command.c + command.h */
d62a17ae 554void init_cmdgraph(struct vty *vty, struct graph **graph)
97c45dae 555{
d62a17ae 556 // initialize graph, add start noe
557 *graph = graph_new();
558 nodegraph_free = *graph;
559 struct cmd_token *token = cmd_token_new(START_TKN, 0, NULL, NULL);
560 graph_new_node(*graph, token, (void (*)(void *)) & cmd_token_del);
561 if (vty)
562 vty_out(vty, "initialized graph\n");
97c45dae 563}