]> git.e2factory.org Git - e2factory.git/commitdiff
Rewrite luafile
authorTobias Ulmer <tu@emlix.com>
Wed, 23 Oct 2013 15:50:11 +0000 (17:50 +0200)
committerTobias Ulmer <tu@emlix.com>
Wed, 16 Nov 2016 14:01:23 +0000 (15:01 +0100)
Turn into a "modern" module like the rest, document, add error
handling and remove all methods not in use by current code.
Remove object-style access like f:close() to increase grepability
of the code. Rename methods to their C equivalents.

Signed-off-by: Tobias Ulmer <tu@emlix.com>
generic/luafile.lua
generic/luafile_ll.c

index 2038138d78a465377ce6e00436920f428aec86a1..53d98141fc308791c4e882fbdb341c4cd5453da0 100644 (file)
 ]]
 
 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
 
 
index b3c6994771434b1bf75a84d596611b0169508e9c..a0a96304d7c121714d81db3359acff891e481aca 100644 (file)
@@ -35,6 +35,7 @@
 #include <stdlib.h>
 #include <string.h>
 #include <unistd.h>
+#include <errno.h>
 
 #include <lua.h>
 #include <lualib.h>
@@ -49,18 +50,22 @@ lua_fopen(lua_State *lua)
 
        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;
 }
 
@@ -68,14 +73,22 @@ static int
 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;
 }
 
@@ -85,14 +98,18 @@ lua_fdopen(lua_State *lua)
        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;
 }
 
@@ -101,16 +118,35 @@ lua_fwrite(lua_State *lua)
 {
        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;
 }
 
@@ -118,81 +154,89 @@ static int
 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
@@ -200,10 +244,13 @@ lua_fileno(lua_State *lua)
 {
        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);
@@ -211,17 +258,19 @@ lua_fileno(lua_State *lua)
 }
 
 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;
 }
 
@@ -229,11 +278,15 @@ static int
 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;
@@ -243,10 +296,18 @@ static int
 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;
 }
 
@@ -300,21 +361,27 @@ static luaL_Reg lib[] = {
   { "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;
 }