Implemented issue: #56

This commit is contained in:
UnlegitDqrk
2026-03-02 14:58:31 +01:00
parent 921606b93f
commit 7aea99d650
7 changed files with 142 additions and 19 deletions

View File

@@ -83,20 +83,15 @@ public class JmeIoLib extends IoLib {
} }
protected File openFile( String filename, boolean readMode, boolean appendMode, boolean updateMode, boolean binaryMode ) throws IOException { protected File openFile( String filename, boolean readMode, boolean appendMode, boolean updateMode, boolean binaryMode ) throws IOException {
if ( appendMode || updateMode ) {
throw new IOException("unsupported mode");
}
String url = "file:///" + filename; String url = "file:///" + filename;
int mode = readMode? Connector.READ: Connector.READ_WRITE; int mode = readMode? Connector.READ: Connector.READ_WRITE;
StreamConnection conn = (StreamConnection) Connector.open( url, mode ); StreamConnection conn = (StreamConnection) Connector.open( url, mode );
File f = readMode? File f = readMode?
new FileImpl(conn, conn.openInputStream(), null): new FileImpl(conn, conn.openInputStream(), null):
new FileImpl(conn, conn.openInputStream(), conn.openOutputStream()); new FileImpl(conn, null, conn.openOutputStream());
/*
if ( appendMode ) {
f.seek("end",0);
} else {
if ( ! readMode )
conn.truncate(0);
}
*/
return f; return f;
} }

View File

@@ -84,14 +84,14 @@ public class JseIoLib extends IoLib {
} }
protected File openFile( String filename, boolean readMode, boolean appendMode, boolean updateMode, boolean binaryMode ) throws IOException { protected File openFile( String filename, boolean readMode, boolean appendMode, boolean updateMode, boolean binaryMode ) throws IOException {
RandomAccessFile f = new RandomAccessFile(filename,readMode? "r": "rw"); RandomAccessFile f = new RandomAccessFile(filename, readMode && !updateMode ? "r": "rw");
if ( appendMode ) { if ( appendMode ) {
f.seek(f.length()); f.seek(f.length());
} else { } else {
if ( ! readMode ) if ( ! readMode )
f.setLength(0); f.setLength(0);
} }
return new FileImpl( f ); return new FileImpl( f, readMode || updateMode, !readMode || updateMode, appendMode );
} }
protected File openProgram(String prog, String mode) throws IOException { protected File openProgram(String prog, String mode) throws IOException {
@@ -104,7 +104,7 @@ public class JseIoLib extends IoLib {
protected File tmpFile() throws IOException { protected File tmpFile() throws IOException {
java.io.File f = java.io.File.createTempFile(".luaj","bin"); java.io.File f = java.io.File.createTempFile(".luaj","bin");
f.deleteOnExit(); f.deleteOnExit();
return new FileImpl( new RandomAccessFile(f,"rw") ); return new FileImpl( new RandomAccessFile(f,"rw"), true, true, false );
} }
private static void notimplemented() { private static void notimplemented() {
@@ -116,21 +116,27 @@ public class JseIoLib extends IoLib {
private final RandomAccessFile file; private final RandomAccessFile file;
private final InputStream is; private final InputStream is;
private final OutputStream os; private final OutputStream os;
private final boolean readable;
private final boolean writable;
private final boolean append;
private boolean closed = false; private boolean closed = false;
private boolean nobuffer = false; private boolean nobuffer = false;
private FileImpl( RandomAccessFile file, InputStream is, OutputStream os ) { private FileImpl( RandomAccessFile file, InputStream is, OutputStream os, boolean readable, boolean writable, boolean append ) {
this.file = file; this.file = file;
this.is = is!=null? is.markSupported()? is: new BufferedInputStream(is): null; this.is = is!=null? is.markSupported()? is: new BufferedInputStream(is): null;
this.os = os; this.os = os;
this.readable = readable;
this.writable = writable;
this.append = append;
} }
private FileImpl( RandomAccessFile f ) { private FileImpl( RandomAccessFile f, boolean readable, boolean writable, boolean append ) {
this( f, null, null ); this( f, null, null, readable, writable, append );
} }
private FileImpl( InputStream i ) { private FileImpl( InputStream i ) {
this( null, i, null ); this( null, i, null, true, false, false );
} }
private FileImpl( OutputStream o ) { private FileImpl( OutputStream o ) {
this( null, null, o ); this( null, null, o, false, true, false );
} }
public String tojstring() { public String tojstring() {
return "file (" + (this.closed ? "closed" : String.valueOf(this.hashCode())) + ")"; return "file (" + (this.closed ? "closed" : String.valueOf(this.hashCode())) + ")";
@@ -149,11 +155,15 @@ public class JseIoLib extends IoLib {
os.flush(); os.flush();
} }
public void write(LuaString s) throws IOException { public void write(LuaString s) throws IOException {
if ( ! writable )
throw new IOException("file is not writable");
if ( os != null ) if ( os != null )
os.write( s.m_bytes, s.m_offset, s.m_length ); os.write( s.m_bytes, s.m_offset, s.m_length );
else if ( file != null ) else if ( file != null ) {
if ( append )
file.seek(file.length());
file.write( s.m_bytes, s.m_offset, s.m_length ); file.write( s.m_bytes, s.m_offset, s.m_length );
else } else
notimplemented(); notimplemented();
if ( nobuffer ) if ( nobuffer )
flush(); flush();
@@ -181,11 +191,15 @@ public class JseIoLib extends IoLib {
// get length remaining to read // get length remaining to read
public int remaining() throws IOException { public int remaining() throws IOException {
if ( ! readable )
throw new IOException("file is not readable");
return file!=null? (int) (file.length()-file.getFilePointer()): -1; return file!=null? (int) (file.length()-file.getFilePointer()): -1;
} }
// peek ahead one character // peek ahead one character
public int peek() throws IOException { public int peek() throws IOException {
if ( ! readable )
throw new IOException("file is not readable");
if ( is != null ) { if ( is != null ) {
is.mark(1); is.mark(1);
int c = is.read(); int c = is.read();
@@ -203,6 +217,8 @@ public class JseIoLib extends IoLib {
// return char if read, -1 if eof, throw IOException on other exception // return char if read, -1 if eof, throw IOException on other exception
public int read() throws IOException { public int read() throws IOException {
if ( ! readable )
throw new IOException("file is not readable");
if ( is != null ) if ( is != null )
return is.read(); return is.read();
else if ( file != null ) { else if ( file != null ) {
@@ -214,6 +230,8 @@ public class JseIoLib extends IoLib {
// return number of bytes read if positive, -1 if eof, throws IOException // return number of bytes read if positive, -1 if eof, throws IOException
public int read(byte[] bytes, int offset, int length) throws IOException { public int read(byte[] bytes, int offset, int length) throws IOException {
if ( ! readable )
throw new IOException("file is not readable");
if (file!=null) { if (file!=null) {
return file.read(bytes, offset, length); return file.read(bytes, offset, length);
} else if (is!=null) { } else if (is!=null) {

View File

@@ -21,6 +21,9 @@
******************************************************************************/ ******************************************************************************/
package org.luaj.vm2; package org.luaj.vm2;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.Reader; import java.io.Reader;
import java.io.StringReader; import java.io.StringReader;
import java.util.concurrent.atomic.AtomicReference; import java.util.concurrent.atomic.AtomicReference;
@@ -236,6 +239,98 @@ public class FragmentsTest extends TestSuite {
runFragment(LuaValue.valueOf(7), "return 1 | 2 & 6\n"); runFragment(LuaValue.valueOf(7), "return 1 | 2 & 6\n");
} }
public void testIoOpenReadModeDisallowsWrite() throws Exception {
File file = writeTempFile("read-mode", "hello");
try {
Globals globals = JsePlatform.standardGlobals();
try {
globals.load("local f = io.open(" + quote(file.getAbsolutePath()) + ", 'r') f:write('x')", "io_r.lua").call();
fail("expected write on read-only file to fail");
} catch (LuaError e) {
assertTrue(e.getMessage().indexOf("not writable") >= 0);
}
} finally {
file.delete();
}
}
public void testIoOpenWriteModeDisallowsRead() throws Exception {
File file = File.createTempFile("luaj-io", ".txt");
try {
Globals globals = JsePlatform.standardGlobals();
try {
globals.load("local f = io.open(" + quote(file.getAbsolutePath()) + ", 'w') return f:read('*a')", "io_w.lua").call();
fail("expected read on write-only file to fail");
} catch (LuaError e) {
assertTrue(e.getMessage().indexOf("not readable") >= 0);
}
} finally {
file.delete();
}
}
public void testIoOpenUpdateModesSupportReadWrite() throws Exception {
File file = writeTempFile("update-mode", "abc");
try {
Globals globals = JsePlatform.standardGlobals();
Varargs result = globals.load(
"local f = assert(io.open(" + quote(file.getAbsolutePath()) + ", 'r+'))\n" +
"local before = f:read('*a')\n" +
"f:seek('set', 0)\n" +
"f:write('xyz')\n" +
"f:seek('set', 0)\n" +
"local after = f:read('*a')\n" +
"f:close()\n" +
"return before, after\n",
"io_rplus.lua").invoke();
assertEquals("abc", result.arg1().tojstring());
assertEquals("xyz", result.arg(2).tojstring());
} finally {
file.delete();
}
}
public void testIoOpenAppendPreservesExistingContent() throws Exception {
File file = writeTempFile("append-mode", "abc");
try {
Globals globals = JsePlatform.standardGlobals();
LuaValue result = globals.load(
"local f = assert(io.open(" + quote(file.getAbsolutePath()) + ", 'a'))\n" +
"f:write('xyz')\n" +
"f:close()\n" +
"local g = assert(io.open(" + quote(file.getAbsolutePath()) + ", 'r'))\n" +
"local contents = g:read('*a')\n" +
"g:close()\n" +
"return contents\n",
"io_a.lua").call();
assertEquals("abcxyz", result.tojstring());
} finally {
file.delete();
}
}
public void testIoOpenAppendUpdateAppendsOnWriteAfterSeek() throws Exception {
File file = writeTempFile("append-update", "abc");
try {
Globals globals = JsePlatform.standardGlobals();
Varargs result = globals.load(
"local f = assert(io.open(" + quote(file.getAbsolutePath()) + ", 'a+'))\n" +
"f:seek('set', 0)\n" +
"local before = f:read('*a')\n" +
"f:seek('set', 0)\n" +
"f:write('xyz')\n" +
"f:seek('set', 0)\n" +
"local after = f:read('*a')\n" +
"f:close()\n" +
"return before, after\n",
"io_aplus.lua").invoke();
assertEquals("abc", result.arg1().tojstring());
assertEquals("abcxyz", result.arg(2).tojstring());
} finally {
file.delete();
}
}
public void testTableMove() { public void testTableMove() {
runFragment( runFragment(
LuaValue.varargsOf(new LuaValue[] { LuaValue.varargsOf(new LuaValue[] {
@@ -844,5 +939,20 @@ public class FragmentsTest extends TestSuite {
+ "return v1, v2, v3"); + "return v1, v2, v3");
} }
private static File writeTempFile(String prefix, String contents) throws IOException {
File file = File.createTempFile(prefix, ".txt");
FileOutputStream out = new FileOutputStream(file);
try {
out.write(contents.getBytes("UTF-8"));
} finally {
out.close();
}
return file;
}
private static String quote(String value) {
return "'" + value.replace("\\", "\\\\").replace("'", "\\'") + "'";
}
} }
} }