diff options
Diffstat (limited to '')
-rw-r--r-- | ml/dlib/tools/htmlify/htmlify.cpp | 632 |
1 files changed, 632 insertions, 0 deletions
diff --git a/ml/dlib/tools/htmlify/htmlify.cpp b/ml/dlib/tools/htmlify/htmlify.cpp new file mode 100644 index 00000000..e822a5aa --- /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(""); +} + +// ------------------------------------------------------------------------------------------------- + |