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