]>
git.proxmox.com Git - mirror_frr.git/blob - tools/xml2cli.pl
3 ## Parse a XML file containing a tree-like representation of Quagga CLI
4 ## commands and generate a file with:
6 ## - a DEFUN function for each command;
7 ## - an initialization function.
10 ## Copyright (C) 2012 Renato Westphal <renatow@digistar.com.br>
11 ## This file is part of GNU Zebra.
13 ## GNU Zebra is free software; you can redistribute it and/or modify it
14 ## under the terms of the GNU General Public License as published by the
15 ## Free Software Foundation; either version 2, or (at your option) any
18 ## GNU Zebra is distributed in the hope that it will be useful, but
19 ## WITHOUT ANY WARRANTY; without even the implied warranty of
20 ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
21 ## General Public License for more details.
23 ## You should have received a copy of the GNU General Public License
24 ## along with GNU Zebra; see the file COPYING. If not, write to the Free
25 ## Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
33 use File
::Basename
qw(fileparse);
41 "ipv4m" => "A.B.C.D/M",
43 "ipv6m" => "X:X::X:X/M",
44 "mtu" => "<1500-9180>",
46 "rd" => "ASN:nn_or_IP-address:nn",
47 "asn" => "<1-4294967295>",
48 "community" => "AA:NN",
51 "disc_time" => "<1-65535>",
52 "session_time" => "<15-65535>",
53 "pwid" => "<1-4294967295>",
57 # parse options node and store the corresponding information
58 # into a global hash of hashes
63 my $options_name = $xml_node->findvalue('./@name');
64 if (not $options_name) {
65 die('error: "options" node without "name" attribute');
69 $::options
{$options_name}{'cmdstr'} = "";
70 $::options
{$options_name}{'help'} = "";
72 my @children = $xml_node->getChildnodes();
73 foreach my $child(@children) {
74 # skip comments, random text, etc
75 if ($child->getType() != XML_ELEMENT_NODE
) {
79 # check for error/special conditions
80 if ($child->getName() ne "option") {
81 die('error: invalid node type: "' . $child->getName() . '"');
84 my $name = $child->findvalue('./@name');
85 my $input = $child->findvalue('./@input');
86 my $help = $child->findvalue('./@help');
88 $name = $::input_strs
{$input};
91 push (@cmdstr, $name);
92 $::options
{$options_name}{'help'} .= "\n \"" . $help . "\\n\"";
94 $::options
{$options_name}{'cmdstr'} = "(" . join('|', @cmdstr) . ")";
97 # given a subtree, replace all the corresponding include nodes by
99 sub subtree_replace_includes
{
102 my $subtree_name = $subtree->findvalue('./@name');
103 if (not $subtree_name) {
104 die("subtree without \"name\" attribute");
107 my $query = "//include[\@subtree='$subtree_name']";
108 foreach my $include_node($::xml-
>findnodes($query)) {
109 my @children = $subtree->getChildnodes();
110 foreach my $child(reverse @children) {
111 my $include_node_parent = $include_node->getParentNode();
112 $include_node_parent->insertAfter($child->cloneNode(1),
115 $include_node->unbindNode();
117 $subtree->unbindNode();
120 # generate arguments for a given command
121 sub generate_arguments
{
127 $arguments .= " struct vty_arg *args[] =\n";
128 $arguments .= " {\n";
129 for (my $i = 0; $i < @nodes; $i++) {
130 my %node = %{$nodes[$i]};
133 if (not $node{'arg'}) {
138 # for input and select nodes, the value of the argument is an
139 # argv[] element. for the other types of nodes, the value of the
140 # argument is the name of the node
141 if ($node{'input'} or $node{'type'} eq "select") {
142 $arg_value = "argv[" . $argc++ . "]";
144 $arg_value = '"' . $node{'name'} . '"';
147 if ($node{'input'} and $node{'input'} eq "line") {
148 # arguments of the type 'line' may have multiple spaces (i.e
149 # they don't fit into a single argv[] element). to properly
150 # handle these arguments, we need to provide direct access
151 # to the argv[] array and the argc variable.
152 my $argc_str = "argc" . (($argc > 1) ?
" - " . ($argc - 1) : "");
153 my $argv_str = "argv" . (($argc > 1) ?
" + " . ($argc - 1) : "");
154 $arguments .= " &(struct vty_arg) { "
155 . ".name = \"" . $node{'arg'} . "\", "
156 . ".argc = $argc_str, "
157 . ".argv = $argv_str },\n";
159 # common case - each argument has a name and a single value
160 $arguments .= " &(struct vty_arg) { "
161 . ".name = \"" . $node{'arg'} . "\", "
162 . ".value = " . $arg_value . " },\n";
165 $arguments .= " NULL\n";
166 $arguments .= " };\n";
168 # handle special case
170 return " struct vty_arg *args[] = { NULL };\n";
185 for (my $i = 0; $i < @nodes; $i++) {
186 my %node = %{$nodes[$i]};
187 if ($node{'input'}) {
188 $funcname .= $node{'input'} . " ";
189 $cmdstr .= $::input_strs
{$node{'input'}} . " ";
190 $helpstr .= "\n \"" . $node{'help'} . "\\n\"";
191 } elsif ($node{'type'} eq "select") {
192 my $options_name = $node{'options'};
193 $funcname .= $options_name . " ";
194 $cmdstr .= $::options
{$options_name}{'cmdstr'} . " ";
195 $helpstr .= $::options
{$options_name}{'help'};
197 $funcname .= $node{'name'} . " ";
198 $cmdstr .= $node{'name'} . " ";
199 $helpstr .= "\n \"" . $node{'help'} . "\\n\"";
202 # update the command string
203 if ($node{'function'} ne "inherited") {
204 $function = $node{'function'};
209 $funcname =~ s/\s+$//;
212 $funcname = lc($funcname);
214 $funcname =~ tr/ /_/;
216 $funcname =~ tr/-/_/;
218 $funcname = $::cmdprefix
. '_' . $funcname;
221 $cmdname = $funcname . "_cmd";
223 # don't generate same command more than once
224 if ($::commands
{$cmdname}) {
227 $::commands
{$cmdname} = "1";
229 print STDOUT
"DEFUN (" . $funcname . ",\n"
230 . " " . $cmdname . ",\n"
231 . " \"" . $cmdstr . "\","
234 . generate_arguments
(@nodes)
235 . " return " . $function . " (vty, args);\n"
241 # parse tree node (recursive function)
244 my $xml_node = $_[0];
245 my @nodes = @{$_[1]};
246 my $tree_name = $_[2];
248 # hash containing all the node attributes
250 $node{'type'} = $xml_node->getName();
252 # check for error/special conditions
253 if ($node{'type'} eq "tree") {
256 if ($node{'type'} eq "include") {
257 die('error: can not include "'
258 . $xml_node->findvalue('./@subtree') . '"');
260 if (not $node{'type'} ~~ [qw(option select)]) {
261 die('error: invalid node type: "' . $node{'type'} . '"');
263 if ($node{'type'} eq "select") {
264 my $options_name = $xml_node->findvalue('./@options');
265 if (not $options_name) {
266 die('error: "select" node without "name" attribute');
268 if (not $::options
{$options_name}) {
269 die('error: can not find options');
271 $node{'options'} = $options_name;
274 # get node attributes
275 $node{'name'} = $xml_node->findvalue('./@name');
276 $node{'input'} = $xml_node->findvalue('./@input');
277 $node{'arg'} = $xml_node->findvalue('./@arg');
278 $node{'help'} = $xml_node->findvalue('./@help');
279 $node{'function'} = $xml_node->findvalue('./@function');
280 $node{'ifdef'} = $xml_node->findvalue('./@ifdef');
283 push (@nodes, \
%node);
286 if ($node{'function'}) {
287 my $cmdname = generate_code
(@nodes);
288 push (@{$::trees
{$tree_name}}, [0, $cmdname, 0]);
291 if ($node{'ifdef'}) {
292 push (@{$::trees
{$tree_name}}, [$node{'ifdef'}, 0, 0]);
296 # recursively process child nodes
297 my @children = $xml_node->getChildnodes();
298 foreach my $child(@children) {
299 # skip comments, random text, etc
300 if ($child->getType() != XML_ELEMENT_NODE
) {
303 parse_tree
($child, \
@nodes, $tree_name);
306 if ($node{'ifdef'}) {
307 push (@{$::trees
{$tree_name}}, [0, 0, $node{'ifdef'}]);
313 my $xml_node = $_[0];
315 my $node_name = $xml_node->findvalue('./@name');
316 if (not $node_name) {
317 die('missing the "name" attribute');
320 my $install = $xml_node->findvalue('./@install');
321 my $config_write = $xml_node->findvalue('./@config_write');
322 if ($install and $install eq "1") {
323 print " install_node (&" .lc( $node_name) . "_node, " . $config_write . ");\n";
326 my $install_default = $xml_node->findvalue('./@install_default');
327 if ($install_default and $install_default eq "1") {
328 print " install_default (" . $node_name . "_NODE);\n";
331 my @children = $xml_node->getChildnodes();
332 foreach my $child(@children) {
333 # skip comments, random text, etc
334 if ($child->getType() != XML_ELEMENT_NODE
) {
338 if ($child->getName() ne "include") {
339 die('error: invalid node type: "' . $child->getName() . '"');
341 my $tree_name = $child->findvalue('./@tree');
342 if (not $tree_name) {
343 die('missing the "tree" attribute');
346 foreach my $entry (@{$::trees
{$tree_name}}) {
347 my ($ifdef, $cmdname, $endif) = @{$entry};
350 print ("#ifdef " . $ifdef . "\n");
354 print " install_element (" . $node_name . "_NODE, &" . $cmdname . ");\n";
358 print ("#endif /* " . $endif . " */\n");
364 # parse command-line arguments
365 if (not getopts
('d')) {
366 die("Usage: xml2cli.pl [-d] FILE\n");
370 # initialize the XML parser
371 my $parser = new XML
::LibXML
;
372 $parser->keep_blanks(0);
375 $::xml
= $parser->parse_file($file);
376 my $xmlroot = $::xml-
>getDocumentElement();
377 if ($xmlroot->getName() ne "file") {
378 die('XML root element name must be "file"');
381 # read file attributes
382 my $init_function = $xmlroot->findvalue('./@init');
383 if (not $init_function) {
384 die('missing the "init" attribute in the "file" node');
386 $::cmdprefix
= $xmlroot->findvalue('./@cmdprefix');
387 if (not $::cmdprefix
) {
388 die('missing the "cmdprefix" attribute in the "file" node');
390 my $header = $xmlroot->findvalue('./@header');
392 die('missing the "header" attribute in the "file" node');
395 # generate source header
396 print STDOUT
"/* Auto-generated from " . fileparse
($file) . ". */\n"
397 . "/* Do not edit! */\n\n"
398 . "#include <zebra.h>\n\n"
399 . "#include \"command.h\"\n"
400 . "#include \"vty.h\"\n"
401 . "#include \"$header\"\n\n";
404 foreach my $options($::xml->findnodes("/file/options
")) {
405 parse_options($options);
408 # replace include nodes by the corresponding subtrees
409 foreach my $subtree(reverse $::xml->findnodes("/file/subtree
")) {
410 subtree_replace_includes($subtree);
414 foreach my $tree($::xml->findnodes("/file/tree
")) {
416 my $tree_name = $tree->findvalue('./@name');
417 parse_tree($tree, \@nodes, $tree_name);
420 # install function header
421 print STDOUT "void
\n"
422 . $init_function . " (void
)\n"
426 foreach my $node($::xml->findnodes("/file/node
")) {
430 # closing braces for the install function
433 # print to stderr the expanded XML file if the debug flag (-d) is given
435 print STDERR $::xml->toString(1);