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 */
trim(string & s,string characters)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 */
trim_comments(string & s)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 */
get_lines(vector<string> & input,int & linenum,string terminator)66 vector<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 */
print_line(stringstream & sout,string line)89 void 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>" << endl;
100     } else {
101       //  fprintf(stderr, "lines=%s\n", line.c_str());
102         sout << line << endl;
103     }
104 }
105 
106 
main(int argc,char * argv[])107 int main(int argc, char *argv[]) {
108     char exclam;
109     int linenum, chapter = 1;
110     vector<string> input, lines, comments, chapters;
111     string line, version;
112     size_t spos, l;
113     stringstream sout;
114     ifstream istream;
115     ofstream ostream;
116 
117     if (argc < 4) {
118         cout << "usage: " << argv[0] << " [lz4_version] [input_file] [output_html]" << endl;
119         return 1;
120     }
121 
122     version = string(argv[1]) + " Manual";
123 
124     istream.open(argv[2], ifstream::in);
125     if (!istream.is_open()) {
126         cout << "Error opening file " << argv[2] << endl;
127         return 1;
128     }
129 
130     ostream.open(argv[3], ifstream::out);
131     if (!ostream.is_open()) {
132         cout << "Error opening file " << argv[3] << endl;
133         return 1;
134    }
135 
136     while (getline(istream, line)) {
137         input.push_back(line);
138     }
139 
140     for (linenum=0; (size_t)linenum < input.size(); linenum++) {
141         line = input[linenum];
142 
143         /* typedefs are detected and included even if uncommented */
144         if (line.substr(0,7) == "typedef" && line.find("{")!=string::npos) {
145             lines = get_lines(input, linenum, "}");
146             sout << "<pre><b>";
147             for (l=0; l<lines.size(); l++) {
148                 print_line(sout, lines[l]);
149             }
150             sout << "</b></pre><BR>" << endl;
151             continue;
152         }
153 
154         /* comments of type  / * * < and  / * ! <  are detected, and only function declaration is highlighted (bold) */
155         if ((line.find("/**<")!=string::npos || line.find("/*!<")!=string::npos)
156           && line.find("*/")!=string::npos) {
157             sout << "<pre><b>";
158             print_line(sout, line);
159             sout << "</b></pre><BR>" << endl;
160             continue;
161         }
162 
163         spos = line.find("/**=");
164         if (spos==string::npos) {
165             spos = line.find("/*!");
166             if (spos==string::npos)
167                 spos = line.find("/**");
168             if (spos==string::npos)
169                 spos = line.find("/*-");
170             if (spos==string::npos)
171                 spos = line.find("/*=");
172             if (spos==string::npos)
173                 continue;
174             exclam = line[spos+2];
175         }
176         else exclam = '=';
177 
178         comments = get_lines(input, linenum, "*/");
179         if (!comments.empty()) comments[0] = line.substr(spos+3);
180         if (!comments.empty())
181             comments[comments.size()-1] = comments[comments.size()-1].substr(0, comments[comments.size()-1].find("*/"));
182         for (l=0; l<comments.size(); l++) {
183             if (comments[l].compare(0, 2, " *") == 0)
184                 comments[l] = comments[l].substr(2);
185             else if (comments[l].compare(0, 3, "  *") == 0)
186                 comments[l] = comments[l].substr(3);
187             trim(comments[l], "*-=");
188         }
189         while (!comments.empty() && comments[comments.size()-1].empty()) comments.pop_back(); // remove empty line at the end
190         while (!comments.empty() && comments[0].empty()) comments.erase(comments.begin()); // remove empty line at the start
191 
192         /* comments of type  / * !  mean: this is a function declaration; switch comments with declarations */
193         if (exclam == '!') {
194             if (!comments.empty()) comments.erase(comments.begin()); /* remove first line like "LZ4_XXX() :" */
195             linenum++;
196             lines = get_lines(input, linenum, "");
197 
198             sout << "<pre><b>";
199             for (l=0; l<lines.size(); l++) {
200                 print_line(sout, lines[l]);
201             }
202             sout << "</b><p>";
203             for (l=0; l<comments.size(); l++) {
204                 print_line(sout, comments[l]);
205             }
206             sout << "</p></pre><BR>" << endl << endl;
207         } else if (exclam == '=') { /* comments of type  / * =  and  / * * =  mean: use a <H3> header and show also all functions until first empty line */
208             trim(comments[0], " ");
209             sout << "<h3>" << comments[0] << "</h3><pre>";
210             for (l=1; l<comments.size(); l++) {
211                 print_line(sout, comments[l]);
212             }
213             sout << "</pre><b><pre>";
214             lines = get_lines(input, ++linenum, "");
215             for (l=0; l<lines.size(); l++) {
216                 print_line(sout, lines[l]);
217             }
218             sout << "</pre></b><BR>" << endl;
219         } else { /* comments of type  / * *  and  / * -  mean: this is a comment; use a <H2> header for the first line */
220             if (comments.empty()) continue;
221 
222             trim(comments[0], " ");
223             sout << "<a name=\"Chapter" << chapter << "\"></a><h2>" << comments[0] << "</h2><pre>";
224             chapters.push_back(comments[0]);
225             chapter++;
226 
227             for (l=1; l<comments.size(); l++) {
228                 print_line(sout, comments[l]);
229             }
230             if (comments.size() > 1)
231                 sout << "<BR></pre>" << endl << endl;
232             else
233                 sout << "</pre>" << endl << endl;
234         }
235     }
236 
237     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;
238     ostream << "<h1>" << version << "</h1>\n";
239 
240     ostream << "<hr>\n<a name=\"Contents\"></a><h2>Contents</h2>\n<ol>\n";
241     for (size_t i=0; i<chapters.size(); i++)
242         ostream << "<li><a href=\"#Chapter" << i+1 << "\">" << chapters[i].c_str() << "</a></li>\n";
243     ostream << "</ol>\n<hr>\n";
244 
245     ostream << sout.str();
246     ostream << "</html>" << endl << "</body>" << endl;
247 
248     return 0;
249 }
250