changeset 5838:84cd98a5641c

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
author sherman
date Thu, 19 Jul 2012 21:23:53 -0700
parents 2c2e4ecc8f7e
children 5dc3f32c0d26
files make/java/nio/Makefile src/share/native/java/io/io_util.h src/solaris/classes/sun/nio/fs/BsdNativeDispatcher.java src/solaris/classes/sun/nio/fs/DefaultFileSystemProvider.java src/solaris/classes/sun/nio/fs/MacOSXFileSystem.java src/solaris/classes/sun/nio/fs/MacOSXFileSystemProvider.java src/solaris/classes/sun/nio/fs/MacOSXNativeDispatcher.java src/solaris/classes/sun/nio/fs/UnixFileSystem.java src/solaris/classes/sun/nio/fs/UnixPath.java src/solaris/native/java/io/UnixFileSystem_md.c src/solaris/native/java/io/io_util_md.c src/solaris/native/java/io/io_util_md.h src/solaris/native/sun/nio/fs/MacOSXNativeDispatcher.c test/java/io/File/MacPathTest.java test/java/io/File/MacPathTest.sh test/java/nio/file/Path/MacPathTest.java test/java/nio/file/Path/MacPathTest.sh
diffstat 17 files changed, 724 insertions(+), 80 deletions(-) [+]
line wrap: on
line diff
--- a/make/java/nio/Makefile	Thu Jul 19 18:19:04 2012 +0100
+++ b/make/java/nio/Makefile	Thu Jul 19 21:23:53 2012 -0700
@@ -282,6 +282,9 @@
 	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 @@
 	\
 	GnomeFileTypeDetector.c \
 	BsdNativeDispatcher.c \
+	MacOSXNativeDispatcher.c \
 	UnixCopyFile.c \
 	UnixNativeDispatcher.c \
 	\
@@ -385,7 +389,7 @@
 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 \
--- a/src/share/native/java/io/io_util.h	Thu Jul 19 18:19:04 2012 +0100
+++ b/src/share/native/java/io/io_util.h	Thu Jul 19 21:23:53 2012 -0700
@@ -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 throwFileNotFoundException(JNIEnv *env, jstring path);
 
 
-
 /*
  * Macros for managing platform strings.  The typical usage pattern is:
  *
@@ -88,35 +84,6 @@
  * 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 @@
     _##var##end: ;                                                            \
     } else ((void)NULL)
 
-#endif
-
 
 /* Macros for transforming Java Strings into native Unicode strings.
  * Works analogously to WITH_PLATFORM_STRING.
--- a/src/solaris/classes/sun/nio/fs/BsdNativeDispatcher.java	Thu Jul 19 18:19:04 2012 +0100
+++ b/src/solaris/classes/sun/nio/fs/BsdNativeDispatcher.java	Thu Jul 19 21:23:53 2012 -0700
@@ -33,7 +33,7 @@
  */
 
 class BsdNativeDispatcher extends UnixNativeDispatcher {
-    private BsdNativeDispatcher() { }
+    protected BsdNativeDispatcher() { }
 
    /**
     * struct fsstat_iter *getfsstat();
@@ -55,11 +55,6 @@
     private static native void initIDs();
 
     static {
-        AccessController.doPrivileged(new PrivilegedAction<Void>() {
-            public Void run() {
-                System.loadLibrary("nio");
-                return null;
-        }});
-        initIDs();
+         initIDs();
     }
 }
--- a/src/solaris/classes/sun/nio/fs/DefaultFileSystemProvider.java	Thu Jul 19 18:19:04 2012 +0100
+++ b/src/solaris/classes/sun/nio/fs/DefaultFileSystemProvider.java	Thu Jul 19 21:23:53 2012 -0700
@@ -69,7 +69,7 @@
         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");
     }
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/solaris/classes/sun/nio/fs/MacOSXFileSystem.java	Thu Jul 19 21:23:53 2012 -0700
@@ -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;
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/solaris/classes/sun/nio/fs/MacOSXFileSystemProvider.java	Thu Jul 19 21:23:53 2012 -0700
@@ -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);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/solaris/classes/sun/nio/fs/MacOSXNativeDispatcher.java	Thu Jul 19 21:23:53 2012 -0700
@@ -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);
+}
--- a/src/solaris/classes/sun/nio/fs/UnixFileSystem.java	Thu Jul 19 18:19:04 2012 +0100
+++ b/src/solaris/classes/sun/nio/fs/UnixFileSystem.java	Thu Jul 19 21:23:53 2012 -0700
@@ -302,7 +302,8 @@
         }
 
         // 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 @@
             }
         };
     }
+
     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 @@
             };
     }
 
+    // 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;
+    }
 }
--- a/src/solaris/classes/sun/nio/fs/UnixPath.java	Thu Jul 19 18:19:04 2012 +0100
+++ b/src/solaris/classes/sun/nio/fs/UnixPath.java	Thu Jul 19 21:23:53 2012 -0700
@@ -68,7 +68,7 @@
 
     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 @@
     }
 
     // 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<CharsetEncoder> ref = encoder.get();
         CharsetEncoder ce = (ref != null) ? ref.get() : null;
         if (ce == null) {
@@ -126,7 +126,7 @@
             encoder.set(new SoftReference<CharsetEncoder>(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 @@
             if (c1 != c2) {
                 return c1 - c2;
             }
-            k++;
+           k++;
         }
         return len1 - len2;
     }
@@ -757,8 +757,9 @@
     @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;
     }
 
--- a/src/solaris/native/java/io/UnixFileSystem_md.c	Thu Jul 19 18:19:04 2012 +0100
+++ b/src/solaris/native/java/io/UnixFileSystem_md.c	Thu Jul 19 21:23:53 2012 -0700
@@ -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 @@
                          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 @@
             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);
--- a/src/solaris/native/java/io/io_util_md.c	Thu Jul 19 18:19:04 2012 +0100
+++ b/src/solaris/native/java/io/io_util_md.c	Thu Jul 19 21:23:53 2012 -0700
@@ -34,37 +34,32 @@
 
 #include <CoreFoundation/CoreFoundation.h>
 
-static inline char *convertToNFD(const char *path, char *buf, size_t bufsize)
+__private_extern__
+jstring newStringPlatform(JNIEnv *env, const char* str)
 {
-    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)
-{
-    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
--- a/src/solaris/native/java/io/io_util_md.h	Thu Jul 19 18:19:04 2012 +0100
+++ b/src/solaris/native/java/io/io_util_md.h	Thu Jul 19 21:23:53 2012 -0700
@@ -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
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/solaris/native/sun/nio/fs/MacOSXNativeDispatcher.c	Thu Jul 19 21:23:53 2012 -0700
@@ -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 <stdlib.h>
+#include <string.h>
+
+#include <CoreFoundation/CoreFoundation.h>
+
+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;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/java/io/File/MacPathTest.java	Thu Jul 19 21:23:53 2012 -0700
@@ -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);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/java/io/File/MacPathTest.sh	Thu Jul 19 21:23:53 2012 -0700
@@ -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
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/java/nio/file/Path/MacPathTest.java	Thu Jul 19 21:23:53 2012 -0700
@@ -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<PosixFilePermission> 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<Path> 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<Path> 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);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/java/nio/file/Path/MacPathTest.sh	Thu Jul 19 21:23:53 2012 -0700
@@ -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