From 9e61768c9f0191d2d2a13af67e2906a63f052301 Mon Sep 17 00:00:00 2001 From: sherman Date: Thu, 19 Jul 2012 21:23:53 -0700 Subject: [PATCH] 7130915: File.equals does not give expected results when path contains Non-English characters on Mac OS X Summary: to support Unicode nfd/nfc file path on Macos Reviewed-by: alanb --- make/java/nio/Makefile | 6 +- src/share/native/java/io/io_util.h | 35 ---- .../sun/nio/fs/BsdNativeDispatcher.java | 9 +- .../sun/nio/fs/DefaultFileSystemProvider.java | 2 +- .../classes/sun/nio/fs/MacOSXFileSystem.java | 69 +++++++ .../sun/nio/fs/MacOSXFileSystemProvider.java | 45 +++++ .../sun/nio/fs/MacOSXNativeDispatcher.java | 41 ++++ .../classes/sun/nio/fs/UnixFileSystem.java | 25 ++- src/solaris/classes/sun/nio/fs/UnixPath.java | 13 +- .../native/java/io/UnixFileSystem_md.c | 9 + src/solaris/native/java/io/io_util_md.c | 47 +++-- src/solaris/native/java/io/io_util_md.h | 4 + .../sun/nio/fs/MacOSXNativeDispatcher.c | 76 ++++++++ test/java/io/File/MacPathTest.java | 166 ++++++++++++++++ test/java/io/File/MacPathTest.sh | 39 ++++ test/java/nio/file/Path/MacPathTest.java | 177 ++++++++++++++++++ test/java/nio/file/Path/MacPathTest.sh | 39 ++++ 17 files changed, 723 insertions(+), 79 deletions(-) create mode 100644 src/solaris/classes/sun/nio/fs/MacOSXFileSystem.java create mode 100644 src/solaris/classes/sun/nio/fs/MacOSXFileSystemProvider.java create mode 100644 src/solaris/classes/sun/nio/fs/MacOSXNativeDispatcher.java create mode 100644 src/solaris/native/sun/nio/fs/MacOSXNativeDispatcher.c create mode 100644 test/java/io/File/MacPathTest.java create mode 100644 test/java/io/File/MacPathTest.sh create mode 100644 test/java/nio/file/Path/MacPathTest.java create mode 100644 test/java/nio/file/Path/MacPathTest.sh diff --git a/make/java/nio/Makefile b/make/java/nio/Makefile index 9b9ef88a4..78b36de9c 100644 --- a/make/java/nio/Makefile +++ b/make/java/nio/Makefile @@ -282,6 +282,9 @@ FILES_java += \ sun/nio/fs/BsdFileSystem.java \ sun/nio/fs/BsdFileSystemProvider.java \ sun/nio/fs/BsdNativeDispatcher.java \ + sun/nio/fs/MacOSXFileSystemProvider.java \ + sun/nio/fs/MacOSXFileSystem.java \ + sun/nio/fs/MacOSXNativeDispatcher.java \ sun/nio/fs/PollingWatchService.java \ sun/nio/fs/UnixChannelFactory.java \ sun/nio/fs/UnixCopyFile.java \ @@ -311,6 +314,7 @@ FILES_c += \ \ GnomeFileTypeDetector.c \ BsdNativeDispatcher.c \ + MacOSXNativeDispatcher.c \ UnixCopyFile.c \ UnixNativeDispatcher.c \ \ @@ -385,7 +389,7 @@ ifeq ($(PLATFORM), linux) OTHER_LDLIBS += -L$(LIBDIR)/$(LIBARCH) -ljava -lnet -lpthread $(LIBDL) endif ifeq ($(PLATFORM), macosx) -OTHER_LDLIBS += -L$(LIBDIR) -ljava -lnet -pthread +OTHER_LDLIBS += -L$(LIBDIR) -ljava -lnet -pthread -framework CoreFoundation endif ifeq ($(PLATFORM), solaris) OTHER_LDLIBS += $(JVMLIB) $(LIBSOCKET) -lposix4 $(LIBDL) -lsendfile \ diff --git a/src/share/native/java/io/io_util.h b/src/share/native/java/io/io_util.h index fd7862a71..31088a946 100644 --- a/src/share/native/java/io/io_util.h +++ b/src/share/native/java/io/io_util.h @@ -25,9 +25,6 @@ #include "jni.h" #include "jni_util.h" -#ifdef MACOSX -char* convertToNFDIfNeeded(const char *origPath, char *buf, size_t bufsize); -#endif extern jfieldID IO_fd_fdID; extern jfieldID IO_handle_fdID; @@ -59,7 +56,6 @@ void fileOpen(JNIEnv *env, jobject this, jstring path, jfieldID fid, int flags); void throwFileNotFoundException(JNIEnv *env, jstring path); - /* * Macros for managing platform strings. The typical usage pattern is: * @@ -88,35 +84,6 @@ void throwFileNotFoundException(JNIEnv *env, jstring path); * declares a unique variable. */ -#ifdef MACOSX - -#define WITH_PLATFORM_STRING(env, strexp, var) \ - if (1) { \ - const char *var; \ - jstring _##var##str = (strexp); \ - if (_##var##str == NULL) { \ - JNU_ThrowNullPointerException((env), NULL); \ - goto _##var##end; \ - } \ - const char *temp_var = JNU_GetStringPlatformChars((env), _##var##str, NULL); \ - if (temp_var == NULL) goto _##var##end; \ - char buf[MAXPATHLEN]; \ - var = convertToNFDIfNeeded(temp_var, buf, sizeof(buf)); - -#define WITH_FIELD_PLATFORM_STRING(env, object, id, var) \ - WITH_PLATFORM_STRING(env, \ - ((object == NULL) \ - ? NULL \ - : (*(env))->GetObjectField((env), (object), (id))), \ - var) - -#define END_PLATFORM_STRING(env, var) \ - JNU_ReleaseStringPlatformChars(env, _##var##str, temp_var); \ - _##var##end: ; \ - } else ((void)NULL) - -#else - #define WITH_PLATFORM_STRING(env, strexp, var) \ if (1) { \ const char *var; \ @@ -140,8 +107,6 @@ void throwFileNotFoundException(JNIEnv *env, jstring path); _##var##end: ; \ } else ((void)NULL) -#endif - /* Macros for transforming Java Strings into native Unicode strings. * Works analogously to WITH_PLATFORM_STRING. diff --git a/src/solaris/classes/sun/nio/fs/BsdNativeDispatcher.java b/src/solaris/classes/sun/nio/fs/BsdNativeDispatcher.java index 74b8cc281..83b668cc2 100644 --- a/src/solaris/classes/sun/nio/fs/BsdNativeDispatcher.java +++ b/src/solaris/classes/sun/nio/fs/BsdNativeDispatcher.java @@ -33,7 +33,7 @@ import java.security.PrivilegedAction; */ class BsdNativeDispatcher extends UnixNativeDispatcher { - private BsdNativeDispatcher() { } + protected BsdNativeDispatcher() { } /** * struct fsstat_iter *getfsstat(); @@ -55,11 +55,6 @@ class BsdNativeDispatcher extends UnixNativeDispatcher { private static native void initIDs(); static { - AccessController.doPrivileged(new PrivilegedAction() { - public Void run() { - System.loadLibrary("nio"); - return null; - }}); - initIDs(); + initIDs(); } } diff --git a/src/solaris/classes/sun/nio/fs/DefaultFileSystemProvider.java b/src/solaris/classes/sun/nio/fs/DefaultFileSystemProvider.java index 9f8d47983..f0947c274 100644 --- a/src/solaris/classes/sun/nio/fs/DefaultFileSystemProvider.java +++ b/src/solaris/classes/sun/nio/fs/DefaultFileSystemProvider.java @@ -69,7 +69,7 @@ public class DefaultFileSystemProvider { if (osname.equals("Linux")) return createProvider("sun.nio.fs.LinuxFileSystemProvider"); if (osname.equals("Darwin") || osname.contains("OS X")) - return createProvider("sun.nio.fs.BsdFileSystemProvider"); + return createProvider("sun.nio.fs.MacOSXFileSystemProvider"); throw new AssertionError("Platform not recognized"); } } diff --git a/src/solaris/classes/sun/nio/fs/MacOSXFileSystem.java b/src/solaris/classes/sun/nio/fs/MacOSXFileSystem.java new file mode 100644 index 000000000..ab9888e7d --- /dev/null +++ b/src/solaris/classes/sun/nio/fs/MacOSXFileSystem.java @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2008, 2011, 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. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * 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. + */ + +package sun.nio.fs; + +import java.nio.file.*; +import java.io.IOException; +import java.util.*; +import java.util.regex.Pattern; +import java.security.AccessController; +import sun.security.action.GetPropertyAction; + +import static sun.nio.fs.MacOSXNativeDispatcher.*; + +/** + * MacOS implementation of FileSystem + */ + +class MacOSXFileSystem extends BsdFileSystem { + + MacOSXFileSystem(UnixFileSystemProvider provider, String dir) { + super(provider, dir); + } + + // match in unicode canon_eq + Pattern compilePathMatchPattern(String expr) { + return Pattern.compile(expr, Pattern.CANON_EQ) ; + } + + char[] normalizeNativePath(char[] path) { + for (char c : path) { + if (c > 0x80) + return normalizepath(path, kCFStringNormalizationFormD); + } + return path; + } + + String normalizeJavaPath(String path) { + for (int i = 0; i < path.length(); i++) { + if (path.charAt(i) > 0x80) + return new String(normalizepath(path.toCharArray(), + kCFStringNormalizationFormC)); + } + return path; + } + +} diff --git a/src/solaris/classes/sun/nio/fs/MacOSXFileSystemProvider.java b/src/solaris/classes/sun/nio/fs/MacOSXFileSystemProvider.java new file mode 100644 index 000000000..5e48c0052 --- /dev/null +++ b/src/solaris/classes/sun/nio/fs/MacOSXFileSystemProvider.java @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2008, 2011, 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. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * 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. + */ + +package sun.nio.fs; + +import java.nio.file.*; +import java.nio.file.attribute.*; +import java.io.IOException; + +/** + * MacOSX implementation of FileSystemProvider + */ + +public class MacOSXFileSystemProvider extends BsdFileSystemProvider { + public MacOSXFileSystemProvider() { + super(); + } + + @Override + MacOSXFileSystem newFileSystem(String dir) { + return new MacOSXFileSystem(this, dir); + } +} diff --git a/src/solaris/classes/sun/nio/fs/MacOSXNativeDispatcher.java b/src/solaris/classes/sun/nio/fs/MacOSXNativeDispatcher.java new file mode 100644 index 000000000..d591857a7 --- /dev/null +++ b/src/solaris/classes/sun/nio/fs/MacOSXNativeDispatcher.java @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2008, 2009, 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. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * 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. + */ + +package sun.nio.fs; + +import java.security.AccessController; +import java.security.PrivilegedAction; + +/** + * MacOSX specific system calls. + */ + +class MacOSXNativeDispatcher extends BsdNativeDispatcher { + private MacOSXNativeDispatcher() { } + + static final int kCFStringNormalizationFormC = 2; + static final int kCFStringNormalizationFormD = 0; + static native char[] normalizepath(char[] path, int form); +} diff --git a/src/solaris/classes/sun/nio/fs/UnixFileSystem.java b/src/solaris/classes/sun/nio/fs/UnixFileSystem.java index 28276e134..33ec2ba7b 100644 --- a/src/solaris/classes/sun/nio/fs/UnixFileSystem.java +++ b/src/solaris/classes/sun/nio/fs/UnixFileSystem.java @@ -302,7 +302,8 @@ abstract class UnixFileSystem } // return matcher - final Pattern pattern = Pattern.compile(expr); + final Pattern pattern = compilePathMatchPattern(expr); + return new PathMatcher() { @Override public boolean matches(Path path) { @@ -310,11 +311,10 @@ abstract class UnixFileSystem } }; } + private static final String GLOB_SYNTAX = "glob"; private static final String REGEX_SYNTAX = "regex"; - - @Override public final UserPrincipalLookupService getUserPrincipalLookupService() { return LookupService.instance; @@ -339,4 +339,23 @@ abstract class UnixFileSystem }; } + // Override if the platform has different path match requrement, such as + // case insensitive or Unicode canonical equal on MacOSX + Pattern compilePathMatchPattern(String expr) { + return Pattern.compile(expr); + } + + // Override if the platform uses different Unicode normalization form + // for native file path. For example on MacOSX, the native path is stored + // in Unicode NFD form. + char[] normalizeNativePath(char[] path) { + return path; + } + + // Override if the native file path use non-NFC form. For example on MacOSX, + // the native path is stored in Unicode NFD form, the path need to be + // normalized back to NFC before passed back to Java level. + String normalizeJavaPath(String path) { + return path; + } } diff --git a/src/solaris/classes/sun/nio/fs/UnixPath.java b/src/solaris/classes/sun/nio/fs/UnixPath.java index c06774345..b9410b4ee 100644 --- a/src/solaris/classes/sun/nio/fs/UnixPath.java +++ b/src/solaris/classes/sun/nio/fs/UnixPath.java @@ -68,7 +68,7 @@ class UnixPath UnixPath(UnixFileSystem fs, String input) { // removes redundant slashes and checks for invalid characters - this(fs, encode(normalizeAndCheck(input))); + this(fs, encode(fs, normalizeAndCheck(input))); } // package-private @@ -116,7 +116,7 @@ class UnixPath } // encodes the given path-string into a sequence of bytes - private static byte[] encode(String input) { + private static byte[] encode(UnixFileSystem fs, String input) { SoftReference ref = encoder.get(); CharsetEncoder ce = (ref != null) ? ref.get() : null; if (ce == null) { @@ -126,7 +126,7 @@ class UnixPath encoder.set(new SoftReference(ce)); } - char[] ca = input.toCharArray(); + char[] ca = fs.normalizeNativePath(input.toCharArray()); // size output buffer for worse-case size byte[] ba = new byte[(int)(ca.length * (double)ce.maxBytesPerChar())]; @@ -728,7 +728,7 @@ class UnixPath if (c1 != c2) { return c1 - c2; } - k++; + k++; } return len1 - len2; } @@ -757,8 +757,9 @@ class UnixPath @Override public String toString() { // OK if two or more threads create a String - if (stringValue == null) - stringValue = new String(path); // platform encoding + if (stringValue == null) { + stringValue = fs.normalizeJavaPath(new String(path)); // platform encoding + } return stringValue; } diff --git a/src/solaris/native/java/io/UnixFileSystem_md.c b/src/solaris/native/java/io/UnixFileSystem_md.c index 5ea55d81b..4309ffb06 100644 --- a/src/solaris/native/java/io/UnixFileSystem_md.c +++ b/src/solaris/native/java/io/UnixFileSystem_md.c @@ -38,6 +38,7 @@ #include "jlong.h" #include "jvm.h" #include "io_util.h" +#include "io_util_md.h" #include "java_io_FileSystem.h" #include "java_io_UnixFileSystem.h" @@ -80,7 +81,11 @@ Java_java_io_UnixFileSystem_canonicalize0(JNIEnv *env, jobject this, canonicalPath, JVM_MAXPATHLEN) < 0) { JNU_ThrowIOExceptionWithLastError(env, "Bad pathname"); } else { +#ifdef MACOSX + rv = newStringPlatform(env, canonicalPath); +#else rv = JNU_NewStringPlatform(env, canonicalPath); +#endif } } END_PLATFORM_STRING(env, path); return rv; @@ -311,7 +316,11 @@ Java_java_io_UnixFileSystem_list(JNIEnv *env, jobject this, if (JNU_CopyObjectArray(env, rv, old, len) < 0) goto error; (*env)->DeleteLocalRef(env, old); } +#ifdef MACOSX + name = newStringPlatform(env, ptr->d_name); +#else name = JNU_NewStringPlatform(env, ptr->d_name); +#endif if (name == NULL) goto error; (*env)->SetObjectArrayElement(env, rv, len++, name); (*env)->DeleteLocalRef(env, name); diff --git a/src/solaris/native/java/io/io_util_md.c b/src/solaris/native/java/io/io_util_md.c index 1d9abffe8..646e1305c 100644 --- a/src/solaris/native/java/io/io_util_md.c +++ b/src/solaris/native/java/io/io_util_md.c @@ -34,37 +34,32 @@ #include -static inline char *convertToNFD(const char *path, char *buf, size_t bufsize) -{ - CFMutableStringRef mutable = CFStringCreateMutable(NULL, 0); - CFStringAppendCString(mutable, path, kCFStringEncodingUTF8); - CFStringNormalize(mutable, kCFStringNormalizationFormD); - - CFStringGetCString(mutable, buf, bufsize, kCFStringEncodingUTF8); - - CFRelease(mutable); - return buf; -} - -/* Converts the path to NFD form if it was in NFC form. Returns a pointer to - * the converting string which could be buf (if the converstion took place) or - * origPath if no conversion was needed - */ __private_extern__ -char* convertToNFDIfNeeded(const char *origPath, char *buf, size_t bufsize) +jstring newStringPlatform(JNIEnv *env, const char* str) { - const char *current = origPath; - int c; - for (c = *current; c != 0; current++, c = *current) { - if (c < 0) { - // Need to convert - return convertToNFD(origPath, buf, bufsize); + jstring rv = NULL; + CFMutableStringRef csref = CFStringCreateMutable(NULL, 0); + if (csref == NULL) { + JNU_ThrowOutOfMemoryError(env, "native heap"); + } else { + CFStringAppendCString(csref, str, kCFStringEncodingUTF8); + CFStringNormalize(csref, kCFStringNormalizationFormC); + int clen = CFStringGetLength(csref); + int ulen = (clen + 1) * 2; // utf16 + zero padding + char* chars = malloc(ulen); + if (chars == NULL) { + CFRelease(csref); + JNU_ThrowOutOfMemoryError(env, "native heap"); + } else { + if (CFStringGetCString(csref, chars, ulen, kCFStringEncodingUTF16)) { + rv = (*env)->NewString(env, (jchar*)chars, clen); + } + free(chars); + CFRelease(csref); } } - - return (char *)origPath; + return rv; } - #endif void diff --git a/src/solaris/native/java/io/io_util_md.h b/src/solaris/native/java/io/io_util_md.h index 5c1160494..fbf8172b2 100644 --- a/src/solaris/native/java/io/io_util_md.h +++ b/src/solaris/native/java/io/io_util_md.h @@ -72,3 +72,7 @@ * IO helper function(s) */ void fileClose(JNIEnv *env, jobject this, jfieldID fid); + +#ifdef MACOSX +jstring newStringPlatform(JNIEnv *env, const char* str); +#endif diff --git a/src/solaris/native/sun/nio/fs/MacOSXNativeDispatcher.c b/src/solaris/native/sun/nio/fs/MacOSXNativeDispatcher.c new file mode 100644 index 000000000..50c5e2101 --- /dev/null +++ b/src/solaris/native/sun/nio/fs/MacOSXNativeDispatcher.c @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2008, 2009, 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. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * 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. + */ + +#include "jni.h" +#include "jni_util.h" +#include "jvm.h" +#include "jlong.h" + +#include +#include + +#include + +JNIEXPORT jcharArray JNICALL +Java_sun_nio_fs_MacOSXNativeDispatcher_normalizepath(JNIEnv* env, jclass this, + jcharArray path, + jint form) +{ + jcharArray result = NULL; + char chars_buf[(PATH_MAX + 1) * 2]; // utf16 + zero padding + CFMutableStringRef csref = CFStringCreateMutable(NULL, 0); + if (csref == NULL) { + JNU_ThrowOutOfMemoryError(env, "native heap"); + } else { + char *chars = (char*)(*env)->GetPrimitiveArrayCritical(env, path, 0); + jsize len = (*env)->GetArrayLength(env, path); + CFStringAppendCharacters(csref, (const UniChar*)chars, len); + (*env)->ReleasePrimitiveArrayCritical(env, path, chars, 0); + CFStringNormalize(csref, form); + len = CFStringGetLength(csref); + if (len < PATH_MAX) { + if (CFStringGetCString(csref, chars_buf, sizeof(chars_buf), kCFStringEncodingUTF16)) { + result = (*env)->NewCharArray(env, len); + (*env)->SetCharArrayRegion(env, result, 0, len, (jchar*)&chars_buf); + } + } else { + int ulen = (len + 1) * 2; + chars = malloc(ulen); + if (chars == NULL) { + CFRelease(csref); + JNU_ThrowOutOfMemoryError(env, "native heap"); + return result; + } else { + if (CFStringGetCString(csref, chars, ulen, kCFStringEncodingUTF16)) { + result = (*env)->NewCharArray(env, len); + (*env)->SetCharArrayRegion(env, result, 0, len, (jchar*)chars); + } + free(chars); + } + } + CFRelease(csref); + } + return result; +} diff --git a/test/java/io/File/MacPathTest.java b/test/java/io/File/MacPathTest.java new file mode 100644 index 000000000..9b310c18d --- /dev/null +++ b/test/java/io/File/MacPathTest.java @@ -0,0 +1,166 @@ +/* + * Copyright (c) 2008, 2012, 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. + */ + +/* @test + * @bug 7130915 + * @summary Tests file path with nfc/nfd forms on MacOSX + * @build MacPathTest + * @run shell MacPathTest.sh + */ + +import java.io.*; +import java.text.*; +import java.util.*; + +public class MacPathTest { + + public static void main(String args[]) throws Throwable { + String osname = System.getProperty("os.name"); + if (!osname.contains("OS X") && !osname.contains("Darwin")) + return; + + // English + test("TestDir_apple", // test dir + "dir_macosx", // dir + "file_macosx"); // file + + // Japanese composite character + test("TestDir_\u30c8\u30a4\u30e4\u30cb\u30ca\u30eb/", + "dir_\u30a4\u30c1\u30b4\u306e\u30b1\u30fc\u30ad", + "file_\u30a4\u30c1\u30b4\u306e\u30b1\u30fc\u30ad"); + + // latin-1 supplementory + test("TestDir_K\u00f6rperlich\u00e4\u00df/", + "dir_Entt\u00e4uschung", + "file_Entt\u00e4uschung"); + + test("TestDir_K\u00f6rperlich\u00e4\u00df/", + "dir_Entt\u00c4uschung", + "file_Entt\u00c4uschung"); + + // Korean syblla + test("TestDir_\uac00\uac01\uac02", + "dir_\uac20\uac21\uac22", + "file_\uacc0\uacc1\uacc2"); + } + + private static void removeAll(File file) throws Throwable { + if (file.isDirectory()) { + for (File f : file.listFiles()) { + removeAll(f); + } + } + file.delete(); + } + + private static boolean equal(Object x, Object y) { + return x == null ? y == null : x.equals(y); + } + + private static boolean match(File target, File src) { + if (target.equals(src)) { + String fname = target.toString(); + System.out.printf(" ->matched : [%s], length=%d%n", fname, fname.length()); + return true; + } + return false; + } + + private static void open_read(String what, File file) throws Throwable { + try (FileInputStream fis = new FileInputStream(file)) { + byte[] bytes = new byte[10]; + fis.read(bytes); + System.out.printf(" %s:%s%n", what, new String(bytes)); + } + } + + private static void test(String testdir, String dname, String fname_nfc) + throws Throwable + { + String fname = null; + String dname_nfd = Normalizer.normalize(dname, Normalizer.Form.NFD); + String fname_nfd = Normalizer.normalize(fname_nfc, Normalizer.Form.NFD); + + System.out.printf("%n%n--------Testing...----------%n"); + File base = new File(testdir); + File dir = new File(base, dname); + File dir_nfd = new File(base, dname_nfd); + File file_nfc = new File(base, fname_nfc); + File file_nfd = new File(base, fname_nfd); + + System.out.printf("base :[%s][len=%d]%n", testdir, testdir.length()); + System.out.printf("dir :[%s][len=%d]%n", dname, dname.length()); + System.out.printf("fname_nfc :[%s][len=%d]%n", fname_nfc, fname_nfc.length()); + System.out.printf("fname_nfd :[%s][len=%d]%n", fname_nfd, fname_nfd.length()); + + fname = file_nfc.toString(); + System.out.printf("file_nfc ->[%s][len=%d]%n", fname, fname.length()); + fname = file_nfd.toString(); + System.out.printf("file_nfd ->[%s][len=%d]%n%n", fname, fname.length()); + + removeAll(base); + dir.mkdirs(); + + fname = dir.toString(); + System.out.printf(":Directory [%s][len=%d] created%n", fname, fname.length()); + + ////////////////////////////////////////////////////////////// + if (!dir.isDirectory() || !dir_nfd.isDirectory()) { + throw new RuntimeException("File.isDirectory() failed"); + } + + ////////////////////////////////////////////////////////////// + // write to via nfd + try (FileOutputStream fos = new FileOutputStream(file_nfd)) { + fos.write('n'); fos.write('f'); fos.write('d'); + } + open_read("read in with nfc (from nfd)", file_nfc); + file_nfd.delete(); + + ////////////////////////////////////////////////////////////// + // write to with nfc + try (FileOutputStream fos = new FileOutputStream(file_nfc)) { + fos.write('n'); fos.write('f'); fos.write('c'); + } + open_read("read in with nfd (from nfc)", file_nfd); + //file_nfc.delete(); + + ////////////////////////////////////////////////////////////// + boolean found_dir = false; + boolean found_file_nfc = false; + boolean found_file_nfd = false; + + for (File f : base.listFiles()) { + fname = f.toString(); + System.out.printf("Found : [%s], length=%d%n", fname, fname.length()); + found_dir |= match(dir, f); + found_file_nfc |= match(file_nfc, f); + found_file_nfd |= match(file_nfd, f); + } + + if (!found_dir || !found_file_nfc || !found_file_nfc) { + throw new RuntimeException("File.equal() failed"); + } + removeAll(base); + } +} diff --git a/test/java/io/File/MacPathTest.sh b/test/java/io/File/MacPathTest.sh new file mode 100644 index 000000000..ebba514af --- /dev/null +++ b/test/java/io/File/MacPathTest.sh @@ -0,0 +1,39 @@ +#! /bin/sh + +# +# Copyright (c) 2012, 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. +# +# +OS=`uname -s` +case "$OS" in + Darwin ) ;; + * ) + exit 0 + ;; +esac + +if [ "x$TESTJAVA" = x ]; then + TESTJAVA=$1; shift + TESTCLASSES=. +fi + +export LC_ALL=en_US.UTF-8 ;${TESTJAVA}/bin/java -cp ${TESTCLASSES} MacPathTest diff --git a/test/java/nio/file/Path/MacPathTest.java b/test/java/nio/file/Path/MacPathTest.java new file mode 100644 index 000000000..3bfa14a68 --- /dev/null +++ b/test/java/nio/file/Path/MacPathTest.java @@ -0,0 +1,177 @@ +/* + * Copyright (c) 2008, 2012, 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. + */ + +/* @test + * @bug 7130915 + * @summary Tests file path with nfc/nfd forms on MacOSX + * @library ../ + * @build MacPathTest + * @run shell MacPathTest.sh + */ + +import java.nio.file.*; +import java.nio.file.attribute.*; +import java.text.*; +import java.util.*; +import java.util.regex.*; + +public class MacPathTest { + + public static void main(String args[]) throws Throwable { + String osname = System.getProperty("os.name"); + if (!osname.contains("OS X") && !osname.contains("Darwin")) + return; + System.out.printf("sun.jnu.encoding=%s, file.encoding=%s%n", + System.getProperty("file.encoding"), + System.getProperty("sun.jnu.encoding")); + // English + test("TestDir_apple", // test dir + "dir_macosx", // dir + "file_macosx"); // file + + // Japanese composite character + test("TestDir_\u30c8\u30a4\u30e4\u30cb\u30ca\u30eb/", + "dir_\u30a4\u30c1\u30b4\u306e\u30b1\u30fc\u30ad", + "file_\u30a4\u30c1\u30b4\u306e\u30b1\u30fc\u30ad"); + + // latin-1 supplementory + test("TestDir_K\u00f6rperlich\u00e4\u00df/", + "dir_Entt\u00e4uschung", + "file_Entt\u00e4uschung"); + + test("TestDir_K\u00f6rperlich\u00e4\u00df/", + "dir_Entt\u00c4uschung", + "file_Entt\u00c4uschung"); + + // Korean syblla + test("TestDir_\uac00\uac01\uac02", + "dir_\uac20\uac21\uac22", + "file_\uacc0\uacc1\uacc2"); + } + + private static boolean equal(Object x, Object y) { + return x == null ? y == null : x.equals(y); + } + + private static boolean match(Path target, Path src) { + String fname = target.toString(); + System.out.printf(" --> Trying [%s], length=%d...", fname, fname.length()); + if (target.equals(src)) { + System.out.println(" MATCHED!"); + return true; + } else { + System.out.println(" NOT MATCHED!"); + } + return false; + } + + private static void test(String testdir, String dname, String fname_nfc) + throws Throwable + { + String fname = null; + String dname_nfd = Normalizer.normalize(dname, Normalizer.Form.NFD); + String fname_nfd = Normalizer.normalize(fname_nfc, Normalizer.Form.NFD); + + System.out.printf("%n%n--------Testing...----------%n"); + Path bpath = Paths.get(testdir); + Path dpath = Paths.get(testdir, dname); + Path dpath_nfd = Paths.get(testdir, dname_nfd); + Path fpath_nfc = Paths.get(testdir, fname_nfc); + Path fpath_nfd = Paths.get(testdir, fname_nfd); + + if (Files.exists(bpath)) + TestUtil.removeAll(bpath); + Files.createDirectories(dpath); + + fname = dpath.toString(); + System.out.printf(":Directory [%s][len=%d] created%n", fname, fname.length()); + + ////////////////////////////////////////////////////////////// + if (!Files.isDirectory(dpath) || !Files.isDirectory(dpath_nfd)) { + throw new RuntimeException("Files.isDirectory(...) failed"); + } + + ////////////////////////////////////////////////////////////// + // write out with nfd, read in with nfc + case + Files.write(fpath_nfd, new byte[] { 'n', 'f', 'd'}); + System.out.println(" read in with nfc (from nfd):" + new String(Files.readAllBytes(fpath_nfc))); + + // check attrs with nfc + case + Set pfp = Files.getPosixFilePermissions(fpath_nfd); + if (!equal(pfp, Files.getPosixFilePermissions(fpath_nfc)) ) { + throw new RuntimeException("Files.getPosixfilePermission(...) failed"); + } + Files.delete(fpath_nfd); + + // write out with nfc, read in with nfd + case + Files.write(fpath_nfc, new byte[] { 'n', 'f', 'c'}); + System.out.println(" read in with nfd (from nfc):" + new String(Files.readAllBytes(fpath_nfd))); + + // check attrs with nfc + case + pfp = Files.getPosixFilePermissions(fpath_nfc); + if (!equal(pfp, Files.getPosixFilePermissions(fpath_nfd))) { + throw new RuntimeException("Files.getPosixfilePermission(...) failed"); + } + ////////////////////////////////////////////////////////////// + boolean found_dir = false; + boolean found_file_nfc = false; + boolean found_file_nfd = false; + try (DirectoryStream stream = Files.newDirectoryStream(bpath)) { + for (Path path: stream) { + fname = path.toString(); + System.out.printf("Found : [%s], length=%d%n", fname, fname.length()); + found_dir |= match(dpath, path); + found_file_nfc |= match(fpath_nfc, path); + found_file_nfd |= match(fpath_nfd, path); + } + } + if (!found_dir || !found_file_nfc || !found_file_nfd) { + throw new RuntimeException("File.equal() failed"); + } + // glob + String glob = "*" + fname_nfd.substring(2); // remove leading "FI" from "FILE..." + System.out.println("glob=" + glob); + boolean globmatched = false; + try (DirectoryStream stream = Files.newDirectoryStream(bpath, glob)) { + for (Path path: stream) { + fname = path.toString(); + System.out.printf("PathMatch : [%s], length=%d%n", fname, fname.length()); + globmatched |= match(fpath_nfc, path); + } + } + if (!globmatched) { + //throw new RuntimeException("path matcher failed"); + // it appears we have a regex.anon_eq bug in hangul syllable handling + System.out.printf("pathmatcher failed, glob=[%s]%n", glob); + System.out.printf(" -> fname_nfd.matches(fname_nfc)=%b%n", + Pattern.compile(fname_nfd, Pattern.CANON_EQ) + .matcher(fname_nfc) + .matches()); + System.out.printf(" -> fname_nfc.matches(fname_nfd)=%b%n", + Pattern.compile(fname_nfc, Pattern.CANON_EQ) + .matcher(fname_nfd) + .matches()); + } + TestUtil.removeAll(bpath); + } +} diff --git a/test/java/nio/file/Path/MacPathTest.sh b/test/java/nio/file/Path/MacPathTest.sh new file mode 100644 index 000000000..e81c62ae9 --- /dev/null +++ b/test/java/nio/file/Path/MacPathTest.sh @@ -0,0 +1,39 @@ +#! /bin/sh + +# +# Copyright (c) 2012, 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. +# +# +OS=`uname -s` +case "$OS" in + Darwin ) ;; + * ) + exit 0 + ;; +esac + +if [ "x$TESTJAVA" = x ]; then + TESTJAVA=$1; shift + TESTCLASSES=. +fi + +export LC_ALL=en_US.UTF-8; ${TESTJAVA}/bin/java -cp ${TESTCLASSES} MacPathTest -- GitLab