/*============================================================================= Boost.Wave: A Standard compliant C++ preprocessor library Sample: IDL oriented preprocessor 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) =============================================================================*/ #include "idl.hpp" // global configuration #include #include #include /////////////////////////////////////////////////////////////////////////////// // Include Wave itself #include /////////////////////////////////////////////////////////////////////////////// // Include the lexer related stuff #include // token type #include "idllexer/idl_lex_iterator.hpp" // lexer type /////////////////////////////////////////////////////////////////////////////// // include lexer specifics, import lexer names // #if BOOST_WAVE_SEPARATE_LEXER_INSTANTIATION == 0 #include "idllexer/idl_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 #include #include #include #include #include #endif /////////////////////////////////////////////////////////////////////////////// // import required names using namespace boost::spirit::classic; using std::string; using std::pair; using std::vector; using std::getline; using std::ifstream; using std::cout; using std::cerr; using std::endl; using std::ostream; using std::istreambuf_iterator; namespace po = boost::program_options; namespace fs = boost::filesystem; /////////////////////////////////////////////////////////////////////////////// // print the current version int print_version() { typedef boost::wave::idllexer::lex_iterator< boost::wave::cpplexer::lex_token<> > lex_iterator_type; typedef boost::wave::context context_type; string version (context_type::get_version_string()); cout << version.substr(1, version.size()-2) // strip quotes << " (" << IDL_VERSION_DATE << ")" // add date << endl; return 0; // exit app } /////////////////////////////////////////////////////////////////////////////// // print the copyright statement int print_copyright() { char const *copyright[] = { "", "Sample: IDL oriented preprocessor", "Based on: Wave, A Standard conformant C++ preprocessor library", "It is hosted by 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 } /////////////////////////////////////////////////////////////////////////////// namespace cmd_line_util { // Additional command line parser which interprets '@something' as an // option "config-file" with the value "something". pair at_option_parser(string const&s) { if ('@' == s[0]) return std::make_pair(string("config-file"), s.substr(1)); else return pair(); } // class, which keeps include file information read from the command line class include_paths { public: include_paths() : seen_separator(false) {} vector paths; // stores user paths vector 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 const &tokens) { if (v.empty()) v = boost::any(include_paths()); include_paths *p = boost::any_cast(&v); BOOST_ASSERT(p); // Assume only one path per '-I' occurrence. string t = tokens[0]; 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 void read_config_file_options(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; } vector options; string line; while (std::getline(ifs, line)) { // skip empty lines string::size_type pos = line.find_first_not_of(" \t"); if (pos == string::npos) continue; // skip comment lines if ('#' != line[pos]) options.push_back(line); } 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); } } // 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; } }; /////////////////////////////////////////////////////////////////////////////// } /////////////////////////////////////////////////////////////////////////////// // // 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 const &s, cmd_line_util::include_paths *, int) { cmd_line_util::include_paths::validate(v, s); } }} // namespace boost::program_options /////////////////////////////////////////////////////////////////////////////// // do the actual preprocessing int do_actual_work (std::string file_name, po::variables_map const &vm) { // current file position is saved for exception handling boost::wave::util::file_position_type current_position; try { // process the given file ifstream instream(file_name.c_str()); string instring; if (!instream.is_open()) { cerr << "waveidl: could not open input file: " << file_name << endl; return -1; } instream.unsetf(std::ios::skipws); #if defined(BOOST_NO_TEMPLATED_ITERATOR_CONSTRUCTORS) // this is known to be very slow for large files on some systems copy (istream_iterator(instream), istream_iterator(), inserter(instring, instring.end())); #else instring = string(istreambuf_iterator(instream.rdbuf()), istreambuf_iterator()); #endif // This sample uses the lex_token type predefined in the Wave library, but // but uses a custom lexer type. typedef boost::wave::idllexer::lex_iterator< boost::wave::cpplexer::lex_token<> > lex_iterator_type; typedef boost::wave::context context_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. // The preprocessing of the input stream is done on the fly behind the // scenes during iteration over the context_type::iterator_type stream. context_type ctx (instring.begin(), instring.end(), file_name.c_str()); // add include directories to the system include search paths if (vm.count("sysinclude")) { vector const &syspaths = vm["sysinclude"].as >(); vector::const_iterator end = syspaths.end(); for (vector::const_iterator cit = syspaths.begin(); cit != end; ++cit) { ctx.add_sysinclude_path((*cit).c_str()); } } // add include directories to the include search paths if (vm.count("include")) { cmd_line_util::include_paths const &ip = vm["include"].as(); vector::const_iterator end = ip.paths.end(); for (vector::const_iterator cit = ip.paths.begin(); cit != end; ++cit) { ctx.add_include_path((*cit).c_str()); } // if on the command line was given -I- , this has to be propagated if (ip.seen_separator) ctx.set_sysinclude_delimiter(); // add system include directories to the include path vector::const_iterator sysend = ip.syspaths.end(); for (vector::const_iterator syscit = ip.syspaths.begin(); syscit != sysend; ++syscit) { ctx.add_sysinclude_path((*syscit).c_str()); } } // add additional defined macros if (vm.count("define")) { vector const ¯os = vm["define"].as >(); vector::const_iterator end = macros.end(); for (vector::const_iterator cit = macros.begin(); cit != end; ++cit) { ctx.add_macro_definition(*cit); } } // add additional predefined macros if (vm.count("predefine")) { vector const &predefmacros = vm["predefine"].as >(); vector::const_iterator end = predefmacros.end(); for (vector::const_iterator cit = predefmacros.begin(); cit != end; ++cit) { ctx.add_macro_definition(*cit, true); } } // undefine specified macros if (vm.count("undefine")) { vector const &undefmacros = vm["undefine"].as >(); vector::const_iterator end = undefmacros.end(); for (vector::const_iterator cit = undefmacros.begin(); cit != end; ++cit) { ctx.remove_macro_definition((*cit).c_str(), true); } } // open the output file std::ofstream output; if (vm.count("output")) { // try to open the file, where to put the preprocessed output string out_file (vm["output"].as()); output.open(out_file.c_str()); if (!output.is_open()) { cerr << "waveidl: could not open output file: " << out_file << endl; return -1; } } else { // output the preprocessed result to std::cout output.copyfmt(cout); output.clear(cout.rdstate()); static_cast &>(output).rdbuf(cout.rdbuf()); } // analyze the input file context_type::iterator_type first = ctx.begin(); context_type::iterator_type last = ctx.end(); // loop over all generated tokens outputing the generated text while (first != last) { // print out the string representation of this token (skip comments) using namespace boost::wave; // store the last known good token position current_position = (*first).get_position(); token_id id = token_id(*first); if (T_CPPCOMMENT == id || T_NEWLINE == id) { // C++ comment tokens contain the trailing newline output << endl; } else if (id != T_CCOMMENT) { // print out the current token value output << (*first).get_value(); } ++first; // advance to the next token } } catch (boost::wave::cpp_exception const& e) { // some preprocessing error cerr << e.file_name() << "(" << e.line_no() << "): " << e.description() << endl; return 1; } catch (boost::wave::cpplexer::lexing_exception const& e) { // some lexing error cerr << e.file_name() << "(" << e.line_no() << "): " << e.description() << endl; return 2; } catch (std::exception const& e) { // use last recognized token to retrieve the error position cerr << current_position.get_file() << "(" << current_position.get_line() << "): " << "exception caught: " << e.what() << endl; return 3; } catch (...) { // use last recognized token to retrieve the error position cerr << current_position.get_file() << "(" << current_position.get_line() << "): " << "unexpected exception caught." << endl; return 4; } return 0; } /////////////////////////////////////////////////////////////////////////////// // main entry point int main (int argc, char *argv[]) { try { // analyze the command line options and arguments // declare the options allowed from 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,c", "print out the copyright statement") ("config-file", po::value >(), "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()->composing(), "specify a file to use for output instead of stdout") ("include,I", po::value()->composing(), "specify an additional include directory") ("sysinclude,S", po::value >()->composing(), "specify an additional system include directory") ("define,D", po::value >()->composing(), "specify a macro to define (as macro[=[value]])") ("predefine,P", po::value >()->composing(), "specify a macro to predefine (as macro[=[value]])") ("undefine,U", po::value >()->composing(), "specify a macro to undefine") ; // 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); desc_overall_cfgfile.add(desc_generic); // 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_util::at_option_parser); po::variables_map vm; po::store(opts, vm); po::notify(vm); // Try to find a waveidl.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], fs::native); filename = filename.branch_path() / "waveidl.cfg"; cmd_line_util::read_config_file_options(filename.string(), desc_overall_cfgfile, vm, true); // 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 const &cfg_files = vm["config-file"].as >(); vector::const_iterator end = cfg_files.end(); for (vector::const_iterator cit = cfg_files.begin(); cit != end; ++cit) { // parse a single config file and store the results cmd_line_util::read_config_file_options(*cit, desc_overall_cfgfile, vm); } } // ... act as required if (vm.count("help")) { po::options_description desc_help ( "Usage: waveidl [options] [@config-file(s)] file"); desc_help.add(desc_cmdline).add(desc_generic); cout << desc_help << endl; return 1; } if (vm.count("version")) { return print_version(); } if (vm.count("copyright")) { return print_copyright(); } // extract the arguments from the parsed command line vector arguments; std::remove_copy_if(opts.options.begin(), opts.options.end(), inserter(arguments, arguments.end()), cmd_line_util::is_argument()); // if there is no input file given, then exit if (0 == arguments.size() || 0 == arguments[0].value.size()) { cerr << "waveidl: no input file given, " << "use --help to get a hint." << endl; return 5; } // preprocess the given input file return do_actual_work(arguments[0].value[0], vm); } catch (std::exception const& e) { cout << "waveidl: exception caught: " << e.what() << endl; return 6; } catch (...) { cerr << "waveidl: unexpected exception caught." << endl; return 7; } }