diff --git a/README.html b/README.html index 7c9ff6bd..c64003d7 100644 --- a/README.html +++ b/README.html @@ -209,15 +209,11 @@ The following libraries are loaded by default in J2ME and J2SE platforms: The following libraries are optional, but preconfigured for some platforms and tools:
           
 	io
+	os
 	debug     
 	luajava 
 
-The following is not yet implemented: -
-	os
-
-

Optional Libraries

I/O Library

@@ -238,6 +234,21 @@ To install into your vm instance use (j2me only):

See the sample midlet int src/sample/SampleMIDlet for an example. +

OS Library

+A basic os library implementation for either J2ME or J2SE is provided ins +
+	src/core/org/luaj/lib/OsLib.java
+
+ +A slightly more complete version for J2SE is in: +
+	src/j2se/org/luaj/lib/j2se/J2seOsLib.java
+
+ +Time is a represented as number of milliseconds since the epoch, +and most time and date formatting, locales, and other features +are not implemented. +

Debug Library

The following library is optional:
diff --git a/src/core/org/luaj/lib/OsLib.java b/src/core/org/luaj/lib/OsLib.java
new file mode 100644
index 00000000..52c243eb
--- /dev/null
+++ b/src/core/org/luaj/lib/OsLib.java
@@ -0,0 +1,354 @@
+/*******************************************************************************
+* Copyright (c) 2009 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;
+
+import java.io.IOException;
+
+import org.luaj.vm.LFunction;
+import org.luaj.vm.LString;
+import org.luaj.vm.LTable;
+import org.luaj.vm.LValue;
+import org.luaj.vm.LuaState;
+
+/**
+ * Base implementation of OsLib, with simplified stub functions
+ * for library functions that cannot be implemented uniformly 
+ * on J2se and J2me.   
+ * 
+ * This can be installed as-is on either platform, or extended 
+ * and refined to be used in a complete J2se implementation. 
+ */
+public class OsLib extends LFunction {
+	public static String TMP_PREFIX    = ".luaj";
+	public static String TMP_SUFFIX    = "tmp";
+
+	/** 
+	 * Create a function stub with a specific index. 
+	 * This is a factory method so that subclasses can 
+	 * use install(LTable,OsLib) to install themselves.
+	 */
+	protected OsLib newInstance( int index ) {
+		return new OsLib(index);
+	}
+	
+	public static final String[] NAMES = {
+		"os",
+		"clock",
+		"date",
+		"difftime",
+		"execute",
+		"exit",
+		"getenv",
+		"remove",
+		"rename",
+		"setlocale",
+		"time",
+		"tmpname",
+	};
+	
+	private static final int INSTALL   = 0;
+	private static final int CLOCK     = 1;
+	private static final int DATE      = 2;
+	private static final int DIFFTIME  = 3;
+	private static final int EXECUTE   = 4;
+	private static final int EXIT      = 5;
+	private static final int GETENV    = 6;
+	private static final int REMOVE    = 7;
+	private static final int RENAME    = 8;
+	private static final int SETLOCALE = 9;
+	private static final int TIME      = 10;
+	private static final int TMPNAME   = 11;
+
+	/** 
+	 * Install the os library using the OsLib class as a factory 
+	 */
+	public static void install( LTable globals ) {
+		install(globals, new OsLib());
+	}
+	
+	/** 
+	 * Initialize the os libaray using the subblied instance as a factory.
+	 * @param factory instance to use as a factory for function stubs.  
+	 */
+	public static void install( LTable globals, OsLib factory ) {
+		LTable os = new LTable();
+		for ( int i=CLOCK; i<=TMPNAME; i++ )
+			os.put(NAMES[i], factory.newInstance(i));
+		globals.put( "os", os );
+		PackageLib.setIsLoaded("os", os);
+	}
+
+	private static final long t0 = System.currentTimeMillis();
+	private static long tmpnames = t0;
+
+	private final int id;
+
+	protected OsLib() {
+		id = 0;
+	}
+
+	protected OsLib( int id ) {
+		this.id = id;
+	}
+
+	public LString luaAsString() {
+		return new LString(toJavaString());
+	}
+	
+	public String toJavaString() {
+		return "os."+toString();
+	}
+
+	public String toString() {
+		return NAMES[id]+"()";
+	}
+
+	public boolean luaStackCall( LuaState vm ) {
+		LValue v;
+		long t,t2;
+		int c;
+		String s;
+		try {
+			switch ( id ) {
+				case INSTALL:
+					install(vm._G, this);
+					break;
+				case CLOCK:
+					vm.resettop();
+					vm.pushnumber(clock());
+					break;
+				case DATE:
+					s = vm.optstring(2, null);
+					t = vm.optlong(3,-1);
+					vm.resettop();
+					vm.pushlvalue( date(s, t==-1? System.currentTimeMillis(): t) );
+					break;
+				case DIFFTIME:
+					t2 = vm.checklong(2);
+					t = vm.checklong(3);
+					vm.resettop();
+					vm.pushnumber(difftime(t2,t));
+					break;
+				case EXECUTE:
+					c = execute(vm.optstring(2, null));
+					vm.resettop();
+					vm.pushinteger(c);
+					break;
+				case EXIT:
+					exit(vm.optint(2, 0));
+					break;
+				case GETENV:
+					s = getenv(vm.checkstring(2));
+					vm.resettop();
+					vm.pushstring(s);
+					break;
+				case REMOVE:
+					remove(vm.checkstring(2));
+					vm.resettop();
+					vm.pushboolean(true);
+					break;
+				case RENAME:
+					rename(vm.checkstring(2), vm.checkstring(3));
+					vm.resettop();
+					vm.pushboolean(true);
+					break;
+				case SETLOCALE:
+					s = setlocale(vm.optstring(2,null), vm.optstring(3, "all"));
+					vm.resettop();
+					if ( s != null )
+						vm.pushstring(s);
+					else
+						vm.pushnil();
+					break;
+				case TIME:
+					t = time(vm.isnoneornil(2)? null: vm.checktable(2));
+					vm.resettop();
+					vm.pushnumber(t);
+					break;
+				case TMPNAME:
+					vm.resettop();
+					vm.pushstring(tmpname());
+					break;
+				default:
+					LuaState.vmerror( "bad os id" );
+			}
+		} catch ( IOException e ) {
+			vm.resettop();
+			vm.pushnil();
+			vm.pushstring(e.getMessage());
+		}
+		return false;
+	}
+
+	/**
+	 * @return an approximation of the amount in seconds of CPU time used by 
+	 * the program.
+	 */
+	protected double clock() {
+		return (System.currentTimeMillis()-t0) / 1000.;
+	}
+
+	/**
+	 * Returns the number of seconds from time t1 to time t2. 
+	 * In POSIX, Windows, and some other systems, this value is exactly t2-t1.
+	 * @param t2
+	 * @param t1
+	 * @return diffeence in time values, in seconds
+	 */
+	protected double difftime(long t2, long t1) {
+		return (t2 - t1) / 1000.;
+	}
+
+	/**
+	 * If the time argument is present, this is the time to be formatted 
+	 * (see the os.time function for a description of this value). 
+	 * Otherwise, date formats the current time.
+	 * 
+	 * If format starts with '!', then the date is formatted in Coordinated 
+	 * Universal Time. After this optional character, if format is the string 
+	 * "*t", then date returns a table with the following fields: year 
+	 * (four digits), month (1--12), day (1--31), hour (0--23), min (0--59), 
+	 * sec (0--61), wday (weekday, Sunday is 1), yday (day of the year), 
+	 * and isdst (daylight saving flag, a boolean).
+	 * 
+	 * If format is not "*t", then date returns the date as a string, 
+	 * formatted according to the same rules as the C function strftime.
+	 * 
+	 * When called without arguments, date returns a reasonable date and 
+	 * time representation that depends on the host system and on the 
+	 * current locale (that is, os.date() is equivalent to os.date("%c")).
+	 *  
+	 * @param format 
+	 * @param time time since epoch, or -1 if not supplied
+	 * @return a LString or a LTable containing date and time, 
+	 * formatted according to the given string format.
+	 */
+	protected LValue date(String format, long time) {
+		return LString.valueOf( new java.util.Date(time).toString() );
+	}
+
+	/** 
+	 * This function is equivalent to the C function system. 
+	 * It passes command to be executed by an operating system shell. 
+	 * It returns a status code, which is system-dependent. 
+	 * If command is absent, then it returns nonzero if a shell 
+	 * is available and zero otherwise.
+	 * @param command command to pass to the system
+	 */ 
+	protected int execute(String command) {
+		return 0;
+	}
+
+	/**
+	 * Calls the C function exit, with an optional code, to terminate the host program. 
+	 * @param code
+	 */
+	protected void exit(int code) {
+		System.exit(code);
+	}
+
+	/**
+	 * Returns the value of the process environment variable varname, 
+	 * or null if the variable is not defined. 
+	 * @param varname
+	 * @return
+	 */
+	protected String getenv(String varname) {
+		return System.getProperty(varname);
+	}
+
+	/**
+	 * Deletes the file or directory with the given name. 
+	 * Directories must be empty to be removed. 
+	 * If this function fails, it throws and IOException
+	 *  
+	 * @param filename
+	 * @throws IOException if it fails
+	 */
+	protected void remove(String filename) throws IOException {
+		throw new IOException( "not implemented" );
+	}
+
+	/**
+	 * Renames file or directory named oldname to newname. 
+	 * If this function fails,it throws and IOException
+	 *  
+	 * @param oldname old file name
+	 * @param newname new file name
+	 * @throws IOException if it fails
+	 */
+	protected void rename(String oldname, String newname) throws IOException {
+		throw new IOException( "not implemented" );
+	}
+
+	/**
+	 * Sets the current locale of the program. locale is a string specifying 
+	 * a locale; category is an optional string describing which category to change: 
+	 * "all", "collate", "ctype", "monetary", "numeric", or "time"; the default category 
+	 * is "all". 
+	 * 
+	 * If locale is the empty string, the current locale is set to an implementation-
+	 * defined native locale. If locale is the string "C", the current locale is set 
+	 * to the standard C locale.
+	 * 
+	 * When called with null as the first argument, this function only returns the 
+	 * name of the current locale for the given category.
+	 *  
+	 * @param locale
+	 * @param category
+	 * @return the name of the new locale, or null if the request 
+	 * cannot be honored.
+	 */
+	protected String setlocale(String locale, String category) {
+		return null;
+	}
+
+	/**
+	 * Returns the current time when called without arguments, 
+	 * or a time representing the date and time specified by the given table. 
+	 * This table must have fields year, month, and day, 
+	 * and may have fields hour, min, sec, and isdst 
+	 * (for a description of these fields, see the os.date function).
+	 * @param table
+	 * @return long value for the time
+	 */
+	protected long time(LTable table) {
+		return System.currentTimeMillis();
+	}
+
+	/**
+	 * Returns a string with a file name that can be used for a temporary file. 
+	 * The file must be explicitly opened before its use and explicitly removed 
+	 * when no longer needed.
+	 * 
+	 * On some systems (POSIX), this function also creates a file with that name, 
+	 * to avoid security risks. (Someone else might create the file with wrong 
+	 * permissions in the time between getting the name and creating the file.) 
+	 * You still have to open the file to use it and to remove it (even if you 
+	 * do not use it). 
+	 * 
+	 * @return String filename to use
+	 */
+	protected String tmpname() {
+		return TMP_PREFIX+(tmpnames++)+TMP_SUFFIX;
+	}
+}
diff --git a/src/j2se/org/luaj/lib/j2se/J2seOsLib.java b/src/j2se/org/luaj/lib/j2se/J2seOsLib.java
new file mode 100644
index 00000000..9860da22
--- /dev/null
+++ b/src/j2se/org/luaj/lib/j2se/J2seOsLib.java
@@ -0,0 +1,84 @@
+/*******************************************************************************
+* Copyright (c) 2009 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.j2se;
+import java.io.File;
+import java.io.IOException;
+
+import org.luaj.lib.OsLib;
+
+/**
+ * Implementation of the lua os library for J2se    
+ */
+public class J2seOsLib extends OsLib {
+	public static int EXEC_IOEXCEPTION = -1;
+	public static int EXEC_INTERRUPTED = -2;
+	public static int EXEC_ERROR       = -3;
+	
+	public J2seOsLib() {
+		super();
+	}
+
+	protected OsLib newInstance(int index) {
+		return new J2seOsLib(index);
+	}
+
+	private J2seOsLib(int id) {
+		super(id);
+	}
+
+	protected int execute(String command) {
+		Runtime r = Runtime.getRuntime();
+		try {
+			final Process p = r.exec(command);
+			try {
+				p.waitFor();
+				return p.exitValue();
+			} finally {
+				p.destroy();
+			}
+		} catch (IOException ioe) {
+			return EXEC_IOEXCEPTION;
+		} catch (InterruptedException e) {
+			return EXEC_INTERRUPTED;
+		} catch (Throwable t) {
+			return EXEC_ERROR;
+		}		
+	}
+
+	protected void remove(String filename) throws IOException {
+		new File(filename).delete();
+	}
+
+	protected void rename(String oldname, String newname) throws IOException {
+		new File(oldname).renameTo(new File(newname));
+	}
+
+	protected String tmpname() {
+		try {
+			java.io.File f = java.io.File.createTempFile(TMP_PREFIX ,TMP_SUFFIX);
+			return f.getName();
+		} catch ( IOException ioe ) {
+			return super.tmpname();
+		}
+	}
+	
+}
diff --git a/src/test/res/oslib.lua b/src/test/res/oslib.lua
new file mode 100644
index 00000000..d47c5f5f
--- /dev/null
+++ b/src/test/res/oslib.lua
@@ -0,0 +1,36 @@
+-- simple os-library tests
+-- these can't really be compared to C meaningfully, 
+-- because they are so highly os-dependent.
+local lib = "org.luaj.lib.j2se.J2seOsLib"
+-- local lib = "org.luaj.lib.OsLib"
+print( 'require "'..lib..'"', pcall( require, lib ) )
+print( 'os', os ~= nil )
+print( 'os.clock()', pcall( os.clock ) )
+print( 'os.date()', pcall( os.date ) )
+print( 'os.difftime(123000, 21250)', pcall( os.difftime, 123000, 21250 ) )
+print( 'os.execute("ipconfig")', pcall( os.execute, 'ipconfig' ) )
+print( 'os.execute("")', pcall( os.execute, '' ) )
+print( 'os.getenv()', pcall( os.getenv ) )
+print( 'os.getenv("bogus.key")', pcall( os.getenv, 'bogus.key' ) )
+print( 'os.getenv("java.runtime.version")', pcall( os.getenv, 'java.runtime.version' ) )
+local s,p = pcall( os.tmpname )
+local s,q = pcall( os.tmpname )
+print( 'os.tmpname()', s, p )
+print( 'os.tmpname()', s, q )
+print( 'os.remove(p)', pcall( os.remove, p ) )
+print( 'os.rename(p,q)', pcall( os.rename, p, q ) )
+local s,f = pcall( io.open, p,"w" )
+print( 'io.open', s, f )
+print( 'write', pcall( f.write, f, "abcdef 12345" ) )
+print( 'close', pcall( f.close, f ) )
+print( 'os.rename(p,q)', pcall( os.rename, p, q ) )
+print( 'os.remove(q)', pcall( os.remove, q ) )
+print( 'os.remove(q)', pcall( os.remove, q ) )
+print( 'os.setlocale()', pcall( os.setlocale ) )
+print( 'os.setlocale("jp")', pcall( os.setlocale, "jp" ) )
+print( 'os.setlocale("us","monetary")', pcall( os.setlocale, "us", "monetary" ) )
+print( 'os.setlocale(nil,"all")', pcall( os.setlocale, nil, "all" ) )
+print( 'os.setlocale("c")', pcall( os.setlocale, "c" ) )
+print( 'os.exit(123)' )
+print( pcall( os.exit, -123 ) )
+print( 'failed to exit' )