1 /* mv -- move or rename files
2 Copyright (C) 1986, 1989, 1990, 1991, 1995 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
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, 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 "backupfile.h"
53 #ifndef _POSIX_VERSION
58 enum backup_type
get_version ();
63 void strip_trailing_slashes ();
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 /* This process's effective user ID. */
91 static struct stat dest_stats
, source_stats
;
93 /* If nonzero, display usage information and exit. */
96 /* If nonzero, print the version on standard output and exit. */
97 static int show_version
;
99 static struct option
const long_options
[] =
101 {"backup", no_argument
, NULL
, 'b'},
102 {"force", no_argument
, NULL
, 'f'},
103 {"interactive", no_argument
, NULL
, 'i'},
104 {"suffix", required_argument
, NULL
, 'S'},
105 {"update", no_argument
, &update
, 1},
106 {"verbose", no_argument
, &verbose
, 1},
107 {"version-control", required_argument
, NULL
, 'V'},
108 {"help", no_argument
, &show_help
, 1},
109 {"version", no_argument
, &show_version
, 1},
113 /* If PATH is an existing directory, return nonzero, else 0. */
116 is_real_dir (char *path
)
120 return lstat (path
, &stats
) == 0 && S_ISDIR (stats
.st_mode
);
123 /* Copy regular file SOURCE onto file DEST.
124 Return 1 if an error occurred, 0 if successful. */
127 copy_reg (char *source
, char *dest
)
132 int len
; /* Number of bytes read into `buf'. */
134 if (!S_ISREG (source_stats
.st_mode
))
136 error (0, 0, _("cannot move `%s' across filesystems: Not a regular file"),
141 if (unlink (dest
) && errno
!= ENOENT
)
143 error (0, errno
, _("cannot remove `%s'"), dest
);
147 ifd
= open (source
, O_RDONLY
, 0);
150 error (0, errno
, "%s", source
);
153 ofd
= open (dest
, O_WRONLY
| O_CREAT
| O_TRUNC
, 0600);
156 error (0, errno
, "%s", dest
);
161 while ((len
= safe_read (ifd
, buf
, sizeof (buf
))) > 0)
163 if (full_write (ofd
, buf
, len
) < 0)
165 error (0, errno
, "%s", dest
);
174 error (0, errno
, "%s", source
);
183 error (0, errno
, "%s", source
);
189 error (0, errno
, "%s", dest
);
193 /* chown turns off set[ug]id bits for non-root,
194 so do the chmod last. */
196 /* Try to copy the old file's modtime and access time. */
200 tv
.actime
= source_stats
.st_atime
;
201 tv
.modtime
= source_stats
.st_mtime
;
202 if (utime (dest
, &tv
))
204 error (0, errno
, "%s", dest
);
209 /* Try to preserve ownership. For non-root it might fail, but that's ok.
210 But root probably wants to know, e.g. if NFS disallows it. */
211 if (chown (dest
, source_stats
.st_uid
, source_stats
.st_gid
)
212 && (errno
!= EPERM
|| myeuid
== 0))
214 error (0, errno
, "%s", dest
);
218 if (chmod (dest
, source_stats
.st_mode
& 07777))
220 error (0, errno
, "%s", dest
);
227 /* Move SOURCE onto DEST. Handles cross-filesystem moves.
228 If DEST is a directory, SOURCE must be also.
229 Return 0 if successful, 1 if an error occurred. */
232 do_move (char *source
, char *dest
)
234 char *dest_backup
= NULL
;
236 if (lstat (source
, &source_stats
) != 0)
238 error (0, errno
, "%s", source
);
242 if (lstat (dest
, &dest_stats
) == 0)
244 if (source_stats
.st_dev
== dest_stats
.st_dev
245 && source_stats
.st_ino
== dest_stats
.st_ino
)
247 error (0, 0, _("`%s' and `%s' are the same file"), source
, dest
);
251 if (S_ISDIR (dest_stats
.st_mode
))
253 error (0, 0, _("%s: cannot overwrite directory"), dest
);
257 if (!S_ISDIR (source_stats
.st_mode
) && update
258 && source_stats
.st_mtime
<= dest_stats
.st_mtime
)
261 if (!override_mode
&& (interactive
|| stdin_tty
)
262 && euidaccess (dest
, W_OK
))
264 fprintf (stderr
, _("%s: replace `%s', overriding mode %04o? "),
266 (unsigned int) (dest_stats
.st_mode
& 07777));
270 else if (interactive
)
272 fprintf (stderr
, _("%s: replace `%s'? "), program_name
, dest
);
277 if (backup_type
!= none
)
279 char *tmp_backup
= find_backup_file_name (dest
);
280 if (tmp_backup
== NULL
)
281 error (1, 0, _("virtual memory exhausted"));
282 dest_backup
= (char *) alloca (strlen (tmp_backup
) + 1);
283 strcpy (dest_backup
, tmp_backup
);
285 if (rename (dest
, dest_backup
))
289 error (0, errno
, _("cannot backup `%s'"), dest
);
297 else if (errno
!= ENOENT
)
299 error (0, errno
, "%s", dest
);
304 printf ("%s -> %s\n", source
, dest
);
306 if (rename (source
, dest
) == 0)
313 error (0, errno
, _("cannot move `%s' to `%s'"), source
, dest
);
317 /* rename failed on cross-filesystem link. Copy the file instead. */
319 if (copy_reg (source
, dest
))
324 error (0, errno
, _("cannot remove `%s'"), source
);
333 if (rename (dest_backup
, dest
))
334 error (0, errno
, _("cannot un-backup `%s'"), dest
);
339 /* Move file SOURCE onto DEST. Handles the case when DEST is a directory.
340 Return 0 if successful, 1 if an error occurred. */
343 movefile (char *source
, char *dest
)
345 strip_trailing_slashes (source
);
347 if ((dest
[strlen (dest
) - 1] == '/' && !is_real_dir (source
))
350 /* Target is a directory; build full target filename. */
354 base
= basename (source
);
355 /* Remove a (single) trailing slash if there is at least one. */
356 if (dest
[strlen (dest
) - 1] == '/')
357 dest
[strlen (dest
) - 1] = '\0';
358 new_dest
= (char *) alloca (strlen (dest
) + 1 + strlen (base
) + 1);
359 stpcpy (stpcpy (stpcpy (new_dest
, dest
), "/"), base
);
360 return do_move (source
, new_dest
);
363 return do_move (source
, dest
);
370 fprintf (stderr
, _("Try `%s --help' for more information.\n"),
375 Usage: %s [OPTION]... SOURCE DEST\n\
376 or: %s [OPTION]... SOURCE... DIRECTORY\n\
378 program_name
, program_name
);
380 Rename SOURCE to DEST, or move SOURCE(s) to DIRECTORY.\n\
382 -b, --backup make backup before removal\n\
383 -f, --force remove existing destinations, never prompt\n\
384 -i, --interactive prompt before overwrite\n\
385 -u, --update move only older or brand new files\n\
386 -v, --verbose explain what is being done\n\
387 -S, --suffix=SUFFIX override the usual backup suffix\n\
388 -V, --version-control=WORD override the usual version control\n\
389 --help display this help and exit\n\
390 --version output version information and exit\n\
392 The backup suffix is ~, unless set with SIMPLE_BACKUP_SUFFIX. The\n\
393 version control may be set with VERSION_CONTROL, values are:\n\
395 t, numbered make numbered backups\n\
396 nil, existing numbered if numbered backups exist, simple otherwise\n\
397 never, simple always make simple backups \n"));
403 main (int argc
, char **argv
)
407 int make_backups
= 0;
410 program_name
= argv
[0];
411 setlocale (LC_ALL
, "");
412 bindtextdomain (PACKAGE
, LOCALEDIR
);
413 textdomain (PACKAGE
);
415 version
= getenv ("SIMPLE_BACKUP_SUFFIX");
417 simple_backup_suffix
= version
;
418 version
= getenv ("VERSION_CONTROL");
421 interactive
= override_mode
= verbose
= update
= 0;
424 while ((c
= getopt_long (argc
, argv
, "bfiuvS:V:", long_options
, (int *) 0))
449 simple_backup_suffix
= optarg
;
461 printf ("mv - %s\n", PACKAGE_VERSION
);
468 if (argc
< optind
+ 2)
470 error (0, 0, _("missing file argument%s"), argc
== optind
? "s" : "");
475 backup_type
= get_version (version
);
477 stdin_tty
= isatty (STDIN_FILENO
);
479 if (argc
> optind
+ 2 && !isdir (argv
[argc
- 1]))
481 _("when moving multiple files, last argument must be a directory"));
483 /* Move each arg but the last onto the last. */
484 for (; optind
< argc
- 1; ++optind
)
485 errors
|= movefile (argv
[optind
], argv
[argc
- 1]);