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 ¯os =
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