diff --git a/src/java.base/unix/classes/java/io/UnixFileSystem.java b/src/java.base/unix/classes/java/io/UnixFileSystem.java index 501b5d8883b..21ccc38f076 100644 --- a/src/java.base/unix/classes/java/io/UnixFileSystem.java +++ b/src/java.base/unix/classes/java/io/UnixFileSystem.java @@ -107,15 +107,23 @@ public int prefixLength(String pathname) { return pathname.startsWith("/") ? 1 : 0; } + // Remove trailing '/' if present and there are at least two characters + private static String trimSeparator(String s) { + int len = s.length(); + if (len > 1 && s.charAt(len - 1) == '/') + return s.substring(0, len - 1); + return s; + } + @Override public String resolve(String parent, String child) { if (child.isEmpty()) return parent; if (child.charAt(0) == '/') { if (parent.equals("/")) return child; - return parent + child; + return trimSeparator(parent + child); } - if (parent.equals("/")) return parent + child; - return parent + '/' + child; + if (parent.equals("/")) return trimSeparator(parent + child); + return trimSeparator(parent + '/' + child); } @Override diff --git a/src/java.base/windows/classes/java/io/WinNTFileSystem.java b/src/java.base/windows/classes/java/io/WinNTFileSystem.java index 0fabd6f2b22..710d832cba8 100644 --- a/src/java.base/windows/classes/java/io/WinNTFileSystem.java +++ b/src/java.base/windows/classes/java/io/WinNTFileSystem.java @@ -287,6 +287,13 @@ public String resolve(String parent, String child) { theChars[parentEnd] = slash; child.getChars(childStart, cn, theChars, parentEnd + 1); } + + // if present, strip trailing name separator unless after a ':' + if (theChars.length > 1 + && theChars[theChars.length - 1] == slash + && theChars[theChars.length - 2] != ':') + return new String(theChars, 0, theChars.length - 1); + return new String(theChars); } diff --git a/test/jdk/java/io/File/Cons.java b/test/jdk/java/io/File/Cons.java index 05433910549..560321084ab 100644 --- a/test/jdk/java/io/File/Cons.java +++ b/test/jdk/java/io/File/Cons.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 2023, 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 @@ -22,7 +22,7 @@ */ /* @test - @bug 4131169 4168988 + @bug 4131169 4168988 8290499 @summary Basic File constructor tests */ @@ -251,11 +251,17 @@ static void testUnix() throws Exception { /* Two-arg constructors cases, string parent */ if (!old) { ck2("//foo", "bar", "/foo", "bar", "/foo/bar"); + ck2("/foo", "/", "/", "foo", "/foo"); + ck2("//foo", "/", "/", "foo", "/foo"); + ck2("/foo", "//", "/", "foo", "/foo"); } /* Two-arg constructor cases, File parent */ if (!old) { ck2f("//foo", "bar", "/foo", "bar", "/foo/bar"); + ck2f("/foo", "/", "/", "foo", "/foo"); + ck2f("//foo", "/", "/", "foo", "/foo"); + ck2f("/foo", "//", "/", "foo", "/foo"); } File f = new File("/foo"); @@ -296,10 +302,17 @@ static void testWin32() throws Exception { ck2("z:/", "//foo", "z:/", "foo", "z:/foo"); ck2("z:/", "foo/", "z:/", "foo", "z:/foo"); ck2("//foo", "bar", "//foo", "bar", "//foo/bar"); + ck2("z:", "", null, "", "z:"); + ck2("z:/", "/", null, "", "z:/"); + ck2("z:/", "a/b/c", "z:/a/b", "c", "z:/a/b/c"); + ck2("z:/", "a/b/c/", "z:/a/b", "c", "z:/a/b/c"); /* Two-arg constructor cases, File parent */ ck2f("//foo", "bar", "//foo", "bar", "//foo/bar"); - + ck2f("z:", "", null, "", "z:"); + ck2f("z:/", "/", null, "", "z:/"); + ck2f("z:/", "a/b/c", "z:/a/b", "c", "z:/a/b/c"); + ck2f("z:/", "a/b/c/", "z:/a/b", "c", "z:/a/b/c"); } }