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.
diff --git a/src/vty/command.c b/src/vty/command.c
index c84c612..7f83a5e 100644
--- a/src/vty/command.c
+++ b/src/vty/command.c
@@ -405,6 +405,145 @@
 	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 @@
 	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 @@
 
 	/* 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 @@
 	}
 	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);