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