Merge branch 'nd/dwim-wildcards-as-pathspecs'
[gitweb.git] / compat / mingw.c
index 717931e35920c3546df9f68148cbe20050d3f004..fbe69b874b06259e1d699572ace7dd8d2c4667c6 100644 (file)
@@ -454,6 +454,39 @@ static inline time_t filetime_to_time_t(const FILETIME *ft)
        return (time_t)(filetime_to_hnsec(ft) / 10000000);
 }
 
+/**
+ * Verifies that safe_create_leading_directories() would succeed.
+ */
+static int has_valid_directory_prefix(wchar_t *wfilename)
+{
+       int n = wcslen(wfilename);
+
+       while (n > 0) {
+               wchar_t c = wfilename[--n];
+               DWORD attributes;
+
+               if (!is_dir_sep(c))
+                       continue;
+
+               wfilename[n] = L'\0';
+               attributes = GetFileAttributesW(wfilename);
+               wfilename[n] = c;
+               if (attributes == FILE_ATTRIBUTE_DIRECTORY ||
+                               attributes == FILE_ATTRIBUTE_DEVICE)
+                       return 1;
+               if (attributes == INVALID_FILE_ATTRIBUTES)
+                       switch (GetLastError()) {
+                       case ERROR_PATH_NOT_FOUND:
+                               continue;
+                       case ERROR_FILE_NOT_FOUND:
+                               /* This implies parent directory exists. */
+                               return 1;
+                       }
+               return 0;
+       }
+       return 1;
+}
+
 /* We keep the do_lstat code in a separate function to avoid recursion.
  * When a path ends with a slash, the stat will fail with ENOENT. In
  * this case, we strip the trailing slashes and stat again.
@@ -514,6 +547,12 @@ static int do_lstat(int follow, const char *file_name, struct stat *buf)
        case ERROR_NOT_ENOUGH_MEMORY:
                errno = ENOMEM;
                break;
+       case ERROR_PATH_NOT_FOUND:
+               if (!has_valid_directory_prefix(wfilename)) {
+                       errno = ENOTDIR;
+                       break;
+               }
+               /* fallthru */
        default:
                errno = ENOENT;
                break;
@@ -1603,7 +1642,12 @@ int mingw_rename(const char *pold, const char *pnew)
        if (gle == ERROR_ACCESS_DENIED &&
            (attrs = GetFileAttributesW(wpnew)) != INVALID_FILE_ATTRIBUTES) {
                if (attrs & FILE_ATTRIBUTE_DIRECTORY) {
-                       errno = EISDIR;
+                       DWORD attrsold = GetFileAttributesW(wpold);
+                       if (attrsold == INVALID_FILE_ATTRIBUTES ||
+                           !(attrsold & FILE_ATTRIBUTE_DIRECTORY))
+                               errno = EISDIR;
+                       else if (!_wrmdir(wpnew))
+                               goto repeat;
                        return -1;
                }
                if ((attrs & FILE_ATTRIBUTE_READONLY) &&
@@ -1935,28 +1979,31 @@ pid_t waitpid(pid_t pid, int *status, int options)
        return -1;
 }
 
+int mingw_skip_dos_drive_prefix(char **path)
+{
+       int ret = has_dos_drive_prefix(*path);
+       *path += ret;
+       return ret;
+}
+
 int mingw_offset_1st_component(const char *path)
 {
-       int offset = 0;
-       if (has_dos_drive_prefix(path))
-               offset = 2;
+       char *pos = (char *)path;
 
        /* unc paths */
-       else if (is_dir_sep(path[0]) && is_dir_sep(path[1])) {
-
+       if (!skip_dos_drive_prefix(&pos) &&
+                       is_dir_sep(pos[0]) && is_dir_sep(pos[1])) {
                /* skip server name */
-               char *pos = strpbrk(path + 2, "\\/");
+               pos = strpbrk(pos + 2, "\\/");
                if (!pos)
                        return 0; /* Error: malformed unc path */
 
                do {
                        pos++;
                } while (*pos && !is_dir_sep(*pos));
-
-               offset = pos - path;
        }
 
-       return offset + is_dir_sep(path[offset]);
+       return pos + is_dir_sep(*pos) - path;
 }
 
 int xutftowcsn(wchar_t *wcs, const char *utfs, size_t wcslen, int utflen)