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 {
if ( appendMode || updateMode ) {
throw new IOException("unsupported mode");
}
String url = "file:///" + filename;
int mode = readMode? Connector.READ: Connector.READ_WRITE;
StreamConnection conn = (StreamConnection) Connector.open( url, mode );
File f = readMode?
new FileImpl(conn, conn.openInputStream(), null):
new FileImpl(conn, conn.openInputStream(), conn.openOutputStream());
/*
if ( appendMode ) {
f.seek("end",0);
} else {
if ( ! readMode )
conn.truncate(0);
}
*/
new FileImpl(conn, null, conn.openOutputStream());
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 {
RandomAccessFile f = new RandomAccessFile(filename,readMode? "r": "rw");
RandomAccessFile f = new RandomAccessFile(filename, readMode && !updateMode ? "r": "rw");
if ( appendMode ) {
f.seek(f.length());
} else {
if ( ! readMode )
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 {
@@ -104,7 +104,7 @@ public class JseIoLib extends IoLib {
protected File tmpFile() throws IOException {
java.io.File f = java.io.File.createTempFile(".luaj","bin");
f.deleteOnExit();
return new FileImpl( new RandomAccessFile(f,"rw") );
return new FileImpl( new RandomAccessFile(f,"rw"), true, true, false );
}
private static void notimplemented() {
@@ -116,21 +116,27 @@ public class JseIoLib extends IoLib {
private final RandomAccessFile file;
private final InputStream is;
private final OutputStream os;
private final boolean readable;
private final boolean writable;
private final boolean append;
private boolean closed = 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.is = is!=null? is.markSupported()? is: new BufferedInputStream(is): null;
this.os = os;
this.readable = readable;
this.writable = writable;
this.append = append;
}
private FileImpl( RandomAccessFile f ) {
this( f, null, null );
private FileImpl( RandomAccessFile f, boolean readable, boolean writable, boolean append ) {
this( f, null, null, readable, writable, append );
}
private FileImpl( InputStream i ) {
this( null, i, null );
this( null, i, null, true, false, false );
}
private FileImpl( OutputStream o ) {
this( null, null, o );
this( null, null, o, false, true, false );
}
public String tojstring() {
return "file (" + (this.closed ? "closed" : String.valueOf(this.hashCode())) + ")";
@@ -149,11 +155,15 @@ public class JseIoLib extends IoLib {
os.flush();
}
public void write(LuaString s) throws IOException {
if ( ! writable )
throw new IOException("file is not writable");
if ( os != null )
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 );
else
} else
notimplemented();
if ( nobuffer )
flush();
@@ -181,11 +191,15 @@ public class JseIoLib extends IoLib {
// get length remaining to read
public int remaining() throws IOException {
if ( ! readable )
throw new IOException("file is not readable");
return file!=null? (int) (file.length()-file.getFilePointer()): -1;
}
// peek ahead one character
public int peek() throws IOException {
if ( ! readable )
throw new IOException("file is not readable");
if ( is != null ) {
is.mark(1);
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
public int read() throws IOException {
if ( ! readable )
throw new IOException("file is not readable");
if ( is != null )
return is.read();
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
public int read(byte[] bytes, int offset, int length) throws IOException {
if ( ! readable )
throw new IOException("file is not readable");
if (file!=null) {
return file.read(bytes, offset, length);
} else if (is!=null) {

View File

@@ -21,6 +21,9 @@
******************************************************************************/
package org.luaj.vm2;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.Reader;
import java.io.StringReader;
import java.util.concurrent.atomic.AtomicReference;
@@ -236,6 +239,98 @@ public class FragmentsTest extends TestSuite {
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() {
runFragment(
LuaValue.varargsOf(new LuaValue[] {
@@ -844,5 +939,20 @@ public class FragmentsTest extends TestSuite {
+ "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("'", "\\'") + "'";
}
}
}