*
* Posix says "cp -Rf dir file" shouldn't delete file, but our -f does.
*
+ * Deviations from posix: -adlnrsvF, --preserve... about half the
+ * functionality in this cp isn't in posix. Posix is stuck in the 1970's.
+ *
* TODO: --preserve=links
* TODO: what's this _CP_mode system.posix_acl_ business? We chmod()?
// options shared between mv/cp must be in same order (right to left)
// for FLAG macros to work out right in shared infrastructure.
-USE_CP(NEWTOY(cp, "<2"USE_CP_PRESERVE("(preserve):;")"RHLPp"USE_CP_MORE("rdaslvnF(remove-destination)")"fi[-HLP"USE_CP_MORE("d")"]"USE_CP_MORE("[-ni]"), TOYFLAG_BIN))
-USE_MV(NEWTOY(mv, "<2"USE_CP_MORE("vnF")"fi"USE_CP_MORE("[-ni]"), TOYFLAG_BIN))
+USE_CP(NEWTOY(cp, "<2"USE_CP_PRESERVE("(preserve):;")"RHLPprdaslvnF(remove-destination)fi[-HLPd][-ni]", TOYFLAG_BIN))
+USE_MV(NEWTOY(mv, "<2vnF(remove-destination)fi[-ni]", TOYFLAG_BIN))
USE_INSTALL(NEWTOY(install, "<1cdDpsvm:o:g:", TOYFLAG_USR|TOYFLAG_BIN))
config CP
bool "cp"
default y
help
- usage: cp [-fipRHLP] SOURCE... DEST
+ usage: cp [-adlnrsvfipRHLP] SOURCE... DEST
Copy files from SOURCE to DEST. If more than one SOURCE, DEST must
be a directory.
-H Follow symlinks listed on command line
-L Follow all symlinks
-P Do not follow symlinks [default]
-
-config CP_MORE
- bool "cp -adlnrsv options"
- default y
- depends on CP
- help
- usage: cp [-adlnrsv]
-
-a same as -dpr
-d don't dereference symlinks
-l hard link instead of copy
config CP_PRESERVE
bool "cp --preserve support"
default y
- depends on CP_MORE
+ depends on CP
help
usage: cp [--preserve=motcxa]
config MV
bool "mv"
default y
- depends on CP
help
- usage: mv [-fi] SOURCE... DEST"
+ usage: mv [-fivn] SOURCE... DEST"
-f force copy by deleting destination file
-i interactive, prompt before overwriting existing DEST
-
-config MV_MORE
- bool
- default y
- depends on MV && CP_MORE
- help
- usage: mv [-vn]
-
-v verbose
-n no clobber (don't overwrite DEST)
config INSTALL
bool "install"
default y
- depends on CP && CP_MORE
help
usage: install [-dDpsv] [-o USER] [-g GROUP] [-m MODE] [SOURCE...] DEST
-v Verbose
*/
+#define FORCE_FLAGS
#define FOR_cp
#include "toys.h"
-#if CFG_CP_PRESERVE
-#include <sys/xattr.h>
-#endif
GLOBALS(
union {
return 0;
}
- // Handle -inv
+ // Handle -invF
if (!faccessat(cfd, catch, F_OK, 0) && !S_ISDIR(cst.st_mode)) {
char *s;
// make symlink, or make block/char/fifo/socket
if (S_ISLNK(try->st.st_mode)
- ? (0 < (i = readlinkat(tfd, try->name, toybuf, sizeof(toybuf))) &&
- sizeof(toybuf) > i && !symlinkat(toybuf, cfd, catch))
+ ? ((i = readlinkat0(tfd, try->name, toybuf, sizeof(toybuf))) &&
+ !symlinkat(toybuf, cfd, catch))
: !mknodat(cfd, catch, try->st.st_mode, try->st.st_rdev))
{
err = 0;
if (fdout == AT_FDCWD)
fchownat(cfd, catch, try->st.st_uid, try->st.st_gid,
AT_SYMLINK_NOFOLLOW);
- else fchown(fdout, try->st.st_uid, try->st.st_gid);
+ else rc = fchown(fdout, try->st.st_uid, try->st.st_gid);
+ if (rc && !geteuid()) {
+ char *pp;
+
+ perror_msg("chown '%s'", pp = dirtree_path(try, 0));
+ free(pp);
+ }
}
// timestamp
err = "%s";
}
- if (err) perror_msg(err, catch);
+ if (err) {
+ char *f = 0;
+
+ if (catch == try->name) {
+ f = dirtree_path(try, 0);
+ while (try->parent) try = try->parent;
+ catch = xmprintf("%s%s", TT.destname, f+strlen(try->name));
+ free(f);
+ f = catch;
+ }
+ perror_msg(err, catch);
+ free(f);
+ }
return 0;
}
// Loop through sources
for (i=0; i<toys.optc; i++) {
- struct dirtree *new;
char *src = toys.optargs[i];
int rc = 1;
errno = EXDEV;
if (CFG_MV && toys.which->name[0] == 'm') {
- if (!(toys.optflags & FLAG_f)) {
+ int force = toys.optflags & FLAG_f, no_clobber = toys.optflags & FLAG_n;
+
+ if (!force || no_clobber) {
struct stat st;
+ int exists = !stat(TT.destname, &st);
- // Technically "is writeable" is more complicated (022 is not writeable
- // by the owner, just everybody _else_) but I don't care.
- if (!stat(TT.destname, &st)
- && ((toys.optflags & FLAG_i) || !(st.st_mode & 0222)))
- {
+ // Prompt if -i or file isn't writable. Technically "is writable" is
+ // more complicated (022 is not writeable by the owner, just everybody
+ // _else_) but I don't care.
+ if (exists && ((toys.optflags & FLAG_i) || !(st.st_mode & 0222))) {
fprintf(stderr, "%s: overwrite '%s'", toys.which->name, TT.destname);
if (!yesno(1)) rc = 0;
else unlink(TT.destname);
}
+ // if -n and dest exists, don't try to rename() or copy
+ if (exists && no_clobber) rc = 0;
}
-
if (rc) rc = rename(src, TT.destname);
}
- // Skip nonexistent sources
+ // Copy if we didn't mv, skipping nonexistent sources
if (rc) {
- if (errno!=EXDEV ||
- !(new = dirtree_start(src, toys.optflags&(FLAG_H|FLAG_L))))
+ if (errno!=EXDEV || dirtree_flagread(src, DIRTREE_SHUTUP+
+ DIRTREE_SYMFOLLOW*!!(toys.optflags&(FLAG_H|FLAG_L)), TT.callback))
perror_msg("bad '%s'", src);
- else dirtree_handle_callback(new, TT.callback);
}
if (destdir) free(TT.destname);
}
if (flags & FLAG_v) toys.optflags |= cp_flag_v();
if (flags & (FLAG_p|FLAG_o|FLAG_g)) toys.optflags |= cp_flag_p();
- if (TT.i.user) TT.uid = xgetpwnamid(TT.i.user)->pw_uid;
- if (TT.i.group) TT.gid = xgetgrnamid(TT.i.group)->gr_gid;
+ if (TT.i.user) TT.uid = xgetuid(TT.i.user);
+ if (TT.i.group) TT.gid = xgetgid(TT.i.group);
TT.callback = install_node;
cp_main();