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