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