1 //  inspect program  -------------------------------------------------------------------//
2 
3 //  Copyright Beman Dawes 2002.
4 //  Copyright Rene Rivera 2004-2006.
5 //  Copyright Gennaro Prota 2006.
6 
7 //  Distributed under the Boost Software License, Version 1.0.
8 //  (See accompanying file LICENSE_1_0.txt or copy at
9 //  http://www.boost.org/LICENSE_1_0.txt)
10 
11 //  This program recurses through sub-directories looking for various problems.
12 //  It contains some Boost specific features, like ignoring "bin",
13 //  and the code that identifies library names assumes the Boost directory
14 //  structure.
15 
16 //  See http://www.boost.org/tools/inspect/ for more information.
17 
18 const char* boost_no_inspect = "boost-" "no-inspect";
19 
20 //  Directories with a file name of the boost_no_inspect value are not inspected.
21 //  Files that contain the boost_no_inspect value are not inspected.
22 
23 
24 #include <vector>
25 #include <list>
26 #include <algorithm>
27 #include <cstring>
28 
29 #include "boost/shared_ptr.hpp"
30 #include "boost/lexical_cast.hpp"
31 #include "boost/filesystem/operations.hpp"
32 #include "boost/filesystem/fstream.hpp"
33 
34 #include <stdio.h>  // for popen, pclose
35 #if defined(_MSC_VER)
36 # define POPEN _popen
37 # define PCLOSE _pclose
38 #else
39 # define POPEN popen
40 # define PCLOSE pclose
41 #endif
42 
43 #include "time_string.hpp"
44 
45 #include "inspector.hpp"
46 
47 // the inspectors
48 #include "copyright_check.hpp"
49 #include "crlf_check.hpp"
50 #include "end_check.hpp"
51 #include "license_check.hpp"
52 #include "link_check.hpp"
53 #include "path_name_check.hpp"
54 #include "tab_check.hpp"
55 #include "ascii_check.hpp"
56 #include "apple_macro_check.hpp"
57 #include "assert_macro_check.hpp"
58 #include "deprecated_macro_check.hpp"
59 #include "minmax_check.hpp"
60 #include "unnamed_namespace_check.hpp"
61 
62 #if !defined(INSPECT_USE_BOOST_TEST)
63 #define INSPECT_USE_BOOST_TEST 0
64 #endif
65 
66 #if INSPECT_USE_BOOST_TEST
67 #include "boost/test/included/prg_exec_monitor.hpp"
68 #endif
69 
70 namespace fs = boost::filesystem;
71 
72 using namespace boost::inspect;
73 
74 namespace
75 {
76   fs::path search_root = fs::initial_path();
77 
78   class inspector_element
79   {
80     typedef boost::shared_ptr< boost::inspect::inspector > inspector_ptr;
81 
82   public:
83     inspector_ptr  inspector;
84     explicit
inspector_element(boost::inspect::inspector * p)85     inspector_element( boost::inspect::inspector * p ) : inspector(p) {}
86   };
87 
88   typedef std::list< inspector_element > inspector_list;
89 
90   long file_count = 0;
91   long directory_count = 0;
92   long error_count = 0;
93   const int max_offenders = 5;  // maximum "worst offenders" to display
94 
95   boost::inspect::string_set content_signatures;
96 
97   struct error_msg
98   {
99     string library;
100     string rel_path;
101     string msg;
102     int    line_number;
103 
operator <__anon15d99a5e0111::error_msg104     bool operator<( const error_msg & rhs ) const
105     {
106       if ( library < rhs.library ) return true;
107       if ( library > rhs.library ) return false;
108       if ( rel_path < rhs.rel_path ) return true;
109       if ( rel_path > rhs.rel_path ) return false;
110       if ( line_number < rhs.line_number ) return true;
111       if ( line_number > rhs.line_number ) return false;
112       return msg < rhs.msg;
113     }
114   };
115 
116   typedef std::vector< error_msg > error_msg_vector;
117   error_msg_vector msgs;
118 
119   struct lib_error_count
120   {
121     int     error_count;
122     string  library;
123 
operator <__anon15d99a5e0111::lib_error_count124     bool operator<( const lib_error_count & rhs ) const
125     {
126       return error_count > rhs.error_count;
127     }
128   };
129 
130   typedef std::vector< lib_error_count > lib_error_count_vector;
131   lib_error_count_vector libs;
132 
133 //  visit_predicate (determines which directories are visited)  --------------//
134 
135   typedef bool(*pred_type)(const path&);
136 
visit_predicate(const path & pth)137   bool visit_predicate( const path & pth )
138   {
139     string local( boost::inspect::relative_to( pth, search_root_path() ) );
140     string leaf( pth.leaf().string() );
141     if (leaf[0] == '.')  // ignore hidden by convention directories such as
142       return false;      //  .htaccess, .git, .svn, .bzr, .DS_Store, etc.
143 
144     return
145       // don't look at binaries
146       leaf != "bin"
147       && leaf != "bin.v2"
148       // no point in checking doxygen xml output
149       && local.find("doc/xml") != 0
150       && local.find("doc\\xml") != 0
151       // ignore if tag file present
152       && !boost::filesystem::exists(pth / boost_no_inspect)
153       ;
154   }
155 
156 //  library_from_content  ----------------------------------------------------//
157 
library_from_content(const string & content)158   string library_from_content( const string & content )
159   {
160     const string unknown_library ( "unknown" );
161     const string lib_root ( "www.boost.org/libs/" );
162     string::size_type pos( content.find( lib_root ) );
163 
164     string lib = unknown_library;
165 
166     if ( pos != string::npos ) {
167 
168         pos += lib_root.length();
169 
170         const char delims[] = " " // space and...
171                               "/\n\r\t";
172 
173         string::size_type n = content.find_first_of( string(delims), pos );
174         if (n != string::npos)
175             lib = string(content, pos, n - pos);
176 
177     }
178 
179     return lib;
180   }
181 
182 //  find_signature  ----------------------------------------------------------//
183 
find_signature(const path & file_path,const boost::inspect::string_set & signatures)184   bool find_signature( const path & file_path,
185     const boost::inspect::string_set & signatures )
186   {
187     string name( file_path.leaf().string() );
188     if ( signatures.find( name ) == signatures.end() )
189     {
190       string::size_type pos( name.rfind( '.' ) );
191       if ( pos == string::npos
192         || signatures.find( name.substr( pos ) )
193           == signatures.end() ) return false;
194     }
195     return true;
196   }
197 
198 //  load_content  ------------------------------------------------------------//
199 
load_content(const path & file_path,string & target)200   void load_content( const path & file_path, string & target )
201   {
202     target = "";
203 
204     if ( !find_signature( file_path, content_signatures ) ) return;
205 
206     fs::ifstream fin( file_path, std::ios_base::in|std::ios_base::binary );
207     if ( !fin )
208       throw string( "could not open input file: " ) + file_path.string();
209     std::getline( fin, target, '\0' ); // read the whole file
210   }
211 
212 //  check  -------------------------------------------------------------------//
213 
check(const string & lib,const path & pth,const string & content,const inspector_list & insp_list)214   void check( const string & lib,
215     const path & pth, const string & content, const inspector_list & insp_list )
216   {
217     // invoke each inspector
218     for ( inspector_list::const_iterator itr = insp_list.begin();
219       itr != insp_list.end(); ++itr )
220     {
221       itr->inspector->inspect( lib, pth ); // always call two-argument form
222       if ( find_signature( pth, itr->inspector->signatures() ) )
223       {
224           itr->inspector->inspect( lib, pth, content );
225       }
226     }
227   }
228 
229 //  visit_all  ---------------------------------------------------------------//
230 
231   template< class DirectoryIterator >
visit_all(const string & lib,const path & dir_path,const inspector_list & insps)232   void visit_all( const string & lib,
233     const path & dir_path, const inspector_list & insps )
234   {
235     static DirectoryIterator end_itr;
236     ++directory_count;
237 
238     for ( DirectoryIterator itr( dir_path ); itr != end_itr; ++itr )
239     {
240       if ( fs::is_directory( *itr ) )
241       {
242         if ( visit_predicate( *itr ) )
243         {
244           string cur_lib( boost::inspect::impute_library( *itr ) );
245           check( cur_lib, *itr, "", insps );
246           visit_all<DirectoryIterator>( cur_lib, *itr, insps );
247         }
248       }
249       else if (itr->path().leaf().string()[0] != '.') // ignore if hidden
250       {
251         ++file_count;
252         string content;
253         load_content( *itr, content );
254         if (content.find(boost_no_inspect) == string::npos)
255           check( lib.empty() ? library_from_content( content ) : lib,
256                  *itr, content, insps );
257       }
258     }
259   }
260 
261 //  display  -----------------------------------------------------------------//
262 
263   enum display_format_type
264   {
265     display_html, display_text
266   }
267   display_format = display_html;
268 
269   enum display_mode_type
270   {
271     display_full, display_brief
272   }
273   display_mode = display_full;
274 
275 //  display_summary_helper  --------------------------------------------------//
276 
display_summary_helper(const string & current_library,int err_count)277   void display_summary_helper( const string & current_library, int err_count )
278   {
279     if (display_format == display_text)
280     {
281         std::cout << "  " << current_library << " (" << err_count << ")\n";
282     }
283     else
284     {
285       std::cout
286         << "  <a href=\"#"
287         << current_library          // what about malformed for URI refs? [gps]
288         << "\">" << current_library
289         << "</a> ("
290         << err_count << ")<br>\n";
291     }
292   }
293 
294 //  display_summary  ---------------------------------------------------------//
295 
display_summary()296   void display_summary()
297   {
298     if (display_format == display_text)
299     {
300       std::cout << "Summary:\n";
301     }
302     else
303     {
304       std::cout <<
305         "<h2>Summary</h2>\n"
306         "<blockquote>\n"
307         ;
308     }
309 
310     string current_library( msgs.begin()->library );
311     int err_count = 0;
312     for ( error_msg_vector::iterator itr ( msgs.begin() );
313       itr != msgs.end(); ++itr )
314     {
315       if ( current_library != itr->library )
316       {
317         display_summary_helper( current_library, err_count );
318         current_library = itr->library;
319         err_count = 0;
320       }
321       ++err_count;
322     }
323     display_summary_helper( current_library, err_count );
324 
325     if (display_format == display_text)
326       std::cout << "\n";
327     else
328       std::cout << "</blockquote>\n";
329   }
330 
331 //  html_encode  -------------------------------------------------------------//
332 
html_encode(std::string const & text)333   std::string html_encode(std::string const& text)
334   {
335     std::string result;
336 
337     for(std::string::const_iterator it = text.begin(),
338         end = text.end(); it != end; ++it)
339     {
340       switch(*it) {
341       case '<':
342         result += "&lt;";
343         break;
344       case '>':
345         result += "&gt;";
346         break;
347       case '&':
348         result += "&amp;";
349         break;
350       default:
351         result += *it;
352       }
353     }
354 
355     return result;
356   }
357 
358 //  display_details  ---------------------------------------------------------//
359 
display_details()360   void display_details()
361   {
362     if (display_format == display_text)
363     {
364       // display error messages with group indication
365       error_msg current;
366       string sep;
367       for ( error_msg_vector::iterator itr ( msgs.begin() );
368         itr != msgs.end(); ++itr )
369       {
370         if ( current.library != itr->library )
371         {
372           if ( display_full == display_mode )
373               std::cout << "\n|" << itr->library << "|\n";
374           else
375               std::cout << "\n\n|" << itr->library << '|';
376         }
377 
378         if ( current.library != itr->library
379           || current.rel_path != itr->rel_path )
380         {
381           if ( display_full == display_mode )
382           {
383             std::cout << "  " << itr->rel_path << ":\n";
384           }
385           else
386           {
387             path current_rel_path(current.rel_path);
388             path this_rel_path(itr->rel_path);
389             if (current_rel_path.branch_path() != this_rel_path.branch_path())
390             {
391               std::cout << "\n  " << this_rel_path.branch_path().string() << '/';
392             }
393             std::cout << "\n    " << this_rel_path.leaf() << ':';
394           }
395         }
396         if ( current.library != itr->library
397           || current.rel_path != itr->rel_path
398           || current.msg != itr->msg )
399         {
400           const string m = itr->msg;
401 
402           if ( display_full == display_mode )
403               std::cout << "    " << m << '\n';
404           else
405               std::cout << ' ' << m;
406         }
407         current.library = itr->library;
408         current.rel_path = itr->rel_path;
409         current.msg = itr->msg;
410       }
411       std::cout << "\n";
412     }
413     else  // html
414     {
415       // display error messages with group indication
416       error_msg current;
417       bool first_sep = true;
418       bool first = true;
419       for ( error_msg_vector::iterator itr ( msgs.begin() );
420         itr != msgs.end(); ++itr )
421       {
422         if ( current.library != itr->library )
423         {
424           if ( !first ) std::cout << "</pre>\n";
425           std::cout << "\n<h3><a name=\"" << itr->library
426                     << "\">" << itr->library << "</a></h3>\n<pre>";
427         }
428         if ( current.library != itr->library
429           || current.rel_path != itr->rel_path )
430         {
431           std::cout << "\n";
432           std::cout << itr->rel_path;
433           first_sep = true;
434         }
435         if ( current.library != itr->library
436           || current.rel_path != itr->rel_path
437           || current.msg != itr->msg )
438         {
439           std::string sep;
440           if (first_sep)
441             if (itr->line_number) sep = ":<br>&nbsp;&nbsp;&nbsp; ";
442             else sep = ": ";
443           else
444             if (itr->line_number) sep = "<br>&nbsp;&nbsp;&nbsp; ";
445             else sep = ", ";
446 
447           // print the message
448           if (itr->line_number)
449             std::cout << sep << "(line " << itr->line_number << ") " << html_encode(itr->msg);
450           else std::cout << sep << html_encode(itr->msg);
451 
452           first_sep = false;
453         }
454         current.library = itr->library;
455         current.rel_path = itr->rel_path;
456         current.msg = itr->msg;
457         first = false;
458       }
459       std::cout << "</pre>\n";
460     }
461   }
462 
463 
464 //  worst_offenders_count_helper  --------------------------------------------------//
465 
worst_offenders_count_helper(const string & current_library,int err_count)466   void worst_offenders_count_helper( const string & current_library, int err_count )
467   {
468         lib_error_count lec;
469         lec.library = current_library;
470         lec.error_count = err_count;
471         libs.push_back( lec );
472   }
473 //  worst_offenders_count  -----------------------------------------------------//
474 
worst_offenders_count()475   void worst_offenders_count()
476   {
477     if ( msgs.empty() )
478     {
479       return;
480     }
481     string current_library( msgs.begin()->library );
482     int err_count = 0;
483     for ( error_msg_vector::iterator itr ( msgs.begin() );
484       itr != msgs.end(); ++itr )
485     {
486       if ( current_library != itr->library )
487       {
488         worst_offenders_count_helper( current_library, err_count );
489         current_library = itr->library;
490         err_count = 0;
491       }
492       ++err_count;
493     }
494     worst_offenders_count_helper( current_library, err_count );
495   }
496 
497 //  display_worst_offenders  -------------------------------------------------//
498 
display_worst_offenders()499   void display_worst_offenders()
500   {
501     if (display_mode == display_brief)
502       return;
503     if (display_format == display_text)
504     {
505       std::cout << "Worst Offenders:\n";
506     }
507     else
508     {
509       std::cout <<
510         "<h2>Worst Offenders</h2>\n"
511         "<blockquote>\n"
512         ;
513     }
514 
515     int display_count = 0;
516     int last_error_count = 0;
517     for ( lib_error_count_vector::iterator itr ( libs.begin() );
518           itr != libs.end()
519             && (display_count < max_offenders
520                 || itr->error_count == last_error_count);
521           ++itr, ++display_count )
522     {
523       if (display_format == display_text)
524       {
525         std::cout << itr->library << " " << itr->error_count << "\n";
526       }
527       else
528       {
529         std::cout
530           << "  <a href=\"#"
531           << itr->library
532           << "\">" << itr->library
533           << "</a> ("
534           << itr->error_count << ")<br>\n";
535       }
536       last_error_count = itr->error_count;
537     }
538 
539     if (display_format == display_text)
540       std::cout << "\n";
541     else
542       std::cout << "</blockquote>\n";
543   }
544 
545 
options()546   const char * options()
547   {
548     return
549          " Output Options:\n\n"
550          "  -brief\n"
551          "  -text\n"
552          "  -version-string <version message>\n"
553          "\n"
554          " Checks:\n\n"
555          "  -license\n"
556          "  -copyright\n"
557          "  -crlf\n"
558          "  -end\n"
559          "  -link\n"
560          "  -path_name\n"
561          "  -tab\n"
562          "  -ascii\n"
563          "  -apple_macro\n"
564          "  -assert_macro\n"
565          "  -deprecated_macro\n"
566          "  -minmax\n"
567          "  -unnamed\n"
568          "  -version-string <version message>\n"
569          " default is all checks on; otherwise options specify desired checks"
570          "\n";
571   }
572 
doctype_declaration()573   const char * doctype_declaration()
574   {
575     return
576          "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\"\n"
577          "\"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">"
578          ;
579   }
580 
validator_link(const std::string & text)581   std::string validator_link(const std::string & text)
582   {
583     return
584         // with link to validation service
585         "<a href=\"http://validator.w3.org/check?uri=referer\">"
586         + text
587         + "</a>"
588         ;
589   }
590 
591 } // unnamed namespace
592 
593 namespace boost
594 {
595   namespace inspect
596   {
597 
598 //  line_break  --------------------------------------------------------------//
599 
line_break()600     const char * line_break()
601     {
602       return display_format ? "\n" : "<br>\n";
603     }
604 
605 //  search_root_path  --------------------------------------------------------//
606 
search_root_path()607     path search_root_path()
608     {
609       return search_root;
610     }
611 
612 //  register_signature  ------------------------------------------------------//
613 
register_signature(const string & signature)614     void inspector::register_signature( const string & signature )
615     {
616       m_signatures.insert( signature );
617       content_signatures.insert( signature );
618     }
619 
620 //  error  -------------------------------------------------------------------//
621 
error(const string & library_name,const path & full_path,const string & msg,int line_number)622     void inspector::error( const string & library_name,
623       const path & full_path, const string & msg, int line_number )
624     {
625       ++error_count;
626       error_msg err_msg;
627       err_msg.library = library_name;
628       err_msg.rel_path = relative_to( full_path, search_root_path() );
629       err_msg.msg = msg;
630       err_msg.line_number = line_number;
631       msgs.push_back( err_msg );
632 
633 //     std::cout << library_name << ": "
634 //        << full_path.string() << ": "
635 //        << msg << '\n';
636 
637     }
638 
source_inspector()639     source_inspector::source_inspector()
640     {
641       // C/C++ source code...
642       register_signature( ".c" );
643       register_signature( ".cpp" );
644       register_signature( ".css" );
645       register_signature( ".cxx" );
646       register_signature( ".h" );
647       register_signature( ".hpp" );
648       register_signature( ".hxx" );
649       register_signature( ".inc" );
650       register_signature( ".ipp" );
651 
652       // Boost.Build BJam source code...
653       register_signature( "Jamfile" );
654       register_signature( ".jam" );
655       register_signature( ".v2" );
656 
657       // Other scripts; Python, shell, autoconfig, etc.
658       register_signature( "configure.in" );
659       register_signature( "GNUmakefile" );
660       register_signature( "Makefile" );
661       register_signature( ".bat" );
662       register_signature( ".mak" );
663       register_signature( ".pl" );
664       register_signature( ".py" );
665       register_signature( ".sh" );
666 
667       // Hypertext, Boost.Book, and other text...
668       register_signature( "news" );
669       register_signature( "readme" );
670       register_signature( "todo" );
671       register_signature( "NEWS" );
672       register_signature( "README" );
673       register_signature( "TODO" );
674       register_signature( ".boostbook" );
675       register_signature( ".htm" );
676       register_signature( ".html" );
677       register_signature( ".rst" );
678       register_signature( ".sgml" );
679       register_signature( ".shtml" );
680       register_signature( ".txt" );
681       register_signature( ".xml" );
682       register_signature( ".xsd" );
683       register_signature( ".xsl" );
684       register_signature( ".qbk" );
685     }
686 
hypertext_inspector()687     hypertext_inspector::hypertext_inspector()
688     {
689       register_signature( ".htm" );
690       register_signature( ".html" );
691       register_signature( ".shtml" );
692     }
693 
694 //  impute_library  ----------------------------------------------------------//
695 
696     // may return an empty string [gps]
impute_library(const path & full_dir_path)697     string impute_library( const path & full_dir_path )
698     {
699       path relative( relative_to( full_dir_path, search_root_path() ) );
700       if ( relative.empty() ) return "boost-root";
701       string first( (*relative.begin()).string() );
702       string second =  // borland 5.61 requires op=
703         ++relative.begin() == relative.end()
704           ? string() : (*++relative.begin()).string();
705 
706       if ( first == "boost" )
707         return second;
708 
709       return (( first == "libs" || first == "tools" ) && !second.empty())
710         ? second : first;
711     }
712 
713   } // namespace inspect
714 } // namespace boost
715 
716 //  cpp_main()  --------------------------------------------------------------//
717 
718 #if !INSPECT_USE_BOOST_TEST
main(int argc_param,char * argv_param[])719 int main( int argc_param, char * argv_param[] )
720 #else
721 int cpp_main( int argc_param, char * argv_param[] )
722 #endif
723 {
724   // <hack> for the moment, let's be on the safe side
725   // and ensure we don't modify anything being pointed to;
726   // then we'll do some cleanup here
727   int argc = argc_param;
728   const char* const * argv = &argv_param[0];
729 
730   if ( argc > 1 && (std::strcmp( argv[1], "-help" ) == 0
731     || std::strcmp( argv[1], "--help" ) == 0 ) )
732   {
733     std::clog << "Usage: inspect [search-root] [options...]\n\n"
734       " search-root default is the current directory (i.e. '.')\n\n"
735       << options() << '\n';
736     return 0;
737   }
738 
739   bool options_not_set = true;
740   bool license_ck = false;
741   bool copyright_ck = false;
742   bool crlf_ck = false;
743   bool end_ck = false;
744   bool link_ck = false;
745   bool path_name_ck = false;
746   bool tab_ck = false;
747   bool ascii_ck = false;
748   bool apple_ck = false;
749   bool assert_ck = false;
750   bool deprecated_ck = false;
751   bool minmax_ck = false;
752   bool unnamed_ck = false;
753   const char* version_string = 0;
754 
755   if ( argc > 1 && *argv[1] != '-' )
756   {
757     search_root = fs::canonical(fs::absolute(argv[1], fs::initial_path()));
758     --argc; ++argv;
759   }
760 
761   bool invalid_options = false;
762   for(; argc > 1; --argc, ++argv )
763   {
764     if ( std::strcmp( argv[1], "-license" ) == 0 ) {
765       options_not_set = false;
766       license_ck = true;
767     }
768     else if ( std::strcmp( argv[1], "-copyright" ) == 0 ) {
769       options_not_set = false;
770       copyright_ck = true;
771     }
772     else if ( std::strcmp( argv[1], "-crlf" ) == 0 ) {
773         options_not_set = false;
774         crlf_ck = true;
775     }
776     else if ( std::strcmp( argv[1], "-end" ) == 0 ) {
777         options_not_set = false;
778         end_ck = true;
779     }
780     else if ( std::strcmp( argv[1], "-link" ) == 0 ) {
781       options_not_set = false;
782       link_ck = true;
783     }
784     else if ( std::strcmp( argv[1], "-path_name" ) == 0 ) {
785       options_not_set = false;
786       path_name_ck = true;
787     }
788     else if ( std::strcmp( argv[1], "-tab" ) == 0 ) {
789       options_not_set = false;
790       tab_ck = true;
791     }
792     else if ( std::strcmp( argv[1], "-ascii" ) == 0 ) {
793       options_not_set = false;
794       ascii_ck = true;
795     }
796     else if ( std::strcmp( argv[1], "-apple_macro" ) == 0 ) {
797       options_not_set = false;
798       apple_ck = true;
799     }
800     else if ( std::strcmp( argv[1], "-assert_macro" ) == 0 ) {
801       options_not_set = false;
802       assert_ck = true;
803     }
804     else if ( std::strcmp( argv[1], "-deprecated_macro" ) == 0 ) {
805       options_not_set = false;
806       deprecated_ck = true;
807     }
808     else if ( std::strcmp( argv[1], "-minmax" ) == 0 ) {
809         options_not_set = false;
810         minmax_ck = true;
811     }
812     else if ( std::strcmp( argv[1], "-unnamed" ) == 0 ) {
813         options_not_set = false;
814         unnamed_ck = true;
815     }
816     else if ( argc > 1 && std::strcmp( argv[1], "-text" ) == 0 )
817     {
818       display_format = display_text;
819     }
820     else if ( argc > 1 && std::strcmp( argv[1], "-brief" ) == 0 )
821     {
822       display_mode = display_brief;
823     }
824     else if ( std::strcmp( argv[1], "-version-string" ) == 0 ) {
825       if (argc == 2 || argv[2][0] == '-') {
826         std::cerr << "Missing value for -version-string.\n";
827         invalid_options = true;
828       }
829       else {
830         --argc, ++argv;
831         version_string = argv[1];
832       }
833     }
834     else
835     {
836       std::cerr << "unknown option: " << argv[1] << '\n';
837       invalid_options = true;
838     }
839   }
840   if ( invalid_options ) {
841       std::cerr << "\nvalid options are:\n"
842                 << options();
843       return 1;
844   }
845 
846   if (options_not_set) {
847     license_ck = true;
848     copyright_ck = true;
849     crlf_ck = true;
850     end_ck = true;
851     link_ck = true;
852     path_name_ck = true;
853     tab_ck = true;
854     ascii_ck = true;
855     apple_ck = true;
856     assert_ck = true;
857     deprecated_ck = true;
858     minmax_ck = true;
859     unnamed_ck = true;
860   }
861 
862   string inspector_keys;
863 
864   { // begin reporting block
865 
866   // since this is in its own block; reporting will happen
867   // automatically, from each registered inspector, when
868   // leaving, due to destruction of the inspector_list object
869   inspector_list inspectors;
870 
871   if ( license_ck )
872     inspectors.push_back( inspector_element( new boost::inspect::license_check ) );
873   if ( copyright_ck )
874     inspectors.push_back( inspector_element( new boost::inspect::copyright_check ) );
875   if ( crlf_ck )
876     inspectors.push_back( inspector_element( new boost::inspect::crlf_check ) );
877   if ( end_ck )
878     inspectors.push_back( inspector_element( new boost::inspect::end_check ) );
879   if ( link_ck )
880     inspectors.push_back( inspector_element( new boost::inspect::link_check ) );
881   if ( path_name_ck )
882     inspectors.push_back( inspector_element( new boost::inspect::file_name_check ) );
883   if ( tab_ck )
884       inspectors.push_back( inspector_element( new boost::inspect::tab_check ) );
885   if ( ascii_ck )
886       inspectors.push_back( inspector_element( new boost::inspect::ascii_check ) );
887   if ( apple_ck )
888       inspectors.push_back( inspector_element( new boost::inspect::apple_macro_check ) );
889   if ( assert_ck )
890       inspectors.push_back( inspector_element( new boost::inspect::assert_macro_check ) );
891   if ( deprecated_ck )
892       inspectors.push_back( inspector_element( new boost::inspect::deprecated_macro_check ) );
893   if ( minmax_ck )
894       inspectors.push_back( inspector_element( new boost::inspect::minmax_check ) );
895   if ( unnamed_ck )
896       inspectors.push_back( inspector_element( new boost::inspect::unnamed_namespace_check ) );
897 
898     visit_all<fs::directory_iterator>( search_root.leaf().string(),
899       search_root, inspectors );
900 
901   // close
902   for ( inspector_list::iterator itr = inspectors.begin();
903         itr != inspectors.end(); ++itr )
904   {
905     itr->inspector->close();
906   }
907 
908   string run_date ( "n/a" );
909   boost::time_string( run_date );
910 
911   if (display_format == display_text)
912   {
913     std::cout
914       <<
915         "Boost Inspection Report\n"
916         "Run Date: " << run_date  << "\n"
917         "\n"
918       ;
919 
920     if (version_string) {
921       std::cout
922         << "The files checked were from "
923         << version_string
924         << ".\n\n";
925     }
926 
927     std::cout
928       << "Totals:\n"
929       << "  " << file_count << " files scanned\n"
930       << "  " << directory_count << " directories scanned (including root)\n"
931       << "  " << error_count << " problems reported\n"
932       << '\n'
933       ;
934   }
935   else
936   {
937     //
938     std::cout << doctype_declaration() << '\n';
939 
940     std::cout
941       << "<html>\n"
942       "<head>\n"
943       "<style> body { font-family: sans-serif; } </style>\n"
944       "<title>Boost Inspection Report</title>\n"
945       "</head>\n"
946 
947       "<body>\n"
948       // we should not use a table, of course [gps]
949       "<table>\n"
950       "<tr>\n"
951       "<td><img src=\"http://www.boost.org/boost.png\" alt=\"Boost logo\" />"
952       "</td>\n"
953       "<td>\n"
954       "<h1>Boost Inspection Report</h1>\n"
955       "<b>Run Date:</b> " << run_date  << "\n"
956       //"&nbsp;&nbsp;/ " << validator_link( "validate me" ) << " /\n"
957       "</td>\n"
958       "</tr>\n"
959       "</table>\n"
960 
961       "<p>This report is generated by an <a href=\"http://www.boost.org/tools/inspect/index.html\">inspection\n"
962       "program</a> that checks files for the problems noted below.</p>\n"
963       ;
964     if (version_string) {
965       std::cout
966         << "<p>The files checked were from "
967         << html_encode(version_string)
968         << ".</p>\n";
969     }
970 
971 
972     std::cout
973       << "<h2>Totals</h2>\n"
974       << file_count << " files scanned<br>\n"
975       << directory_count << " directories scanned (including root)<br>\n"
976       << error_count << " problems reported\n<p>";
977   }
978 
979   for ( inspector_list::iterator itr = inspectors.begin();
980         itr != inspectors.end(); ++itr )
981   {
982 
983     inspector_keys += static_cast<string>("  ")
984         + itr->inspector->name()
985         + ' ' + itr->inspector->desc()
986         + line_break()
987         ;
988   }
989 
990   if (display_format == display_text)
991      std::cout << "\nProblem counts:\n";
992   else
993     std::cout << "\n<h2>Problem counts</h2>\n<blockquote><p>\n" ;
994 
995   } // end of block: starts reporting
996 
997   if (display_format == display_text)
998     std::cout << "\n" ;
999   else
1000     std::cout << "</blockquote>\n";
1001 
1002   std::sort( msgs.begin(), msgs.end() );
1003 
1004   worst_offenders_count();
1005   std::stable_sort( libs.begin(), libs.end() );
1006 
1007   if ( !libs.empty() && display_mode != display_brief)
1008     display_worst_offenders();
1009 
1010   if ( !msgs.empty() )
1011   {
1012     display_summary();
1013 
1014     if (display_format == display_text)
1015     {
1016       std::cout << "Details:\n" << inspector_keys;
1017       std::cout << "\nDirectories with a file named \"" << boost_no_inspect << "\" will not be inspected.\n"
1018                    "Files containing \"" << boost_no_inspect << "\" will not be inspected.\n";
1019    }
1020     else
1021     {
1022       std::cout << "<h2>Details</h2>\n" << inspector_keys;
1023       std::cout << "\n<p>Directories with a file named \"" << boost_no_inspect << "\" will not be inspected.<br>\n"
1024                    "Files containing \"" << boost_no_inspect << "\" will not be inspected.</p>\n";
1025     }
1026     display_details();
1027   }
1028 
1029   if (display_format == display_text)
1030   {
1031     std::cout << "\n\n" ;
1032   }
1033   else
1034   {
1035     std::cout
1036       << "</body>\n"
1037       "</html>\n";
1038   }
1039 
1040   return error_count ? 1 : 0;
1041 }
1042