]>
Commit | Line | Data |
---|---|---|
7c673cae FG |
1 | /*============================================================================= |
2 | Boost.Wave: A Standard compliant C++ preprocessor library | |
3 | ||
4 | http://www.boost.org/ | |
5 | ||
6 | Copyright (c) 2001-2012 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 | #define BOOST_WAVE_SERIALIZATION 0 // enable serialization | |
12 | #define BOOST_WAVE_BINARY_SERIALIZATION 0 // use binary archives | |
13 | #define BOOST_WAVE_XML_SERIALIZATION 1 // use XML archives | |
14 | ||
15 | #include "cpp.hpp" // global configuration | |
16 | ||
17 | /////////////////////////////////////////////////////////////////////////////// | |
18 | // Include additional Boost libraries | |
19 | #include <boost/filesystem/path.hpp> | |
20 | #include <boost/filesystem/convenience.hpp> | |
21 | #include <boost/timer.hpp> | |
22 | #include <boost/any.hpp> | |
b32b8144 FG |
23 | #include <boost/algorithm/cxx11/any_of.hpp> |
24 | #include <boost/algorithm/string/join.hpp> | |
25 | #include <boost/range/algorithm/find.hpp> | |
26 | #include <boost/range/end.hpp> | |
27 | #include <boost/foreach.hpp> | |
7c673cae FG |
28 | |
29 | /////////////////////////////////////////////////////////////////////////////// | |
30 | // Include Wave itself | |
31 | #include <boost/wave.hpp> | |
32 | ||
33 | /////////////////////////////////////////////////////////////////////////////// | |
34 | // Include the lexer related stuff | |
35 | #include <boost/wave/cpplexer/cpp_lex_token.hpp> // token type | |
36 | #include <boost/wave/cpplexer/cpp_lex_iterator.hpp> // lexer type | |
37 | ||
38 | /////////////////////////////////////////////////////////////////////////////// | |
39 | // Include serialization support, if requested | |
40 | #if BOOST_WAVE_SERIALIZATION != 0 | |
41 | #include <boost/serialization/serialization.hpp> | |
42 | #if BOOST_WAVE_BINARY_SERIALIZATION != 0 | |
43 | #include <boost/archive/binary_iarchive.hpp> | |
44 | #include <boost/archive/binary_oarchive.hpp> | |
45 | typedef boost::archive::binary_iarchive iarchive; | |
46 | typedef boost::archive::binary_oarchive oarchive; | |
47 | #elif BOOST_WAVE_XML_SERIALIZATION != 0 | |
48 | #include <boost/archive/xml_iarchive.hpp> | |
49 | #include <boost/archive/xml_oarchive.hpp> | |
50 | typedef boost::archive::xml_iarchive iarchive; | |
51 | typedef boost::archive::xml_oarchive oarchive; | |
52 | #else | |
53 | #include <boost/archive/text_iarchive.hpp> | |
54 | #include <boost/archive/text_oarchive.hpp> | |
55 | typedef boost::archive::text_iarchive iarchive; | |
56 | typedef boost::archive::text_oarchive oarchive; | |
57 | #endif | |
58 | #endif | |
59 | ||
60 | /////////////////////////////////////////////////////////////////////////////// | |
61 | // Include the context policies to use | |
62 | #include "trace_macro_expansion.hpp" | |
63 | ||
64 | /////////////////////////////////////////////////////////////////////////////// | |
65 | // Include lexer specifics, import lexer names | |
66 | #if BOOST_WAVE_SEPARATE_LEXER_INSTANTIATION == 0 | |
67 | #include <boost/wave/cpplexer/re2clex/cpp_re2c_lexer.hpp> | |
68 | #endif | |
69 | ||
70 | /////////////////////////////////////////////////////////////////////////////// | |
71 | // Include the grammar definitions, if these shouldn't be compiled separately | |
72 | // (ATTENTION: _very_ large compilation times!) | |
73 | #if BOOST_WAVE_SEPARATE_GRAMMAR_INSTANTIATION == 0 | |
74 | #include <boost/wave/grammars/cpp_intlit_grammar.hpp> | |
75 | #include <boost/wave/grammars/cpp_chlit_grammar.hpp> | |
76 | #include <boost/wave/grammars/cpp_grammar.hpp> | |
77 | #include <boost/wave/grammars/cpp_expression_grammar.hpp> | |
78 | #include <boost/wave/grammars/cpp_predef_macros_grammar.hpp> | |
79 | #include <boost/wave/grammars/cpp_defined_grammar.hpp> | |
80 | #endif | |
81 | ||
82 | /////////////////////////////////////////////////////////////////////////////// | |
83 | // Import required names | |
84 | using namespace boost::spirit::classic; | |
85 | ||
86 | using std::pair; | |
87 | using std::vector; | |
88 | using std::getline; | |
89 | using std::ofstream; | |
90 | using std::cout; | |
91 | using std::cerr; | |
92 | using std::endl; | |
93 | using std::ostream; | |
94 | using std::istreambuf_iterator; | |
95 | ||
96 | /////////////////////////////////////////////////////////////////////////////// | |
97 | // | |
98 | // This application uses the lex_iterator and lex_token types predefined | |
99 | // with the Wave library, but it is possible to use your own types. | |
100 | // | |
101 | // You may want to have a look at the other samples to see how this is | |
102 | // possible to achieve. | |
103 | typedef boost::wave::cpplexer::lex_token<> token_type; | |
104 | typedef boost::wave::cpplexer::lex_iterator<token_type> | |
105 | lex_iterator_type; | |
106 | ||
107 | // The C++ preprocessor iterators shouldn't be constructed directly. They | |
108 | // are to be generated through a boost::wave::context<> object. This | |
109 | // boost::wave::context object is additionally to be used to initialize and | |
110 | // define different parameters of the actual preprocessing. | |
111 | typedef boost::wave::context< | |
112 | std::string::iterator, lex_iterator_type, | |
113 | boost::wave::iteration_context_policies::load_file_to_string, | |
114 | trace_macro_expansion<token_type> > | |
115 | context_type; | |
116 | ||
117 | /////////////////////////////////////////////////////////////////////////////// | |
118 | // print the current version | |
119 | std::string get_version() | |
120 | { | |
121 | std::string version (context_type::get_version_string()); | |
122 | version = version.substr(1, version.size()-2); // strip quotes | |
123 | version += std::string(" (" CPP_VERSION_DATE_STR ")"); // add date | |
124 | return version; | |
125 | } | |
126 | ||
127 | /////////////////////////////////////////////////////////////////////////////// | |
128 | // print the current version for interactive sessions | |
129 | int print_interactive_version() | |
130 | { | |
131 | cout << "Wave: A Standard conformant C++ preprocessor based on the Boost.Wave library" << endl; | |
132 | cout << "Version: " << get_version() << endl; | |
133 | return 0; | |
134 | } | |
135 | ||
136 | /////////////////////////////////////////////////////////////////////////////// | |
137 | // print the copyright statement | |
138 | int print_copyright() | |
139 | { | |
140 | char const *copyright[] = { | |
141 | "", | |
142 | "Wave: A Standard conformant C++ preprocessor based on the Boost.Wave library", | |
143 | "http://www.boost.org/", | |
144 | "", | |
145 | "Copyright (c) 2001-2012 Hartmut Kaiser, Distributed under the Boost", | |
146 | "Software License, Version 1.0. (See accompanying file", | |
147 | "LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)", | |
148 | 0 | |
149 | }; | |
150 | ||
151 | for (int i = 0; 0 != copyright[i]; ++i) | |
152 | cout << copyright[i] << endl; | |
153 | ||
154 | return 0; // exit app | |
155 | } | |
156 | ||
157 | /////////////////////////////////////////////////////////////////////////////// | |
158 | // forward declarations only | |
159 | namespace cmd_line_utils | |
160 | { | |
161 | class include_paths; | |
162 | } | |
163 | ||
164 | namespace boost { namespace program_options { | |
165 | ||
166 | void validate(boost::any &v, std::vector<std::string> const &s, | |
167 | cmd_line_utils::include_paths *, long); | |
168 | ||
169 | }} // boost::program_options | |
170 | ||
171 | /////////////////////////////////////////////////////////////////////////////// | |
172 | #include <boost/program_options.hpp> | |
173 | ||
174 | namespace po = boost::program_options; | |
175 | namespace fs = boost::filesystem; | |
176 | ||
177 | /////////////////////////////////////////////////////////////////////////////// | |
178 | namespace cmd_line_utils { | |
7c673cae FG |
179 | // Additional command line parser which interprets '@something' as an |
180 | // option "config-file" with the value "something". | |
181 | inline pair<std::string, std::string> | |
182 | at_option_parser(std::string const&s) | |
183 | { | |
184 | if ('@' == s[0]) | |
185 | return std::make_pair(std::string("config-file"), s.substr(1)); | |
186 | else | |
187 | return pair<std::string, std::string>(); | |
188 | } | |
189 | ||
190 | // class, which keeps include file information read from the command line | |
191 | class include_paths { | |
192 | public: | |
193 | include_paths() : seen_separator(false) {} | |
194 | ||
195 | vector<std::string> paths; // stores user paths | |
196 | vector<std::string> syspaths; // stores system paths | |
197 | bool seen_separator; // command line contains a '-I-' option | |
198 | ||
199 | // Function which validates additional tokens from command line. | |
200 | static void | |
201 | validate(boost::any &v, vector<std::string> const &tokens) | |
202 | { | |
203 | if (v.empty()) | |
204 | v = boost::any(include_paths()); | |
205 | ||
206 | include_paths *p = boost::any_cast<include_paths>(&v); | |
207 | ||
208 | BOOST_ASSERT(p); | |
209 | // Assume only one path per '-I' occurrence. | |
210 | std::string const& t = po::validators::get_single_string(tokens); | |
211 | if (t == "-") { | |
212 | // found -I- option, so switch behaviour | |
213 | p->seen_separator = true; | |
214 | } | |
215 | else if (p->seen_separator) { | |
216 | // store this path as a system path | |
217 | p->syspaths.push_back(t); | |
218 | } | |
219 | else { | |
220 | // store this path as an user path | |
221 | p->paths.push_back(t); | |
222 | } | |
223 | } | |
224 | }; | |
225 | ||
226 | // Read all options from a given config file, parse and add them to the | |
227 | // given variables_map | |
228 | bool read_config_file_options(std::string const &filename, | |
229 | po::options_description const &desc, po::variables_map &vm, | |
230 | bool may_fail = false) | |
231 | { | |
232 | std::ifstream ifs(filename.c_str()); | |
233 | ||
234 | if (!ifs.is_open()) { | |
235 | if (!may_fail) { | |
236 | cerr << filename | |
237 | << ": command line warning: config file not found" | |
238 | << endl; | |
239 | } | |
240 | return false; | |
241 | } | |
242 | ||
243 | vector<std::string> options; | |
244 | std::string line; | |
245 | ||
246 | while (std::getline(ifs, line)) { | |
247 | // skip empty lines | |
248 | std::string::size_type pos = line.find_first_not_of(" \t"); | |
249 | if (pos == std::string::npos) | |
250 | continue; | |
251 | ||
252 | // skip comment lines | |
253 | if ('#' != line[pos]) { | |
254 | // strip leading and trailing whitespace | |
255 | std::string::size_type endpos = line.find_last_not_of(" \t"); | |
256 | BOOST_ASSERT(endpos != std::string::npos); | |
257 | options.push_back(line.substr(pos, endpos-pos+1)); | |
258 | } | |
259 | } | |
260 | ||
261 | if (options.size() > 0) { | |
262 | using namespace boost::program_options::command_line_style; | |
263 | po::store(po::command_line_parser(options) | |
264 | .options(desc).style(unix_style).run(), vm); | |
265 | po::notify(vm); | |
266 | } | |
267 | return true; | |
268 | } | |
269 | ||
270 | // predicate to extract all positional arguments from the command line | |
271 | struct is_argument { | |
272 | bool operator()(po::option const &opt) | |
273 | { | |
274 | return (opt.position_key == -1) ? true : false; | |
275 | } | |
276 | }; | |
277 | ||
278 | // trim quotes from path names, if any | |
279 | std::string trim_quotes(std::string const& file) | |
280 | { | |
281 | if (('"' == file[0] || '\'' == file[0]) && file[0] == file[file.size()-1]) | |
282 | { | |
283 | return file.substr(1, file.size()-2); | |
284 | } | |
285 | return file; | |
286 | } | |
287 | ||
288 | /////////////////////////////////////////////////////////////////////////////// | |
289 | } | |
290 | ||
291 | /////////////////////////////////////////////////////////////////////////////// | |
292 | // | |
293 | // Special validator overload, which allows to handle the -I- syntax for | |
294 | // switching the semantics of an -I option. | |
295 | // | |
296 | /////////////////////////////////////////////////////////////////////////////// | |
297 | namespace boost { namespace program_options { | |
298 | ||
299 | void validate(boost::any &v, std::vector<std::string> const &s, | |
300 | cmd_line_utils::include_paths *, long) | |
301 | { | |
302 | cmd_line_utils::include_paths::validate(v, s); | |
303 | } | |
304 | ||
305 | }} // namespace boost::program_options | |
306 | ||
307 | /////////////////////////////////////////////////////////////////////////////// | |
308 | namespace { | |
309 | ||
310 | class auto_stop_watch : public stop_watch | |
311 | { | |
312 | public: | |
313 | auto_stop_watch(std::ostream &outstrm_) | |
314 | : print_time(false), outstrm(outstrm_) | |
315 | { | |
316 | } | |
317 | ||
318 | ~auto_stop_watch() | |
319 | { | |
320 | if (print_time) { | |
321 | outstrm << "Elapsed time: " | |
322 | << this->format_elapsed_time() | |
323 | << std::endl; | |
324 | } | |
325 | } | |
326 | ||
327 | void set_print_time(bool print_time_) | |
328 | { | |
329 | print_time = print_time_; | |
330 | } | |
331 | ||
332 | private: | |
333 | bool print_time; | |
334 | std::ostream &outstrm; | |
335 | }; | |
336 | ||
337 | /////////////////////////////////////////////////////////////////////////// | |
338 | inline std::string | |
339 | report_iostate_error(std::ios::iostate state) | |
340 | { | |
341 | BOOST_ASSERT(state & (std::ios::badbit | std::ios::failbit | std::ios::eofbit)); | |
342 | std::string result; | |
343 | if (state & std::ios::badbit) { | |
344 | result += " the reported problem was: " | |
345 | "loss of integrity of the stream buffer\n"; | |
346 | } | |
347 | if (state & std::ios::failbit) { | |
348 | result += " the reported problem was: " | |
349 | "an operation was not processed correctly\n"; | |
350 | } | |
351 | if (state & std::ios::eofbit) { | |
352 | result += " the reported problem was: " | |
353 | "end-of-file while writing to the stream\n"; | |
354 | } | |
355 | return result; | |
356 | } | |
357 | ||
358 | /////////////////////////////////////////////////////////////////////////// | |
359 | // Retrieve the position of a macro definition | |
360 | template <typename Context> | |
361 | inline bool | |
362 | get_macro_position(Context &ctx, | |
363 | typename Context::token_type::string_type const& name, | |
364 | typename Context::position_type &pos) | |
365 | { | |
366 | bool has_parameters = false; | |
367 | bool is_predefined = false; | |
368 | std::vector<typename Context::token_type> parameters; | |
369 | typename Context::token_sequence_type definition; | |
370 | ||
371 | return ctx.get_macro_definition(name, has_parameters, is_predefined, | |
372 | pos, parameters, definition); | |
373 | } | |
374 | ||
375 | /////////////////////////////////////////////////////////////////////////// | |
376 | // Generate some meaningful error messages | |
377 | template <typename Exception> | |
378 | inline int | |
b32b8144 | 379 | report_error_message(Exception const &e, bool treat_warnings_as_error) |
7c673cae FG |
380 | { |
381 | // default error reporting | |
382 | cerr | |
383 | << e.file_name() << ":" << e.line_no() << ":" << e.column_no() | |
384 | << ": " << e.description() << endl; | |
385 | ||
386 | // errors count as one | |
b32b8144 FG |
387 | return (treat_warnings_as_error || |
388 | e.get_severity() == boost::wave::util::severity_error || | |
7c673cae FG |
389 | e.get_severity() == boost::wave::util::severity_fatal) ? 1 : 0; |
390 | } | |
391 | ||
392 | template <typename Context> | |
393 | inline int | |
b32b8144 FG |
394 | report_error_message(Context &ctx, boost::wave::cpp_exception const &e, |
395 | bool treat_warnings_as_error) | |
7c673cae FG |
396 | { |
397 | // default error reporting | |
b32b8144 | 398 | int result = report_error_message(e, treat_warnings_as_error); |
7c673cae FG |
399 | |
400 | using boost::wave::preprocess_exception; | |
401 | switch(e.get_errorcode()) { | |
402 | case preprocess_exception::macro_redefinition: | |
403 | { | |
404 | // report the point of the initial macro definition | |
405 | typename Context::position_type pos; | |
406 | if (get_macro_position(ctx, e.get_related_name(), pos)) { | |
407 | cerr | |
408 | << pos << ": " | |
409 | << preprocess_exception::severity_text(e.get_severity()) | |
410 | << ": this is the location of the previous definition." | |
411 | << endl; | |
412 | } | |
413 | else { | |
414 | cerr | |
415 | << e.file_name() << ":" << e.line_no() << ":" | |
416 | << e.column_no() << ": " | |
417 | << preprocess_exception::severity_text(e.get_severity()) | |
418 | << ": not able to retrieve the location of the previous " | |
419 | << "definition." << endl; | |
420 | } | |
421 | } | |
422 | break; | |
423 | ||
424 | default: | |
425 | break; | |
426 | } | |
427 | ||
428 | return result; | |
429 | } | |
430 | ||
431 | /////////////////////////////////////////////////////////////////////////// | |
432 | // Read one logical line of text | |
433 | inline bool | |
434 | read_a_line (std::istream &instream, std::string &instring) | |
435 | { | |
436 | bool eol = true; | |
437 | do { | |
438 | std::string line; | |
439 | std::getline(instream, line); | |
440 | if (instream.rdstate() & std::ios::failbit) | |
441 | return false; // nothing to do | |
442 | ||
443 | eol = true; | |
444 | if (line.find_last_of('\\') == line.size()-1) | |
445 | eol = false; | |
446 | ||
447 | instring += line + '\n'; | |
448 | } while (!eol); | |
449 | return true; | |
450 | } | |
451 | ||
452 | /////////////////////////////////////////////////////////////////////////// | |
453 | // Load and save the internal tables of the wave::context object | |
454 | template <typename Context> | |
455 | inline void | |
456 | load_state(po::variables_map const &vm, Context &ctx) | |
457 | { | |
458 | #if BOOST_WAVE_SERIALIZATION != 0 | |
459 | try { | |
460 | if (vm.count("state") > 0) { | |
461 | fs::path state_file ( | |
462 | boost::wave::util::create_path(vm["state"].as<std::string>())); | |
463 | if (state_file == "-") | |
464 | state_file = boost::wave::util::create_path("wave.state"); | |
465 | ||
466 | std::ios::openmode mode = std::ios::in; | |
467 | ||
468 | #if BOOST_WAVE_BINARY_SERIALIZATION != 0 | |
469 | mode = (std::ios::openmode)(mode | std::ios::binary); | |
470 | #endif | |
471 | std::ifstream ifs (state_file.string().c_str(), mode); | |
472 | if (ifs.is_open()) { | |
473 | using namespace boost::serialization; | |
474 | iarchive ia(ifs); | |
475 | std::string version; | |
476 | ||
477 | ia >> make_nvp("version", version); // load version | |
478 | if (version == CPP_VERSION_FULL_STR) | |
479 | ia >> make_nvp("state", ctx); // load the internal tables from disc | |
480 | else { | |
481 | cerr << "wave: detected version mismatch while loading state, state was not loaded." << endl; | |
482 | cerr << " loaded version: " << version << endl; | |
483 | cerr << " expected version: " << CPP_VERSION_FULL_STR << endl; | |
484 | } | |
485 | } | |
486 | } | |
487 | } | |
488 | catch (boost::archive::archive_exception const& e) { | |
489 | cerr << "wave: error while loading state: " | |
490 | << e.what() << endl; | |
491 | } | |
492 | catch (boost::wave::preprocess_exception const& e) { | |
493 | cerr << "wave: error while loading state: " | |
494 | << e.description() << endl; | |
495 | } | |
496 | #endif | |
497 | } | |
498 | ||
499 | template <typename Context> | |
500 | inline void | |
501 | save_state(po::variables_map const &vm, Context const &ctx) | |
502 | { | |
503 | #if BOOST_WAVE_SERIALIZATION != 0 | |
504 | try { | |
505 | if (vm.count("state") > 0) { | |
506 | fs::path state_file (boost::wave::util::create_path( | |
507 | vm["state"].as<std::string>())); | |
508 | if (state_file == "-") | |
509 | state_file = boost::wave::util::create_path("wave.state"); | |
510 | ||
511 | std::ios::openmode mode = std::ios::out; | |
512 | ||
513 | #if BOOST_WAVE_BINARY_SERIALIZATION != 0 | |
514 | mode = (std::ios::openmode)(mode | std::ios::binary); | |
515 | #endif | |
516 | ofstream ofs(state_file.string().c_str(), mode); | |
517 | if (!ofs.is_open()) { | |
518 | cerr << "wave: could not open state file for writing: " | |
519 | << state_file.string() << endl; | |
520 | // this is non-fatal | |
521 | } | |
522 | else { | |
523 | using namespace boost::serialization; | |
524 | oarchive oa(ofs); | |
525 | std::string version(CPP_VERSION_FULL_STR); | |
526 | oa << make_nvp("version", version); // write version | |
527 | oa << make_nvp("state", ctx); // write the internal tables to disc | |
528 | } | |
529 | } | |
530 | } | |
531 | catch (boost::archive::archive_exception const& e) { | |
532 | cerr << "wave: error while writing state: " | |
533 | << e.what() << endl; | |
534 | } | |
535 | #endif | |
536 | } | |
537 | ||
538 | /////////////////////////////////////////////////////////////////////////// | |
539 | // list all defined macros | |
540 | bool list_macro_names(context_type const& ctx, std::string filename) | |
541 | { | |
542 | // open file for macro names listing | |
543 | std::ofstream macronames_out; | |
544 | fs::path macronames_file (boost::wave::util::create_path(filename)); | |
545 | ||
546 | if (macronames_file != "-") { | |
547 | macronames_file = boost::wave::util::complete_path(macronames_file); | |
548 | boost::wave::util::create_directories( | |
549 | boost::wave::util::branch_path(macronames_file)); | |
550 | macronames_out.open(macronames_file.string().c_str()); | |
551 | if (!macronames_out.is_open()) { | |
552 | cerr << "wave: could not open file for macro name listing: " | |
553 | << macronames_file.string() << endl; | |
554 | return false; | |
555 | } | |
556 | } | |
557 | else { | |
558 | macronames_out.copyfmt(cout); | |
559 | macronames_out.clear(cout.rdstate()); | |
560 | static_cast<std::basic_ios<char> &>(macronames_out).rdbuf(cout.rdbuf()); | |
561 | } | |
562 | ||
563 | // simply list all defined macros and its definitions | |
564 | typedef context_type::const_name_iterator name_iterator; | |
565 | name_iterator end = ctx.macro_names_end(); | |
566 | for (name_iterator it = ctx.macro_names_begin(); it != end; ++it) | |
567 | { | |
568 | typedef std::vector<context_type::token_type> parameters_type; | |
569 | ||
570 | bool has_pars = false; | |
571 | bool predef = false; | |
572 | context_type::position_type pos; | |
573 | parameters_type pars; | |
574 | context_type::token_sequence_type def; | |
575 | ||
576 | if (ctx.get_macro_definition(*it, has_pars, predef, pos, pars, def)) | |
577 | { | |
578 | macronames_out << (predef ? "-P" : "-D") << *it; | |
579 | if (has_pars) { | |
580 | // list the parameter names for function style macros | |
581 | macronames_out << "("; | |
582 | parameters_type::const_iterator pend = pars.end(); | |
583 | for (parameters_type::const_iterator pit = pars.begin(); | |
584 | pit != pend; /**/) | |
585 | { | |
586 | macronames_out << (*pit).get_value(); | |
587 | if (++pit != pend) | |
588 | macronames_out << ", "; | |
589 | } | |
590 | macronames_out << ")"; | |
591 | } | |
592 | macronames_out << "="; | |
593 | ||
594 | // print the macro definition | |
595 | context_type::token_sequence_type::const_iterator dend = def.end(); | |
596 | for (context_type::token_sequence_type::const_iterator dit = def.begin(); | |
597 | dit != dend; ++dit) | |
598 | { | |
599 | macronames_out << (*dit).get_value(); | |
600 | } | |
601 | ||
602 | macronames_out << std::endl; | |
603 | } | |
604 | } | |
605 | return true; | |
606 | } | |
607 | ||
608 | /////////////////////////////////////////////////////////////////////////// | |
609 | // list macro invocation counts | |
610 | bool list_macro_counts(context_type const& ctx, std::string filename) | |
611 | { | |
612 | // open file for macro invocation count listing | |
613 | std::ofstream macrocounts_out; | |
614 | fs::path macrocounts_file (boost::wave::util::create_path(filename)); | |
615 | ||
616 | if (macrocounts_file != "-") { | |
617 | macrocounts_file = boost::wave::util::complete_path(macrocounts_file); | |
618 | boost::wave::util::create_directories( | |
619 | boost::wave::util::branch_path(macrocounts_file)); | |
620 | macrocounts_out.open(macrocounts_file.string().c_str()); | |
621 | if (!macrocounts_out.is_open()) { | |
622 | cerr << "wave: could not open file for macro invocation count listing: " | |
623 | << macrocounts_file.string() << endl; | |
624 | return false; | |
625 | } | |
626 | } | |
627 | else { | |
628 | macrocounts_out.copyfmt(cout); | |
629 | macrocounts_out.clear(cout.rdstate()); | |
630 | static_cast<std::basic_ios<char> &>(macrocounts_out).rdbuf(cout.rdbuf()); | |
631 | } | |
632 | ||
633 | // list all expanded macro names and their counts in alphabetical order | |
634 | std::map<std::string, std::size_t> const& counts = | |
635 | ctx.get_hooks().get_macro_counts(); | |
636 | ||
637 | typedef std::map<std::string, std::size_t>::const_iterator iterator; | |
638 | iterator end = counts.end(); | |
639 | for (iterator it = counts.begin(); it != end; ++it) | |
640 | macrocounts_out << (*it).first << "," << (*it).second << std::endl; | |
641 | ||
642 | return true; | |
643 | } | |
644 | ||
645 | /////////////////////////////////////////////////////////////////////////// | |
646 | // read all of a file into a string | |
647 | std::string read_entire_file(std::istream& instream) | |
648 | { | |
649 | std::string content; | |
650 | ||
651 | instream.unsetf(std::ios::skipws); | |
652 | ||
653 | #if defined(BOOST_NO_TEMPLATED_ITERATOR_CONSTRUCTORS) | |
654 | // this is known to be very slow for large files on some systems | |
655 | copy (std::istream_iterator<char>(instream), | |
656 | std::istream_iterator<char>(), | |
657 | std::inserter(content, content.end())); | |
658 | #else | |
659 | content = std::string(std::istreambuf_iterator<char>(instream.rdbuf()), | |
660 | std::istreambuf_iterator<char>()); | |
661 | #endif | |
662 | return content; | |
663 | } | |
664 | } // anonymous namespace | |
665 | ||
666 | /////////////////////////////////////////////////////////////////////////////// | |
667 | // do the actual preprocessing | |
668 | int | |
669 | do_actual_work (std::string file_name, std::istream &instream, | |
670 | po::variables_map const &vm, bool input_is_stdin) | |
671 | { | |
672 | // current file position is saved for exception handling | |
673 | boost::wave::util::file_position_type current_position; | |
674 | auto_stop_watch elapsed_time(cerr); | |
675 | int error_count = 0; | |
b32b8144 FG |
676 | const bool treat_warnings_as_error = vm.count("warning") && |
677 | boost::algorithm::any_of_equal( | |
678 | vm["warning"].as<std::vector<std::string> >(), "error"); | |
7c673cae FG |
679 | |
680 | try { | |
681 | // process the given file | |
682 | std::string instring; | |
683 | ||
684 | instream.unsetf(std::ios::skipws); | |
685 | if (!input_is_stdin) | |
686 | instring = read_entire_file(instream); | |
687 | ||
688 | // The preprocessing of the input stream is done on the fly behind the | |
689 | // scenes during iteration over the context_type::iterator_type stream. | |
690 | std::ofstream output; | |
691 | std::ofstream traceout; | |
692 | std::ofstream includelistout; | |
693 | std::ofstream listguardsout; | |
694 | ||
695 | trace_flags enable_trace = trace_nothing; | |
696 | ||
697 | if (vm.count("traceto")) { | |
698 | // try to open the file, where to put the trace output | |
699 | fs::path trace_file (boost::wave::util::create_path( | |
700 | vm["traceto"].as<std::string>())); | |
701 | ||
702 | if (trace_file != "-") { | |
703 | boost::wave::util::create_directories( | |
704 | boost::wave::util::branch_path(trace_file)); | |
705 | traceout.open(trace_file.string().c_str()); | |
706 | if (!traceout.is_open()) { | |
707 | cerr << "wave: could not open trace file: " << trace_file | |
708 | << endl; | |
709 | return -1; | |
710 | } | |
711 | } | |
712 | enable_trace = trace_macros; | |
713 | } | |
714 | if ((enable_trace & trace_macros) && !traceout.is_open()) { | |
715 | // by default trace to std::cerr | |
716 | traceout.copyfmt(cerr); | |
717 | traceout.clear(cerr.rdstate()); | |
718 | static_cast<std::basic_ios<char> &>(traceout).rdbuf(cerr.rdbuf()); | |
719 | } | |
720 | ||
721 | // Open the stream where to output the list of included file names | |
722 | if (vm.count("listincludes")) { | |
723 | // try to open the file, where to put the include list | |
724 | fs::path includes_file(boost::wave::util::create_path( | |
725 | vm["listincludes"].as<std::string>())); | |
726 | ||
727 | if (includes_file != "-") { | |
728 | boost::wave::util::create_directories( | |
729 | boost::wave::util::branch_path(includes_file)); | |
730 | includelistout.open(includes_file.string().c_str()); | |
731 | if (!includelistout.is_open()) { | |
732 | cerr << "wave: could not open include list file: " | |
733 | << includes_file.string() << endl; | |
734 | return -1; | |
735 | } | |
736 | } | |
737 | enable_trace = trace_flags(enable_trace | trace_includes); | |
738 | } | |
739 | if ((enable_trace & trace_includes) && !includelistout.is_open()) { | |
740 | // by default list included names to std::cout | |
741 | includelistout.copyfmt(cout); | |
742 | includelistout.clear(cout.rdstate()); | |
743 | static_cast<std::basic_ios<char> &>(includelistout). | |
744 | rdbuf(cout.rdbuf()); | |
745 | } | |
746 | ||
747 | // Open the stream where to output the list of included file names | |
748 | if (vm.count("listguards")) { | |
749 | // try to open the file, where to put the include list | |
750 | fs::path listguards_file(boost::wave::util::create_path( | |
751 | vm["listguards"].as<std::string>())); | |
752 | ||
753 | if (listguards_file != "-") { | |
754 | boost::wave::util::create_directories( | |
755 | boost::wave::util::branch_path(listguards_file)); | |
756 | listguardsout.open(listguards_file.string().c_str()); | |
757 | if (!listguardsout.is_open()) { | |
758 | cerr << "wave: could not open include guard list file: " | |
759 | << listguards_file.string() << endl; | |
760 | return -1; | |
761 | } | |
762 | } | |
763 | enable_trace = trace_flags(enable_trace | trace_guards); | |
764 | } | |
765 | if ((enable_trace & trace_guards) && !listguardsout.is_open()) { | |
766 | // by default list included names to std::cout | |
767 | listguardsout.copyfmt(cout); | |
768 | listguardsout.clear(cout.rdstate()); | |
769 | static_cast<std::basic_ios<char> &>(listguardsout). | |
770 | rdbuf(cout.rdbuf()); | |
771 | } | |
772 | ||
773 | // enable preserving comments mode | |
774 | bool preserve_comments = false; | |
775 | bool preserve_whitespace = false; | |
776 | bool preserve_bol_whitespace = false; | |
777 | ||
778 | if (vm.count("preserve")) { | |
779 | int preserve = vm["preserve"].as<int>(); | |
780 | ||
781 | switch(preserve) { | |
782 | case 0: break; // preserve no whitespace | |
783 | case 3: // preserve all whitespace | |
784 | preserve_whitespace = true; | |
785 | preserve_comments = true; | |
786 | preserve_bol_whitespace = true; | |
787 | break; | |
788 | ||
789 | case 2: // preserve comments and BOL whitespace only | |
790 | preserve_comments = true; | |
791 | preserve_bol_whitespace = true; | |
792 | break; | |
793 | ||
794 | case 1: // preserve BOL whitespace only | |
795 | preserve_bol_whitespace = true; | |
796 | break; | |
797 | ||
798 | default: | |
799 | cerr << "wave: bogus preserve whitespace option value: " | |
800 | << preserve << ", should be 0, 1, 2, or 3" << endl; | |
801 | return -1; | |
802 | } | |
803 | } | |
804 | ||
805 | // Since the #pragma wave system() directive may cause a potential security | |
806 | // threat, it has to be enabled explicitly by --extended or -x | |
807 | bool enable_system_command = false; | |
808 | ||
809 | if (vm.count("extended")) | |
810 | enable_system_command = true; | |
811 | ||
812 | // This this the central piece of the Wave library, it provides you with | |
813 | // the iterators to get the preprocessed tokens and allows to configure | |
814 | // the preprocessing stage in advance. | |
815 | bool allow_output = true; // will be manipulated from inside the hooks object | |
816 | std::string default_outfile; // will be used from inside the hooks object | |
817 | trace_macro_expansion<token_type> hooks(preserve_whitespace, | |
818 | preserve_bol_whitespace, output, traceout, includelistout, | |
819 | listguardsout, enable_trace, enable_system_command, allow_output, | |
820 | default_outfile); | |
821 | ||
822 | // enable macro invocation count, if appropriate | |
823 | if (vm.count("macrocounts")) | |
824 | hooks.enable_macro_counting(); | |
825 | ||
826 | // check, if we have a license file to prepend | |
827 | std::string license; | |
828 | ||
829 | if (vm.count ("license")) { | |
830 | // try to open the file, where to put the preprocessed output | |
831 | std::string license_file(vm["license"].as<std::string>()); | |
832 | std::ifstream license_stream(license_file.c_str()); | |
833 | ||
834 | if (!license_stream.is_open()) { | |
835 | cerr << "wave: could not open specified license file: " | |
836 | << license_file << endl; | |
837 | return -1; | |
838 | } | |
839 | license = read_entire_file(license_stream); | |
840 | hooks.set_license_info(license); | |
841 | } | |
842 | ||
843 | context_type ctx (instring.begin(), instring.end(), file_name.c_str(), hooks); | |
844 | ||
845 | #if BOOST_WAVE_SUPPORT_VARIADICS_PLACEMARKERS != 0 | |
846 | // enable C99 mode, if appropriate (implies variadics) | |
847 | if (vm.count("c99")) { | |
848 | #if BOOST_WAVE_SUPPORT_CPP0X != 0 | |
849 | if (vm.count("c++11")) { | |
850 | cerr << "wave: multiple language options specified: --c99 " | |
851 | "and --c++11" << endl; | |
852 | return -1; | |
853 | } | |
854 | #endif | |
855 | ctx.set_language( | |
856 | boost::wave::language_support( | |
857 | boost::wave::support_c99 | |
858 | | boost::wave::support_option_convert_trigraphs | |
859 | | boost::wave::support_option_emit_line_directives | |
860 | #if BOOST_WAVE_SUPPORT_PRAGMA_ONCE != 0 | |
861 | | boost::wave::support_option_include_guard_detection | |
862 | #endif | |
863 | #if BOOST_WAVE_EMIT_PRAGMA_DIRECTIVES != 0 | |
864 | | boost::wave::support_option_emit_pragma_directives | |
865 | #endif | |
866 | | boost::wave::support_option_insert_whitespace | |
867 | )); | |
868 | } | |
869 | else if (vm.count("variadics")) { | |
870 | // enable variadics and placemarkers, if appropriate | |
871 | ctx.set_language(boost::wave::enable_variadics(ctx.get_language())); | |
872 | } | |
873 | #endif // BOOST_WAVE_SUPPORT_VARIADICS_PLACEMARKERS != 0 | |
874 | #if BOOST_WAVE_SUPPORT_CPP0X != 0 | |
875 | if (vm.count("c++11")) { | |
876 | if (vm.count("c99")) { | |
877 | cerr << "wave: multiple language options specified: --c99 " | |
878 | "and --c++11" << endl; | |
879 | return -1; | |
880 | } | |
881 | ctx.set_language( | |
882 | boost::wave::language_support( | |
883 | boost::wave::support_cpp0x | |
884 | | boost::wave::support_option_convert_trigraphs | |
885 | | boost::wave::support_option_long_long | |
886 | | boost::wave::support_option_emit_line_directives | |
887 | #if BOOST_WAVE_SUPPORT_PRAGMA_ONCE != 0 | |
888 | | boost::wave::support_option_include_guard_detection | |
889 | #endif | |
890 | #if BOOST_WAVE_EMIT_PRAGMA_DIRECTIVES != 0 | |
891 | | boost::wave::support_option_emit_pragma_directives | |
892 | #endif | |
893 | | boost::wave::support_option_insert_whitespace | |
894 | )); | |
895 | } | |
896 | #endif // BOOST_WAVE_SUPPORT_CPP0X != 0 | |
897 | ||
898 | // enable long long support, if appropriate | |
899 | if (vm.count("long_long")) { | |
900 | ctx.set_language( | |
901 | boost::wave::enable_long_long(ctx.get_language())); | |
902 | } | |
903 | ||
904 | #if BOOST_WAVE_SUPPORT_PRAGMA_ONCE != 0 | |
905 | // disable include guard detection | |
906 | if (vm.count("noguard")) { | |
907 | ctx.set_language( | |
908 | boost::wave::enable_include_guard_detection( | |
909 | ctx.get_language(), false)); | |
910 | } | |
911 | #endif | |
912 | ||
913 | // enable preserving comments mode | |
914 | if (preserve_comments) { | |
915 | ctx.set_language( | |
916 | boost::wave::enable_preserve_comments(ctx.get_language())); | |
917 | } | |
918 | ||
919 | // control the generation of #line directives | |
920 | if (vm.count("line")) { | |
921 | int lineopt = vm["line"].as<int>(); | |
922 | if (0 != lineopt && 1 != lineopt && 2 != lineopt) { | |
923 | cerr << "wave: bogus value for --line command line option: " | |
924 | << lineopt << endl; | |
925 | return -1; | |
926 | } | |
927 | ctx.set_language( | |
928 | boost::wave::enable_emit_line_directives(ctx.get_language(), | |
929 | lineopt != 0)); | |
930 | ||
931 | if (2 == lineopt) | |
932 | ctx.get_hooks().enable_relative_names_in_line_directives(true); | |
933 | } | |
934 | ||
935 | // control whether whitespace should be inserted to disambiguate output | |
936 | if (vm.count("disambiguate")) { | |
937 | int disambiguateopt = vm["disambiguate"].as<int>(); | |
938 | if (0 != disambiguateopt && 1 != disambiguateopt) { | |
939 | cerr << "wave: bogus value for --disambiguate command line option: " | |
940 | << disambiguateopt << endl; | |
941 | return -1; | |
942 | } | |
943 | ctx.set_language( | |
944 | boost::wave::enable_insert_whitespace(ctx.get_language(), | |
945 | disambiguateopt != 0)); | |
946 | } | |
947 | ||
948 | // add include directories to the system include search paths | |
949 | if (vm.count("sysinclude")) { | |
950 | vector<std::string> syspaths = vm["sysinclude"].as<vector<std::string> >(); | |
951 | ||
952 | vector<std::string>::const_iterator end = syspaths.end(); | |
953 | for (vector<std::string>::const_iterator cit = syspaths.begin(); | |
954 | cit != end; ++cit) | |
955 | { | |
956 | ctx.add_sysinclude_path(cmd_line_utils::trim_quotes(*cit).c_str()); | |
957 | } | |
958 | } | |
959 | ||
960 | // add include directories to the include search paths | |
961 | if (vm.count("include")) { | |
962 | cmd_line_utils::include_paths const &ip = | |
963 | vm["include"].as<cmd_line_utils::include_paths>(); | |
964 | vector<std::string>::const_iterator end = ip.paths.end(); | |
965 | ||
966 | for (vector<std::string>::const_iterator cit = ip.paths.begin(); | |
967 | cit != end; ++cit) | |
968 | { | |
969 | ctx.add_include_path(cmd_line_utils::trim_quotes(*cit).c_str()); | |
970 | } | |
971 | ||
972 | // if -I- was given on the command line, this has to be propagated | |
973 | if (ip.seen_separator) | |
974 | ctx.set_sysinclude_delimiter(); | |
975 | ||
976 | // add system include directories to the include path | |
977 | vector<std::string>::const_iterator sysend = ip.syspaths.end(); | |
978 | for (vector<std::string>::const_iterator syscit = ip.syspaths.begin(); | |
979 | syscit != sysend; ++syscit) | |
980 | { | |
981 | ctx.add_sysinclude_path(cmd_line_utils::trim_quotes(*syscit).c_str()); | |
982 | } | |
983 | } | |
984 | ||
985 | // add additional defined macros | |
986 | if (vm.count("define")) { | |
987 | vector<std::string> const ¯os = vm["define"].as<vector<std::string> >(); | |
988 | vector<std::string>::const_iterator end = macros.end(); | |
989 | for (vector<std::string>::const_iterator cit = macros.begin(); | |
990 | cit != end; ++cit) | |
991 | { | |
992 | ctx.add_macro_definition(*cit); | |
993 | } | |
994 | } | |
995 | ||
996 | // add additional predefined macros | |
997 | if (vm.count("predefine")) { | |
998 | vector<std::string> const &predefmacros = | |
999 | vm["predefine"].as<vector<std::string> >(); | |
1000 | vector<std::string>::const_iterator end = predefmacros.end(); | |
1001 | for (vector<std::string>::const_iterator cit = predefmacros.begin(); | |
1002 | cit != end; ++cit) | |
1003 | { | |
1004 | ctx.add_macro_definition(*cit, true); | |
1005 | } | |
1006 | } | |
1007 | ||
1008 | // undefine specified macros | |
1009 | if (vm.count("undefine")) { | |
1010 | vector<std::string> const &undefmacros = | |
1011 | vm["undefine"].as<vector<std::string> >(); | |
1012 | vector<std::string>::const_iterator end = undefmacros.end(); | |
1013 | for (vector<std::string>::const_iterator cit = undefmacros.begin(); | |
1014 | cit != end; ++cit) | |
1015 | { | |
1016 | ctx.remove_macro_definition(*cit, true); | |
1017 | } | |
1018 | } | |
1019 | ||
1020 | #if BOOST_WAVE_USE_DEPRECIATED_PREPROCESSING_HOOKS == 0 | |
1021 | // suppress expansion of specified macros | |
1022 | if (vm.count("noexpand")) { | |
1023 | vector<std::string> const &noexpandmacros = | |
1024 | vm["noexpand"].as<vector<std::string> >(); | |
1025 | vector<std::string>::const_iterator end = noexpandmacros.end(); | |
1026 | for (vector<std::string>::const_iterator cit = noexpandmacros.begin(); | |
1027 | cit != end; ++cit) | |
1028 | { | |
1029 | ctx.get_hooks().add_noexpandmacro(*cit); | |
1030 | } | |
1031 | } | |
1032 | #endif | |
1033 | ||
1034 | // maximal include nesting depth | |
1035 | if (vm.count("nesting")) { | |
1036 | int max_depth = vm["nesting"].as<int>(); | |
1037 | if (max_depth < 1 || max_depth > 100000) { | |
1038 | cerr << "wave: bogus maximal include nesting depth: " | |
1039 | << max_depth << endl; | |
1040 | return -1; | |
1041 | } | |
1042 | ctx.set_max_include_nesting_depth(max_depth); | |
1043 | } | |
1044 | ||
1045 | // open the output file | |
1046 | if (vm.count("output")) { | |
1047 | // try to open the file, where to put the preprocessed output | |
1048 | fs::path out_file (boost::wave::util::create_path( | |
1049 | vm["output"].as<std::string>())); | |
1050 | ||
1051 | if (out_file == "-") { | |
1052 | allow_output = false; // inhibit output initially | |
1053 | default_outfile = "-"; | |
1054 | } | |
1055 | else { | |
1056 | out_file = boost::wave::util::complete_path(out_file); | |
1057 | boost::wave::util::create_directories( | |
1058 | boost::wave::util::branch_path(out_file)); | |
1059 | output.open(out_file.string().c_str()); | |
1060 | if (!output.is_open()) { | |
1061 | cerr << "wave: could not open output file: " | |
1062 | << out_file.string() << endl; | |
1063 | return -1; | |
1064 | } | |
1065 | if (!license.empty()) | |
1066 | output << license; | |
1067 | default_outfile = out_file.string(); | |
1068 | } | |
1069 | } | |
1070 | else if (!input_is_stdin && vm.count("autooutput")) { | |
1071 | // generate output in the file <input_base_name>.i | |
1072 | fs::path out_file (boost::wave::util::create_path(file_name)); | |
1073 | std::string basename (boost::wave::util::leaf(out_file)); | |
1074 | std::string::size_type pos = basename.find_last_of("."); | |
1075 | ||
1076 | if (std::string::npos != pos) | |
1077 | basename = basename.substr(0, pos); | |
1078 | out_file = boost::wave::util::branch_path(out_file) / (basename + ".i"); | |
1079 | ||
1080 | boost::wave::util::create_directories( | |
1081 | boost::wave::util::branch_path(out_file)); | |
1082 | output.open(out_file.string().c_str()); | |
1083 | if (!output.is_open()) { | |
1084 | cerr << "wave: could not open output file: " | |
1085 | << out_file.string() << endl; | |
1086 | return -1; | |
1087 | } | |
1088 | if (!license.empty()) | |
1089 | output << license; | |
1090 | default_outfile = out_file.string(); | |
1091 | } | |
1092 | ||
1093 | // we assume the session to be interactive if input is stdin and output is | |
1094 | // stdout and the output is not inhibited | |
1095 | bool is_interactive = input_is_stdin && !output.is_open() && allow_output; | |
1096 | ||
1097 | if (is_interactive) { | |
1098 | // if interactive we don't warn for missing endif's etc. | |
1099 | ctx.set_language( | |
1100 | boost::wave::enable_single_line(ctx.get_language()), false); | |
1101 | } | |
1102 | ||
1103 | // analyze the input file | |
1104 | context_type::iterator_type first = ctx.begin(); | |
1105 | context_type::iterator_type last = ctx.end(); | |
1106 | ||
1107 | // preprocess the required include files | |
1108 | if (vm.count("forceinclude")) { | |
1109 | // add the filenames to force as include files in _reverse_ order | |
1110 | // the second parameter 'is_last' of the force_include function should | |
1111 | // be set to true for the last (first given) file. | |
1112 | std::vector<std::string> const &force = | |
1113 | vm["forceinclude"].as<std::vector<std::string> >(); | |
1114 | std::vector<std::string>::const_reverse_iterator rend = force.rend(); | |
1115 | for (std::vector<std::string>::const_reverse_iterator cit = force.rbegin(); | |
1116 | cit != rend; /**/) | |
1117 | { | |
1118 | std::string filename(*cit); | |
1119 | first.force_include(filename.c_str(), ++cit == rend); | |
1120 | } | |
1121 | } | |
1122 | ||
1123 | elapsed_time.set_print_time(!input_is_stdin && vm.count("timer") > 0); | |
1124 | if (is_interactive) { | |
1125 | print_interactive_version(); // print welcome message | |
1126 | load_state(vm, ctx); // load the internal tables from disc | |
1127 | } | |
1128 | else if (vm.count("state")) { | |
1129 | // the option "state" is usable in interactive mode only | |
1130 | cerr << "wave: ignoring the command line option 'state', " | |
1131 | << "use it in interactive mode only." << endl; | |
1132 | } | |
1133 | ||
1134 | // >>>>>>>>>>>>> The actual preprocessing happens here. <<<<<<<<<<<<<<<<<<< | |
1135 | // loop over the input lines if reading from stdin, otherwise this loop | |
1136 | // will be executed once | |
1137 | do { | |
1138 | // loop over all generated tokens outputting the generated text | |
1139 | bool finished = false; | |
1140 | ||
1141 | if (input_is_stdin) { | |
1142 | if (is_interactive) | |
1143 | cout << ">>> "; // prompt if is interactive | |
1144 | ||
1145 | // read next line and continue | |
1146 | instring.clear(); | |
1147 | if (!read_a_line(instream, instring)) | |
1148 | break; // end of input reached | |
1149 | first = ctx.begin(instring.begin(), instring.end()); | |
1150 | } | |
1151 | ||
1152 | bool need_to_advanve = false; | |
1153 | ||
1154 | do { | |
1155 | try { | |
1156 | if (need_to_advanve) { | |
1157 | ++first; | |
1158 | need_to_advanve = false; | |
1159 | } | |
1160 | ||
1161 | while (first != last) { | |
1162 | // store the last known good token position | |
1163 | current_position = (*first).get_position(); | |
1164 | ||
1165 | // print out the current token value | |
1166 | if (allow_output) { | |
1167 | if (!output.good()) { | |
1168 | cerr << "wave: problem writing to the current " | |
1169 | << "output file" << endl; | |
1170 | cerr << report_iostate_error(output.rdstate()); | |
1171 | break; | |
1172 | } | |
1173 | if (output.is_open()) | |
1174 | output << (*first).get_value(); | |
1175 | else | |
1176 | cout << (*first).get_value(); | |
1177 | } | |
1178 | ||
1179 | // advance to the next token | |
1180 | ++first; | |
1181 | } | |
1182 | finished = true; | |
1183 | } | |
1184 | catch (boost::wave::cpp_exception const &e) { | |
1185 | // some preprocessing error | |
1186 | if (is_interactive || boost::wave::is_recoverable(e)) { | |
b32b8144 FG |
1187 | error_count += report_error_message(ctx, e, |
1188 | treat_warnings_as_error); | |
7c673cae FG |
1189 | need_to_advanve = true; // advance to the next token |
1190 | } | |
1191 | else { | |
1192 | throw; // re-throw for non-recoverable errors | |
1193 | } | |
1194 | } | |
1195 | catch (boost::wave::cpplexer::lexing_exception const &e) { | |
1196 | // some preprocessing error | |
1197 | if (is_interactive || | |
1198 | boost::wave::cpplexer::is_recoverable(e)) | |
1199 | { | |
b32b8144 FG |
1200 | error_count += |
1201 | report_error_message(e, treat_warnings_as_error); | |
7c673cae FG |
1202 | need_to_advanve = true; // advance to the next token |
1203 | } | |
1204 | else { | |
1205 | throw; // re-throw for non-recoverable errors | |
1206 | } | |
1207 | } | |
1208 | } while (!finished); | |
1209 | } while (input_is_stdin); | |
1210 | ||
1211 | if (is_interactive) | |
1212 | save_state(vm, ctx); // write the internal tables to disc | |
1213 | ||
1214 | // list all defined macros at the end of the preprocessing | |
1215 | if (vm.count("macronames")) { | |
1216 | if (!list_macro_names(ctx, vm["macronames"].as<std::string>())) | |
1217 | return -1; | |
1218 | } | |
1219 | if (vm.count("macrocounts")) { | |
1220 | if (!list_macro_counts(ctx, vm["macrocounts"].as<std::string>())) | |
1221 | return -1; | |
1222 | } | |
1223 | } | |
1224 | catch (boost::wave::cpp_exception const &e) { | |
1225 | // some preprocessing error | |
b32b8144 | 1226 | report_error_message(e, treat_warnings_as_error); |
7c673cae FG |
1227 | return 1; |
1228 | } | |
1229 | catch (boost::wave::cpplexer::lexing_exception const &e) { | |
1230 | // some lexing error | |
b32b8144 | 1231 | report_error_message(e, treat_warnings_as_error); |
7c673cae FG |
1232 | return 2; |
1233 | } | |
1234 | catch (std::exception const &e) { | |
1235 | // use last recognized token to retrieve the error position | |
1236 | cerr | |
1237 | << current_position << ": " | |
1238 | << "exception caught: " << e.what() | |
1239 | << endl; | |
1240 | return 3; | |
1241 | } | |
1242 | catch (...) { | |
1243 | // use last recognized token to retrieve the error position | |
1244 | cerr | |
1245 | << current_position << ": " | |
1246 | << "unexpected exception caught." << endl; | |
1247 | return 4; | |
1248 | } | |
1249 | return -error_count; // returns the number of errors as a negative integer | |
1250 | } | |
1251 | ||
1252 | /////////////////////////////////////////////////////////////////////////////// | |
1253 | // main entry point | |
1254 | int | |
1255 | main (int argc, char *argv[]) | |
1256 | { | |
b32b8144 FG |
1257 | const std::string accepted_w_args[] = {"error"}; |
1258 | ||
7c673cae FG |
1259 | // test Wave compilation configuration |
1260 | if (!BOOST_WAVE_TEST_CONFIGURATION()) { | |
1261 | cout << "wave: warning: the library this application was linked against was compiled " | |
1262 | << endl | |
1263 | << " using a different configuration (see wave_config.hpp)." | |
1264 | << endl; | |
1265 | } | |
1266 | ||
1267 | // analyze the command line options and arguments | |
1268 | try { | |
1269 | // declare the options allowed on the command line only | |
1270 | po::options_description desc_cmdline ("Options allowed on the command line only"); | |
1271 | ||
1272 | desc_cmdline.add_options() | |
1273 | ("help,h", "print out program usage (this message)") | |
1274 | ("version,v", "print the version number") | |
1275 | ("copyright", "print out the copyright statement") | |
1276 | ("config-file", po::value<vector<std::string> >()->composing(), | |
1277 | "specify a config file (alternatively: @filepath)") | |
1278 | ; | |
1279 | ||
b32b8144 FG |
1280 | const std::string w_arg_desc = "Warning settings. Currently supported: -W" + |
1281 | boost::algorithm::join(accepted_w_args, ", -W"); | |
1282 | ||
7c673cae FG |
1283 | // declare the options allowed on command line and in config files |
1284 | po::options_description desc_generic ("Options allowed additionally in a config file"); | |
1285 | ||
1286 | desc_generic.add_options() | |
1287 | ("output,o", po::value<std::string>(), | |
1288 | "specify a file [arg] to use for output instead of stdout or " | |
1289 | "disable output [-]") | |
1290 | ("autooutput,E", | |
1291 | "output goes into a file named <input_basename>.i") | |
1292 | ("license", po::value<std::string>(), | |
1293 | "prepend the content of the specified file to each created file") | |
1294 | ("include,I", po::value<cmd_line_utils::include_paths>()->composing(), | |
1295 | "specify an additional include directory") | |
1296 | ("sysinclude,S", po::value<vector<std::string> >()->composing(), | |
1297 | "specify an additional system include directory") | |
1298 | ("forceinclude,F", po::value<std::vector<std::string> >()->composing(), | |
1299 | "force inclusion of the given file") | |
1300 | ("define,D", po::value<std::vector<std::string> >()->composing(), | |
1301 | "specify a macro to define (as macro[=[value]])") | |
1302 | ("predefine,P", po::value<std::vector<std::string> >()->composing(), | |
1303 | "specify a macro to predefine (as macro[=[value]])") | |
1304 | ("undefine,U", po::value<std::vector<std::string> >()->composing(), | |
1305 | "specify a macro to undefine") | |
1306 | #if BOOST_WAVE_USE_DEPRECIATED_PREPROCESSING_HOOKS == 0 | |
1307 | ("noexpand,N", po::value<std::vector<std::string> >()->composing(), | |
1308 | "specify a macro name, which should not be expanded") | |
1309 | #endif | |
1310 | ("nesting,n", po::value<int>(), | |
1311 | "specify a new maximal include nesting depth") | |
b32b8144 FG |
1312 | ("warning,W", po::value<std::vector<std::string> >()->composing(), |
1313 | w_arg_desc.c_str()) | |
7c673cae FG |
1314 | ; |
1315 | ||
1316 | po::options_description desc_ext ("Extended options (allowed everywhere)"); | |
1317 | ||
1318 | desc_ext.add_options() | |
1319 | ("traceto,t", po::value<std::string>(), | |
1320 | "output macro expansion tracing information to a file [arg] " | |
1321 | "or to stderr [-]") | |
1322 | ("timer", "output overall elapsed computing time to stderr") | |
1323 | ("long_long", "enable long long support in C++ mode") | |
1324 | #if BOOST_WAVE_SUPPORT_VARIADICS_PLACEMARKERS != 0 | |
1325 | ("variadics", "enable certain C99 extensions in C++ mode") | |
1326 | ("c99", "enable C99 mode (implies --variadics)") | |
1327 | #endif | |
1328 | #if BOOST_WAVE_SUPPORT_CPP0X != 0 | |
1329 | ("c++11", "enable C++11 mode (implies --variadics and --long_long)") | |
1330 | #endif | |
1331 | ("listincludes,l", po::value<std::string>(), | |
1332 | "list names of included files to a file [arg] or to stdout [-]") | |
1333 | ("macronames,m", po::value<std::string>(), | |
1334 | "list all defined macros to a file [arg] or to stdout [-]") | |
1335 | ("macrocounts,c", po::value<std::string>(), | |
1336 | "list macro invocation counts to a file [arg] or to stdout [-]") | |
1337 | ("preserve,p", po::value<int>()->default_value(0), | |
1338 | "preserve whitespace\n" | |
1339 | "0: no whitespace is preserved (default),\n" | |
1340 | "1: begin of line whitespace is preserved,\n" | |
1341 | "2: comments and begin of line whitespace is preserved,\n" | |
1342 | "3: all whitespace is preserved") | |
1343 | ("line,L", po::value<int>()->default_value(1), | |
1344 | "control the generation of #line directives\n" | |
1345 | "0: no #line directives are generated,\n" | |
1346 | "1: #line directives will be emitted (default),\n" | |
1347 | "2: #line directives will be emitted using relative\n" | |
1348 | " filenames") | |
1349 | ("disambiguate", po::value<int>()->default_value(1), | |
1350 | "control whitespace insertion to disambiguate\n" | |
1351 | "consecutive tokens\n" | |
1352 | "0: no additional whitespace is generated,\n" | |
1353 | "1: whitespace is used to disambiguate output (default)") | |
1354 | ("extended,x", "enable the #pragma wave system() directive") | |
1355 | #if BOOST_WAVE_SUPPORT_PRAGMA_ONCE != 0 | |
1356 | ("noguard,G", "disable include guard detection") | |
1357 | ("listguards,g", po::value<std::string>(), | |
1358 | "list names of files flagged as 'include once' to a file [arg] " | |
1359 | "or to stdout [-]") | |
1360 | #endif | |
1361 | #if BOOST_WAVE_SERIALIZATION != 0 | |
1362 | ("state,s", po::value<std::string>(), | |
1363 | "load and save state information from/to the given file [arg] " | |
1364 | "or 'wave.state' [-] (interactive mode only)") | |
1365 | #endif | |
1366 | ; | |
1367 | ||
1368 | // combine the options for the different usage schemes | |
1369 | po::options_description desc_overall_cmdline; | |
1370 | po::options_description desc_overall_cfgfile; | |
1371 | ||
1372 | desc_overall_cmdline.add(desc_cmdline).add(desc_generic).add(desc_ext); | |
1373 | desc_overall_cfgfile.add(desc_generic).add(desc_ext); | |
1374 | ||
1375 | // parse command line and store results | |
1376 | using namespace boost::program_options::command_line_style; | |
1377 | ||
1378 | po::parsed_options opts(po::parse_command_line(argc, argv, | |
1379 | desc_overall_cmdline, unix_style, cmd_line_utils::at_option_parser)); | |
1380 | po::variables_map vm; | |
1381 | ||
1382 | po::store(opts, vm); | |
1383 | po::notify(vm); | |
1384 | ||
1385 | // // Try to find a wave.cfg in the same directory as the executable was | |
1386 | // // started from. If this exists, treat it as a wave config file | |
1387 | // fs::path filename(argv[0]); | |
1388 | // | |
1389 | // filename = filename.branch_path() / "wave.cfg"; | |
1390 | // cmd_line_utils::read_config_file_options(filename.string(), | |
1391 | // desc_overall_cfgfile, vm, true); | |
1392 | ||
1393 | // extract the arguments from the parsed command line | |
1394 | vector<po::option> arguments; | |
1395 | ||
1396 | std::remove_copy_if(opts.options.begin(), opts.options.end(), | |
1397 | back_inserter(arguments), cmd_line_utils::is_argument()); | |
1398 | ||
1399 | // try to find a config file somewhere up the filesystem hierarchy | |
1400 | // starting with the input file path. This allows to use a general wave.cfg | |
1401 | // file for all files in a certain project. | |
1402 | if (arguments.size() > 0 && arguments[0].value[0] != "-") { | |
1403 | // construct full path of input file | |
1404 | fs::path input_dir (boost::wave::util::complete_path( | |
1405 | boost::wave::util::create_path(arguments[0].value[0]))); | |
1406 | ||
1407 | // chop of file name | |
1408 | input_dir = boost::wave::util::branch_path( | |
1409 | boost::wave::util::normalize(input_dir)); | |
1410 | ||
1411 | // walk up the hierarchy, trying to find a file wave.cfg | |
1412 | while (!input_dir.empty()) { | |
1413 | fs::path filename = input_dir / "wave.cfg"; | |
1414 | if (cmd_line_utils::read_config_file_options(filename.string(), | |
1415 | desc_overall_cfgfile, vm, true)) | |
1416 | { | |
1417 | break; // break on the first cfg file found | |
1418 | } | |
1419 | input_dir = boost::wave::util::branch_path(input_dir); | |
1420 | } | |
1421 | } | |
1422 | ||
1423 | // if there is specified at least one config file, parse it and add the | |
1424 | // options to the main variables_map | |
1425 | if (vm.count("config-file")) { | |
1426 | vector<std::string> const &cfg_files = | |
1427 | vm["config-file"].as<vector<std::string> >(); | |
1428 | vector<std::string>::const_iterator end = cfg_files.end(); | |
1429 | for (vector<std::string>::const_iterator cit = cfg_files.begin(); | |
1430 | cit != end; ++cit) | |
1431 | { | |
1432 | // parse a single config file and store the results | |
1433 | cmd_line_utils::read_config_file_options(*cit, | |
1434 | desc_overall_cfgfile, vm); | |
1435 | } | |
1436 | } | |
1437 | ||
b32b8144 FG |
1438 | // validate warning settings |
1439 | if (vm.count("warning")) | |
1440 | { | |
1441 | BOOST_FOREACH(const std::string& arg, | |
1442 | vm["warning"].as<std::vector<std::string> >()) | |
1443 | { | |
1444 | if (boost::range::find(accepted_w_args, arg) == | |
1445 | boost::end(accepted_w_args)) | |
1446 | { | |
1447 | cerr << "wave: Invalid warning setting: " << arg << endl; | |
1448 | return -1; | |
1449 | } | |
1450 | } | |
1451 | } | |
1452 | ||
7c673cae FG |
1453 | // ... act as required |
1454 | if (vm.count("help")) { | |
1455 | po::options_description desc_help ( | |
1456 | "Usage: wave [options] [@config-file(s)] [file]"); | |
1457 | ||
1458 | desc_help.add(desc_cmdline).add(desc_generic).add(desc_ext); | |
1459 | cout << desc_help << endl; | |
1460 | return 1; | |
1461 | } | |
1462 | ||
1463 | if (vm.count("version")) { | |
1464 | cout << get_version() << endl; | |
1465 | return 0; | |
1466 | } | |
1467 | ||
1468 | if (vm.count("copyright")) { | |
1469 | return print_copyright(); | |
1470 | } | |
1471 | ||
1472 | // if there is no input file given, then take input from stdin | |
1473 | if (0 == arguments.size() || 0 == arguments[0].value.size() || | |
1474 | arguments[0].value[0] == "-") | |
1475 | { | |
1476 | // preprocess the given input from stdin | |
1477 | return do_actual_work("<stdin>", std::cin, vm, true); | |
1478 | } | |
1479 | else { | |
1480 | if (arguments.size() > 1) { | |
1481 | // this driver understands to parse one input file only | |
1482 | cerr << "wave: more than one input file specified, " | |
1483 | << "ignoring all but the first!" << endl; | |
1484 | } | |
1485 | ||
1486 | std::string file_name(arguments[0].value[0]); | |
1487 | std::ifstream instream(file_name.c_str()); | |
1488 | ||
1489 | // preprocess the given input file | |
1490 | if (!instream.is_open()) { | |
1491 | cerr << "wave: could not open input file: " << file_name << endl; | |
1492 | return -1; | |
1493 | } | |
1494 | return do_actual_work(file_name, instream, vm, false); | |
1495 | } | |
1496 | } | |
1497 | catch (std::exception const &e) { | |
1498 | cout << "wave: exception caught: " << e.what() << endl; | |
1499 | return 6; | |
1500 | } | |
1501 | catch (...) { | |
1502 | cerr << "wave: unexpected exception caught." << endl; | |
1503 | return 7; | |
1504 | } | |
1505 | } | |
1506 |