mirror of
https://github.com/boostorg/wave.git
synced 2026-01-26 19:12:15 +00:00
These changes, a subset of what clang-format generates, ensure code and comments are aligned properly for the level of the code. There were a number of places where comments (many) and code (some) were four spaces to the left of where they should be. This will make the code easier to read. One advantage of separating out these changes is they can be verified to be whitespace-only with a simple diff.
1564 lines
61 KiB
C++
1564 lines
61 KiB
C++
/*=============================================================================
|
|
Boost.Wave: A Standard compliant C++ preprocessor library
|
|
|
|
http://www.boost.org/
|
|
|
|
Copyright (c) 2001-2012 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/filesystem/fstream.hpp>
|
|
#include <boost/timer/timer.hpp>
|
|
#include <boost/any.hpp>
|
|
#include <boost/algorithm/cxx11/any_of.hpp>
|
|
#include <boost/algorithm/string/join.hpp>
|
|
#include <boost/range/algorithm/find.hpp>
|
|
#include <boost/range/end.hpp>
|
|
#include <boost/foreach.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 <iostream>
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// 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>
|
|
#include <boost/wave/grammars/cpp_has_include_grammar.hpp>
|
|
#endif
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// Import required names
|
|
using namespace boost::spirit::classic;
|
|
|
|
using std::pair;
|
|
using std::vector;
|
|
using std::getline;
|
|
using boost::filesystem::ofstream;
|
|
using boost::filesystem::ifstream;
|
|
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-2012 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, bool treat_warnings_as_error)
|
|
{
|
|
// default error reporting
|
|
cerr
|
|
<< e.file_name() << ":" << e.line_no() << ":" << e.column_no()
|
|
<< ": " << e.description() << endl;
|
|
|
|
// errors count as one
|
|
return (treat_warnings_as_error ||
|
|
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,
|
|
bool treat_warnings_as_error)
|
|
{
|
|
// default error reporting
|
|
int result = report_error_message(e, treat_warnings_as_error);
|
|
|
|
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
|
|
ofstream macronames_out;
|
|
fs::path macronames_file (boost::wave::util::create_path(filename));
|
|
|
|
if (macronames_file != "-") {
|
|
macronames_file = boost::wave::util::complete_path(macronames_file);
|
|
boost::wave::util::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
|
|
ofstream macrocounts_out;
|
|
fs::path macrocounts_file (boost::wave::util::create_path(filename));
|
|
|
|
if (macrocounts_file != "-") {
|
|
macrocounts_file = boost::wave::util::complete_path(macrocounts_file);
|
|
boost::wave::util::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;
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////
|
|
// read all of a file into a string
|
|
std::string read_entire_file(std::istream& instream)
|
|
{
|
|
std::string content;
|
|
|
|
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 (std::istream_iterator<char>(instream),
|
|
std::istream_iterator<char>(),
|
|
std::inserter(content, content.end()));
|
|
#else
|
|
content = std::string(std::istreambuf_iterator<char>(instream.rdbuf()),
|
|
std::istreambuf_iterator<char>());
|
|
#endif
|
|
return content;
|
|
}
|
|
} // 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;
|
|
const bool treat_warnings_as_error = vm.count("warning") &&
|
|
boost::algorithm::any_of_equal(
|
|
vm["warning"].as<std::vector<std::string> >(), "error");
|
|
|
|
try {
|
|
// process the given file
|
|
std::string instring;
|
|
|
|
instream.unsetf(std::ios::skipws);
|
|
if (!input_is_stdin)
|
|
instring = read_entire_file(instream);
|
|
|
|
// The preprocessing of the input stream is done on the fly behind the
|
|
// scenes during iteration over the context_type::iterator_type stream.
|
|
ofstream output;
|
|
ofstream traceout;
|
|
ofstream includelistout;
|
|
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 != "-") {
|
|
boost::wave::util::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 != "-") {
|
|
boost::wave::util::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 != "-") {
|
|
boost::wave::util::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;
|
|
bool preserve_bol_whitespace = false;
|
|
|
|
if (vm.count("preserve")) {
|
|
int preserve = vm["preserve"].as<int>();
|
|
|
|
switch(preserve) {
|
|
case 0: break; // preserve no whitespace
|
|
case 3: // preserve all whitespace
|
|
preserve_whitespace = true;
|
|
preserve_comments = true;
|
|
preserve_bol_whitespace = true;
|
|
break;
|
|
|
|
case 2: // preserve comments and BOL whitespace only
|
|
preserve_comments = true;
|
|
preserve_bol_whitespace = true;
|
|
break;
|
|
|
|
case 1: // preserve BOL whitespace only
|
|
preserve_bol_whitespace = true;
|
|
break;
|
|
|
|
default:
|
|
cerr << "wave: bogus preserve whitespace option value: "
|
|
<< preserve << ", should be 0, 1, 2, or 3" << 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,
|
|
preserve_bol_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();
|
|
|
|
// check, if we have a license file to prepend
|
|
std::string license;
|
|
|
|
if (vm.count("license")) {
|
|
// try to open the file, where to put the preprocessed output
|
|
std::string license_file(vm["license"].as<std::string>());
|
|
ifstream license_stream(license_file.c_str());
|
|
|
|
if (!license_stream.is_open()) {
|
|
cerr << "wave: could not open specified license file: "
|
|
<< license_file << endl;
|
|
return -1;
|
|
}
|
|
license = read_entire_file(license_stream);
|
|
hooks.set_license_info(license);
|
|
}
|
|
|
|
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")) {
|
|
#if BOOST_WAVE_SUPPORT_CPP0X != 0
|
|
if (vm.count("c++11")) {
|
|
cerr << "wave: multiple language options specified: --c99 "
|
|
"and --c++11" << endl;
|
|
return -1;
|
|
}
|
|
#endif
|
|
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
|
|
#if BOOST_WAVE_SUPPORT_CPP0X != 0
|
|
if (vm.count("c++11")) {
|
|
if (vm.count("c99")) {
|
|
cerr << "wave: multiple language options specified: --c99 "
|
|
"and --c++11" << endl;
|
|
return -1;
|
|
}
|
|
ctx.set_language(
|
|
boost::wave::language_support(
|
|
boost::wave::support_cpp0x
|
|
| boost::wave::support_option_convert_trigraphs
|
|
| boost::wave::support_option_long_long
|
|
| 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
|
|
));
|
|
}
|
|
#endif // BOOST_WAVE_SUPPORT_CPP0X != 0
|
|
|
|
#if BOOST_WAVE_SUPPORT_CPP1Z != 0
|
|
if (vm.count("c++17")) {
|
|
ctx.set_language(
|
|
boost::wave::language_support(
|
|
boost::wave::support_cpp1z
|
|
#if BOOST_WAVE_SUPPORT_HAS_INCLUDE != 0
|
|
| boost::wave::support_option_has_include
|
|
#endif
|
|
| boost::wave::support_option_convert_trigraphs
|
|
| boost::wave::support_option_long_long
|
|
| 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
|
|
));
|
|
}
|
|
#endif // BOOST_WAVE_SUPPORT_CPP1Z
|
|
|
|
#if BOOST_WAVE_SUPPORT_CPP2A != 0
|
|
if (vm.count("c++20")) {
|
|
ctx.set_language(
|
|
boost::wave::language_support(
|
|
boost::wave::support_cpp2a
|
|
#if BOOST_WAVE_SUPPORT_HAS_INCLUDE != 0
|
|
| boost::wave::support_option_has_include
|
|
#endif
|
|
#if BOOST_WAVE_SUPPORT_VA_OPT != 0
|
|
| boost::wave::support_option_va_opt
|
|
#endif
|
|
| boost::wave::support_option_convert_trigraphs
|
|
| boost::wave::support_option_long_long
|
|
| 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
|
|
));
|
|
}
|
|
#endif // BOOST_WAVE_SUPPORT_CPP2A != 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);
|
|
}
|
|
}
|
|
|
|
#if BOOST_WAVE_USE_DEPRECIATED_PREPROCESSING_HOOKS == 0
|
|
// suppress expansion of specified macros
|
|
if (vm.count("noexpand")) {
|
|
vector<std::string> const &noexpandmacros =
|
|
vm["noexpand"].as<vector<std::string> >();
|
|
vector<std::string>::const_iterator end = noexpandmacros.end();
|
|
for (vector<std::string>::const_iterator cit = noexpandmacros.begin();
|
|
cit != end; ++cit)
|
|
{
|
|
ctx.get_hooks().add_noexpandmacro(*cit);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
// 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 = boost::wave::util::complete_path(out_file);
|
|
boost::wave::util::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;
|
|
}
|
|
if (!license.empty())
|
|
output << license;
|
|
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");
|
|
|
|
boost::wave::util::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;
|
|
}
|
|
if (!license.empty())
|
|
output << license;
|
|
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()), false);
|
|
}
|
|
|
|
// 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.
|
|
std::vector<std::string> const& force =
|
|
vm["forceinclude"].as<std::vector<std::string> >();
|
|
std::vector<std::string>::const_reverse_iterator rend = force.rend();
|
|
for (std::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,
|
|
treat_warnings_as_error);
|
|
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, treat_warnings_as_error);
|
|
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, treat_warnings_as_error);
|
|
return 1;
|
|
}
|
|
catch (boost::wave::cpplexer::lexing_exception const &e) {
|
|
// some lexing error
|
|
report_error_message(e, treat_warnings_as_error);
|
|
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[])
|
|
{
|
|
const std::string accepted_w_args[] = {"error"};
|
|
|
|
// 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)")
|
|
;
|
|
|
|
const std::string w_arg_desc = "Warning settings. Currently supported: -W" +
|
|
boost::algorithm::join(accepted_w_args, ", -W");
|
|
|
|
// 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")
|
|
("license", po::value<std::string>(),
|
|
"prepend the content of the specified file to each created file")
|
|
("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<std::vector<std::string> >()->composing(),
|
|
"force inclusion of the given file")
|
|
("define,D", po::value<std::vector<std::string> >()->composing(),
|
|
"specify a macro to define (as macro[=[value]])")
|
|
("predefine,P", po::value<std::vector<std::string> >()->composing(),
|
|
"specify a macro to predefine (as macro[=[value]])")
|
|
("undefine,U", po::value<std::vector<std::string> >()->composing(),
|
|
"specify a macro to undefine")
|
|
#if BOOST_WAVE_USE_DEPRECIATED_PREPROCESSING_HOOKS == 0
|
|
("noexpand,N", po::value<std::vector<std::string> >()->composing(),
|
|
"specify a macro name, which should not be expanded")
|
|
#endif
|
|
("nesting,n", po::value<int>(),
|
|
"specify a new maximal include nesting depth")
|
|
("warning,W", po::value<std::vector<std::string> >()->composing(),
|
|
w_arg_desc.c_str())
|
|
;
|
|
|
|
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
|
|
#if BOOST_WAVE_SUPPORT_CPP0X != 0
|
|
("c++11", "enable C++11 mode (implies --variadics and --long_long)")
|
|
#endif
|
|
#if BOOST_WAVE_SUPPORT_CPP1Z != 0
|
|
("c++17", "enable C++17 mode (implies --variadics and --long_long, adds __has_include)")
|
|
#endif
|
|
#if BOOST_WAVE_SUPPORT_CPP2A != 0
|
|
("c++20", "enable C++20 mode (implies --variadics and --long_long, adds __VA_OPT__)")
|
|
#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: begin of line whitespace is preserved,\n"
|
|
"2: comments and begin of line whitespace is preserved,\n"
|
|
"3: 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(boost::wave::util::complete_path(
|
|
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);
|
|
}
|
|
}
|
|
|
|
// validate warning settings
|
|
if (vm.count("warning"))
|
|
{
|
|
BOOST_FOREACH(const std::string& arg,
|
|
vm["warning"].as<std::vector<std::string> >())
|
|
{
|
|
if (boost::range::find(accepted_w_args, arg) ==
|
|
boost::end(accepted_w_args))
|
|
{
|
|
cerr << "wave: Invalid warning setting: " << arg << endl;
|
|
return -1;
|
|
}
|
|
}
|
|
}
|
|
|
|
// ... 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;
|
|
}
|
|
}
|