1*06f32e7eSjoerg // Copyright 2015 Google Inc. All rights reserved.
2*06f32e7eSjoerg //
3*06f32e7eSjoerg // Licensed under the Apache License, Version 2.0 (the "License");
4*06f32e7eSjoerg // you may not use this file except in compliance with the License.
5*06f32e7eSjoerg // You may obtain a copy of the License at
6*06f32e7eSjoerg //
7*06f32e7eSjoerg //     http://www.apache.org/licenses/LICENSE-2.0
8*06f32e7eSjoerg //
9*06f32e7eSjoerg // Unless required by applicable law or agreed to in writing, software
10*06f32e7eSjoerg // distributed under the License is distributed on an "AS IS" BASIS,
11*06f32e7eSjoerg // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12*06f32e7eSjoerg // See the License for the specific language governing permissions and
13*06f32e7eSjoerg // limitations under the License.
14*06f32e7eSjoerg 
15*06f32e7eSjoerg #ifndef BENCHMARK_RE_H_
16*06f32e7eSjoerg #define BENCHMARK_RE_H_
17*06f32e7eSjoerg 
18*06f32e7eSjoerg #include "internal_macros.h"
19*06f32e7eSjoerg 
20*06f32e7eSjoerg #if !defined(HAVE_STD_REGEX) && \
21*06f32e7eSjoerg     !defined(HAVE_GNU_POSIX_REGEX) && \
22*06f32e7eSjoerg     !defined(HAVE_POSIX_REGEX)
23*06f32e7eSjoerg   // No explicit regex selection; detect based on builtin hints.
24*06f32e7eSjoerg   #if defined(BENCHMARK_OS_LINUX) || defined(BENCHMARK_OS_APPLE)
25*06f32e7eSjoerg     #define HAVE_POSIX_REGEX 1
26*06f32e7eSjoerg   #elif __cplusplus >= 199711L
27*06f32e7eSjoerg     #define HAVE_STD_REGEX 1
28*06f32e7eSjoerg   #endif
29*06f32e7eSjoerg #endif
30*06f32e7eSjoerg 
31*06f32e7eSjoerg // Prefer C regex libraries when compiling w/o exceptions so that we can
32*06f32e7eSjoerg // correctly report errors.
33*06f32e7eSjoerg #if defined(BENCHMARK_HAS_NO_EXCEPTIONS) && \
34*06f32e7eSjoerg     defined(BENCHMARK_HAVE_STD_REGEX) && \
35*06f32e7eSjoerg     (defined(HAVE_GNU_POSIX_REGEX) || defined(HAVE_POSIX_REGEX))
36*06f32e7eSjoerg   #undef HAVE_STD_REGEX
37*06f32e7eSjoerg #endif
38*06f32e7eSjoerg 
39*06f32e7eSjoerg #if defined(HAVE_STD_REGEX)
40*06f32e7eSjoerg   #include <regex>
41*06f32e7eSjoerg #elif defined(HAVE_GNU_POSIX_REGEX)
42*06f32e7eSjoerg   #include <gnuregex.h>
43*06f32e7eSjoerg #elif defined(HAVE_POSIX_REGEX)
44*06f32e7eSjoerg   #include <regex.h>
45*06f32e7eSjoerg #else
46*06f32e7eSjoerg #error No regular expression backend was found!
47*06f32e7eSjoerg #endif
48*06f32e7eSjoerg #include <string>
49*06f32e7eSjoerg 
50*06f32e7eSjoerg #include "check.h"
51*06f32e7eSjoerg 
52*06f32e7eSjoerg namespace benchmark {
53*06f32e7eSjoerg 
54*06f32e7eSjoerg // A wrapper around the POSIX regular expression API that provides automatic
55*06f32e7eSjoerg // cleanup
56*06f32e7eSjoerg class Regex {
57*06f32e7eSjoerg  public:
Regex()58*06f32e7eSjoerg   Regex() : init_(false) {}
59*06f32e7eSjoerg 
60*06f32e7eSjoerg   ~Regex();
61*06f32e7eSjoerg 
62*06f32e7eSjoerg   // Compile a regular expression matcher from spec.  Returns true on success.
63*06f32e7eSjoerg   //
64*06f32e7eSjoerg   // On failure (and if error is not nullptr), error is populated with a human
65*06f32e7eSjoerg   // readable error message if an error occurs.
66*06f32e7eSjoerg   bool Init(const std::string& spec, std::string* error);
67*06f32e7eSjoerg 
68*06f32e7eSjoerg   // Returns whether str matches the compiled regular expression.
69*06f32e7eSjoerg   bool Match(const std::string& str);
70*06f32e7eSjoerg 
71*06f32e7eSjoerg  private:
72*06f32e7eSjoerg   bool init_;
73*06f32e7eSjoerg // Underlying regular expression object
74*06f32e7eSjoerg #if defined(HAVE_STD_REGEX)
75*06f32e7eSjoerg   std::regex re_;
76*06f32e7eSjoerg #elif defined(HAVE_POSIX_REGEX) || defined(HAVE_GNU_POSIX_REGEX)
77*06f32e7eSjoerg   regex_t re_;
78*06f32e7eSjoerg #else
79*06f32e7eSjoerg   #error No regular expression backend implementation available
80*06f32e7eSjoerg #endif
81*06f32e7eSjoerg };
82*06f32e7eSjoerg 
83*06f32e7eSjoerg #if defined(HAVE_STD_REGEX)
84*06f32e7eSjoerg 
Init(const std::string & spec,std::string * error)85*06f32e7eSjoerg inline bool Regex::Init(const std::string& spec, std::string* error) {
86*06f32e7eSjoerg #ifdef BENCHMARK_HAS_NO_EXCEPTIONS
87*06f32e7eSjoerg   ((void)error); // suppress unused warning
88*06f32e7eSjoerg #else
89*06f32e7eSjoerg   try {
90*06f32e7eSjoerg #endif
91*06f32e7eSjoerg     re_ = std::regex(spec, std::regex_constants::extended);
92*06f32e7eSjoerg     init_ = true;
93*06f32e7eSjoerg #ifndef BENCHMARK_HAS_NO_EXCEPTIONS
94*06f32e7eSjoerg   } catch (const std::regex_error& e) {
95*06f32e7eSjoerg     if (error) {
96*06f32e7eSjoerg       *error = e.what();
97*06f32e7eSjoerg     }
98*06f32e7eSjoerg   }
99*06f32e7eSjoerg #endif
100*06f32e7eSjoerg   return init_;
101*06f32e7eSjoerg }
102*06f32e7eSjoerg 
~Regex()103*06f32e7eSjoerg inline Regex::~Regex() {}
104*06f32e7eSjoerg 
Match(const std::string & str)105*06f32e7eSjoerg inline bool Regex::Match(const std::string& str) {
106*06f32e7eSjoerg   if (!init_) {
107*06f32e7eSjoerg     return false;
108*06f32e7eSjoerg   }
109*06f32e7eSjoerg   return std::regex_search(str, re_);
110*06f32e7eSjoerg }
111*06f32e7eSjoerg 
112*06f32e7eSjoerg #else
Init(const std::string & spec,std::string * error)113*06f32e7eSjoerg inline bool Regex::Init(const std::string& spec, std::string* error) {
114*06f32e7eSjoerg   int ec = regcomp(&re_, spec.c_str(), REG_EXTENDED | REG_NOSUB);
115*06f32e7eSjoerg   if (ec != 0) {
116*06f32e7eSjoerg     if (error) {
117*06f32e7eSjoerg       size_t needed = regerror(ec, &re_, nullptr, 0);
118*06f32e7eSjoerg       char* errbuf = new char[needed];
119*06f32e7eSjoerg       regerror(ec, &re_, errbuf, needed);
120*06f32e7eSjoerg 
121*06f32e7eSjoerg       // regerror returns the number of bytes necessary to null terminate
122*06f32e7eSjoerg       // the string, so we move that when assigning to error.
123*06f32e7eSjoerg       CHECK_NE(needed, 0);
124*06f32e7eSjoerg       error->assign(errbuf, needed - 1);
125*06f32e7eSjoerg 
126*06f32e7eSjoerg       delete[] errbuf;
127*06f32e7eSjoerg     }
128*06f32e7eSjoerg 
129*06f32e7eSjoerg     return false;
130*06f32e7eSjoerg   }
131*06f32e7eSjoerg 
132*06f32e7eSjoerg   init_ = true;
133*06f32e7eSjoerg   return true;
134*06f32e7eSjoerg }
135*06f32e7eSjoerg 
~Regex()136*06f32e7eSjoerg inline Regex::~Regex() {
137*06f32e7eSjoerg   if (init_) {
138*06f32e7eSjoerg     regfree(&re_);
139*06f32e7eSjoerg   }
140*06f32e7eSjoerg }
141*06f32e7eSjoerg 
Match(const std::string & str)142*06f32e7eSjoerg inline bool Regex::Match(const std::string& str) {
143*06f32e7eSjoerg   if (!init_) {
144*06f32e7eSjoerg     return false;
145*06f32e7eSjoerg   }
146*06f32e7eSjoerg   return regexec(&re_, str.c_str(), 0, nullptr, 0) == 0;
147*06f32e7eSjoerg }
148*06f32e7eSjoerg #endif
149*06f32e7eSjoerg 
150*06f32e7eSjoerg }  // end namespace benchmark
151*06f32e7eSjoerg 
152*06f32e7eSjoerg #endif  // BENCHMARK_RE_H_
153