don't whine if all we need to do is remove a bg job
[oweals/busybox.git] / mv.c
1 /* vi: set sw=4 ts=4: */
2 /*
3  * Mini mv implementation for busybox
4  *
5  *
6  * Copyright (C) 2000 by Matt Kraai <kraai@alumni.carnegiemellon.edu>
7  *
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 2 of the License, or
11  * (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16  * General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software
20  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21  *
22  */
23
24 #include <sys/types.h>
25 #include <sys/stat.h>
26 #include <unistd.h>
27 #include <dirent.h>
28 #include <errno.h>
29 #include <stdlib.h>
30
31 #include "busybox.h"
32
33 static int flags;
34
35 static int manual_rename(const char *source, const char *dest)
36 {
37         struct stat source_stat;
38         struct stat dest_stat;
39         int source_exists = 1;
40         int dest_exists = 1;
41
42         if (stat(source, &source_stat) < 0) {
43                 if (errno != ENOENT) {
44                         perror_msg("unable to stat `%s'", source);
45                         return -1;
46                 }
47                 source_exists = 0;
48         }
49
50         if (stat(dest, &dest_stat) < 0) {
51                 if (errno != ENOENT) {
52                         perror_msg("unable to stat `%s'", dest);
53                         return -1;
54                 }
55                 dest_exists = 0;
56         }
57
58         if (dest_exists) {
59                 if (S_ISDIR(dest_stat.st_mode) &&
60                           (!source_exists || !S_ISDIR(source_stat.st_mode))) {
61                         error_msg("cannot overwrite directory with non-directory");
62                         return -1;
63                 }
64
65                 if (!S_ISDIR(dest_stat.st_mode) && source_exists &&
66                                 S_ISDIR(source_stat.st_mode)) {
67                         error_msg("cannot overwrite non-directory with directory");
68                         return -1;
69                 }
70
71                 if (unlink(dest) < 0) {
72                         perror_msg("cannot remove `%s'", dest);
73                         return -1;
74                 }
75         }
76
77         if (copy_file(source, dest, FILEUTILS_RECUR | FILEUTILS_PRESERVE_STATUS |
78                         FILEUTILS_PRESERVE_SYMLINKS) < 0)
79                 return -1;
80
81         if (remove_file(source, FILEUTILS_RECUR | FILEUTILS_FORCE) < 0)
82                 return -1;
83
84         return 0;
85 }
86
87 static int move_file(const char *source, const char *dest)
88 {
89         struct stat dest_stat;
90         int dest_exists = 1;
91
92         if (stat(dest, &dest_stat) < 0) {
93                 if (errno != ENOENT) {
94                         perror_msg("unable to stat `%s'", dest);
95                         return -1;
96                 }
97                 dest_exists = 0;
98         }
99
100         if (dest_exists && !(flags & FILEUTILS_FORCE) &&
101                         ((access(dest, W_OK) < 0 && isatty(0)) ||
102                          (flags & FILEUTILS_INTERACTIVE))) {
103                 fprintf(stderr, "mv: overwrite `%s'? ", dest);
104                 if (!ask_confirmation())
105                         return 0;
106         }
107
108         if (rename(source, dest) < 0) {
109                 if (errno == EXDEV)
110                         return manual_rename(source, dest);
111
112                 perror_msg("unable to rename `%s'", source);
113                 return -1;
114         }
115         
116         return 0;
117 }
118
119 extern int mv_main(int argc, char **argv)
120 {
121         int status = 0;
122         int opt;
123         int i;
124
125         while ((opt = getopt(argc, argv, "fi")) != -1)
126                 switch (opt) {
127                 case 'f':
128                         flags &= ~FILEUTILS_INTERACTIVE;
129                         flags |= FILEUTILS_FORCE;
130                         break;
131                 case 'i':
132                         flags &= ~FILEUTILS_FORCE;
133                         flags |= FILEUTILS_INTERACTIVE;
134                         break;
135                 default:
136                         show_usage();
137                 }
138
139         if (optind + 2 > argc)
140                 show_usage();
141
142         if (optind + 2 == argc) {
143                 struct stat dest_stat;
144                 int dest_exists = 1;
145
146                 if (stat(argv[optind + 1], &dest_stat) < 0) {
147                         if (errno != ENOENT)
148                                 perror_msg_and_die("unable to stat `%s'", argv[optind + 1]);
149                         dest_exists = 0;
150                 }
151
152                 if (!dest_exists || !S_ISDIR(dest_stat.st_mode)) {
153                         if (move_file(argv[optind], argv[optind + 1]) < 0)
154                                 status = 1;
155                         return status;
156                 }
157         }
158
159         for (i = optind; i < argc - 1; i++) {
160                 char *dest = concat_path_file(argv[argc - 1],
161                                 get_last_path_component(argv[i]));
162                 if (move_file(argv[i], dest) < 0)
163                         status = 1;
164                 free(dest);
165         }
166
167         return status;
168 }