aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorHolger Hans Peter Freyther <zecke@selfish.org>2011-11-18 23:14:24 +0100
committerHolger Hans Peter Freyther <zecke@selfish.org>2012-07-25 11:58:58 +0200
commit8297c819e985ba0d46752971b274b174098afceb (patch)
tree51ac1f236957dfa6c1ce45574f25c6b875eef822
parentaa5d0e88944fe3258260aedfbce9101301e35b44 (diff)
vty: Add xsd and a command that can generate the documentation.
When building the doxygen documentation do not remove the other VTY documentation files in the doc/vty folder. Create a command that can be installed to dump all nodes and commands as XML on the given VTY. Create a schema for the XML file and a XSL-T script that can merge the generated file with additional information.
-rw-r--r--.gitignore3
-rw-r--r--Makefile.am3
-rw-r--r--doc/vty/example.xml22
-rw-r--r--doc/vty/merge_doc.xsl37
-rw-r--r--doc/vty/vtydoc.xsd46
-rw-r--r--src/vty/command.c148
6 files changed, 256 insertions, 3 deletions
diff --git a/.gitignore b/.gitignore
index 3c8afd9b..b91b5aa8 100644
--- a/.gitignore
+++ b/.gitignore
@@ -61,7 +61,8 @@ utils/osmo-auc-gen
doc/codec
doc/core
-doc/vty
+doc/vty/latex
+doc/vty/html
doc/gsm
doc/html.tar
diff --git a/Makefile.am b/Makefile.am
index d0fb027a..c9b7ccd9 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -36,8 +36,7 @@ $(top_builddir)/doc/gsm/html/index.html: $(SOURCES) Doxyfile.gsm
$(DOXYGEN) Doxyfile.gsm
$(top_builddir)/doc/vty/html/index.html: $(SOURCES) Doxyfile.vty
- @rm -rf doc/vty
- mkdir -p doc/vty
+ @rm -rf doc/vty/{html,latex}
$(DOXYGEN) Doxyfile.vty
$(top_builddir)/doc/codec/html/index.html: $(SOURCES) Doxyfile.codec
diff --git a/doc/vty/example.xml b/doc/vty/example.xml
new file mode 100644
index 00000000..400c6340
--- /dev/null
+++ b/doc/vty/example.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<vtydoc xmlns="urn:osmocom:xml:libosmocore:vty:doc:1.0">
+ <!-- test a nested hierachy -->
+ <node id="mgcp" name="MGCP Node">
+ <!-- define a command -->
+ <command id="foo_cmd">
+ <doc>General docs</doc>
+ <params>
+ <param name="do" doc="Explain do" />
+ <param name="fo" doc="Explain foo" />
+ </params>
+ </command>
+ <command id="foo_cmd">
+ <doc>General docs</doc>
+ <params>
+ <param name="do" doc="Explain do" />
+ <param name="fo" doc="Explain foo" />
+ </params>
+ </command>
+
+ </node>
+</vtydoc>
diff --git a/doc/vty/merge_doc.xsl b/doc/vty/merge_doc.xsl
new file mode 100644
index 00000000..6e1bab1d
--- /dev/null
+++ b/doc/vty/merge_doc.xsl
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<xsl:transform version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+ xmlns:vty="urn:osmocom:xml:libosmocore:vty:doc:1.0">
+ <xsl:output method="xml" version="1.0" encoding="ISO-8859-1" indent="yes" />
+ <xsl:variable name="with" select="'additions.xml'" />
+
+ <xsl:template match="@*|node()">
+ <xsl:copy>
+ <xsl:apply-templates select="@*|node()" />
+ </xsl:copy>
+ </xsl:template>
+
+
+ <!-- Copy the name of the node -->
+ <xsl:template match="vty:node">
+ <xsl:copy>
+ <xsl:apply-templates select="@*|node()" />
+ <xsl:variable name="info" select="document($with)/vty:vtydoc/vty:node[@id=current()/@id]/." />
+ <xsl:for-each select="$info/vty:name">
+ <xsl:copy-of select="." />
+ </xsl:for-each>
+ </xsl:copy>
+ </xsl:template>
+
+
+ <!-- Copy command and add nodes -->
+ <xsl:template match="vty:command">
+ <xsl:copy>
+ <xsl:apply-templates select="@*|node()" />
+ <xsl:variable name="info" select="document($with)/vty:vtydoc/vty:node[@id=current()/../@id]/vty:command[@id=current()/@id]/." />
+ <xsl:for-each select="$info/*">
+ <xsl:copy-of select="." />
+ </xsl:for-each>
+ </xsl:copy>
+ </xsl:template>
+</xsl:transform>
+
diff --git a/doc/vty/vtydoc.xsd b/doc/vty/vtydoc.xsd
new file mode 100644
index 00000000..53a67a36
--- /dev/null
+++ b/doc/vty/vtydoc.xsd
@@ -0,0 +1,46 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<xs:schema
+ xmlns="urn:osmocom:xml:libosmocore:vty:doc:1.0"
+ xmlns:xs="http://www.w3.org/2001/XMLSchema"
+ targetNamespace="urn:osmocom:xml:libosmocore:vty:doc:1.0"
+ elementFormDefault="qualified"
+ attributeFormDefault="unqualified">
+
+ <xs:complexType name="ParamType">
+ <xs:attribute name="name" type="xs:string" use="required" />
+ <xs:attribute name="doc" type="xs:string" use="required" />
+ </xs:complexType>
+
+ <xs:complexType name="ParamsType">
+ <xs:sequence>
+ <xs:element name="param" type="ParamType" maxOccurs="unbounded" />
+ </xs:sequence>
+ </xs:complexType>
+
+ <xs:complexType name="CommandType">
+ <xs:sequence>
+ <xs:element name="doc" type="xs:string" minOccurs="0" maxOccurs="1" />
+ <xs:element name="params" type="ParamsType" minOccurs="1" maxOccurs="1"/>
+ <xs:element name="enter" type="xs:string" minOccurs="0" maxOccurs="unbounded" />
+ </xs:sequence>
+ <xs:attribute name="id" type="xs:string" use="required" />
+ </xs:complexType>
+
+ <xs:complexType name="NodeType">
+ <xs:sequence>
+ <xs:element name="command" type="CommandType" minOccurs="0" maxOccurs="unbounded"/>
+ </xs:sequence>
+ <xs:attribute name="id" type="xs:anyURI"/>
+ <xs:attribute name="name" type="xs:string"/>
+ </xs:complexType>
+
+ <!-- the main entry -->
+ <xs:element name="vtydoc">
+ <xs:complexType>
+ <xs:sequence>
+ <xs:element name="node" type="NodeType" minOccurs="0" maxOccurs="unbounded"/>
+ </xs:sequence>
+ </xs:complexType>
+ </xs:element>
+</xs:schema>
+
diff --git a/src/vty/command.c b/src/vty/command.c
index c84c612d..7f83a5e4 100644
--- a/src/vty/command.c
+++ b/src/vty/command.c
@@ -405,6 +405,145 @@ const char *cmd_prompt(enum node_type node)
return cnode->prompt;
}
+static char *xml_escape(const char *inp)
+{
+ int _strlen;
+ char *out, *out_ptr;
+ int len = 0, i, j;
+
+ if (!inp)
+ return NULL;
+ _strlen = strlen(inp);
+
+ for (i = 0; i < _strlen; ++i) {
+ switch (inp[i]) {
+ case '"':
+ len += 6;
+ break;
+ case '\'':
+ len += 6;
+ break;
+ case '<':
+ len += 4;
+ break;
+ case '>':
+ len += 4;
+ break;
+ case '&':
+ len += 5;
+ break;
+ default:
+ len += 1;
+ break;
+ }
+ }
+
+ out = talloc_size(NULL, len + 1);
+ if (!out)
+ return NULL;
+
+ out_ptr = out;
+
+#define ADD(out, str) \
+ for (j = 0; j < strlen(str); ++j) \
+ *(out++) = str[j];
+
+ for (i = 0; i < _strlen; ++i) {
+ switch (inp[i]) {
+ case '"':
+ ADD(out_ptr, "&quot;");
+ break;
+ case '\'':
+ ADD(out_ptr, "&apos;");
+ break;
+ case '<':
+ ADD(out_ptr, "&lt;");
+ break;
+ case '>':
+ ADD(out_ptr, "&gt;");
+ break;
+ case '&':
+ ADD(out_ptr, "&amp;");
+ break;
+ default:
+ *(out_ptr++) = inp[i];
+ break;
+ }
+ }
+
+#undef ADD
+
+ out_ptr[0] = '\0';
+ return out;
+}
+
+/*
+ * Write one cmd_element as XML to the given VTY.
+ */
+static int vty_dump_element(struct cmd_element *cmd, struct vty *vty)
+{
+ char *xml_string = xml_escape(cmd->string);
+
+ vty_out(vty, " <command id='%s'>%s", xml_string, VTY_NEWLINE);
+ vty_out(vty, " <params>%s", VTY_NEWLINE);
+
+ int j;
+ for (j = 0; j < vector_count(cmd->strvec); ++j) {
+ vector descvec = vector_slot(cmd->strvec, j);
+ int i;
+ for (i = 0; i < vector_active(descvec); ++i) {
+ char *xml_param, *xml_doc;
+ struct desc *desc = vector_slot(descvec, i);
+ if (desc == NULL)
+ continue;
+
+ xml_param = xml_escape(desc->cmd);
+ xml_doc = xml_escape(desc->str);
+ vty_out(vty, " <param name='%s' doc='%s' />%s",
+ xml_param, xml_doc, VTY_NEWLINE);
+ talloc_free(xml_param);
+ talloc_free(xml_doc);
+ }
+ }
+
+ vty_out(vty, " </params>%s", VTY_NEWLINE);
+ vty_out(vty, " </command>%s", VTY_NEWLINE);
+
+ talloc_free(xml_string);
+ return 0;
+}
+
+/*
+ * Dump all nodes and commands associated with a given node as XML to the VTY.
+ */
+static int vty_dump_nodes(struct vty *vty)
+{
+ int i, j;
+
+ vty_out(vty, "<vtydoc xmlns='urn:osmocom:xml:libosmocore:vty:doc:1.0'>%s", VTY_NEWLINE);
+
+ for (i = 0; i < vector_active(cmdvec); ++i) {
+ struct cmd_node *cnode;
+ cnode = vector_slot(cmdvec, i);
+ if (!cnode)
+ continue;
+
+ vty_out(vty, " <node id='%d'>%s", i, VTY_NEWLINE);
+
+ for (j = 0; j < vector_active(cnode->cmd_vector); ++j) {
+ struct cmd_element *elem;
+ elem = vector_slot(cnode->cmd_vector, j);
+ vty_dump_element(elem, vty);
+ }
+
+ vty_out(vty, " </node>%s", VTY_NEWLINE);
+ }
+
+ vty_out(vty, "</vtydoc>%s", VTY_NEWLINE);
+
+ return 0;
+}
+
/*! \brief Install a command into a node
* \param[in] ntype Node Type
* \param[cmd] element to be installed
@@ -2232,6 +2371,13 @@ DEFUN(show_version,
return CMD_SUCCESS;
}
+DEFUN(show_online_help,
+ show_online_help_cmd, "show online-help", SHOW_STR "Online help\n")
+{
+ vty_dump_nodes(vty);
+ return CMD_SUCCESS;
+}
+
/* Help display function for all node. */
gDEFUN(config_help,
config_help_cmd, "help", "Description of the interactive help system\n")
@@ -3269,6 +3415,7 @@ void cmd_init(int terminal)
/* Each node's basic commands. */
install_element(VIEW_NODE, &show_version_cmd);
+ install_element(VIEW_NODE, &show_online_help_cmd);
if (terminal) {
install_element(VIEW_NODE, &config_list_cmd);
install_element(VIEW_NODE, &config_exit_cmd);
@@ -3288,6 +3435,7 @@ void cmd_init(int terminal)
}
install_element (ENABLE_NODE, &show_startup_config_cmd);
install_element(ENABLE_NODE, &show_version_cmd);
+ install_element(ENABLE_NODE, &show_online_help_cmd);
if (terminal) {
install_element(ENABLE_NODE, &config_terminal_length_cmd);