(do_move): Moving a directory specified with a trailing
[platform/upstream/coreutils.git] / src / mv.c
1 /* mv -- move or rename files
2    Copyright (C) 86, 89, 90, 91, 1995-2000 Free Software Foundation, Inc.
3
4    This program is free software; you can redistribute it and/or modify
5    it under the terms of the GNU General Public License as published by
6    the Free Software Foundation; either version 2, or (at your option)
7    any later version.
8
9    This program is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12    GNU General Public License for more details.
13
14    You should have received a copy of the GNU General Public License
15    along with this program; if not, write to the Free Software Foundation,
16    Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
17
18 /* Written by Mike Parker, David MacKenzie, and Jim Meyering */
19
20 #ifdef _AIX
21  #pragma alloca
22 #endif
23
24 #include <config.h>
25 #include <stdio.h>
26 #include <getopt.h>
27 #include <sys/types.h>
28 #include <assert.h>
29
30 #include "system.h"
31 #include "backupfile.h"
32 #include "copy.h"
33 #include "cp-hash.h"
34 #include "error.h"
35 #include "path-concat.h"
36 #include "quote.h"
37 #include "remove.h"
38
39 /* The official name of this program (e.g., no `g' prefix).  */
40 #define PROGRAM_NAME "mv"
41
42 #define AUTHORS "Mike Parker, David MacKenzie, and Jim Meyering"
43
44 /* Initial number of entries in each hash table entry's table of inodes.  */
45 #define INITIAL_HASH_MODULE 100
46
47 /* Initial number of entries in the inode hash table.  */
48 #define INITIAL_ENTRY_TAB_SIZE 70
49
50 /* For long options that have no equivalent short option, use a
51    non-character as a pseudo short option, starting with CHAR_MAX + 1.  */
52 enum
53 {
54   TARGET_DIRECTORY_OPTION = CHAR_MAX + 1,
55   STRIP_TRAILING_SLASHES_OPTION
56 };
57
58 int euidaccess ();
59 int full_write ();
60 int isdir ();
61 int lstat ();
62 int yesno ();
63
64 /* The name this program was run with. */
65 char *program_name;
66
67 /* Remove any trailing slashes from each SOURCE argument.  */
68 static int remove_trailing_slashes;
69
70 static struct option const long_options[] =
71 {
72   {"backup", optional_argument, NULL, 'b'},
73   {"force", no_argument, NULL, 'f'},
74   {"interactive", no_argument, NULL, 'i'},
75   {"strip-trailing-slash", no_argument, NULL, STRIP_TRAILING_SLASHES_OPTION},
76   {"suffix", required_argument, NULL, 'S'},
77   {"target-directory", required_argument, NULL, TARGET_DIRECTORY_OPTION},
78   {"update", no_argument, NULL, 'u'},
79   {"verbose", no_argument, NULL, 'v'},
80   {"version-control", required_argument, NULL, 'V'},
81   {GETOPT_HELP_OPTION_DECL},
82   {GETOPT_VERSION_OPTION_DECL},
83   {NULL, 0, NULL, 0}
84 };
85
86 static void
87 rm_option_init (struct rm_options *x)
88 {
89   x->unlink_dirs = 0;
90
91   /* FIXME: maybe this should be 1.  The POSIX spec doesn't specify.  */
92   x->ignore_missing_files = 0;
93
94   x->recursive = 1;
95
96   /* Should we prompt for removal, too?  No.  Prompting for the `move'
97      part is enough.  It implies removal.  */
98   x->interactive = 0;
99   x->stdin_tty = 0;
100
101   x->verbose = 0;
102 }
103
104 static void
105 cp_option_init (struct cp_options *x)
106 {
107   x->copy_as_regular = 0;  /* FIXME: maybe make this an option */
108   x->dereference = DEREF_NEVER;
109   x->unlink_dest_before_opening = 0;
110   x->unlink_dest_after_failed_open = 0;
111   x->failed_unlink_is_fatal = 1;
112   x->hard_link = 0;
113   x->interactive = 0;
114   x->move_mode = 1;
115   x->myeuid = geteuid ();
116   x->one_file_system = 0;
117   x->preserve_owner_and_group = 1;
118   x->preserve_chmod_bits = 1;
119   x->preserve_timestamps = 1;
120   x->require_preserve = 0;  /* FIXME: maybe make this an option */
121   x->recursive = 1;
122   x->sparse_mode = SPARSE_AUTO;  /* FIXME: maybe make this an option */
123   x->symbolic_link = 0;
124   x->set_mode = 0;
125   x->mode = 0;
126
127   /* Find out the current file creation mask, to knock the right bits
128      when using chmod.  The creation mask is set to be liberal, so
129      that created directories can be written, even if it would not
130      have been allowed with the mask this process was started with.  */
131   x->umask_kill = ~ umask (0);
132
133   x->update = 0;
134   x->verbose = 0;
135   x->xstat = lstat;
136 }
137
138 /* If PATH is an existing directory, return nonzero, else 0.  */
139
140 static int
141 is_real_dir (const char *path)
142 {
143   struct stat stats;
144
145   return lstat (path, &stats) == 0 && S_ISDIR (stats.st_mode);
146 }
147
148 static int
149 strip_trailing_slashes_2 (char *path)
150 {
151   char *end_p = path + strlen (path) - 1;
152   char *slash = end_p;
153
154   while (slash > path && *slash == '/')
155     *slash-- = '\0';
156
157   return slash < end_p;
158 }
159
160 /* Move SOURCE onto DEST.  Handles cross-filesystem moves.
161    If SOURCE is a directory, DEST must not exist.
162    Return 0 if successful, non-zero if an error occurred.  */
163
164 static int
165 do_move (const char *source, const char *dest, const struct cp_options *x)
166 {
167   static int first = 1;
168   int copy_into_self;
169   int rename_succeeded;
170   int fail;
171
172   if (first)
173     {
174       first = 0;
175
176       /* Allocate space for remembering copied and created files.  */
177       hash_init (INITIAL_HASH_MODULE, INITIAL_ENTRY_TAB_SIZE);
178     }
179
180   fail = copy (source, dest, 0, x, &copy_into_self, &rename_succeeded);
181
182   if (!fail)
183     {
184       const char *dir_to_remove;
185       if (copy_into_self)
186         {
187           /* In general, when copy returns with copy_into_self set, SOURCE is
188              the same as, or a parent of DEST.  In this case we know it's a
189              parent.  It doesn't make sense to move a directory into itself, and
190              besides in some situations doing so would give highly nonintuitive
191              results.  Run this `mkdir b; touch a c; mv * b' in an empty
192              directory.  Here's the result of running echo `find b -print`:
193              b b/a b/b b/b/a b/c.  Notice that only file `a' was copied
194              into b/b.  Handle this by giving a diagnostic, removing the
195              copied-into-self directory, DEST (`b/b' in the example),
196              and failing.  */
197
198           dir_to_remove = NULL;
199           error (0, 0,
200                  _("cannot move %s to a subdirectory of itself, %s"),
201                  quote_n (0, source), quote_n (1, dest));
202         }
203       else if (rename_succeeded)
204         {
205           /* No need to remove anything.  SOURCE was successfully
206              renamed to DEST.  */
207           dir_to_remove = NULL;
208         }
209       else
210         {
211           /* This may mean SOURCE and DEST referred to different devices.
212              It may also conceivably mean that even though they referred
213              to the same device, rename wasn't implemented for that device.
214
215              E.g., (from Joel N. Weber),
216              [...] there might someday be cases where you can't rename
217              but you can copy where the device name is the same, especially
218              on Hurd.  Consider an ftpfs with a primitive ftp server that
219              supports uploading, downloading and deleting, but not renaming.
220
221              Also, note that comparing device numbers is not a reliable
222              check for `can-rename'.  Some systems can be set up so that
223              files from many different physical devices all have the same
224              st_dev field.  This is a feature of some NFS mounting
225              configurations.
226
227              We reach this point if SOURCE has been successfully copied
228              to DEST.  Now we have to remove SOURCE.
229
230              This function used to resort to copying only when rename
231              failed and set errno to EXDEV.  */
232
233           dir_to_remove = source;
234         }
235
236       if (dir_to_remove != NULL)
237         {
238           struct rm_options rm_options;
239           struct File_spec fs;
240           enum RM_status status;
241
242           rm_option_init (&rm_options);
243           rm_options.verbose = x->verbose;
244
245           remove_init ();
246
247           fspec_init_file (&fs, dir_to_remove);
248
249           /* Remove any trailing slashes.  This is necessary if we
250              took the else branch of movefile.  */
251           strip_trailing_slashes_2 (fs.filename);
252
253           status = rm (&fs, 1, &rm_options);
254           assert (VALID_STATUS (status));
255           if (status == RM_ERROR)
256             fail = 1;
257
258           remove_fini ();
259
260           if (fail)
261             error (0, errno, _("cannot remove %s"), quote (dir_to_remove));
262         }
263
264       if (copy_into_self)
265         fail = 1;
266     }
267
268   return fail;
269 }
270
271 /* Move file SOURCE onto DEST.  Handles the case when DEST is a directory.
272    DEST_IS_DIR must be nonzero when DEST is a directory or a symlink to a
273    directory and zero otherwise.
274    Return 0 if successful, non-zero if an error occurred.  */
275
276 static int
277 movefile (char *source, char *dest, int dest_is_dir,
278           const struct cp_options *x)
279 {
280   int dest_had_trailing_slash = strip_trailing_slashes_2 (dest);
281   int fail;
282
283   /* This code was introduced to handle the ambiguity in the semantics
284      of mv that is induced by the varying semantics of the rename function.
285      Some systems (e.g., Linux) have a rename function that honors a
286      trailing slash, while others (like Solaris 5,6,7) have a rename
287      function that ignores a trailing slash.  I believe the Linux
288      rename semantics are POSIX and susv2 compliant.  */
289
290   if (remove_trailing_slashes)
291     strip_trailing_slashes_2 (source);
292
293   /* In addition to when DEST is a directory, if DEST has a trailing
294      slash and neither SOURCE nor DEST is a directory, presume the target
295      is DEST/`basename source`.  This converts `mv x y/' to `mv x y/x'.
296      This change means that the command `mv any file/' will now fail
297      rather than performing the move.  The case when SOURCE is a
298      directory and DEST is not is properly diagnosed by do_move.  */
299
300   if (dest_is_dir || (dest_had_trailing_slash && !is_real_dir (source)))
301     {
302       /* DEST is a directory; build full target filename. */
303       char *src_basename;
304       char *new_dest;
305
306       /* Remove trailing slashes before taking base_name.
307          Otherwise, base_name ("a/") returns "".  */
308       strip_trailing_slashes_2 (source);
309
310       src_basename = base_name (source);
311       new_dest = path_concat (dest, src_basename, NULL);
312       if (new_dest == NULL)
313         xalloc_die ();
314       fail = do_move (source, new_dest, x);
315
316       /* Do not free new_dest.  It may have been squirreled away by
317          the remember_copied function.  */
318     }
319   else
320     {
321       fail = do_move (source, dest, x);
322     }
323
324   return fail;
325 }
326
327 void
328 usage (int status)
329 {
330   if (status != 0)
331     fprintf (stderr, _("Try `%s --help' for more information.\n"),
332              program_name);
333   else
334     {
335       printf (_("\
336 Usage: %s [OPTION]... SOURCE DEST\n\
337   or:  %s [OPTION]... SOURCE... DIRECTORY\n\
338   or:  %s [OPTION]... --target-directory=DIRECTORY SOURCE...\n\
339 "),
340               program_name, program_name, program_name);
341       printf (_("\
342 Rename SOURCE to DEST, or move SOURCE(s) to DIRECTORY.\n\
343 \n\
344       --backup[=CONTROL]       make a backup of each existing destination file\n\
345   -b                           like --backup but does not accept an argument\n\
346   -f, --force                  never prompt before overwriting\n\
347   -i, --interactive            prompt before overwrite\n\
348       --strip-trailing-slashes  remove any trailing slashes from each SOURCE\n\
349                                  argument\n\
350   -S, --suffix=SUFFIX          override the usual backup suffix\n\
351       --target-directory=DIRECTORY  move all SOURCE arguments into DIRECTORY\n\
352   -u, --update                 move only older or brand new non-directories\n\
353   -v, --verbose                explain what is being done\n\
354       --help                   display this help and exit\n\
355       --version                output version information and exit\n\
356 \n\
357 "));
358       printf (_("\
359 The backup suffix is `~', unless set with --suffix or SIMPLE_BACKUP_SUFFIX.\n\
360 The version control method may be selected via the --backup option or through\n\
361 the VERSION_CONTROL environment variable.  Here are the values:\n\
362 \n\
363   none, off       never make backups (even if --backup is given)\n\
364   numbered, t     make numbered backups\n\
365   existing, nil   numbered if numbered backups exist, simple otherwise\n\
366   simple, never   always make simple backups\n\
367 "));
368       puts (_("\nReport bugs to <bug-fileutils@gnu.org>."));
369     }
370   exit (status);
371 }
372
373 int
374 main (int argc, char **argv)
375 {
376   int c;
377   int errors;
378   int make_backups = 0;
379   int dest_is_dir;
380   char *backup_suffix_string;
381   char *version_control_string = NULL;
382   struct cp_options x;
383   char *target_directory = NULL;
384   int target_directory_specified;
385   unsigned int n_files;
386   char **file;
387
388   program_name = argv[0];
389   setlocale (LC_ALL, "");
390   bindtextdomain (PACKAGE, LOCALEDIR);
391   textdomain (PACKAGE);
392
393   atexit (close_stdout);
394
395   cp_option_init (&x);
396
397   /* FIXME: consider not calling getenv for SIMPLE_BACKUP_SUFFIX unless
398      we'll actually use backup_suffix_string.  */
399   backup_suffix_string = getenv ("SIMPLE_BACKUP_SUFFIX");
400
401   errors = 0;
402
403   while ((c = getopt_long (argc, argv, "bfiuvS:V:", long_options, NULL)) != -1)
404     {
405       switch (c)
406         {
407         case 0:
408           break;
409
410         case 'V':  /* FIXME: this is deprecated.  Remove it in 2001.  */
411           error (0, 0,
412                  _("warning: --version-control (-V) is obsolete;  support for\
413  it\nwill be removed in some future release.  Use --backup=%s instead."
414                    ), optarg);
415           /* Fall through.  */
416
417         case 'b':
418           make_backups = 1;
419           if (optarg)
420             version_control_string = optarg;
421           break;
422         case 'f':
423           x.interactive = 0;
424           break;
425         case 'i':
426           x.interactive = 1;
427           break;
428         case STRIP_TRAILING_SLASHES_OPTION:
429           remove_trailing_slashes = 1;
430           break;
431         case TARGET_DIRECTORY_OPTION:
432           target_directory = optarg;
433           break;
434         case 'u':
435           x.update = 1;
436           break;
437         case 'v':
438           x.verbose = 1;
439           break;
440         case 'S':
441           make_backups = 1;
442           backup_suffix_string = optarg;
443           break;
444         case_GETOPT_HELP_CHAR;
445         case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
446         default:
447           usage (1);
448         }
449     }
450
451   n_files = argc - optind;
452   file = argv + optind;
453
454   target_directory_specified = (target_directory != NULL);
455   if (target_directory == NULL && n_files != 0)
456     target_directory = file[n_files - 1];
457
458   dest_is_dir = (n_files > 0 && isdir (target_directory));
459
460   if (n_files == 0 || (n_files == 1 && !target_directory_specified))
461     {
462       error (0, 0, _("missing file argument"));
463       usage (1);
464     }
465
466   if (target_directory_specified)
467     {
468       if (!dest_is_dir)
469         {
470           error (0, 0, _("specified target, %s is not a directory"),
471                  quote (target_directory));
472           usage (1);
473         }
474     }
475   else if (n_files > 2 && !dest_is_dir)
476     {
477       error (0, 0,
478             _("when moving multiple files, last argument must be a directory"));
479       usage (1);
480     }
481
482   if (backup_suffix_string)
483     simple_backup_suffix = xstrdup (backup_suffix_string);
484
485   x.backup_type = (make_backups
486                    ? xget_version (_("backup type"),
487                                    version_control_string)
488                    : none);
489
490   /* Move each arg but the last into the target_directory.  */
491   {
492     unsigned int last_file_idx = (target_directory_specified
493                                   ? n_files - 1
494                                   : n_files - 2);
495     unsigned int i;
496     for (i = 0; i <= last_file_idx; ++i)
497       errors |= movefile (file[i], target_directory, dest_is_dir, &x);
498   }
499
500   exit (errors);
501 }