summaryrefslogtreecommitdiffstats
path: root/ml/dlib/tools/htmlify
diff options
context:
space:
mode:
Diffstat (limited to 'ml/dlib/tools/htmlify')
-rw-r--r--ml/dlib/tools/htmlify/CMakeLists.txt31
-rw-r--r--ml/dlib/tools/htmlify/htmlify.cpp632
-rw-r--r--ml/dlib/tools/htmlify/to_xml.cpp1599
-rw-r--r--ml/dlib/tools/htmlify/to_xml.h22
-rw-r--r--ml/dlib/tools/htmlify/to_xml_example/bigminus.gifbin0 -> 91 bytes
-rw-r--r--ml/dlib/tools/htmlify/to_xml_example/bigplus.gifbin0 -> 99 bytes
-rw-r--r--ml/dlib/tools/htmlify/to_xml_example/example.xml8
-rw-r--r--ml/dlib/tools/htmlify/to_xml_example/minus.gifbin0 -> 56 bytes
-rw-r--r--ml/dlib/tools/htmlify/to_xml_example/output.xml49
-rw-r--r--ml/dlib/tools/htmlify/to_xml_example/plus.gifbin0 -> 59 bytes
-rw-r--r--ml/dlib/tools/htmlify/to_xml_example/stylesheet.xsl354
-rw-r--r--ml/dlib/tools/htmlify/to_xml_example/test.cpp78
12 files changed, 2773 insertions, 0 deletions
diff --git a/ml/dlib/tools/htmlify/CMakeLists.txt b/ml/dlib/tools/htmlify/CMakeLists.txt
new file mode 100644
index 000000000..02cae2172
--- /dev/null
+++ b/ml/dlib/tools/htmlify/CMakeLists.txt
@@ -0,0 +1,31 @@
+#
+# This is a CMake makefile. You can find the cmake utility and
+# information about it at http://www.cmake.org
+#
+
+cmake_minimum_required(VERSION 2.8.12)
+
+# create a variable called target_name and set it to the string "htmlify"
+set (target_name htmlify)
+
+project(${target_name})
+
+add_subdirectory(../../dlib dlib_build)
+
+# add all the cpp files we want to compile to this list. This tells
+# cmake that they are part of our target (which is the executable named htmlify)
+add_executable(${target_name}
+ htmlify.cpp
+ to_xml.cpp
+ )
+
+# Tell cmake to link our target executable to dlib.
+target_link_libraries(${target_name} dlib::dlib )
+
+
+
+install(TARGETS ${target_name}
+ RUNTIME DESTINATION bin
+ )
+
+
diff --git a/ml/dlib/tools/htmlify/htmlify.cpp b/ml/dlib/tools/htmlify/htmlify.cpp
new file mode 100644
index 000000000..e822a5aaa
--- /dev/null
+++ b/ml/dlib/tools/htmlify/htmlify.cpp
@@ -0,0 +1,632 @@
+#include <fstream>
+#include <iostream>
+#include <string>
+
+
+#include "dlib/cpp_pretty_printer.h"
+#include "dlib/cmd_line_parser.h"
+#include "dlib/queue.h"
+#include "dlib/misc_api.h"
+#include "dlib/dir_nav.h"
+#include "to_xml.h"
+
+
+const char* VERSION = "3.5";
+
+using namespace std;
+using namespace dlib;
+
+typedef cpp_pretty_printer::kernel_1a cprinter;
+typedef cpp_pretty_printer::kernel_2a bprinter;
+typedef dlib::map<string,string>::kernel_1a map_string_to_string;
+typedef dlib::set<string>::kernel_1a set_of_string;
+typedef queue<file>::kernel_1a queue_of_files;
+typedef queue<directory>::kernel_1a queue_of_dirs;
+
+void print_manual (
+);
+/*!
+ ensures
+ - prints detailed information about this program.
+!*/
+
+void htmlify (
+ const map_string_to_string& file_map,
+ bool colored,
+ bool number_lines,
+ const std::string& title
+);
+/*!
+ ensures
+ - for all valid out_file:
+ - the file out_file is the html transformed version of
+ file_map[out_file]
+ - if (number_lines) then
+ - the html version will have numbered lines
+ - if (colored) then
+ - the html version will have colors
+ - title will be the first part of the HTML title in the output file
+!*/
+
+void htmlify (
+ istream& in,
+ ostream& out,
+ const std::string& title,
+ bool colored,
+ bool number_lines
+);
+/*!
+ ensures
+ - transforms in into html with the given title and writes it to out.
+ - if (number_lines) then
+ - the html version of in will have numbered lines
+ - if (colored) then
+ - the html version of in will have colors
+!*/
+
+void add_files (
+ const directory& dir,
+ const std::string& out_dir,
+ map_string_to_string& file_map,
+ bool flatten,
+ bool cat,
+ const set_of_string& filter,
+ unsigned long search_depth,
+ unsigned long cur_depth = 0
+);
+/*!
+ ensures
+ - searches the directory dir for files matching the filter and adds them
+ to the file_map. only looks search_depth deep.
+!*/
+
+int main(int argc, char** argv)
+{
+ if (argc == 1)
+ {
+ cout << "\nTry the -h option for more information.\n";
+ return 0;
+ }
+
+ string file;
+ try
+ {
+ command_line_parser parser;
+ parser.add_option("b","Pretty print in black and white. The default is to pretty print in color.");
+ parser.add_option("n","Number lines.");
+ parser.add_option("h","Displays this information.");
+ parser.add_option("index","Create an index.");
+ parser.add_option("v","Display version.");
+ parser.add_option("man","Display the manual.");
+ parser.add_option("f","Specifies a list of file extensions to process when using the -i option. The list elements should be separated by spaces. The default is \"cpp h c\".",1);
+ parser.add_option("i","Specifies an input directory.",1);
+ parser.add_option("cat","Puts all the output into a single html file with the given name.",1);
+ parser.add_option("depth","Specifies how many directories deep to search when using the i option. The default value is 30.",1);
+ parser.add_option("o","This option causes all the output files to be created inside the given directory. If this option is not given then all output goes to the current working directory.",1);
+ parser.add_option("flatten","When this option is given it prevents the input directory structure from being replicated.");
+ parser.add_option("title","This option specifies a string which is prepended onto the title of the generated HTML",1);
+ parser.add_option("to-xml","Instead of generating HTML output, create a single output file called output.xml that contains "
+ "a simple XML database which lists all documented classes and functions.");
+ parser.add_option("t", "When creating XML output, replace tabs in comments with <arg> spaces.", 1);
+
+
+ parser.parse(argc,argv);
+
+
+ parser.check_incompatible_options("cat","o");
+ parser.check_incompatible_options("cat","flatten");
+ parser.check_incompatible_options("cat","index");
+ parser.check_option_arg_type<unsigned long>("depth");
+ parser.check_option_arg_range("t", 1, 100);
+
+ parser.check_incompatible_options("to-xml", "b");
+ parser.check_incompatible_options("to-xml", "n");
+ parser.check_incompatible_options("to-xml", "index");
+ parser.check_incompatible_options("to-xml", "cat");
+ parser.check_incompatible_options("to-xml", "o");
+ parser.check_incompatible_options("to-xml", "flatten");
+ parser.check_incompatible_options("to-xml", "title");
+
+ const char* singles[] = {"b","n","h","index","v","man","f","cat","depth","o","flatten","title","to-xml", "t"};
+ parser.check_one_time_options(singles);
+
+ const char* i_sub_ops[] = {"f","depth","flatten"};
+ parser.check_sub_options("i",i_sub_ops);
+
+ const char* to_xml_sub_ops[] = {"t"};
+ parser.check_sub_options("to-xml",to_xml_sub_ops);
+
+ const command_line_parser::option_type& b_opt = parser.option("b");
+ const command_line_parser::option_type& n_opt = parser.option("n");
+ const command_line_parser::option_type& h_opt = parser.option("h");
+ const command_line_parser::option_type& index_opt = parser.option("index");
+ const command_line_parser::option_type& v_opt = parser.option("v");
+ const command_line_parser::option_type& o_opt = parser.option("o");
+ const command_line_parser::option_type& man_opt = parser.option("man");
+ const command_line_parser::option_type& f_opt = parser.option("f");
+ const command_line_parser::option_type& cat_opt = parser.option("cat");
+ const command_line_parser::option_type& i_opt = parser.option("i");
+ const command_line_parser::option_type& flatten_opt = parser.option("flatten");
+ const command_line_parser::option_type& depth_opt = parser.option("depth");
+ const command_line_parser::option_type& title_opt = parser.option("title");
+ const command_line_parser::option_type& to_xml_opt = parser.option("to-xml");
+
+
+ string filter = "cpp h c";
+
+ bool cat = false;
+ bool color = true;
+ bool number = false;
+ unsigned long search_depth = 30;
+
+ string out_dir; // the name of the output directory if the o option is given. "" otherwise
+ string full_out_dir; // the full name of the output directory if the o option is given. "" otherwise
+ const char separator = directory::get_separator();
+
+ bool no_run = false;
+ if (v_opt)
+ {
+ cout << "Htmlify v" << VERSION
+ << "\nCompiled: " << __TIME__ << " " << __DATE__
+ << "\nWritten by Davis King\n";
+ cout << "Check for updates at http://dlib.net\n\n";
+ no_run = true;
+ }
+
+ if (h_opt)
+ {
+ cout << "This program pretty prints C or C++ source code to HTML.\n";
+ cout << "Usage: htmlify [options] [file]...\n";
+ parser.print_options();
+ cout << "\n\n";
+ no_run = true;
+ }
+
+ if (man_opt)
+ {
+ print_manual();
+ no_run = true;
+ }
+
+ if (no_run)
+ return 0;
+
+ if (f_opt)
+ {
+ filter = f_opt.argument();
+ }
+
+ if (cat_opt)
+ {
+ cat = true;
+ }
+
+ if (depth_opt)
+ {
+ search_depth = string_cast<unsigned long>(depth_opt.argument());
+ }
+
+ if (to_xml_opt)
+ {
+ unsigned long expand_tabs = 0;
+ if (parser.option("t"))
+ expand_tabs = string_cast<unsigned long>(parser.option("t").argument());
+
+ generate_xml_markup(parser, filter, search_depth, expand_tabs);
+ return 0;
+ }
+
+ if (o_opt)
+ {
+ // make sure this directory exists
+ out_dir = o_opt.argument();
+ create_directory(out_dir);
+ directory dir(out_dir);
+ full_out_dir = dir.full_name();
+
+ // make sure the last character of out_dir is a separator
+ if (out_dir[out_dir.size()-1] != separator)
+ out_dir += separator;
+ if (full_out_dir[out_dir.size()-1] != separator)
+ full_out_dir += separator;
+ }
+
+ if (b_opt)
+ color = false;
+ if (n_opt)
+ number = true;
+
+ // this is a map of output file names to input file names.
+ map_string_to_string file_map;
+
+
+ // add all the files that are just given on the command line to the
+ // file_map.
+ for (unsigned long i = 0; i < parser.number_of_arguments(); ++i)
+ {
+ string in_file, out_file;
+ in_file = parser[i];
+ string::size_type pos = in_file.find_last_of(separator);
+ if (pos != string::npos)
+ {
+ out_file = out_dir + in_file.substr(pos+1) + ".html";
+ }
+ else
+ {
+ out_file = out_dir + in_file + ".html";
+ }
+
+ if (file_map.is_in_domain(out_file))
+ {
+ if (file_map[out_file] != in_file)
+ {
+ // there is a file name colision in the output folder. definitly a bad thing
+ cout << "Error: Two of the input files have the same name and would overwrite each\n";
+ cout << "other. They are " << in_file << " and " << file_map[out_file] << ".\n" << endl;
+ return 1;
+ }
+ else
+ {
+ continue;
+ }
+ }
+
+ file_map.add(out_file,in_file);
+ }
+
+ // pick out the filter strings
+ set_of_string sfilter;
+ istringstream sin(filter);
+ string temp;
+ sin >> temp;
+ while (sin)
+ {
+ if (sfilter.is_member(temp) == false)
+ sfilter.add(temp);
+ sin >> temp;
+ }
+
+ // now get all the files given by the i options
+ for (unsigned long i = 0; i < i_opt.count(); ++i)
+ {
+ directory dir(i_opt.argument(0,i));
+ add_files(dir, out_dir, file_map, flatten_opt, cat, sfilter, search_depth);
+ }
+
+ if (cat)
+ {
+ file_map.reset();
+ ofstream fout(cat_opt.argument().c_str());
+ if (!fout)
+ {
+ throw error("Error: unable to open file " + cat_opt.argument());
+ }
+ fout << "<html><title>" << cat_opt.argument() << "</title></html>";
+
+ const char separator = directory::get_separator();
+ string file;
+ while (file_map.move_next())
+ {
+ ifstream fin(file_map.element().value().c_str());
+ if (!fin)
+ {
+ throw error("Error: unable to open file " + file_map.element().value());
+ }
+
+ string::size_type pos = file_map.element().value().find_last_of(separator);
+ if (pos != string::npos)
+ file = file_map.element().value().substr(pos+1);
+ else
+ file = file_map.element().value();
+
+ std::string title;
+ if (title_opt)
+ title = title_opt.argument();
+ htmlify(fin, fout, title + file, color, number);
+ }
+
+ }
+ else
+ {
+ std::string title;
+ if (title_opt)
+ title = title_opt.argument();
+ htmlify(file_map,color,number,title);
+ }
+
+
+
+ if (index_opt)
+ {
+ ofstream index((out_dir + "index.html").c_str());
+ ofstream menu((out_dir + "menu.html").c_str());
+
+ if (!index)
+ {
+ cout << "Error: unable to create " << out_dir << "index.html\n\n";
+ return 0;
+ }
+
+ if (!menu)
+ {
+ cout << "Error: unable to create " << out_dir << "menu.html\n\n";
+ return 0;
+ }
+
+
+ index << "<html><frameset cols='200,*'>";
+ index << "<frame src='menu.html' name='menu'>";
+ index << "<frame name='main'></frameset></html>";
+
+ menu << "<html><body><br>";
+
+ file_map.reset();
+ while (file_map.move_next())
+ {
+ if (o_opt)
+ {
+ file = file_map.element().key();
+ if (file.find(full_out_dir) != string::npos)
+ file = file.substr(full_out_dir.size());
+ else
+ file = file.substr(out_dir.size());
+ }
+ else
+ {
+ file = file_map.element().key();
+ }
+ // strip the .html from file
+ file = file.substr(0,file.size()-5);
+ menu << "<a href='" << file << ".html' target='main'>"
+ << file << "</a><br>";
+ }
+
+ menu << "</body></html>";
+
+ }
+
+ }
+ catch (ios_base::failure&)
+ {
+ cout << "ERROR: unable to write to " << file << endl;
+ cout << endl;
+ }
+ catch (exception& e)
+ {
+ cout << e.what() << endl;
+ cout << "\nTry the -h option for more information.\n";
+ cout << endl;
+ }
+}
+
+// -------------------------------------------------------------------------------------------------
+
+void htmlify (
+ istream& in,
+ ostream& out,
+ const std::string& title,
+ bool colored,
+ bool number_lines
+)
+{
+ if (colored)
+ {
+ static cprinter cp;
+ if (number_lines)
+ {
+ cp.print_and_number(in,out,title);
+ }
+ else
+ {
+ cp.print(in,out,title);
+ }
+ }
+ else
+ {
+ static bprinter bp;
+ if (number_lines)
+ {
+ bp.print_and_number(in,out,title);
+ }
+ else
+ {
+ bp.print(in,out,title);
+ }
+ }
+}
+
+// -------------------------------------------------------------------------------------------------
+
+void htmlify (
+ const map_string_to_string& file_map,
+ bool colored,
+ bool number_lines,
+ const std::string& title
+)
+{
+ file_map.reset();
+ const char separator = directory::get_separator();
+ string file;
+ while (file_map.move_next())
+ {
+ ifstream fin(file_map.element().value().c_str());
+ if (!fin)
+ {
+ throw error("Error: unable to open file " + file_map.element().value() );
+ }
+
+ ofstream fout(file_map.element().key().c_str());
+
+ if (!fout)
+ {
+ throw error("Error: unable to open file " + file_map.element().key());
+ }
+
+ string::size_type pos = file_map.element().value().find_last_of(separator);
+ if (pos != string::npos)
+ file = file_map.element().value().substr(pos+1);
+ else
+ file = file_map.element().value();
+
+ htmlify(fin, fout,title + file, colored, number_lines);
+ }
+}
+
+// -------------------------------------------------------------------------------------------------
+
+void add_files (
+ const directory& dir,
+ const std::string& out_dir,
+ map_string_to_string& file_map,
+ bool flatten,
+ bool cat,
+ const set_of_string& filter,
+ unsigned long search_depth,
+ unsigned long cur_depth
+)
+{
+ const char separator = directory::get_separator();
+
+ queue_of_files files;
+ queue_of_dirs dirs;
+
+ dir.get_files(files);
+
+ // look though all the files in the current directory and add the
+ // ones that match the filter to file_map
+ string name, ext, in_file, out_file;
+ files.reset();
+ while (files.move_next())
+ {
+ name = files.element().name();
+ string::size_type pos = name.find_last_of('.');
+ if (pos != string::npos && filter.is_member(name.substr(pos+1)))
+ {
+ in_file = files.element().full_name();
+
+ if (flatten)
+ {
+ pos = in_file.find_last_of(separator);
+ }
+ else
+ {
+ // figure out how much of the file's path we need to keep
+ // for the output file name
+ pos = in_file.size();
+ for (unsigned long i = 0; i <= cur_depth && pos != string::npos; ++i)
+ {
+ pos = in_file.find_last_of(separator,pos-1);
+ }
+ }
+
+ if (pos != string::npos)
+ {
+ out_file = out_dir + in_file.substr(pos+1) + ".html";
+ }
+ else
+ {
+ out_file = out_dir + in_file + ".html";
+ }
+
+ if (file_map.is_in_domain(out_file))
+ {
+ if (file_map[out_file] != in_file)
+ {
+ // there is a file name colision in the output folder. definitly a bad thing
+ ostringstream sout;
+ sout << "Error: Two of the input files have the same name and would overwrite each\n";
+ sout << "other. They are " << in_file << " and " << file_map[out_file] << ".";
+ throw error(sout.str());
+ }
+ else
+ {
+ continue;
+ }
+ }
+
+ file_map.add(out_file,in_file);
+
+ }
+ } // while (files.move_next())
+ files.clear();
+
+ if (search_depth > cur_depth)
+ {
+ // search all the sub directories
+ dir.get_dirs(dirs);
+ dirs.reset();
+ while (dirs.move_next())
+ {
+ if (!flatten && !cat)
+ {
+ string d = dirs.element().full_name();
+
+ // figure out how much of the directorie's path we need to keep.
+ string::size_type pos = d.size();
+ for (unsigned long i = 0; i <= cur_depth && pos != string::npos; ++i)
+ {
+ pos = d.find_last_of(separator,pos-1);
+ }
+
+ // make sure this directory exists in the output directory tree
+ d = d.substr(pos+1);
+ create_directory(out_dir + separator + d);
+ }
+
+ add_files(dirs.element(), out_dir, file_map, flatten, cat, filter, search_depth, cur_depth+1);
+ }
+ }
+
+}
+
+// -------------------------------------------------------------------------------------------------
+
+void print_manual (
+)
+{
+ ostringstream sout;
+
+ const unsigned long indent = 2;
+
+ cout << "\n";
+ sout << "Htmlify v" << VERSION;
+ cout << wrap_string(sout.str(),indent,indent); sout.str("");
+
+
+ sout << "This is a fairly simple program that takes source files and pretty prints them "
+ << "in HTML. There are two pretty printing styles, black and white or color. The "
+ << "black and white style is meant to look nice when printed out on paper. It looks "
+ << "a little funny on the screen but on paper it is pretty nice. The color version "
+ << "on the other hand has nonprintable HTML elements such as links and anchors.";
+ cout << "\n\n" << wrap_string(sout.str(),indent,indent); sout.str("");
+
+
+ sout << "The colored style puts HTML anchors on class and function names. This means "
+ << "you can link directly to the part of the code that contains these names. For example, "
+ << "if you had a source file bar.cpp with a function called foo in it you could link "
+ << "directly to the function with a link address of \"bar.cpp.html#foo\". It is also "
+ << "possible to instruct Htmlify to place HTML anchors at arbitrary spots by using a "
+ << "special comment of the form /*!A anchor_name */. You can put other things in the "
+ << "comment but the important bit is to have it begin with /*!A then some white space "
+ << "then the anchor name you want then more white space and then you can add whatever "
+ << "you like. You would then refer to this anchor with a link address of "
+ << "\"file.html#anchor_name\".";
+ cout << "\n\n" << wrap_string(sout.str(),indent,indent); sout.str("");
+
+ sout << "Htmlify also has the ability to create a simple index of all the files it is given. "
+ << "The --index option creates a file named index.html with a frame on the left side "
+ << "that contains links to all the files.";
+ cout << "\n\n" << wrap_string(sout.str(),indent,indent); sout.str("");
+
+
+ sout << "Finally, Htmlify can produce annotated XML output instead of HTML. The output will "
+ << "contain all functions which are immediately followed by comments of the form /*! comment body !*/. "
+ << "Similarly, all classes or structs that immediately contain one of these comments following their "
+ << "opening { will also be output as annotated XML. Note also that if you wish to document a "
+ << "piece of code using one of these comments but don't want it to appear in the output XML then "
+ << "use either a comment like /* */ or /*!P !*/ to mark the code as \"private\".";
+ cout << "\n\n" << wrap_string(sout.str(),indent,indent) << "\n\n"; sout.str("");
+}
+
+// -------------------------------------------------------------------------------------------------
+
diff --git a/ml/dlib/tools/htmlify/to_xml.cpp b/ml/dlib/tools/htmlify/to_xml.cpp
new file mode 100644
index 000000000..7fae43380
--- /dev/null
+++ b/ml/dlib/tools/htmlify/to_xml.cpp
@@ -0,0 +1,1599 @@
+
+#include "to_xml.h"
+#include "dlib/dir_nav.h"
+#include <vector>
+#include <sstream>
+#include <iostream>
+#include <fstream>
+#include <stack>
+#include "dlib/cpp_tokenizer.h"
+#include "dlib/string.h"
+
+using namespace dlib;
+using namespace std;
+
+// ----------------------------------------------------------------------------------------
+
+typedef cpp_tokenizer::kernel_1a_c tok_type;
+
+// ----------------------------------------------------------------------------------------
+
+class file_filter
+{
+public:
+
+ file_filter(
+ const string& filter
+ )
+ {
+ // pick out the filter strings
+ istringstream sin(filter);
+ string temp;
+ sin >> temp;
+ while (sin)
+ {
+ endings.push_back("." + temp);
+ sin >> temp;
+ }
+ }
+
+ bool operator() ( const file& f) const
+ {
+ // check if any of the endings match
+ for (unsigned long i = 0; i < endings.size(); ++i)
+ {
+ // if the ending is bigger than f's name then it obviously doesn't match
+ if (endings[i].size() > f.name().size())
+ continue;
+
+ // now check if the actual characters that make up the end of the file name
+ // matches what is in endings[i].
+ if ( std::equal(endings[i].begin(), endings[i].end(), f.name().end()-endings[i].size()))
+ return true;
+ }
+
+ return false;
+ }
+
+ std::vector<string> endings;
+};
+
+// ----------------------------------------------------------------------------------------
+
+void obtain_list_of_files (
+ const cmd_line_parser<char>::check_1a_c& parser,
+ const std::string& filter,
+ const unsigned long search_depth,
+ std::vector<std::pair<string,string> >& files
+)
+{
+ for (unsigned long i = 0; i < parser.option("i").count(); ++i)
+ {
+ const directory dir(parser.option("i").argument(0,i));
+
+ const std::vector<file>& temp = get_files_in_directory_tree(dir, file_filter(filter), search_depth);
+
+ // figure out how many characters need to be removed from the path of each file
+ const string parent = dir.get_parent().full_name();
+ unsigned long strip = parent.size();
+ if (parent.size() > 0 && parent[parent.size()-1] != '\\' && parent[parent.size()-1] != '/')
+ strip += 1;
+
+ for (unsigned long i = 0; i < temp.size(); ++i)
+ {
+ files.push_back(make_pair(temp[i].full_name().substr(strip), temp[i].full_name()));
+ }
+ }
+
+ for (unsigned long i = 0; i < parser.number_of_arguments(); ++i)
+ {
+ files.push_back(make_pair(parser[i], parser[i]));
+ }
+
+ std::sort(files.begin(), files.end());
+}
+
+// ----------------------------------------------------------------------------------------
+
+struct tok_function_record
+{
+ std::vector<std::pair<int,string> > declaration;
+ string scope;
+ string file;
+ string comment;
+};
+
+struct tok_method_record
+{
+ std::vector<std::pair<int,string> > declaration;
+ string comment;
+};
+
+struct tok_variable_record
+{
+ std::vector<std::pair<int,string> > declaration;
+};
+
+struct tok_typedef_record
+{
+ std::vector<std::pair<int,string> > declaration;
+};
+
+struct tok_class_record
+{
+ std::vector<std::pair<int,string> > declaration;
+ string name;
+ string scope;
+ string file;
+ string comment;
+
+ std::vector<tok_method_record> public_methods;
+ std::vector<tok_method_record> protected_methods;
+ std::vector<tok_variable_record> public_variables;
+ std::vector<tok_typedef_record> public_typedefs;
+ std::vector<tok_variable_record> protected_variables;
+ std::vector<tok_typedef_record> protected_typedefs;
+ std::vector<tok_class_record> public_inner_classes;
+ std::vector<tok_class_record> protected_inner_classes;
+};
+
+// ----------------------------------------------------------------------------------------
+
+struct function_record
+{
+ string name;
+ string scope;
+ string declaration;
+ string file;
+ string comment;
+};
+
+struct method_record
+{
+ string name;
+ string declaration;
+ string comment;
+};
+
+struct variable_record
+{
+ string declaration;
+};
+
+struct typedef_record
+{
+ string declaration;
+};
+
+struct class_record
+{
+ string name;
+ string scope;
+ string declaration;
+ string file;
+ string comment;
+
+ std::vector<method_record> public_methods;
+ std::vector<variable_record> public_variables;
+ std::vector<typedef_record> public_typedefs;
+
+ std::vector<method_record> protected_methods;
+ std::vector<variable_record> protected_variables;
+ std::vector<typedef_record> protected_typedefs;
+
+ std::vector<class_record> public_inner_classes;
+ std::vector<class_record> protected_inner_classes;
+};
+
+// ----------------------------------------------------------------------------------------
+
+unsigned long count_newlines (
+ const string& str
+)
+/*!
+ ensures
+ - returns the number of '\n' characters inside str
+!*/
+{
+ unsigned long count = 0;
+ for (unsigned long i = 0; i < str.size(); ++i)
+ {
+ if (str[i] == '\n')
+ ++count;
+ }
+ return count;
+}
+
+// ----------------------------------------------------------------------------------------
+
+bool contains_unescaped_newline (
+ const string& str
+)
+/*!
+ ensures
+ - returns true if str contains a '\n' character that isn't preceded by a '\'
+ character.
+!*/
+{
+ if (str.size() == 0)
+ return false;
+
+ if (str[0] == '\n')
+ return true;
+
+ for (unsigned long i = 1; i < str.size(); ++i)
+ {
+ if (str[i] == '\n' && str[i-1] != '\\')
+ return true;
+ }
+
+ return false;
+}
+
+// ----------------------------------------------------------------------------------------
+
+bool is_formal_comment (
+ const string& str
+)
+{
+ if (str.size() < 6)
+ return false;
+
+ if (str[0] == '/' &&
+ str[1] == '*' &&
+ str[2] == '!' &&
+ str[3] != 'P' &&
+ str[3] != 'p' &&
+ str[str.size()-3] == '!' &&
+ str[str.size()-2] == '*' &&
+ str[str.size()-1] == '/' )
+ return true;
+
+ return false;
+}
+
+// ----------------------------------------------------------------------------------------
+
+string make_scope_string (
+ const std::vector<string>& namespaces,
+ unsigned long exclude_last_num_scopes = 0
+)
+{
+ string temp;
+ for (unsigned long i = 0; i + exclude_last_num_scopes < namespaces.size(); ++i)
+ {
+ if (namespaces[i].size() == 0)
+ continue;
+
+ if (temp.size() == 0)
+ temp = namespaces[i];
+ else
+ temp += "::" + namespaces[i];
+ }
+ return temp;
+}
+
+// ----------------------------------------------------------------------------------------
+
+bool looks_like_function_declaration (
+ const std::vector<std::pair<int,string> >& declaration
+)
+{
+
+ // Check if declaration contains IDENTIFIER ( ) somewhere in it.
+ bool seen_first_part = false;
+ bool seen_operator = false;
+ int local_paren_count = 0;
+ for (unsigned long i = 1; i < declaration.size(); ++i)
+ {
+ if (declaration[i].first == tok_type::KEYWORD &&
+ declaration[i].second == "operator")
+ {
+ seen_operator = true;
+ }
+
+ if (declaration[i].first == tok_type::OTHER &&
+ declaration[i].second == "(" &&
+ (declaration[i-1].first == tok_type::IDENTIFIER || seen_operator))
+ {
+ seen_first_part = true;
+ }
+
+ if (declaration[i].first == tok_type::OTHER)
+ {
+ if ( declaration[i].second == "(")
+ ++local_paren_count;
+ else if ( declaration[i].second == ")")
+ --local_paren_count;
+ }
+ }
+
+ if (seen_first_part && local_paren_count == 0)
+ return true;
+ else
+ return false;
+}
+
+// ----------------------------------------------------------------------------------------
+
+enum scope_type
+{
+ public_scope,
+ protected_scope,
+ private_scope
+};
+
+
+void process_file (
+ istream& fin,
+ const string& file,
+ std::vector<tok_function_record>& functions,
+ std::vector<tok_class_record>& classes
+)
+/*!
+ ensures
+ - scans the given file for global functions and appends any found into functions.
+ - scans the given file for global classes and appends any found into classes.
+!*/
+{
+ tok_type tok;
+ tok.set_stream(fin);
+
+ bool recently_seen_struct_keyword = false;
+ // true if we have seen the struct keyword and
+ // we have not seen any identifiers or { characters
+
+ string last_struct_name;
+ // the name of the last struct we have seen
+
+ bool recently_seen_class_keyword = false;
+ // true if we have seen the class keyword and
+ // we have not seen any identifiers or { characters
+
+ string last_class_name;
+ // the name of the last class we have seen
+
+ bool recently_seen_namespace_keyword = false;
+ // true if we have seen the namespace keyword and
+ // we have not seen any identifiers or { characters
+
+ string last_namespace_name;
+ // the name of the last namespace we have seen
+
+ bool recently_seen_pound_define = false;
+ // true if we have seen a #define and haven't seen an unescaped newline
+
+ bool recently_seen_preprocessor = false;
+ // true if we have seen a preprocessor statement and haven't seen an unescaped newline
+
+ bool recently_seen_typedef = false;
+ // true if we have seen a typedef keyword and haven't seen a ;
+
+ bool recently_seen_paren_0 = false;
+ // true if we have seen paren_count transition to zero but haven't yet seen a ; or { or
+ // a new line if recently_seen_pound_define is true.
+
+ bool recently_seen_slots = false;
+ // true if we have seen the identifier "slots" at a zero scope but haven't seen any
+ // other identifiers or the ';' or ':' characters.
+
+ bool recently_seen_closing_bracket = false;
+ // true if we have seen a } and haven't yet seen an IDENTIFIER or ;
+
+ bool recently_seen_new_scope = false;
+ // true if we have seen the keywords class, namespace, struct, or extern and
+ // we have not seen the characters {, ), or ; since then
+
+ bool at_top_of_new_scope = false;
+ // true if we have seen the { that started a new scope but haven't seen anything yet but WHITE_SPACE
+
+ std::vector<string> namespaces;
+ // a stack to hold the names of the scopes we have entered. This is the classes, structs, and namespaces we enter.
+ namespaces.push_back(""); // this is the global namespace
+
+ std::stack<scope_type> scope_access;
+ // If the stack isn't empty then we are inside a class or struct and the top value
+ // in the stack tells if we are in a public, protected, or private region.
+
+ std::stack<unsigned long> scopes; // a stack to hold current and old scope counts
+ // the top of the stack counts the number of new scopes (i.e. unmatched { } we have entered
+ // since we were at a scope where functions can be defined.
+ // We also maintain the invariant that scopes.size() == namespaces.size()
+ scopes.push(0);
+
+ std::stack<tok_class_record> class_stack;
+ // This is a stack where class_stack.top() == the incomplete class record for the class declaration we are
+ // currently in.
+
+ unsigned long paren_count = 0;
+ // this is the number of ( we have seen minus the number of ) we have
+ // seen.
+
+ std::vector<std::pair<int,string> > token_accum;
+ // Used to accumulate tokens for function and class declarations
+
+ std::vector<std::pair<int,string> > last_full_declaration;
+ // Once we determine that token_accum has a full declaration in it we copy it into last_full_declaration.
+
+ int type;
+ string token;
+
+ tok.get_token(type, token);
+
+ while (type != tok_type::END_OF_FILE)
+ {
+ switch(type)
+ {
+ case tok_type::KEYWORD: // ------------------------------------------
+ {
+ token_accum.push_back(make_pair(type,token));
+
+ if (token[0] == '#')
+ recently_seen_preprocessor = true;
+
+ if (token == "class")
+ {
+ recently_seen_class_keyword = true;
+ recently_seen_new_scope = true;
+ }
+ else if (token == "struct")
+ {
+ recently_seen_struct_keyword = true;
+ recently_seen_new_scope = true;
+ }
+ else if (token == "namespace")
+ {
+ recently_seen_namespace_keyword = true;
+ recently_seen_new_scope = true;
+ }
+ else if (token == "extern")
+ {
+ recently_seen_new_scope = true;
+ }
+ else if (token == "#define")
+ {
+ recently_seen_pound_define = true;
+ }
+ else if (token == "typedef")
+ {
+ recently_seen_typedef = true;
+ }
+ else if (recently_seen_pound_define == false)
+ {
+ // eat white space
+ int temp_type;
+ string temp_token;
+ if (tok.peek_type() == tok_type::WHITE_SPACE)
+ tok.get_token(temp_type, temp_token);
+
+ const bool next_is_colon = (tok.peek_type() == tok_type::OTHER && tok.peek_token() == ":");
+ if (next_is_colon)
+ {
+ // eat the colon
+ tok.get_token(temp_type, temp_token);
+
+ if (scope_access.size() > 0 && token == "public")
+ {
+ scope_access.top() = public_scope;
+ token_accum.clear();
+ last_full_declaration.clear();
+ }
+ else if (scope_access.size() > 0 && token == "protected")
+ {
+ scope_access.top() = protected_scope;
+ token_accum.clear();
+ last_full_declaration.clear();
+ }
+ else if (scope_access.size() > 0 && token == "private")
+ {
+ scope_access.top() = private_scope;
+ token_accum.clear();
+ last_full_declaration.clear();
+ }
+ }
+ }
+
+ at_top_of_new_scope = false;
+
+ }break;
+
+ case tok_type::COMMENT: // ------------------------------------------
+ {
+ if (scopes.top() == 0 && last_full_declaration.size() > 0 && is_formal_comment(token) &&
+ paren_count == 0)
+ {
+
+ // if we are inside a class or struct
+ if (scope_access.size() > 0)
+ {
+ // if we are looking at a comment at the top of a class
+ if (at_top_of_new_scope)
+ {
+ // push an entry for this class into the class_stack
+ tok_class_record temp;
+ temp.declaration = last_full_declaration;
+ temp.file = file;
+ temp.name = namespaces.back();
+ temp.scope = make_scope_string(namespaces,1);
+ temp.comment = token;
+ class_stack.push(temp);
+ }
+ else if (scope_access.top() == public_scope || scope_access.top() == protected_scope)
+ {
+ // This should be a member function.
+ // Only do anything if the class that contains this member function is
+ // in the class_stack.
+ if (class_stack.size() > 0 && class_stack.top().name == namespaces.back() &&
+ looks_like_function_declaration(last_full_declaration))
+ {
+ tok_method_record temp;
+
+ // Check if there is an initialization list inside the declaration and if there is
+ // then find out where the starting : is located so we can avoid including it in
+ // the output.
+ unsigned long pos = last_full_declaration.size();
+ long temp_paren_count = 0;
+ for (unsigned long i = 0; i < last_full_declaration.size(); ++i)
+ {
+ if (last_full_declaration[i].first == tok_type::OTHER)
+ {
+ if (last_full_declaration[i].second == "(")
+ ++temp_paren_count;
+ else if (last_full_declaration[i].second == ")")
+ --temp_paren_count;
+ else if (temp_paren_count == 0 && last_full_declaration[i].second == ":")
+ {
+ // if this is a :: then ignore it
+ if (i > 0 && last_full_declaration[i-1].second == ":")
+ continue;
+ else if (i+1 < last_full_declaration.size() && last_full_declaration[i+1].second == ":")
+ continue;
+ else
+ {
+ pos = i;
+ break;
+ }
+ }
+ }
+ }
+
+ temp.declaration.assign(last_full_declaration.begin(), last_full_declaration.begin()+pos);
+ temp.comment = token;
+ if (scope_access.top() == public_scope)
+ class_stack.top().public_methods.push_back(temp);
+ else
+ class_stack.top().protected_methods.push_back(temp);
+ }
+ }
+ }
+ else
+ {
+ // we should be looking at a global declaration of some kind.
+ if (looks_like_function_declaration(last_full_declaration))
+ {
+ tok_function_record temp;
+
+ // make sure we never include anything beyond the first closing )
+ // if we are looking at a #defined function
+ unsigned long pos = last_full_declaration.size();
+ if (last_full_declaration[0].second == "#define")
+ {
+ long temp_paren_count = 0;
+ for (unsigned long i = 0; i < last_full_declaration.size(); ++i)
+ {
+ if (last_full_declaration[i].first == tok_type::OTHER)
+ {
+ if (last_full_declaration[i].second == "(")
+ {
+ ++temp_paren_count;
+ }
+ else if (last_full_declaration[i].second == ")")
+ {
+ --temp_paren_count;
+ if (temp_paren_count == 0)
+ {
+ pos = i+1;
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ temp.declaration.assign(last_full_declaration.begin(), last_full_declaration.begin()+pos);
+ temp.file = file;
+ temp.scope = make_scope_string(namespaces);
+ temp.comment = token;
+ functions.push_back(temp);
+ }
+ }
+
+ token_accum.clear();
+ last_full_declaration.clear();
+ }
+
+ at_top_of_new_scope = false;
+ }break;
+
+ case tok_type::IDENTIFIER: // ------------------------------------------
+ {
+ if (recently_seen_class_keyword)
+ {
+ last_class_name = token;
+ last_struct_name.clear();
+ last_namespace_name.clear();
+ }
+ else if (recently_seen_struct_keyword)
+ {
+ last_struct_name = token;
+ last_class_name.clear();
+ last_namespace_name.clear();
+ }
+ else if (recently_seen_namespace_keyword)
+ {
+ last_namespace_name = token;
+ last_class_name.clear();
+ last_struct_name.clear();
+ }
+
+ if (scopes.top() == 0 && token == "slots")
+ recently_seen_slots = true;
+ else
+ recently_seen_slots = false;
+
+ recently_seen_class_keyword = false;
+ recently_seen_struct_keyword = false;
+ recently_seen_namespace_keyword = false;
+ recently_seen_closing_bracket = false;
+ at_top_of_new_scope = false;
+
+ token_accum.push_back(make_pair(type,token));
+ }break;
+
+ case tok_type::OTHER: // ------------------------------------------
+ {
+ switch(token[0])
+ {
+ case '{':
+ // if we are entering a new scope
+ if (recently_seen_new_scope)
+ {
+ scopes.push(0);
+ at_top_of_new_scope = true;
+
+ // if we are entering a class
+ if (last_class_name.size() > 0)
+ {
+ scope_access.push(private_scope);
+ namespaces.push_back(last_class_name);
+ }
+ else if (last_struct_name.size() > 0)
+ {
+ scope_access.push(public_scope);
+ namespaces.push_back(last_struct_name);
+ }
+ else if (last_namespace_name.size() > 0)
+ {
+ namespaces.push_back(last_namespace_name);
+ }
+ else
+ {
+ namespaces.push_back("");
+ }
+ }
+ else
+ {
+ scopes.top() += 1;
+ }
+ recently_seen_new_scope = false;
+ recently_seen_class_keyword = false;
+ recently_seen_struct_keyword = false;
+ recently_seen_namespace_keyword = false;
+ recently_seen_paren_0 = false;
+
+ // a { at function scope is an end of a potential declaration
+ if (scopes.top() == 0)
+ {
+ // put token_accum into last_full_declaration
+ token_accum.swap(last_full_declaration);
+ }
+ token_accum.clear();
+ break;
+
+ case '}':
+ if (scopes.top() > 0)
+ {
+ scopes.top() -= 1;
+ }
+ else if (scopes.size() > 1)
+ {
+ scopes.pop();
+
+ if (scope_access.size() > 0)
+ scope_access.pop();
+
+ // If the scope we are leaving is the top class on the class_stack
+ // then we need to either pop it into its containing class or put it
+ // into the classes output vector.
+ if (class_stack.size() > 0 && namespaces.back() == class_stack.top().name)
+ {
+ // If this class is a inner_class of another then push it into the
+ // public_inner_classes or protected_inner_classes field of it's containing class.
+ if (class_stack.size() > 1)
+ {
+ tok_class_record temp = class_stack.top();
+ class_stack.pop();
+ if (scope_access.size() > 0)
+ {
+ if (scope_access.top() == public_scope)
+ class_stack.top().public_inner_classes.push_back(temp);
+ else if (scope_access.top() == protected_scope)
+ class_stack.top().protected_inner_classes.push_back(temp);
+ }
+ }
+ else if (class_stack.size() > 0)
+ {
+ classes.push_back(class_stack.top());
+ class_stack.pop();
+ }
+ }
+
+ namespaces.pop_back();
+ last_full_declaration.clear();
+ }
+
+ token_accum.clear();
+ recently_seen_closing_bracket = true;
+ at_top_of_new_scope = false;
+ break;
+
+ case ';':
+ // a ; at function scope is an end of a potential declaration
+ if (scopes.top() == 0)
+ {
+ // put token_accum into last_full_declaration
+ token_accum.swap(last_full_declaration);
+ }
+ token_accum.clear();
+
+ // if we are inside the public area of a class and this ; might be the end
+ // of a typedef or variable declaration
+ if (scopes.top() == 0 && scope_access.size() > 0 &&
+ (scope_access.top() == public_scope || scope_access.top() == protected_scope) &&
+ recently_seen_closing_bracket == false)
+ {
+ if (recently_seen_typedef)
+ {
+ // This should be a typedef inside the public area of a class or struct:
+ // Only do anything if the class that contains this typedef is in the class_stack.
+ if (class_stack.size() > 0 && class_stack.top().name == namespaces.back())
+ {
+ tok_typedef_record temp;
+ temp.declaration = last_full_declaration;
+ if (scope_access.top() == public_scope)
+ class_stack.top().public_typedefs.push_back(temp);
+ else
+ class_stack.top().protected_typedefs.push_back(temp);
+ }
+
+ }
+ else if (recently_seen_paren_0 == false && recently_seen_new_scope == false)
+ {
+ // This should be some kind of public variable declaration inside a class or struct:
+ // Only do anything if the class that contains this member variable is in the class_stack.
+ if (class_stack.size() > 0 && class_stack.top().name == namespaces.back())
+ {
+ tok_variable_record temp;
+ temp.declaration = last_full_declaration;
+ if (scope_access.top() == public_scope)
+ class_stack.top().public_variables.push_back(temp);
+ else
+ class_stack.top().protected_variables.push_back(temp);
+ }
+
+ }
+ }
+
+ recently_seen_new_scope = false;
+ recently_seen_typedef = false;
+ recently_seen_paren_0 = false;
+ recently_seen_closing_bracket = false;
+ recently_seen_slots = false;
+ at_top_of_new_scope = false;
+ break;
+
+ case ':':
+ token_accum.push_back(make_pair(type,token));
+ if (recently_seen_slots)
+ {
+ token_accum.clear();
+ last_full_declaration.clear();
+ recently_seen_slots = false;
+ }
+ break;
+
+ case '(':
+ ++paren_count;
+ token_accum.push_back(make_pair(type,token));
+ at_top_of_new_scope = false;
+ break;
+
+ case ')':
+ token_accum.push_back(make_pair(type,token));
+
+ --paren_count;
+ if (paren_count == 0)
+ {
+ recently_seen_paren_0 = true;
+ if (scopes.top() == 0)
+ {
+ last_full_declaration = token_accum;
+ }
+ }
+
+ recently_seen_new_scope = false;
+ at_top_of_new_scope = false;
+ break;
+
+ default:
+ token_accum.push_back(make_pair(type,token));
+ at_top_of_new_scope = false;
+ break;
+ }
+ }break;
+
+
+ case tok_type::WHITE_SPACE: // ------------------------------------------
+ {
+ if (recently_seen_pound_define)
+ {
+ if (contains_unescaped_newline(token))
+ {
+ recently_seen_pound_define = false;
+ recently_seen_paren_0 = false;
+ recently_seen_preprocessor = false;
+
+ // this is an end of a potential declaration
+ token_accum.swap(last_full_declaration);
+ token_accum.clear();
+ }
+ }
+
+ if (recently_seen_preprocessor)
+ {
+ if (contains_unescaped_newline(token))
+ {
+ recently_seen_preprocessor = false;
+
+ last_full_declaration.clear();
+ token_accum.clear();
+ }
+ }
+ }break;
+
+ default: // ------------------------------------------
+ {
+ token_accum.push_back(make_pair(type,token));
+ at_top_of_new_scope = false;
+ }break;
+ }
+
+
+ tok.get_token(type, token);
+ }
+}
+
+// ----------------------------------------------------------------------------------------
+
+string get_function_name (
+ const std::vector<std::pair<int,string> >& declaration
+)
+{
+ string name;
+
+ bool contains_operator = false;
+ unsigned long operator_pos = 0;
+ for (unsigned long i = 0; i < declaration.size(); ++i)
+ {
+ if (declaration[i].first == tok_type::KEYWORD &&
+ declaration[i].second == "operator")
+ {
+ contains_operator = true;
+ operator_pos = i;
+ break;
+ }
+ }
+
+
+ // find the opening ( for the function
+ unsigned long paren_pos = 0;
+ long paren_count = 0;
+ for (long i = declaration.size()-1; i >= 0; --i)
+ {
+ if (declaration[i].first == tok_type::OTHER &&
+ declaration[i].second == ")")
+ {
+ ++paren_count;
+ }
+ else if (declaration[i].first == tok_type::OTHER &&
+ declaration[i].second == "(")
+ {
+ --paren_count;
+ if (paren_count == 0)
+ {
+ paren_pos = i;
+ break;
+ }
+ }
+ }
+
+
+ if (contains_operator)
+ {
+ name = declaration[operator_pos].second;
+ for (unsigned long i = operator_pos+1; i < paren_pos; ++i)
+ {
+ if (declaration[i].first == tok_type::IDENTIFIER || declaration[i].first == tok_type::KEYWORD)
+ {
+ name += " ";
+ }
+
+ name += declaration[i].second;
+ }
+ }
+ else
+ {
+ // if this is a destructor then include the ~
+ if (paren_pos > 1 && declaration[paren_pos-2].second == "~")
+ name = "~" + declaration[paren_pos-1].second;
+ else if (paren_pos > 0)
+ name = declaration[paren_pos-1].second;
+
+
+ }
+
+ return name;
+}
+
+// ----------------------------------------------------------------------------------------
+
+string pretty_print_declaration (
+ const std::vector<std::pair<int,string> >& decl
+)
+{
+ string temp;
+ long angle_count = 0;
+ long paren_count = 0;
+
+ if (decl.size() == 0)
+ return temp;
+
+ temp = decl[0].second;
+
+
+ bool just_closed_template = false;
+ bool in_template = false;
+ bool last_was_scope_res = false;
+ bool seen_operator = false;
+
+ if (temp == "operator")
+ seen_operator = true;
+
+ for (unsigned long i = 1; i < decl.size(); ++i)
+ {
+ bool last_was_less_than = false;
+ if (decl[i-1].first == tok_type::OTHER && decl[i-1].second == "<")
+ last_was_less_than = true;
+
+
+ if (decl[i].first == tok_type::OTHER && decl[i].second == "<" &&
+ (decl[i-1].second != "operator" && ((i>1 && decl[i-2].second != "operator") || decl[i-1].second != "<") ))
+ ++angle_count;
+
+ if (decl[i-1].first == tok_type::KEYWORD && decl[i-1].second == "template" &&
+ decl[i].first == tok_type::OTHER && decl[i].second == "<")
+ {
+ in_template = true;
+ temp += " <\n ";
+ }
+ else if (decl[i].first == tok_type::OTHER && decl[i].second == ">")
+ {
+ // don't count angle brackets when they are part of an operator
+ if (decl[i-1].second != "operator" && ((i>1 && decl[i-2].second != "operator") || decl[i-1].second != ">"))
+ --angle_count;
+
+ if (angle_count == 0 && in_template)
+ {
+ temp += "\n >\n";
+ just_closed_template = true;
+ in_template = false;
+ }
+ else
+ {
+ temp += ">";
+ }
+ }
+ else if (decl[i].first == tok_type::OTHER && decl[i].second == "<")
+ {
+ temp += "<";
+ }
+ else if (decl[i].first == tok_type::OTHER && decl[i].second == ",")
+ {
+ if (in_template || (paren_count == 1 && angle_count == 0))
+ temp += ",\n ";
+ else
+ temp += ",";
+ }
+ else if (decl[i].first == tok_type::OTHER && decl[i].second == "&")
+ {
+ temp += "&";
+ }
+ else if (decl[i].first == tok_type::OTHER && decl[i].second == ".")
+ {
+ temp += ".";
+ }
+ else if (decl[i].first == tok_type::SINGLE_QUOTED_TEXT)
+ {
+ temp += decl[i].second;
+ }
+ else if (decl[i].first == tok_type::DOUBLE_QUOTED_TEXT)
+ {
+ temp += decl[i].second;
+ }
+ else if (decl[i-1].first == tok_type::SINGLE_QUOTED_TEXT && decl[i].second == "'")
+ {
+ temp += decl[i].second;
+ }
+ else if (decl[i-1].first == tok_type::DOUBLE_QUOTED_TEXT && decl[i].second == "\"")
+ {
+ temp += decl[i].second;
+ }
+ else if (decl[i].first == tok_type::OTHER && decl[i].second == "[")
+ {
+ temp += "[";
+ }
+ else if (decl[i].first == tok_type::OTHER && decl[i].second == "]")
+ {
+ temp += "]";
+ }
+ else if (decl[i].first == tok_type::OTHER && decl[i].second == "-")
+ {
+ temp += "-";
+ }
+ else if (decl[i].first == tok_type::NUMBER)
+ {
+ if (decl[i-1].second == "=")
+ temp += " " + decl[i].second;
+ else
+ temp += decl[i].second;
+ }
+ else if (decl[i].first == tok_type::OTHER && decl[i].second == "*")
+ {
+ temp += "*";
+ }
+ else if (decl[i].first == tok_type::KEYWORD && decl[i].second == "operator")
+ {
+ temp += "\noperator";
+ seen_operator = true;
+ }
+ else if (decl[i].first == tok_type::OTHER && decl[i].second == ":" &&
+ (decl[i-1].second == ":" || (i+1<decl.size() && decl[i+1].second == ":") ) )
+ {
+ temp += ":";
+ last_was_scope_res = true;
+ }
+ else if (decl[i].first == tok_type::OTHER && decl[i].second == "(")
+ {
+ const bool next_is_paren = (i+1 < decl.size() && decl[i+1].first == tok_type::OTHER && decl[i+1].second == ")");
+
+ if (paren_count == 0 && next_is_paren == false && in_template == false)
+ temp += " (\n ";
+ else
+ temp += "(";
+
+ ++paren_count;
+ }
+ else if (decl[i].first == tok_type::OTHER && decl[i].second == ")")
+ {
+ --paren_count;
+ if (paren_count == 0 && decl[i-1].second != "(" && in_template == false)
+ temp += "\n)";
+ else
+ temp += ")";
+ }
+ else if (decl[i].first == tok_type::IDENTIFIER && i+1 < decl.size() &&
+ decl[i+1].first == tok_type::OTHER && decl[i+1].second == "(")
+ {
+ if (just_closed_template || paren_count != 0 || decl[i-1].second == "~")
+ temp += decl[i].second;
+ else if (seen_operator)
+ temp += " " + decl[i].second;
+ else
+ temp += "\n" + decl[i].second;
+
+ just_closed_template = false;
+ last_was_scope_res = false;
+ }
+ else
+ {
+ if (just_closed_template || last_was_scope_res || last_was_less_than ||
+ (seen_operator && paren_count == 0 && decl[i].first == tok_type::OTHER ) ||
+ ((decl[i].first == tok_type::KEYWORD || decl[i].first == tok_type::IDENTIFIER) && i>0 && decl[i-1].second == "("))
+ temp += decl[i].second;
+ else
+ temp += " " + decl[i].second;
+
+ just_closed_template = false;
+ last_was_scope_res = false;
+ }
+
+
+
+ }
+
+ return temp;
+}
+
+// ----------------------------------------------------------------------------------------
+
+string format_comment (
+ const string& comment,
+ const unsigned long expand_tabs
+)
+{
+ if (comment.size() <= 6)
+ return "";
+
+ string temp = trim(trim(comment.substr(3,comment.size()-6), " \t"), "\n\r");
+
+
+ // if we should expand tabs to spaces
+ if (expand_tabs != 0)
+ {
+ unsigned long column = 0;
+ string str;
+ for (unsigned long i = 0; i < temp.size(); ++i)
+ {
+ if (temp[i] == '\t')
+ {
+ const unsigned long num_spaces = expand_tabs - column%expand_tabs;
+ column += num_spaces;
+ str.insert(str.end(), num_spaces, ' ');
+ }
+ else if (temp[i] == '\n' || temp[i] == '\r')
+ {
+ str += temp[i];
+ column = 0;
+ }
+ else
+ {
+ str += temp[i];
+ ++column;
+ }
+ }
+
+ // put str into temp
+ str.swap(temp);
+ }
+
+ // now figure out what the smallest amount of leading white space is and remove it from each line.
+ unsigned long num_whitespace = 100000;
+
+ string::size_type pos1 = 0, pos2 = 0;
+
+ while (pos1 != string::npos)
+ {
+ // find start of non-white-space
+ pos2 = temp.find_first_not_of(" \t",pos1);
+
+ // if this is a line of just white space then ignore it
+ if (pos2 != string::npos && temp[pos2] != '\n' && temp[pos2] != '\r')
+ {
+ if (pos2-pos1 < num_whitespace)
+ num_whitespace = pos2-pos1;
+ }
+
+ // find end-of-line
+ pos1 = temp.find_first_of("\n\r", pos2);
+ // find start of next line
+ pos2 = temp.find_first_not_of("\n\r", pos1);
+ pos1 = pos2;
+ }
+
+ // now remove the leading white space
+ string temp2;
+ unsigned long counter = 0;
+ for (unsigned long i = 0; i < temp.size(); ++i)
+ {
+ // if we are looking at a new line
+ if (temp[i] == '\n' || temp[i] == '\r')
+ {
+ counter = 0;
+ }
+ else if (counter < num_whitespace)
+ {
+ ++counter;
+ continue;
+ }
+
+ temp2 += temp[i];
+ }
+
+ return temp2;
+}
+
+// ----------------------------------------------------------------------------------------
+
+typedef_record convert_tok_typedef_record (
+ const tok_typedef_record& rec
+)
+{
+ typedef_record temp;
+ temp.declaration = pretty_print_declaration(rec.declaration);
+ return temp;
+}
+
+// ----------------------------------------------------------------------------------------
+
+variable_record convert_tok_variable_record (
+ const tok_variable_record& rec
+)
+{
+ variable_record temp;
+ temp.declaration = pretty_print_declaration(rec.declaration);
+ return temp;
+}
+
+// ----------------------------------------------------------------------------------------
+
+method_record convert_tok_method_record (
+ const tok_method_record& rec,
+ const unsigned long expand_tabs
+)
+{
+ method_record temp;
+
+ temp.comment = format_comment(rec.comment, expand_tabs);
+ temp.name = get_function_name(rec.declaration);
+ temp.declaration = pretty_print_declaration(rec.declaration);
+ return temp;
+}
+
+// ----------------------------------------------------------------------------------------
+
+class_record convert_tok_class_record (
+ const tok_class_record& rec,
+ const unsigned long expand_tabs
+)
+{
+ class_record crec;
+
+
+ crec.scope = rec.scope;
+ crec.file = rec.file;
+ crec.comment = format_comment(rec.comment, expand_tabs);
+
+ crec.name.clear();
+
+ // find the first class token
+ for (unsigned long i = 0; i+1 < rec.declaration.size(); ++i)
+ {
+ if (rec.declaration[i].first == tok_type::KEYWORD &&
+ (rec.declaration[i].second == "class" ||
+ rec.declaration[i].second == "struct" )
+ )
+ {
+ crec.name = rec.declaration[i+1].second;
+ break;
+ }
+ }
+
+ crec.declaration = pretty_print_declaration(rec.declaration);
+
+ for (unsigned long i = 0; i < rec.public_typedefs.size(); ++i)
+ crec.public_typedefs.push_back(convert_tok_typedef_record(rec.public_typedefs[i]));
+
+ for (unsigned long i = 0; i < rec.public_variables.size(); ++i)
+ crec.public_variables.push_back(convert_tok_variable_record(rec.public_variables[i]));
+
+ for (unsigned long i = 0; i < rec.protected_typedefs.size(); ++i)
+ crec.protected_typedefs.push_back(convert_tok_typedef_record(rec.protected_typedefs[i]));
+
+ for (unsigned long i = 0; i < rec.protected_variables.size(); ++i)
+ crec.protected_variables.push_back(convert_tok_variable_record(rec.protected_variables[i]));
+
+ for (unsigned long i = 0; i < rec.public_methods.size(); ++i)
+ crec.public_methods.push_back(convert_tok_method_record(rec.public_methods[i], expand_tabs));
+
+ for (unsigned long i = 0; i < rec.protected_methods.size(); ++i)
+ crec.protected_methods.push_back(convert_tok_method_record(rec.protected_methods[i], expand_tabs));
+
+ for (unsigned long i = 0; i < rec.public_inner_classes.size(); ++i)
+ crec.public_inner_classes.push_back(convert_tok_class_record(rec.public_inner_classes[i], expand_tabs));
+
+ for (unsigned long i = 0; i < rec.protected_inner_classes.size(); ++i)
+ crec.protected_inner_classes.push_back(convert_tok_class_record(rec.protected_inner_classes[i], expand_tabs));
+
+
+ return crec;
+}
+
+// ----------------------------------------------------------------------------------------
+
+function_record convert_tok_function_record (
+ const tok_function_record& rec,
+ const unsigned long expand_tabs
+)
+{
+ function_record temp;
+
+ temp.scope = rec.scope;
+ temp.file = rec.file;
+ temp.comment = format_comment(rec.comment, expand_tabs);
+ temp.name = get_function_name(rec.declaration);
+ temp.declaration = pretty_print_declaration(rec.declaration);
+
+ return temp;
+}
+
+// ----------------------------------------------------------------------------------------
+
+void convert_to_normal_records (
+ const std::vector<tok_function_record>& tok_functions,
+ const std::vector<tok_class_record>& tok_classes,
+ const unsigned long expand_tabs,
+ std::vector<function_record>& functions,
+ std::vector<class_record>& classes
+)
+{
+ functions.clear();
+ classes.clear();
+
+
+ for (unsigned long i = 0; i < tok_functions.size(); ++i)
+ {
+ functions.push_back(convert_tok_function_record(tok_functions[i], expand_tabs));
+ }
+
+
+ for (unsigned long i = 0; i < tok_classes.size(); ++i)
+ {
+ classes.push_back(convert_tok_class_record(tok_classes[i], expand_tabs));
+ }
+
+
+}
+
+// ----------------------------------------------------------------------------------------
+
+string add_entity_ref (const string& str)
+{
+ string temp;
+ for (unsigned long i = 0; i < str.size(); ++i)
+ {
+ if (str[i] == '&')
+ temp += "&amp;";
+ else if (str[i] == '<')
+ temp += "&lt;";
+ else if (str[i] == '>')
+ temp += "&gt;";
+ else
+ temp += str[i];
+ }
+ return temp;
+}
+
+// ----------------------------------------------------------------------------------------
+
+string flip_slashes (string str)
+{
+ for (unsigned long i = 0; i < str.size(); ++i)
+ {
+ if (str[i] == '\\')
+ str[i] = '/';
+ }
+ return str;
+}
+
+// ----------------------------------------------------------------------------------------
+
+void write_as_xml (
+ const function_record& rec,
+ ostream& fout
+)
+{
+ fout << " <function>\n";
+ fout << " <name>" << add_entity_ref(rec.name) << "</name>\n";
+ fout << " <scope>" << add_entity_ref(rec.scope) << "</scope>\n";
+ fout << " <declaration>" << add_entity_ref(rec.declaration) << "</declaration>\n";
+ fout << " <file>" << flip_slashes(add_entity_ref(rec.file)) << "</file>\n";
+ fout << " <comment>" << add_entity_ref(rec.comment) << "</comment>\n";
+ fout << " </function>\n";
+}
+
+// ----------------------------------------------------------------------------------------
+
+void write_as_xml (
+ const class_record& rec,
+ ostream& fout,
+ unsigned long indent
+)
+{
+ const string pad(indent, ' ');
+
+ fout << pad << "<class>\n";
+ fout << pad << " <name>" << add_entity_ref(rec.name) << "</name>\n";
+ fout << pad << " <scope>" << add_entity_ref(rec.scope) << "</scope>\n";
+ fout << pad << " <declaration>" << add_entity_ref(rec.declaration) << "</declaration>\n";
+ fout << pad << " <file>" << flip_slashes(add_entity_ref(rec.file)) << "</file>\n";
+ fout << pad << " <comment>" << add_entity_ref(rec.comment) << "</comment>\n";
+
+
+ if (rec.public_typedefs.size() > 0)
+ {
+ fout << pad << " <public_typedefs>\n";
+ for (unsigned long i = 0; i < rec.public_typedefs.size(); ++i)
+ {
+ fout << pad << " <typedef>" << add_entity_ref(rec.public_typedefs[i].declaration) << "</typedef>\n";
+ }
+ fout << pad << " </public_typedefs>\n";
+ }
+
+
+ if (rec.public_variables.size() > 0)
+ {
+ fout << pad << " <public_variables>\n";
+ for (unsigned long i = 0; i < rec.public_variables.size(); ++i)
+ {
+ fout << pad << " <variable>" << add_entity_ref(rec.public_variables[i].declaration) << "</variable>\n";
+ }
+ fout << pad << " </public_variables>\n";
+ }
+
+ if (rec.protected_typedefs.size() > 0)
+ {
+ fout << pad << " <protected_typedefs>\n";
+ for (unsigned long i = 0; i < rec.protected_typedefs.size(); ++i)
+ {
+ fout << pad << " <typedef>" << add_entity_ref(rec.protected_typedefs[i].declaration) << "</typedef>\n";
+ }
+ fout << pad << " </protected_typedefs>\n";
+ }
+
+
+ if (rec.protected_variables.size() > 0)
+ {
+ fout << pad << " <protected_variables>\n";
+ for (unsigned long i = 0; i < rec.protected_variables.size(); ++i)
+ {
+ fout << pad << " <variable>" << add_entity_ref(rec.protected_variables[i].declaration) << "</variable>\n";
+ }
+ fout << pad << " </protected_variables>\n";
+ }
+
+
+ if (rec.public_methods.size() > 0)
+ {
+ fout << pad << " <public_methods>\n";
+ for (unsigned long i = 0; i < rec.public_methods.size(); ++i)
+ {
+ fout << pad << " <method>\n";
+ fout << pad << " <name>" << add_entity_ref(rec.public_methods[i].name) << "</name>\n";
+ fout << pad << " <declaration>" << add_entity_ref(rec.public_methods[i].declaration) << "</declaration>\n";
+ fout << pad << " <comment>" << add_entity_ref(rec.public_methods[i].comment) << "</comment>\n";
+ fout << pad << " </method>\n";
+ }
+ fout << pad << " </public_methods>\n";
+ }
+
+
+ if (rec.protected_methods.size() > 0)
+ {
+ fout << pad << " <protected_methods>\n";
+ for (unsigned long i = 0; i < rec.protected_methods.size(); ++i)
+ {
+ fout << pad << " <method>\n";
+ fout << pad << " <name>" << add_entity_ref(rec.protected_methods[i].name) << "</name>\n";
+ fout << pad << " <declaration>" << add_entity_ref(rec.protected_methods[i].declaration) << "</declaration>\n";
+ fout << pad << " <comment>" << add_entity_ref(rec.protected_methods[i].comment) << "</comment>\n";
+ fout << pad << " </method>\n";
+ }
+ fout << pad << " </protected_methods>\n";
+ }
+
+
+ if (rec.public_inner_classes.size() > 0)
+ {
+ fout << pad << " <public_inner_classes>\n";
+ for (unsigned long i = 0; i < rec.public_inner_classes.size(); ++i)
+ {
+ write_as_xml(rec.public_inner_classes[i], fout, indent+4);
+ }
+ fout << pad << " </public_inner_classes>\n";
+ }
+
+ if (rec.protected_inner_classes.size() > 0)
+ {
+ fout << pad << " <protected_inner_classes>\n";
+ for (unsigned long i = 0; i < rec.protected_inner_classes.size(); ++i)
+ {
+ write_as_xml(rec.protected_inner_classes[i], fout, indent+4);
+ }
+ fout << pad << " </protected_inner_classes>\n";
+ }
+
+
+ fout << pad << "</class>\n";
+}
+
+// ----------------------------------------------------------------------------------------
+
+void save_to_xml_file (
+ const std::vector<function_record>& functions,
+ const std::vector<class_record>& classes
+)
+{
+ ofstream fout("output.xml");
+
+ fout << "<!-- This XML file was generated using the htmlify tool available from http://dlib.net. -->" << endl;
+ fout << "<code>" << endl;
+
+ fout << " <classes>" << endl;
+ for (unsigned long i = 0; i < classes.size(); ++i)
+ {
+ write_as_xml(classes[i], fout, 4);
+ fout << "\n";
+ }
+ fout << " </classes>\n\n" << endl;
+
+
+ fout << " <global_functions>" << endl;
+ for (unsigned long i = 0; i < functions.size(); ++i)
+ {
+ write_as_xml(functions[i], fout);
+ fout << "\n";
+ }
+ fout << " </global_functions>" << endl;
+
+ fout << "</code>" << endl;
+}
+
+// ----------------------------------------------------------------------------------------
+
+void generate_xml_markup(
+ const cmd_line_parser<char>::check_1a_c& parser,
+ const std::string& filter,
+ const unsigned long search_depth,
+ const unsigned long expand_tabs
+)
+{
+
+ // first figure out which files should be processed
+ std::vector<std::pair<string,string> > files;
+ obtain_list_of_files(parser, filter, search_depth, files);
+
+
+ std::vector<tok_function_record> tok_functions;
+ std::vector<tok_class_record> tok_classes;
+
+ for (unsigned long i = 0; i < files.size(); ++i)
+ {
+ ifstream fin(files[i].second.c_str());
+ if (!fin)
+ {
+ cerr << "Error opening file: " << files[i].second << endl;
+ return;
+ }
+ process_file(fin, files[i].first, tok_functions, tok_classes);
+ }
+
+ std::vector<function_record> functions;
+ std::vector<class_record> classes;
+
+ convert_to_normal_records(tok_functions, tok_classes, expand_tabs, functions, classes);
+
+ save_to_xml_file(functions, classes);
+}
+
+// ----------------------------------------------------------------------------------------
+
diff --git a/ml/dlib/tools/htmlify/to_xml.h b/ml/dlib/tools/htmlify/to_xml.h
new file mode 100644
index 000000000..4bdf3f00e
--- /dev/null
+++ b/ml/dlib/tools/htmlify/to_xml.h
@@ -0,0 +1,22 @@
+#ifndef DLIB_HTMLIFY_TO_XmL_H__
+#define DLIB_HTMLIFY_TO_XmL_H__
+
+#include "dlib/cmd_line_parser.h"
+#include <string>
+
+void generate_xml_markup(
+ const dlib::cmd_line_parser<char>::check_1a_c& parser,
+ const std::string& filter,
+ const unsigned long search_depth,
+ const unsigned long expand_tabs
+);
+/*!
+ ensures
+ - reads all the files indicated by the parser arguments and converts them
+ to XML. The output will be stored in the output.xml file.
+ - if (expand_tabs != 0) then
+ - tabs will be replaced with expand_tabs spaces inside comment blocks
+!*/
+
+#endif // DLIB_HTMLIFY_TO_XmL_H__
+
diff --git a/ml/dlib/tools/htmlify/to_xml_example/bigminus.gif b/ml/dlib/tools/htmlify/to_xml_example/bigminus.gif
new file mode 100644
index 000000000..aea8e5c01
--- /dev/null
+++ b/ml/dlib/tools/htmlify/to_xml_example/bigminus.gif
Binary files differ
diff --git a/ml/dlib/tools/htmlify/to_xml_example/bigplus.gif b/ml/dlib/tools/htmlify/to_xml_example/bigplus.gif
new file mode 100644
index 000000000..6bee68e21
--- /dev/null
+++ b/ml/dlib/tools/htmlify/to_xml_example/bigplus.gif
Binary files differ
diff --git a/ml/dlib/tools/htmlify/to_xml_example/example.xml b/ml/dlib/tools/htmlify/to_xml_example/example.xml
new file mode 100644
index 000000000..472a4a5e1
--- /dev/null
+++ b/ml/dlib/tools/htmlify/to_xml_example/example.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<?xml-stylesheet type="text/xsl" href="stylesheet.xsl"?>
+
+<doc>
+ <title>Documented Code</title>
+ <body from_file="output.xml"/>
+</doc>
+
diff --git a/ml/dlib/tools/htmlify/to_xml_example/minus.gif b/ml/dlib/tools/htmlify/to_xml_example/minus.gif
new file mode 100644
index 000000000..1deac2fe1
--- /dev/null
+++ b/ml/dlib/tools/htmlify/to_xml_example/minus.gif
Binary files differ
diff --git a/ml/dlib/tools/htmlify/to_xml_example/output.xml b/ml/dlib/tools/htmlify/to_xml_example/output.xml
new file mode 100644
index 000000000..95e4de6ae
--- /dev/null
+++ b/ml/dlib/tools/htmlify/to_xml_example/output.xml
@@ -0,0 +1,49 @@
+<!-- This XML file was generated using the htmlify tool available from http://dlib.net. -->
+<code>
+ <classes>
+ <class>
+ <name>test</name>
+ <scope></scope>
+ <declaration>class test</declaration>
+ <file>test.cpp</file>
+ <comment>WHAT THIS OBJECT REPRESENTS
+ This is a simple test class that doesn't do anything</comment>
+ <public_typedefs>
+ <typedef>typedef int type</typedef>
+ </public_typedefs>
+ <public_methods>
+ <method>
+ <name>test</name>
+ <declaration>test()</declaration>
+ <comment>ensures
+ - constructs a test object</comment>
+ </method>
+ <method>
+ <name>print</name>
+ <declaration>void
+print() const</declaration>
+ <comment>ensures
+ - prints a message to the screen</comment>
+ </method>
+ </public_methods>
+ </class>
+
+ </classes>
+
+
+ <global_functions>
+ <function>
+ <name>add_numbers</name>
+ <scope></scope>
+ <declaration>int
+add_numbers (
+ int a,
+ int b
+)</declaration>
+ <file>test.cpp</file>
+ <comment>ensures
+ - returns a + b</comment>
+ </function>
+
+ </global_functions>
+</code>
diff --git a/ml/dlib/tools/htmlify/to_xml_example/plus.gif b/ml/dlib/tools/htmlify/to_xml_example/plus.gif
new file mode 100644
index 000000000..2d15c1417
--- /dev/null
+++ b/ml/dlib/tools/htmlify/to_xml_example/plus.gif
Binary files differ
diff --git a/ml/dlib/tools/htmlify/to_xml_example/stylesheet.xsl b/ml/dlib/tools/htmlify/to_xml_example/stylesheet.xsl
new file mode 100644
index 000000000..7a44862a3
--- /dev/null
+++ b/ml/dlib/tools/htmlify/to_xml_example/stylesheet.xsl
@@ -0,0 +1,354 @@
+<?xml version="1.0" encoding="ISO-8859-1" ?>
+
+<!--
+ To the extent possible under law, Davis E King has waived all copyright and
+ related or neighboring rights to dlib documentation (XML, HTML, and XSLT files).
+ This work is published from United States.
+-->
+
+<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
+ <xsl:output method='html' version='1.0' encoding='UTF-8' indent='yes' />
+
+
+ <!-- ************************************************************************* -->
+
+ <xsl:variable name="lcletters">abcdefghijklmnopqrstuvwxyz </xsl:variable>
+ <xsl:variable name="ucletters">ABCDEFGHIJKLMNOPQRSTUVWXYZ </xsl:variable>
+
+ <!-- ************************************************************************* -->
+
+ <xsl:template match="/doc">
+ <html>
+ <head>
+ <title>
+ <xsl:if test="title">
+ <xsl:value-of select="title" />
+ </xsl:if>
+ </title>
+
+
+ <!-- [client side code for collapsing and unfolding branches] -->
+ <script language="JavaScript">
+
+ // ---------------------------------------------
+ // --- Name: Easy DHTML Treeview --
+ // --- Author: D.D. de Kerf --
+ // --- Version: 0.2 Date: 13-6-2001 --
+ // ---------------------------------------------
+ function Toggle(node)
+ {
+ // Unfold the branch if it isn't visible
+ var next_node = node.nextSibling;
+ if (next_node.style.display == 'none')
+ {
+ // Change the image (if there is an image)
+ if (node.childNodes.length > 0)
+ {
+ if (node.childNodes.length > 0)
+ {
+ if (node.childNodes.item(0).nodeName == "IMG")
+ {
+ node.childNodes.item(0).src = "minus.gif";
+ }
+ }
+ }
+
+ next_node.style.display = 'block';
+ }
+ // Collapse the branch if it IS visible
+ else
+ {
+ // Change the image (if there is an image)
+ if (node.childNodes.length > 0)
+ {
+ if (node.childNodes.length > 0)
+ {
+ if (node.childNodes.item(0).nodeName == "IMG")
+ {
+ node.childNodes.item(0).src = "plus.gif";
+ }
+ }
+ }
+
+ next_node.style.display = 'none';
+ }
+
+ }
+ function BigToggle(node)
+ {
+ // Unfold the branch if it isn't visible
+ var next_node = node.nextSibling;
+ if (next_node.style.display == 'none')
+ {
+ // Change the image (if there is an image)
+ if (node.childNodes.length > 0)
+ {
+ if (node.childNodes.length > 0)
+ {
+ if (node.childNodes.item(0).nodeName == "IMG")
+ {
+ node.childNodes.item(0).src = "bigminus.gif";
+ }
+ }
+ }
+
+ next_node.style.display = 'block';
+ }
+ // Collapse the branch if it IS visible
+ else
+ {
+ // Change the image (if there is an image)
+ if (node.childNodes.length > 0)
+ {
+ if (node.childNodes.length > 0)
+ {
+ if (node.childNodes.item(0).nodeName == "IMG")
+ {
+ node.childNodes.item(0).src = "bigplus.gif";
+ }
+ }
+ }
+
+ next_node.style.display = 'none';
+ }
+
+ }
+ </script>
+
+ <style type="text/css">
+ pre {margin:0px;}
+
+ ul.tree li { list-style: none; margin-left:10px;}
+ ul.tree { margin:0px; padding:0px; margin-left:5px; font-size:0.95em; }
+ ul.tree li ul { margin-left:10px; padding:0px; }
+
+ div#component {
+ background-color:white;
+ border: 2px solid rgb(102,102,102);
+ text-align:left;
+ margin-top: 1.5em;
+ padding: 0.7em;
+ }
+
+ div#function {
+ background-color:white;
+ border: 2px solid rgb(102,102,255);
+ text-align:left;
+ margin-top: 0.3em;
+ padding: 0.3em;
+ }
+
+ div#class {
+ background-color:white;
+ border: 2px solid rgb(255,102,102);
+ text-align:left;
+ margin-top: 0.3em;
+ padding: 0.3em;
+ }
+
+ </style>
+ </head>
+ <body>
+ <xsl:if test="title">
+ <center><h1> <xsl:value-of select="title" /> </h1></center>
+ </xsl:if>
+ <xsl:apply-templates select="body"/>
+ </body>
+ </html>
+ </xsl:template>
+
+
+
+
+
+ <!-- ************************************************************************* -->
+
+ <xsl:template match="body">
+ <xsl:choose>
+ <xsl:when test="@from_file">
+ <xsl:apply-templates select="document(@from_file)"/>
+ <xsl:apply-templates/>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:apply-templates/>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:template>
+
+
+ <!-- ************************************************************************* -->
+ <!-- ************************************************************************* -->
+ <!-- XSLT for dealing with <code> blocks generated by the htmlify to-xml option -->
+ <!-- ************************************************************************* -->
+ <!-- ************************************************************************* -->
+
+ <xsl:template match="code">
+
+ <h1>Classes and Structs:</h1>
+ <xsl:for-each select="classes/class">
+ <xsl:sort select="translate(concat(name,.),$lcletters, $ucletters)"/>
+ <xsl:apply-templates select="."/>
+ </xsl:for-each>
+
+ <h1>Global Functions:</h1>
+ <xsl:for-each select="global_functions/function">
+ <xsl:sort select="translate(concat(name,.),$lcletters, $ucletters)"/>
+ <div id="function">
+ <a onclick="Toggle(this)" style="cursor: pointer"><img src="plus.gif" border="0"/><font color="blue">
+ <u><b><xsl:value-of select="name"/>()</b></u></font></a>
+ <div style="display:none;">
+ <br/>
+ <xsl:if test="scope != ''">
+ <u>Scope</u>: <xsl:value-of select="scope"/> <br/>
+ </xsl:if>
+ <u>File</u>: <xsl:value-of select="file"/> <br/><br/>
+ <div style="margin-left:1.5em">
+ <pre style="font-size:1.1em;"><xsl:value-of select="declaration"/>;</pre>
+ <font color="#009900"><pre><xsl:value-of select="comment"/></pre></font>
+ </div>
+ <br/>
+ </div>
+ </div>
+ </xsl:for-each>
+
+ </xsl:template>
+
+ <!-- ************************************************************************* -->
+
+ <xsl:template match="class">
+ <div id="class">
+ <a onclick="Toggle(this)" style="cursor: pointer"><img src="plus.gif" border="0"/><font color="blue">
+ <u><b><xsl:value-of select="name"/></b></u></font></a>
+ <div style="display:none;">
+ <br/>
+ <xsl:if test="scope != ''">
+ <u>Scope</u>: <xsl:value-of select="scope"/> <br/>
+ </xsl:if>
+ <u>File</u>: <xsl:value-of select="file"/> <br/><br/>
+ <div style="margin-left:1.5em">
+ <pre style="font-size:1.1em;"><xsl:value-of select="declaration"/>;</pre> <br/>
+ <font color="#009900"><pre><xsl:value-of select="comment"/></pre></font> <br/>
+ </div>
+
+ <xsl:if test="protected_typedefs">
+ <a onclick="BigToggle(this)" style="cursor: pointer"><img src="bigplus.gif" border="0"/><font color="blue">
+ <u style="font-size:2em">Protected Typedefs</u></font></a>
+ <div style="display:none;">
+ <ul>
+ <xsl:for-each select="protected_typedefs/typedef">
+ <li><xsl:value-of select="."/>;</li>
+ </xsl:for-each>
+ </ul>
+ </div>
+ <br/>
+ </xsl:if>
+
+ <xsl:if test="public_typedefs">
+ <a onclick="BigToggle(this)" style="cursor: pointer"><img src="bigplus.gif" border="0" style="size:2em"/><font color="blue">
+ <u style="font-size:2em">Public Typedefs</u></font></a>
+ <div style="display:none;">
+ <ul>
+ <xsl:for-each select="public_typedefs/typedef">
+ <li><xsl:value-of select="."/>;</li>
+ </xsl:for-each>
+ </ul>
+ </div>
+ <br/>
+ </xsl:if>
+
+ <xsl:if test="protected_variables">
+ <a onclick="BigToggle(this)" style="cursor: pointer"><img src="bigplus.gif" border="0"/><font color="blue">
+ <u style="font-size:2em">Protected Variables</u></font></a>
+ <div style="display:none;">
+ <ul>
+ <xsl:for-each select="protected_variables/variable">
+ <li><xsl:value-of select="."/>;</li>
+ </xsl:for-each>
+ </ul>
+ </div>
+ <br/>
+ </xsl:if>
+
+ <xsl:if test="public_variables">
+ <a onclick="BigToggle(this)" style="cursor: pointer"><img src="bigplus.gif" border="0"/><font color="blue">
+ <u style="font-size:2em">Public Variables</u></font></a>
+ <div style="display:none;">
+ <ul>
+ <xsl:for-each select="public_variables/variable">
+ <li><xsl:value-of select="."/>;</li>
+ </xsl:for-each>
+ </ul>
+ </div>
+ <br/>
+ </xsl:if>
+
+ <xsl:if test="protected_methods">
+ <a onclick="BigToggle(this)" style="cursor: pointer"><img src="bigplus.gif" border="0"/><font color="blue">
+ <u style="font-size:2em">Protected Methods</u></font></a>
+ <div style="display:none;">
+ <xsl:for-each select="protected_methods/method">
+ <div id="function">
+ <u>Method Name</u>: <b><xsl:value-of select="name"/></b> <br/><br/>
+ <div style="margin-left:1.5em">
+ <pre style="font-size:1.1em;"><xsl:value-of select="declaration"/>;</pre>
+ <font color="#009900"><pre><xsl:value-of select="comment"/></pre></font> <br/>
+ </div>
+ </div>
+ </xsl:for-each>
+ </div>
+ <br/>
+ </xsl:if>
+
+ <xsl:if test="public_methods">
+ <a onclick="BigToggle(this)" style="cursor: pointer"><img src="bigplus.gif" border="0"/><font color="blue">
+ <u style="font-size:2em">Public Methods</u></font></a>
+ <div style="display:none;">
+ <xsl:for-each select="public_methods/method">
+ <div id="function">
+ <u>Method Name</u>: <b><xsl:value-of select="name"/></b> <br/><br/>
+ <div style="margin-left:1.5em">
+ <pre style="font-size:1.1em;"><xsl:value-of select="declaration"/>;</pre>
+ <font color="#009900"><pre><xsl:value-of select="comment"/></pre></font> <br/>
+ </div>
+ </div>
+ </xsl:for-each>
+ </div>
+ <br/>
+ </xsl:if>
+
+ <xsl:if test="protected_inner_classes">
+ <a onclick="BigToggle(this)" style="cursor: pointer"><img src="bigplus.gif" border="0"/><font color="blue">
+ <u style="font-size:2em">Protected Inner Classes</u></font></a>
+ <div style="display:none;">
+ <xsl:for-each select="protected_inner_classes/class">
+ <xsl:apply-templates select="."/>
+ </xsl:for-each>
+ </div>
+ <br/>
+ </xsl:if>
+
+ <xsl:if test="public_inner_classes">
+ <a onclick="BigToggle(this)" style="cursor: pointer"><img src="bigplus.gif" border="0"/><font color="blue">
+ <u style="font-size:2em">Public Inner Classes</u></font></a>
+ <div style="display:none;">
+ <xsl:for-each select="public_inner_classes/class">
+ <xsl:apply-templates select="."/>
+ </xsl:for-each>
+ </div>
+ <br/>
+ </xsl:if>
+
+ </div>
+ </div>
+ </xsl:template>
+
+
+ <!-- ************************************************************************* -->
+ <!-- ************************************************************************* -->
+ <!-- ************************************************************************* -->
+ <!-- ************************************************************************* -->
+
+
+
+
+</xsl:stylesheet>
diff --git a/ml/dlib/tools/htmlify/to_xml_example/test.cpp b/ml/dlib/tools/htmlify/to_xml_example/test.cpp
new file mode 100644
index 000000000..edbdfff54
--- /dev/null
+++ b/ml/dlib/tools/htmlify/to_xml_example/test.cpp
@@ -0,0 +1,78 @@
+#include <iostream>
+
+// ----------------------------------------------------------------------------------------
+
+using namespace std;
+
+// ----------------------------------------------------------------------------------------
+
+class test
+{
+ /*!
+ WHAT THIS OBJECT REPRESENTS
+ This is a simple test class that doesn't do anything
+ !*/
+
+public:
+
+ typedef int type;
+
+ test ();
+ /*!
+ ensures
+ - constructs a test object
+ !*/
+
+ void print () const;
+ /*!
+ ensures
+ - prints a message to the screen
+ !*/
+
+};
+
+// ----------------------------------------------------------------------------------------
+
+test::test() {}
+
+void test::print() const
+{
+ cout << "A message!" << endl;
+}
+
+// ----------------------------------------------------------------------------------------
+
+int add_numbers (
+ int a,
+ int b
+)
+/*!
+ ensures
+ - returns a + b
+!*/
+{
+ return a + b;
+}
+
+// ----------------------------------------------------------------------------------------
+
+void a_function (
+)
+/*!P
+ This is a function which won't show up in the output of htmlify --to-xml
+ because of the presence of the P in the above /*!P above.
+!*/
+{
+}
+
+// ----------------------------------------------------------------------------------------
+
+int main()
+{
+ test a;
+ a.print();
+}
+
+// ----------------------------------------------------------------------------------------
+
+