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