Convert the lfs module to using the ffi
This commit is contained in:
parent
4f08afd808
commit
0cf35894e1
8 changed files with 260 additions and 133 deletions
48
automation/include/aegisub/ffi.moon
Normal file
48
automation/include/aegisub/ffi.moon
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
-- Copyright (c) 2014, Thomas Goyne <plorkyeran@aegisub.org>
|
||||||
|
--
|
||||||
|
-- Permission to use, copy, modify, and distribute this software for any
|
||||||
|
-- purpose with or without fee is hereby granted, provided that the above
|
||||||
|
-- copyright notice and this permission notice appear in all copies.
|
||||||
|
--
|
||||||
|
-- THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
-- WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
-- MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
-- ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
-- WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
-- ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
-- OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
--
|
||||||
|
-- Aegisub Project http://www.aegisub.org/
|
||||||
|
|
||||||
|
ffi = require 'ffi'
|
||||||
|
ffi.cdef[[
|
||||||
|
void free(void *ptr);
|
||||||
|
]]
|
||||||
|
|
||||||
|
char_ptr = ffi.typeof 'char *'
|
||||||
|
|
||||||
|
-- Convert a const char * to a lua string, returning nil rather than crashing
|
||||||
|
-- if it's NULL and freeing the input if it's non-const
|
||||||
|
string = (cdata) ->
|
||||||
|
return nil if cdata == nil
|
||||||
|
str = ffi.string cdata
|
||||||
|
if type(cdata) == char_ptr
|
||||||
|
ffi.C.free cdata
|
||||||
|
str
|
||||||
|
|
||||||
|
err_buff = ffi.new 'char *[1]'
|
||||||
|
|
||||||
|
-- Convert a function which has an error out parameter to one which returns
|
||||||
|
-- the original return value and the error as a string
|
||||||
|
err_arg_to_multiple_return = (f) -> (arg) ->
|
||||||
|
err_buff[0] = nil
|
||||||
|
result = if arg != nil
|
||||||
|
f arg, err_buff
|
||||||
|
else
|
||||||
|
f err_buff
|
||||||
|
errmsg = string err_buff[0]
|
||||||
|
if errmsg
|
||||||
|
return nil, errmsg
|
||||||
|
return result
|
||||||
|
|
||||||
|
{:string, :err_arg_to_multiple_return}
|
80
automation/include/aegisub/lfs.moon
Normal file
80
automation/include/aegisub/lfs.moon
Normal file
|
@ -0,0 +1,80 @@
|
||||||
|
-- Copyright (c) 2014, Thomas Goyne <plorkyeran@aegisub.org>
|
||||||
|
--
|
||||||
|
-- Permission to use, copy, modify, and distribute this software for any
|
||||||
|
-- purpose with or without fee is hereby granted, provided that the above
|
||||||
|
-- copyright notice and this permission notice appear in all copies.
|
||||||
|
--
|
||||||
|
-- THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
-- WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
-- MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
-- ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
-- WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
-- ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
-- OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
--
|
||||||
|
-- Aegisub Project http://www.aegisub.org/
|
||||||
|
|
||||||
|
impl = require 'aegisub.__lfs_impl'
|
||||||
|
ffi = require 'ffi'
|
||||||
|
ffi_util = require 'aegisub.ffi'
|
||||||
|
|
||||||
|
for k, v in pairs impl
|
||||||
|
impl[k] = ffi_util.err_arg_to_multiple_return v
|
||||||
|
|
||||||
|
string_ret = (f) -> (...) ->
|
||||||
|
res, err = f ...
|
||||||
|
ffi_util.string(res), err
|
||||||
|
|
||||||
|
number_ret = (f) -> (...) ->
|
||||||
|
res, err = f ...
|
||||||
|
tonumber(res), err
|
||||||
|
|
||||||
|
attributes = (path, field) ->
|
||||||
|
switch field
|
||||||
|
when 'mode'
|
||||||
|
res, err = impl.get_mode path
|
||||||
|
ffi_util.string(res), err
|
||||||
|
when 'modification'
|
||||||
|
res, err = impl.get_mtime path
|
||||||
|
tonumber(res), err
|
||||||
|
when 'size'
|
||||||
|
res, err = impl.get_size path
|
||||||
|
tonumber(res), err
|
||||||
|
else
|
||||||
|
mode, err = impl.get_mode path
|
||||||
|
if err or mode == nil then return nil, err
|
||||||
|
|
||||||
|
mod, err = impl.get_mtime path
|
||||||
|
if err then return nil, err
|
||||||
|
|
||||||
|
size, err = impl.get_size path
|
||||||
|
if err then return nil, err
|
||||||
|
|
||||||
|
mode: ffi_util.string(mode), modification: tonumber(mod), size: tonumber(size)
|
||||||
|
|
||||||
|
class dir_iter
|
||||||
|
new: (iter) =>
|
||||||
|
@iter = ffi.gc iter, -> impl.dir_free iter
|
||||||
|
close: =>
|
||||||
|
impl.dir_close @iter
|
||||||
|
next: =>
|
||||||
|
str, err = impl.dir_next @iter
|
||||||
|
if err then error err, 2
|
||||||
|
ffi_util.string str
|
||||||
|
|
||||||
|
dir = (path) ->
|
||||||
|
obj, err = impl.dir_new path
|
||||||
|
if err
|
||||||
|
error 2, err
|
||||||
|
iter = dir_iter obj
|
||||||
|
iter.next, iter
|
||||||
|
|
||||||
|
return {
|
||||||
|
:attributes
|
||||||
|
chdir: number_ret impl.chdir
|
||||||
|
currentdir: string_ret impl.currentdir
|
||||||
|
:dir
|
||||||
|
mkdir: number_ret impl.mkdir
|
||||||
|
rmdir: number_ret impl.rmdir
|
||||||
|
touch: number_ret impl.touch
|
||||||
|
}
|
|
@ -30,25 +30,16 @@
|
||||||
|
|
||||||
impl = require 'aegisub.__unicode_impl'
|
impl = require 'aegisub.__unicode_impl'
|
||||||
ffi = require 'ffi'
|
ffi = require 'ffi'
|
||||||
ffi.cdef[[
|
ffi_util = require 'aegisub.ffi'
|
||||||
void free(void *ptr);
|
|
||||||
]]
|
|
||||||
|
|
||||||
transfer_string = (cdata) ->
|
err_buff = ffi.new 'char *[1]'
|
||||||
return nil if cdata == nil
|
conv_func = (f) -> (str) ->
|
||||||
str = ffi.string cdata
|
err_buff[0] = nil
|
||||||
ffi.C.free cdata
|
result = f str, err_buff
|
||||||
str
|
errmsg = ffi_util.string err_buff[0]
|
||||||
|
if errmsg
|
||||||
conv_func = (f) ->
|
error errmsg, 2
|
||||||
err = ffi.new 'char *[1]'
|
ffi_util.string result
|
||||||
(str) ->
|
|
||||||
err[0] = nil
|
|
||||||
result = f str, err
|
|
||||||
errmsg = transfer_string err[0]
|
|
||||||
if errmsg
|
|
||||||
error errmsg, 2
|
|
||||||
transfer_string result
|
|
||||||
|
|
||||||
local unicode
|
local unicode
|
||||||
unicode =
|
unicode =
|
||||||
|
|
2
automation/include/lfs.lua
Normal file
2
automation/include/lfs.lua
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
lfs = require 'aegisub.lfs'
|
||||||
|
return lfs
|
|
@ -12,7 +12,7 @@
|
||||||
-- ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
-- ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
-- OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
-- OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
|
||||||
lfs = require 'lfs'
|
lfs = require 'aegisub.lfs'
|
||||||
uuid = require 'uuid'
|
uuid = require 'uuid'
|
||||||
|
|
||||||
uuid.randomseed os.time()
|
uuid.randomseed os.time()
|
||||||
|
|
|
@ -21,15 +21,19 @@ namespace agi {
|
||||||
// Currently supports primitives, pointers, references, const, and function pointers
|
// Currently supports primitives, pointers, references, const, and function pointers
|
||||||
template<typename T> struct type_name;
|
template<typename T> struct type_name;
|
||||||
|
|
||||||
#define AGI_TYPE_NAME_PRIMITIVE(type) \
|
#define AGI_DEFINE_TYPE_NAME(type) \
|
||||||
template<> struct type_name<type> { static const char *name() { return #type; }}
|
template<> struct type_name<type> { static const char *name() { return #type; }}
|
||||||
|
|
||||||
AGI_TYPE_NAME_PRIMITIVE(bool);
|
AGI_DEFINE_TYPE_NAME(bool);
|
||||||
AGI_TYPE_NAME_PRIMITIVE(char);
|
AGI_DEFINE_TYPE_NAME(char);
|
||||||
AGI_TYPE_NAME_PRIMITIVE(double);
|
AGI_DEFINE_TYPE_NAME(double);
|
||||||
AGI_TYPE_NAME_PRIMITIVE(float);
|
AGI_DEFINE_TYPE_NAME(float);
|
||||||
AGI_TYPE_NAME_PRIMITIVE(int);
|
AGI_DEFINE_TYPE_NAME(int);
|
||||||
AGI_TYPE_NAME_PRIMITIVE(void);
|
AGI_DEFINE_TYPE_NAME(long long);
|
||||||
|
AGI_DEFINE_TYPE_NAME(long);
|
||||||
|
AGI_DEFINE_TYPE_NAME(size_t);
|
||||||
|
AGI_DEFINE_TYPE_NAME(unsigned long long);
|
||||||
|
AGI_DEFINE_TYPE_NAME(void);
|
||||||
|
|
||||||
#undef AGI_TYPE_NAME_PRIMITIVE
|
#undef AGI_TYPE_NAME_PRIMITIVE
|
||||||
|
|
||||||
|
|
|
@ -21,7 +21,7 @@
|
||||||
extern "C" int luaopen_luabins(lua_State *L);
|
extern "C" int luaopen_luabins(lua_State *L);
|
||||||
extern "C" int luaopen_re_impl(lua_State *L);
|
extern "C" int luaopen_re_impl(lua_State *L);
|
||||||
extern "C" int luaopen_unicode_impl(lua_State *L);
|
extern "C" int luaopen_unicode_impl(lua_State *L);
|
||||||
extern "C" int luaopen_lfs(lua_State *L);
|
extern "C" int luaopen_lfs_impl(lua_State *L);
|
||||||
extern "C" int luaopen_lpeg(lua_State *L);
|
extern "C" int luaopen_lpeg(lua_State *L);
|
||||||
|
|
||||||
namespace agi { namespace lua {
|
namespace agi { namespace lua {
|
||||||
|
@ -35,7 +35,7 @@ void preload_modules(lua_State *L) {
|
||||||
|
|
||||||
set_field(L, "aegisub.__re_impl", luaopen_re_impl);
|
set_field(L, "aegisub.__re_impl", luaopen_re_impl);
|
||||||
set_field(L, "aegisub.__unicode_impl", luaopen_unicode_impl);
|
set_field(L, "aegisub.__unicode_impl", luaopen_unicode_impl);
|
||||||
set_field(L, "lfs", luaopen_lfs);
|
set_field(L, "aegisub.__lfs_impl", luaopen_lfs_impl);
|
||||||
set_field(L, "lpeg", luaopen_lpeg);
|
set_field(L, "lpeg", luaopen_lpeg);
|
||||||
set_field(L, "luabins", luaopen_luabins);
|
set_field(L, "luabins", luaopen_luabins);
|
||||||
|
|
||||||
|
|
|
@ -14,150 +14,152 @@
|
||||||
//
|
//
|
||||||
// Aegisub Project http://www.aegisub.org/
|
// Aegisub Project http://www.aegisub.org/
|
||||||
|
|
||||||
#include "libaegisub/lua/utils.h"
|
#include "libaegisub/fs.h"
|
||||||
|
#include "libaegisub/type_name.h"
|
||||||
|
|
||||||
#include <boost/filesystem/operations.hpp>
|
#include <boost/filesystem/operations.hpp>
|
||||||
#include <boost/filesystem/path.hpp>
|
#include <boost/filesystem/path.hpp>
|
||||||
#include <boost/range/size.hpp>
|
#include <lua.hpp>
|
||||||
|
|
||||||
namespace {
|
|
||||||
using namespace agi::lua;
|
|
||||||
using namespace agi::fs;
|
using namespace agi::fs;
|
||||||
namespace bfs = boost::filesystem;
|
namespace bfs = boost::filesystem;
|
||||||
|
|
||||||
template<void (*func)(lua_State *L)>
|
namespace agi {
|
||||||
int wrap(lua_State *L) {
|
AGI_DEFINE_TYPE_NAME(DirectoryIterator);
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
template<typename Func>
|
||||||
|
auto wrap(char **err, Func f) -> decltype(f()) {
|
||||||
try {
|
try {
|
||||||
func(L);
|
return f();
|
||||||
return 1;
|
|
||||||
}
|
}
|
||||||
catch (bfs::filesystem_error const& e) {
|
catch (bfs::filesystem_error const& e) {
|
||||||
lua_pushnil(L);
|
*err = strdup(e.what());
|
||||||
push_value(L, e.what());
|
return 0;
|
||||||
return 2;
|
|
||||||
}
|
}
|
||||||
catch (agi::Exception const& e) {
|
catch (agi::Exception const& e) {
|
||||||
lua_pushnil(L);
|
*err = strdup(e.GetMessage().c_str());
|
||||||
push_value(L, e.GetMessage());
|
return 0;
|
||||||
return 2;
|
|
||||||
}
|
|
||||||
catch (error_tag) {
|
|
||||||
return lua_error(L);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void chdir(lua_State *L) {
|
template<typename Ret>
|
||||||
bfs::current_path(check_string(L, 1));
|
bool setter(const char *path, char **err, Ret (*f)(bfs::path const&)) {
|
||||||
push_value(L, true);
|
return wrap(err, [=]{
|
||||||
|
f(path);
|
||||||
|
return true;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void currentdir(lua_State *L) {
|
bool lfs_chdir(const char *dir, char **err) {
|
||||||
push_value(L, bfs::current_path());
|
return setter(dir, err, &bfs::current_path);
|
||||||
}
|
}
|
||||||
|
|
||||||
void mkdir(lua_State *L) {
|
char *currentdir(char **err) {
|
||||||
CreateDirectory(check_string(L, 1));
|
return wrap(err, []{
|
||||||
push_value(L, true);
|
return strdup(bfs::current_path().string().c_str());
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void rmdir(lua_State *L) {
|
bool mkdir(const char *dir, char **err) {
|
||||||
Remove(check_string(L, 1));
|
return setter(dir, err, &CreateDirectory);
|
||||||
push_value(L, true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void touch(lua_State *L) {
|
bool lfs_rmdir(const char *dir, char **err) {
|
||||||
Touch(check_string(L, 1));
|
return setter(dir, err, &Remove);
|
||||||
push_value(L, true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int dir_next(lua_State *L) {
|
bool touch(const char *path, char **err) {
|
||||||
auto& it = get<agi::fs::DirectoryIterator>(L, 1, "aegisub.lfs.dir");
|
return setter(path, err, &Touch);
|
||||||
if (it == end(it)) return 0;
|
|
||||||
push_value(L, *it);
|
|
||||||
++it;
|
|
||||||
return 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int dir_close(lua_State *L) {
|
char *dir_next(DirectoryIterator &it, char **err) {
|
||||||
auto& it = get<agi::fs::DirectoryIterator>(L, 1, "aegisub.lfs.dir");
|
if (it == end(it)) return nullptr;
|
||||||
// Convert to end iterator rather than destroying to avoid crashes if this
|
return wrap(err, [&]{
|
||||||
// is called multiple times
|
auto str = strdup((*it).c_str());
|
||||||
|
++it;
|
||||||
|
return str;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void dir_close(DirectoryIterator &it) {
|
||||||
it = DirectoryIterator();
|
it = DirectoryIterator();
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int dir(lua_State *L) {
|
void dir_free(DirectoryIterator *it) {
|
||||||
const path p = check_string(L, 1);
|
delete it;
|
||||||
push_value(L, dir_next);
|
|
||||||
make<agi::fs::DirectoryIterator>(L, "aegisub.lfs.dir", check_string(L, 1), "");
|
|
||||||
return 2;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void attributes(lua_State *L) {
|
DirectoryIterator *dir_new(const char *path, char **err) {
|
||||||
static std::pair<const char *, void (*)(lua_State *, path const&)> fields[] = {
|
return wrap(err, [=]{
|
||||||
{"mode", [](lua_State *L, path const& p) {
|
return new DirectoryIterator(path, "");
|
||||||
switch (status(p).type()) {
|
});
|
||||||
case bfs::file_not_found: lua_pushnil(L); break;
|
}
|
||||||
case bfs::regular_file: push_value(L, "file"); break;
|
|
||||||
case bfs::directory_file: push_value(L, "directory"); break;
|
|
||||||
case bfs::symlink_file: push_value(L, "link"); break;
|
|
||||||
case bfs::block_file: push_value(L, "block device"); break;
|
|
||||||
case bfs::character_file: push_value(L, "char device"); break;
|
|
||||||
case bfs::fifo_file: push_value(L, "fifo"); break;
|
|
||||||
case bfs::socket_file: push_value(L, "socket"); break;
|
|
||||||
case bfs::reparse_file: push_value(L, "reparse point"); break;
|
|
||||||
default: push_value(L, "other"); break;
|
|
||||||
}
|
|
||||||
}},
|
|
||||||
{"modification", [](lua_State *L, path const& p) { push_value(L, ModifiedTime(p)); }},
|
|
||||||
{"size", [](lua_State *L, path const& p) { push_value(L, Size(p)); }}
|
|
||||||
};
|
|
||||||
|
|
||||||
const path p = check_string(L, 1);
|
const char *get_mode(const char *path, char **err) {
|
||||||
|
return wrap(err, [=]() -> const char * {
|
||||||
const auto field = get_string(L, 2);
|
switch (bfs::status(path).type()) {
|
||||||
if (!field.empty()) {
|
case bfs::file_not_found: return nullptr; break;
|
||||||
for (const auto getter : fields) {
|
case bfs::regular_file: return "file"; break;
|
||||||
if (field == getter.first) {
|
case bfs::directory_file: return "directory"; break;
|
||||||
getter.second(L, p);
|
case bfs::symlink_file: return "link"; break;
|
||||||
return;
|
case bfs::block_file: return "block device"; break;
|
||||||
}
|
case bfs::character_file: return "char device"; break;
|
||||||
|
case bfs::fifo_file: return "fifo"; break;
|
||||||
|
case bfs::socket_file: return "socket"; break;
|
||||||
|
case bfs::reparse_file: return "reparse point"; break;
|
||||||
|
default: return "other"; break;
|
||||||
}
|
}
|
||||||
error(L, "Invalid attribute name: %s", field.c_str());
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
lua_createtable(L, 0, boost::size(fields));
|
time_t get_mtime(const char *path, char **err) {
|
||||||
for (const auto getter : fields) {
|
return wrap(err, [=] { return ModifiedTime(path); });
|
||||||
getter.second(L, p);
|
}
|
||||||
lua_setfield(L, -2, getter.first);
|
|
||||||
}
|
uintmax_t get_size(const char *path, char **err) {
|
||||||
|
return wrap(err, [=] { return Size(path); });
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
void push_ffi_function(lua_State *L, const char *name, T *func) {
|
||||||
|
lua_pushvalue(L, -2); // push cast function
|
||||||
|
lua_pushstring(L, agi::type_name<T*>::name().c_str());
|
||||||
|
// This cast isn't legal, but LuaJIT internally requires that it work
|
||||||
|
lua_pushlightuserdata(L, (void *)func);
|
||||||
|
lua_call(L, 2, 1);
|
||||||
|
lua_setfield(L, -2, name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C" int luaopen_lfs(lua_State *L) {
|
extern "C" int luaopen_lfs_impl(lua_State *L) {
|
||||||
if (luaL_newmetatable(L, "aegisub.lfs.dir")) {
|
lua_getglobal(L, "require");
|
||||||
set_field<dir_close>(L, "__gc");
|
lua_pushstring(L, "ffi");
|
||||||
|
lua_call(L, 1, 1);
|
||||||
|
|
||||||
lua_createtable(L, 0, 2);
|
lua_getfield(L, -1, "cdef");
|
||||||
set_field<dir_next>(L, "next");
|
lua_pushstring(L, "typedef struct DirectoryIterator DirectoryIterator;");
|
||||||
set_field<dir_close>(L, "close");
|
lua_call(L, 1, 0);
|
||||||
lua_setfield(L, -2, "__index");
|
|
||||||
lua_pop(L, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
const struct luaL_Reg lib[] = {
|
lua_getfield(L, -1, "cast");
|
||||||
{"attributes", wrap<attributes>},
|
lua_remove(L, -2); // ffi table
|
||||||
{"chdir", wrap<chdir>},
|
|
||||||
{"currentdir", wrap<currentdir>},
|
lua_createtable(L, 0, 12);
|
||||||
{"dir", exception_wrapper<dir>},
|
push_ffi_function(L, "chdir", lfs_chdir);
|
||||||
{"mkdir", wrap<mkdir>},
|
push_ffi_function(L, "currentdir", currentdir);
|
||||||
{"rmdir", wrap<rmdir>},
|
push_ffi_function(L, "mkdir", mkdir);
|
||||||
{"touch", wrap<touch>},
|
push_ffi_function(L, "rmdir", lfs_rmdir);
|
||||||
{nullptr, nullptr},
|
push_ffi_function(L, "touch", touch);
|
||||||
};
|
push_ffi_function(L, "get_mtime", get_mtime);
|
||||||
lua_createtable(L, 0, boost::size(lib) - 1);
|
push_ffi_function(L, "get_mode", get_mode);
|
||||||
luaL_register(L, nullptr, lib);
|
push_ffi_function(L, "get_size", get_size);
|
||||||
lua_pushvalue(L, -1);
|
|
||||||
lua_setglobal(L, "lfs");
|
push_ffi_function(L, "dir_new", dir_new);
|
||||||
|
push_ffi_function(L, "dir_free", dir_free);
|
||||||
|
push_ffi_function(L, "dir_next", dir_next);
|
||||||
|
push_ffi_function(L, "dir_close", dir_close);
|
||||||
|
|
||||||
|
lua_remove(L, -2); // ffi.cast function
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue