1 /*=============================================================================
2 Boost.Wave: A Standard compliant C++ preprocessor library
3
4 http://www.boost.org/
5
6 Copyright (c) 2001-2010 Hartmut Kaiser. Distributed under the Boost
7 Software License, Version 1.0. (See accompanying file
8 LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
9 =============================================================================*/
10
11 #include "token_statistics.hpp" // config data
12
13 ///////////////////////////////////////////////////////////////////////////////
14 // include required boost libraries
15 #include <boost/assert.hpp>
16 #include <boost/program_options.hpp>
17
18 ///////////////////////////////////////////////////////////////////////////////
19 // Include Wave itself
20 #include <boost/wave.hpp>
21
22 ///////////////////////////////////////////////////////////////////////////////
23 // Include the lexer stuff
24 #include <boost/wave/cpplexer/cpp_lex_token.hpp> // token class
25 #include "xlex_iterator.hpp" // lexer class
26
27 #include "collect_token_statistics.hpp"
28
29 #include <iostream>
30
31 ///////////////////////////////////////////////////////////////////////////////
32 // import required names
33 using namespace boost::spirit::classic;
34
35 using std::string;
36 using std::vector;
37 using std::cout;
38 using std::cerr;
39 using std::endl;
40 using std::ifstream;
41 using std::ostream;
42 using std::istreambuf_iterator;
43
44 namespace po = boost::program_options;
45
46 ///////////////////////////////////////////////////////////////////////////////
47 namespace cmd_line_util {
48
49 // predicate to extract all positional arguments from the command line
50 struct is_argument {
51
operator ()cmd_line_util::is_argument52 bool operator()(po::option const &opt)
53 {
54 return (opt.position_key == -1) ? true : false;
55 }
56 };
57
58 ///////////////////////////////////////////////////////////////////////////////
59 }
60
61 ///////////////////////////////////////////////////////////////////////////////
62 // print the current version
63
print_version()64 int print_version()
65 {
66 // get time of last compilation of this file
67 boost::wave::util::time_conversion_helper compilation_time(__DATE__ " " __TIME__);
68
69 // calculate the number of days since May 9 2005
70 // (the day the token_statistics project was started)
71 std::tm first_day;
72
73 std::memset (&first_day, 0, sizeof(std::tm));
74 first_day.tm_mon = 4; // May
75 first_day.tm_mday = 9; // 09
76 first_day.tm_year = 105; // 2005
77
78 long seconds = long(std::difftime(compilation_time.get_time(),
79 std::mktime(&first_day)));
80
81 cout
82 << TOKEN_STATISTICS_VERSION_MAJOR << '.'
83 << TOKEN_STATISTICS_VERSION_MINOR << '.'
84 << TOKEN_STATISTICS_VERSION_SUBMINOR << '.'
85 << seconds/(3600*24); // get number of days from seconds
86 return 1; // exit app
87 }
88
89 ///////////////////////////////////////////////////////////////////////////////
90 //
91 int
do_actual_work(vector<string> const & arguments,po::variables_map const & vm)92 do_actual_work(vector<string> const &arguments, po::variables_map const &vm)
93 {
94 // current file position is saved for exception handling
95 boost::wave::util::file_position_type current_position;
96
97 try {
98 // this object keeps track of all the statistics
99 collect_token_statistics stats;
100
101 // collect the token statistics for all arguments given
102 vector<string>::const_iterator lastfile = arguments.end();
103 for (vector<string>::const_iterator file_it = arguments.begin();
104 file_it != lastfile; ++file_it)
105 {
106 ifstream instream((*file_it).c_str());
107 string instring;
108
109 if (!instream.is_open()) {
110 cerr << "token_statistics: could not open input file: "
111 << *file_it << endl;
112 continue;
113 }
114 instream.unsetf(std::ios::skipws);
115 instring = string(istreambuf_iterator<char>(instream.rdbuf()),
116 istreambuf_iterator<char>());
117
118 // The template boost::wave::cpplexer::lex_token<> is the token type to be
119 // used by the Wave library.
120 typedef boost::wave::cpplexer::xlex::xlex_iterator<
121 boost::wave::cpplexer::lex_token<> >
122 lexer_type;
123 typedef boost::wave::context<
124 std::string::iterator, lexer_type
125 > context_type;
126
127 // The preprocessor iterator shouldn't be constructed directly. It is
128 // to be generated through a wave::context<> object. This wave:context<>
129 // object is additionally to be used to initialize and define different
130 // parameters of the actual preprocessing.
131 // The preprocessing of the input stream is done on the fly behind the
132 // scenes during iteration over the context_type::iterator_type stream.
133 context_type ctx (instring.begin(), instring.end(), (*file_it).c_str());
134
135 // add include directories to the include path
136 if (vm.count("include")) {
137 vector<string> const &paths =
138 vm["include"].as<vector<string> >();
139 vector<string>::const_iterator end = paths.end();
140 for (vector<string>::const_iterator cit = paths.begin();
141 cit != end; ++cit)
142 {
143 ctx.add_include_path((*cit).c_str());
144 }
145 }
146
147 // add system include directories to the include path
148 if (vm.count("sysinclude")) {
149 vector<string> const &syspaths =
150 vm["sysinclude"].as<vector<string> >();
151 vector<string>::const_iterator end = syspaths.end();
152 for (vector<string>::const_iterator cit = syspaths.begin();
153 cit != end; ++cit)
154 {
155 ctx.add_sysinclude_path((*cit).c_str());
156 }
157 }
158
159 // analyze the actual file
160 context_type::iterator_type first = ctx.begin();
161 context_type::iterator_type last = ctx.end();
162
163 while (first != last) {
164 current_position = (*first).get_position();
165 stats(*first);
166 ++first;
167 }
168 }
169
170 // print out the collected statistics
171 stats.print();
172 }
173 catch (boost::wave::cpp_exception const& e) {
174 // some preprocessing error
175 cerr
176 << e.file_name() << "(" << e.line_no() << "): "
177 << e.description() << endl;
178 return 2;
179 }
180 catch (std::exception const& e) {
181 // use last recognized token to retrieve the error position
182 cerr
183 << current_position.get_file()
184 << "(" << current_position.get_line() << "): "
185 << "exception caught: " << e.what()
186 << endl;
187 return 3;
188 }
189 catch (...) {
190 // use last recognized token to retrieve the error position
191 cerr
192 << current_position.get_file()
193 << "(" << current_position.get_line() << "): "
194 << "unexpected exception caught." << endl;
195 return 4;
196 }
197 return 0;
198 }
199
200 ///////////////////////////////////////////////////////////////////////////////
201 // here we go!
202 int
main(int argc,char * argv[])203 main (int argc, char *argv[])
204 {
205 try {
206 // analyze the command line options and arguments
207 vector<string> syspathes;
208 po::options_description desc("Usage: token_statistics [options] file ...");
209
210 desc.add_options()
211 ("help,h", "print out program usage (this message)")
212 ("version,v", "print the version number")
213 ("include,I", po::value<vector<string> >(),
214 "specify additional include directory")
215 ("sysinclude,S", po::value<vector<string> >(),
216 "specify additional system include directory")
217 ;
218
219 using namespace boost::program_options::command_line_style;
220
221 po::parsed_options opts = po::parse_command_line(argc, argv, desc, unix_style);
222 po::variables_map vm;
223
224 po::store(opts, vm);
225 po::notify(vm);
226
227 if (vm.count("help")) {
228 cout << desc << endl;
229 return 1;
230 }
231
232 if (vm.count("version")) {
233 return print_version();
234 }
235
236 // extract the arguments from the parsed command line
237 vector<po::option> arguments;
238
239 std::remove_copy_if(opts.options.begin(), opts.options.end(),
240 inserter(arguments, arguments.end()), cmd_line_util::is_argument());
241
242 // if there is no input file given, then exit
243 if (0 == arguments.size() || 0 == arguments[0].value.size()) {
244 cerr << "token_statistics: No input file given. "
245 << "Use --help to get a hint." << endl;
246 return 5;
247 }
248
249 // iterate over all given input files
250 return do_actual_work(arguments[0].value , vm);
251 }
252 catch (std::exception const& e) {
253 cout << "token_statistics: exception caught: " << e.what() << endl;
254 return 6;
255 }
256 catch (...) {
257 cerr << "token_statistics: unexpected exception caught." << endl;
258 return 7;
259 }
260 }
261
262