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"
54 #ifndef _POSIX_VERSION
59 enum backup_type
get_version ();
64 void strip_trailing_slashes ();
68 /* The name this program was run with. */
71 /* If nonzero, query the user before overwriting files. */
72 static int interactive
;
74 /* If nonzero, do not query the user before overwriting unwritable
76 static int override_mode
;
78 /* If nonzero, do not move a nondirectory that has an existing destination
79 with the same or newer modification time. */
80 static int update
= 0;
82 /* If nonzero, list each file as it is moved. */
85 /* If nonzero, stdin is a tty. */
88 /* This process's effective user ID. */
92 static struct stat dest_stats
, source_stats
;
94 /* If nonzero, display usage information and exit. */
97 /* If nonzero, print the version on standard output and exit. */
98 static int show_version
;
100 static struct option
const long_options
[] =
102 {"backup", no_argument
, NULL
, 'b'},
103 {"force", no_argument
, NULL
, 'f'},
104 {"interactive", no_argument
, NULL
, 'i'},
105 {"suffix", required_argument
, NULL
, 'S'},
106 {"update", no_argument
, &update
, 1},
107 {"verbose", no_argument
, &verbose
, 1},
108 {"version-control", required_argument
, NULL
, 'V'},
109 {"help", no_argument
, &show_help
, 1},
110 {"version", no_argument
, &show_version
, 1},
114 /* If PATH is an existing directory, return nonzero, else 0. */
117 is_real_dir (char *path
)
121 return lstat (path
, &stats
) == 0 && S_ISDIR (stats
.st_mode
);
124 /* Copy regular file SOURCE onto file DEST.
125 Return 1 if an error occurred, 0 if successful. */
128 copy_reg (char *source
, char *dest
)
133 int len
; /* Number of bytes read into `buf'. */
135 if (!S_ISREG (source_stats
.st_mode
))
137 error (0, 0, _("cannot move `%s' across filesystems: Not a regular file"),
142 if (unlink (dest
) && errno
!= ENOENT
)
144 error (0, errno
, _("cannot remove `%s'"), dest
);
148 ifd
= open (source
, O_RDONLY
, 0);
151 error (0, errno
, "%s", source
);
154 ofd
= open (dest
, O_WRONLY
| O_CREAT
| O_TRUNC
, 0600);
157 error (0, errno
, "%s", dest
);
162 while ((len
= safe_read (ifd
, buf
, sizeof (buf
))) > 0)
164 if (full_write (ofd
, buf
, len
) < 0)
166 error (0, errno
, "%s", dest
);
175 error (0, errno
, "%s", source
);
184 error (0, errno
, "%s", source
);
190 error (0, errno
, "%s", dest
);
194 /* chown turns off set[ug]id bits for non-root,
195 so do the chmod last. */
197 /* Try to copy the old file's modtime and access time. */
201 tv
.actime
= source_stats
.st_atime
;
202 tv
.modtime
= source_stats
.st_mtime
;
203 if (utime (dest
, &tv
))
205 error (0, errno
, "%s", dest
);
210 /* Try to preserve ownership. For non-root it might fail, but that's ok.
211 But root probably wants to know, e.g. if NFS disallows it. */
212 if (chown (dest
, source_stats
.st_uid
, source_stats
.st_gid
)
213 && (errno
!= EPERM
|| myeuid
== 0))
215 error (0, errno
, "%s", dest
);
219 if (chmod (dest
, source_stats
.st_mode
& 07777))
221 error (0, errno
, "%s", dest
);
228 /* Move SOURCE onto DEST. Handles cross-filesystem moves.
229 If DEST is a directory, SOURCE must be also.
230 Return 0 if successful, 1 if an error occurred. */
233 do_move (char *source
, char *dest
)
235 char *dest_backup
= NULL
;
237 if (lstat (source
, &source_stats
) != 0)
239 error (0, errno
, "%s", source
);
243 if (lstat (dest
, &dest_stats
) == 0)
245 if (source_stats
.st_dev
== dest_stats
.st_dev
246 && source_stats
.st_ino
== dest_stats
.st_ino
)
248 error (0, 0, _("`%s' and `%s' are the same file"), source
, dest
);
252 if (S_ISDIR (dest_stats
.st_mode
))
254 error (0, 0, _("%s: cannot overwrite directory"), dest
);
258 if (!S_ISDIR (source_stats
.st_mode
) && update
259 && source_stats
.st_mtime
<= dest_stats
.st_mtime
)
262 if (!override_mode
&& (interactive
|| stdin_tty
)
263 && euidaccess (dest
, W_OK
))
265 fprintf (stderr
, _("%s: replace `%s', overriding mode %04o? "),
267 (unsigned int) (dest_stats
.st_mode
& 07777));
271 else if (interactive
)
273 fprintf (stderr
, _("%s: replace `%s'? "), program_name
, dest
);
278 if (backup_type
!= none
)
280 char *tmp_backup
= find_backup_file_name (dest
);
281 if (tmp_backup
== NULL
)
282 error (1, 0, _("virtual memory exhausted"));
283 dest_backup
= (char *) alloca (strlen (tmp_backup
) + 1);
284 strcpy (dest_backup
, tmp_backup
);
286 if (rename (dest
, dest_backup
))
290 error (0, errno
, _("cannot backup `%s'"), dest
);
298 else if (errno
!= ENOENT
)
300 error (0, errno
, "%s", dest
);
305 printf ("%s -> %s\n", source
, dest
);
307 if (rename (source
, dest
) == 0)
314 error (0, errno
, _("cannot move `%s' to `%s'"), source
, dest
);
318 /* rename failed on cross-filesystem link. Copy the file instead. */
320 if (copy_reg (source
, dest
))
325 error (0, errno
, _("cannot remove `%s'"), source
);
334 if (rename (dest_backup
, dest
))
335 error (0, errno
, _("cannot un-backup `%s'"), dest
);
340 /* Move file SOURCE onto DEST. Handles the case when DEST is a directory.
341 Return 0 if successful, 1 if an error occurred. */
344 movefile (char *source
, char *dest
)
346 strip_trailing_slashes (source
);
348 if ((dest
[strlen (dest
) - 1] == '/' && !is_real_dir (source
))
351 /* Target is a directory; build full target filename. */
355 base
= basename (source
);
356 /* Remove a (single) trailing slash if there is at least one. */
357 if (dest
[strlen (dest
) - 1] == '/')
358 dest
[strlen (dest
) - 1] = '\0';
359 new_dest
= (char *) alloca (strlen (dest
) + 1 + strlen (base
) + 1);
360 stpcpy (stpcpy (stpcpy (new_dest
, dest
), "/"), base
);
361 return do_move (source
, new_dest
);
364 return do_move (source
, dest
);
371 fprintf (stderr
, _("Try `%s --help' for more information.\n"),
376 Usage: %s [OPTION]... SOURCE DEST\n\
377 or: %s [OPTION]... SOURCE... DIRECTORY\n\
379 program_name
, program_name
);
381 Rename SOURCE to DEST, or move SOURCE(s) to DIRECTORY.\n\
383 -b, --backup make backup before removal\n\
384 -f, --force remove existing destinations, never prompt\n\
385 -i, --interactive prompt before overwrite\n\
386 -u, --update move only older or brand new files\n\
387 -v, --verbose explain what is being done\n\
388 -S, --suffix=SUFFIX override the usual backup suffix\n\
389 -V, --version-control=WORD override the usual version control\n\
390 --help display this help and exit\n\
391 --version output version information and exit\n\
393 The backup suffix is ~, unless set with SIMPLE_BACKUP_SUFFIX. The\n\
394 version control may be set with VERSION_CONTROL, values are:\n\
396 t, numbered make numbered backups\n\
397 nil, existing numbered if numbered backups exist, simple otherwise\n\
398 never, simple always make simple backups \n"));
404 main (int argc
, char **argv
)
408 int make_backups
= 0;
411 version
= getenv ("SIMPLE_BACKUP_SUFFIX");
413 simple_backup_suffix
= version
;
414 version
= getenv ("VERSION_CONTROL");
415 program_name
= argv
[0];
417 interactive
= override_mode
= verbose
= update
= 0;
420 while ((c
= getopt_long (argc
, argv
, "bfiuvS:V:", long_options
, (int *) 0))
445 simple_backup_suffix
= optarg
;
457 printf ("mv - %s\n", version_string
);
464 if (argc
< optind
+ 2)
466 error (0, 0, _("missing file argument%s"), argc
== optind
? "s" : "");
471 backup_type
= get_version (version
);
473 stdin_tty
= isatty (STDIN_FILENO
);
475 if (argc
> optind
+ 2 && !isdir (argv
[argc
- 1]))
477 _("when moving multiple files, last argument must be a directory"));
479 /* Move each arg but the last onto the last. */
480 for (; optind
< argc
- 1; ++optind
)
481 errors
|= movefile (argv
[optind
], argv
[argc
- 1]);