Add CLDC-1.0 based implementation of io libraries.

This commit is contained in:
James Roseborough
2008-12-05 22:29:07 +00:00
parent b8237ec872
commit 308e909df1
7 changed files with 483 additions and 128 deletions

View File

@@ -84,19 +84,13 @@ The following pattern is used within J2SE
</pre> </pre>
<p> <p>
You must include the library <b>lib/luaj-j2se-${VER}.jar</b> in your class path. A simple example may be found in
<p>
A working example may be found in
<pre> <pre>
src/sample/SampleJ2seMain.java src/sample/SampleJ2seMain.java
</pre> </pre>
<p> <p>
Additional usage may be found in You must include the library <b>lib/luaj-j2se-${VER}.jar</b> in your class path.
<pre>
src/sample/LuaRunner.java
</pre>
<h2>Run a script in a MIDlet</h2> <h2>Run a script in a MIDlet</h2>
@@ -120,6 +114,12 @@ The following pattern is used within MIDlets:
The file must be a resource within within the midlet jar for <em>dofile()</em> to find it. The file must be a resource within within the midlet jar for <em>dofile()</em> to find it.
Any files included via <em>require()</em> must also be part of the midlet resources. Any files included via <em>require()</em> must also be part of the midlet resources.
<p>
A simple example may be found in
<pre>
src/sample/SampleMIDlet.java
</pre>
<p> <p>
You must include the library <b>lib/luaj-j2me-${VER}.jar</b> in your midlet jar. You must include the library <b>lib/luaj-j2me-${VER}.jar</b> in your midlet jar.
They can be obfuscated if desired. They can be obfuscated if desired.
@@ -219,7 +219,7 @@ and the math operations are limited to those supported by J2ME.
<h2>Standard Libraries</h2> <h2>Standard Libraries</h2>
<p> <p>
The following libraries are loaded by default: The following libraries are loaded by default in J2ME and J2SE platforms:
<pre> <pre>
base base
coroutine coroutine
@@ -229,20 +229,37 @@ The following libraries are loaded by default:
table table
</pre> </pre>
In addition, J2SE contains these two libraries:
<pre>
io
luajava
</pre>
See <a href="http://www.lua.org/manual/5.1/">standard lua documentation</a> for details on the library API's See <a href="http://www.lua.org/manual/5.1/">standard lua documentation</a> for details on the library API's
<h2>Optional Libraries</h2> <p>
A java library may be loaded dynamically if created properly. Currently, there is one such library, <em>luajava.</em> There is a partial implementation of the <em>io</em> for J2ME in
<pre>
src/j2me/org/luaj/lib/j2me/Cldc10IoLib.java
</pre>
See the sample midlet to see how it is added to a platform.
<h2>The Luajava Library</h2>
The <em>luajava</em> library implements a few functions which allow access to most classes
within the host J2SE runtime. They are included as part of the standard J2SE platform.
<h3>LuaJava</h3> <h3>LuaJava</h3>
The luajava library is used to easily bind to Java classes via reflection. The luajava library is used to easily bind to Java classes via reflection.
It is patterned after the original <a href="http://www.keplerproject.org/luajava/">luajava project</a>. It is patterned after the original <a href="http://www.keplerproject.org/luajava/">luajava project</a>.
Because J2ME does not contain a reflection API, this library cannot be made to work on J2ME,
and is not included by default.
<h2>Unimplemented Libraries</h2> <h2>Unimplemented Libraries</h2>
The following libraries are not yet implemented: The following libraries are not yet implemented:
<pre> <pre>
os
debug debug
io
</pre> </pre>
<h2>Building the jars</h2> <h2>Building the jars</h2>

View File

@@ -21,6 +21,8 @@
******************************************************************************/ ******************************************************************************/
package org.luaj.lib; package org.luaj.lib;
import java.io.ByteArrayOutputStream;
import java.io.EOFException;
import java.io.IOException; import java.io.IOException;
import org.luaj.vm.LFunction; import org.luaj.vm.LFunction;
@@ -41,13 +43,17 @@ public class IoLib extends LFunction {
public boolean isstdfile(); public boolean isstdfile();
public void close() throws IOException; public void close() throws IOException;
public boolean isclosed(); public boolean isclosed();
/** returns new position */ // returns new position
public int seek(String option, int bytecount) throws IOException; public int seek(String option, int bytecount) throws IOException;
public LValue readBytes(int count) throws IOException;
public Double readNumber() throws IOException;
public LValue readLine() throws IOException;
public LValue readFile() throws IOException;
public void setvbuf(String mode, int size); public void setvbuf(String mode, int size);
// get length remaining to read
public int remaining() throws IOException;
// peek ahead one character
public int peek() throws IOException, EOFException;
// return char if read, -1 if eof, throw IOException on other exception
public int read() throws IOException, EOFException;
// return length if fully read, false if eof, throw IOException on other exception
public int readFully(byte[] bytes, int offset, int length) throws IOException;
} }
@@ -56,6 +62,20 @@ public class IoLib extends LFunction {
*/ */
abstract protected IoLib newInstance( int index ); abstract protected IoLib newInstance( int index );
/**
* Wrap the standard input.
* @return File
* @throws IOException
*/
abstract protected File wrapStdin() throws IOException;
/**
* Wrap the standard output.
* @return File
* @throws IOException
*/
abstract protected File wrapStdout() throws IOException;
/** /**
* Open a file in a particular mode. * Open a file in a particular mode.
* @param filename * @param filename
@@ -63,7 +83,7 @@ public class IoLib extends LFunction {
* @return File object if successful * @return File object if successful
* @throws IOException if could not be opened * @throws IOException if could not be opened
*/ */
abstract protected File openFile(String filename, String mode) throws IOException; abstract protected File openFile( String filename, boolean readMode, boolean appendMode, boolean updateMode, boolean binaryMode ) throws IOException;
/** /**
* Open a temporary file. * Open a temporary file.
@@ -233,7 +253,7 @@ public class IoLib extends LFunction {
vm.pushlvalue(lines(vm,INPUT)); vm.pushlvalue(lines(vm,INPUT));
break; break;
case IO_OPEN: case IO_OPEN:
setresult(vm, openFile(vm.checkstring(2), vm.optstring(3,"r"))); setresult(vm, rawopenfile(vm.checkstring(2), vm.optstring(3,"r")));
break; break;
case IO_OUTPUT: case IO_OUTPUT:
OUTPUT = vm.isnoneornil(2)? OUTPUT = vm.isnoneornil(2)?
@@ -343,7 +363,7 @@ public class IoLib extends LFunction {
public boolean luaStackCall(LuaState vm) { public boolean luaStackCall(LuaState vm) {
vm.resettop(); vm.resettop();
try { try {
vm.pushlvalue(f.readLine()); vm.pushlvalue(freadline(f));
} catch (IOException e) { } catch (IOException e) {
seterrorresult(vm,e); seterrorresult(vm,e);
} }
@@ -363,15 +383,15 @@ public class IoLib extends LFunction {
int i,n=vm.gettop(); int i,n=vm.gettop();
for ( i=2; i<=n; i++ ) { for ( i=2; i<=n; i++ ) {
if ( vm.isnumber(i) ) { if ( vm.isnumber(i) ) {
vm.pushlvalue(f.readBytes(vm.tointeger(i))); vm.pushlvalue(freadbytes(f,vm.tointeger(i)));
} else { } else {
String format = vm.checkstring(i); String format = vm.checkstring(i);
if ( "*n".equals(format) ) if ( "*n".equals(format) )
vm.pushnumber(f.readNumber()); vm.pushnumber(freadnumber(f));
else if ( "*a".equals(format) ) else if ( "*a".equals(format) )
vm.pushlvalue(f.readFile()); vm.pushlvalue(freadall(f));
else if ( "*l".equals(format) ) else if ( "*l".equals(format) )
vm.pushlvalue(f.readLine()); vm.pushlvalue(freadline(f));
else else
vm.typerror( i, "(invalid format)" ); vm.typerror( i, "(invalid format)" );
} }
@@ -408,12 +428,92 @@ public class IoLib extends LFunction {
private File ioopenfile(LuaState vm, String filename, String mode) { private File ioopenfile(LuaState vm, String filename, String mode) {
try { try {
File f = openFile( filename, mode ); return rawopenfile(filename, mode);
return f;
} catch ( Exception e ) { } catch ( Exception e ) {
vm.error("io error: "+e.getMessage()); vm.error("io error: "+e.getMessage());
return null; return null;
} }
} }
private File rawopenfile(String filename, String mode) throws IOException {
boolean isstdfile = "-".equals(filename);
boolean isreadmode = mode.startsWith("r");
if ( isstdfile ) {
return isreadmode?
wrapStdin():
wrapStdout();
}
boolean isappend = mode.startsWith("a");
boolean isupdate = mode.indexOf("+") > 0;
boolean isbinary = mode.endsWith("b");
return openFile( filename, isreadmode, isappend, isupdate, isbinary );
}
// ------------- file reading utilitied ------------------
public static LValue freadbytes(File f, int count) throws IOException {
byte[] b = new byte[count];
if ( f.readFully(b,0,b.length) < 0 )
return LNil.NIL;
return new LString(b);
}
public static LValue freaduntil(File f,int delim) throws IOException {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
int c;
try {
while ( true ) {
c = f.read();
if ( c < 0 || c == delim )
break;
baos.write(c);
}
} catch ( EOFException e ) {
c = -1;
}
return ( c < 0 && baos.size() == 0 )?
(LValue) LNil.NIL:
(LValue) new LString(baos.toByteArray());
}
public static LValue freadline(File f) throws IOException {
return freaduntil(f,'\n');
}
public static LValue freadall(File f) throws IOException {
int n = f.remaining();
if ( n >= 0 ) {
return freadbytes(f, n);
} else {
return freaduntil(f,-1);
}
}
public static Double freadnumber(File f) throws IOException {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
freadchars(f," \t\r\n",null);
freadchars(f,"-+",baos);
//freadchars(f,"0",baos);
//freadchars(f,"xX",baos);
freadchars(f,"0123456789",baos);
freadchars(f,".",baos);
freadchars(f,"0123456789",baos);
//freadchars(f,"eEfFgG",baos);
// freadchars(f,"+-",baos);
//freadchars(f,"0123456789",baos);
String s = baos.toString();
return s.length()>0? Double.valueOf(s): null;
}
private static void freadchars(File f, String chars, ByteArrayOutputStream baos) throws IOException {
int c;
while ( true ) {
c = f.peek();
if ( chars.indexOf(c) < 0 ) {
return;
}
f.read();
if ( baos != null )
baos.write( c );
}
}
} }

View File

@@ -0,0 +1,211 @@
/*******************************************************************************
* Copyright (c) 2008 LuaJ. All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
******************************************************************************/
package org.luaj.lib.j2me;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import javax.microedition.io.Connector;
import javax.microedition.io.StreamConnection;
import org.luaj.lib.BaseLib;
import org.luaj.lib.IoLib;
import org.luaj.vm.LString;
import org.luaj.vm.LTable;
/**
* Implementation of the lua io library based on CLDC 1.0 and StreamConnection.
*
* Seek is not supported.
*/
public class Cldc10IoLib extends IoLib {
public static void install( LTable globals ) {
new Cldc10IoLib().initialize(globals);
}
public Cldc10IoLib() {
super();
}
public Cldc10IoLib(int index) {
super(index);
}
protected IoLib newInstance(int index) {
return new Cldc10IoLib(index);
}
protected File wrapStdin() throws IOException {
return new FileImpl(BaseLib.STDIN);
}
protected File wrapStdout() throws IOException {
return new FileImpl(BaseLib.STDOUT != null? BaseLib.STDOUT: System.out);
}
protected File openFile( String filename, boolean readMode, boolean appendMode, boolean updateMode, boolean binaryMode ) throws IOException {
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);
}
*/
return f;
}
private static void notimplemented() throws IOException {
throw new IOException("not implemented");
}
protected File openProgram(String prog, String mode) throws IOException {
notimplemented();
return null;
}
protected File tmpFile() throws IOException {
notimplemented();
return null;
}
private static final class FileImpl implements File {
private final StreamConnection conn;
private final InputStream is;
private final OutputStream os;
private boolean closed = false;
private boolean nobuffer = false;
private int lookahead = -1;
private FileImpl( StreamConnection conn, InputStream is, OutputStream os ) {
this.conn = conn;
this.is = is;
this.os = os;
}
private FileImpl( InputStream i ) {
this( null, i, null );
}
private FileImpl( OutputStream o ) {
this( null, null, o );
}
public String toString() {
return "file ("+this.hashCode()+")";
}
public boolean isstdfile() {
return conn == null;
}
public void close() throws IOException {
closed = true;
if ( conn != null ) {
conn.close();
}
}
public void flush() throws IOException {
if ( os != null )
os.flush();
}
public void write(LString s) throws IOException {
if ( os != null )
os.write( s.m_bytes, s.m_offset, s.m_length );
else
notimplemented();
if ( nobuffer )
flush();
}
public boolean isclosed() {
return closed;
}
public int seek(String option, int pos) throws IOException {
/*
if ( conn != null ) {
if ( "set".equals(option) ) {
conn.seek(pos);
return (int) conn.getFilePointer();
} else if ( "end".equals(option) ) {
conn.seek(conn.length()+1+pos);
return (int) conn.length()+1;
} else {
conn.seek(conn.getFilePointer()+pos);
return (int) conn.getFilePointer();
}
}
*/
notimplemented();
return 0;
}
public void setvbuf(String mode, int size) {
nobuffer = "no".equals(mode);
}
// get length remaining to read
public int remaining() throws IOException {
return -1;
}
// peek ahead one character
public int peek() throws IOException {
if ( lookahead < 0 )
lookahead = is.read();
return lookahead;
}
// return char if read, -1 if eof, throw IOException on other exception
public int read() throws IOException {
if ( lookahead >= 0 ) {
int c = lookahead;
lookahead = -1;
return c;
}
if ( is != null )
return is.read();
notimplemented();
return 0;
}
// return length if fully read, -1 if eof, throws IOException
public int readFully(byte[] bytes, int offset, int length) throws IOException {
int n,i=0;
if (is!=null) {
if ( length > 0 && lookahead >= 0 ) {
bytes[offset] = (byte) lookahead;
lookahead = -1;
i += 1;
}
for ( ; i<length; ) {
n = is.read(bytes, offset+i, length-i);
if ( n < 0 )
return -1;
i += n;
}
} else {
notimplemented();
}
return length;
}
}
}

View File

@@ -21,7 +21,6 @@
******************************************************************************/ ******************************************************************************/
package org.luaj.lib.j2se; package org.luaj.lib.j2se;
import java.io.BufferedInputStream; import java.io.BufferedInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.OutputStream; import java.io.OutputStream;
@@ -29,12 +28,13 @@ import java.io.RandomAccessFile;
import org.luaj.lib.BaseLib; import org.luaj.lib.BaseLib;
import org.luaj.lib.IoLib; import org.luaj.lib.IoLib;
import org.luaj.vm.LNil;
import org.luaj.vm.LString; import org.luaj.vm.LString;
import org.luaj.vm.LTable; import org.luaj.vm.LTable;
import org.luaj.vm.LValue;
/**
* Implementation of the lua io library for J2se using RandomAccessFile
* to implement seek.
*/
public class J2seIoLib extends IoLib { public class J2seIoLib extends IoLib {
public static void install( LTable globals ) { public static void install( LTable globals ) {
@@ -53,26 +53,38 @@ public class J2seIoLib extends IoLib {
return new J2seIoLib(index); return new J2seIoLib(index);
} }
protected File openFile(String filename, String mode) throws IOException { protected File wrapStdin() throws IOException {
boolean isstdfile = "-".equals(filename); return new FileImpl(BaseLib.STDIN != null? BaseLib.STDIN: System.in);
boolean isreadmode = mode.startsWith("r"); }
if ( isstdfile )
return isreadmode? protected File wrapStdout() throws IOException {
new FileImpl(BaseLib.STDIN != null? BaseLib.STDIN: System.in): return new FileImpl(BaseLib.STDOUT != null? BaseLib.STDOUT: System.out);
new FileImpl(BaseLib.STDOUT != null? BaseLib.STDOUT: System.out); }
boolean isappend = mode.startsWith("a");
// TODO: handle update mode protected File openFile( String filename, boolean readMode, boolean appendMode, boolean updateMode, boolean binaryMode ) throws IOException {
// boolean isupdate = mode.endsWith("+"); RandomAccessFile f = new RandomAccessFile(filename,readMode? "r": "rw");
RandomAccessFile f = new RandomAccessFile(filename,isreadmode? "r": "rw"); if ( appendMode ) {
if ( isappend ) {
f.seek(f.length()); f.seek(f.length());
} else { } else {
if ( ! isreadmode ) if ( ! readMode )
f.setLength(0); f.setLength(0);
} }
return new FileImpl( f ); return new FileImpl( f );
} }
protected File openProgram(String prog, String mode) throws IOException {
final Process p = Runtime.getRuntime().exec(prog);
return "w".equals(mode)?
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") );
}
private static void notimplemented() { private static void notimplemented() {
throw new RuntimeException("not implemented"); throw new RuntimeException("not implemented");
} }
@@ -145,88 +157,54 @@ public class J2seIoLib extends IoLib {
public void setvbuf(String mode, int size) { public void setvbuf(String mode, int size) {
nobuffer = "no".equals(mode); nobuffer = "no".equals(mode);
} }
public LValue readBytes(int count) throws IOException {
if ( file != null ) { // get length remaining to read
byte[] b = new byte[count]; public int remaining() throws IOException {
file.readFully(b); return file!=null? (int) (file.length()-file.getFilePointer()): -1;
return new LString(b);
} }
notimplemented();
return LNil.NIL; // peek ahead one character
} public int peek() throws IOException {
public LValue readLine() throws IOException {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
int c;
while ( true ) {
if ( is != null ) {
c = is.read();
} else {
c = file.read();
}
if ( c < 0 || c == '\n' )
break;
baos.write(c);
}
return ( c < 0 && baos.size() == 0 )?
LNil.NIL:
new LString(baos.toByteArray());
}
public LValue readFile() throws IOException {
if ( file != null ) {
return readBytes((int) (file.length() - file.getFilePointer()));
}
notimplemented();
return null;
}
public Double readNumber() throws IOException {
if ( is == null && file == null )
notimplemented();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
readChars(" \t\r\n",null);
readChars("-+",baos);
//readChars("0",baos);
//readChars("xX",baos);
readChars("0123456789",baos);
readChars(".",baos);
readChars("0123456789",baos);
//readChars("eEfFgG",baos);
// readChars("+-",baos);
//readChars("0123456789",baos);
String s = baos.toString();
return s.length()>0? Double.valueOf(s): null;
}
private void readChars(String chars, ByteArrayOutputStream baos) throws IOException {
int c;
while ( true ) {
if ( is != null ) { if ( is != null ) {
is.mark(1); is.mark(1);
c = is.read(); int c = is.read();
} else {
c = file.read();
}
if ( chars.indexOf(c) < 0 ) {
if ( is != null )
is.reset(); is.reset();
else if ( file != null ) return c;
} else if ( file != null ) {
int c = file.read();
file.seek(file.getFilePointer()-1); file.seek(file.getFilePointer()-1);
return; return c;
}
if ( baos != null )
baos.write( c );
}
} }
notimplemented();
return 0;
} }
protected File openProgram(String prog, String mode) throws IOException { // return char if read, -1 if eof, throw IOException on other exception
final Process p = Runtime.getRuntime().exec(prog); public int read() throws IOException {
return "w".equals(mode)? if ( is != null )
new FileImpl( p.getOutputStream() ): return is.read();
new FileImpl( p.getInputStream() ); else if ( file != null ) {
return file.read();
}
notimplemented();
return 0;
} }
protected File tmpFile() throws IOException { // return length if fully read, -1 if eof, throws IOException
java.io.File f = java.io.File.createTempFile(".luaj","bin"); public int readFully(byte[] bytes, int offset, int length) throws IOException {
f.deleteOnExit(); if (file!=null) {
return new FileImpl( new RandomAccessFile(f,"rw") ); file.readFully(bytes, offset, length);
} else if (is!=null) {
for ( int i=0; i<length; i++, offset++ ) {
int c = is.read();
if ( c < 0 )
return -1;
bytes[offset++] = (byte) c;
}
} else {
notimplemented();
}
return length;
}
} }
} }

View File

@@ -0,0 +1,42 @@
package org.luaj.sample;
import javax.microedition.midlet.MIDlet;
import javax.microedition.midlet.MIDletStateChangeException;
import org.luaj.lib.j2me.Cldc10IoLib;
import org.luaj.platform.J2meMidp20Cldc11Platform;
import org.luaj.vm.LuaState;
import org.luaj.vm.Platform;
public class SampleMIDlet extends MIDlet {
// the script will be loaded as a resource
private static final String DEFAULT_SCRIPT = "test1.lua";
protected void startApp() throws MIDletStateChangeException {
// get the script as an app property
String script = this.getAppProperty("script");
if ( script == null )
script = DEFAULT_SCRIPT;
// set up the j2me platform. files will be loaded as resources
Platform.setInstance( new J2meMidp20Cldc11Platform(this) );
LuaState vm = Platform.newLuaState();
// extend the basic vm to include the compiler and io packages
org.luaj.compiler.LuaC.install();
Cldc10IoLib.install(vm._G);
// run the script
vm.getglobal( "dofile" );
vm.pushstring( script );
vm.call( 1, 0 );
}
protected void destroyApp(boolean arg0) throws MIDletStateChangeException {
}
protected void pauseApp() {
}
}

View File

@@ -11,6 +11,7 @@ checkallpass('io.close',{{f}})
checkallerrors('io.close',{notanil},'bad argument #1') checkallerrors('io.close',{notanil},'bad argument #1')
-- io.input ([file]) -- io.input ([file])
f = io.open("abc.txt","r")
checkallpass('io.input',{{nil,f,"abc.txt"}}) checkallpass('io.input',{{nil,f,"abc.txt"}})
checkallerrors('io.input',{nonstring},'bad argument #1') checkallerrors('io.input',{nonstring},'bad argument #1')

View File

@@ -24,8 +24,8 @@ for i,v in ipairs(t) do
print( string.format("%q",tostring(v)), type(v)) print( string.format("%q",tostring(v)), type(v))
end end
local h = io.open("abc.txt", "a") local h,s = io.open("abc.txt", "a")
print( 'h', io.type(h) ) print( 'h', io.type(h), s )
print( 'write', h:write('\nmore text\neven more text\n') ) print( 'write', h:write('\nmore text\neven more text\n') )
print( 'close', h:close() ) print( 'close', h:close() )
@@ -61,6 +61,12 @@ for l in io.lines() do
print( string.format('%q',l) ) print( string.format('%q',l) )
end end
local count = 0
io.tmpfile = function()
count = count + 1
return io.open("tmp"..count..".out","w")
end
local a = io.tmpfile() local a = io.tmpfile()
local b = io.tmpfile() local b = io.tmpfile()
print( io.type(a) ) print( io.type(a) )