--- origsrc/src/copy.c 2022-04-15 07:53:28.000000000 -0600 +++ src/src/copy.c 2022-05-09 00:11:47.335547900 -0600 @@ -84,6 +84,10 @@ #ifdef HAVE_LINUX_FS_H # include #endif + +#ifdef __CYGWIN__ +# include "cygwin.h" +#endif #if !defined FICLONE && defined __linux__ # define FICLONE _IOW (0x94, 9, int) @@ -1479,7 +1483,11 @@ close_src_desc: static bool same_file_ok (char const *src_name, struct stat const *src_sb, int dst_dirfd, char const *dst_relname, struct stat const *dst_sb, - const struct cp_options *x, bool *return_now) + const struct cp_options *x, bool *return_now +#if __CYGWIN__ + , bool *case_change +#endif + ) { const struct stat *src_sb_link; const struct stat *dst_sb_link; @@ -1624,6 +1632,18 @@ same_file_ok (char const *src_name, stru if (S_ISLNK (dst_sb_link->st_mode)) return true; +#if __CYGWIN__ + /* If the files have the same name, but differ in case, then let + rename() change the case. */ + if (same_link && x->move_mode && same_name (src_name, dst_relname) + && memcmp (last_component (src_name), last_component (dst_relname), + base_len (src_name))) + { + *case_change = true; + return true; + } +#endif + /* It's not ok if they're distinct hard links to the same file as this causes a race condition and we may lose data in this case. */ if (same_link @@ -2053,13 +2073,23 @@ copy_internal (char const *src_name, cha new_dst = true; else if (follow_fstatat (dst_dirfd, dst_relname, &dst_sb, fstatat_flags) - == 0) + == 0 +#if __CYGWIN__ + /* stat("a") succeeds if "a.exe" - only identical accepted. */ + && cygwin_spelling (dst_relname) == 0 +#endif + ) { have_dst_lstat = use_lstat; rename_errno = EEXIST; } else { +#if __CYGWIN__ + /* only DST_RELNAME.exe exists - want non-existant DST_RELNAME. */ + if (cygwin_spelling (dst_relname) != 0) + errno = ENOENT; +#endif if (errno == ELOOP && x->unlink_dest_after_failed_open) /* leave new_dst=false so we unlink later. */; else if (errno != ENOENT) @@ -2075,10 +2105,17 @@ copy_internal (char const *src_name, cha if (rename_errno == EEXIST) { bool return_now = false; +#if __CYGWIN__ + bool case_change = false; +#endif if (x->interactive != I_ALWAYS_NO && ! same_file_ok (src_name, &src_sb, dst_dirfd, dst_relname, - &dst_sb, x, &return_now)) + &dst_sb, x, &return_now +#if __CYGWIN__ + , &case_change +#endif + )) { error (0, 0, _("%s and %s are the same file"), quoteaf_n (0, src_name), quoteaf_n (1, dst_name)); @@ -2140,6 +2177,9 @@ copy_internal (char const *src_name, cha cp and mv treat -i and -f differently. */ if (x->move_mode) { +#if __CYGWIN__ + if (!case_change) +#endif if (abandon_move (x, dst_name, dst_dirfd, dst_relname, &dst_sb)) { /* Pretend the rename succeeded, so the caller (mv) @@ -2288,7 +2328,11 @@ copy_internal (char const *src_name, cha /* Never unlink dst_name when in move mode. */ && ! x->move_mode && (x->unlink_dest_before_opening || (x->data_copy_required - && ((x->preserve_links && 1 < dst_sb.st_nlink) + && ((x->preserve_links && 1 < dst_sb.st_nlink +#if __CYGWIN__ + && ! case_change +#endif + ) || (x->dereference == DEREF_NEVER && ! S_ISREG (src_sb.st_mode)))) )) @@ -3113,6 +3157,24 @@ copy (char const *src_name, char const * { assert (valid_options (options)); +#if __CYGWIN__ + /* .exe magic - if src exists with an implicit .exe suffix and is + not a symlink, but dst does not exist and was also specified + without a suffix, then append .exe to dst. */ + int cygwin = cygwin_spelling (src_name); + char *p; + if (cygwin == 2 + && ((p = strchr (dst_name, '\0') - 4) <= dst_name + || strcasecmp (p, ".exe") != 0)) + { + cygwin = 3; + CYGWIN_APPEND_EXE (p, dst_name); + if ((p = strchr (dst_relname, '\0') - 4) <= dst_relname + || strcasecmp (p, ".exe") != 0) + CYGWIN_APPEND_EXE (p, dst_relname); + } +#endif + /* Record the file names: they're used in case of error, when copying a directory into itself. I don't like to make these tools do *any* extra work in the common case when that work is solely to handle @@ -3124,11 +3186,19 @@ copy (char const *src_name, char const * top_level_dst_name = dst_name; bool first_dir_created_per_command_line_arg = false; - return copy_internal (src_name, dst_name, dst_dirfd, dst_relname, + bool done = copy_internal (src_name, dst_name, dst_dirfd, dst_relname, nonexistent_dst, NULL, NULL, options, true, &first_dir_created_per_command_line_arg, copy_into_self, rename_succeeded); +#if __CYGWIN__ + if (cygwin == 3) + { + freea ((char *) dst_name); + freea ((char *) dst_relname); + } +#endif + return done; } /* Set *X to the default options for a value of type struct cp_options. */