| /** |
| @section license License |
| |
| Licensed to the Apache Software Foundation (ASF) under one |
| or more contributor license agreements. See the NOTICE file |
| distributed with this work for additional information |
| regarding copyright ownership. The ASF licenses this file |
| to you under the Apache License, Version 2.0 (the |
| "License"); you may not use this file except in compliance |
| with the License. You may obtain a copy of the License at |
| |
| http://www.apache.org/licenses/LICENSE-2.0 |
| |
| Unless required by applicable law or agreed to in writing, software |
| distributed under the License is distributed on an "AS IS" BASIS, |
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| See the License for the specific language governing permissions and |
| limitations under the License. |
| |
| Copyright 2019, Oath Inc. |
| */ |
| |
| #include <string> |
| |
| #include "txn_box/common.h" |
| #include "txn_box/Rxp.h" |
| #include "txn_box/Config.h" |
| |
| using swoc::TextView; |
| using namespace swoc::literals; |
| using swoc::Errata; |
| using swoc::Rv; |
| |
| Rv<Rxp> |
| Rxp::parse(TextView const &str, Options const &options) |
| { |
| int errc = 0; |
| size_t err_off = 0; |
| uint32_t rxp_opt = 0; |
| if (options.f.nc) { |
| rxp_opt = PCRE2_CASELESS; |
| } |
| auto result = pcre2_compile(reinterpret_cast<unsigned const char *>(str.data()), str.size(), rxp_opt, &errc, &err_off, nullptr); |
| if (nullptr == result) { |
| PCRE2_UCHAR err_buff[128]; |
| auto err_size = pcre2_get_error_message(errc, err_buff, sizeof(err_buff)); |
| return Errata(S_ERROR, R"(Failed to parse regular expression - error "{}" [{}] at offset {} in "{}".)", |
| TextView(reinterpret_cast<char const *>(err_buff), err_size), errc, err_off, str); |
| } |
| return {result}; |
| }; |
| |
| int |
| Rxp::operator()(swoc::TextView text, pcre2_match_data *match) const |
| { |
| return pcre2_match(_rxp.get(), reinterpret_cast<PCRE2_SPTR>(text.data()), text.size(), 0, 0, match, nullptr); |
| } |
| |
| size_t |
| Rxp::capture_count() const |
| { |
| uint32_t count = 0; |
| auto result = pcre2_pattern_info(_rxp.get(), PCRE2_INFO_CAPTURECOUNT, &count); |
| return result == 0 ? count + 1 : 0; // output doesn't reflect capture group 0, apparently. |
| } |
| /* ------------------------------------------------------------------------------------ */ |
| RxpOp::RxpOp(Rxp &&rxp) : _raw(std::move(rxp)) {} |
| RxpOp::RxpOp(Expr &&expr, Rxp::Options opt) : _raw(DynamicRxp{std::move(expr), opt}) {} |
| |
| Rv<RxpOp> |
| RxpOp::Cfg_Visitor::operator()(Feature &f) |
| { |
| if (IndexFor(STRING) != f.index()) { |
| return Errata(S_ERROR, R"(Regular expression literal was not a string as required.)"); |
| } |
| |
| auto &&[rxp, rxp_errata]{Rxp::parse(std::get<IndexFor(STRING)>(f), _rxp_opt)}; |
| if (!rxp_errata.is_ok()) { |
| rxp_errata.note(R"(While parsing regular expression.)"); |
| return std::move(rxp_errata); |
| } |
| _cfg.require_rxp_group_count(rxp.capture_count()); |
| return RxpOp(std::move(rxp)); |
| } |
| |
| Rv<RxpOp> |
| RxpOp::Cfg_Visitor::operator()(std::monostate) |
| { |
| return Errata(S_ERROR, R"(Literal must be a string)"); |
| } |
| |
| Rv<RxpOp> |
| RxpOp::Cfg_Visitor::operator()(Expr::Direct &d) |
| { |
| return RxpOp(Expr(std::move(d)), _rxp_opt); |
| } |
| |
| Rv<RxpOp> |
| RxpOp::Cfg_Visitor::operator()(Expr::Composite &comp) |
| { |
| return RxpOp(Expr(std::move(comp)), _rxp_opt); |
| } |
| |
| Rv<RxpOp> |
| RxpOp::Cfg_Visitor::operator()(Expr::List &) |
| { |
| return Errata(S_ERROR, R"(Literal must be a string)"); |
| } |
| |
| bool |
| RxpOp::Apply_Visitor::operator()(std::monostate) const |
| { |
| return false; |
| } |
| |
| bool |
| RxpOp::Apply_Visitor::operator()(const Rxp &rxp) const |
| { |
| auto result = rxp(_src, _ctx.rxp_working_match_data()); |
| if (result > 0) { |
| _ctx.rxp_commit_match(_src); |
| _ctx._remainder.clear(); |
| return true; |
| } |
| return false; |
| } |
| |
| bool |
| RxpOp::Apply_Visitor::operator()(DynamicRxp const &dr) const |
| { |
| auto f = _ctx.extract(dr._expr); |
| if (auto text = std::get_if<IndexFor(STRING)>(&f); text != nullptr) { |
| auto &&[rxp, rxp_errata]{Rxp::parse(*text, dr._opt)}; |
| if (rxp_errata.is_ok()) { |
| _ctx.rxp_match_require(rxp.capture_count()); |
| return (*this)(rxp); // forward to Rxp overload. |
| } |
| } |
| return false; |
| } |
| |
| Rv<RxpOp> |
| RxpOp::load(Config &cfg, Expr &&expr, Rxp::Options opt) |
| { |
| return {std::visit(Cfg_Visitor(cfg, opt), expr._raw)}; |
| } |
| |
| int |
| RxpOp::operator()(Context &ctx, swoc::TextView src) |
| { |
| return std::visit(Apply_Visitor{ctx, src}, _raw); |
| } |
| |
| size_t |
| RxpOp::capture_count() |
| { |
| if (_raw.index() == STATIC) { |
| return std::get<STATIC>(_raw).capture_count(); |
| } |
| return 0; // No rxp or indeterminate. |
| } |