| /* |
| * |
| * Copyright (c) 2003 Dr John Maddock |
| * Use, modification and distribution is subject to the |
| * Boost Software License, Version 1.0. (See accompanying file |
| * LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) |
| * |
| */ |
| |
| #include "licence_info.hpp" |
| #include "bcp_imp.hpp" |
| #include "fileview.hpp" |
| #include <fstream> |
| #include <iomanip> |
| #include <cstring> |
| #include <stdexcept> |
| #include <boost/lexical_cast.hpp> |
| #include <boost/filesystem/operations.hpp> |
| #include <boost/throw_exception.hpp> |
| |
| // |
| // split_path is a small helper for outputting a path name, |
| // complete with a link to that path: |
| // |
| struct split_path |
| { |
| const fs::path& root; |
| const fs::path& file; |
| split_path(const fs::path& r, const fs::path& f) |
| : root(r), file(f){} |
| }; |
| |
| std::ostream& operator << (std::ostream& os, const split_path& p) |
| { |
| os << "<a href=\"" << (p.root / p.file).string() << "\">" << p.file.string() << "</a>"; |
| return os; |
| } |
| |
| std::string make_link_target(const std::string& s) |
| { |
| // convert an arbitrary string into something suitable |
| // for an <a> name: |
| std::string result; |
| for(unsigned i = 0; i < s.size(); ++i) |
| { |
| result.append(1, static_cast<std::string::value_type>(std::isalnum(s[i]) ? s[i] : '_')); |
| } |
| return result; |
| } |
| |
| |
| void bcp_implementation::output_license_info() |
| { |
| std::pair<const license_info*, int> licenses = get_licenses(); |
| |
| std::map<int, license_data>::const_iterator i, j; |
| i = m_license_data.begin(); |
| j = m_license_data.end(); |
| |
| std::ofstream os(m_dest_path.native_file_string().c_str()); |
| if(!os) |
| { |
| std::string msg("Error opening "); |
| msg += m_dest_path.string(); |
| msg += " for output."; |
| std::runtime_error e(msg); |
| boost::throw_exception(e); |
| } |
| os << |
| "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0 Transitional//EN\">\n" |
| "<html>\n" |
| "<head>\n" |
| "<title>Boost Licence Dependency Information"; |
| if(m_module_list.size() == 1) |
| { |
| os << " for " << *(m_module_list.begin()); |
| } |
| os << |
| "</title>\n" |
| "</head>\n" |
| "<body>\n" |
| "<H1>Boost Licence Dependency Information"; |
| if(m_module_list.size() == 1) |
| { |
| os << " for " << *(m_module_list.begin()); |
| } |
| os << |
| "</H1>\n" |
| "<H2>Contents</h2>\n" |
| "<pre><a href=\"#input\">Input Information</a>\n"; |
| if(!m_bsl_summary_mode) |
| os << "<a href=\"#summary\">Licence Summary</a>\n"; |
| os << "<a href=\"#details\">Licence Details</a>\n"; |
| |
| while(i != j) |
| { |
| // title: |
| os << " <A href=\"#" << make_link_target(licenses.first[i->first].license_name) |
| << "\">" << licenses.first[i->first].license_name << "</a>\n"; |
| ++i; |
| } |
| |
| os << "<a href=\"#files\">Files with no recognised license</a>\n" |
| "<a href=\"#authors\">Files with no recognised copyright holder</a>\n"; |
| if(!m_bsl_summary_mode) |
| { |
| os << |
| "Moving to the Boost Software License...\n" |
| " <a href=\"#bsl-converted\">Files that can be automatically converted to the Boost Software License</a>\n" |
| " <a href=\"#to-bsl\">Files that can be manually converted to the Boost Software License</a>\n" |
| " <a href=\"#not-to-bsl\">Files that can <b>NOT</b> be moved to the Boost Software License</a>\n" |
| " <a href=\"#need-bsl-authors\">Authors we need to move to the Boost Software License</a>\n" |
| "<a href=\"#copyright\">Copyright Holder Information</a>\n"; |
| } |
| os << |
| "<a href=\"#depend\">File Dependency Information</a>\n" |
| "</pre>"; |
| |
| // |
| // input Information: |
| // |
| os << "<a name=\"input\"></a><h2>Input Information</h2>\n"; |
| if(m_scan_mode) |
| os << "<P>The following files were scanned for boost dependencies:<BR>"; |
| else |
| os << "<P>The following Boost modules were checked:<BR>"; |
| |
| std::list<std::string>::const_iterator si = m_module_list.begin(); |
| std::list<std::string>::const_iterator sj = m_module_list.end(); |
| while(si != sj) |
| { |
| os << *si << "<BR>"; |
| ++si; |
| } |
| os << "</p><p>The Boost path was: <code>" << m_boost_path.string() << "</code></P>"; |
| // |
| // extract the boost version number from the boost directory tree, |
| // not from this app (which may have been built from a previous |
| // version): |
| // |
| fileview version_file(m_boost_path / "boost/version.hpp"); |
| static const boost::regex version_regex( |
| "^[[:blank:]]*#[[:blank:]]*define[[:blank:]]+BOOST_VERSION[[:blank:]]+(\\d+)"); |
| boost::cmatch what; |
| if(boost::regex_search(version_file.begin(), version_file.end(), what, version_regex)) |
| { |
| int version = boost::lexical_cast<int>(what.str(1)); |
| os << "<p>The Boost version is: " << version / 100000 << "." << version / 100 % 1000 << "." << version % 100 << "</P>\n"; |
| } |
| |
| // |
| // output each license: |
| // |
| i = m_license_data.begin(); |
| j = m_license_data.end(); |
| if(!m_bsl_summary_mode) |
| { |
| // |
| // start with the summary: |
| // |
| os << "<a name=\"summary\"></a><h2>Licence Summary</h2>\n"; |
| while(i != j) |
| { |
| // title: |
| os << |
| "<H3>" << licenses.first[i->first].license_name << "</H3>\n"; |
| // license text: |
| os << "<BLOCKQUOTE>" << licenses.first[i->first].license_text << "</BLOCKQUOTE>"; |
| // Copyright holders: |
| os << "<P>This license is used by " << i->second.authors.size() |
| << " authors and " << i->second.files.size() |
| << " files <a href=\"#" << make_link_target(licenses.first[i->first].license_name) << "\">(see details)</a>"; |
| os << "</P></BLOCKQUOTE>\n"; |
| ++i; |
| } |
| } |
| // |
| // and now the details: |
| // |
| i = m_license_data.begin(); |
| j = m_license_data.end(); |
| int license_index = 0; |
| os << "<a name=\"details\"></a><h2>Licence Details</h2>\n"; |
| while(i != j) |
| { |
| // title: |
| os << |
| "<H3><A name=\"" << make_link_target(licenses.first[i->first].license_name) |
| << "\"></a>" << licenses.first[i->first].license_name << "</H3>\n"; |
| // license text: |
| os << "<BLOCKQUOTE>" << licenses.first[i->first].license_text << "</BLOCKQUOTE>"; |
| if(!m_bsl_summary_mode || (license_index >= 3)) |
| { |
| // Copyright holders: |
| os << "<P>This license is used by the following " << i->second.authors.size() << " copyright holders:</P>\n<BLOCKQUOTE><P>"; |
| std::set<std::string>::const_iterator x, y; |
| x = i->second.authors.begin(); |
| y = i->second.authors.end(); |
| while(x != y) |
| { |
| os << *x << "<BR>\n"; |
| ++x; |
| } |
| os << "</P></BLOCKQUOTE>\n"; |
| // Files using this license: |
| os << "<P>This license applies to the following " << i->second.files.size() << " files:</P>\n<BLOCKQUOTE><P>"; |
| std::set<fs::path, path_less>::const_iterator m, n; |
| m = i->second.files.begin(); |
| n = i->second.files.end(); |
| while(m != n) |
| { |
| os << split_path(m_boost_path, *m) << "<br>\n"; |
| ++m; |
| } |
| os << "</P></BLOCKQUOTE>\n"; |
| } |
| else |
| { |
| os << "<P>This license is used by " << i->second.authors.size() << " authors (list omitted for brevity).</P>\n"; |
| os << "<P>This license applies to " << i->second.files.size() << " files (list omitted for brevity).</P>\n"; |
| } |
| ++license_index; |
| ++i; |
| } |
| // |
| // Output list of files not found to be under license control: |
| // |
| os << "<h2><a name=\"files\"></a>Files With No Recognisable Licence</h2>\n" |
| "<P>The following " << m_unknown_licenses.size() << " files had no recognisable license information:</P><BLOCKQUOTE><P>\n"; |
| std::set<fs::path, path_less>::const_iterator i2, j2; |
| i2 = m_unknown_licenses.begin(); |
| j2 = m_unknown_licenses.end(); |
| while(i2 != j2) |
| { |
| os << split_path(m_boost_path, *i2) << "<br>\n"; |
| ++i2; |
| } |
| os << "</p></BLOCKQUOTE>"; |
| // |
| // Output list of files with no found copyright holder: |
| // |
| os << "<h2><a name=\"authors\"></a>Files With No Recognisable Copyright Holder</h2>\n" |
| "<P>The following " << m_unknown_authors.size() << " files had no recognisable copyright holder:</P>\n<BLOCKQUOTE><P>"; |
| i2 = m_unknown_authors.begin(); |
| j2 = m_unknown_authors.end(); |
| while(i2 != j2) |
| { |
| os << split_path(m_boost_path, *i2) << "<br>\n"; |
| ++i2; |
| } |
| os << "</p></BLOCKQUOTE>"; |
| if(!m_bsl_summary_mode) |
| { |
| // |
| // Output list of files that have been moved over to the Boost |
| // Software License, along with enough information for human |
| // verification. |
| // |
| os << "<h2><a name=\"bsl-converted\"></a>Files that can be automatically converted to the Boost Software License</h2>\n" |
| << "<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"; |
| if (!m_converted_to_bsl.empty()) |
| { |
| typedef std::map<fs::path, std::pair<std::string, std::string>, path_less> |
| ::const_iterator conv_iterator; |
| conv_iterator i = m_converted_to_bsl.begin(), |
| ie = m_converted_to_bsl.end(); |
| int file_num = 1; |
| while (i != ie) |
| { |
| os << "<P>[" << file_num << "] File: <tt>" << split_path(m_boost_path, i->first) |
| << "</tt><br>\n<table border=\"1\">\n <tr>\n <td><pre>" |
| << i->second.first << "</pre></td>\n <td><pre>" |
| << i->second.second << "</pre></td>\n </tr>\n</table>\n"; |
| ++i; |
| ++file_num; |
| } |
| } |
| // |
| // Output list of files that could be moved over to the Boost Software License |
| // |
| os << "<h2><a name=\"to-bsl\"></a>Files that could be converted to the Boost Software License</h2>\n" |
| "<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>"; |
| i2 = m_can_migrate_to_bsl.begin(); |
| j2 = m_can_migrate_to_bsl.end(); |
| while(i2 != j2) |
| { |
| os << split_path(m_boost_path, *i2) << "<br>\n"; |
| ++i2; |
| } |
| os << "</p></BLOCKQUOTE>"; |
| // |
| // Output list of files that can not be moved over to the Boost Software License |
| // |
| os << "<h2><a name=\"not-to-bsl\"></a>Files that can NOT be converted to the Boost Software License</h2>\n" |
| "<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>"; |
| i2 = m_cannot_migrate_to_bsl.begin(); |
| j2 = m_cannot_migrate_to_bsl.end(); |
| while(i2 != j2) |
| { |
| os << split_path(m_boost_path, *i2) << "<br>\n"; |
| ++i2; |
| } |
| os << "</p></BLOCKQUOTE>"; |
| // |
| // Output list of authors that we need permission for to move to the BSL |
| // |
| os << "<h2><a name=\"need-bsl-authors\"></a>Authors we need for the BSL</h2>\n" |
| "<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>"; |
| std::copy(m_authors_for_bsl_migration.begin(), m_authors_for_bsl_migration.end(), |
| std::ostream_iterator<std::string>(os, "<br>\n")); |
| os << "</p></BLOCKQUOTE>"; |
| // |
| // output a table of copyright information: |
| // |
| os << "<H2><a name=\"copyright\"></a>Copyright Holder Information</H2><table border=\"1\">\n"; |
| std::map<std::string, std::set<fs::path, path_less> >::const_iterator ad, ead; |
| ad = m_author_data.begin(); |
| ead = m_author_data.end(); |
| while(ad != ead) |
| { |
| os << "<tr><td>" << ad->first << "</td><td>"; |
| std::set<fs::path, path_less>::const_iterator fi, efi; |
| fi = ad->second.begin(); |
| efi = ad->second.end(); |
| while(fi != efi) |
| { |
| os << split_path(m_boost_path, *fi) << " "; |
| ++fi; |
| } |
| os << "</td></tr>\n"; |
| ++ad; |
| } |
| os << "</table>\n"; |
| } |
| |
| // |
| // output file dependency information: |
| // |
| os << "<H2><a name=\"depend\"></a>File Dependency Information</H2><BLOCKQUOTE><pre>\n"; |
| std::map<fs::path, fs::path, path_less>::const_iterator dep, last_dep; |
| std::set<fs::path, path_less>::const_iterator fi, efi; |
| fi = m_copy_paths.begin(); |
| efi = m_copy_paths.end(); |
| // if in summary mode, just figure out the "bad" files and print those only: |
| std::set<fs::path, path_less> bad_paths; |
| if(m_bsl_summary_mode) |
| { |
| bad_paths.insert(m_unknown_licenses.begin(), m_unknown_licenses.end()); |
| bad_paths.insert(m_unknown_authors.begin(), m_unknown_authors.end()); |
| bad_paths.insert(m_can_migrate_to_bsl.begin(), m_can_migrate_to_bsl.end()); |
| bad_paths.insert(m_cannot_migrate_to_bsl.begin(), m_cannot_migrate_to_bsl.end()); |
| typedef std::map<fs::path, std::pair<std::string, std::string>, path_less> |
| ::const_iterator conv_iterator; |
| conv_iterator i = m_converted_to_bsl.begin(), |
| ie = m_converted_to_bsl.end(); |
| while(i != ie) |
| { |
| bad_paths.insert(i->first); |
| ++i; |
| } |
| fi = bad_paths.begin(); |
| efi = bad_paths.end(); |
| os << "<P>For brevity, only files not under the BSL are shown</P>\n"; |
| } |
| while(fi != efi) |
| { |
| os << split_path(m_boost_path, *fi); |
| dep = m_dependencies.find(*fi); |
| last_dep = m_dependencies.end(); |
| std::set<fs::path, path_less> seen_deps; |
| if (dep != last_dep) |
| while(true) |
| { |
| os << " -> "; |
| if(fs::exists(m_boost_path / dep->second)) |
| os << split_path(m_boost_path, dep->second); |
| else if(fs::exists(dep->second)) |
| os << split_path(fs::path(), dep->second); |
| else |
| os << dep->second.string(); |
| if(seen_deps.find(dep->second) != seen_deps.end()) |
| { |
| os << " <I>(Circular dependency!)</I>"; |
| break; // circular dependency!!! |
| } |
| seen_deps.insert(dep->second); |
| last_dep = dep; |
| dep = m_dependencies.find(dep->second); |
| if((dep == m_dependencies.end()) || (0 == compare_paths(dep->second, last_dep->second))) |
| break; |
| } |
| os << "\n"; |
| ++fi; |
| } |
| os << "</pre></BLOCKQUOTE>\n"; |
| |
| os << "</body></html>\n"; |
| |
| if(!os) |
| { |
| std::string msg("Error writing to "); |
| msg += m_dest_path.string(); |
| msg += "."; |
| std::runtime_error e(msg); |
| boost::throw_exception(e); |
| } |
| |
| } |