src/windows/classes/java/io/WinNTFileSystem.java

Print this page

        

@@ -1,7 +1,7 @@
 /*
- * Copyright (c) 2001, 2012, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2001, 2013, 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

@@ -72,137 +72,171 @@
     @Override
     public char getPathSeparator() {
         return semicolon;
     }
 
-    /* Check that the given pathname is normal.  If not, invoke the real
-       normalizer on the part of the pathname that requires normalization.
-       This way we iterate through the whole pathname string only once. */
+    /*
+     * Check whether the given path name is normal.  If not, invoke the real
+     * normalizer on the part of the path name that requires normalization.
+     * The following logic iterates the whole path name string only once.
+     */
     @Override
     public String normalize(String path) {
-        int n = path.length();
-        char slash = this.slash;
-        char altSlash = this.altSlash;
+        final int len = path.length();
+        if (len == 0) return path;
+
+        StringBuilder sb = new StringBuilder(len);
+        int sbLen = 0;
         char prev = 0;
-        for (int i = 0; i < n; i++) {
+        int prevIdx = -1;
+
+        for (int i = 0; i < len; i++) {
             char c = path.charAt(i);
-            if (c == altSlash)
-                return normalize(path, n, (prev == slash) ? i - 1 : i);
-            if ((c == slash) && (prev == slash) && (i > 1))
-                return normalize(path, n, i - 1);
-            if ((c == ':') && (i > 1))
-                return normalize(path, n, 0);
+            if (c == CHAR_NUL) continue;
+
+            // Force to the preferred slash
+            if (c == altSlash) c = slash;
+
+            sbLen = sb.length();
+            if ((c == slash) && (prev == slash) && (sbLen > 1)) {
+                sb.setLength(sbLen - 1);
+                return normalize(path, prevIdx, sb);
+            }
+
+            // If a path starts with a disk designator, like "c:", no
+            // normalization is needed. But if a disk designator is not at
+            // the beginning, normalization will be triggered to normalize
+            // the prefix. StringBuilder, sb, will be cleared in the real
+            // normalizer.
+            if ((c == ':') && isLetter(prev) && (sbLen > 1))
+                return normalize(path, 0, sb);
+
+            sb.append(c);
             prev = c;
+            prevIdx = i;
         }
-        if (prev == slash) return normalize(path, n, n - 1);
-        return path;
+
+        sbLen = sb.length();
+        if (prev == slash && sbLen > 1) {
+            sb.setLength(sbLen - 1);
+            return normalize(path, prevIdx, sb);
     }
 
-    /* Normalize the given pathname, whose length is len, starting at the given
-       offset; everything before this offset is already normal. */
-    private String normalize(String path, int len, int off) {
-        if (len == 0) return path;
-        if (off < 3) off = 0;   /* Avoid fencepost cases with UNC pathnames */
-        int src;
-        char slash = this.slash;
-        StringBuffer sb = new StringBuffer(len);
+        return sb.toString();
+    }
 
-        if (off == 0) {
+    /*
+     * Normalize a path name, starting at the given offset.
+     * Everything before this offset is already normal.
+     */
+    private String normalize(String path, int off, StringBuilder sb) {
+        final int len = path.length();
+        /* Avoid fencepost cases with UNC pathnames */
+        int src = sb.length() < 3 ? 0 : off;
+
+        if (src == 0) {
             /* Complete normalization, including prefix */
-            src = normalizePrefix(path, len, sb);
-        } else {
-            /* Partial normalization */
-            src = off;
-            sb.append(path.substring(0, off));
+            sb.setLength(0);
+            src = normalizePrefix(path, sb);
         }
 
-        /* Remove redundant slashes from the remainder of the path, forcing all
+        /* Partial normalization
+           Remove redundant slashes from the remainder of the path, and force all
            slashes into the preferred slash */
+        char prev = 0;
+        char c;
         while (src < len) {
-            char c = path.charAt(src++);
+            c = path.charAt(src++);
+            if (c == CHAR_NUL) continue;
             if (isSlash(c)) {
-                while ((src < len) && isSlash(path.charAt(src))) src++;
-                if (src == len) {
-                    /* Check for trailing separator */
-                    int sn = sb.length();
-                    if ((sn == 2) && (sb.charAt(1) == ':')) {
-                        /* "z:\\" */
-                        sb.append(slash);
-                        break;
+                if (prev == slash)
+                    continue;
+                else if (c == altSlash)
+                    c = slash;
                     }
-                    if (sn == 0) {
-                        /* "\\" */
-                        sb.append(slash);
-                        break;
-                    }
-                    if ((sn == 1) && (isSlash(sb.charAt(0)))) {
-                        /* "\\\\" is not collapsed to "\\" because "\\\\" marks
-                           the beginning of a UNC pathname.  Even though it is
-                           not, by itself, a valid UNC pathname, we leave it as
-                           is in order to be consistent with the win32 APIs,
-                           which treat this case as an invalid UNC pathname
-                           rather than as an alias for the root directory of
-                           the current drive. */
-                        sb.append(slash);
-                        break;
-                    }
-                    /* Path does not denote a root directory, so do not append
-                       trailing slash */
-                    break;
-                } else {
-                    sb.append(slash);
-                }
-            } else {
+
                 sb.append(c);
+            prev = c;
+        }
+
+        /* Handle the trailing slash
+         * Note, "\\\\" is not collapsed to "\\" because "\\\\" marks the
+         * beginning of a UNC pathname.  Even though it is not, by itself, a
+         * valid UNC pathname, we leave it as is in order to be consistent with
+         * the win32 APIs, which treat this case as an invalid UNC pathname
+         * rather than as an alias for the root directory of the current drive.
+         */
+        if (prev == slash) {
+            int sbLen = sb.length();
+            if (!(sbLen == 1 ||
+                 (sbLen == 2 && sb.charAt(0) == slash) ||
+                 (sbLen == 3 && isLetter(sb.charAt(0)) && sb.charAt(1) == ':')))
+            {
+                sb.setLength(sbLen - 1);
             }
         }
 
-        String rv = sb.toString();
-        return rv;
+        return sb.toString();
     }
 
     /* A normal Win32 pathname contains no duplicate slashes, except possibly
        for a UNC prefix, and does not end with a slash.  It may be the empty
-       string.  Normalized Win32 pathnames have the convenient property that
+       string.  Normalized Win32 path names have the convenient property that
        the length of the prefix almost uniquely identifies the type of the path
        and whether it is absolute or relative:
 
            0  relative to both drive and directory
            1  drive-relative (begins with '\\')
            2  absolute UNC (if first char is '\\'),
                 else directory-relative (has form "z:foo")
            3  absolute local pathname (begins with "z:\\")
      */
-    private int normalizePrefix(String path, int len, StringBuffer sb) {
-        int src = 0;
-        while ((src < len) && isSlash(path.charAt(src))) src++;
+    private int normalizePrefix(String path, StringBuilder sb) {
+        final int len = path.length();
+        // Remove leading NUL chars
+        int start = -1;
+        while (++start < len && path.charAt(start) == CHAR_NUL);
+
+        // Normalize disk designator preifx
+        int src = start;
+        // Remove slashes and NUL chars
+        while (src < len &&
+                (isSlash(path.charAt(src)) || path.charAt(src) == CHAR_NUL))
+        {
+            src++;
+        }
+
         char c;
-        if ((len - src >= 2)
-            && isLetter(c = path.charAt(src))
-            && path.charAt(src + 1) == ':') {
+        if ((len - src >= 2) && isLetter(c = path.charAt(src))) {
+            // Find the next non-NUL char
+            while (++src < len && path.charAt(src) == CHAR_NUL);
+            if (src < len && path.charAt(src) == ':') {
             /* Remove leading slashes if followed by drive specifier.
                This hack is necessary to support file URLs containing drive
                specifiers (e.g., "file://c:/path").  As a side effect,
                "/c:/path" can be used as an alternative to "c:/path". */
             sb.append(c);
             sb.append(':');
-            src += 2;
-        } else {
-            src = 0;
-            if ((len >= 2)
-                && isSlash(path.charAt(0))
-                && isSlash(path.charAt(1))) {
+                return src + 1;
+            }
+        }
+
+        // Normalize UNC prefix
+        src = start;
+        if ((len - src >= 2) && isSlash(path.charAt(src))) {
+            while (++src < len && path.charAt(src) == CHAR_NUL);
+            if (src < len && isSlash(path.charAt(src))) {
                 /* UNC pathname: Retain first slash; leave src pointed at
                    second slash so that further slashes will be collapsed
                    into the second slash.  The result will be a pathname
                    beginning with "\\\\" followed (most likely) by a host
                    name. */
-                src = 1;
                 sb.append(slash);
+                return src;
             }
         }
-        return src;
+        return start;
     }
 
     @Override
     public int prefixLength(String path) {
         char slash = this.slash;