1/* 2 * Copyright (c) 2004, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26#include <stdlib.h> 27#include <string.h> 28#include <malloc.h> 29 30#include "FileSystemSupport_md.h" 31 32/* 33 * Windows implementation of file system support functions 34 */ 35 36#define slash '\\' 37#define altSlash '/' 38 39static int isSlash(char c) { 40 return (c == '\\') || (c == '/'); 41} 42 43static int isLetter(char c) { 44 return ((c >= 'a') && (c <= 'z')) || ((c >= 'A') && (c <= 'Z')); 45} 46 47char pathSeparator() { 48 return ';'; 49} 50 51/* filename are case insensitive on windows */ 52int filenameStrcmp(const char* s1, const char* s2) { 53 return strcasecmp(s1, s2); 54} 55 56char* basePath(const char* path) { 57 char* pos = strchr(path, slash); 58 char* last = NULL; 59 while (pos != NULL) { 60 last = pos; 61 pos++; 62 pos = strchr(pos, slash); 63 } 64 if (last == NULL) { 65 return (char*)path; 66 } else { 67 int len = (int)(last - path); 68 char* str = (char*)malloc(len+1); 69 if (len > 0) { 70 memcpy(str, path, len); 71 } 72 str[len] = '\0'; 73 return str; 74 } 75} 76 77 78 79/* -- Normalization - src/windows/classes/java/io/Win32FileSystem.java */ 80 81 82/* A normal Win32 pathname contains no duplicate slashes, except possibly 83 * for a UNC prefix, and does not end with a slash. It may be the empty 84 * string. Normalized Win32 pathnames have the convenient property that 85 * the length of the prefix almost uniquely identifies the type of the path 86 * and whether it is absolute or relative: 87 * 88 * 0 relative to both drive and directory 89 * 1 drive-relative (begins with '\\') 90 * 2 absolute UNC (if first char is '\\'), 91 * else directory-relative (has form "z:foo") 92 * 3 absolute local pathname (begins with "z:\\") 93 */ 94static int normalizePrefix(const char* path, int len, char* sb, int* sbLen) { 95 char c; 96 int src = 0; 97 while ((src < len) && isSlash(path[src])) src++; 98 if ((len - src >= 2) 99 && isLetter(c = path[src]) 100 && path[src + 1] == ':') { 101 /* Remove leading slashes if followed by drive specifier. 102 This hack is necessary to support file URLs containing drive 103 specifiers (e.g., "file://c:/path"). As a side effect, 104 "/c:/path" can be used as an alternative to "c:/path". */ 105 sb[(*sbLen)++] = c; 106 sb[(*sbLen)++] = ':'; 107 src += 2; 108 } else { 109 src = 0; 110 if ((len >= 2) 111 && isSlash(path[0]) 112 && isSlash(path[1])) { 113 /* UNC pathname: Retain first slash; leave src pointed at 114 second slash so that further slashes will be collapsed 115 into the second slash. The result will be a pathname 116 beginning with "\\\\" followed (most likely) by a host 117 name. */ 118 src = 1; 119 sb[(*sbLen)++] = slash; 120 } 121 } 122 return src; 123} 124 125/* 126 * Normalize the given pathname, whose length is len, starting at the given 127 * offset; everything before this offset is already normal. 128 */ 129static char* normalizePath(const char* path, int len, int off) { 130 int src; 131 char* sb; 132 int sbLen; 133 134 if (len == 0) return (char*)path; 135 if (off < 3) off = 0; /* Avoid fencepost cases with UNC pathnames */ 136 137 sb = (char*)malloc(len+1); 138 sbLen = 0; 139 140 if (off == 0) { 141 /* Complete normalization, including prefix */ 142 src = normalizePrefix(path, len, sb, &sbLen); 143 } else { 144 /* Partial normalization */ 145 src = off; 146 memcpy(sb+sbLen, path, off); 147 sbLen += off; 148 } 149 150 /* Remove redundant slashes from the remainder of the path, forcing all 151 slashes into the preferred slash */ 152 while (src < len) { 153 char c = path[src++]; 154 if (isSlash(c)) { 155 while ((src < len) && isSlash(path[src])) src++; 156 if (src == len) { 157 /* Check for trailing separator */ 158 if ((sbLen == 2) && (sb[1] == ':')) { 159 /* "z:\\" */ 160 sb[sbLen++] = slash; 161 break; 162 } 163 if (sbLen == 0) { 164 /* "\\" */ 165 sb[sbLen++] = slash; 166 break; 167 } 168 if ((sbLen == 1) && (isSlash(sb[0]))) { 169 /* "\\\\" is not collapsed to "\\" because "\\\\" marks 170 the beginning of a UNC pathname. Even though it is 171 not, by itself, a valid UNC pathname, we leave it as 172 is in order to be consistent with the win32 APIs, 173 which treat this case as an invalid UNC pathname 174 rather than as an alias for the root directory of 175 the current drive. */ 176 sb[sbLen++] = slash; 177 break; 178 } 179 /* Path does not denote a root directory, so do not append 180 trailing slash */ 181 break; 182 } else { 183 sb[sbLen++] = slash; 184 } 185 } else { 186 sb[sbLen++] = c; 187 } 188 } 189 190 sb[sbLen] = '\0'; 191 return sb; 192} 193 194/* 195 * Check that the given pathname is normal. If not, invoke the real 196 * normalizer on the part of the pathname that requires normalization. 197 * This way we iterate through the whole pathname string only once. 198 */ 199char* normalize(char* path) { 200 int n = (int)strlen(path); 201 int i; 202 char c = 0; 203 int prev = 0; 204 for (i = 0; i < n; i++) { 205 char c = path[i]; 206 if (c == altSlash) 207 return normalizePath(path, n, (prev == slash) ? i - 1 : i); 208 if ((c == slash) && (prev == slash) && (i > 1)) 209 return normalizePath(path, n, i - 1); 210 if ((c == ':') && (i > 1)) 211 return normalizePath(path, n, 0); 212 prev = c; 213 } 214 if (prev == slash) 215 return normalizePath(path, n, n - 1); 216 return path; 217} 218 219 220/* -- Resolution - src/windows/classes/java/io/Win32FileSystem.java */ 221 222 223char* resolve(const char* parent, const char* child) { 224 char* c; 225 char* theChars; 226 int parentEnd, childStart, len; 227 228 int pn = (int)strlen(parent); 229 int cn = (int)strlen(child); 230 231 if (pn == 0) return (char*)child; 232 if (cn == 0) return (char*)parent; 233 234 c = (char*)child; 235 childStart = 0; 236 parentEnd = pn; 237 238 if ((cn > 1) && (c[0] == slash)) { 239 if (c[1] == slash) { 240 /* Drop prefix when child is a UNC pathname */ 241 childStart = 2; 242 } else { 243 /* Drop prefix when child is drive-relative */ 244 childStart = 1; 245 246 } 247 if (cn == childStart) { // Child is double slash 248 if (parent[pn - 1] == slash) { 249 char* str = strdup(parent); 250 str[pn-1] = '\0'; 251 return str; 252 } 253 return (char*)parent; 254 } 255 } 256 257 if (parent[pn - 1] == slash) 258 parentEnd--; 259 260 len = parentEnd + cn - childStart; 261 262 if (child[childStart] == slash) { 263 theChars = (char*)malloc(len+1); 264 memcpy(theChars, parent, parentEnd); 265 memcpy(theChars+parentEnd, child+childStart, (cn-childStart)); 266 theChars[len] = '\0'; 267 } else { 268 theChars = (char*)malloc(len+2); 269 memcpy(theChars, parent, parentEnd); 270 theChars[parentEnd] = slash; 271 memcpy(theChars+parentEnd+1, child+childStart, (cn-childStart)); 272 theChars[len+1] = '\0'; 273 } 274 return theChars; 275} 276 277 278static int prefixLength(const char* path) { 279 char c0, c1; 280 281 int n = (int)strlen(path); 282 if (n == 0) return 0; 283 c0 = path[0]; 284 c1 = (n > 1) ? path[1] : 0; 285 if (c0 == slash) { 286 if (c1 == slash) return 2; /* Absolute UNC pathname "\\\\foo" */ 287 return 1; /* Drive-relative "\\foo" */ 288 } 289 if (isLetter(c0) && (c1 == ':')) { 290 if ((n > 2) && (path[2] == slash)) 291 return 3; /* Absolute local pathname "z:\\foo" */ 292 return 2; /* Directory-relative "z:foo" */ 293 } 294 return 0; /* Completely relative */ 295} 296 297 298int isAbsolute(const char* path) { 299 int pl = prefixLength(path); 300 return (((pl == 2) && (path[0] == slash)) || (pl == 3)); 301} 302 303 304char* fromURIPath(const char* path) { 305 int start = 0; 306 int len = (int)strlen(path); 307 308 if ((len > 2) && (path[2] == ':')) { 309 // "/c:/foo" --> "c:/foo" 310 start = 1; 311 // "c:/foo/" --> "c:/foo", but "c:/" --> "c:/" 312 if ((len > 3) && path[len-1] == '/') 313 len--; 314 } else if ((len > 1) && path[len-1] == '/') { 315 // "/foo/" --> "/foo" 316 len--; 317 } 318 319 if (start == 0 && len == (int)strlen(path)) { 320 return (char*)path; 321 } else { 322 char* p = (char*)malloc(len+1); 323 if (p != NULL) { 324 memcpy(p, path+start, len); 325 p[len] = '\0'; 326 } 327 return p; 328 } 329} 330