Use .part suffix for temp file names.

Untested for BTRFS cloning.
diff --git a/developer-xcode/libzip.xcodeproj/project.pbxproj b/developer-xcode/libzip.xcodeproj/project.pbxproj
index acd593a..08c5e07 100644
--- a/developer-xcode/libzip.xcodeproj/project.pbxproj
+++ b/developer-xcode/libzip.xcodeproj/project.pbxproj
@@ -171,7 +171,6 @@
 		4B3A5F531DF96EB4005A53A1 /* zip_ftell.c in Sources */ = {isa = PBXBuildFile; fileRef = 4B3A5F4E1DF96D83005A53A1 /* zip_ftell.c */; };
 		4B3FAE802385C5CC00192D6A /* zip_algorithm_xz.c in Sources */ = {isa = PBXBuildFile; fileRef = 4B3FAE7F2385C5A300192D6A /* zip_algorithm_xz.c */; };
 		4B3FAE822385C79200192D6A /* liblzma.5.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 4B3FAE812385C79200192D6A /* liblzma.5.dylib */; };
-		4B5169A822A7993E00AA4340 /* zip_mkstempm.c in Sources */ = {isa = PBXBuildFile; fileRef = 4B5169A722A7993D00AA4340 /* zip_mkstempm.c */; };
 		4B51DDBA1FDAE20A00C5CA85 /* libz.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 4B01D70815B2F4CF002D5007 /* libz.dylib */; };
 		4B51DDBB1FDAE20A00C5CA85 /* libzip.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4B01D68B15B2F3F1002D5007 /* libzip.framework */; };
 		4B51DDC11FDAE25B00C5CA85 /* ziptool.c in Sources */ = {isa = PBXBuildFile; fileRef = 4BACD57C15BC2AEF00920691 /* ziptool.c */; };
@@ -2015,7 +2014,6 @@
 				4BCF302A199A2F820064207B /* zip_source_seek.c in Sources */,
 				4B01D6C515B2F46B002D5007 /* zip_fopen_index_encrypted.c in Sources */,
 				4B01D6C615B2F46B002D5007 /* zip_fopen_index.c in Sources */,
-				4B5169A822A7993E00AA4340 /* zip_mkstempm.c in Sources */,
 				4B69E6EE2032F18B0001EEE7 /* zip_winzip_aes.c in Sources */,
 				4B01D6C715B2F46B002D5007 /* zip_fopen.c in Sources */,
 				3D9284821C309510001EABA7 /* zip_hash.c in Sources */,
diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt
index 5ec2451..1213fa0 100644
--- a/lib/CMakeLists.txt
+++ b/lib/CMakeLists.txt
@@ -130,7 +130,6 @@
   endif()
 else(WIN32)
   target_sources(zip PRIVATE
-    zip_mkstempm.c
     zip_source_file_stdio_named.c
     zip_random_unix.c
     )
diff --git a/lib/zip_mkstempm.c b/lib/zip_mkstempm.c
index ede5142..4efcf79 100644
--- a/lib/zip_mkstempm.c
+++ b/lib/zip_mkstempm.c
@@ -43,24 +43,28 @@
  * or default permissions if there is no previous file
  */
 int
-_zip_mkstempm(char *path, int mode) {
+_zip_mkstempm(char *path, int mode, bool create_file) {
     int fd;
     char *start, *end, *xs;
 
     int xcnt = 0;
 
-    end = path + strlen(path);
-    start = end - 1;
+    end = path + strlen(path) - 1;
+    while (end >= path) {
+        if (*end == 'X') {
+            break;
+        }
+        end--;
+    }
+    if (end < path) {
+        errno = EINVAL;
+        return -1;
+    }
+    start = end;
     while (start >= path && *start == 'X') {
         xcnt++;
         start--;
     }
-
-    if (xcnt == 0) {
-        errno = EINVAL;
-        return -1;
-    }
-
     start++;
 
     for (;;) {
@@ -68,7 +72,7 @@
 
         xs = start;
 
-        while (xs < end) {
+        while (xs <= end) {
             char digit = value % 36;
             if (digit < 10) {
                 *(xs++) = digit + '0';
@@ -79,19 +83,33 @@
             value /= 36;
         }
 
-        if ((fd = open(path, O_CREAT | O_EXCL | O_RDWR | O_CLOEXEC, mode == -1 ? 0666 : (mode_t)mode)) >= 0) {
-            if (mode != -1) {
-                /* open() honors umask(), which we don't want in this case */
+        if (create_file) {
+            if ((fd = open(path, O_CREAT | O_EXCL | O_RDWR | O_CLOEXEC, mode == -1 ? 0666 : (mode_t)mode)) >= 0) {
+                if (mode != -1) {
+                    /* open() honors umask(), which we don't want in this case */
 #ifdef HAVE_FCHMOD
-                (void)fchmod(fd, (mode_t)mode);
+                    (void)fchmod(fd, (mode_t)mode);
 #else
-                (void)chmod(path, (mode_t)mode);
+                    (void)chmod(path, (mode_t)mode);
+                }
 #endif
+                return fd;
             }
-            return fd;
+            if (errno != EEXIST) {
+                return -1;
+            }
         }
-        if (errno != EEXIST) {
-            return -1;
+        else {
+            struct stat st;
+            
+            if (stat(path, &st) < 0) {
+                if (errno == ENOENT) {
+                    return 0;
+                }
+                else {
+                    return -1;
+                }
+            }
         }
     }
 }
diff --git a/lib/zip_source_file_stdio_named.c b/lib/zip_source_file_stdio_named.c
index 8027e73..974bf52 100644
--- a/lib/zip_source_file_stdio_named.c
+++ b/lib/zip_source_file_stdio_named.c
@@ -36,6 +36,7 @@
 #include "zip_source_file.h"
 #include "zip_source_file_stdio.h"
 
+#include <fcntl.h>
 #include <stdlib.h>
 #include <sys/stat.h>
 #ifdef HAVE_UNISTD_H
@@ -53,6 +54,8 @@
 #define CAN_CLONE
 #endif
 
+static int create_temp_file(zip_source_file_context_t *ctx, bool create_file);
+
 static zip_int64_t _zip_stdio_op_commit_write(zip_source_file_context_t *ctx);
 static zip_int64_t _zip_stdio_op_create_temp_output(zip_source_file_context_t *ctx);
 #ifdef CAN_CLONE
@@ -123,82 +126,51 @@
 
 static zip_int64_t
 _zip_stdio_op_create_temp_output(zip_source_file_context_t *ctx) {
-    char *temp;
-    int tfd;
-    int mode;
-    FILE *tfp;
-    struct stat st;
-
-    if ((temp = (char *)malloc(strlen(ctx->fname) + 8)) == NULL) {
-        zip_error_set(&ctx->error, ZIP_ER_MEMORY, 0);
+    int fd = create_temp_file(ctx, true);
+    
+    if (fd < 0) {
         return -1;
     }
-
-    if (stat(ctx->fname, &st) == 0) {
-        mode = st.st_mode;
-    }
-    else {
-        mode = -1;
-    }
-
-    sprintf(temp, "%s.XXXXXX", ctx->fname);
-
-    if ((tfd = _zip_mkstempm(temp, mode)) == -1) {
+    
+    if ((ctx->fout = fdopen(fd, "r+b")) == NULL) {
         zip_error_set(&ctx->error, ZIP_ER_TMPOPEN, errno);
-        free(temp);
+        close(fd);
+        (void)remove(ctx->tmpname);
+        free(ctx->tmpname);
+        ctx->tmpname = NULL;
         return -1;
     }
 
-    if ((tfp = fdopen(tfd, "r+b")) == NULL) {
-        zip_error_set(&ctx->error, ZIP_ER_TMPOPEN, errno);
-        close(tfd);
-        (void)remove(temp);
-        free(temp);
-        return -1;
-    }
-
-    ctx->fout = tfp;
-    ctx->tmpname = temp;
-
     return 0;
 }
 
 #ifdef CAN_CLONE
 static zip_int64_t
 _zip_stdio_op_create_temp_output_cloning(zip_source_file_context_t *ctx, zip_uint64_t offset) {
-    char *temp;
     FILE *tfp;
-
+    
     if (offset > ZIP_OFF_MAX) {
         zip_error_set(&ctx->error, ZIP_ER_SEEK, E2BIG);
         return -1;
     }
-
-    if ((temp = (char *)malloc(strlen(ctx->fname) + 8)) == NULL) {
-        zip_error_set(&ctx->error, ZIP_ER_MEMORY, 0);
-        return -1;
-    }
-    sprintf(temp, "%s.XXXXXX", ctx->fname);
-
+    
 #ifdef HAVE_CLONEFILE
-#ifndef __clang_analyzer__
-    /* we can't use mkstemp, since clonefile insists on creating the file */
-    if (mktemp(temp) == NULL) {
-        zip_error_set(&ctx->error, ZIP_ER_TMPOPEN, errno);
-        free(temp);
+    /* clonefile insists on creating the file, so just create a name */
+    if (create_temp_file(ctx, false) < 0) {
         return -1;
     }
-#endif
-
-    if (clonefile(ctx->fname, temp, 0) < 0) {
+    
+    if (clonefile(ctx->fname, ctx->tmpname, 0) < 0) {
         zip_error_set(&ctx->error, ZIP_ER_TMPOPEN, errno);
-        free(temp);
+        free(ctx->tmpname);
+        ctx->tmpname = NULL;
         return -1;
     }
-    if ((tfp = _zip_fopen_close_on_exec(temp, true)) == NULL) {
+    if ((tfp = _zip_fopen_close_on_exec(ctx->tmpname, true)) == NULL) {
         zip_error_set(&ctx->error, ZIP_ER_TMPOPEN, errno);
-        (void)remove(temp);
-        free(temp);
+        (void)remove(ctx->tmpname);
+        free(ctx->tmpname);
+        ctx->tmpname = NULL;
         return -1;
     }
 #else
@@ -206,19 +178,16 @@
         int fd;
         struct file_clone_range range;
         struct stat st;
-
+        
         if (fstat(fileno(ctx->f), &st) < 0) {
             zip_error_set(&ctx->error, ZIP_ER_TMPOPEN, errno);
-            free(temp);
             return -1;
         }
-
-        if ((fd = mkstemp(temp)) < 0) {
-            zip_error_set(&ctx->error, ZIP_ER_TMPOPEN, errno);
-            free(temp);
+        
+        if ((fd = create_temp_file(ctx, true)) < 0) {
             return -1;
         }
-
+            
         range.src_fd = fileno(ctx->f);
         range.src_offset = 0;
         range.src_length = ((offset + st.st_blksize - 1) / st.st_blksize) * st.st_blksize;
@@ -229,16 +198,18 @@
         if (ioctl(fd, FICLONERANGE, &range) < 0) {
             zip_error_set(&ctx->error, ZIP_ER_TMPOPEN, errno);
             (void)close(fd);
-            (void)remove(temp);
-            free(temp);
+            (void)remove(ctx->tmpname);
+            free(ctx->tmpname);
+            ctx->tmpname = NULL;
             return -1;
         }
 
         if ((tfp = fdopen(fd, "r+b")) == NULL) {
             zip_error_set(&ctx->error, ZIP_ER_TMPOPEN, errno);
             (void)close(fd);
-            (void)remove(temp);
-            free(temp);
+            (void)remove(ctx->tmpname);
+            free(ctx->tmpname);
+            ctx->tmpname = NULL;
             return -1;
         }
     }
@@ -246,20 +217,21 @@
 
     if (ftruncate(fileno(tfp), (off_t)offset) < 0) {
         (void)fclose(tfp);
-        (void)remove(temp);
-        free(temp);
+        (void)remove(ctx->tmpname);
+        free(ctx->tmpname);
+        ctx->tmpname = NULL;
         return -1;
     }
     if (fseeko(tfp, (off_t)offset, SEEK_SET) < 0) {
-        (void)fclose(tfp);
-        (void)remove(temp);
-        free(temp);
         zip_error_set(&ctx->error, ZIP_ER_TMPOPEN, errno);
+        (void)fclose(tfp);
+        (void)remove(ctx->tmpname);
+        free(ctx->tmpname);
+        ctx->tmpname = NULL;
         return -1;
     }
 
     ctx->fout = tfp;
-    ctx->tmpname = temp;
 
     return 0;
 }
@@ -312,3 +284,78 @@
 
     return (zip_int64_t)ret;
 }
+
+
+static int create_temp_file(zip_source_file_context_t *ctx, bool create_file) {
+    char *temp;
+    int mode;
+    struct stat st;
+    int fd;
+    char *start, *end;
+    
+    if (stat(ctx->fname, &st) == 0) {
+        mode = st.st_mode;
+    }
+    else {
+        mode = -1;
+    }
+    
+    if ((temp = (char *)malloc(strlen(ctx->fname) + 13)) == NULL) {
+        zip_error_set(&ctx->error, ZIP_ER_MEMORY, 0);
+        return -1;
+    }
+    sprintf(temp, "%s.XXXXXX.part", ctx->fname);
+    end = temp + strlen(temp) - 5;
+    start = end - 6;
+    
+    for (;;) {
+        zip_uint32_t value = zip_random_uint32();
+        char *xs = start;
+        
+        while (xs < end) {
+            char digit = value % 36;
+            if (digit < 10) {
+                *(xs++) = digit + '0';
+            }
+            else {
+                *(xs++) = digit - 10 + 'a';
+            }
+            value /= 36;
+        }
+        
+        if (create_file) {
+            if ((fd = open(temp, O_CREAT | O_EXCL | O_RDWR | O_CLOEXEC, mode == -1 ? 0666 : (mode_t)mode)) >= 0) {
+                if (mode != -1) {
+                    /* open() honors umask(), which we don't want in this case */
+#ifdef HAVE_FCHMOD
+                    (void)fchmod(fd, (mode_t)mode);
+#else
+                    (void)chmod(temp, (mode_t)mode);
+#endif
+                }
+                break;
+            }
+            if (errno != EEXIST) {
+                zip_error_set(&ctx->error, ZIP_ER_TMPOPEN, errno);
+                free(temp);
+                return -1;
+            }
+        }
+        else {
+            if (stat(temp, &st) < 0) {
+                if (errno == ENOENT) {
+                    break;
+                }
+                else {
+                    zip_error_set(&ctx->error, ZIP_ER_TMPOPEN, errno);
+                    free(temp);
+                    return -1;
+                }
+            }
+        }
+    }
+    
+    ctx->tmpname = temp;
+    
+    return create_file ? fd : 0;
+}
diff --git a/lib/zipint.h b/lib/zipint.h
index eab1e3a..6a26dc3 100644
--- a/lib/zipint.h
+++ b/lib/zipint.h
@@ -576,7 +576,7 @@
 bool _zip_hash_reserve_capacity(zip_hash_t *hash, zip_uint64_t capacity, zip_error_t *error);
 bool _zip_hash_revert(zip_hash_t *hash, zip_error_t *error);
 
-int _zip_mkstempm(char *path, int mode);
+int _zip_mkstempm(char *path, int mode, bool create_file);
 
 zip_t *_zip_open(zip_source_t *, unsigned int, zip_error_t *);