Merge remote-tracking branch 'toybox/master' into HEAD
Change-Id: I4eda86cd36e580d8c24e576fdf639d3b71437606
diff --git a/generated/flags.h b/generated/flags.h
index 7019fc9..b4bbe0c 100644
--- a/generated/flags.h
+++ b/generated/flags.h
@@ -2742,9 +2742,9 @@
#undef FLAG_f
#endif
-// tar &(full-time)(no-recursion)(numeric-owner)(no-same-permissions)(overwrite)(exclude)*(mtime):(group):(owner):(to-command):o(no-same-owner)p(same-permissions)k(keep-old)c(create)|h(dereference)x(extract)|t(list)|v(verbose)j(bzip2)z(gzip)O(to-stdout)m(touch)X(exclude-from)*T(files-from)*C(directory):f(file):[!txc][!jz] &(full-time)(no-recursion)(numeric-owner)(no-same-permissions)(overwrite)(exclude)*(mtime):(group):(owner):(to-command):o(no-same-owner)p(same-permissions)k(keep-old)c(create)|h(dereference)x(extract)|t(list)|v(verbose)j(bzip2)z(gzip)O(to-stdout)m(touch)X(exclude-from)*T(files-from)*C(directory):f(file):[!txc][!jz]
+// tar &(restrict)(full-time)(no-recursion)(numeric-owner)(no-same-permissions)(overwrite)(exclude)*(mtime):(group):(owner):(to-command):o(no-same-owner)p(same-permissions)k(keep-old)c(create)|h(dereference)x(extract)|t(list)|v(verbose)j(bzip2)z(gzip)O(to-stdout)m(touch)X(exclude-from)*T(files-from)*C(directory):f(file):[!txc][!jz] &(restrict)(full-time)(no-recursion)(numeric-owner)(no-same-permissions)(overwrite)(exclude)*(mtime):(group):(owner):(to-command):o(no-same-owner)p(same-permissions)k(keep-old)c(create)|h(dereference)x(extract)|t(list)|v(verbose)j(bzip2)z(gzip)O(to-stdout)m(touch)X(exclude-from)*T(files-from)*C(directory):f(file):[!txc][!jz]
#undef OPTSTR_tar
-#define OPTSTR_tar "&(full-time)(no-recursion)(numeric-owner)(no-same-permissions)(overwrite)(exclude)*(mtime):(group):(owner):(to-command):o(no-same-owner)p(same-permissions)k(keep-old)c(create)|h(dereference)x(extract)|t(list)|v(verbose)j(bzip2)z(gzip)O(to-stdout)m(touch)X(exclude-from)*T(files-from)*C(directory):f(file):[!txc][!jz]"
+#define OPTSTR_tar "&(restrict)(full-time)(no-recursion)(numeric-owner)(no-same-permissions)(overwrite)(exclude)*(mtime):(group):(owner):(to-command):o(no-same-owner)p(same-permissions)k(keep-old)c(create)|h(dereference)x(extract)|t(list)|v(verbose)j(bzip2)z(gzip)O(to-stdout)m(touch)X(exclude-from)*T(files-from)*C(directory):f(file):[!txc][!jz]"
#ifdef CLEANUP_tar
#undef CLEANUP_tar
#undef FOR_tar
@@ -2774,6 +2774,7 @@
#undef FLAG_numeric_owner
#undef FLAG_no_recursion
#undef FLAG_full_time
+#undef FLAG_restrict
#endif
// taskset <1^pa <1^pa
@@ -5671,6 +5672,7 @@
#define FLAG_numeric_owner (1<<23)
#define FLAG_no_recursion (1<<24)
#define FLAG_full_time (1<<25)
+#define FLAG_restrict (1<<26)
#endif
#ifdef FOR_taskset
diff --git a/generated/help.h b/generated/help.h
index f4728dc..407e590 100644
--- a/generated/help.h
+++ b/generated/help.h
@@ -340,7 +340,7 @@
#define HELP_tcpsvd "usage: tcpsvd [-hEv] [-c N] [-C N[:MSG]] [-b N] [-u User] [-l Name] IP Port Prog\nusage: udpsvd [-hEv] [-c N] [-u User] [-l Name] IP Port Prog\n\nCreate TCP/UDP socket, bind to IP:PORT and listen for incoming connection.\nRun PROG for each connection.\n\nIP IP to listen on, 0 = all\nPORT Port to listen on\nPROG ARGS Program to run\n-l NAME Local hostname (else looks up local hostname in DNS)\n-u USER[:GRP] Change to user/group after bind\n-c N Handle up to N (> 0) connections simultaneously\n-b N (TCP Only) Allow a backlog of approximately N TCP SYNs\n-C N[:MSG] (TCP Only) Allow only up to N (> 0) connections from the same IP\n New connections from this IP address are closed\n immediately. MSG is written to the peer before close\n-h Look up peer's hostname\n-E Don't set up environment variables\n-v Verbose\n\n"
-#define HELP_tar "usage: tar [-cxtjzhmvO] [-X FILE] [-T FILE] [-f TARFILE] [-C DIR]\n\nCreate, extract, or list files in a .tar (or compressed t?z) file.\n\nOptions:\nc Create x Extract t Test\nf Name of TARFILE C Change to DIR first v Verbose: show filenames\no Ignore owner h Follow symlinks m Ignore mtime\nj bzip2 compression z gzip compression\nO Extract to stdout X exclude names in FILE T include names in FILE\n--exclude=FILE File pattern(s) to exclude\n\n"
+#define HELP_tar "usage: tar [-cxtfvohmjkO] [-XT FILE] [-f TARFILE] [-C DIR]\n\nCreate, extract, or list files in a .tar (or compressed t?z) file.\n\nOptions:\nc Create x Extract t Test\nf Name of TARFILE C Change to DIR first v Verbose: show filenames\no Ignore owner h Follow symlinks m Ignore mtime\nj bzip2 compression z gzip compression\nO Extract to stdout X exclude names in FILE T include names in FILE\n--exclude=FILE File pattern(s) to exclude\n--restrict All archive contents must extract under a single subdirctory.\n\n"
#define HELP_syslogd "usage: syslogd [-a socket] [-O logfile] [-f config file] [-m interval]\n [-p socket] [-s SIZE] [-b N] [-R HOST] [-l N] [-nSLKD]\n\nSystem logging utility\n\n-a Extra unix socket for listen\n-O FILE Default log file <DEFAULT: /var/log/messages>\n-f FILE Config file <DEFAULT: /etc/syslog.conf>\n-p Alternative unix domain socket <DEFAULT : /dev/log>\n-n Avoid auto-backgrounding\n-S Smaller output\n-m MARK interval <DEFAULT: 20 minutes> (RANGE: 0 to 71582787)\n-R HOST Log to IP or hostname on PORT (default PORT=514/UDP)\"\n-L Log locally and via network (default is network only if -R)\"\n-s SIZE Max size (KB) before rotation (default:200KB, 0=off)\n-b N rotated logs to keep (default:1, max=99, 0=purge)\n-K Log to kernel printk buffer (use dmesg to read it)\n-l N Log only messages more urgent than prio(default:8 max:8 min:1)\n-D Drop duplicates\n\n"
diff --git a/generated/newtoys.h b/generated/newtoys.h
index e820557..0a5c77b 100644
--- a/generated/newtoys.h
+++ b/generated/newtoys.h
@@ -246,7 +246,7 @@
USE_SYSLOGD(NEWTOY(syslogd,">0l#<1>8=8R:b#<0>99=1s#<0=200m#<0>71582787=20O:p:f:a:nSKLD", TOYFLAG_SBIN|TOYFLAG_STAYROOT))
USE_TAC(NEWTOY(tac, NULL, TOYFLAG_USR|TOYFLAG_BIN))
USE_TAIL(NEWTOY(tail, "?fc-n-[-cn]", TOYFLAG_USR|TOYFLAG_BIN))
-USE_TAR(NEWTOY(tar, "&(full-time)(no-recursion)(numeric-owner)(no-same-permissions)(overwrite)(exclude)*(mtime):(group):(owner):(to-command):o(no-same-owner)p(same-permissions)k(keep-old)c(create)|h(dereference)x(extract)|t(list)|v(verbose)j(bzip2)z(gzip)O(to-stdout)m(touch)X(exclude-from)*T(files-from)*C(directory):f(file):[!txc][!jz]", TOYFLAG_USR|TOYFLAG_BIN))
+USE_TAR(NEWTOY(tar, "&(restrict)(full-time)(no-recursion)(numeric-owner)(no-same-permissions)(overwrite)(exclude)*(mtime):(group):(owner):(to-command):o(no-same-owner)p(same-permissions)k(keep-old)c(create)|h(dereference)x(extract)|t(list)|v(verbose)j(bzip2)z(gzip)O(to-stdout)m(touch)X(exclude-from)*T(files-from)*C(directory):f(file):[!txc][!jz]", TOYFLAG_USR|TOYFLAG_BIN))
USE_TASKSET(NEWTOY(taskset, "<1^pa", TOYFLAG_USR|TOYFLAG_BIN|TOYFLAG_STAYROOT))
USE_TCPSVD(NEWTOY(tcpsvd, "^<3c#=30<1C:b#=20<0u:l:hEv", TOYFLAG_USR|TOYFLAG_BIN))
USE_TEE(NEWTOY(tee, "ia", TOYFLAG_USR|TOYFLAG_BIN))
diff --git a/scripts/runtest.sh b/scripts/runtest.sh
index ab10bf4..8b99258 100644
--- a/scripts/runtest.sh
+++ b/scripts/runtest.sh
@@ -74,7 +74,15 @@
else
eval "$@"
fi
- [ $? -eq 0 ] || SKIPNOT=1
+ [ $? -eq 0 ] || SKIPNEXT=1
+}
+
+toyonly()
+{
+ IS_TOYBOX="$("$C" --version 2>/dev/null)"
+ [ "${IS_TOYBOX/toybox/}" == "$IS_TOYBOX" ] && SKIPNEXT=1
+
+ "$@"
}
wrong_args()
@@ -97,10 +105,10 @@
[ -n "$DEBUG" ] && set -x
- if [ -n "$SKIP" -o -n "$SKIP_HOST" -a -n "$TEST_HOST" -o -n "$SKIPNOT" ]
+ if [ -n "$SKIP" -o -n "$SKIP_HOST" -a -n "$TEST_HOST" -o -n "$SKIPNEXT" ]
then
[ ! -z "$VERBOSE" ] && echo "$SHOWSKIP: $NAME"
- unset SKIPNOT
+ unset SKIPNEXT
return 0
fi
diff --git a/tests/files/tar/tar.tar b/tests/files/tar/tar.tar
new file mode 100644
index 0000000..ca7c53d
--- /dev/null
+++ b/tests/files/tar/tar.tar
Binary files differ
diff --git a/tests/tar.test b/tests/tar.test
index 27586b6..40cf9c3 100644
--- a/tests/tar.test
+++ b/tests/tar.test
@@ -24,34 +24,38 @@
export BLOCKS=3
SUM='head -c $(($BLOCKS*512)) | sha1sum | sed "s/ .*//"'
[ -n "$TARHD" ] && SUM="tee >(hd >&2) | $SUM"
-LST='tar tv | sed "s/[ \t][ \t]*/ /g"'
+
+function LST()
+{
+ tar tv $LSTARG | sed "s/[ \t][ \t]*/ /g"
+}
touch file
testing "create file" "$TAR file | $SUM" \
"fecaecba936e604bb115627a6ef4db7c7a3a8f81\n" "" ""
-testing "pass file" "$TAR file | $LST" \
+testing "pass file" "$TAR file | LST" \
"-rw-rw-r-- root/root 0 2009-02-13 23:31 file\n" "" ""
# The kernel has two hardwired meaningful UIDs: 0 (root) and 65534 (nobody).
# (Technically changeable via /proc/sys/*/overflowuid but nobody ever does)
skipnot id nobody >/dev/null
-testing "pass user" "tar -c --owner nobody --group root --mtime @0 file | $LST" \
+testing "pass user" "tar -c --owner nobody --group root --mtime @0 file | LST" \
"-rw-rw-r-- nobody/root 0 1970-01-01 00:00 file\n" "" ""
skipnot grep nobody /etc/group >/dev/null
-testing "pass group" "tar c --owner root --group nobody --mtime @0 file | $LST" \
+testing "pass group" "tar c --owner root --group nobody --mtime @0 file | LST" \
"-rw-rw-r-- root/nobody 0 1970-01-01 00:00 file\n" "" ""
touch -t 198701231234.56 file
-testing "pass mtime" \
- "tar c --owner root --group root file | tar tv --full-time | sed 's/[ \t][ \t]*/ /g'" \
+LSTARG=--full-time testing "pass mtime" \
+ "tar c --owner root --group root file | LST" \
"-rw-rw-r-- root/root 0 1987-01-23 12:34:56 file\n" "" ""
mkdir dir
testing "create dir" "$TAR dir | $SUM" \
"05739c423d7d4a7f12b3dbb7c94149acb2bb4f8d\n" "" ""
-testing "pass dir" "$TAR dir | $LST" \
+testing "pass dir" "$TAR dir | LST" \
"drwxrwxr-x root/root 0 2009-02-13 23:31 dir/\n" "" ""
# note: does _not_ include dir entry in archive, just file
@@ -63,9 +67,13 @@
testing "create dir and dir/file" "$TAR dir | $SUM" \
"0bcc8005a3e07eb63c9b735267aecc5b774795d7\n" "" ""
-testing "pass dir/file" "$TAR dir | $LST" \
+testing "pass dir/file" "$TAR dir | LST" \
"drwxrwxr-x root/root 0 2009-02-13 23:31 dir/\n-rw-rw-r-- root/root 0 2009-02-13 23:31 dir/file\n" "" ""
+echo boing > dir/that
+testing "tar C" "$TAR -C dir that | $SUM" \
+ "f0deff71bf4858eb0c5f49d99d052f12f1831feb\n" "" ""
+
# / and .. only stripped from name, not symlink target.
ln -s ../name.././.. dir/link
testing "create symlink" "$TAR dir/link | $SUM" \
@@ -106,28 +114,34 @@
"55652846506cf0a9d43b3ef03ccf9e98123befaf\n" "" ""
ln -s /dev/null dir/linknull
-testing "pass absolute symlink" "$TAR dir/linknull | $LST" \
+testing "pass absolute symlink" "$TAR dir/linknull | LST" \
"lrwxrwxrwx root/root 0 2009-02-13 23:31 dir/linknull -> /dev/null\n" "" ""
ln -s rel/broken dir/relbrok
-testing "pass broken symlink" "$TAR dir/relbrok | $LST" \
+testing "pass broken symlink" "$TAR dir/relbrok | LST" \
"lrwxrwxrwx root/root 0 2009-02-13 23:31 dir/relbrok -> rel/broken\n" "" ""
ln -s /does/not/exist dir/linkabsbrok
-testing "pass broken absolute symlink" "$TAR dir/linkabsbrok | $LST" \
+testing "pass broken absolute symlink" "$TAR dir/linkabsbrok | LST" \
"lrwxrwxrwx root/root 0 2009-02-13 23:31 dir/linkabsbrok -> /does/not/exist\n" \
"" ""
# this expects devtmpfs values
testing "pass /dev/null" \
- "tar c --mtime @0 /dev/null 2>/dev/null | $LST" \
+ "tar c --mtime @0 /dev/null 2>/dev/null | LST" \
"crw-rw-rw- root/root 1,3 1970-01-01 00:00 dev/null\n" "" ""
testing "pass /dev/loop0" \
- "tar c --numeric-owner --mtime @0 /dev/loop0 2>/dev/null | $LST" \
+ "tar c --numeric-owner --mtime @0 /dev/loop0 2>/dev/null | LST" \
"brw-rw---- 0/6 7,0 1970-01-01 00:00 dev/loop0\n" "" ""
+# compression types
+testing "autodetect gzip" \
+ 'tar tvf $FILES/tar/tar.tgz | sed "s/[ \t][ \t]*/ /g"' \
+ "drwxr-x--- enh/eng 0 2017-05-13 01:05 dir/\n-rw-r----- enh/eng 12 2017-05-13 01:05 dir/file\n" \
+ "" ""
+
skipnot mknod dir/char c 12 34
testing "create char2" "$TAR /dev/null | $SUM" \
"" "" ""
@@ -142,6 +156,42 @@
testing "ownership" "$TAR dir/block | $SUM" \
"blat" "" ""
+mkdir -p dd/sub/blah &&
+tar cf test.tar dd/sub/blah &&
+rm -rf dd/sub &&
+ln -s ../.. dd/sub || SKIPNEXT=1
+toyonly testing "symlink out of cwd" \
+ "tar xf test.tar 2> /dev/null || echo yes ; [ ! -e dd/sub/blah ] && echo yes" \
+ "yes\nyes\n" "" ""
+
+# If not root can't preserve ownership, so don't try yet.
+
+testing "extract dir/file from tar" \
+ "tar xvCf dd $FILES/tar/tar.tar && stat -c '%A %Y %n' dd/dir dd/dir/file" \
+ "dir/\ndir/file\ndrwxr-x--- 1494637555 dd/dir\n-rw-r----- 1494637555 dd/dir/file\n" \
+ "" ""
+
+testing "extract dir/file from tgz (autodetect)" \
+ "tar xvCf dd $FILES/tar/tar.tgz && stat -c '%A %Y %n' dd/dir dd/dir/file" \
+ "dir/\ndir/file\ndrwxr-x--- 1494637555 dd/dir\n-rw-r----- 1494637555 dd/dir/file\n" \
+ "" ""
+
+toyonly testing "cat tgz | extract dir/file (autodetect)" \
+ "cat $FILES/tar/tar.tgz | tar xvC dd && stat -c '%A %Y %n' dd/dir dd/dir/file" \
+ "dir/\ndir/file\ndrwxr-x--- 1494637555 dd/dir\n-rw-r----- 1494637555 dd/dir/file\n" \
+ "" ""
+
+testing "extract dir/file from tbz2 (autodetect)" \
+ "tar xvCf dd $FILES/tar/tar.tbz2 && stat -c '%A %Y %n' dd/dir dd/dir/file" \
+ "dir/\ndir/file\ndrwxr-x--- 1494637555 dd/dir\n-rw-r----- 1494637555 dd/dir/file\n" \
+ "" ""
+
+toyonly testing "cat tbz | extract dir/file (autodetect)" \
+ "cat $FILES/tar/tar.tbz2 | tar xvC dd && stat -c '%A %Y %n' dd/dir dd/dir/file" \
+ "dir/\ndir/file\ndrwxr-x--- 1494637555 dd/dir\n-rw-r----- 1494637555 dd/dir/file\n" \
+ "" ""
+
TZ="$OLDTZ"
umask $OLDUMASK
-unset LONG TAR SUM OLDUMASK OLDTZ LST
+unset LONG TAR SUM OLDUMASK OLDTZ
+unset -f LST
diff --git a/toys/other/stat.c b/toys/other/stat.c
index d828534..3f1d176 100644
--- a/toys/other/stat.c
+++ b/toys/other/stat.c
@@ -108,10 +108,10 @@
}
llist_traverse(mt, free);
} else if (type == 'N') {
- xprintf("%s", TT.file);
+ printf("%s", TT.file);
if (S_ISLNK(stat->st_mode))
if (readlink0(TT.file, toybuf, sizeof(toybuf)))
- xprintf(" -> `%s'", toybuf);
+ printf(" -> `%s'", toybuf);
} else if (type == 'o') out('u', stat->st_blksize);
else if (type == 's') out('u', stat->st_size);
else if (type == 't') out('x', dev_major(stat->st_rdev));
@@ -124,7 +124,7 @@
else if (type == 'Y') out('u', stat->st_mtime);
else if (type == 'z') date_stat_format(&stat->st_ctim);
else if (type == 'Z') out('u', stat->st_ctime);
- else xprintf("?");
+ else putchar('?');
}
static void print_statfs(char type) {
@@ -166,13 +166,13 @@
void stat_main(void)
{
- int flagf = toys.optflags & FLAG_f, i;
+ int flagf = FLAG(f), i;
char *format, *f;
- if (toys.optflags&FLAG_t) {
- format = flagf ? "%n %i %l %t %s %S %b %f %a %c %d" :
- "%n %s %b %f %u %g %D %i %h %t %T %X %Y %Z %o";
- } else format = flagf
+ if (FLAG(t)) format = flagf
+ ? "%n %i %l %t %s %S %b %f %a %c %d"
+ : "%n %s %b %f %u %g %D %i %h %t %T %X %Y %Z %o";
+ else format = flagf
? " File: \"%n\"\n ID: %i Namelen: %l Type: %T\n"
"Block Size: %s Fundamental block size: %S\n"
"Blocks: Total: %b\tFree: %f\tAvailable: %a\n"
@@ -182,24 +182,27 @@
"Access: (0%a/%A)\tUid: (%5u/%8U)\tGid: (%5g/%8G)\n"
"Access: %x\nModify: %y\nChange: %z";
- if (toys.optflags & FLAG_c) format = TT.c;
+ if (FLAG(c)) format = TT.c;
+ // loop through files listed on command line
for (i = 0; toys.optargs[i]; i++) {
- int L = toys.optflags & FLAG_L;
+ // stat the file or filesystem
TT.file = toys.optargs[i];
if (flagf && !statfs(TT.file, (void *)&TT.stat));
- else if (flagf || (L ? stat : lstat)(TT.file, (void *)&TT.stat)) {
+ else if (flagf || (FLAG(L) ? stat : lstat)(TT.file, (void *)&TT.stat)) {
perror_msg("'%s'", TT.file);
continue;
}
+ // parse format and print what it says
for (f = format; *f; f++) {
- if (*f != '%') putchar(*f);
+ if (*f != '%' || !f[1]) putchar(*f);
+ else if (f[1]=='%') putchar(*f++);
else {
f = next_printf(f, &TT.pattern);
TT.patlen = f-TT.pattern;
- if (TT.patlen>99) error_exit("bad %s", TT.pattern);
+ if (!*f || TT.patlen>99) error_exit("bad %s", TT.pattern);
if (*f == 'n') strout(TT.file);
else if (flagf) print_statfs(*f);
else print_stat(*f);
diff --git a/toys/pending/tar.c b/toys/pending/tar.c
index b32a509..1f71590 100644
--- a/toys/pending/tar.c
+++ b/toys/pending/tar.c
@@ -18,13 +18,13 @@
* Extract into dir same as filename, --restrict? "Tarball is splodey"
*
-USE_TAR(NEWTOY(tar, "&(full-time)(no-recursion)(numeric-owner)(no-same-permissions)(overwrite)(exclude)*(mtime):(group):(owner):(to-command):o(no-same-owner)p(same-permissions)k(keep-old)c(create)|h(dereference)x(extract)|t(list)|v(verbose)j(bzip2)z(gzip)O(to-stdout)m(touch)X(exclude-from)*T(files-from)*C(directory):f(file):[!txc][!jz]", TOYFLAG_USR|TOYFLAG_BIN))
+USE_TAR(NEWTOY(tar, "&(restrict)(full-time)(no-recursion)(numeric-owner)(no-same-permissions)(overwrite)(exclude)*(mtime):(group):(owner):(to-command):o(no-same-owner)p(same-permissions)k(keep-old)c(create)|h(dereference)x(extract)|t(list)|v(verbose)j(bzip2)z(gzip)O(to-stdout)m(touch)X(exclude-from)*T(files-from)*C(directory):f(file):[!txc][!jz]", TOYFLAG_USR|TOYFLAG_BIN))
config TAR
bool "tar"
default n
help
- usage: tar [-cxtjzhmvO] [-X FILE] [-T FILE] [-f TARFILE] [-C DIR]
+ usage: tar [-cxtfvohmjkO] [-XT FILE] [-f TARFILE] [-C DIR]
Create, extract, or list files in a .tar (or compressed t?z) file.
@@ -35,6 +35,7 @@
j bzip2 compression z gzip compression
O Extract to stdout X exclude names in FILE T include names in FILE
--exclude=FILE File pattern(s) to exclude
+ --restrict All archive contents must extract under a single subdirctory.
*/
#define FOR_tar
@@ -150,6 +151,7 @@
struct double_list *end = lst;
if (lst)
+ // constant is FNM_LEADING_DIR
do if (!fnmatch(lst->data, name, 1<<3)) return lst;
while (end != (lst = lst->next));
@@ -193,9 +195,11 @@
name = dirtree_path(node, &i);
// exclusion defaults to --no-anchored and --wildcards-match-slash
- for (p = name; *p; p++)
- if ((p == name || p[-1] == '/') && *p != '/' && filter(TT.excl, p))
- goto done;
+ for (p = name; *p;) {
+ if (filter(TT.excl, p)) goto done;
+ while (*p && *p!='/') p++;
+ while (*p=='/') p++;
+ }
// Consume the 1 extra byte alocated in dirtree_path()
if (S_ISDIR(st->st_mode) && name[i-1] != '/') strcat(name, "/");
@@ -328,7 +332,7 @@
setenv("TAR_FILETYPE", "f", 1);
sprintf(buf, "%0o", TT.hdr.mode);
setenv("TAR_MODE", buf, 1);
- sprintf(buf, "%ld", (long)TT.hdr.size);
+ sprintf(buf, "%lld", (long long)TT.hdr.size);
setenv("TAR_SIZE", buf, 1);
setenv("TAR_FILENAME", TT.hdr.name, 1);
setenv("TAR_UNAME", TT.hdr.uname, 1);
@@ -354,6 +358,14 @@
}
}
+static void wsettime(char *s, long long sec)
+{
+ struct timespec times[2] = {{sec, 0},{sec, 0}};
+
+ if (utimensat(AT_FDCWD, s, times, AT_SYMLINK_NOFOLLOW))
+ perror_msg("settime %lld %s", sec, s);
+}
+
// Do pending directory utimes(), NULL to flush all.
static int dirflush(char *name)
{
@@ -368,20 +380,22 @@
return 1;
}
+
+ if (FLAG(restrict)) {
+ free(TT.cwd);
+ TT.cwd = strdup(s);
+ toys.optflags ^= FLAG_restrict;
+ }
}
// Set deferred utimes() for directories this file isn't under.
// (Files must be depth-first ordered in tarball for this to matter.)
while (TT.dirs) {
- long long ll = *(long long *)TT.dirs->str;
- struct timeval times[2] = {{ll, 0},{ll, 0}};
// If next file is under (or equal to) this dir, keep waiting
if (name && strstart(&ss, ss = s) && (!*ss || *ss=='/')) break;
- if (utimes(TT.dirs->str+sizeof(long long), times))
- perror_msg("utimes %lld %s", ll,
- TT.dirs->str+sizeof(long long));
+ wsettime(TT.dirs->str+sizeof(long long), *(long long *)TT.dirs->str);
free(llist_pop(&TT.dirs));
}
free(s);
@@ -453,7 +467,7 @@
}
// || !FLAG(no_same_permissions))
- if (FLAG(p) && !S_ISLNK(ala)) chmod(TT.hdr.name, ala);
+ if (!S_ISLNK(ala)) chmod(TT.hdr.name, FLAG(p) ? ala : ala&0777);
// Apply mtime.
if (!FLAG(m)) {
@@ -468,10 +482,7 @@
strcpy(sl->str+sizeof(long long), name);
sl->next = TT.dirs;
TT.dirs = sl;
- } else {
- struct timeval times[2] = {{TT.hdr.mtime, 0},{TT.hdr.mtime, 0}};
- utimes(TT.hdr.name, times);
- }
+ } else wsettime(TT.hdr.name, TT.hdr.mtime);
}
}
@@ -493,14 +504,11 @@
i = readall(TT.fd, &tar, 512);
}
- if (i && i != 512) error_exit("read error");
+ if (i && i!=512) error_exit("short header");
// Two consecutive empty headers ends tar even if there's more data
if (!i || !*tar.name) {
- if (!i || and++) {
- dirflush(0);
- return;
- }
+ if (!i || and++) return;
TT.hdr.size = 0;
continue;
}
@@ -591,6 +599,7 @@
// Files are seen even if excluded, so check them here.
// TT.seen points to first seen entry in TT.incl, or NULL if none yet.
+
if ((delete = filter(TT.incl, TT.hdr.name)) && TT.incl != TT.seen) {
if (!TT.seen) TT.seen = delete;
@@ -630,7 +639,7 @@
skippy(TT.hdr.size);
} else {
if (FLAG(v)) printf("%s\n", TT.hdr.name);
- if (FLAG(O)) xsendfile_len(TT.fd, 0, TT.hdr.size);
+ if (FLAG(O)) xsendfile_len(TT.fd, 1, TT.hdr.size);
else if (FLAG(to_command)) extract_to_command();
else extract_to_disk();
}
@@ -643,18 +652,24 @@
}
}
-// Add copy of filename to TT.incl or TT.excl, minus trailing \n and /
-static void trim_list(char **pline, long len)
+// Add copy of filename (minus trailing \n and /) to dlist **
+static void trim2list(void *list, char *pline)
{
- char *n = strdup(*pline);
+ char *n = xstrdup(pline);
int i = strlen(n);
- dlist_add(TT.X ? &TT.excl : &TT.incl, n);
+ dlist_add(list, n);
if (i && n[i-1]=='\n') i--;
while (i && n[i-1] == '/') i--;
n[i] = 0;
}
+// do_lines callback, selects TT.incl or TT.excl based on call order
+static void do_XT(char **pline, long len)
+{
+ if (pline) trim2list(TT.X ? &TT.excl : &TT.incl, *pline);
+}
+
void tar_main(void)
{
char *s, **args = toys.optargs;
@@ -669,10 +684,12 @@
if (TT.group) TT.ggid = xgetgid(TT.group);
if (TT.mtime) xparsedate(TT.mtime, &TT.mtt, (void *)&s, 1);
- // Collect file list. Note: trim_list appends to TT.incl when !TT.X
- for (;TT.X; TT.X = TT.X->next) do_lines(xopenro(TT.X->arg), '\n', trim_list);
- for (args = toys.optargs; *args; args++) trim_list(args, strlen(*args));
- for (;TT.T; TT.T = TT.T->next) do_lines(xopenro(TT.T->arg), '\n', trim_list);
+ // Collect file list.
+ for (; TT.exclude; TT.exclude = TT.exclude->next)
+ trim2list(&TT.excl, TT.exclude->arg);
+ for (;TT.X; TT.X = TT.X->next) do_lines(xopenro(TT.X->arg), '\n', do_XT);
+ for (args = toys.optargs; *args; args++) trim2list(&TT.incl, *args);
+ for (;TT.T; TT.T = TT.T->next) do_lines(xopenro(TT.T->arg), '\n', do_XT);
// If include file list empty, don't create empty archive
if (FLAG(c)) {
@@ -704,11 +721,11 @@
// Are we reading?
if (FLAG(x)||FLAG(t)) {
- struct tar_hdr *hdr = (void *)(toybuf+sizeof(toybuf)-512);
+ struct tar_hdr *hdr = 0;
// autodetect compression type when not specified
if (!FLAG(j)&&!FLAG(z)) {
- len = xread(TT.fd, hdr, 512);
+ len = xread(TT.fd, hdr = (void *)(toybuf+sizeof(toybuf)-512), 512);
if (len!=512 || strncmp("ustar", hdr->magic, 5)) {
// detect gzip and bzip signatures
if (SWAP_BE16(*(short *)hdr)==0x1f8b) toys.optflags |= FLAG_z;
@@ -721,35 +738,48 @@
}
if (FLAG(j)||FLAG(z)) {
- int pipefd[2] = {hdr ? -1 : TT.fd, -1}, i;
+ int pipefd[2] = {hdr ? -1 : TT.fd, -1}, i, pid;
xpopen_both((char *[]){FLAG(z)?"gunzip":"bunzip2", "-cf", "-", NULL},
pipefd);
- close(TT.fd);
- TT.fd = pipefd[1];
- // If we autodetected type but then couldn't lseek to put the data back
- if (hdr) {
- // dirty trick: move pipefd[0] to 0 so child closes spare copy
+ if (!hdr) {
+ // If we could seek, child gzip inherited fd and we read its output
+ close(TT.fd);
+ TT.fd = pipefd[1];
+
+ } else {
+
+ // If we autodetected type but then couldn't lseek to put the data back
+ // we have to loop reading data from TT.fd and pass it to gzip ourselves
+ // (starting with the block of data we read to autodetect).
+
+ // dirty trick: move gzip input pipe to stdin so child closes spare copy
dup2(pipefd[0], 0);
if (pipefd[0]) close(pipefd[0]);
- // Fork a copy of ourselves to handle extraction (reads from zip proc)
- pipefd[0] = TT.fd;
+ // Fork a copy of ourselves to handle extraction (reads from zip output
+ // pipe, writes to stdout).
+ pipefd[0] = pipefd[1];
pipefd[1] = 1;
- xpopen_both(0, pipefd);
- close(TT.fd);
+ pid = xpopen_both(0, pipefd);
+ close(pipefd[1]);
// loop writing collated data to zip proc
xwrite(0, hdr, len);
for (;;) {
- if ((i = read(0, toybuf, sizeof(toybuf)))<1) return;
+ if ((i = read(TT.fd, toybuf, sizeof(toybuf)))<1) {
+ close(0);
+ xwaitpid(pid);
+ return;
+ }
xwrite(0, toybuf, i);
}
- } else hdr = 0;
+ }
}
unpack_tar(hdr);
+ dirflush(0);
if (TT.seen != TT.incl) {
if (!TT.seen) TT.seen = TT.incl;
while (TT.incl != TT.seen) {
@@ -787,8 +817,11 @@
}
if (CFG_TOYBOX_FREE) {
+ llist_traverse(TT.excl, llist_free_double);
+ llist_traverse(TT.incl, llist_free_double);
while(TT.hlc) free(TT.hlx[--TT.hlc].arg);
free(TT.hlx);
+ free(TT.cwd);
close(TT.fd);
}
}
diff --git a/toys/pending/vi.c b/toys/pending/vi.c
index c5c8750..31516b5 100644
--- a/toys/pending/vi.c
+++ b/toys/pending/vi.c
@@ -63,12 +63,13 @@
static int utf8_len(char *str);
static int utf8_width(char *str, int bytes);
static int draw_rune(char *c, int x, int y, int highlight);
+static char* utf8_last(char* str, int size);
-static void cur_left();
-static void cur_right();
-static void cur_up();
-static void cur_down();
+static int cur_left(int count);
+static int cur_right(int count);
+static int cur_up(int count);
+static int cur_down(int count);
static void check_cursor_bounds();
static void adjust_screen_buffer();
@@ -252,29 +253,57 @@
return 1;
}
-//does not work with utf8 yet
int vi_x(int count)
{
char *s;
+ char *last;
int *l;
- int *p;
+ int length = 0;
+ int width = 0;
+ int remaining = 0;
+ char *end;
+ char *start;
if (!c_r)
return 0;
s = c_r->line->str_data;
l = &c_r->line->str_len;
- p = &TT.cur_col;
- if (!(*l)) return 0;
- if ((*p) == (*l)-1) {
- s[*p] = 0;
- if (*p) (*p)--;
- (*l)--;
- } else {
- memmove(s+(*p), s+(*p)+1, (*l)-(*p));
- s[*l] = 0;
- (*l)--;
+
+ last = utf8_last(s,*l);
+ if (last == s+TT.cur_col) {
+ memset(last, 0, (*l)-TT.cur_col);
+ *l = TT.cur_col;
+ if (!TT.cur_col) return 1;
+ last = utf8_last(s, TT.cur_col);
+ TT.cur_col = last-s;
+ return 1;
}
- count--;
- return (count) ? vi_x(count) : 1;
+
+ start = s+TT.cur_col;
+ end = start;
+ remaining = (*l)-TT.cur_col;
+ for (;remaining;) {
+ int next = utf8_lnw(&width, end, remaining);
+ if (next && width) {
+ if (!count) break;
+ count--;
+ } if (!next) break;
+ length += next;
+ end += next;
+ remaining -= next;
+ }
+ if (remaining) {
+ memmove(start, end, remaining);
+ memset(end+remaining,0,end-start);
+ } else {
+ memset(start,0,(*l)-TT.cur_col);
+ }
+ *l -= end-start;
+ if (!TT.cur_col) return 1;
+ if (TT.cur_col == (*l)) {
+ last = utf8_last(s, TT.cur_col);
+ TT.cur_col = last-s;
+ }
+ return 1;
}
//move commands does not behave correct way yet.
@@ -402,7 +431,7 @@
int (*vi_cmd_ptr)(int);
};
-struct vi_cmd_param vi_cmds[7] =
+struct vi_cmd_param vi_cmds[11] =
{
{"dd", &ex_dd},
{"dw", &ex_dw},
@@ -411,6 +440,10 @@
{"b", &vi_movb},
{"e", &vi_move},
{"x", &vi_x},
+ {"h", &cur_left},
+ {"j", &cur_down},
+ {"k", &cur_up},
+ {"l", &cur_right},
};
int run_vi_cmd(char *cmd)
@@ -426,7 +459,7 @@
else {
cmd = cmd_e;
}
- for (; i<7; i++) {
+ for (; i < 11; i++) {
if (strstr(cmd, vi_cmds[i].cmd)) {
return vi_cmds[i].vi_cmd_ptr(val);
}
@@ -440,7 +473,7 @@
struct linelist *lst = c_r;
char *c = strstr(&c_r->line->str_data[TT.cur_col], s);
if (c) {
- TT.cur_col = c_r->line->str_data-c;
+ TT.cur_col = c_r->line->str_data-c; //TODO ??
TT.cur_col = c-c_r->line->str_data;
}
else for (; !c;) {
@@ -523,18 +556,6 @@
}
if (TT.vi_mode == 1) { //NORMAL
switch (key) {
- case 'h':
- cur_left();
- break;
- case 'j':
- cur_down();
- break;
- case 'k':
- cur_up();
- break;
- case 'l':
- cur_right();
- break;
case '/':
case '?':
case ':':
@@ -554,7 +575,7 @@
break;
default:
if (key > 0x20 && key < 0x7B) {
- vi_buf[vi_buf_pos] = key;
+ vi_buf[vi_buf_pos] = key;//TODO handle input better
vi_buf_pos++;
if (run_vi_cmd(vi_buf)) {
memset(vi_buf, 0, 16);
@@ -562,6 +583,7 @@
}
else if (vi_buf_pos == 16) {
vi_buf_pos = 0;
+ memset(vi_buf, 0, 16);
}
}
@@ -834,12 +856,10 @@
static void check_cursor_bounds()
{
- if (c_r->line->str_len-1 < TT.cur_col) {
- if (c_r->line->str_len == 0)
- TT.cur_col = 0;
- else
- TT.cur_col = c_r->line->str_len-1;
- }
+ if (c_r->line->str_len == 0) TT.cur_col = 0;
+ else if (c_r->line->str_len-1 < TT.cur_col) TT.cur_col = c_r->line->str_len-1;
+ if(utf8_width(&c_r->line->str_data[TT.cur_col], c_r->line->str_len-TT.cur_col) <= 0)
+ cur_left(1);
}
static void adjust_screen_buffer()
@@ -963,6 +983,20 @@
return 0;
}
+static char* utf8_last(char* str, int size)
+{
+ char* end = str+size;
+ int pos = size;
+ int len = 0;
+ int width = 0;
+ while (pos >= 0) {
+ len = utf8_lnw(&width, end, size-pos);
+ if (len && width) return end;
+ end--; pos--;
+ }
+ return 0;
+}
+
static int draw_str_until(int *drawn, char *str, int width, int bytes)
{
int rune_width = 0;
@@ -991,38 +1025,50 @@
return max_width-width;
}
-static void cur_left()
+static int cur_left(int count)
{
- if (!TT.cur_col) return;
- TT.cur_col--;
+ for (;count--;) {
+ if (!TT.cur_col) return 1;
- if (!utf8_len(&c_r->line->str_data[TT.cur_col])) cur_left();
+ TT.cur_col--;
+ check_cursor_bounds();//has bit ugly recursion hidden here
+ }
+ return 1;
}
-static void cur_right()
+static int cur_right(int count)
{
- if (c_r->line->str_len <= 1) return;
- if (TT.cur_col == c_r->line->str_len-1) return;
- TT.cur_col++;
- if (!utf8_len(&c_r->line->str_data[TT.cur_col])) cur_right();
+ for (;count--;) {
+ if (c_r->line->str_len <= 1) return 1;
+ if (TT.cur_col >= c_r->line->str_len-1) {
+ TT.cur_col = utf8_last(c_r->line->str_data, c_r->line->str_len)
+ - c_r->line->str_data;
+ return 1;
+ }
+ TT.cur_col++;
+ if (utf8_width(&c_r->line->str_data[TT.cur_col],
+ c_r->line->str_len-TT.cur_col) <= 0)
+ cur_right(1);
+ }
+ return 1;
}
-static void cur_up()
+static int cur_up(int count)
{
- if (c_r->up != 0)
+ for (;count-- && c_r->up;)
c_r = c_r->up;
- if (!utf8_len(&c_r->line->str_data[TT.cur_col])) cur_left();
check_cursor_bounds();
adjust_screen_buffer();
+ return 1;
}
-static void cur_down()
+static int cur_down(int count)
{
- if (c_r->down != 0)
+ for (;count-- && c_r->down;)
c_r = c_r->down;
- if (!utf8_len(&c_r->line->str_data[TT.cur_col])) cur_left();
check_cursor_bounds();
adjust_screen_buffer();
+ return 1;
}