1 /* 2 Copyright (c) 2016-present, Przemyslaw Skibinski 3 All rights reserved. 4 5 BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) 6 7 Redistribution and use in source and binary forms, with or without 8 modification, are permitted provided that the following conditions are 9 met: 10 11 * Redistributions of source code must retain the above copyright 12 notice, this list of conditions and the following disclaimer. 13 * Redistributions in binary form must reproduce the above 14 copyright notice, this list of conditions and the following disclaimer 15 in the documentation and/or other materials provided with the 16 distribution. 17 18 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 19 "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 20 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 21 A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 22 OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 23 SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 24 LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 25 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 26 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 28 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 30 You 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> 39 using namespace std; 40 41 42 /* trim string at the beginning and at the end */ 43 void 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 */ 55 void 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 */ 66 vector<string> get_lines(vector<string>& input, int& linenum, string terminator) 67 { 68 vector<string> out; 69 string line; 70 size_t epos; 71 72 while ((size_t)linenum < input.size()) { 73 line = input[linenum]; 74 75 if (terminator.empty() && line.empty()) { linenum--; break; } 76 77 epos = line.find(terminator); 78 if (!terminator.empty() && epos!=string::npos) { 79 out.push_back(line); 80 break; 81 } 82 out.push_back(line); 83 linenum++; 84 } 85 return out; 86 } 87 88 89 /* print line with LZ4LIB_API removed and C++ comments not bold */ 90 void print_line(stringstream &sout, string line) 91 { 92 size_t spos; 93 94 if (line.substr(0,11) == "LZ4LIB_API ") line = line.substr(11); 95 spos = line.find("/*"); 96 if (spos!=string::npos) { 97 sout << line.substr(0, spos); 98 sout << "</b>" << line.substr(spos) << "<b>" << endl; 99 } else { 100 // fprintf(stderr, "lines=%s\n", line.c_str()); 101 sout << line << endl; 102 } 103 } 104 105 106 int 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 = "lz4 " + 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) && line.find("*/")!=string::npos) { 155 sout << "<pre><b>"; 156 print_line(sout, line); 157 sout << "</b></pre><BR>" << endl; 158 continue; 159 } 160 161 /* comments of type /*= and /**= mean: use a <H3> header and show also all functions until first empty line */ 162 if ((line.substr(0,3) == "/*=" || line.substr(0,4) == "/**=") && line.find("*/")!=string::npos) { 163 trim_comments(line); 164 trim(line, "= "); 165 sout << "<h3>" << line << "</h3><pre><b>"; 166 lines = get_lines(input, ++linenum, ""); 167 for (l=0; l<lines.size(); l++) { 168 print_line(sout, lines[l]); 169 } 170 sout << "</b></pre><BR>" << endl; 171 continue; 172 } 173 174 spos = line.find("/*!"); 175 if (spos==string::npos) 176 spos = line.find("/**"); 177 if (spos==string::npos) 178 spos = line.find("/*-"); 179 180 if (spos==string::npos) 181 continue; 182 183 exclam = line[spos+2]; 184 comments = get_lines(input, linenum, "*/"); 185 if (!comments.empty()) comments[0] = line.substr(spos+3); 186 if (!comments.empty()) comments[comments.size()-1] = comments[comments.size()-1].substr(0, comments[comments.size()-1].find("*/")); 187 for (l=0; l<comments.size(); l++) { 188 if (comments[l].find(" *")==0) comments[l] = comments[l].substr(2); 189 else if (comments[l].find(" *")==0) comments[l] = comments[l].substr(3); 190 trim(comments[l], "*-"); 191 } 192 while (!comments.empty() && comments[comments.size()-1].empty()) comments.pop_back(); // remove empty line at the end 193 while (!comments.empty() && comments[0].empty()) comments.erase(comments.begin()); // remove empty line at the start 194 195 /* comments of type /*! mean: this is a function declaration; switch comments with declarations */ 196 if (exclam == '!') { 197 if (!comments.empty()) comments.erase(comments.begin()); /* remove first line like "LZ4_XXX() :" */ 198 linenum++; 199 lines = get_lines(input, linenum, ""); 200 201 sout << "<pre><b>"; 202 for (l=0; l<lines.size(); l++) { 203 // fprintf(stderr, "line[%d]=%s\n", l, lines[l].c_str()); 204 print_line(sout, lines[l]); 205 } 206 sout << "</b><p>"; 207 for (l=0; l<comments.size(); l++) { 208 print_line(sout, comments[l]); 209 } 210 sout << "</p></pre><BR>" << endl << endl; 211 } else { /* comments of type /** and /*- mean: this is a comment; use a <H2> header for the first line */ 212 if (comments.empty()) continue; 213 214 trim(comments[0], " "); 215 sout << "<a name=\"Chapter" << chapter << "\"></a><h2>" << comments[0] << "</h2><pre>"; 216 chapters.push_back(comments[0]); 217 chapter++; 218 219 for (l=1; l<comments.size(); l++) { 220 print_line(sout, comments[l]); 221 } 222 if (comments.size() > 1) 223 sout << "<BR></pre>" << endl << endl; 224 else 225 sout << "</pre>" << endl << endl; 226 } 227 } 228 229 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; 230 ostream << "<h1>" << version << "</h1>\n"; 231 232 ostream << "<hr>\n<a name=\"Contents\"></a><h2>Contents</h2>\n<ol>\n"; 233 for (size_t i=0; i<chapters.size(); i++) 234 ostream << "<li><a href=\"#Chapter" << i+1 << "\">" << chapters[i].c_str() << "</a></li>\n"; 235 ostream << "</ol>\n<hr>\n"; 236 237 ostream << sout.str(); 238 ostream << "</html>" << endl << "</body>" << endl; 239 240 return 0; 241 } 242