1 /* mv -- move or rename files
2 Copyright (C) 86, 89, 90, 91, 95, 96, 97, 1998 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)
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. */
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
26 -u, --update Do not move a nondirectory that has an
27 existing destination with the same or newer
30 -v, --verbose List the name of each file as it is moved, and
31 the name it is moved to.
38 Written by Mike Parker and David MacKenzie */
47 #include <sys/types.h>
50 #include "path-concat.h"
51 #include "backupfile.h"
56 # define chown(PATH, OWNER, GROUP) lchown(PATH, OWNER, GROUP)
60 enum backup_type
get_version ();
67 /* The name this program was run with. */
70 /* If nonzero, query the user before overwriting files. */
71 static int interactive
;
73 /* If nonzero, do not query the user before overwriting unwritable
75 static int override_mode
;
77 /* If nonzero, do not move a nondirectory that has an existing destination
78 with the same or newer modification time. */
79 static int update
= 0;
81 /* If nonzero, list each file as it is moved. */
84 /* If nonzero, stdin is a tty. */
87 /* If nonzero, display usage information and exit. */
90 /* If nonzero, print the version on standard output and exit. */
91 static int show_version
;
93 static struct option
const long_options
[] =
95 {"backup", no_argument
, NULL
, 'b'},
96 {"force", no_argument
, NULL
, 'f'},
97 {"interactive", no_argument
, NULL
, 'i'},
98 {"suffix", required_argument
, NULL
, 'S'},
99 {"update", no_argument
, &update
, 1},
100 {"verbose", no_argument
, &verbose
, 1},
101 {"version-control", required_argument
, NULL
, 'V'},
102 {"help", no_argument
, &show_help
, 1},
103 {"version", no_argument
, &show_version
, 1},
107 /* If PATH is an existing directory, return nonzero, else 0. */
110 is_real_dir (const char *path
)
114 return lstat (path
, &stats
) == 0 && S_ISDIR (stats
.st_mode
);
117 /* Apply as many of the file attributes (the struct stat fields: st_atime,
118 st_mtime, st_uid, st_gid, st_mode) of ATTR to FILE as possible.
119 Return non-zero if any operation failed; return zero otherwise. */
122 apply_attributes (const char *file
, const struct stat
*attr
)
125 mode_t mode
= attr
->st_mode
;
128 /* Try to apply the modtime and access time. */
129 tv
.actime
= attr
->st_atime
;
130 tv
.modtime
= attr
->st_mtime
;
131 if (utime (file
, &tv
))
133 error (0, errno
, "%s: unable to restore file times", file
);
137 /* chown would turn off set[ug]id bits for non-root, so do the
138 chown before the chmod. */
140 /* Try to apply group ID and owner ID. */
141 if (chown (file
, attr
->st_uid
, attr
->st_gid
))
143 error (0, errno
, "%s: unable to restore owner and group IDs", file
);
145 /* If the owner and group cannot be preserved, then mask off
146 any setgid and setuid bits. */
147 mode
&= (~(S_ISUID
| S_ISGID
));
151 /* Try to apply file mode. */
152 if (chmod (file
, mode
& 07777))
154 error (0, errno
, "%s: unable to restore file mode", file
);
161 /* Copy regular file SOURCE onto file DEST. SOURCE_STATS must be
162 the result of calling lstat on SOURCE.
163 Return 1 if an error occurred, 0 if successful. */
166 copy_reg (const char *source
, const char *dest
, const struct stat
*source_stats
)
171 int len
; /* Number of bytes read into `buf'. */
173 if (!S_ISREG (source_stats
->st_mode
))
176 _("cannot move `%s' across filesystems: Not a regular file"),
181 if (unlink (dest
) && errno
!= ENOENT
)
183 error (0, errno
, _("cannot remove `%s'"), dest
);
187 ifd
= open (source
, O_RDONLY
, 0);
190 error (0, errno
, "%s", source
);
193 ofd
= open (dest
, O_WRONLY
| O_CREAT
| O_TRUNC
, 0600);
196 error (0, errno
, "%s", dest
);
201 while ((len
= safe_read (ifd
, buf
, sizeof (buf
))) > 0)
203 if (full_write (ofd
, buf
, len
) < 0)
205 error (0, errno
, "%s", dest
);
214 error (0, errno
, "%s", source
);
223 error (0, errno
, "%s", source
);
229 error (0, errno
, "%s", dest
);
233 /* Try to apply the attributes of SOURCE to DEST.
234 Each failure gets a diagnostic, but POSIX requires that failure
235 to preserve attributes not change mv's exit status. */
236 apply_attributes (dest
, source_stats
);
241 /* Move SOURCE onto DEST. Handles cross-filesystem moves.
242 If SOURCE is a directory, DEST must not exist.
243 Return 0 if successful, non-zero if an error occurred. */
246 do_move (const char *source
, const char *dest
)
248 char *dest_backup
= NULL
;
249 struct stat source_stats
;
250 struct stat dest_stats
;
253 if (lstat (source
, &source_stats
) != 0)
255 error (0, errno
, "%s", source
);
259 if (lstat (dest
, &dest_stats
) == 0)
262 if (source_stats
.st_dev
== dest_stats
.st_dev
263 && source_stats
.st_ino
== dest_stats
.st_ino
)
265 error (0, 0, _("`%s' and `%s' are the same file"), source
, dest
);
269 if (S_ISDIR (dest_stats
.st_mode
))
271 error (0, 0, _("%s: cannot overwrite directory"), dest
);
275 if (!S_ISDIR (source_stats
.st_mode
) && update
276 && source_stats
.st_mtime
<= dest_stats
.st_mtime
)
279 if (!override_mode
&& (interactive
|| stdin_tty
)
280 && euidaccess (dest
, W_OK
))
282 fprintf (stderr
, _("%s: replace `%s', overriding mode %04o? "),
284 (unsigned int) (dest_stats
.st_mode
& 07777));
288 else if (interactive
)
290 fprintf (stderr
, _("%s: replace `%s'? "), program_name
, dest
);
295 if (backup_type
!= none
)
297 char *tmp_backup
= find_backup_file_name (dest
);
298 if (tmp_backup
== NULL
)
299 error (1, 0, _("virtual memory exhausted"));
300 dest_backup
= (char *) alloca (strlen (tmp_backup
) + 1);
301 strcpy (dest_backup
, tmp_backup
);
303 if (rename (dest
, dest_backup
))
307 error (0, errno
, _("cannot backup `%s'"), dest
);
315 else if (errno
!= ENOENT
)
317 error (0, errno
, "%s", dest
);
322 printf ("%s -> %s\n", source
, dest
);
324 /* Always try rename first. */
325 fail
= rename (source
, dest
);
329 /* This may mean SOURCE and DEST are on different devices.
330 It may also (conceivably) mean that even though they are
331 on the same device, rename isn't implemented for that device.
333 E.g., (from Joel N. Weber),
334 [...] there might someday be cases where you can't rename but you
335 can copy where the device name is the same, especially on Hurd.
336 Consider an ftpfs with a primitive ftp server that supports
337 uploading, downloading and deleting, but not renaming.
339 Also, note that comparing device numbers is not a reliable check
340 for `can-rename'. Some systems can be set up so that files from
341 many different physical devices all have the same st_dev field.
342 This is a feature of some NFS mounting configurations.
344 Try copying-then-removing SOURCE instead.
346 This function used to resort to copying only when rename failed
347 and set errno to EXDEV. */
349 fail
= copy_reg (source
, dest
, &source_stats
);
352 /* Restore original destination file DEST if made a backup. */
353 if (dest_backup
&& rename (dest_backup
, dest
))
354 error (0, errno
, _("cannot un-backup `%s'"), dest
);
358 fail
= unlink (source
);
360 error (0, errno
, _("cannot remove `%s'"), source
);
368 strip_trailing_slashes_2 (char *path
)
370 char *end_p
= path
+ strlen (path
) - 1;
373 while (slash
> path
&& *slash
== '/')
376 return slash
< end_p
;
379 /* Move file SOURCE onto DEST. Handles the case when DEST is a directory.
380 DEST_IS_DIR must be nonzero when DEST is a directory or a symlink to a
381 directory and zero otherwise.
382 Return 0 if successful, non-zero if an error occurred. */
385 movefile (char *source
, char *dest
, int dest_is_dir
)
387 int dest_had_trailing_slash
= strip_trailing_slashes_2 (dest
);
390 /* In addition to when DEST is a directory, if DEST has a trailing
391 slash and neither SOURCE nor DEST is a directory, presume the target
392 is DEST/`basename source`. This converts `mv x y/' to `mv x y/x'.
393 This change means that the command `mv any file/' will now fail
394 rather than performing the move. The case when SOURCE is a
395 directory and DEST is not is properly diagnosed by do_move. */
397 if (dest_is_dir
|| (dest_had_trailing_slash
&& !is_real_dir (source
)))
399 /* DEST is a directory; build full target filename. */
403 /* Remove trailing slashes before taking base_name.
404 Otherwise, base_name ("a/") returns "". */
405 strip_trailing_slashes_2 (source
);
407 src_basename
= base_name (source
);
408 new_dest
= path_concat (dest
, src_basename
, NULL
);
409 if (new_dest
== NULL
)
410 error (1, 0, _("virtual memory exhausted"));
411 fail
= do_move (source
, new_dest
);
416 fail
= do_move (source
, dest
);
426 fprintf (stderr
, _("Try `%s --help' for more information.\n"),
431 Usage: %s [OPTION]... SOURCE DEST\n\
432 or: %s [OPTION]... SOURCE... DIRECTORY\n\
434 program_name
, program_name
);
436 Rename SOURCE to DEST, or move SOURCE(s) to DIRECTORY.\n\
438 -b, --backup make backup before removal\n\
439 -f, --force remove existing destinations, never prompt\n\
440 -i, --interactive prompt before overwrite\n\
441 -S, --suffix=SUFFIX override the usual backup suffix\n\
442 -u, --update move only older or brand new files\n\
443 -v, --verbose explain what is being done\n\
444 -V, --version-control=WORD override the usual version control\n\
445 --help display this help and exit\n\
446 --version output version information and exit\n\
450 The backup suffix is ~, unless set with SIMPLE_BACKUP_SUFFIX. The\n\
451 version control may be set with VERSION_CONTROL, values are:\n\
453 t, numbered make numbered backups\n\
454 nil, existing numbered if numbered backups exist, simple otherwise\n\
455 never, simple always make simple backups\n\
457 puts (_("\nReport bugs to <fileutils-bugs@gnu.org>."));
464 main (int argc
, char **argv
)
468 int make_backups
= 0;
472 program_name
= argv
[0];
473 setlocale (LC_ALL
, "");
474 bindtextdomain (PACKAGE
, LOCALEDIR
);
475 textdomain (PACKAGE
);
477 version
= getenv ("SIMPLE_BACKUP_SUFFIX");
479 simple_backup_suffix
= version
;
480 version
= getenv ("VERSION_CONTROL");
482 interactive
= override_mode
= verbose
= update
= 0;
485 while ((c
= getopt_long (argc
, argv
, "bfiuvS:V:", long_options
, NULL
)) != -1)
509 simple_backup_suffix
= optarg
;
521 printf ("mv (%s) %s\n", GNU_PACKAGE
, VERSION
);
529 if (argc
< optind
+ 2)
531 error (0, 0, "%s", (argc
== optind
532 ? _("missing file arguments")
533 : _("missing file argument")));
538 backup_type
= get_version (version
);
540 stdin_tty
= isatty (STDIN_FILENO
);
541 dest_is_dir
= isdir (argv
[argc
- 1]);
543 if (argc
> optind
+ 2 && !dest_is_dir
)
545 _("when moving multiple files, last argument must be a directory"));
547 /* Move each arg but the last onto the last. */
548 for (; optind
< argc
- 1; ++optind
)
549 errors
|= movefile (argv
[optind
], argv
[argc
- 1], dest_is_dir
);