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
context_before_license(const fileview & v,fileview::const_iterator start,int context_lines=3)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
context_after_license(const fileview & v,fileview::const_iterator end,int context_lines=3)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
find_prefix(const fileview & v,fileview::const_iterator start_of_line)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
html_escape(fileview::const_iterator first,fileview::const_iterator last)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 += "&lt;"; break;
89     case '>': result += "&gt;"; break;
90     case '&': result += "&amp;"; break;
91     default: result += *first;
92     }
93     ++first;
94   }
95   return result;
96 }
97 
is_non_bsl_license(int index)98 static bool is_non_bsl_license(int index)
99 {
100   return index > 2;
101 }
102 
scan_license(const fs::path & p,const fileview & v)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).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(p);
271        else m_can_migrate_to_bsl.insert(p);
272      }
273    }
274 }
275 
276