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