Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: downloads/boost_1_34_1/tools/wave/cpp.cpp @ 35

Last change on this file since 35 was 29, checked in by landauf, 16 years ago

updated boost from 1_33_1 to 1_34_1

File size: 43.8 KB
Line 
1/*=============================================================================
2    Boost.Wave: A Standard compliant C++ preprocessor library
3
4    http://www.boost.org/
5
6    Copyright (c) 2001-2007 Hartmut Kaiser. Distributed under the Boost
7    Software License, Version 1.0. (See accompanying file
8    LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
9=============================================================================*/
10
11#define BOOST_WAVE_SERIALIZATION        1             // enable serialization
12#define BOOST_WAVE_BINARY_SERIALIZATION 1             // use binary archives
13
14#include "cpp.hpp"                                    // global configuration
15
16///////////////////////////////////////////////////////////////////////////////
17// Include additional Boost libraries
18#include <boost/filesystem/path.hpp>
19#include <boost/filesystem/convenience.hpp>
20#include <boost/timer.hpp>
21#include <boost/any.hpp>
22
23///////////////////////////////////////////////////////////////////////////////
24//  Include Wave itself
25#include <boost/wave.hpp>
26
27///////////////////////////////////////////////////////////////////////////////
28//  Include the lexer related stuff
29#include <boost/wave/cpplexer/cpp_lex_token.hpp>      // token type
30#include <boost/wave/cpplexer/cpp_lex_iterator.hpp>   // lexer type
31
32///////////////////////////////////////////////////////////////////////////////
33//  Include serialization support, if requested
34#if BOOST_WAVE_SERIALIZATION != 0
35#include <boost/serialization/serialization.hpp>
36#if BOOST_WAVE_BINARY_SERIALIZATION != 0
37#include <boost/archive/binary_iarchive.hpp>
38#include <boost/archive/binary_oarchive.hpp>
39typedef boost::archive::binary_iarchive iarchive;
40typedef boost::archive::binary_oarchive oarchive;
41#else
42#include <boost/archive/text_iarchive.hpp>
43#include <boost/archive/text_oarchive.hpp>
44typedef boost::archive::text_iarchive iarchive;
45typedef boost::archive::text_oarchive oarchive;
46#endif
47#endif
48
49///////////////////////////////////////////////////////////////////////////////
50//  Include the context policies to use
51#include "trace_macro_expansion.hpp"
52
53///////////////////////////////////////////////////////////////////////////////
54//  Include lexer specifics, import lexer names
55#if BOOST_WAVE_SEPARATE_LEXER_INSTANTIATION == 0
56#include <boost/wave/cpplexer/re2clex/cpp_re2c_lexer.hpp>
57#endif
58
59///////////////////////////////////////////////////////////////////////////////
60//  Include the grammar definitions, if these shouldn't be compiled separately
61//  (ATTENTION: _very_ large compilation times!)
62#if BOOST_WAVE_SEPARATE_GRAMMAR_INSTANTIATION == 0
63#include <boost/wave/grammars/cpp_intlit_grammar.hpp>
64#include <boost/wave/grammars/cpp_chlit_grammar.hpp>
65#include <boost/wave/grammars/cpp_grammar.hpp>
66#include <boost/wave/grammars/cpp_expression_grammar.hpp>
67#include <boost/wave/grammars/cpp_predef_macros_grammar.hpp>
68#include <boost/wave/grammars/cpp_defined_grammar.hpp>
69#endif
70
71///////////////////////////////////////////////////////////////////////////////
72//  Import required names
73using namespace boost::spirit;
74
75using std::string;
76using std::pair;
77using std::vector;
78using std::getline;
79using std::ifstream;
80using std::ofstream;
81using std::cout;
82using std::cerr;
83using std::endl;
84using std::ostream;
85using std::istreambuf_iterator;
86
87///////////////////////////////////////////////////////////////////////////////
88//
89//  This application uses the lex_iterator and lex_token types predefined
90//  with the Wave library, but it is possible to use your own types.
91//
92//  You may want to have a look at the other samples to see how this is
93//  possible to achieve.
94    typedef boost::wave::cpplexer::lex_token<> token_type;
95    typedef boost::wave::cpplexer::lex_iterator<token_type>
96        lex_iterator_type;
97
98//  The C++ preprocessor iterators shouldn't be constructed directly. They
99//  are to be generated through a boost::wave::context<> object. This
100//  boost::wave::context object is additionally to be used to initialize and
101//  define different parameters of the actual preprocessing.
102    typedef boost::wave::context<
103            std::string::iterator, lex_iterator_type,
104            boost::wave::iteration_context_policies::load_file_to_string,
105            trace_macro_expansion<token_type> > 
106        context_type;
107
108///////////////////////////////////////////////////////////////////////////////
109// print the current version
110string get_version()
111{
112    string version (context_type::get_version_string());
113    version = version.substr(1, version.size()-2);      // strip quotes
114    version += string(" (" CPP_VERSION_DATE_STR ")");   // add date
115    return version;
116}
117
118///////////////////////////////////////////////////////////////////////////////
119// print the current version for interactive sessions
120int print_interactive_version()
121{
122    cout << "Wave: A Standard conformant C++ preprocessor based on the Boost.Wave library" << endl;
123    cout << "Version: " << get_version() << endl;
124    return 0;
125}
126
127///////////////////////////////////////////////////////////////////////////////
128// print the copyright statement
129int print_copyright()
130{
131    char const *copyright[] = {
132        "",
133        "Wave: A Standard conformant C++ preprocessor based on the Boost.Wave library",
134        "http://www.boost.org/",
135        "",
136        "Copyright (c) 2001-2007 Hartmut Kaiser, Distributed under the Boost",
137        "Software License, Version 1.0. (See accompanying file",
138        "LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)",
139        0
140    };
141   
142    for (int i = 0; 0 != copyright[i]; ++i)
143        cout << copyright[i] << endl;
144       
145    return 0;                       // exit app
146}
147
148///////////////////////////////////////////////////////////////////////////////
149// forward declarations only
150namespace cmd_line_utils
151{
152    class include_paths;
153}
154
155namespace boost { namespace program_options {
156
157    void validate(boost::any &v, std::vector<std::string> const &s,
158        cmd_line_utils::include_paths *, long);
159
160}} // boost::program_options
161
162///////////////////////////////////////////////////////////////////////////////
163#include <boost/program_options.hpp>
164
165namespace po = boost::program_options;
166namespace fs = boost::filesystem;
167
168///////////////////////////////////////////////////////////////////////////////
169namespace cmd_line_utils {
170
171    // Additional command line parser which interprets '@something' as an
172    // option "config-file" with the value "something".
173    inline pair<string, string> 
174    at_option_parser(string const&s)
175    {
176        if ('@' == s[0]) 
177            return std::make_pair(string("config-file"), s.substr(1));
178        else
179            return pair<string, string>();
180    }
181
182    // class, which keeps include file information read from the command line
183    class include_paths {
184    public:
185        include_paths() : seen_separator(false) {}
186
187        vector<string> paths;       // stores user paths
188        vector<string> syspaths;    // stores system paths
189        bool seen_separator;        // command line contains a '-I-' option
190
191        // Function which validates additional tokens from command line.
192        static void 
193        validate(boost::any &v, vector<string> const &tokens)
194        {
195            if (v.empty())
196                v = boost::any(include_paths());
197
198            include_paths *p = boost::any_cast<include_paths>(&v);
199
200            BOOST_ASSERT(p);
201            // Assume only one path per '-I' occurrence.
202            string const& t = po::validators::get_single_string(tokens);
203            if (t == "-") {
204            // found -I- option, so switch behaviour
205                p->seen_separator = true;
206            } 
207            else if (p->seen_separator) {
208            // store this path as a system path
209                p->syspaths.push_back(t); 
210            } 
211            else {
212            // store this path as an user path
213                p->paths.push_back(t);
214            }           
215        }
216    };
217
218    // Read all options from a given config file, parse and add them to the
219    // given variables_map
220    bool read_config_file_options(string const &filename, 
221        po::options_description const &desc, po::variables_map &vm,
222        bool may_fail = false)
223    {
224    ifstream ifs(filename.c_str());
225
226        if (!ifs.is_open()) {
227            if (!may_fail) {
228                cerr << filename
229                    << ": command line warning: config file not found"
230                    << endl;
231            }
232            return false;
233        }
234       
235    vector<string> options;
236    string line;
237
238        while (std::getline(ifs, line)) {
239        // skip empty lines
240            string::size_type pos = line.find_first_not_of(" \t");
241            if (pos == string::npos) 
242                continue;
243           
244        // skip comment lines
245            if ('#' != line[pos]) {
246            // strip leading and trailing whitespace
247                string::size_type endpos = line.find_last_not_of(" \t");
248                BOOST_ASSERT(endpos != string::npos);
249                options.push_back(line.substr(pos, endpos-pos+1));
250            }
251        }
252
253        if (options.size() > 0) {
254            using namespace boost::program_options::command_line_style;
255            po::store(po::command_line_parser(options)
256                .options(desc).style(unix_style).run(), vm);
257            po::notify(vm);
258        }
259        return true;
260    }
261
262    // predicate to extract all positional arguments from the command line
263    struct is_argument {
264        bool operator()(po::option const &opt)
265        {
266          return (opt.position_key == -1) ? true : false;
267        }
268    };
269
270    // trim quotes from path names, if any
271    std::string trim_quotes(std::string const& file)
272    {
273        if (('"' == file[0] || '\'' == file[0]) && file[0] == file[file.size()-1])
274        {
275            return file.substr(1, file.size()-2);
276        }
277        return file;
278    }
279   
280///////////////////////////////////////////////////////////////////////////////
281}
282
283///////////////////////////////////////////////////////////////////////////////
284//
285//  Special validator overload, which allows to handle the -I- syntax for
286//  switching the semantics of an -I option.
287//
288///////////////////////////////////////////////////////////////////////////////
289namespace boost { namespace program_options {
290
291    void validate(boost::any &v, std::vector<std::string> const &s,
292        cmd_line_utils::include_paths *, long) 
293    {
294        cmd_line_utils::include_paths::validate(v, s);
295    }
296
297}}  // namespace boost::program_options
298
299///////////////////////////////////////////////////////////////////////////////
300namespace {
301
302    class auto_stop_watch : public stop_watch
303    {
304    public:
305        auto_stop_watch(std::ostream &outstrm_)
306        :   print_time(false), outstrm(outstrm_)
307        {
308        }
309       
310        ~auto_stop_watch()
311        {
312            if (print_time) {
313                outstrm << "Elapsed time: " 
314                      << this->format_elapsed_time() 
315                      << std::endl;
316            }
317        }
318   
319        void set_print_time(bool print_time_)
320        {
321            print_time = print_time_;
322        }
323       
324    private:
325        bool print_time;
326        std::ostream &outstrm;
327    };
328
329    ///////////////////////////////////////////////////////////////////////////
330    inline std::string
331    report_iostate_error(std::ios::iostate state)
332    {
333        BOOST_ASSERT(state & (std::ios::badbit | std::ios::failbit | std::ios::eofbit));
334        std::string result;
335        if (state & std::ios::badbit) {
336            result += "      the reported problem was: "
337                      "loss of integrity of the stream buffer\n";
338        }
339        if (state & std::ios::failbit) {
340            result += "      the reported problem was: "
341                      "an operation was not processed correctly\n";
342        }
343        if (state & std::ios::eofbit) {
344            result += "      the reported problem was: "
345                      "end-of-file while writing to the stream\n";
346        }
347        return result;
348    }                           
349
350    ///////////////////////////////////////////////////////////////////////////
351    //  Retrieve the position of a macro definition
352    template <typename Context>
353    inline bool
354    get_macro_position(Context &ctx, 
355        typename Context::token_type::string_type const& name,
356        typename Context::position_type &pos)
357    {
358        bool has_parameters = false;
359        bool is_predefined = false;
360        std::vector<typename Context::token_type> parameters;
361        typename Context::token_sequence_type definition;
362       
363        return ctx.get_macro_definition(name, has_parameters, is_predefined, 
364            pos, parameters, definition);
365    }
366
367    ///////////////////////////////////////////////////////////////////////////
368    //  Generate some meaningful error messages
369    template <typename Exception>       
370    inline int 
371    report_error_message(Exception const &e)
372    {
373        // default error reporting
374        cerr
375            << e.file_name() << ":" << e.line_no() << ":" << e.column_no() 
376            << ": " << e.description() << endl;
377
378        // errors count as one
379        return (e.get_severity() == boost::wave::util::severity_error ||
380                e.get_severity() == boost::wave::util::severity_fatal) ? 1 : 0;
381    }
382
383    template <typename Context>
384    inline int 
385    report_error_message(Context &ctx, boost::wave::cpp_exception const &e)
386    {
387        // default error reporting
388        int result = report_error_message(e);
389
390        using boost::wave::preprocess_exception;
391        switch(e.get_errorcode()) {
392        case preprocess_exception::macro_redefinition:
393            {
394                // report the point of the initial macro definition
395                typename Context::position_type pos;
396                if (get_macro_position(ctx, e.get_related_name(), pos)) {
397                    cerr
398                        << pos << ": " 
399                        << preprocess_exception::severity_text(e.get_severity())
400                        << ": this is the location of the previous definition." 
401                        << endl;
402                }
403                else {
404                    cerr
405                        << e.file_name() << ":" << e.line_no() << ":" 
406                        << e.column_no() << ": " 
407                        << preprocess_exception::severity_text(e.get_severity())
408                        << ": not able to retrieve the location of the previous "
409                        << "definition." << endl;
410                }
411            }
412            break;
413
414        default:
415            break;
416        }
417
418        return result;
419    }
420   
421    ///////////////////////////////////////////////////////////////////////////
422    //  Read one logical line of text
423    inline bool 
424    read_a_line (std::istream &instream, std::string &instring)
425    {
426        bool eol = true;
427        do {
428            std::string line;
429            std::getline(instream, line);
430            if (instream.rdstate() & std::ios::failbit)
431                return false;       // nothing to do
432
433            eol = true;
434            if (line.find_last_of('\\') == line.size()-1) 
435                eol = false;
436
437            instring += line + '\n';
438        } while (!eol);
439        return true;
440    }
441
442    ///////////////////////////////////////////////////////////////////////////
443    //  Load and save the internal tables of the wave::context object
444    template <typename Context>
445    inline void 
446    load_state(po::variables_map const &vm, Context &ctx)
447    {
448#if BOOST_WAVE_SERIALIZATION != 0
449        try {
450            if (vm.count("state") > 0) {
451                fs::path state_file (vm["state"].as<string>(), fs::native);
452                if (state_file == "-") 
453                    state_file = fs::path("wave.state", fs::native);
454
455                std::ios::openmode mode = std::ios::in;
456
457#if BOOST_WAVE_BINARY_SERIALIZATION != 0
458                mode = (std::ios::openmode)(mode | std::ios::binary);
459#endif
460                ifstream ifs (state_file.string().c_str(), mode);
461                if (ifs.is_open()) {
462                    iarchive ia(ifs);
463                    string version;
464                   
465                    ia >> version;      // load version
466                    if (version == CPP_VERSION_FULL_STR)
467                        ia >> ctx;      // load the internal tables from disc
468                    else {
469                        cerr << "wave: detected version mismatch while loading state, state was not loaded." << endl;
470                        cerr << "      loaded version:   " << version << endl;
471                        cerr << "      expected version: " << CPP_VERSION_FULL_STR << endl;
472                    }
473                }
474            }
475        }
476        catch (boost::archive::archive_exception const& e) {
477            cerr << "wave: error while loading state: " 
478                 << e.what() << endl;
479        }
480        catch (boost::wave::preprocess_exception const& e) {
481            cerr << "wave: error while loading state: " 
482                 << e.description() << endl;
483        }
484#endif
485    }
486
487    template <typename Context>
488    inline void 
489    save_state(po::variables_map const &vm, Context const &ctx)
490    {
491#if BOOST_WAVE_SERIALIZATION != 0
492        try {
493            if (vm.count("state") > 0) {
494                fs::path state_file (vm["state"].as<string>(), fs::native);
495                if (state_file == "-") 
496                    state_file = fs::path("wave.state", fs::native);
497
498                std::ios::openmode mode = std::ios::out;
499
500#if BOOST_WAVE_BINARY_SERIALIZATION != 0
501                mode = (std::ios::openmode)(mode | std::ios::binary);
502#endif
503                ofstream ofs(state_file.string().c_str(), mode);
504                if (!ofs.is_open()) {
505                    cerr << "wave: could not open state file for writing: " 
506                         << state_file.string() << endl;
507                    // this is non-fatal
508                }
509                else {
510                    oarchive oa(ofs);
511                    string version(CPP_VERSION_FULL_STR);
512                    oa << version; // write version
513                    oa << ctx;                  // write the internal tables to disc
514                }
515            }
516        }
517        catch (boost::archive::archive_exception const& e) {
518            cerr << "wave: error while writing state: " 
519                 << e.what() << endl;
520        }
521#endif
522    }
523
524///////////////////////////////////////////////////////////////////////////////
525}   // anonymous namespace
526
527///////////////////////////////////////////////////////////////////////////////
528//  do the actual preprocessing
529int 
530do_actual_work (std::string file_name, std::istream &instream, 
531    po::variables_map const &vm, bool input_is_stdin)
532{
533// current file position is saved for exception handling
534boost::wave::util::file_position_type current_position;
535auto_stop_watch elapsed_time(cerr);
536int error_count = 0;
537
538    try {
539    // process the given file
540    string instring;
541
542        instream.unsetf(std::ios::skipws);
543
544        if (!input_is_stdin) {
545#if defined(BOOST_NO_TEMPLATED_ITERATOR_CONSTRUCTORS)
546            // this is known to be very slow for large files on some systems
547            copy (istream_iterator<char>(instream),
548                  istream_iterator<char>(), 
549                  inserter(instring, instring.end()));
550#else
551            instring = string(istreambuf_iterator<char>(instream.rdbuf()),
552                              istreambuf_iterator<char>());
553#endif
554        }
555       
556    // The preprocessing of the input stream is done on the fly behind the
557    // scenes during iteration over the context_type::iterator_type stream.
558    std::ofstream output;
559    std::ofstream traceout;
560    std::ofstream includelistout;
561   
562    trace_flags enable_trace = trace_nothing;
563   
564        if (vm.count("traceto")) {
565        // try to open the file, where to put the trace output
566        fs::path trace_file (vm["traceto"].as<string>(), fs::native);
567       
568            if (trace_file != "-") {
569                fs::create_directories(trace_file.branch_path());
570                traceout.open(trace_file.string().c_str());
571                if (!traceout.is_open()) {
572                    cerr << "wave: could not open trace file: " << trace_file
573                         << endl;
574                    return -1;
575                }
576            }
577            enable_trace = trace_macros;
578        }
579        if ((enable_trace & trace_macros) && !traceout.is_open()) {
580        // by default trace to std::cerr
581            traceout.copyfmt(cerr);
582            traceout.clear(cerr.rdstate());
583            static_cast<std::basic_ios<char> &>(traceout).rdbuf(cerr.rdbuf());
584        }
585
586    // Open the stream where to output the list of included file names
587        if (vm.count("listincludes")) {
588        // try to open the file, where to put the include list
589        fs::path includes_file(vm["listincludes"].as<string>(), fs::native);
590       
591            if (includes_file != "-") {
592                fs::create_directories(includes_file.branch_path());
593                includelistout.open(includes_file.string().c_str());
594                if (!includelistout.is_open()) {
595                    cerr << "wave: could not open include list file: " 
596                         << includes_file.string() << endl;
597                    return -1;
598                }
599            }
600            enable_trace = trace_includes;
601        }
602        if ((enable_trace & trace_includes) && !includelistout.is_open()) {
603        // by default list included names to std::cout
604            includelistout.copyfmt(cout);
605            includelistout.clear(cout.rdstate());
606            static_cast<std::basic_ios<char> &>(includelistout).
607                rdbuf(cout.rdbuf());
608        }
609       
610    // enable preserving comments mode
611    bool preserve_comments = false;
612    bool preserve_whitespace = false;
613   
614        if (vm.count("preserve")) {
615        int preserve = vm["preserve"].as<int>();
616       
617            switch(preserve) {
618            case 0:   break;
619            case 2:
620                preserve_whitespace = true;
621                /* fall through */
622            case 1:
623                preserve_comments = true;
624                break;
625               
626            default:
627                cerr << "wave: bogus preserve whitespace option value: " 
628                     << preserve << ", should be 0, 1, or 2" << endl;
629                return -1;
630            }
631        }
632
633    // Since the #pragma wave system() directive may cause a potential security
634    // threat, it has to be enabled explicitly by --extended or -x
635    bool enable_system_command = false;
636   
637        if (vm.count("extended")) 
638            enable_system_command = true;
639
640    // This this the central piece of the Wave library, it provides you with
641    // the iterators to get the preprocessed tokens and allows to configure
642    // the preprocessing stage in advance.
643    bool allow_output = true;   // will be manipulated from inside the hooks object
644    string default_outfile;     // will be used from inside the hooks object
645    trace_macro_expansion<token_type> hooks(preserve_whitespace, 
646        output, traceout, includelistout, enable_trace, enable_system_command,
647        allow_output, default_outfile);
648    context_type ctx (instring.begin(), instring.end(), file_name.c_str(), hooks);
649
650#if BOOST_WAVE_SUPPORT_VARIADICS_PLACEMARKERS != 0
651    // enable C99 mode, if appropriate (implies variadics)
652        if (vm.count("c99")) {
653            ctx.set_language(
654                boost::wave::language_support(
655                    boost::wave::support_c99
656                 |  boost::wave::support_option_convert_trigraphs
657                 |  boost::wave::support_option_emit_line_directives
658#if BOOST_WAVE_SUPPORT_PRAGMA_ONCE != 0
659                 |  boost::wave::support_option_include_guard_detection
660#endif
661                ));
662        }
663        else if (vm.count("variadics")) {
664        // enable variadics and placemarkers, if appropriate
665            ctx.set_language(boost::wave::enable_variadics(ctx.get_language()));
666        }
667#endif // BOOST_WAVE_SUPPORT_VARIADICS_PLACEMARKERS != 0
668
669    // enable long long support, if appropriate
670        if (vm.count("long_long")) {
671            ctx.set_language(
672                boost::wave::enable_long_long(ctx.get_language()));
673        }
674
675#if BOOST_WAVE_SUPPORT_PRAGMA_ONCE != 0
676// disable include guard detection
677        if (vm.count("noguard")) {
678            ctx.set_language(
679                boost::wave::enable_include_guard_detection(
680                    ctx.get_language(), false));
681        }
682#endif
683       
684    // enable preserving comments mode
685        if (preserve_comments) {
686            ctx.set_language(
687                boost::wave::enable_preserve_comments(ctx.get_language()));
688        }
689       
690    // control the generation of #line directives
691        if (vm.count("line")) {
692            int lineopt = vm["line"].as<int>();
693            if (0 != lineopt && 1 != lineopt) {
694                cerr << "wave: bogus value for --line command line option: " 
695                    << lineopt << endl;
696                return -1;
697            }
698            ctx.set_language(
699                boost::wave::enable_emit_line_directives(ctx.get_language(), 
700                    lineopt != 0));
701
702        }
703
704    // add include directories to the system include search paths
705        if (vm.count("sysinclude")) {
706        vector<string> syspaths = vm["sysinclude"].as<vector<string> >();
707       
708            vector<string>::const_iterator end = syspaths.end();
709            for (vector<string>::const_iterator cit = syspaths.begin(); 
710                 cit != end; ++cit)
711            {
712                ctx.add_sysinclude_path(cmd_line_utils::trim_quotes(*cit).c_str());
713            }
714        }
715       
716    // add include directories to the include search paths
717        if (vm.count("include")) {
718            cmd_line_utils::include_paths const &ip = 
719                vm["include"].as<cmd_line_utils::include_paths>();
720            vector<string>::const_iterator end = ip.paths.end();
721
722            for (vector<string>::const_iterator cit = ip.paths.begin(); 
723                 cit != end; ++cit)
724            {
725                ctx.add_include_path(cmd_line_utils::trim_quotes(*cit).c_str());
726            }
727
728        // if -I- was given on the command line, this has to be propagated
729            if (ip.seen_separator) 
730                ctx.set_sysinclude_delimiter();
731                 
732        // add system include directories to the include path
733            vector<string>::const_iterator sysend = ip.syspaths.end();
734            for (vector<string>::const_iterator syscit = ip.syspaths.begin(); 
735                 syscit != sysend; ++syscit)
736            {
737                ctx.add_sysinclude_path(cmd_line_utils::trim_quotes(*syscit).c_str());
738            }
739        }
740   
741    // add additional defined macros
742        if (vm.count("define")) {
743            vector<string> const &macros = vm["define"].as<vector<string> >();
744            vector<string>::const_iterator end = macros.end();
745            for (vector<string>::const_iterator cit = macros.begin(); 
746                 cit != end; ++cit)
747            {
748                ctx.add_macro_definition(*cit);
749            }
750        }
751
752    // add additional predefined macros
753        if (vm.count("predefine")) {
754            vector<string> const &predefmacros = 
755                vm["predefine"].as<vector<string> >();
756            vector<string>::const_iterator end = predefmacros.end();
757            for (vector<string>::const_iterator cit = predefmacros.begin(); 
758                 cit != end; ++cit)
759            {
760                ctx.add_macro_definition(*cit, true);
761            }
762        }
763
764    // undefine specified macros
765        if (vm.count("undefine")) {
766            vector<string> const &undefmacros = 
767                vm["undefine"].as<vector<string> >();
768            vector<string>::const_iterator end = undefmacros.end();
769            for (vector<string>::const_iterator cit = undefmacros.begin(); 
770                 cit != end; ++cit)
771            {
772                ctx.remove_macro_definition((*cit).c_str(), true);
773            }
774        }
775
776    // maximal include nesting depth
777        if (vm.count("nesting")) {
778            int max_depth = vm["nesting"].as<int>();
779            if (max_depth < 1 || max_depth > 100000) {
780                cerr << "wave: bogus maximal include nesting depth: " 
781                    << max_depth << endl;
782                return -1;
783            }
784            ctx.set_max_include_nesting_depth(max_depth);
785        }
786       
787    // open the output file
788        if (vm.count("output")) {
789        // try to open the file, where to put the preprocessed output
790        fs::path out_file (vm["output"].as<string>(), fs::native);
791
792            if (out_file == "-") {
793                allow_output = false;     // inhibit output initially
794                default_outfile = "-";
795            }
796            else {
797                out_file = fs::complete(out_file);
798                fs::create_directories(out_file.branch_path());
799                output.open(out_file.string().c_str());
800                if (!output.is_open()) {
801                    cerr << "wave: could not open output file: " 
802                         << out_file.string() << endl;
803                    return -1;
804                }
805                default_outfile = out_file.string();
806            }
807        }
808        else if (!input_is_stdin && vm.count("autooutput")) {
809        // generate output in the file <input_base_name>.i
810        fs::path out_file (file_name, fs::native);
811        std::string basename (out_file.leaf());
812        std::string::size_type pos = basename.find_last_of(".");
813       
814            if (std::string::npos != pos)
815                basename = basename.substr(0, pos);
816            out_file = out_file.branch_path() / (basename + ".i");
817
818            fs::create_directories(out_file.branch_path());
819            output.open(out_file.string().c_str());
820            if (!output.is_open()) {
821                cerr << "wave: could not open output file: " 
822                     << out_file.string() << endl;
823                return -1;
824            }
825            default_outfile = out_file.string();
826        }
827
828    //  we assume the session to be interactive if input is stdin and output is
829    //  stdout and the output is not inhibited
830    bool is_interactive = input_is_stdin && !output.is_open() && allow_output;
831   
832        if (is_interactive) {
833        // if interactive we don't warn for missing endif's etc.
834            ctx.set_language(
835                boost::wave::enable_single_line(ctx.get_language()));
836        }
837       
838    // analyze the input file
839    context_type::iterator_type first = ctx.begin();
840    context_type::iterator_type last = ctx.end();
841   
842    // preprocess the required include files
843        if (vm.count("forceinclude")) {
844        // add the filenames to force as include files in _reverse_ order
845        // the second parameter 'is_last' of the force_include function should
846        // be set to true for the last (first given) file.
847            vector<string> const &force = 
848                vm["forceinclude"].as<vector<string> >();
849            vector<string>::const_reverse_iterator rend = force.rend();
850            for (vector<string>::const_reverse_iterator cit = force.rbegin(); 
851                 cit != rend; /**/)
852            {
853                string filename(*cit);
854                first.force_include(filename.c_str(), ++cit == rend);
855            }
856        }
857
858        elapsed_time.set_print_time(!input_is_stdin && vm.count("timer") > 0);
859        if (is_interactive) {
860            print_interactive_version();  // print welcome message
861            load_state(vm, ctx);          // load the internal tables from disc
862        }
863        else if (vm.count("state")) {
864        // the option "state" is usable in interactive mode only
865            cerr << "wave: ignoring the command line option 'state', "
866                 << "use it in interactive mode only." << endl;
867        }
868       
869    // >>>>>>>>>>>>> The actual preprocessing happens here. <<<<<<<<<<<<<<<<<<<
870    // loop over the input lines if reading from stdin, otherwise this loop
871    // will be executed once
872        do {
873        // loop over all generated tokens outputting the generated text
874        bool finished = false;
875       
876            if (input_is_stdin) {
877                if (is_interactive) 
878                    cout << ">>> ";     // prompt if is interactive
879
880            // read next line and continue
881                instring.clear();
882                if (!read_a_line(instream, instring))
883                    break;        // end of input reached
884                first = ctx.begin(instring.begin(), instring.end());
885            }
886           
887            do {
888                try {
889                    while (first != last) {
890                    // store the last known good token position
891                        current_position = (*first).get_position();
892
893                    // print out the current token value
894                        if (allow_output) {
895                            if (output.rdstate() & (std::ios::badbit | std::ios::failbit | std::ios::eofbit))
896                            {
897                                cerr << "wave: problem writing to the current "
898                                     << "output file" << endl;
899                                cerr << report_iostate_error(output.rdstate());
900                                break;
901                            }
902                            if (output.is_open())
903                                output << (*first).get_value();
904                            else
905                                cout << (*first).get_value();
906                        }
907                       
908                    // advance to the next token
909                        ++first;
910                    }
911                    finished = true;
912                }
913                catch (boost::wave::cpp_exception const &e) {
914                // some preprocessing error
915                    if (is_interactive || boost::wave::is_recoverable(e)) {
916                        error_count += report_error_message(ctx, e);
917                    }
918                    else {
919                        throw;      // re-throw for non-recoverable errors
920                    }
921                }
922                catch (boost::wave::cpplexer::lexing_exception const &e) {
923                // some preprocessing error
924                    if (is_interactive || 
925                        boost::wave::cpplexer::is_recoverable(e)) 
926                    {
927                        error_count += report_error_message(e);
928                    }
929                    else {
930                        throw;      // re-throw for non-recoverable errors
931                    }
932                }
933            } while (!finished);
934        } while (input_is_stdin);
935
936        if (is_interactive) 
937            save_state(vm, ctx);    // write the internal tables to disc
938    }
939    catch (boost::wave::cpp_exception const &e) {
940    // some preprocessing error
941        report_error_message(e);
942        return 1;
943    }
944    catch (boost::wave::cpplexer::lexing_exception const &e) {
945    // some lexing error
946        report_error_message(e);
947        return 2;
948    }
949    catch (std::exception const &e) {
950    // use last recognized token to retrieve the error position
951        cerr
952            << current_position << ": "
953            << "exception caught: " << e.what()
954            << endl;
955        return 3;
956    }
957    catch (...) {
958    // use last recognized token to retrieve the error position
959        cerr
960            << current_position << ": "
961            << "unexpected exception caught." << endl;
962        return 4;
963    }
964    return -error_count;  // returns the number of errors as a negative integer
965}
966
967///////////////////////////////////////////////////////////////////////////////
968//  main entry point
969int
970main (int argc, char *argv[])
971{
972    // test Wave compilation configuration
973    if (!BOOST_WAVE_TEST_CONFIGURATION()) {
974        cout << "wave: warning: the library this application was linked against was compiled "
975             << endl
976             << "               using a different configuration (see wave_config.hpp)."
977             << endl;
978    }
979   
980    // analyze the command line options and arguments
981    try {
982    // declare the options allowed on the command line only
983    po::options_description desc_cmdline ("Options allowed on the command line only");
984       
985        desc_cmdline.add_options()
986            ("help,h", "print out program usage (this message)")
987            ("version,v", "print the version number")
988            ("copyright,c", "print out the copyright statement")
989            ("config-file", po::value<vector<string> >()->composing(), 
990                "specify a config file (alternatively: @filepath)")
991        ;
992
993    // declare the options allowed on command line and in config files
994    po::options_description desc_generic ("Options allowed additionally in a config file");
995
996        desc_generic.add_options()
997            ("output,o", po::value<string>(), 
998                "specify a file [arg] to use for output instead of stdout or "
999                "disable output [-]")
1000            ("autooutput,E", 
1001                "output goes into a file named <input_basename>.i")
1002            ("include,I", po::value<cmd_line_utils::include_paths>()->composing(), 
1003                "specify an additional include directory")
1004            ("sysinclude,S", po::value<vector<string> >()->composing(), 
1005                "specify an additional system include directory")
1006            ("forceinclude,F", po::value<vector<string> >()->composing(),
1007                "force inclusion of the given file")
1008            ("define,D", po::value<vector<string> >()->composing(), 
1009                "specify a macro to define (as macro[=[value]])")
1010            ("predefine,P", po::value<vector<string> >()->composing(), 
1011                "specify a macro to predefine (as macro[=[value]])")
1012            ("undefine,U", po::value<vector<string> >()->composing(), 
1013                "specify a macro to undefine")
1014            ("nesting,n", po::value<int>(), 
1015                "specify a new maximal include nesting depth")
1016        ;
1017       
1018    po::options_description desc_ext ("Extended options (allowed everywhere)");
1019
1020        desc_ext.add_options()
1021            ("traceto,t", po::value<string>(), 
1022                "output macro expansion tracing information to a file [arg] "
1023                "or to stderr [-]")
1024            ("timer", "output overall elapsed computing time to stderr")
1025            ("long_long", "enable long long support in C++ mode")
1026#if BOOST_WAVE_SUPPORT_VARIADICS_PLACEMARKERS != 0
1027            ("variadics", "enable certain C99 extensions in C++ mode")
1028            ("c99", "enable C99 mode (implies --variadics)")
1029#endif
1030            ("listincludes,l", po::value<string>(), 
1031                "list names of included files to a file [arg] or to stdout [-]")
1032            ("preserve,p", po::value<int>()->default_value(0), 
1033                "preserve whitespace\n"
1034                            "0: no whitespace is preserved (default),\n"
1035                            "1: comments are preserved,\n" 
1036                            "2: all whitespace is preserved")
1037            ("line,L", po::value<int>()->default_value(1), 
1038                "control the generation of #line directives\n"
1039                            "0: no #line directives are generated,\n"
1040                            "1: #line directives will be emitted (default)")
1041            ("extended,x", "enable the #pragma wave system() directive")
1042#if BOOST_WAVE_SUPPORT_PRAGMA_ONCE != 0
1043            ("noguard,G", "disable include guard detection")
1044#endif
1045#if BOOST_WAVE_SERIALIZATION != 0
1046            ("state,s", po::value<string>(), 
1047                "load and save state information from/to the given file [arg] "
1048                "or 'wave.state' [-] (interactive mode only)")
1049#endif
1050        ;
1051   
1052    // combine the options for the different usage schemes
1053    po::options_description desc_overall_cmdline;
1054    po::options_description desc_overall_cfgfile;
1055
1056        desc_overall_cmdline.add(desc_cmdline).add(desc_generic).add(desc_ext);
1057        desc_overall_cfgfile.add(desc_generic).add(desc_ext);
1058       
1059    // parse command line and store results
1060        using namespace boost::program_options::command_line_style;
1061
1062    po::parsed_options opts(po::parse_command_line(argc, argv, 
1063            desc_overall_cmdline, unix_style, cmd_line_utils::at_option_parser));
1064    po::variables_map vm;
1065   
1066        po::store(opts, vm);
1067        po::notify(vm);
1068
1069//     // Try to find a wave.cfg in the same directory as the executable was
1070//     // started from. If this exists, treat it as a wave config file
1071//     fs::path filename(argv[0], fs::native);
1072//
1073//         filename = filename.branch_path() / "wave.cfg";
1074//         cmd_line_utils::read_config_file_options(filename.string(),
1075//             desc_overall_cfgfile, vm, true);
1076
1077    // extract the arguments from the parsed command line
1078    vector<po::option> arguments;
1079   
1080        std::remove_copy_if(opts.options.begin(), opts.options.end(), 
1081            back_inserter(arguments), cmd_line_utils::is_argument());
1082           
1083    // try to find a config file somewhere up the filesystem hierarchy
1084    // starting with the input file path. This allows to use a general wave.cfg
1085    // file for all files in a certain project.
1086        if (arguments.size() > 0 && arguments[0].value[0] != "-") {
1087        // construct full path of input file
1088            fs::path input_dir (fs::complete(fs::path(arguments[0].value[0], fs::native)));
1089            input_dir = input_dir.normalize().branch_path();    // chop of file name
1090
1091        // walk up the hierarchy, trying to find a file wave.cfg
1092            while (!input_dir.empty()) {
1093                fs::path filename = input_dir / "wave.cfg";
1094                if (cmd_line_utils::read_config_file_options(filename.string(), 
1095                    desc_overall_cfgfile, vm, true))
1096                {
1097                    break;    // break on the first cfg file found
1098                }
1099                input_dir = input_dir.branch_path();
1100            }
1101        }
1102       
1103    // if there is specified at least one config file, parse it and add the
1104    // options to the main variables_map
1105        if (vm.count("config-file")) {
1106            vector<string> const &cfg_files = 
1107                vm["config-file"].as<vector<string> >();
1108            vector<string>::const_iterator end = cfg_files.end();
1109            for (vector<string>::const_iterator cit = cfg_files.begin(); 
1110                 cit != end; ++cit)
1111            {
1112            // parse a single config file and store the results
1113                cmd_line_utils::read_config_file_options(*cit, 
1114                    desc_overall_cfgfile, vm);
1115            }
1116        }
1117
1118    // ... act as required
1119        if (vm.count("help")) {
1120        po::options_description desc_help (
1121            "Usage: wave [options] [@config-file(s)] [file]");
1122
1123            desc_help.add(desc_cmdline).add(desc_generic).add(desc_ext);
1124            cout << desc_help << endl;
1125            return 1;
1126        }
1127       
1128        if (vm.count("version")) {
1129            cout << get_version() << endl;
1130            return 0;
1131        }
1132
1133        if (vm.count("copyright")) {
1134            return print_copyright();
1135        }
1136
1137    // if there is no input file given, then take input from stdin
1138        if (0 == arguments.size() || 0 == arguments[0].value.size() ||
1139            arguments[0].value[0] == "-") 
1140        {
1141        // preprocess the given input from stdin
1142            return do_actual_work("<stdin>", std::cin, vm, true);
1143        }
1144        else {
1145            if (arguments.size() > 1) {
1146            // this driver understands to parse one input file only
1147                cerr << "wave: more than one input file specified, "
1148                     << "ignoring all but the first!" << endl;
1149            }
1150
1151        std::string file_name(arguments[0].value[0]);
1152        ifstream instream(file_name.c_str());
1153
1154        // preprocess the given input file
1155            if (!instream.is_open()) {
1156                cerr << "wave: could not open input file: " << file_name << endl;
1157                return -1;
1158            }
1159            return do_actual_work(file_name, instream, vm, false);
1160        }
1161    }
1162    catch (std::exception const &e) {
1163        cout << "wave: exception caught: " << e.what() << endl;
1164        return 6;
1165    }
1166    catch (...) {
1167        cerr << "wave: unexpected exception caught." << endl;
1168        return 7;
1169    }
1170}
1171
Note: See TracBrowser for help on using the repository browser.