GNU mv, with changes in the upcoming coreutils-6.2, had the portability problem outlined in
<http://permalink.gmane.org/gmane.comp.gnu.core-utils.bugs/8111> I've fixed it with the following additions to gnulib: [ChangeLog] * modules/rename-dest-slash: New module. * MODULES.html.sh (posix_compat): Add it here. [lib/ChangeLog] * rename-dest-slash.c: New file. [m4/ChangeLog] * rename-dest-slash.m4 (gl_FUNC_RENAME_TRAILING_DEST_SLASH): New file. With these changes, coreutils-6.2-to-be passes its tests on NetBSD 1.6. Jim Index: modules/rename-dest-slash =================================================================== RCS file: modules/rename-dest-slash diff -N modules/rename-dest-slash --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ modules/rename-dest-slash 15 Sep 2006 13:49:07 -0000 @@ -0,0 +1,24 @@ +Description: +rename() function: change the name or location of a file. + +Files: +lib/rename-dest-slash.c +m4/rename-dest-slash.m4 + +Depends-on: +xalloc +dirname + +configure.ac: +gl_FUNC_RENAME_TRAILING_DEST_SLASH + +Makefile.am: + +Include: +<stdio.h> + +License: +GPL + +Maintainer: +Jim Meyering Index: MODULES.html.sh =================================================================== RCS file: /sources/gnulib/gnulib/MODULES.html.sh,v retrieving revision 1.148 diff -u -r1.148 MODULES.html.sh --- MODULES.html.sh 6 Sep 2006 12:22:29 -0000 1.148 +++ MODULES.html.sh 15 Sep 2006 13:49:07 -0000 @@ -1836,6 +1836,7 @@ func_module d-ino func_module d-type func_module link-follow + func_module rename-dest-slash func_module rmdir-errno func_module unlink-busy func_module winsz-ioctl Index: lib/rename-dest-slash.c =================================================================== RCS file: lib/rename-dest-slash.c diff -N lib/rename-dest-slash.c --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ lib/rename-dest-slash.c 15 Sep 2006 13:49:07 -0000 @@ -0,0 +1,89 @@ +/* A rename wrapper to make tools like mv -- that would normally rely + on the underlying rename syscall -- work more consistently. + On at least NetBSD 1.6, `rename ("dir", "B/")' fails when B doesn't + exist, whereas it succeeds on Linux-2.6.x and Solaris 10. This wrapper + provides an interface for systems like the former so that the tools + (namely mv) relying on the rename syscall have more consistent + semantics. + + Copyright (C) 2006 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + +/* written by Jim Meyering */ + +#include <config.h> +#undef rename + +#include <sys/types.h> +#include <sys/stat.h> +#include <errno.h> + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "dirname.h" +#include "xalloc.h" + +static inline bool +has_trailing_slash (char const *file, size_t len) +{ + /* Don't count "/" as having a trailing slash. */ + if (len <= 1) + return false; + + char last = file[len - 1]; + return ISSLASH (last); +} + +/* This is a rename wrapper for systems where the rename syscall + works differently than desired when SRC is a directory and DST does + not exist but is specified with a trailing slash. On NetBSD 6.1, + rename fails in that case. On Linux and Solaris systems, it succeeds. + This wrapper makes it succeed on NetBSD by running the originally + requested rename, and if it fails due to the above scenario, calling + it again with DST's trailing slashes removed. */ +int +rpl_rename_dest_slash (char const *src, char const *dst) +{ + size_t d_len; + int ret_val = rename (src, dst); + if (ret_val == 0 || errno != ENOENT) + return ret_val; + + { + /* Fail now, unless SRC is a directory. */ + struct stat sb; + if (lstat (src, &sb) != 0 || ! S_ISDIR (sb.st_mode)) + return ret_val; + } + + /* Don't call rename again if there are no trailing slashes. */ + d_len = strlen (dst); + if ( ! has_trailing_slash (dst, d_len)) + return ret_val; + + { + char *dst_temp; + dst_temp = xmemdup (dst, d_len + 1); + strip_trailing_slashes (dst_temp); + + ret_val = rename (src, dst_temp); + free (dst_temp); + } + + return ret_val; +} Index: m4/rename-dest-slash.m4 =================================================================== RCS file: m4/rename-dest-slash.m4 diff -N m4/rename-dest-slash.m4 --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ m4/rename-dest-slash.m4 15 Sep 2006 13:49:07 -0000 @@ -0,0 +1,41 @@ +#serial 1 + +# Copyright (C) 2006 Free Software Foundation, Inc. +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# Derived from rename.m4. + +# A rename wrapper to make tools like mv -- that would normally +# rely on the underlying rename syscall -- work more consistently. + +AC_DEFUN([gl_FUNC_RENAME_TRAILING_DEST_SLASH], +[ + AC_CACHE_CHECK([whether rename is broken with respect to destintation slashes], + gl_cv_func_rename_trailing_dest_slash_bug, + [ + rm -rf conftest.d1 conftest.d2 + mkdir conftest.d1 || + AC_MSG_ERROR([cannot create temporary directory]) + AC_TRY_RUN([ +# include <stdio.h> + int + main () + { + return (rename ("conftest.d1", "conftest.d2/") ? 1 : 0); + } + ], + gl_cv_func_rename_trailing_dest_slash_bug=no, + gl_cv_func_rename_trailing_dest_slash_bug=yes, + dnl When crosscompiling, assume rename is broken. + gl_cv_func_rename_trailing_dest_slash_bug=yes) + + rm -rf conftest.d1 conftest.d2 + ]) + if test $gl_cv_func_rename_trailing_dest_slash_bug = yes; then + AC_LIBOBJ(rename-dest-slash) + AC_DEFINE(rename, rpl_rename_dest_slash, + [Define to rpl_rename_dest_slash if the replacement function should be used.]) + fi +])