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 } 225