From 19e1e1382530768fd1fabd226b94ae5737c1e901 Mon Sep 17 00:00:00 2001 From: Egor Date: Thu, 12 Feb 2026 16:35:40 +0200 Subject: [PATCH] admin: generate bindings for rpl_ and err_ --- Makefile.am | 14 +++++++-- configure.ac | 3 ++ lua/init.lua | 4 +-- rplbindings.lua | 75 +++++++++++++++++++++++++++++++++++++++++++++++++ src/admin.cpp | 27 ++++++++++++------ src/common.hpp | 1 + src/irc.cpp | 2 +- src/ircfmt.cpp | 16 ++++++----- src/ircfmt.hpp | 2 +- 9 files changed, 122 insertions(+), 22 deletions(-) create mode 100755 rplbindings.lua diff --git a/Makefile.am b/Makefile.am index f62c282..43acfad 100644 --- a/Makefile.am +++ b/Makefile.am @@ -5,6 +5,7 @@ ACLOCAL_AMFLAGS=-I m4 --install bin_PROGRAMS = ircd dbtool ircd_SOURCES = \ src/admin.cpp src/admin.hpp \ + src/admin_rpl.hpp \ src/common.hpp \ src/crypt_blowfish.cpp src/crypt_blowfish.hpp \ src/db.cpp src/db.hpp \ @@ -25,11 +26,19 @@ dbtool_SOURCES = \ src/db.cpp src/db.hpp \ src/dbtool.cpp AM_CXXFLAGS = $(WARNINGCXXFLAGS) $(LIBUV_CFLAGS) $(LIBTLS_CFLAGS) $(LUA_CFLAGS) +AM_CPPFLAGS = -Isrc -I$(srcdir)/src LDADD = $(LIBUV_LIBS) $(LIBTLS_LIBS) $(LUA_LIBS) $(SQLITE3_LIBS) +src/admin.$(OBJEXT): src/admin_rpl.hpp +src/admin_rpl.hpp: src/ircfmt.hpp rplbindings.lua + @if test "x$(LUA)" = xno; then echo "No lua interpreter! Please set LUA="; exit 1; fi + $(AM_V_GEN)$(LUA) $(srcdir)/rplbindings.lua <$< >$@ +MAINTAINERCLEANFILES = src/admin_rpl.hpp + dist_ircddata_DATA = \ data/help.example.txt dist_ircdlua_DATA = \ + lua/autojoin.lua \ lua/channeldump.lua \ lua/ctcp.lua \ lua/init.lua \ @@ -47,7 +56,8 @@ EXTRA_DIST = \ LICENSES/GPL-2.0-or-later.txt \ LICENSES/MIT.txt \ LICENSES/Unlicense.txt \ - libtls.supp + libtls.supp \ + rplbindings.lua .PHONY: valgrind valgrind: ircd @@ -56,7 +66,7 @@ valgrind: ircd .PHONY: pch if ENABLE_PCH MOSTLYCLEANFILES = common.hpp.$(PCHEXT) -AM_CPPFLAGS = -Winvalid-pch `test -f common.hpp.$(PCHEXT) && printf $(PCHOPT) "common.hpp"` +AM_CPPFLAGS += -Winvalid-pch `test -f common.hpp.$(PCHEXT) && printf $(PCHOPT) "common.hpp"` common.hpp.$(PCHEXT): src/common.hpp $(AM_V_GEN)$(RM) $@; $(CXXCOMPILE) -x c++-header -o $@ $< pch: common.hpp.$(PCHEXT) diff --git a/configure.ac b/configure.ac index d833463..d3c2a68 100644 --- a/configure.ac +++ b/configure.ac @@ -59,4 +59,7 @@ PKG_HAVE_DEFINE_WITH_MODULES(LIBTLS, [libtls]) PKG_CHECK_MODULES(LUA, [lua5.4]) PKG_CHECK_MODULES(SQLITE3, [sqlite3]) +AC_ARG_VAR([LUA], [the Lua 5.4 interpreter]) +AS_IF([test "x$LUA" = x], [AC_PATH_PROGS([LUA], [lua5.4 lua54], [no])]) + AC_OUTPUT diff --git a/lua/init.lua b/lua/init.lua index 252be05..4ecb5f0 100644 --- a/lua/init.lua +++ b/lua/init.lua @@ -5,11 +5,11 @@ if not commands then end function dispatch_command(client, source, command, ...) if client.regstate ~= 2 then -- REG_REGISTERED - -- TODO: err_notregistered(), sendbuf(client) + err_notregistered(client) return end if client.umode & 2 == 0 then -- UMODE_OPER - -- TODO: err_noprivileges(), sendbuf(client) + err_noprivileges(client) return end commands[command](client, source, command, table.pack(...)) diff --git a/rplbindings.lua b/rplbindings.lua new file mode 100755 index 0000000..6f3ac54 --- /dev/null +++ b/rplbindings.lua @@ -0,0 +1,75 @@ +#!/usr/bin/env lua5.4 +-- Copyright (c) 2026 Egor +-- SPDX-License-Identifier: GPL-2.0-or-later + +-- print line with automatic indentation +local indent = 0 +local function printlni(format, ...) + local s = format:format(...) + if s:sub(1, 1) == "}" then + indent = indent - 1 + end + io.write(string.rep("\t", indent), format:format(...), "\n") + if s:sub(-1) == "{" then + indent = indent + 1 + end +end +local check = { + ["Client *"] = "ClientRef::check(L, %d)", + ["Channel *"] = "ChannelRef::check(L, %d)", + ["const char *"] = "luaL_checkstring(L, %d)", + ["std::string_view "] = "checkstringview(L, %d)", + ["char "] = "luaL_checkstring(L, %d)[0]", + ["int "] = "(int)luaL_checkinteger(L, %d)", + ["time_t "] = "(time_t)luaL_checkinteger(L, %d)", + ["std::span "] = "checkclientlist(L, %d)", +} +function set(t) + local u = {} + for i, v in ipairs(t) do u[v] = true end + return u +end +local blocklist = set{ + -- unused + "rpl_bounce", + "rpl_tryagain", + "rpl_none", + "err_unknownerror", + "err_toomanychannels", + "err_wasnosuchnick", + "err_noorigin", + "err_inputtoolong", + "err_yourebannedcreep", +} +printlni("static void admin_rpl_init(lua_State *L) {") +printlni("luaL_Reg funcs[] = {") +for line in io.lines() do + local name, fargs = line:match("^void (rpl_[%w_]+)%((.*)%)") + if not name then + name, fargs = line:match("^void (err_[%w_]+)%((.*)%)") + end + if name and not blocklist[name] then + printlni("{\"%s\", [](lua_State *L) {", name) + local aargs = {} + local isvoid + if fargs == "void" or fargs == "" then + isvoid = true + fargs = "Client *client" + end + for typename, argname in fargs:gmatch("([^ ,][^,]* %**)([%w_]+)") do + assert(check[typename], name) + aargs[#aargs+1] = argname + printlni("%s%s = %s;", typename, argname, check[typename]:format(#aargs)) + end + assert(aargs[1] == "client") + if isvoid then aargs = {} end + printlni("return %s(%s), sendbuf(client), 0;", name, table.concat(aargs, ", ")) + printlni("}},"); + end +end +printlni("{nullptr, nullptr},") +printlni("};") +printlni("lua_pushglobaltable(L);") +printlni("luaL_setfuncs(L, funcs, 0);") +printlni("lua_pop(L, 1);") +printlni("}") diff --git a/src/admin.cpp b/src/admin.cpp index bf47c3d..05419ea 100644 --- a/src/admin.cpp +++ b/src/admin.cpp @@ -151,6 +151,10 @@ struct ClientRef { static Client *check(lua_State *L, int idx) { return static_cast(luaL_checkudata(L, idx, mtname))->cl; } + static Client *test(lua_State *L, int idx) { + auto ref = static_cast(luaL_testudata(L, idx, mtname)); + return ref ? ref->cl : 0; + } int index(lua_State *L) { auto key = checkstringview(L, 2); if (!cl) @@ -376,13 +380,6 @@ static int fn_client_chghost(lua_State *L) client_chghost(cl, newusername, newhostname); return 0; } -static int fn_rpl_whoisspecial(lua_State *L) { - Client *cl1 = ClientRef::check(L, 1); - Client *cl2 = ClientRef::check(L, 2); - const char *text = luaL_checkstring(L, 3); - rpl_whoisspecial(cl1, cl2, "", text), sendbuf(cl1); - return 0; -} static int fn_getdatafile(lua_State *L) { const char *fn = luaL_checkstring(L, 1); const char *res = getdatafile(fn); @@ -442,7 +439,19 @@ static int fn_querydisk(lua_State *L) { static int fn_querymemory(lua_State *L) { return query(L, db::memory); } - +static std::span checkclientlist(lua_State *L, int idx) { + lua_Integer len = luaL_len(L, idx); + Client **p = static_cast(lua_newuserdatauv(L, len*sizeof(Client *), 0)); + for (lua_Integer i = 0; i < len; i++) { + lua_geti(L, idx, i+1); + p[i] = ClientRef::test(L, -1); + if (!p[i]) + luaL_error(L, "expected a Set at index #%d", i+1); + lua_pop(L, 1); + } + return std::span(p, p + len); +} +#include "admin_rpl.hpp" void admin_init(void) { lua_State *L = luaL_newstate(); @@ -476,7 +485,6 @@ void admin_init(void) {"channel_mode", fn_channel_mode}, {"channel_topic", fn_channel_topic}, {"client_chghost", fn_client_chghost}, - {"rpl_whoisspecial", fn_rpl_whoisspecial}, {"getdatafile", fn_getdatafile}, {"getluafile", fn_getluafile}, {"querydisk", fn_querydisk}, @@ -486,6 +494,7 @@ void admin_init(void) lua_pushglobaltable(L); luaL_setfuncs(L, funcs, 0); lua_pop(L, 1); + admin_rpl_init(L); } } void admin_fini(void) diff --git a/src/common.hpp b/src/common.hpp index 077a445..4686052 100644 --- a/src/common.hpp +++ b/src/common.hpp @@ -36,6 +36,7 @@ #include #include #include +#include #include #include #include diff --git a/src/irc.cpp b/src/irc.cpp index a381faf..d2bec40 100644 --- a/src/irc.cpp +++ b/src/irc.cpp @@ -1028,7 +1028,7 @@ privmsg_notice_common: if (cl) *p++ = cl; } - rpl_userhost(client, cls, (int)(p - cls)); + rpl_userhost(client, std::span(cls, p)); sendbuf(client); // User-Based Queries } MATCH_COMMAND("WHO") { diff --git a/src/ircfmt.cpp b/src/ircfmt.cpp index 296b2b2..37c0d98 100644 --- a/src/ircfmt.cpp +++ b/src/ircfmt.cpp @@ -177,15 +177,17 @@ void rpl_none(Client *client, const char *line) { // 300 void rpl_away(Client *client, Client *cl) { // 301 num_reply(301, " %s :%s\r\n", cl->nick, cl->away); } -void rpl_userhost(Client *client, Client **cls, int count) { // 302 +void rpl_userhost(Client *client, std::span cls) { // 302 num_reply(302, ""); - for (int i = 0; i < count; i++) { + bool first = true; + for (Client *cl : cls) { append_strf(&databuf, " %s%s%s=%s%s", - i ? "" : ":", - cls[i]->nick, - cls[i]->umode&UMODE_OPER ? "*" : "", - cls[i]->away ? "-" : "+", - cls[i]->hostname); + first ? ":" : "", + cl->nick, + cl->umode&UMODE_OPER ? "*" : "", + cl->away ? "-" : "+", + cl->hostname); + first = false; } append_str(&databuf, "\r\n"); } diff --git a/src/ircfmt.hpp b/src/ircfmt.hpp index 2819699..e25c4a3 100644 --- a/src/ircfmt.hpp +++ b/src/ircfmt.hpp @@ -46,7 +46,7 @@ void rpl_globalusers(Client *client, int current, int max); // 266 void rpl_whoiscertfp(Client *client, Client *cl, const char *certfp); // 276 void rpl_none(Client *client, const char *line); // 300 void rpl_away(Client *client, Client *cl); // 301 -void rpl_userhost(Client *client, Client **cls, int count); // 302 +void rpl_userhost(Client *client, std::span cls); // 302 void rpl_unaway(Client *client); // 305 void rpl_nowaway(Client *client); // 306 void rpl_whoreply(Client *client, Client *cl, Channel *chan); // 352 -- 2.56.0