1 /* mv -- move or rename files
2 Copyright (C) 1986, 1989, 1990, 1991 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 */
45 #if defined (CONFIG_BROKETS)
46 /* We use <config.h> instead of "config.h" so that a compilation
47 using -I. -I$srcdir will use ./config.h rather than $srcdir/config.h
48 (which it would do because it found this file in $srcdir). */
57 #include <sys/types.h>
59 #include "backupfile.h"
62 #ifndef _POSIX_VERSION
67 enum backup_type
get_version ();
71 void strip_trailing_slashes ();
75 static int copy_reg ();
76 static int do_move ();
77 static int movefile ();
80 /* The name this program was run with. */
83 /* If nonzero, query the user before overwriting files. */
84 static int interactive
;
86 /* If nonzero, do not query the user before overwriting unwritable
88 static int override_mode
;
90 /* If nonzero, do not move a nondirectory that has an existing destination
91 with the same or newer modification time. */
92 static int update
= 0;
94 /* If nonzero, list each file as it is moved. */
97 /* If nonzero, stdin is a tty. */
100 /* This process's effective user ID. */
103 /* If non-zero, display usage information and exit. */
104 static int show_help
;
106 /* If non-zero, print the version on standard output and exit. */
107 static int show_version
;
109 static struct option
const long_options
[] =
111 {"backup", no_argument
, NULL
, 'b'},
112 {"force", no_argument
, NULL
, 'f'},
113 {"interactive", no_argument
, NULL
, 'i'},
114 {"suffix", required_argument
, NULL
, 'S'},
115 {"update", no_argument
, &update
, 1},
116 {"verbose", no_argument
, &verbose
, 1},
117 {"version-control", required_argument
, NULL
, 'V'},
118 {"help", no_argument
, &show_help
, 1},
119 {"version", no_argument
, &show_version
, 1},
130 int make_backups
= 0;
133 version
= getenv ("SIMPLE_BACKUP_SUFFIX");
135 simple_backup_suffix
= version
;
136 version
= getenv ("VERSION_CONTROL");
137 program_name
= argv
[0];
139 interactive
= override_mode
= verbose
= update
= 0;
142 while ((c
= getopt_long (argc
, argv
, "bfiuvS:V:", long_options
, (int *) 0))
167 simple_backup_suffix
= optarg
;
179 printf ("%s\n", version_string
);
186 if (argc
< optind
+ 2)
190 backup_type
= get_version (version
);
192 stdin_tty
= isatty (0);
194 if (argc
> optind
+ 2 && !isdir (argv
[argc
- 1]))
195 error (1, 0, "when moving multiple files, last argument must be a directory");
197 /* Move each arg but the last onto the last. */
198 for (; optind
< argc
- 1; ++optind
)
199 errors
|= movefile (argv
[optind
], argv
[argc
- 1]);
204 /* If PATH is an existing directory, return nonzero, else 0. */
212 return lstat (path
, &stats
) == 0 && S_ISDIR (stats
.st_mode
);
215 /* Move file SOURCE onto DEST. Handles the case when DEST is a directory.
216 Return 0 if successful, 1 if an error occurred. */
219 movefile (source
, dest
)
223 strip_trailing_slashes (source
);
225 if ((dest
[strlen (dest
) - 1] == '/' && !is_real_dir (source
))
228 /* Target is a directory; build full target filename. */
232 base
= basename (source
);
233 new_dest
= (char *) alloca (strlen (dest
) + 1 + strlen (base
) + 1);
234 stpcpy (stpcpy (stpcpy (new_dest
, dest
), "/"), base
);
235 return do_move (source
, new_dest
);
238 return do_move (source
, dest
);
241 static struct stat dest_stats
, source_stats
;
243 /* Move SOURCE onto DEST. Handles cross-filesystem moves.
244 If DEST is a directory, SOURCE must be also.
245 Return 0 if successful, 1 if an error occurred. */
248 do_move (source
, dest
)
252 char *dest_backup
= NULL
;
254 if (lstat (source
, &source_stats
) != 0)
256 error (0, errno
, "%s", source
);
260 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 && eaccess_stat (&dest_stats
, 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 if (rename (source
, dest
) == 0)
331 error (0, errno
, "cannot move `%s' to `%s'", source
, dest
);
335 /* rename failed on cross-filesystem link. Copy the file instead. */
337 if (copy_reg (source
, dest
))
342 error (0, errno
, "cannot remove `%s'", source
);
351 if (rename (dest_backup
, dest
))
352 error (0, errno
, "cannot un-backup `%s'", dest
);
357 /* Copy regular file SOURCE onto file DEST.
358 Return 1 if an error occurred, 0 if successful. */
361 copy_reg (source
, dest
)
367 int len
; /* Number of bytes read into `buf'. */
369 if (!S_ISREG (source_stats
.st_mode
))
371 error (0, 0, "cannot move `%s' across filesystems: Not a regular file",
376 if (unlink (dest
) && errno
!= ENOENT
)
378 error (0, errno
, "cannot remove `%s'", dest
);
382 ifd
= open (source
, O_RDONLY
, 0);
385 error (0, errno
, "%s", source
);
388 ofd
= open (dest
, O_WRONLY
| O_CREAT
| O_TRUNC
, 0600);
391 error (0, errno
, "%s", dest
);
396 while ((len
= read (ifd
, buf
, sizeof (buf
))) > 0)
403 wrote
= write (ofd
, bp
, len
);
406 error (0, errno
, "%s", dest
);
418 error (0, errno
, "%s", source
);
427 error (0, errno
, "%s", source
);
433 error (0, errno
, "%s", dest
);
437 /* chown turns off set[ug]id bits for non-root,
438 so do the chmod last. */
440 /* Try to copy the old file's modtime and access time. */
444 tv
.actime
= source_stats
.st_atime
;
445 tv
.modtime
= source_stats
.st_mtime
;
446 if (utime (dest
, &tv
))
448 error (0, errno
, "%s", dest
);
453 /* Try to preserve ownership. For non-root it might fail, but that's ok.
454 But root probably wants to know, e.g. if NFS disallows it. */
455 if (chown (dest
, source_stats
.st_uid
, source_stats
.st_gid
)
456 && (errno
!= EPERM
|| myeuid
== 0))
458 error (0, errno
, "%s", dest
);
462 if (chmod (dest
, source_stats
.st_mode
& 07777))
464 error (0, errno
, "%s", dest
);
475 fprintf (status
== 0 ? stdout
: stderr
, "\
476 Usage: %s [OPTION]... SOURCE DEST\n\
477 or: %s [OPTION]... SOURCE... DIRECTORY\n\
479 program_name
, program_name
);
483 -b, --backup make backup before removal\n\
484 -f, --force remove existing destinations, never prompt\n\
485 -i, --interactive prompt before overwrite\n\
486 -u, --update move only older or brand new files\n\
487 -v, --verbose explain what is being done\n\
488 -S, --suffix SUFFIX override the usual backup suffix\n\
489 -V, --version-control WORD override the usual version control\n\
490 --help display this help and exit\n\
491 --version output version information and exit\n\
493 The backup suffix is ~, unless set with SIMPLE_BACKUP_SUFFIX. The\n\
494 version control may be set with VERSION_CONTROL, values are:\n\
496 t, numbered make numbered backups\n\
497 nil, existing numbered if numbered backups exist, simple otherwise\n\
498 never, simple always make simple backups \n");
501 fprintf (stderr
, "Try `%s --help' for more information.\n",