| 1 | /* |
| 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. |
| 3 | * All rights reserved. |
| 4 | * |
| 5 | * This source code is licensed under both the BSD-style license (found in the |
| 6 | * LICENSE file in the root directory of this source tree) and the GPLv2 (found |
| 7 | * in the COPYING file in the root directory of this source tree). |
| 8 | */ |
| 9 | |
| 10 | #include <iostream> |
| 11 | #include <fstream> |
| 12 | #include <sstream> |
| 13 | #include <vector> |
| 14 | using namespace std; |
| 15 | |
| 16 | |
| 17 | /* trim string at the beginning and at the end */ |
| 18 | void trim(string& s, string characters) |
| 19 | { |
| 20 | size_t p = s.find_first_not_of(characters); |
| 21 | s.erase(0, p); |
| 22 | |
| 23 | p = s.find_last_not_of(characters); |
| 24 | if (string::npos != p) |
| 25 | s.erase(p+1); |
| 26 | } |
| 27 | |
| 28 | |
| 29 | /* trim C++ style comments */ |
| 30 | void trim_comments(string &s) |
| 31 | { |
| 32 | size_t spos, epos; |
| 33 | |
| 34 | spos = s.find("/*"); |
| 35 | epos = s.find("*/"); |
| 36 | s = s.substr(spos+3, epos-(spos+3)); |
| 37 | } |
| 38 | |
| 39 | |
| 40 | /* get lines until a given terminator */ |
| 41 | vector<string> get_lines(vector<string>& input, int& linenum, string terminator) |
| 42 | { |
| 43 | vector<string> out; |
| 44 | string line; |
| 45 | size_t epos; |
| 46 | |
| 47 | while ((size_t)linenum < input.size()) { |
| 48 | line = input[linenum]; |
| 49 | |
| 50 | if (terminator.empty() && line.empty()) { linenum--; break; } |
| 51 | |
| 52 | epos = line.find(terminator); |
| 53 | if (!terminator.empty() && epos!=string::npos) { |
| 54 | out.push_back(line); |
| 55 | break; |
| 56 | } |
| 57 | out.push_back(line); |
| 58 | linenum++; |
| 59 | } |
| 60 | return out; |
| 61 | } |
| 62 | |
| 63 | |
| 64 | /* print line with ZSTDLIB_API removed and C++ comments not bold */ |
| 65 | void print_line(stringstream &sout, string line) |
| 66 | { |
| 67 | size_t spos; |
| 68 | |
| 69 | if (line.substr(0,12) == "ZSTDLIB_API ") line = line.substr(12); |
| 70 | spos = line.find("/*"); |
| 71 | if (spos!=string::npos) { |
| 72 | sout << line.substr(0, spos); |
| 73 | sout << "</b>" << line.substr(spos) << "<b>" << endl; |
| 74 | } else { |
| 75 | // fprintf(stderr, "lines=%s\n", line.c_str()); |
| 76 | sout << line << endl; |
| 77 | } |
| 78 | } |
| 79 | |
| 80 | |
| 81 | int main(int argc, char *argv[]) { |
| 82 | char exclam; |
| 83 | int linenum, chapter = 1; |
| 84 | vector<string> input, lines, comments, chapters; |
| 85 | string line, version; |
| 86 | size_t spos, l; |
| 87 | stringstream sout; |
| 88 | ifstream istream; |
| 89 | ofstream ostream; |
| 90 | |
| 91 | if (argc < 4) { |
| 92 | cout << "usage: " << argv[0] << " [zstd_version] [input_file] [output_html]" << endl; |
| 93 | return 1; |
| 94 | } |
| 95 | |
| 96 | version = "zstd " + string(argv[1]) + " Manual"; |
| 97 | |
| 98 | istream.open(argv[2], ifstream::in); |
| 99 | if (!istream.is_open()) { |
| 100 | cout << "Error opening file " << argv[2] << endl; |
| 101 | return 1; |
| 102 | } |
| 103 | |
| 104 | ostream.open(argv[3], ifstream::out); |
| 105 | if (!ostream.is_open()) { |
| 106 | cout << "Error opening file " << argv[3] << endl; |
| 107 | return 1; |
| 108 | } |
| 109 | |
| 110 | while (getline(istream, line)) { |
| 111 | input.push_back(line); |
| 112 | } |
| 113 | |
| 114 | for (linenum=0; (size_t)linenum < input.size(); linenum++) { |
| 115 | line = input[linenum]; |
| 116 | |
| 117 | /* typedefs are detected and included even if uncommented */ |
| 118 | if (line.substr(0,7) == "typedef" && line.find("{")!=string::npos) { |
| 119 | lines = get_lines(input, linenum, "}"); |
| 120 | sout << "<pre><b>"; |
| 121 | for (l=0; l<lines.size(); l++) { |
| 122 | print_line(sout, lines[l]); |
| 123 | } |
| 124 | sout << "</b></pre><BR>" << endl; |
| 125 | continue; |
| 126 | } |
| 127 | |
| 128 | /* comments of type /**< and /*!< are detected and only function declaration is highlighted (bold) */ |
| 129 | if ((line.find("/**<")!=string::npos || line.find("/*!<")!=string::npos) && line.find("*/")!=string::npos) { |
| 130 | sout << "<pre><b>"; |
| 131 | print_line(sout, line); |
| 132 | sout << "</b></pre><BR>" << endl; |
| 133 | continue; |
| 134 | } |
| 135 | |
| 136 | spos = line.find("/**="); |
| 137 | if (spos==string::npos) { |
| 138 | spos = line.find("/*!"); |
| 139 | if (spos==string::npos) |
| 140 | spos = line.find("/**"); |
| 141 | if (spos==string::npos) |
| 142 | spos = line.find("/*-"); |
| 143 | if (spos==string::npos) |
| 144 | spos = line.find("/*="); |
| 145 | if (spos==string::npos) |
| 146 | continue; |
| 147 | exclam = line[spos+2]; |
| 148 | } |
| 149 | else exclam = '='; |
| 150 | |
| 151 | comments = get_lines(input, linenum, "*/"); |
| 152 | if (!comments.empty()) comments[0] = line.substr(spos+3); |
| 153 | if (!comments.empty()) comments[comments.size()-1] = comments[comments.size()-1].substr(0, comments[comments.size()-1].find("*/")); |
| 154 | for (l=0; l<comments.size(); l++) { |
| 155 | if (comments[l].find(" *")==0) comments[l] = comments[l].substr(2); |
| 156 | else if (comments[l].find(" *")==0) comments[l] = comments[l].substr(3); |
| 157 | trim(comments[l], "*-="); |
| 158 | } |
| 159 | while (!comments.empty() && comments[comments.size()-1].empty()) comments.pop_back(); // remove empty line at the end |
| 160 | while (!comments.empty() && comments[0].empty()) comments.erase(comments.begin()); // remove empty line at the start |
| 161 | |
| 162 | /* comments of type /*! mean: this is a function declaration; switch comments with declarations */ |
| 163 | if (exclam == '!') { |
| 164 | if (!comments.empty()) comments.erase(comments.begin()); /* remove first line like "ZSTD_XXX() :" */ |
| 165 | linenum++; |
| 166 | lines = get_lines(input, linenum, ""); |
| 167 | |
| 168 | sout << "<pre><b>"; |
| 169 | for (l=0; l<lines.size(); l++) { |
| 170 | // fprintf(stderr, "line[%d]=%s\n", l, lines[l].c_str()); |
| 171 | string fline = lines[l]; |
| 172 | if (fline.substr(0, 12) == "ZSTDLIB_API " || |
| 173 | fline.substr(0, 12) == string(12, ' ')) |
| 174 | fline = fline.substr(12); |
| 175 | print_line(sout, fline); |
| 176 | } |
| 177 | sout << "</b><p>"; |
| 178 | for (l=0; l<comments.size(); l++) { |
| 179 | print_line(sout, comments[l]); |
| 180 | } |
| 181 | sout << "</p></pre><BR>" << endl << endl; |
| 182 | } else if (exclam == '=') { /* comments of type /*= and /**= mean: use a <H3> header and show also all functions until first empty line */ |
| 183 | trim(comments[0], " "); |
| 184 | sout << "<h3>" << comments[0] << "</h3><pre>"; |
| 185 | for (l=1; l<comments.size(); l++) { |
| 186 | print_line(sout, comments[l]); |
| 187 | } |
| 188 | sout << "</pre><b><pre>"; |
| 189 | lines = get_lines(input, ++linenum, ""); |
| 190 | for (l=0; l<lines.size(); l++) { |
| 191 | print_line(sout, lines[l]); |
| 192 | } |
| 193 | sout << "</pre></b><BR>" << endl; |
| 194 | } else { /* comments of type /** and /*- mean: this is a comment; use a <H2> header for the first line */ |
| 195 | if (comments.empty()) continue; |
| 196 | |
| 197 | trim(comments[0], " "); |
| 198 | sout << "<a name=\"Chapter" << chapter << "\"></a><h2>" << comments[0] << "</h2><pre>"; |
| 199 | chapters.push_back(comments[0]); |
| 200 | chapter++; |
| 201 | |
| 202 | for (l=1; l<comments.size(); l++) { |
| 203 | print_line(sout, comments[l]); |
| 204 | } |
| 205 | if (comments.size() > 1) |
| 206 | sout << "<BR></pre>" << endl << endl; |
| 207 | else |
| 208 | sout << "</pre>" << endl << endl; |
| 209 | } |
| 210 | } |
| 211 | |
| 212 | 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; |
| 213 | ostream << "<h1>" << version << "</h1>\n"; |
| 214 | |
| 215 | ostream << "<hr>\n<a name=\"Contents\"></a><h2>Contents</h2>\n<ol>\n"; |
| 216 | for (size_t i=0; i<chapters.size(); i++) |
| 217 | ostream << "<li><a href=\"#Chapter" << i+1 << "\">" << chapters[i].c_str() << "</a></li>\n"; |
| 218 | ostream << "</ol>\n<hr>\n"; |
| 219 | |
| 220 | ostream << sout.str(); |
| 221 | ostream << "</html>" << endl << "</body>" << endl; |
| 222 | |
| 223 | return 0; |
| 224 | } |