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