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 = 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(first: v.begin(), last: v.end(), m, e: 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(index: 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(x: 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(fmt: licenses.first[i].copyright_formatter, flags: 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(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(x: name); |
160 | // add file to author index: |
161 | m_author_data[name].insert(x: 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(x: name) == m_bsl_authors.end()) { |
168 | ++nonbsl_author_count; |
169 | m_authors_for_bsl_migration.insert(x: 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(x: p); |
197 | if(license_count && !author_count) |
198 | m_unknown_authors.insert(x: 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: start_of_license); |
207 | fileview::const_iterator context_end = |
208 | context_after_license(v, end: 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_line: 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(first: context_start, last: start_of_license); |
218 | before_conversion += "<b>" ; |
219 | before_conversion += html_escape(first: start_of_license, last: end_of_license); |
220 | before_conversion += "</b>" ; |
221 | before_conversion += html_escape(first: end_of_license, last: context_end); |
222 | |
223 | std::string after_conversion = |
224 | html_escape(first: context_start, last: 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(first: end_of_license, last: context_end); |
235 | |
236 | m_converted_to_bsl[p] = |
237 | std::make_pair(x&: before_conversion, y&: after_conversion); |
238 | |
239 | // Perform the actual conversion |
240 | if (m_bsl_convert_mode) { |
241 | try{ |
242 | std::ofstream out((m_boost_path / p).string().c_str()); |
243 | if (!out) { |
244 | std::string msg("Cannot open file for license conversion: " ); |
245 | msg += p.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(x: p); |
271 | else m_can_migrate_to_bsl.insert(x: p); |
272 | } |
273 | } |
274 | } |
275 | |
276 | |