~rootmos/lua-hack

5247b6743ff4c4c696c28f0a7cae97b54b524a80 — Gustav Behm 5 months ago 0ab6274 + 47f6891
Merge branch 'notify'
6 files changed, 853 insertions(+), 0 deletions(-)

M README.md
A notify/.k
A notify/Makefile
A notify/go.lua
A notify/notify.c
A notify/r.h
M README.md => README.md +1 -0
@@ 13,6 13,7 @@ A set of Lua modules guaranteed to be public domain.
- [lamport-merkle](lamport-merkle/init.lua): implementation of the combined [Lamport-Merkle](https://en.wikipedia.org/wiki/Merkle_signature_scheme) signature scheme
- [lamport](lamport/init.lua): implementation of the [Lamport](https://en.wikipedia.org/wiki/Lamport_signature) signature scheme
- [merkle](merkle/init.lua): implementation of the [Merkle tree]() data structure
- [notify](notify/notify.c): a [libnotify](https://gnome.pages.gitlab.gnome.org/libnotify/index.html) wrapper
- [random.prng](random/prng.lua): Pseudorandom number generators (e.g. [xorshift32](https://en.wikipedia.org/wiki/Xorshift))
- [random.rng](random/rng.lua): entropy sources (e.g. `/dev/random`)
- [ser](ser.lua): serialize Lua values as Lua expressions and a pretty-printer for free

A notify/.k => notify/.k +7 -0
@@ 0,0 1,7 @@
go() {
    make LOG_LEVEL=DEBUG && lua go.lua
}

libr() {
    "$HOME/git/libr/bundle" -o r.h logging lua
}

A notify/Makefile => notify/Makefile +26 -0
@@ 0,0 1,26 @@
CC = gcc
PKG_CONFIG ?= pkg-config

CFLAGS ?= -Wall -Werror -O2
LDFLAGS ?=

LOG_LEVEL ?= ERROR
CFLAGS += -DLOG_LEVEL=LOG_$(LOG_LEVEL)

LUA_PKG ?= lua
CFLAGS += $(shell $(PKG_CONFIG) --cflags "$(LUA_PKG)")
LDFLAGS += $(shell $(PKG_CONFIG) --libs "$(LUA_PKG)")

LIBNOTIFY_PKG ?= libnotify
CFLAGS += $(shell $(PKG_CONFIG) --cflags "$(LIBNOTIFY_PKG)")
LDFLAGS += $(shell $(PKG_CONFIG) --libs "$(LIBNOTIFY_PKG)")

.PHONY: build
build: notify.so

notify.so: notify.c
	$(CC) -rdynamic -fPIC -shared $(CFLAGS) -o$@ $^ $(LDFLAGS)

.PHONY: clean
clean:
	rm -f *.so

A notify/go.lua => notify/go.lua +6 -0
@@ 0,0 1,6 @@
local N = require("notify")
N.app_name = "foo"

local n <close> = N:new{ summary = "hello", body = "foobar", timeout = 10000 }:show()

os.execute("sleep 1")

A notify/notify.c => notify/notify.c +484 -0
@@ 0,0 1,484 @@
#include <lua.h>
#include <lauxlib.h>

#include <libnotify/notify.h>

#define LIBR_IMPLEMENTATION
#include "r.h"

#define TYPE_NOTIFY "notify"
#define TYPE_NOTIFICATION "notify.notification"

struct notify {
    int ref;
};

static struct notify* notify_ref(struct notify* notify)
{
    notify->ref += 1;
    return notify;
}

static void notify_unref(struct notify* notify)
{
    if(--notify->ref == 0) {
        debug("libnotify uninit");
        notify_uninit();
    }
}

static int notify_gc(lua_State* L)
{
    luaR_stack(L);
    notify_unref(luaL_checkudata(L, 1, TYPE_NOTIFY));
    luaR_return(L, 0);
}


struct notification {
    int ref;
    struct notify* notify;

    NotifyNotification* n;
};

static struct notification* notification_ref(struct notification* notification)
{
    notification->ref += 1;
    return notification;
}

static void notification_unref(struct notification* notification)
{
    if(--notification->ref == 0) {
        g_object_unref(G_OBJECT(notification->n));
        notify_unref(notification->notify);
    }
}

static int notification_gc(lua_State* L)
{
    luaR_stack(L);
    notification_unref(luaL_checkudata(L, 1, TYPE_NOTIFICATION));
    luaR_return(L, 0);
}

static int notification_show(lua_State* L)
{
    luaR_stack(L);
    struct notification* notification = luaL_checkudata(L, 1, TYPE_NOTIFICATION);

    GError* e = NULL;
    if(!notify_notification_show(notification->n, &e)) {
        luaL_where(L, 1);
        lua_pushfstring(L, "unable to show notification: %s", e->message);
        g_error_free(e);
        lua_concat(L, 2);
        return lua_error(L);
    }

    lua_pushvalue(L, 1);
    luaR_return(L, 1);
}

static int notification_close(lua_State* L)
{
    luaR_stack(L);
    struct notification* notification = luaL_checkudata(L, 1, TYPE_NOTIFICATION);

    GError* e = NULL;
    if(!notify_notification_close(notification->n, &e)) {
        luaL_where(L, 1);
        lua_pushfstring(L, "unable to close notification: %s", e->message);
        g_error_free(e);
        lua_concat(L, 2);
        return lua_error(L);
    }

    lua_pushvalue(L, 1);
    luaR_return(L, 1);
}

static int notification_index(lua_State* L)
{
    luaR_stack(L);
    struct notification* notification = luaL_checkudata(L, 1, TYPE_NOTIFICATION);
    luaL_checkany(L, 2);

    lua_pushvalue(L, 2);
    if(lua_gettable(L, lua_upvalueindex(1)) != LUA_TNIL) {
        luaR_return(L, 1);
    }
    lua_pop(L, 1);

    lua_pushliteral(L, "summary");
    int r = lua_rawequal(L, 2, -1);
    lua_pop(L, 1);
    if(r) {
        gchar* summary;
        g_object_get(notification->n, "summary", &summary, NULL);
        lua_pushstring(L, summary);
        luaR_return(L, 1);
    }

    lua_pushliteral(L, "body");
    r = lua_rawequal(L, 2, -1);
    lua_pop(L, 1);
    if(r) {
        gchar* body;
        g_object_get(notification->n, "body", &body, NULL);
        lua_pushstring(L, body);
        luaR_return(L, 1);
    }

    lua_pushliteral(L, "icon");
    r = lua_rawequal(L, 2, -1);
    lua_pop(L, 1);
    if(r) {
        gchar* icon;
        g_object_get(notification->n, "icon-name", &icon, NULL);
        lua_pushstring(L, icon);
        luaR_return(L, 1);
    }

    lua_pushliteral(L, "app_name");
    r = lua_rawequal(L, 2, -1);
    lua_pop(L, 1);
    if(r) {
        gchar* app_name;
        g_object_get(notification->n, "app-name", &app_name, NULL);
        lua_pushstring(L, app_name);
        luaR_return(L, 1);
    }

    lua_pushliteral(L, "id");
    r = lua_rawequal(L, 2, -1);
    lua_pop(L, 1);
    if(r) {
        gint id;
        g_object_get(notification->n, "id", &id, NULL);
        lua_pushinteger(L, id);
        luaR_return(L, 1);
    }

    lua_pushnil(L);
    luaR_return(L, 1);
}

static int notification_newindex(lua_State* L)
{
    luaR_stack(L);
    struct notification* notification = luaL_checkudata(L, 1, TYPE_NOTIFICATION);
    luaL_checkany(L, 2);
    luaL_checkany(L, 3);

    lua_pushliteral(L, "summary");
    int r = lua_rawequal(L, 2, -1);
    lua_pop(L, 1);
    if(r) {
        const gchar *summary, *body, *icon;
        int t = lua_type(L, 3);
        if(t == LUA_TSTRING) {
            summary = lua_tostring(L, 3);
            if(summary == NULL) {
                failwith("lua_tostring failed unexpectedly");
            }
        } else if(t == LUA_TNIL) {
            summary = NULL;
        } else {
            return luaL_argerror(L, 3, "unexpected type for summary field");
        }
        g_object_get(notification->n, "body", &body, "icon-name", &icon, NULL);

        if(!notify_notification_update(notification->n, summary, body, icon)) {
            return luaL_error(L, "notification update failed");
        }

        luaR_return(L, 0);
    }

    lua_pushliteral(L, "body");
    r = lua_rawequal(L, 2, -1);
    lua_pop(L, 1);
    if(r) {
        const gchar *summary, *body, *icon;
        int t = lua_type(L, 3);
        if(t == LUA_TSTRING) {
            body = lua_tostring(L, 3);
            if(body == NULL) {
                failwith("lua_tostring failed unexpectedly");
            }
        } else if(t == LUA_TNIL) {
            body = NULL;
        } else {
            return luaL_argerror(L, 3, "unexpected type for body field");
        }
        g_object_get(notification->n, "summary", &summary, "icon-name", &icon, NULL);

        if(!notify_notification_update(notification->n, summary, body, icon)) {
            return luaL_error(L, "notification update failed");
        }

        luaR_return(L, 0);
    }

    lua_pushliteral(L, "icon");
    r = lua_rawequal(L, 2, -1);
    lua_pop(L, 1);
    if(r) {
        const gchar *summary, *body, *icon;
        int t = lua_type(L, 3);
        if(t == LUA_TSTRING) {
            icon = lua_tostring(L, 3);
            if(icon == NULL) {
                failwith("lua_tostring failed unexpectedly");
            }
        } else if(t == LUA_TNIL) {
            icon = NULL;
        } else {
            return luaL_argerror(L, 3, "unexpected type for icon field");
        }
        g_object_get(notification->n, "summary", &summary, "body", &body, NULL);

        if(!notify_notification_update(notification->n, summary, body, icon)) {
            return luaL_error(L, "notification update failed");
        }

        luaR_return(L, 0);
    }

    lua_pushliteral(L, "timeout");
    r = lua_rawequal(L, 2, -1);
    lua_pop(L, 1);
    if(r) {
        gint timeout = luaL_checkinteger(L, 3);
        notify_notification_set_timeout(notification->n, timeout);
        luaR_return(L, 0);
    }

    return luaL_argerror(L, 2, "unpexpected field");
}

static int notification_new(lua_State* L)
{
    luaR_stack(L);
    struct notify* notify = luaL_checkudata(L, 1, TYPE_NOTIFY);

    const char* summary = NULL;
    const char* body = NULL;
    const char* icon = NULL;
    const char* app_name = NULL;
    gint timeout = NOTIFY_EXPIRES_DEFAULT;
    gint id = 0;

    for(int arg = 2, t = lua_type(L, arg); t != LUA_TNONE; t = lua_type(L, ++arg)) {
        if(t == LUA_TSTRING) {
            if(summary == NULL) {
                summary = lua_tostring(L, arg);
                if(summary == NULL) {
                    failwith("lua_tostring failed unexpectedly");
                }
            } else if(body == NULL) {
                body = lua_tostring(L, arg);
                if(body == NULL) {
                    failwith("lua_tostring failed unexpectedly");
                }
            } else if(icon == NULL) {
                icon = lua_tostring(L, arg);
                if(icon == NULL) {
                    failwith("lua_tostring failed unexpectedly");
                }
            } else {
                return luaL_argerror(L, arg, "unexpected string argument");
            }
        } else if(t == LUA_TTABLE) {
            int f = lua_getfield(L, arg, "summary");
            if(f == LUA_TSTRING) {
                summary = lua_tostring(L, -1);
                if(summary == NULL) {
                    failwith("lua_tostring failed unexpectedly");
                }
            } else if(f != LUA_TNIL) {
                return luaL_argerror(L, arg, "summary field not a string");
            }
            lua_pop(L, 1);

            f = lua_getfield(L, arg, "body");
            if(f == LUA_TSTRING) {
                body = lua_tostring(L, -1);
                if(body == NULL) {
                    failwith("lua_tostring failed unexpectedly");
                }
            } else if(f != LUA_TNIL) {
                return luaL_argerror(L, arg, "body field not a string");
            }
            lua_pop(L, 1);

            f = lua_getfield(L, arg, "icon");
            if(f == LUA_TSTRING) {
                icon = lua_tostring(L, -1);
                if(icon == NULL) {
                    failwith("lua_tostring failed unexpectedly");
                }
            } else if(f != LUA_TNIL) {
                return luaL_argerror(L, arg, "icon field not a string");
            }
            lua_pop(L, 1);

            f = lua_getfield(L, arg, "app_name");
            if(f == LUA_TSTRING) {
                app_name = lua_tostring(L, -1);
                if(body == NULL) {
                    failwith("lua_tostring failed unexpectedly");
                }
            } else if(f != LUA_TNIL) {
                return luaL_argerror(L, arg, "app_name field not a string");
            }
            lua_pop(L, 1);

            f = lua_getfield(L, arg, "timeout");
            if(f == LUA_TNUMBER) {
                int isnum;
                timeout = lua_tointegerx(L, -1, &isnum);
                if(!isnum) {
                    return luaL_argerror(L, arg, "timeout field not an integer");
                }
            } else if(f != LUA_TNIL) {
                return luaL_argerror(L, arg, "timeout field not a number");
            }
            lua_pop(L, 1);

            f = lua_getfield(L, arg, "id");
            if(f == LUA_TNUMBER) {
                int isnum;
                id = lua_tointegerx(L, -1, &isnum);
                if(!isnum) {
                    return luaL_argerror(L, arg, "id field not an integer");
                }
            } else if(f != LUA_TNIL) {
                return luaL_argerror(L, arg, "id field not a number");
            }
            lua_pop(L, 1);
        } else {
            return luaL_argerror(L, arg, "unexpected argument");
        }
    }

    struct notification* notification = lua_newuserdatauv(L, sizeof(struct notification), 0);
    memset(notification, 0, sizeof(*notification));
    notification_ref(notification);
    notification->notify = notify_ref(notify);

    notification->n = notify_notification_new(summary, body, icon);
    CHECK_NOT(notification->n, NULL, "notify_notification_new");

    notify_notification_set_timeout(notification->n, timeout);
    notify_notification_set_app_name(notification->n, app_name);

    if(id > 0) {
        g_object_set(notification->n, "id", id, NULL);
    }

    if(luaL_newmetatable(L, TYPE_NOTIFICATION)) {
        luaL_Reg l[] = {
            { "show", notification_show },
            { "close", notification_close },
            { NULL, NULL },
        };
        luaL_newlib(L, l);
        lua_pushcclosure(L, notification_index, 1);
        lua_setfield(L, -2, "__index");

        lua_pushcfunction(L, notification_newindex);
        lua_setfield(L, -2, "__newindex");

        lua_pushcfunction(L, notification_gc);
        lua_setfield(L, -2, "__gc");

        lua_pushcfunction(L, notification_close);
        lua_setfield(L, -2, "__close");
    }
    lua_setmetatable(L, -2);

    luaR_return(L, 1);
}

static int notify_index(lua_State* L)
{
    luaR_stack(L);
    luaR_checkmetatable(L, 1, TYPE_NOTIFY);
    luaL_checkany(L, 2);

    lua_pushvalue(L, 2);
    if(lua_gettable(L, lua_upvalueindex(1)) != LUA_TNIL) {
        luaR_return(L, 1);
    }
    lua_pop(L, 1);

    lua_pushliteral(L, "app_name");
    int r = lua_rawequal(L, 2, -1);
    lua_pop(L, 1);
    if(r) {
        lua_pushstring(L, notify_get_app_name());
        luaR_return(L, 1);
    }

    lua_pushnil(L);
    luaR_return(L, 1);
}

static int notify_newindex(lua_State* L)
{
    luaR_stack(L);
    luaR_checkmetatable(L, 1, TYPE_NOTIFY);
    luaL_checkany(L, 2);
    luaL_checkany(L, 3);

    lua_pushliteral(L, "app_name");
    int r = lua_rawequal(L, 2, -1);
    lua_pop(L, 1);
    if(r) {
        const char* app_name = luaL_checkstring(L, 3);
        debug("libnotify; setting app name: %s", app_name);
        notify_set_app_name(app_name);
        luaR_return(L, 0);
    }

    return luaL_argerror(L, 2, "unpexpected field");
}

int luaopen_notify(lua_State* L)
{
    luaL_checkversion(L);
    luaR_stack(L);

    struct notify* notify = lua_newuserdatauv(L, sizeof(struct notify), 0);
    memset(notify, 0, sizeof(*notify));
    notify_ref(notify);

    if(!notify_is_initted()) {
        if(notify_init("lua") != TRUE) {
            return luaL_error(L, "unable to initialize libnotify");
        }
        debug("libnotify init: %s", notify_get_app_name());
    }

    if(luaL_newmetatable(L, TYPE_NOTIFY)) {
        luaL_Reg l[] = {
            { "new", notification_new },
            { NULL, NULL },
        };
        luaL_newlib(L, l);
        lua_pushcclosure(L, notify_index, 1);
        lua_setfield(L, -2, "__index");

        lua_pushcfunction(L, notify_gc);
        lua_setfield(L, -2, "__gc");

        lua_pushcfunction(L, notify_newindex);
        lua_setfield(L, -2, "__newindex");
    }
    lua_setmetatable(L, -2);

    luaR_return(L, 1);
}

A notify/r.h => notify/r.h +329 -0
@@ 0,0 1,329 @@
// libr 0.3.0 (3e106adce5e0b2549a10daf7c82df05a81fe60d4) (https://github.com/rootmos/libr.git) (2023-09-11T00:53:05+02:00)
// modules: logging now lua fail util

#ifndef LIBR_HEADER
#define LIBR_HEADER

// libr: logging.h

#include <stdarg.h>

#define LOG_QUIET 0
#define LOG_ERROR 1
#define LOG_WARNING 2
#define LOG_INFO 3
#define LOG_DEBUG 4
#define LOG_TRACE 5

#ifndef LOG_LEVEL
#define LOG_LEVEL LOG_INFO
#endif

#define __r_log(level, format, ...) do { \
    r_log(level, __extension__ __FUNCTION__, __extension__ __FILE__, \
          __extension__ __LINE__, format "\n", ##__VA_ARGS__); \
} while(0)

#ifdef __cplusplus
void r_dummy(...);
#else
void r_dummy();
#endif

#if LOG_LEVEL >= LOG_ERROR
#define error(format, ...) __r_log(LOG_ERROR, format, ##__VA_ARGS__)
#else
#define error(format, ...) do { if(0) r_dummy(__VA_ARGS__); } while(0)
#endif

#if LOG_LEVEL >= LOG_WARNING
#define warning(format, ...) __r_log(LOG_WARNING, format, ##__VA_ARGS__)
#else
#define warning(format, ...) do { if(0) r_dummy(__VA_ARGS__); } while(0)
#endif

#if LOG_LEVEL >= LOG_INFO
#define info(format, ...) __r_log(LOG_INFO, format, ##__VA_ARGS__)
#else
#define info(format, ...) do { if(0) r_dummy(__VA_ARGS__); } while(0)
#endif

#if LOG_LEVEL >= LOG_DEBUG
#define debug(format, ...) __r_log(LOG_DEBUG, format, ##__VA_ARGS__)
#else
#define debug(format, ...) do { if(0) r_dummy(__VA_ARGS__); } while(0)
#endif

#if LOG_LEVEL >= LOG_TRACE
#define trace(format, ...) __r_log(LOG_TRACE, format, ##__VA_ARGS__)
#else
#define trace(format, ...) do { if(0) r_dummy(__VA_ARGS__); } while(0)
#endif

void r_log(int level,
           const char* const caller,
           const char* const file,
           const unsigned int line,
           const char* const fmt, ...)
    __attribute__ ((format (printf, 5, 6)));

void r_vlog(int level,
            const char* const caller,
            const char* const file,
            const unsigned int line,
            const char* const fmt, va_list vl);

// libr: now.h

// returns current time formated as compact ISO8601: 20190123T182628Z
const char* now_iso8601_compact(void);

// libr: lua.h

#include <lua.h>

#define CHECK_LUA(L, err, format, ...) do { \
    if(err != LUA_OK) { \
        r_failwith(__extension__ __FUNCTION__, __extension__ __FILE__, \
                   __extension__ __LINE__, 0, \
                   format ": %s\n", ##__VA_ARGS__, lua_tostring(L, -1)); \
    } \
} while(0)

#define LUA_EXPECT_TYPE(L, t, expected, format, ...) do { \
    if(t != expected) {\
        r_failwith(__extension__ __FUNCTION__, __extension__ __FILE__, \
                   __extension__ __LINE__, 0, \
                   format ": unexpected type %s (expected %s)\n", \
                   ##__VA_ARGS__, lua_typename(L, t), \
                   lua_typename(L, expected)); \
    } \
} while(0)

#ifndef LUA_STACK_MARKER
#define LUA_STACK_MARKER __luaR_stack_marker
#endif

#define luaR_stack(L) int LUA_STACK_MARKER = lua_gettop(L)
#define luaR_stack_expect(L, n) do { \
    int r = lua_gettop(L) - LUA_STACK_MARKER; \
    if(r < n) { \
        failwith("too few stack elements: found %d expected %d", r ,n); \
    } else if(r > n) { \
        failwith("too many stack elements: found %d expected %d", r ,n); \
    } \
} while(0)

#define luaR_return(L, n) do { \
    luaR_stack_expect(L, n); \
    return n; \
} while(0)

#define luaR_failwith(L, format, ...) \
    r_lua_failwith(L, \
            __extension__ __FUNCTION__, \
            __extension__ __FILE__, \
            __extension__ __LINE__, \
            format, ##__VA_ARGS__)

void r_lua_failwith(lua_State* L,
                    const char* const caller,
                    const char* const file,
                    const unsigned int line,
                    const char* const fmt, ...)
    __attribute__ ((noreturn, format (printf, 5, 6)));

int luaR_testmetatable(lua_State* L, int arg, const char* tname);
void luaR_checkmetatable(lua_State* L, int arg, const char* tname);

// libr: fail.h

#define CHECK(res, format, ...) CHECK_NOT(res, -1, format, ##__VA_ARGS__)

#define CHECK_NOT(res, err, format, ...) \
    CHECK_IF(res == err, format, ##__VA_ARGS__)

#define CHECK_IF(cond, format, ...) do { \
    if(cond) { \
        r_failwith(__extension__ __FUNCTION__, __extension__ __FILE__, \
                   __extension__ __LINE__, 1, \
                   format "\n", ##__VA_ARGS__); \
    } \
} while(0)

#define CHECK_MALLOC(x) CHECK_NOT(x, NULL, "memory allocation failed")
#define CHECK_MMAP(x) CHECK_NOT(x, MAP_FAILED, "memory mapping failed")

#define failwith(format, ...) \
    r_failwith(__extension__ __FUNCTION__, __extension__ __FILE__, \
               __extension__ __LINE__, 0, format "\n", ##__VA_ARGS__)

#define not_implemented() failwith("not implemented")

void r_failwith(const char* const caller,
                const char* const file,
                const unsigned int line,
                const int include_errno,
                const char* const fmt, ...)
    __attribute__ ((noreturn, format (printf, 5, 6)));

// libr: util.h

#ifndef LENGTH
#define LENGTH(xs) (sizeof(xs)/sizeof((xs)[0]))
#endif

#ifndef LIT
#define LIT(x) x,sizeof(x)
#endif

#ifndef MAX
#define MAX(a,b) ((a) > (b) ? (a) : (b))
#endif

#ifndef MIN
#define MIN(a,b) ((a) < (b) ? (a) : (b))
#endif
#endif // LIBR_HEADER

#ifdef LIBR_IMPLEMENTATION

// libr: logging.c

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>

#ifdef __cplusplus
void r_dummy(...)
#else
void r_dummy()
#endif
{
    abort();
}

void r_vlog(int level,
            const char* const caller,
            const char* const file,
            const unsigned int line,
            const char* const fmt, va_list vl)
{
    fprintf(stderr, "%s:%d:%s:%s:%u ",
            now_iso8601_compact(), getpid(), caller, file, line);

    vfprintf(stderr, fmt, vl);
}

void r_log(int level,
           const char* const caller,
           const char* const file,
           const unsigned int line,
           const char* const fmt, ...)
{
    va_list vl;
    va_start(vl, fmt);
    r_vlog(level, caller, file, line, fmt, vl);
    va_end(vl);
}

// libr: now.c

#include <time.h>
#include <stdlib.h>

const char* now_iso8601_compact(void)
{
    static char buf[17];
    const time_t t = time(NULL);
    size_t r = strftime(buf, sizeof(buf), "%Y%m%dT%H%M%SZ", gmtime(&t));
    if(r <= 0) abort();
    return buf;
}

// libr: lua.c

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

#include <lua.h>
#include <lauxlib.h>

void r_lua_failwith(lua_State* L,
    const char* const caller,
    const char* const file,
    const unsigned int line,
    const char* const fmt, ...)
{

    size_t N = 256;
    char* buf;
    while(1) {
        buf = alloca(N);

        int r = snprintf(buf, N, "(%s:%d:%s:%s:%u) ", now_iso8601_compact(), getpid(), caller, file, line);
        if(r >= N) {
            while(N < r) {
                N <<= 1;
            }
            continue;
        }

        va_list vl;
        va_start(vl, fmt);

        r += vsnprintf(buf+r, N-r, fmt, vl);
        if(r < N) {
            lua_pushstring(L, buf);
            lua_error(L); // "This function does a long jump, and therefore never returns [...]."
        }

        va_end(vl);
        N <<= 1;
    }
}

int luaR_testmetatable(lua_State* L, int arg, const char* tname)
{
    if(lua_getmetatable(L, arg)) {
        luaL_getmetatable(L, tname);
        int r = lua_rawequal(L, -1, -2);
        lua_pop(L, 2);
        return r;
    }
    return 0;
}

void luaR_checkmetatable(lua_State* L, int arg, const char* tname)
{
    luaL_argexpected(L, luaR_testmetatable(L, arg, tname), arg, tname);
}

// libr: fail.c

#include <stdlib.h>
#include <errno.h>
#include <stdio.h>
#include <string.h>

void r_failwith(const char* const caller,
                const char* const file,
                const unsigned int line,
                const int include_errno,
                const char* const fmt, ...)
{
    va_list vl;
    va_start(vl, fmt);

    if(include_errno) {
        r_log(LOG_ERROR, caller, file, line, "(%s) ", strerror(errno));
        vfprintf(stderr, fmt, vl);
    } else {
        r_vlog(LOG_ERROR, caller, file, line, fmt, vl);
    }
    va_end(vl);

    abort();
}
#endif // LIBR_IMPLEMENTATION