// inspect program -------------------------------------------------------------------// // Copyright Beman Dawes 2002. // Copyright Rene Rivera 2004-2006. // Copyright Gennaro Prota 2006. // Distributed under the Boost Software License, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) // This program recurses through sub-directories looking for various problems. // It contains some Boost specific features, like ignoring "bin", // and the code that identifies library names assumes the Boost directory // structure. // See http://www.boost.org/tools/inspect/ for more information. const char* boost_no_inspect = "boost-" "no-inspect"; // Directories with a file name of the boost_no_inspect value are not inspected. // Files that contain the boost_no_inspect value are not inspected. #include #include #include #include #include "boost/shared_ptr.hpp" #include "boost/lexical_cast.hpp" #include "boost/filesystem/operations.hpp" #include "boost/filesystem/fstream.hpp" #include // for popen, pclose #if defined(_MSC_VER) # define POPEN _popen # define PCLOSE _pclose #else # define POPEN popen # define PCLOSE pclose #endif #include "time_string.hpp" #include "inspector.hpp" // the inspectors #include "copyright_check.hpp" #include "crlf_check.hpp" #include "end_check.hpp" #include "license_check.hpp" #include "link_check.hpp" #include "path_name_check.hpp" #include "tab_check.hpp" #include "ascii_check.hpp" #include "apple_macro_check.hpp" #include "assert_macro_check.hpp" #include "deprecated_macro_check.hpp" #include "minmax_check.hpp" #include "unnamed_namespace_check.hpp" #if !defined(INSPECT_USE_BOOST_TEST) #define INSPECT_USE_BOOST_TEST 0 #endif #if INSPECT_USE_BOOST_TEST #include "boost/test/included/prg_exec_monitor.hpp" #endif namespace fs = boost::filesystem; using namespace boost::inspect; namespace { fs::path search_root = fs::initial_path(); class inspector_element { typedef boost::shared_ptr< boost::inspect::inspector > inspector_ptr; public: inspector_ptr inspector; explicit inspector_element( boost::inspect::inspector * p ) : inspector(p) {} }; typedef std::list< inspector_element > inspector_list; long file_count = 0; long directory_count = 0; long error_count = 0; const int max_offenders = 5; // maximum "worst offenders" to display boost::inspect::string_set content_signatures; struct error_msg { string library; string rel_path; string msg; int line_number; bool operator<( const error_msg & rhs ) const { if ( library < rhs.library ) return true; if ( library > rhs.library ) return false; if ( rel_path < rhs.rel_path ) return true; if ( rel_path > rhs.rel_path ) return false; if ( line_number < rhs.line_number ) return true; if ( line_number > rhs.line_number ) return false; return msg < rhs.msg; } }; typedef std::vector< error_msg > error_msg_vector; error_msg_vector msgs; struct lib_error_count { int error_count; string library; bool operator<( const lib_error_count & rhs ) const { return error_count > rhs.error_count; } }; typedef std::vector< lib_error_count > lib_error_count_vector; lib_error_count_vector libs; // visit_predicate (determines which directories are visited) --------------// typedef bool(*pred_type)(const path&); bool visit_predicate( const path & pth ) { string local( boost::inspect::relative_to( pth, search_root_path() ) ); string leaf( pth.leaf().string() ); if (leaf[0] == '.') // ignore hidden by convention directories such as return false; // .htaccess, .git, .svn, .bzr, .DS_Store, etc. return // don't look at binaries leaf != "bin" && leaf != "bin.v2" // no point in checking doxygen xml output && local.find("doc/xml") != 0 && local.find("doc\\xml") != 0 // ignore if tag file present && !boost::filesystem::exists(pth / boost_no_inspect) ; } // library_from_content ----------------------------------------------------// string library_from_content( const string & content ) { const string unknown_library ( "unknown" ); const string lib_root ( "www.boost.org/libs/" ); string::size_type pos( content.find( lib_root ) ); string lib = unknown_library; if ( pos != string::npos ) { pos += lib_root.length(); const char delims[] = " " // space and... "/\n\r\t"; string::size_type n = content.find_first_of( string(delims), pos ); if (n != string::npos) lib = string(content, pos, n - pos); } return lib; } // find_signature ----------------------------------------------------------// bool find_signature( const path & file_path, const boost::inspect::string_set & signatures ) { string name( file_path.leaf().string() ); if ( signatures.find( name ) == signatures.end() ) { string::size_type pos( name.rfind( '.' ) ); if ( pos == string::npos || signatures.find( name.substr( pos ) ) == signatures.end() ) return false; } return true; } // load_content ------------------------------------------------------------// void load_content( const path & file_path, string & target ) { target = ""; if ( !find_signature( file_path, content_signatures ) ) return; fs::ifstream fin( file_path, std::ios_base::in|std::ios_base::binary ); if ( !fin ) throw string( "could not open input file: " ) + file_path.string(); std::getline( fin, target, '\0' ); // read the whole file } // check -------------------------------------------------------------------// void check( const string & lib, const path & pth, const string & content, const inspector_list & insp_list ) { // invoke each inspector for ( inspector_list::const_iterator itr = insp_list.begin(); itr != insp_list.end(); ++itr ) { itr->inspector->inspect( lib, pth ); // always call two-argument form if ( find_signature( pth, itr->inspector->signatures() ) ) { itr->inspector->inspect( lib, pth, content ); } } } // visit_all ---------------------------------------------------------------// template< class DirectoryIterator > void visit_all( const string & lib, const path & dir_path, const inspector_list & insps ) { static DirectoryIterator end_itr; ++directory_count; for ( DirectoryIterator itr( dir_path ); itr != end_itr; ++itr ) { if ( fs::is_directory( *itr ) ) { if ( visit_predicate( *itr ) ) { string cur_lib( boost::inspect::impute_library( *itr ) ); check( cur_lib, *itr, "", insps ); visit_all( cur_lib, *itr, insps ); } } else if (itr->path().leaf().string()[0] != '.') // ignore if hidden { ++file_count; string content; load_content( *itr, content ); if (content.find(boost_no_inspect) == string::npos) check( lib.empty() ? library_from_content( content ) : lib, *itr, content, insps ); } } } // display -----------------------------------------------------------------// enum display_format_type { display_html, display_text } display_format = display_html; enum display_mode_type { display_full, display_brief } display_mode = display_full; // display_summary_helper --------------------------------------------------// void display_summary_helper( const string & current_library, int err_count ) { if (display_format == display_text) { std::cout << " " << current_library << " (" << err_count << ")\n"; } else { std::cout << " " << current_library << " (" << err_count << ")
\n"; } } // display_summary ---------------------------------------------------------// void display_summary() { if (display_format == display_text) { std::cout << "Summary:\n"; } else { std::cout << "

Summary

\n" "
\n" ; } string current_library( msgs.begin()->library ); int err_count = 0; for ( error_msg_vector::iterator itr ( msgs.begin() ); itr != msgs.end(); ++itr ) { if ( current_library != itr->library ) { display_summary_helper( current_library, err_count ); current_library = itr->library; err_count = 0; } ++err_count; } display_summary_helper( current_library, err_count ); if (display_format == display_text) std::cout << "\n"; else std::cout << "
\n"; } // html_encode -------------------------------------------------------------// std::string html_encode(std::string const& text) { std::string result; for(std::string::const_iterator it = text.begin(), end = text.end(); it != end; ++it) { switch(*it) { case '<': result += "<"; break; case '>': result += ">"; break; case '&': result += "&"; break; default: result += *it; } } return result; } // display_details ---------------------------------------------------------// void display_details() { if (display_format == display_text) { // display error messages with group indication error_msg current; string sep; for ( error_msg_vector::iterator itr ( msgs.begin() ); itr != msgs.end(); ++itr ) { if ( current.library != itr->library ) { if ( display_full == display_mode ) std::cout << "\n|" << itr->library << "|\n"; else std::cout << "\n\n|" << itr->library << '|'; } if ( current.library != itr->library || current.rel_path != itr->rel_path ) { if ( display_full == display_mode ) { std::cout << " " << itr->rel_path << ":\n"; } else { path current_rel_path(current.rel_path); path this_rel_path(itr->rel_path); if (current_rel_path.branch_path() != this_rel_path.branch_path()) { std::cout << "\n " << this_rel_path.branch_path().string() << '/'; } std::cout << "\n " << this_rel_path.leaf() << ':'; } } if ( current.library != itr->library || current.rel_path != itr->rel_path || current.msg != itr->msg ) { const string m = itr->msg; if ( display_full == display_mode ) std::cout << " " << m << '\n'; else std::cout << ' ' << m; } current.library = itr->library; current.rel_path = itr->rel_path; current.msg = itr->msg; } std::cout << "\n"; } else // html { // display error messages with group indication error_msg current; bool first_sep = true; bool first = true; for ( error_msg_vector::iterator itr ( msgs.begin() ); itr != msgs.end(); ++itr ) { if ( current.library != itr->library ) { if ( !first ) std::cout << "\n"; std::cout << "\n

library << "\">" << itr->library << "

\n
";
        }
        if ( current.library != itr->library
          || current.rel_path != itr->rel_path )
        {
          std::cout << "\n";
          std::cout << itr->rel_path;
          first_sep = true;
        }
        if ( current.library != itr->library
          || current.rel_path != itr->rel_path
          || current.msg != itr->msg )
        {
          std::string sep;
          if (first_sep)
            if (itr->line_number) sep = ":
    "; else sep = ": "; else if (itr->line_number) sep = "
    "; else sep = ", "; // print the message if (itr->line_number) std::cout << sep << "(line " << itr->line_number << ") " << html_encode(itr->msg); else std::cout << sep << html_encode(itr->msg); first_sep = false; } current.library = itr->library; current.rel_path = itr->rel_path; current.msg = itr->msg; first = false; } std::cout << "
\n"; } } // worst_offenders_count_helper --------------------------------------------------// void worst_offenders_count_helper( const string & current_library, int err_count ) { lib_error_count lec; lec.library = current_library; lec.error_count = err_count; libs.push_back( lec ); } // worst_offenders_count -----------------------------------------------------// void worst_offenders_count() { if ( msgs.empty() ) { return; } string current_library( msgs.begin()->library ); int err_count = 0; for ( error_msg_vector::iterator itr ( msgs.begin() ); itr != msgs.end(); ++itr ) { if ( current_library != itr->library ) { worst_offenders_count_helper( current_library, err_count ); current_library = itr->library; err_count = 0; } ++err_count; } worst_offenders_count_helper( current_library, err_count ); } // display_worst_offenders -------------------------------------------------// void display_worst_offenders() { if (display_mode == display_brief) return; if (display_format == display_text) { std::cout << "Worst Offenders:\n"; } else { std::cout << "

Worst Offenders

\n" "
\n" ; } int display_count = 0; int last_error_count = 0; for ( lib_error_count_vector::iterator itr ( libs.begin() ); itr != libs.end() && (display_count < max_offenders || itr->error_count == last_error_count); ++itr, ++display_count ) { if (display_format == display_text) { std::cout << itr->library << " " << itr->error_count << "\n"; } else { std::cout << " library << "\">" << itr->library << " (" << itr->error_count << ")
\n"; } last_error_count = itr->error_count; } if (display_format == display_text) std::cout << "\n"; else std::cout << "
\n"; } const char * options() { return " Output Options:\n\n" " -brief\n" " -text\n" " -version-string \n" "\n" " Checks:\n\n" " -license\n" " -copyright\n" " -crlf\n" " -end\n" " -link\n" " -path_name\n" " -tab\n" " -ascii\n" " -apple_macro\n" " -assert_macro\n" " -deprecated_macro\n" " -minmax\n" " -unnamed\n" " -version-string \n" " default is all checks on; otherwise options specify desired checks" "\n"; } const char * doctype_declaration() { return "" ; } std::string validator_link(const std::string & text) { return // with link to validation service "" + text + "" ; } } // unnamed namespace namespace boost { namespace inspect { // line_break --------------------------------------------------------------// const char * line_break() { return display_format ? "\n" : "
\n"; } // search_root_path --------------------------------------------------------// path search_root_path() { return search_root; } // register_signature ------------------------------------------------------// void inspector::register_signature( const string & signature ) { m_signatures.insert( signature ); content_signatures.insert( signature ); } // error -------------------------------------------------------------------// void inspector::error( const string & library_name, const path & full_path, const string & msg, int line_number ) { ++error_count; error_msg err_msg; err_msg.library = library_name; err_msg.rel_path = relative_to( full_path, search_root_path() ); err_msg.msg = msg; err_msg.line_number = line_number; msgs.push_back( err_msg ); // std::cout << library_name << ": " // << full_path.string() << ": " // << msg << '\n'; } source_inspector::source_inspector() { // C/C++ source code... register_signature( ".c" ); register_signature( ".cpp" ); register_signature( ".css" ); register_signature( ".cxx" ); register_signature( ".h" ); register_signature( ".hpp" ); register_signature( ".hxx" ); register_signature( ".inc" ); register_signature( ".ipp" ); // Boost.Build BJam source code... register_signature( "Jamfile" ); register_signature( ".jam" ); register_signature( ".v2" ); // Other scripts; Python, shell, autoconfig, etc. register_signature( "configure.in" ); register_signature( "GNUmakefile" ); register_signature( "Makefile" ); register_signature( ".bat" ); register_signature( ".mak" ); register_signature( ".pl" ); register_signature( ".py" ); register_signature( ".sh" ); // Hypertext, Boost.Book, and other text... register_signature( "news" ); register_signature( "readme" ); register_signature( "todo" ); register_signature( "NEWS" ); register_signature( "README" ); register_signature( "TODO" ); register_signature( ".boostbook" ); register_signature( ".htm" ); register_signature( ".html" ); register_signature( ".rst" ); register_signature( ".sgml" ); register_signature( ".shtml" ); register_signature( ".txt" ); register_signature( ".xml" ); register_signature( ".xsd" ); register_signature( ".xsl" ); register_signature( ".qbk" ); } hypertext_inspector::hypertext_inspector() { register_signature( ".htm" ); register_signature( ".html" ); register_signature( ".shtml" ); } // impute_library ----------------------------------------------------------// // may return an empty string [gps] string impute_library( const path & full_dir_path ) { path relative( relative_to( full_dir_path, search_root_path() ) ); if ( relative.empty() ) return "boost-root"; string first( (*relative.begin()).string() ); string second = // borland 5.61 requires op= ++relative.begin() == relative.end() ? string() : (*++relative.begin()).string(); if ( first == "boost" ) return second; return (( first == "libs" || first == "tools" ) && !second.empty()) ? second : first; } } // namespace inspect } // namespace boost // cpp_main() --------------------------------------------------------------// #if !INSPECT_USE_BOOST_TEST int main( int argc_param, char * argv_param[] ) #else int cpp_main( int argc_param, char * argv_param[] ) #endif { // for the moment, let's be on the safe side // and ensure we don't modify anything being pointed to; // then we'll do some cleanup here int argc = argc_param; const char* const * argv = &argv_param[0]; if ( argc > 1 && (std::strcmp( argv[1], "-help" ) == 0 || std::strcmp( argv[1], "--help" ) == 0 ) ) { std::clog << "Usage: inspect [search-root] [options...]\n\n" " search-root default is the current directory (i.e. '.')\n\n" << options() << '\n'; return 0; } bool options_not_set = true; bool license_ck = false; bool copyright_ck = false; bool crlf_ck = false; bool end_ck = false; bool link_ck = false; bool path_name_ck = false; bool tab_ck = false; bool ascii_ck = false; bool apple_ck = false; bool assert_ck = false; bool deprecated_ck = false; bool minmax_ck = false; bool unnamed_ck = false; const char* version_string = 0; if ( argc > 1 && *argv[1] != '-' ) { search_root = fs::canonical(fs::absolute(argv[1], fs::initial_path())); --argc; ++argv; } bool invalid_options = false; for(; argc > 1; --argc, ++argv ) { if ( std::strcmp( argv[1], "-license" ) == 0 ) { options_not_set = false; license_ck = true; } else if ( std::strcmp( argv[1], "-copyright" ) == 0 ) { options_not_set = false; copyright_ck = true; } else if ( std::strcmp( argv[1], "-crlf" ) == 0 ) { options_not_set = false; crlf_ck = true; } else if ( std::strcmp( argv[1], "-end" ) == 0 ) { options_not_set = false; end_ck = true; } else if ( std::strcmp( argv[1], "-link" ) == 0 ) { options_not_set = false; link_ck = true; } else if ( std::strcmp( argv[1], "-path_name" ) == 0 ) { options_not_set = false; path_name_ck = true; } else if ( std::strcmp( argv[1], "-tab" ) == 0 ) { options_not_set = false; tab_ck = true; } else if ( std::strcmp( argv[1], "-ascii" ) == 0 ) { options_not_set = false; ascii_ck = true; } else if ( std::strcmp( argv[1], "-apple_macro" ) == 0 ) { options_not_set = false; apple_ck = true; } else if ( std::strcmp( argv[1], "-assert_macro" ) == 0 ) { options_not_set = false; assert_ck = true; } else if ( std::strcmp( argv[1], "-deprecated_macro" ) == 0 ) { options_not_set = false; deprecated_ck = true; } else if ( std::strcmp( argv[1], "-minmax" ) == 0 ) { options_not_set = false; minmax_ck = true; } else if ( std::strcmp( argv[1], "-unnamed" ) == 0 ) { options_not_set = false; unnamed_ck = true; } else if ( argc > 1 && std::strcmp( argv[1], "-text" ) == 0 ) { display_format = display_text; } else if ( argc > 1 && std::strcmp( argv[1], "-brief" ) == 0 ) { display_mode = display_brief; } else if ( std::strcmp( argv[1], "-version-string" ) == 0 ) { if (argc == 2 || argv[2][0] == '-') { std::cerr << "Missing value for -version-string.\n"; invalid_options = true; } else { --argc, ++argv; version_string = argv[1]; } } else { std::cerr << "unknown option: " << argv[1] << '\n'; invalid_options = true; } } if ( invalid_options ) { std::cerr << "\nvalid options are:\n" << options(); return 1; } if (options_not_set) { license_ck = true; copyright_ck = true; crlf_ck = true; end_ck = true; link_ck = true; path_name_ck = true; tab_ck = true; ascii_ck = true; apple_ck = true; assert_ck = true; deprecated_ck = true; minmax_ck = true; unnamed_ck = true; } string inspector_keys; { // begin reporting block // since this is in its own block; reporting will happen // automatically, from each registered inspector, when // leaving, due to destruction of the inspector_list object inspector_list inspectors; if ( license_ck ) inspectors.push_back( inspector_element( new boost::inspect::license_check ) ); if ( copyright_ck ) inspectors.push_back( inspector_element( new boost::inspect::copyright_check ) ); if ( crlf_ck ) inspectors.push_back( inspector_element( new boost::inspect::crlf_check ) ); if ( end_ck ) inspectors.push_back( inspector_element( new boost::inspect::end_check ) ); if ( link_ck ) inspectors.push_back( inspector_element( new boost::inspect::link_check ) ); if ( path_name_ck ) inspectors.push_back( inspector_element( new boost::inspect::file_name_check ) ); if ( tab_ck ) inspectors.push_back( inspector_element( new boost::inspect::tab_check ) ); if ( ascii_ck ) inspectors.push_back( inspector_element( new boost::inspect::ascii_check ) ); if ( apple_ck ) inspectors.push_back( inspector_element( new boost::inspect::apple_macro_check ) ); if ( assert_ck ) inspectors.push_back( inspector_element( new boost::inspect::assert_macro_check ) ); if ( deprecated_ck ) inspectors.push_back( inspector_element( new boost::inspect::deprecated_macro_check ) ); if ( minmax_ck ) inspectors.push_back( inspector_element( new boost::inspect::minmax_check ) ); if ( unnamed_ck ) inspectors.push_back( inspector_element( new boost::inspect::unnamed_namespace_check ) ); visit_all( search_root.leaf().string(), search_root, inspectors ); // close for ( inspector_list::iterator itr = inspectors.begin(); itr != inspectors.end(); ++itr ) { itr->inspector->close(); } string run_date ( "n/a" ); boost::time_string( run_date ); if (display_format == display_text) { std::cout << "Boost Inspection Report\n" "Run Date: " << run_date << "\n" "\n" ; if (version_string) { std::cout << "The files checked were from " << version_string << ".\n\n"; } std::cout << "Totals:\n" << " " << file_count << " files scanned\n" << " " << directory_count << " directories scanned (including root)\n" << " " << error_count << " problems reported\n" << '\n' ; } else { // std::cout << doctype_declaration() << '\n'; std::cout << "\n" "\n" "\n" "Boost Inspection Report\n" "\n" "\n" // we should not use a table, of course [gps] "\n" "\n" "\n" "\n" "\n" "
\"Boost" "\n" "

Boost Inspection Report

\n" "Run Date: " << run_date << "\n" //"  / " << validator_link( "validate me" ) << " /\n" "
\n" "

This report is generated by an inspection\n" "program that checks files for the problems noted below.

\n" ; if (version_string) { std::cout << "

The files checked were from " << html_encode(version_string) << ".

\n"; } std::cout << "

Totals

\n" << file_count << " files scanned
\n" << directory_count << " directories scanned (including root)
\n" << error_count << " problems reported\n

"; } for ( inspector_list::iterator itr = inspectors.begin(); itr != inspectors.end(); ++itr ) { inspector_keys += static_cast(" ") + itr->inspector->name() + ' ' + itr->inspector->desc() + line_break() ; } if (display_format == display_text) std::cout << "\nProblem counts:\n"; else std::cout << "\n

Problem counts

\n

\n" ; } // end of block: starts reporting if (display_format == display_text) std::cout << "\n" ; else std::cout << "

\n"; std::sort( msgs.begin(), msgs.end() ); worst_offenders_count(); std::stable_sort( libs.begin(), libs.end() ); if ( !libs.empty() && display_mode != display_brief) display_worst_offenders(); if ( !msgs.empty() ) { display_summary(); if (display_format == display_text) { std::cout << "Details:\n" << inspector_keys; std::cout << "\nDirectories with a file named \"" << boost_no_inspect << "\" will not be inspected.\n" "Files containing \"" << boost_no_inspect << "\" will not be inspected.\n"; } else { std::cout << "

Details

\n" << inspector_keys; std::cout << "\n

Directories with a file named \"" << boost_no_inspect << "\" will not be inspected.
\n" "Files containing \"" << boost_no_inspect << "\" will not be inspected.

\n"; } display_details(); } if (display_format == display_text) { std::cout << "\n\n" ; } else { std::cout << "\n" "\n"; } return error_count ? 1 : 0; }