(show_date): Change an automatic aggregate initializer
[coreutils.git] / src / mv.c
blob0ed75e22d82d7da84b029a1200b0682b7e7f54dd
1 /* mv -- move or rename files
2 Copyright (C) 86, 89, 90, 91, 1995-1999 Free Software Foundation, Inc.
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.
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.
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. */
18 /* Options:
19 -f, --force Assume a 'y' answer to all questions it would
20 normally ask, and not ask the questions.
22 -i, --interactive Require confirmation from the user before
23 performing any move that would destroy an
24 existing file.
26 -u, --update Do not move a nondirectory that has an
27 existing destination with the same or newer
28 modification time.
30 -v, --verbose List the name of each file as it is moved, and
31 the name it is moved to.
33 -b, --backup
34 -S, --suffix
35 -V, --version-control
36 Backup file creation.
38 Written by Mike Parker, David MacKenzie, and Jim Meyering */
40 #ifdef _AIX
41 #pragma alloca
42 #endif
44 #include <config.h>
45 #include <stdio.h>
46 #include <getopt.h>
47 #include <sys/types.h>
48 #include <assert.h>
50 #include "system.h"
51 #include "backupfile.h"
52 #include "copy.h"
53 #include "cp-hash.h"
54 #include "error.h"
55 #include "path-concat.h"
56 #include "remove.h"
58 /* The official name of this program (e.g., no `g' prefix). */
59 #define PROGRAM_NAME "mv"
61 #define AUTHORS "Mike Parker, David MacKenzie, and Jim Meyering"
63 /* Initial number of entries in each hash table entry's table of inodes. */
64 #define INITIAL_HASH_MODULE 100
66 /* Initial number of entries in the inode hash table. */
67 #define INITIAL_ENTRY_TAB_SIZE 70
69 int euidaccess ();
70 int full_write ();
71 int isdir ();
72 int lstat ();
73 int yesno ();
75 /* The name this program was run with. */
76 char *program_name;
78 /* If nonzero, stdin is a tty. */
79 static int stdin_tty;
81 static struct option const long_options[] =
83 {"backup", no_argument, NULL, 'b'},
84 {"force", no_argument, NULL, 'f'},
85 {"interactive", no_argument, NULL, 'i'},
86 {"suffix", required_argument, NULL, 'S'},
87 {"update", no_argument, NULL, 'u'},
88 {"verbose", no_argument, NULL, 'v'},
89 {"version-control", required_argument, NULL, 'V'},
90 {GETOPT_HELP_OPTION_DECL},
91 {GETOPT_VERSION_OPTION_DECL},
92 {NULL, 0, NULL, 0}
95 static void
96 rm_option_init (struct rm_options *x)
98 x->unlink_dirs = 0;
100 /* FIXME: maybe this should be 1. The POSIX spec doesn't specify. */
101 x->ignore_missing_files = 0;
103 x->recursive = 1;
105 /* Should we prompt for removal, too? No. Prompting for the `move'
106 part is enough. It implies removal. */
107 x->interactive = 0;
108 x->stdin_tty = 0;
110 x->verbose = 0;
113 static void
114 cp_option_init (struct cp_options *x)
116 x->copy_as_regular = 0; /* FIXME: maybe make this an option */
117 x->dereference = 0;
118 x->force = 0;
119 x->failed_unlink_is_fatal = 1;
120 x->hard_link = 0;
121 x->interactive = 0;
122 x->move_mode = 1;
123 x->myeuid = geteuid ();
124 x->one_file_system = 0;
125 x->preserve_owner_and_group = 1;
126 x->preserve_chmod_bits = 1;
127 x->preserve_timestamps = 1;
128 x->require_preserve = 0; /* FIXME: maybe make this an option */
129 x->recursive = 1;
130 x->sparse_mode = SPARSE_AUTO; /* FIXME: maybe make this an option */
131 x->symbolic_link = 0;
132 x->set_mode = 0;
133 x->mode = 0;
135 /* Find out the current file creation mask, to knock the right bits
136 when using chmod. The creation mask is set to be liberal, so
137 that created directories can be written, even if it would not
138 have been allowed with the mask this process was started with. */
139 x->umask_kill = ~ umask (0);
141 x->update = 0;
142 x->verbose = 0;
143 x->xstat = lstat;
146 /* If PATH is an existing directory, return nonzero, else 0. */
148 static int
149 is_real_dir (const char *path)
151 struct stat stats;
153 return lstat (path, &stats) == 0 && S_ISDIR (stats.st_mode);
156 /* Move SOURCE onto DEST. Handles cross-filesystem moves.
157 If SOURCE is a directory, DEST must not exist.
158 Return 0 if successful, non-zero if an error occurred. */
160 static int
161 do_move (const char *source, const char *dest, const struct cp_options *x)
163 static int first = 1;
164 int copy_into_self;
165 int rename_succeeded;
166 int fail;
168 if (first)
170 first = 0;
172 /* Allocate space for remembering copied and created files. */
173 hash_init (INITIAL_HASH_MODULE, INITIAL_ENTRY_TAB_SIZE);
176 fail = copy (source, dest, 0, x, &copy_into_self, &rename_succeeded);
178 if (!fail)
180 const char *dir_to_remove;
181 if (copy_into_self)
183 /* In general, when copy returns with copy_into_self set, SOURCE is
184 the same as, or a parent of DEST. In this case we know it's a
185 parent. It doesn't make sense to move a directory into itself, and
186 besides in some situations doing so would give highly nonintuitive
187 results. Run this `mkdir b; touch a c; mv * b' in an empty
188 directory. Here's the result of running echo `find b -print`:
189 b b/a b/b b/b/a b/c. Notice that only file `a' was copied
190 into b/b. Handle this by giving a diagnostic, removing the
191 copied-into-self directory, DEST (`b/b' in the example),
192 and failing. */
194 dir_to_remove = NULL;
195 error (0, 0,
196 _("cannot move `%s' to a subdirectory of itself, `%s'"),
197 source, dest);
199 else if (rename_succeeded)
201 /* No need to remove anything. SOURCE was successfully
202 renamed to DEST. */
203 dir_to_remove = NULL;
205 else
207 /* This may mean SOURCE and DEST referred to different devices.
208 It may also conceivably mean that even though they referred
209 to the same device, rename wasn't implemented for that device.
211 E.g., (from Joel N. Weber),
212 [...] there might someday be cases where you can't rename
213 but you can copy where the device name is the same, especially
214 on Hurd. Consider an ftpfs with a primitive ftp server that
215 supports uploading, downloading and deleting, but not renaming.
217 Also, note that comparing device numbers is not a reliable
218 check for `can-rename'. Some systems can be set up so that
219 files from many different physical devices all have the same
220 st_dev field. This is a feature of some NFS mounting
221 configurations.
223 We reach this point if SOURCE has been successfully copied
224 to DEST. Now we have to remove SOURCE.
226 This function used to resort to copying only when rename
227 failed and set errno to EXDEV. */
229 dir_to_remove = source;
232 if (dir_to_remove != NULL)
234 struct rm_options rm_options;
235 struct File_spec fs;
236 enum RM_status status;
238 rm_option_init (&rm_options);
239 rm_options.verbose = x->verbose;
241 remove_init ();
243 fspec_init_file (&fs, dir_to_remove);
244 status = rm (&fs, 1, &rm_options);
245 assert (VALID_STATUS (status));
246 if (status == RM_ERROR)
247 fail = 1;
249 remove_fini ();
251 if (fail)
252 error (0, errno, _("cannot remove `%s'"), dir_to_remove);
255 if (copy_into_self)
256 fail = 1;
259 return fail;
262 static int
263 strip_trailing_slashes_2 (char *path)
265 char *end_p = path + strlen (path) - 1;
266 char *slash = end_p;
268 while (slash > path && *slash == '/')
269 *slash-- = '\0';
271 return slash < end_p;
274 /* Move file SOURCE onto DEST. Handles the case when DEST is a directory.
275 DEST_IS_DIR must be nonzero when DEST is a directory or a symlink to a
276 directory and zero otherwise.
277 Return 0 if successful, non-zero if an error occurred. */
279 static int
280 movefile (char *source, char *dest, int dest_is_dir, const struct cp_options *x)
282 int dest_had_trailing_slash = strip_trailing_slashes_2 (dest);
283 int fail;
285 /* In addition to when DEST is a directory, if DEST has a trailing
286 slash and neither SOURCE nor DEST is a directory, presume the target
287 is DEST/`basename source`. This converts `mv x y/' to `mv x y/x'.
288 This change means that the command `mv any file/' will now fail
289 rather than performing the move. The case when SOURCE is a
290 directory and DEST is not is properly diagnosed by do_move. */
292 if (dest_is_dir || (dest_had_trailing_slash && !is_real_dir (source)))
294 /* DEST is a directory; build full target filename. */
295 char *src_basename;
296 char *new_dest;
298 /* Remove trailing slashes before taking base_name.
299 Otherwise, base_name ("a/") returns "". */
300 strip_trailing_slashes_2 (source);
302 src_basename = base_name (source);
303 new_dest = path_concat (dest, src_basename, NULL);
304 if (new_dest == NULL)
305 error (1, 0, _("virtual memory exhausted"));
306 fail = do_move (source, new_dest, x);
308 /* Do not free new_dest. It may have been squirelled away by
309 the remember_copied function. */
311 else
313 fail = do_move (source, dest, x);
316 return fail;
319 void
320 usage (int status)
322 if (status != 0)
323 fprintf (stderr, _("Try `%s --help' for more information.\n"),
324 program_name);
325 else
327 printf (_("\
328 Usage: %s [OPTION]... SOURCE DEST\n\
329 or: %s [OPTION]... SOURCE... DIRECTORY\n\
331 program_name, program_name);
332 printf (_("\
333 Rename SOURCE to DEST, or move SOURCE(s) to DIRECTORY.\n\
335 -b, --backup make backup before removal\n\
336 -f, --force remove existing destinations, never prompt\n\
337 -i, --interactive prompt before overwrite\n\
338 -S, --suffix=SUFFIX override the usual backup suffix\n\
339 -u, --update move only older or brand new non-directories\n\
340 -v, --verbose explain what is being done\n\
341 -V, --version-control=WORD override the usual version control\n\
342 --help display this help and exit\n\
343 --version output version information and exit\n\
345 "));
346 printf (_("\
347 The backup suffix is ~, unless set with SIMPLE_BACKUP_SUFFIX. The\n\
348 version control may be set with VERSION_CONTROL, values are:\n\
350 none, off never make backups (even if --backup is given)\n\
351 numbered, t make numbered backups\n\
352 existing, nil numbered if numbered backups exist, simple otherwise\n\
353 simple, never always make simple backups\n\
354 "));
355 puts (_("\nReport bugs to <bug-fileutils@gnu.org>."));
356 close_stdout ();
358 exit (status);
362 main (int argc, char **argv)
364 int c;
365 int errors;
366 int make_backups = 0;
367 int dest_is_dir;
368 const char *version;
369 struct cp_options x;
371 program_name = argv[0];
372 setlocale (LC_ALL, "");
373 bindtextdomain (PACKAGE, LOCALEDIR);
374 textdomain (PACKAGE);
376 cp_option_init (&x);
378 /* FIXME: consider not calling getenv for SIMPLE_BACKUP_SUFFIX unless
379 we'll actually use simple_backup_suffix. */
380 version = getenv ("SIMPLE_BACKUP_SUFFIX");
381 if (version)
382 simple_backup_suffix = version;
383 version = NULL;
385 errors = 0;
387 while ((c = getopt_long (argc, argv, "bfiuvS:V:", long_options, NULL)) != -1)
389 switch (c)
391 case 0:
392 break;
393 case 'b':
394 make_backups = 1;
395 break;
396 case 'f':
397 x.interactive = 0;
398 x.force = 1;
399 break;
400 case 'i':
401 x.interactive = 1;
402 x.force = 0;
403 break;
404 case 'u':
405 x.update = 1;
406 break;
407 case 'v':
408 x.verbose = 1;
409 break;
410 case 'S':
411 simple_backup_suffix = optarg;
412 break;
413 case 'V':
414 version = optarg;
415 break;
416 case_GETOPT_HELP_CHAR;
417 case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
418 default:
419 usage (1);
423 if (argc < optind + 2)
425 error (0, 0, "%s", (argc == optind
426 ? _("missing file arguments")
427 : _("missing file argument")));
428 usage (1);
431 x.backup_type = (make_backups
432 ? xget_version (_("--version-control"), version)
433 : none);
435 stdin_tty = isatty (STDIN_FILENO);
436 dest_is_dir = isdir (argv[argc - 1]);
438 if (argc > optind + 2 && !dest_is_dir)
439 error (1, 0,
440 _("when moving multiple files, last argument must be a directory"));
442 /* Move each arg but the last onto the last. */
443 for (; optind < argc - 1; ++optind)
444 errors |= movefile (argv[optind], argv[argc - 1], dest_is_dir, &x);
446 if (x.verbose)
447 close_stdout ();
448 exit (errors);