• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*=============================================================================
2     Boost.Wave: A Standard compliant C++ preprocessor library
3     http://www.boost.org/
4 
5     Copyright (c) 2001-2013 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 // disable stupid compiler warnings
11 #include <boost/config/warning_disable.hpp>
12 
13 // system headers
14 #include <string>
15 #include <iosfwd>
16 #include <vector>
17 #include <ctime>
18 
19 // include boost
20 #include <boost/config.hpp>
21 #include <boost/assert.hpp>
22 #include <boost/throw_exception.hpp>
23 #include <boost/filesystem/path.hpp>
24 #include <boost/filesystem/operations.hpp>
25 #include <boost/detail/workaround.hpp>
26 
27 //  include Wave
28 
29 // always use new hooks
30 #define BOOST_WAVE_USE_DEPRECIATED_PREPROCESSING_HOOKS 0
31 
32 #include <boost/wave.hpp>
33 
34 //  include the lexer related stuff
35 #include <boost/wave/cpplexer/cpp_lex_token.hpp>      // token type
36 #include <boost/wave/cpplexer/cpp_lex_iterator.hpp>   // lexer type
37 
38 ///////////////////////////////////////////////////////////////////////////////
39 //  Include lexer specifics, import lexer names
40 #if BOOST_WAVE_SEPARATE_LEXER_INSTANTIATION == 0
41 #include <boost/wave/cpplexer/re2clex/cpp_re2c_lexer.hpp>
42 #endif
43 
44 ///////////////////////////////////////////////////////////////////////////////
45 //  Include the grammar definitions, if these shouldn't be compiled separately
46 //  (ATTENTION: _very_ large compilation times!)
47 #if BOOST_WAVE_SEPARATE_GRAMMAR_INSTANTIATION == 0
48 #include <boost/wave/grammars/cpp_intlit_grammar.hpp>
49 #include <boost/wave/grammars/cpp_chlit_grammar.hpp>
50 #include <boost/wave/grammars/cpp_grammar.hpp>
51 #include <boost/wave/grammars/cpp_expression_grammar.hpp>
52 #include <boost/wave/grammars/cpp_predef_macros_grammar.hpp>
53 #include <boost/wave/grammars/cpp_defined_grammar.hpp>
54 #endif
55 
56 //  test application related headers
57 #include "cmd_line_utils.hpp"
58 #include "testwave_app.hpp"
59 #include "collect_hooks_information.hpp"
60 
61 #include <boost/filesystem/path.hpp>
62 #include <boost/filesystem/detail/utf8_codecvt_facet.hpp>
63 
64 # ifdef BOOST_NO_STDC_NAMESPACE
65 namespace std
66 {
67     using ::asctime; using ::gmtime; using ::localtime;
68     using ::difftime; using ::time; using ::tm; using ::mktime; using ::system;
69 }
70 # endif
71 
72 namespace po = boost::program_options;
73 namespace fs = boost::filesystem;
74 
75 ///////////////////////////////////////////////////////////////////////////////
76 // testwave version definitions
77 #define TESTWAVE_VERSION_MAJOR           0
78 #define TESTWAVE_VERSION_MINOR           6
79 #define TESTWAVE_VERSION_SUBMINOR        0
80 
81 namespace {
82     struct fs_path_imbue_utf8
83     {
fs_path_imbue_utf8__anonc8d66f490111::fs_path_imbue_utf884         explicit fs_path_imbue_utf8(bool enable)
85             : m_enabled(enable), m_prevLocale()
86         {
87             if (!m_enabled) return;
88             static std::locale global_loc = std::locale();
89             static std::locale utf_8_loc(global_loc, new boost::filesystem::detail::utf8_codecvt_facet);
90 
91             m_prevLocale = boost::filesystem::path::imbue(utf_8_loc);
92 
93         }
~fs_path_imbue_utf8__anonc8d66f490111::fs_path_imbue_utf894         ~fs_path_imbue_utf8()
95         {
96             if (!m_enabled) return;
97             boost::filesystem::path::imbue(m_prevLocale);
98         }
99     private:
100         fs_path_imbue_utf8();
101         fs_path_imbue_utf8(fs_path_imbue_utf8 const&);
102         fs_path_imbue_utf8& operator=(fs_path_imbue_utf8 const&);
103 
104         bool m_enabled;
105         std::locale m_prevLocale;
106     };
107 
108     ///////////////////////////////////////////////////////////////////////////
109     template <typename Iterator>
110     inline bool
handle_next_token(Iterator & it,Iterator const & end,std::string & result)111     handle_next_token(Iterator &it, Iterator const& end,
112         std::string &result)
113     {
114         typedef typename Iterator::value_type token_type;
115 
116         token_type tok = *it++;
117         result = result + tok.get_value().c_str();
118         return (it == end) ? false : true;
119     }
120 
121     ///////////////////////////////////////////////////////////////////////////
122     template <typename String>
handle_quoted_filepath(String & name)123     String const& handle_quoted_filepath(String &name)
124     {
125         using boost::wave::util::impl::unescape_lit;
126 
127         String unesc_name = unescape_lit(name.substr(1, name.size()-2));
128         fs::path p (boost::wave::util::create_path(unesc_name.c_str()));
129 
130         name = String("\"") + boost::wave::util::leaf(p).c_str() + String("\"");
131         return name;
132     }
133 
134     ///////////////////////////////////////////////////////////////////////////
135     template <typename Iterator>
handle_line_directive(Iterator & it,Iterator const & end,std::string & result)136     bool handle_line_directive(Iterator &it, Iterator const& end,
137         std::string &result)
138     {
139         typedef typename Iterator::value_type token_type;
140         typedef typename token_type::string_type string_type;
141 
142         if (!handle_next_token(it, end, result) ||  // #line
143             !handle_next_token(it, end, result) ||  // whitespace
144             !handle_next_token(it, end, result) ||  // number
145             !handle_next_token(it, end, result))    // whitespace
146         {
147             return false;
148         }
149 
150         using boost::wave::util::impl::unescape_lit;
151 
152         token_type filename = *it;
153         string_type name = filename.get_value();
154 
155         handle_quoted_filepath(name);
156         result = result + name.c_str();
157         return true;
158     }
159 
160     template <typename T>
161     inline T const&
variables_map_as(po::variable_value const & v,T *)162     variables_map_as(po::variable_value const& v, T*)
163     {
164 #if (__GNUC__ == 3 && (__GNUC_MINOR__ == 2 || __GNUC_MINOR__ == 3)) || \
165     BOOST_WORKAROUND(__MWERKS__, < 0x3200)
166 // gcc 3.2.x and  3.3.x choke on vm[...].as<...>()
167 // CW 8.3 has problems with the v.as<T>() below
168         T const* r = boost::any_cast<T>(&v.value());
169         if (!r)
170             boost::throw_exception(boost::bad_any_cast());
171         return *r;
172 #else
173         return v.as<T>();
174 #endif
175     }
176 
177 }
178 
179 ///////////////////////////////////////////////////////////////////////////
180 //
181 //  This function compares the real result and the expected one but first
182 //  replaces all occurrences in the expected result of
183 //      $E: to the result of preprocessing the given expression
184 //      $F: to the passed full filepath
185 //      $P: to the full path
186 //      $B: to the full path (same as $P, but using forward slash '/' on Windows)
187 //      $V: to the current Boost version number
188 //
189 ///////////////////////////////////////////////////////////////////////////
190 bool
got_expected_result(std::string const & filename,std::string const & result,std::string & expected)191 testwave_app::got_expected_result(std::string const& filename,
192     std::string const& result, std::string& expected)
193 {
194     using boost::wave::util::impl::escape_lit;
195 
196     std::string full_result;
197     std::string::size_type pos = 0;
198     std::string::size_type pos1 = expected.find_first_of("$");
199 
200     if (pos1 != std::string::npos) {
201         do {
202             switch(expected[pos1+1]) {
203             case 'E':       // preprocess the given token sequence
204                 {
205                     if ('(' == expected[pos1+2]) {
206                         std::size_t p = expected.find_first_of(")", pos1+1);
207                         if (std::string::npos == p) {
208                             std::cerr
209                                 << "testwave: unmatched parenthesis in $E"
210                                     " directive" << std::endl;
211                             return false;
212                         }
213                         std::string source = expected.substr(pos1+3, p-pos1-3);
214                         std::string result, error, hooks;
215                         bool pp_result = preprocess_file(filename, source,
216                             result, error, hooks, "", true);
217                         if (!pp_result) {
218                             std::cerr
219                                 << "testwave: preprocessing error in $E directive: "
220                                 << error << std::endl;
221                             return false;
222                         }
223                         full_result = full_result +
224                             expected.substr(pos, pos1-pos) + result;
225                         pos1 = expected.find_first_of ("$",
226                             pos = pos1 + 4 + source.size());
227                     }
228                 }
229                 break;
230 
231             case 'F':       // insert base file name
232                 full_result = full_result +
233                     expected.substr(pos, pos1-pos) + escape_lit(filename);
234                 pos1 = expected.find_first_of ("$", pos = pos1 + 2);
235                 break;
236 
237             case 'P':       // insert full path
238             case 'B':       // same as 'P', but forward slashes on Windows
239                 {
240                     fs::path fullpath (
241                         boost::wave::util::complete_path(
242                             boost::wave::util::create_path(filename),
243                             boost::wave::util::current_path())
244                         );
245 
246                     if ('(' == expected[pos1+2]) {
247                     // the $P(basename) syntax is used
248                         std::size_t p = expected.find_first_of(")", pos1+1);
249                         if (std::string::npos == p) {
250                             std::cerr
251                                 << "testwave: unmatched parenthesis in $P"
252                                     " directive" << std::endl;
253                             return false;
254                         }
255                         std::string base = expected.substr(pos1+3, p-pos1-3);
256                         fullpath = boost::wave::util::branch_path(fullpath) /
257                             boost::wave::util::create_path(base);
258                         full_result += expected.substr(pos, pos1-pos);
259                         if ('P' == expected[pos1+1]) {
260 #if defined(BOOST_WINDOWS)
261                             std::string p = replace_slashes(
262                                 boost::wave::util::native_file_string(
263                                     boost::wave::util::normalize(fullpath)),
264                                 "/", '\\');
265 #else
266                             std::string p (
267                                 boost::wave::util::native_file_string(
268                                     boost::wave::util::normalize(fullpath)));
269 #endif
270                             full_result += escape_lit(p);
271                         }
272                         else {
273 #if defined(BOOST_WINDOWS)
274                             std::string p = replace_slashes(
275                                 boost::wave::util::normalize(fullpath).string());
276 #else
277                             std::string p (
278                                 boost::wave::util::normalize(fullpath).string());
279 #endif
280                             full_result += escape_lit(p);
281                         }
282                         pos1 = expected.find_first_of ("$",
283                             pos = pos1 + 4 + base.size());
284                     }
285                     else {
286                     // the $P is used on its own
287                         full_result += expected.substr(pos, pos1-pos);
288                         if ('P' == expected[pos1+1]) {
289                             full_result += escape_lit(
290                                 boost::wave::util::native_file_string(fullpath));
291                         }
292                         else {
293 #if defined(BOOST_WINDOWS)
294                             std::string p = replace_slashes(fullpath.string());
295 #else
296                             std::string p (fullpath.string());
297 #endif
298                             full_result += escape_lit(fullpath.string());
299                         }
300                         pos1 = expected.find_first_of ("$", pos = pos1 + 2);
301                     }
302                 }
303                 break;
304 
305             case 'R':       // insert relative file name
306             case 'S':       // same as 'R', but forward slashes on Windows
307                 {
308                     fs::path relpath;
309                     boost::wave::util::as_relative_to(
310                         boost::wave::util::create_path(filename),
311                         boost::wave::util::current_path(),
312                         relpath);
313 
314                     if ('(' == expected[pos1+2]) {
315                     // the $R(basename) syntax is used
316                         std::size_t p = expected.find_first_of(")", pos1+1);
317                         if (std::string::npos == p) {
318                             std::cerr
319                                 << "testwave: unmatched parenthesis in $R"
320                                     " directive" << std::endl;
321                             return false;
322                         }
323                         std::string base = expected.substr(pos1+3, p-pos1-3);
324                         relpath = boost::wave::util::branch_path(relpath) /
325                             boost::wave::util::create_path(base);
326                         full_result += expected.substr(pos, pos1-pos);
327                         if ('R' == expected[pos1+1]) {
328                             full_result += escape_lit(
329                                 boost::wave::util::native_file_string(
330                                     boost::wave::util::normalize(relpath)));
331                         }
332                         else {
333 #if defined(BOOST_WINDOWS)
334                             std::string p = replace_slashes(
335                                 boost::wave::util::normalize(relpath).string());
336 #else
337                             std::string p (
338                                 boost::wave::util::normalize(relpath).string());
339 #endif
340                             full_result += escape_lit(p);
341                         }
342                         pos1 = expected.find_first_of ("$",
343                             pos = pos1 + 4 + base.size());
344                     }
345                     else {
346                     // the $R is used on its own
347                         full_result += expected.substr(pos, pos1-pos);
348                         if ('R' == expected[pos1+1]) {
349                             full_result += escape_lit(
350                                 boost::wave::util::native_file_string(relpath));
351                         }
352                         else {
353 #if defined(BOOST_WINDOWS)
354                             std::string p = replace_slashes(relpath.string());
355 #else
356                             std::string p (relpath.string());
357 #endif
358                             full_result += escape_lit(p);
359                         }
360                         pos1 = expected.find_first_of ("$", pos = pos1 + 2);
361                     }
362                 }
363                 break;
364 
365             case 'V':       // insert Boost version
366                 full_result = full_result +
367                     expected.substr(pos, pos1-pos) + BOOST_LIB_VERSION;
368                 pos1 = expected.find_first_of ("$", pos = pos1 + 2);
369                 break;
370 
371             default:
372                 full_result = full_result +
373                     expected.substr(pos, pos1-pos);
374                 pos1 = expected.find_first_of ("$", (pos = pos1) + 1);
375                 break;
376             }
377 
378         } while(pos1 != std::string::npos);
379         full_result += expected.substr(pos);
380     }
381     else {
382         full_result = expected;
383     }
384 
385     expected = full_result;
386     return full_result == result;
387 }
388 
389 ///////////////////////////////////////////////////////////////////////////////
testwave_app(po::variables_map const & vm)390 testwave_app::testwave_app(po::variables_map const& vm)
391 :   debuglevel(1), desc_options("Preprocessor configuration options"),
392     global_vm(vm)
393 {
394     desc_options.add_options()
395         ("include,I", po::value<cmd_line_utils::include_paths>()->composing(),
396             "specify an additional include directory")
397         ("sysinclude,S", po::value<std::vector<std::string> >()->composing(),
398             "specify an additional system include directory")
399         ("forceinclude,F", po::value<std::vector<std::string> >()->composing(),
400             "force inclusion of the given file")
401         ("define,D", po::value<std::vector<std::string> >()->composing(),
402             "specify a macro to define (as macro[=[value]])")
403         ("predefine,P", po::value<std::vector<std::string> >()->composing(),
404             "specify a macro to predefine (as macro[=[value]])")
405         ("undefine,U", po::value<std::vector<std::string> >()->composing(),
406             "specify a macro to undefine")
407         ("nesting,n", po::value<int>(),
408             "specify a new maximal include nesting depth")
409         ("long_long", "enable long long support in C++ mode")
410         ("preserve", "preserve comments")
411 #if BOOST_WAVE_SUPPORT_VARIADICS_PLACEMARKERS != 0
412         ("variadics", "enable certain C99 extensions in C++ mode")
413         ("c99", "enable C99 mode (implies --variadics)")
414 #endif
415 #if BOOST_WAVE_SUPPORT_PRAGMA_ONCE != 0
416         ("noguard,G", "disable include guard detection")
417 #endif
418         ("skipped_token_hooks", "record skipped_token hook calls")
419 #if BOOST_WAVE_SUPPORT_CPP0X != 0
420         ("c++11", "enable C++11 mode (implies --variadics and --long_long)")
421 #endif
422 #if BOOST_WAVE_SUPPORT_CPP1Z != 0
423         ("c++17", "enable C++17 mode (implies --variadics and --long_long, adds __has_include)")
424 #endif
425 #if BOOST_WAVE_SUPPORT_CPP2A != 0
426         ("c++20", "enable C++20 mode (implies --variadics and --long_long, adds __has_include and __VA_OPT__)")
427 #endif
428         ("warning,W", po::value<std::vector<std::string> >()->composing(),
429             "Warning settings.")
430     ;
431 }
432 
433 ///////////////////////////////////////////////////////////////////////////////
434 //
435 //  Test the given file (i.e. preprocess the file and compare the result
436 //  against the embedded 'R' comments, if an error occurs compare the error
437 //  message against the given 'E' comments, if no error occurred, compare the
438 //  generated hooks result against the given 'H' comments).
439 //
440 ///////////////////////////////////////////////////////////////////////////////
441 bool
test_a_file(std::string filename)442 testwave_app::test_a_file(std::string filename)
443 {
444 // read the input file into a string
445     std::string instr;
446     if (!read_file(filename, instr))
447         return false;     // error was reported already
448 
449     std::string use_utf8;
450     extract_special_information(filename, instr, 'U', use_utf8);
451     fs_path_imbue_utf8 to_utf8(use_utf8.substr(0,3) == "yes");
452 
453     bool test_hooks = true;
454     if (global_vm.count("hooks"))
455         test_hooks = variables_map_as(global_vm["hooks"], (bool *)NULL);
456 
457     std::string expected_cfg_macro;
458     extract_special_information(filename, instr, 'D', expected_cfg_macro);
459 
460 // extract expected output, preprocess the data and compare results
461     std::string expected, expected_hooks;
462     if (extract_expected_output(filename, instr, expected, expected_hooks)) {
463         bool retval = true;   // assume success
464         bool printed_result = false;
465         std::string result, error, hooks;
466         bool pp_result = preprocess_file(filename, instr, result, error, hooks,
467             expected_cfg_macro);
468         if (pp_result || !result.empty()) {
469         // did we expect an error?
470             std::string expected_error;
471             if (!extract_special_information(filename, instr, 'E', expected_error))
472                 return false;
473 
474             if (!expected_error.empty() &&
475                 !got_expected_result(filename, error, expected_error))
476             {
477             // we expected an error but got none (or a different one)
478                 if (debuglevel > 2) {
479                     std::cerr
480                         << filename << ": failed" << std::endl
481                         << "result: " << std::endl << result << std::endl;
482 
483                     if (!error.empty()) {
484                         std::cerr << "expected result: " << std::endl
485                                   << expected << std::endl;
486                     }
487                     if (!expected_error.empty()) {
488                         std::cerr << "expected error: " << std::endl
489                                   << expected_error << std::endl;
490                     }
491                 }
492                 else if (debuglevel > 1) {
493                     std::cerr << filename << ": failed" << std::endl;
494                 }
495                 retval = false;
496             }
497             else if (!got_expected_result(filename, result, expected)) {
498             //  no preprocessing error encountered
499                 if (debuglevel > 2) {
500                     std::cerr
501                         << filename << ": failed" << std::endl
502                         << "result: " << std::endl << result << std::endl
503                         << "expected: " << std::endl << expected << std::endl;
504                 }
505                 else if (debuglevel > 1) {
506                     std::cerr << filename << ": failed" << std::endl;
507                 }
508                 retval = false;
509             }
510             else {
511             // preprocessing succeeded, check hook information, if appropriate
512                 if (test_hooks && !expected_hooks.empty() &&
513                     !got_expected_result(filename, hooks, expected_hooks))
514                 {
515                     if (debuglevel > 2) {
516                         std::cerr << filename << ": failed" << std::endl
517                                   << "hooks result: " << std::endl << hooks
518                                   << std::endl;
519                         std::cerr << "expected hooks result: " << std::endl
520                                   << expected_hooks << std::endl;
521                     }
522                     else if (debuglevel > 1) {
523                         std::cerr << filename << ": failed" << std::endl;
524                     }
525                     retval = false;
526                 }
527             }
528 
529             // print success message, if appropriate
530             if (retval) {
531                 if (debuglevel > 5) {
532                     std::cerr
533                         << filename << ": succeeded" << std::endl
534                         << "result: " << std::endl << result << std::endl
535                         << "hooks result: " << std::endl << hooks << std::endl;
536                 }
537                 else if (debuglevel > 4) {
538                     std::cerr
539                         << filename << ": succeeded" << std::endl
540                         << "result: " << std::endl << result << std::endl;
541                 }
542                 else if (debuglevel > 3) {
543                     std::cerr << filename << ": succeeded" << std::endl;
544                 }
545                 printed_result = true;
546             }
547         }
548 
549         if (!pp_result) {
550         //  there was a preprocessing error, was it expected?
551             std::string expected_error;
552             if (!extract_special_information(filename, instr, 'E', expected_error))
553                 return false;
554 
555             if (!got_expected_result(filename, error, expected_error)) {
556             // the error was unexpected
557                 if (debuglevel > 2) {
558                     std::cerr
559                         << filename << ": failed" << std::endl;
560 
561                     if (!expected_error.empty()) {
562                         std::cerr
563                             << "error result: " << std::endl << error << std::endl
564                             << "expected error: " << std::endl
565                             << expected_error << std::endl;
566                     }
567                     else {
568                         std::cerr << "unexpected error: " << error << std::endl;
569                     }
570                 }
571                 else if (debuglevel > 1) {
572                     std::cerr << filename << ": failed" << std::endl;
573                 }
574                 retval = false;
575             }
576 
577             if (retval) {
578                 if (debuglevel > 5) {
579                     std::cerr
580                         << filename << ": succeeded (caught expected error)"
581                         << std::endl << "error result: " << std::endl << error
582                         << std::endl;
583 
584                     if (!printed_result) {
585                         std::cerr
586                             << "hooks result: " << std::endl << hooks
587                             << std::endl;
588                     }
589                 }
590                 else if (debuglevel > 4) {
591                     std::cerr
592                         << filename << ": succeeded (caught expected error)"
593                         << std::endl << "error result: " << std::endl << error
594                         << std::endl;
595                 }
596                 else if (debuglevel > 3) {
597                 // caught the expected error message
598                     std::cerr << filename << ": succeeded" << std::endl;
599                 }
600             }
601         }
602         return retval;
603     }
604     else {
605         std::cerr
606             << filename << ": no information about expected results found"
607             << std::endl;
608     }
609     return false;
610 }
611 
612 ///////////////////////////////////////////////////////////////////////////////
613 //
614 //  print the current version of this program
615 //
616 ///////////////////////////////////////////////////////////////////////////////
617 int
print_version()618 testwave_app::print_version()
619 {
620 // get time of last compilation of this file
621 boost::wave::util::time_conversion_helper compilation_time(__DATE__ " " __TIME__);
622 
623 // calculate the number of days since Feb 12 2005
624 // (the day the testwave project was started)
625 std::tm first_day;
626 
627     using namespace std;      // some platforms have memset in namespace std
628     memset (&first_day, 0, sizeof(std::tm));
629     first_day.tm_mon = 1;           // Feb
630     first_day.tm_mday = 12;         // 12
631     first_day.tm_year = 105;        // 2005
632 
633 long seconds = long(std::difftime(compilation_time.get_time(),
634     std::mktime(&first_day)));
635 
636     std::cout
637         << TESTWAVE_VERSION_MAJOR << '.'
638         << TESTWAVE_VERSION_MINOR << '.'
639         << TESTWAVE_VERSION_SUBMINOR << '.'
640         << seconds/(3600*24)        // get number of days from seconds
641         << std::endl;
642     return 0;                       // exit app
643 }
644 
645 ///////////////////////////////////////////////////////////////////////////////
646 //
647 //  print the copyright statement
648 //
649 ///////////////////////////////////////////////////////////////////////////////
650 int
print_copyright()651 testwave_app::print_copyright()
652 {
653     char const *copyright[] = {
654         "",
655         "Testwave: A test driver for the Boost.Wave C++ preprocessor library",
656         "http://www.boost.org/",
657         "",
658         "Copyright (c) 2001-2012 Hartmut Kaiser, Distributed under the Boost",
659         "Software License, Version 1.0. (See accompanying file",
660         "LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)",
661         0
662     };
663 
664     for (int i = 0; 0 != copyright[i]; ++i)
665         std::cout << copyright[i] << std::endl;
666 
667     return 0;                       // exit app
668 }
669 
670 ///////////////////////////////////////////////////////////////////////////////
671 //
672 //  Read the given file into a string
673 //
674 ///////////////////////////////////////////////////////////////////////////////
675 bool
read_file(std::string const & filename,std::string & instr)676 testwave_app::read_file(std::string const& filename, std::string& instr)
677 {
678 // open the given file and report error, if appropriate
679     std::ifstream instream(filename.c_str());
680     if (!instream.is_open()) {
681         std::cerr << "testwave: could not open input file: "
682                   << filename << std::endl;
683         return false;
684     }
685     else if (9 == debuglevel) {
686         std::cerr << "read_file: succeeded to open input file: "
687                   << filename << std::endl;
688     }
689     instream.unsetf(std::ios::skipws);
690 
691 // read the input file into a string
692 
693 #if defined(BOOST_NO_TEMPLATED_ITERATOR_CONSTRUCTORS)
694 // this is known to be very slow for large files on some systems
695     std::copy (std::istream_iterator<char>(instream),
696         std::istream_iterator<char>(),
697         std::inserter(instr, instr.end()));
698 #else
699     instr = std::string(std::istreambuf_iterator<char>(instream.rdbuf()),
700         std::istreambuf_iterator<char>());
701 #endif
702 
703     if (9 == debuglevel) {
704         std::cerr << "read_file: succeeded to read input file: "
705                   << filename << std::endl;
706     }
707     return true;
708 }
709 
710 ///////////////////////////////////////////////////////////////////////////////
711 namespace {
712 
trim_whitespace(std::string & value)713     std::string const& trim_whitespace(std::string& value)
714     {
715         std::string::size_type first = value.find_first_not_of(" \t");
716         if (std::string::npos == first)
717             value.clear();
718         else {
719             std::string::size_type last = value.find_last_not_of(" \t");
720             BOOST_ASSERT(std::string::npos != last);
721             value = value.substr(first, last-first+1);
722         }
723         return value;
724     }
725 }
726 
727 ///////////////////////////////////////////////////////////////////////////////
728 //
729 //  Extract special information from comments marked with the given letter
730 //
731 ///////////////////////////////////////////////////////////////////////////////
732 bool
extract_special_information(std::string const & filename,std::string const & instr,char flag,std::string & content)733 testwave_app::extract_special_information(std::string const& filename,
734     std::string const& instr, char flag, std::string& content)
735 {
736     if (9 == debuglevel) {
737         std::cerr << "extract_special_information: extracting special information ('"
738                   << flag << "') from input file: " << filename << std::endl;
739     }
740 
741 // tokenize the input data into C++ tokens using the C++ lexer
742     typedef boost::wave::cpplexer::lex_token<> token_type;
743     typedef boost::wave::cpplexer::lex_iterator<token_type> lexer_type;
744     typedef token_type::position_type position_type;
745 
746     boost::wave::language_support const lang_opts =
747         (boost::wave::language_support)(
748             boost::wave::support_option_variadics |
749             boost::wave::support_option_long_long |
750             boost::wave::support_option_no_character_validation |
751             boost::wave::support_option_convert_trigraphs |
752             boost::wave::support_option_insert_whitespace);
753 
754     position_type pos(filename.c_str());
755     lexer_type it = lexer_type(instr.begin(), instr.end(), pos, lang_opts);
756     lexer_type end = lexer_type();
757 
758     try {
759     // look for C or C++ comments starting with the special character
760         for (/**/; it != end; ++it) {
761             using namespace boost::wave;
762             token_id id = token_id(*it);
763             if (T_CCOMMENT == id) {
764                 std::string value = (*it).get_value().c_str();
765                 if (flag == value[2]) {
766                     if (value.size() > 3 && '(' == value[3]) {
767                         std::size_t p = value.find_first_of(")");
768                         if (std::string::npos == p) {
769                             std::cerr
770                                 << "testwave: missing closing parenthesis in '"
771                                 << flag << "()' directive" << std::endl;
772                             return false;
773                         }
774                         std::string source = value.substr(4, p-4);
775                         std::string result, error, hooks;
776                         bool pp_result = preprocess_file(filename, source,
777                             result, error, hooks, "", true);
778                         if (!pp_result) {
779                             std::cerr
780                                 << "testwave: preprocessing error in '" << flag
781                                 << "()' directive: " << error << std::endl;
782                             return false;
783                         }
784 
785                         // include this text into the extracted information
786                         // only if the result is not zero
787                         using namespace std;    // some system have atoi in namespace std
788                         if (0 != atoi(result.c_str())) {
789                             std::string thiscontent(value.substr(p+1));
790                             if (9 == debuglevel) {
791                                 std::cerr << "extract_special_information: extracted: "
792                                           << thiscontent << std::endl;
793                             }
794                             trim_whitespace(thiscontent);
795                             content += thiscontent;
796                         }
797                     }
798                     else {
799                         std::string thiscontent(value.substr(3, value.size()-5));
800                         if (9 == debuglevel) {
801                             std::cerr << "extract_special_information: extracted: "
802                                       << thiscontent << std::endl;
803                         }
804                         trim_whitespace(thiscontent);
805                         content += thiscontent;
806                     }
807                 }
808             }
809             else if (T_CPPCOMMENT == id) {
810                 std::string value = (*it).get_value().c_str();
811                 if (flag == value[2]) {
812                     if (value.size() > 3 && '(' == value[3]) {
813                         std::size_t p = value.find_first_of(")");
814                         if (std::string::npos == p) {
815                             std::cerr
816                                 << "testwave: missing closing parenthesis in '"
817                                 << flag << "()' directive" << std::endl;
818                             return false;
819                         }
820                         std::string source = value.substr(4, p-4);
821                         std::string result, error, hooks;
822                         bool pp_result = preprocess_file(filename, source,
823                             result, error, hooks, "", true);
824                         if (!pp_result) {
825                             std::cerr
826                                 << "testwave: preprocessing error in '" << flag
827                                 << "()' directive: " << error << std::endl;
828                             return false;
829                         }
830 
831                         // include this text into the extracted information
832                         // only if the result is not zero
833                         using namespace std;    // some system have atoi in namespace std
834                         if (0 != atoi(result.c_str())) {
835                             std::string thiscontent(value.substr((' ' == value[p+1]) ? p+2 : p+1));
836                             if (9 == debuglevel) {
837                                 std::cerr << "extract_special_information: extracted: "
838                                           << thiscontent << std::endl;
839                             }
840                             trim_whitespace(thiscontent);
841                             content += thiscontent;
842                         }
843                     }
844                     else {
845                         std::string thiscontent(value.substr((' ' == value[3]) ? 4 : 3));
846                         if (9 == debuglevel) {
847                             std::cerr << "extract_special_information: extracted: "
848                                       << thiscontent;
849                         }
850                         trim_whitespace(content);
851                         content += thiscontent;
852                     }
853                 }
854             }
855         }
856     }
857     catch (boost::wave::cpplexer::lexing_exception const &e) {
858     // some lexing error
859         std::cerr
860             << e.file_name() << "(" << e.line_no() << "): "
861             << e.description() << std::endl;
862         return false;
863     }
864 
865     if (9 == debuglevel) {
866         std::cerr << "extract_special_information: succeeded extracting special information ('"
867                   << flag << "')" << std::endl;
868     }
869     return true;
870 }
871 
872 ///////////////////////////////////////////////////////////////////////////////
873 //
874 //  Extract the expected output from the given input data
875 //
876 //  The expected output has to be provided inside of special comments which
877 //  start with a capital 'R'. All such comments are concatenated and returned
878 //  through the parameter 'expected'.
879 //
880 ///////////////////////////////////////////////////////////////////////////////
881 inline bool
extract_expected_output(std::string const & filename,std::string const & instr,std::string & expected,std::string & expectedhooks)882 testwave_app::extract_expected_output(std::string const& filename,
883     std::string const& instr, std::string& expected, std::string& expectedhooks)
884 {
885     return extract_special_information(filename, instr, 'R', expected) &&
886            extract_special_information(filename, instr, 'H', expectedhooks);
887 }
888 
889 ///////////////////////////////////////////////////////////////////////////////
890 //
891 //  Extracts the required preprocessing options from the given input data and
892 //  initialises the given Wave context object accordingly.
893 //  We allow the same (applicable) options to be used as are valid for the wave
894 //  driver executable.
895 //
896 ///////////////////////////////////////////////////////////////////////////////
897 template <typename Context>
898 bool
extract_options(std::string const & filename,std::string const & instr,Context & ctx,bool single_line,po::variables_map & vm)899 testwave_app::extract_options(std::string const& filename,
900     std::string const& instr, Context& ctx, bool single_line,
901     po::variables_map& vm)
902 {
903     if (9 == debuglevel) {
904         std::cerr << "extract_options: extracting options" << std::endl;
905     }
906 
907 //  extract the required information from the comments flagged by a
908 //  capital 'O'
909     std::string options;
910     if (!extract_special_information(filename, instr, 'O', options))
911         return false;
912 
913     try {
914     //  parse the configuration information into a program_options_description
915     //  object
916         cmd_line_utils::read_config_options(debuglevel, options, desc_options, vm);
917         initialise_options(ctx, vm, single_line);
918     }
919     catch (std::exception const &e) {
920         std::cerr << filename << ": exception caught: " << e.what()
921                   << std::endl;
922         return false;
923     }
924 
925     if (9 == debuglevel) {
926         std::cerr << "extract_options: succeeded extracting options"
927                   << std::endl;
928     }
929 
930     return true;
931 }
932 
933 template <typename Context>
934 bool
initialise_options(Context & ctx,po::variables_map const & vm,bool single_line)935 testwave_app::initialise_options(Context& ctx, po::variables_map const& vm,
936     bool single_line)
937 {
938     if (9 == debuglevel) {
939         std::cerr << "initialise_options: initializing options" << std::endl;
940     }
941 
942     if (vm.count("skipped_token_hooks")) {
943         if (9 == debuglevel) {
944             std::cerr << "initialise_options: option: skipped_token_hooks" << std::endl;
945         }
946         ctx.get_hooks().set_skipped_token_hooks(true);
947     }
948 
949 //  initialize the given context from the parsed options
950 #if BOOST_WAVE_SUPPORT_VARIADICS_PLACEMARKERS != 0
951 // enable C99 mode, if appropriate (implies variadics)
952     if (vm.count("c99")) {
953         if (9 == debuglevel) {
954             std::cerr << "initialise_options: option: c99" << std::endl;
955         }
956         ctx.set_language(
957             boost::wave::language_support(
958                 boost::wave::support_c99
959               | boost::wave::support_option_emit_line_directives
960 #if BOOST_WAVE_SUPPORT_PRAGMA_ONCE != 0
961               | boost::wave::support_option_include_guard_detection
962 #endif
963 #if BOOST_WAVE_EMIT_PRAGMA_DIRECTIVES != 0
964               | boost::wave::support_option_emit_pragma_directives
965 #endif
966               | boost::wave::support_option_insert_whitespace
967             ));
968     }
969     else if (vm.count("variadics")) {
970     // enable variadics and placemarkers, if appropriate
971         if (9 == debuglevel) {
972             std::cerr << "initialise_options: option: variadics" << std::endl;
973         }
974         ctx.set_language(boost::wave::enable_variadics(ctx.get_language()));
975     }
976 #endif // BOOST_WAVE_SUPPORT_VARIADICS_PLACEMARKERS != 0
977 
978 #if BOOST_WAVE_SUPPORT_CPP0X
979     if (vm.count("c++11")) {
980         ctx.set_language(
981             boost::wave::language_support(
982                  boost::wave::support_cpp0x
983               |  boost::wave::support_option_convert_trigraphs
984               |  boost::wave::support_option_long_long
985               |  boost::wave::support_option_emit_line_directives
986 #if BOOST_WAVE_SUPPORT_PRAGMA_ONCE != 0
987               |  boost::wave::support_option_include_guard_detection
988 #endif
989 #if BOOST_WAVE_EMIT_PRAGMA_DIRECTIVES != 0
990               |  boost::wave::support_option_emit_pragma_directives
991 #endif
992               |  boost::wave::support_option_insert_whitespace
993             ));
994     } else {
995         if (9 == debuglevel) {
996             std::cerr << "initialise_options: option: c++11" << std::endl;
997         }
998     }
999 #endif
1000 
1001 #if BOOST_WAVE_SUPPORT_CPP1Z
1002     if (vm.count("c++17")) {
1003         ctx.set_language(
1004             boost::wave::language_support(
1005                  boost::wave::support_cpp1z
1006 #if BOOST_WAVE_SUPPORT_HAS_INCLUDE != 0
1007               |  boost::wave::support_option_has_include
1008 #endif
1009               |  boost::wave::support_option_convert_trigraphs
1010               |  boost::wave::support_option_long_long
1011               |  boost::wave::support_option_emit_line_directives
1012 #if BOOST_WAVE_SUPPORT_PRAGMA_ONCE != 0
1013               |  boost::wave::support_option_include_guard_detection
1014 #endif
1015 #if BOOST_WAVE_EMIT_PRAGMA_DIRECTIVES != 0
1016               |  boost::wave::support_option_emit_pragma_directives
1017 #endif
1018               |  boost::wave::support_option_insert_whitespace
1019             ));
1020     } else {
1021         if (9 == debuglevel) {
1022             std::cerr << "initialise_options: option: c++17" << std::endl;
1023         }
1024     }
1025 
1026 #endif
1027 
1028 #if BOOST_WAVE_SUPPORT_CPP2A
1029     if (vm.count("c++20")) {
1030         ctx.set_language(
1031             boost::wave::language_support(
1032                  boost::wave::support_cpp2a
1033 #if BOOST_WAVE_SUPPORT_HAS_INCLUDE != 0
1034               |  boost::wave::support_option_has_include
1035 #endif
1036 #if BOOST_WAVE_SUPPORT_VA_OPT != 0
1037               |  boost::wave::support_option_va_opt
1038 #endif
1039               |  boost::wave::support_option_convert_trigraphs
1040               |  boost::wave::support_option_long_long
1041               |  boost::wave::support_option_emit_line_directives
1042  #if BOOST_WAVE_SUPPORT_PRAGMA_ONCE != 0
1043               |  boost::wave::support_option_include_guard_detection
1044  #endif
1045  #if BOOST_WAVE_EMIT_PRAGMA_DIRECTIVES != 0
1046               |  boost::wave::support_option_emit_pragma_directives
1047  #endif
1048               |  boost::wave::support_option_insert_whitespace
1049             ));
1050 
1051             if (9 == debuglevel) {
1052                 std::cerr << "initialise_options: option: c++20" << std::endl;
1053             }
1054     }
1055 #endif
1056 
1057 // enable long_long mode, if appropriate
1058     if (vm.count("long_long")) {
1059         if (9 == debuglevel) {
1060             std::cerr << "initialise_options: option: long_long" << std::endl;
1061         }
1062         ctx.set_language(boost::wave::enable_long_long(ctx.get_language()));
1063     }
1064 
1065 // enable preserving comments mode, if appropriate
1066     if (vm.count("preserve")) {
1067         if (9 == debuglevel) {
1068             std::cerr << "initialise_options: option: preserve" << std::endl;
1069         }
1070         ctx.set_language(
1071             boost::wave::enable_preserve_comments(ctx.get_language()));
1072     }
1073 
1074 // disable automatic include guard detection
1075     if (vm.count("noguard")) {
1076         if (9 == debuglevel) {
1077             std::cerr << "initialise_options: option: guard" << std::endl;
1078         }
1079         ctx.set_language(
1080             boost::wave::enable_include_guard_detection(ctx.get_language(), false));
1081     }
1082 
1083 // enable trigraph conversion
1084     if (9 == debuglevel) {
1085         std::cerr << "initialise_options: option: convert_trigraphs" << std::endl;
1086     }
1087     ctx.set_language(boost::wave::enable_convert_trigraphs(ctx.get_language()));
1088 
1089 // enable single_line mode
1090     if (single_line) {
1091         if (9 == debuglevel) {
1092             std::cerr << "initialise_options: option: single_line" << std::endl;
1093         }
1094         ctx.set_language(boost::wave::enable_single_line(ctx.get_language()));
1095         ctx.set_language(boost::wave::enable_emit_line_directives(ctx.get_language(), false));
1096     }
1097 
1098 //  add include directories to the system include search paths
1099     if (vm.count("sysinclude")) {
1100     std::vector<std::string> const& syspaths =
1101         variables_map_as(vm["sysinclude"], (std::vector<std::string> *)NULL);
1102 
1103         std::vector<std::string>::const_iterator end = syspaths.end();
1104         for (std::vector<std::string>::const_iterator cit = syspaths.begin();
1105               cit != end; ++cit)
1106         {
1107             std::string full(*cit);
1108             got_expected_result(ctx.get_current_filename(),"",full);
1109 
1110             if (9 == debuglevel) {
1111                 std::cerr << "initialise_options: option: -S" << *cit
1112                           << std::endl;
1113             }
1114             ctx.add_sysinclude_path(full.c_str());
1115         }
1116     }
1117 
1118 //  add include directories to the user include search paths
1119     if (vm.count("include")) {
1120         cmd_line_utils::include_paths const &ip =
1121             variables_map_as(vm["include"], (cmd_line_utils::include_paths*)NULL);
1122         std::vector<std::string>::const_iterator end = ip.paths.end();
1123 
1124         for (std::vector<std::string>::const_iterator cit = ip.paths.begin();
1125               cit != end; ++cit)
1126         {
1127             std::string full(*cit);
1128             got_expected_result(ctx.get_current_filename(),"",full);
1129 
1130             if (9 == debuglevel) {
1131                 std::cerr << "initialise_options: option: -I" << *cit
1132                           << std::endl;
1133             }
1134             ctx.add_include_path(full.c_str());
1135         }
1136 
1137     // if on the command line was given -I- , this has to be propagated
1138         if (ip.seen_separator) {
1139             if (9 == debuglevel) {
1140                 std::cerr << "initialise_options: option: -I-" << std::endl;
1141             }
1142             ctx.set_sysinclude_delimiter();
1143         }
1144 
1145     // add system include directories to the include path
1146         std::vector<std::string>::const_iterator sysend = ip.syspaths.end();
1147         for (std::vector<std::string>::const_iterator syscit = ip.syspaths.begin();
1148               syscit != sysend; ++syscit)
1149         {
1150             if (9 == debuglevel) {
1151                 std::cerr << "initialise_options: option: -S" << *syscit
1152                           << std::endl;
1153             }
1154             ctx.add_sysinclude_path((*syscit).c_str());
1155         }
1156     }
1157 
1158 //  add additional defined macros
1159     if (vm.count("define")) {
1160         std::vector<std::string> const &macros =
1161             variables_map_as(vm["define"], (std::vector<std::string>*)NULL);
1162         std::vector<std::string>::const_iterator end = macros.end();
1163         for (std::vector<std::string>::const_iterator cit = macros.begin();
1164               cit != end; ++cit)
1165         {
1166             if (9 == debuglevel) {
1167                 std::cerr << "initialise_options: option: -D" << *cit
1168                           << std::endl;
1169             }
1170             ctx.add_macro_definition(*cit, true);
1171         }
1172     }
1173 
1174 //  add additional predefined macros
1175     if (vm.count("predefine")) {
1176         std::vector<std::string> const &predefmacros =
1177             variables_map_as(vm["predefine"], (std::vector<std::string>*)NULL);
1178         std::vector<std::string>::const_iterator end = predefmacros.end();
1179         for (std::vector<std::string>::const_iterator cit = predefmacros.begin();
1180               cit != end; ++cit)
1181         {
1182             if (9 == debuglevel) {
1183                 std::cerr << "initialise_options: option: -P" << *cit
1184                           << std::endl;
1185             }
1186             ctx.add_macro_definition(*cit, true);
1187         }
1188     }
1189 
1190 //  undefine specified macros
1191     if (vm.count("undefine")) {
1192         std::vector<std::string> const &undefmacros =
1193             variables_map_as(vm["undefine"], (std::vector<std::string>*)NULL);
1194         std::vector<std::string>::const_iterator end = undefmacros.end();
1195         for (std::vector<std::string>::const_iterator cit = undefmacros.begin();
1196               cit != end; ++cit)
1197         {
1198             if (9 == debuglevel) {
1199                 std::cerr << "initialise_options: option: -U" << *cit
1200                           << std::endl;
1201             }
1202             ctx.remove_macro_definition(*cit);
1203         }
1204     }
1205 
1206 //  maximal include nesting depth
1207     if (vm.count("nesting")) {
1208         int max_depth = variables_map_as(vm["nesting"], (int*)NULL);
1209         if (max_depth < 1 || max_depth > 100000) {
1210             std::cerr << "testwave: bogus maximal include nesting depth: "
1211                 << max_depth << std::endl;
1212             return false;
1213         }
1214         else if (9 == debuglevel) {
1215             std::cerr << "initialise_options: option: -n" << max_depth
1216                       << std::endl;
1217         }
1218         ctx.set_max_include_nesting_depth(max_depth);
1219     }
1220 
1221     if (9 == debuglevel) {
1222         std::cerr << "initialise_options: succeeded to initialize options"
1223                   << std::endl;
1224     }
1225     return true;
1226 }
1227 
1228 ///////////////////////////////////////////////////////////////////////////////
1229 //  construct a SIZEOF macro definition string and predefine this macro
1230 template <typename Context>
1231 inline bool
add_sizeof_definition(Context & ctx,char const * name,int value)1232 testwave_app::add_sizeof_definition(Context& ctx, char const *name, int value)
1233 {
1234     BOOST_WAVETEST_OSSTREAM strm;
1235     strm << "__TESTWAVE_SIZEOF_" << name << "__=" << value;
1236 
1237     std::string macro(BOOST_WAVETEST_GETSTRING(strm));
1238     if (!ctx.add_macro_definition(macro, true)) {
1239         std::cerr << "testwave: failed to predefine macro: " << macro
1240                   << std::endl;
1241         return false;
1242     }
1243     else if (9 == debuglevel) {
1244         std::cerr << "add_sizeof_definition: predefined macro: " << macro
1245                   << std::endl;
1246     }
1247     return true;
1248 }
1249 
1250 //  construct a MIN macro definition string and predefine this macro
1251 template <typename T, typename Context>
1252 inline bool
add_min_definition(Context & ctx,char const * name)1253 testwave_app::add_min_definition(Context& ctx, char const *name)
1254 {
1255     BOOST_WAVETEST_OSSTREAM strm;
1256     if (!std::numeric_limits<T>::is_signed) {
1257         strm << "__TESTWAVE_" << name << "_MIN__="
1258               << "0x" << std::hex
1259               << (std::numeric_limits<T>::min)() << "U";
1260     }
1261     else {
1262         strm << "__TESTWAVE_" << name << "_MIN__=( "
1263               << (std::numeric_limits<T>::min)()+1 << "-1)";
1264     }
1265 
1266     std::string macro(BOOST_WAVETEST_GETSTRING(strm));
1267     if (!ctx.add_macro_definition(macro, true)) {
1268         std::cerr << "testwave: failed to predefine macro: " << macro
1269                   << std::endl;
1270         return false;
1271     }
1272     else if (9 == debuglevel) {
1273         std::cerr << "add_min_definition: predefined macro: " << macro
1274                   << std::endl;
1275     }
1276     return true;
1277 }
1278 
1279 //  construct a MAX macro definition string and predefine this macro
1280 template <typename T, typename Context>
1281 inline bool
add_max_definition(Context & ctx,char const * name)1282 testwave_app::add_max_definition(Context& ctx, char const *name)
1283 {
1284     BOOST_WAVETEST_OSSTREAM strm;
1285     if (!std::numeric_limits<T>::is_signed) {
1286         strm << "__TESTWAVE_" << name << "_MAX__="
1287               << "0x" << std::hex
1288               << (std::numeric_limits<T>::max)() << "U";
1289     }
1290     else {
1291         strm << "__TESTWAVE_" << name << "_MAX__="
1292               << (std::numeric_limits<T>::max)();
1293     }
1294 
1295     std::string macro(BOOST_WAVETEST_GETSTRING(strm));
1296     if (!ctx.add_macro_definition(macro, true)) {
1297         std::cerr << "testwave: failed to predefine macro: " << macro
1298                   << std::endl;
1299         return false;
1300     }
1301     else if (9 == debuglevel) {
1302         std::cerr << "add_max_definition: predefined macro: " << macro
1303                   << std::endl;
1304     }
1305     return true;
1306 }
1307 
1308 //  Predefine __TESTWAVE_HAS_STRICT_LEXER__
1309 template <typename Context>
1310 inline bool
add_strict_lexer_definition(Context & ctx)1311 testwave_app::add_strict_lexer_definition(Context& ctx)
1312 {
1313     std::string macro("__TESTWAVE_HAS_STRICT_LEXER__=1");
1314     if (!ctx.add_macro_definition(macro, true)) {
1315         std::cerr << "testwave: failed to predefine macro: " << macro
1316                   << std::endl;
1317         return false;
1318     }
1319     else if (9 == debuglevel) {
1320         std::cerr << "add_strict_lexer_definition: predefined macro: " << macro
1321                   << std::endl;
1322     }
1323     return true;
1324 }
1325 
1326 #if BOOST_WAVE_SUPPORT_MS_EXTENSIONS
1327 //  Predefine __TESTWAVE_SUPPORT_MS_EXTENSIONS__
1328 template <typename Context>
1329 inline bool
add_support_ms_extensions_definition(Context & ctx)1330 testwave_app::add_support_ms_extensions_definition(Context& ctx)
1331 {
1332     std::string macro("__TESTWAVE_SUPPORT_MS_EXTENSIONS__=1");
1333     if (!ctx.add_macro_definition(macro, true)) {
1334         std::cerr << "testwave: failed to predefine macro: " << macro
1335                   << std::endl;
1336         return false;
1337     }
1338     else if (9 == debuglevel) {
1339         std::cerr << "add_support_ms_extensions_definition: predefined macro: "
1340                   << macro
1341                   << std::endl;
1342     }
1343     return true;
1344 }
1345 #endif
1346 
1347 ///////////////////////////////////////////////////////////////////////////////
1348 //
1349 //  Add special predefined macros to the context object.
1350 //
1351 //  This adds a lot of macros to the test environment, which allows to adjust
1352 //  the test cases for different platforms.
1353 //
1354 ///////////////////////////////////////////////////////////////////////////////
1355 template <typename Context>
1356 bool
add_predefined_macros(Context & ctx)1357 testwave_app::add_predefined_macros(Context& ctx)
1358 {
1359     // add the __TESTWAVE_SIZEOF_<type>__ macros
1360     if (!add_sizeof_definition(ctx, "CHAR", sizeof(char)) ||
1361         !add_sizeof_definition(ctx, "SHORT", sizeof(short)) ||
1362         !add_sizeof_definition(ctx, "INT", sizeof(int)) ||
1363 #if defined(BOOST_HAS_LONG_LONG)
1364         !add_sizeof_definition(ctx, "LONGLONG", sizeof(boost::long_long_type)) ||
1365 #endif
1366         !add_sizeof_definition(ctx, "LONG", sizeof(long)))
1367     {
1368         std::cerr << "testwave: failed to add a predefined macro (SIZEOF)."
1369                   << std::endl;
1370         return false;
1371     }
1372 
1373     // add the __TESTWAVE_<type>_MIN__ macros
1374     if (/*!add_min_definition<char>(ctx, "CHAR") ||*/
1375         /*!add_min_definition<unsigned char>(ctx, "UCHAR") ||*/
1376         !add_min_definition<short>(ctx, "SHORT") ||
1377         !add_min_definition<unsigned short>(ctx, "USHORT") ||
1378         !add_min_definition<int>(ctx, "INT") ||
1379         !add_min_definition<unsigned int>(ctx, "UINT") ||
1380 #if defined(BOOST_HAS_LONG_LONG)
1381         !add_min_definition<boost::long_long_type>(ctx, "LONGLONG") ||
1382         !add_min_definition<boost::ulong_long_type>(ctx, "ULONGLONG") ||
1383 #endif
1384         !add_min_definition<long>(ctx, "LONG") ||
1385         !add_min_definition<unsigned long>(ctx, "ULONG"))
1386     {
1387         std::cerr << "testwave: failed to add a predefined macro (MIN)."
1388                   << std::endl;
1389     }
1390 
1391     // add the __TESTWAVE_<type>_MAX__ macros
1392     if (/*!add_max_definition<char>(ctx, "CHAR") ||*/
1393         /*!add_max_definition<unsigned char>(ctx, "UCHAR") ||*/
1394         !add_max_definition<short>(ctx, "SHORT") ||
1395         !add_max_definition<unsigned short>(ctx, "USHORT") ||
1396         !add_max_definition<int>(ctx, "INT") ||
1397         !add_max_definition<unsigned int>(ctx, "UINT") ||
1398 #if defined(BOOST_HAS_LONG_LONG)
1399         !add_max_definition<boost::long_long_type>(ctx, "LONGLONG") ||
1400         !add_max_definition<boost::ulong_long_type>(ctx, "ULONGLONG") ||
1401 #endif
1402         !add_max_definition<long>(ctx, "LONG") ||
1403         !add_max_definition<unsigned long>(ctx, "ULONG"))
1404     {
1405         std::cerr << "testwave: failed to add a predefined macro (MAX)."
1406                   << std::endl;
1407     }
1408 
1409 #if BOOST_WAVE_SUPPORT_MS_EXTENSIONS
1410 //  Predefine __TESTWAVE_SUPPORT_MS_EXTENSIONS__
1411     if (!add_support_ms_extensions_definition(ctx))
1412     {
1413         std::cerr << "testwave: failed to add a predefined macro "
1414                      "(__TESTWAVE_SUPPORT_MS_EXTENSIONS__)."
1415                   << std::endl;
1416     }
1417 #endif
1418 
1419 #if BOOST_WAVE_USE_STRICT_LEXER != 0
1420     return add_strict_lexer_definition(ctx);
1421 #else
1422     return true;
1423 #endif
1424 }
1425 
1426 ///////////////////////////////////////////////////////////////////////////////
1427 //
1428 //  Preprocess the given input data and return the generated output through
1429 //  the parameter 'result'.
1430 //
1431 ///////////////////////////////////////////////////////////////////////////////
1432 bool
preprocess_file(std::string filename,std::string const & instr,std::string & result,std::string & error,std::string & hooks,std::string const & expected_cfg_macro,bool single_line)1433 testwave_app::preprocess_file(std::string filename, std::string const& instr,
1434     std::string& result, std::string& error, std::string& hooks,
1435     std::string const& expected_cfg_macro, bool single_line)
1436 {
1437 //  create the wave::context object and initialize it from the file to
1438 //  preprocess (may contain options inside of special comments)
1439     typedef boost::wave::cpplexer::lex_token<> token_type;
1440     typedef boost::wave::cpplexer::lex_iterator<token_type> lexer_type;
1441     typedef boost::wave::context<
1442         std::string::const_iterator, lexer_type,
1443         boost::wave::iteration_context_policies::load_file_to_string,
1444         collect_hooks_information<token_type> >
1445     context_type;
1446 
1447     if (9 == debuglevel) {
1448         std::cerr << "preprocess_file: preprocessing input file: " << filename
1449                   << std::endl;
1450     }
1451 
1452     try {
1453     //  create preprocessing context
1454         context_type ctx(instr.begin(), instr.end(), filename.c_str(),
1455             collect_hooks_information<token_type>(hooks));
1456 
1457     //  initialize the context from the options given on the command line
1458         if (!initialise_options(ctx, global_vm, single_line))
1459             return false;
1460 
1461     //  extract the options from the input data and initialize the context
1462         boost::program_options::variables_map local_vm;
1463         if (!extract_options(filename, instr, ctx, single_line, local_vm))
1464             return false;
1465 
1466     //  add special predefined macros
1467         if (!add_predefined_macros(ctx))
1468             return false;
1469 
1470         if (!expected_cfg_macro.empty() &&
1471             !ctx.is_defined_macro(expected_cfg_macro))
1472         {
1473             // skip this test as it is for a disabled configuration
1474             return false;
1475         }
1476 
1477     //  preprocess the input, loop over all generated tokens collecting the
1478     //  generated text
1479         context_type::iterator_type it = ctx.begin();
1480         context_type::iterator_type end = ctx.end();
1481 
1482         if (local_vm.count("forceinclude")) {
1483         // add the filenames to force as include files in _reverse_ order
1484         // the second parameter 'is_last' of the force_include function should
1485         // be set to true for the last (first given) file.
1486             std::vector<std::string> const &force =
1487                 local_vm["forceinclude"].as<std::vector<std::string> >();
1488             std::vector<std::string>::const_reverse_iterator rend = force.rend();
1489             for (std::vector<std::string>::const_reverse_iterator cit = force.rbegin();
1490                  cit != rend; /**/)
1491             {
1492                 std::string forceinclude(*cit);
1493                 if (9 == debuglevel) {
1494                     std::cerr << "preprocess_file: option: forceinclude ("
1495                               << forceinclude << ")" << std::endl;
1496                 }
1497                 it.force_include(forceinclude.c_str(), ++cit == rend);
1498             }
1499         }
1500 
1501     // perform actual preprocessing
1502         for (/**/; it != end; ++it)
1503         {
1504             using namespace boost::wave;
1505 
1506             if (T_PP_LINE == token_id(*it)) {
1507             // special handling of the whole #line directive is required to
1508             // allow correct file name matching
1509                 if (!handle_line_directive(it, end, result))
1510                     return false;   // unexpected eof
1511             }
1512             else {
1513                 // add the value of the current token
1514                 result = result + (*it).get_value().c_str();
1515             }
1516         }
1517         error.clear();
1518     }
1519     catch (boost::wave::cpplexer::lexing_exception const& e) {
1520     // some lexer error
1521         BOOST_WAVETEST_OSSTREAM strm;
1522         std::string filename = e.file_name();
1523         strm
1524             << handle_filepath(filename) << "(" << e.line_no() << "): "
1525             << e.description() << std::endl;
1526 
1527         error = BOOST_WAVETEST_GETSTRING(strm);
1528         return false;
1529     }
1530     catch (boost::wave::cpp_exception const& e) {
1531     // some preprocessing error
1532         BOOST_WAVETEST_OSSTREAM strm;
1533         std::string filename = e.file_name();
1534         strm
1535             << handle_filepath(filename) << "(" << e.line_no() << "): "
1536             << e.description() << std::endl;
1537 
1538         error = BOOST_WAVETEST_GETSTRING(strm);
1539         return false;
1540     }
1541 
1542     if (9 == debuglevel) {
1543         std::cerr << "preprocess_file: succeeded to preprocess input file: "
1544                   << filename << std::endl;
1545     }
1546 
1547     return true;
1548 }
1549 
1550