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 &macros = 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