aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRob Landley <rob@landley.net>2018-04-08 22:37:33 -0500
committerRob Landley <rob@landley.net>2018-04-08 22:37:33 -0500
commit8fdd58a02257baf8f2bca1c086571ea7b7e17365 (patch)
treedaffcd848c786e63372d4caeb4b9181d0b3f914c
parent221439164eb6683a5af35540b24b5620e5d8ab19 (diff)
downloadtoybox-8fdd58a02257baf8f2bca1c086571ea7b7e17365.tar.gz
Add cp --parents
-rw-r--r--lib/lib.c23
-rw-r--r--lib/lib.h2
-rw-r--r--toys/posix/cp.c20
3 files changed, 41 insertions, 4 deletions
diff --git a/lib/lib.c b/lib/lib.c
index 27d14be7..8c46678f 100644
--- a/lib/lib.c
+++ b/lib/lib.c
@@ -1002,6 +1002,17 @@ void mode_to_string(mode_t mode, char *buf)
*buf = c;
}
+// dirname() can modify its argument or return a pointer to a constant string
+// This always returns a malloc() copy of everyting before last (run of ) '/'.
+char *getdirname(char *name)
+{
+ char *s = xstrdup(name), *ss = strrchr(s, '/');
+
+ while (*ss && *ss == '/' && s != ss) *ss-- = 0;
+
+ return s;
+}
+
// basename() can modify its argument or return a pointer to a constant string
// This just gives after the last '/' or the whole stirng if no /
char *getbasename(char *name)
@@ -1013,6 +1024,18 @@ char *getbasename(char *name)
return name;
}
+// Is this file under this directory?
+int fileunderdir(char *file, char *dir)
+{
+ char *s1 = xabspath(dir, 1), *s2 = xabspath(file, -1), *ss = s2;
+ int rc = s1 && s2 && strstart(&ss, s1) && (!s1[1] || s2[strlen(s1)] == '/');
+
+ free(s1);
+ free(s2);
+
+ return rc;
+}
+
// Execute a callback for each PID that matches a process name from a list.
void names_to_pid(char **names, int (*callback)(pid_t pid, char *name))
{
diff --git a/lib/lib.h b/lib/lib.h
index ccf342cc..353e262d 100644
--- a/lib/lib.h
+++ b/lib/lib.h
@@ -337,7 +337,9 @@ char *num_to_sig(int sig);
mode_t string_to_mode(char *mode_str, mode_t base);
void mode_to_string(mode_t mode, char *buf);
+char *getdirname(char *name);
char *getbasename(char *name);
+int fileunderdir(char *file, char *dir);
void names_to_pid(char **names, int (*callback)(pid_t pid, char *name));
pid_t __attribute__((returns_twice)) xvforkwrap(pid_t pid);
diff --git a/toys/posix/cp.c b/toys/posix/cp.c
index d4475a02..f0522af9 100644
--- a/toys/posix/cp.c
+++ b/toys/posix/cp.c
@@ -15,7 +15,7 @@
// 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):;")"RHLPprdaslvnF(remove-destination)fi[-HLPd][-ni]", TOYFLAG_BIN))
+USE_CP(NEWTOY(cp, "<2"USE_CP_PRESERVE("(preserve):;")"D(parents)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))
@@ -28,6 +28,7 @@ config CP
Copy files from SOURCE to DEST. If more than one SOURCE, DEST must
be a directory.
+ -D create leading dirs under DEST (--parents)
-f delete destination files we can't write to
-F delete any existing destination file first (--remove-destination)
-i interactive, prompt before overwriting existing DEST
@@ -366,7 +367,8 @@ void cp_main(void)
char *destname = toys.optargs[--toys.optc];
int i, destdir = !stat(destname, &TT.top) && S_ISDIR(TT.top.st_mode);
- if (toys.optc>1 && !destdir) error_exit("'%s' not directory", destname);
+ if ((toys.optc>1 || (toys.optflags&FLAG_D)) && !destdir)
+ error_exit("'%s' not directory", destname);
if (toys.optflags & (FLAG_a|FLAG_p)) {
TT.pflags = CP_mode|CP_ownership|CP_timestamps;
@@ -402,8 +404,18 @@ void cp_main(void)
char *src = toys.optargs[i];
int rc = 1;
- if (destdir) TT.destname = xmprintf("%s/%s", destname, basename(src));
- else TT.destname = destname;
+ if (destdir) {
+ char *s = (toys.optflags&FLAG_D) ? getdirname(src) : getbasename(src);
+
+ TT.destname = xmprintf("%s/%s", destname, s);
+ if (toys.optflags&FLAG_D) {
+ free(s);
+ if (!fileunderdir(TT.destname, destname)) {
+ error_msg("%s not under %s", TT.destname, destname);
+ continue;
+ } else mkpath(TT.destname);
+ }
+ } else TT.destname = destname;
errno = EXDEV;
if (CFG_MV && toys.which->name[0] == 'm') {