1 | /* |
2 | * |
3 | * Copyright (c) 2003 Dr John Maddock |
4 | * Use, modification and distribution is subject to the |
5 | * Boost Software License, Version 1.0. (See accompanying file |
6 | * LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) |
7 | * |
8 | */ |
9 | |
10 | #include "licence_info.hpp" |
11 | #include "bcp_imp.hpp" |
12 | #include "fileview.hpp" |
13 | #include <fstream> |
14 | #include <iomanip> |
15 | #include <cstring> |
16 | #include <stdexcept> |
17 | #include <boost/lexical_cast.hpp> |
18 | #include <boost/filesystem/operations.hpp> |
19 | #include <boost/throw_exception.hpp> |
20 | |
21 | // |
22 | // split_path is a small helper for outputting a path name, |
23 | // complete with a link to that path: |
24 | // |
25 | struct split_path |
26 | { |
27 | const fs::path& root; |
28 | const fs::path& file; |
29 | split_path(const fs::path& r, const fs::path& f) |
30 | : root(r), file(f){} |
31 | private: |
32 | split_path& operator=(const split_path&); |
33 | }; |
34 | |
35 | std::ostream& operator << (std::ostream& os, const split_path& p) |
36 | { |
37 | os << "<a href=\"" << (p.root / p.file).string() << "\">" << p.file.string() << "</a>" ; |
38 | return os; |
39 | } |
40 | |
41 | std::string make_link_target(const std::string& s) |
42 | { |
43 | // convert an arbitrary string into something suitable |
44 | // for an <a> name: |
45 | std::string result; |
46 | for(unsigned i = 0; i < s.size(); ++i) |
47 | { |
48 | result.append(n: 1, c: static_cast<std::string::value_type>(std::isalnum(s[i]) ? s[i] : '_')); |
49 | } |
50 | return result; |
51 | } |
52 | |
53 | |
54 | void bcp_implementation::output_license_info() |
55 | { |
56 | std::pair<const license_info*, int> licenses = get_licenses(); |
57 | |
58 | std::map<int, license_data>::const_iterator i, j; |
59 | i = m_license_data.begin(); |
60 | j = m_license_data.end(); |
61 | |
62 | std::ofstream os(m_dest_path.string().c_str()); |
63 | if(!os) |
64 | { |
65 | std::string msg("Error opening " ); |
66 | msg += m_dest_path.string(); |
67 | msg += " for output." ; |
68 | std::runtime_error e(msg); |
69 | boost::throw_exception(e); |
70 | } |
71 | os << |
72 | "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0 Transitional//EN\">\n" |
73 | "<html>\n" |
74 | "<head>\n" |
75 | "<title>Boost Licence Dependency Information" ; |
76 | if(m_module_list.size() == 1) |
77 | { |
78 | os << " for " << *(m_module_list.begin()); |
79 | } |
80 | os << |
81 | "</title>\n" |
82 | "</head>\n" |
83 | "<body>\n" |
84 | "<H1>Boost Licence Dependency Information" ; |
85 | if(m_module_list.size() == 1) |
86 | { |
87 | os << " for " << *(m_module_list.begin()); |
88 | } |
89 | os << |
90 | "</H1>\n" |
91 | "<H2>Contents</h2>\n" |
92 | "<pre><a href=\"#input\">Input Information</a>\n" ; |
93 | if(!m_bsl_summary_mode) |
94 | os << "<a href=\"#summary\">Licence Summary</a>\n" ; |
95 | os << "<a href=\"#details\">Licence Details</a>\n" ; |
96 | |
97 | while(i != j) |
98 | { |
99 | // title: |
100 | os << " <A href=\"#" << make_link_target(s: licenses.first[i->first].license_name) |
101 | << "\">" << licenses.first[i->first].license_name << "</a>\n" ; |
102 | ++i; |
103 | } |
104 | |
105 | os << "<a href=\"#files\">Files with no recognised license</a>\n" |
106 | "<a href=\"#authors\">Files with no recognised copyright holder</a>\n" ; |
107 | if(!m_bsl_summary_mode) |
108 | { |
109 | os << |
110 | "Moving to the Boost Software License...\n" |
111 | " <a href=\"#bsl-converted\">Files that can be automatically converted to the Boost Software License</a>\n" |
112 | " <a href=\"#to-bsl\">Files that can be manually converted to the Boost Software License</a>\n" |
113 | " <a href=\"#not-to-bsl\">Files that can <b>NOT</b> be moved to the Boost Software License</a>\n" |
114 | " <a href=\"#need-bsl-authors\">Authors we need to move to the Boost Software License</a>\n" |
115 | "<a href=\"#copyright\">Copyright Holder Information</a>\n" ; |
116 | } |
117 | os << |
118 | "<a href=\"#depend\">File Dependency Information</a>\n" |
119 | "</pre>" ; |
120 | |
121 | // |
122 | // input Information: |
123 | // |
124 | os << "<a name=\"input\"></a><h2>Input Information</h2>\n" ; |
125 | if(m_scan_mode) |
126 | os << "<P>The following files were scanned for boost dependencies:<BR>" ; |
127 | else |
128 | os << "<P>The following Boost modules were checked:<BR>" ; |
129 | |
130 | std::list<std::string>::const_iterator si = m_module_list.begin(); |
131 | std::list<std::string>::const_iterator sj = m_module_list.end(); |
132 | while(si != sj) |
133 | { |
134 | os << *si << "<BR>" ; |
135 | ++si; |
136 | } |
137 | os << "</p><p>The Boost path was: <code>" << m_boost_path.string() << "</code></P>" ; |
138 | // |
139 | // extract the boost version number from the boost directory tree, |
140 | // not from this app (which may have been built from a previous |
141 | // version): |
142 | // |
143 | fileview version_file(m_boost_path / "boost/version.hpp" ); |
144 | static const boost::regex version_regex( |
145 | "^[[:blank:]]*#[[:blank:]]*define[[:blank:]]+BOOST_VERSION[[:blank:]]+(\\d+)" ); |
146 | boost::cmatch what; |
147 | if(boost::regex_search(first: version_file.begin(), last: version_file.end(), m&: what, e: version_regex)) |
148 | { |
149 | int version = boost::lexical_cast<int>(arg: what.str(sub: 1)); |
150 | os << "<p>The Boost version is: " << version / 100000 << "." << version / 100 % 1000 << "." << version % 100 << "</P>\n" ; |
151 | } |
152 | |
153 | // |
154 | // output each license: |
155 | // |
156 | i = m_license_data.begin(); |
157 | j = m_license_data.end(); |
158 | if(!m_bsl_summary_mode) |
159 | { |
160 | // |
161 | // start with the summary: |
162 | // |
163 | os << "<a name=\"summary\"></a><h2>Licence Summary</h2>\n" ; |
164 | while(i != j) |
165 | { |
166 | // title: |
167 | os << |
168 | "<H3>" << licenses.first[i->first].license_name << "</H3>\n" ; |
169 | // license text: |
170 | os << "<BLOCKQUOTE>" << licenses.first[i->first].license_text << "</BLOCKQUOTE>" ; |
171 | // Copyright holders: |
172 | os << "<P>This license is used by " << i->second.authors.size() |
173 | << " authors and " << i->second.files.size() |
174 | << " files <a href=\"#" << make_link_target(s: licenses.first[i->first].license_name) << "\">(see details)</a>" ; |
175 | os << "</P></BLOCKQUOTE>\n" ; |
176 | ++i; |
177 | } |
178 | } |
179 | // |
180 | // and now the details: |
181 | // |
182 | i = m_license_data.begin(); |
183 | j = m_license_data.end(); |
184 | int license_index = 0; |
185 | os << "<a name=\"details\"></a><h2>Licence Details</h2>\n" ; |
186 | while(i != j) |
187 | { |
188 | // title: |
189 | os << |
190 | "<H3><A name=\"" << make_link_target(s: licenses.first[i->first].license_name) |
191 | << "\"></a>" << licenses.first[i->first].license_name << "</H3>\n" ; |
192 | // license text: |
193 | os << "<BLOCKQUOTE>" << licenses.first[i->first].license_text << "</BLOCKQUOTE>" ; |
194 | if(!m_bsl_summary_mode || (license_index >= 3)) |
195 | { |
196 | // Copyright holders: |
197 | os << "<P>This license is used by the following " << i->second.authors.size() << " copyright holders:</P>\n<BLOCKQUOTE><P>" ; |
198 | std::set<std::string>::const_iterator x, y; |
199 | x = i->second.authors.begin(); |
200 | y = i->second.authors.end(); |
201 | while(x != y) |
202 | { |
203 | os << *x << "<BR>\n" ; |
204 | ++x; |
205 | } |
206 | os << "</P></BLOCKQUOTE>\n" ; |
207 | // Files using this license: |
208 | os << "<P>This license applies to the following " << i->second.files.size() << " files:</P>\n<BLOCKQUOTE><P>" ; |
209 | std::set<fs::path, path_less>::const_iterator m, n; |
210 | m = i->second.files.begin(); |
211 | n = i->second.files.end(); |
212 | while(m != n) |
213 | { |
214 | os << split_path(m_boost_path, *m) << "<br>\n" ; |
215 | ++m; |
216 | } |
217 | os << "</P></BLOCKQUOTE>\n" ; |
218 | } |
219 | else |
220 | { |
221 | os << "<P>This license is used by " << i->second.authors.size() << " authors (list omitted for brevity).</P>\n" ; |
222 | os << "<P>This license applies to " << i->second.files.size() << " files (list omitted for brevity).</P>\n" ; |
223 | } |
224 | ++license_index; |
225 | ++i; |
226 | } |
227 | // |
228 | // Output list of files not found to be under license control: |
229 | // |
230 | os << "<h2><a name=\"files\"></a>Files With No Recognisable Licence</h2>\n" |
231 | "<P>The following " << m_unknown_licenses.size() << " files had no recognisable license information:</P><BLOCKQUOTE><P>\n" ; |
232 | std::set<fs::path, path_less>::const_iterator i2, j2; |
233 | i2 = m_unknown_licenses.begin(); |
234 | j2 = m_unknown_licenses.end(); |
235 | while(i2 != j2) |
236 | { |
237 | os << split_path(m_boost_path, *i2) << "<br>\n" ; |
238 | ++i2; |
239 | } |
240 | os << "</p></BLOCKQUOTE>" ; |
241 | // |
242 | // Output list of files with no found copyright holder: |
243 | // |
244 | os << "<h2><a name=\"authors\"></a>Files With No Recognisable Copyright Holder</h2>\n" |
245 | "<P>The following " << m_unknown_authors.size() << " files had no recognisable copyright holder:</P>\n<BLOCKQUOTE><P>" ; |
246 | i2 = m_unknown_authors.begin(); |
247 | j2 = m_unknown_authors.end(); |
248 | while(i2 != j2) |
249 | { |
250 | os << split_path(m_boost_path, *i2) << "<br>\n" ; |
251 | ++i2; |
252 | } |
253 | os << "</p></BLOCKQUOTE>" ; |
254 | if(!m_bsl_summary_mode) |
255 | { |
256 | // |
257 | // Output list of files that have been moved over to the Boost |
258 | // Software License, along with enough information for human |
259 | // verification. |
260 | // |
261 | os << "<h2><a name=\"bsl-converted\"></a>Files that can be automatically converted to the Boost Software License</h2>\n" |
262 | << "<P>The following " << m_converted_to_bsl.size() << " files can be automatically converted to the Boost Software License, but require manual verification before they can be committed to CVS:</P>\n" ; |
263 | if (!m_converted_to_bsl.empty()) |
264 | { |
265 | typedef std::map<fs::path, std::pair<std::string, std::string>, path_less> |
266 | ::const_iterator conv_iterator; |
267 | conv_iterator i = m_converted_to_bsl.begin(), |
268 | ie = m_converted_to_bsl.end(); |
269 | int file_num = 1; |
270 | while (i != ie) |
271 | { |
272 | os << "<P>[" << file_num << "] File: <tt>" << split_path(m_boost_path, i->first) |
273 | << "</tt><br>\n<table border=\"1\">\n <tr>\n <td><pre>" |
274 | << i->second.first << "</pre></td>\n <td><pre>" |
275 | << i->second.second << "</pre></td>\n </tr>\n</table>\n" ; |
276 | ++i; |
277 | ++file_num; |
278 | } |
279 | } |
280 | // |
281 | // Output list of files that could be moved over to the Boost Software License |
282 | // |
283 | os << "<h2><a name=\"to-bsl\"></a>Files that could be converted to the Boost Software License</h2>\n" |
284 | "<P>The following " << m_can_migrate_to_bsl.size() << " files could be manually converted to the Boost Software License, but have not yet been:</P>\n<BLOCKQUOTE><P>" ; |
285 | i2 = m_can_migrate_to_bsl.begin(); |
286 | j2 = m_can_migrate_to_bsl.end(); |
287 | while(i2 != j2) |
288 | { |
289 | os << split_path(m_boost_path, *i2) << "<br>\n" ; |
290 | ++i2; |
291 | } |
292 | os << "</p></BLOCKQUOTE>" ; |
293 | // |
294 | // Output list of files that can not be moved over to the Boost Software License |
295 | // |
296 | os << "<h2><a name=\"not-to-bsl\"></a>Files that can NOT be converted to the Boost Software License</h2>\n" |
297 | "<P>The following " << m_cannot_migrate_to_bsl.size() << " files cannot be converted to the Boost Software License because we need the permission of more authors:</P>\n<BLOCKQUOTE><P>" ; |
298 | i2 = m_cannot_migrate_to_bsl.begin(); |
299 | j2 = m_cannot_migrate_to_bsl.end(); |
300 | while(i2 != j2) |
301 | { |
302 | os << split_path(m_boost_path, *i2) << "<br>\n" ; |
303 | ++i2; |
304 | } |
305 | os << "</p></BLOCKQUOTE>" ; |
306 | // |
307 | // Output list of authors that we need permission for to move to the BSL |
308 | // |
309 | os << "<h2><a name=\"need-bsl-authors\"></a>Authors we need for the BSL</h2>\n" |
310 | "<P>Permission of the following authors is needed before we can convert to the Boost Software License. The list of authors that have given their permission is contained in <code>more/blanket-permission.txt</code>.</P>\n<BLOCKQUOTE><P>" ; |
311 | std::copy(first: m_authors_for_bsl_migration.begin(), last: m_authors_for_bsl_migration.end(), |
312 | result: std::ostream_iterator<std::string>(os, "<br>\n" )); |
313 | os << "</p></BLOCKQUOTE>" ; |
314 | // |
315 | // output a table of copyright information: |
316 | // |
317 | os << "<H2><a name=\"copyright\"></a>Copyright Holder Information</H2><table border=\"1\">\n" ; |
318 | std::map<std::string, std::set<fs::path, path_less> >::const_iterator ad, ead; |
319 | ad = m_author_data.begin(); |
320 | ead = m_author_data.end(); |
321 | while(ad != ead) |
322 | { |
323 | os << "<tr><td>" << ad->first << "</td><td>" ; |
324 | std::set<fs::path, path_less>::const_iterator fi, efi; |
325 | fi = ad->second.begin(); |
326 | efi = ad->second.end(); |
327 | while(fi != efi) |
328 | { |
329 | os << split_path(m_boost_path, *fi) << " " ; |
330 | ++fi; |
331 | } |
332 | os << "</td></tr>\n" ; |
333 | ++ad; |
334 | } |
335 | os << "</table>\n" ; |
336 | } |
337 | |
338 | // |
339 | // output file dependency information: |
340 | // |
341 | os << "<H2><a name=\"depend\"></a>File Dependency Information</H2><BLOCKQUOTE><pre>\n" ; |
342 | std::map<fs::path, fs::path, path_less>::const_iterator dep, last_dep; |
343 | std::set<fs::path, path_less>::const_iterator fi, efi; |
344 | fi = m_copy_paths.begin(); |
345 | efi = m_copy_paths.end(); |
346 | // if in summary mode, just figure out the "bad" files and print those only: |
347 | std::set<fs::path, path_less> bad_paths; |
348 | if(m_bsl_summary_mode) |
349 | { |
350 | bad_paths.insert(first: m_unknown_licenses.begin(), last: m_unknown_licenses.end()); |
351 | bad_paths.insert(first: m_unknown_authors.begin(), last: m_unknown_authors.end()); |
352 | bad_paths.insert(first: m_can_migrate_to_bsl.begin(), last: m_can_migrate_to_bsl.end()); |
353 | bad_paths.insert(first: m_cannot_migrate_to_bsl.begin(), last: m_cannot_migrate_to_bsl.end()); |
354 | typedef std::map<fs::path, std::pair<std::string, std::string>, path_less> |
355 | ::const_iterator conv_iterator; |
356 | conv_iterator i = m_converted_to_bsl.begin(), |
357 | ie = m_converted_to_bsl.end(); |
358 | while(i != ie) |
359 | { |
360 | bad_paths.insert(x: i->first); |
361 | ++i; |
362 | } |
363 | fi = bad_paths.begin(); |
364 | efi = bad_paths.end(); |
365 | os << "<P>For brevity, only files not under the BSL are shown</P>\n" ; |
366 | } |
367 | while(fi != efi) |
368 | { |
369 | os << split_path(m_boost_path, *fi); |
370 | dep = m_dependencies.find(x: *fi); |
371 | last_dep = m_dependencies.end(); |
372 | std::set<fs::path, path_less> seen_deps; |
373 | if (dep != last_dep) |
374 | while(true) |
375 | { |
376 | os << " -> " ; |
377 | if(fs::exists(p: m_boost_path / dep->second)) |
378 | os << split_path(m_boost_path, dep->second); |
379 | else if(fs::exists(p: dep->second)) |
380 | os << split_path(fs::path(), dep->second); |
381 | else |
382 | os << dep->second.string(); |
383 | if(seen_deps.find(x: dep->second) != seen_deps.end()) |
384 | { |
385 | os << " <I>(Circular dependency!)</I>" ; |
386 | break; // circular dependency!!! |
387 | } |
388 | seen_deps.insert(x: dep->second); |
389 | last_dep = dep; |
390 | dep = m_dependencies.find(x: dep->second); |
391 | if((dep == m_dependencies.end()) || (0 == compare_paths(a: dep->second, b: last_dep->second))) |
392 | break; |
393 | } |
394 | os << "\n" ; |
395 | ++fi; |
396 | } |
397 | os << "</pre></BLOCKQUOTE>\n" ; |
398 | |
399 | os << "</body></html>\n" ; |
400 | |
401 | if(!os) |
402 | { |
403 | std::string msg("Error writing to " ); |
404 | msg += m_dest_path.string(); |
405 | msg += "." ; |
406 | std::runtime_error e(msg); |
407 | boost::throw_exception(e); |
408 | } |
409 | |
410 | } |
411 | |