mpv: sane_sysvol: fix non_blocking_popen to use metatables and be more readable
This commit is contained in:
parent
f50c0a98c2
commit
4dde01245e
|
@ -3,78 +3,89 @@
|
|||
--
|
||||
-- Implements a basic binding for popen that allows non-blocking reads
|
||||
-- returned "file" table only supports :read(with an optional size argument, no mode etc.) and :close
|
||||
ffi = require("ffi")
|
||||
-- C functions that we need
|
||||
ffi.cdef([[
|
||||
void* popen(const char* cmd, const char* mode);
|
||||
int pclose(void* stream);
|
||||
int fileno(void* stream);
|
||||
int fcntl(int fd, int cmd, int arg);
|
||||
int *__errno_location ();
|
||||
ssize_t read(int fd, void* buf, size_t count);
|
||||
]])
|
||||
|
||||
-- you can compile a simple C programm to find these values(Or look in the headers)
|
||||
F_SETFL = 4
|
||||
O_NONBLOCK = 2048
|
||||
EAGAIN = 11
|
||||
|
||||
-- this "array" holds the errno variable
|
||||
_errno = ffi.C.__errno_location()
|
||||
|
||||
popen_meta = {
|
||||
__index = {
|
||||
-- close the process, prevent reading, allow garbage colletion
|
||||
close = function(self)
|
||||
if self._file ~= nil then
|
||||
local _file = self._file
|
||||
self._file = nil
|
||||
self._fd = nil
|
||||
self._read_buffer = nil
|
||||
ffi.C.pclose(_file)
|
||||
end
|
||||
end,
|
||||
-- read up to size bytes from the process. Returns data(string) and number of bytes read if successfull,
|
||||
-- nil, "EAGAIN" if there is no data aviable, and
|
||||
-- nil, "closed" if the process has ended
|
||||
read = function(self, size)
|
||||
if self._fd == nil then
|
||||
return nil, "closed"
|
||||
end
|
||||
|
||||
size = math.min(self._read_buffer_size, size)
|
||||
local nbytes = ffi.C.read(self._fd, self._read_buffer, size)
|
||||
|
||||
if nbytes > 0 then
|
||||
local data = ffi.string(self._read_buffer, nbytes)
|
||||
return data, nbytes
|
||||
elseif (nbytes == -1) and (_errno[0] == EAGAIN) then
|
||||
return nil, "EAGAIN"
|
||||
else
|
||||
self:close()
|
||||
return nil, "closed"
|
||||
end
|
||||
end
|
||||
}
|
||||
-- __gc = function(self) self:close() end
|
||||
-- __close = function(self) p:close() end
|
||||
}
|
||||
|
||||
local function non_blocking_popen(cmd, read_buffer_size)
|
||||
local ffi = require("ffi")
|
||||
-- the buffer for reading from the process
|
||||
local read_buffer_size = tonumber(read_buffer_size) or 2048
|
||||
local read_buffer = ffi.new('uint8_t[?]',read_buffer_size)
|
||||
|
||||
-- C functions that we need
|
||||
ffi.cdef([[
|
||||
void* popen(const char* cmd, const char* mode);
|
||||
int pclose(void* stream);
|
||||
int fileno(void* stream);
|
||||
int fcntl(int fd, int cmd, int arg);
|
||||
int *__errno_location ();
|
||||
ssize_t read(int fd, void* buf, size_t count);
|
||||
]])
|
||||
|
||||
-- you can compile a simple C programm to find these values(Or look in the headers)
|
||||
local F_SETFL = 4
|
||||
local O_NONBLOCK = 2048
|
||||
local EAGAIN = 11
|
||||
|
||||
-- this "array" holds the errno variable
|
||||
local _errno = ffi.C.__errno_location()
|
||||
-- get a FILE* for our command
|
||||
local file = assert(ffi.C.popen(cmd, "r"))
|
||||
|
||||
-- the buffer for reading from the process
|
||||
local read_buffer_size = tonumber(read_buffer_size) or 2048
|
||||
local read_buffer = ffi.new('uint8_t[?]',read_buffer_size)
|
||||
-- turn the FILE* to a fd (int) for fcntl
|
||||
local fd = ffi.C.fileno(file)
|
||||
|
||||
-- get a FILE* for our command
|
||||
local file = assert(ffi.C.popen(cmd, "r"))
|
||||
|
||||
-- turn the FILE* to a fd(int) for fcntl
|
||||
local fd = ffi.C.fileno(file)
|
||||
|
||||
-- set non-blocking mode for read
|
||||
assert(ffi.C.fcntl(fd, F_SETFL, O_NONBLOCK)==0, "fcntl failed")
|
||||
-- set non-blocking mode for read
|
||||
assert(ffi.C.fcntl(fd, F_SETFL, O_NONBLOCK)==0, "fcntl failed")
|
||||
|
||||
-- close the process, prevent reading, allow garbage colletion
|
||||
function file_close(self)
|
||||
ffi.C.pclose(file)
|
||||
self.read_buffer = nil
|
||||
read_buffer = nil
|
||||
self.read = function() return nil, "closed"end
|
||||
end
|
||||
local p = {
|
||||
_fd = fd,
|
||||
_file = file,
|
||||
_read_buffer = read_buffer,
|
||||
_read_buffer_size = read_buffer_size,
|
||||
}
|
||||
|
||||
-- read up to size bytes from the process. Returns data(string) and number of bytes read if successfull,
|
||||
-- nil, "EAGAIN" if there is no data aviable, and
|
||||
-- nil, "closed" if the process has ended
|
||||
local read = ffi.C.read
|
||||
function file_read(self, size)
|
||||
local _size = math.min(read_buffer_size, size)
|
||||
while true do
|
||||
local nbytes = read(fd,read_buffer,_size)
|
||||
if nbytes > 0 then
|
||||
local data = ffi.string(read_buffer, nbytes)
|
||||
return data, nbytes
|
||||
elseif (nbytes == -1) and (_errno[0] == EAGAIN) then
|
||||
return nil, "EAGAIN"
|
||||
else
|
||||
file_close(self)
|
||||
return nil, "closed"
|
||||
end
|
||||
end
|
||||
end
|
||||
setmetatable(p, popen_meta)
|
||||
|
||||
return {
|
||||
_fd = fd,
|
||||
_file = file,
|
||||
_read_buffer = read_buffer,
|
||||
_read_buffer_size = read_buffer_size,
|
||||
read = file_read,
|
||||
close = file_close
|
||||
}
|
||||
return p
|
||||
end
|
||||
|
||||
return {
|
||||
non_blocking_popen = non_blocking_popen
|
||||
non_blocking_popen = non_blocking_popen
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user