]]
local luafile = {}
+local e2lib = require("e2lib")
+local err = require("err")
+local luafile_ll = require("luafile_ll")
local strict = require("strict")
-require("luafile_ll")
+
+--- Numeric constant for stdin.
+luafile.STDIN = 0;
+--- Numeric constant for stdout.
+luafile.STDOUT = 1;
+--- Numeric constant for sterr.
+luafile.STDERR = 2;
+
+--- Check whether a luafile object is valid and contains an open file.
+-- @param luafile File object.
+-- @return True on success, false on error.
+-- @return Error object on failure.
+local function valid_open_luafile(luafile)
+ local msg = "Internal luafile error: Please report this error:"
+
+ if type(luafile) ~= "table" then
+ return false, err.new("%s invalid object", msg)
+ end
+
+ if type(luafile.file) == "boolean" and not luafile.file then
+ return false,
+ err.new("%s no open file", msg)
+ end
+
+ if type(luafile.file) ~= "userdata" then
+ return false, err.new("%s invalid internal field structure")
+ end
+
+ return true
+end
--- Create new file object.
--- @return File object. This functions always succeeds.
+-- @return File object. This function always succeeds.
function luafile.new()
- local f = {}
- local meta = { __index = luafile }
- setmetatable(f, meta)
- return f
+ local luafile = {}
+ luafile.file = false
+ return strict.lock(luafile)
end
--- Open a file.
-- @param path Path to file (string).
-- @param mode Mode string of r, r+, w, w+, a or a+. See fopen(3) for details.
--- @return File object on success, nil on error.
-function luafile.open(path, mode)
- local f = luafile.new()
- f.file = luafile_ll.fopen(path, mode)
- if f.file then
- return f
- end
- return nil
+-- @return File object on success, false on error.
+-- @return Error object on failure.
+function luafile.fopen(path, mode)
+ local f, handle, errstring
+
+ handle, errstring = luafile_ll.fopen(path, mode)
+ if not handle then
+ return false, err.new("could not open file %q with mode %q: %s",
+ path, mode, errstring)
+ end
+
+ f = luafile.new()
+ f.file = handle
+ return f
end
--- Open a file descriptor.
-- @param fd Valid UNIX file descriptor (number).
-- @param mode Mode string of r, r+, w, w+, a or a+. See fdopen(3) for details.
--- @return File object on success, nil on error.
+-- @return File object on success, false on error.
+-- @return Error object on failure.
function luafile.fdopen(fd, mode)
- local f = luafile.new()
- f.file = luafile_ll.fdopen(fd, mode)
- if f.file then
- return f
+ local f, handle, errstring
+
+ handle, errstring = luafile_ll.fdopen(fd, mode)
+ if not handle then
+ return false,
+ err.new("could not open file descriptor %d with mode %q: %s",
+ fd, mode, errstring)
end
- return nil
+
+ f = luafile.new()
+ f.file = handle
+ return f
end
--- Close a file object.
-- @param luafile File object.
-- @return True on success, false on error.
-function luafile.close(luafile)
- if luafile and luafile.file then
- if luafile_ll.fclose(luafile.file) then
- luafile.file = nil
- return true
- end
+-- @return Error object on failure.
+function luafile.fclose(luafile)
+ local rc, re, errstring
+
+ rc, re = valid_open_luafile(luafile)
+ if not rc then
+ return false, re
+ end
+
+ rc, errstring = luafile_ll.fclose(luafile.file)
+ luafile.file = false
+ if not rc then
+ return false, err.new("error closing file: %s", errstring)
end
- return false
+
+ return true
end
--- Read a file.
-function luafile.read(luafile)
- if luafile and luafile.file then
- return luafile_ll.fread(luafile.file)
+-- @param luafile File object.
+-- @return File data as a string, or false on error. May be up to 16K bytes
+-- large and contain embedded zero's. On EOF an empty string is returned.
+-- @return Error object on failure.
+function luafile.fread(luafile)
+ local rc, re, errstring, buffer
+
+ rc, re = valid_open_luafile(luafile)
+ if not rc then
+ return false, re
end
- return nil
-end
---- Write buffer to a file.
-function luafile.write(luafile, buffer)
- if luafile and luafile.file and buffer then
- return luafile_ll.fwrite(luafile.file, buffer)
+ buffer, errstring = luafile_ll.fread(luafile.file)
+ if not buffer then
+ return false, err.new("error reading file: %s", errstring)
end
- return nil
+
+ return buffer
end
---- Read line from a file.
-function luafile.readline(luafile)
- if luafile and luafile.file then
- return luafile_ll.fgets(luafile.file)
+--- Read character from file.
+-- @param luafile File object.
+-- @return Character as a string, string of length 0 on EOF, or false on error.
+-- @return Error object on failure.
+function luafile.fgetc(luafile)
+ local rc, re, errstring, char
+
+ rc, re = valid_open_luafile(luafile)
+ if not rc then
+ return false, re
+ end
+
+ char, errstring = luafile_ll.fgetc(luafile.file)
+ if not char then
+ return false, err.new("error reading character from file: %s",
+ errstring)
end
- return nil
+
+
+ return char
end
---- Seek in a file.
-function luafile.seek(luafile, offset)
- if luafile and luafile.file and offset then
- return luafile_ll.fseek(luafile.file, offset)
+--- Write buffer to a file.
+-- @param luafile File object.
+-- @param buffer Data string to be written. May contain embedded zero's.
+-- @return True on success, False on error.
+-- @return Error object on failure.
+function luafile.fwrite(luafile, buffer)
+ local rc, re, errstring
+
+ rc, re = valid_open_luafile(luafile)
+ if not rc then
+ return false, rc
+ end
+
+ rc, errstring = luafile_ll.fwrite(luafile.file, buffer)
+ if not rc then
+ return false, err.new("error writing file: %s", errstring)
end
- return nil
+
+ return true
end
---- Flush file buffers.
-function luafile.flush(luafile)
- if luafile and luafile.file then
- return luafile_ll.fflush(luafile.file)
+--- Read line from a file.
+-- @param file File object.
+-- @return Line of data, potentially including a new-line character at the end
+-- but no further. Returns the empty string on end-of-file, or false in
+-- case of an error.
+-- @return Error object on failure.
+function luafile.readline(file)
+ local rc, re, line, char
+
+ --rc, re = valid_open_luafile(file)
+ --if not rc then
+ -- return false, rc
+ --end
+
+ line = ""
+ while true do
+ char, re = luafile.fgetc(file)
+ if not char then
+ return false, re
+ elseif char == "\0" then
+ -- fgets cannot handle embedded zeros, causing mayhem in C.
+ -- We could do this in Lua, but lets signal an error till
+ -- we have a use case.
+ return false, err.new("got NUL character while reading line")
+ elseif char == "\n" or char == "" then
+ line = line..char -- retain newline just like fgets does.
+ return line
+ end
+
+ line = line..char
end
- return nil
end
---- Return file descriptor of a file.
+--- Return file descriptor of a file object.
+-- @param luafile File object.
+-- @return Integer file descriptor of the file descriptor. This method does not
+-- have an error condition. If passed an invalid or closed file object, it calls
+-- e2lib.abort() signaling an internal error.
function luafile.fileno(luafile)
- if luafile and luafile.file then
- return luafile_ll.fileno(luafile.file)
+ local rc, re, fd, errstring
+
+ rc, re = valid_open_luafile(luafile)
+ if not rc then
+ e2lib.abort(re)
+ end
+
+ fd, errstring = luafile_ll.fileno(luafile.file)
+ if not fd then
+ e2lib.abort(err.new("%s", errstring))
end
- return nil
+
+ return fd
end
--- Test for end of file.
-function luafile.eof(luafile)
- if luafile and luafile.file then
- return luafile_ll.feof(luafile.file)
+-- @param luafile File object.
+-- @return True on end-of-file, false otherwise. feof calls
+-- e2lib.abort() when used with an invalid file object.
+function luafile.feof(luafile)
+ local rc, re
+
+ rc, re = valid_open_luafile(luafile)
+ if not rc then
+ e2lib.abort(re)
end
- return nil
+
+ rc, re = luafile_ll.feof(luafile.file)
+ if not rc and re then
+ e2lib.abort(err.new("%s", re))
+ end
+
+ return rc
end
---- Set buffer size used internally.
+--- Enable line buffer mode. See setbuf(3) for details. setlinebuf has no
+-- error conditions. If an invalid file object is passed, it calls
+-- e2lib.abort() terminating the process.
+-- @param luafile File object
function luafile.setlinebuf(luafile)
- if luafile and luafile.file then
- return luafile_ll.setlinebuf(luafile.file)
+ local errstring, rc, re
+
+ rc, re = valid_open_luafile(luafile)
+ if not rc then
+ e2lib.abort(re)
end
- return nil
-end
---- Create a pipe.
-function luafile.pipe()
- local rc, r, w = luafile_ll.pipe()
- local fr, fw
+ rc, errstring = luafile_ll.setlinebuf(luafile.file)
if not rc then
- return false, nil, nil
+ e2lib.abort(err.new("%s", errstring))
end
- fr = luafile.fdopen(r, "r")
- fw = luafile.fdopen(w, "w")
- return rc, fr, fw
end
---- Duplicate a file descriptor.
+--- Duplicate a file descriptor. See dup(2) for details.
+-- @param oldfd File descriptor to duplicate.
+-- @param newfd Duplicated file descritor. If the file descriptor was open
+-- before the call, it's closed automatically.
+-- @return True on success, false on error.
+-- @return Error object on failure.
function luafile.dup2(oldfd, newfd)
- if oldfd and newfd then
- return luafile_ll.dup2(oldfd, newfd)
+ local rc, errstring
+
+ rc, errstring = luafile_ll.dup2(oldfd, newfd)
+ if not rc then
+ return false,
+ err.new("duplicating file descriptor failed: %s", errstring)
end
- return nil
+
+ return true
+end
+
+
+--- Create a new UNIX pipe(2) between two file objects.
+-- @return File object in read mode, or false on error.
+-- @return File object in write mode, or error object on failure.
+function luafile.pipe()
+ local fd1, fd2, fr, fw, re
+
+ fd1, fd2 = luafile_ll.pipe()
+ if not fd1 then
+ return false, err.new("failed creating pipe: %s", fd2)
+ end
+
+ fr, re = luafile.fdopen(fd1, "r")
+ if not fr then
+ return false, re
+ end
+
+ fw,re = luafile.fdopen(fd2, "w")
+ if not fw then
+ return false, re
+ end
+
+ return fr, fw
end
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
+#include <errno.h>
#include <lua.h>
#include <lualib.h>
file = luaL_checkstring(lua, 1);
mode = luaL_checkstring(lua, 2);
+
f = fopen(file, mode);
if (f == NULL) {
- lua_pushnil(lua);
- } else {
- fd = fileno(f);
- if (fcntl(fd, F_SETFD, FD_CLOEXEC) != 0) {
- lua_pushfstring(lua, "%s: fcntl(%d): %s: %s", __func__,
- fd, file, strerror(errno));
- lua_error(lua);
- }
- lua_pushlightuserdata(lua, (void *)f);
+ lua_pushboolean(lua, 0);
+ lua_pushstring(lua, strerror(errno));
+ return 2;
+ }
+
+ fd = fileno(f);
+ if (fcntl(fd, F_SETFD, FD_CLOEXEC) != 0) {
+ lua_pushfstring(lua, "%s: fcntl(%d): %s: %s", __func__,
+ fd, file, strerror(errno));
+ lua_error(lua);
}
+
+ lua_pushlightuserdata(lua, f);
return 1;
}
lua_fclose(lua_State *lua)
{
FILE *f;
- int rc;
- f = (FILE *)lua_topointer(lua, 1);
- if(f) {
- rc = fclose(f);
- lua_pushboolean(lua, (rc == 0));
- } else {
+
+ f = lua_touserdata(lua, 1);
+ if (f == NULL) {
+ lua_pushboolean(lua, 0);
+ lua_pushstring(lua,
+ "lua_fclose: one or more arguments of wrong type/missing");
+ return 2;
+ }
+
+ if (fclose(f) == EOF) {
lua_pushboolean(lua, 0);
+ lua_pushstring(lua, strerror(errno));
+ return 2;
}
+
+ lua_pushboolean(lua, 1);
return 1;
}
FILE *f;
int fd;
const char *mode;
+
fd = luaL_checkinteger(lua, 1);
mode = luaL_checkstring(lua, 2);
+
f = fdopen(fd, mode);
- if(f == NULL) {
- lua_pushnil(lua);
- } else {
- lua_pushlightuserdata(lua, (void *)f);
+ if (f == NULL) {
+ lua_pushboolean(lua, 0);
+ lua_pushstring(lua, strerror(errno));
+ return 2;
}
+
+ lua_pushlightuserdata(lua, f);
return 1;
}
{
FILE *f;
const char *b;
- int n = 0, rc;
- f = (FILE *)lua_topointer(lua, 1);
- b = luaL_checkstring(lua, 2);
- if(!f || !b) {
+ size_t sz, ret;
+
+ f = lua_touserdata(lua, 1);
+ b = lua_tolstring(lua, 2, &sz);
+ if (f == NULL || b == NULL) {
lua_pushboolean(lua, 0);
- return 1;
+ lua_pushstring(lua,
+ "lua_fwrite: one or more arguments of wrong type/missing");
+ return 2;
+ }
+
+ ret = fwrite(b, 1, sz, f);
+ if (ret != sz) {
+ if (ferror(f)) {
+ lua_pushboolean(lua, 0);
+ lua_pushstring(lua, strerror(errno));
+ return 2;
+ }
+
+ if (feof(f)) {
+ /* What does end of file on write mean?
+ * Signal an error */
+ lua_pushboolean(lua, 0);
+ lua_pushstring(lua, "lua_fwrite: end of file");
+ return 2;
+ }
}
- n = strlen(b);
- rc = fwrite(b, 1, n, f);
- lua_pushboolean(lua, (rc == n));
+
+ lua_pushboolean(lua, 1);
return 1;
}
lua_fread(lua_State *lua)
{
char buf[16384];
- int rc;
+ size_t ret;
FILE *f;
- f = (FILE *)lua_topointer(lua, 1);
- rc = fread(buf, 1, sizeof(buf), f);
- if(rc>0) {
- lua_pushlstring(lua, buf, rc);
- } else if (rc == 0) {
- lua_pushstring(lua, "");
- } else {
- lua_pushnil(lua);
- }
- return 1;
-}
-static int
-lua_fgets(lua_State *lua)
-{
- FILE *f;
- char buf[16384], *rc;
- f = (FILE *)lua_topointer(lua, 1);
- if(!f) {
- lua_pushnil(lua);
- return 1;
+ f = lua_touserdata(lua, 1);
+ if (f == NULL) {
+ lua_pushboolean(lua, 0);
+ lua_pushstring(lua,
+ "lua_fread: one or more arguments of wrong type/missing");
+ return 2;
}
- rc = fgets(buf, sizeof(buf), f);
- if(!rc) {
- lua_pushnil(lua);
- return 1;
+
+
+ ret = fread(buf, 1, sizeof(buf), f);
+ if (ret != sizeof(buf)) {
+ if (ferror(f)) {
+ lua_pushboolean(lua, 0);
+ lua_pushstring(lua, strerror(errno));
+ return 2;
+ }
+
+ if (ret <= 0 && feof(f)) {
+ /* ret <= 0: do not discard data on short reads,
+ * only signal EOF when all data is returned. */
+ lua_pushstring(lua, "");
+ return 1;
+ }
}
- lua_pushstring(lua, buf);
+
+ lua_pushlstring(lua, buf, ret);
return 1;
}
static int
-lua_fseek(lua_State *lua)
+lua_fgetc(lua_State *L)
{
- int rc;
- long offset;
FILE *f;
- f = (FILE *)lua_topointer(lua, 1);
- offset = luaL_checklong(lua, 2);
- if(!f) {
- lua_pushboolean(lua, 0);
- return 1;
+ int c;
+ char ch;
+
+ f = lua_touserdata(L, 1);
+ if (f == NULL) {
+ lua_pushboolean(L, 0);
+ lua_pushstring(L,
+ "lua_fgetc: argument of wrong type or missing");
+ return 2;
}
- rc = fseek(f, offset, SEEK_SET);
- lua_pushboolean(lua, rc == 0);
- return 1;
-}
-static int
-lua_fflush(lua_State *lua)
-{
- int rc;
- FILE *f;
- f = (FILE *)lua_topointer(lua, 1);
- if(!f) {
- lua_pushnil(lua);
- return 1;
+ c = fgetc(f);
+ if (c == EOF) {
+ if (feof(f)) {
+ lua_pushstring(L, "");
+ return 1;
+ }
+
+ if (ferror(f)) {
+ lua_pushboolean(L, 0);
+ lua_pushstring(L, strerror(errno));
+ return 2;
+ }
+
}
- rc = fflush(f);
- lua_pushboolean(lua, rc == 0);
+
+ ch = (char)c;
+ lua_pushlstring(L, &ch, 1);
return 1;
}
static int
-lua_pipe(lua_State *lua)
+lua_pipe(lua_State *L)
{
int fd[2];
int rc;
+
rc = pipe(fd);
- lua_pushboolean(lua, rc == 0);
- lua_pushnumber(lua, fd[0]);
- lua_pushnumber(lua, fd[1]);
- return 3;
+ if (rc != 0) {
+ lua_pushboolean(L, 0);
+ lua_pushstring(L, strerror(errno));
+ return 2;
+ }
+
+ lua_pushnumber(L, fd[0]);
+ lua_pushnumber(L, fd[1]);
+ return 2;
}
static int
{
FILE *f;
int fd;
- f = (FILE *)lua_topointer(lua, 1);
- if(!f) {
- lua_pushnil(lua);
- return 1;
+
+ f = lua_touserdata(lua, 1);
+ if (f == NULL) {
+ lua_pushboolean(lua, 0);
+ lua_pushstring(lua,
+ "lua_fileno: one or more arguments of wrong type/missing");
+ return 2;
}
fd = fileno(f);
lua_pushinteger(lua, fd);
}
static int
-lua_eof(lua_State *lua)
+lua_feof(lua_State *lua)
{
FILE *f;
- int eof;
- f = (FILE *)lua_topointer(lua, 1);
- if(!f) {
- lua_pushnil(lua);
- return 1;
+
+ f = lua_touserdata(lua, 1);
+ if (f == NULL) {
+ lua_pushboolean(lua, 0);
+ lua_pushstring(lua,
+ "lua_feof: arguments wrong type or missing");
+ return 2;
}
- eof = feof(f);
- lua_pushboolean(lua, eof);
+
+ lua_pushboolean(lua, feof(f));
return 1;
}
lua_setlinebuf(lua_State *lua)
{
FILE *f;
- f = (FILE *)lua_topointer(lua, 1);
- if(!f) {
+
+ f = lua_touserdata(lua, 1);
+ if (!f) {
lua_pushboolean(lua, 0);
- return 1;
+ lua_pushstring(lua, "lua_setlinebuf: one or more arguments "
+ "of wrong type/missing");
+ return 2;
}
+
setlinebuf(f);
lua_pushboolean(lua, 1);
return 1;
lua_dup2(lua_State *lua)
{
int oldfd, newfd, rc;
+
oldfd = luaL_checkinteger(lua, 1);
newfd = luaL_checkinteger(lua, 2);
+
rc = dup2(oldfd, newfd);
- lua_pushboolean(lua, (rc == 0));
+ if (rc < 0) {
+ lua_pushboolean(lua, 0);
+ lua_pushstring(lua, strerror(errno));
+ return 2;
+ }
+
+ lua_pushboolean(lua, 1);
return 1;
}
{ "fclose", lua_fclose },
{ "fwrite", lua_fwrite },
{ "fread", lua_fread },
- { "fseek", lua_fseek },
- { "fflush", lua_fflush },
- { "fileno", lua_fileno },
- { "feof", lua_eof },
- { "fgets", lua_fgets },
- { "setlinebuf", lua_setlinebuf },
+ { "fgetc", lua_fgetc },
{ "pipe", lua_pipe },
+ { "setlinebuf", lua_setlinebuf },
+ { "feof", lua_feof },
+ { "fileno", lua_fileno },
{ "dup2", lua_dup2 },
{ "cloexec", lua_cloexec },
{ NULL, NULL }
};
-int luaopen_luafile_ll(lua_State *lua)
+int
+luaopen_luafile_ll(lua_State *lua)
{
- luaL_register(lua, "luafile_ll", lib);
- return 1;
+ luaL_Reg *next;
+
+ lua_newtable(lua);
+ for (next = lib; next->name != NULL; next++) {
+ lua_pushcfunction(lua, next->func);
+ lua_setfield(lua, -2, next->name);
+ }
+
+ return 1;
}