2
0
mirror of https://github.com/boostorg/wave.git synced 2026-01-26 19:12:15 +00:00
Files
wave/tool/cpp.cpp
Jeff Trull f1ec8fd22c Make indentation consistent with code nesting
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.
2020-10-23 15:05:00 -07:00

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