]> git.proxmox.com Git - mirror_frr.git/blame - lib/grammar_sandbox.c
lib: Adding GR capabilites encode and decode.
[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"
1eb5e8dc 31#include "graph.h"
0a22b979 32#include "linklist.h"
9d0662e0 33#include "command_match.h"
51e156b3
QY
34
35#define GRAMMAR_STR "CLI grammar sandbox\n"
36
fa133e00
DL
37DEFINE_MTYPE_STATIC(LIB, CMD_TOKENS, "Command desc")
38
97c45dae 39/** headers **/
d62a17ae 40void grammar_sandbox_init(void);
eb51bb9b
DL
41static void pretty_print_graph(struct vty *vty, struct graph_node *, int, int,
42 struct graph_node **, size_t);
43static void init_cmdgraph(struct vty *, struct graph **);
1ab84bf3 44
97c45dae 45/** shim interface commands **/
c17faa4b 46static struct graph *nodegraph = NULL, *nodegraph_free = NULL;
340a2b4a 47
d62a17ae 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)
45082064 55
9d0662e0
QY
56DEFUN (grammar_test,
57 grammar_test_cmd,
fa133e00 58 "grammar parse LINE...",
9d0662e0 59 GRAMMAR_STR
fa133e00 60 "parse a command\n"
9d0662e0
QY
61 "command to pass to new parser\n")
62{
d62a17ae 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;
9d0662e0
QY
87}
88
eceb1066
QY
89DEFUN (grammar_test_complete,
90 grammar_test_complete_cmd,
fa133e00 91 "grammar complete COMMAND...",
9d0662e0 92 GRAMMAR_STR
eceb1066 93 "attempt to complete input on DFA\n"
fa133e00 94 "command to complete\n")
9d0662e0 95{
d62a17ae 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
6a154c88 142 list_delete(&completions);
d62a17ae 143 cmd_free_strvec(command);
144 XFREE(MTYPE_TMP, cmdstr);
145
146 return CMD_SUCCESS;
340a2b4a
QY
147}
148
eceb1066
QY
149DEFUN (grammar_test_match,
150 grammar_test_match_cmd,
fa133e00 151 "grammar match COMMAND...",
eceb1066
QY
152 GRAMMAR_STR
153 "attempt to match input on DFA\n"
fa133e00 154 "command to match\n")
eceb1066 155{
d62a17ae 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
6a154c88 186 list_delete(&argvv);
d62a17ae 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;
eceb1066
QY
210}
211
1ab84bf3
QY
212/**
213 * Testing shim to test docstrings
214 */
215DEFUN (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{
d62a17ae 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;
1ab84bf3
QY
249}
250
251/**
252 * Debugging command to print command graph
253 */
254DEFUN (grammar_test_show,
255 grammar_test_show_cmd,
fa133e00 256 "grammar show [doc]",
1ab84bf3 257 GRAMMAR_STR
fa133e00
DL
258 "print current accumulated DFA\n"
259 "include docstrings\n")
1ab84bf3 260{
d62a17ae 261 check_nodegraph();
fa133e00 262
09f6d019 263 struct graph_node *stack[CMD_ARGC_MAX];
d62a17ae 264 pretty_print_graph(vty, vector_slot(nodegraph->nodes, 0), 0, argc >= 3,
265 stack, 0);
266 return CMD_SUCCESS;
1ab84bf3 267}
51e156b3 268
818a5168
DL
269DEFUN (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{
d62a17ae 276 check_nodegraph();
d62a17ae 277 FILE *ofd = fopen(argv[2]->arg, "w");
26fbe472 278
d62a17ae 279 if (!ofd) {
280 vty_out(vty, "%s: %s\r\n", argv[2]->arg, strerror(errno));
281 return CMD_SUCCESS;
282 }
283
26fbe472
QY
284 char *dot = cmd_graph_dump_dot(nodegraph);
285
286 fprintf(ofd, "%s", dot);
d62a17ae 287 fclose(ofd);
26fbe472
QY
288 XFREE(MTYPE_TMP, dot);
289
d62a17ae 290 return CMD_SUCCESS;
818a5168
DL
291}
292
d62a17ae 293struct cmd_permute_item {
294 char *cmd;
295 struct cmd_element *el;
0a22b979
DL
296};
297
d62a17ae 298static void cmd_permute_free(void *arg)
0a22b979 299{
d62a17ae 300 struct cmd_permute_item *i = arg;
301 XFREE(MTYPE_TMP, i->cmd);
302 XFREE(MTYPE_TMP, i);
0a22b979
DL
303}
304
d62a17ae 305static int cmd_permute_cmp(void *a, void *b)
0a22b979 306{
d62a17ae 307 struct cmd_permute_item *aa = a, *bb = b;
308 return strcmp(aa->cmd, bb->cmd);
0a22b979
DL
309}
310
d62a17ae 311static void cmd_graph_permute(struct list *out, struct graph_node **stack,
312 size_t stackpos, char *cmd)
0a22b979 313{
d62a17ae 314 struct graph_node *gn = stack[stackpos];
315 struct cmd_token *tok = gn->data;
316 char *appendp = cmd + strlen(cmd);
c683bd44 317 size_t j;
d62a17ae 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
09f6d019 331 if (++stackpos == CMD_ARGC_MAX)
d62a17ae 332 return;
333
c683bd44 334 for (size_t i = 0; i < vector_active(gn->to); i++) {
d62a17ae 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 }
0a22b979
DL
346}
347
d62a17ae 348static struct list *cmd_graph_permutations(struct graph *graph)
0a22b979 349{
d62a17ae 350 char accumulate[2048] = "";
09f6d019 351 struct graph_node *stack[CMD_ARGC_MAX];
d62a17ae 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;
0a22b979
DL
359}
360
361extern vector cmdvec;
362
363DEFUN (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{
d62a17ae 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;
996c9314
LB
401 vty_out(vty, "scanning node %d (%s)\n", scannode - 1,
402 node_names[scannode - 1]);
d62a17ae 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 }
6a154c88 423 list_delete(&commands);
d62a17ae 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;
0a22b979
DL
433}
434
500b1a5b
QY
435DEFUN (grammar_init_graph,
436 grammar_init_graph_cmd,
fa133e00 437 "grammar init",
500b1a5b
QY
438 GRAMMAR_STR
439 "(re)initialize graph\n")
440{
d62a17ae 441 if (nodegraph_free)
442 graph_delete_graph(nodegraph_free);
443 nodegraph_free = NULL;
af2567b6 444
d62a17ae 445 init_cmdgraph(vty, &nodegraph);
446 return CMD_SUCCESS;
500b1a5b
QY
447}
448
af2567b6
DL
449DEFUN (grammar_access,
450 grammar_access_cmd,
451 "grammar access (0-65535)",
452 GRAMMAR_STR
453 "access node graph\n"
454 "node number\n")
455{
d62a17ae 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;
af2567b6
DL
471}
472
1ab84bf3 473/* this is called in vtysh.c to set up the testing shim */
d62a17ae 474void 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);
51e156b3 486}
1ab84bf3 487
97f13f08
QY
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 */
eb51bb9b
DL
494static void pretty_print_graph(struct vty *vty, struct graph_node *start,
495 int level, int desc, struct graph_node **stack,
496 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 */
eb51bb9b 554static void 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}