1 /*
2  *
3  * Copyright (c) 2004
4  * John Maddock
5  *
6  * Use, modification and distribution are subject to the
7  * Boost Software License, Version 1.0. (See accompanying file
8  * LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
9  *
10  */
11 
12  /*
13   *   LOCATION:    see http://www.boost.org for most recent version.
14   *   FILE         info.hpp
15   *   VERSION      see <boost/version.hpp>
16   *   DESCRIPTION: Error handling for test cases.
17   */
18 
19 #ifndef BOOST_REGEX_REGRESS_INFO_HPP
20 #define BOOST_REGEX_REGRESS_INFO_HPP
21 #include <iostream>
22 #include <string>
23 #include <boost/regex.hpp>
24 
25 #ifdef TEST_THREADS
26 #include <boost/thread/once.hpp>
27 #include <boost/thread.hpp>
28 #endif
29 
30 #ifdef GENERATE_CORPUS
31 #include <boost/lexical_cast.hpp>
32 #include <fstream>
33 //
34 // class de_fuzz_output
35 // Generates de-fuzzing corpus files
36 //
37 template <class charT>
38 class de_fuzz_output
39 {
40 public:
de_fuzz_output()41    de_fuzz_output() {}
42    template <class U>
add(const U &,const U &)43    void add(const U&, const U&) {}
44 };
45 template<>
46 class de_fuzz_output<char>
47 {
48    std::set<std::pair<std::string, std::string> > data;
49 public:
de_fuzz_output()50    de_fuzz_output() {}
add(const std::string & re,const std::string & text)51    void add(const std::string& re, const std::string& text)
52    {
53       data.insert(std::make_pair(re, text));
54    }
~de_fuzz_output()55    ~de_fuzz_output()
56    {
57       unsigned j = 0;
58       for(typename std::set<std::pair<std::string, std::string> >::const_iterator i = data.begin(); i != data.end(); ++i)
59       {
60          std::string filename = "corpus_" + boost::lexical_cast<std::string>(j);
61          std::fstream ofs(filename.c_str(), std::ios_base::out | std::ios_base::binary);
62          ofs.put(static_cast<char>(i->first.size() >> 8));
63          ofs.put(static_cast<char>(i->first.size() & 0xff));
64          ofs.write(i->first.c_str(), i->first.size());
65          ofs.write(i->second.c_str(), i->second.size());
66          ++j;
67       }
68    }
69 };
70 #endif
71 //
72 // class test info,
73 // store information about the test we are about to conduct:
74 //
75 template <class charT>
76 class test_info_base
77 {
78 public:
79    typedef std::basic_string<charT> string_type;
80 private:
81    struct data_type
82    {
83       std::string file;
84       int line;
85       string_type expression;
86       boost::regex_constants::syntax_option_type options;
87       string_type search_text;
88       boost::regex_constants::match_flag_type match_options;
89       const int* answer_table;
90       string_type format_string;
91       string_type result_string;
92       bool need_to_print;
93       std::string expression_type_name;
94    };
95 #ifdef TEST_THREADS
do_get_data()96    static data_type& do_get_data()
97    {
98       static boost::thread_specific_ptr<data_type> pd;
99       if(pd.get() == 0)
100          pd.reset(new data_type());
101       return *(pd.get());
102    }
init_data()103    static void init_data()
104    {
105       do_get_data();
106    }
107 #endif
data()108    static data_type& data()
109    {
110 #ifdef TEST_THREADS
111       static boost::once_flag f = BOOST_ONCE_INIT;
112       boost::call_once(f,&init_data);
113       return do_get_data();
114 #else
115       static data_type d;
116       return d;
117 #endif
118    }
119 public:
test_info_base()120    test_info_base(){};
set_info(const char * file,int line,const string_type & ex,boost::regex_constants::syntax_option_type opt,const string_type & search_text=string_type (),boost::regex_constants::match_flag_type match_options=boost::match_default,const int * answer_table=0,const string_type & format_string=string_type (),const string_type & result_string=string_type ())121    static void set_info(
122       const char* file,
123       int line,
124       const string_type& ex,
125       boost::regex_constants::syntax_option_type opt,
126       const string_type& search_text = string_type(),
127       boost::regex_constants::match_flag_type match_options = boost::match_default,
128       const int* answer_table = 0,
129       const string_type& format_string = string_type(),
130       const string_type& result_string = string_type())
131    {
132       data_type& dat = data();
133       dat.file = file;
134       dat.line = line;
135       dat.expression = ex;
136       dat.options = opt;
137       dat.search_text = search_text;
138       dat.match_options = match_options;
139       dat.answer_table = answer_table;
140       dat.format_string = format_string;
141       dat.result_string = result_string;
142       dat.need_to_print = true;
143 #ifdef GENERATE_CORPUS
144       static de_fuzz_output<charT> corpus;
145       corpus.add(ex, search_text);
146 #endif
147    }
set_typename(const std::string & n)148    static void set_typename(const std::string& n)
149    {
150       data().expression_type_name = n;
151    }
152 
expression()153    static const string_type& expression()
154    {
155       return data().expression;
156    }
syntax_options()157    static boost::regex_constants::syntax_option_type syntax_options()
158    {
159       return data().options;
160    }
search_text()161    static const string_type& search_text()
162    {
163       return data().search_text;
164    }
match_options()165    static boost::regex_constants::match_flag_type match_options()
166    {
167       return data().match_options;
168    }
answer_table()169    static const int* answer_table()
170    {
171       return data().answer_table;
172    }
format_string()173    static const string_type& format_string()
174    {
175       return data().format_string;
176    }
result_string()177    static const string_type& result_string()
178    {
179       return data().result_string;
180    }
need_to_print()181    static bool need_to_print()
182    {
183       return data().need_to_print;
184    }
file()185    static const std::string& file()
186    {
187       return data().file;
188    }
line()189    static int line()
190    {
191       return data().line;
192    }
clear()193    static void clear()
194    {
195       data().need_to_print = false;
196    }
expression_typename()197    static std::string& expression_typename()
198    {
199       return data().expression_type_name;
200    }
201 };
202 
203 template <class T>
204 struct test_info
205    : public test_info_base<wchar_t>
206 {};
207 
208 template<>
209 struct test_info<char>
210    : public test_info_base<char>
211 {};
212 
213 #if BOOST_WORKAROUND(__DECCXX_VER, BOOST_TESTED_AT(60590042))
214 
215 // Some template instantiation modes (namely local, implicit local, and weak) of
216 // this compiler need an explicit instantiation because otherwise we end up with
217 // multiple copies of the static variable defined in this method. This explicit
218 // instantiation generates the static variable with common linkage, which makes
219 // the linker choose only one of the available definitions. For more details,
220 // see "man ld".
221 
222 template test_info_base<wchar_t>::data_type & test_info_base<wchar_t>::data();
223 template test_info_base<char>::data_type & test_info_base<char>::data();
224 
225 #endif
226 
227 template <class charT>
operator <<(std::ostream & os,const test_info<charT> &)228 std::ostream& operator<<(std::ostream& os, const test_info<charT>&)
229 {
230    if(test_info<charT>::need_to_print())
231    {
232       os << test_info<charT>::file() << ":" << test_info<charT>::line() << ": Error in test here:" << std::endl;
233       test_info<charT>::clear();
234    }
235    return os;
236 }
237 //
238 // define some test macros:
239 //
240 extern int error_count;
241 
242 #define BOOST_REGEX_TEST_ERROR(msg, charT)\
243    ++error_count;\
244    std::cerr << test_info<charT>();\
245    std::cerr << "  " << __FILE__ << ":" << __LINE__ << ":" << msg \
246              << " (While testing " << test_info<charT>::expression_typename() << ")" << std::endl
247 
248 class errors_as_warnings
249 {
250 public:
errors_as_warnings()251    errors_as_warnings()
252    {
253       m_saved_error_count = error_count;
254    }
~errors_as_warnings()255    ~errors_as_warnings()
256    {
257       if(m_saved_error_count != error_count)
258       {
259          std::cerr << "<note>The above " << (error_count - m_saved_error_count) << " errors are treated as warnings only.</note>" << std::endl;
260          error_count = m_saved_error_count;
261       }
262    }
263 private:
264    int m_saved_error_count;
265 };
266 
267 #endif
268 
269