diff --git a/include/config.h b/include/config.h index a8c460b50..592bb82bd 100644 --- a/include/config.h +++ b/include/config.h @@ -18,6 +18,7 @@ #include "regchannel.h" #include "users.h" #include "opertype.h" +#include "miscutils.h" namespace Configuration { @@ -28,6 +29,7 @@ namespace Configuration public: typedef Anope::map ItemMap; typedef Anope::multimap BlockMap; + typedef Anope::iterator_range BlockList; private: Anope::string name; @@ -42,6 +44,7 @@ namespace Configuration Block(const Anope::string &); const Anope::string &GetName() const; int CountBlock(const Anope::string &name) const; + BlockList GetBlocks(const Anope::string &name) const; const Block &GetBlock(const Anope::string &name, int num = 0) const; Block *GetMutableBlock(const Anope::string &name, int num = 0); diff --git a/include/miscutils.h b/include/miscutils.h new file mode 100644 index 000000000..b4e626593 --- /dev/null +++ b/include/miscutils.h @@ -0,0 +1,87 @@ +// Anope IRC Services +// +// Copyright (C) 2003-2026 Anope Contributors +// +// Anope is free software. You can use, modify, and/or distribute it under the +// terms of version 2 of the GNU General Public License. See docs/LICENSE.txt +// for the complete terms of this license and docs/AUTHORS.txt for a list of +// contributors. +// +// Based on the original code of Epona by Lara +// Based on the original code of Services by Andy Church +// +// SPDX-License-Identifier: GPL-2.0-only + +#pragma once + +#include +#include + +namespace Anope +{ + template + class iterator_range; + + /** Returns a range containing all elements equivalent to \p value. + * @param collection The collection to search within. + * @param value The value to search for. + */ + template + auto equal_range(const Collection& collection, const Value& value) + { + return iterator_range(collection.equal_range(value)); + } + + /** Returns a range representing a reverse iterator for the specified colleciton. + * @param collection The collection to create a reverse iterator for. + */ + template + auto reverse_range(const Collection& collection) + { + return iterator_range(collection.rbegin(), collection.rend()); + } +} + +/** Represents a range of iterators. */ +template +class Anope::iterator_range final +{ +private: + /** An iterator which points to the start of the range. */ + const Iterator begini; + + /* An iterator which points to one past the end of the range. */ + const Iterator endi; + +public: + /** Initialises a new iterator range with the specified iterators. + * @param begin An iterator which points to the start of the range. + * @param end An iterator which points to one past the end of the range. + */ + explicit iterator_range(Iterator begin, Iterator end) + : begini(begin) + , endi(end) + { + } + + /** Initialises a new iterator range from a pair of iterators. + * @param range A pair of iterators in the format [first, last). + */ + explicit iterator_range(std::pair range) + : begini(range.first) + , endi(range.second) + { + } + + /** Determines whether the iterator range is empty. */ + bool empty() const { return begini == endi; } + + /** Retrieves an iterator which points to the start of the range. */ + const Iterator& begin() const { return begini; } + + /** Retrieves an iterator which points to one past the end of the range. */ + const Iterator& end() const { return endi; } + + /** Retrieves the number of hops within the iterator range. */ + typename std::iterator_traits::difference_type count() const { return std::distance(begini, endi); } +}; diff --git a/modules/chanserv/cs_access.cpp b/modules/chanserv/cs_access.cpp index 72c5ff3d0..15aac048c 100644 --- a/modules/chanserv/cs_access.cpp +++ b/modules/chanserv/cs_access.cpp @@ -963,10 +963,8 @@ public: { defaultLevels.clear(); - for (int i = 0; i < conf.CountBlock("privilege"); ++i) + for (const auto &[_, priv] : conf.GetBlocks("privilege")) { - const auto &priv = conf.GetBlock("privilege", i); - const Anope::string &pname = priv.Get("name"); Privilege *p = PrivilegeManager::FindPrivilege(pname); diff --git a/modules/chanserv/cs_flags.cpp b/modules/chanserv/cs_flags.cpp index 3d0067f1d..ffc9ac3b2 100644 --- a/modules/chanserv/cs_flags.cpp +++ b/modules/chanserv/cs_flags.cpp @@ -661,10 +661,8 @@ public: { defaultFlags.clear(); - for (int i = 0; i < conf.CountBlock("privilege"); ++i) + for (const auto &[_, priv] : conf.GetBlocks("privilege")) { - const auto &priv = conf.GetBlock("privilege", i); - const Anope::string &pname = priv.Get("name"); Privilege *p = PrivilegeManager::FindPrivilege(pname); diff --git a/modules/chanserv/cs_log.cpp b/modules/chanserv/cs_log.cpp index cb8e14021..00d5adb96 100644 --- a/modules/chanserv/cs_log.cpp +++ b/modules/chanserv/cs_log.cpp @@ -331,10 +331,8 @@ public: const auto &block = conf.GetModule(this); defaults.clear(); - for (int i = 0; i < block.CountBlock("default"); ++i) + for (const auto &[_, def] : block.GetBlocks("default")) { - const auto &def = block.GetBlock("default", i); - LogDefault ld; ld.service = def.Get("service"); diff --git a/modules/chanserv/cs_mode.cpp b/modules/chanserv/cs_mode.cpp index c84a9d672..dfb968c4b 100644 --- a/modules/chanserv/cs_mode.cpp +++ b/modules/chanserv/cs_mode.cpp @@ -999,10 +999,8 @@ public: { modes.clear(); - for (int i = 0; i < conf.CountBlock("command"); ++i) + for (const auto &[_, block] : conf.GetBlocks("command")) { - const auto &block = conf.GetBlock("command", i); - const Anope::string &cname = block.Get("name"), &cmd = block.Get("command"); diff --git a/modules/chanserv/cs_set_misc.cpp b/modules/chanserv/cs_set_misc.cpp index 7b87322b7..717b4a664 100644 --- a/modules/chanserv/cs_set_misc.cpp +++ b/modules/chanserv/cs_set_misc.cpp @@ -268,9 +268,9 @@ public: void OnReload(Configuration::Conf &conf) override { command_data.clear(); - for (int i = 0; i < conf.CountBlock("command"); ++i) + + for (const auto &[_, block] : conf.GetBlocks("command")) { - const auto &block = conf.GetBlock("command", i); if (block.Get("command") != "chanserv/set/misc") continue; diff --git a/modules/chanserv/cs_xop.cpp b/modules/chanserv/cs_xop.cpp index 297a8baf3..fd3de85a7 100644 --- a/modules/chanserv/cs_xop.cpp +++ b/modules/chanserv/cs_xop.cpp @@ -688,9 +688,8 @@ public: order.clear(); permissions.clear(); - for (int i = 0; i < conf.CountBlock("privilege"); ++i) + for (const auto &[_, block] : conf.GetBlocks("privilege")) { - const auto &block = conf.GetBlock("privilege", i); const Anope::string &pname = block.Get("name"); Privilege *p = PrivilegeManager::FindPrivilege(pname); @@ -704,9 +703,8 @@ public: permissions[xop].push_back(pname); } - for (int i = 0; i < conf.CountBlock("command"); ++i) + for (const auto &[_, block] : conf.GetBlocks("command")) { - const auto &block = conf.GetBlock("command", i); const Anope::string &cname = block.Get("name"), &cserv = block.Get("command"); if (cname.empty() || cserv != "chanserv/xop") diff --git a/modules/database/db_atheme.cpp b/modules/database/db_atheme.cpp index 04e7b3ced..881bbf127 100644 --- a/modules/database/db_atheme.cpp +++ b/modules/database/db_atheme.cpp @@ -1584,9 +1584,8 @@ public: const auto &modconf = conf.GetModule(this); csmiscdata.clear(); - for (auto idx = 0; idx < modconf.CountBlock("cs_set_misc"); ++idx) + for (const auto &[_, data] : modconf.GetBlocks("cs_set_misc")) { - const auto &data = modconf.GetBlock("cs_set_misc", idx); const auto &anope = data.Get("anope"); const auto &atheme = data.Get("atheme"); if (!anope.empty() && !atheme.empty()) @@ -1594,9 +1593,8 @@ public: } nsmiscdata.clear(); - for (auto idx = 0; idx < modconf.CountBlock("ns_set_misc"); ++idx) + for (const auto &[_, data] : modconf.GetBlocks("ns_set_misc")) { - const auto &data = modconf.GetBlock("ns_set_misc", idx); const auto &anope = data.Get("anope"); const auto &atheme = data.Get("atheme"); if (!anope.empty() && !atheme.empty()) @@ -1604,9 +1602,8 @@ public: } flags.clear(); - for (int i = 0; i < Config->CountBlock("privilege"); ++i) + for (const auto &[_, priv] : conf.GetBlocks("privilege")) { - const auto &priv = Config->GetBlock("privilege", i); const Anope::string &name = priv.Get("name"); const Anope::string &value = priv.Get("flag"); if (!name.empty() && !value.empty()) diff --git a/modules/dns.cpp b/modules/dns.cpp index ced55de54..64efd3800 100644 --- a/modules/dns.cpp +++ b/modules/dns.cpp @@ -1095,9 +1095,8 @@ public: refresh = block.Get("refresh", "3600"); std::vector > notify; - for (int i = 0; i < block.CountBlock("notify"); ++i) + for (const auto &[_, n] : block.GetBlocks("notify")) { - const auto &n = block.GetBlock("notify", i); auto nip = n.Get("ip"); auto nport = n.Get("port"); diff --git a/modules/dnsbl.cpp b/modules/dnsbl.cpp index 637ebe5d1..c7da11c92 100644 --- a/modules/dnsbl.cpp +++ b/modules/dnsbl.cpp @@ -126,9 +126,8 @@ public: this->add_to_akill = block.Get("add_to_akill", "yes"); this->blacklists.clear(); - for (int i = 0; i < block.CountBlock("blacklist"); ++i) + for (const auto &[_, bl] : block.GetBlocks("blacklist")) { - const auto &bl = block.GetBlock("blacklist", i); Blacklist blacklist; blacklist.name = bl.Get("name"); @@ -137,9 +136,8 @@ public: blacklist.bantime = bl.Get("time", "4h"); blacklist.reason = bl.Get("reason"); - for (int j = 0; j < bl.CountBlock("reply"); ++j) + for (const auto &[_, reply] : block.GetBlocks("reply")) { - const auto &reply = bl.GetBlock("reply", j); Blacklist::Reply r; r.code = reply.Get("code"); @@ -153,9 +151,8 @@ public: } this->exempts.clear(); - for (int i = 0; i < block.CountBlock("exempt"); ++i) + for (const auto &[_, bl] : block.GetBlocks("exempt")) { - const auto &bl = block.GetBlock("exempt", i); this->exempts.insert(bl.Get("ip")); } } diff --git a/modules/extra/ldap.cpp b/modules/extra/ldap.cpp index f8dffb358..a583d6a4d 100644 --- a/modules/extra/ldap.cpp +++ b/modules/extra/ldap.cpp @@ -654,10 +654,8 @@ public: } } - for (int i = 0; i < conf.CountBlock("ldap"); ++i) + for (const auto &[_, ldap] : conf.GetBlocks("ldap")) { - const auto &ldap = conf.GetBlock("ldap", i); - const Anope::string &connname = ldap.Get("name", "ldap/main"); if (this->LDAPServices.find(connname) == this->LDAPServices.end()) diff --git a/modules/extra/mysql.cpp b/modules/extra/mysql.cpp index 9d9309b35..9fdf781e3 100644 --- a/modules/extra/mysql.cpp +++ b/modules/extra/mysql.cpp @@ -298,9 +298,8 @@ public: } } - for (int i = 0; i < config.CountBlock("mysql"); ++i) + for (const auto &[_, block] : config.GetBlocks("mysql")) { - const auto &block = config.GetBlock("mysql", i); const Anope::string &connname = block.Get("name", "mysql/main"); if (this->MySQLServices.find(connname) == this->MySQLServices.end()) diff --git a/modules/extra/sqlite.cpp b/modules/extra/sqlite.cpp index 6003c9746..f77237949 100644 --- a/modules/extra/sqlite.cpp +++ b/modules/extra/sqlite.cpp @@ -145,9 +145,8 @@ public: } } - for (int i = 0; i < config.CountBlock("sqlite"); ++i) + for (const auto &[_, block] : config.GetBlocks("sqlite")) { - const auto &block = config.GetBlock("sqlite", i); Anope::string connname = block.Get("name", "sqlite/main"); if (this->SQLiteServices.find(connname) == this->SQLiteServices.end()) diff --git a/modules/extra/xmlrpc.cpp b/modules/extra/xmlrpc.cpp index 078e17bcf..f4aa456ae 100644 --- a/modules/extra/xmlrpc.cpp +++ b/modules/extra/xmlrpc.cpp @@ -331,10 +331,8 @@ public: throw ConfigException("Unable to find http reference, is httpd loaded?"); xmlrpcinterface.tokens.clear(); - for (int i = 0; i < modconf.CountBlock("token"); ++i) + for (const auto &[_, block] : modconf.GetBlocks("token")) { - const auto &block = modconf.GetBlock("token", i); - RPC::Token token; token.token = block.Get("token"); if (!token.token.empty()) diff --git a/modules/httpd.cpp b/modules/httpd.cpp index e4490a0c3..e03ec8ecb 100644 --- a/modules/httpd.cpp +++ b/modules/httpd.cpp @@ -372,11 +372,8 @@ public: const auto &conf = config.GetModule(this); std::set existing; - for (int i = 0; i < conf.CountBlock("httpd"); ++i) + for (const auto &[_, block] : conf.GetBlocks("httpd")) { - const auto &block = conf.GetBlock("httpd", i); - - const Anope::string &hname = block.Get("name", "httpd/main"); existing.insert(hname); diff --git a/modules/nickserv/ns_set_misc.cpp b/modules/nickserv/ns_set_misc.cpp index bc88904e6..bb6c3224d 100644 --- a/modules/nickserv/ns_set_misc.cpp +++ b/modules/nickserv/ns_set_misc.cpp @@ -330,9 +330,9 @@ public: command_data.clear(); items_by_priority.clear(); - for (int i = 0; i < conf.CountBlock("command"); ++i) + time_t default_priority = 0; + for (const auto &[_, block] : conf.GetBlocks("command")) { - const auto &block = conf.GetBlock("command", i); const Anope::string &cmd = block.Get("command"); if (cmd != "nickserv/set/misc" && cmd != "nickserv/saset/misc") continue; @@ -353,6 +353,8 @@ public: continue; } + default_priority += 1000; + data.set_description = desc; data.pattern = block.Get("misc_pattern"); data.syntax = block.Get("misc_syntax"); @@ -361,7 +363,7 @@ public: if (data.priority <= 0) { // If no priority is specified, go by order processed - data.priority = i * 1000; + data.priority = default_priority; } data.swhois = block.Get("misc_swhois"); items_by_priority.emplace_back(data.priority, item); diff --git a/modules/operserv/os_config.cpp b/modules/operserv/os_config.cpp index 7fa64357c..6aacf5125 100644 --- a/modules/operserv/os_config.cpp +++ b/modules/operserv/os_config.cpp @@ -84,9 +84,8 @@ public: lflist.AddColumn(_("Module Name")).AddColumn(_("Name")).AddColumn(_("Value")); lflist.SetFlexible(_("\002{}{module_name}}:{name}\002 = {value}")); - for (int i = 0; i < Config->CountBlock("module"); ++i) + for (const auto &[_, block] : Config->GetBlocks("module")) { - const auto &block = Config->GetBlock("module", i); const auto &items = block.GetItems(); if (items.size() <= 1) diff --git a/modules/operserv/os_forbid.cpp b/modules/operserv/os_forbid.cpp index 4f4e2f93e..7cc999d88 100644 --- a/modules/operserv/os_forbid.cpp +++ b/modules/operserv/os_forbid.cpp @@ -529,10 +529,8 @@ public: fileforbids.clear(); const auto &modconf = conf.GetModule(this); - for (auto i = 0; i < modconf.CountBlock("file"); ++i) + for (const auto &[_, fileblock] : modconf.GetBlocks("file")) { - const auto &fileblock = modconf.GetBlock("file", i); - const auto reasonstr = fileblock.Get("reason"); const auto typestr = fileblock.Get("type"); diff --git a/modules/proxyscan.cpp b/modules/proxyscan.cpp index 38fa2298a..8cf5b4ea4 100644 --- a/modules/proxyscan.cpp +++ b/modules/proxyscan.cpp @@ -309,9 +309,9 @@ public: } this->proxyscans.clear(); - for (int i = 0; i < config.CountBlock("proxyscan"); ++i) + + for (const auto &[_, block] : config.GetBlocks("proxyscan")) { - const auto &block = config.GetBlock("proxyscan", i); ProxyCheck p; Anope::string token; diff --git a/modules/rewrite.cpp b/modules/rewrite.cpp index 8fae2b8ef..c046013d6 100644 --- a/modules/rewrite.cpp +++ b/modules/rewrite.cpp @@ -173,10 +173,8 @@ public: { Rewrite::rewrites.clear(); - for (int i = 0; i < conf.CountBlock("command"); ++i) + for (const auto &[_, block] : conf.GetBlocks("command")) { - const auto &block = conf.GetBlock("command", i); - if (!block.Get("rewrite")) continue; diff --git a/modules/rpc/jsonrpc.cpp b/modules/rpc/jsonrpc.cpp index d59b4c080..b556dfe5c 100644 --- a/modules/rpc/jsonrpc.cpp +++ b/modules/rpc/jsonrpc.cpp @@ -313,10 +313,9 @@ public: throw ConfigException("Unable to find http reference, is httpd loaded?"); jsonrpcinterface.tokens.clear(); - for (int i = 0; i < modconf.CountBlock("token"); ++i) - { - const auto &block = modconf.GetBlock("token", i); + for (const auto &[_, block] : modconf.GetBlocks("token")) + { RPC::Token token; token.token = block.Get("token"); if (!token.token.empty()) diff --git a/src/config.cpp b/src/config.cpp index 48ef423b7..14d514f55 100644 --- a/src/config.cpp +++ b/src/config.cpp @@ -43,6 +43,11 @@ int Configuration::Block::CountBlock(const Anope::string &bname) const return blocks.count(bname); } +Configuration::Block::BlockList Configuration::Block::GetBlocks(const Anope::string &bname) const +{ + return Anope::equal_range(blocks, bname); +} + const Configuration::Block &Configuration::Block::GetBlock(const Anope::string &bname, int num) const { auto it = blocks.equal_range(bname); @@ -125,10 +130,8 @@ Configuration::Conf::Conf() : Configuration::Block("") this->LoadConf(ServicesConf); - for (int i = 0; i < this->CountBlock("include"); ++i) + for (const auto &[_, include] : this->GetBlocks("include")) { - const auto &include = this->GetBlock("include", i); - const Anope::string &type = include.Get("type"), &file = include.Get("name"); @@ -205,10 +208,8 @@ Configuration::Conf::Conf() : Configuration::Block("") this->TimeoutCheck = options.Get("timeoutcheck"); this->NickChars = networkinfo.Get("nick_chars"); - for (int i = 0; i < this->CountBlock("uplink"); ++i) + for (const auto &[_, uplink] : this->GetBlocks("uplink")) { - const auto &uplink = this->GetBlock("uplink", i); - int protocol; const Anope::string &protocolstr = uplink.Get("protocol", "ipv4"); if (protocolstr == "ipv4") @@ -238,10 +239,8 @@ Configuration::Conf::Conf() : Configuration::Block("") this->Uplinks.emplace_back(host, port, password, protocol); } - for (int i = 0; i < this->CountBlock("module"); ++i) + for (const auto &[_, module] : this->GetBlocks("module")) { - const auto &module = this->GetBlock("module", i); - const Anope::string &modname = module.Get("name"); ValidateNotEmptyOrSpaces("module", "name", modname); @@ -249,10 +248,8 @@ Configuration::Conf::Conf() : Configuration::Block("") this->ModulesAutoLoad.push_back(modname); } - for (int i = 0; i < this->CountBlock("opertype"); ++i) + for (const auto &[_, opertype] : this->GetBlocks("opertype")) { - const auto &opertype = this->GetBlock("opertype", i); - const Anope::string &oname = opertype.Get("name"), &modes = opertype.Get("modes"), &inherits = opertype.Get("inherits"), @@ -292,10 +289,8 @@ Configuration::Conf::Conf() : Configuration::Block("") this->MyOperTypes.push_back(ot); } - for (int i = 0; i < this->CountBlock("oper"); ++i) + for (const auto &[_, oper] : this->GetBlocks("oper")) { - const auto &oper = this->GetBlock("oper", i); - const Anope::string &nname = oper.Get("name"), &type = oper.Get("type"), &password = oper.Get("password"), @@ -330,10 +325,9 @@ Configuration::Conf::Conf() : Configuration::Block("") for (const auto &[_, bi] : *BotListByNick) bi->conf = false; - for (int i = 0; i < this->CountBlock("service"); ++i) - { - const auto &service = this->GetBlock("service", i); + for (const auto &[_, service] : this->GetBlocks("service")) + { const Anope::string &nick = service.Get("nick"), &user = service.Get("user", nick.lower()), &host = service.Get("host", servername), @@ -421,10 +415,8 @@ Configuration::Conf::Conf() : Configuration::Block("") } } - for (int i = 0; i < this->CountBlock("log"); ++i) + for (const auto &[_, log] : this->GetBlocks("log")) { - const auto &log = this->GetBlock("log", i); - int logage = log.Get("logage"); bool rawio = log.Get("rawio"); bool debug = log.Get("debug"); @@ -447,10 +439,8 @@ Configuration::Conf::Conf() : Configuration::Block("") for (const auto &[_, bi] : *BotListByNick) bi->commands.clear(); - for (int i = 0; i < this->CountBlock("command"); ++i) + for (const auto &[_, command] : this->GetBlocks("command")) { - const auto &command = this->GetBlock("command", i); - const Anope::string &service = command.Get("service"), &nname = command.Get("name"), &cmd = command.Get("command"), @@ -472,10 +462,8 @@ Configuration::Conf::Conf() : Configuration::Block("") } PrivilegeManager::ClearPrivileges(); - for (int i = 0; i < this->CountBlock("privilege"); ++i) + for (const auto &[_, privilege] : this->GetBlocks("privilege")) { - const auto &privilege = this->GetBlock("privilege", i); - const Anope::string &nname = privilege.Get("name"), &desc = privilege.Get("desc"); int rank = privilege.Get("rank"); @@ -483,10 +471,8 @@ Configuration::Conf::Conf() : Configuration::Block("") PrivilegeManager::AddPrivilege(Privilege(nname, desc, rank)); } - for (int i = 0; i < this->CountBlock("fantasy"); ++i) + for (const auto &[_, fantasy] : this->GetBlocks("fantasy")) { - const auto &fantasy = this->GetBlock("fantasy", i); - const Anope::string &nname = fantasy.Get("name"), &service = fantasy.Get("command"), &permission = fantasy.Get("permission"), @@ -504,10 +490,8 @@ Configuration::Conf::Conf() : Configuration::Block("") c.require_privilege = fantasy.Get("require_privilege", "yes"); } - for (int i = 0; i < this->CountBlock("command_group"); ++i) + for (const auto &[_, command_group] : this->GetBlocks("command_group")) { - const auto &command_group = this->GetBlock("command_group", i); - const Anope::string &nname = command_group.Get("name"), &description = command_group.Get("description"); @@ -1020,9 +1004,8 @@ Anope::string Configuration::Conf::ReplaceVars(const Anope::string &str, const C } auto found = false; - for (int i = 0; i < this->CountBlock("define"); ++i) + for (const auto &[_, define] : this->GetBlocks("define")) { - const auto &define = this->GetBlock("define", i); const auto defname = define.Get("name"); if (defname == var) { diff --git a/src/init.cpp b/src/init.cpp index 28487875e..45b6fd87f 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -537,8 +537,8 @@ bool Anope::Init(int ac, char **av) /* load modules */ Log() << "Loading modules..."; - for (int i = 0; i < Config->CountBlock("module"); ++i) - ModuleManager::LoadModule(Config->GetBlock("module", i).Get("name"), NULL); + for (const auto &[_, block] : Config->GetBlocks("module")) + ModuleManager::LoadModule(block.Get("name"), NULL); #ifndef _WIN32 /* If we're root, issue a warning now */