]> git.proxmox.com Git - mirror_frr.git/blob - lib/grammar_sandbox.c
Merge pull request #5778 from ton31337/fix/add_doc_for_ebgp_connected_route_check
[mirror_frr.git] / lib / grammar_sandbox.c
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.
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 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
24 */
25
26 #ifdef HAVE_CONFIG_H
27 #include "config.h"
28 #endif
29
30 #include "command.h"
31 #include "graph.h"
32 #include "linklist.h"
33 #include "command_match.h"
34
35 #define GRAMMAR_STR "CLI grammar sandbox\n"
36
37 DEFINE_MTYPE_STATIC(LIB, CMD_TOKENS, "Command desc")
38
39 /** headers **/
40 void grammar_sandbox_init(void);
41 static void pretty_print_graph(struct vty *vty, struct graph_node *, int, int,
42 struct graph_node **, size_t);
43 static void init_cmdgraph(struct vty *, struct graph **);
44
45 /** shim interface commands **/
46 static struct graph *nodegraph = NULL, *nodegraph_free = NULL;
47
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)
55
56 DEFUN (grammar_test,
57 grammar_test_cmd,
58 "grammar parse LINE...",
59 GRAMMAR_STR
60 "parse a command\n"
61 "command to pass to new parser\n")
62 {
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;
87 }
88
89 DEFUN (grammar_test_complete,
90 grammar_test_complete_cmd,
91 "grammar complete COMMAND...",
92 GRAMMAR_STR
93 "attempt to complete input on DFA\n"
94 "command to complete\n")
95 {
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
142 list_delete(&completions);
143 cmd_free_strvec(command);
144 XFREE(MTYPE_TMP, cmdstr);
145
146 return CMD_SUCCESS;
147 }
148
149 DEFUN (grammar_test_match,
150 grammar_test_match_cmd,
151 "grammar match COMMAND...",
152 GRAMMAR_STR
153 "attempt to match input on DFA\n"
154 "command to match\n")
155 {
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
186 list_delete(&argvv);
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;
210 }
211
212 /**
213 * Testing shim to test docstrings
214 */
215 DEFUN (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 {
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;
249 }
250
251 /**
252 * Debugging command to print command graph
253 */
254 DEFUN (grammar_test_show,
255 grammar_test_show_cmd,
256 "grammar show [doc]",
257 GRAMMAR_STR
258 "print current accumulated DFA\n"
259 "include docstrings\n")
260 {
261 check_nodegraph();
262
263 struct graph_node *stack[CMD_ARGC_MAX];
264 pretty_print_graph(vty, vector_slot(nodegraph->nodes, 0), 0, argc >= 3,
265 stack, 0);
266 return CMD_SUCCESS;
267 }
268
269 DEFUN (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 {
276 check_nodegraph();
277 FILE *ofd = fopen(argv[2]->arg, "w");
278
279 if (!ofd) {
280 vty_out(vty, "%s: %s\r\n", argv[2]->arg, strerror(errno));
281 return CMD_SUCCESS;
282 }
283
284 char *dot = cmd_graph_dump_dot(nodegraph);
285
286 fprintf(ofd, "%s", dot);
287 fclose(ofd);
288 XFREE(MTYPE_TMP, dot);
289
290 return CMD_SUCCESS;
291 }
292
293 struct cmd_permute_item {
294 char *cmd;
295 struct cmd_element *el;
296 };
297
298 static void cmd_permute_free(void *arg)
299 {
300 struct cmd_permute_item *i = arg;
301 XFREE(MTYPE_TMP, i->cmd);
302 XFREE(MTYPE_TMP, i);
303 }
304
305 static int cmd_permute_cmp(void *a, void *b)
306 {
307 struct cmd_permute_item *aa = a, *bb = b;
308 return strcmp(aa->cmd, bb->cmd);
309 }
310
311 static void cmd_graph_permute(struct list *out, struct graph_node **stack,
312 size_t stackpos, char *cmd)
313 {
314 struct graph_node *gn = stack[stackpos];
315 struct cmd_token *tok = gn->data;
316 char *appendp = cmd + strlen(cmd);
317 size_t j;
318
319 if (tok->type < SPECIAL_TKN) {
320 sprintf(appendp, "%s ", tok->text);
321 appendp += strlen(appendp);
322 } else if (tok->type == END_TKN) {
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 == CMD_ARGC_MAX)
332 return;
333
334 for (size_t i = 0; i < vector_active(gn->to); i++) {
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 *cmd_graph_permutations(struct graph *graph)
349 {
350 char accumulate[2048] = "";
351 struct graph_node *stack[CMD_ARGC_MAX];
352
353 struct list *rv = list_new();
354 rv->cmp = cmd_permute_cmp;
355 rv->del = cmd_permute_free;
356 stack[0] = vector_slot(graph->nodes, 0);
357 cmd_graph_permute(rv, stack, 0, accumulate);
358 return rv;
359 }
360
361 extern vector cmdvec;
362
363 DEFUN (grammar_findambig,
364 grammar_findambig_cmd,
365 "grammar find-ambiguous [{printall|nodescan}]",
366 GRAMMAR_STR
367 "Find ambiguous commands\n"
368 "Print all permutations\n"
369 "Scan all nodes\n")
370 {
371 struct list *commands;
372 struct cmd_permute_item *prev = NULL, *cur = NULL;
373 struct listnode *ln;
374 int i, printall, scan, scannode = 0;
375 int ambig = 0;
376
377 i = 0;
378 printall = argv_find(argv, argc, "printall", &i);
379 i = 0;
380 scan = argv_find(argv, argc, "nodescan", &i);
381
382 if (scan && nodegraph_free) {
383 graph_delete_graph(nodegraph_free);
384 nodegraph_free = NULL;
385 }
386
387 if (!scan && !nodegraph) {
388 vty_out(vty, "nodegraph uninitialized\r\n");
389 return CMD_WARNING_CONFIG_FAILED;
390 }
391
392 do {
393 if (scan) {
394 struct cmd_node *cnode =
395 vector_slot(cmdvec, scannode++);
396 if (!cnode)
397 continue;
398 nodegraph = cnode->cmdgraph;
399 if (!nodegraph)
400 continue;
401 vty_out(vty, "scanning node %d (%s)\n", scannode - 1,
402 node_names[scannode - 1]);
403 }
404
405 commands = cmd_graph_permutations(nodegraph);
406 prev = NULL;
407 for (ALL_LIST_ELEMENTS_RO(commands, ln, cur)) {
408 int same = prev && !strcmp(prev->cmd, cur->cmd);
409 if (printall && !same)
410 vty_out(vty, "'%s' [%x]\n", cur->cmd,
411 cur->el->daemon);
412 if (same) {
413 vty_out(vty, "'%s' AMBIGUOUS:\n", cur->cmd);
414 vty_out(vty, " %s\n '%s'\n", prev->el->name,
415 prev->el->string);
416 vty_out(vty, " %s\n '%s'\n", cur->el->name,
417 cur->el->string);
418 vty_out(vty, "\n");
419 ambig++;
420 }
421 prev = cur;
422 }
423 list_delete(&commands);
424
425 vty_out(vty, "\n");
426 } while (scan && scannode < LINK_PARAMS_NODE);
427
428 vty_out(vty, "%d ambiguous commands found.\n", ambig);
429
430 if (scan)
431 nodegraph = NULL;
432 return ambig == 0 ? CMD_SUCCESS : CMD_WARNING_CONFIG_FAILED;
433 }
434
435 DEFUN (grammar_init_graph,
436 grammar_init_graph_cmd,
437 "grammar init",
438 GRAMMAR_STR
439 "(re)initialize graph\n")
440 {
441 if (nodegraph_free)
442 graph_delete_graph(nodegraph_free);
443 nodegraph_free = NULL;
444
445 init_cmdgraph(vty, &nodegraph);
446 return CMD_SUCCESS;
447 }
448
449 DEFUN (grammar_access,
450 grammar_access_cmd,
451 "grammar access (0-65535)",
452 GRAMMAR_STR
453 "access node graph\n"
454 "node number\n")
455 {
456 if (nodegraph_free)
457 graph_delete_graph(nodegraph_free);
458 nodegraph_free = NULL;
459
460 struct cmd_node *cnode;
461
462 cnode = vector_slot(cmdvec, atoi(argv[2]->arg));
463 if (!cnode) {
464 vty_out(vty, "%% no such node\n");
465 return CMD_WARNING_CONFIG_FAILED;
466 }
467
468 vty_out(vty, "node %d\n", (int)cnode->node);
469 nodegraph = cnode->cmdgraph;
470 return CMD_SUCCESS;
471 }
472
473 /* this is called in vtysh.c to set up the testing shim */
474 void grammar_sandbox_init(void)
475 {
476 // install all enable elements
477 install_element(ENABLE_NODE, &grammar_test_cmd);
478 install_element(ENABLE_NODE, &grammar_test_show_cmd);
479 install_element(ENABLE_NODE, &grammar_test_dot_cmd);
480 install_element(ENABLE_NODE, &grammar_test_match_cmd);
481 install_element(ENABLE_NODE, &grammar_test_complete_cmd);
482 install_element(ENABLE_NODE, &grammar_test_doc_cmd);
483 install_element(ENABLE_NODE, &grammar_findambig_cmd);
484 install_element(ENABLE_NODE, &grammar_init_graph_cmd);
485 install_element(ENABLE_NODE, &grammar_access_cmd);
486 }
487
488 /**
489 * Pretty-prints a graph, assuming it is a tree.
490 *
491 * @param start the node to take as the root
492 * @param level indent level for recursive calls, always pass 0
493 */
494 static void pretty_print_graph(struct vty *vty, struct graph_node *start,
495 int level, int desc, struct graph_node **stack,
496 size_t stackpos)
497 {
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
512 if (stackpos == CMD_ARGC_MAX) {
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");
551 }
552
553 /** stuff that should go in command.c + command.h */
554 static void init_cmdgraph(struct vty *vty, struct graph **graph)
555 {
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");
563 }