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(TRACE_MACRO_EXPANSION_HPP_D8469318_8407_4B9D_A19F_13CA60C1661F_INCLUDED)
11 #define 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     virtual char const *what() const throw()
89     {
90         return "boost::wave::bad_pragma_exception";
91     }
is_recoverable() const92     virtual bool is_recoverable() const throw()
93     {
94         return true;
95     }
get_severity() const96     virtual int get_severity() const throw()
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, "%ld", 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 token_type::string_type string_type;
1211         typedef typename ContainerT::const_iterator const_iterator;
1212         typedef typename ContainerT::iterator iterator;
1213 
1214         token_type* current = 0;
1215 
1216         const_iterator end = values.end();
1217         for (const_iterator it = values.begin(); it != end; ++it) {
1218             token_id id(*it);
1219 
1220             if (id == T_STRINGLIT) {
1221                 if (!current) {
1222                     joined_values.push_back(*it);
1223                     current = &joined_values.back();
1224                 }
1225                 else {
1226                     current->set_value(merge_string_lits(
1227                         current->get_value(), (*it).get_value()));
1228                 }
1229             }
1230             else if (current) {
1231                 typedef util::impl::next_token<const_iterator> next_token_type;
1232                 token_id next_id (next_token_type::peek(it, end, true));
1233 
1234                 if (next_id != T_STRINGLIT) {
1235                     current = 0;
1236                     joined_values.push_back(*it);
1237                 }
1238             }
1239             else {
1240                 joined_values.push_back(*it);
1241             }
1242         }
1243     }
1244 
1245     ///////////////////////////////////////////////////////////////////////////
1246     //  interpret the pragma wave option() directives
1247     template <typename ContextT, typename ContainerT>
1248     bool
1249     interpret_pragma_option(ContextT &ctx, ContainerT const &cvalues,
1250         typename ContextT::token_type const &act_token)
1251     {
1252         using namespace boost::wave;
1253 
1254         typedef typename ContextT::token_type token_type;
1255         typedef typename token_type::string_type string_type;
1256         typedef typename ContainerT::const_iterator const_iterator;
1257 
1258         ContainerT values;
1259         join_adjacent_string_tokens(ctx, cvalues, values);
1260 
1261         const_iterator end = values.end();
1262         for (const_iterator it = values.begin(); it != end; /**/) {
1263         bool valid_option = false;
1264 
1265             token_type const &value = *it;
1266             if (value.get_value() == "preserve") {
1267             // #pragma wave option(preserve: [0|1|2|3|push|pop])
1268                 valid_option = interpret_pragma_option_preserve(ctx, it, end,
1269                     act_token);
1270             }
1271             else if (value.get_value() == "line") {
1272             // #pragma wave option(line: [0|1|2|push|pop])
1273                 valid_option = interpret_pragma_option_line(ctx, it, end,
1274                     act_token);
1275             }
1276             else if (value.get_value() == "output") {
1277             // #pragma wave option(output: ["filename"|null|default|push|pop])
1278                 valid_option = interpret_pragma_option_output(ctx, it, end,
1279                     act_token);
1280             }
1281 
1282             if (!valid_option) {
1283             // unknown option value
1284             string_type option_str ("option");
1285 
1286                 if (values.size() > 0) {
1287                     option_str += "(";
1288                     option_str += util::impl::as_string(values);
1289                     option_str += ")";
1290                 }
1291                 BOOST_WAVE_THROW_CTX(ctx, boost::wave::preprocess_exception,
1292                     ill_formed_pragma_option,
1293                     option_str.c_str(), act_token.get_position());
1294                 return false;
1295             }
1296 
1297             token_id id = util::impl::skip_whitespace(it, end);
1298             if (id == T_COMMA)
1299                 util::impl::skip_whitespace(it, end);
1300         }
1301         return true;
1302     }
1303 
1304     ///////////////////////////////////////////////////////////////////////////
1305     // interpret the #pragma wave system() directive
1306     template <typename ContextT, typename ContainerT>
1307     bool
1308     interpret_pragma_system(ContextT& ctx, ContainerT &pending,
1309         ContainerT const &values,
1310         typename ContextT::token_type const &act_token)
1311     {
1312         typedef typename ContextT::token_type token_type;
1313         typedef typename token_type::string_type string_type;
1314 
1315         if (0 == values.size()) return false;   // ill_formed_pragma_option
1316 
1317     string_type stdout_file(std::tmpnam(0));
1318     string_type stderr_file(std::tmpnam(0));
1319     string_type system_str(boost::wave::util::impl::as_string(values));
1320     string_type native_cmd(system_str);
1321 
1322         system_str += " >" + stdout_file + " 2>" + stderr_file;
1323         if (0 != std::system(system_str.c_str())) {
1324         // unable to spawn the command
1325         string_type error_str("unable to spawn command: ");
1326 
1327             error_str += native_cmd;
1328             BOOST_WAVE_THROW_CTX(ctx, boost::wave::preprocess_exception,
1329                 ill_formed_pragma_option,
1330                 error_str.c_str(), act_token.get_position());
1331             return false;
1332         }
1333 
1334     // rescan the content of the stdout_file and insert it as the
1335     // _Pragma replacement
1336         typedef typename ContextT::lexer_type lexer_type;
1337         typedef typename ContextT::input_policy_type input_policy_type;
1338         typedef boost::wave::iteration_context<
1339                 ContextT, lexer_type, input_policy_type>
1340             iteration_context_type;
1341 
1342     iteration_context_type iter_ctx(ctx, stdout_file.c_str(),
1343         act_token.get_position(), ctx.get_language());
1344     ContainerT pragma;
1345 
1346         for (/**/; iter_ctx.first != iter_ctx.last; ++iter_ctx.first)
1347             pragma.push_back(*iter_ctx.first);
1348 
1349     // prepend the newly generated token sequence to the 'pending' container
1350         pending.splice(pending.begin(), pragma);
1351 
1352     // erase the created tempfiles
1353         std::remove(stdout_file.c_str());
1354         std::remove(stderr_file.c_str());
1355         return true;
1356     }
1357 
1358     ///////////////////////////////////////////////////////////////////////////
1359     //  The function enable_tracing is called, whenever the status of the
1360     //  tracing was changed.
1361     //  The parameter 'enable' is to be used as the new tracing status.
1362     void enable_tracing(trace_flags flags)
1363         { logging_flags = flags; }
1364 
1365     //  The function tracing_enabled should return the current tracing status.
1366     trace_flags tracing_enabled()
1367         { return logging_flags; }
1368 
1369     //  Helper functions for generating the trace output
1370     void open_trace_body(char const *label = 0)
1371     {
1372         if (label)
1373             output(label);
1374         output("[\n");
1375         increment_level();
1376     }
1377     void close_trace_body()
1378     {
1379         if (get_level() > 0) {
1380             decrement_level();
1381             output("]\n");
1382             tracestrm << std::flush;      // flush the stream buffer
1383         }
1384     }
1385 
1386     template <typename StringT>
1387     void output(StringT const &outstr) const
1388     {
1389         indent(get_level());
1390         tracestrm << outstr;          // output the given string
1391     }
1392 
1393     void indent(int level) const
1394     {
1395         for (int i = 0; i < level; ++i)
1396             tracestrm << "  ";        // indent
1397     }
1398 
1399     int increment_level() { return ++level; }
1400     int decrement_level() { BOOST_ASSERT(level > 0); return --level; }
1401     int get_level() const { return level; }
1402 
1403     bool enabled_macro_tracing() const
1404     {
1405         return (flags & trace_macros) && (logging_flags & trace_macros);
1406     }
1407     bool enabled_include_tracing() const
1408     {
1409         return (flags & trace_includes);
1410     }
1411     bool enabled_guard_tracing() const
1412     {
1413         return (flags & trace_guards);
1414     }
1415     bool enabled_macro_counting() const
1416     {
1417         return logging_flags & trace_macro_counts;
1418     }
1419 
1420     void count_invocation(std::string const& name)
1421     {
1422         typedef std::map<std::string, std::size_t>::iterator iterator;
1423         typedef std::map<std::string, std::size_t>::value_type value_type;
1424 
1425         iterator it = counts.find(name);
1426         if (it == counts.end())
1427         {
1428             std::pair<iterator, bool> p = counts.insert(value_type(name, 0));
1429             if (p.second)
1430                 it = p.first;
1431         }
1432 
1433         if (it != counts.end())
1434             ++(*it).second;
1435     }
1436 
1437     void timer(TokenT const &value)
1438     {
1439         if (value.get_value() == "0" || value.get_value() == "restart") {
1440         // restart the timer
1441             elapsed_time.restart();
1442         }
1443         else if (value.get_value() == "1") {
1444         // print out the current elapsed time
1445             std::cerr
1446                 << value.get_position() << ": "
1447                 << elapsed_time.format_elapsed_time()
1448                 << std::endl;
1449         }
1450         else if (value.get_value() == "suspend") {
1451         // suspend the timer
1452             elapsed_time.suspend();
1453         }
1454         else if (value.get_value() == "resume") {
1455         // resume the timer
1456             elapsed_time.resume();
1457         }
1458     }
1459 
1460 private:
1461     std::ofstream &outputstrm;      // main output stream
1462     std::ostream &tracestrm;        // trace output stream
1463     std::ostream &includestrm;      // included list output stream
1464     std::ostream &guardstrm;        // include guard output stream
1465     int level;                      // indentation level
1466     trace_flags flags;              // enabled globally
1467     trace_flags logging_flags;      // enabled by a #pragma
1468     bool enable_system_command;     // enable #pragma wave system() command
1469     bool preserve_whitespace;       // enable whitespace preservation
1470     bool preserve_bol_whitespace;   // enable begin of line whitespace preservation
1471     bool& generate_output;          // allow generated tokens to be streamed to output
1472     std::string const& default_outfile;         // name of the output file given on command line
1473     boost::filesystem::path current_outfile;    // name of the current output file
1474 
1475     stop_watch elapsed_time;        // trace timings
1476     std::set<boost::filesystem::path> written_by_us;    // all files we have written to
1477 
1478     typedef std::pair<bool, boost::filesystem::path> output_option_type;
1479     std::stack<output_option_type> output_options;  // output option stack
1480     std::stack<int> line_options;       // line option stack
1481     std::stack<int> preserve_options;   // preserve option stack
1482 
1483     std::map<std::string, std::size_t> counts;    // macro invocation counts
1484     bool emit_relative_filenames;   // emit relative names in #line directives
1485 
1486     std::set<std::string> noexpandmacros;   // list of macros not to expand
1487 
1488     std::string license_info;       // text to pre-pend to all generated output files
1489 };
1490 
1491 #undef BOOST_WAVE_GETSTRING
1492 #undef BOOST_WAVE_OSSTREAM
1493 
1494 #endif // !defined(TRACE_MACRO_EXPANSION_HPP_D8469318_8407_4B9D_A19F_13CA60C1661F_INCLUDED)
1495