From b8237ec872ad68eea0a1a6bd929e612d626fa17d Mon Sep 17 00:00:00 2001 From: James Roseborough Date: Fri, 5 Dec 2008 18:25:30 +0000 Subject: [PATCH] Implement io.tmpfile(), io.setvbuf(), and tests for both --- src/core/org/luaj/lib/IoLib.java | 77 +++++++++++++++++------ src/j2se/org/luaj/lib/j2se/J2seIoLib.java | 22 +++++-- src/test/errors/iolibargs.lua | 7 +++ src/test/res/iolib.lua | 46 ++++++++++++++ 4 files changed, 128 insertions(+), 24 deletions(-) diff --git a/src/core/org/luaj/lib/IoLib.java b/src/core/org/luaj/lib/IoLib.java index 4180e49b..580055e7 100644 --- a/src/core/org/luaj/lib/IoLib.java +++ b/src/core/org/luaj/lib/IoLib.java @@ -38,6 +38,7 @@ public class IoLib extends LFunction { protected interface File { public void write( LString string ) throws IOException; public void flush() throws IOException; + public boolean isstdfile(); public void close() throws IOException; public boolean isclosed(); /** returns new position */ @@ -46,6 +47,7 @@ public class IoLib extends LFunction { public Double readNumber() throws IOException; public LValue readLine() throws IOException; public LValue readFile() throws IOException; + public void setvbuf(String mode, int size); } @@ -63,6 +65,13 @@ public class IoLib extends LFunction { */ abstract protected File openFile(String filename, String mode) throws IOException; + /** + * Open a temporary file. + * @return File object if successful + * @throws IOException if could not be opened + */ + abstract protected File tmpFile() throws IOException; + /** * Start a new process and return a file for input or output * @param prog the program to execute @@ -198,9 +207,8 @@ public class IoLib extends LFunction { f = vm.isnoneornil(2)? output(vm): checkfile(vm,2); - f.close(); - vm.resettop(); - vm.pushboolean(true); + checkopen(vm, f); + ioclose(vm,f); break; case IO_FLUSH: checkopen(vm,output(vm)); @@ -220,8 +228,9 @@ public class IoLib extends LFunction { INPUT = vm.isnoneornil(2)? input(vm): ioopenfile(vm,vm.checkstring(2),"r"); + checkopen(vm, INPUT); vm.resettop(); - vm.pushlvalue(lines(INPUT)); + vm.pushlvalue(lines(vm,INPUT)); break; case IO_OPEN: setresult(vm, openFile(vm.checkstring(2), vm.optstring(3,"r"))); @@ -238,9 +247,11 @@ public class IoLib extends LFunction { setresult(vm, openProgram(vm.checkstring(2),vm.optstring(3, "r"))); break; case IO_READ: + checkopen(vm, INPUT); ioread( vm, INPUT ); break; case IO_TMPFILE: + setresult(vm, tmpFile()); break; case IO_TYPE: f = optfile(vm,2); @@ -251,22 +262,23 @@ public class IoLib extends LFunction { vm.pushnil(); break; case IO_WRITE: + checkopen(vm, output(vm)); iowrite( vm, OUTPUT ); break; case FILE_CLOSE: - checkfile(vm,2).close(); - vm.resettop(); - vm.pushboolean(true); + f = checkfile(vm,2); + ioclose(vm, f); break; case FILE_FLUSH: - checkfile(vm,2).flush(); + f = checkfile(vm,2); + f.flush(); vm.resettop(); vm.pushboolean(true); break; case FILE_LINES: f = checkfile(vm,2); vm.resettop(); - vm.pushlvalue(lines(f)); + vm.pushlvalue(lines(vm,f)); break; case FILE_READ: f = checkfile(vm,2); @@ -281,7 +293,11 @@ public class IoLib extends LFunction { vm.pushinteger(n); break; case FILE_SETVBUF: + f = checkfile(vm,2); + vm.remove(2); + f.setvbuf(vm.checkstring(2),vm.optint(3, 1024)); vm.resettop(); + vm.pushboolean(true); break; case FILE_WRITE: f = checkfile(vm,2); @@ -292,22 +308,44 @@ public class IoLib extends LFunction { LuaState.vmerror( "bad io id" ); } } catch ( IOException ioe ) { - vm.resettop(); - vm.pushnil(); - vm.pushstring("io error: "+ioe.getMessage()); + seterrorresult(vm,ioe); } return false; } - private LValue lines(final File f) { + private static void ioclose(LuaState vm, File f) throws IOException { + if ( f.isstdfile() ) + seterrorresult(vm,"cannot close standard file"); + else { + f.close(); + setsuccessresult(vm); + } + } + + private static void setsuccessresult(LuaState vm) { + vm.resettop(); + vm.pushboolean(true); + } + + private static void seterrorresult(LuaState vm, IOException ioe) { + String s = ioe.getMessage(); + seterrorresult(vm, "io error: "+(s!=null? s: ioe.toString())); + } + + private static void seterrorresult(LuaState vm, String errortext) { + vm.resettop(); + vm.pushnil(); + vm.pushstring(errortext); + } + + private LValue lines(LuaState vm, final File f) { return new LFunction() { public boolean luaStackCall(LuaState vm) { vm.resettop(); try { vm.pushlvalue(f.readLine()); } catch (IOException e) { - vm.pushnil(); - vm.pushstring("io error: "+e); + seterrorresult(vm,e); } return false; } @@ -315,7 +353,6 @@ public class IoLib extends LFunction { } private static void iowrite(LuaState vm, File f) throws IOException { - checkopen(vm,f); for ( int i=2, n=vm.gettop(); i<=n; i++ ) f.write( vm.checklstring(i) ); vm.resettop(); @@ -323,7 +360,6 @@ public class IoLib extends LFunction { } private static void ioread(LuaState vm, File f) throws IOException { - checkopen( vm, f ); int i,n=vm.gettop(); for ( i=2; i<=n; i++ ) { if ( vm.isnumber(i) ) { @@ -345,7 +381,9 @@ public class IoLib extends LFunction { } private static File checkfile(LuaState vm, int index) { - return (File) vm.checkudata(index, File.class); + File f = (File) vm.checkudata(index, File.class); + checkopen( vm, f ); + return f; } private File optfile(LuaState vm, int index) { @@ -353,9 +391,10 @@ public class IoLib extends LFunction { return (u instanceof File? (File) u: null); } - private static void checkopen(LuaState vm, File file) { + private static File checkopen(LuaState vm, File file) { if ( file.isclosed() ) vm.error("attempt to use a closed file"); + return file; } private static void setresult(LuaState vm, File file) { diff --git a/src/j2se/org/luaj/lib/j2se/J2seIoLib.java b/src/j2se/org/luaj/lib/j2se/J2seIoLib.java index 8cc8cd20..df2a19f9 100644 --- a/src/j2se/org/luaj/lib/j2se/J2seIoLib.java +++ b/src/j2se/org/luaj/lib/j2se/J2seIoLib.java @@ -82,6 +82,7 @@ public class J2seIoLib extends IoLib { private final InputStream is; private final OutputStream os; private boolean closed = false; + private boolean nobuffer = false; private FileImpl( RandomAccessFile file, InputStream is, OutputStream os ) { this.file = file; this.is = is!=null? is.markSupported()? is: new BufferedInputStream(is): null; @@ -99,14 +100,14 @@ public class J2seIoLib extends IoLib { public String toString() { return "file ("+this.hashCode()+")"; } + public boolean isstdfile() { + return file == null; + } public void close() throws IOException { closed = true; - if ( file != null ) + if ( file != null ) { file.close(); - else if ( os != null ) - os.close(); - else if ( is != null ) - is.close(); + } } public void flush() throws IOException { if ( os != null ) @@ -119,6 +120,8 @@ public class J2seIoLib extends IoLib { file.write( s.m_bytes, s.m_offset, s.m_length ); else notimplemented(); + if ( nobuffer ) + flush(); } public boolean isclosed() { return closed; @@ -139,6 +142,9 @@ public class J2seIoLib extends IoLib { notimplemented(); return 0; } + public void setvbuf(String mode, int size) { + nobuffer = "no".equals(mode); + } public LValue readBytes(int count) throws IOException { if ( file != null ) { byte[] b = new byte[count]; @@ -217,4 +223,10 @@ public class J2seIoLib extends IoLib { new FileImpl( p.getOutputStream() ): new FileImpl( p.getInputStream() ); } + + protected File tmpFile() throws IOException { + java.io.File f = java.io.File.createTempFile(".luaj","bin"); + f.deleteOnExit(); + return new FileImpl( new RandomAccessFile(f,"rw") ); + } } diff --git a/src/test/errors/iolibargs.lua b/src/test/errors/iolibargs.lua index 1718bf67..26fbca55 100644 --- a/src/test/errors/iolibargs.lua +++ b/src/test/errors/iolibargs.lua @@ -66,3 +66,10 @@ checkallerrors('file.seek',{},'bad argument #1') checkallerrors('file.seek',{{file},nonstring},'bad argument #1') checkallerrors('file.seek',{{file},{"set","cur","end"},nonnumber},'bad argument #2') +-- file:setvbuf (mode [, size]) +checkallpass('file.setvbuf',{{file},{"no","full","line"}}) +checkallpass('file.setvbuf',{{file},{"full"},{1024,"512"}}) +checkallerrors('file.setvbuf',{},'bad argument #1') +checkallerrors('file.setvbuf',{{file},notastring},'bad argument #1') +checkallerrors('file.setvbuf',{{file},{"full"},nonnumber},'bad argument #2') + diff --git a/src/test/res/iolib.lua b/src/test/res/iolib.lua index f99b767d..c335bb58 100644 --- a/src/test/res/iolib.lua +++ b/src/test/res/iolib.lua @@ -61,3 +61,49 @@ for l in io.lines() do print( string.format('%q',l) ) end +local a = io.tmpfile() +local b = io.tmpfile() +print( io.type(a) ) +print( io.type(b) ) +print( "a:write", a:write('aaaaaaa') ) +print( "b:write", b:write('bbbbbbb') ) +print( "a:setvbuf", a:setvbuf("no") ) +print( "a:setvbuf", a:setvbuf("full",1024) ) +print( "a:setvbuf", a:setvbuf("line") ) +print( "a:write", a:write('ccccc') ) +print( "b:write", b:write('ddddd') ) +print( "a:flush", a:flush() ) +print( "b:flush", b:flush() ) +--[[ +print( "a:read", a:read(7) ) +print( "b:read", b:read(7) ) +print( "a:seek", a:seek("cur",-4) ) +print( "b:seek", b:seek("cur",-4) ) +print( "a:write", a:read(7) ) +print( "b:write", b:read(7) ) +--]] + +local pcall = function(...) return ( pcall(...) )end + +print( 'a:close', pcall( a.close, a ) ) +print( 'a:write', pcall( a.write, a, 'eee') ) +print( 'a:flush', pcall( a.flush, a) ) +print( 'a:read', pcall( a.read, a, 5) ) +print( 'a:lines', pcall( a.lines, a) ) +print( 'a:seek', pcall( a.seek, a, "cur", -2) ) +print( 'a:setvbuf', pcall( a.setvbuf, a, "no") ) +print( 'a:close', pcall( a.close, a ) ) +print( 'io.type(a)', pcall( io.type, a ) ) + +print( 'io.close()', pcall( io.close ) ) +print( 'io.close(io.output())', pcall( io.close, io.output() ) ) + +io.output('abc.txt') +print( 'io.close()', pcall( io.close ) ) +print( 'io.write', pcall( io.write, 'eee') ) +print( 'io.flush', pcall( io.flush) ) +print( 'io.close', pcall( io.close ) ) +io.input('abc.txt'):close() +print( 'io.read', pcall( io.read, 5) ) +print( 'io.lines', pcall( io.lines) ) +