Adjust to backup enum rename.
[platform/upstream/coreutils.git] / src / mv.c
1 /* mv -- move or rename files
2    Copyright (C) 86, 89, 90, 91, 1995-2004 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 #include <config.h>
21 #include <stdio.h>
22 #include <getopt.h>
23 #include <sys/types.h>
24 #include <assert.h>
25
26 #include "system.h"
27 #include "argmatch.h"
28 #include "backupfile.h"
29 #include "copy.h"
30 #include "cp-hash.h"
31 #include "dirname.h"
32 #include "error.h"
33 #include "path-concat.h"
34 #include "quote.h"
35 #include "remove.h"
36
37 /* The official name of this program (e.g., no `g' prefix).  */
38 #define PROGRAM_NAME "mv"
39
40 #define AUTHORS "Mike Parker", "David MacKenzie", "Jim Meyering"
41
42 /* Initial number of entries in each hash table entry's table of inodes.  */
43 #define INITIAL_HASH_MODULE 100
44
45 /* Initial number of entries in the inode hash table.  */
46 #define INITIAL_ENTRY_TAB_SIZE 70
47
48 /* For long options that have no equivalent short option, use a
49    non-character as a pseudo short option, starting with CHAR_MAX + 1.  */
50 enum
51 {
52   REPLY_OPTION = CHAR_MAX + 1,
53   STRIP_TRAILING_SLASHES_OPTION
54 };
55
56 /* The name this program was run with. */
57 char *program_name;
58
59 /* Remove any trailing slashes from each SOURCE argument.  */
60 static bool remove_trailing_slashes;
61
62 /* Valid arguments to the `--reply' option. */
63 static char const* const reply_args[] =
64 {
65   "yes", "no", "query", 0
66 };
67
68 /* The values that correspond to the above strings. */
69 static int const reply_vals[] =
70 {
71   I_ALWAYS_YES, I_ALWAYS_NO, I_ASK_USER
72 };
73
74 static struct option const long_options[] =
75 {
76   {"backup", optional_argument, NULL, 'b'},
77   {"force", no_argument, NULL, 'f'},
78   {"interactive", no_argument, NULL, 'i'},
79   {"no-target-directory", no_argument, NULL, 'T'},
80   {"reply", required_argument, NULL, REPLY_OPTION},
81   {"strip-trailing-slashes", no_argument, NULL, STRIP_TRAILING_SLASHES_OPTION},
82   {"suffix", required_argument, NULL, 'S'},
83   {"target-directory", required_argument, NULL, 't'},
84   {"update", no_argument, NULL, 'u'},
85   {"verbose", no_argument, NULL, 'v'},
86   {"version-control", required_argument, NULL, 'V'},
87   {GETOPT_HELP_OPTION_DECL},
88   {GETOPT_VERSION_OPTION_DECL},
89   {NULL, 0, NULL, 0}
90 };
91
92 static void
93 rm_option_init (struct rm_options *x)
94 {
95   x->unlink_dirs = false;
96   x->ignore_missing_files = false;
97   x->root_dev_ino = NULL;
98   x->recursive = true;
99
100   /* Should we prompt for removal, too?  No.  Prompting for the `move'
101      part is enough.  It implies removal.  */
102   x->interactive = 0;
103   x->stdin_tty = false;
104
105   x->verbose = false;
106
107   /* Since this program may well have to process additional command
108      line arguments after any call to `rm', that function must preserve
109      the initial working directory, in case one of those is a
110      `.'-relative name.  */
111   x->require_restore_cwd = true;
112 }
113
114 static void
115 cp_option_init (struct cp_options *x)
116 {
117   x->copy_as_regular = false;  /* FIXME: maybe make this an option */
118   x->dereference = DEREF_NEVER;
119   x->unlink_dest_before_opening = false;
120   x->unlink_dest_after_failed_open = false;
121   x->hard_link = false;
122   x->interactive = I_UNSPECIFIED;
123   x->move_mode = true;
124   x->myeuid = geteuid ();
125   x->one_file_system = false;
126   x->preserve_ownership = true;
127   x->preserve_links = true;
128   x->preserve_mode = true;
129   x->preserve_timestamps = true;
130   x->require_preserve = false;  /* FIXME: maybe make this an option */
131   x->recursive = true;
132   x->sparse_mode = SPARSE_AUTO;  /* FIXME: maybe make this an option */
133   x->symbolic_link = false;
134   x->set_mode = false;
135   x->mode = 0;
136   x->stdin_tty = isatty (STDIN_FILENO);
137
138   /* Find out the current file creation mask, to knock the right bits
139      when using chmod.  The creation mask is set to be liberal, so
140      that created directories can be written, even if it would not
141      have been allowed with the mask this process was started with.  */
142   x->umask_kill = ~ umask (0);
143
144   x->update = false;
145   x->verbose = false;
146   x->dest_info = NULL;
147   x->src_info = NULL;
148 }
149
150 /* FILE is the last operand of this command.  Return true if FILE is a
151    directory.  But report an error there is a problem accessing FILE,
152    or if FILE does not exist but would have to refer to an existing
153    directory if it referred to anything at all.  */
154
155 static bool
156 target_directory_operand (char const *file)
157 {
158   char const *b = base_name (file);
159   size_t blen = strlen (b);
160   bool looks_like_a_dir = (blen == 0 || ISSLASH (b[blen - 1]));
161   struct stat st;
162   int err = (stat (file, &st) == 0 ? 0 : errno);
163   bool is_a_dir = !err && S_ISDIR (st.st_mode);
164   if (err && err != ENOENT)
165     error (EXIT_FAILURE, err, _("accessing %s"), quote (file));
166   if (is_a_dir < looks_like_a_dir)
167     error (EXIT_FAILURE, err, _("target %s is not a directory"), quote (file));
168   return is_a_dir;
169 }
170
171 /* Move SOURCE onto DEST.  Handles cross-file-system moves.
172    If SOURCE is a directory, DEST must not exist.
173    Return true if successful.  */
174
175 static bool
176 do_move (const char *source, const char *dest, const struct cp_options *x)
177 {
178   bool copy_into_self;
179   bool rename_succeeded;
180   bool ok = copy (source, dest, false, x, &copy_into_self, &rename_succeeded);
181
182   if (ok)
183     {
184       char const *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           ok = false;
200         }
201       else if (rename_succeeded)
202         {
203           /* No need to remove anything.  SOURCE was successfully
204              renamed to DEST.  Or the user declined to rename a file.  */
205           dir_to_remove = NULL;
206         }
207       else
208         {
209           /* This may mean SOURCE and DEST referred to different devices.
210              It may also conceivably mean that even though they referred
211              to the same device, rename wasn't implemented for that device.
212
213              E.g., (from Joel N. Weber),
214              [...] there might someday be cases where you can't rename
215              but you can copy where the device name is the same, especially
216              on Hurd.  Consider an ftpfs with a primitive ftp server that
217              supports uploading, downloading and deleting, but not renaming.
218
219              Also, note that comparing device numbers is not a reliable
220              check for `can-rename'.  Some systems can be set up so that
221              files from many different physical devices all have the same
222              st_dev field.  This is a feature of some NFS mounting
223              configurations.
224
225              We reach this point if SOURCE has been successfully copied
226              to DEST.  Now we have to remove SOURCE.
227
228              This function used to resort to copying only when rename
229              failed and set errno to EXDEV.  */
230
231           dir_to_remove = source;
232         }
233
234       if (dir_to_remove != NULL)
235         {
236           struct rm_options rm_options;
237           enum RM_status status;
238
239           rm_option_init (&rm_options);
240           rm_options.verbose = x->verbose;
241
242           status = rm (1, &dir_to_remove, &rm_options);
243           assert (VALID_STATUS (status));
244           if (status == RM_ERROR)
245             ok = false;
246         }
247     }
248
249   return ok;
250 }
251
252 /* Move file SOURCE onto DEST.  Handles the case when DEST is a directory.
253    Treat DEST as a directory if DEST_IS_DIR.
254    Return true if successful.  */
255
256 static bool
257 movefile (char *source, char *dest, bool dest_is_dir,
258           const struct cp_options *x)
259 {
260   bool ok;
261
262   /* This code was introduced to handle the ambiguity in the semantics
263      of mv that is induced by the varying semantics of the rename function.
264      Some systems (e.g., Linux) have a rename function that honors a
265      trailing slash, while others (like Solaris 5,6,7) have a rename
266      function that ignores a trailing slash.  I believe the Linux
267      rename semantics are POSIX and susv2 compliant.  */
268
269   strip_trailing_slashes (dest);
270   if (remove_trailing_slashes)
271     strip_trailing_slashes (source);
272
273   if (dest_is_dir)
274     {
275       /* Treat DEST as a directory; build the full filename.  */
276       char const *src_basename = base_name (source);
277       char *new_dest = path_concat (dest, src_basename, NULL);
278       strip_trailing_slashes (new_dest);
279       ok = do_move (source, new_dest, x);
280       free (new_dest);
281     }
282   else
283     {
284       ok = do_move (source, dest, x);
285     }
286
287   return ok;
288 }
289
290 void
291 usage (int status)
292 {
293   if (status != EXIT_SUCCESS)
294     fprintf (stderr, _("Try `%s --help' for more information.\n"),
295              program_name);
296   else
297     {
298       printf (_("\
299 Usage: %s [OPTION]... [-T] SOURCE DEST\n\
300   or:  %s [OPTION]... SOURCE... DIRECTORY\n\
301   or:  %s [OPTION]... -t DIRECTORY SOURCE...\n\
302 "),
303               program_name, program_name, program_name);
304       fputs (_("\
305 Rename SOURCE to DEST, or move SOURCE(s) to DIRECTORY.\n\
306 \n\
307 "), stdout);
308       fputs (_("\
309 Mandatory arguments to long options are mandatory for short options too.\n\
310 "), stdout);
311       fputs (_("\
312       --backup[=CONTROL]       make a backup of each existing destination file\n\
313   -b                           like --backup but does not accept an argument\n\
314   -f, --force                  do not prompt before overwriting\n\
315                                  (equivalent to --reply=yes)\n\
316   -i, --interactive            prompt before overwrite\n\
317                                  (equivalent to --reply=query)\n\
318 "), stdout);
319       fputs (_("\
320       --reply={yes,no,query}   specify how to handle the prompt about an\n\
321                                  existing destination file\n\
322       --strip-trailing-slashes remove any trailing slashes from each SOURCE\n\
323                                  argument\n\
324   -S, --suffix=SUFFIX          override the usual backup suffix\n\
325 "), stdout);
326       fputs (_("\
327   -t, --target-directory=DIRECTORY  move all SOURCE arguments into DIRECTORY\n\
328   -T, --no-target-directory    treat DEST as a normal file\n\
329   -u, --update                 move only when the SOURCE file is newer\n\
330                                  than the destination file or when the\n\
331                                  destination file is missing\n\
332   -v, --verbose                explain what is being done\n\
333 "), stdout);
334       fputs (HELP_OPTION_DESCRIPTION, stdout);
335       fputs (VERSION_OPTION_DESCRIPTION, stdout);
336       fputs (_("\
337 \n\
338 The backup suffix is `~', unless set with --suffix or SIMPLE_BACKUP_SUFFIX.\n\
339 The version control method may be selected via the --backup option or through\n\
340 the VERSION_CONTROL environment variable.  Here are the values:\n\
341 \n\
342 "), stdout);
343       fputs (_("\
344   none, off       never make backups (even if --backup is given)\n\
345   numbered, t     make numbered backups\n\
346   existing, nil   numbered if numbered backups exist, simple otherwise\n\
347   simple, never   always make simple backups\n\
348 "), stdout);
349       printf (_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT);
350     }
351   exit (status);
352 }
353
354 int
355 main (int argc, char **argv)
356 {
357   int c;
358   bool ok;
359   bool make_backups = false;
360   char *backup_suffix_string;
361   char *version_control_string = NULL;
362   struct cp_options x;
363   char *target_directory = NULL;
364   bool no_target_directory = false;
365   int n_files;
366   char **file;
367
368   initialize_main (&argc, &argv);
369   program_name = argv[0];
370   setlocale (LC_ALL, "");
371   bindtextdomain (PACKAGE, LOCALEDIR);
372   textdomain (PACKAGE);
373
374   atexit (close_stdout);
375
376   cp_option_init (&x);
377
378   /* FIXME: consider not calling getenv for SIMPLE_BACKUP_SUFFIX unless
379      we'll actually use backup_suffix_string.  */
380   backup_suffix_string = getenv ("SIMPLE_BACKUP_SUFFIX");
381
382   while ((c = getopt_long (argc, argv, "bfit:uvS:TV:", long_options, NULL))
383          != -1)
384     {
385       switch (c)
386         {
387         case 0:
388           break;
389
390         case 'V':  /* FIXME: this is deprecated.  Remove it in 2001.  */
391           error (0, 0,
392                  _("warning: --version-control (-V) is obsolete;  support for\
393  it\nwill be removed in some future release.  Use --backup=%s instead."
394                    ), optarg);
395           /* Fall through.  */
396
397         case 'b':
398           make_backups = true;
399           if (optarg)
400             version_control_string = optarg;
401           break;
402         case 'f':
403           x.interactive = I_ALWAYS_YES;
404           break;
405         case 'i':
406           x.interactive = I_ASK_USER;
407           break;
408         case REPLY_OPTION:
409           x.interactive = XARGMATCH ("--reply", optarg,
410                                      reply_args, reply_vals);
411           break;
412         case STRIP_TRAILING_SLASHES_OPTION:
413           remove_trailing_slashes = true;
414           break;
415         case 't':
416           if (target_directory)
417             error (EXIT_FAILURE, 0, _("multiple target directories specified"));
418           else
419             {
420               struct stat st;
421               if (stat (optarg, &st) != 0)
422                 error (EXIT_FAILURE, errno, _("accessing %s"), quote (optarg));
423               if (! S_ISDIR (st.st_mode))
424                 error (EXIT_FAILURE, 0, _("target %s is not a directory"),
425                        quote (optarg));
426             }
427           target_directory = optarg;
428           break;
429         case 'T':
430           no_target_directory = true;
431           break;
432         case 'u':
433           x.update = true;
434           break;
435         case 'v':
436           x.verbose = true;
437           break;
438         case 'S':
439           make_backups = true;
440           backup_suffix_string = optarg;
441           break;
442         case_GETOPT_HELP_CHAR;
443         case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
444         default:
445           usage (EXIT_FAILURE);
446         }
447     }
448
449   n_files = argc - optind;
450   file = argv + optind;
451
452   if (n_files <= !target_directory)
453     {
454       if (n_files <= 0)
455         error (0, 0, _("missing file operand"));
456       else
457         error (0, 0, _("missing destination file operand after %s"),
458                quote (file[0]));
459       usage (EXIT_FAILURE);
460     }
461
462   if (no_target_directory)
463     {
464       if (target_directory)
465         error (EXIT_FAILURE, 0,
466                _("Cannot combine --target-directory (-t) "
467                  "and --no-target-directory (-T)"));
468       if (2 < n_files)
469         {
470           error (0, 0, _("extra operand %s"), quote (file[2]));
471           usage (EXIT_FAILURE);
472         }
473     }
474   else if (!target_directory)
475     {
476       if (2 <= n_files && target_directory_operand (file[n_files - 1]))
477         target_directory = file[--n_files];
478       else if (2 < n_files)
479         error (EXIT_FAILURE, 0, _("target %s is not a directory"),
480                quote (file[n_files - 1]));
481     }
482
483   if (backup_suffix_string)
484     simple_backup_suffix = xstrdup (backup_suffix_string);
485
486   x.backup_type = (make_backups
487                    ? xget_version (_("backup type"),
488                                    version_control_string)
489                    : no_backups);
490
491   hash_init ();
492
493   if (target_directory)
494     {
495       int i;
496
497       /* Initialize the hash table only if we'll need it.
498          The problem it is used to detect can arise only if there are
499          two or more files to move.  */
500       if (2 <= n_files)
501         dest_info_init (&x);
502
503       ok = true;
504       for (i = 0; i < n_files; ++i)
505         ok &= movefile (file[i], target_directory, true, &x);
506     }
507   else
508     ok = movefile (file[0], file[1], false, &x);
509
510   exit (ok ? EXIT_SUCCESS : EXIT_FAILURE);
511 }