From 93e668aa34d1315140349217cb39563c78b33fe2 Mon Sep 17 00:00:00 2001 From: Christian Goll Date: Fri, 22 Jul 2022 14:39:37 +0200 Subject: [PATCH 1/2] added lua bindings --- config/ac_lua_extensions.m4 | 21 ++ config/x_ac_lua.m4 | 60 ++++ configure.ac | 21 ++ genders.spec | 110 ++++++ src/extensions/Makefile.am | 2 +- .../lua/GendersTest/GendersTest.lua | 134 ++++++++ src/extensions/lua/GendersTest/testgenders | 21 ++ src/extensions/lua/Makefile.am | 21 ++ src/extensions/lua/genderslua.c | 322 ++++++++++++++++++ 9 files changed, 711 insertions(+), 1 deletion(-) create mode 100644 config/ac_lua_extensions.m4 create mode 100644 config/x_ac_lua.m4 create mode 100644 genders.spec create mode 100755 src/extensions/lua/GendersTest/GendersTest.lua create mode 100644 src/extensions/lua/GendersTest/testgenders create mode 100644 src/extensions/lua/Makefile.am create mode 100644 src/extensions/lua/genderslua.c diff --git a/config/ac_lua_extensions.m4 b/config/ac_lua_extensions.m4 new file mode 100644 index 0000000..9421588 --- /dev/null +++ b/config/ac_lua_extensions.m4 @@ -0,0 +1,21 @@ + +AC_DEFUN([AC_LUA_EXTENSIONS], +[ + AC_MSG_CHECKING(for --with-lua-extensions) + AC_ARG_WITH(lua-extensions, + AC_HELP_STRING([--with-lua-extensions=], + [enable or disable lua extensions build]), + [ case "$withval" in + yes) + ac_with_lua_extensions=yes + ;; + no) + ac_with_lua_extensions=no + ;; + *) + ac_with_lua_extensions=yes + ;; + esac ] + ) + AC_MSG_RESULT(${ac_with_lua_extensions=no}) +]) diff --git a/config/x_ac_lua.m4 b/config/x_ac_lua.m4 new file mode 100644 index 0000000..2f5f587 --- /dev/null +++ b/config/x_ac_lua.m4 @@ -0,0 +1,60 @@ +##***************************************************************************** +# AUTHOR: +# Mark Grondona +# +# SYNOPSIS: +# AC_LUA +# +# DESCRIPTION: +# Check for presence of lua libs and headers +##***************************************************************************** + + +AC_DEFUN([X_AC_LUA], +[ + x_ac_lua_pkg_name="lua" + #check for 5.3 then 5.2 then 5.1 + PKG_CHECK_EXISTS([lua5.3], [x_ac_lua_pkg_name=lua5.3], + [PKG_CHECK_EXISTS([lua-5.3], [x_ac_lua_pkg_name=lua-5.3], + [PKG_CHECK_EXISTS([lua5.2], [x_ac_lua_pkg_name=lua5.2], + [PKG_CHECK_EXISTS([lua-5.2], [x_ac_lua_pkg_name=lua-5.2], + [PKG_CHECK_EXISTS([lua5.1], [x_ac_lua_pkg_name=lua5.1], + [PKG_CHECK_EXISTS([lua-5.1], [x_ac_lua_pkg_name=lua-5.1], + [x_ac_lua_pkg_name="lua >= 5.1"])])])])])]) + PKG_CHECK_MODULES([lua], ${x_ac_lua_pkg_name}, + [x_ac_have_lua="yes"], + [x_ac_have_lua="no"]) + + if test "x$x_ac_have_lua" = "xyes"; then + saved_CFLAGS="$CFLAGS" + saved_LIBS="$LIBS" + lua_CFLAGS="$lua_CFLAGS" + CFLAGS="$CFLAGS $lua_CFLAGS" + LIBS="$LIBS $lua_LIBS" + AC_MSG_CHECKING([for whether we can link to liblua]) + AC_TRY_LINK( + [#include + #include + #include + ], + [lua_State *L = luaL_newstate (); luaL_openlibs(L); + ], + [], [x_ac_have_lua="no"]) + + AC_MSG_RESULT([$x_ac_have_lua $x_ac_lua_pkg_name]) + if test "x$x_ac_have_lua" = "xno"; then + AC_MSG_WARN([unable to link against lua libraries]) + else + AC_DEFINE(HAVE_LUA, 1, [Define to 1 if we have the Lua library]) + # We can not define something here to determine version for systems + # that use just liblua we will not know what version we are using. + # Use LUA_VERSION_NUM as in lua.h it will always be right. + fi + CFLAGS="$saved_CFLAGS" + LIBS="$saved_LIBS" + else + AC_MSG_WARN([unable to locate lua package]) + fi + + AM_CONDITIONAL(HAVE_LUA, test "x$x_ac_have_lua" = "xyes") +]) diff --git a/configure.ac b/configure.ac index 07496ea..58c6684 100644 --- a/configure.ac +++ b/configure.ac @@ -68,6 +68,7 @@ PYTHONGENDERS_MINOR=2 PYTHONGENDERS_VERSION=$PYTHONGENDERS_MAJOR.$PYTHONGENDERS_MINOR AC_SUBST([PYTHONGENDERS_VERSION]) + ## # Checks for programs. ## @@ -86,6 +87,7 @@ AC_PATH_PROG([JAVAH], [javah]) AC_PATH_PROG([JAVA], [java]) AC_PATH_PROG([JAR], [jar]) AC_PATH_PROG([JAVADOC], [javadoc]) +AC_PATH_PROG([JAVADOC], [lua]) AC_DEBUG ## @@ -206,6 +208,24 @@ AM_CONDITIONAL(WITH_CPLUSPLUS_EXTENSIONS, [test "$ac_with_cplusplus_extensions" AC_JAVA_EXTENSIONS AM_CONDITIONAL(WITH_JAVA_EXTENSIONS, [test "$ac_with_java_extensions" = "yes"]) +# +# Check for lua, which means create commanline conditional +# +AC_LUA_EXTENSIONS +AM_CONDITIONAL(WITH_LUA_EXTENSIONS, [test "$ac_with_lua_extensions" = "yes"]) +# +# expand lua +# +if test "x$ac_with_lua_extensions" = "xyes" ; then +# beware, the ACTION-IF-NOT-FOUND is not working +AX_PROG_LUA([5.3],,,:) +# The test of headers fail, if LUA_VERSION is empty +if test LUA_VERSION ; then +AX_LUA_HEADERS +AX_LUA_LIBS +fi +fi + ## # Checks for typedefs, structures, and compiler characteristics. ## @@ -246,6 +266,7 @@ AC_CONFIG_FILES( \ src/extensions/perl/Genders/Makefile \ src/extensions/perl/Genders/Genders.pm \ src/extensions/python/Makefile \ + src/extensions/lua/Makefile \ src/extensions/python/genderssetup.py \ src/extensions/java/Makefile \ src/testsuite/Makefile \ diff --git a/genders.spec b/genders.spec new file mode 100644 index 0000000..cd09930 --- /dev/null +++ b/genders.spec @@ -0,0 +1,110 @@ +Name: genders +Version: 1.26 +Release: 1 +Summary: Static cluster configuration database +URL: https://github.com/chaos/genders +Group: System Environment/Base +License: GPL +Source: %{name}-%{version}.tar.gz +Requires: perl +BuildRequires: bison flex +BuildRequires: perl(ExtUtils::MakeMaker) +BuildRequires: python +BuildRequires: python-devel +BuildRequires: libtool +BuildRoot: %{_tmppath}/%{name}-%{version} + +%description +Genders is a static cluster configuration database used for cluster +configuration management. It is used by a variety of tools and +scripts for management of large clusters. The genders database is +typically replicated on every node of the cluster. It describes the +layout and configuration of the cluster so that tools and scripts can +sense the variations of cluster nodes. By abstracting this information +into a plain text file, it becomes possible to change the +configuration of a cluster by modifying only one file. + +%package compat +Summary: Compatibility library +Group: System Environment/Base +%description compat +genders API that is compatible with earlier releases of genders + +%{!?_with_perl_extensions: %{!?_without_perl_extensions: %define _with_perl_extensions --with-perl-extensions}} +%{!?_with_python_extensions: %{!?_without_python_extensions: %define _with_python_extensions --with-python-extensions}} +%{!?_with_cplusplus_extensions: %{!?_without_cplusplus_extensions: %define _with_cplusplus_extensions --with-cplusplus-extensions}} +%{!?_with_java_extensions: %{!?_without_java_extensions: %define _without_java_extensions --without-java-extensions}} +%{!?_with_lua_extensions: %{!?_without_lua_extensions: %define _without_lua_extensions --without-lua-extensions}} + +# choose vendor arch by default +%{!?_with_perl_site_arch: %{!?_with_perl_vendor_arch: %define _with_perl_vendor_arch --with-perl-vendor-arch}} + +%prep +%setup -q -n %{name}-%{version} + +%build +%configure --program-prefix=%{?_program_prefix:%{_program_prefix}} \ + --with-extension-destdir="$RPM_BUILD_ROOT" \ + %{?_with_perl_extensions} \ + %{?_without_perl_extensions} \ + %{?_with_perl_site_arch} \ + %{?_without_perl_site_arch} \ + %{?_with_perl_vendor_arch} \ + %{?_without_perl_vendor_arch} \ + %{?_with_python_extensions} \ + %{?_without_python_extensions} \ + %{?_with_cplusplus_extensions} \ + %{?_without_cplusplus_extensions} \ + %{?_with_java_extensions} \ + %{?_without_java_extensions}\ + %{?_with_lua_extensions} \ + %{?_without_lua_extension} +make + +%install +rm -rf $RPM_BUILD_ROOT +DESTDIR="$RPM_BUILD_ROOT" make install + +%files +%defattr(-,root,root) +%doc README NEWS ChangeLog DISCLAIMER DISCLAIMER.UC COPYING TUTORIAL genders.sample +%if %{?_with_java_extensions:1}%{!?_with_java_extensions:0} +%dir %{_datadir}/doc/%{name}-%{version}-javadoc/ +%doc %{_datadir}/doc/%{name}-%{version}-javadoc/* +%endif +# It doesn't matter if the user chooses a 32bit or 64bit target. The +# packaging must work off whatever Perl is installed. +%if %{?_with_perl_site_arch:1}%{!?_with_perl_site_arch:0} +%define _perldir %(perl -e 'use Config; $T=$Config{installsitearch}; $P=$Config{siteprefix}; $T=~/$P\\/(.*)/; print "%{_prefix}/$1\\n"') +%endif +%if %{?_with_perl_vendor_arch:1}%{!?_with_perl_vendor_arch:0} +%define _perldir %(perl -e 'use Config; $T=$Config{installvendorarch}; $P=$Config{vendorprefix}; $T=~/$P\\/(.*)/; print "%{_prefix}/$1\\n"') +%endif +%{_mandir}/man1/* +%{_mandir}/man3/genders* +%{_mandir}/man3/libgenders* +%{_includedir}/* +%{_bindir}/* +%{_libdir}/libgenders.* +%if %{?_with_perl_extensions:1}%{!?_with_perl_extensions:0} +%{_mandir}/man3/Libgenders* +%{_mandir}/man3/Genders* +%{_perldir}/* +%endif +%if %{?_with_python_extensions:1}%{!?_with_python_extensions:0} +%{_exec_prefix}/lib*/python* +%endif +%if %{?_with_cplusplus_extensions:1}%{!?_with_cplusplus_extensions:0} +%{_libdir}/libgendersplusplus.* +%endif +%if %{?_with_java_extensions:1}%{!?_with_java_extensions:0} +%{_javadir}/* +%{_libdir}/libGendersjni.* +%endif + +%files compat +%defattr(-,root,root) +%{_mandir}/man3/gendlib* +%{_prefix}/lib/genders/* + + diff --git a/src/extensions/Makefile.am b/src/extensions/Makefile.am index ec963c4..e85f6c2 100644 --- a/src/extensions/Makefile.am +++ b/src/extensions/Makefile.am @@ -4,4 +4,4 @@ ## Process this file with automake to produce Makefile.in. ##***************************************************************************** -SUBDIRS = cplusplus java perl python +SUBDIRS = cplusplus java perl python lua diff --git a/src/extensions/lua/GendersTest/GendersTest.lua b/src/extensions/lua/GendersTest/GendersTest.lua new file mode 100755 index 0000000..7e28473 --- /dev/null +++ b/src/extensions/lua/GendersTest/GendersTest.lua @@ -0,0 +1,134 @@ +#!/usr/bin/lua + +db_file = "testgenders" + +genders_lib = require("genders") +genders_handle = nil + +-- wrapper to for using pcall +function wrapper_new() + genders_handle = genders_lib.new(db_file) +end + +str_err = "" +str_out = "" + +no_load = nil +error_status = 0 + +getter_funcs = {"getnumattrs","getnumnodes","getnodes"} +list_nodes = {"foobar","hype355","null"} +list_attr = {"foobar","mgmt"} +-- attr <-> value mismatch as this is dict! +list_attrval = {["foobaar"] = "cfhost", ["hypei"] = "cfhost"} +list_query = { "mgmt","mgmt||login","mhmt&&login","~mgmt"} + +local open_lib, genders_handle = pcall(genders_lib.new,db_file,genders_lib) +if open_lib then + print("Loaded database \""..db_file.."\"") + for _, func_name in ipairs(getter_funcs) do + local str = "return function(handle) return pcall(handle."..func_name..",handle) end" + local wrap_func = assert(load(str)) + local func = wrap_func() + local ok,ret_val = func(genders_handle) + if ok then + if type(ret_val) ~= "table" then + print(func_name.." :: "..ret_val) + else + local tmp_str = func_name.." :: " + for _,val in ipairs(ret_val) do + tmp_str = tmp_str..val..", " + end + print(tmp_str) + end + else + error_status = 1 + str_err = "Could not call "..func_name..": "..ret_val + end + end + local ok, ret_val = pcall(genders_handle.getnodes,genders_handle,"mgmt") + if ok then + local tmp_str = "getnodes(\"mgmt\") :: " for _,val in ipairs(ret_val) do tmp_str = tmp_str..val..", " end + print(tmp_str) + else + error_status = 1 + str_err = "Could not call: getnodes(\"mgmt\")"..ret_val + end + local ok, ret_val = pcall(genders_handle.getnodes,genders_handle,"foobarlalala") + if ok then + local tmp_str = "getnodes(\"foobarlalala\") :: " for _,val in ipairs(ret_val) do tmp_str = tmp_str..val..", " end + print(tmp_str) + else + error_status = 1 + str_err = "Could not call: getnodes(\"foobarlalala\")"..ret_val + end + local ok, ret_val = pcall(genders_handle.getnodes,genders_handle,"cfhost","foobar") + if ok then + local tmp_str = "getnodes(\"cfhost\",\"foobar\") :: " for _,val in ipairs(ret_val) do tmp_str = tmp_str..val..", " end + print(tmp_str) + else + error_status = 1 + str_err = "Could not call: getnodes(\"cfhost\",\"foobar\")"..ret_val + end + local ok, ret_val = pcall(genders_handle.getnodes,genders_handle,"cfhost","hypei") + if ok then + local tmp_str = "getnodes(\"cfhost\",\"hypei\") :: " for _,val in ipairs(ret_val) do tmp_str = tmp_str..val..", " end + print(tmp_str) + else + error_status = 1 + str_err = "Could not call: getnodes(\"cfhost\",\"hypei\")"..ret_val + end + local ok, ret_val = pcall(genders_handle.getattr,genders_handle,"hype355") + if ok then + local tmp_str = "getattr(\"hype355\") :: " for key,value in pairs(ret_val) do tmp_str = tmp_str..key if value == "" then tmp_str = tmp_str..", " else tmp_str = tmp_str.."="..value.."," end end + print(tmp_str) + else + error_status = 1 + str_err = "Could not call: getattr(\"hype355\")"..ret_val + end + for _,node in ipairs(list_nodes) do + local ok, ret_val = pcall(genders_handle.isnode,genders_handle,node) + if ok then + if ret_val then print("isnode("..node..") = true") else print("isnode("..node..") = false") end + else + error_status = 1 + str_err = "Could not call: isnode("..node..") "..ret_val + end + end + for _,attr in ipairs(list_attr) do + local ok, ret_val = pcall(genders_handle.isattr,genders_handle,attr) + if ok then + if ret_val then print("isattr("..attr..") = true") else print("isattr("..attr..") = false") end + else + error_status = 1 + str_err = "Could not call: isattr("..attr..") "..ret_val + end + end + for value,attr in pairs(list_attrval) do + local ok, ret_val = pcall(genders_handle.isattrval,genders_handle,attr,value) + if ok then + if ret_val then print("isattrval("..attr..","..value..") = true") else print("isattrval("..attr..","..value..") = false") end + else + error_status = 1 + str_err = "Could not call: isattr("..attr..") "..ret_val + end + end + for _,qry in ipairs(list_query) do + local ok, ret_val = pcall(genders_handle.query,genders_handle,qry) + if ok then + local tmp_str = "query("..qry..") :: " for _,val in pairs(ret_val) do tmp_str = tmp_str..val..", " end + print(tmp_str) + else + error_status = 1 + str_err = "Could not call: query("..qry..")"..ret_val + end + end +else + str_err = "Failure in loading database \""..db_file.."\"" + error_status = 1 +end +if error_status == 0 then + print("No errors") +else + print("Collected errors are: "..str_err) +end diff --git a/src/extensions/lua/GendersTest/testgenders b/src/extensions/lua/GendersTest/testgenders new file mode 100644 index 0000000..4d17db1 --- /dev/null +++ b/src/extensions/lua/GendersTest/testgenders @@ -0,0 +1,21 @@ +########################################################################## +# $URL: file:///var/svn/cfengine/clusters/hype/genders $ +# $Author: tdhooge $ +# $Date: 2012-06-29 17:05:40 -0700 (Fri, 29 Jun 2012) $ +# $Rev: 3594 $ +########################################################################### + +# Mgmt +hype[137,355] pdsh_all_skip,mgmt,dhcpd,sshd,tftp,named,nfs,ntpserv +hype[137,355] networker,crond,rsshd + +hype355 primgmt,opensmd,powerman,conman,sendmail,sysloghost,moab +hype355 passwdhost=hype:hypei,sshkeyhost,cfhost=hypei,mysqld +hype355 skummee,perfmgr,httpd,logarch +hype137 httpd +hype137 altmgmt + +# Login +hype[136,356] login,cups,iptables,ksshd,ksshd_client,bluenet,atd +hype136 ipforw,skummee,mysqld +hype[136,356] crond,rsshd,psacct diff --git a/src/extensions/lua/Makefile.am b/src/extensions/lua/Makefile.am new file mode 100644 index 0000000..4512cee --- /dev/null +++ b/src/extensions/lua/Makefile.am @@ -0,0 +1,21 @@ +# DESTDIR is usually set during make install time, not configure or +# make time, so we work around it with the --with-extension-destdir +# autoconf option. + + +if WITH_LUA_EXTENSIONS + +luaexec_LTLIBRARIES = genders.la + +genders_la_SOURCES = genderslua.c + +genders_la_CFLAGS = @LUA_INCLUDE@ -I../../libgenders/ + +genders_la_LDFLAGS = -module -avoid-version + +genders_la_LIBADD = ../../libgenders/libgenders.la $(OTHER_FLAGS) @LUA_LIB@ + + +endif + +EXTRA_DIST = genderslua.c diff --git a/src/extensions/lua/genderslua.c b/src/extensions/lua/genderslua.c new file mode 100644 index 0000000..f2364af --- /dev/null +++ b/src/extensions/lua/genderslua.c @@ -0,0 +1,322 @@ +/***************************************************************************** + * Copyright (C) 2018 SUSE LLC + * Written by Christian Goll + * + * This file is part of Genders, a cluster configuration database. + * For details, see . + * + * Genders is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * Genders is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along + * with Genders. If not, see . +\*****************************************************************************/ +#if HAVE_CONFIG_H +#include "config.h" +#endif /* HAVE_CONFIG_H */ +#include +#include +#include +#include +#include +#include + +typedef struct { + /* genders_t itself is a pointer */ + genders_t handle; + char *db_name; + } lgenders_userdata_t; + +static int lgenders_new(lua_State *L) { + lgenders_userdata_t *dbh; + const char *db_name, *g_error; + /* check for argument vailidy */ + db_name = luaL_checkstring(L,1); + if (db_name == NULL) + luaL_error(L,"database name could not be empty"); + /* Create the user data pushing it onto the stack. We also pre-initialize + * the member of the userdata in case initialization fails in some way. If + * that happens we want the userdata to be in a consistent state for __gc. + */ + dbh = (lgenders_userdata_t *)lua_newuserdata(L, sizeof(*dbh)); + dbh->handle = NULL; + dbh->db_name = NULL; + /* Add the metatable to the stack. */ + luaL_getmetatable(L, "LGenders"); + /* Set the metatable on the userdata. */ + lua_setmetatable(L, -2); + /* Create the handle */ + dbh->handle = genders_handle_create(); + dbh->db_name = strdup(db_name); + if(genders_load_data(dbh->handle,dbh->db_name) != 0) { + g_error = strdup(genders_errormsg(dbh->handle)); + luaL_error(L,g_error); + } + return 1; +} +static int lgenders_reload(lua_State *L) { + lgenders_userdata_t *dbh; + const char *db_name, *g_error; + dbh = (lgenders_userdata_t *)luaL_checkudata(L, 1, "LGenders"); + /* check for argument vailidy */ + db_name = luaL_checkstring(L,2); + if (db_name == NULL) + db_name = strdup(dbh->db_name); + if (dbh->handle != NULL) + genders_handle_destroy(dbh->handle); + dbh->db_name = NULL; + /* Create the handle */ + dbh->handle = genders_handle_create(); + dbh->db_name = strdup(db_name); + if(genders_load_data(dbh->handle,dbh->db_name) != 0) { + g_error = strdup(genders_errormsg(dbh->handle)); + luaL_error(L,g_error); + } + return 0; +} +static int lgenders_destroy(lua_State *L) { + lgenders_userdata_t *dbh; + dbh = (lgenders_userdata_t *)luaL_checkudata(L, 1, "LGenders"); + /* pass return value to lus */ + if (dbh->handle != NULL) + genders_handle_destroy(dbh->handle); + + if (dbh->db_name != NULL) + free(dbh->db_name); + dbh->db_name = NULL; + return 0; +} + +static int lgenders_getnumnodes(lua_State *L) { + lgenders_userdata_t *dbh; + dbh = (lgenders_userdata_t *)luaL_checkudata(L, 1, "LGenders"); + lua_pushinteger(L,genders_getnumnodes(dbh->handle)); + return 1; +} + +static int lgenders_getnumattrs(lua_State *L) { + lgenders_userdata_t *dbh; + dbh = (lgenders_userdata_t *)luaL_checkudata(L, 1, "LGenders"); + lua_pushinteger(L,genders_getnumattrs(dbh->handle)); + return 1; +} + +static int lgenders_getnodes(lua_State *L) { + char** nodelist; + const char *g_error, *attr, *val; + int size, nr_nodes, i, nr_args = 0; + lgenders_userdata_t *dbh; + dbh = (lgenders_userdata_t *)luaL_checkudata(L, 1, "LGenders"); + /* create space for the genders stuff */ + size = genders_nodelist_create(dbh->handle,&nodelist); + if(size == -1) { + g_error = strdup(genders_errormsg(dbh->handle)); + luaL_error(L,g_error); + } + attr = NULL; val = NULL; + /* check for attr and val */ + nr_args = lua_gettop(L); + if(nr_args > 1) + attr = luaL_checkstring(L,2); + if(nr_args > 2) + val = luaL_checkstring(L,3); + if(nr_args > 3) + luaL_error(L,"getnodes accepts none,one or two arguments"); + nr_nodes = genders_getnodes(dbh->handle,nodelist,size,attr,val); + lua_newtable(L); + for(i = 0; i < nr_nodes; i++) { + lua_pushstring(L,nodelist[i]); + lua_rawseti(L,-2,i+1); + } + /* destroy list of nodes */ + if(genders_nodelist_destroy(dbh->handle,nodelist) == -1) { + g_error = strdup(genders_errormsg(dbh->handle)); + luaL_error(L,g_error); + } + return 1; +} + +static int lgenders_query(lua_State *L) { + char** nodelist; + const char *g_error, *query; + int size, nr_nodes, i, nr_args = 0; + lgenders_userdata_t *dbh; + dbh = (lgenders_userdata_t *)luaL_checkudata(L, 1, "LGenders"); + /* create space for the genders stuff */ + size = genders_nodelist_create(dbh->handle,&nodelist); + if(size == -1) { + g_error = strdup(genders_errormsg(dbh->handle)); + luaL_error(L,g_error); + } + nr_args = lua_gettop(L); + if(nr_args == 2) + query = luaL_checkstring(L,2); + else + luaL_error(L,"query must be called with one argument"); + nr_nodes = genders_query(dbh->handle,nodelist,size,query); + lua_newtable(L); + for(i = 0; i < nr_nodes; i++) { + lua_pushstring(L,nodelist[i]); + lua_rawseti(L,-2,i+1); + } + /* destroy list of nodes */ + if(genders_nodelist_destroy(dbh->handle,nodelist) == -1) { + g_error = strdup(genders_errormsg(dbh->handle)); + luaL_error(L,g_error); + } + return 1; +} +static int lgenders_getattr(lua_State *L) { + char **attr_list, **val_list; + const char *node, *g_error; + int ret_code, nr_args, size_attr, i = 0; + lgenders_userdata_t *dbh; + dbh = (lgenders_userdata_t *)luaL_checkudata(L, 1, "LGenders"); + nr_args = lua_gettop(L); + if(nr_args == 2) + node = luaL_checkstring(L,2); + else + luaL_error(L,"query must be called with one argument"); + /* create space for the lists */ + ret_code = genders_attrlist_create(dbh->handle,&attr_list); + if(ret_code == -1) { + g_error = strdup(genders_errormsg(dbh->handle)); + luaL_error(L,g_error); + } + ret_code = genders_vallist_create(dbh->handle,&val_list); + if(ret_code == -1) { + g_error = strdup(genders_errormsg(dbh->handle)); + luaL_error(L,g_error); + } + /* hopefully size (which is in ret_code) of attr_list and val_list are the same */ + size_attr = genders_getattr(dbh->handle,attr_list,val_list,ret_code,node); + if(size_attr == -1) { + g_error = strdup(genders_errormsg(dbh->handle)); + luaL_error(L,g_error); + } + lua_newtable(L); + for(i = 0; i < size_attr; i++) { + lua_pushstring(L, attr_list[i]); + lua_pushstring(L, val_list[i]); + lua_settable(L, -3); + } + /* destroy list of nodes */ + if(genders_vallist_destroy(dbh->handle,val_list) == -1) { + g_error = strdup(genders_errormsg(dbh->handle)); + luaL_error(L,g_error); + } + if(genders_attrlist_destroy(dbh->handle,attr_list) == -1) { + g_error = strdup(genders_errormsg(dbh->handle)); + luaL_error(L,g_error); + } + return 1; +} + +static int lgenders_isnode(lua_State *L) { + const char *node, *g_error; + int ret_code, nr_args; + lgenders_userdata_t *dbh; + dbh = (lgenders_userdata_t *)luaL_checkudata(L, 1, "LGenders"); + nr_args = lua_gettop(L); + if(nr_args == 2) + node = luaL_checkstring(L,2); + else + luaL_error(L,"isnode must be called with one argument"); + ret_code = genders_isnode(dbh->handle,node); + if(ret_code == -1) { + g_error = strdup(genders_errormsg(dbh->handle)); + luaL_error(L,g_error); + } + lua_pushboolean(L,ret_code); + return 1; +} + +static int lgenders_isattr(lua_State *L) { + const char *attr, *g_error; + int ret_code, nr_args; + lgenders_userdata_t *dbh; + dbh = (lgenders_userdata_t *)luaL_checkudata(L, 1, "LGenders"); + nr_args = lua_gettop(L); + if(nr_args == 2) + attr = luaL_checkstring(L,2); + else + luaL_error(L,"isattr must be called with one argument"); + ret_code = genders_isattr(dbh->handle,attr); + if(ret_code == -1) { + g_error = strdup(genders_errormsg(dbh->handle)); + luaL_error(L,g_error); + } + lua_pushboolean(L,ret_code); + return 1; +} + +static int lgenders_isattrval(lua_State *L) { + const char *attr, *val, *g_error; + int ret_code, nr_args; + lgenders_userdata_t *dbh; + dbh = (lgenders_userdata_t *)luaL_checkudata(L, 1, "LGenders"); + nr_args = lua_gettop(L); + if(nr_args == 3) { + attr = luaL_checkstring(L,2); + val = luaL_checkstring(L,3); + } + else + luaL_error(L,"isattrval must be called with two arguments"); + ret_code = genders_isattrval(dbh->handle,attr,val); + if(ret_code == -1) { + g_error = strdup(genders_errormsg(dbh->handle)); + luaL_error(L,g_error); + } + lua_pushboolean(L,ret_code); + return 1; +} + +static const struct luaL_Reg genders_methods[] = { + {"getnumattrs",lgenders_getnumattrs}, + {"getnumnodes",lgenders_getnumnodes}, + {"getnodes",lgenders_getnodes}, + {"getattr",lgenders_getattr}, + {"query",lgenders_query}, + {"isnode",lgenders_isnode}, + {"isattr",lgenders_isattr}, + {"isattrval",lgenders_isattrval}, + {"reload",lgenders_reload}, + {"__gc",lgenders_destroy}, + {NULL,NULL}, +}; + +static const struct luaL_Reg genders_functions[] = { + { "new", lgenders_new }, + { NULL, NULL} +}; + + +int luaopen_genders(lua_State *L) { + /* Create the metatable and put it on the stack. */ + luaL_newmetatable(L, "LGenders"); + /* Duplicate the metatable on the stack (We know have 2). */ + lua_pushvalue(L, -1); + /* Pop the first metatable off the stack and assign it to __index + * of the second one. We set the metatable for the table to itself. + * This is equivalent to the following in lua: + * metatable = {} + * metatable.__index = metatable + */ + lua_setfield(L, -2, "__index"); + + /* Set the methods to the metatable that should be accessed via object:func */ + luaL_setfuncs(L, genders_methods, 0); + + /* Register the object.func functions into the table that is at the top of the + * stack. */ + luaL_newlib(L, genders_functions); + + return 1; +} -- 2.37.1