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