| /*============================================================================= |
| Boost.Wave: A Standard compliant C++ preprocessor library |
| |
| http://www.boost.org/ |
| |
| Copyright (c) 2001-2010 Hartmut Kaiser. Distributed under the Boost |
| Software License, Version 1.0. (See accompanying file |
| LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) |
| =============================================================================*/ |
| |
| #define BOOST_WAVE_SERIALIZATION 0 // enable serialization |
| #define BOOST_WAVE_BINARY_SERIALIZATION 0 // use binary archives |
| #define BOOST_WAVE_XML_SERIALIZATION 1 // use XML archives |
| |
| #include "cpp.hpp" // global configuration |
| |
| /////////////////////////////////////////////////////////////////////////////// |
| // Include additional Boost libraries |
| #include <boost/filesystem/path.hpp> |
| #include <boost/filesystem/convenience.hpp> |
| #include <boost/timer.hpp> |
| #include <boost/any.hpp> |
| |
| /////////////////////////////////////////////////////////////////////////////// |
| // Include Wave itself |
| #include <boost/wave.hpp> |
| |
| /////////////////////////////////////////////////////////////////////////////// |
| // Include the lexer related stuff |
| #include <boost/wave/cpplexer/cpp_lex_token.hpp> // token type |
| #include <boost/wave/cpplexer/cpp_lex_iterator.hpp> // lexer type |
| |
| /////////////////////////////////////////////////////////////////////////////// |
| // Include serialization support, if requested |
| #if BOOST_WAVE_SERIALIZATION != 0 |
| #include <boost/serialization/serialization.hpp> |
| #if BOOST_WAVE_BINARY_SERIALIZATION != 0 |
| #include <boost/archive/binary_iarchive.hpp> |
| #include <boost/archive/binary_oarchive.hpp> |
| typedef boost::archive::binary_iarchive iarchive; |
| typedef boost::archive::binary_oarchive oarchive; |
| #elif BOOST_WAVE_XML_SERIALIZATION != 0 |
| #include <boost/archive/xml_iarchive.hpp> |
| #include <boost/archive/xml_oarchive.hpp> |
| typedef boost::archive::xml_iarchive iarchive; |
| typedef boost::archive::xml_oarchive oarchive; |
| #else |
| #include <boost/archive/text_iarchive.hpp> |
| #include <boost/archive/text_oarchive.hpp> |
| typedef boost::archive::text_iarchive iarchive; |
| typedef boost::archive::text_oarchive oarchive; |
| #endif |
| #endif |
| |
| /////////////////////////////////////////////////////////////////////////////// |
| // Include the context policies to use |
| #include "trace_macro_expansion.hpp" |
| |
| /////////////////////////////////////////////////////////////////////////////// |
| // Include lexer specifics, import lexer names |
| #if BOOST_WAVE_SEPARATE_LEXER_INSTANTIATION == 0 |
| #include <boost/wave/cpplexer/re2clex/cpp_re2c_lexer.hpp> |
| #endif |
| |
| /////////////////////////////////////////////////////////////////////////////// |
| // Include the grammar definitions, if these shouldn't be compiled separately |
| // (ATTENTION: _very_ large compilation times!) |
| #if BOOST_WAVE_SEPARATE_GRAMMAR_INSTANTIATION == 0 |
| #include <boost/wave/grammars/cpp_intlit_grammar.hpp> |
| #include <boost/wave/grammars/cpp_chlit_grammar.hpp> |
| #include <boost/wave/grammars/cpp_grammar.hpp> |
| #include <boost/wave/grammars/cpp_expression_grammar.hpp> |
| #include <boost/wave/grammars/cpp_predef_macros_grammar.hpp> |
| #include <boost/wave/grammars/cpp_defined_grammar.hpp> |
| #endif |
| |
| /////////////////////////////////////////////////////////////////////////////// |
| // Import required names |
| using namespace boost::spirit::classic; |
| |
| using std::pair; |
| using std::vector; |
| using std::getline; |
| using std::ifstream; |
| using std::ofstream; |
| using std::cout; |
| using std::cerr; |
| using std::endl; |
| using std::ostream; |
| using std::istreambuf_iterator; |
| |
| /////////////////////////////////////////////////////////////////////////////// |
| // |
| // This application uses the lex_iterator and lex_token types predefined |
| // with the Wave library, but it is possible to use your own types. |
| // |
| // You may want to have a look at the other samples to see how this is |
| // possible to achieve. |
| typedef boost::wave::cpplexer::lex_token<> token_type; |
| typedef boost::wave::cpplexer::lex_iterator<token_type> |
| lex_iterator_type; |
| |
| // The C++ preprocessor iterators shouldn't be constructed directly. They |
| // are to be generated through a boost::wave::context<> object. This |
| // boost::wave::context object is additionally to be used to initialize and |
| // define different parameters of the actual preprocessing. |
| typedef boost::wave::context< |
| std::string::iterator, lex_iterator_type, |
| boost::wave::iteration_context_policies::load_file_to_string, |
| trace_macro_expansion<token_type> > |
| context_type; |
| |
| /////////////////////////////////////////////////////////////////////////////// |
| // print the current version |
| std::string get_version() |
| { |
| std::string version (context_type::get_version_string()); |
| version = version.substr(1, version.size()-2); // strip quotes |
| version += std::string(" (" CPP_VERSION_DATE_STR ")"); // add date |
| return version; |
| } |
| |
| /////////////////////////////////////////////////////////////////////////////// |
| // print the current version for interactive sessions |
| int print_interactive_version() |
| { |
| cout << "Wave: A Standard conformant C++ preprocessor based on the Boost.Wave library" << endl; |
| cout << "Version: " << get_version() << endl; |
| return 0; |
| } |
| |
| /////////////////////////////////////////////////////////////////////////////// |
| // print the copyright statement |
| int print_copyright() |
| { |
| char const *copyright[] = { |
| "", |
| "Wave: A Standard conformant C++ preprocessor based on the Boost.Wave library", |
| "http://www.boost.org/", |
| "", |
| "Copyright (c) 2001-2010 Hartmut Kaiser, Distributed under the Boost", |
| "Software License, Version 1.0. (See accompanying file", |
| "LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)", |
| 0 |
| }; |
| |
| for (int i = 0; 0 != copyright[i]; ++i) |
| cout << copyright[i] << endl; |
| |
| return 0; // exit app |
| } |
| |
| /////////////////////////////////////////////////////////////////////////////// |
| // forward declarations only |
| namespace cmd_line_utils |
| { |
| class include_paths; |
| } |
| |
| namespace boost { namespace program_options { |
| |
| void validate(boost::any &v, std::vector<std::string> const &s, |
| cmd_line_utils::include_paths *, long); |
| |
| }} // boost::program_options |
| |
| /////////////////////////////////////////////////////////////////////////////// |
| #include <boost/program_options.hpp> |
| |
| namespace po = boost::program_options; |
| namespace fs = boost::filesystem; |
| |
| /////////////////////////////////////////////////////////////////////////////// |
| namespace cmd_line_utils { |
| |
| // Additional command line parser which interprets '@something' as an |
| // option "config-file" with the value "something". |
| inline pair<std::string, std::string> |
| at_option_parser(std::string const&s) |
| { |
| if ('@' == s[0]) |
| return std::make_pair(std::string("config-file"), s.substr(1)); |
| else |
| return pair<std::string, std::string>(); |
| } |
| |
| // class, which keeps include file information read from the command line |
| class include_paths { |
| public: |
| include_paths() : seen_separator(false) {} |
| |
| vector<std::string> paths; // stores user paths |
| vector<std::string> syspaths; // stores system paths |
| bool seen_separator; // command line contains a '-I-' option |
| |
| // Function which validates additional tokens from command line. |
| static void |
| validate(boost::any &v, vector<std::string> const &tokens) |
| { |
| if (v.empty()) |
| v = boost::any(include_paths()); |
| |
| include_paths *p = boost::any_cast<include_paths>(&v); |
| |
| BOOST_ASSERT(p); |
| // Assume only one path per '-I' occurrence. |
| std::string const& t = po::validators::get_single_string(tokens); |
| if (t == "-") { |
| // found -I- option, so switch behaviour |
| p->seen_separator = true; |
| } |
| else if (p->seen_separator) { |
| // store this path as a system path |
| p->syspaths.push_back(t); |
| } |
| else { |
| // store this path as an user path |
| p->paths.push_back(t); |
| } |
| } |
| }; |
| |
| // Read all options from a given config file, parse and add them to the |
| // given variables_map |
| bool read_config_file_options(std::string const &filename, |
| po::options_description const &desc, po::variables_map &vm, |
| bool may_fail = false) |
| { |
| ifstream ifs(filename.c_str()); |
| |
| if (!ifs.is_open()) { |
| if (!may_fail) { |
| cerr << filename |
| << ": command line warning: config file not found" |
| << endl; |
| } |
| return false; |
| } |
| |
| vector<std::string> options; |
| std::string line; |
| |
| while (std::getline(ifs, line)) { |
| // skip empty lines |
| std::string::size_type pos = line.find_first_not_of(" \t"); |
| if (pos == std::string::npos) |
| continue; |
| |
| // skip comment lines |
| if ('#' != line[pos]) { |
| // strip leading and trailing whitespace |
| std::string::size_type endpos = line.find_last_not_of(" \t"); |
| BOOST_ASSERT(endpos != std::string::npos); |
| options.push_back(line.substr(pos, endpos-pos+1)); |
| } |
| } |
| |
| if (options.size() > 0) { |
| using namespace boost::program_options::command_line_style; |
| po::store(po::command_line_parser(options) |
| .options(desc).style(unix_style).run(), vm); |
| po::notify(vm); |
| } |
| return true; |
| } |
| |
| // predicate to extract all positional arguments from the command line |
| struct is_argument { |
| bool operator()(po::option const &opt) |
| { |
| return (opt.position_key == -1) ? true : false; |
| } |
| }; |
| |
| // trim quotes from path names, if any |
| std::string trim_quotes(std::string const& file) |
| { |
| if (('"' == file[0] || '\'' == file[0]) && file[0] == file[file.size()-1]) |
| { |
| return file.substr(1, file.size()-2); |
| } |
| return file; |
| } |
| |
| /////////////////////////////////////////////////////////////////////////////// |
| } |
| |
| /////////////////////////////////////////////////////////////////////////////// |
| // |
| // Special validator overload, which allows to handle the -I- syntax for |
| // switching the semantics of an -I option. |
| // |
| /////////////////////////////////////////////////////////////////////////////// |
| namespace boost { namespace program_options { |
| |
| void validate(boost::any &v, std::vector<std::string> const &s, |
| cmd_line_utils::include_paths *, long) |
| { |
| cmd_line_utils::include_paths::validate(v, s); |
| } |
| |
| }} // namespace boost::program_options |
| |
| /////////////////////////////////////////////////////////////////////////////// |
| namespace { |
| |
| class auto_stop_watch : public stop_watch |
| { |
| public: |
| auto_stop_watch(std::ostream &outstrm_) |
| : print_time(false), outstrm(outstrm_) |
| { |
| } |
| |
| ~auto_stop_watch() |
| { |
| if (print_time) { |
| outstrm << "Elapsed time: " |
| << this->format_elapsed_time() |
| << std::endl; |
| } |
| } |
| |
| void set_print_time(bool print_time_) |
| { |
| print_time = print_time_; |
| } |
| |
| private: |
| bool print_time; |
| std::ostream &outstrm; |
| }; |
| |
| /////////////////////////////////////////////////////////////////////////// |
| inline std::string |
| report_iostate_error(std::ios::iostate state) |
| { |
| BOOST_ASSERT(state & (std::ios::badbit | std::ios::failbit | std::ios::eofbit)); |
| std::string result; |
| if (state & std::ios::badbit) { |
| result += " the reported problem was: " |
| "loss of integrity of the stream buffer\n"; |
| } |
| if (state & std::ios::failbit) { |
| result += " the reported problem was: " |
| "an operation was not processed correctly\n"; |
| } |
| if (state & std::ios::eofbit) { |
| result += " the reported problem was: " |
| "end-of-file while writing to the stream\n"; |
| } |
| return result; |
| } |
| |
| /////////////////////////////////////////////////////////////////////////// |
| // Retrieve the position of a macro definition |
| template <typename Context> |
| inline bool |
| get_macro_position(Context &ctx, |
| typename Context::token_type::string_type const& name, |
| typename Context::position_type &pos) |
| { |
| bool has_parameters = false; |
| bool is_predefined = false; |
| std::vector<typename Context::token_type> parameters; |
| typename Context::token_sequence_type definition; |
| |
| return ctx.get_macro_definition(name, has_parameters, is_predefined, |
| pos, parameters, definition); |
| } |
| |
| /////////////////////////////////////////////////////////////////////////// |
| // Generate some meaningful error messages |
| template <typename Exception> |
| inline int |
| report_error_message(Exception const &e) |
| { |
| // default error reporting |
| cerr |
| << e.file_name() << ":" << e.line_no() << ":" << e.column_no() |
| << ": " << e.description() << endl; |
| |
| // errors count as one |
| return (e.get_severity() == boost::wave::util::severity_error || |
| e.get_severity() == boost::wave::util::severity_fatal) ? 1 : 0; |
| } |
| |
| template <typename Context> |
| inline int |
| report_error_message(Context &ctx, boost::wave::cpp_exception const &e) |
| { |
| // default error reporting |
| int result = report_error_message(e); |
| |
| using boost::wave::preprocess_exception; |
| switch(e.get_errorcode()) { |
| case preprocess_exception::macro_redefinition: |
| { |
| // report the point of the initial macro definition |
| typename Context::position_type pos; |
| if (get_macro_position(ctx, e.get_related_name(), pos)) { |
| cerr |
| << pos << ": " |
| << preprocess_exception::severity_text(e.get_severity()) |
| << ": this is the location of the previous definition." |
| << endl; |
| } |
| else { |
| cerr |
| << e.file_name() << ":" << e.line_no() << ":" |
| << e.column_no() << ": " |
| << preprocess_exception::severity_text(e.get_severity()) |
| << ": not able to retrieve the location of the previous " |
| << "definition." << endl; |
| } |
| } |
| break; |
| |
| default: |
| break; |
| } |
| |
| return result; |
| } |
| |
| /////////////////////////////////////////////////////////////////////////// |
| // Read one logical line of text |
| inline bool |
| read_a_line (std::istream &instream, std::string &instring) |
| { |
| bool eol = true; |
| do { |
| std::string line; |
| std::getline(instream, line); |
| if (instream.rdstate() & std::ios::failbit) |
| return false; // nothing to do |
| |
| eol = true; |
| if (line.find_last_of('\\') == line.size()-1) |
| eol = false; |
| |
| instring += line + '\n'; |
| } while (!eol); |
| return true; |
| } |
| |
| /////////////////////////////////////////////////////////////////////////// |
| // Load and save the internal tables of the wave::context object |
| template <typename Context> |
| inline void |
| load_state(po::variables_map const &vm, Context &ctx) |
| { |
| #if BOOST_WAVE_SERIALIZATION != 0 |
| try { |
| if (vm.count("state") > 0) { |
| fs::path state_file ( |
| boost::wave::util::create_path(vm["state"].as<std::string>())); |
| if (state_file == "-") |
| state_file = boost::wave::util::create_path("wave.state"); |
| |
| std::ios::openmode mode = std::ios::in; |
| |
| #if BOOST_WAVE_BINARY_SERIALIZATION != 0 |
| mode = (std::ios::openmode)(mode | std::ios::binary); |
| #endif |
| ifstream ifs (state_file.string().c_str(), mode); |
| if (ifs.is_open()) { |
| using namespace boost::serialization; |
| iarchive ia(ifs); |
| std::string version; |
| |
| ia >> make_nvp("version", version); // load version |
| if (version == CPP_VERSION_FULL_STR) |
| ia >> make_nvp("state", ctx); // load the internal tables from disc |
| else { |
| cerr << "wave: detected version mismatch while loading state, state was not loaded." << endl; |
| cerr << " loaded version: " << version << endl; |
| cerr << " expected version: " << CPP_VERSION_FULL_STR << endl; |
| } |
| } |
| } |
| } |
| catch (boost::archive::archive_exception const& e) { |
| cerr << "wave: error while loading state: " |
| << e.what() << endl; |
| } |
| catch (boost::wave::preprocess_exception const& e) { |
| cerr << "wave: error while loading state: " |
| << e.description() << endl; |
| } |
| #endif |
| } |
| |
| template <typename Context> |
| inline void |
| save_state(po::variables_map const &vm, Context const &ctx) |
| { |
| #if BOOST_WAVE_SERIALIZATION != 0 |
| try { |
| if (vm.count("state") > 0) { |
| fs::path state_file (boost::wave::util::create_path( |
| vm["state"].as<std::string>())); |
| if (state_file == "-") |
| state_file = boost::wave::util::create_path("wave.state"); |
| |
| std::ios::openmode mode = std::ios::out; |
| |
| #if BOOST_WAVE_BINARY_SERIALIZATION != 0 |
| mode = (std::ios::openmode)(mode | std::ios::binary); |
| #endif |
| ofstream ofs(state_file.string().c_str(), mode); |
| if (!ofs.is_open()) { |
| cerr << "wave: could not open state file for writing: " |
| << state_file.string() << endl; |
| // this is non-fatal |
| } |
| else { |
| using namespace boost::serialization; |
| oarchive oa(ofs); |
| std::string version(CPP_VERSION_FULL_STR); |
| oa << make_nvp("version", version); // write version |
| oa << make_nvp("state", ctx); // write the internal tables to disc |
| } |
| } |
| } |
| catch (boost::archive::archive_exception const& e) { |
| cerr << "wave: error while writing state: " |
| << e.what() << endl; |
| } |
| #endif |
| } |
| |
| /////////////////////////////////////////////////////////////////////////// |
| // list all defined macros |
| bool list_macro_names(context_type const& ctx, std::string filename) |
| { |
| // open file for macro names listing |
| std::ofstream macronames_out; |
| fs::path macronames_file (boost::wave::util::create_path(filename)); |
| |
| if (macronames_file != "-") { |
| macronames_file = fs::complete(macronames_file); |
| fs::create_directories(boost::wave::util::branch_path(macronames_file)); |
| macronames_out.open(macronames_file.string().c_str()); |
| if (!macronames_out.is_open()) { |
| cerr << "wave: could not open file for macro name listing: " |
| << macronames_file.string() << endl; |
| return false; |
| } |
| } |
| else { |
| macronames_out.copyfmt(cout); |
| macronames_out.clear(cout.rdstate()); |
| static_cast<std::basic_ios<char> &>(macronames_out).rdbuf(cout.rdbuf()); |
| } |
| |
| // simply list all defined macros and its definitions |
| typedef context_type::const_name_iterator name_iterator; |
| name_iterator end = ctx.macro_names_end(); |
| for (name_iterator it = ctx.macro_names_begin(); it != end; ++it) |
| { |
| typedef std::vector<context_type::token_type> parameters_type; |
| |
| bool has_pars = false; |
| bool predef = false; |
| context_type::position_type pos; |
| parameters_type pars; |
| context_type::token_sequence_type def; |
| |
| if (ctx.get_macro_definition(*it, has_pars, predef, pos, pars, def)) |
| { |
| macronames_out << (predef ? "-P" : "-D") << *it; |
| if (has_pars) { |
| // list the parameter names for function style macros |
| macronames_out << "("; |
| parameters_type::const_iterator pend = pars.end(); |
| for (parameters_type::const_iterator pit = pars.begin(); |
| pit != pend; /**/) |
| { |
| macronames_out << (*pit).get_value(); |
| if (++pit != pend) |
| macronames_out << ", "; |
| } |
| macronames_out << ")"; |
| } |
| macronames_out << "="; |
| |
| // print the macro definition |
| context_type::token_sequence_type::const_iterator dend = def.end(); |
| for (context_type::token_sequence_type::const_iterator dit = def.begin(); |
| dit != dend; ++dit) |
| { |
| macronames_out << (*dit).get_value(); |
| } |
| |
| macronames_out << std::endl; |
| } |
| } |
| return true; |
| } |
| |
| /////////////////////////////////////////////////////////////////////////// |
| // list macro invocation counts |
| bool list_macro_counts(context_type const& ctx, std::string filename) |
| { |
| // open file for macro invocation count listing |
| std::ofstream macrocounts_out; |
| fs::path macrocounts_file (boost::wave::util::create_path(filename)); |
| |
| if (macrocounts_file != "-") { |
| macrocounts_file = fs::complete(macrocounts_file); |
| fs::create_directories(boost::wave::util::branch_path(macrocounts_file)); |
| macrocounts_out.open(macrocounts_file.string().c_str()); |
| if (!macrocounts_out.is_open()) { |
| cerr << "wave: could not open file for macro invocation count listing: " |
| << macrocounts_file.string() << endl; |
| return false; |
| } |
| } |
| else { |
| macrocounts_out.copyfmt(cout); |
| macrocounts_out.clear(cout.rdstate()); |
| static_cast<std::basic_ios<char> &>(macrocounts_out).rdbuf(cout.rdbuf()); |
| } |
| |
| // list all expanded macro names and their counts in alphabetical order |
| std::map<std::string, std::size_t> const& counts = |
| ctx.get_hooks().get_macro_counts(); |
| |
| typedef std::map<std::string, std::size_t>::const_iterator iterator; |
| iterator end = counts.end(); |
| for (iterator it = counts.begin(); it != end; ++it) |
| macrocounts_out << (*it).first << "," << (*it).second << std::endl; |
| |
| return true; |
| } |
| |
| /////////////////////////////////////////////////////////////////////////////// |
| } // anonymous namespace |
| |
| /////////////////////////////////////////////////////////////////////////////// |
| // do the actual preprocessing |
| int |
| do_actual_work (std::string file_name, std::istream &instream, |
| po::variables_map const &vm, bool input_is_stdin) |
| { |
| // current file position is saved for exception handling |
| boost::wave::util::file_position_type current_position; |
| auto_stop_watch elapsed_time(cerr); |
| int error_count = 0; |
| |
| try { |
| // process the given file |
| std::string instring; |
| |
| instream.unsetf(std::ios::skipws); |
| |
| if (!input_is_stdin) { |
| #if defined(BOOST_NO_TEMPLATED_ITERATOR_CONSTRUCTORS) |
| // this is known to be very slow for large files on some systems |
| copy (std::istream_iterator<char>(instream), |
| std::istream_iterator<char>(), |
| std::inserter(instring, instring.end())); |
| #else |
| instring = std::string(std::istreambuf_iterator<char>(instream.rdbuf()), |
| std::istreambuf_iterator<char>()); |
| #endif |
| } |
| |
| // The preprocessing of the input stream is done on the fly behind the |
| // scenes during iteration over the context_type::iterator_type stream. |
| std::ofstream output; |
| std::ofstream traceout; |
| std::ofstream includelistout; |
| std::ofstream listguardsout; |
| |
| trace_flags enable_trace = trace_nothing; |
| |
| if (vm.count("traceto")) { |
| // try to open the file, where to put the trace output |
| fs::path trace_file (boost::wave::util::create_path( |
| vm["traceto"].as<std::string>())); |
| |
| if (trace_file != "-") { |
| fs::create_directories(boost::wave::util::branch_path(trace_file)); |
| traceout.open(trace_file.string().c_str()); |
| if (!traceout.is_open()) { |
| cerr << "wave: could not open trace file: " << trace_file |
| << endl; |
| return -1; |
| } |
| } |
| enable_trace = trace_macros; |
| } |
| if ((enable_trace & trace_macros) && !traceout.is_open()) { |
| // by default trace to std::cerr |
| traceout.copyfmt(cerr); |
| traceout.clear(cerr.rdstate()); |
| static_cast<std::basic_ios<char> &>(traceout).rdbuf(cerr.rdbuf()); |
| } |
| |
| // Open the stream where to output the list of included file names |
| if (vm.count("listincludes")) { |
| // try to open the file, where to put the include list |
| fs::path includes_file(boost::wave::util::create_path( |
| vm["listincludes"].as<std::string>())); |
| |
| if (includes_file != "-") { |
| fs::create_directories(boost::wave::util::branch_path(includes_file)); |
| includelistout.open(includes_file.string().c_str()); |
| if (!includelistout.is_open()) { |
| cerr << "wave: could not open include list file: " |
| << includes_file.string() << endl; |
| return -1; |
| } |
| } |
| enable_trace = trace_flags(enable_trace | trace_includes); |
| } |
| if ((enable_trace & trace_includes) && !includelistout.is_open()) { |
| // by default list included names to std::cout |
| includelistout.copyfmt(cout); |
| includelistout.clear(cout.rdstate()); |
| static_cast<std::basic_ios<char> &>(includelistout). |
| rdbuf(cout.rdbuf()); |
| } |
| |
| // Open the stream where to output the list of included file names |
| if (vm.count("listguards")) { |
| // try to open the file, where to put the include list |
| fs::path listguards_file(boost::wave::util::create_path( |
| vm["listguards"].as<std::string>())); |
| |
| if (listguards_file != "-") { |
| fs::create_directories(boost::wave::util::branch_path(listguards_file)); |
| listguardsout.open(listguards_file.string().c_str()); |
| if (!listguardsout.is_open()) { |
| cerr << "wave: could not open include guard list file: " |
| << listguards_file.string() << endl; |
| return -1; |
| } |
| } |
| enable_trace = trace_flags(enable_trace | trace_guards); |
| } |
| if ((enable_trace & trace_guards) && !listguardsout.is_open()) { |
| // by default list included names to std::cout |
| listguardsout.copyfmt(cout); |
| listguardsout.clear(cout.rdstate()); |
| static_cast<std::basic_ios<char> &>(listguardsout). |
| rdbuf(cout.rdbuf()); |
| } |
| |
| // enable preserving comments mode |
| bool preserve_comments = false; |
| bool preserve_whitespace = false; |
| |
| if (vm.count("preserve")) { |
| int preserve = vm["preserve"].as<int>(); |
| |
| switch(preserve) { |
| case 0: break; |
| case 2: |
| preserve_whitespace = true; |
| /* fall through */ |
| case 1: |
| preserve_comments = true; |
| break; |
| |
| default: |
| cerr << "wave: bogus preserve whitespace option value: " |
| << preserve << ", should be 0, 1, or 2" << endl; |
| return -1; |
| } |
| } |
| |
| // Since the #pragma wave system() directive may cause a potential security |
| // threat, it has to be enabled explicitly by --extended or -x |
| bool enable_system_command = false; |
| |
| if (vm.count("extended")) |
| enable_system_command = true; |
| |
| // This this the central piece of the Wave library, it provides you with |
| // the iterators to get the preprocessed tokens and allows to configure |
| // the preprocessing stage in advance. |
| bool allow_output = true; // will be manipulated from inside the hooks object |
| std::string default_outfile; // will be used from inside the hooks object |
| trace_macro_expansion<token_type> hooks(preserve_whitespace, |
| output, traceout, includelistout, listguardsout, enable_trace, |
| enable_system_command, allow_output, default_outfile); |
| |
| // enable macro invocation count, if appropriate |
| if (vm.count("macrocounts")) |
| hooks.enable_macro_counting(); |
| |
| context_type ctx (instring.begin(), instring.end(), file_name.c_str(), hooks); |
| |
| #if BOOST_WAVE_SUPPORT_VARIADICS_PLACEMARKERS != 0 |
| // enable C99 mode, if appropriate (implies variadics) |
| if (vm.count("c99")) { |
| ctx.set_language( |
| boost::wave::language_support( |
| boost::wave::support_c99 |
| | boost::wave::support_option_convert_trigraphs |
| | boost::wave::support_option_emit_line_directives |
| #if BOOST_WAVE_SUPPORT_PRAGMA_ONCE != 0 |
| | boost::wave::support_option_include_guard_detection |
| #endif |
| #if BOOST_WAVE_EMIT_PRAGMA_DIRECTIVES != 0 |
| | boost::wave::support_option_emit_pragma_directives |
| #endif |
| | boost::wave::support_option_insert_whitespace |
| )); |
| } |
| else if (vm.count("variadics")) { |
| // enable variadics and placemarkers, if appropriate |
| ctx.set_language(boost::wave::enable_variadics(ctx.get_language())); |
| } |
| #endif // BOOST_WAVE_SUPPORT_VARIADICS_PLACEMARKERS != 0 |
| |
| // enable long long support, if appropriate |
| if (vm.count("long_long")) { |
| ctx.set_language( |
| boost::wave::enable_long_long(ctx.get_language())); |
| } |
| |
| #if BOOST_WAVE_SUPPORT_PRAGMA_ONCE != 0 |
| // disable include guard detection |
| if (vm.count("noguard")) { |
| ctx.set_language( |
| boost::wave::enable_include_guard_detection( |
| ctx.get_language(), false)); |
| } |
| #endif |
| |
| // enable preserving comments mode |
| if (preserve_comments) { |
| ctx.set_language( |
| boost::wave::enable_preserve_comments(ctx.get_language())); |
| } |
| |
| // control the generation of #line directives |
| if (vm.count("line")) { |
| int lineopt = vm["line"].as<int>(); |
| if (0 != lineopt && 1 != lineopt && 2 != lineopt) { |
| cerr << "wave: bogus value for --line command line option: " |
| << lineopt << endl; |
| return -1; |
| } |
| ctx.set_language( |
| boost::wave::enable_emit_line_directives(ctx.get_language(), |
| lineopt != 0)); |
| |
| if (2 == lineopt) |
| ctx.get_hooks().enable_relative_names_in_line_directives(true); |
| } |
| |
| // control whether whitespace should be inserted to disambiguate output |
| if (vm.count("disambiguate")) { |
| int disambiguateopt = vm["disambiguate"].as<int>(); |
| if (0 != disambiguateopt && 1 != disambiguateopt) { |
| cerr << "wave: bogus value for --disambiguate command line option: " |
| << disambiguateopt << endl; |
| return -1; |
| } |
| ctx.set_language( |
| boost::wave::enable_insert_whitespace(ctx.get_language(), |
| disambiguateopt != 0)); |
| } |
| |
| // add include directories to the system include search paths |
| if (vm.count("sysinclude")) { |
| vector<std::string> syspaths = vm["sysinclude"].as<vector<std::string> >(); |
| |
| vector<std::string>::const_iterator end = syspaths.end(); |
| for (vector<std::string>::const_iterator cit = syspaths.begin(); |
| cit != end; ++cit) |
| { |
| ctx.add_sysinclude_path(cmd_line_utils::trim_quotes(*cit).c_str()); |
| } |
| } |
| |
| // add include directories to the include search paths |
| if (vm.count("include")) { |
| cmd_line_utils::include_paths const &ip = |
| vm["include"].as<cmd_line_utils::include_paths>(); |
| vector<std::string>::const_iterator end = ip.paths.end(); |
| |
| for (vector<std::string>::const_iterator cit = ip.paths.begin(); |
| cit != end; ++cit) |
| { |
| ctx.add_include_path(cmd_line_utils::trim_quotes(*cit).c_str()); |
| } |
| |
| // if -I- was given on the command line, this has to be propagated |
| if (ip.seen_separator) |
| ctx.set_sysinclude_delimiter(); |
| |
| // add system include directories to the include path |
| vector<std::string>::const_iterator sysend = ip.syspaths.end(); |
| for (vector<std::string>::const_iterator syscit = ip.syspaths.begin(); |
| syscit != sysend; ++syscit) |
| { |
| ctx.add_sysinclude_path(cmd_line_utils::trim_quotes(*syscit).c_str()); |
| } |
| } |
| |
| // add additional defined macros |
| if (vm.count("define")) { |
| vector<std::string> const ¯os = vm["define"].as<vector<std::string> >(); |
| vector<std::string>::const_iterator end = macros.end(); |
| for (vector<std::string>::const_iterator cit = macros.begin(); |
| cit != end; ++cit) |
| { |
| ctx.add_macro_definition(*cit); |
| } |
| } |
| |
| // add additional predefined macros |
| if (vm.count("predefine")) { |
| vector<std::string> const &predefmacros = |
| vm["predefine"].as<vector<std::string> >(); |
| vector<std::string>::const_iterator end = predefmacros.end(); |
| for (vector<std::string>::const_iterator cit = predefmacros.begin(); |
| cit != end; ++cit) |
| { |
| ctx.add_macro_definition(*cit, true); |
| } |
| } |
| |
| // undefine specified macros |
| if (vm.count("undefine")) { |
| vector<std::string> const &undefmacros = |
| vm["undefine"].as<vector<std::string> >(); |
| vector<std::string>::const_iterator end = undefmacros.end(); |
| for (vector<std::string>::const_iterator cit = undefmacros.begin(); |
| cit != end; ++cit) |
| { |
| ctx.remove_macro_definition(*cit, true); |
| } |
| } |
| |
| // maximal include nesting depth |
| if (vm.count("nesting")) { |
| int max_depth = vm["nesting"].as<int>(); |
| if (max_depth < 1 || max_depth > 100000) { |
| cerr << "wave: bogus maximal include nesting depth: " |
| << max_depth << endl; |
| return -1; |
| } |
| ctx.set_max_include_nesting_depth(max_depth); |
| } |
| |
| // open the output file |
| if (vm.count("output")) { |
| // try to open the file, where to put the preprocessed output |
| fs::path out_file (boost::wave::util::create_path( |
| vm["output"].as<std::string>())); |
| |
| if (out_file == "-") { |
| allow_output = false; // inhibit output initially |
| default_outfile = "-"; |
| } |
| else { |
| out_file = fs::complete(out_file); |
| fs::create_directories(boost::wave::util::branch_path(out_file)); |
| output.open(out_file.string().c_str()); |
| if (!output.is_open()) { |
| cerr << "wave: could not open output file: " |
| << out_file.string() << endl; |
| return -1; |
| } |
| default_outfile = out_file.string(); |
| } |
| } |
| else if (!input_is_stdin && vm.count("autooutput")) { |
| // generate output in the file <input_base_name>.i |
| fs::path out_file (boost::wave::util::create_path(file_name)); |
| std::string basename (boost::wave::util::leaf(out_file)); |
| std::string::size_type pos = basename.find_last_of("."); |
| |
| if (std::string::npos != pos) |
| basename = basename.substr(0, pos); |
| out_file = boost::wave::util::branch_path(out_file) / (basename + ".i"); |
| |
| fs::create_directories(boost::wave::util::branch_path(out_file)); |
| output.open(out_file.string().c_str()); |
| if (!output.is_open()) { |
| cerr << "wave: could not open output file: " |
| << out_file.string() << endl; |
| return -1; |
| } |
| default_outfile = out_file.string(); |
| } |
| |
| // we assume the session to be interactive if input is stdin and output is |
| // stdout and the output is not inhibited |
| bool is_interactive = input_is_stdin && !output.is_open() && allow_output; |
| |
| if (is_interactive) { |
| // if interactive we don't warn for missing endif's etc. |
| ctx.set_language( |
| boost::wave::enable_single_line(ctx.get_language())); |
| } |
| |
| // analyze the input file |
| context_type::iterator_type first = ctx.begin(); |
| context_type::iterator_type last = ctx.end(); |
| |
| // preprocess the required include files |
| if (vm.count("forceinclude")) { |
| // add the filenames to force as include files in _reverse_ order |
| // the second parameter 'is_last' of the force_include function should |
| // be set to true for the last (first given) file. |
| vector<std::string> const &force = |
| vm["forceinclude"].as<vector<std::string> >(); |
| vector<std::string>::const_reverse_iterator rend = force.rend(); |
| for (vector<std::string>::const_reverse_iterator cit = force.rbegin(); |
| cit != rend; /**/) |
| { |
| std::string filename(*cit); |
| first.force_include(filename.c_str(), ++cit == rend); |
| } |
| } |
| |
| elapsed_time.set_print_time(!input_is_stdin && vm.count("timer") > 0); |
| if (is_interactive) { |
| print_interactive_version(); // print welcome message |
| load_state(vm, ctx); // load the internal tables from disc |
| } |
| else if (vm.count("state")) { |
| // the option "state" is usable in interactive mode only |
| cerr << "wave: ignoring the command line option 'state', " |
| << "use it in interactive mode only." << endl; |
| } |
| |
| // >>>>>>>>>>>>> The actual preprocessing happens here. <<<<<<<<<<<<<<<<<<< |
| // loop over the input lines if reading from stdin, otherwise this loop |
| // will be executed once |
| do { |
| // loop over all generated tokens outputting the generated text |
| bool finished = false; |
| |
| if (input_is_stdin) { |
| if (is_interactive) |
| cout << ">>> "; // prompt if is interactive |
| |
| // read next line and continue |
| instring.clear(); |
| if (!read_a_line(instream, instring)) |
| break; // end of input reached |
| first = ctx.begin(instring.begin(), instring.end()); |
| } |
| |
| bool need_to_advanve = false; |
| |
| do { |
| try { |
| if (need_to_advanve) { |
| ++first; |
| need_to_advanve = false; |
| } |
| |
| while (first != last) { |
| // store the last known good token position |
| current_position = (*first).get_position(); |
| |
| // print out the current token value |
| if (allow_output) { |
| if (!output.good()) { |
| cerr << "wave: problem writing to the current " |
| << "output file" << endl; |
| cerr << report_iostate_error(output.rdstate()); |
| break; |
| } |
| if (output.is_open()) |
| output << (*first).get_value(); |
| else |
| cout << (*first).get_value(); |
| } |
| |
| // advance to the next token |
| ++first; |
| } |
| finished = true; |
| } |
| catch (boost::wave::cpp_exception const &e) { |
| // some preprocessing error |
| if (is_interactive || boost::wave::is_recoverable(e)) { |
| error_count += report_error_message(ctx, e); |
| need_to_advanve = true; // advance to the next token |
| } |
| else { |
| throw; // re-throw for non-recoverable errors |
| } |
| } |
| catch (boost::wave::cpplexer::lexing_exception const &e) { |
| // some preprocessing error |
| if (is_interactive || |
| boost::wave::cpplexer::is_recoverable(e)) |
| { |
| error_count += report_error_message(e); |
| need_to_advanve = true; // advance to the next token |
| } |
| else { |
| throw; // re-throw for non-recoverable errors |
| } |
| } |
| } while (!finished); |
| } while (input_is_stdin); |
| |
| if (is_interactive) |
| save_state(vm, ctx); // write the internal tables to disc |
| |
| // list all defined macros at the end of the preprocessing |
| if (vm.count("macronames")) { |
| if (!list_macro_names(ctx, vm["macronames"].as<std::string>())) |
| return -1; |
| } |
| if (vm.count("macrocounts")) { |
| if (!list_macro_counts(ctx, vm["macrocounts"].as<std::string>())) |
| return -1; |
| } |
| } |
| catch (boost::wave::cpp_exception const &e) { |
| // some preprocessing error |
| report_error_message(e); |
| return 1; |
| } |
| catch (boost::wave::cpplexer::lexing_exception const &e) { |
| // some lexing error |
| report_error_message(e); |
| return 2; |
| } |
| catch (std::exception const &e) { |
| // use last recognized token to retrieve the error position |
| cerr |
| << current_position << ": " |
| << "exception caught: " << e.what() |
| << endl; |
| return 3; |
| } |
| catch (...) { |
| // use last recognized token to retrieve the error position |
| cerr |
| << current_position << ": " |
| << "unexpected exception caught." << endl; |
| return 4; |
| } |
| return -error_count; // returns the number of errors as a negative integer |
| } |
| |
| /////////////////////////////////////////////////////////////////////////////// |
| // main entry point |
| int |
| main (int argc, char *argv[]) |
| { |
| // test Wave compilation configuration |
| if (!BOOST_WAVE_TEST_CONFIGURATION()) { |
| cout << "wave: warning: the library this application was linked against was compiled " |
| << endl |
| << " using a different configuration (see wave_config.hpp)." |
| << endl; |
| } |
| |
| // analyze the command line options and arguments |
| try { |
| // declare the options allowed on the command line only |
| po::options_description desc_cmdline ("Options allowed on the command line only"); |
| |
| desc_cmdline.add_options() |
| ("help,h", "print out program usage (this message)") |
| ("version,v", "print the version number") |
| ("copyright", "print out the copyright statement") |
| ("config-file", po::value<vector<std::string> >()->composing(), |
| "specify a config file (alternatively: @filepath)") |
| ; |
| |
| // declare the options allowed on command line and in config files |
| po::options_description desc_generic ("Options allowed additionally in a config file"); |
| |
| desc_generic.add_options() |
| ("output,o", po::value<std::string>(), |
| "specify a file [arg] to use for output instead of stdout or " |
| "disable output [-]") |
| ("autooutput,E", |
| "output goes into a file named <input_basename>.i") |
| ("include,I", po::value<cmd_line_utils::include_paths>()->composing(), |
| "specify an additional include directory") |
| ("sysinclude,S", po::value<vector<std::string> >()->composing(), |
| "specify an additional system include directory") |
| ("forceinclude,F", po::value<vector<std::string> >()->composing(), |
| "force inclusion of the given file") |
| ("define,D", po::value<vector<std::string> >()->composing(), |
| "specify a macro to define (as macro[=[value]])") |
| ("predefine,P", po::value<vector<std::string> >()->composing(), |
| "specify a macro to predefine (as macro[=[value]])") |
| ("undefine,U", po::value<vector<std::string> >()->composing(), |
| "specify a macro to undefine") |
| ("nesting,n", po::value<int>(), |
| "specify a new maximal include nesting depth") |
| ; |
| |
| po::options_description desc_ext ("Extended options (allowed everywhere)"); |
| |
| desc_ext.add_options() |
| ("traceto,t", po::value<std::string>(), |
| "output macro expansion tracing information to a file [arg] " |
| "or to stderr [-]") |
| ("timer", "output overall elapsed computing time to stderr") |
| ("long_long", "enable long long support in C++ mode") |
| #if BOOST_WAVE_SUPPORT_VARIADICS_PLACEMARKERS != 0 |
| ("variadics", "enable certain C99 extensions in C++ mode") |
| ("c99", "enable C99 mode (implies --variadics)") |
| #endif |
| ("listincludes,l", po::value<std::string>(), |
| "list names of included files to a file [arg] or to stdout [-]") |
| ("macronames,m", po::value<std::string>(), |
| "list all defined macros to a file [arg] or to stdout [-]") |
| ("macrocounts,c", po::value<std::string>(), |
| "list macro invocation counts to a file [arg] or to stdout [-]") |
| ("preserve,p", po::value<int>()->default_value(0), |
| "preserve whitespace\n" |
| "0: no whitespace is preserved (default),\n" |
| "1: comments are preserved,\n" |
| "2: all whitespace is preserved") |
| ("line,L", po::value<int>()->default_value(1), |
| "control the generation of #line directives\n" |
| "0: no #line directives are generated,\n" |
| "1: #line directives will be emitted (default),\n" |
| "2: #line directives will be emitted using relative\n" |
| " filenames") |
| ("disambiguate", po::value<int>()->default_value(1), |
| "control whitespace insertion to disambiguate\n" |
| "consecutive tokens\n" |
| "0: no additional whitespace is generated,\n" |
| "1: whitespace is used to disambiguate output (default)") |
| ("extended,x", "enable the #pragma wave system() directive") |
| #if BOOST_WAVE_SUPPORT_PRAGMA_ONCE != 0 |
| ("noguard,G", "disable include guard detection") |
| ("listguards,g", po::value<std::string>(), |
| "list names of files flagged as 'include once' to a file [arg] " |
| "or to stdout [-]") |
| #endif |
| #if BOOST_WAVE_SERIALIZATION != 0 |
| ("state,s", po::value<std::string>(), |
| "load and save state information from/to the given file [arg] " |
| "or 'wave.state' [-] (interactive mode only)") |
| #endif |
| ; |
| |
| // combine the options for the different usage schemes |
| po::options_description desc_overall_cmdline; |
| po::options_description desc_overall_cfgfile; |
| |
| desc_overall_cmdline.add(desc_cmdline).add(desc_generic).add(desc_ext); |
| desc_overall_cfgfile.add(desc_generic).add(desc_ext); |
| |
| // parse command line and store results |
| using namespace boost::program_options::command_line_style; |
| |
| po::parsed_options opts(po::parse_command_line(argc, argv, |
| desc_overall_cmdline, unix_style, cmd_line_utils::at_option_parser)); |
| po::variables_map vm; |
| |
| po::store(opts, vm); |
| po::notify(vm); |
| |
| // // Try to find a wave.cfg in the same directory as the executable was |
| // // started from. If this exists, treat it as a wave config file |
| // fs::path filename(argv[0]); |
| // |
| // filename = filename.branch_path() / "wave.cfg"; |
| // cmd_line_utils::read_config_file_options(filename.string(), |
| // desc_overall_cfgfile, vm, true); |
| |
| // extract the arguments from the parsed command line |
| vector<po::option> arguments; |
| |
| std::remove_copy_if(opts.options.begin(), opts.options.end(), |
| back_inserter(arguments), cmd_line_utils::is_argument()); |
| |
| // try to find a config file somewhere up the filesystem hierarchy |
| // starting with the input file path. This allows to use a general wave.cfg |
| // file for all files in a certain project. |
| if (arguments.size() > 0 && arguments[0].value[0] != "-") { |
| // construct full path of input file |
| fs::path input_dir (fs::complete( |
| boost::wave::util::create_path(arguments[0].value[0]))); |
| |
| // chop of file name |
| input_dir = boost::wave::util::branch_path( |
| boost::wave::util::normalize(input_dir)); |
| |
| // walk up the hierarchy, trying to find a file wave.cfg |
| while (!input_dir.empty()) { |
| fs::path filename = input_dir / "wave.cfg"; |
| if (cmd_line_utils::read_config_file_options(filename.string(), |
| desc_overall_cfgfile, vm, true)) |
| { |
| break; // break on the first cfg file found |
| } |
| input_dir = boost::wave::util::branch_path(input_dir); |
| } |
| } |
| |
| // if there is specified at least one config file, parse it and add the |
| // options to the main variables_map |
| if (vm.count("config-file")) { |
| vector<std::string> const &cfg_files = |
| vm["config-file"].as<vector<std::string> >(); |
| vector<std::string>::const_iterator end = cfg_files.end(); |
| for (vector<std::string>::const_iterator cit = cfg_files.begin(); |
| cit != end; ++cit) |
| { |
| // parse a single config file and store the results |
| cmd_line_utils::read_config_file_options(*cit, |
| desc_overall_cfgfile, vm); |
| } |
| } |
| |
| // ... act as required |
| if (vm.count("help")) { |
| po::options_description desc_help ( |
| "Usage: wave [options] [@config-file(s)] [file]"); |
| |
| desc_help.add(desc_cmdline).add(desc_generic).add(desc_ext); |
| cout << desc_help << endl; |
| return 1; |
| } |
| |
| if (vm.count("version")) { |
| cout << get_version() << endl; |
| return 0; |
| } |
| |
| if (vm.count("copyright")) { |
| return print_copyright(); |
| } |
| |
| // if there is no input file given, then take input from stdin |
| if (0 == arguments.size() || 0 == arguments[0].value.size() || |
| arguments[0].value[0] == "-") |
| { |
| // preprocess the given input from stdin |
| return do_actual_work("<stdin>", std::cin, vm, true); |
| } |
| else { |
| if (arguments.size() > 1) { |
| // this driver understands to parse one input file only |
| cerr << "wave: more than one input file specified, " |
| << "ignoring all but the first!" << endl; |
| } |
| |
| std::string file_name(arguments[0].value[0]); |
| ifstream instream(file_name.c_str()); |
| |
| // preprocess the given input file |
| if (!instream.is_open()) { |
| cerr << "wave: could not open input file: " << file_name << endl; |
| return -1; |
| } |
| return do_actual_work(file_name, instream, vm, false); |
| } |
| } |
| catch (std::exception const &e) { |
| cout << "wave: exception caught: " << e.what() << endl; |
| return 6; |
| } |
| catch (...) { |
| cerr << "wave: unexpected exception caught." << endl; |
| return 7; |
| } |
| } |
| |