/*
 * Runtime.java
 * 
 * Based on the specification of java.lang.Runtime in the JDK 1.4.1.
 * Within OVM runtime lifecycle functions are simply forwarded to the 
 * underlying JavaVirtualMachine instance.
 *
 * NOTE: No Process/exec functionality is present as yet.
 * We are initially dealing only with VM lifecycle issues.
 *
 * We copy the necessary functionality from the GNU Classpath Runtime class
 * so that we fit in with the rest of Classpath.
 */
package java.lang;

import java.lang.IllegalArgumentException;
import java.io.File;
import java.io.InputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Properties;
import java.util.Set;
import java.util.StringTokenizer;

/**
 * Every Java application has a single instance of class 
 * <code>Runtime</code> that allows the application to interface with 
 * the environment in which the application is running. The current 
 * runtime can be obtained from the <code>getRuntime</code> method. 
 * <p>
 * An application cannot create its own instance of this class. 
 *
 * <h3>OVM Notes</h3>
 * <p>The bootstrap initialization sequence is quite fragile. 
 * Classpath requires
 * that System be initialized first, which in turn initialises Runtime - which
 * means that nothing used in the initialisation of Runtime or any dependent
 * classes can require that System be initialized - which primarily means don't
 * try and access System properties. This means no use of Strings in a way
 * that requires the EncodingManager to be used, and no throwing of exceptions
 * at all. If an exception does occur then there will be a secondary exception
 * that causes the OVM to abort.
 *
 * <h4>To-Do (Maybe)</h4>
 * <p>Implement library/file loading
 * <p>Finish filling in system properties properly
 * <p>Process execution
 *
 * @author  David Holmes
 * @since   JDK1.0
 */
public class Runtime {


    /**
     * The library path, to search when loading libraries. 
     */
    private final String[] libpath;

    /**
     * The current security manager. This is located here instead of in
     * System, to avoid security problems, as well as bootstrap issues.
     * Make sure to access it in a thread-safe manner; it is package visible
     * to avoid overhead in java.lang.
     */
    static volatile SecurityManager securityManager;

    /**
     * The default properties defined by the system. This is likewise located
     * here instead of in System, to avoid bootstrap issues; it is package
     * visible to avoid overhead in java.lang. Note that System will add a
     * few more properties to this collection, but that after that, it is
     * treated as read-only.
     *
     */
    static Properties defaultProperties = new Properties();

    static {
        insertSystemProperties(defaultProperties);
    }


    /** 
     * The actual singleton instance of the runtime. 
     * Must be initialized after the default properties
     */
    private static final Runtime currentRuntime = new Runtime();

    /**
     * Returns the runtime object associated with the current Java application.
     * Most of the methods of class <code>Runtime</code> are instance 
     * methods and must be invoked with respect to the current runtime object. 
     * 
     * @return  the <code>Runtime</code> object associated with the current
     *          Java application.
     */
    public static Runtime getRuntime() { 
	return currentRuntime;
    }

    /* don't let anyone instantiate this class  */
    private Runtime() { 
        // Using defaultProperties directly avoids a security check, as well
        // as bootstrap issues (since System is not initialized yet).
        String path = defaultProperties.getProperty("java.library.path", ".");
        String pathSep = defaultProperties.getProperty("path.separator", ":");
        String fileSep = defaultProperties.getProperty("file.separator", "/");
	
        StringTokenizer t = new StringTokenizer(path, pathSep);
        libpath = new String[t.countTokens()];
        for (int i = 0; i < libpath.length; i++) {
            String prefix = t.nextToken();
            if (! prefix.endsWith(fileSep)) {
                prefix += fileSep;
            }
            libpath[i] = prefix;
        }
    }

    /**
     * Terminates the currently running Java virtual machine by initiating its
     * shutdown sequence.  This method never returns normally.  The argument
     * serves as a status code; by convention, a nonzero status code indicates
     * abnormal termination.
     *
     * <p> The virtual machine's shutdown sequence constists of two phases.  In
     * the first phase all registered {@link #addShutdownHook shutdown hooks},
     * if any, are started in some unspecified order and allowed to run
     * concurrently until they finish.  In the second phase all uninvoked
     * finalizers are run if {@link #runFinalizersOnExit finalization-on-exit}
     * has been enabled.  Once this is done the virtual machine {@link #halt
     * halts}.
     *
     * <p> If this method is invoked after the virtual machine has begun its
     * shutdown sequence then if shutdown hooks are being run this method will
     * block indefinitely.  If shutdown hooks have already been run and on-exit
     * finalization has been enabled then this method halts the virtual machine
     * with the given status code if the status is nonzero; otherwise, it
     * blocks indefinitely.
     *
     * <p> The <tt>{@link System#exit(int) System.exit}</tt> method is the
     * conventional and convenient means of invoking this method. <p>
     *
     * @param  status
     *         Termination status.  By convention, a nonzero status code
     *         indicates abnormal termination.
     *
     * @throws SecurityException
     *         If a security manager is present and its <tt>{@link
     *         SecurityManager#checkExit checkExit}</tt> method does not permit
     *         exiting with the specified status
     *
     * @see java.lang.SecurityException
     * @see java.lang.SecurityManager#checkExit(int)
     * @see #addShutdownHook
     * @see #removeShutdownHook
     * @see #runFinalizersOnExit
     * @see #halt(int)
     */
    public void exit(int status) {
  	SecurityManager security = System.getSecurityManager();
  	if (security != null) {
  	    security.checkExit(status);
  	}
        // debugging/testing help for 'silent' terminations
        rawPrint(EXIT_CALLED);
//        new Error().printStackTrace(System.out);
	JavaVirtualMachine.getInstance().exit(status);
    }

    // statically allocated so that no allocation is needed to use this at
    // runtime
    static final String EXIT_CALLED = "Runtime.exit() was invoked\n";

    /**
     * Helper method to print raw string charaters as bytes directly with no
     * conversion. This is for use in situations where we think any Library
     * call could fail - eg due to memory exhaustion.
     */
     void rawPrint(String msg) {
         int len = msg.length();
         for(int i = 0; i < len; i++)
             LibraryImports.printCharAsByte(msg.charAt(i));
     }


    /**
     * Registers a new virtual-machine shutdown hook.
     *
     * <p> The Java virtual machine <i>shuts down</i> in response to two kinds
     * of events:
     *
     *   <ul>
     *
     *   <p> <li> The program <i>exits</i> normally, when the last non-daemon
     *   thread exits or when the <tt>{@link #exit exit}</tt> (equivalently,
     *   <tt>{@link System#exit(int) System.exit}</tt>) method is invoked, or
     *
     *   <p> <li> The virtual machine is <i>terminated</i> in response to a
     *   user interrupt, such as typing <tt>^C</tt>, or a system-wide event,
     *   such as user logoff or system shutdown.
     *
     *   </ul>
     *
     * <p> A <i>shutdown hook</i> is simply an initialized but unstarted
     * thread.  When the virtual machine begins its shutdown sequence it will
     * start all registered shutdown hooks in some unspecified order and let
     * them run concurrently.  When all the hooks have finished it will then
     * run all uninvoked finalizers if finalization-on-exit has been enabled.
     * Finally, the virtual machine will halt.  Note that daemon threads will
     * continue to run during the shutdown sequence, as will non-daemon threads
     * if shutdown was initiated by invoking the <tt>{@link #exit exit}</tt>
     * method.
     *
     * <p> Once the shutdown sequence has begun it can be stopped only by
     * invoking the <tt>{@link #halt halt}</tt> method, which forcibly
     * terminates the virtual machine.
     *
     * <p> Once the shutdown sequence has begun it is impossible to register a
     * new shutdown hook or de-register a previously-registered hook.
     * Attempting either of these operations will cause an
     * <tt>{@link IllegalStateException}</tt> to be thrown.
     *
     * <p> Shutdown hooks run at a delicate time in the life cycle of a virtual
     * machine and should therefore be coded defensively.  They should, in
     * particular, be written to be thread-safe and to avoid deadlocks insofar
     * as possible.  They should also not rely blindly upon services that may
     * have registered their own shutdown hooks and therefore may themselves in
     * the process of shutting down.
     *
     * <p> Shutdown hooks should also finish their work quickly.  When a
     * program invokes <tt>{@link #exit exit}</tt> the expectation is
     * that the virtual machine will promptly shut down and exit.  When the
     * virtual machine is terminated due to user logoff or system shutdown the
     * underlying operating system may only allow a fixed amount of time in
     * which to shut down and exit.  It is therefore inadvisable to attempt any
     * user interaction or to perform a long-running computation in a shutdown
     * hook.
     *
     * <p> Uncaught exceptions are handled in shutdown hooks just as in any
     * other thread, by invoking the <tt>{@link ThreadGroup#uncaughtException
     * uncaughtException}</tt> method of the thread's <tt>{@link
     * ThreadGroup}</tt> object.  The default implementation of this method
     * prints the exception's stack trace to <tt>{@link System#err}</tt> and
     * terminates the thread; it does not cause the virtual machine to exit or
     * halt.
     *
     * <p> In rare circumstances the virtual machine may <i>abort</i>, that is,
     * stop running without shutting down cleanly.  This occurs when the
     * virtual machine is terminated externally, for example with the
     * <tt>SIGKILL</tt> signal on Unix or the <tt>TerminateProcess</tt> call on
     * Win32.  The virtual machine may also abort if a native method goes awry
     * by, for example, corrupting internal data structures or attempting to
     * access nonexistent memory.  If the virtual machine aborts then no
     * guarantee can be made about whether or not any shutdown hooks will be
     * run. <p>
     *
     * @param   hook
     *          An initialized but unstarted <tt>{@link Thread}</tt> object
     *
     * @throws  IllegalArgumentException
     *          If the specified hook has already been registered,
     *          or if it can be determined that the hook is already running or
     *          has already been run
     *
     * @throws  IllegalStateException
     *          If the virtual machine is already in the process
     *          of shutting down
     *
     * @throws  SecurityException
     *          If a security manager is present and it denies
     *          <tt>{@link RuntimePermission}("shutdownHooks")</tt>
     *
     * @see #removeShutdownHook
     * @see #halt(int)
     * @see #exit(int)
     * @since 1.3
     */
    public void addShutdownHook(Thread hook) {
  	SecurityManager sm = System.getSecurityManager();
  	if (sm != null) {
  	    sm.checkPermission(new RuntimePermission("shutdownHooks"));
  	}
        // note that there is nothing to prevent the hook from being executed
        // prior to the shutdown sequence.
	JavaVirtualMachine.getInstance().addShutdownHook(hook);
    }

    /**
     * De-registers a previously-registered virtual-machine shutdown hook. <p>
     *
     * @param hook the hook to remove
     * @return <tt>true</tt> if the specified hook had previously been
     * registered and was successfully de-registered, <tt>false</tt>
     * otherwise.
     *
     * @throws  IllegalStateException
     *          If the virtual machine is already in the process of shutting
     *          down
     *
     * @throws  SecurityException
     *          If a security manager is present and it denies
     *          <tt>{@link RuntimePermission}("shutdownHooks")</tt>
     *
     * @see #addShutdownHook
     * @see #exit(int)
     * @since 1.3
     */
    public boolean removeShutdownHook(Thread hook) {
  	SecurityManager sm = System.getSecurityManager();
  	if (sm != null) {
  	    sm.checkPermission(new RuntimePermission("shutdownHooks"));
  	}
	return JavaVirtualMachine.getInstance().removeShutdownHook(hook);
    }

    /**
     * Forcibly terminates the currently running Java virtual machine.  This
     * method never returns normally.
     *
     * <p> This method should be used with extreme caution.  Unlike the
     * <tt>{@link #exit exit}</tt> method, this method does not cause shutdown
     * hooks to be started and does not run uninvoked finalizers if
     * finalization-on-exit has been enabled.  If the shutdown sequence has
     * already been initiated then this method does not wait for any running
     * shutdown hooks or finalizers to finish their work. <p>
     *
     * @param  status
     *         Termination status.  By convention, a nonzero status code
     *         indicates abnormal termination.  If the <tt>{@link Runtime#exit
     *         exit}</tt> (equivalently, <tt>{@link System#exit(int)
     *         System.exit}</tt>) method has already been invoked then this
     *         status code will override the status code passed to that method.
     *
     * @throws SecurityException
     *         If a security manager is present and its <tt>{@link
     *         SecurityManager#checkExit checkExit}</tt> method does not permit
     *         an exit with the specified status
     *
     * @see #exit
     * @see #addShutdownHook
     * @see #removeShutdownHook
     * @since 1.3
     */
    public void halt(int status) {
  	SecurityManager sm = System.getSecurityManager();
  	if (sm != null) {
  	    sm.checkExit(status);
  	}
	JavaVirtualMachine.getInstance().halt(status);
    }

    /**
     * Enable or disable finalization on exit; doing so specifies that the
     * finalizers of all objects that have finalizers that have not yet been
     * automatically invoked are to be run before the Java runtime exits.
     * By default, finalization on exit is disabled.
     * 
     * <p>If there is a security manager, 
     * its <code>checkExit</code> method is first called
     * with 0 as its argument to ensure the exit is allowed. 
     * This could result in a SecurityException.
     *
     * @param value true to enable finalization on exit, false to disable
     * @deprecated  This method is inherently unsafe.  It may result in
     * 	    finalizers being called on live objects while other threads are
     *      concurrently manipulating those objects, resulting in erratic
     *	    behavior or deadlock.
     * 
     * @throws  SecurityException
     *        if a security manager exists and its <code>checkExit</code> 
     *        method doesn't allow the exit.
     *
     * @see     java.lang.Runtime#exit(int)
     * @see     java.lang.SecurityManager#checkExit(int)
     * @since   JDK1.1
     */
    public static void runFinalizersOnExit(boolean value) {
  	SecurityManager security = System.getSecurityManager();
  	if (security != null) {
  	    try {
  		security.checkExit(0); 
  	    } catch (SecurityException e) {
  		throw new SecurityException("runFinalizersOnExit");
  	    }
  	}
	JavaVirtualMachine.getInstance().runFinalizersOnExit(value);
    }

    /**
     * Executes the specified string command in a separate process. 
     * <p>
     * The <code>command</code> argument is parsed into tokens and then 
     * executed as a command in a separate process. The token parsing is 
     * done by a {@link java.util.StringTokenizer} created by the call:
     * <blockquote><pre>
     * new StringTokenizer(command)
     * </pre></blockquote> 
     * with no further modifications of the character categories. 
     * This method has exactly the same effect as 
     * <code>exec(command, null)</code>. 
     *
     * @param      command   a specified system command.
     * @return     a <code>Process</code> object for managing the subprocess.
     * @exception  SecurityException  if a security manager exists and its  
     *             <code>checkExec</code> method doesn't allow creation of a subprocess.
     * @exception  IOException if an I/O error occurs
     * @see        java.lang.Runtime#exec(java.lang.String, java.lang.String[])
     * @see     java.lang.SecurityManager#checkExec(java.lang.String)
     */
    public Process exec(String command) throws IOException {
	return exec(command, null, null);
    }

    /**
     * Executes the specified string command in a separate process with the 
     * specified environment. 
     * <p>
     * This method breaks the <code>command</code> string into tokens and 
     * creates a new array <code>cmdarray</code> containing the tokens in the 
     * order that they were produced by the string tokenizer; it 
     * then performs the call <code>exec(cmdarray, envp)</code>. The token
     * parsing is done by a {@link java.util.StringTokenizer} created by 
     * the call: 
     * <blockquote><pre>
     * new StringTokenizer(command)
     * </pre></blockquote>
     * with no further modification of the character categories. 
     *
     * <p>
     * The environment variable settings are specified by <tt>envp</tt>.
     * If <tt>envp</tt> is <tt>null</tt>, the subprocess inherits the 
     * environment settings of the current process.
     *
     * @param      cmd       a specified system command.
     * @param      envp      array of strings, each element of which 
     *                       has environment variable settings in format
     *                       <i>name</i>=<i>value</i>.
     * @return     a <code>Process</code> object for managing the subprocess.
     * @exception  SecurityException  if a security manager exists and its  
     *             <code>checkExec</code> method doesn't allow creation of a subprocess.
     * @exception  IOException if an I/O error occurs
     * @see        java.lang.Runtime#exec(java.lang.String[])
     * @see        java.lang.Runtime#exec(java.lang.String[], java.lang.String[])
     * @see        java.lang.SecurityManager#checkExec(java.lang.String)
     */
    public Process exec(String cmd, String envp[]) throws IOException {
        return exec(cmd, envp, null);
    }
    
    /**
     * Executes the specified string command in a separate process with the
     * specified environment and working directory. 
     * <p>
     * This method breaks the <code>command</code> string into tokens and 
     * creates a new array <code>cmdarray</code> containing the tokens in the 
     * order that they were produced by the string tokenizer; it 
     * then performs the call <code>exec(cmdarray, envp)</code>. The token
     * parsing is done by a {@link java.util.StringTokenizer} created by 
     * the call: 
     * <blockquote><pre>
     * new StringTokenizer(command)
     * </pre></blockquote>
     * with no further modification of the character categories. 
     *
     * <p>
     * The environment variable settings are specified by <tt>envp</tt>.
     * If <tt>envp</tt> is <tt>null</tt>, the subprocess inherits the 
     * environment settings of the current process.
     *
     * <p>
     * The working directory of the new subprocess is specified by <tt>dir</tt>.
     * If <tt>dir</tt> is <tt>null</tt>, the subprocess inherits the 
     * current working directory of the current process.
     *
     * @param      command   a specified system command.
     * @param      envp      array of strings, each element of which 
     *                       has environment variable settings in format
     *                       <i>name</i>=<i>value</i>.
     * @param      dir       the working directory of the subprocess, or
     *                       <tt>null</tt> if the subprocess should inherit
     *                       the working directory of the current process.
     * @return     a <code>Process</code> object for managing the subprocess.
     * @exception  SecurityException  if a security manager exists and its  
     *             <code>checkExec</code> method doesn't allow creation of a subprocess.
     * @exception  IOException if an I/O error occurs
     * @see        java.lang.Runtime#exec(java.lang.String[], java.lang.String[], File)
     * @see        java.lang.SecurityManager#checkExec(java.lang.String)
     */
    public Process exec(String command, String envp[], File dir) 
        throws IOException {
 	StringTokenizer st = new StringTokenizer(command);
	String[] cmd = new String[st.countTokens()];
        for (int i = 0; i < cmd.length; i++)
            cmd[i] = st.nextToken();
        return exec(cmd, envp, dir);
    }

    /**
     * Executes the specified command and arguments in a separate process.
     * <p>
     * The command specified by the tokens in <code>cmdarray</code> is 
     * executed as a command in a separate process. This has exactly the 
     * same effect as <code>exec(cmdarray, null)</code>. 
     * <p>
     * If there is a security manager, its <code>checkExec</code> 
     * method is called with the first component of the array 
     * <code>cmdarray</code> as its argument. This may result in a security 
     * exception. 
     *
     * @param      cmdarray   array containing the command to call and
     *                        its arguments.
     * @return     a <code>Process</code> object for managing the subprocess.
     * @exception  SecurityException  if a security manager exists and its  
     *             <code>checkExec</code> method doesn't allow creation of a subprocess.
     * @exception  IOException if an I/O error occurs
     * @see        java.lang.Runtime#exec(java.lang.String[], java.lang.String[])
     * @see        java.lang.SecurityManager#checkExec(java.lang.String)
     */
    public Process exec(String cmdarray[]) throws IOException {
	return exec(cmdarray, null, null);
    }

    /**
     * Executes the specified command and arguments in a separate process
     * with the specified environment. 
     * <p>
     * Given an array of strings <code>cmdarray</code>, representing the 
     * tokens of a command line, and an array of strings <code>envp</code>, 
     * representing "environment" variable settings, this method creates 
     * a new process in which to execute the specified command. 
     *
     * <p>
     * If <tt>envp</tt> is <tt>null</tt>, the subprocess inherits the 
     * environment settings of the current process.
     *
     * @param      cmdarray   array containing the command to call and
     *                        its arguments.
     * @param      envp       array of strings, each element of which 
     *                        has environment variable settings in format
     *                        <i>name</i>=<i>value</i>.
     * @return     a <code>Process</code> object for managing the subprocess.
     * @exception  SecurityException  if a security manager exists and its  
     *             <code>checkExec</code> method doesn't allow creation of a subprocess.
     * @exception  NullPointerException if <code>cmdarray</code> is 
     *             <code>null</code>.
     * @exception  IndexOutOfBoundsException if <code>cmdarray</code> is an 
     *             empty array (has length <code>0</code>).
     * @exception  IOException if an I/O error occurs
     * @see     java.lang.Process
     * @see     java.lang.SecurityException
     * @see     java.lang.SecurityManager#checkExec(java.lang.String)
     */
    public Process exec(String cmdarray[], String envp[]) throws IOException {
	return exec(cmdarray, envp, null);
    }


    /**
     * Executes the specified command and arguments in a separate process with
     * the specified environment and working directory. 
     * <p>
     * If there is a security manager, its <code>checkExec</code> 
     * method is called with the first component of the array 
     * <code>cmdarray</code> as its argument. This may result in a security 
     * exception. 
     * <p>
     * Given an array of strings <code>cmdarray</code>, representing the 
     * tokens of a command line, and an array of strings <code>envp</code>, 
     * representing "environment" variable settings, this method creates 
     * a new process in which to execute the specified command. 
     *
     * <p>
     * If <tt>envp</tt> is <tt>null</tt>, the subprocess inherits the 
     * environment settings of the current process.
     *
     * <p>
     * The working directory of the new subprocess is specified by <tt>dir</tt>.
     * If <tt>dir</tt> is <tt>null</tt>, the subprocess inherits the 
     * current working directory of the current process.
     *
     *
     * @param      cmdarray   array containing the command to call and
     *                        its arguments.
     * @param      envp       array of strings, each element of which 
     *                        has environment variable settings in format
     *                        <i>name</i>=<i>value</i>.
     * @param      dir        the working directory of the subprocess, or
     *                        <tt>null</tt> if the subprocess should inherit
     *                        the working directory of the current process.
     * @return     a <code>Process</code> object for managing the subprocess.
     * @exception  SecurityException  if a security manager exists and its  
     *             <code>checkExec</code> method doesn't allow creation of a 
     *             subprocess.
     * @exception  NullPointerException if <code>cmdarray</code> is 
     *             <code>null</code>.
     * @exception  IndexOutOfBoundsException if <code>cmdarray</code> is an 
     *             empty array (has length <code>0</code>).
     * @exception  IOException if an I/O error occurs.
     * @see     java.lang.Process
     * @see     java.lang.SecurityException
     * @see     java.lang.SecurityManager#checkExec(java.lang.String)
     */
    public Process exec(String cmdarray[], String envp[], File dir) 
	throws IOException {	
	cmdarray = (String[])cmdarray.clone();
	envp = (envp != null ? (String[])envp.clone() : null);

        if (cmdarray.length == 0) {
            throw new IndexOutOfBoundsException();            
        }
        for (int i = 0; i < cmdarray.length; i++) {
            if (cmdarray[i] == null) {
                throw new NullPointerException();
            }
        }
        if (envp != null) {
            for (int i = 0; i < envp.length; i++) {
                if (envp[i] == null) {
                    throw new NullPointerException();
                }
            }
        }
	SecurityManager security = securityManager;
	if (security != null) {
	    security.checkExec(cmdarray[0]);
	}
	return execInternal(cmdarray, envp, dir);
    }

    private Process execInternal(String[] cmd, String[] envp, File dir) {
        throw new NoSuchMethodError("exec not supported");
    }

    /**
     * Returns the number of processors available to the Java virtual machine.
     *
     * <p> This value may change during a particular invocation of the virtual
     * machine.  Applications that are sensitive to the number of available
     * processors should therefore occasionally poll this property and adjust
     * their resource usage appropriately. </p>
     *
     * @return  the maximum number of processors available to the virtual
     *          machine; never smaller than one
     */
    public int availableProcessors() {
        return LibraryImports.availableProcessors();
    }

    /**
     * Returns the amount of free memory in the Java Virtual Machine.
     * Calling the 
     * <code>gc</code> method may result in increasing the value returned 
     * by <code>freeMemory.</code>
     *
     * @return  an approximation to the total amount of memory currently
     *          available for future allocated objects, measured in bytes.
     */
    public long freeMemory() {
        return LibraryImports.freeMemory();
    }

    /**
     * Returns the total amount of memory in the Java virtual machine.
     * The value returned by this method may vary over time, depending on 
     * the host environment.
     * <p>
     * Note that the amount of memory required to hold an object of any 
     * given type may be implementation-dependent.
     * 
     * @return  the total amount of memory currently available for current 
     *          and future objects, measured in bytes.
     */
    public long totalMemory() {
        return LibraryImports.totalMemory();
    }

    /**
     * Returns the maximum amount of memory that the Java virtual machine will
     * attempt to use.  If there is no inherent limit then the value {@link
     * java.lang.Long#MAX_VALUE} will be returned. </p>
     *
     * @return  the maximum amount of memory that the virtual machine will
     *          attempt to use, measured in bytes
     */
    public long maxMemory() {
        return LibraryImports.maxMemory();
    }

    /**
     * Runs the garbage collector.
     * Calling this method suggests that the Java virtual machine expend 
     * effort toward recycling unused objects in order to make the memory 
     * they currently occupy available for quick reuse. When control 
     * returns from the method call, the virtual machine has made 
     * its best effort to recycle all discarded objects. 
     * <p>
     * The name <code>gc</code> stands for "garbage 
     * collector". The virtual machine performs this recycling 
     * process automatically as needed, in a separate thread, even if the 
     * <code>gc</code> method is not invoked explicitly.
     * <p>
     * The method {@link System#gc()} is the conventional and convenient 
     * means of invoking this method. 
     */
    public void gc() {
        JavaVirtualMachine.getInstance().gc();
    }

    /**
     * Runs the finalization methods of any objects pending finalization.
     * Calling this method suggests that the Java virtual machine expend 
     * effort toward running the <code>finalize</code> methods of objects 
     * that have been found to be discarded but whose <code>finalize</code> 
     * methods have not yet been run. When control returns from the 
     * method call, the virtual machine has made a best effort to 
     * complete all outstanding finalizations. 
     * <p>
     * The virtual machine performs the finalization process 
     * automatically as needed, in a separate thread, if the 
     * <code>runFinalization</code> method is not invoked explicitly. 
     * <p>
     * The method {@link System#runFinalization()} is the conventional 
     * and convenient means of invoking this method.
     *
     * @see     java.lang.Object#finalize()
     */
    public void runFinalization() {
	//FIXME: Do we really need a way to wait for the finalizer
	//thread?  It seems to run to completion just fine on its
	//own.  I did not think that this RTE method was used, so I
	//decided to put it to work as the finalizer thread entry
	//point.  --JB
	//LibraryImports.runFinalization();
    }

    /**
     * <b>Not supported in OVM.</b>
     * <p>Enables/Disables tracing of instructions.
     * <p>If the <code>boolean</code> argument is <code>true</code>, this 
     * method suggests that the Java virtual machine emit debugging 
     * information for each instruction in the virtual machine as it 
     * is executed. The format of this information, and the file or other 
     * output stream to which it is emitted, depends on the host environment. 
     * The virtual machine may ignore this request if it does not support 
     * this feature. The destination of the trace output is system 
     * dependent. 
     * <p>
     * If the <code>boolean</code> argument is <code>false</code>, this 
     * method causes the virtual machine to stop performing the 
     * detailed instruction trace it is performing.
     *
     * @param   on   <code>true</code> to enable instruction tracing;
     *               <code>false</code> to disable this feature.
     */
    public void traceInstructions(boolean on) {
        // not supported
    }

    /**
     * <b>Not supported in OVM.</b>
     * <p>Enables/Disables tracing of method calls.
     * <p>If the <code>boolean</code> argument is <code>true</code>, this 
     * method suggests that the Java virtual machine emit debugging 
     * information for each method in the virtual machine as it is 
     * called. The format of this information, and the file or other output 
     * stream to which it is emitted, depends on the host environment. The 
     * virtual machine may ignore this request if it does not support 
     * this feature.  
     * <p>
     * Calling this method with argument false suggests that the
     * virtual machine cease emitting per-call debugging information.
     *
     * @param   on   <code>true</code> to enable instruction tracing;
     *               <code>false</code> to disable this feature.
     */
    public void traceMethodCalls(boolean on) {
        // not supported
    }

    /**
     * Loads the specified filename as a dynamic library. The filename 
     * argument must be a complete path name. 
     * From <code>java_g</code> it will automagically insert "_g" before the
     * ".so" (for example
     * <code>Runtime.getRuntime().load("/home/avh/lib/libX11.so");</code>).
     * <p>
     * First, if there is a security manager, its <code>checkLink</code> 
     * method is called with the <code>filename</code> as its argument. 
     * This may result in a security exception. 
     * <p>
     * This is similar to the method {@link #loadLibrary(String)}, but it 
     * accepts a general file name as an argument rather than just a library 
     * name, allowing any file of native code to be loaded.
     * <p>
     * The method {@link System#load(String)} is the conventional and 
     * convenient means of invoking this method.
     *
     * @param      filename   the file to load.
     * @exception  SecurityException  if a security manager exists and its  
     *             <code>checkLink</code> method doesn't allow 
     *             loading of the specified dynamic library
     * @exception  UnsatisfiedLinkError  if the file does not exist.
     * @see        java.lang.Runtime#getRuntime()
     * @see        java.lang.SecurityException
     * @see        java.lang.SecurityManager#checkLink(java.lang.String)
     */
    public void load(String filename) {
	SecurityManager security = System.getSecurityManager();
	if (security != null) {
	    security.checkLink(filename);
	}
	if (!(new File(filename).isAbsolute())) {
	    throw new UnsatisfiedLinkError(
	        "Expecting an absolute path of the library: " + filename);
	}
	// ##### TO DO - actually load the file
    }

    /**
     * Loads the dynamic library with the specified library name. 
     * A file containing native code is loaded from the local file system 
     * from a place where library files are conventionally obtained. The 
     * details of this process are implementation-dependent. The 
     * mapping from a library name to a specific filename is done in a 
     * system-specific manner. 
     * <p>
     * First, if there is a security manager, its <code>checkLink</code> 
     * method is called with the <code>libname</code> as its argument. 
     * This may result in a security exception. 
     * <p>
     * The method {@link System#loadLibrary(String)} is the conventional 
     * and convenient means of invoking this method. If native
     * methods are to be used in the implementation of a class, a standard 
     * strategy is to put the native code in a library file (call it 
     * <code>LibFile</code>) and then to put a static initializer:
     * <blockquote><pre>
     * static { System.loadLibrary("LibFile"); }
     * </pre></blockquote>
     * within the class declaration. When the class is loaded and 
     * initialized, the necessary native code implementation for the native 
     * methods will then be loaded as well. 
     * <p>
     * If this method is called more than once with the same library 
     * name, the second and subsequent calls are ignored. 
     *
     * @param      libname   the name of the library.
     * @exception  SecurityException  if a security manager exists and its  
     *             <code>checkLink</code> method doesn't allow 
     *             loading of the specified dynamic library
     * @exception  UnsatisfiedLinkError  if the library does not exist.
     * @see        java.lang.SecurityException
     * @see        java.lang.SecurityManager#checkLink(java.lang.String)
     */
    public void loadLibrary(String libname) {
        String filename;
        ClassLoader cl = VMSecurityManager.currentClassLoader();
        if (cl != null) {
            filename = cl.findLibrary(libname);
            if (filename != null) {
                load(filename);
                return;
            }
        }
        filename = System.mapLibraryName(libname);
        for (int i = 0; i < libpath.length; i++) {
            try {
                load(libpath[i] + filename);
                return;
            }
            catch (UnsatisfiedLinkError e) {
                // Try next path element.
            }
            throw new UnsatisfiedLinkError(
                "Could not find library " + libname + ".");
        }
    }

    /**
     * Creates a localized version of an input stream. This method takes 
     * an <code>InputStream</code> and returns an <code>InputStream</code> 
     * equivalent to the argument in all respects except that it is 
     * localized: as characters in the local character set are read from 
     * the stream, they are automatically converted from the local 
     * character set to Unicode. 
     * <p>
     * If the argument is already a localized stream, it may be returned 
     * as the result. 
     *
     * @param      in InputStream to localize
     * @return     a localized input stream
     * @see        java.io.InputStream
     * @see        java.io.BufferedReader#BufferedReader(java.io.Reader)
     * @see        java.io.InputStreamReader#InputStreamReader(java.io.InputStream)
     * @deprecated As of JDK&nbsp;1.1, the preferred way to translate a byte
     * stream in the local encoding into a character stream in Unicode is via
     * the <code>InputStreamReader</code> and <code>BufferedReader</code>
     * classes.
     */
    public InputStream getLocalizedInputStream(InputStream in) {
	return in;
    }

    /**
     * Creates a localized version of an output stream. This method 
     * takes an <code>OutputStream</code> and returns an 
     * <code>OutputStream</code> equivalent to the argument in all respects 
     * except that it is localized: as Unicode characters are written to 
     * the stream, they are automatically converted to the local 
     * character set. 
     * <p>
     * If the argument is already a localized stream, it may be returned 
     * as the result. 
     *
     * @deprecated As of JDK&nbsp;1.1, the preferred way to translate a
     * Unicode character stream into a byte stream in the local encoding is via
     * the <code>OutputStreamWriter</code>, <code>BufferedWriter</code>, and
     * <code>PrintWriter</code> classes.
     *
     * @param      out OutputStream to localize
     * @return     a localized output stream
     * @see        java.io.OutputStream
     * @see        java.io.BufferedWriter#BufferedWriter(java.io.Writer)
     * @see        java.io.OutputStreamWriter#OutputStreamWriter(java.io.OutputStream)
     * @see        java.io.PrintWriter#PrintWriter(java.io.OutputStream)
     */
    public OutputStream getLocalizedOutputStream(OutputStream out) {
	return out;
    }


   /**
    * Get the system properties. This is done here, instead of in System,
    * because of the bootstrap sequence. Note that the native code should
    * not try to use the Java I/O classes yet, as they rely on the properties
    * already existing. The only safe method to use to insert these default
    * system properties is {@link Properties#setProperty(String, String)}.
    *
    * <p>These properties MUST include:
    * <dl>
    * <dt>java.version         <dd>Java version number
    * <dt>java.vendor          <dd>Java vendor specific string
    * <dt>java.vendor.url      <dd>Java vendor URL
    * <dt>java.home            <dd>Java installation directory
    * <dt>java.vm.specification.version <dd>VM Spec version
    * <dt>java.vm.specification.vendor  <dd>VM Spec vendor
    * <dt>java.vm.specification.name    <dd>VM Spec name
    * <dt>java.vm.version      <dd>VM implementation version
    * <dt>java.vm.vendor       <dd>VM implementation vendor
    * <dt>java.vm.name         <dd>VM implementation name
    * <dt>java.specification.version    <dd>Java Runtime Environment version
    * <dt>java.specification.vendor     <dd>Java Runtime Environment vendor
    * <dt>java.specification.name       <dd>Java Runtime Environment name
    * <dt>java.class.version   <dd>Java class version number
    * <dt>java.class.path      <dd>Java classpath
    * <dt>java.library.path    <dd>Path for finding Java libraries
    * <dt>java.io.tmpdir       <dd>Default temp file path
    * <dt>java.compiler        <dd>Name of JIT to use
    * <dt>java.ext.dirs        <dd>Java extension path
    * <dt>os.name              <dd>Operating System Name
    * <dt>os.arch              <dd>Operating System Architecture
    * <dt>os.version           <dd>Operating System Version
    * <dt>file.separator       <dd>File separator ("/" on Unix)
    * <dt>path.separator       <dd>Path separator (":" on Unix)
    * <dt>line.separator       <dd>Line separator ("\n" on Unix)
    * <dt>user.name            <dd>User account name
    * <dt>user.home            <dd>User home directory
    * <dt>user.dir             <dd>User's current working directory
    * </dl>
    *
    * @param p the Properties object to insert the system properties into
    */
    static void insertSystemProperties(Properties p) {

        // the first thing we do is read the default platform encoding.
        // We know this string has to be expressed in the Portable Character
        // Set and so we can just convert byte to char directly
        byte[] charset = new byte[32]; // should be big enough ?
        int rc = LibraryGlue.get_default_charset(charset, charset.length);
        while (rc == org.ovmj.java.NativeConstants.ERANGE) {
            charset = new byte[charset.length*2];
            rc = LibraryGlue.get_default_charset(charset, charset.length);
        }

        String charsetName = makeRawString(charset);

        // if we couldn't tell what charset we have we don't define the
        // property and the EncodingManager will make its own assumptions.

        if (charsetName.length() > 0) {
            p.setProperty("file.encoding", charsetName);
        }

        // Temporarily make System.getProperty work by seeing our properties
        System.properties = p;

        // fill in the properties for encoding aliases
        System.initializeEncodingAliases();


        // Now it should be safe to create Strings from byte[]
        
        // these properties are relatively static

        // FIXEME: is this our internal version number or the Java Platform
        // version we support?
        p.setProperty("java.version", "1.3"); 
        p.setProperty("java.vendor", "Purdue University"); 
        p.setProperty("java.vendor.url", "http://ovmj.org"); 
        p.setProperty("java.vm.specification.version", "2.0"); 
        p.setProperty("java.vm.specification.vendor", "Sun Microsystems Inc.");
        p.setProperty("java.vm.specification.name", "Java Virtual Machine Specification"); 

        // FIXME: this should be tied to our build/version numbers
        p.setProperty("java.vm.version", "0.01"); 
        p.setProperty("java.vm.vendor", "Purdue University"); 
        p.setProperty("java.vm.name", "OVM"); 
        p.setProperty("java.specification.version", "1.3"); 
        p.setProperty("java.specification.vendor", "Sun Microsystems Inc."); 
        p.setProperty("java.specification.name", "Java Platform API Specification"); 
        p.setProperty("java.class.version", "47.0"); 
    
        // FIXME these are runtime properties but we don't support them yet anyway
        p.setProperty("java.class.path", LibraryImports.getClassPath()); 
        p.setProperty("java.library.path", "");
        p.setProperty("java.ext.dirs", "");

        // The JDK leaves this unset
        //p.setProperty("java.compiler", ""); 

        // FIXME: These should be system specific but we don't run on win32
        p.setProperty("file.separator", "/"); 
        p.setProperty("path.separator", ":"); 
        p.setProperty("line.separator", "\n");

        // These next properties are all dynamic and must be found at runtime

        // the native query functions try to balance space versus time issues.
        // we could ask for all values at once and require lots of temporary
        // array space; or we could ask for one at a time and require more
        // native calls and a much bigger API. We comprise by grouping things
        // that are related and/or the info is retrieved by a single system
        // call.

        // temp arrays: short strings < 256 chars; long < 4096
        // short for simple names, long for paths (4096 is _PATH_MAX on
        // some linux systems)

        byte[] sstr = new byte[256];
        byte[] lstr1 = new byte[4096];
        byte[] lstr2 = new byte[4096];


        // if there are any "hard" errors each buffer reads "<unascertainable>"
        // Truncation is still possible in most cases.

        LibraryGlue.get_user_info(sstr, sstr.length, 
                                  lstr1, lstr1.length,
                                  lstr2, lstr2.length);

        p.setProperty("user.name", makeString(sstr));
        p.setProperty("user.home", makeString(lstr1));
        p.setProperty("user.dir", makeString(lstr2));

        LibraryGlue.get_system_info(sstr, sstr.length, 
                                    lstr1, lstr1.length,
                                    lstr2, lstr2.length);
        p.setProperty("os.name", makeString(sstr));
        p.setProperty("os.version", makeString(lstr1));
        p.setProperty("os.arch", makeString(lstr2));

        LibraryGlue.get_temp_directory(lstr1, lstr1.length);
        p.setProperty("java.io.tmpdir", makeString(lstr1));

        // an error here means truncated output
        LibraryGlue.get_ovm_home(lstr1, lstr1.length);
        p.setProperty("java.home", makeString(lstr1));

        
        LibraryGlue.get_default_timezone_id(sstr, sstr.length);
        String tz = makeString(sstr);
        if (tz.length() > 0)
            p.setProperty("user.timezone", tz);

	int i = 0;
	for (String k = LibraryImports.VMPropertyName(i);
	     k != null;
	     k = LibraryImports.VMPropertyName(++i))
	    p.setProperty(k, LibraryImports.VMPropertyValue(i));
    }

    /**
     * Creates a new String by copying the bytes in data, up to the first
     * NUL (if present), into a character array that is used to construct 
     * a String. 
     * <p>We can't use the byte[] constructor for String because that will 
     * invoke the EncodingManager and we're using this method to define
     * the default character set encoding. The bytes are known to be simple
     * single-byte characters as they must be in the Portable Character Set
     *
     * <p>This method is not thread-safe and is only intended to be called
     * by insertSystemProperties.
     */
    private static String makeRawString(byte[] data) {
        char[] temp = new char[data.length];
        int i = 0;
        boolean foundNul = false;
        for (i = 0; i < temp.length; i++) {
            if (data[i] == '\0') {
                foundNul = true;
                break;
            }
            temp[i] = (char) data[i];
        }
        if (i == 0)
            return "";
        else 
            return new String(temp, 0, foundNul ? i : data.length );
    }

    /**
     * Creates a new String using the bytes in data, up to the first
     * NUL (if present), using the platforms default decoding.
     *
     * <p>This method is not thread-safe and is only intended to be called
     * by insertSystemProperties.
     */
    private static String makeString(byte[] data) {
        boolean foundNul = false;
        int i;
        for (i = 0; i < data.length; i++) {
            if (data[i] == '\0') {
                foundNul = true;
                break;
            }
        }
        if (i == 0)
            return "";
        else 
            return new String(data, 0, foundNul ? i : data.length );
    }


    /**
     * Map a system-independent "short name" to the full file name, and append
     * it to the path.
     * XXX This method is being replaced by System.mapLibraryName.
     *
     * @param pathname the path
     * @param libname the short version of the library name
     * @return the full filename
     */
    static String nativeGetLibname(String pathname, String libname) {
        return pathname + File.separator + libname;
    }

}
