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 <iomanip>
15 #include <cstring>
16 #include <stdexcept>
17 #include <boost/lexical_cast.hpp>
18 #include <boost/filesystem/operations.hpp>
19 #include <boost/throw_exception.hpp>
20 
21 //
22 // split_path is a small helper for outputting a path name,
23 // complete with a link to that path:
24 //
25 struct split_path
26 {
27    const fs::path& root;
28    const fs::path& file;
split_pathsplit_path29    split_path(const fs::path& r, const fs::path& f)
30       : root(r), file(f){}
31 private:
32    split_path& operator=(const split_path&);
33 };
34 
operator <<(std::ostream & os,const split_path & p)35 std::ostream& operator << (std::ostream& os, const split_path& p)
36 {
37    os << "<a href=\"" << (p.root / p.file).string() << "\">" << p.file.string() << "</a>";
38    return os;
39 }
40 
make_link_target(const std::string & s)41 std::string make_link_target(const std::string& s)
42 {
43    // convert an arbitrary string into something suitable
44    // for an <a> name:
45    std::string result;
46    for(unsigned i = 0; i < s.size(); ++i)
47    {
48       result.append(1, static_cast<std::string::value_type>(std::isalnum(s[i]) ? s[i] : '_'));
49    }
50    return result;
51 }
52 
53 
output_license_info()54 void bcp_implementation::output_license_info()
55 {
56    std::pair<const license_info*, int> licenses = get_licenses();
57 
58    std::map<int, license_data>::const_iterator i, j;
59    i = m_license_data.begin();
60    j = m_license_data.end();
61 
62    std::ofstream os(m_dest_path.string().c_str());
63    if(!os)
64    {
65       std::string msg("Error opening ");
66       msg += m_dest_path.string();
67       msg += " for output.";
68       std::runtime_error e(msg);
69       boost::throw_exception(e);
70    }
71    os <<
72       "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0 Transitional//EN\">\n"
73       "<html>\n"
74       "<head>\n"
75       "<title>Boost Licence Dependency Information";
76    if(m_module_list.size() == 1)
77    {
78       os << " for " << *(m_module_list.begin());
79    }
80    os <<
81       "</title>\n"
82       "</head>\n"
83       "<body>\n"
84       "<H1>Boost Licence Dependency Information";
85    if(m_module_list.size() == 1)
86    {
87       os << " for " << *(m_module_list.begin());
88    }
89    os <<
90       "</H1>\n"
91       "<H2>Contents</h2>\n"
92       "<pre><a href=\"#input\">Input Information</a>\n";
93    if(!m_bsl_summary_mode)
94       os << "<a href=\"#summary\">Licence Summary</a>\n";
95    os << "<a href=\"#details\">Licence Details</a>\n";
96 
97    while(i != j)
98    {
99       // title:
100       os << "   <A href=\"#" << make_link_target(licenses.first[i->first].license_name)
101          << "\">" << licenses.first[i->first].license_name << "</a>\n";
102       ++i;
103    }
104 
105    os << "<a href=\"#files\">Files with no recognised license</a>\n"
106       "<a href=\"#authors\">Files with no recognised copyright holder</a>\n";
107    if(!m_bsl_summary_mode)
108    {
109       os <<
110       "Moving to the Boost Software License...\n"
111       "  <a href=\"#bsl-converted\">Files that can be automatically converted to the Boost Software License</a>\n"
112       "  <a href=\"#to-bsl\">Files that can be manually converted to the Boost Software License</a>\n"
113       "  <a href=\"#not-to-bsl\">Files that can <b>NOT</b> be moved to the Boost Software License</a>\n"
114       "  <a href=\"#need-bsl-authors\">Authors we need to move to the Boost Software License</a>\n"
115       "<a href=\"#copyright\">Copyright Holder Information</a>\n";
116    }
117    os <<
118       "<a href=\"#depend\">File Dependency Information</a>\n"
119       "</pre>";
120 
121    //
122    // input Information:
123    //
124    os << "<a name=\"input\"></a><h2>Input Information</h2>\n";
125    if(m_scan_mode)
126       os << "<P>The following files were scanned for boost dependencies:<BR>";
127    else
128       os << "<P>The following Boost modules were checked:<BR>";
129 
130    std::list<std::string>::const_iterator si = m_module_list.begin();
131    std::list<std::string>::const_iterator sj = m_module_list.end();
132    while(si != sj)
133    {
134       os << *si << "<BR>";
135       ++si;
136    }
137    os << "</p><p>The Boost path was: <code>" << m_boost_path.string() << "</code></P>";
138    //
139    // extract the boost version number from the boost directory tree,
140    // not from this app (which may have been built from a previous
141    // version):
142    //
143    fileview version_file(m_boost_path / "boost/version.hpp");
144    static const boost::regex version_regex(
145       "^[[:blank:]]*#[[:blank:]]*define[[:blank:]]+BOOST_VERSION[[:blank:]]+(\\d+)");
146    boost::cmatch what;
147    if(boost::regex_search(version_file.begin(), version_file.end(), what, version_regex))
148    {
149       int version = boost::lexical_cast<int>(what.str(1));
150       os << "<p>The Boost version is: " << version / 100000 << "." << version / 100 % 1000 << "." << version % 100 << "</P>\n";
151    }
152 
153    //
154    // output each license:
155    //
156    i = m_license_data.begin();
157    j = m_license_data.end();
158    if(!m_bsl_summary_mode)
159    {
160       //
161       // start with the summary:
162       //
163       os << "<a name=\"summary\"></a><h2>Licence Summary</h2>\n";
164       while(i != j)
165       {
166          // title:
167          os <<
168             "<H3>" << licenses.first[i->first].license_name << "</H3>\n";
169          // license text:
170          os << "<BLOCKQUOTE>" << licenses.first[i->first].license_text << "</BLOCKQUOTE>";
171          // Copyright holders:
172          os << "<P>This license is used by " << i->second.authors.size()
173             << " authors and " << i->second.files.size()
174             << " files <a href=\"#" << make_link_target(licenses.first[i->first].license_name) << "\">(see details)</a>";
175          os << "</P></BLOCKQUOTE>\n";
176          ++i;
177       }
178    }
179    //
180    // and now the details:
181    //
182    i = m_license_data.begin();
183    j = m_license_data.end();
184    int license_index = 0;
185    os << "<a name=\"details\"></a><h2>Licence Details</h2>\n";
186    while(i != j)
187    {
188       // title:
189       os <<
190          "<H3><A name=\"" << make_link_target(licenses.first[i->first].license_name)
191          << "\"></a>" << licenses.first[i->first].license_name << "</H3>\n";
192       // license text:
193       os << "<BLOCKQUOTE>" << licenses.first[i->first].license_text << "</BLOCKQUOTE>";
194       if(!m_bsl_summary_mode || (license_index >= 3))
195       {
196          // Copyright holders:
197          os << "<P>This license is used by the following " << i->second.authors.size() << " copyright holders:</P>\n<BLOCKQUOTE><P>";
198          std::set<std::string>::const_iterator x, y;
199          x = i->second.authors.begin();
200          y = i->second.authors.end();
201          while(x != y)
202          {
203             os << *x << "<BR>\n";
204             ++x;
205          }
206          os << "</P></BLOCKQUOTE>\n";
207          // Files using this license:
208          os << "<P>This license applies to the following " << i->second.files.size() << " files:</P>\n<BLOCKQUOTE><P>";
209          std::set<fs::path, path_less>::const_iterator m, n;
210          m = i->second.files.begin();
211          n = i->second.files.end();
212          while(m != n)
213          {
214             os << split_path(m_boost_path, *m) << "<br>\n";
215             ++m;
216          }
217          os << "</P></BLOCKQUOTE>\n";
218       }
219       else
220       {
221          os << "<P>This license is used by " << i->second.authors.size() << " authors (list omitted for brevity).</P>\n";
222          os << "<P>This license applies to " << i->second.files.size() << " files (list omitted for brevity).</P>\n";
223       }
224       ++license_index;
225       ++i;
226    }
227    //
228    // Output list of files not found to be under license control:
229    //
230    os << "<h2><a name=\"files\"></a>Files With No Recognisable Licence</h2>\n"
231       "<P>The following " << m_unknown_licenses.size() << " files had no recognisable license information:</P><BLOCKQUOTE><P>\n";
232    std::set<fs::path, path_less>::const_iterator i2, j2;
233    i2 = m_unknown_licenses.begin();
234    j2 = m_unknown_licenses.end();
235    while(i2 != j2)
236    {
237       os << split_path(m_boost_path, *i2) << "<br>\n";
238       ++i2;
239    }
240    os << "</p></BLOCKQUOTE>";
241    //
242    // Output list of files with no found copyright holder:
243    //
244    os << "<h2><a name=\"authors\"></a>Files With No Recognisable Copyright Holder</h2>\n"
245       "<P>The following " << m_unknown_authors.size() << " files had no recognisable copyright holder:</P>\n<BLOCKQUOTE><P>";
246    i2 = m_unknown_authors.begin();
247    j2 = m_unknown_authors.end();
248    while(i2 != j2)
249    {
250       os << split_path(m_boost_path, *i2) << "<br>\n";
251       ++i2;
252    }
253    os << "</p></BLOCKQUOTE>";
254    if(!m_bsl_summary_mode)
255    {
256       //
257       // Output list of files that have been moved over to the Boost
258       // Software License, along with enough information for human
259       // verification.
260       //
261       os << "<h2><a name=\"bsl-converted\"></a>Files that can be automatically converted to the Boost Software License</h2>\n"
262          << "<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";
263       if (!m_converted_to_bsl.empty())
264       {
265          typedef std::map<fs::path, std::pair<std::string, std::string>, path_less>
266             ::const_iterator conv_iterator;
267          conv_iterator i = m_converted_to_bsl.begin(),
268                         ie = m_converted_to_bsl.end();
269          int file_num = 1;
270          while (i != ie)
271          {
272             os << "<P>[" << file_num << "] File: <tt>" << split_path(m_boost_path, i->first)
273                << "</tt><br>\n<table border=\"1\">\n  <tr>\n    <td><pre>"
274                << i->second.first << "</pre></td>\n    <td><pre>"
275                << i->second.second << "</pre></td>\n  </tr>\n</table>\n";
276             ++i;
277             ++file_num;
278          }
279       }
280       //
281       // Output list of files that could be moved over to the Boost Software License
282       //
283       os << "<h2><a name=\"to-bsl\"></a>Files that could be converted to the Boost Software License</h2>\n"
284       "<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>";
285       i2 = m_can_migrate_to_bsl.begin();
286       j2 = m_can_migrate_to_bsl.end();
287       while(i2 != j2)
288       {
289          os << split_path(m_boost_path, *i2) << "<br>\n";
290          ++i2;
291       }
292       os << "</p></BLOCKQUOTE>";
293       //
294       // Output list of files that can not be moved over to the Boost Software License
295       //
296       os << "<h2><a name=\"not-to-bsl\"></a>Files that can NOT be converted to the Boost Software License</h2>\n"
297       "<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>";
298       i2 = m_cannot_migrate_to_bsl.begin();
299       j2 = m_cannot_migrate_to_bsl.end();
300       while(i2 != j2)
301       {
302          os << split_path(m_boost_path, *i2) << "<br>\n";
303          ++i2;
304       }
305       os << "</p></BLOCKQUOTE>";
306       //
307       // Output list of authors that we need permission for to move to the BSL
308       //
309       os << "<h2><a name=\"need-bsl-authors\"></a>Authors we need for the BSL</h2>\n"
310          "<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>";
311       std::copy(m_authors_for_bsl_migration.begin(), m_authors_for_bsl_migration.end(),
312                std::ostream_iterator<std::string>(os, "<br>\n"));
313       os << "</p></BLOCKQUOTE>";
314       //
315       // output a table of copyright information:
316       //
317       os << "<H2><a name=\"copyright\"></a>Copyright Holder Information</H2><table border=\"1\">\n";
318       std::map<std::string, std::set<fs::path, path_less> >::const_iterator ad, ead;
319       ad = m_author_data.begin();
320       ead = m_author_data.end();
321       while(ad != ead)
322       {
323          os << "<tr><td>" << ad->first << "</td><td>";
324          std::set<fs::path, path_less>::const_iterator fi, efi;
325          fi = ad->second.begin();
326          efi = ad->second.end();
327          while(fi != efi)
328          {
329             os << split_path(m_boost_path, *fi) << " ";
330             ++fi;
331          }
332          os << "</td></tr>\n";
333          ++ad;
334       }
335       os << "</table>\n";
336    }
337 
338    //
339    // output file dependency information:
340    //
341    os << "<H2><a name=\"depend\"></a>File Dependency Information</H2><BLOCKQUOTE><pre>\n";
342    std::map<fs::path, fs::path, path_less>::const_iterator dep, last_dep;
343    std::set<fs::path, path_less>::const_iterator fi, efi;
344    fi = m_copy_paths.begin();
345    efi = m_copy_paths.end();
346    // if in summary mode, just figure out the "bad" files and print those only:
347    std::set<fs::path, path_less> bad_paths;
348    if(m_bsl_summary_mode)
349    {
350       bad_paths.insert(m_unknown_licenses.begin(), m_unknown_licenses.end());
351       bad_paths.insert(m_unknown_authors.begin(), m_unknown_authors.end());
352       bad_paths.insert(m_can_migrate_to_bsl.begin(), m_can_migrate_to_bsl.end());
353       bad_paths.insert(m_cannot_migrate_to_bsl.begin(), m_cannot_migrate_to_bsl.end());
354       typedef std::map<fs::path, std::pair<std::string, std::string>, path_less>
355          ::const_iterator conv_iterator;
356       conv_iterator i = m_converted_to_bsl.begin(),
357                      ie = m_converted_to_bsl.end();
358       while(i != ie)
359       {
360          bad_paths.insert(i->first);
361          ++i;
362       }
363       fi = bad_paths.begin();
364       efi = bad_paths.end();
365       os << "<P>For brevity, only files not under the BSL are shown</P>\n";
366    }
367    while(fi != efi)
368    {
369       os << split_path(m_boost_path, *fi);
370       dep = m_dependencies.find(*fi);
371       last_dep = m_dependencies.end();
372       std::set<fs::path, path_less> seen_deps;
373       if (dep != last_dep)
374         while(true)
375           {
376             os << " -> ";
377             if(fs::exists(m_boost_path / dep->second))
378               os << split_path(m_boost_path, dep->second);
379             else if(fs::exists(dep->second))
380               os << split_path(fs::path(), dep->second);
381             else
382               os << dep->second.string();
383             if(seen_deps.find(dep->second) != seen_deps.end())
384               {
385                 os << " <I>(Circular dependency!)</I>";
386                 break; // circular dependency!!!
387               }
388             seen_deps.insert(dep->second);
389             last_dep = dep;
390             dep = m_dependencies.find(dep->second);
391             if((dep == m_dependencies.end()) || (0 == compare_paths(dep->second, last_dep->second)))
392               break;
393           }
394       os << "\n";
395       ++fi;
396    }
397    os << "</pre></BLOCKQUOTE>\n";
398 
399    os << "</body></html>\n";
400 
401    if(!os)
402    {
403       std::string msg("Error writing to ");
404       msg += m_dest_path.string();
405       msg += ".";
406       std::runtime_error e(msg);
407       boost::throw_exception(e);
408    }
409 
410 }
411