1 /*=============================================================================
2     Boost.Wave: A Standard compliant C++ preprocessor library
3     http://www.boost.org/
4 
5     Copyright (c) 2001-2012 Hartmut Kaiser. Distributed under the Boost
6     Software License, Version 1.0. (See accompanying file
7     LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
8 =============================================================================*/
9 
10 #if !defined(BOOST_TRACE_MACRO_EXPANSION_HPP_D8469318_8407_4B9D_A19F_13CA60C1661F_INCLUDED)
11 #define BOOST_TRACE_MACRO_EXPANSION_HPP_D8469318_8407_4B9D_A19F_13CA60C1661F_INCLUDED
12 
13 #include <cstdio>
14 #include <cstdlib>
15 #include <ctime>
16 
17 #include <ostream>
18 #include <string>
19 #include <stack>
20 #include <set>
21 
22 #include <boost/assert.hpp>
23 #include <boost/config.hpp>
24 #include <boost/filesystem/path.hpp>
25 #include <boost/filesystem/operations.hpp>
26 #include <boost/filesystem/convenience.hpp>
27 
28 #include <boost/wave/token_ids.hpp>
29 #include <boost/wave/util/macro_helpers.hpp>
30 #include <boost/wave/util/filesystem_compatibility.hpp>
31 #include <boost/wave/preprocessing_hooks.hpp>
32 #include <boost/wave/whitespace_handling.hpp>
33 #include <boost/wave/language_support.hpp>
34 #include <boost/wave/cpp_exceptions.hpp>
35 
36 #include "stop_watch.hpp"
37 
38 #ifdef BOOST_NO_STRINGSTREAM
39 #include <strstream>
40 #define BOOST_WAVE_OSSTREAM std::ostrstream
BOOST_WAVE_GETSTRING(std::ostrstream & ss)41 std::string BOOST_WAVE_GETSTRING(std::ostrstream& ss)
42 {
43     ss << std::ends;
44     std::string rval = ss.str();
45     ss.freeze(false);
46     return rval;
47 }
48 #else
49 #include <sstream>
50 #define BOOST_WAVE_GETSTRING(ss) ss.str()
51 #define BOOST_WAVE_OSSTREAM std::ostringstream
52 #endif
53 
54 //  trace_flags:  enable single tracing functionality
55 enum trace_flags {
56     trace_nothing = 0,      // disable tracing
57     trace_macros = 1,       // enable macro tracing
58     trace_macro_counts = 2, // enable invocation counting
59     trace_includes = 4,     // enable include file tracing
60     trace_guards = 8        // enable include guard tracing
61 };
62 
63 ///////////////////////////////////////////////////////////////////////////////
64 //
65 //  Special error thrown whenever the #pragma wave system() directive is
66 //  disabled
67 //
68 ///////////////////////////////////////////////////////////////////////////////
69 class bad_pragma_exception :
70     public boost::wave::preprocess_exception
71 {
72 public:
73     enum error_code {
74         pragma_system_not_enabled =
75             boost::wave::preprocess_exception::last_error_number + 1,
76         pragma_mismatched_push_pop,
77     };
78 
bad_pragma_exception(char const * what_,error_code code,std::size_t line_,std::size_t column_,char const * filename_)79     bad_pragma_exception(char const *what_, error_code code, std::size_t line_,
80         std::size_t column_, char const *filename_) throw()
81     :   boost::wave::preprocess_exception(what_,
82             (boost::wave::preprocess_exception::error_code)code, line_,
83             column_, filename_)
84     {
85     }
~bad_pragma_exception()86     ~bad_pragma_exception() throw() {}
87 
what() const88     char const *what() const throw() BOOST_OVERRIDE
89     {
90         return "boost::wave::bad_pragma_exception";
91     }
is_recoverable() const92     virtual bool is_recoverable() const throw() BOOST_OVERRIDE
93     {
94         return true;
95     }
get_severity() const96     virtual int get_severity() const throw() BOOST_OVERRIDE
97     {
98         return boost::wave::util::severity_remark;
99     }
100 
error_text(int code)101     static char const *error_text(int code)
102     {
103         switch(code) {
104         case pragma_system_not_enabled:
105             return "the directive '#pragma wave system()' was not enabled, use the "
106                    "-x command line argument to enable the execution of";
107 
108         case pragma_mismatched_push_pop:
109             return "unbalanced #pragma push/pop in input file(s) for option";
110         }
111         return "Unknown exception";
112     }
severity_level(int code)113     static boost::wave::util::severity severity_level(int code)
114     {
115         switch(code) {
116         case pragma_system_not_enabled:
117             return boost::wave::util::severity_remark;
118 
119         case pragma_mismatched_push_pop:
120             return boost::wave::util::severity_error;
121         }
122         return boost::wave::util::severity_fatal;
123     }
severity_text(int code)124     static char const *severity_text(int code)
125     {
126         return boost::wave::util::get_severity(boost::wave::util::severity_remark);
127     }
128 };
129 
130 ///////////////////////////////////////////////////////////////////////////////
131 //
132 //  The trace_macro_expansion policy is used to trace the macro expansion of
133 //  macros whenever it is requested from inside the input stream to preprocess
134 //  through the '#pragma wave_option(trace: enable)' directive. The macro
135 //  tracing is disabled with the help of a '#pragma wave_option(trace: disable)'
136 //  directive.
137 //
138 //  This policy type is used as a template parameter to the boost::wave::context<>
139 //  object.
140 //
141 ///////////////////////////////////////////////////////////////////////////////
142 template <typename TokenT>
143 class trace_macro_expansion
144 :   public boost::wave::context_policies::eat_whitespace<TokenT>
145 {
146     typedef boost::wave::context_policies::eat_whitespace<TokenT> base_type;
147 
148 public:
trace_macro_expansion(bool preserve_whitespace_,bool preserve_bol_whitespace_,std::ofstream & output_,std::ostream & tracestrm_,std::ostream & includestrm_,std::ostream & guardstrm_,trace_flags flags_,bool enable_system_command_,bool & generate_output_,std::string const & default_outfile_)149     trace_macro_expansion(
150             bool preserve_whitespace_, bool preserve_bol_whitespace_,
151             std::ofstream &output_, std::ostream &tracestrm_,
152             std::ostream &includestrm_, std::ostream &guardstrm_,
153             trace_flags flags_, bool enable_system_command_,
154             bool& generate_output_, std::string const& default_outfile_)
155     :   outputstrm(output_), tracestrm(tracestrm_),
156         includestrm(includestrm_), guardstrm(guardstrm_),
157         level(0), flags(flags_), logging_flags(trace_nothing),
158         enable_system_command(enable_system_command_),
159         preserve_whitespace(preserve_whitespace_),
160         preserve_bol_whitespace(preserve_bol_whitespace_),
161         generate_output(generate_output_),
162         default_outfile(default_outfile_),
163         emit_relative_filenames(false)
164     {
165     }
~trace_macro_expansion()166     ~trace_macro_expansion()
167     {
168     }
169 
enable_macro_counting()170     void enable_macro_counting()
171     {
172         logging_flags = trace_flags(logging_flags | trace_macro_counts);
173     }
get_macro_counts() const174     std::map<std::string, std::size_t> const& get_macro_counts() const
175     {
176         return counts;
177     }
178 
enable_relative_names_in_line_directives(bool flag)179     void enable_relative_names_in_line_directives(bool flag)
180     {
181         emit_relative_filenames = flag;
182     }
enable_relative_names_in_line_directives() const183     bool enable_relative_names_in_line_directives() const
184     {
185         return emit_relative_filenames;
186     }
187 
188     // add a macro name, which should not be expanded at all (left untouched)
add_noexpandmacro(std::string const & name)189     void add_noexpandmacro(std::string const& name)
190     {
191         noexpandmacros.insert(name);
192     }
193 
set_license_info(std::string const & info)194     void set_license_info(std::string const& info)
195     {
196         license_info = info;
197     }
198 
199     ///////////////////////////////////////////////////////////////////////////
200     //
201     //  The function 'expanding_function_like_macro' is called whenever a
202     //  function-like macro is to be expanded.
203     //
204     //  The parameter 'ctx' is a reference to the context object used for
205     //  instantiating the preprocessing iterators by the user.
206     //
207     //  The parameter 'macrodef' marks the position, where the macro to expand
208     //  is defined.
209     //
210     //  The parameter 'formal_args' holds the formal arguments used during the
211     //  definition of the macro.
212     //
213     //  The parameter 'definition' holds the macro definition for the macro to
214     //  trace.
215     //
216     //  The parameter 'macro_call' marks the position, where this macro invoked.
217     //
218     //  The parameter 'arguments' holds the macro arguments used during the
219     //  invocation of the macro
220     //
221     //  The parameters 'seqstart' and 'seqend' point into the input token
222     //  stream allowing to access the whole token sequence comprising the macro
223     //  invocation (starting with the opening parenthesis and ending after the
224     //  closing one).
225     //
226     //  The return value defines whether the corresponding macro will be
227     //  expanded (return false) or will be copied to the output (return true).
228     //  Note: the whole argument list is copied unchanged to the output as well
229     //        without any further processing.
230     //
231     ///////////////////////////////////////////////////////////////////////////
232 #if BOOST_WAVE_USE_DEPRECIATED_PREPROCESSING_HOOKS != 0
233     // old signature
234     template <typename ContainerT>
expanding_function_like_macro(TokenT const & macrodef,std::vector<TokenT> const & formal_args,ContainerT const & definition,TokenT const & macrocall,std::vector<ContainerT> const & arguments)235     void expanding_function_like_macro(
236         TokenT const &macrodef, std::vector<TokenT> const &formal_args,
237         ContainerT const &definition,
238         TokenT const &macrocall, std::vector<ContainerT> const &arguments)
239     {
240         if (enabled_macro_counting())
241             count_invocation(macrodef.get_value().c_str());
242 
243         if (!enabled_macro_tracing())
244             return;
245 #else
246     // new signature
247     template <typename ContextT, typename ContainerT, typename IteratorT>
248     bool
249     expanding_function_like_macro(ContextT const& ctx,
250         TokenT const &macrodef, std::vector<TokenT> const &formal_args,
251         ContainerT const &definition,
252         TokenT const &macrocall, std::vector<ContainerT> const &arguments,
253         IteratorT const& seqstart, IteratorT const& seqend)
254     {
255         if (enabled_macro_counting() || !noexpandmacros.empty()) {
256             std::string name (macrodef.get_value().c_str());
257 
258             if (noexpandmacros.find(name.c_str()) != noexpandmacros.end())
259                 return true;    // do not expand this macro
260 
261             if (enabled_macro_counting())
262                 count_invocation(name.c_str());
263         }
264 
265         if (!enabled_macro_tracing())
266             return false;
267 #endif
268         if (0 == get_level()) {
269             // output header line
270             BOOST_WAVE_OSSTREAM stream;
271 
272             stream
273                 << macrocall.get_position() << ": "
274                 << macrocall.get_value() << "(";
275 
276             // argument list
277             for (typename ContainerT::size_type i = 0; i < arguments.size(); ++i) {
278                 stream << boost::wave::util::impl::as_string(arguments[i]);
279                 if (i < arguments.size()-1)
280                     stream << ", ";
281             }
282             stream << ")" << std::endl;
283             output(BOOST_WAVE_GETSTRING(stream));
284             increment_level();
285         }
286 
287         // output definition reference
288         {
289             BOOST_WAVE_OSSTREAM stream;
290 
291             stream
292                 << macrodef.get_position() << ": see macro definition: "
293                 << macrodef.get_value() << "(";
294 
295             // formal argument list
296             for (typename std::vector<TokenT>::size_type i = 0;
297                 i < formal_args.size(); ++i)
298             {
299                 stream << formal_args[i].get_value();
300                 if (i < formal_args.size()-1)
301                     stream << ", ";
302             }
303             stream << ")" << std::endl;
304             output(BOOST_WAVE_GETSTRING(stream));
305         }
306 
307         if (formal_args.size() > 0) {
308             // map formal and real arguments
309             open_trace_body("invoked with\n");
310             for (typename std::vector<TokenT>::size_type j = 0;
311                 j < formal_args.size(); ++j)
312             {
313                 using namespace boost::wave;
314 
315                 BOOST_WAVE_OSSTREAM stream;
316                 stream << formal_args[j].get_value() << " = ";
317 #if BOOST_WAVE_SUPPORT_VARIADICS_PLACEMARKERS != 0
318                 if (T_ELLIPSIS == token_id(formal_args[j])) {
319                     // ellipsis
320                     for (typename ContainerT::size_type k = j;
321                         k < arguments.size(); ++k)
322                     {
323                         stream << boost::wave::util::impl::as_string(arguments[k]);
324                         if (k < arguments.size()-1)
325                             stream << ", ";
326                     }
327                 }
328                 else
329 #endif
330                 {
331                     stream << boost::wave::util::impl::as_string(arguments[j]);
332                 }
333                 stream << std::endl;
334                 output(BOOST_WAVE_GETSTRING(stream));
335             }
336             close_trace_body();
337         }
338         open_trace_body();
339 
340 #if BOOST_WAVE_USE_DEPRECIATED_PREPROCESSING_HOOKS == 0
341         return false;
342 #endif
343     }
344 
345     ///////////////////////////////////////////////////////////////////////////
346     //
347     //  The function 'expanding_object_like_macro' is called whenever a
348     //  object-like macro is to be expanded .
349     //
350     //  The parameter 'ctx' is a reference to the context object used for
351     //  instantiating the preprocessing iterators by the user.
352     //
353     //  The parameter 'macrodef' marks the position, where the macro to expand
354     //  is defined.
355     //
356     //  The definition 'definition' holds the macro definition for the macro to
357     //  trace.
358     //
359     //  The parameter 'macrocall' marks the position, where this macro invoked.
360     //
361     ///////////////////////////////////////////////////////////////////////////
362 #if BOOST_WAVE_USE_DEPRECIATED_PREPROCESSING_HOOKS != 0
363     // old signature
364     template <typename ContainerT>
365     void expanding_object_like_macro(TokenT const &macrodef,
366         ContainerT const &definition, TokenT const &macrocall)
367     {
368         if (enabled_macro_counting())
369             count_invocation(macrodef.get_value().c_str());
370 
371         if (!enabled_macro_tracing())
372             return;
373 #else
374     // new signature
375     template <typename ContextT, typename ContainerT>
376     bool
377     expanding_object_like_macro(ContextT const& ctx,
378         TokenT const &macrodef, ContainerT const &definition,
379         TokenT const &macrocall)
380     {
381         if (enabled_macro_counting() || !noexpandmacros.empty()) {
382             std::string name (macrodef.get_value().c_str());
383 
384             if (noexpandmacros.find(name.c_str()) != noexpandmacros.end())
385                 return true;    // do not expand this macro
386 
387             if (enabled_macro_counting())
388                 count_invocation(name.c_str());
389         }
390 
391         if (!enabled_macro_tracing())
392             return false;
393 #endif
394         if (0 == get_level()) {
395         // output header line
396         BOOST_WAVE_OSSTREAM stream;
397 
398             stream
399                 << macrocall.get_position() << ": "
400                 << macrocall.get_value() << std::endl;
401             output(BOOST_WAVE_GETSTRING(stream));
402             increment_level();
403         }
404 
405         // output definition reference
406         {
407             BOOST_WAVE_OSSTREAM stream;
408 
409             stream
410                 << macrodef.get_position() << ": see macro definition: "
411                 << macrodef.get_value() << std::endl;
412             output(BOOST_WAVE_GETSTRING(stream));
413         }
414         open_trace_body();
415 
416 #if BOOST_WAVE_USE_DEPRECIATED_PREPROCESSING_HOOKS == 0
417         return false;
418 #endif
419     }
420 
421     ///////////////////////////////////////////////////////////////////////////
422     //
423     //  The function 'expanded_macro' is called whenever the expansion of a
424     //  macro is finished but before the rescanning process starts.
425     //
426     //  The parameter 'ctx' is a reference to the context object used for
427     //  instantiating the preprocessing iterators by the user.
428     //
429     //  The parameter 'result' contains the token sequence generated as the
430     //  result of the macro expansion.
431     //
432     ///////////////////////////////////////////////////////////////////////////
433 #if BOOST_WAVE_USE_DEPRECIATED_PREPROCESSING_HOOKS != 0
434     // old signature
435     template <typename ContainerT>
436     void expanded_macro(ContainerT const &result)
437 #else
438     // new signature
439     template <typename ContextT, typename ContainerT>
440     void expanded_macro(ContextT const& ctx,ContainerT const &result)
441 #endif
442     {
443         if (!enabled_macro_tracing()) return;
444 
445         BOOST_WAVE_OSSTREAM stream;
446         stream << boost::wave::util::impl::as_string(result) << std::endl;
447         output(BOOST_WAVE_GETSTRING(stream));
448 
449         open_trace_body("rescanning\n");
450     }
451 
452     ///////////////////////////////////////////////////////////////////////////
453     //
454     //  The function 'rescanned_macro' is called whenever the rescanning of a
455     //  macro is finished.
456     //
457     //  The parameter 'ctx' is a reference to the context object used for
458     //  instantiating the preprocessing iterators by the user.
459     //
460     //  The parameter 'result' contains the token sequence generated as the
461     //  result of the rescanning.
462     //
463     ///////////////////////////////////////////////////////////////////////////
464 #if BOOST_WAVE_USE_DEPRECIATED_PREPROCESSING_HOOKS != 0
465     // old signature
466     template <typename ContainerT>
467     void rescanned_macro(ContainerT const &result)
468 #else
469     // new signature
470     template <typename ContextT, typename ContainerT>
471     void rescanned_macro(ContextT const& ctx,ContainerT const &result)
472 #endif
473     {
474         if (!enabled_macro_tracing() || get_level() == 0)
475             return;
476 
477         BOOST_WAVE_OSSTREAM stream;
478         stream << boost::wave::util::impl::as_string(result) << std::endl;
479         output(BOOST_WAVE_GETSTRING(stream));
480         close_trace_body();
481         close_trace_body();
482 
483         if (1 == get_level())
484             decrement_level();
485     }
486 
487     ///////////////////////////////////////////////////////////////////////////
488     //
489     //  The function 'interpret_pragma' is called whenever a #pragma command
490     //  directive is found which isn't known to the core Wave library, where
491     //  command is the value defined as the BOOST_WAVE_PRAGMA_KEYWORD constant
492     //  which defaults to "wave".
493     //
494     //  The parameter 'ctx' is a reference to the context object used for
495     //  instantiating the preprocessing iterators by the user.
496     //
497     //  The parameter 'pending' may be used to push tokens back into the input
498     //  stream, which are to be used as the replacement text for the whole
499     //  #pragma directive.
500     //
501     //  The parameter 'option' contains the name of the interpreted pragma.
502     //
503     //  The parameter 'values' holds the values of the parameter provided to
504     //  the pragma operator.
505     //
506     //  The parameter 'act_token' contains the actual #pragma token, which may
507     //  be used for error output.
508     //
509     //  If the return value is 'false', the whole #pragma directive is
510     //  interpreted as unknown and a corresponding error message is issued. A
511     //  return value of 'true' signs a successful interpretation of the given
512     //  #pragma.
513     //
514     ///////////////////////////////////////////////////////////////////////////
515     template <typename ContextT, typename ContainerT>
516     bool
517     interpret_pragma(ContextT &ctx, ContainerT &pending,
518         typename ContextT::token_type const &option, ContainerT const &valuetokens,
519         typename ContextT::token_type const &act_token)
520     {
521         typedef typename ContextT::token_type token_type;
522 
523         ContainerT values(valuetokens);
524         boost::wave::util::impl::trim_sequence(values);    // trim whitespace
525 
526         if (option.get_value() == "timer") {
527             // #pragma wave timer(value)
528             if (0 == values.size()) {
529                 // no value means '1'
530                 using namespace boost::wave;
531                 timer(token_type(T_INTLIT, "1", act_token.get_position()));
532             }
533             else {
534                 timer(values.front());
535             }
536             return true;
537         }
538         if (option.get_value() == "trace") {
539             // enable/disable tracing option
540             return interpret_pragma_trace(ctx, values, act_token);
541         }
542         if (option.get_value() == "system") {
543             if (!enable_system_command) {
544                 // if the #pragma wave system() directive is not enabled, throw
545                 // a corresponding error (actually its a remark),
546                 typename ContextT::string_type msg(
547                     boost::wave::util::impl::as_string(values));
548                 BOOST_WAVE_THROW_CTX(ctx, bad_pragma_exception,
549                     pragma_system_not_enabled,
550                     msg.c_str(), act_token.get_position());
551                 return false;
552             }
553 
554             // try to spawn the given argument as a system command and return the
555             // std::cout of this process as the replacement of this _Pragma
556             return interpret_pragma_system(ctx, pending, values, act_token);
557         }
558         if (option.get_value() == "stop") {
559             // stop the execution and output the argument
560             typename ContextT::string_type msg(
561                 boost::wave::util::impl::as_string(values));
562             BOOST_WAVE_THROW_CTX(ctx, boost::wave::preprocess_exception,
563                 error_directive, msg.c_str(), act_token.get_position());
564             return false;
565         }
566         if (option.get_value() == "option") {
567             // handle different options
568             return interpret_pragma_option(ctx, values, act_token);
569         }
570         return false;
571     }
572 
573     ///////////////////////////////////////////////////////////////////////////
574     //
575     //  The function 'emit_line_directive' is called whenever a #line directive
576     //  has to be emitted into the generated output.
577     //
578     //  The parameter 'ctx' is a reference to the context object used for
579     //  instantiating the preprocessing iterators by the user.
580     //
581     //  The parameter 'pending' may be used to push tokens back into the input
582     //  stream, which are to be used instead of the default output generated
583     //  for the #line directive.
584     //
585     //  The parameter 'act_token' contains the actual #pragma token, which may
586     //  be used for error output. The line number stored in this token can be
587     //  used as the line number emitted as part of the #line directive.
588     //
589     //  If the return value is 'false', a default #line directive is emitted
590     //  by the library. A return value of 'true' will inhibit any further
591     //  actions, the tokens contained in 'pending' will be copied verbatim
592     //  to the output.
593     //
594     ///////////////////////////////////////////////////////////////////////////
595     template <typename ContextT, typename ContainerT>
596     bool
597     emit_line_directive(ContextT const& ctx, ContainerT &pending,
598         typename ContextT::token_type const& act_token)
599     {
600         if (!need_emit_line_directives(ctx.get_language()) ||
601             !enable_relative_names_in_line_directives())
602         {
603             return false;
604         }
605 
606         // emit a #line directive showing the relative filename instead
607         typename ContextT::position_type pos = act_token.get_position();
608         unsigned int column = 6;
609 
610         typedef typename ContextT::token_type result_type;
611         using namespace boost::wave;
612 
613         pos.set_column(1);
614         pending.push_back(result_type(T_PP_LINE, "#line", pos));
615 
616         pos.set_column(column);      // account for '#line'
617         pending.push_back(result_type(T_SPACE, " ", pos));
618 
619         // 21 is the max required size for a 64 bit integer represented as a
620         // string
621         char buffer[22];
622 
623         using namespace std;    // for some systems sprintf is in namespace std
624         sprintf (buffer, "%zd", pos.get_line());
625 
626         pos.set_column(++column);                 // account for ' '
627         pending.push_back(result_type(T_INTLIT, buffer, pos));
628         pos.set_column(column += (unsigned int)strlen(buffer)); // account for <number>
629         pending.push_back(result_type(T_SPACE, " ", pos));
630         pos.set_column(++column);                 // account for ' '
631 
632         std::string file("\"");
633         boost::filesystem::path filename(
634             boost::wave::util::create_path(ctx.get_current_relative_filename().c_str()));
635 
636         using boost::wave::util::impl::escape_lit;
637         file += escape_lit(boost::wave::util::native_file_string(filename)) + "\"";
638 
639         pending.push_back(result_type(T_STRINGLIT, file.c_str(), pos));
640         pos.set_column(column += (unsigned int)file.size());    // account for filename
641         pending.push_back(result_type(T_GENERATEDNEWLINE, "\n", pos));
642 
643         return true;
644     }
645 
646     ///////////////////////////////////////////////////////////////////////////
647     //
648     //  The function 'opened_include_file' is called whenever a file referred
649     //  by an #include directive was successfully located and opened.
650     //
651     //  The parameter 'ctx' is a reference to the context object used for
652     //  instantiating the preprocessing iterators by the user.
653     //
654     //  The parameter 'filename' contains the file system path of the
655     //  opened file (this is relative to the directory of the currently
656     //  processed file or a absolute path depending on the paths given as the
657     //  include search paths).
658     //
659     //  The include_depth parameter contains the current include file depth.
660     //
661     //  The is_system_include parameter denotes, whether the given file was
662     //  found as a result of a #include <...> directive.
663     //
664     ///////////////////////////////////////////////////////////////////////////
665 #if BOOST_WAVE_USE_DEPRECIATED_PREPROCESSING_HOOKS != 0
666     // old signature
667     void
668     opened_include_file(std::string const &relname, std::string const &absname,
669         std::size_t include_depth, bool is_system_include)
670     {
671 #else
672     // new signature
673     template <typename ContextT>
674     void
675     opened_include_file(ContextT const& ctx, std::string const &relname,
676         std::string const &absname, bool is_system_include)
677     {
678         std::size_t include_depth = ctx.get_iteration_depth();
679 #endif
680         if (enabled_include_tracing()) {
681             // print indented filename
682             for (std::size_t i = 0; i < include_depth; ++i)
683                 includestrm << " ";
684 
685             if (is_system_include)
686                 includestrm << "<" << relname << "> (" << absname << ")";
687             else
688                 includestrm << "\"" << relname << "\" (" << absname << ")";
689 
690             includestrm << std::endl;
691         }
692     }
693 
694 #if BOOST_WAVE_SUPPORT_PRAGMA_ONCE != 0
695     ///////////////////////////////////////////////////////////////////////////
696     //
697     //  The function 'detected_include_guard' is called whenever either a
698     //  include file is about to be added to the list of #pragma once headers.
699     //  That means this header file will not be opened and parsed again even
700     //  if it is specified in a later #include directive.
701     //  This function is called as the result of a detected include guard
702     //  scheme.
703     //
704     //  The implemented heuristics for include guards detects two forms of
705     //  include guards:
706     //
707     //       #ifndef INCLUDE_GUARD_MACRO
708     //       #define INCLUDE_GUARD_MACRO
709     //       ...
710     //       #endif
711     //
712     //   or
713     //
714     //       if !defined(INCLUDE_GUARD_MACRO)
715     //       #define INCLUDE_GUARD_MACRO
716     //       ...
717     //       #endif
718     //
719     //  note, that the parenthesis are optional (i.e. !defined INCLUDE_GUARD_MACRO
720     //  will work as well). The code allows for any whitespace, newline and single
721     //  '#' tokens before the #if/#ifndef and after the final #endif.
722     //
723     //  The parameter 'ctx' is a reference to the context object used for
724     //  instantiating the preprocessing iterators by the user.
725     //
726     //  The parameter 'filename' contains the file system path of the
727     //  opened file (this is relative to the directory of the currently
728     //  processed file or a absolute path depending on the paths given as the
729     //  include search paths).
730     //
731     //  The parameter contains the name of the detected include guard.
732     //
733     ///////////////////////////////////////////////////////////////////////////
734     template <typename ContextT>
735     void
736     detected_include_guard(ContextT const& ctx, std::string const& filename,
737         std::string const& include_guard)
738     {
739         if (enabled_guard_tracing()) {
740             guardstrm << include_guard << ":" << std::endl
741                       << "  " << filename << std::endl;
742         }
743     }
744 #endif
745 
746     ///////////////////////////////////////////////////////////////////////////
747     //
748     //  The function 'may_skip_whitespace' will be called by the
749     //  library whenever a token is about to be returned to the calling
750     //  application.
751     //
752     //  The parameter 'ctx' is a reference to the context object used for
753     //  instantiating the preprocessing iterators by the user.
754     //
755     //  The 'token' parameter holds a reference to the current token. The policy
756     //  is free to change this token if needed.
757     //
758     //  The 'skipped_newline' parameter holds a reference to a boolean value
759     //  which should be set to true by the policy function whenever a newline
760     //  is going to be skipped.
761     //
762     //  If the return value is true, the given token is skipped and the
763     //  preprocessing continues to the next token. If the return value is
764     //  false, the given token is returned to the calling application.
765     //
766     //  ATTENTION!
767     //  Caution has to be used, because by returning true the policy function
768     //  is able to force skipping even significant tokens, not only whitespace.
769     //
770     ///////////////////////////////////////////////////////////////////////////
771     template <typename ContextT>
772     bool may_skip_whitespace(ContextT const &ctx, TokenT &token,
773         bool &skipped_newline)
774     {
775         return this->base_type::may_skip_whitespace(
776                 ctx, token, need_preserve_comments(ctx.get_language()),
777                 preserve_bol_whitespace, skipped_newline) ?
778             !preserve_whitespace : false;
779     }
780 
781     ///////////////////////////////////////////////////////////////////////////
782     //
783     //  The function 'throw_exception' will be called by the library whenever a
784     //  preprocessing exception occurs.
785     //
786     //  The parameter 'ctx' is a reference to the context object used for
787     //  instantiating the preprocessing iterators by the user.
788     //
789     //  The parameter 'e' is the exception object containing detailed error
790     //  information.
791     //
792     //  The default behavior is to call the function boost::throw_exception.
793     //
794     ///////////////////////////////////////////////////////////////////////////
795     template <typename ContextT>
796     void
797     throw_exception(ContextT const& ctx, boost::wave::preprocess_exception const& e)
798     {
799 #if BOOST_WAVE_SUPPORT_MS_EXTENSIONS != 0
800         if (!is_import_directive_error(e))
801             boost::throw_exception(e);
802 #else
803         boost::throw_exception(e);
804 #endif
805     }
806     using base_type::throw_exception;
807 
808 protected:
809 #if BOOST_WAVE_SUPPORT_MS_EXTENSIONS != 0
810     ///////////////////////////////////////////////////////////////////////////
811     //  Avoid throwing an error from a #import directive
812     bool is_import_directive_error(boost::wave::preprocess_exception const& e)
813     {
814         using namespace boost::wave;
815         if (e.get_errorcode() != preprocess_exception::ill_formed_directive)
816             return false;
817 
818         // the error string is formatted as 'severity: error: directive'
819         std::string error(e.description());
820         std::string::size_type p = error.find_last_of(":");
821         return p != std::string::npos && error.substr(p+2) == "import";
822     }
823 #endif
824 
825     ///////////////////////////////////////////////////////////////////////////
826     //  Interpret the different Wave specific pragma directives/operators
827     template <typename ContextT, typename ContainerT>
828     bool
829     interpret_pragma_trace(ContextT& ctx, ContainerT const &values,
830         typename ContextT::token_type const &act_token)
831     {
832         typedef typename ContextT::token_type token_type;
833         typedef typename token_type::string_type string_type;
834 
835         bool valid_option = false;
836 
837         if (1 == values.size()) {
838             token_type const& value = values.front();
839 
840             if (value.get_value() == "enable" ||
841                 value.get_value() == "on" ||
842                 value.get_value() == "1")
843             {
844                 // #pragma wave trace(enable)
845                 enable_tracing(static_cast<trace_flags>(
846                     tracing_enabled() | trace_macros));
847                 valid_option = true;
848             }
849             else if (value.get_value() == "disable" ||
850                 value.get_value() == "off" ||
851                 value.get_value() == "0")
852             {
853                 // #pragma wave trace(disable)
854                 enable_tracing(static_cast<trace_flags>(
855                     tracing_enabled() & ~trace_macros));
856                 valid_option = true;
857             }
858         }
859         if (!valid_option) {
860             // unknown option value
861             string_type option_str("trace");
862 
863             if (values.size() > 0) {
864                 option_str += "(";
865                 option_str += boost::wave::util::impl::as_string(values);
866                 option_str += ")";
867             }
868             BOOST_WAVE_THROW_CTX(ctx, boost::wave::preprocess_exception,
869                 ill_formed_pragma_option, option_str.c_str(),
870                 act_token.get_position());
871             return false;
872         }
873         return true;
874     }
875 
876     ///////////////////////////////////////////////////////////////////////////
877     //  interpret the pragma wave option(preserve: [0|1|2|3|push|pop]) directive
878     template <typename ContextT>
879     static bool
880     interpret_pragma_option_preserve_set(int mode, bool &preserve_whitespace,
881         bool& preserve_bol_whitespace, ContextT &ctx)
882     {
883         switch(mode) {
884         // preserve no whitespace
885         case 0:
886             preserve_whitespace = false;
887             preserve_bol_whitespace = false;
888             ctx.set_language(
889                 enable_preserve_comments(ctx.get_language(), false),
890                 false);
891             break;
892 
893         // preserve BOL whitespace only
894         case 1:
895             preserve_whitespace = false;
896             preserve_bol_whitespace = true;
897             ctx.set_language(
898                 enable_preserve_comments(ctx.get_language(), false),
899                 false);
900             break;
901 
902         // preserve comments and BOL whitespace only
903         case 2:
904             preserve_whitespace = false;
905             preserve_bol_whitespace = true;
906             ctx.set_language(
907                 enable_preserve_comments(ctx.get_language()),
908                 false);
909             break;
910 
911         // preserve all whitespace
912         case 3:
913             preserve_whitespace = true;
914             preserve_bol_whitespace = true;
915             ctx.set_language(
916                 enable_preserve_comments(ctx.get_language()),
917                 false);
918             break;
919 
920         default:
921             return false;
922         }
923         return true;
924     }
925 
926     template <typename ContextT, typename IteratorT>
927     bool
928     interpret_pragma_option_preserve(ContextT &ctx, IteratorT &it,
929         IteratorT end, typename ContextT::token_type const &act_token)
930     {
931         using namespace boost::wave;
932 
933         token_id id = util::impl::skip_whitespace(it, end);
934         if (T_COLON == id)
935             id = util::impl::skip_whitespace(it, end);
936 
937         // implement push/pop
938         if (T_IDENTIFIER == id) {
939             if ((*it).get_value() == "push") {
940                 // push current preserve option onto the internal option stack
941                 if (need_preserve_comments(ctx.get_language())) {
942                     if (preserve_whitespace)
943                         preserve_options.push(3);
944                     else
945                         preserve_options.push(2);
946                 }
947                 else if (preserve_bol_whitespace) {
948                     preserve_options.push(1);
949                 }
950                 else {
951                     preserve_options.push(0);
952                 }
953                 return true;
954             }
955             else if ((*it).get_value() == "pop") {
956                 // test for mismatched push/pop #pragmas
957                 if (preserve_options.empty()) {
958                     BOOST_WAVE_THROW_CTX(ctx, bad_pragma_exception,
959                         pragma_mismatched_push_pop, "preserve",
960                         act_token.get_position());
961                 }
962 
963                 // pop output preserve from the internal option stack
964                 bool result = interpret_pragma_option_preserve_set(
965                     preserve_options.top(), preserve_whitespace,
966                     preserve_bol_whitespace, ctx);
967                 preserve_options.pop();
968                 return result;
969             }
970             return false;
971         }
972 
973         if (T_PP_NUMBER != id)
974             return false;
975 
976         using namespace std;    // some platforms have atoi in namespace std
977         return interpret_pragma_option_preserve_set(
978             atoi((*it).get_value().c_str()), preserve_whitespace,
979             preserve_bol_whitespace, ctx);
980     }
981 
982     //  interpret the pragma wave option(line: [0|1|2|push|pop]) directive
983     template <typename ContextT, typename IteratorT>
984     bool
985     interpret_pragma_option_line(ContextT &ctx, IteratorT &it,
986         IteratorT end, typename ContextT::token_type const &act_token)
987     {
988         using namespace boost::wave;
989 
990         token_id id = util::impl::skip_whitespace(it, end);
991         if (T_COLON == id)
992             id = util::impl::skip_whitespace(it, end);
993 
994         // implement push/pop
995         if (T_IDENTIFIER == id) {
996             if ((*it).get_value() == "push") {
997                 // push current line option onto the internal option stack
998                 int mode = 0;
999                 if (need_emit_line_directives(ctx.get_language())) {
1000                     mode = 1;
1001                     if (enable_relative_names_in_line_directives())
1002                         mode = 2;
1003                 }
1004                 line_options.push(mode);
1005                 return true;
1006             }
1007             else if ((*it).get_value() == "pop") {
1008                 // test for mismatched push/pop #pragmas
1009                 if (line_options.empty()) {
1010                     BOOST_WAVE_THROW_CTX(ctx, bad_pragma_exception,
1011                         pragma_mismatched_push_pop, "line",
1012                         act_token.get_position());
1013                 }
1014 
1015                 // pop output line from the internal option stack
1016                 ctx.set_language(
1017                     enable_emit_line_directives(ctx.get_language(), 0 != line_options.top()),
1018                     false);
1019                 enable_relative_names_in_line_directives(2 == line_options.top());
1020                 line_options.pop();
1021                 return true;
1022             }
1023             return false;
1024         }
1025 
1026         if (T_PP_NUMBER != id)
1027             return false;
1028 
1029         using namespace std;    // some platforms have atoi in namespace std
1030         int emit_lines = atoi((*it).get_value().c_str());
1031         if (0 == emit_lines || 1 == emit_lines || 2 == emit_lines) {
1032             // set the new emit #line directive mode
1033             ctx.set_language(
1034                 enable_emit_line_directives(ctx.get_language(), emit_lines),
1035                 false);
1036             return true;
1037         }
1038         return false;
1039     }
1040 
1041     //  interpret the pragma wave option(output: ["filename"|null|default|push|pop])
1042     //  directive
1043     template <typename ContextT>
1044     bool
1045     interpret_pragma_option_output_open(boost::filesystem::path &fpath,
1046         ContextT& ctx, typename ContextT::token_type const &act_token)
1047     {
1048         namespace fs = boost::filesystem;
1049 
1050         // ensure all directories for this file do exist
1051         boost::wave::util::create_directories(
1052             boost::wave::util::branch_path(fpath));
1053 
1054         // figure out, whether the file has been written to by us, if yes, we
1055         // append any output to this file, otherwise we overwrite it
1056         std::ios::openmode mode = std::ios::out;
1057         if (fs::exists(fpath) && written_by_us.find(fpath) != written_by_us.end())
1058             mode = (std::ios::openmode)(std::ios::out | std::ios::app);
1059 
1060         written_by_us.insert(fpath);
1061 
1062         // close the current file
1063         if (outputstrm.is_open())
1064             outputstrm.close();
1065 
1066         // open the new file
1067         outputstrm.open(fpath.string().c_str(), mode);
1068         if (!outputstrm.is_open()) {
1069             BOOST_WAVE_THROW_CTX(ctx, boost::wave::preprocess_exception,
1070                 could_not_open_output_file,
1071                 fpath.string().c_str(), act_token.get_position());
1072             return false;
1073         }
1074 
1075         // write license text, if file was created and if requested
1076         if (mode == std::ios::out && !license_info.empty())
1077             outputstrm << license_info;
1078 
1079         generate_output = true;
1080         current_outfile = fpath;
1081         return true;
1082     }
1083 
1084     bool interpret_pragma_option_output_close(bool generate)
1085     {
1086         if (outputstrm.is_open())
1087             outputstrm.close();
1088         current_outfile = boost::filesystem::path();
1089         generate_output = generate;
1090         return true;
1091     }
1092 
1093     template <typename ContextT, typename IteratorT>
1094     bool
1095     interpret_pragma_option_output(ContextT &ctx, IteratorT &it,
1096         IteratorT end, typename ContextT::token_type const &act_token)
1097     {
1098         using namespace boost::wave;
1099         namespace fs = boost::filesystem;
1100 
1101         typedef typename ContextT::token_type token_type;
1102         typedef typename token_type::string_type string_type;
1103 
1104         token_id id = util::impl::skip_whitespace(it, end);
1105         if (T_COLON == id)
1106             id = util::impl::skip_whitespace(it, end);
1107 
1108         bool result = false;
1109         if (T_STRINGLIT == id) {
1110             namespace fs = boost::filesystem;
1111 
1112             string_type fname ((*it).get_value());
1113             fs::path fpath (boost::wave::util::create_path(
1114                 util::impl::unescape_lit(fname.substr(1, fname.size()-2)).c_str()));
1115             fpath = boost::wave::util::complete_path(fpath, ctx.get_current_directory());
1116             result = interpret_pragma_option_output_open(fpath, ctx, act_token);
1117         }
1118         else if (T_IDENTIFIER == id) {
1119             if ((*it).get_value() == "null") {
1120                 // suppress all output from this point on
1121                 result = interpret_pragma_option_output_close(false);
1122             }
1123             else if ((*it).get_value() == "push") {
1124                 // initialize the current_outfile, if appropriate
1125                 if (output_options.empty() && current_outfile.empty() &&
1126                     !default_outfile.empty() && default_outfile != "-")
1127                 {
1128                     current_outfile = boost::wave::util::complete_path(
1129                         default_outfile, ctx.get_current_directory());
1130                 }
1131 
1132                 // push current output option onto the internal option stack
1133                 output_options.push(
1134                     output_option_type(generate_output, current_outfile));
1135                 result = true;
1136             }
1137             else if ((*it).get_value() == "pop") {
1138                 // test for mismatched push/pop #pragmas
1139                 if (output_options.empty()) {
1140                     BOOST_WAVE_THROW_CTX(ctx, bad_pragma_exception,
1141                         pragma_mismatched_push_pop, "output",
1142                         act_token.get_position());
1143                     return false;
1144                 }
1145 
1146                 // pop output option from the internal option stack
1147                 output_option_type const& opts = output_options.top();
1148                 generate_output = opts.first;
1149                 current_outfile = opts.second;
1150                 if (!current_outfile.empty()) {
1151                     // re-open the last file
1152                     result = interpret_pragma_option_output_open(current_outfile,
1153                         ctx, act_token);
1154                 }
1155                 else {
1156                     // either no output or generate to std::cout
1157                     result = interpret_pragma_option_output_close(generate_output);
1158                 }
1159                 output_options.pop();
1160             }
1161         }
1162         else if (T_DEFAULT == id) {
1163             // re-open the default output given on command line
1164             if (!default_outfile.empty()) {
1165                 if (default_outfile == "-") {
1166                     // the output was suppressed on the command line
1167                     result = interpret_pragma_option_output_close(false);
1168                 }
1169                 else {
1170                     // there was a file name on the command line
1171                     fs::path fpath(boost::wave::util::create_path(default_outfile));
1172                     result = interpret_pragma_option_output_open(fpath, ctx,
1173                         act_token);
1174                 }
1175             }
1176             else {
1177                 // generate the output to std::cout
1178                 result = interpret_pragma_option_output_close(true);
1179             }
1180         }
1181         return result;
1182     }
1183 
1184     ///////////////////////////////////////////////////////////////////////////
1185     // join all adjacent string tokens into the first one
1186     template <typename StringT>
1187     StringT unlit(StringT const& str)
1188     {
1189         return str.substr(1, str.size()-2);
1190     }
1191 
1192     template <typename StringT>
1193     StringT merge_string_lits(StringT const& lhs, StringT const& rhs)
1194     {
1195         StringT result ("\"");
1196 
1197         result += unlit(lhs);
1198         result += unlit(rhs);
1199         result += "\"";
1200         return result;
1201     }
1202 
1203     template <typename ContextT, typename ContainerT>
1204     void join_adjacent_string_tokens(ContextT &ctx, ContainerT const& values,
1205         ContainerT& joined_values)
1206     {
1207         using namespace boost::wave;
1208 
1209         typedef typename ContextT::token_type token_type;
1210         typedef typename ContainerT::const_iterator const_iterator;
1211 
1212         token_type* current = 0;
1213 
1214         const_iterator end = values.end();
1215         for (const_iterator it = values.begin(); it != end; ++it) {
1216             token_id id(*it);
1217 
1218             if (id == T_STRINGLIT) {
1219                 if (!current) {
1220                     joined_values.push_back(*it);
1221                     current = &joined_values.back();
1222                 }
1223                 else {
1224                     current->set_value(merge_string_lits(
1225                         current->get_value(), (*it).get_value()));
1226                 }
1227             }
1228             else if (current) {
1229                 typedef util::impl::next_token<const_iterator> next_token_type;
1230                 token_id next_id (next_token_type::peek(it, end, true));
1231 
1232                 if (next_id != T_STRINGLIT) {
1233                     current = 0;
1234                     joined_values.push_back(*it);
1235                 }
1236             }
1237             else {
1238                 joined_values.push_back(*it);
1239             }
1240         }
1241     }
1242 
1243     ///////////////////////////////////////////////////////////////////////////
1244     //  interpret the pragma wave option() directives
1245     template <typename ContextT, typename ContainerT>
1246     bool
1247     interpret_pragma_option(ContextT &ctx, ContainerT const &cvalues,
1248         typename ContextT::token_type const &act_token)
1249     {
1250         using namespace boost::wave;
1251 
1252         typedef typename ContextT::token_type token_type;
1253         typedef typename token_type::string_type string_type;
1254         typedef typename ContainerT::const_iterator const_iterator;
1255 
1256         ContainerT values;
1257         join_adjacent_string_tokens(ctx, cvalues, values);
1258 
1259         const_iterator end = values.end();
1260         for (const_iterator it = values.begin(); it != end; /**/) {
1261             bool valid_option = false;
1262 
1263             token_type const &value = *it;
1264             if (value.get_value() == "preserve") {
1265                 // #pragma wave option(preserve: [0|1|2|3|push|pop])
1266                 valid_option = interpret_pragma_option_preserve(ctx, it, end,
1267                     act_token);
1268             }
1269             else if (value.get_value() == "line") {
1270                 // #pragma wave option(line: [0|1|2|push|pop])
1271                 valid_option = interpret_pragma_option_line(ctx, it, end,
1272                     act_token);
1273             }
1274             else if (value.get_value() == "output") {
1275                 // #pragma wave option(output: ["filename"|null|default|push|pop])
1276                 valid_option = interpret_pragma_option_output(ctx, it, end,
1277                     act_token);
1278             }
1279 
1280             if (!valid_option) {
1281                 // unknown option value
1282                 string_type option_str("option");
1283 
1284                 if (values.size() > 0) {
1285                     option_str += "(";
1286                     option_str += util::impl::as_string(values);
1287                     option_str += ")";
1288                 }
1289                 BOOST_WAVE_THROW_CTX(ctx, boost::wave::preprocess_exception,
1290                     ill_formed_pragma_option,
1291                     option_str.c_str(), act_token.get_position());
1292                 return false;
1293             }
1294 
1295             token_id id = util::impl::skip_whitespace(it, end);
1296             if (id == T_COMMA)
1297                 util::impl::skip_whitespace(it, end);
1298         }
1299         return true;
1300     }
1301 
1302     ///////////////////////////////////////////////////////////////////////////
1303     // interpret the #pragma wave system() directive
1304     template <typename ContextT, typename ContainerT>
1305     bool
1306     interpret_pragma_system(ContextT& ctx, ContainerT &pending,
1307         ContainerT const &values,
1308         typename ContextT::token_type const &act_token)
1309     {
1310         typedef typename ContextT::token_type token_type;
1311         typedef typename token_type::string_type string_type;
1312 
1313         if (0 == values.size()) return false;   // ill_formed_pragma_option
1314 
1315         string_type stdout_file(std::tmpnam(0));
1316         string_type stderr_file(std::tmpnam(0));
1317         string_type system_str(boost::wave::util::impl::as_string(values));
1318         string_type native_cmd(system_str);
1319 
1320         system_str += " >" + stdout_file + " 2>" + stderr_file;
1321         if (0 != std::system(system_str.c_str())) {
1322             // unable to spawn the command
1323             string_type error_str("unable to spawn command: ");
1324 
1325             error_str += native_cmd;
1326             BOOST_WAVE_THROW_CTX(ctx, boost::wave::preprocess_exception,
1327                 ill_formed_pragma_option,
1328                 error_str.c_str(), act_token.get_position());
1329             return false;
1330         }
1331 
1332         // rescan the content of the stdout_file and insert it as the
1333         // _Pragma replacement
1334         typedef typename ContextT::lexer_type lexer_type;
1335         typedef typename ContextT::input_policy_type input_policy_type;
1336         typedef boost::wave::iteration_context<
1337                 ContextT, lexer_type, input_policy_type>
1338             iteration_context_type;
1339 
1340     iteration_context_type iter_ctx(ctx, stdout_file.c_str(),
1341         act_token.get_position(), ctx.get_language());
1342     ContainerT pragma;
1343 
1344         for (/**/; iter_ctx.first != iter_ctx.last; ++iter_ctx.first)
1345             pragma.push_back(*iter_ctx.first);
1346 
1347         // prepend the newly generated token sequence to the 'pending' container
1348         pending.splice(pending.begin(), pragma);
1349 
1350         // erase the created tempfiles
1351         std::remove(stdout_file.c_str());
1352         std::remove(stderr_file.c_str());
1353         return true;
1354     }
1355 
1356     ///////////////////////////////////////////////////////////////////////////
1357     //  The function enable_tracing is called, whenever the status of the
1358     //  tracing was changed.
1359     //  The parameter 'enable' is to be used as the new tracing status.
1360     void enable_tracing(trace_flags flags)
1361         { logging_flags = flags; }
1362 
1363     //  The function tracing_enabled should return the current tracing status.
1364     trace_flags tracing_enabled()
1365         { return logging_flags; }
1366 
1367     //  Helper functions for generating the trace output
1368     void open_trace_body(char const *label = 0)
1369     {
1370         if (label)
1371             output(label);
1372         output("[\n");
1373         increment_level();
1374     }
1375     void close_trace_body()
1376     {
1377         if (get_level() > 0) {
1378             decrement_level();
1379             output("]\n");
1380             tracestrm << std::flush;      // flush the stream buffer
1381         }
1382     }
1383 
1384     template <typename StringT>
1385     void output(StringT const &outstr) const
1386     {
1387         indent(get_level());
1388         tracestrm << outstr;          // output the given string
1389     }
1390 
1391     void indent(int level) const
1392     {
1393         for (int i = 0; i < level; ++i)
1394             tracestrm << "  ";        // indent
1395     }
1396 
1397     int increment_level() { return ++level; }
1398     int decrement_level() { BOOST_ASSERT(level > 0); return --level; }
1399     int get_level() const { return level; }
1400 
1401     bool enabled_macro_tracing() const
1402     {
1403         return (flags & trace_macros) && (logging_flags & trace_macros);
1404     }
1405     bool enabled_include_tracing() const
1406     {
1407         return (flags & trace_includes);
1408     }
1409     bool enabled_guard_tracing() const
1410     {
1411         return (flags & trace_guards);
1412     }
1413     bool enabled_macro_counting() const
1414     {
1415         return logging_flags & trace_macro_counts;
1416     }
1417 
1418     void count_invocation(std::string const& name)
1419     {
1420         typedef std::map<std::string, std::size_t>::iterator iterator;
1421         typedef std::map<std::string, std::size_t>::value_type value_type;
1422 
1423         iterator it = counts.find(name);
1424         if (it == counts.end())
1425         {
1426             std::pair<iterator, bool> p = counts.insert(value_type(name, 0));
1427             if (p.second)
1428                 it = p.first;
1429         }
1430 
1431         if (it != counts.end())
1432             ++(*it).second;
1433     }
1434 
1435     void timer(TokenT const &value)
1436     {
1437         if (value.get_value() == "0" || value.get_value() == "restart") {
1438             // restart the timer
1439             elapsed_time.start();
1440         }
1441         else if (value.get_value() == "1") {
1442             // print out the current elapsed time
1443             std::cerr
1444                 << value.get_position() << ": "
1445                 << elapsed_time.format_elapsed_time()
1446                 << std::endl;
1447         }
1448         else if (value.get_value() == "suspend") {
1449             // suspend the timer
1450             elapsed_time.stop();
1451         }
1452         else if (value.get_value() == "resume") {
1453             // resume the timer
1454             elapsed_time.resume();
1455         }
1456     }
1457 
1458 private:
1459     std::ofstream &outputstrm;      // main output stream
1460     std::ostream &tracestrm;        // trace output stream
1461     std::ostream &includestrm;      // included list output stream
1462     std::ostream &guardstrm;        // include guard output stream
1463     int level;                      // indentation level
1464     trace_flags flags;              // enabled globally
1465     trace_flags logging_flags;      // enabled by a #pragma
1466     bool enable_system_command;     // enable #pragma wave system() command
1467     bool preserve_whitespace;       // enable whitespace preservation
1468     bool preserve_bol_whitespace;   // enable begin of line whitespace preservation
1469     bool& generate_output;          // allow generated tokens to be streamed to output
1470     std::string const& default_outfile;         // name of the output file given on command line
1471     boost::filesystem::path current_outfile;    // name of the current output file
1472 
1473     stop_watch elapsed_time;        // trace timings
1474     std::set<boost::filesystem::path> written_by_us;    // all files we have written to
1475 
1476     typedef std::pair<bool, boost::filesystem::path> output_option_type;
1477     std::stack<output_option_type> output_options;  // output option stack
1478     std::stack<int> line_options;       // line option stack
1479     std::stack<int> preserve_options;   // preserve option stack
1480 
1481     std::map<std::string, std::size_t> counts;    // macro invocation counts
1482     bool emit_relative_filenames;   // emit relative names in #line directives
1483 
1484     std::set<std::string> noexpandmacros;   // list of macros not to expand
1485 
1486     std::string license_info;       // text to pre-pend to all generated output files
1487 };
1488 
1489 #undef BOOST_WAVE_GETSTRING
1490 #undef BOOST_WAVE_OSSTREAM
1491 
1492 #endif // !defined(BOOST_TRACE_MACRO_EXPANSION_HPP_D8469318_8407_4B9D_A19F_13CA60C1661F_INCLUDED)
1493