new design for iolib (object style)

This commit is contained in:
Roberto Ierusalimschy
2002-04-04 17:24:56 -03:00
parent a2e414d679
commit c3b90061ea

297
liolib.c
View File

@@ -1,5 +1,5 @@
/* /*
** $Id: liolib.c,v 1.132 2002/03/20 12:54:08 roberto Exp roberto $ ** $Id: liolib.c,v 1.133 2002/03/27 15:30:41 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
*/ */
@@ -27,32 +27,21 @@
*/ */
#ifdef POPEN #ifndef POPEN
/* FILE *popen(); #define pclose(f) (-1)
int pclose(); */
#define CLOSEFILE(L, f) ((pclose(f) == -1) ? fclose(f) : 0)
#else
/* no support for popen */
#define popen(x,y) NULL /* that is, popen always fails */
#define CLOSEFILE(L, f) (fclose(f))
#endif #endif
#define INFILE 0
#define OUTFILE 1
#define NOFILE 2
#define FILEHANDLE "FileHandle" #define FILEHANDLE "FileHandle"
#define CLOSEDFILEHANDLE "ClosedFileHandle" #define CLOSEDFILEHANDLE "ClosedFileHandle"
#define IO_INPUT "_input"
static const char *const filenames[] = {"_INPUT", "_OUTPUT"}; #define IO_OUTPUT "_output"
static const char *const basicfiles[] = {"stdin", "stdout"};
static int pushresult (lua_State *L, int i) { static int pushresult (lua_State *L, int i) {
if (i) { if (i) {
lua_pushnumber(L, 1); lua_pushboolean(L, 1);
return 1; return 1;
} }
else { else {
@@ -64,43 +53,15 @@ static int pushresult (lua_State *L, int i) {
} }
static int checkfile (lua_State *L, int findex, const char *tname) { static FILE *tofile (lua_State *L, int findex) {
int res; FILE *f = (FILE *)lua_touserdata(L, findex);
lua_getmetatable(L, findex); if (f && lua_getmetatable(L, findex) &&
lua_pushstring(L, tname); lua_equal(L, -1, lua_upvalueindex(1))) {
lua_rawget(L, LUA_REGISTRYINDEX); lua_pop(L, 1);
res = lua_equal(L, -1, -2); return f;
lua_pop(L, 2);
return res;
}
/* temporary?? should be in auxlib... */
static void *luaL_check_userdata (lua_State *L, int findex, const char *tn) {
luaL_arg_check(L, checkfile(L, findex, tn), findex, "bad file");
return lua_touserdata(L, findex);
}
static FILE *getopthandle (lua_State *L, int inout) {
FILE *p = (FILE *)(lua_touserdata(L, 1));
if (p != NULL) { /* is it a userdata ? */
if (!checkfile(L, 1, FILEHANDLE)) { /* not a valid file handle? */
if (checkfile(L, 1, CLOSEDFILEHANDLE))
luaL_argerror(L, 1, "file is closed");
else
luaL_argerror(L, 1, "(invalid value)");
}
lua_pushvalue(L, 1); lua_remove(L, 1); /* move it to stack top */
} }
else { /* try global value */ luaL_argerror(L, findex, "bad file");
lua_getglobal(L, filenames[inout]); return NULL; /* to avoid warnings */
if (!checkfile(L, -1, FILEHANDLE))
luaL_verror(L, "global variable `%.10s' is not a valid file handle",
filenames[inout]);
p = (FILE *)(lua_touserdata(L, -1));
}
return p; /* leave handle at stack top to avoid GC */
} }
@@ -112,102 +73,106 @@ static void newfile (lua_State *L, FILE *f) {
} }
static void newfilewithname (lua_State *L, FILE *f, const char *name) { static void registerfile (lua_State *L, FILE *f, const char *name,
const char *impname) {
lua_pushstring(L, name); lua_pushstring(L, name);
newfile(L, f); newfile(L, f);
if (impname) {
lua_pushstring(L, impname);
lua_pushvalue(L, -2);
lua_settable(L, -6);
}
lua_settable(L, -3); lua_settable(L, -3);
} }
static int setnewfile (lua_State *L, FILE *f, int inout) { static int setnewfile (lua_State *L, FILE *f) {
if (f == NULL) if (f == NULL)
return pushresult(L, 0); return pushresult(L, 0);
else { else {
newfile(L, f); newfile(L, f);
if (inout != NOFILE) {
lua_pushvalue(L, -1);
lua_setglobal(L, filenames[inout]);
}
return 1; return 1;
} }
} }
static void resetfile (lua_State *L, int inout) {
lua_getglobal(L, "io");
lua_pushstring(L, basicfiles[inout]);
lua_gettable(L, -2);
lua_setglobal(L, filenames[inout]);
lua_pop(L, 1);
}
static int io_close (lua_State *L) { static int io_close (lua_State *L) {
FILE *f = (FILE *)(luaL_check_userdata(L, 1, FILEHANDLE)); FILE *f = tofile(L, 1);
int status = 1; int status = 1;
if (f != stdin && f != stdout && f != stderr) { if (f != stdin && f != stdout && f != stderr) {
lua_settop(L, 1); /* make sure file is on top */ lua_settop(L, 1); /* make sure file is on top */
lua_pushliteral(L, CLOSEDFILEHANDLE); lua_pushliteral(L, CLOSEDFILEHANDLE);
lua_rawget(L, LUA_REGISTRYINDEX); lua_rawget(L, LUA_REGISTRYINDEX);
lua_setmetatable(L, 1); lua_setmetatable(L, 1);
status = (CLOSEFILE(L, f) == 0); status = (pclose(f) != -1) || (fclose(f) == 0);
} }
return pushresult(L, status); return pushresult(L, status);
} }
static int file_collect (lua_State *L) { static int io_open (lua_State *L) {
FILE *f = (FILE *)(luaL_check_userdata(L, 1, FILEHANDLE)); FILE *f = fopen(luaL_check_string(L, 1), luaL_opt_string(L, 2, "r"));
if (f != stdin && f != stdout && f != stderr) return setnewfile(L, f);
CLOSEFILE(L, f);
return 0;
} }
static int io_open (lua_State *L) { static int io_popen (lua_State *L) {
FILE *f = fopen(luaL_check_string(L, 1), luaL_check_string(L, 2)); #ifndef POPEN
return setnewfile(L, f, NOFILE); lua_error(L, "`popen' not supported");
return 0;
#else
FILE *f = popen(luaL_check_string(L, 1), luaL_opt_string(L, 2, "r"));
return setnewfile(L, f);
#endif
} }
static int io_tmpfile (lua_State *L) { static int io_tmpfile (lua_State *L) {
return setnewfile(L, tmpfile(), NOFILE); return setnewfile(L, tmpfile());
} }
static FILE *getiofile (lua_State *L, const char *name) {
lua_pushstring(L, name);
lua_rawget(L, lua_upvalueindex(1));
return tofile(L, -1);
}
static int io_fromto (lua_State *L, int inout, const char *mode) {
FILE *current; static int g_iofile (lua_State *L, const char *name, const char *mode) {
if (lua_isnone(L, 1)) { if (lua_isnone(L, 1)) {
getopthandle(L, inout); lua_pushstring(L, name);
resetfile(L, inout); lua_rawget(L, lua_upvalueindex(1));
return io_close(L); return 1;
} }
else { else {
const char *s = luaL_check_string(L, 1); const char *filename = lua_tostring(L, 1);
current = (*s == '|') ? popen(s+1, mode) : fopen(s, mode); lua_pushstring(L, name);
return setnewfile(L, current, inout); if (filename) {
FILE *f = fopen(filename, mode);
luaL_arg_check(L, f, 1, strerror(errno));
newfile(L, f);
}
else {
lua_pushvalue(L, 1);
tofile(L, -1); /* check that it's a valid file handle */
}
lua_rawset(L, lua_upvalueindex(1));
return 0;
} }
} }
static int io_readfrom (lua_State *L) { static int io_input (lua_State *L) {
return io_fromto(L, INFILE, "r"); return g_iofile(L, IO_INPUT, "r");
} }
static int io_writeto (lua_State *L) { static int io_output (lua_State *L) {
return io_fromto(L, OUTFILE, "w"); return g_iofile(L, IO_OUTPUT, "w");
} }
static int io_appendto (lua_State *L) {
FILE *current = fopen(luaL_check_string(L, 1), "a");
return setnewfile(L, current, OUTFILE);
}
/* /*
** {====================================================== ** {======================================================
** READ ** READ
@@ -307,19 +272,18 @@ static int read_chars (lua_State *L, FILE *f, size_t n) {
} }
static int io_read (lua_State *L) { static int g_read (lua_State *L, FILE *f, int first) {
FILE *f = getopthandle(L, INFILE);
int nargs = lua_gettop(L) - 1; int nargs = lua_gettop(L) - 1;
int success; int success;
int n; int n;
if (nargs == 0) { /* no arguments? */ if (nargs == 0) { /* no arguments? */
success = read_until(L, f, "\n", 1); /* read until \n (a line) */ success = read_until(L, f, "\n", 1); /* read until \n (a line) */
n = 2; /* will return n-1 results */ n = first+1; /* to return 1 result */
} }
else { /* ensure stack space for all results and for auxlib's buffer */ else { /* ensure stack space for all results and for auxlib's buffer */
luaL_check_stack(L, nargs+LUA_MINSTACK, "too many arguments"); luaL_check_stack(L, nargs+LUA_MINSTACK, "too many arguments");
success = 1; success = 1;
for (n = 1; n<=nargs && success; n++) { for (n = first; nargs-- && success; n++) {
if (lua_type(L, n) == LUA_TNUMBER) { if (lua_type(L, n) == LUA_TNUMBER) {
size_t l = (size_t)lua_tonumber(L, n); size_t l = (size_t)lua_tonumber(L, n);
success = (l == 0) ? test_eof(L, f) : read_chars(L, f, l); success = (l == 0) ? test_eof(L, f) : read_chars(L, f, l);
@@ -360,18 +324,26 @@ static int io_read (lua_State *L) {
lua_pop(L, 1); /* remove last result */ lua_pop(L, 1); /* remove last result */
lua_pushnil(L); /* push nil instead */ lua_pushnil(L); /* push nil instead */
} }
return n - 1; return n - first;
}
static int io_read (lua_State *L) {
return g_read(L, getiofile(L, IO_INPUT), 1);
}
static int f_read (lua_State *L) {
return g_read(L, tofile(L, 1), 2);
} }
/* }====================================================== */ /* }====================================================== */
static int io_write (lua_State *L) { static int g_write (lua_State *L, FILE *f, int arg) {
FILE *f = getopthandle(L, OUTFILE); int nargs = lua_gettop(L) - 1;
int nargs = lua_gettop(L)-1;
int arg;
int status = 1; int status = 1;
for (arg=1; arg<=nargs; arg++) { for (; nargs--; arg++) {
if (lua_type(L, arg) == LUA_TNUMBER) { if (lua_type(L, arg) == LUA_TNUMBER) {
/* optimization: could be done exactly as for strings */ /* optimization: could be done exactly as for strings */
status = status && status = status &&
@@ -388,10 +360,20 @@ static int io_write (lua_State *L) {
} }
static int io_seek (lua_State *L) { static int io_write (lua_State *L) {
return g_write(L, getiofile(L, IO_OUTPUT), 1);
}
static int f_write (lua_State *L) {
return g_write(L, tofile(L, 1), 2);
}
static int f_seek (lua_State *L) {
static const int mode[] = {SEEK_SET, SEEK_CUR, SEEK_END}; static const int mode[] = {SEEK_SET, SEEK_CUR, SEEK_END};
static const char *const modenames[] = {"set", "cur", "end", NULL}; static const char *const modenames[] = {"set", "cur", "end", NULL};
FILE *f = (FILE *)(luaL_check_userdata(L, 1, FILEHANDLE)); FILE *f = tofile(L, 1);
int op = luaL_findstring(luaL_opt_string(L, 2, "cur"), modenames); int op = luaL_findstring(luaL_opt_string(L, 2, "cur"), modenames);
long offset = luaL_opt_long(L, 3, 0); long offset = luaL_opt_long(L, 3, 0);
luaL_arg_check(L, op != -1, 2, "invalid mode"); luaL_arg_check(L, op != -1, 2, "invalid mode");
@@ -406,25 +388,60 @@ static int io_seek (lua_State *L) {
static int io_flush (lua_State *L) { static int io_flush (lua_State *L) {
FILE *f = (lua_isnoneornil(L, 1)) ? (FILE *)(NULL) : return pushresult(L, fflush(getiofile(L, IO_OUTPUT)) == 0);
(FILE *)(luaL_check_userdata(L, 1, FILEHANDLE));
return pushresult(L, fflush(f) == 0);
} }
static int f_flush (lua_State *L) {
return pushresult(L, fflush(tofile(L, 1)) == 0);
}
static const luaL_reg iolib[] = { static const luaL_reg iolib[] = {
{"appendto", io_appendto}, {"input", io_input},
{"output", io_output},
{"close", io_close}, {"close", io_close},
{"flush", io_flush}, {"flush", io_flush},
{"open", io_open}, {"open", io_open},
{"read", io_read}, {"popen", io_popen},
{"readfrom", io_readfrom}, {"read", io_read},
{"seek", io_seek}, {"tmpfile", io_tmpfile},
{"tmpfile", io_tmpfile}, {"write", io_write},
{"write", io_write},
{"writeto", io_writeto},
{NULL, NULL} {NULL, NULL}
}; };
static const luaL_reg flib[] = {
{"flush", f_flush},
{"read", f_read},
{"seek", f_seek},
{"write", f_write},
{NULL, NULL}
};
static void createmeta (lua_State *L) {
lua_pushliteral(L, FILEHANDLE); /* S: FH */
lua_newtable(L); /* S: mt FH */
/* close files when collected */
lua_pushliteral(L, "__gc"); /* S: `gc' mt FH */
lua_pushvalue(L, -2); /* S: mt `gc' mt FH */
lua_pushcclosure(L, io_close, 1); /* S: close `gc' mt FH */
lua_rawset(L, -3); /* S: mt FH */
/* file methods */
lua_pushliteral(L, "__gettable"); /* S: `gettable' mt FH */
lua_pushvalue(L, -2); /* S: mt `gettable' mt FH */
lua_rawset(L, -3); /* S: mt FH */
lua_pushvalue(L, -1); /* S: mt mt FH */
luaL_openlib(L, flib, 1); /* S: mt FH */
/* put new metatable into registry */
lua_rawset(L, LUA_REGISTRYINDEX); /* S: empty */
/* meta table for CLOSEDFILEHANDLE */
lua_pushliteral(L, CLOSEDFILEHANDLE);
lua_newtable(L);
lua_rawset(L, LUA_REGISTRYINDEX);
}
/* }====================================================== */ /* }====================================================== */
@@ -613,29 +630,19 @@ static const luaL_reg syslib[] = {
LUALIB_API int lua_iolibopen (lua_State *L) { LUALIB_API int lua_iolibopen (lua_State *L) {
lua_pushliteral(L, FILEHANDLE); createmeta(L);
lua_newtable(L); /* meta table for FILEHANDLE */ luaL_opennamedlib(L, "os", syslib, 0);
/* close files when collected */ lua_pushliteral(L, FILEHANDLE); /* S: FH */
lua_pushliteral(L, "__gc"); lua_rawget(L, LUA_REGISTRYINDEX); /* S: mt */
lua_pushcfunction(L, file_collect); lua_pushvalue(L, -1); /* S: mt mt */
lua_rawset(L, -3); luaL_opennamedlib(L, "io", iolib, 1); /* S: mt */
/* put new metatable into registry */ lua_pushliteral(L, "io"); /* S: `io' mt */
lua_rawset(L, LUA_REGISTRYINDEX); lua_gettable(L, LUA_GLOBALSINDEX); /* S: io mt */
/* meta table for CLOSEDFILEHANDLE */ /* put predefined file handles into `io' table */
lua_pushliteral(L, CLOSEDFILEHANDLE); registerfile(L, stdin, "stdin", IO_INPUT);
lua_newtable(L); registerfile(L, stdout, "stdout", IO_OUTPUT);
lua_rawset(L, LUA_REGISTRYINDEX); registerfile(L, stderr, "stderr", NULL);
luaL_opennamedlib(L, "os", syslib); lua_pop(L, 2); /* S: empty */
lua_pushliteral(L, "io");
lua_newtable(L);
luaL_openlib(L, iolib);
/* predefined file handles */
newfilewithname(L, stdin, basicfiles[INFILE]);
newfilewithname(L, stdout, basicfiles[OUTFILE]);
newfilewithname(L, stderr, "stderr");
lua_settable(L, LUA_GLOBALSINDEX);
resetfile(L, INFILE);
resetfile(L, OUTFILE);
return 0; return 0;
} }