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 <iostream> |
---|
15 | |
---|
16 | |
---|
17 | const int boost_license_lines = 3; |
---|
18 | static const std::string boost_license_text[boost_license_lines] = { |
---|
19 | "Distributed under the Boost Software License, Version 1.0. (See", |
---|
20 | "accompanying file LICENSE_1_0.txt or copy at", |
---|
21 | "http://www.boost.org/LICENSE_1_0.txt)" |
---|
22 | }; |
---|
23 | |
---|
24 | fileview::const_iterator |
---|
25 | context_before_license(const fileview& v, fileview::const_iterator start, |
---|
26 | int context_lines = 3) |
---|
27 | { |
---|
28 | char last_char = '\0'; |
---|
29 | while (start != v.begin() && context_lines >= 0) { |
---|
30 | if (*start == '\r' || *start == '\n' |
---|
31 | && (last_char == *start || (last_char != '\r' && last_char != '\n'))) |
---|
32 | --context_lines; |
---|
33 | |
---|
34 | last_char = *start; |
---|
35 | --start; |
---|
36 | } |
---|
37 | |
---|
38 | // Unless we hit the beginning, we need to step forward one to start |
---|
39 | // on the next line. |
---|
40 | if (start != v.begin()) ++start; |
---|
41 | |
---|
42 | return start; |
---|
43 | } |
---|
44 | |
---|
45 | fileview::const_iterator |
---|
46 | context_after_license(const fileview& v, fileview::const_iterator end, |
---|
47 | int context_lines = 3) |
---|
48 | { |
---|
49 | char last_char = '\0'; |
---|
50 | while (end != v.end() && context_lines >= 0) { |
---|
51 | if (*end == '\r' || *end == '\n' |
---|
52 | && (last_char == *end || (last_char != '\r' && last_char != '\n'))) |
---|
53 | --context_lines; |
---|
54 | |
---|
55 | last_char = *end; |
---|
56 | ++end; |
---|
57 | } |
---|
58 | |
---|
59 | return end; |
---|
60 | } |
---|
61 | |
---|
62 | static std::string |
---|
63 | find_prefix(const fileview& v, fileview::const_iterator start_of_line) |
---|
64 | { |
---|
65 | while (start_of_line != v.begin() |
---|
66 | && *start_of_line != '\n' |
---|
67 | && *start_of_line != '\r') |
---|
68 | --start_of_line; |
---|
69 | if (start_of_line != v.begin()) |
---|
70 | ++start_of_line; |
---|
71 | |
---|
72 | fileview::const_iterator first_noncomment_char = start_of_line; |
---|
73 | while (*first_noncomment_char == '/' |
---|
74 | || *first_noncomment_char == '*' |
---|
75 | || *first_noncomment_char == ' ' |
---|
76 | || *first_noncomment_char == '#') |
---|
77 | ++first_noncomment_char; |
---|
78 | |
---|
79 | return std::string(start_of_line, first_noncomment_char); |
---|
80 | } |
---|
81 | |
---|
82 | static std::string |
---|
83 | html_escape(fileview::const_iterator first, fileview::const_iterator last) |
---|
84 | { |
---|
85 | std::string result; |
---|
86 | while (first != last) { |
---|
87 | switch (*first) { |
---|
88 | case '<': result += "<"; break; |
---|
89 | case '>': result += ">"; break; |
---|
90 | case '&': result += "&"; break; |
---|
91 | default: result += *first; |
---|
92 | } |
---|
93 | ++first; |
---|
94 | } |
---|
95 | return result; |
---|
96 | } |
---|
97 | |
---|
98 | static bool is_non_bsl_license(int index) |
---|
99 | { |
---|
100 | return index > 2; |
---|
101 | } |
---|
102 | |
---|
103 | void bcp_implementation::scan_license(const fs::path& p, const fileview& v) |
---|
104 | { |
---|
105 | std::pair<const license_info*, int> licenses = get_licenses(); |
---|
106 | // |
---|
107 | // scan file for all the licenses in the list: |
---|
108 | // |
---|
109 | int license_count = 0; |
---|
110 | int author_count = 0; |
---|
111 | int nonbsl_author_count = 0; |
---|
112 | bool has_non_bsl_license = false; |
---|
113 | fileview::const_iterator start_of_license = v.begin(), |
---|
114 | end_of_license = v.end(); |
---|
115 | bool start_in_middle_of_line = false; |
---|
116 | |
---|
117 | for(int i = 0; i < licenses.second; ++i) |
---|
118 | { |
---|
119 | boost::match_results<fileview::const_iterator> m; |
---|
120 | if(boost::regex_search(v.begin(), v.end(), m, licenses.first[i].license_signature)) |
---|
121 | { |
---|
122 | start_of_license = m[0].first; |
---|
123 | end_of_license = m[0].second; |
---|
124 | |
---|
125 | if (is_non_bsl_license(i) && i < licenses.second - 1) |
---|
126 | has_non_bsl_license = true; |
---|
127 | |
---|
128 | // add this license to the list: |
---|
129 | m_license_data[i].files.insert(p); |
---|
130 | ++license_count; |
---|
131 | // |
---|
132 | // scan for the associated copyright declarations: |
---|
133 | // |
---|
134 | boost::regex_iterator<const char*> cpy(v.begin(), v.end(), licenses.first[i].copyright_signature); |
---|
135 | boost::regex_iterator<const char*> ecpy; |
---|
136 | while(cpy != ecpy) |
---|
137 | { |
---|
138 | #if 0 |
---|
139 | // Not dealing with copyrights because we don't have the years |
---|
140 | if ((*cpy)[0].first < start_of_license) |
---|
141 | start_of_license = (*cpy)[0].first; |
---|
142 | if ((*cpy)[0].second > end_of_license) |
---|
143 | end_of_license = (*cpy)[0].second; |
---|
144 | #endif |
---|
145 | |
---|
146 | // extract the copy holders as a list: |
---|
147 | std::string author_list = cpy->format(licenses.first[i].copyright_formatter, boost::format_all); |
---|
148 | // now enumerate that list for all the names: |
---|
149 | static const boost::regex author_separator("(?:\\s*,(?!\\s*(?:inc|ltd)\\b)\\s*|\\s+(,\\s*)?(and|&)\\s+)|by\\s+", boost::regex::perl | boost::regex::icase); |
---|
150 | boost::regex_token_iterator<std::string::const_iterator> atr(author_list.begin(), author_list.end(), author_separator, -1); |
---|
151 | boost::regex_token_iterator<std::string::const_iterator> eatr; |
---|
152 | while(atr != eatr) |
---|
153 | { |
---|
154 | // get the reformatted authors name: |
---|
155 | std::string name = format_authors_name(*atr); |
---|
156 | // add to list of authors for this file: |
---|
157 | if(name.size() && name[0] != '-') |
---|
158 | { |
---|
159 | m_license_data[i].authors.insert(name); |
---|
160 | // add file to author index: |
---|
161 | m_author_data[name].insert(p); |
---|
162 | ++author_count; |
---|
163 | |
---|
164 | // If this is not the Boost Software License (license 0), and the author hasn't given |
---|
165 | // blanket permission, note this for the report. |
---|
166 | if (has_non_bsl_license |
---|
167 | && m_bsl_authors.find(name) == m_bsl_authors.end()) { |
---|
168 | ++nonbsl_author_count; |
---|
169 | m_authors_for_bsl_migration.insert(name); |
---|
170 | } |
---|
171 | } |
---|
172 | ++atr; |
---|
173 | } |
---|
174 | ++cpy; |
---|
175 | } |
---|
176 | |
---|
177 | while (start_of_license != v.begin() |
---|
178 | && *start_of_license != '\r' |
---|
179 | && *start_of_license != '\n' |
---|
180 | && *start_of_license != '.') |
---|
181 | --start_of_license; |
---|
182 | |
---|
183 | if (start_of_license != v.begin()) { |
---|
184 | if (*start_of_license == '.') |
---|
185 | start_in_middle_of_line = true; |
---|
186 | ++start_of_license; |
---|
187 | } |
---|
188 | |
---|
189 | while (end_of_license != v.end() |
---|
190 | && *end_of_license != '\r' |
---|
191 | && *end_of_license != '\n') |
---|
192 | ++end_of_license; |
---|
193 | } |
---|
194 | } |
---|
195 | if(license_count == 0) |
---|
196 | m_unknown_licenses.insert(p); |
---|
197 | if(license_count && !author_count) |
---|
198 | m_unknown_authors.insert(p); |
---|
199 | |
---|
200 | if (has_non_bsl_license) { |
---|
201 | bool converted = false; |
---|
202 | if (nonbsl_author_count == 0 |
---|
203 | && license_count == 1) { |
---|
204 | // Grab a few lines of context |
---|
205 | fileview::const_iterator context_start = |
---|
206 | context_before_license(v, start_of_license); |
---|
207 | fileview::const_iterator context_end = |
---|
208 | context_after_license(v, end_of_license); |
---|
209 | |
---|
210 | // TBD: For files that aren't C++ code, this will have to |
---|
211 | // change. |
---|
212 | std::string prefix = find_prefix(v, start_of_license); |
---|
213 | |
---|
214 | // Create enough information to permit manual verification of |
---|
215 | // the correctness of the transformation |
---|
216 | std::string before_conversion = |
---|
217 | html_escape(context_start, start_of_license); |
---|
218 | before_conversion += "<b>"; |
---|
219 | before_conversion += html_escape(start_of_license, end_of_license); |
---|
220 | before_conversion += "</b>"; |
---|
221 | before_conversion += html_escape(end_of_license, context_end); |
---|
222 | |
---|
223 | std::string after_conversion = |
---|
224 | html_escape(context_start, start_of_license); |
---|
225 | if (start_in_middle_of_line) |
---|
226 | after_conversion += '\n'; |
---|
227 | |
---|
228 | after_conversion += "<b>"; |
---|
229 | for (int i = 0; i < boost_license_lines; ++i) { |
---|
230 | if (i > 0) after_conversion += '\n'; |
---|
231 | after_conversion += prefix + boost_license_text[i]; |
---|
232 | } |
---|
233 | after_conversion += "</b>"; |
---|
234 | after_conversion += html_escape(end_of_license, context_end); |
---|
235 | |
---|
236 | m_converted_to_bsl[p] = |
---|
237 | std::make_pair(before_conversion, after_conversion); |
---|
238 | |
---|
239 | // Perform the actual conversion |
---|
240 | if (m_bsl_convert_mode) { |
---|
241 | try{ |
---|
242 | std::ofstream out((m_boost_path / p).native_file_string().c_str()); |
---|
243 | if (!out) { |
---|
244 | std::string msg("Cannot open file for license conversion: "); |
---|
245 | msg += p.native_file_string(); |
---|
246 | std::runtime_error e(msg); |
---|
247 | boost::throw_exception(e); |
---|
248 | } |
---|
249 | |
---|
250 | out << std::string(v.begin(), start_of_license); |
---|
251 | if (start_in_middle_of_line) |
---|
252 | out << std::endl; |
---|
253 | |
---|
254 | for (int j = 0; j < boost_license_lines; ++j) { |
---|
255 | if (j > 0) out << std::endl; |
---|
256 | out << prefix << boost_license_text[j]; |
---|
257 | } |
---|
258 | out << std::string(end_of_license, v.end()); |
---|
259 | |
---|
260 | converted = true; |
---|
261 | } |
---|
262 | catch(const std::exception& e) |
---|
263 | { |
---|
264 | std::cerr << e.what() << std::endl; |
---|
265 | } |
---|
266 | } |
---|
267 | } |
---|
268 | |
---|
269 | if (!converted) { |
---|
270 | if (nonbsl_author_count > 0) m_cannot_migrate_to_bsl.insert(p); |
---|
271 | else m_can_migrate_to_bsl.insert(p); |
---|
272 | } |
---|
273 | } |
---|
274 | } |
---|
275 | |
---|