浏览代码

io.write returns number of written bytes on error

Roberto Ierusalimschy 3 月之前
父节点
当前提交
3f4f28010a
共有 4 个文件被更改,包括 65 次插入7 次删除
  1. 11 7
      liolib.c
  2. 20 0
      ltests.c
  3. 3 0
      manual/manual.of
  4. 31 0
      testes/files.lua

+ 11 - 7
liolib.c

@@ -662,11 +662,12 @@ static int io_readline (lua_State *L) {
 
 static int g_write (lua_State *L, FILE *f, int arg) {
   int nargs = lua_gettop(L) - arg;
-  int status = 1;
+  size_t totalbytes = 0;  /* total number of bytes written */
   errno = 0;
-  for (; nargs--; arg++) {
+  for (; nargs--; arg++) {  /* for each argument */
     char buff[LUA_N2SBUFFSZ];
     const char *s;
+    size_t numbytes;  /* bytes written in one call to 'fwrite' */
     size_t len = lua_numbertocstring(L, arg, buff);  /* try as a number */
     if (len > 0) {  /* did conversion work (value was a number)? */
       s = buff;
@@ -674,12 +675,15 @@ static int g_write (lua_State *L, FILE *f, int arg) {
     }
     else  /* must be a string */
       s = luaL_checklstring(L, arg, &len);
-    status = status && (fwrite(s, sizeof(char), len, f) == len);
+    numbytes = fwrite(s, sizeof(char), len, f);
+    totalbytes += numbytes;
+    if (numbytes < len) {  /* write error? */
+      int n = luaL_fileresult(L, 0, NULL);
+      lua_pushinteger(L, cast_st2S(totalbytes));
+      return n + 1;  /* return fail, error msg., error code, and counter */
+    }
   }
-  if (l_likely(status))
-    return 1;  /* file handle already on stack top */
-  else
-    return luaL_fileresult(L, status, NULL);
+  return 1;  /* no errors; file handle already on stack top */
 }
 
 

+ 20 - 0
ltests.c

@@ -2106,6 +2106,25 @@ static int coresume (lua_State *L) {
   }
 }
 
+#if !defined(LUA_USE_POSIX)
+
+#define nonblock	NULL
+
+#else
+
+#include <unistd.h>
+#include <fcntl.h>
+
+static int nonblock (lua_State *L) {
+  FILE *f = cast(luaL_Stream*, luaL_checkudata(L, 1, LUA_FILEHANDLE))->f;
+  int fd = fileno(f);
+  int flags = fcntl(fd, F_GETFL, 0);
+  flags |= O_NONBLOCK;
+  fcntl(fd, F_SETFL, flags);
+  return 0;
+}
+#endif
+
 /* }====================================================== */
 
 
@@ -2159,6 +2178,7 @@ static const struct luaL_Reg tests_funcs[] = {
   {"upvalue", upvalue},
   {"externKstr", externKstr},
   {"externstr", externstr},
+  {"nonblock", nonblock},
   {NULL, NULL}
 };
 

+ 3 - 0
manual/manual.of

@@ -8699,6 +8699,9 @@ Writes the value of each of its arguments to @id{file}.
 The arguments must be strings or numbers.
 
 In case of success, this function returns @id{file}.
+Otherwise, it returns four values:
+@fail, the error message, the error code,
+and the number of bytes it was able to write.
 
 }
 

+ 31 - 0
testes/files.lua

@@ -696,6 +696,37 @@ do
 end
 
 
+if T and T.nonblock then
+  print("testing failed write")
+
+  -- unable to write anything to /dev/full
+  local f = io.open("/dev/full", "w")
+  assert(f:setvbuf("no"))
+  local _, _, err, count = f:write("abcd")
+  assert(err > 0 and count == 0)
+  assert(f:close())
+
+  -- receiver will read a "few" bytes (enough to empty a large buffer)
+  local receiver = [[
+    lua -e 'assert(io.stdin:setvbuf("no")); assert(#io.read(1e4) == 1e4)' ]]
+
+  local f = io.popen(receiver, "w")
+  assert(f:setvbuf("no"))
+  T.nonblock(f)
+
+  -- able to write a few bytes
+  assert(f:write(string.rep("a", 1e2)))
+
+  -- Unable to write more bytes than the pipe buffer supports.
+  -- (In Linux, the pipe buffer size is 64K (2^16). Posix requires at
+  -- least 512 bytes.)
+  local _, _, err, count = f:write("abcd", string.rep("a", 2^17))
+  assert(err > 0 and count >= 512 and count < 2^17)
+
+  assert(f:close())
+end
+
+
 if not _soft then
   print("testing large files (> BUFSIZ)")
   io.output(file)