--- /dev/null
+#include <iostream>
+#include <fstream>
+#include <sstream>
+#include <vector>
+#include <map>
+
+#include <exceptions/exceptions.h>
+#include <cc/data.h>
+
+using namespace std;
+using namespace isc;
+using namespace isc::data;
+
+
+
+class DocGen {
+public:
+
+ const string OUTPUT = "../guide/api.xml";
+
+ void loadFiles(const vector<string>& files) {
+
+ map <string, ElementPtr> commands;
+
+ int cnt = 0;
+
+ try {
+ for (auto f : files) {
+ string cmd = f.substr(0, f.find("."));
+
+ cout << "Loading description of command " << cmd;
+ ElementPtr x = Element::fromJSONFile(f, false);
+ cout << ".";
+
+ sanityCheck(f, x);
+ cout << ".";
+ cmds_.insert(make_pair(cmd, x));
+ cout << "loaded" << endl;
+
+ cnt++;
+ }
+ } catch (const Unexpected& e) {
+ isc_throw(Unexpected, e.what() << " while processing "
+ << cnt + 1 << " file out of " << files.size());
+ }
+
+ cout << "Loaded " << cmds_.size() << " commands out of " << files.size()
+ << " file(s)" << endl;
+ }
+
+
+ void requireString(const ElementPtr& x, const string& name, const string& fname) {
+ if (!x->contains(name)) {
+ isc_throw(Unexpected, "Mandatory '" + name + " field missing while "
+ "processing file " + fname);
+ }
+ if (x->get(name)->getType() != Element::string) {
+ isc_throw(BadValue, "'" + name + " field is present, but is not a string"
+ " in file " + fname);
+ }
+ if (x->get(name)->stringValue().empty()) {
+ isc_throw(BadValue, "'" + name + " field is present, is a string, but is "
+ "empty in file " + fname);
+ }
+ }
+
+ void requireList(const ElementPtr& x, const string& name, const string& fname) {
+ if (!x->contains(name)) {
+ isc_throw(Unexpected, "Mandatory '" + name + " field missing while "
+ "processing file " + fname);
+ }
+ if (x->get(name)->getType() != Element::list) {
+ isc_throw(BadValue, "'" + name + " field is present, but is not a list "
+ "in file " + fname);
+ }
+
+ ConstElementPtr l = x->get(name);
+
+ if (l->size() == 0) {
+ isc_throw(BadValue, "'" + name + " field is a list, but is empty in file "
+ + fname);
+ }
+
+ // todo: check that every element is a string
+ }
+
+ void sanityCheck(const string& fname, const ElementPtr& x) {
+ requireString(x, "name", fname);
+ requireString(x, "brief", fname);
+ requireList (x, "support", fname);
+ requireString(x, "avail", fname);
+ requireString(x, "brief", fname);
+ requireString(x, "cmd-syntax", fname);
+ requireString(x, "cmd-comment", fname);
+ requireString(x, "resp-comment", fname);
+ requireString(x, "resp-syntax", fname);
+ }
+
+ void generateCopyright(stringstream& f) {
+ f << "<!--" << endl;
+ f << " - Copyright (C) 2018 Internet Systems Consortium, Inc. (\"ISC\")" << endl;
+ f << " -" << endl;
+ f << " - This Source Code Form is subject to the terms of the Mozilla Public" << endl;
+ f << " - License, v. 2.0. If a copy of the MPL was not distributed with this" << endl;
+ f << " - file, You can obtain one at http://mozilla.org/MPL/2.0/." << endl;
+ f << " -->" << endl;
+ f << endl;
+ f << "<!-- autogenerated using cmd_docgen. Do not edit by hand! -->" << endl;
+ }
+
+ void generateOutput() {
+
+ stringstream f;
+
+ generateCopyright(f);
+
+ f << "<chapter xmlns=\"http://docbook.org/ns/docbook\" version=\"5.0\" xml:id=\"api\">"
+ << endl;
+ f << " <title>Management API Reference</title>" << endl;
+
+ // Generate initial list of commands
+ f << " <para>Kea currently supports " << cmds_.size() << " commands:" << endl
+ << " <orderedlist>" << endl;
+
+ for (auto cmd : cmds_) {
+ f << " <listitem><simpara>" << cmd.first << "</simpara></listitem>" << endl;
+ }
+
+ f << " </orderedlist>" << endl;
+ f << " </para>" << endl;
+
+ // Generate actual commands references.
+ generateCommands(f);
+
+ f << "</chapter>" << endl;
+
+ cout << "----------------" << endl;
+ ofstream file(OUTPUT.c_str(), ofstream::trunc);
+ file << f.str();
+ cout << f.str();
+ cout << "----------------" << endl;
+ }
+
+ void generateCommands(stringstream& f){
+
+ for (auto cmd : cmds_) {
+ f << "<!-- start of " << cmd.first << " -->" << endl;
+ f << "<section xml:id=\"reference-" << cmd.first << "\">" << endl;
+ f << "<title>" << cmd.first << " reference</title>" << endl;
+ generateCommand(f, cmd.second);
+ f << "</section>" << endl;
+ f << "<!-- end of " << cmd.first << " -->" << endl;
+ f << endl;
+ }
+ }
+
+void replaceAll(std::string& str, const std::string& from, const std::string& to) {
+ if(from.empty())
+ return;
+ size_t start_pos = 0;
+ while((start_pos = str.find(from, start_pos)) != std::string::npos) {
+ str.replace(start_pos, from.length(), to);
+ start_pos += to.length();
+ }
+}
+
+string escapeString(string txt) {
+
+ replaceAll(txt, "<", "<");
+ replaceAll(txt, ">", ">");
+ return (txt);
+}
+
+void generateCommand(stringstream& f, const ElementPtr& cmd) {
+
+ // command overview
+ f << "<para><command>" << cmd->get("name")->stringValue() << "</command> - "
+ << cmd->get("brief")->stringValue() << "</para>" << endl << endl;
+
+ // command can be issued to the following daemons
+ f << "<para>Supported by: ";
+ ConstElementPtr daemons = cmd->get("support");
+ for (int i = 0; i < daemons->size(); i++) {
+ if (i) {
+ f << ", ";
+ }
+
+ f << daemons->get(i)->stringValue();
+ }
+ f << "</para>" << endl << endl;
+
+ // availability
+ f << "<para>Availability: " << cmd->get("avail")->stringValue() << "</para>"
+ << endl << endl;
+
+ // description and examples
+ f << "<para>Description and examples: See <xref linkend=\"cmd-"
+ << cmd->get("name")->stringValue() << "\"/></para>" << endl << endl;
+
+ // Command syntax:
+ f << "<para>Command syntax:" << endl
+ << " <screen>" << escapeString(cmd->get("cmd-syntax")->stringValue())
+ << "</screen>"
+ << endl
+ << cmd->get("cmd-comment")->stringValue() << "</para>" << endl << endl;
+
+ // Response syntax
+ f << "<para>Response syntax:" << endl
+ << " <screen>" << escapeString(cmd->get("resp-syntax")->stringValue())
+ << "</screen>"
+ << endl
+ << cmd->get("resp-comment")->stringValue() << "</para>" << endl << endl;
+}
+
+ map<string, ElementPtr> cmds_;
+};
+
+int main(int argc, const char*argv[]) {
+
+ vector<string> files;
+
+ for (int i = 1; i < argc; i++) {
+ files.push_back(string(argv[i]));
+ }
+
+ cout << "Loading " << files.size() << " files(s)." << endl;
+
+ try {
+ DocGen doc_gen;
+
+ doc_gen.loadFiles(files);
+
+ doc_gen.generateOutput();
+ } catch (const exception& e) {
+ cerr << "ERROR: " << e.what() << endl;
+ }
+
+ return (0);
+}