OSDN Git Service

Implement mv -n / cp -n (no clobber).
authorAndy Chu <andychu@google.com>
Sat, 19 Mar 2016 07:34:42 +0000 (00:34 -0700)
committerRob Landley <rob@landley.net>
Wed, 30 Mar 2016 08:11:55 +0000 (03:11 -0500)
This fixes a failing test case in mv.test.

Test changes:
- Add coverage for -i (interactive).
- Better descriptions, better formatting, and removed some redundant
  cases.

tests/mv.test
toys/posix/cp.c

index ab2ca5e..b699d01 100755 (executable)
 #testing "name" "command" "result" "infile" "stdin"
 
 touch file
-testing "old_file to new_file" "mv file file1 && [ ! -e file -a -f file1 ] &&
-   echo 'yes'" "yes\n" "" ""
+testing "file to file" \
+  "mv file file1 && [ ! -e file -a -f file1 ] && echo yes" \
+  "yes\n" "" ""
 rm -f file*
 
 touch file
 mkdir dir
-testing "file to a dir" "mv file dir && [ ! -e file -a -f dir/file ] &&
-   echo 'yes'" "yes\n" "" ""
+testing "file to dir" \
+  "mv file dir && [ ! -e file -a -f dir/file ] && echo yes" \
+  "yes\n" "" ""
 rm -rf file* dir*
 
 mkdir dir
-testing "old_dir to new_dir" "mv dir dir1 && [ ! -e dir -a -d dir1 ] &&
-   echo 'yes'" "yes\n" "" ""
+testing "dir to dir" \
+  "mv dir dir1 && [ ! -e dir -a -d dir1 ] && echo yes" \
+  "yes\n" "" ""
 rm -rf dir*
 
 mkdir dir1 dir2
 touch file1 file2 dir1/file3
 ln -s file1 link1
-testing "multiple files/dir to a dir" "mv file1 file2 link1 dir1 dir2 &&
-   [ ! -e file1 -a ! -e file2 -a ! -e link1 -a ! -e dir1 ] &&
-   [ -f dir2/file1 -a -f dir2/file2 -a -L dir2/link1 -a -d dir2/dir1 ] &&
-   [ -f dir2/dir1/file3 ] && readlink dir2/link1" "file1\n" "" ""
+testing "multiple files/dirs to a dir" \
+  "mv file1 file2 link1 dir1 dir2 &&
+  [ ! -e file1 -a ! -e file2 -a ! -e link1 -a ! -e dir1 ] &&
+  [ -f dir2/file1 -a -f dir2/file2 -a -L dir2/link1 -a -d dir2/dir1 ] &&
+  [ -f dir2/dir1/file3 ] && readlink dir2/link1" \
+  "file1\n" "" ""
 rm -rf file* link* dir*
 
-touch file1
-testing "a empty file to new_file" "mv file1 file2 &&
-   [ ! -e file1 -a -f file2 ] && stat -c %s file2" "0\n" "" ""
-rm -rf file*
-
-mkdir dir1
-testing "enpty dir to new_dir" "mv dir1 dir2 &&
-   [ ! -d dir1 -a -d dir2 ] && echo 'yes'" "yes\n" "" ""
-rm -rf dir*
-
 dd if=/dev/zero of=file1 seek=10k count=1 >/dev/null 2>&1
-testing "file new_file (random file)" "mv file1 file2 &&
-   [ ! -e file1 -a -f file2 ] && stat -c %s file2" "5243392\n" "" ""
+testing "random file to new file" \
+  "mv file1 file2 && [ ! -e file1 -a -f file2 ] && stat -c %s file2" \
+  "5243392\n" "" ""
 rm -f file*
 
 touch file1
 ln -s file1 link1
-testing "link new_link (softlink)" "mv link1 link2 &&
-   [ ! -e link1 -a -L link2 ] && readlink link2" "file1\n" "" ""
+testing "symlink to new symlink" \
+  "mv link1 link2 && [ ! -e link1 -a -L link2 ] && readlink link2" \
+  "file1\n" "" ""
 unlink tLink2 &>/dev/null
 rm -f file* link*
 
 touch file1
 ln file1 link1
-testing "link new_link (hardlink)" "mv link1 link2 &&
-   [ ! -e link1 -a -f link2 -a file1 -ef link2 ] && echo 'yes'" "yes\n" "" ""
+testing "hard link to new hardlink" \
+  "mv link1 link2 && [ ! -e link1 -a -f link2 -a file1 -ef link2 ] && echo yes" \
+  "yes\n" "" ""
 unlink link2 &>/dev/null
 rm -f file* link*
 
 touch file1
 chmod a-r file1
-testing "file new_file (unreadable)" "mv file1 file2 &&
-   [ ! -e file1 -a -f file2 ] && echo 'yes'" "yes\n" "" ""
+testing "file to unreadable file" \
+  "mv file1 file2 && [ ! -e file1 -a -f file2 ] && echo yes" \
+  "yes\n" "" ""
 rm -f file*
 
 touch file1
 ln file1 link1
 mkdir dir1
-testing "file link dir (hardlink)" "mv file1 link1 dir1 &&
-   [ ! -e file1 -a ! -e link1 -a -f dir1/file1 -a -f dir1/link1 ] &&
-   [ dir1/file1 -ef dir1/link1 ] && echo 'yes'" "yes\n" "" ""
+testing "file hardlink dir" \
+  "mv file1 link1 dir1 &&
+  [ ! -e file1 -a ! -e link1 -a -f dir1/file1 -a -f dir1/link1 ] &&
+  [ dir1/file1 -ef dir1/link1 ] && echo yes" \
+  "yes\n" "" ""
 rm -rf file* link* dir*
 
 mkdir -p dir1/dir2 dir3
 touch dir1/dir2/file1 dir1/dir2/file2
-testing "dir1/dir2 dir3/new_dir" "mv dir1/dir2 dir3/dir4 &&
-   [ ! -e dir1/dir2 -a -d dir3/dir4 -a -f dir3/dir4/file1 ] &&
-   [ -f dir3/dir4/file2 ] && echo 'yes'" "yes\n" "" ""
+testing "dir to new dir" \
+  "mv dir1/dir2 dir3/new &&
+  [ ! -e dir1/dir2 -a -d dir3/new -a -f dir3/new/file1 ] &&
+  [ -f dir3/new/file2 ] && echo yes" \
+  "yes\n" "" ""
 rm -rf file* dir*
 
 mkdir dir1 dir2
-testing "dir new_dir (already exist)" "mv dir1 dir2 &&
-   [ ! -e dir1 -a -d dir2/dir1 ] && echo 'yes'" "yes\n" "" ""
+testing "dir to existing dir" \
+  "mv dir1 dir2 && [ ! -e dir1 -a -d dir2/dir1 ] && echo yes" \
+  "yes\n" "" ""
 rm -rf dir*
 
 touch file1 file2
-testing "-f file new_file (exist)" "mv -f file1 file2 &&
-   [ ! -e file1 -a -e file2 ] && echo 'yes'" "yes\n" "" ""
+chmod 400 file1 file2
+testing "force over unwritable" \
+  "mv -f file1 file2 && [ ! -e file1 -a -e file2 ] && echo yes" \
+  "yes\n" "" ""
+rm -f file*
+
+touch file1 file2
+testing "no clobber (dest exists)" \
+  "mv -n file1 file2 && [ -e file1 -a -e file2 ] && echo yes"\
+  "yes\n" "" ""
+rm -f file*
+
+touch file1
+testing "no clobber (dest doesn't exist)" \
+  "mv -n file1 new-dest && [ ! -e file1 -a -e new-dest ] && echo yes"\
+  "yes\n" "" ""
+rm -f file*
+
+# If there is stdin, it prompts.  If no stdin, it moves anyway and file2 won't
+# exist.
+touch file1 file2
+chmod 400 file1 file2
+testing "mv over unwritable file: no stdin" \
+  "mv file2 file1 && [ -e file1 -a ! -e file2 ] && echo yes" \
+  "yes\n" "" ""
 rm -f file*
 
 touch file1 file2
-testing "-n file new_file (exist)" "mv -n file1 file2 &&
-   [ -e file1 -a -e file2 ] && echo 'yes'" "yes\n" "" ""
+chmod 400 file1 file2
+testing "mv over unwritable file: answered YES" \
+  "mv file2 file1 && [ -e file1 -a ! -e file2 ] && echo yes" \
+  "yes\n" "" "y\n"
 rm -f file*
 
 touch file1 file2
 chmod 400 file1 file2
-testing "file over unwritable file with no stdin" \
-   "</dev/null mv file2 file1 && [ -e file -a ! -e file2 ] && echo 'yes'" \
-   "yes\n" "" ""
+testing "mv over unwritable file: answered NO" \
+  "mv file2 file1 && [ -e file1 -a -e file2 ] && echo yes" \
+  "yes\n" "" "n\n"
+rm -f file*
+
+touch file1 file2
+testing "interactive: no stdin" \
+  "mv -i file2 file1 && [ -e file1 -a ! -e file2 ] && echo yes" \
+  "yes\n" "" ""
+rm -f file*
+
+touch file1 file2
+testing "interactive: answered YES" \
+  "mv -i file2 file1 && [ -e file1 -a ! -e file2 ] && echo yes" \
+  "yes\n" "" "y\n"
+rm -f file*
+
+touch file1 file2
+testing "interactive: answered NO" \
+  "mv -i file2 file1 && [ -e file1 -a -e file2 ] && echo yes" \
+  "yes\n" "" "n\n"
 rm -f file*
index d822b1e..8bcb81e 100644 (file)
@@ -395,20 +395,21 @@ void cp_main(void)
 
     errno = EXDEV;
     if (CFG_MV && toys.which->name[0] == 'm') {
-      if (!(toys.optflags & FLAG_f)) {
+      int force = toys.optflags & FLAG_f, no_clobber = toys.optflags & FLAG_n;
+      if (!force || no_clobber) {
         struct stat st;
-
-        // Technically "is writeable" is more complicated (022 is not writeable
-        // by the owner, just everybody _else_) but I don't care.
-        if (!stat(TT.destname, &st)
-          && ((toys.optflags & FLAG_i) || !(st.st_mode & 0222)))
-        {
+        int exists = !stat(TT.destname, &st);
+        // Prompt if -i or file isn't writable.  Technically "is writable" is
+        // more complicated (022 is not writeable by the owner, just everybody
+        // _else_) but I don't care.
+        if (exists && ((toys.optflags & FLAG_i) || !(st.st_mode & 0222))) {
           fprintf(stderr, "%s: overwrite '%s'", toys.which->name, TT.destname);
           if (!yesno(1)) rc = 0;
           else unlink(TT.destname);
         }
+        // if -n and dest exists, don't try to rename() or copy
+        if (exists && no_clobber) rc = 0;
       }
-
       if (rc) rc = rename(src, TT.destname);
     }