/* * * 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 <iostream> const int boost_license_lines = 3; static const std::string boost_license_text[boost_license_lines] = { "Distributed under 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)" }; fileview::const_iterator context_before_license(const fileview& v, fileview::const_iterator start, int context_lines = 3) { char last_char = '\0'; while (start != v.begin() && context_lines >= 0) { if ((*start == '\r') || (*start == '\n') && ((last_char == *start) || ((last_char != '\r') && (last_char != '\n')))) --context_lines; last_char = *start; --start; } // Unless we hit the beginning, we need to step forward one to start // on the next line. if (start != v.begin()) ++start; return start; } fileview::const_iterator context_after_license(const fileview& v, fileview::const_iterator end, int context_lines = 3) { char last_char = '\0'; while (end != v.end() && context_lines >= 0) { if (*end == '\r' || *end == '\n' && (last_char == *end || (last_char != '\r' && last_char != '\n'))) --context_lines; last_char = *end; ++end; } return end; } static std::string find_prefix(const fileview& v, fileview::const_iterator start_of_line) { while (start_of_line != v.begin() && *start_of_line != '\n' && *start_of_line != '\r') --start_of_line; if (start_of_line != v.begin()) ++start_of_line; fileview::const_iterator first_noncomment_char = start_of_line; while (*first_noncomment_char == '/' || *first_noncomment_char == '*' || *first_noncomment_char == ' ' || *first_noncomment_char == '#') ++first_noncomment_char; return std::string(start_of_line, first_noncomment_char); } static std::string html_escape(fileview::const_iterator first, fileview::const_iterator last) { std::string result; while (first != last) { switch (*first) { case '<': result += "<"; break; case '>': result += ">"; break; case '&': result += "&"; break; default: result += *first; } ++first; } return result; } static bool is_non_bsl_license(int index) { return index > 2; } void bcp_implementation::scan_license(const fs::path& p, const fileview& v) { std::pair<const license_info*, int> licenses = get_licenses(); // // scan file for all the licenses in the list: // int license_count = 0; int author_count = 0; int nonbsl_author_count = 0; bool has_non_bsl_license = false; fileview::const_iterator start_of_license = v.begin(), end_of_license = v.end(); bool start_in_middle_of_line = false; for(int i = 0; i < licenses.second; ++i) { boost::match_results<fileview::const_iterator> m; if(boost::regex_search(v.begin(), v.end(), m, licenses.first[i].license_signature)) { start_of_license = m[0].first; end_of_license = m[0].second; if (is_non_bsl_license(i) && i < licenses.second - 1) has_non_bsl_license = true; // add this license to the list: m_license_data[i].files.insert(p); ++license_count; // // scan for the associated copyright declarations: // boost::regex_iterator<const char*> cpy(v.begin(), v.end(), licenses.first[i].copyright_signature); boost::regex_iterator<const char*> ecpy; while(cpy != ecpy) { #if 0 // Not dealing with copyrights because we don't have the years if ((*cpy)[0].first < start_of_license) start_of_license = (*cpy)[0].first; if ((*cpy)[0].second > end_of_license) end_of_license = (*cpy)[0].second; #endif // extract the copy holders as a list: std::string author_list = cpy->format(licenses.first[i].copyright_formatter, boost::format_all); // now enumerate that list for all the names: static const boost::regex author_separator("(?:\\s*,(?!\\s*(?:inc|ltd)\\b)\\s*|\\s+(,\\s*)?(and|&)\\s+)|by\\s+", boost::regex::perl | boost::regex::icase); boost::regex_token_iterator<std::string::const_iterator> atr(author_list.begin(), author_list.end(), author_separator, -1); boost::regex_token_iterator<std::string::const_iterator> eatr; while(atr != eatr) { // get the reformatted authors name: std::string name = format_authors_name(*atr); // add to list of authors for this file: if(name.size() && name[0] != '-') { m_license_data[i].authors.insert(name); // add file to author index: m_author_data[name].insert(p); ++author_count; // If this is not the Boost Software License (license 0), and the author hasn't given // blanket permission, note this for the report. if (has_non_bsl_license && m_bsl_authors.find(name) == m_bsl_authors.end()) { ++nonbsl_author_count; m_authors_for_bsl_migration.insert(name); } } ++atr; } ++cpy; } while (start_of_license != v.begin() && *start_of_license != '\r' && *start_of_license != '\n' && *start_of_license != '.') --start_of_license; if (start_of_license != v.begin()) { if (*start_of_license == '.') start_in_middle_of_line = true; ++start_of_license; } while (end_of_license != v.end() && *end_of_license != '\r' && *end_of_license != '\n') ++end_of_license; } } if(license_count == 0) m_unknown_licenses.insert(p); if(license_count && !author_count) m_unknown_authors.insert(p); if (has_non_bsl_license) { bool converted = false; if (nonbsl_author_count == 0 && license_count == 1) { // Grab a few lines of context fileview::const_iterator context_start = context_before_license(v, start_of_license); fileview::const_iterator context_end = context_after_license(v, end_of_license); // TBD: For files that aren't C++ code, this will have to // change. std::string prefix = find_prefix(v, start_of_license); // Create enough information to permit manual verification of // the correctness of the transformation std::string before_conversion = html_escape(context_start, start_of_license); before_conversion += "<b>"; before_conversion += html_escape(start_of_license, end_of_license); before_conversion += "</b>"; before_conversion += html_escape(end_of_license, context_end); std::string after_conversion = html_escape(context_start, start_of_license); if (start_in_middle_of_line) after_conversion += '\n'; after_conversion += "<b>"; for (int i = 0; i < boost_license_lines; ++i) { if (i > 0) after_conversion += '\n'; after_conversion += prefix + boost_license_text[i]; } after_conversion += "</b>"; after_conversion += html_escape(end_of_license, context_end); m_converted_to_bsl[p] = std::make_pair(before_conversion, after_conversion); // Perform the actual conversion if (m_bsl_convert_mode) { try{ std::ofstream out((m_boost_path / p).string().c_str()); if (!out) { std::string msg("Cannot open file for license conversion: "); msg += p.string(); std::runtime_error e(msg); boost::throw_exception(e); } out << std::string(v.begin(), start_of_license); if (start_in_middle_of_line) out << std::endl; for (int j = 0; j < boost_license_lines; ++j) { if (j > 0) out << std::endl; out << prefix << boost_license_text[j]; } out << std::string(end_of_license, v.end()); converted = true; } catch(const std::exception& e) { std::cerr << e.what() << std::endl; } } } if (!converted) { if (nonbsl_author_count > 0) m_cannot_migrate_to_bsl.insert(p); else m_can_migrate_to_bsl.insert(p); } } }