Browse Source

change in the representation of file handles

Roberto Ierusalimschy 14 years ago
parent
commit
801f43f09a
1 changed files with 128 additions and 139 deletions
  1. 128 139
      liolib.c

+ 128 - 139
liolib.c

@@ -1,5 +1,5 @@
 /*
 /*
-** $Id: liolib.c,v 2.98 2011/02/21 19:12:54 roberto Exp roberto $
+** $Id: liolib.c,v 2.99 2011/03/03 16:34:46 roberto Exp roberto $
 ** Standard I/O (and system) library
 ** Standard I/O (and system) library
 ** See Copyright Notice in lua.h
 ** See Copyright Notice in lua.h
 */
 */
@@ -19,8 +19,6 @@
 #include "lualib.h"
 #include "lualib.h"
 
 
 
 
-#define MAX_SIZE_T	(~(size_t)0)
-
 
 
 /*
 /*
 ** lua_popen spawns a new process connected to the current one through
 ** lua_popen spawns a new process connected to the current one through
@@ -51,11 +49,15 @@
 #endif			/* } */
 #endif			/* } */
 
 
 
 
-#define IO_INPUT	1
-#define IO_OUTPUT	2
+#define IO_PREFIX	"lua_io_"
+#define IO_INPUT	(IO_PREFIX "input")
+#define IO_OUTPUT	(IO_PREFIX "output")
 
 
 
 
-static const char *const fnames[] = {"input", "output"};
+typedef struct LStream {
+  FILE *f;  /* stream */
+  lua_CFunction closef;  /* to close stream (NULL for closed streams) */
+} LStream;
 
 
 
 
 static void fileerror (lua_State *L, int arg, const char *filename) {
 static void fileerror (lua_State *L, int arg, const char *filename) {
@@ -64,16 +66,18 @@ static void fileerror (lua_State *L, int arg, const char *filename) {
 }
 }
 
 
 
 
-#define tofilep(L)	((FILE **)luaL_checkudata(L, 1, LUA_FILEHANDLE))
+#define tolstream(L)	((LStream *)luaL_checkudata(L, 1, LUA_FILEHANDLE))
+
+#define isclosed(p)	((p)->closef == NULL)
 
 
 
 
 static int io_type (lua_State *L) {
 static int io_type (lua_State *L) {
-  void *ud;
+  LStream *p;
   luaL_checkany(L, 1);
   luaL_checkany(L, 1);
-  ud = luaL_testudata(L, 1, LUA_FILEHANDLE);
-  if (ud == NULL)
+  p = (LStream *)luaL_testudata(L, 1, LUA_FILEHANDLE);
+  if (p == NULL)
     lua_pushnil(L);  /* not a file */
     lua_pushnil(L);  /* not a file */
-  else if (*((FILE **)ud) == NULL)
+  else if (isclosed(p))
     lua_pushliteral(L, "closed file");
     lua_pushliteral(L, "closed file");
   else
   else
     lua_pushliteral(L, "file");
     lua_pushliteral(L, "file");
@@ -81,11 +85,22 @@ static int io_type (lua_State *L) {
 }
 }
 
 
 
 
+static int f_tostring (lua_State *L) {
+  LStream *p = tolstream(L);
+  if (isclosed(p))
+    lua_pushliteral(L, "file (closed)");
+  else
+    lua_pushfstring(L, "file (%p)", p->f);
+  return 1;
+}
+
+
 static FILE *tofile (lua_State *L) {
 static FILE *tofile (lua_State *L) {
-  FILE **f = tofilep(L);
-  if (*f == NULL)
+  LStream *p = tolstream(L);
+  if (isclosed(p))
     luaL_error(L, "attempt to use a closed file");
     luaL_error(L, "attempt to use a closed file");
-  return *f;
+  lua_assert(p->f);
+  return p->f;
 }
 }
 
 
 
 
@@ -94,40 +109,35 @@ static FILE *tofile (lua_State *L) {
 ** before opening the actual file; so, if there is a memory error, the
 ** before opening the actual file; so, if there is a memory error, the
 ** file is not left opened.
 ** file is not left opened.
 */
 */
-static FILE **newprefile (lua_State *L) {
-  FILE **pf = (FILE **)lua_newuserdata(L, sizeof(FILE *));
-  *pf = NULL;  /* file handle is currently `closed' */
+static LStream *newprefile (lua_State *L) {
+  LStream *p = (LStream *)lua_newuserdata(L, sizeof(LStream));
+  p->closef = NULL;  /* mark file handle as 'closed' */
   luaL_setmetatable(L, LUA_FILEHANDLE);
   luaL_setmetatable(L, LUA_FILEHANDLE);
-  return pf;
+  return p;
 }
 }
 
 
 
 
-static FILE **newfile (lua_State *L) {
-  FILE **pf = newprefile(L);
-  lua_pushvalue(L, lua_upvalueindex(1));  /* set upvalue... */
-  lua_setuservalue(L, -2);  /* ... as environment for new file */
-  return pf;
+static int aux_close (lua_State *L) {
+  LStream *p = tolstream(L);
+  lua_CFunction cf = p->closef;
+  p->closef = NULL;  /* mark stream as closed */
+  return (*cf)(L);  /* close it */
 }
 }
 
 
 
 
-/*
-** function to (not) close the standard files stdin, stdout, and stderr
-*/
-static int io_noclose (lua_State *L) {
-  lua_pushnil(L);
-  lua_pushliteral(L, "cannot close standard file");
-  return 2;
+static int io_close (lua_State *L) {
+  if (lua_isnone(L, 1))  /* no argument? */
+    lua_getfield(L, LUA_REGISTRYINDEX, IO_OUTPUT);  /* use standard output */
+  tofile(L);  /* make sure argument is an open stream */
+  return aux_close(L);
 }
 }
 
 
 
 
-/*
-** function to close 'popen' files
-*/
-static int io_pclose (lua_State *L) {
-  FILE **p = tofilep(L);
-  int stat = lua_pclose(L, *p);
-  *p = NULL;  /* mark stream as closed (for GC) */
-  return luaL_execresult(L, stat);
+static int f_gc (lua_State *L) {
+  LStream *p = tolstream(L);
+  if (!isclosed(p) && p->f != NULL)
+    aux_close(L);  /* ignore closed and incompletely open files */
+  return 0;
 }
 }
 
 
 
 
@@ -135,111 +145,90 @@ static int io_pclose (lua_State *L) {
 ** function to close regular files
 ** function to close regular files
 */
 */
 static int io_fclose (lua_State *L) {
 static int io_fclose (lua_State *L) {
-  FILE **p = tofilep(L);
-  int ok = (fclose(*p) == 0);
-  *p = NULL;  /* mark stream as closed (for GC) */
-  return luaL_fileresult(L, ok, NULL);
+  LStream *p = tolstream(L);
+  int res = fclose(p->f);
+  return luaL_fileresult(L, (res == 0), NULL);
 }
 }
 
 
 
 
-static int aux_close (lua_State *L) {
-  lua_getuservalue(L, 1);
-  lua_getfield(L, -1, "__close");
-  return (lua_tocfunction(L, -1))(L);
-}
-
-
-static int io_close (lua_State *L) {
-  if (lua_isnone(L, 1))
-    lua_rawgeti(L, lua_upvalueindex(1), IO_OUTPUT);
-  tofile(L);  /* make sure argument is a file */
-  return aux_close(L);
-}
-
-
-static int io_gc (lua_State *L) {
-  FILE *f = *tofilep(L);
-  if (f != NULL)  /* ignore closed files */
-    aux_close(L);
-  return 0;
-}
-
-
-static int io_tostring (lua_State *L) {
-  FILE *f = *tofilep(L);
-  if (f == NULL)
-    lua_pushliteral(L, "file (closed)");
-  else
-    lua_pushfstring(L, "file (%p)", f);
-  return 1;
+static LStream *newfile (lua_State *L) {
+  LStream *p = newprefile(L);
+  p->f = NULL;
+  p->closef = &io_fclose;
+  return p;
 }
 }
 
 
 
 
 static int io_open (lua_State *L) {
 static int io_open (lua_State *L) {
   const char *filename = luaL_checkstring(L, 1);
   const char *filename = luaL_checkstring(L, 1);
   const char *mode = luaL_optstring(L, 2, "r");
   const char *mode = luaL_optstring(L, 2, "r");
-  FILE **pf;
+  LStream *p = newfile(L);
   int i = 0;
   int i = 0;
   /* check whether 'mode' matches '[rwa]%+?b?' */
   /* check whether 'mode' matches '[rwa]%+?b?' */
   if (!(mode[i] != '\0' && strchr("rwa", mode[i++]) != NULL &&
   if (!(mode[i] != '\0' && strchr("rwa", mode[i++]) != NULL &&
        (mode[i] != '+' || ++i) &&  /* skip if char is '+' */
        (mode[i] != '+' || ++i) &&  /* skip if char is '+' */
        (mode[i] != 'b' || ++i) &&  /* skip if char is 'b' */
        (mode[i] != 'b' || ++i) &&  /* skip if char is 'b' */
        (mode[i] == '\0')))
        (mode[i] == '\0')))
-    luaL_error(L, "invalid mode " LUA_QL("%s")
-                  " (should match " LUA_QL("[rwa]%%+?b?") ")", mode);
-  pf = newfile(L);
-  *pf = fopen(filename, mode);
-  return (*pf == NULL) ? luaL_fileresult(L, 0, filename) : 1;
+    return luaL_error(L, "invalid mode " LUA_QL("%s")
+                         " (should match " LUA_QL("[rwa]%%+?b?") ")", mode);
+  p->f = fopen(filename, mode);
+  return (p->f == NULL) ? luaL_fileresult(L, 0, filename) : 1;
 }
 }
 
 
 
 
 /*
 /*
-** this function has a separated environment, which defines the
-** correct __close for 'popen' files
+** function to close 'popen' files
 */
 */
+static int io_pclose (lua_State *L) {
+  LStream *p = tolstream(L);
+  return luaL_execresult(L, lua_pclose(L, p->f));
+}
+
+
 static int io_popen (lua_State *L) {
 static int io_popen (lua_State *L) {
   const char *filename = luaL_checkstring(L, 1);
   const char *filename = luaL_checkstring(L, 1);
   const char *mode = luaL_optstring(L, 2, "r");
   const char *mode = luaL_optstring(L, 2, "r");
-  FILE **pf = newfile(L);
-  *pf = lua_popen(L, filename, mode);
-  return (*pf == NULL) ? luaL_fileresult(L, 0, filename) : 1;
+  LStream *p = newprefile(L);
+  p->f = lua_popen(L, filename, mode);
+  p->closef = &io_pclose;
+  return (p->f == NULL) ? luaL_fileresult(L, 0, filename) : 1;
 }
 }
 
 
 
 
 static int io_tmpfile (lua_State *L) {
 static int io_tmpfile (lua_State *L) {
-  FILE **pf = newfile(L);
-  *pf = tmpfile();
-  return (*pf == NULL) ? luaL_fileresult(L, 0, NULL) : 1;
+  LStream *p = newfile(L);
+  p->f = tmpfile();
+  return (p->f == NULL) ? luaL_fileresult(L, 0, NULL) : 1;
 }
 }
 
 
 
 
-static FILE *getiofile (lua_State *L, int findex) {
-  FILE *f;
-  lua_rawgeti(L, lua_upvalueindex(1), findex);
-  f = *(FILE **)lua_touserdata(L, -1);
-  if (f == NULL)
-    luaL_error(L, "standard %s file is closed", fnames[findex - 1]);
-  return f;
+static FILE *getiofile (lua_State *L, const char *findex) {
+  LStream *p;
+  lua_getfield(L, LUA_REGISTRYINDEX, findex);
+  p = (LStream *)lua_touserdata(L, -1);
+  if (isclosed(p))
+    luaL_error(L, "standard %s file is closed", findex + strlen(IO_PREFIX));
+  return p->f;
 }
 }
 
 
 
 
-static int g_iofile (lua_State *L, int f, const char *mode) {
+static int g_iofile (lua_State *L, const char *f, const char *mode) {
   if (!lua_isnoneornil(L, 1)) {
   if (!lua_isnoneornil(L, 1)) {
     const char *filename = lua_tostring(L, 1);
     const char *filename = lua_tostring(L, 1);
     if (filename) {
     if (filename) {
-      FILE **pf = newfile(L);
-      *pf = fopen(filename, mode);
-      if (*pf == NULL)
+      LStream *p = newfile(L);
+      p->f = fopen(filename, mode);
+      if (p->f == NULL)
         fileerror(L, 1, filename);
         fileerror(L, 1, filename);
     }
     }
     else {
     else {
       tofile(L);  /* check that it's a valid file handle */
       tofile(L);  /* check that it's a valid file handle */
       lua_pushvalue(L, 1);
       lua_pushvalue(L, 1);
     }
     }
-    lua_rawseti(L, lua_upvalueindex(1), f);
+    lua_setfield(L, LUA_REGISTRYINDEX, f);
   }
   }
   /* return current value */
   /* return current value */
-  lua_rawgeti(L, lua_upvalueindex(1), f);
+  lua_getfield(L, LUA_REGISTRYINDEX, f);
   return 1;
   return 1;
 }
 }
 
 
@@ -281,16 +270,16 @@ static int io_lines (lua_State *L) {
   int toclose;
   int toclose;
   if (lua_isnone(L, 1)) lua_pushnil(L);  /* at least one argument */
   if (lua_isnone(L, 1)) lua_pushnil(L);  /* at least one argument */
   if (lua_isnil(L, 1)) {  /* no file name? */
   if (lua_isnil(L, 1)) {  /* no file name? */
-    lua_rawgeti(L, lua_upvalueindex(1), IO_INPUT);  /* get default input */
+    lua_getfield(L, LUA_REGISTRYINDEX, IO_INPUT);  /* get default input */
     lua_replace(L, 1);  /* put it at index 1 */
     lua_replace(L, 1);  /* put it at index 1 */
     tofile(L);  /* check that it's a valid file handle */
     tofile(L);  /* check that it's a valid file handle */
     toclose = 0;  /* do not close it after iteration */
     toclose = 0;  /* do not close it after iteration */
   }
   }
   else {  /* open a new file */
   else {  /* open a new file */
     const char *filename = luaL_checkstring(L, 1);
     const char *filename = luaL_checkstring(L, 1);
-    FILE **pf = newfile(L);
-    *pf = fopen(filename, "r");
-    if (*pf == NULL)
+    LStream *p = newfile(L);
+    p->f = fopen(filename, "r");
+    if (p->f == NULL)
       fileerror(L, 1, filename);
       fileerror(L, 1, filename);
     lua_replace(L, 1);  /* put file at index 1 */
     lua_replace(L, 1);  /* put file at index 1 */
     toclose = 1;  /* close it after iteration */
     toclose = 1;  /* close it after iteration */
@@ -350,6 +339,8 @@ static int read_line (lua_State *L, FILE *f, int chop) {
 }
 }
 
 
 
 
+#define MAX_SIZE_T	(~(size_t)0)
+
 static void read_all (lua_State *L, FILE *f) {
 static void read_all (lua_State *L, FILE *f) {
   size_t rlen = LUAL_BUFFERSIZE;  /* how much to read in each cycle */
   size_t rlen = LUAL_BUFFERSIZE;  /* how much to read in each cycle */
   luaL_Buffer b;
   luaL_Buffer b;
@@ -440,15 +431,15 @@ static int f_read (lua_State *L) {
 
 
 
 
 static int io_readline (lua_State *L) {
 static int io_readline (lua_State *L) {
-  FILE *f = *(FILE **)lua_touserdata(L, lua_upvalueindex(1));
+  LStream *p = (LStream *)lua_touserdata(L, lua_upvalueindex(1));
   int i;
   int i;
   int n = (int)lua_tointeger(L, lua_upvalueindex(2));
   int n = (int)lua_tointeger(L, lua_upvalueindex(2));
-  if (f == NULL)  /* file is already closed? */
-    luaL_error(L, "file is already closed");
+  if (isclosed(p))  /* file is already closed? */
+    return luaL_error(L, "file is already closed");
   lua_settop(L , 1);
   lua_settop(L , 1);
   for (i = 1; i <= n; i++)  /* push arguments to 'g_read' */
   for (i = 1; i <= n; i++)  /* push arguments to 'g_read' */
     lua_pushvalue(L, lua_upvalueindex(3 + i));
     lua_pushvalue(L, lua_upvalueindex(3 + i));
-  n = g_read(L, f, 2);  /* 'n' is number of results */
+  n = g_read(L, p->f, 2);  /* 'n' is number of results */
   lua_assert(n > 0);  /* should return at least a nil */
   lua_assert(n > 0);  /* should return at least a nil */
   if (!lua_isnil(L, -n))  /* read at least one value? */
   if (!lua_isnil(L, -n))  /* read at least one value? */
     return n;  /* return them */
     return n;  /* return them */
@@ -494,7 +485,7 @@ static int io_write (lua_State *L) {
 
 
 
 
 static int f_write (lua_State *L) {
 static int f_write (lua_State *L) {
-  FILE * f = tofile(L);
+  FILE *f = tofile(L);
   lua_pushvalue(L, 1);  /* push file at the stack top (to be returned) */
   lua_pushvalue(L, 1);  /* push file at the stack top (to be returned) */
   return g_write(L, f, 2);
   return g_write(L, f, 2);
 }
 }
@@ -538,6 +529,9 @@ static int f_flush (lua_State *L) {
 }
 }
 
 
 
 
+/*
+** functions for 'io' library
+*/
 static const luaL_Reg iolib[] = {
 static const luaL_Reg iolib[] = {
   {"close", io_close},
   {"close", io_close},
   {"flush", io_flush},
   {"flush", io_flush},
@@ -554,6 +548,9 @@ static const luaL_Reg iolib[] = {
 };
 };
 
 
 
 
+/*
+** methods for file handles
+*/
 static const luaL_Reg flib[] = {
 static const luaL_Reg flib[] = {
   {"close", io_close},
   {"close", io_close},
   {"flush", f_flush},
   {"flush", f_flush},
@@ -562,8 +559,8 @@ static const luaL_Reg flib[] = {
   {"seek", f_seek},
   {"seek", f_seek},
   {"setvbuf", f_setvbuf},
   {"setvbuf", f_setvbuf},
   {"write", f_write},
   {"write", f_write},
-  {"__gc", io_gc},
-  {"__tostring", io_tostring},
+  {"__gc", f_gc},
+  {"__tostring", f_tostring},
   {NULL, NULL}
   {NULL, NULL}
 };
 };
 
 
@@ -577,46 +574,38 @@ static void createmeta (lua_State *L) {
 }
 }
 
 
 
 
-static void createstdfile (lua_State *L, FILE *f, int k, const char *fname) {
-  *newprefile(L) = f;
-  if (k > 0) {
-    lua_pushvalue(L, -1);  /* copy new file */
-    lua_rawseti(L, 1, k);  /* add it to common upvalue */
-  }
-  lua_pushvalue(L, 3);  /* get environment for default files */
-  lua_setuservalue(L, -2);  /* set it as environment for file */
-  lua_setfield(L, 2, fname);  /* add file to module */
+/*
+** function to (not) close the standard files stdin, stdout, and stderr
+*/
+static int io_noclose (lua_State *L) {
+  LStream *p = tolstream(L);
+  p->closef = &io_noclose;  /* keep file opened */
+  lua_pushnil(L);
+  lua_pushliteral(L, "cannot close standard file");
+  return 2;
 }
 }
 
 
 
 
-/*
-** pushes a new table with {__close = cls}
-*/
-static void newenv (lua_State *L, lua_CFunction cls) {
-  lua_createtable(L, 0, 1);
-  lua_pushcfunction(L, cls);
-  lua_setfield(L, -2, "__close");
+static void createstdfile (lua_State *L, FILE *f, const char *k,
+                           const char *fname) {
+  LStream *p = newprefile(L);
+  p->f = f;
+  p->closef = &io_noclose;
+  if (k != NULL) {
+    lua_pushvalue(L, -1);
+    lua_setfield(L, LUA_REGISTRYINDEX, k);  /* add file to registry */
+  }
+  lua_setfield(L, -2, fname);  /* add file to module */
 }
 }
 
 
 
 
 LUAMOD_API int luaopen_io (lua_State *L) {
 LUAMOD_API int luaopen_io (lua_State *L) {
-  lua_settop(L, 0);
+  luaL_newlib(L, iolib);  /* new module */
   createmeta(L);
   createmeta(L);
-  /* create (private) environment (with fields IO_INPUT, IO_OUTPUT, __close) */
-  newenv(L, io_fclose);  /* upvalue for all io functions at index 1 */
-  luaL_newlibtable(L, iolib);  /* new module at index 2 */
-  lua_pushvalue(L, 1);  /* copy of env to be consumed by 'setfuncs' */
-  luaL_setfuncs(L, iolib, 1);
   /* create (and set) default files */
   /* create (and set) default files */
-  newenv(L, io_noclose);  /* environment for default files at index 3 */
   createstdfile(L, stdin, IO_INPUT, "stdin");
   createstdfile(L, stdin, IO_INPUT, "stdin");
   createstdfile(L, stdout, IO_OUTPUT, "stdout");
   createstdfile(L, stdout, IO_OUTPUT, "stdout");
-  createstdfile(L, stderr, 0, "stderr");
-  lua_pop(L, 1);  /* pop environment for default files */
-  lua_getfield(L, 2, "popen");
-  newenv(L, io_pclose);  /* create environment for 'popen' streams */
-  lua_setupvalue(L, -2, 1);  /* set it as upvalue for 'popen' */
-  lua_pop(L, 1);  /* pop 'popen' */
+  createstdfile(L, stderr, NULL, "stderr");
   return 1;
   return 1;
 }
 }