| /** | |
| @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. | |
| } |