Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: downloads/boost_1_33_1/tools/wave/cpp.cpp @ 12

Last change on this file since 12 was 12, checked in by landauf, 18 years ago

added boost

File size: 25.7 KB
Line 
1/*=============================================================================
2    Boost.Wave: A Standard compliant C++ preprocessor library
3
4    http://www.boost.org/
5
6    Copyright (c) 2001-2005 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#include "cpp.hpp"                  // global configuration
12
13///////////////////////////////////////////////////////////////////////////////
14// Include additional Boost libraries
15#include <boost/filesystem/path.hpp>
16#include <boost/timer.hpp>
17#include <boost/any.hpp>
18
19///////////////////////////////////////////////////////////////////////////////
20//  Include Wave itself
21#include <boost/wave.hpp>
22
23///////////////////////////////////////////////////////////////////////////////
24//  Include the lexer related stuff
25#include <boost/wave/cpplexer/cpp_lex_token.hpp>      // token type
26#include <boost/wave/cpplexer/cpp_lex_iterator.hpp>   // lexer type
27
28///////////////////////////////////////////////////////////////////////////////
29//  Include the context trace policies to use
30#include "trace_macro_expansion.hpp"
31
32///////////////////////////////////////////////////////////////////////////////
33//  include lexer specifics, import lexer names
34//
35#if BOOST_WAVE_SEPARATE_LEXER_INSTANTIATION == 0
36#include <boost/wave/cpplexer/re2clex/cpp_re2c_lexer.hpp>
37#endif
38
39///////////////////////////////////////////////////////////////////////////////
40//  include the grammar definitions, if these shouldn't be compiled separately
41//  (ATTENTION: _very_ large compilation times!)
42//
43#if BOOST_WAVE_SEPARATE_GRAMMAR_INSTANTIATION == 0
44#include <boost/wave/grammars/cpp_intlit_grammar.hpp>
45#include <boost/wave/grammars/cpp_chlit_grammar.hpp>
46#include <boost/wave/grammars/cpp_grammar.hpp>
47#include <boost/wave/grammars/cpp_expression_grammar.hpp>
48#include <boost/wave/grammars/cpp_predef_macros_grammar.hpp>
49#include <boost/wave/grammars/cpp_defined_grammar.hpp>
50#endif
51
52///////////////////////////////////////////////////////////////////////////////
53//  import required names
54using namespace boost::spirit;
55
56using std::string;
57using std::pair;
58using std::vector;
59using std::getline;
60using std::ifstream;
61using std::cout;
62using std::cerr;
63using std::endl;
64using std::ostream;
65using std::istreambuf_iterator;
66
67///////////////////////////////////////////////////////////////////////////////
68// print the current version
69int print_version()
70{
71    typedef boost::wave::cpplexer::lex_iterator<
72            boost::wave::cpplexer::lex_token<> >
73        lex_iterator_type;
74    typedef boost::wave::context<
75            std::string::iterator, lex_iterator_type,
76            boost::wave::iteration_context_policies::load_file_to_string,
77            trace_macro_expansion> 
78        context_type;
79       
80    string version (context_type::get_version_string());
81    cout
82        << version.substr(1, version.size()-2)  // strip quotes
83        << " (" << CPP_VERSION_DATE << ")"      // add date
84        << endl;
85    return 0;                       // exit app
86}
87
88///////////////////////////////////////////////////////////////////////////////
89// print the copyright statement
90int print_copyright()
91{
92    char const *copyright[] = {
93        "",
94        "Wave: A Standard conformant C++ preprocessor based on the Boost.Wave library",
95        "http://www.boost.org/",
96        "",
97        "Copyright (c) 2001-2005 Hartmut Kaiser, Distributed under the Boost",
98        "Software License, Version 1.0. (See accompanying file",
99        "LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)",
100        0
101    };
102   
103    for (int i = 0; 0 != copyright[i]; ++i)
104        cout << copyright[i] << endl;
105       
106    return 0;                       // exit app
107}
108
109///////////////////////////////////////////////////////////////////////////////
110// forward declarations only
111namespace cmd_line_utils
112{
113    class include_paths;
114}
115
116namespace boost { namespace program_options
117{
118    void validate(boost::any &v, std::vector<std::string> const &s,
119        cmd_line_utils::include_paths *, int);
120}} // boost::program_options
121
122///////////////////////////////////////////////////////////////////////////////
123#include <boost/program_options.hpp>
124
125namespace po = boost::program_options;
126namespace fs = boost::filesystem;
127
128///////////////////////////////////////////////////////////////////////////////
129namespace cmd_line_util {
130
131    // Additional command line parser which interprets '@something' as an
132    // option "config-file" with the value "something".
133    inline pair<string, string> 
134    at_option_parser(string const&s)
135    {
136        if ('@' == s[0]) 
137            return std::make_pair(string("config-file"), s.substr(1));
138        else
139            return pair<string, string>();
140    }
141
142    // class, which keeps include file information read from the command line
143    class include_paths {
144    public:
145        include_paths() : seen_separator(false) {}
146
147        vector<string> paths;       // stores user paths
148        vector<string> syspaths;    // stores system paths
149        bool seen_separator;        // command line contains a '-I-' option
150
151        // Function which validates additional tokens from command line.
152        static void 
153        validate(boost::any &v, vector<string> const &tokens)
154        {
155            if (v.empty())
156                v = boost::any(include_paths());
157
158            include_paths *p = boost::any_cast<include_paths>(&v);
159
160            BOOST_ASSERT(p);
161            // Assume only one path per '-I' occurrence.
162            string const& t = po::validators::get_single_string(tokens);
163            if (t == "-") {
164            // found -I- option, so switch behaviour
165                p->seen_separator = true;
166            } 
167            else if (p->seen_separator) {
168            // store this path as a system path
169                p->syspaths.push_back(t); 
170            } 
171            else {
172            // store this path as an user path
173                p->paths.push_back(t);
174            }           
175        }
176    };
177
178    // Read all options from a given config file, parse and add them to the
179    // given variables_map
180    void read_config_file_options(string const &filename, 
181        po::options_description const &desc, po::variables_map &vm,
182        bool may_fail = false)
183    {
184    ifstream ifs(filename.c_str());
185
186        if (!ifs.is_open()) {
187            if (!may_fail) {
188                cerr << filename
189                    << ": command line warning: config file not found"
190                    << endl;
191            }
192            return;
193        }
194       
195    vector<string> options;
196    string line;
197
198        while (std::getline(ifs, line)) {
199        // skip empty lines
200            string::size_type pos = line.find_first_not_of(" \t");
201            if (pos == string::npos) 
202                continue;
203
204        // skip comment lines
205            if ('#' != line[pos])
206                options.push_back(line);
207        }
208
209        if (options.size() > 0) {
210            using namespace boost::program_options::command_line_style;
211            po::store(po::command_line_parser(options)
212                .options(desc).style(unix_style).run(), vm);
213            po::notify(vm);
214        }
215    }
216
217    // predicate to extract all positional arguments from the command line
218    struct is_argument {
219        bool operator()(po::option const &opt)
220        {
221          return (opt.position_key == -1) ? true : false;
222        }
223    };
224
225///////////////////////////////////////////////////////////////////////////////
226}
227
228///////////////////////////////////////////////////////////////////////////////
229//
230//  Special validator overload, which allows to handle the -I- syntax for
231//  switching the semantics of an -I option.
232//
233///////////////////////////////////////////////////////////////////////////////
234namespace boost { namespace program_options {
235
236    void validate(boost::any &v, std::vector<std::string> const &s,
237        cmd_line_util::include_paths *, int) 
238    {
239        cmd_line_util::include_paths::validate(v, s);
240    }
241
242}}  // namespace boost::program_options
243
244///////////////////////////////////////////////////////////////////////////////
245namespace {
246
247  class auto_stop_watch : public stop_watch
248  {
249  public:
250      auto_stop_watch(bool print_time_, std::ostream &outstrm_)
251      :   print_time(print_time_), outstrm(outstrm_)
252      {
253      }
254     
255      ~auto_stop_watch()
256      {
257          if (print_time) {
258              outstrm << "Elapsed time: " 
259                    << this->format_elapsed_time() 
260                    << std::endl;
261          }
262      }
263 
264  private:
265      bool print_time;
266      std::ostream &outstrm;
267  };
268 
269///////////////////////////////////////////////////////////////////////////////
270}   // anonymous namespace
271
272///////////////////////////////////////////////////////////////////////////////
273//  do the actual preprocessing
274int 
275do_actual_work (std::string file_name, std::istream &instream, 
276    po::variables_map const &vm)
277{
278// current file position is saved for exception handling
279boost::wave::util::file_position_type current_position;
280
281    try {
282    // process the given file
283    string instring;
284
285        instream.unsetf(std::ios::skipws);
286
287#if defined(BOOST_NO_TEMPLATED_ITERATOR_CONSTRUCTORS)
288        // this is known to be very slow for large files on some systems
289        copy (istream_iterator<char>(instream),
290              istream_iterator<char>(), 
291              inserter(instring, instring.end()));
292#else
293        instring = string(istreambuf_iterator<char>(instream.rdbuf()),
294                          istreambuf_iterator<char>());
295#endif
296
297    //  This application uses the lex_iterator and lex_token types predefined
298    //  with the Wave library, but it is possible to use your own types.
299    //
300    //  You may want to have a look at the other samples to see how this is
301    //  possible to achieve.
302        typedef boost::wave::cpplexer::lex_iterator<
303                boost::wave::cpplexer::lex_token<> >
304            lex_iterator_type;
305
306    // The C++ preprocessor iterators shouldn't be constructed directly. They
307    // are to be generated through a boost::wave::context<> object. This
308    // boost::wave::context object is additionally to be used to initialize and
309    // define different parameters of the actual preprocessing.
310        typedef boost::wave::context<
311                std::string::iterator, lex_iterator_type,
312                boost::wave::iteration_context_policies::load_file_to_string,
313                trace_macro_expansion> 
314            context_type;
315
316    // The preprocessing of the input stream is done on the fly behind the
317    // scenes during iteration over the context_type::iterator_type stream.
318    std::ofstream traceout;
319    std::ofstream includelistout;
320    trace_flags enable_trace = trace_nothing;
321   
322        if (vm.count("traceto")) {
323        // try to open the file, where to put the trace output
324        string trace_file (vm["traceto"].as<string>());
325       
326            if (trace_file != "-") {
327                traceout.open(trace_file.c_str());
328                if (!traceout.is_open()) {
329                    cerr << "wave: could not open trace file: " << trace_file
330                        << endl;
331                    return -1;
332                }
333            }
334            enable_trace = trace_macros;
335        }
336        if ((enable_trace & trace_macros) && !traceout.is_open()) {
337        // by default trace to std::cerr
338            traceout.copyfmt(cerr);
339            traceout.clear(cerr.rdstate());
340            static_cast<std::basic_ios<char> &>(traceout).rdbuf(cerr.rdbuf());
341        }
342
343    // Open the stream where to output the list of included file names
344        if (vm.count("listincludes")) {
345        // try to open the file, where to put the include list
346        string includes_file (vm["listincludes"].as<string>());
347       
348            if (includes_file != "-") {
349                includelistout.open(includes_file.c_str());
350                if (!includelistout.is_open()) {
351                    cerr << "wave: could not open include list file: " 
352                        << includes_file << endl;
353                    return -1;
354                }
355            }
356            enable_trace = trace_includes;
357        }
358        if ((enable_trace & trace_includes) && !includelistout.is_open()) {
359        // by default list included names to std::cout
360            includelistout.copyfmt(cout);
361            includelistout.clear(cout.rdstate());
362            static_cast<std::basic_ios<char> &>(includelistout).
363                rdbuf(cout.rdbuf());
364        }
365       
366    // This this the central piece of the Wave library, it provides you with
367    // the iterators to get the preprocessed tokens and allows to configure
368    // the preprocessing stage in advance.
369    context_type ctx (instring.begin(), instring.end(), file_name.c_str(),
370        trace_macro_expansion(traceout, includelistout, enable_trace));
371
372#if BOOST_WAVE_SUPPORT_VARIADICS_PLACEMARKERS != 0
373    // enable C99 mode, if appropriate (implies variadics)
374        if (vm.count("c99")) {
375            ctx.set_language(boost::wave::support_c99);
376        }
377        else if (vm.count("variadics")) {
378        // enable variadics and placemarkers, if appropriate
379            ctx.set_language(boost::wave::enable_variadics(ctx.get_language()));
380        }
381#endif // BOOST_WAVE_SUPPORT_VARIADICS_PLACEMARKERS != 0
382
383    // enable long long support, if appropriate
384         if (vm.count("long_long")) {
385             ctx.set_language(
386                boost::wave::enable_long_long(ctx.get_language()));
387         }
388
389    // enable preserving comments mode
390        if (vm.count("preserve")) {
391            ctx.set_language(
392                boost::wave::enable_preserve_comments(ctx.get_language()));
393        }
394       
395    // add include directories to the system include search paths
396        if (vm.count("sysinclude")) {
397        vector<string> syspaths = vm["sysinclude"].as<vector<string> >();
398       
399            vector<string>::const_iterator end = syspaths.end();
400            for (vector<string>::const_iterator cit = syspaths.begin(); 
401                 cit != end; ++cit)
402            {
403                ctx.add_sysinclude_path((*cit).c_str());
404            }
405        }
406       
407    // add include directories to the include search paths
408        if (vm.count("include")) {
409            cmd_line_util::include_paths const &ip = 
410                vm["include"].as<cmd_line_util::include_paths>();
411            vector<string>::const_iterator end = ip.paths.end();
412
413            for (vector<string>::const_iterator cit = ip.paths.begin(); 
414                 cit != end; ++cit)
415            {
416                ctx.add_include_path((*cit).c_str());
417            }
418
419        // if on the command line was given -I- , this has to be propagated
420            if (ip.seen_separator) 
421                ctx.set_sysinclude_delimiter();
422                 
423        // add system include directories to the include path
424            vector<string>::const_iterator sysend = ip.syspaths.end();
425            for (vector<string>::const_iterator syscit = ip.syspaths.begin(); 
426                 syscit != sysend; ++syscit)
427            {
428                ctx.add_sysinclude_path((*syscit).c_str());
429            }
430        }
431   
432    // add additional defined macros
433        if (vm.count("define")) {
434            vector<string> const &macros = vm["define"].as<vector<string> >();
435            vector<string>::const_iterator end = macros.end();
436            for (vector<string>::const_iterator cit = macros.begin(); 
437                 cit != end; ++cit)
438            {
439                ctx.add_macro_definition(*cit);
440            }
441        }
442
443    // add additional predefined macros
444        if (vm.count("predefine")) {
445            vector<string> const &predefmacros = 
446                vm["predefine"].as<vector<string> >();
447            vector<string>::const_iterator end = predefmacros.end();
448            for (vector<string>::const_iterator cit = predefmacros.begin(); 
449                 cit != end; ++cit)
450            {
451                ctx.add_macro_definition(*cit, true);
452            }
453        }
454
455    // undefine specified macros
456        if (vm.count("undefine")) {
457            vector<string> const &undefmacros = 
458                vm["undefine"].as<vector<string> >();
459            vector<string>::const_iterator end = undefmacros.end();
460            for (vector<string>::const_iterator cit = undefmacros.begin(); 
461                 cit != end; ++cit)
462            {
463                ctx.remove_macro_definition((*cit).c_str(), true);
464            }
465        }
466
467    // maximal include nesting depth
468        if (vm.count("nesting")) {
469            int max_depth = vm["nesting"].as<int>();
470            if (max_depth < 1 || max_depth > 100000) {
471                cerr << "wave: bogus maximal include nesting depth: " 
472                    << max_depth << endl;
473                return -1;
474            }
475            ctx.set_max_include_nesting_depth(max_depth);
476        }
477       
478    // open the output file
479    std::ofstream output;
480   
481        if (vm.count("output")) {
482        // try to open the file, where to put the preprocessed output
483        string out_file (vm["output"].as<string>());
484       
485            output.open(out_file.c_str());
486            if (!output.is_open()) {
487                cerr << "wave: could not open output file: " << out_file << endl;
488                return -1;
489            }
490        }
491        else {
492        // output the preprocessed result to std::cout
493            output.copyfmt(cout);
494            output.clear(cout.rdstate());
495            static_cast<std::basic_ios<char> &>(output).rdbuf(cout.rdbuf());
496        }
497
498    // analyze the input file
499    auto_stop_watch elapsed_time(vm.count("timer") > 0, cout);
500    context_type::iterator_type first = ctx.begin();
501    context_type::iterator_type last = ctx.end();
502   
503    // preprocess the required include files
504        if (vm.count("forceinclude")) {
505        // add the filenames to force as include files in _reverse_ order
506        // the second parameter 'is_last' of the force_include function should
507        // be set to true for the last (first given) file.
508            vector<string> const &force = 
509                vm["forceinclude"].as<vector<string> >();
510            vector<string>::const_reverse_iterator rend = force.rend();
511            for (vector<string>::const_reverse_iterator cit = force.rbegin(); 
512                 cit != rend; /**/)
513            {
514                string filename(*cit);
515                first.force_include(filename.c_str(), ++cit == rend);
516            }
517        }
518
519    // >>>>>>>>>>>>> Here the actual preprocessing happens. <<<<<<<<<<<<<<<<<<<
520    // loop over all generated tokens outputting the generated text
521        while (first != last) {
522        // store the last known good token position
523            current_position = (*first).get_position();
524
525        // print out the current token value
526            output << (*first).get_value();
527
528        // advance to the next token
529            ++first;
530        }
531    }
532    catch (boost::wave::cpp_exception &e) {
533    // some preprocessing error
534        cerr
535            << e.file_name() << "(" << e.line_no() << "): "
536            << e.description() << endl;
537        return 1;
538    }
539    catch (boost::wave::cpplexer::lexing_exception &e) {
540    // some lexing error
541        cerr
542            << e.file_name() << "(" << e.line_no() << "): "
543            << e.description() << endl;
544        return 2;
545    }
546    catch (std::exception &e) {
547    // use last recognized token to retrieve the error position
548        cerr
549            << current_position.get_file() 
550            << "(" << current_position.get_line() << "): "
551            << "exception caught: " << e.what()
552            << endl;
553        return 3;
554    }
555    catch (...) {
556    // use last recognized token to retrieve the error position
557        cerr
558            << current_position.get_file() 
559            << "(" << current_position.get_line() << "): "
560            << "unexpected exception caught." << endl;
561        return 4;
562    }
563    return 0;
564}
565
566///////////////////////////////////////////////////////////////////////////////
567//  main entry point
568int
569main (int argc, char *argv[])
570{
571    try {
572    // analyze the command line options and arguments
573   
574    // declare the options allowed from the command line only
575    po::options_description desc_cmdline ("Options allowed on the command line only");
576       
577        desc_cmdline.add_options()
578            ("help,h", "print out program usage (this message)")
579            ("version,v", "print the version number")
580            ("copyright,c", "print out the copyright statement")
581            ("config-file", po::value<vector<string> >()->composing(), 
582                "specify a config file (alternatively: @filepath)")
583        ;
584
585    // declare the options allowed on command line and in config files
586    po::options_description desc_generic ("Options allowed additionally in a config file");
587
588        desc_generic.add_options()
589            ("output,o", po::value<string>(), 
590                "specify a file to use for output instead of stdout")
591            ("include,I", po::value<cmd_line_util::include_paths>()->composing(), 
592                "specify an additional include directory")
593            ("sysinclude,S", po::value<vector<string> >()->composing(), 
594                "specify an additional system include directory")
595            ("forceinclude,F", po::value<vector<string> >()->composing(),
596                "force inclusion of the given file")
597            ("define,D", po::value<vector<string> >()->composing(), 
598                "specify a macro to define (as macro[=[value]])")
599            ("predefine,P", po::value<vector<string> >()->composing(), 
600                "specify a macro to predefine (as macro[=[value]])")
601            ("undefine,U", po::value<vector<string> >()->composing(), 
602                "specify a macro to undefine")
603            ("nesting,n", po::value<int>(), 
604                "specify a new maximal include nesting depth")
605        ;
606       
607    po::options_description desc_ext ("Extended options (allowed everywhere)");
608
609        desc_ext.add_options()
610            ("traceto,t", po::value<string>(), 
611                "output trace info to a file [arg] or to stderr [-]")
612            ("timer", "output overall elapsed computing time to stderr")
613            ("long_long", "enable long long support in C++ mode")
614#if BOOST_WAVE_SUPPORT_VARIADICS_PLACEMARKERS != 0
615            ("variadics", "enable certain C99 extensions in C++ mode")
616            ("c99", "enable C99 mode (implies --variadics)")
617#endif
618            ("listincludes,l", po::value<string>(), 
619                "list included file to a file [arg] or to stdout [-]")
620            ("preserve,p", "preserve comments")
621        ;
622   
623    // combine the options for the different usage schemes
624    po::options_description desc_overall_cmdline;
625    po::options_description desc_overall_cfgfile;
626
627        desc_overall_cmdline.add(desc_cmdline).add(desc_generic).add(desc_ext);
628        desc_overall_cfgfile.add(desc_generic).add(desc_ext);
629       
630    // parse command line and store results
631        using namespace boost::program_options::command_line_style;
632
633    po::parsed_options opts(po::parse_command_line(argc, argv, 
634            desc_overall_cmdline, unix_style, cmd_line_util::at_option_parser));
635    po::variables_map vm;
636   
637        po::store(opts, vm);
638        po::notify(vm);
639
640    // Try to find a wave.cfg in the same directory as the executable was
641    // started from. If this exists, treat it as a wave config file
642    fs::path filename(argv[0], fs::native);
643
644        filename = filename.branch_path() / "wave.cfg";
645        cmd_line_util::read_config_file_options(filename.string(), 
646            desc_overall_cfgfile, vm, true);
647
648    // if there is specified at least one config file, parse it and add the
649    // options to the main variables_map
650        if (vm.count("config-file")) {
651            vector<string> const &cfg_files = 
652                vm["config-file"].as<vector<string> >();
653            vector<string>::const_iterator end = cfg_files.end();
654            for (vector<string>::const_iterator cit = cfg_files.begin(); 
655                 cit != end; ++cit)
656            {
657            // parse a single config file and store the results
658                cmd_line_util::read_config_file_options(*cit, 
659                    desc_overall_cfgfile, vm);
660            }
661        }
662
663    // ... act as required
664        if (vm.count("help")) {
665        po::options_description desc_help (
666            "Usage: wave [options] [@config-file(s)] [file]");
667
668            desc_help.add(desc_cmdline).add(desc_generic).add(desc_ext);
669            cout << desc_help << endl;
670            return 1;
671        }
672       
673        if (vm.count("version")) {
674            return print_version();
675        }
676
677        if (vm.count("copyright")) {
678            return print_copyright();
679        }
680
681    // extract the arguments from the parsed command line
682    vector<po::option> arguments;
683   
684        std::remove_copy_if(opts.options.begin(), opts.options.end(), 
685            back_inserter(arguments), cmd_line_util::is_argument());
686           
687    // if there is no input file given, then take input from stdin
688        if (0 == arguments.size() || 0 == arguments[0].value.size() ||
689            arguments[0].value[0] == "-") 
690        {
691        // preprocess the given input from stdin
692            return do_actual_work("stdin", std::cin, vm);
693        }
694        else {
695        std::string file_name(arguments[0].value[0]);
696        ifstream instream(file_name.c_str());
697
698        // preprocess the given input file
699            if (!instream.is_open()) {
700                cerr << "wave: could not open input file: " << file_name << endl;
701                return -1;
702            }
703            return do_actual_work(file_name, instream, vm);
704        }
705    }
706    catch (std::exception &e) {
707        cout << "wave: exception caught: " << e.what() << endl;
708        return 6;
709    }
710    catch (...) {
711        cerr << "wave: unexpected exception caught." << endl;
712        return 7;
713    }
714}
715
Note: See TracBrowser for help on using the repository browser.