| /* | |
| * Copyright (c) 1998, 2017, Oracle and/or its affiliates. All rights reserved. | |
| * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. | |
| * | |
| * This code is free software; you can redistribute it and/or modify it | |
| * under the terms of the GNU General Public License version 2 only, as | |
| * published by the Free Software Foundation. | |
| * | |
| * This code is distributed in the hope that it will be useful, but WITHOUT | |
| * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
| * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | |
| * version 2 for more details (a copy is included in the LICENSE file that | |
| * accompanied this code). | |
| * | |
| * You should have received a copy of the GNU General Public License version | |
| * 2 along with this work; if not, write to the Free Software Foundation, | |
| * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. | |
| * | |
| * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA | |
| * or visit www.oracle.com if you need additional information or have any | |
| * questions. | |
| */ | |
| /* | |
| @summary Common definitions for general exhaustive pathname tests | |
| @author Mark Reinhold | |
| */ | |
| import java.io.*; | |
| import java.util.*; | |
| import java.nio.file.*; | |
| public class General { | |
| public static boolean debug = false; | |
| private static boolean win32 = (File.separatorChar == '\\'); | |
| private static int gensymCounter = 0; | |
| protected static final String userDir = System.getProperty("user.dir"); | |
| protected static final String workSubDir = "tmp"; | |
| protected static String baseDir = null; | |
| protected static String relative = null; | |
| /* Generate a filename unique to this run */ | |
| private static String gensym() { | |
| return "x." + ++gensymCounter; | |
| } | |
| /** | |
| * Create files and folders in the test working directory. | |
| * The purpose is to make sure the test will not go out of | |
| * its user dir when walking the file tree. | |
| * | |
| * @param depth The number of directory levels to be created under | |
| * the user directory. It should be the maximum value | |
| * of the depths passed to checkNames method (including | |
| * direct or indirect calling) in a whole test. | |
| */ | |
| protected static void initTestData(int depth) throws IOException { | |
| File parent = new File(userDir + File.separator + workSubDir); | |
| if (!parent.mkdir()) { | |
| throw new IOException("Fail to create directory: " + parent); | |
| } | |
| for (int i = 0; i < depth; i++) { | |
| File tmp = new File(parent, gensym()); | |
| tmp.createNewFile(); | |
| tmp = new File(parent, gensym()); | |
| if (tmp.mkdir()) | |
| parent = tmp; | |
| else | |
| throw new IOException("Fail to create directory, " + tmp); | |
| } | |
| baseDir = parent.getAbsolutePath(); | |
| relative = baseDir.substring(userDir.length() + 1); | |
| } | |
| /** | |
| * Find a file in the given subdirectory, or descend into further | |
| * subdirectories, if any, if no file is found here. Return null if no | |
| * file can be found anywhere beneath the given subdirectory. | |
| * @param dir Directory at which we started | |
| * @param subdir Subdirectory that we're exploring | |
| * @param dl Listing of subdirectory | |
| */ | |
| private static String findSomeFile(String dir, String subdir, String[] dl) { | |
| for (int i = 0; i < dl.length; i++) { | |
| File f = new File(subdir, dl[i]); | |
| File df = new File(dir, f.getPath()); | |
| if (Files.isRegularFile(df.toPath(), LinkOption.NOFOLLOW_LINKS)) { | |
| return f.getPath(); | |
| } | |
| } | |
| for (int i = 0; i < dl.length; i++) { | |
| File f = (subdir.length() == 0) ? new File(dl[i]) | |
| : new File(subdir, dl[i]); | |
| File df = new File(dir, f.getPath()); | |
| if (Files.isDirectory(df.toPath(), LinkOption.NOFOLLOW_LINKS)) { | |
| String[] dl2 = df.list(); | |
| if (dl2 != null) { | |
| String ff = findSomeFile(dir, f.getPath(), dl2); | |
| if (ff != null) return ff; | |
| } | |
| } | |
| } | |
| return null; | |
| } | |
| /** | |
| * Construct a string that names a file in the given directory. If create | |
| * is true, then create a file if none is found, and throw an exception if | |
| * that is not possible; otherwise, return null if no file can be found. | |
| */ | |
| private static String findSomeFile(String dir, boolean create) { | |
| File d = new File(dir); | |
| String[] dl = d.list(); | |
| if (dl == null) { | |
| throw new RuntimeException("Can't list " + dir); | |
| } | |
| for (int i = 0; i < dl.length; i++) { | |
| File f = new File(dir, dl[i]); | |
| if (Files.isRegularFile(f.toPath(), LinkOption.NOFOLLOW_LINKS)) { | |
| return dl[i]; | |
| } | |
| } | |
| String f = findSomeFile(dir, "", dl); | |
| if (f != null) { | |
| return f; | |
| } | |
| if (create) { | |
| File nf = new File(d, gensym()); | |
| OutputStream os; | |
| try { | |
| os = new FileOutputStream(nf); | |
| os.close(); | |
| } catch (IOException x) { | |
| throw new RuntimeException("Can't create a file in " + dir); | |
| } | |
| return nf.getName(); | |
| } | |
| return null; | |
| } | |
| /** | |
| * Construct a string that names a subdirectory of the given directory. | |
| * If create is true, then create a subdirectory if none is found, and | |
| * throw an exception if that is not possible; otherwise, return null if | |
| * no subdirectory can be found. | |
| */ | |
| private static String findSomeDir(String dir, boolean create) { | |
| File d = new File(dir); | |
| String[] dl = d.list(); | |
| if (dl == null) { | |
| throw new RuntimeException("Can't list " + dir); | |
| } | |
| for (int i = 0; i < dl.length; i++) { | |
| File f = new File(d, dl[i]); | |
| if (Files.isDirectory(f.toPath(), LinkOption.NOFOLLOW_LINKS)) { | |
| String[] dl2 = f.list(); | |
| if (dl2 == null || dl2.length >= 250) { | |
| /* Heuristic to avoid scanning huge directories */ | |
| continue; | |
| } | |
| return dl[i]; | |
| } | |
| } | |
| if (create) { | |
| File sd = new File(d, gensym()); | |
| if (sd.mkdir()) return sd.getName(); | |
| } | |
| return null; | |
| } | |
| /** Construct a string that does not name a file in the given directory */ | |
| private static String findNon(String dir) { | |
| File d = new File(dir); | |
| String[] x = new String[] { "foo", "bar", "baz" }; | |
| for (int i = 0; i < x.length; i++) { | |
| File f = new File(d, x[i]); | |
| if (!f.exists()) { | |
| return x[i]; | |
| } | |
| } | |
| for (int i = 0; i < 1024; i++) { | |
| String n = "xx" + Integer.toString(i); | |
| File f = new File(d, n); | |
| if (!f.exists()) { | |
| return n; | |
| } | |
| } | |
| throw new RuntimeException("Can't find a non-existent file in " + dir); | |
| } | |
| /** Ensure that the named file does not exist */ | |
| public static void ensureNon(String fn) { | |
| if ((new File(fn)).exists()) { | |
| throw new RuntimeException("Test path " + fn + " exists"); | |
| } | |
| } | |
| /** Tell whether the given character is a "slash" on this platform */ | |
| private static boolean isSlash(char x) { | |
| if (x == File.separatorChar) return true; | |
| if (win32 && (x == '/')) return true; | |
| return false; | |
| } | |
| /** | |
| * Trim trailing slashes from the given string, but leave singleton slashes | |
| * alone (they denote root directories) | |
| */ | |
| private static String trimTrailingSlashes(String s) { | |
| int n = s.length(); | |
| if (n == 0) return s; | |
| n--; | |
| while ((n > 0) && isSlash(s.charAt(n))) { | |
| if ((n >= 1) && s.charAt(n - 1) == ':') break; | |
| n--; | |
| } | |
| return s.substring(0, n + 1); | |
| } | |
| /** Concatenate two paths, trimming slashes as needed */ | |
| private static String pathConcat(String a, String b) { | |
| if (a.length() == 0) return b; | |
| if (b.length() == 0) return a; | |
| if (isSlash(a.charAt(a.length() - 1)) | |
| || isSlash(b.charAt(0)) | |
| || (win32 && (a.charAt(a.length() - 1) == ':'))) { | |
| return a + b; | |
| } else { | |
| return a + File.separatorChar + b; | |
| } | |
| } | |
| /** Hash table of input pathnames, used to detect duplicates */ | |
| private static Hashtable<String, String> checked = new Hashtable<>(); | |
| /** | |
| * Check the given pathname. Its canonical pathname should be the given | |
| * answer. If the path names a file that exists and is readable, then | |
| * FileInputStream and RandomAccessFile should both be able to open it. | |
| */ | |
| public static void check(String answer, String path) throws IOException { | |
| String ans = trimTrailingSlashes(answer); | |
| if (path.length() == 0) return; | |
| if (checked.get(path) != null) { | |
| System.err.println("DUP " + path); | |
| return; | |
| } | |
| checked.put(path, path); | |
| String cpath; | |
| try { | |
| File f = new File(path); | |
| cpath = f.getCanonicalPath(); | |
| if (f.exists() && f.isFile() && f.canRead()) { | |
| InputStream in = new FileInputStream(path); | |
| in.close(); | |
| RandomAccessFile raf = new RandomAccessFile(path, "r"); | |
| raf.close(); | |
| } | |
| } catch (IOException x) { | |
| System.err.println(ans + " <-- " + path + " ==> " + x); | |
| if (debug) return; | |
| else throw x; | |
| } | |
| if (cpath.equals(ans)) { | |
| System.err.println(ans + " <== " + path); | |
| } else { | |
| System.err.println(ans + " <-- " + path + " ==> " + cpath + " MISMATCH"); | |
| if (!debug) { | |
| throw new RuntimeException("Mismatch: " + path + " ==> " + cpath + | |
| ", should be " + ans); | |
| } | |
| } | |
| } | |
| /* | |
| * The following three mutually-recursive methods generate and check a tree | |
| * of filenames of arbitrary depth. Each method has (at least) these | |
| * arguments: | |
| * | |
| * int depth Remaining tree depth | |
| * boolean create Controls whether test files and directories | |
| * will be created as needed | |
| * String ans Expected answer for the check method (above) | |
| * String ask Input pathname to be passed to the check method | |
| */ | |
| /** Check a single slash case, plus its children */ | |
| private static void checkSlash(int depth, boolean create, | |
| String ans, String ask, String slash) | |
| throws Exception | |
| { | |
| check(ans, ask + slash); | |
| checkNames(depth, create, | |
| ans.endsWith(File.separator) ? ans : ans + File.separator, | |
| ask + slash); | |
| } | |
| /** Check slash cases for the given ask string */ | |
| public static void checkSlashes(int depth, boolean create, | |
| String ans, String ask) | |
| throws Exception | |
| { | |
| check(ans, ask); | |
| if (depth == 0) return; | |
| checkSlash(depth, create, ans, ask, "/"); | |
| checkSlash(depth, create, ans, ask, "//"); | |
| checkSlash(depth, create, ans, ask, "///"); | |
| if (win32) { | |
| checkSlash(depth, create, ans, ask, "\\"); | |
| checkSlash(depth, create, ans, ask, "\\\\"); | |
| checkSlash(depth, create, ans, ask, "\\/"); | |
| checkSlash(depth, create, ans, ask, "/\\"); | |
| checkSlash(depth, create, ans, ask, "\\\\\\"); | |
| } | |
| } | |
| /** Check name cases for the given ask string */ | |
| public static void checkNames(int depth, boolean create, | |
| String ans, String ask) | |
| throws Exception | |
| { | |
| int d = depth - 1; | |
| File f = new File(ans); | |
| String n; | |
| /* Normal name */ | |
| if (f.exists()) { | |
| if (Files.isDirectory(f.toPath(), LinkOption.NOFOLLOW_LINKS) && f.list() != null) { | |
| if ((n = findSomeFile(ans, create)) != null) | |
| checkSlashes(d, create, ans + n, ask + n); | |
| if ((n = findSomeDir(ans, create)) != null) | |
| checkSlashes(d, create, ans + n, ask + n); | |
| } | |
| n = findNon(ans); | |
| checkSlashes(d, create, ans + n, ask + n); | |
| } else { | |
| n = "foo" + depth; | |
| checkSlashes(d, create, ans + n, ask + n); | |
| } | |
| /* "." */ | |
| checkSlashes(d, create, trimTrailingSlashes(ans), ask + "."); | |
| /* ".." */ | |
| if ((n = f.getParent()) != null) { | |
| String n2; | |
| if (win32 | |
| && ((n2 = f.getParentFile().getParent()) != null) | |
| && n2.equals("\\\\")) { | |
| /* Win32 resolves \\foo\bar\.. to \\foo\bar */ | |
| checkSlashes(d, create, ans, ask + ".."); | |
| } else { | |
| checkSlashes(d, create, n, ask + ".."); | |
| } | |
| } | |
| else { | |
| if (win32) | |
| checkSlashes(d, create, ans, ask + ".."); | |
| else { | |
| // Fix for 4237875. We must ensure that we are sufficiently | |
| // deep in the path hierarchy to test parents this high up | |
| File thisPath = new File(ask); | |
| File nextPath = new File(ask + ".."); | |
| if (!thisPath.getCanonicalPath().equals(nextPath.getCanonicalPath())) | |
| checkSlashes(d, create, ans + "..", ask + ".."); | |
| } | |
| } | |
| } | |
| } |