// Copyright 1996, Marimba Inc. All Rights Reserved.

// @(#)FileSystem.java, 1.31, 10/02/96



package marimba.io;



import java.io.*;

import java.util.*;

import java.net.Socket;



import marimba.util.ThreadUtil;



/**

 * File system utillities. This class provides some minor

 * functionality that is missing from java.io.

 *

 * @author	Jonathan Payne

 * @version 	1.31, 10/02/96

 */

public class FileSystem implements Runnable {

    File file;



    /**

     * Private constructor.

     */

    FileSystem(File file) {

	this.file = file;

    }



    /**

     * Remove an empty directory.

     */

    private static native void rmdir(String path) throws IOException;



    /**

     * Remove a file.

     */

    private static native void unlink(String path) throws IOException;



    /**

     * Remove a file or directory.

     */

    public static boolean remove(String path) {

	return remove(path, false);

    }



    /**

     * Remove a file or directory

     */

    public static boolean remove(File path) {

	return remove(path, false);

    }



    /**

     * Remove a file or director, synchronous or asynchronous.

     */

    public static boolean remove(String path, boolean background) {

	return remove(new File(path), background);

    }



    /**

     * Remove a file or a directory, synchronous or asynchronous.

     */

    public static boolean remove(File f, boolean background) {

	SecurityManager security = System.getSecurityManager();

	if (security != null) {

	    security.checkWrite(f.getPath());

	}

	if (!f.exists()) {

	    return true;

	}

	if (f.isDirectory()) {

	    return removeDirectory(f, background);

	}

	try {

	    unlink(f.getPath());

	} catch (IOException e) {

	    return false;

	} catch (UnsatisfiedLinkError e) {

	    return false;

	}

	return true;

    }



    /**

     * Remove a directory, synchronous or asynchronous.

     */

    private static boolean removeDirectory(File path, boolean background) {

	if (background) {

	    ThreadUtil.fork("RemoveDirectory", new FileSystem(path));

	    return true;

	}

	try {

  	    if (clearDirectory(path)) {

		try {

		    rmdir(path.getPath());

		} catch (UnsatisfiedLinkError e) {

		    return false;

		}

		return true;

	    }

	    return false;

	} catch (IOException e) {

	    return false;

	}

    }



    /**

     * Clear a directory (all files in it, but not the directory

     * itself).

     */

    public static boolean clearDirectory(File d) {

	SecurityManager security = System.getSecurityManager();

	if (security != null) {

	    security.checkWrite(d.getPath());

	}

	boolean result = true;

	String list[] = d.list();

	for (int i = list.length; --i >= 0; ) {

	    if (!remove(new File(d, list[i]), false)) {

		result = false;

	    }

	}

	return result;

    }



    /**

     * Find a file.

     */

    public static Vector find(String path, String name, boolean exact) {

	Vector toVisit = new Vector();

	Vector results = new Vector();



	toVisit.addElement(path);

	while (toVisit.size() > 0) {

	    File f = new File((String) toVisit.elementAt(0));

	    toVisit.removeElementAt(0);

	    if (f.isDirectory()) {

		String list[] = f.list();

		for (int i = list.length; --i >= 0; ) {

		    File child = new File(f, list[i]);

		    if ((exact && list[i].equals(name))

			|| (!exact && list[i].indexOf(name) >= 0)) {

			results.addElement(child.getPath());

		    }

		    if (child.isDirectory()) {

			toVisit.addElement(child.getPath());

		    }

		}

	    }

	}

	return results.size() > 0 ? results : null;

    }



    /**

     * Create a file. If the directory does not exist, create the parent directories.

     */

    public static FileOutputStream createFile(File file) throws IOException {

	SecurityManager security = System.getSecurityManager();

	if (security != null) {

	    security.checkWrite(file.getPath());

	}

	try {

	    return new FileOutputStream(file);

	} catch (IOException e) {

	    File parent = new File(file.getParent());

	    if (!parent.exists() && !parent.mkdirs())

		throw e;

	    return new FileOutputStream(file);

	}

    }



    /**

     * Rename a file.

     */

    public static boolean renameFile(File a, File b) {

	SecurityManager security = System.getSecurityManager();

	if (security != null) {

	    security.checkWrite(a.getPath());

	    security.checkWrite(b.getPath());

	}

	if (!a.renameTo(b)) {

	    /* check to see if destination directory is missing */

	    File destDir = new File(b.getParent());



	    if (destDir.exists() || !destDir.mkdirs())

		return false;

	    /* REMIND: We retry the rename EVEN if the destination

	       directory was already there, just because the UNIX

	       implementation doesn't seem to realize that a rename

	       can fail with EINTR, which in reality isn't actually an

	       error. */

	    return a.renameTo(b);

	}

	return true;

    }



    private native static boolean setSocketLinger_n(Socket socket,int seconds);



    public static boolean setSocketLinger(Socket socket, int seconds) {

	try {

	    return setSocketLinger_n(socket, seconds);

	} catch (UnsatisfiedLinkError e) {

	    return false;

	}

    }



    /**

     * Sets a file to readonly (or not) depending on ON.

     */

    public static boolean setReadonly(File path, boolean on) {

	SecurityManager security = System.getSecurityManager();

	if (security != null) {

	    security.checkWrite(path.getPath());

	}

	return setReadonly0(path.getPath(), on);

    }



    static native boolean setReadonly0(String path, boolean on);



    /**

     * Remove directories and files in the background.

     */

    public void run() {

	Thread.currentThread().setPriority(Thread.NORM_PRIORITY - 3);

	removeDirectory(file, false);

    }

    

    static {

	System.loadLibrary("marimba");

    }



    public static void main(String args[]) {

	setReadonly(new File(args[0]), args.length > 1);

    }

}

