When using Gdb I skip in mv.c's main() function over the first few calls and come to the while loop, line #344. It is entered and
378 target_directory = optarg;. is – visited? The name "out" contains a "t" which is also listed in the list of command line options. After the while loop target_directory is still 0x0. It also "visits" line #386 because of "u". But this too does not seem to change anything… But then the if at line 431 if (no_target_directory) is reached. no_target_directory is false, so 443 else if (target_directory) is reached. target_directory is still 0x0, the "nullptr", so we come to line 450 else and then to an if on line 457 if (x.rename_errno != 0) x.rename_errno is 17 and therefore != 0, so the if expression is true and its block entered: 458 { 459 int fd = target_directory_operand (lastfile, &sb); 460 if (target_dirfd_valid (fd)) 461 { 462 x.rename_errno = -1; 463 target_dirfd = fd; 464 target_directory = lastfile; 465 n_files--; 466 } 467 else 468 { ... 484 } 485 } Here x.rename_errno is (re)set to -1 although already != 0, and most importantly target_directory receives value "out". So from now on mv might be sure that something needs to be moved into a *directory* "out", and we come to 519 if (target_directory) 520 { 521 /* Initialize the hash table only if we'll need it. 522 The problem it is used to detect can arise only if there are 523 two or more files to move. */ 524 if (2 <= n_files) 525 dest_info_init (&x); 526 527 ok = true; 528 for (int i = 0; i < n_files; ++i) 529 { 530 x.last_file = i + 1 == n_files; 531 char const *source = file[i]; 532 char const *source_basename = last_component (source); 533 char *dest_relname; 534 char *dest = file_name_concat (target_directory, source_basename, 535 &dest_relname); 536 strip_trailing_slashes (dest_relname); 537 ok &= do_move (source, dest, target_dirfd, dest_relname, &x); 538 free (dest); 539 } 540 } target_directory is "out" and therefore we enter the true block. n_files has been decremented on line #365, so dest_info_init() is not called and we enter the for loop to do our job… The cause for faulty behaviour is the value of x.rename_errno. It receives its value from 450 else 451 { 452 char const *lastfile = file[n_files - 1]; 453 if (n_files == 2 && !x.exchange) 454 x.rename_errno = (renameatu (AT_FDCWD, file[0], AT_FDCWD, lastfile, 455 RENAME_NOREPLACE) 456 ? errno : 0); 457 if (x.rename_errno != 0) On line # 453 n_files is 2, and x.exchange is false, so we have if( true && true) and renameatu() is called, with: renameatu (fd1=-3041965, src=0xbfffd607 "k", fd2=-3041965, dst=0xbfffd609 "out", flags=1) at lib/renameatu.c:257 254 #else /* !HAVE_RENAMEAT */ 255 256 /* RENAME_NOREPLACE is the only flag currently supported. */ 257 if (flags & ~RENAME_NOREPLACE) 258 return errno_fail (ENOTSUP); 259 return at_func2 (fd1, src, fd2, dst, flags ? rename_noreplace : rename); 260 261 #endif /* !HAVE_RENAMEAT */ Mac OS X 10.4, Tiger, does not have the renameat() system call, so not much is used from the GNUlib function. The if clause on line #257 is false, so renameatu() returns, calling at_func2(): at_func2 (fd1=-3041965, file1=0xbfffd607 "k", fd2=-3041965, file2=0xbfffd609 "out", func=0x25270 <rename_noreplace>) at lib/at-func2.c:77 77 if ((fd1 == AT_FDCWD || IS_ABSOLUTE_FILE_NAME (file1)) 78 && (fd2 == AT_FDCWD || IS_ABSOLUTE_FILE_NAME (file2))) 79 return func (file1, file2); /* Case 0-2, 4-6, 8-10. */ Here the if clause on lines #77/78 is true and func() is called: rename_noreplace (src=0xbfffd607 "k", dst=0xbfffd609 "out") at lib/renameatu.c:55 39 #if HAVE_RENAMEAT 40 41 # include <stdlib.h> 42 # include <string.h> 43 44 # include "dirname.h" 45 # include "openat.h" 46 47 #else 48 # include "openat-priv.h" 49 50 static int 51 rename_noreplace (char const *src, char const *dst) 52 { 53 /* This has a race between the call to lstat and the call to rename. */ 54 struct stat st; 55 return (lstat (dst, &st) == 0 || errno == EOVERFLOW ? errno_fail (EEXIST) 56 : errno == ENOENT ? rename (src, dst) 57 : -1); 58 } 59 #endif Isn't calling rename_noreplace() already faulty? Shouldn't it be rename_and_do_replace_and/or_remove()? The faulty behaviour starts here in mv.c: 454 x.rename_errno = (renameatu (AT_FDCWD, file[0], AT_FDCWD, lastfile, 455 RENAME_NOREPLACE) RENAME_NOREPLACE should not be passed to the function here. Anyway, lib/renameatu.c is divided into two parts, one for systems with renameat() family of library functions and another one without them. And this part might behave differently and take the last parameter as a target directory. I checked on my up-to-date Mac coreutils 9.7: mv -v k out just works! So I should start to debug here too to understand what produces the different behaviour – but first I need a working version of Gdb! -- Greetings Pete Clovis' Consideration of an Atmospheric Anomaly: The perversity of nature is nowhere better demonstrated than by the fact that, when exposed to the same atmosphere, bread becomes hard while crackers become soft