1/****************************************************************************
2 * Copyright (C) 2012-2016 Woboq GmbH
3 * Olivier Goffart <contact at woboq.com>
4 * https://woboq.com/codebrowser.html
5 *
6 * This file is part of the Woboq Code Browser.
7 *
8 * Commercial License Usage:
9 * Licensees holding valid commercial licenses provided by Woboq may use
10 * this file in accordance with the terms contained in a written agreement
11 * between the licensee and Woboq.
12 * For further information see https://woboq.com/codebrowser.html
13 *
14 * Alternatively, this work may be used under a Creative Commons
15 * Attribution-NonCommercial-ShareAlike 3.0 (CC-BY-NC-SA 3.0) License.
16 * http://creativecommons.org/licenses/by-nc-sa/3.0/deed.en_US
17 * This license does not allow you to use the code browser to assist the
18 * development of your commercial software. If you intent to do so, consider
19 * purchasing a commercial licence.
20 ****************************************************************************/
21
22
23#include <iostream>
24#include <fstream>
25#include <sstream>
26#include <memory>
27#include <vector>
28#include <map>
29#include <ctime>
30
31#include "../global.h"
32
33const char *data_url = "../data";
34
35std::map<std::string, std::string, std::greater<std::string> > project_map;
36
37struct FolderInfo {
38// std::string name;
39 std::map<std::string, std::shared_ptr<FolderInfo>> subfolders;
40};
41
42std::string extractMetaFromHTML(std::string metaName, std::string fullPath) {
43 std::ifstream filein(fullPath, std::ifstream::in);
44 std::string needle = "<meta name=\"woboq:interestingDefinitions\" content=\"";
45 std::string endneedle = "\"/>\n";
46 for (std::string line; std::getline(filein, line); ) {
47 if (line.find(needle, 0) == 0) {
48 return line.substr(needle.length(), line.length() - needle.length() - endneedle.length());
49 }
50 }
51 return "";
52}
53
54std::string cutNameSpace(std::string &className) {
55 int colonPos = className.find_last_of("::");
56 if (colonPos != std::string::npos)
57 return className.substr(colonPos+1);
58 else
59 return className;
60}
61
62void linkInterestingDefinitions(std::ofstream &myfile, std::string linkFile, std::string &interestingDefitions)
63{
64 if (interestingDefitions.length() == 0) {
65 return;
66 }
67 myfile << "<ul>";
68 std::istringstream f(interestingDefitions);
69 std::string className;
70 while (std::getline(f, className, ',')) {
71 if (className.length() == 0) {
72 continue;
73 }
74 myfile << "<li><a href='" << linkFile << "#" << className << "'"
75 << " title='" << className << "'" << ">";
76 if (className.find("(anonymous") == std::string::npos) {
77 className = cutNameSpace(className);
78 }
79 myfile << className << "</a></li>";
80 }
81 myfile << "</ul>";
82
83}
84
85void gererateRecursisively(FolderInfo *folder, const std::string &root, const std::string &path, const std::string &rel = "") {
86 std::ofstream myfile;
87 std::string filename = root + "/" + path + "index.html";
88 myfile.open(filename);
89 if (!myfile) {
90 std::cerr << "Error generating " << filename << std::endl;
91 return;
92 }
93 std::cerr << "Generating " << filename << std::endl;
94
95 std::string data_path = data_url[0] == '.' ? (rel + data_url) : std::string(data_url);
96
97
98 size_t pos = root.rfind('/', root.size()-2);
99 std::string project = pos < root.size() ? root.substr(pos+1) : root;
100 std::string breadcrumb = "<a href=''>" +path + "</a>";
101 std::string parent;
102
103 pos = path.rfind('/', path.size()-2);
104 if (pos < path.size()) {
105 breadcrumb = "<a href=''>" + path.substr(pos+1)+ "</a>";
106
107 unsigned int next_pos;
108 while (pos > 0 && (next_pos = path.rfind('/', pos-1)) < path.size()) {
109 if (pos != next_pos +1) {
110 parent += "../";
111 breadcrumb = "<a href='" +parent +"'>" + path.substr(next_pos + 1, pos - next_pos - 1) + "</a>/" + breadcrumb;
112 }
113 pos = next_pos;
114 }
115 if (pos > 1) {
116 parent += "../";
117 breadcrumb = "<a href='" +parent +"'>" + path.substr(0, pos) + "</a>/" + breadcrumb;
118 }
119 }
120 if (path.length() > 0) {
121 breadcrumb = "<a href='../" +parent +"'>" + project + "</a>/" + breadcrumb;
122 } else {
123 breadcrumb = "<a href=''>" + project + "</a>/" + breadcrumb;
124 }
125
126 myfile << "<!doctype html>\n"
127 "<head><title>";
128 myfile << project << "/" << path;
129 myfile << " Source Tree - Woboq Code Browser</title>\n"
130 << "<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">"
131 "<link rel=\"stylesheet\" href=\"" << data_path << "/indexstyle.css\"/>\n";
132 myfile << "<script type=\"text/javascript\" src=\"" << data_path << "/jquery/jquery.min.js\"></script>\n";
133 myfile << "<script type=\"text/javascript\" src=\"" << data_path << "/jquery/jquery-ui.min.js\"></script>\n";
134 myfile << "<script>var path = '"<< path <<"'; var root_path = '"<< rel <<"'; var project='"<< project <<"'; var ecma_script_api_version = 2;</script>\n"
135 "<script src='" << data_path << "/indexscript.js'></script>\n"
136 "</head>\n<body>\n";
137 myfile << "<div id='header'><div id='toprightlogo'><a href='https://code.woboq.org'></a></div>\n";
138 myfile << "<p><input id='searchline' placeholder='Search for a file or function' type='text'/></p>\n";
139 myfile << "<h1 id='title'>Browse the source code of " << breadcrumb << " online</h1>\n";
140 myfile << "</div><hr/><table id='tree'>\n";
141
142 //if (!path.empty())
143 {
144 myfile << " <tr><td class='parent'> <a href='../'>../</a></td><td></td></tr>\n";
145 }
146
147 for (auto it : folder->subfolders) {
148 const std::string &name = it.first;
149 if (it.second) {
150 gererateRecursisively(it.second.get(), root, path+name+"/", rel + "../");
151 myfile << "<tr><td class='folder'><a href='"<< name <<"/' class='opener' data-path='" << path << name << "'>[+]</a> "
152 "<a href='" << name << "/'>" << name << "/</a></td><td></td></tr>\n";
153 } else {
154 std::string interestingDefintions = extractMetaFromHTML("woboq:interestingDefinitions", root + "/" + path + name + ".html");
155 myfile << "<tr><td class='file'> <a href='" << name << ".html'>"
156 << name
157 << "</a>"
158 << "<span class='meta'>";
159 linkInterestingDefinitions(myfile, name+".html", interestingDefintions);
160 myfile << "</span>"
161 << "</td>"
162 << "</tr>\n";
163 }
164 }
165
166 char timebuf[80];
167 auto now = std::time(0);
168 auto tm = std::localtime(&now);
169 std::strftime(timebuf, sizeof(timebuf), "%Y-%b-%d", tm);
170
171 myfile << "</table>"
172 "<hr/><p id='footer'>\n"
173 "Generated on <em>" << timebuf << "</em>";
174
175 auto it = project_map.lower_bound(path);
176 if (it != project_map.end() && std::equal(it->first.begin(), it->first.end(), path.c_str())) {
177 myfile << " from project " << it->first;
178 if (!it->second.empty()) {
179 myfile <<" revision <em>" << it->second << "</em>";
180 }
181 }
182 myfile << "<br />Powered by <a href='https://woboq.com'><img alt='Woboq' src='https://code.woboq.org/woboq-16.png' width='41' height='16' /></a> <a href='https://code.woboq.org'>Code Browser</a> "
183 CODEBROWSER_VERSION "\n<br/>Generator usage only permitted with license</p>\n</body></html>\n";
184}
185
186int main(int argc, char **argv) {
187
188 std::string root;
189 bool skipOptions = false;
190
191 for (int i = 1; i < argc; ++i) {
192 std::string arg = argv[i];
193 if (!skipOptions && arg[0]=='-') {
194 if (arg == "--") {
195 skipOptions = true;
196 } else if (arg=="-d") {
197 i++;
198 if (i < argc)
199 data_url = argv[i];
200 } else if (arg=="-p") {
201 i++;
202 if (i < argc) {
203 std::string s = argv[i];
204 auto colonPos = s.find(':');
205 if (colonPos >= s.size()) {
206 std::cerr << "fail to parse project option : " << s << std::endl;
207 continue;
208 }
209 auto secondColonPos = s.find(':', colonPos+1);
210 if (secondColonPos < s.size()) {
211 project_map[s.substr(0, colonPos)] = s.substr(secondColonPos + 1);
212 }
213 }
214 } else if (arg=="-e") {
215 i++;
216 // ignore -e XXX for compatibility with the generator project definitions
217 }
218 } else {
219 if (root.empty()) {
220 root = arg;
221 } else {
222 root = "";
223 break;
224 }
225 }
226 }
227
228 if (root.empty()) {
229 std::cerr << "Usage: " << argv[0] << " <path> [-d data_url] [-p project_definition]" << std::endl;
230 return -1;
231 }
232 std::ifstream fileIndex(root + "/" + "fileIndex");
233 std::string line;
234
235 FolderInfo rootInfo;
236 while (std::getline(fileIndex, line))
237 {
238 FolderInfo *parent = &rootInfo;
239
240 unsigned int pos = 0;
241 unsigned int next_pos;
242 while ((next_pos = line.find('/', pos)) < line.size()) {
243 auto &sub = parent->subfolders[line.substr(pos, next_pos - pos)];
244 if (!sub) sub = std::make_shared<FolderInfo>();
245 parent = sub.get();
246 pos = next_pos + 1;
247 }
248 parent->subfolders[line.substr(pos)]; //make sure it exists;
249 }
250 gererateRecursisively(&rootInfo, root, "");
251 return 0;
252}
253