From 27d82672ab642f2841f385ccc03a48e5767ce588 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=92=9F=E5=AD=9F=E6=8D=B7?= <718335940@qq.com> Date: Tue, 19 Nov 2024 01:50:24 +0800 Subject: [PATCH] =?UTF-8?q?v0.11.0-a1=20=E6=80=A7=E8=83=BD=E4=BC=98?= =?UTF-8?q?=E5=8C=96=EF=BC=9A=20-=20=E9=A2=84=E7=BC=96=E8=AF=91=E5=92=8C?= =?UTF-8?q?=E7=BC=93=E5=AD=98=E9=83=A8=E5=88=86=E6=AD=A3=E5=88=99=20-=20?= =?UTF-8?q?=E9=99=8D=E4=BD=8E=20processRemark=20=E7=9A=84=E6=97=B6?= =?UTF-8?q?=E9=97=B4=E5=A4=8D=E6=9D=82=E5=BA=A6=EF=BC=8CO(n^2)=20~=20O(n^3?= =?UTF-8?q?)=20=E9=99=8D=E4=B8=BA=20O(n)=20-=20=E9=99=8D=E4=BD=8E=20groupG?= =?UTF-8?q?enerate=20=E7=9A=84=E6=97=B6=E9=97=B4=E5=A4=8D=E6=9D=82?= =?UTF-8?q?=E5=BA=A6=EF=BC=8CO(n^2)=20=E9=99=8D=E4=B8=BA=20O(n)=20?= =?UTF-8?q?=E4=B8=8D=E5=85=BC=E5=AE=B9=E7=9A=84=E5=8A=9F=E8=83=BD=E6=9B=B4?= =?UTF-8?q?=E6=94=B9=EF=BC=9A=20-=20addEmoji=20=E4=BB=A5=E5=89=8D=E5=BD=93?= =?UTF-8?q?=E9=81=87=E5=88=B0=E6=9F=90=E4=B8=AA=20emoji=20=E4=B8=BA?= =?UTF-8?q?=E7=A9=BA=E6=97=B6=EF=BC=8C=E4=BC=9A=E8=B7=B3=E8=BF=87=E5=B9=B6?= =?UTF-8?q?=E7=BB=A7=E7=BB=AD=E4=B8=8B=E4=B8=80=E4=B8=AA=E6=AD=A3=E5=88=99?= =?UTF-8?q?=E5=8C=B9=E9=85=8D=EF=BC=8C=E7=8E=B0=E5=9C=A8=20=20=20=E4=B8=8D?= =?UTF-8?q?=E4=BC=9A=E8=B7=B3=E8=BF=87=EF=BC=8C=E5=BD=93=E5=8C=B9=E9=85=8D?= =?UTF-8?q?=E6=88=90=E5=8A=9F=E6=97=B6=E4=BC=9A=E7=9B=B4=E6=8E=A5=E5=81=9C?= =?UTF-8?q?=E6=AD=A2=E5=90=8E=E7=BB=AD=E5=8C=B9=E9=85=8D=EF=BC=8C=E4=B8=8D?= =?UTF-8?q?=E6=B7=BB=E5=8A=A0=20emoji?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/config/regmatch.h | 48 +++++++ src/generator/config/nodemanip.cpp | 43 +++--- src/generator/config/nodemanip.h | 9 +- src/generator/config/subexport.cpp | 204 ++++++++++++++++++----------- src/handler/interfaces.cpp | 11 +- src/utils/regexp.cpp | 137 ++++++++++++++++++- src/utils/regexp.h | 20 +++ src/version.h | 2 +- 8 files changed, 368 insertions(+), 106 deletions(-) diff --git a/src/config/regmatch.h b/src/config/regmatch.h index 16d854875..f95ca2782 100644 --- a/src/config/regmatch.h +++ b/src/config/regmatch.h @@ -2,12 +2,60 @@ #define REGMATCH_H_INCLUDED #include "def.h" +#include +using jp = jpcre2::select; + +class RegexWrapper { +private: + std::optional _reg; + std::optional _reg_non_multiline; + std::optional _reg_full_match; +public: + const std::string pattern; + RegexWrapper(const std::string &pattern) : pattern(pattern) {} + const jp::Regex ®() { + if (!_reg) { + _reg = jp::Regex(); + _reg->setPattern(pattern).addModifier("m").addPcre2Option(PCRE2_UTF|PCRE2_ALT_BSUX).compile(); + } + return *_reg; + }; + const jp::Regex ®_non_multiline() { + if (!_reg_non_multiline) { + _reg_non_multiline = jp::Regex(); + _reg_non_multiline->setPattern(pattern).addPcre2Option(PCRE2_UTF|PCRE2_ALT_BSUX).compile(); + } + return *_reg_non_multiline; + }; + const jp::Regex ®_full_match() { + if (!_reg_full_match) { + _reg_full_match = jp::Regex(); + _reg_full_match->setPattern(pattern).addModifier("m").addPcre2Option(PCRE2_ANCHORED|PCRE2_ENDANCHORED|PCRE2_UTF).compile(); + } + return *_reg_full_match; + }; + bool empty() const { + return pattern.empty(); + } +}; struct RegexMatchConfig { String Match; String Replace; String Script; + + std::optional target; + std::optional real_rule; + RegexWrapper ®_wrapper() { + if (!real_rule) { + real_rule = RegexWrapper(Match); + } + return *real_rule; + } + bool empty() { + return reg_wrapper().empty(); + } }; using RegexMatchConfigs = std::vector; diff --git a/src/generator/config/nodemanip.cpp b/src/generator/config/nodemanip.cpp index 60ff77dee..0ef651f33 100644 --- a/src/generator/config/nodemanip.cpp +++ b/src/generator/config/nodemanip.cpp @@ -20,7 +20,7 @@ extern Settings global; -bool applyMatcher(const std::string &rule, std::string &real_rule, const Proxy &node); +// bool applyMatcher(const std::string &rule, std::string &real_rule, const Proxy &node); int explodeConf(const std::string &filepath, std::vector &nodes) { @@ -35,8 +35,8 @@ void copyNodes(std::vector &source, std::vector &dest) int addNodes(std::string link, std::vector &allNodes, int groupID, parse_settings &parse_set) { std::string &proxy = *parse_set.proxy, &subInfo = *parse_set.sub_info; - string_array &exclude_remarks = *parse_set.exclude_remarks; - string_array &include_remarks = *parse_set.include_remarks; + RegexMatchConfigs &exclude_remarks = *parse_set.exclude_remarks; + RegexMatchConfigs &include_remarks = *parse_set.include_remarks; RegexMatchConfigs &stream_rules = *parse_set.stream_rules; RegexMatchConfigs &time_rules = *parse_set.time_rules; string_icase_map *request_headers = parse_set.request_header; @@ -231,19 +231,18 @@ int addNodes(std::string link, std::vector &allNodes, int groupID, parse_ return 0; } -bool chkIgnore(const Proxy &node, string_array &exclude_remarks, string_array &include_remarks) +bool chkIgnore(const Proxy &node, RegexMatchConfigs &exclude_remarks, RegexMatchConfigs &include_remarks) { bool excluded = false, included = false; //std::string remarks = UTF8ToACP(node.remarks); //std::string remarks = node.remarks; //writeLog(LOG_TYPE_INFO, "Comparing exclude remarks..."); - excluded = std::any_of(exclude_remarks.cbegin(), exclude_remarks.cend(), [&node](const auto &x) + excluded = std::any_of(exclude_remarks.begin(), exclude_remarks.end(), [&node](const auto &x) { - std::string real_rule; - if(applyMatcher(x, real_rule, node)) + if(applyMatcher(x, node)) { - if(real_rule.empty()) return true; - return regFind(node.Remark, real_rule); + if(x.empty()) return true; + return regFind(node.Remark, x); } else return false; @@ -251,13 +250,12 @@ bool chkIgnore(const Proxy &node, string_array &exclude_remarks, string_array &i if(include_remarks.size() != 0) { //writeLog(LOG_TYPE_INFO, "Comparing include remarks..."); - included = std::any_of(include_remarks.cbegin(), include_remarks.cend(), [&node](const auto &x) + included = std::any_of(include_remarks.begin(), include_remarks.end(), [&node](const auto &x) { - std::string real_rule; - if(applyMatcher(x, real_rule, node)) + if(applyMatcher(x, node)) { - if(real_rule.empty()) return true; - return regFind(node.Remark, real_rule); + if(x.empty()) return true; + return regFind(node.Remark, x); } else return false; @@ -271,7 +269,7 @@ bool chkIgnore(const Proxy &node, string_array &exclude_remarks, string_array &i return excluded || !included; } -void filterNodes(std::vector &nodes, string_array &exclude_remarks, string_array &include_remarks, int groupID) +void filterNodes(std::vector &nodes, RegexMatchConfigs &exclude_remarks, RegexMatchConfigs &include_remarks, int groupID) { int node_index = 0; std::vector::iterator iter = nodes.begin(); @@ -377,7 +375,7 @@ void filterNodes(std::vector &nodes, string_array &exclude_remarks, strin void nodeRename(Proxy &node, const RegexMatchConfigs &rename_array, extra_settings &ext) { - std::string &remark = node.Remark, original_remark = node.Remark, returned_remark, real_rule; + std::string &remark = node.Remark, original_remark = node.Remark, returned_remark; for(const RegexMatchConfig &x : rename_array) { @@ -403,8 +401,8 @@ void nodeRename(Proxy &node, const RegexMatchConfigs &rename_array, extra_settin }, global.scriptCleanContext); continue; } - if(applyMatcher(x.Match, real_rule, node) && real_rule.size()) - remark = regReplace(remark, real_rule, x.Replace); + if(applyMatcher(x, node) && !x.empty()) + remark = regReplace(remark, x); } if(remark.empty()) remark = original_remark; @@ -429,7 +427,7 @@ std::string removeEmoji(const std::string &orig_remark) std::string addEmoji(const Proxy &node, const RegexMatchConfigs &emoji_array, extra_settings &ext) { - std::string real_rule, ret; + std::string ret; for(const RegexMatchConfig &x : emoji_array) { @@ -458,10 +456,11 @@ std::string addEmoji(const Proxy &node, const RegexMatchConfigs &emoji_array, ex return result; continue; } - if(x.Replace.empty()) - continue; - if(applyMatcher(x.Match, real_rule, node) && real_rule.size() && regFind(node.Remark, real_rule)) + if(applyMatcher(x, node) && !x.empty() && regFind(node.Remark, x)) { + if(x.Replace.empty()) + return node.Remark; return x.Replace + " " + node.Remark; + } } return node.Remark; } diff --git a/src/generator/config/nodemanip.h b/src/generator/config/nodemanip.h index 5bce6363a..22f7f3f88 100644 --- a/src/generator/config/nodemanip.h +++ b/src/generator/config/nodemanip.h @@ -17,8 +17,8 @@ struct parse_settings { std::string *proxy = nullptr; - string_array *exclude_remarks = nullptr; - string_array *include_remarks = nullptr; + RegexMatchConfigs *exclude_remarks = nullptr; + RegexMatchConfigs *include_remarks = nullptr; RegexMatchConfigs *stream_rules = nullptr; RegexMatchConfigs *time_rules = nullptr; std::string *sub_info = nullptr; @@ -31,8 +31,9 @@ struct parse_settings }; int addNodes(std::string link, std::vector &allNodes, int groupID, parse_settings &parse_set); -void filterNodes(std::vector &nodes, string_array &exclude_remarks, string_array &include_remarks, int groupID); -bool applyMatcher(const std::string &rule, std::string &real_rule, const Proxy &node); +void filterNodes(std::vector &nodes, RegexMatchConfigs &exclude_remarks, RegexMatchConfigs &include_remarks, int groupID); +// bool applyMatcher(const std::string &rule, std::string &real_rule, const Proxy &node); +bool applyMatcher(RegexMatchConfig &config, const Proxy &node); void preprocessNodes(std::vector &nodes, extra_settings &ext); #endif // NODEMANIP_H_INCLUDED diff --git a/src/generator/config/subexport.cpp b/src/generator/config/subexport.cpp index 22b15b074..023cffb5c 100644 --- a/src/generator/config/subexport.cpp +++ b/src/generator/config/subexport.cpp @@ -3,6 +3,8 @@ #include #include #include +#include +#include #include "config/regmatch.h" #include "generator/config/subexport.h" @@ -70,7 +72,12 @@ bool matchRange(const std::string &range, int target) { bool match = false; std::string range_begin_str, range_end_str; int range_begin, range_end; - static const std::string reg_num = "-?\\d+", reg_range = "(\\d+)-(\\d+)", reg_not = "\\!-?(\\d+)", reg_not_range = "\\!(\\d+)-(\\d+)", reg_less = "(\\d+)-", reg_more = "(\\d+)\\+"; + static const jp::Regex reg_num = RegexWrapper("-?\\d+").reg_full_match(), + reg_range = RegexWrapper("(\\d+)-(\\d+)").reg_full_match(), + reg_not = RegexWrapper("\\!-?(\\d+)").reg_full_match(), + reg_not_range = RegexWrapper("\\!(\\d+)-(\\d+)").reg_full_match(), + reg_less = RegexWrapper("(\\d+)-").reg_full_match(), + reg_more = RegexWrapper("(\\d+)\\+").reg_full_match(); for (std::string &x: vArray) { if (regMatch(x, reg_num)) { if (to_int(x, INT_MAX) == target) @@ -103,51 +110,85 @@ bool matchRange(const std::string &range, int target) { return match; } -bool applyMatcher(const std::string &rule, std::string &real_rule, const Proxy &node) { - std::string target, ret_real_rule; - static const std::string groupid_regex = R"(^!!(?:GROUPID|INSERT)=([\d\-+!,]+)(?:!!(.*))?$)", group_regex = R"(^!!(?:GROUP)=(.+?)(?:!!(.*))?$)"; - static const std::string type_regex = R"(^!!(?:TYPE)=(.+?)(?:!!(.*))?$)", port_regex = R"(^!!(?:PORT)=(.+?)(?:!!(.*))?$)", server_regex = R"(^!!(?:SERVER)=(.+?)(?:!!(.*))?$)"; - static const std::map types = {{ProxyType::Shadowsocks, "SS"}, - {ProxyType::ShadowsocksR, "SSR"}, - {ProxyType::VMess, "VMESS"}, - {ProxyType::Trojan, "TROJAN"}, - {ProxyType::Snell, "SNELL"}, - {ProxyType::HTTP, "HTTP"}, - {ProxyType::HTTPS, "HTTPS"}, - {ProxyType::SOCKS5, "SOCKS5"}, - {ProxyType::WireGuard, "WIREGUARD"}, - {ProxyType::VLESS, "VLESS"}, - {ProxyType::Hysteria, "HYSTERIA"}, - {ProxyType::Hysteria2, "HYSTERIA2"}}; +const jp::Regex groupid_regex = RegexWrapper(R"(^!!(?:GROUPID|INSERT)=([\d\-+!,]+)(?:!!(.*))?$)").reg(); +const jp::Regex group_regex = RegexWrapper(R"(^!!(?:GROUP)=(.+?)(?:!!(.*))?$)").reg(); +const jp::Regex type_regex = RegexWrapper(R"(^!!(?:TYPE)=(.+?)(?:!!(.*))?$)").reg(); +const jp::Regex port_regex = RegexWrapper(R"(^!!(?:PORT)=(.+?)(?:!!(.*))?$)").reg(); +const jp::Regex server_regex = RegexWrapper(R"(^!!(?:SERVER)=(.+?)(?:!!(.*))?$)").reg(); +const std::map types = { + {ProxyType::Shadowsocks, "SS"}, + {ProxyType::ShadowsocksR, "SSR"}, + {ProxyType::VMess, "VMESS"}, + {ProxyType::Trojan, "TROJAN"}, + {ProxyType::Snell, "SNELL"}, + {ProxyType::HTTP, "HTTP"}, + {ProxyType::HTTPS, "HTTPS"}, + {ProxyType::SOCKS5, "SOCKS5"}, + {ProxyType::WireGuard, "WIREGUARD"}, + {ProxyType::VLESS, "VLESS"}, + {ProxyType::Hysteria, "HYSTERIA"}, + {ProxyType::Hysteria2, "HYSTERIA2"} +}; + +// bool applyMatcher(const std::string &rule, std::string &real_rule, const Proxy &node) { +// std::string target; +// if (startsWith(rule, "!!GROUP=")) { +// regGetMatch(rule, group_regex, 3, 0, &target, &real_rule); +// return regFind(node.Group, target); +// } else if (startsWith(rule, "!!GROUPID=") || startsWith(rule, "!!INSERT=")) { +// int dir = startsWith(rule, "!!INSERT=") ? -1 : 1; +// regGetMatch(rule, groupid_regex, 3, 0, &target, &real_rule); +// return matchRange(target, dir * node.GroupId); +// } else if (startsWith(rule, "!!TYPE=")) { +// regGetMatch(rule, type_regex, 3, 0, &target, &real_rule); +// if (node.Type == ProxyType::Unknown) +// return false; +// return regMatch(types.at(node.Type), target); +// } else if (startsWith(rule, "!!PORT=")) { +// regGetMatch(rule, port_regex, 3, 0, &target, &real_rule); +// return matchRange(target, node.Port); +// } else if (startsWith(rule, "!!SERVER=")) { +// regGetMatch(rule, server_regex, 3, 0, &target, &real_rule); +// return regFind(node.Hostname, target); +// } else +// real_rule = rule; +// return true; +// } + +void extract_rule_once(RegexMatchConfig &config, const jp::Regex ®ex) { + if (config.target) return; + std::string target, real_rule; + regGetMatch(config.Match, regex, 3, 0, &target, &real_rule); + config.target = RegexWrapper(target); + config.real_rule = RegexWrapper(real_rule); +} + +bool applyMatcher(RegexMatchConfig &config, const Proxy &node) { + const std::string &rule = config.Match; + RegexWrapper &target = *config.target; if (startsWith(rule, "!!GROUP=")) { - regGetMatch(rule, group_regex, 3, 0, &target, &ret_real_rule); - real_rule = ret_real_rule; + extract_rule_once(config, group_regex); return regFind(node.Group, target); } else if (startsWith(rule, "!!GROUPID=") || startsWith(rule, "!!INSERT=")) { int dir = startsWith(rule, "!!INSERT=") ? -1 : 1; - regGetMatch(rule, groupid_regex, 3, 0, &target, &ret_real_rule); - real_rule = ret_real_rule; - return matchRange(target, dir * node.GroupId); + extract_rule_once(config, groupid_regex); + return matchRange(target.pattern, dir * node.GroupId); } else if (startsWith(rule, "!!TYPE=")) { - regGetMatch(rule, type_regex, 3, 0, &target, &ret_real_rule); - real_rule = ret_real_rule; + extract_rule_once(config, type_regex); if (node.Type == ProxyType::Unknown) return false; return regMatch(types.at(node.Type), target); } else if (startsWith(rule, "!!PORT=")) { - regGetMatch(rule, port_regex, 3, 0, &target, &ret_real_rule); - real_rule = ret_real_rule; - return matchRange(target, node.Port); + extract_rule_once(config, port_regex); + return matchRange(target.pattern, node.Port); } else if (startsWith(rule, "!!SERVER=")) { - regGetMatch(rule, server_regex, 3, 0, &target, &ret_real_rule); - real_rule = ret_real_rule; + extract_rule_once(config, server_regex); return regFind(node.Hostname, target); - } else - real_rule = rule; + } return true; } -void processRemark(std::string &remark, const string_array &remarks_list, bool proc_comma = true) { +std::pair processRemark(std::string &remark, std::unordered_map &remarks_list, bool proc_comma = true) { // Replace every '=' with '-' in the remark string to avoid parse errors from the clients. // Surge is tested to yield an error when handling '=' in the remark string, // not sure if other clients have the same problem. @@ -159,19 +200,24 @@ void processRemark(std::string &remark, const string_array &remarks_list, bool p remark.append("\""); } } - std::string tempRemark = remark; - int cnt = 2; - while (std::find(remarks_list.cbegin(), remarks_list.cend(), tempRemark) != remarks_list.cend()) { - tempRemark = remark + " " + std::to_string(cnt); + int &cnt = remarks_list[remark]; + if (cnt == 0) { + return { cnt, cnt }; + } + for (;;) { + std::string tempRemark = remark + " " + std::to_string(cnt + 1); + int &cnt2 = remarks_list[tempRemark]; + if (cnt2 == 0) { + remark = tempRemark; + return { cnt, cnt2 }; + } cnt++; } - remark = tempRemark; } void -groupGenerate(const std::string &rule, std::vector &nodelist, string_array &filtered_nodelist, bool add_direct, +groupGenerate(const std::string &rule, std::vector &nodelist, string_array &filtered_nodelist, std::unordered_set &vis, bool add_direct, extra_settings &ext) { - std::string real_rule; if (startsWith(rule, "[]") && add_direct) { filtered_nodelist.emplace_back(rule.substr(2)); } @@ -192,9 +238,9 @@ groupGenerate(const std::string &rule, std::vector &nodelist, string_arra } #endif // NO_JS_RUNTIME else { + RegexMatchConfig config = { rule }; for (Proxy &x: nodelist) { - if (applyMatcher(rule, real_rule, x) && (real_rule.empty() || regFind(x.Remark, real_rule)) && - std::find(filtered_nodelist.begin(), filtered_nodelist.end(), x.Remark) == filtered_nodelist.end()) + if (applyMatcher(config, x) && (config.empty() || regFind(x.Remark, config)) && vis.emplace(&x).second) filtered_nodelist.emplace_back(x.Remark); } } @@ -205,7 +251,7 @@ proxyToClash(std::vector &nodes, YAML::Node &yamlnode, const ProxyGroupCo extra_settings &ext) { YAML::Node proxies, original_groups; std::vector nodelist; - string_array remarks_list; + std::unordered_map remarks_list; /// proxies style bool block = false, compact = false; switch (hash_(ext.clash_proxies_style)) { @@ -228,7 +274,7 @@ proxyToClash(std::vector &nodes, YAML::Node &yamlnode, const ProxyGroupCo if (ext.append_proxy_type) x.Remark = "[" + type + "] " + x.Remark; - processRemark(x.Remark, remarks_list, false); + auto [cnt, cnt2] = processRemark(x.Remark, remarks_list, false); tribool udp = ext.udp; tribool xudp = ext.xudp; @@ -588,7 +634,7 @@ proxyToClash(std::vector &nodes, YAML::Node &yamlnode, const ProxyGroupCo else singleproxy.SetStyle(YAML::EmitterStyle::Flow); proxies.push_back(singleproxy); - remarks_list.emplace_back(x.Remark); + cnt++; cnt2 = 1; nodelist.emplace_back(x); } @@ -611,6 +657,7 @@ proxyToClash(std::vector &nodes, YAML::Node &yamlnode, const ProxyGroupCo for (const ProxyGroupConfig &x: extra_proxy_group) { YAML::Node singlegroup; string_array filtered_nodelist; + std::unordered_set vis; singlegroup["name"] = x.Name; singlegroup["type"] = x.TypeStr(); @@ -640,7 +687,7 @@ proxyToClash(std::vector &nodes, YAML::Node &yamlnode, const ProxyGroupCo singlegroup["disable-udp"] = x.DisableUdp.get(); for (const auto &y: x.Proxies) - groupGenerate(y, nodelist, filtered_nodelist, true, ext); + groupGenerate(y, nodelist, filtered_nodelist, vis, true, ext); if (!x.UsingProvider.empty()) singlegroup["use"] = x.UsingProvider; @@ -751,7 +798,7 @@ std::string proxyToSurge(std::vector &nodes, const std::string &base_conf std::string output_nodelist; std::vector nodelist; unsigned short local_port = 1080; - string_array remarks_list; + std::unordered_map remarks_list; ini.store_any_line = true; // filter out sections that requires direct-save @@ -778,7 +825,7 @@ std::string proxyToSurge(std::vector &nodes, const std::string &base_conf x.Remark = "[" + type + "] " + x.Remark; } - processRemark(x.Remark, remarks_list); + auto [cnt, cnt2] = processRemark(x.Remark, remarks_list); std::string &hostname = x.Hostname, &sni = x.ServerName, &username = x.Username, &password = x.Password, &method = x.EncryptMethod, &id = x.UserId, &transproto = x.TransferProtocol, &host = x.Host, &edge = x.Edge, &path = x.Path, &protocol = x.Protocol, &protoparam = x.ProtocolParam, &obfs = x.OBFS, &obfsparam = x.OBFSParam, &plugin = x.Plugin, &pluginopts = x.PluginOption; std::string port = std::to_string(x.Port); @@ -958,7 +1005,7 @@ std::string proxyToSurge(std::vector &nodes, const std::string &base_conf ini.set("{NONAME}", x.Remark + " = " + proxy); nodelist.emplace_back(x); } - remarks_list.emplace_back(x.Remark); + cnt++; cnt2 = 1; } if (ext.nodelist) @@ -968,6 +1015,7 @@ std::string proxyToSurge(std::vector &nodes, const std::string &base_conf ini.erase_section(); for (const ProxyGroupConfig &x: extra_proxy_group) { string_array filtered_nodelist; + std::unordered_set vis; std::string group; switch (x.Type) { @@ -989,7 +1037,7 @@ std::string proxyToSurge(std::vector &nodes, const std::string &base_conf } for (const auto &y: x.Proxies) - groupGenerate(y, nodelist, filtered_nodelist, true, ext); + groupGenerate(y, nodelist, filtered_nodelist, vis, true, ext); if (filtered_nodelist.empty()) filtered_nodelist.emplace_back("DIRECT"); @@ -1189,7 +1237,7 @@ void proxyToQuan(std::vector &nodes, INIReader &ini, std::vector nodelist; - string_array remarks_list; + std::unordered_map remarks_list; ini.set_current_section("SERVER"); ini.erase_section(); @@ -1199,7 +1247,7 @@ void proxyToQuan(std::vector &nodes, INIReader &ini, std::vector &nodes, INIReader &ini, std::vector &nodes, INIReader &ini, std::vector vis; std::string type; std::string singlegroup; std::string name, proxies; @@ -1357,7 +1406,7 @@ void proxyToQuan(std::vector &nodes, INIReader &ini, std::vector &nodes, INIReader &ini, std::vector nodelist; - string_array remarks_list; + std::unordered_map remarks_list; ini.set_current_section("server_local"); ini.erase_section(); @@ -1423,7 +1472,7 @@ void proxyToQuanX(std::vector &nodes, INIReader &ini, std::vector &nodes, INIReader &ini, std::vector &nodes, INIReader &ini, std::vector vis; switch (x.Type) { case ProxyGroupType::Select: @@ -1601,7 +1651,7 @@ void proxyToQuanX(std::vector &nodes, INIReader &ini, std::vector &nodes, INIReader &ini, std::vector nodelist; - string_array vArray, remarks_list; + string_array vArray; + std::unordered_map remarks_list; ini.set_current_section("Endpoint"); @@ -1772,7 +1823,7 @@ void proxyToMellow(std::vector &nodes, INIReader &ini, std::vector &nodes, INIReader &ini, std::vector &nodes, INIReader &ini, std::vector vis; url.clear(); proxy.clear(); @@ -1854,13 +1906,15 @@ void proxyToMellow(std::vector &nodes, INIReader &ini, std::vector &nodes, const std::string &base_conf, std::vector std::string output_nodelist; std::vector nodelist; - string_array remarks_list; + std::unordered_map remarks_list; ini.store_any_line = true; ini.add_direct_save_section("Plugin"); @@ -1912,7 +1966,7 @@ proxyToLoon(std::vector &nodes, const std::string &base_conf, std::vector std::string type = getProxyTypeName(x.Type); x.Remark = "[" + type + "] " + x.Remark; } - processRemark(x.Remark, remarks_list); + auto [cnt, cnt2] = processRemark(x.Remark, remarks_list); std::string &hostname = x.Hostname, &username = x.Username, &password = x.Password, &method = x.EncryptMethod, &plugin = x.Plugin, &pluginopts = x.PluginOption, &id = x.UserId, &transproto = x.TransferProtocol, &host = x.Host, &path = x.Path, &protocol = x.Protocol, &protoparam = x.ProtocolParam, &obfs = x.OBFS, &obfsparam = x.OBFSParam; std::string port = std::to_string(x.Port), aid = std::to_string(x.AlterId); @@ -2019,7 +2073,7 @@ proxyToLoon(std::vector &nodes, const std::string &base_conf, std::vector else { ini.set("{NONAME}", x.Remark + " = " + proxy); nodelist.emplace_back(x); - remarks_list.emplace_back(x.Remark); + cnt++; cnt2 = 1; } } @@ -2033,6 +2087,7 @@ proxyToLoon(std::vector &nodes, const std::string &base_conf, std::vector for (const ProxyGroupConfig &x: extra_proxy_group) { string_array filtered_nodelist; + std::unordered_set vis; std::string group, group_extra; switch (x.Type) { @@ -2053,7 +2108,7 @@ proxyToLoon(std::vector &nodes, const std::string &base_conf, std::vector } for (const auto &y: x.Proxies) - groupGenerate(y, nodelist, filtered_nodelist, true, ext); + groupGenerate(y, nodelist, filtered_nodelist, vis, true, ext); if (filtered_nodelist.empty()) filtered_nodelist.emplace_back("DIRECT"); @@ -2195,7 +2250,7 @@ proxyToSingBox(std::vector &nodes, rapidjson::Document &json, std::vector rapidjson::Document::AllocatorType &allocator = json.GetAllocator(); rapidjson::Value outbounds(rapidjson::kArrayType), route(rapidjson::kArrayType); std::vector nodelist; - string_array remarks_list; + std::unordered_map remarks_list; std::string search = " Mbps"; if (!ext.nodelist) { @@ -2212,7 +2267,7 @@ proxyToSingBox(std::vector &nodes, rapidjson::Document &json, std::vector if (ext.append_proxy_type) x.Remark = "[" + type + "] " + x.Remark; - processRemark(x.Remark, remarks_list, false); + auto [cnt, cnt2] = processRemark(x.Remark, remarks_list, false); tribool udp = ext.udp, tfo = ext.tfo, scv = ext.skip_cert_verify, xudp = ext.xudp; udp.define(x.UDP); @@ -2497,7 +2552,7 @@ proxyToSingBox(std::vector &nodes, rapidjson::Document &json, std::vector proxy.AddMember("tcp_fast_open", buildBooleanValue(tfo), allocator); } nodelist.push_back(x); - remarks_list.emplace_back(x.Remark); + cnt++; cnt2 = 1; outbounds.PushBack(proxy, allocator); } @@ -2508,6 +2563,7 @@ proxyToSingBox(std::vector &nodes, rapidjson::Document &json, std::vector for (const ProxyGroupConfig &x: extra_proxy_group) { string_array filtered_nodelist; + std::unordered_set vis; std::string type; switch (x.Type) { case ProxyGroupType::Select: { @@ -2524,7 +2580,7 @@ proxyToSingBox(std::vector &nodes, rapidjson::Document &json, std::vector continue; } for (const auto &y: x.Proxies) - groupGenerate(y, nodelist, filtered_nodelist, true, ext); + groupGenerate(y, nodelist, filtered_nodelist, vis, true, ext); if (filtered_nodelist.empty()) filtered_nodelist.emplace_back("DIRECT"); @@ -2556,8 +2612,8 @@ proxyToSingBox(std::vector &nodes, rapidjson::Document &json, std::vector global_group.AddMember("tag", "GLOBAL", allocator); global_group.AddMember("outbounds", rapidjson::Value(rapidjson::kArrayType), allocator); global_group["outbounds"].PushBack("DIRECT", allocator); - for (auto &x: remarks_list) { - global_group["outbounds"].PushBack(rapidjson::Value(x.c_str(), allocator), allocator); + for (auto &x: nodelist) { + global_group["outbounds"].PushBack(rapidjson::Value(x.Remark.c_str(), allocator), allocator); } outbounds.PushBack(global_group, allocator); } diff --git a/src/handler/interfaces.cpp b/src/handler/interfaces.cpp index bca6b7dd7..6a579f643 100644 --- a/src/handler/interfaces.cpp +++ b/src/handler/interfaces.cpp @@ -2,6 +2,7 @@ #include #include #include +#include #include #include @@ -554,8 +555,12 @@ std::string subconverter(RESPONSE_CALLBACK_ARGS) { parse_settings parse_set; parse_set.proxy = &proxy; - parse_set.exclude_remarks = &lExcludeRemarks; - parse_set.include_remarks = &lIncludeRemarks; + RegexMatchConfigs exclude_remarks(lExcludeRemarks.size()); + std::transform(lExcludeRemarks.begin(), lExcludeRemarks.end(), exclude_remarks.begin(), [](const std::string &str) { return RegexMatchConfig{str}; }); + RegexMatchConfigs include_remarks(lIncludeRemarks.size()); + std::transform(lIncludeRemarks.begin(), lIncludeRemarks.end(), include_remarks.begin(), [](const std::string &str) { return RegexMatchConfig{str}; }); + parse_set.exclude_remarks = &exclude_remarks; + parse_set.include_remarks = &include_remarks; parse_set.stream_rules = &stream_temp; parse_set.time_rules = &time_temp; parse_set.sub_info = &subInfo; @@ -1012,7 +1017,7 @@ std::string surgeConfToClash(RESPONSE_CALLBACK_ARGS) { std::string subInfo; parse_settings parse_set; parse_set.proxy = &proxy; - parse_set.exclude_remarks = parse_set.include_remarks = &dummy_str_array; + parse_set.exclude_remarks = parse_set.include_remarks = &dummy_regex_array; parse_set.stream_rules = parse_set.time_rules = &dummy_regex_array; parse_set.request_header = &request.headers; parse_set.sub_info = &subInfo; diff --git a/src/utils/regexp.cpp b/src/utils/regexp.cpp index e18e89e91..16688af17 100644 --- a/src/utils/regexp.cpp +++ b/src/utils/regexp.cpp @@ -152,7 +152,7 @@ bool regFind(const std::string &src, const std::string &match) std::string regReplace(const std::string &src, const std::string &match, const std::string &rep, bool global, bool multiline) { jp::Regex reg; - reg.setPattern(match).addModifier(multiline ? "m" : "").addPcre2Option(PCRE2_UTF|PCRE2_MULTILINE|PCRE2_ALT_BSUX).compile(); + reg.setPattern(match).addModifier(multiline ? "m" : "").addPcre2Option(PCRE2_UTF|PCRE2_ALT_BSUX).compile(); if(!reg) return src; return reg.replace(src, rep, global ? "gEx" : "Ex"); @@ -219,7 +219,140 @@ std::vector regGetAllMatch(const std::string &src, const std::strin //#endif // USE_STD_REGEX +const jp::Regex _reg_trim = RegexWrapper(R"(^\s*([\s\S]*?)\s*$)").reg_non_multiline(); + std::string regTrim(const std::string &src) { - return regReplace(src, R"(^\s*([\s\S]*)\s*$)", "$1", false, false); + return regReplace(src, _reg_trim, "$1", false); +} + + +bool regFind(const std::string &src, const jp::Regex ®) { + if(!reg) + return false; + return reg.match(src, "g"); +} +std::string regReplace(const std::string &src, const jp::Regex ®, const std::string &rep, bool global) { + if(!reg) + return src; + return reg.replace(src, rep, global ? "gEx" : "Ex"); +} +bool regMatch(const std::string &src, const jp::Regex ®) { + if(!reg) + return false; + return reg.match(src, "g"); +} +int regGetMatch(const std::string &src, const jp::Regex ®, size_t group_count, ...) { + auto result = regGetAllMatch(src, reg, false); + if(result.empty()) + return -1; + va_list vl; + va_start(vl, group_count); + size_t index = 0; + while(group_count) + { + std::string* arg = va_arg(vl, std::string*); + if(arg != nullptr) + *arg = std::move(result[index]); + index++; + group_count--; + if(result.size() <= index) + break; + } + va_end(vl); + return 0; +} +std::vector regGetAllMatch(const std::string &src, const jp::Regex ®, bool group_only) { + jp::VecNum vec_num; + jp::RegexMatch rm; + size_t count = rm.setRegexObject(®).setSubject(src).setNumberedSubstringVector(&vec_num).setModifier("gm").match(); + std::vector result; + if(!count) + return result; + size_t begin = 0; + if(group_only) + begin = 1; + size_t index = begin, match_index = 0; + while(true) + { + if(vec_num.size() <= match_index) + break; + if(vec_num[match_index].size() <= index) + { + match_index++; + index = begin; + continue; + } + result.push_back(std::move(vec_num[match_index][index])); + index++; + } + return result; +} + + +bool regFind(const std::string &src, RegexWrapper ®_wrapper) { + return regFind(src, reg_wrapper.reg()); +} +std::string regReplace(const std::string &src, RegexWrapper ®_wrapper, const std::string &rep, bool global, bool multiline) { + return regReplace(src, multiline ? reg_wrapper.reg() : reg_wrapper.reg_non_multiline(), rep, global); +} +bool regMatch(const std::string &src, RegexWrapper ®_wrapper) { + return regMatch(src, reg_wrapper.reg_full_match()); +} +int regGetMatch(const std::string &src, RegexWrapper ®_wrapper, size_t group_count, ...) { + auto result = regGetAllMatch(src, reg_wrapper, false); + if(result.empty()) + return -1; + va_list vl; + va_start(vl, group_count); + size_t index = 0; + while(group_count) + { + std::string* arg = va_arg(vl, std::string*); + if(arg != nullptr) + *arg = std::move(result[index]); + index++; + group_count--; + if(result.size() <= index) + break; + } + va_end(vl); + return 0; +} +std::vector regGetAllMatch(const std::string &src, RegexWrapper ®_wrapper, bool group_only) { + return regGetAllMatch(src, reg_wrapper.reg(), group_only); +} + + +bool regFind(const std::string &src, RegexMatchConfig &config) { + return regFind(src, config.reg_wrapper()); +} +std::string regReplace(const std::string &src, RegexMatchConfig &config, bool global, bool multiline) { + return regReplace(src, config.reg_wrapper(), config.Replace, global, multiline); +} +bool regMatch(const std::string &src, RegexMatchConfig &config) { + return regMatch(src, config.reg_wrapper()); +} +int regGetMatch(const std::string &src, RegexMatchConfig &config, size_t group_count, ...) { + auto result = regGetAllMatch(src, config, false); + if(result.empty()) + return -1; + va_list vl; + va_start(vl, group_count); + size_t index = 0; + while(group_count) + { + std::string* arg = va_arg(vl, std::string*); + if(arg != nullptr) + *arg = std::move(result[index]); + index++; + group_count--; + if(result.size() <= index) + break; + } + va_end(vl); + return 0; +} +std::vector regGetAllMatch(const std::string &src, RegexMatchConfig &config, bool group_only) { + return regGetAllMatch(src, config.reg_wrapper(), group_only); } diff --git a/src/utils/regexp.h b/src/utils/regexp.h index 06558a532..25e23a4cd 100644 --- a/src/utils/regexp.h +++ b/src/utils/regexp.h @@ -3,6 +3,8 @@ #include +#include "config/regmatch.h" + bool regValid(const std::string ®); bool regFind(const std::string &src, const std::string &match); std::string regReplace(const std::string &src, const std::string &match, const std::string &rep, bool global = true, bool multiline = true); @@ -11,4 +13,22 @@ int regGetMatch(const std::string &src, const std::string &match, size_t group_c std::vector regGetAllMatch(const std::string &src, const std::string &match, bool group_only = false); std::string regTrim(const std::string &src); +bool regFind(const std::string &src, const jp::Regex ®); +std::string regReplace(const std::string &src, const jp::Regex ®, const std::string &rep, bool global = true); +bool regMatch(const std::string &src, const jp::Regex ®); +int regGetMatch(const std::string &src, const jp::Regex ®, size_t group_count, ...); +std::vector regGetAllMatch(const std::string &src, const jp::Regex ®, bool group_only = false); + +bool regFind(const std::string &src, RegexWrapper ®_wrapper); +std::string regReplace(const std::string &src, RegexWrapper ®_wrapper, const std::string &rep, bool global = true, bool multiline = true); +bool regMatch(const std::string &src, RegexWrapper ®_wrapper); +int regGetMatch(const std::string &src, RegexWrapper ®_wrapper, size_t group_count, ...); +std::vector regGetAllMatch(const std::string &src, RegexWrapper ®_wrapper, bool group_only = false); + +bool regFind(const std::string &src, RegexMatchConfig &config); +std::string regReplace(const std::string &src, RegexMatchConfig &config, bool global = true, bool multiline = true); +bool regMatch(const std::string &src, RegexMatchConfig &config); +int regGetMatch(const std::string &src, RegexMatchConfig &config, size_t group_count, ...); +std::vector regGetAllMatch(const std::string &src, RegexMatchConfig &config, bool group_only = false); + #endif // REGEXP_H_INCLUDED diff --git a/src/version.h b/src/version.h index 3634b203e..5205fb635 100644 --- a/src/version.h +++ b/src/version.h @@ -1,6 +1,6 @@ #ifndef VERSION_H_INCLUDED #define VERSION_H_INCLUDED -#define VERSION "v0.10.3 Mannix" +#define VERSION "v0.11.0-a1 Mannix" #endif // VERSION_H_INCLUDED