1/* 2Copyright (c) 2016-present, Przemyslaw Skibinski 3All rights reserved. 4 5BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) 6 7Redistribution and use in source and binary forms, with or without 8modification, are permitted provided that the following conditions are 9met: 10 11* Redistributions of source code must retain the above copyright 12notice, this list of conditions and the following disclaimer. 13* Redistributions in binary form must reproduce the above 14copyright notice, this list of conditions and the following disclaimer 15in the documentation and/or other materials provided with the 16distribution. 17 18THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 19"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 20LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 21A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 22OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 23SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 24LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 25DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 26THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 28OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 30You can contact the author at : 31- LZ4 homepage : http://www.lz4.org 32- LZ4 source repository : https://github.com/lz4/lz4 33*/ 34 35#include <iostream> 36#include <fstream> 37#include <sstream> 38#include <vector> 39using namespace std; 40 41 42/* trim string at the beginning and at the end */ 43void trim(string& s, string characters) 44{ 45 size_t p = s.find_first_not_of(characters); 46 s.erase(0, p); 47 48 p = s.find_last_not_of(characters); 49 if (string::npos != p) 50 s.erase(p+1); 51} 52 53 54/* trim C++ style comments */ 55void trim_comments(string &s) 56{ 57 size_t spos, epos; 58 59 spos = s.find("/*"); 60 epos = s.find("*/"); 61 s = s.substr(spos+3, epos-(spos+3)); 62} 63 64 65/* get lines until a given terminator */ 66vector<string> get_lines(vector<string>& input, int& linenum, string terminator) 67{ 68 vector<string> out; 69 string line; 70 71 while ((size_t)linenum < input.size()) { 72 line = input[linenum]; 73 74 if (terminator.empty() && line.empty()) { linenum--; break; } 75 76 size_t const epos = line.find(terminator); 77 if (!terminator.empty() && epos!=string::npos) { 78 out.push_back(line); 79 break; 80 } 81 out.push_back(line); 82 linenum++; 83 } 84 return out; 85} 86 87 88/* print line with LZ4LIB_API removed and C++ comments not bold */ 89void print_line(stringstream &sout, string line) 90{ 91 size_t spos, epos; 92 93 if (line.substr(0,11) == "LZ4LIB_API ") line = line.substr(11); 94 if (line.substr(0,12) == "LZ4FLIB_API ") line = line.substr(12); 95 spos = line.find("/*"); 96 epos = line.find("*/"); 97 if (spos!=string::npos && epos!=string::npos) { 98 sout << line.substr(0, spos); 99 sout << "</b>" << line.substr(spos) << "<b>" << '\n'; 100 } else { 101 sout << line << '\n'; 102 } 103} 104 105 106int main(int argc, char *argv[]) { 107 char exclam; 108 int linenum, chapter = 1; 109 vector<string> input, lines, comments, chapters; 110 string line, version; 111 size_t spos, l; 112 stringstream sout; 113 ifstream istream; 114 ofstream ostream; 115 116 if (argc < 4) { 117 cout << "usage: " << argv[0] << " [lz4_version] [input_file] [output_html]" << endl; 118 return 1; 119 } 120 121 version = string(argv[1]) + " Manual"; 122 123 istream.open(argv[2], ifstream::in); 124 if (!istream.is_open()) { 125 cout << "Error opening file " << argv[2] << endl; 126 return 1; 127 } 128 129 ostream.open(argv[3], ifstream::out); 130 if (!ostream.is_open()) { 131 cout << "Error opening file " << argv[3] << endl; 132 return 1; 133 } 134 135 while (getline(istream, line)) { 136 input.push_back(line); 137 } 138 139 for (linenum=0; (size_t)linenum < input.size(); linenum++) { 140 line = input[linenum]; 141 142 /* typedefs are detected and included even if uncommented */ 143 if (line.substr(0,7) == "typedef" && line.find("{")!=string::npos) { 144 lines = get_lines(input, linenum, "}"); 145 sout << "<pre><b>"; 146 for (l=0; l<lines.size(); l++) { 147 print_line(sout, lines[l]); 148 } 149 sout << "</b></pre><BR>" << endl; 150 continue; 151 } 152 153 /* comments of type / * * < and / * ! < are detected, and only function declaration is highlighted (bold) */ 154 if ((line.find("/**<")!=string::npos || line.find("/*!<")!=string::npos) 155 && line.find("*/")!=string::npos) { 156 sout << "<pre><b>"; 157 print_line(sout, line); 158 sout << "</b></pre><BR>" << endl; 159 continue; 160 } 161 162 spos = line.find("/**="); 163 if (spos==string::npos) { 164 spos = line.find("/*!"); 165 if (spos==string::npos) 166 spos = line.find("/**"); 167 if (spos==string::npos) 168 spos = line.find("/*-"); 169 if (spos==string::npos) 170 spos = line.find("/*="); 171 if (spos==string::npos) 172 continue; 173 exclam = line[spos+2]; 174 } 175 else exclam = '='; 176 177 comments = get_lines(input, linenum, "*/"); 178 if (!comments.empty()) comments[0] = line.substr(spos+3); 179 if (!comments.empty()) 180 comments[comments.size()-1] = comments[comments.size()-1].substr(0, comments[comments.size()-1].find("*/")); 181 for (l=0; l<comments.size(); l++) { 182 if (comments[l].compare(0, 2, " *") == 0) 183 comments[l] = comments[l].substr(2); 184 else if (comments[l].compare(0, 3, " *") == 0) 185 comments[l] = comments[l].substr(3); 186 trim(comments[l], "*-="); 187 } 188 while (!comments.empty() && comments[comments.size()-1].empty()) comments.pop_back(); // remove empty line at the end 189 while (!comments.empty() && comments[0].empty()) comments.erase(comments.begin()); // remove empty line at the start 190 191 /* comments of type / * ! mean: this is a function declaration; switch comments with declarations */ 192 if (exclam == '!') { 193 if (!comments.empty()) comments.erase(comments.begin()); /* remove first line like "LZ4_XXX() :" */ 194 linenum++; 195 lines = get_lines(input, linenum, ""); 196 197 sout << "<pre><b>"; 198 for (l=0; l<lines.size(); l++) { 199 print_line(sout, lines[l]); 200 } 201 sout << "</b><p>"; 202 for (l=0; l<comments.size(); l++) { 203 print_line(sout, comments[l]); 204 } 205 sout << "</p></pre><BR>" << endl << endl; 206 } else if (exclam == '=') { /* comments of type / * = and / * * = mean: use a <H3> header and show also all functions until first empty line */ 207 trim(comments[0], " "); 208 sout << "<h3>" << comments[0] << "</h3><pre>"; 209 for (l=1; l<comments.size(); l++) { 210 print_line(sout, comments[l]); 211 } 212 sout << "</pre><b><pre>"; 213 lines = get_lines(input, ++linenum, ""); 214 for (l=0; l<lines.size(); l++) { 215 print_line(sout, lines[l]); 216 } 217 sout << "</pre></b><BR>" << endl; 218 } else { /* comments of type / * * and / * - mean: this is a comment; use a <H2> header for the first line */ 219 if (comments.empty()) continue; 220 221 trim(comments[0], " "); 222 sout << "<a name=\"Chapter" << chapter << "\"></a><h2>" << comments[0] << "</h2><pre>"; 223 chapters.push_back(comments[0]); 224 chapter++; 225 226 for (l=1; l<comments.size(); l++) { 227 print_line(sout, comments[l]); 228 } 229 if (comments.size() > 1) 230 sout << "<BR></pre>" << endl << endl; 231 else 232 sout << "</pre>" << endl << endl; 233 } 234 } 235 236 ostream << "<html>\n<head>\n<meta http-equiv=\"Content-Type\" content=\"text/html; charset=ISO-8859-1\">\n<title>" << version << "</title>\n</head>\n<body>" << endl; 237 ostream << "<h1>" << version << "</h1>\n"; 238 239 ostream << "<hr>\n<a name=\"Contents\"></a><h2>Contents</h2>\n<ol>\n"; 240 for (size_t i=0; i<chapters.size(); i++) 241 ostream << "<li><a href=\"#Chapter" << i+1 << "\">" << chapters[i].c_str() << "</a></li>\n"; 242 ostream << "</ol>\n<hr>\n"; 243 244 ostream << sout.str(); 245 ostream << "</html>" << endl << "</body>" << endl; 246 247 return 0; 248} 249