--- /home/tromey/gnu/Nightly/classpath/classpath/java/io/File.java 2004-11-29 02:19:39.000000000 -0700 +++ java/io/File.java 2004-09-13 02:17:02.000000000 -0600 @@ -43,6 +43,8 @@ import java.net.URI; import java.net.URISyntaxException; import java.net.URL; +import gnu.classpath.Configuration; +import gnu.gcj.runtime.FileDeleter; /* Written using "Java Class Libraries", 2nd edition, ISBN 0-201-31002-3 * "The Java Language Specification", ISBN 0-201-63451-1 @@ -61,6 +63,27 @@ public class File implements Serializable, Comparable { private static final long serialVersionUID = 301077366599181567L; + + // QUERY arguments to access function. + private final static int READ = 0; + private final static int WRITE = 1; + private final static int EXISTS = 2; + + // QUERY arguments to stat function. + private final static int DIRECTORY = 0; + private final static int ISFILE = 1; + private final static int ISHIDDEN = 2; + + // QUERY arguments to attr function. + private final static int MODIFIED = 0; + private final static int LENGTH = 1; + + private final native long attr (int query); + // On OSF1 V5.0, `stat' is a macro. It is easiest to use the name + // `_stat' instead. We do the same thing for `_access' just in + // case. + private final native boolean _access (int query); + private final native boolean _stat (int query); /** * This is the path separator string for the current host. This field @@ -93,12 +116,34 @@ */ public static final char pathSeparatorChar = pathSeparator.charAt(0); + static final String tmpdir = System.getProperty("java.io.tmpdir"); + static int maxPathLen; + static boolean caseSensitive; + + static + { + if (Configuration.INIT_LOAD_LIBRARY) + { + System.loadLibrary("javaio"); + } + + init_native(); + } + + // Native function called at class initialization. This should should + // set the maxPathLen and caseSensitive variables. + private static native void init_native(); + /** * This is the path to the file set when the object is created. It * may be an absolute or relative path name. */ private String path; + // We keep a counter for use by createTempFile. We choose the first + // value randomly to try to avoid clashes with other VMs. + private static long counter = Double.doubleToLongBits (Math.random()); + /** * This method tests whether or not the current thread is allowed to * to read the file pointed to by this object. This will be true if and @@ -114,11 +159,8 @@ */ public boolean canRead() { - // Test for existence. This also does the SecurityManager check - if (!exists()) - return false; - - return VMFile.canRead(path); + checkRead(); + return _access (READ); } /** @@ -137,18 +179,11 @@ */ public boolean canWrite() { - // First do a SecurityCheck before doing anything else. checkWrite(); - - // Test for existence. This is required by the spec - if (! VMFile.exists(path)) - return false; - - if (VMFile.isDirectory(path)) - return VMFile.canWriteDirectory(this); - else - return VMFile.canWrite(path); + return _access (WRITE); } + + private native boolean performCreate() throws IOException; /** * This method creates a new file of zero length with the same name as @@ -170,8 +205,14 @@ public boolean createNewFile() throws IOException { checkWrite(); - return VMFile.create(path); + return performCreate(); } + + /* + * This native method handles the actual deleting of the file + */ + private native boolean performDelete(); + /** * This method deletes the file represented by this object. If this file * is a directory, it must be empty in order for the delete to succeed. @@ -188,7 +229,7 @@ if (s != null) s.checkDelete(path); - return VMFile.delete(path); + return performDelete(); } /** @@ -213,7 +254,7 @@ File other = (File) obj; - if (VMFile.IS_CASE_SENSITIVE) + if (caseSensitive) return path.equals(other.path); else return path.equalsIgnoreCase(other.path); @@ -230,7 +271,7 @@ public boolean exists() { checkRead(); - return VMFile.exists(path); + return _access (EXISTS); } /** @@ -243,7 +284,7 @@ { path = normalizePath (name); } - + // Remove duplicate and redundant separator characters. private String normalizePath(String p) { @@ -474,22 +515,7 @@ * * @exception IOException If an error occurs */ - public String getCanonicalPath() throws IOException - { - // On Windows, getAbsolutePath might end up calling us, so we - // have to special case that call to avoid infinite recursion. - if (separatorChar == '\\' && path.length() == 2 && - ((path.charAt(0) >= 'a' && path.charAt(0) <= 'z') || - (path.charAt(0) >= 'A' && path.charAt(0) <= 'Z')) && - path.charAt(1) == ':') - { - return VMFile.toCanonicalForm(path); - } - // Call getAbsolutePath first to make sure that we do the - // current directory handling, because the native code - // may have a different idea of the current directory. - return VMFile.toCanonicalForm(getAbsolutePath()); - } + public native String getCanonicalPath() throws IOException; /** * This method returns a File object representing the @@ -516,7 +542,30 @@ */ public String getName() { - return VMFile.getName(path); + int nameSeqIndex = 0; + + if (separatorChar == '\\' && path.length() > 1) + { + // On Windows, ignore the drive specifier or the leading '\\' + // of a UNC network path, if any (a.k.a. the "prefix"). + if ((path.charAt (0) == '\\' && path.charAt (1) == '\\') + || (((path.charAt (0) >= 'a' && path.charAt (0) <= 'z') + || (path.charAt (0) >= 'A' && path.charAt (0) <= 'Z')) + && path.charAt (1) == ':')) + { + if (path.length() > 2) + nameSeqIndex = 2; + else + return ""; + } + } + + String nameSeq + = (nameSeqIndex > 0 ? path.substring (nameSeqIndex) : path); + + int last = nameSeq.lastIndexOf (separatorChar); + + return nameSeq.substring (last + 1); } /** @@ -616,7 +665,7 @@ */ public int hashCode() { - if (VMFile.IS_CASE_SENSITIVE) + if (caseSensitive) return path.hashCode() ^ 1234321; else return path.toLowerCase().hashCode() ^ 1234321; @@ -631,18 +680,7 @@ * @return true if this object represents an absolute * file name, false otherwise. */ - public boolean isAbsolute() - { - if (separatorChar == '\\') - return path.startsWith(dupSeparator) || - (path.length() > 2 && - ((path.charAt(0) >= 'a' && path.charAt(0) <= 'z') || - (path.charAt(0) >= 'A' && path.charAt(0) <= 'Z')) && - path.charAt(1) == ':' && - path.charAt(2) == '\\'); - else - return path.startsWith(separator); - } + public native boolean isAbsolute(); /** * This method tests whether or not the file represented by this object @@ -657,7 +695,7 @@ public boolean isDirectory() { checkRead(); - return VMFile.isDirectory(path); + return _stat (DIRECTORY); } /** @@ -673,7 +711,7 @@ public boolean isFile() { checkRead(); - return VMFile.isFile(path); + return _stat (ISFILE); } /** @@ -689,7 +727,8 @@ */ public boolean isHidden() { - return VMFile.isHidden(path); + checkRead(); + return _stat (ISHIDDEN); } /** @@ -708,7 +747,7 @@ public long lastModified() { checkRead(); - return VMFile.lastModified(path); + return attr (MODIFIED); } /** @@ -722,9 +761,17 @@ public long length() { checkRead(); - return VMFile.length(path); + return attr (LENGTH); } + /* + * This native function actually produces the list of file in this + * directory + */ + private final native Object[] performList (FilenameFilter filter, + FileFilter fileFilter, + Class result_type); + /** * This method returns a array of String's representing the * list of files is then directory represented by this object. If this @@ -755,37 +802,7 @@ public String[] list(FilenameFilter filter) { checkRead(); - - if (!exists() || !isDirectory()) - return null; - - // Get the list of files - String files[] = VMFile.list(path); - - // Check if an error occured in listInternal(). - if (files == null) - return null; - - if (filter == null) - return files; - - // Apply the filter - int count = 0; - for (int i = 0; i < files.length; i++) - { - if (filter.accept(this, files[i])) - ++count; - else - files[i] = null; - } - - String[] retfiles = new String[count]; - count = 0; - for (int i = 0; i < files.length; i++) - if (files[i] != null) - retfiles[count++] = files[i]; - - return retfiles; + return (String[]) performList (filter, null, String.class); } /** @@ -808,7 +825,8 @@ */ public String[] list() { - return list(null); + checkRead(); + return (String[]) performList (null, null, String.class); } /** @@ -831,7 +849,8 @@ */ public File[] listFiles() { - return listFiles((FilenameFilter) null); + checkRead(); + return (File[]) performList (null, null, File.class); } /** @@ -860,17 +879,8 @@ */ public File[] listFiles(FilenameFilter filter) { - String[] filelist = list(filter); - - if (filelist == null) - return null; - - File[] fobjlist = new File [filelist.length]; - - for (int i = 0; i < filelist.length; i++) - fobjlist [i] = new File(this, filelist [i]); - - return fobjlist; + checkRead(); + return (File[]) performList (filter, null, File.class); } /** @@ -899,29 +909,8 @@ */ public File[] listFiles(FileFilter filter) { - File[] fobjlist = listFiles((FilenameFilter) null); - - if (fobjlist == null) - return null; - - if (filter == null) - return fobjlist; - - int count = 0; - for (int i = 0; i < fobjlist.length; i++) - if (filter.accept(fobjlist[i]) == true) - ++count; - - File[] final_list = new File[count]; - count = 0; - for (int i = 0; i < fobjlist.length; i++) - if (filter.accept(fobjlist[i]) == true) - { - final_list[count] = fobjlist[i]; - ++count; - } - - return final_list; + checkRead(); + return (File[]) performList (null, filter, File.class); } /** @@ -943,22 +932,16 @@ String abspath = getAbsolutePath(); if (isDirectory()) - abspath = abspath + separatorChar; - - if (separatorChar == '\\') - abspath = separatorChar + abspath; + abspath = abspath + separator; try { - return new URI("file", null, null, -1, - abspath.replace(separatorChar, '/'), - null, null); + return new URI("file", "", abspath.replace(separatorChar, '/')); } catch (URISyntaxException use) { // Can't happen. - throw (InternalError) new InternalError("Unconvertible file: " - + this).initCause(use); + throw new RuntimeException(use); } } @@ -984,6 +967,10 @@ + (isDirectory() ? "/" : "")); } + /* + * This native method actually creates the directory + */ + private final native boolean performMkdir(); /** * This method creates a directory for the path represented by this object. @@ -996,7 +983,23 @@ public boolean mkdir() { checkWrite(); - return VMFile.mkdir(path); + return performMkdir(); + } + + private static boolean mkdirs (File x) + { + if (x.isDirectory()) + return true; + String p = x.getPath(); + String parent = x.getParent(); + if (parent != null) + { + x.path = parent; + if (! mkdirs (x)) + return false; + x.path = p; + } + return x.mkdir(); } /** @@ -1010,21 +1013,15 @@ */ public boolean mkdirs() { - String parent = getParent(); - if (parent == null) - { - return mkdir(); - } - - File f = new File(parent); - if (!f.exists()) - { - boolean rc = f.mkdirs(); - if (rc == false) - return false; - } + checkWrite(); + if (isDirectory()) + return false; + return mkdirs (new File (path)); + } - return mkdir(); + private static synchronized String nextValue() + { + return Long.toString(counter++, Character.MAX_RADIX); } /** @@ -1064,15 +1061,15 @@ // Grab the system temp directory if necessary if (directory == null) { - String dirname = System.getProperty("java.io.tmpdir"); + String dirname = tmpdir; if (dirname == null) throw new IOException("Cannot determine system temporary directory"); directory = new File(dirname); - if (! VMFile.exists(directory.path)) + if (!directory.exists()) throw new IOException("System temporary directory " + directory.getName() + " does not exist."); - if (! VMFile.isDirectory(directory.path)) + if (!directory.isDirectory()) throw new IOException("System temporary directory " + directory.getName() + " is not really a directory."); @@ -1086,44 +1083,45 @@ if (suffix == null) suffix = ".tmp"; - // Now identify a file name and make sure it doesn't exist. - File file; - if (!VMFile.IS_DOS_8_3) - { - do - { - String filename = prefix + System.currentTimeMillis() + suffix; - file = new File(directory, filename); - } - while (VMFile.exists(file.path)); - } - else + // Truncation rules. + // `6' is the number of characters we generate. + if (prefix.length() + 6 + suffix.length() > maxPathLen) { - // make sure prefix is not longer than 7 characters - if (prefix.length() >= 8) - throw new IllegalArgumentException("Prefix too long: " + prefix + "(valid length 3..7)"); - - long mask = 0x000000ffffFFFFL >> (prefix.length() * 4); - do - { - int n = (int) (System.currentTimeMillis() & mask); - String filename = prefix + java.lang.Integer.toHexString(n) + suffix; - file = new File(directory, filename); - } - while (VMFile.exists(file.path)); + int suf_len = 0; + if (suffix.charAt(0) == '.') + suf_len = 4; + suffix = suffix.substring(0, suf_len); + if (prefix.length() + 6 + suf_len > maxPathLen) + prefix = prefix.substring(0, maxPathLen - 6 - suf_len); } - // Verify that we are allowed to create this file - SecurityManager sm = System.getSecurityManager(); - if (sm != null) - sm.checkWrite(file.getAbsolutePath()); + File f; + + // How many times should we try? We choose 100. + for (int i = 0; i < 100; ++i) + { + // This is ugly. + String t = "ZZZZZZ" + nextValue(); + String l = prefix + t.substring(t.length() - 6) + suffix; + try + { + f = new File(directory, l); + if (f.createNewFile()) + return f; + } + catch (IOException ignored) + { + } + } - // Now create the file and return our file object - // XXX - FIXME race condition. - VMFile.create(file.getAbsolutePath()); - return file; + throw new IOException ("cannot create temporary file"); } + /* + * This native method sets the permissions to make the file read only. + */ + private native boolean performSetReadOnly(); + /** * This method sets the file represented by this object to be read only. * A read only file or directory cannot be modified. Please note that @@ -1142,14 +1140,11 @@ { // Do a security check before trying to do anything else. checkWrite(); - - // Test for existence. - if (! VMFile.exists(path)) - return false; - - return VMFile.setReadOnly(path); + return performSetReadOnly(); } + private static native File[] performListRoots(); + /** * This method returns an array of filesystem roots. Some operating systems * have volume oriented filesystem. This method provides a mechanism for @@ -1163,7 +1158,38 @@ */ public static File[] listRoots() { - return VMFile.listRoots(); + File[] roots = performListRoots(); + + SecurityManager s = System.getSecurityManager(); + if (s != null) + { + // Only return roots to which the security manager permits read access. + int count = roots.length; + for (int i = 0; i < roots.length; i++) + { + try + { + s.checkRead (roots[i].path); + } + catch (SecurityException sx) + { + roots[i] = null; + count--; + } + } + if (count != roots.length) + { + File[] newRoots = new File[count]; + int k = 0; + for (int i=0; i < roots.length; i++) + { + if (roots[i] != null) + newRoots[k++] = roots[i]; + } + roots = newRoots; + } + } + return roots; } /** @@ -1217,7 +1243,7 @@ */ public int compareTo(File other) { - if (VMFile.IS_CASE_SENSITIVE) + if (caseSensitive) return path.compareTo (other.path); else return path.compareToIgnoreCase (other.path); @@ -1249,6 +1275,11 @@ return compareTo((File) obj); } + /* + * This native method actually performs the rename. + */ + private native boolean performRenameTo (File dest); + /** * This method renames the file represented by this object to the path * of the file represented by the argument File. @@ -1263,12 +1294,22 @@ */ public synchronized boolean renameTo(File dest) { - checkWrite(); - dest.checkWrite(); - // Call our native rename method - return VMFile.renameTo(path, dest.path); + SecurityManager s = System.getSecurityManager(); + String sname = getName(); + String dname = dest.getName(); + if (s != null) + { + s.checkWrite (sname); + s.checkWrite (dname); + } + return performRenameTo (dest); } + /* + * This method does the actual setting of the modification time. + */ + private native boolean performSetLastModified(long time); + /** * This method sets the modification time on the file to the specified * value. This is specified as the number of seconds since midnight @@ -1291,7 +1332,7 @@ throw new IllegalArgumentException("Negative modification time: " + time); checkWrite(); - return VMFile.setLastModified(path, time); + return performSetLastModified(time); } private void checkWrite() @@ -1322,14 +1363,15 @@ * * @since 1.2 */ + // FIXME: This should use the ShutdownHook API once we implement that. public void deleteOnExit() { // Check the SecurityManager SecurityManager sm = System.getSecurityManager(); if (sm != null) - sm.checkDelete(path); + sm.checkDelete (getName()); - DeleteFileHelper.add(this); + FileDeleter.add (this); } private void writeObject(ObjectOutputStream oos) throws IOException