blob: dc2ecdd369c2e8774a627f861b155915453e2b5f [file] [log] [blame]
/*=============================================================================
Boost.Wave: A Standard compliant C++ preprocessor library
http://www.boost.org/
Copyright (c) 2001-2010 Hartmut Kaiser. 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)
=============================================================================*/
#include "token_statistics.hpp" // config data
///////////////////////////////////////////////////////////////////////////////
// include required boost libraries
#include <boost/assert.hpp>
#include <boost/program_options.hpp>
///////////////////////////////////////////////////////////////////////////////
// Include Wave itself
#include <boost/wave.hpp>
///////////////////////////////////////////////////////////////////////////////
// Include the lexer stuff
#include <boost/wave/cpplexer/cpp_lex_token.hpp> // token class
#include "xlex_iterator.hpp" // lexer class
#include "collect_token_statistics.hpp"
///////////////////////////////////////////////////////////////////////////////
// import required names
using namespace boost::spirit::classic;
using std::string;
using std::vector;
using std::cout;
using std::cerr;
using std::endl;
using std::ifstream;
using std::ostream;
using std::istreambuf_iterator;
namespace po = boost::program_options;
///////////////////////////////////////////////////////////////////////////////
namespace cmd_line_util {
// predicate to extract all positional arguments from the command line
struct is_argument {
bool operator()(po::option const &opt)
{
return (opt.position_key == -1) ? true : false;
}
};
///////////////////////////////////////////////////////////////////////////////
}
///////////////////////////////////////////////////////////////////////////////
// print the current version
int print_version()
{
// get time of last compilation of this file
boost::wave::util::time_conversion_helper compilation_time(__DATE__ " " __TIME__);
// calculate the number of days since May 9 2005
// (the day the token_statistics project was started)
std::tm first_day;
std::memset (&first_day, 0, sizeof(std::tm));
first_day.tm_mon = 4; // May
first_day.tm_mday = 9; // 09
first_day.tm_year = 105; // 2005
long seconds = long(std::difftime(compilation_time.get_time(),
std::mktime(&first_day)));
cout
<< TOKEN_STATISTICS_VERSION_MAJOR << '.'
<< TOKEN_STATISTICS_VERSION_MINOR << '.'
<< TOKEN_STATISTICS_VERSION_SUBMINOR << '.'
<< seconds/(3600*24); // get number of days from seconds
return 1; // exit app
}
///////////////////////////////////////////////////////////////////////////////
//
int
do_actual_work(vector<string> const &arguments, po::variables_map const &vm)
{
// current file position is saved for exception handling
boost::wave::util::file_position_type current_position;
try {
// this object keeps track of all the statistics
collect_token_statistics stats;
// collect the token statistics for all arguments given
vector<string>::const_iterator lastfile = arguments.end();
for (vector<string>::const_iterator file_it = arguments.begin();
file_it != lastfile; ++file_it)
{
ifstream instream((*file_it).c_str());
string instring;
if (!instream.is_open()) {
cerr << "token_statistics: could not open input file: "
<< *file_it << endl;
continue;
}
instream.unsetf(std::ios::skipws);
instring = string(istreambuf_iterator<char>(instream.rdbuf()),
istreambuf_iterator<char>());
// The template boost::wave::cpplexer::lex_token<> is the token type to be
// used by the Wave library.
typedef boost::wave::cpplexer::xlex::xlex_iterator<
boost::wave::cpplexer::lex_token<> >
lexer_type;
typedef boost::wave::context<
std::string::iterator, lexer_type
> context_type;
// The preprocessor iterator shouldn't be constructed directly. It is
// to be generated through a wave::context<> object. This wave:context<>
// object is additionally to be used to initialize and define different
// parameters of the actual preprocessing.
// The preprocessing of the input stream is done on the fly behind the
// scenes during iteration over the context_type::iterator_type stream.
context_type ctx (instring.begin(), instring.end(), (*file_it).c_str());
// add include directories to the include path
if (vm.count("include")) {
vector<string> const &paths =
vm["include"].as<vector<string> >();
vector<string>::const_iterator end = paths.end();
for (vector<string>::const_iterator cit = paths.begin();
cit != end; ++cit)
{
ctx.add_include_path((*cit).c_str());
}
}
// add system include directories to the include path
if (vm.count("sysinclude")) {
vector<string> const &syspaths =
vm["sysinclude"].as<vector<string> >();
vector<string>::const_iterator end = syspaths.end();
for (vector<string>::const_iterator cit = syspaths.begin();
cit != end; ++cit)
{
ctx.add_sysinclude_path((*cit).c_str());
}
}
// analyze the actual file
context_type::iterator_type first = ctx.begin();
context_type::iterator_type last = ctx.end();
while (first != last) {
current_position = (*first).get_position();
stats(*first);
++first;
}
}
// print out the collected statistics
stats.print();
}
catch (boost::wave::cpp_exception const& e) {
// some preprocessing error
cerr
<< e.file_name() << "(" << e.line_no() << "): "
<< e.description() << endl;
return 2;
}
catch (std::exception const& e) {
// use last recognized token to retrieve the error position
cerr
<< current_position.get_file()
<< "(" << current_position.get_line() << "): "
<< "exception caught: " << e.what()
<< endl;
return 3;
}
catch (...) {
// use last recognized token to retrieve the error position
cerr
<< current_position.get_file()
<< "(" << current_position.get_line() << "): "
<< "unexpected exception caught." << endl;
return 4;
}
return 0;
}
///////////////////////////////////////////////////////////////////////////////
// here we go!
int
main (int argc, char *argv[])
{
try {
// analyze the command line options and arguments
vector<string> syspathes;
po::options_description desc("Usage: token_statistics [options] file ...");
desc.add_options()
("help,h", "print out program usage (this message)")
("version,v", "print the version number")
("include,I", po::value<vector<string> >(),
"specify additional include directory")
("sysinclude,S", po::value<vector<string> >(),
"specify additional system include directory")
;
using namespace boost::program_options::command_line_style;
po::parsed_options opts = po::parse_command_line(argc, argv, desc, unix_style);
po::variables_map vm;
po::store(opts, vm);
po::notify(vm);
if (vm.count("help")) {
cout << desc << endl;
return 1;
}
if (vm.count("version")) {
return print_version();
}
// extract the arguments from the parsed command line
vector<po::option> arguments;
std::remove_copy_if(opts.options.begin(), opts.options.end(),
inserter(arguments, arguments.end()), cmd_line_util::is_argument());
// if there is no input file given, then exit
if (0 == arguments.size() || 0 == arguments[0].value.size()) {
cerr << "token_statistics: No input file given. "
<< "Use --help to get a hint." << endl;
return 5;
}
// iterate over all given input files
return do_actual_work(arguments[0].value , vm);
}
catch (std::exception const& e) {
cout << "token_statistics: exception caught: " << e.what() << endl;
return 6;
}
catch (...) {
cerr << "token_statistics: unexpected exception caught." << endl;
return 7;
}
}