(main): Using --reply now evokes a warning.
[platform/upstream/coreutils.git] / src / mv.c
1 /* mv -- move or rename files
2    Copyright (C) 86, 89, 90, 91, 1995-2005 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 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 "filenamecat.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", NULL
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}, /* Deprecated 2005-07-03,
81                                                        remove in 2008. */
82   {"strip-trailing-slashes", no_argument, NULL, STRIP_TRAILING_SLASHES_OPTION},
83   {"suffix", required_argument, NULL, 'S'},
84   {"target-directory", required_argument, NULL, 't'},
85   {"update", no_argument, NULL, 'u'},
86   {"verbose", no_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->chown_privileges = chown_privileges ();
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 = file_name_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   -i, --interactive            prompt before overwrite\n\
316 "), stdout);
317       fputs (_("\
318       --strip-trailing-slashes remove any trailing slashes from each SOURCE\n\
319                                  argument\n\
320   -S, --suffix=SUFFIX          override the usual backup suffix\n\
321 "), stdout);
322       fputs (_("\
323   -t, --target-directory=DIRECTORY  move all SOURCE arguments into DIRECTORY\n\
324   -T, --no-target-directory    treat DEST as a normal file\n\
325   -u, --update                 move only when the SOURCE file is newer\n\
326                                  than the destination file or when the\n\
327                                  destination file is missing\n\
328   -v, --verbose                explain what is being done\n\
329 "), stdout);
330       fputs (HELP_OPTION_DESCRIPTION, stdout);
331       fputs (VERSION_OPTION_DESCRIPTION, stdout);
332       fputs (_("\
333 \n\
334 The backup suffix is `~', unless set with --suffix or SIMPLE_BACKUP_SUFFIX.\n\
335 The version control method may be selected via the --backup option or through\n\
336 the VERSION_CONTROL environment variable.  Here are the values:\n\
337 \n\
338 "), stdout);
339       fputs (_("\
340   none, off       never make backups (even if --backup is given)\n\
341   numbered, t     make numbered backups\n\
342   existing, nil   numbered if numbered backups exist, simple otherwise\n\
343   simple, never   always make simple backups\n\
344 "), stdout);
345       printf (_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT);
346     }
347   exit (status);
348 }
349
350 int
351 main (int argc, char **argv)
352 {
353   int c;
354   bool ok;
355   bool make_backups = false;
356   char *backup_suffix_string;
357   char *version_control_string = NULL;
358   struct cp_options x;
359   char *target_directory = NULL;
360   bool no_target_directory = false;
361   int n_files;
362   char **file;
363
364   initialize_main (&argc, &argv);
365   program_name = argv[0];
366   setlocale (LC_ALL, "");
367   bindtextdomain (PACKAGE, LOCALEDIR);
368   textdomain (PACKAGE);
369
370   atexit (close_stdout);
371
372   cp_option_init (&x);
373
374   /* FIXME: consider not calling getenv for SIMPLE_BACKUP_SUFFIX unless
375      we'll actually use backup_suffix_string.  */
376   backup_suffix_string = getenv ("SIMPLE_BACKUP_SUFFIX");
377
378   while ((c = getopt_long (argc, argv, "bfit:uvS:T", long_options, NULL))
379          != -1)
380     {
381       switch (c)
382         {
383         case 'b':
384           make_backups = true;
385           if (optarg)
386             version_control_string = optarg;
387           break;
388         case 'f':
389           x.interactive = I_ALWAYS_YES;
390           break;
391         case 'i':
392           x.interactive = I_ASK_USER;
393           break;
394         case REPLY_OPTION: /* Deprecated */
395           x.interactive = XARGMATCH ("--reply", optarg,
396                                      reply_args, reply_vals);
397           error (0, 0,
398                  _("the --reply option is deprecated; use -i or -f instead"));
399           break;
400         case STRIP_TRAILING_SLASHES_OPTION:
401           remove_trailing_slashes = true;
402           break;
403         case 't':
404           if (target_directory)
405             error (EXIT_FAILURE, 0, _("multiple target directories specified"));
406           else
407             {
408               struct stat st;
409               if (stat (optarg, &st) != 0)
410                 error (EXIT_FAILURE, errno, _("accessing %s"), quote (optarg));
411               if (! S_ISDIR (st.st_mode))
412                 error (EXIT_FAILURE, 0, _("target %s is not a directory"),
413                        quote (optarg));
414             }
415           target_directory = optarg;
416           break;
417         case 'T':
418           no_target_directory = true;
419           break;
420         case 'u':
421           x.update = true;
422           break;
423         case 'v':
424           x.verbose = true;
425           break;
426         case 'S':
427           make_backups = true;
428           backup_suffix_string = optarg;
429           break;
430         case_GETOPT_HELP_CHAR;
431         case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
432         default:
433           usage (EXIT_FAILURE);
434         }
435     }
436
437   n_files = argc - optind;
438   file = argv + optind;
439
440   if (n_files <= !target_directory)
441     {
442       if (n_files <= 0)
443         error (0, 0, _("missing file operand"));
444       else
445         error (0, 0, _("missing destination file operand after %s"),
446                quote (file[0]));
447       usage (EXIT_FAILURE);
448     }
449
450   if (no_target_directory)
451     {
452       if (target_directory)
453         error (EXIT_FAILURE, 0,
454                _("Cannot combine --target-directory (-t) "
455                  "and --no-target-directory (-T)"));
456       if (2 < n_files)
457         {
458           error (0, 0, _("extra operand %s"), quote (file[2]));
459           usage (EXIT_FAILURE);
460         }
461     }
462   else if (!target_directory)
463     {
464       if (2 <= n_files && target_directory_operand (file[n_files - 1]))
465         target_directory = file[--n_files];
466       else if (2 < n_files)
467         error (EXIT_FAILURE, 0, _("target %s is not a directory"),
468                quote (file[n_files - 1]));
469     }
470
471   if (backup_suffix_string)
472     simple_backup_suffix = xstrdup (backup_suffix_string);
473
474   x.backup_type = (make_backups
475                    ? xget_version (_("backup type"),
476                                    version_control_string)
477                    : no_backups);
478
479   hash_init ();
480
481   if (target_directory)
482     {
483       int i;
484
485       /* Initialize the hash table only if we'll need it.
486          The problem it is used to detect can arise only if there are
487          two or more files to move.  */
488       if (2 <= n_files)
489         dest_info_init (&x);
490
491       ok = true;
492       for (i = 0; i < n_files; ++i)
493         ok &= movefile (file[i], target_directory, true, &x);
494     }
495   else
496     ok = movefile (file[0], file[1], false, &x);
497
498   exit (ok ? EXIT_SUCCESS : EXIT_FAILURE);
499 }