+# if (RENAME_TRAILING_SLASH_SOURCE_BUG || RENAME_DEST_EXISTS_BUG \
+ || RENAME_HARD_LINK_BUG)
+ /* If the only bug was that a trailing slash was allowed on a
+ non-existing file destination, as in Solaris 10, then we've
+ already covered that situation. But if there is any problem with
+ a trailing slash on an existing source or destination, as in
+ Solaris 9, or if a directory can overwrite a symlink, as on
+ Cygwin 1.5, or if directories cannot be created with trailing
+ slash, as on NetBSD 1.6, then we must strip the offending slash
+ and check that we have not encountered a symlink instead of a
+ directory.
+
+ Stripping a trailing slash interferes with POSIX semantics, where
+ rename behavior on a symlink with a trailing slash operates on
+ the corresponding target directory. We prefer the GNU semantics
+ of rejecting any use of a symlink with trailing slash, but do not
+ enforce them, since Solaris 10 is able to obey POSIX semantics
+ and there might be clients expecting it, as counter-intuitive as
+ those semantics are.
+
+ Technically, we could also follow the POSIX behavior by chasing a
+ readlink trail, but that is harder to implement. */
+ if (src_slash)
+ {
+ src_temp = strdup (src);
+ if (!src_temp)
+ {
+ /* Rather than rely on strdup-posix, we set errno ourselves. */
+ rename_errno = ENOMEM;
+ goto out;
+ }
+ strip_trailing_slashes (src_temp);
+ if (lstat (src_temp, &src_st))
+ {
+ rename_errno = errno;
+ goto out;
+ }
+ if (S_ISLNK (src_st.st_mode))
+ goto out;
+ }
+ if (dst_slash)
+ {
+ dst_temp = strdup (dst);
+ if (!dst_temp)
+ {
+ rename_errno = ENOMEM;
+ goto out;
+ }
+ strip_trailing_slashes (dst_temp);
+ if (lstat (dst_temp, &dst_st))
+ {
+ if (errno != ENOENT)
+ {
+ rename_errno = errno;
+ goto out;
+ }
+ }
+ else if (S_ISLNK (dst_st.st_mode))
+ goto out;
+ }
+# endif /* RENAME_TRAILING_SLASH_SOURCE_BUG || RENAME_DEST_EXISTS_BUG
+ || RENAME_HARD_LINK_BUG */
+
+# if RENAME_DEST_EXISTS_BUG
+ /* Cygwin 1.5 sometimes behaves oddly when moving a non-empty
+ directory on top of an empty one (the old directory name can
+ reappear if the new directory tree is removed). Work around this
+ by removing the target first, but don't remove the target if it
+ is a subdirectory of the source. Note that we can end up losing
+ a directory if rename then fails, but it was empty, so not much
+ damage was done. */
+ if (dst_exists && S_ISDIR (dst_st.st_mode))
+ {
+ if (src_st.st_dev != dst_st.st_dev)
+ {
+ rename_errno = EXDEV;
+ goto out;
+ }
+ if (src_temp != src)
+ free (src_temp);
+ src_temp = canonicalize_file_name (src);
+ if (dst_temp != dst)
+ free (dst_temp);
+ dst_temp = canonicalize_file_name (dst);
+ if (!src_temp || !dst_temp)
+ {
+ rename_errno = ENOMEM;
+ goto out;
+ }
+ src_len = strlen (src_temp);
+ if (strncmp (src_temp, dst_temp, src_len) == 0
+ && dst_temp[src_len] == '/')
+ {
+ rename_errno = EINVAL;
+ goto out;
+ }
+ if (rmdir (dst))
+ {
+ rename_errno = errno;
+ goto out;
+ }
+ }
+# endif /* RENAME_DEST_EXISTS_BUG */