@@ 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);
+}
@@ 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