aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorElliott Hughes <enh@google.com>2019-03-27 09:10:04 -0700
committerandroid-build-merger <android-build-merger@google.com>2019-03-27 09:10:04 -0700
commit2326921ef9d6cc0a902e992304a3819b39de949d (patch)
tree2028151cb3f6c8ebf72646846c21d9e18fa3ba02
parentf3798e79707c6dfb700c2a93494d6b5b260f6b8d (diff)
parent0fc5c5bf45cdce2d655c426c2117802ed07f37f0 (diff)
downloadtoybox-2326921ef9d6cc0a902e992304a3819b39de949d.tar.gz
Merge remote-tracking branch 'toybox/master' into HEAD am: 244fbabb55
am: 0fc5c5bf45 Change-Id: I85f771308f7b240abb74779303a262371e78eb15
-rw-r--r--generated/flags.h32
-rw-r--r--generated/globals.h16
-rw-r--r--generated/help.h6
-rw-r--r--generated/newtoys.h4
-rw-r--r--lib/lib.h2
-rw-r--r--lib/xwrap.c108
-rwxr-xr-xscripts/genconfig.sh1
-rwxr-xr-xscripts/make.sh4
-rw-r--r--scripts/portability.sh8
-rwxr-xr-xtests/find.test4
-rwxr-xr-xtests/rm.test10
-rw-r--r--tests/tar.test106
-rw-r--r--toys/other/login.c2
-rw-r--r--toys/pending/bc.c4
-rw-r--r--toys/pending/tar.c377
-rw-r--r--toys/pending/vi.c894
-rw-r--r--toys/posix/date.c133
-rw-r--r--toys/posix/find.c2
-rw-r--r--toys/posix/rm.c13
19 files changed, 1271 insertions, 455 deletions
diff --git a/generated/flags.h b/generated/flags.h
index 02276c30..3be9e367 100644
--- a/generated/flags.h
+++ b/generated/flags.h
@@ -2321,12 +2321,13 @@
#undef FOR_rfkill
#endif
-// rm fiRr[-fi] fiRr[-fi]
+// rm fiRrv[-fi] fiRrv[-fi]
#undef OPTSTR_rm
-#define OPTSTR_rm "fiRr[-fi]"
+#define OPTSTR_rm "fiRrv[-fi]"
#ifdef CLEANUP_rm
#undef CLEANUP_rm
#undef FOR_rm
+#undef FLAG_v
#undef FLAG_r
#undef FLAG_R
#undef FLAG_i
@@ -2741,9 +2742,9 @@
#undef FLAG_f
#endif
-// tar &(no-recursion)(numeric-owner)(no-same-permissions)(overwrite)(exclude)*(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] &(no-recursion)(numeric-owner)(no-same-permissions)(overwrite)(exclude)*(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 &(no-recursion)(numeric-owner)(no-same-permissions)(overwrite)(exclude)*(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] &(no-recursion)(numeric-owner)(no-same-permissions)(overwrite)(exclude)*(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 "&(no-recursion)(numeric-owner)(no-same-permissions)(overwrite)(exclude)*(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 "&(no-recursion)(numeric-owner)(no-same-permissions)(overwrite)(exclude)*(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
@@ -2764,6 +2765,8 @@
#undef FLAG_p
#undef FLAG_o
#undef FLAG_to_command
+#undef FLAG_owner
+#undef FLAG_group
#undef FLAG_exclude
#undef FLAG_overwrite
#undef FLAG_no_same_permissions
@@ -5291,10 +5294,11 @@
#ifndef TT
#define TT this.rm
#endif
-#define FLAG_r (1<<0)
-#define FLAG_R (1<<1)
-#define FLAG_i (1<<2)
-#define FLAG_f (1<<3)
+#define FLAG_v (1<<0)
+#define FLAG_r (1<<1)
+#define FLAG_R (1<<2)
+#define FLAG_i (1<<3)
+#define FLAG_f (1<<4)
#endif
#ifdef FOR_rmdir
@@ -5656,11 +5660,13 @@
#define FLAG_p (1<<14)
#define FLAG_o (1<<15)
#define FLAG_to_command (1<<16)
-#define FLAG_exclude (1<<17)
-#define FLAG_overwrite (1<<18)
-#define FLAG_no_same_permissions (1<<19)
-#define FLAG_numeric_owner (1<<20)
-#define FLAG_no_recursion (1<<21)
+#define FLAG_owner (1<<17)
+#define FLAG_group (1<<18)
+#define FLAG_exclude (1<<19)
+#define FLAG_overwrite (1<<20)
+#define FLAG_no_same_permissions (1<<21)
+#define FLAG_numeric_owner (1<<22)
+#define FLAG_no_recursion (1<<23)
#endif
#ifdef FOR_taskset
diff --git a/generated/globals.h b/generated/globals.h
index 60f31597..79dc6b73 100644
--- a/generated/globals.h
+++ b/generated/globals.h
@@ -805,14 +805,13 @@ struct syslogd_data {
struct tar_data {
char *f, *C;
struct arg_list *T, *X;
- char *to_command;
- struct arg_list *exc;
+ char *to_command, *owner, *group;
+ struct arg_list *exclude;
-// exc is an argument but inc isn't?
- struct arg_list *inc, *pass;
+ struct double_list *incl, *excl, *seen;
void *inodes;
char *cwd;
- int fd;
+ int fd, ouid, ggid;
// Parsed information about a tar header.
struct {
@@ -937,8 +936,11 @@ struct useradd_data {
// toys/pending/vi.c
struct vi_data {
- struct linestack *ls;
- char *statline;
+ int cur_col;
+ int cur_row;
+ unsigned screen_height;
+ unsigned screen_width;
+ int vi_mode;
};
// toys/pending/wget.c
diff --git a/generated/help.h b/generated/help.h
index b5c532e7..bd0db872 100644
--- a/generated/help.h
+++ b/generated/help.h
@@ -320,7 +320,7 @@
#define HELP_wget "usage: wget -f filename URL\n-f filename: specify the filename to be saved\nURL: HTTP uniform resource location and only HTTP, not HTTPS\n\nexamples:\n wget -f index.html http://www.example.com\n wget -f sample.jpg http://www.example.com:8080/sample.jpg\n\n"
-#define HELP_vi "usage: vi FILE\n\nVisual text editor. Predates the existence of standardized cursor keys,\nso the controls are weird and historical.\n\n"
+#define HELP_vi "usage: vi FILE\nVisual text editor. Predates the existence of standardized cursor keys,\nso the controls are weird and historical.\n\n"
#define HELP_userdel "usage: userdel [-r] USER\nusage: deluser [-r] USER\n\nDelete USER from the SYSTEM\n\n-r remove home directory\n\n"
@@ -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 Force bzip2 format z Force gzip format\nO Extract to stdout\nX File of names to exclude\nT File of names to include\n--exclude=FILE File pattern(s) to exclude\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_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"
@@ -490,7 +490,7 @@
#define HELP_rmdir "usage: rmdir [-p] [dirname...]\n\nRemove one or more directories.\n\n-p Remove path\n\n"
-#define HELP_rm "usage: rm [-fiRr] FILE...\n\nRemove each argument from the filesystem.\n\n-f Force: remove without confirmation, no error if it doesn't exist\n-i Interactive: prompt for confirmation\n-rR Recursive: remove directory contents\n\n"
+#define HELP_rm "usage: rm [-fiRrv] FILE...\n\nRemove each argument from the filesystem.\n\n-f Force: remove without confirmation, no error if it doesn't exist\n-i Interactive: prompt for confirmation\n-rR Recursive: remove directory contents\n-v Verbose\n\n"
#define HELP_renice "usage: renice [-gpu] -n increment ID ...\n\n"
diff --git a/generated/newtoys.h b/generated/newtoys.h
index 0c8ef6c7..591b7233 100644
--- a/generated/newtoys.h
+++ b/generated/newtoys.h
@@ -206,7 +206,7 @@ USE_RESET(NEWTOY(reset, 0, TOYFLAG_USR|TOYFLAG_BIN))
USE_RESTORECON(NEWTOY(restorecon, "<1DFnRrv", TOYFLAG_USR|TOYFLAG_SBIN))
USE_REV(NEWTOY(rev, NULL, TOYFLAG_USR|TOYFLAG_BIN))
USE_RFKILL(NEWTOY(rfkill, "<1>2", TOYFLAG_USR|TOYFLAG_SBIN))
-USE_RM(NEWTOY(rm, "fiRr[-fi]", TOYFLAG_BIN))
+USE_RM(NEWTOY(rm, "fiRrv[-fi]", TOYFLAG_BIN))
USE_RMDIR(NEWTOY(rmdir, "<1p", TOYFLAG_BIN))
USE_RMMOD(NEWTOY(rmmod, "<1wf", TOYFLAG_SBIN|TOYFLAG_NEEDROOT))
USE_ROUTE(NEWTOY(route, "?neA:", TOYFLAG_BIN))
@@ -246,7 +246,7 @@ USE_SYSCTL(NEWTOY(sysctl, "^neNqwpaA[!ap][!aq][!aw][+aA]", TOYFLAG_SBIN))
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, "&(no-recursion)(numeric-owner)(no-same-permissions)(overwrite)(exclude)*(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, "&(no-recursion)(numeric-owner)(no-same-permissions)(overwrite)(exclude)*(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/lib/lib.h b/lib/lib.h
index 599ade06..174c1fd6 100644
--- a/lib/lib.h
+++ b/lib/lib.h
@@ -181,6 +181,8 @@ void xregcomp(regex_t *preg, char *rexec, int cflags);
char *xtzset(char *new);
void xsignal_flags(int signal, void *handler, int flags);
void xsignal(int signal, void *handler);
+time_t xvali_date(struct tm *tm, char *str);
+void xparsedate(char *str, time_t *t, unsigned *nano);
// lib.c
void verror_msg(char *msg, int err, va_list va);
diff --git a/lib/xwrap.c b/lib/xwrap.c
index c133125a..ee07fda2 100644
--- a/lib/xwrap.c
+++ b/lib/xwrap.c
@@ -944,3 +944,111 @@ void xsignal(int signal, void *handler)
{
xsignal_flags(signal, handler, 0);
}
+
+
+time_t xvali_date(struct tm *tm, char *str)
+{
+ time_t t;
+
+ if (tm && (unsigned)tm->tm_sec<=60 && (unsigned)tm->tm_min<=59
+ && (unsigned)tm->tm_hour<=23 && tm->tm_mday && (unsigned)tm->tm_mday<=31
+ && (unsigned)tm->tm_mon<=11 && (t = mktime(tm)) != -1) return t;
+
+ error_exit("bad date %s", str);
+}
+
+// Parse date string (relative to current *t). Sets time_t and nanoseconds.
+void xparsedate(char *str, time_t *t, unsigned *nano)
+{
+ struct tm tm;
+ time_t now = *t;
+ int len = 0, i;
+ // Formats with years must come first.
+ char *s = str, *p, *formats[] = {"%F %T", "%FT%T", "%F %H:%M", "%F",
+ "%H:%M:%S", "%H:%M"};
+
+ *nano = 0;
+
+ // Parse @UNIXTIME[.FRACTION]
+ if (*str == '@') {
+ long long ll;
+
+ // Collect seconds and nanoseconds.
+ // &ll is not just t because we can't guarantee time_t is 64 bit (yet).
+ sscanf(s, "@%lld%n", &ll, &len);
+ if (s[len]=='.') {
+ s += len+1;
+ for (len = 0; len<9; len++) {
+ *nano *= 10;
+ if (isdigit(*s)) *nano += *s++-'0';
+ }
+ }
+ if (s[len]) goto bad_dates;
+ *t = ll;
+
+ return;
+ }
+
+ // Is it one of the fancy formats?
+ for (i = 0; i<ARRAY_LEN(formats); i++) {
+ localtime_r(&now, &tm);
+ tm.tm_hour = tm.tm_min = tm.tm_sec = 0;
+ tm.tm_isdst = -1;
+ if ((p = strptime(s, formats[i], &tm)) && !*p) {
+ *t = xvali_date(&tm, str);
+
+ return;
+ }
+ }
+
+ // Posix format?
+ sscanf(s, "%2u%2u%2u%2u%n", &tm.tm_mon, &tm.tm_mday, &tm.tm_hour,
+ &tm.tm_min, &len);
+ if (len != 8) goto bad_dates;
+ s += len;
+ tm.tm_mon--;
+
+ // If year specified, overwrite one we fetched earlier.
+ if (*s && *s != '.') {
+ unsigned year;
+
+ len = 0;
+ sscanf(s, "%u%n", &year, &len);
+ if (len == 4) tm.tm_year = year - 1900;
+ else if (len != 2) goto bad_dates;
+ s += len;
+
+ // 2 digit years, next 50 years are "future", last 50 years are "past".
+ // A "future" date in past is a century ahead.
+ // A non-future date in the future is a century behind.
+ if (len == 2) {
+ unsigned r1 = tm.tm_year % 100, r2 = (tm.tm_year + 50) % 100,
+ century = tm.tm_year - r1;
+
+ if ((r1 < r2) ? (r1 < year && year < r2) : (year < r1 || year > r2)) {
+ if (year < r1) year += 100;
+ } else if (year > r1) year -= 100;
+ tm.tm_year = year + century;
+ }
+ }
+ // Fractional part?
+ if (*s == '.') {
+ len = 0;
+ sscanf(s, ".%2u%n", &tm.tm_sec, &len);
+ s += len;
+ for (len = 0; len<9; len++) {
+ *nano *= 10;
+ if (isdigit(*s)) *nano += *s++-'0';
+ }
+ } else tm.tm_sec = 0;
+
+ // Sanity check field ranges
+ *t = xvali_date(&tm, str);
+
+ // Shouldn't be any trailing garbage.
+ if (!*s) return;
+
+bad_dates:
+ // monkey died
+ xvali_date(0, str);
+}
diff --git a/scripts/genconfig.sh b/scripts/genconfig.sh
index c4940dfd..21e6c7b7 100755
--- a/scripts/genconfig.sh
+++ b/scripts/genconfig.sh
@@ -5,7 +5,6 @@
mkdir -p generated
-source configure
source scripts/portability.sh
probecc()
diff --git a/scripts/make.sh b/scripts/make.sh
index 58456f6f..2fc94b4c 100755
--- a/scripts/make.sh
+++ b/scripts/make.sh
@@ -5,12 +5,8 @@
export LANG=c
export LC_ALL=C
set -o pipefail
-source ./configure
source scripts/portability.sh
-[ ! -z "$CROSS_COMPILE" ] && [ ! -e "$CROSS_COMPILE"cc ] &&
- echo "missing ${CROSS_COMPILE}cc" && exit 1
-
[ -z "$KCONFIG_CONFIG" ] && KCONFIG_CONFIG=.config
[ -z "$OUTNAME" ] && OUTNAME=toybox
UNSTRIPPED="generated/unstripped/$(basename "$OUTNAME")"
diff --git a/scripts/portability.sh b/scripts/portability.sh
index fddd84ec..abeb31f2 100644
--- a/scripts/portability.sh
+++ b/scripts/portability.sh
@@ -1,5 +1,13 @@
# sourced to find alternate names for things
+source configure
+
+if [ -z "$(command -v "${CROSS_COMPILE}${CC}")" ]
+then
+ echo "No ${CROSS_COMPILE}${CC} found" >&2
+ exit 1
+fi
+
if [ -z "$SED" ]
then
[ ! -z "$(which gsed 2>/dev/null)" ] && SED=gsed || SED=sed
diff --git a/tests/find.test b/tests/find.test
index de48c5c8..6b0b0a8a 100755
--- a/tests/find.test
+++ b/tests/find.test
@@ -56,6 +56,10 @@ testing "-type f -user -exec" \
"find dir -type f -user $USER -exec ls {} \\;" "dir/file\n" "" ""
testing "-type l -newer -exec" \
"find dir -type l -newer dir/file -exec ls {} \\;" "dir/link\n" "" ""
+testing "-exec true \\; -print" \
+ "find dir/file -exec true \\; -print" "dir/file\n" "" ""
+testing "-exec false \\; -print" \
+ "find dir/file -exec false \\; -print" "" "" ""
testing "-perm (exact success)" \
"find perm -type f -perm 0444" "perm/all-read-only\n" "" ""
testing "-perm (exact failure)" \
diff --git a/tests/rm.test b/tests/rm.test
index 95710c82..42727442 100755
--- a/tests/rm.test
+++ b/tests/rm.test
@@ -48,3 +48,13 @@ mkdir -p one && touch one/two && chmod 000 one
SKIP_HOST=1 testing "-rf 000 dir" \
"rm -rf one 2>/dev/null && [ ! -e one ] && echo yes" "yes\n" "" ""
chmod 777 one 2>/dev/null ; rm -rf one
+
+mkdir -p d1
+touch d1/f1.txt d1/f2.txt
+testing "-rv dir" \
+ "rm -rv d1" "rm 'd1/f1.txt'\nrm 'd1/f2.txt'\nrmdir 'd1'\n" "" ""
+rm -rf d1
+
+touch "'"
+testing "-v \\'" "rm -v \\'" "rm '''\n" "" "" # TODO: coreutils escapes quote
+rm -f \'
diff --git a/tests/tar.test b/tests/tar.test
index 50a94e60..31aee386 100644
--- a/tests/tar.test
+++ b/tests/tar.test
@@ -1,104 +1,14 @@
#!/bin/bash
-# Copyright 2014 Divya Kothari <divya.s.kothari@gmail.com>
-# Copyright 2014 Naha Maggu <maggu.neha@gmail.com>
-
[ -f testing.sh ] && . testing.sh
#testing "name" "command" "result" "infile" "stdin"
-#Creating dir
-mkdir dir/dir1 -p
-echo "This is testdata" > dir/dir1/file
-testing "tgz - compession, extraction and data validation" "tar -czf dir.tgz dir/ && [ -e dir.tgz ] && echo 'yes'; rm -rf dir; tar -xf dir.tgz && [ -f dir/dir1/file ] && cat dir/dir1/file; rm -rf dir.tgz" "yes\nThis is testdata\n" "" ""
-
-#Creating dir
-mkdir dir/dir1 -p
-echo "This is testdata" > dir/dir1/file
-testing "tar.gz - compession, extraction and data validation" "tar -czf dir.tar.gz dir/ && [ -e dir.tar.gz ] && echo 'yes'; rm -rf dir; tar -xf dir.tar.gz && [ -f dir/dir1/file ] && cat dir/dir1/file; rm -rf dir.tar.gz" "yes\nThis is testdata\n" "" ""
-
-#Creating dir
-mkdir dir/dir1 -p
-echo "This is testdata" > dir/dir1/file
-testing "verbose compression" "tar -cvzf dir.tgz dir/; rm -rf dir.tgz" "dir/\ndir/dir1/\ndir/dir1/file\n" "" ""
-rm -rf dir/
-
-#creating test file
-dd if=/dev/zero of=testFile ibs=4096 obs=4096 count=1000 2>/dev/null
-testing "- compession and extraction of a file" "tar -czf testFile.tgz testFile && [ -e testFile.tgz ] && echo 'yes'; rm -rf testFile; tar -xf testFile.tgz && [ -f testFile ] && echo 'yes'; rm -rf testFile.tgz" "yes\nyes\n" "" ""
-
-#creating empty test file
-touch testFile
-testing "- compession and extraction of a empty file" "tar -czf testFile.tgz testFile && [ -e testFile.tgz ] && echo 'yes'; rm -rf testFile; tar -xf testFile.tgz && [ -f testFile ] && echo 'yes'; rm -rf testFile.tgz" "yes\nyes\n" "" ""
-
-#Creating dir
-mkdir dir/dir1 -p
-touch dir/dir1/file1 dir/dir1/file2 dir/dir1/file3 dir/dir1/file4
-testing "-t option" "tar -czf dir.tar.gz dir/; rm -rf dir; tar -tf dir.tar.gz | sort; rm -rf dir.tar.gz" "dir/\ndir/dir1/\ndir/dir1/file1\ndir/dir1/file2\ndir/dir1/file3\ndir/dir1/file4\n" "" ""
-rm -rf dir/
-
-#Creating nested directory
-mkdir -p dir/dir1 dir/dir2 dir/dir3 dir/dir4
-echo "This is testdata" > dir/dir1/file; echo "Dont exclude me" > dir/dir3/file1 ;
-echo "Exclude me" > dir/dir3/file2 ; echo "YO" > dir/dir4/file1 ; echo "Hello" >dir/dir4/file2; echo "Dont" > dir/dir2/file1
-echo -ne "dir/dir4\ndir/dir3/file2\n" > exclude_file
-testing "create with files excluded : -X" "tar -czf dir.tgz dir/ -X exclude_file ; rm -rf dir ; tar -tf dir.tgz | sort; rm -rf dir.tgz " "dir/\ndir/dir1/\ndir/dir1/file\ndir/dir2/\ndir/dir2/file1\ndir/dir3/\ndir/dir3/file1\n" "" ""
-rm -rf exclude_file
-
-#Creating nested directory
-mkdir dir/dir1 -p ; mkdir dir/dir2 ; mkdir dir/dir3 ; mkdir dir/dir4
-echo "This is testdata" > dir/dir1/file
-echo "Dont exclude me" > dir/dir3/file1 ; echo "Exclude me" > dir/dir3/file2 ; echo "YO" > dir/dir4/file1 ; echo "Hello" >dir/dir4/file2; echo "Dont" > dir/dir2/file1
-testing "with pattern --exclude" "tar --exclude=dir/dir3/* -czf dir.tgz dir/ ; rm -rf dir ; tar -tf dir.tgz | sort; rm -rf dir.tgz " "dir/\ndir/dir1/\ndir/dir1/file\ndir/dir2/\ndir/dir2/file1\ndir/dir3/\ndir/dir4/\ndir/dir4/file1\ndir/dir4/file2\n" "" ""
-
-#Creating directory to be compressed
-mkdir dir/dir1 -p
-echo "This is testdata" > dir/dir1/file
-mkdir temp
-testing "extract with -C Dir" "tar -czf dir.tgz dir/ ;rm -rf dir ;tar -xf dir.tgz -C temp/ ; [ -e temp/dir ] && echo 'yes' ; rm -rf dir dir.tgz" "yes\n" "" ""
-rm -rf temp
-
-#Creating nested directory
-mkdir dir/dir1 -p ; mkdir dir/dir2 ; mkdir dir/dir3 ; mkdir dir/dir4 ; mkdir temp_dir
-echo "dir1/file" > dir/dir1/file ; echo "temp_dir/file" > temp_dir/file
-echo "dir3/file1" > dir/dir3/file1 ; echo "dir3/file2" > dir/dir3/file2 ; echo "YO" > dir/dir4/file1 ; echo "Hello" >dir/dir4/file2; echo "dir2/file1" > dir/dir2/file1
-echo "temp_dir/file" > exclude_file
-testing "create with extra files/directory included : -T" "tar -czf dir.tgz dir/ -T exclude_file ; rm -rf dir ; tar -tf dir.tgz | sort; rm -rf dir.tgz " "dir/\ndir/dir1/\ndir/dir1/file\ndir/dir2/\ndir/dir2/file1\ndir/dir3/\ndir/dir3/file1\ndir/dir3/file2\ndir/dir4/\ndir/dir4/file1\ndir/dir4/file2\ntemp_dir/file\n" "" ""
-rm -rf exclude_file
-rm -rf temp_dir
-
-#Creating dir
-mkdir dir/dir1 -p
-echo "Inside dir/dir1" > dir/dir1/file ; echo "Hello Inside dir" > dir/file
-testing "extract to STDOUT : -O" " tar -czf dir.tgz dir/ ; rm -rf dir ; tar -xf dir.tgz -O ; [ -e 'Inside dir/dir1/\nHello Inside dir\n' ] && echo 'yes'; rm -rf dir.tgz " "" "" ""
-
-#Creating short filename
-f="filename_with_100_chars_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
-echo "This is testdata" > $f
-testing "shortname filename" "tar -cf testFile.tar $f && [ -e testFile.tar ] && echo 'yes'; rm -f $f; tar -xf testFile.tar && [ -f $f ] && cat $f && strings testFile.tar | grep -o LongLink; rm -f testFile.tar; rm -f $f" "yes\nThis is testdata\n" "" ""
-
-#Creating long filename
-f="filename_with_101_chars_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
-echo "This is testdata" > $f
-testing "longname filename" "tar -cf testFile.tar $f && [ -e testFile.tar ] && echo 'yes'; rm -f $f; tar -xf testFile.tar && [ -f $f ] && cat $f && strings testFile.tar | grep -o LongLink; rm -f testFile.tar; rm -f $f" "yes\nThis is testdata\nLongLink\n" "" ""
-
-#Creating long pathname
-d="dirname_with_50_chars_xxxxxxxxxxxxxxxxxxxxxxxxxxxx"
-f="filename_with_50_chars_xxxxxxxxxxxxxxxxxxxxxxxxxxx"
-mkdir $d
-echo "This is testdata" > $d/$f
-testing "longname pathname" "tar -cf testFile.tar $d/$f && [ -e testFile.tar ] && echo 'yes'; rm -rf $d; tar -xf testFile.tar && [ -f $d/$f ] && cat $d/$f && strings testFile.tar | grep -o LongLink; rm -f testFile.tar; rm -rf $d" "yes\nThis is testdata\nLongLink\n" "" ""
-
-# gzip
-rm -rf d
-mkdir d
-echo "hello world" > d/f
-testing "gzip compression" "tar zcf f d && file f | grep -q gzip && echo y ; rm -rf d ; tar xf f && cat d/f" "y\nhello world\n" "" ""
-testing "gzip decompression" "tar xf $FILES/tar/tar.tgz && cat dir/file" "hello world\n" "" ""
-
-# bzip2
-rm -rf d
-mkdir d
-echo "hello world" > d/f
-testing "bzip2 compression" "tar jcf f d && file f | grep -q bzip2 && echo y ; rm -rf d ; tar xf f && cat d/f" "y\nhello world\n" "" ""
-testing "bzip2 decompression" "tar xf $FILES/tar/tar.tbz2 && cat dir/file" "hello world\n" "" ""
+TARSUM='--owner root --group root | head -c $((3*512)) | sha1sum | sed "s/ .*//"'
+touch -t 198001010101 file
+testing "create file" "tar c file $TARSUM" \
+ "d551292408833aa5e9db32c0d14d7f32e7e96882\n" "" ""
+mkdir walrus
+touch -t 198001010101 dir
+testing "create dir" "tar c dir $TARSUM" \
+ "c4e630d9c89f4f20d603a6f71ff4410ab56fe965\n" "" ""
diff --git a/toys/other/login.c b/toys/other/login.c
index 9bd6cc95..5214b937 100644
--- a/toys/other/login.c
+++ b/toys/other/login.c
@@ -119,7 +119,7 @@ void login_main(void)
if (fchown(tty, pwd->pw_uid, pwd->pw_gid) || fchmod(tty, 0600))
printf("can't claim tty");
xsetuser(pwd);
- reset_env(pwd, FLAG(p));
+ reset_env(pwd, !FLAG(p));
// Message of the day
if ((ss = readfile("/etc/motd", 0, 0))) puts(ss);
diff --git a/toys/pending/bc.c b/toys/pending/bc.c
index 142c0ce2..bb5d86cb 100644
--- a/toys/pending/bc.c
+++ b/toys/pending/bc.c
@@ -952,8 +952,8 @@ void bc_vec_concat(BcVec *v, char *str) {
if (!v->len) bc_vec_pushByte(v, '\0');
len = strlen(str);
- bc_vec_grow(v, len+1);
- strcpy(v->v+v->len, str);
+ bc_vec_grow(v, len);
+ strcpy(v->v+v->len-1, str);
v->len += len;
}
diff --git a/toys/pending/tar.c b/toys/pending/tar.c
index 97e699b4..d0d840ba 100644
--- a/toys/pending/tar.c
+++ b/toys/pending/tar.c
@@ -18,11 +18,7 @@
* Extract into dir same as filename, --restrict? "Tarball is splodey"
*
-USE_TAR(NEWTOY(tar, "&(no-recursion)(numeric-owner)(no-same-permissions)(overwrite)(exclude)*(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))
-
-todo: support .txz
-todo: directory timestamps not set on extract
-todo: extract into chmod 000 directory
+USE_TAR(NEWTOY(tar, "&(no-recursion)(numeric-owner)(no-same-permissions)(overwrite)(exclude)*(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"
@@ -36,10 +32,8 @@ config TAR
c Create x Extract t Test
f Name of TARFILE C Change to DIR first v Verbose: show filenames
o Ignore owner h Follow symlinks m Ignore mtime
- j Force bzip2 format z Force gzip format
- O Extract to stdout
- X File of names to exclude
- T File of names to include
+ 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
*/
@@ -49,14 +43,13 @@ config TAR
GLOBALS(
char *f, *C;
struct arg_list *T, *X;
- char *to_command;
- struct arg_list *exc;
+ char *to_command, *owner, *group;
+ struct arg_list *exclude;
-// exc is an argument but inc isn't?
- struct arg_list *inc, *pass;
+ struct double_list *incl, *excl, *seen;
void *inodes;
char *cwd;
- int fd;
+ int fd, ouid, ggid;
// Parsed information about a tar header.
struct {
@@ -76,6 +69,34 @@ struct tar_hdr {
prefix[155], padd[12];
};
+// convert to int to octal (or base-256)
+static void itoo(char *str, int len, unsigned long long val)
+{
+ // Do we need binary encoding?
+ if (!(val>>(3*(len-1)))) sprintf(str, "%0*llo", len-1, val);
+ else {
+ *str = 128;
+ while (--len) *++str = val>>(3*len);
+ }
+}
+#define ITOO(x, y) itoo(x, sizeof(x), y)
+
+//convert octal (or base-256) to int
+static unsigned long long otoi(char *str, unsigned len)
+{
+ unsigned long long val = 0;
+
+ // When tar value too big or octal, use binary encoding with high bit set
+ if (128&*str) while (--len) val = (val<<8)+*++str;
+ else {
+ while (len && *str>='0' && *str<='7') val = val*8+*str++-'0', len--;
+ if (len && *str && *str != ' ') error_exit("bad header");
+ }
+
+ return val;
+}
+
+
struct inode_list {
struct inode_list *next;
char *arg;
@@ -83,13 +104,6 @@ struct inode_list {
dev_t dev;
};
-//convert to octal
-static void itoo(char *str, int len, off_t val)
-{
- sprintf(str, "%0*llo", len-1, (unsigned long long)val);
-}
-
-// This really needs a hash table
static struct inode_list *seen_inode(void **list, struct stat *st, char *name)
{
if (!S_ISDIR(st->st_mode) && st->st_nlink > 1) {
@@ -121,31 +135,37 @@ static unsigned cksum(void *data)
static void write_longname(char *name, char type)
{
- struct tar_hdr tmp[2];
+ struct tar_hdr tmp;
int sz = strlen(name) +1;
- memset(tmp, 0, sizeof(tmp));
- strcpy(tmp->name, "././@LongLink");
- memset(tmp->mode, '0', sizeof(tmp->mode)-1);
- memset(tmp->uid, '0', sizeof(tmp->uid)-1);
- memset(tmp->gid, '0', sizeof(tmp->gid)-1);
- itoo(tmp->size, sizeof(tmp->size), sz);
- memset(tmp->mtime, '0', sizeof(tmp->mtime)-1);
- tmp->type = type;
- strcpy(tmp->magic, "ustar ");
-
- // Calculate checksum
- itoo(tmp->chksum, sizeof(tmp->chksum), cksum(&tmp));
+ memset(&tmp, 0, sizeof(tmp));
+ strcpy(tmp.name, "././@LongLink");
+ ITOO(tmp.mode, 0);
+ ITOO(tmp.uid, 0);
+ ITOO(tmp.gid, 0);
+ ITOO(tmp.size, sz);
+ ITOO(tmp.mtime, 0);
+ tmp.type = type;
+ strcpy(tmp.magic, "ustar ");
+
+ // Calculate checksum. Since 0777777 is twice 512*255 it can never use more
+ // than 6 digits, last byte is ' ' or historical reasons.
+ itoo(tmp.chksum, sizeof(tmp.chksum)-1, cksum(&tmp));
+ tmp.chksum[7] = ' ';
// write header and name, padded with NUL to block size
- xwrite(TT.fd, tmp, sizeof(*tmp));
+ xwrite(TT.fd, &tmp, 512);
xwrite(TT.fd, name, sz);
- xwrite(TT.fd, tmp+1, 512-(sz%512));
+ if (sz%512) xwrite(TT.fd, toybuf, 512-(sz%512));
}
-static int filter(struct arg_list *lst, char *name)
+static struct double_list *filter(struct double_list *lst, char *name)
{
- for (; lst; lst = lst->next) if (!fnmatch(lst->arg, name, 1<<3)) return 1;
+ struct double_list *end = lst;
+
+ if (lst)
+ do if (!fnmatch(lst->data, name, 1<<3)) return lst;
+ while (end != (lst = lst->next));
return 0;
}
@@ -169,15 +189,15 @@ static void alloread(void *buf, int len)
static void add_file(char **nam, struct stat *st)
{
struct tar_hdr hdr;
- struct passwd *pw;
- struct group *gr;
+ struct passwd *pw = pw;
+ struct group *gr = gr;
struct inode_list *node = node;
int i, fd =-1;
- char *c, *p, *name = *nam, *lnk, *hname, buf[512] = {0,};
+ char *c, *p, *name = *nam, *lnk, *hname;
static int warn = 1;
for (p = name; *p; p++)
- if ((p == name || p[-1] == '/') && *p != '/' && filter(TT.exc, p)) return;
+ if ((p == name || p[-1] == '/') && *p != '/' && filter(TT.excl, p)) return;
if (S_ISDIR(st->st_mode) && name[strlen(name)-1] != '/') {
lnk = xmprintf("%s/",name);
@@ -195,55 +215,52 @@ static void add_file(char **nam, struct stat *st)
warn = 0;
}
+ if (TT.owner) st->st_uid = TT.ouid;
+ if (TT.group) st->st_gid = TT.ggid;
+
memset(&hdr, 0, sizeof(hdr));
strncpy(hdr.name, hname, sizeof(hdr.name));
- itoo(hdr.mode, sizeof(hdr.mode), st->st_mode &07777);
- itoo(hdr.uid, sizeof(hdr.uid), st->st_uid);
- itoo(hdr.gid, sizeof(hdr.gid), st->st_gid);
- itoo(hdr.size, sizeof(hdr.size), 0); //set size later
- itoo(hdr.mtime, sizeof(hdr.mtime), st->st_mtime);
+ ITOO(hdr.mode, st->st_mode &07777);
+ ITOO(hdr.uid, st->st_uid);
+ ITOO(hdr.gid, st->st_gid);
+ ITOO(hdr.size, 0); //set size later
+ ITOO(hdr.mtime, st->st_mtime);
// Hard link or symlink?
i = !!S_ISLNK(st->st_mode);
if (i || (node = seen_inode(&TT.inodes, st, hname))) {
-// TODO: test preserve symlink ownership
hdr.type = '1'+i;
if (!(lnk = i ? xreadlink(name) : node->arg)) return perror_msg("readlink");
-// TODO: does this need NUL terminator?
- if (strlen(lnk) > sizeof(hdr.link))
- write_longname(lnk, 'K'); //write longname LINK
-// TODO: this will error_exit() if too long, not truncate.
- xstrncpy(hdr.link, lnk, sizeof(hdr.link));
+ if (strlen(lnk) > sizeof(hdr.link)) write_longname(lnk, 'K');
+ strncpy(hdr.link, lnk, sizeof(hdr.link));
if (i) free(lnk);
} else if (S_ISREG(st->st_mode)) {
hdr.type = '0';
- if (st->st_size <= (off_t)077777777777LL)
- itoo(hdr.size, sizeof(hdr.size), st->st_size);
- else {
-// TODO: test accept 12 7's but don't emit without terminator
- return error_msg("TODO: need base-256 encoding for '%s' '%lld'\n",
- hname, (unsigned long long)st->st_size);
- }
+ ITOO(hdr.size, st->st_size);
} else if (S_ISDIR(st->st_mode)) hdr.type = '5';
else if (S_ISFIFO(st->st_mode)) hdr.type = '6';
else if (S_ISBLK(st->st_mode) || S_ISCHR(st->st_mode)) {
hdr.type = (S_ISCHR(st->st_mode))?'3':'4';
- itoo(hdr.major, sizeof(hdr.major), dev_major(st->st_rdev));
- itoo(hdr.minor, sizeof(hdr.minor), dev_minor(st->st_rdev));
+ ITOO(hdr.major, dev_major(st->st_rdev));
+ ITOO(hdr.minor, dev_minor(st->st_rdev));
} else return error_msg("unknown file type '%o'", st->st_mode & S_IFMT);
- if (strlen(hname) > sizeof(hdr.name))
- write_longname(hname, 'L'); //write longname NAME
+ if (strlen(hname) > sizeof(hdr.name)) write_longname(hname, 'L');
strcpy(hdr.magic, "ustar ");
- if ((pw = bufgetpwuid(st->st_uid)))
- snprintf(hdr.uname, sizeof(hdr.uname), "%s", pw->pw_name);
- else sprintf(hdr.uname, "%d", st->st_uid);
-
- if ((gr = bufgetgrgid(st->st_gid)))
- snprintf(hdr.gname, sizeof(hdr.gname), "%s", gr->gr_name);
- else sprintf(hdr.gname, "%d", st->st_gid);
+ if (!FLAG(numeric_owner)) {
+ if (!TT.owner && !(pw = bufgetpwuid(st->st_uid)))
+ sprintf(hdr.uname, "%d", st->st_uid);
+ else snprintf(hdr.uname, sizeof(hdr.uname), "%s",
+ TT.owner ? TT.owner : pw->pw_name);
+ if (!TT.group && !(gr = bufgetgrgid(st->st_gid)))
+ sprintf(hdr.gname, "%d", st->st_gid);
+ else snprintf(hdr.gname, sizeof(hdr.gname), "%s",
+ TT.group ? TT.group : gr->gr_name);
+ }
itoo(hdr.chksum, sizeof(hdr.chksum)-1, cksum(&hdr));
+ hdr.chksum[7] = ' ';
+
if (FLAG(v)) printf("%s\n",hname);
xwrite(TT.fd, (void*)&hdr, 512);
@@ -254,7 +271,7 @@ static void add_file(char **nam, struct stat *st)
return;
}
xsendfile_pad(fd, TT.fd, st->st_size);
- if (st->st_size%512) writeall(TT.fd, buf, (512-(st->st_size%512)));
+ if (st->st_size%512) writeall(TT.fd, toybuf, (512-(st->st_size%512)));
close(fd);
}
@@ -273,8 +290,8 @@ static int add_to_tar(struct dirtree *node)
path = dirtree_path(node, 0);
add_file(&path, &(node->st)); //path may be modified
free(path);
- if (FLAG(no_recursion)) return 0;
- return ((DIRTREE_RECURSE | (FLAG(h)?DIRTREE_SYMFOLLOW:0)));
+
+ return (DIRTREE_RECURSE|(FLAG(h)?DIRTREE_SYMFOLLOW:0))*!FLAG(no_recursion);
}
// Does anybody actually use this?
@@ -283,13 +300,10 @@ static void extract_to_command(void)
int pipefd[2], status = 0;
pid_t cpid;
- xpipe(pipefd);
if (!S_ISREG(TT.hdr.mode)) return; //only regular files are supported.
- cpid = fork();
- if (cpid == -1) perror_exit("fork");
-
- if (!cpid) { // Child reads from pipe
+ xpipe(pipefd);
+ if (!(cpid = xfork())) { // Child reads from pipe
char buf[64], *argv[4] = {"sh", "-c", TT.to_command, NULL};
setenv("TAR_FILETYPE", "f", 1);
@@ -327,7 +341,6 @@ static void extract_to_disk(void)
char *s;
struct stat ex;
-// while not if
flags = strlen(TT.hdr.name);
if (flags>2)
if (strstr(TT.hdr.name, "/../") || !strcmp(TT.hdr.name, "../") ||
@@ -381,25 +394,30 @@ COPY:
xsendfile_len(TT.fd, dst_fd, TT.hdr.size);
close(dst_fd);
- if (!FLAG(o)) {
+ if (!FLAG(o) && !geteuid()) {
//set ownership..., --no-same-owner, --numeric-owner
uid_t u = TT.hdr.uid;
gid_t g = TT.hdr.gid;
- if (!FLAG(numeric_owner)) {
- struct group *gr = getgrnam(TT.hdr.gname);
+ if (TT.owner) u = TT.ouid;
+ else if (!FLAG(numeric_owner)) {
struct passwd *pw = getpwnam(TT.hdr.uname);
- if (pw) u = pw->pw_uid;
+ if (pw && (TT.owner || !FLAG(numeric_owner))) u = pw->pw_uid;
+ }
+
+ if (TT.group) g = TT.ggid;
+ else if (!FLAG(numeric_owner)) {
+ struct group *gr = getgrnam(TT.hdr.gname);
if (gr) g = gr->gr_gid;
}
- if (!geteuid() && lchown(TT.hdr.name, u, g))
+
+ if (lchown(TT.hdr.name, u, g))
perror_msg("chown %d:%d '%s'", u, g, TT.hdr.name);;
}
// || !FLAG(no_same_permissions))
if (FLAG(p) && !S_ISLNK(TT.hdr.mode)) chmod(TT.hdr.name, TT.hdr.mode);
-// TODO: defer directory mtime until we've finished with contents
//apply mtime
if (!FLAG(m)) {
struct timeval times[2] = {{TT.hdr.mtime, 0},{TT.hdr.mtime, 0}};
@@ -407,59 +425,30 @@ COPY:
}
}
-static void add_to_list(struct arg_list **llist, char *name)
-{
- struct arg_list **list = llist;
-
- while (*list) list=&((*list)->next);
- *list = xzalloc(sizeof(struct arg_list));
- (*list)->arg = name;
- if ((name[strlen(name)-1] == '/') && strlen(name) != 1)
- name[strlen(name)-1] = '\0';
-}
-
-static void file_to_list(char *file, struct arg_list **llist)
-{
- int fd = xopenro(file);
- char *line = 0;
-
- while ((line = get_line(fd))) add_to_list(llist, xstrdup(line));
- if (fd) close(fd);
- free(line);
-}
-
-//convert octal to int
-static unsigned long long otoi(char *str, int len)
-{
- unsigned long long val;
-
-// todo: base-256 encoding, just do it symmetrically for all fields
- str[len-1] = 0;
- val = strtoull(str, &str, 8);
- if (*str && *str != ' ') error_exit("bad header");
-
- return val;
-}
-
static void unpack_tar(void)
{
+ struct double_list *walk, *delete;
struct tar_hdr tar;
- int i;
+ int i, and = 0;
char *s;
for (;;) {
// align to next block and read it
if (TT.hdr.size%512) skippy(512-TT.hdr.size%512);
+ if (!(i = readall(TT.fd, &tar, 512))) return;
- i = readall(TT.fd, &tar, 512);
if (i != 512) error_exit("read error");
+
+ // Two consecutive empty headers ends tar even if there's more data
+ if (!*tar.name) {
+ if (and++) return;
+ TT.hdr.size = 0;
+ continue;
+ }
// ensure null temination even of pathological packets
- tar.padd[0] = 0;
- // End of tar
- if (!*tar.name) return;
+ tar.padd[0] = and = 0;
-// can you append a bzip to a gzip _within_ a tarball? Nested compress?
-// Or compressed data after uncompressed data?
+ // Is this a valid Unix Standard TAR header?
if (memcmp(tar.magic, "ustar", 5)) error_exit("bad header");
if (cksum(&tar) != otoi(tar.chksum, sizeof(tar.chksum)))
error_exit("bad cksum");
@@ -467,6 +456,8 @@ static void unpack_tar(void)
// If this header isn't writing something to the filesystem
if (tar.type<'0' || tar.type>'7') {
+
+ // Long name extension header?
if (tar.type == 'K') alloread(&TT.hdr.link_target, TT.hdr.size);
else if (tar.type == 'L') alloread(&TT.hdr.name, TT.hdr.size);
else if (tar.type == 'x') {
@@ -479,7 +470,7 @@ static void unpack_tar(void)
if ((i = sscanf(p, "%u path=%n", &len, &n))<1 || len<4 ||
len>TT.hdr.size)
{
- error_msg("corrupted extended header");
+ error_msg("bad header");
break;
}
p[len-1] = 0;
@@ -490,14 +481,13 @@ static void unpack_tar(void)
}
free(buf);
- // This could be if (strchr("DMNSVg", tar.type)) but an unknown header
- // type with trailing contents is unlikely to have a valid type & cksum
+ // Ignore everything else.
} else skippy(TT.hdr.size);
continue;
}
- // At this point, we're writing something to the filesystem. Parse fields.
+ // At this point, we have something to output. Convert metadata.
TT.hdr.mode = otoi(tar.mode, sizeof(tar.mode));
TT.hdr.mode |= (char []){8,8,10,2,6,4,1,8}[tar.type-'0']<<12;
TT.hdr.uid = otoi(tar.uid, sizeof(tar.uid));
@@ -506,57 +496,66 @@ static void unpack_tar(void)
TT.hdr.device = dev_makedev(otoi(tar.major, sizeof(tar.major)),
otoi(tar.minor, sizeof(tar.minor)));
- TT.hdr.uname = xstrndup(tar.uname, sizeof(tar.uname));
- TT.hdr.gname = xstrndup(tar.gname, sizeof(tar.gname));
+ TT.hdr.uname = xstrndup(TT.owner ? TT.owner : tar.uname,sizeof(tar.uname));
+ TT.hdr.gname = xstrndup(TT.group ? TT.group : tar.gname,sizeof(tar.gname));
if (!TT.hdr.link_target && *tar.link)
TT.hdr.link_target = xstrndup(tar.link, sizeof(tar.link));
if (!TT.hdr.name) {
+ // Glue prefix and name fields together with / if necessary
i = strnlen(tar.prefix, sizeof(tar.prefix));
TT.hdr.name = xmprintf("%.*s%s%.*s", i, tar.prefix,
(i && tar.prefix[i-1] != '/') ? "/" : "",
(int)sizeof(tar.name), tar.name);
}
- // Directories sometimes recorded as "file with trailing slash"
+ // Old broken tar recorded dir as "file with trailing slash"
if (S_ISREG(TT.hdr.mode) && (s = strend(TT.hdr.name, "/"))) {
*s = 0;
TT.hdr.mode = (TT.hdr.mode & ~S_IFMT) | S_IFDIR;
}
- // Hardlinks, symlinks, and directories do not have contents in archive
- // (Neither do fifo, block or char devices, but not testing for that...?)
- if ((TT.hdr.link_target && *TT.hdr.link_target)
- || S_ISLNK(TT.hdr.mode) || S_ISDIR(TT.hdr.mode))
+ // Non-regular files don't have contents stored in archive.
+ if ((TT.hdr.link_target && *TT.hdr.link_target) || !S_ISREG(TT.hdr.mode))
TT.hdr.size = 0;
+ // 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;
+
+ // Move seen entry to end of list.
+ if (TT.incl == delete) TT.incl = TT.incl->next;
+ else for (walk = TT.incl; walk != TT.seen; walk = walk->next) {
+ if (walk == delete) {
+ dlist_pop(&walk);
+ dlist_add_nomalloc(&TT.incl, delete);
+ }
+ }
+ }
+
// Skip excluded files
- if (filter(TT.exc, TT.hdr.name) || (TT.inc && !filter(TT.inc, TT.hdr.name)))
+ if (filter(TT.excl, TT.hdr.name) || (TT.incl && !delete))
skippy(TT.hdr.size);
- else {
-
-// TODO: wrong, shouldn't grow endlessly, mark seen TT.inc instead
- add_to_list(&TT.pass, xstrdup(TT.hdr.name));
-
- if (FLAG(t)) {
- if (FLAG(v)) {
- char perm[11];
- struct tm *lc = localtime(&TT.hdr.mtime);
-
- mode_to_string(TT.hdr.mode, perm);
- printf("%s %s/%s %9ld %d-%02d-%02d %02d:%02d:%02d ", perm,
- TT.hdr.uname, TT.hdr.gname, (long)TT.hdr.size, 1900+lc->tm_year,
- 1+lc->tm_mon, lc->tm_mday, lc->tm_hour, lc->tm_min, lc->tm_sec);
- }
- printf("%s", TT.hdr.name);
- if (TT.hdr.link_target) printf(" -> %s", TT.hdr.link_target);
- xputc('\n');
- 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);
- else if (FLAG(to_command)) extract_to_command();
- else extract_to_disk();
+ else if (FLAG(t)) {
+ if (FLAG(v)) {
+ struct tm *lc = localtime(&TT.hdr.mtime);
+ char perm[11];
+
+ mode_to_string(TT.hdr.mode, perm);
+ printf("%s %s/%s %9lld %d-%02d-%02d %02d:%02d:%02d ", perm,
+ TT.hdr.uname, TT.hdr.gname, (long long)TT.hdr.size,
+ 1900+lc->tm_year, 1+lc->tm_mon, lc->tm_mday, lc->tm_hour,
+ lc->tm_min, lc->tm_sec);
}
+ printf("%s", TT.hdr.name);
+ if (TT.hdr.link_target) printf(" -> %s", TT.hdr.link_target);
+ xputc('\n');
+ 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);
+ else if (FLAG(to_command)) extract_to_command();
+ else extract_to_disk();
}
free(TT.hdr.name);
@@ -567,24 +566,37 @@ static void unpack_tar(void)
}
}
+// Add copy of filename to TT.incl or TT.excl, minus trailing \n and /
+static void trim_list(char **pline, long len)
+{
+ char *n = strdup(*pline);
+ int i = strlen(n);
+
+ dlist_add(TT.X ? &TT.excl : &TT.incl, n);
+ if (i && n[i-1]=='\n') i--;
+ while (i && n[i-1] == '/') i--;
+ n[i] = 0;
+}
+
void tar_main(void)
{
- struct arg_list *tmp;
char *s, **args = toys.optargs;
// When extracting to command
signal(SIGPIPE, SIG_IGN);
if (!geteuid()) toys.optflags |= FLAG_p;
+ if (TT.owner) TT.ouid = xgetuid(TT.owner);
+ if (TT.group) TT.ggid = xgetgid(TT.group);
- // Collect file list
- while (*args) add_to_list(&TT.inc, *args++);
- for (;TT.T; TT.T = TT.T->next) file_to_list(TT.T->arg, &TT.inc);
- for (;TT.X; TT.X = TT.X->next) file_to_list(TT.X->arg, &TT.exc);
+ // 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);
// Open archive file
if (FLAG(c)) {
- if (!TT.inc) error_exit("empty archive");
+ if (!TT.incl) error_exit("empty archive");
TT.fd = 1;
}
if (TT.f && strcmp(TT.f, "-"))
@@ -597,15 +609,6 @@ void tar_main(void)
// Are we reading?
if (FLAG(x)||FLAG(t)) {
-// TODO: autodtect
-
-// Try detecting .gz or .bz2 by looking for their magic.
-// if ((!memcmp(tar.name, "\x1f\x8b", 2) || !memcmp(tar.name, "BZh", 3))
-// && !lseek(TT.fd, -i, SEEK_CUR)) {
-// toys.optflags |= (*tar.name == 'B') ? FLAG_j : FLAG_z;
-// extract_stream(tar_hdl);
-// continue;
-// }
if (FLAG(j)||FLAG(z)) {
int pipefd[2] = {TT.fd, -1};
@@ -616,13 +619,18 @@ void tar_main(void)
}
unpack_tar();
- for (tmp = TT.inc; tmp; tmp = tmp->next)
- if (!filter(TT.exc, tmp->arg) && !filter(TT.pass, tmp->arg))
- error_msg("'%s' not in archive", tmp->arg);
+ if (TT.seen != TT.incl) {
+ if (!TT.seen) TT.seen = TT.incl;
+ while (TT.incl != TT.seen) {
+ error_msg("'%s' not in archive", TT.incl->data);
+ TT.incl = TT.incl->next;
+ }
+ }
// are we writing? (Don't have to test flag here one of 3 must be set)
} else {
-// TODO: autodetect
+ struct double_list *dl = TT.incl;
+
if (FLAG(j)||FLAG(z)) {
int pipefd[2] = {-1, TT.fd};
@@ -630,10 +638,9 @@ void tar_main(void)
close(TT.fd);
TT.fd = pipefd[0];
}
- for (tmp = TT.inc; tmp; tmp = tmp->next)
- dirtree_flagread(tmp->arg, FLAG(h)?DIRTREE_SYMFOLLOW:0, add_to_tar);
+ do dirtree_flagread(dl->data, FLAG(h)?DIRTREE_SYMFOLLOW:0, add_to_tar);
+ while (TT.incl != (dl = dl->next));
- memset(toybuf, 0, 1024);
writeall(TT.fd, toybuf, 1024);
}
}
diff --git a/toys/pending/vi.c b/toys/pending/vi.c
index bf8db841..6a1c2349 100644
--- a/toys/pending/vi.c
+++ b/toys/pending/vi.c
@@ -1,6 +1,7 @@
/* vi.c - You can't spell "evil" without "vi".
*
* Copyright 2015 Rob Landley <rob@landley.net>
+ * Copyright 2019 Jarno Mäkipää <jmakip87@gmail.com>
*
* See http://pubs.opengroup.org/onlinepubs/9699919799/utilities/vi.html
@@ -11,7 +12,6 @@ config VI
default n
help
usage: vi FILE
-
Visual text editor. Predates the existence of standardized cursor keys,
so the controls are weird and historical.
*/
@@ -20,29 +20,899 @@ config VI
#include "toys.h"
GLOBALS(
- struct linestack *ls;
- char *statline;
+ int cur_col;
+ int cur_row;
+ unsigned screen_height;
+ unsigned screen_width;
+ int vi_mode;
)
+/*
+ *
+ * TODO:
+ * BUGS: screen pos adjust does not cover "widelines"
+ * utf8 problems with some files. perhaps use lib utf8 functions instead
+ * append to EOL does not show input but works when ESC out
+ *
+ *
+ * REFACTOR: use dllist functions where possible.
+ * draw_page dont draw full page at time if nothing changed...
+ * ex callbacks
+ *
+ * FEATURE: ex: / ? % //atleast easy cases
+ * vi: x dw d$ d0
+ * vi: yw yy (y0 y$)
+ * vi+ex: gg G //line movements
+ * ex: r
+ * ex: !external programs
+ * ex: w filename //only writes to same file now
+ * big file support?
+ */
+
+
struct linestack_show {
struct linestack_show *next;
long top, left;
int x, width, y, height;
};
-// linestack, what to show, where to show it
-void linestack_show(struct linestack *ls, struct linestack_show *lss)
+static void draw_page();
+static void draw_char(char c, int x, int y, int highlight);
+//utf8 support
+static int utf8_dec(char key, char *utf8_scratch, int *sta_p) ;
+static int utf8_len(char *str);
+static int draw_rune(char *c, int x, int y, int highlight);
+
+
+static void cur_left();
+static void cur_right();
+static void cur_up();
+static void cur_down();
+static void check_cursor_bounds();
+static void adjust_screen_buffer();
+
+
+struct str_line {
+ int alloc_len;
+ int str_len;
+ char *str_data;
+};
+
+//lib dllist uses next and prev kinda opposite what im used to so I just
+//renamed both ends to up and down
+struct linelist {
+ struct linelist *up;//next
+ struct linelist *down;//prev
+ struct str_line *line;
+};
+//inserted line not yet pushed to buffer
+struct str_line *il;
+struct linelist *text; //file loaded into buffer
+struct linelist *scr_r;//current screen coord 0 row
+struct linelist *c_r;//cursor position row
+int modified;
+
+void dlist_insert_nomalloc(struct double_list **list, struct double_list *new)
{
- return;
+ if (*list) {
+ new->next = *list;
+ new->prev = (*list)->prev;
+ if ((*list)->prev) (*list)->prev->next = new;
+ (*list)->prev = new;
+ } else *list = new->next = new->prev = new;
}
-void vi_main(void)
+
+// Add an entry to the end of a doubly linked list
+struct double_list *dlist_insert(struct double_list **list, char *data)
{
- int i;
+ struct double_list *new = xmalloc(sizeof(struct double_list));
+ new->data = data;
+ dlist_insert_nomalloc(list, new);
- if (!(TT.ls = linestack_load(*toys.optargs)))
- TT.ls = xzalloc(sizeof(struct linestack));
+ return new;
+}
+void linelist_unload()
+{
- for (i=0; i<TT.ls->len; i++)
- printf("%.*s\n", (int)TT.ls->idx[i].len, (char *)TT.ls->idx[i].ptr);
+}
+
+void write_file(char *filename)
+{
+ struct linelist *lst = text;
+ FILE *fp = 0;
+ if (!filename)
+ filename = (char*)*toys.optargs;
+ fp = fopen(filename, "w");
+ if (!fp) return ;
+ while (lst) {
+ fprintf(fp, "%s\n", lst->line->str_data);
+ lst = lst->down;
+ }
+ fclose(fp);
+}
+
+int linelist_load(char *filename)
+{
+ struct linelist *lst = c_r;//cursor position or 0
+ FILE *fp = 0;
+ if (!filename)
+ filename = (char*)*toys.optargs;
+
+ fp = fopen(filename, "r");
+ if (!fp) return 0;
+
+ for (;;) {
+ char *line = xzalloc(80);
+ ssize_t alc = 80;
+ ssize_t len;
+ if ((len = getline(&line, (void *)&alc, fp)) == -1) {
+ if (errno == EINVAL || errno == ENOMEM) {
+ printf("error %d\n", errno);
+ }
+ free(line);
+ break;
+ }
+ lst = (struct linelist*)dlist_add((struct double_list**)&lst,
+ xzalloc(sizeof(struct str_line)));
+ lst->line->alloc_len = alc;
+ lst->line->str_len = len;
+ lst->line->str_data = line;
+
+ if (lst->line->str_data[len-1] == '\n') {
+ lst->line->str_data[len-1] = 0;
+ lst->line->str_len--;
+ }
+ if (text == 0) {
+ text = lst;
+ }
+
+ }
+ if (text) {
+ dlist_terminate(text->up);
+ }
+ fclose(fp);
+ return 1;
+
+}
+//TODO this is overly complicated refactor with lib dllist
+int ex_dd(int count)
+{
+ struct linelist *lst = c_r;
+ if (c_r == text && text == scr_r) {
+ if (!text->down && !text->up && text->line) {
+ text->line->str_len = 1;
+ sprintf(text->line->str_data, " ");
+ goto success_exit;
+ }
+ if (text->down) {
+ text = text->down;
+ text->up = 0;
+ c_r = text;
+ scr_r = text;
+ free(lst->line->str_data);
+ free(lst->line);
+ free(lst);
+ }
+ goto recursion_exit;
+ }
+ //TODO use lib dllist stuff
+ if (lst)
+ {
+ if (lst->down) {
+ lst->down->up = lst->up;
+ }
+ if (lst->up) {
+ lst->up->down = lst->down;
+ }
+ if (scr_r == c_r) {
+ scr_r = c_r->down ? c_r->down : c_r->up;
+ }
+ if (c_r->down)
+ c_r = c_r->down;
+ else {
+ c_r = c_r->up;
+ count = 1;
+ }
+ free(lst->line->str_data);
+ free(lst->line);
+ free(lst);
+ }
+
+recursion_exit:
+ count--;
+ //make this recursive
+ if (count)
+ return ex_dd(count);
+success_exit:
+ check_cursor_bounds();
+ adjust_screen_buffer();
+ return 1;
+}
+
+int ex_dw(int count)
+{
+ return 1;
+}
+
+int ex_deol(int count)
+{
+ return 1;
+}
+
+//does not work with utf8 yet
+int vi_x(int count)
+{
+ char *s;
+ int *l;
+ int *p;
+ 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)--;
+ }
+ count--;
+ return (count) ? vi_x(count) : 1;
+}
+
+//move commands does not behave correct way yet.
+//only jump to next space for now.
+int vi_movw(int count)
+{
+ if (!c_r)
+ return 0;
+ //could we call moveend first
+ while (c_r->line->str_data[TT.cur_col] > ' ')
+ TT.cur_col++;
+ while (c_r->line->str_data[TT.cur_col] <= ' ') {
+ TT.cur_col++;
+ if (!c_r->line->str_data[TT.cur_col]) {
+ //we could call j and g0
+ if (!c_r->down) return 0;
+ c_r = c_r->down;
+ TT.cur_col = 0;
+ }
+ }
+ count--;
+ if (count>1)
+ return vi_movw(count);
+
+ check_cursor_bounds();
+ adjust_screen_buffer();
+ return 1;
+}
+
+int vi_movb(int count)
+{
+ if (!c_r)
+ return 0;
+ if (!TT.cur_col) {
+ if (!c_r->up) return 0;
+ c_r = c_r->up;
+ TT.cur_col = (c_r->line->str_len) ? c_r->line->str_len-1 : 0;
+ goto exit_function;
+ }
+ if (TT.cur_col)
+ TT.cur_col--;
+ while (c_r->line->str_data[TT.cur_col] <= ' ') {
+ if (TT.cur_col) TT.cur_col--;
+ else goto exit_function;
+ }
+ while (c_r->line->str_data[TT.cur_col] > ' ') {
+ if (TT.cur_col)TT.cur_col--;
+ else goto exit_function;
+ }
+ TT.cur_col++;
+exit_function:
+ count--;
+ if (count>1)
+ return vi_movb(count);
+ check_cursor_bounds();
+ adjust_screen_buffer();
+ return 1;
+}
+
+int vi_move(int count)
+{
+ if (!c_r)
+ return 0;
+ if (TT.cur_col < c_r->line->str_len)
+ TT.cur_col++;
+ if (c_r->line->str_data[TT.cur_col] <= ' ' || count > 1)
+ vi_movw(count); //find next word;
+ while (c_r->line->str_data[TT.cur_col] > ' ')
+ TT.cur_col++;
+ if (TT.cur_col) TT.cur_col--;
+
+ check_cursor_bounds();
+ adjust_screen_buffer();
+ return 1;
+}
+
+void i_insert()
+{
+ char *t = xzalloc(c_r->line->alloc_len);
+ char *s = c_r->line->str_data;
+ int sel = c_r->line->str_len-TT.cur_col;
+ strncpy(t, &s[TT.cur_col], sel);
+ t[sel+1] = 0;
+ if (c_r->line->alloc_len < c_r->line->str_len+il->str_len+5) {
+ c_r->line->str_data = xrealloc(c_r->line->str_data,
+ c_r->line->alloc_len*2+il->alloc_len*2);
+
+ c_r->line->alloc_len = c_r->line->alloc_len*2+2*il->alloc_len;
+ memset(&c_r->line->str_data[c_r->line->str_len], 0,
+ c_r->line->alloc_len-c_r->line->str_len);
+
+ s = c_r->line->str_data;
+ }
+ strcpy(&s[TT.cur_col], il->str_data);
+ strcpy(&s[TT.cur_col+il->str_len], t);
+ TT.cur_col += il->str_len;
+ if (TT.cur_col) TT.cur_col--;
+ c_r->line->str_len += il->str_len;
+ free(t);
+
+}
+
+//new line at split pos;
+void i_split()
+{
+ struct str_line *l = xmalloc(sizeof(struct str_line));
+ int l_a = c_r->line->alloc_len;
+ int l_len = c_r->line->str_len-TT.cur_col;
+ l->str_data = xzalloc(l_a);
+ l->alloc_len = l_a;
+ l->str_len = l_len;
+ strncpy(l->str_data, &c_r->line->str_data[TT.cur_col], l_len);
+ l->str_data[l_len] = 0;
+ c_r->line->str_len -= l_len;
+ c_r->line->str_data[c_r->line->str_len] = 0;
+ c_r = (struct linelist*)dlist_insert((struct double_list**)&c_r, (char*)l);
+ c_r->line = l;
+ TT.cur_col = 0;
+ check_cursor_bounds();
+ adjust_screen_buffer();
+}
+
+struct vi_cmd_param {
+ const char *cmd;
+ int (*vi_cmd_ptr)(int);
+};
+
+struct vi_cmd_param vi_cmds[7] =
+{
+ {"dd", &ex_dd},
+ {"dw", &ex_dw},
+ {"d$", &ex_deol},
+ {"w", &vi_movw},
+ {"b", &vi_movb},
+ {"e", &vi_move},
+ {"x", &vi_x},
+};
+
+int run_vi_cmd(char *cmd)
+{
+ int val = 0;
+ char *cmd_e;
+ errno = 0;
+ int i = 0;
+ val = strtol(cmd, &cmd_e, 10);
+ if (errno || val == 0) {
+ val = 1;
+ }
+ else {
+ cmd = cmd_e;
+ }
+ for (; i<7; i++) {
+ if (strstr(cmd, vi_cmds[i].cmd)) {
+ return vi_cmds[i].vi_cmd_ptr(val);
+ }
+ }
+ return 0;
+
+}
+
+int search_str(char *s)
+{
+ 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-c_r->line->str_data;
+ }
+ else for (; !c;) {
+ lst = lst->down;
+ if (!lst) return 1;
+ c = strstr(&lst->line->str_data[TT.cur_col], s);
+ }
+ c_r = lst;
+ TT.cur_col = c-c_r->line->str_data;
+ return 0;
+}
+
+int run_ex_cmd(char *cmd)
+{
+ if (cmd[0] == '/') {
+ //search pattern
+ if (!search_str(&cmd[1]) ) {
+ check_cursor_bounds();
+ adjust_screen_buffer();
+ }
+ } else if (cmd[0] == '?') {
+
+ } else if (cmd[0] == ':') {
+ if (strstr(&cmd[1], "q!")) {
+ //exit_application;
+ return -1;
+ }
+ else if (strstr(&cmd[1], "wq")) {
+ write_file(0);
+ return -1;
+ }
+ else if (strstr(&cmd[1], "w")) {
+ write_file(0);
+ return 1;
+ }
+ }
+ return 0;
+
+}
+
+void vi_main(void)
+{
+ char keybuf[16];
+ char utf8_code[8];
+ int utf8_dec_p = 0;
+ int key = 0;
+ char vi_buf[16];
+ int vi_buf_pos = 0;
+ il = xzalloc(sizeof(struct str_line));
+ il->str_data = xzalloc(80);
+ il->alloc_len = 80;
+ keybuf[0] = 0;
+ memset(vi_buf, 0, 16);
+ memset(utf8_code, 0, 8);
+ linelist_load(0);
+ scr_r = text;
+ c_r = text;
+ TT.cur_row = 0;
+ TT.cur_col = 0;
+ TT.screen_width = 80;
+ TT.screen_height = 24;
+ TT.vi_mode = 1;
+ terminal_size(&TT.screen_width, &TT.screen_height);
+ TT.screen_height -= 2; //TODO this is hack fix visual alignment
+ set_terminal(0, 1, 0, 0);
+ //writes stdout into different xterm buffer so when we exit
+ //we dont get scroll log full of junk
+ tty_esc("?1049h");
+ tty_esc("H");
+ xflush();
+ draw_page();
+ while(1) {
+ key = scan_key(keybuf, -1);
+ printf("key %d\n", key);
+ switch (key) {
+ case -1:
+ case 3:
+ case 4:
+ goto cleanup_vi;
+ }
+ 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 ':':
+ TT.vi_mode = 0;
+ il->str_data[0]=key;
+ il->str_len++;
+ break;
+ case 'a':
+ TT.cur_col++;
+ case 'i':
+ TT.vi_mode = 2;
+ break;
+ case 27:
+ vi_buf[0] = 0;
+ vi_buf_pos = 0;
+ break;
+ default:
+ if (key > 0x20 && key < 0x7B) {
+ vi_buf[vi_buf_pos] = key;
+ vi_buf_pos++;
+ if (run_vi_cmd(vi_buf)) {
+ memset(vi_buf, 0, 16);
+ vi_buf_pos = 0;
+ }
+ else if (vi_buf_pos == 16) {
+ vi_buf_pos = 0;
+ }
+
+ }
+
+ break;
+ }
+ } else if (TT.vi_mode == 0) { //EX MODE
+ switch (key) {
+ case 27:
+ TT.vi_mode = 1;
+ il->str_len = 0;
+ memset(il->str_data, 0, il->alloc_len);
+ break;
+ case 0x7F:
+ case 0x08:
+ if (il->str_len) {
+ il->str_data[il->str_len] = 0;
+ if (il->str_len > 1) il->str_len--;
+ }
+ break;
+ case 0x0D:
+ if (run_ex_cmd(il->str_data) == -1)
+ goto cleanup_vi;
+ TT.vi_mode = 1;
+ il->str_len = 0;
+ memset(il->str_data, 0, il->alloc_len);
+ break;
+ default: //add chars to ex command until ENTER
+ if (key >= 0x20 && key < 0x7F) { //might be utf?
+ if (il->str_len == il->alloc_len) {
+ il->str_data = realloc(il->str_data, il->alloc_len*2);
+ il->alloc_len *= 2;
+ }
+ il->str_data[il->str_len] = key;
+ il->str_len++;
+ }
+ break;
+ }
+ } else if (TT.vi_mode == 2) {//INSERT MODE
+ switch (key) {
+ case 27:
+ i_insert();
+ TT.vi_mode = 1;
+ il->str_len = 0;
+ memset(il->str_data, 0, il->alloc_len);
+ break;
+ case 0x7F:
+ case 0x08:
+ if (il->str_len)
+ il->str_data[il->str_len--] = 0;
+ break;
+ case 0x09:
+ //TODO implement real tabs
+ il->str_data[il->str_len++] = ' ';
+ il->str_data[il->str_len++] = ' ';
+ break;
+
+ case 0x0D:
+ //insert newline
+ //
+ i_insert();
+ il->str_len = 0;
+ memset(il->str_data, 0, il->alloc_len);
+ i_split();
+ break;
+ default:
+ if (key >= 0x20 && utf8_dec(key, utf8_code, &utf8_dec_p)) {
+ if (il->str_len+utf8_dec_p+1 >= il->alloc_len) {
+ il->str_data = realloc(il->str_data, il->alloc_len*2);
+ il->alloc_len *= 2;
+ }
+ strcpy(il->str_data+il->str_len, utf8_code);
+ il->str_len += utf8_dec_p;
+ utf8_dec_p = 0;
+ *utf8_code = 0;
+
+ }
+ break;
+ }
+ }
+
+ draw_page();
+
+ }
+cleanup_vi:
+ tty_reset();
+ tty_esc("?1049l");
+}
+
+static void draw_page()
+{
+ unsigned y = 0;
+ int cy_scr = 0;
+ int cx_scr = 0;
+ int utf_l = 0;
+ struct linelist *scr_buf= scr_r;
+ //clear screen
+ tty_esc("2J");
+ tty_esc("H");
+
+
+ tty_jump(0, 0);
+ for (; y < TT.screen_height; ) {
+ if (scr_buf && scr_buf->line->str_data && scr_buf->line->str_len) {
+ int p = 0;
+ for (; p < scr_buf->line->str_len; y++) {
+ unsigned x = 0;
+ for (; x < TT.screen_width; x++) {
+ if (p < scr_buf->line->str_len) {
+ int hi = 0;
+ if (scr_buf == c_r && p == TT.cur_col) {
+ if (TT.vi_mode == 2) {
+ tty_jump(x, y);
+
+ tty_esc("1m"); //bold
+ printf("%s", il->str_data);
+ x += il->str_len;
+ tty_esc("0m");
+ }
+ cy_scr = y;
+ cx_scr = x;
+ }
+ utf_l = draw_rune(&scr_buf->line->str_data[p], x, y, hi);
+ if (!utf_l)
+ break;
+ p += utf_l;
+ if (utf_l > 2) x++;//traditional chinese is somehow 2 width in tty???
+ }
+ else {
+ if (scr_buf == c_r && p == TT.cur_col) {
+ if (TT.vi_mode == 2) {
+ tty_jump(x, y);
+
+ tty_esc("1m"); //bold
+ printf("%s", il->str_data);
+ x += il->str_len;
+ tty_esc("0m");
+ }
+ cy_scr = y;
+ cx_scr = x;
+ }
+ break;
+ }
+ }
+ printf("\r\n");
+ }
+ }
+ else {
+ if (scr_buf == c_r){
+ cy_scr = y;
+ cx_scr = 0;
+ if (TT.vi_mode == 2) {
+ tty_jump(0, y);
+ tty_esc("1m"); //bold
+ printf("%s", il->str_data);
+ cx_scr += il->str_len;
+ tty_esc("0m");
+ } else draw_char(' ', 0, y, 1);
+ }
+ y++;
+ }
+ printf("\n");
+ if (scr_buf->down)
+ scr_buf=scr_buf->down;
+ else break;
+ }
+ for (; y < TT.screen_height; y++) {
+ printf("\n");
+ }
+
+ tty_jump(0, TT.screen_height);
+ switch (TT.vi_mode) {
+ case 0:
+ tty_esc("30;44m");
+ printf("COMMAND|");
+ break;
+ case 1:
+ tty_esc("30;42m");
+ printf("NORMAL|");
+ break;
+ case 2:
+ tty_esc("30;41m");
+ printf("INSERT|");
+ break;
+
+ }
+ //DEBUG
+ tty_esc("47m");
+ tty_esc("30m");
+ utf_l = utf8_len(&c_r->line->str_data[TT.cur_col]);
+ if (utf_l) {
+ char t[5] = {0, 0, 0, 0, 0};
+ strncpy(t, &c_r->line->str_data[TT.cur_col], utf_l);
+ printf("utf: %d %s", utf_l, t);
+ }
+ printf("| %d, %d\n", cx_scr, cy_scr); //screen coord
+
+ tty_jump(TT.screen_width-12, TT.screen_height);
+ printf("| %d, %d\n", TT.cur_row, TT.cur_col);
+ tty_esc("37m");
+ tty_esc("40m");
+ if (!TT.vi_mode) {
+ tty_esc("1m");
+ tty_jump(0, TT.screen_height+1);
+ printf("%s", il->str_data);
+ } else tty_jump(cx_scr, cy_scr);
+ xflush();
+
+}
+
+static void draw_char(char c, int x, int y, int highlight)
+{
+ tty_jump(x, y);
+ if (highlight) {
+ tty_esc("30m"); //foreground black
+ tty_esc("47m"); //background white
+ }
+ printf("%c", c);
+}
+
+//utf rune draw
+//printf and useless copy could be replaced by direct write() to stdout
+static int draw_rune(char *c, int x, int y, int highlight)
+{
+ int l = utf8_len(c);
+ char t[5] = {0, 0, 0, 0, 0};
+ if (!l) return 0;
+ tty_jump(x, y);
+ tty_esc("0m");
+ if (highlight) {
+ tty_esc("30m"); //foreground black
+ tty_esc("47m"); //background white
+ }
+ strncpy(t, c, 5);
+ printf("%s", t);
+ tty_esc("0m");
+ return l;
+}
+
+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;
+ }
+}
+
+static void adjust_screen_buffer()
+{
+ //search cursor and screen TODO move this perhaps
+ struct linelist *t = text;
+ int c = -1;
+ int s = -1;
+ int i = 0;
+ for (;;) {
+ i++;
+ if (t == c_r)
+ c = i;
+ if (t == scr_r)
+ s = i;
+ t = t->down;
+ if ( ((c != -1) && (s != -1)) || t == 0)
+ break;
+ }
+ if (c <= s) {
+ scr_r = c_r;
+ }
+ else if ( c > s ) {
+ //should count multiline long strings!
+ int distance = c - s + 1;
+ //TODO instead iterate scr_r up and check strlen%screen_width
+ //for each iteration
+ if (distance >= (int)TT.screen_height) {
+ int adj = distance - TT.screen_height;
+ while(adj--) {
+ scr_r = scr_r->down;
+ }
+ }
+ }
+ TT.cur_row = c;
+
+}
+
+//return 0 if not ASCII nor UTF-8
+//this is not fully tested
+//naive implementation with branches
+//there is better branchless lookup table versions out there
+//1 0xxxxxxx
+//2 110xxxxx 10xxxxxx
+//3 1110xxxx 10xxxxxx 10xxxxxx
+//4 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
+static int utf8_len(char *str)
+{
+ int len = 0;
+ int i = 0;
+ uint8_t *c = (uint8_t*)str;
+ if (!c || !(*c)) return 0;
+ if (*c < 0x7F) return 1;
+ if ((*c & 0xE0) == 0xc0) len = 2;
+ else if ((*c & 0xF0) == 0xE0 ) len = 3;
+ else if ((*c & 0xF8) == 0xF0 ) len = 4;
+ else return 0;
+ c++;
+ for (i = len-1; i > 0; i--) {
+ if ((*c++ & 0xc0) != 0x80) return 0;
+ }
+ return len;
+}
+
+static int utf8_dec(char key, char *utf8_scratch, int *sta_p)
+{
+ int len = 0;
+ char *c = utf8_scratch;
+ c[*sta_p] = key;
+ if (!(*sta_p)) *c = key;
+ if (*c < 0x7F) { *sta_p = 1; return 1; }
+ if ((*c & 0xE0) == 0xc0) len = 2;
+ else if ((*c & 0xF0) == 0xE0 ) len = 3;
+ else if ((*c & 0xF8) == 0xF0 ) len = 4;
+ else {*sta_p = 0; return 0; }
+
+ (*sta_p)++;
+
+ if (*sta_p == 1) return 0;
+ if ((c[*sta_p-1] & 0xc0) != 0x80) {*sta_p = 0; return 0; }
+
+ if (*sta_p == len) { c[(*sta_p)] = 0; return 1; }
+
+ return 0;
+}
+
+static void cur_left()
+{
+ if (!TT.cur_col) return;
+ TT.cur_col--;
+
+ if (!utf8_len(&c_r->line->str_data[TT.cur_col])) cur_left();
+}
+
+static void cur_right()
+{
+ 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();
+}
+
+static void cur_up()
+{
+ if (c_r->up != 0)
+ c_r = c_r->up;
+
+ if (!utf8_len(&c_r->line->str_data[TT.cur_col])) cur_left();
+ check_cursor_bounds();
+ adjust_screen_buffer();
+}
+
+static void cur_down()
+{
+ if (c_r->down != 0)
+ c_r = c_r->down;
+
+ if (!utf8_len(&c_r->line->str_data[TT.cur_col])) cur_left();
+ check_cursor_bounds();
+ adjust_screen_buffer();
}
diff --git a/toys/posix/date.c b/toys/posix/date.c
index 43b558a5..685ac8bf 100644
--- a/toys/posix/date.c
+++ b/toys/posix/date.c
@@ -59,135 +59,29 @@ GLOBALS(
unsigned nano;
)
-static void check_range(int a, int low, int high)
-{
- if (a<low) error_exit("%d<%d", a, low);
- if (a>high) error_exit("%d>%d", a, high);
-}
-
-static void check_tm(struct tm *tm)
-{
- check_range(tm->tm_sec, 0, 60);
- check_range(tm->tm_min, 0, 59);
- check_range(tm->tm_hour, 0, 23);
- check_range(tm->tm_mday, 1, 31);
- check_range(tm->tm_mon, 0, 11);
-}
-
-// Returns 0 success, nonzero for error.
-static int parse_formats(char *str, time_t *t)
-{
- struct tm tm;
- time_t now;
- int len = 0, i;
- char *formats[] = {
- // Formats with years must come first.
- "%Y-%m-%d %H:%M:%S", "%Y-%m-%d %H:%M", "%Y-%m-%d",
- "%H:%M:%S", "%H:%M"
- };
-
- // Parse @UNIXTIME[.FRACTION]
- if (*str == '@') {
- long long ll;
-
- // Collect seconds and nanoseconds.
- // &ll is not just t because we can't guarantee time_t is 64 bit (yet).
- sscanf(str, "@%lld%n", &ll, &len);
- if (str[len]=='.') {
- str += len+1;
- for (len = 0; len<9; len++) {
- TT.nano *= 10;
- if (isdigit(str[len])) TT.nano += str[len]-'0';
- }
- }
- if (str[len]) return 1;
- *t = ll;
- return 0;
- }
-
- // Is it one of the fancy formats?
- for (i = 0; i<ARRAY_LEN(formats); i++) {
- char *p;
-
- now = time(0);
- localtime_r(&now, &tm);
- tm.tm_hour = tm.tm_min = tm.tm_sec = 0;
- tm.tm_isdst = -1;
- if ((p = strptime(str,formats[i],&tm)) && !*p) {
- if ((*t = mktime(&tm)) != -1) return 0;
- }
- }
-
- // Posix format?
- sscanf(str, "%2u%2u%2u%2u%n", &tm.tm_mon, &tm.tm_mday, &tm.tm_hour,
- &tm.tm_min, &len);
- if (len != 8) return 1;
- str += len;
- tm.tm_mon--;
-
- // If year specified, overwrite one we fetched earlier.
- if (*str && *str != '.') {
- unsigned year;
-
- len = 0;
- sscanf(str, "%u%n", &year, &len);
- if (len == 4) tm.tm_year = year - 1900;
- else if (len != 2) return 1;
- str += len;
-
- // 2 digit years, next 50 years are "future", last 50 years are "past".
- // A "future" date in past is a century ahead.
- // A non-future date in the future is a century behind.
- if (len == 2) {
- unsigned r1 = tm.tm_year % 100, r2 = (tm.tm_year + 50) % 100,
- century = tm.tm_year - r1;
-
- if ((r1 < r2) ? (r1 < year && year < r2) : (year < r1 || year > r2)) {
- if (year < r1) year += 100;
- } else if (year > r1) year -= 100;
- tm.tm_year = year + century;
- }
- }
- // Fractional part?
- if (*str == '.') {
- len = 0;
- sscanf(str, ".%u%n", &tm.tm_sec, &len);
- str += len;
- } else tm.tm_sec = 0;
-
- // Does that look like a valid date?
- check_tm(&tm);
- if ((*t = mktime(&tm)) == -1) return 1;
-
- // Shouldn't be any trailing garbage.
- return *str;
-}
-
// Handles any leading `TZ="blah" ` in the input string.
-static int parse_date(char *str, time_t *t)
+static void parse_date(char *str, time_t *t)
{
- char *new_tz = NULL, *old_tz;
- int result;
+ char *new_tz = NULL, *old_tz, *s = str;
if (!strncmp(str, "TZ=\"", 4)) {
// Extract the time zone and skip any whitespace.
new_tz = str+4;
- str = strchr(new_tz, '"');
- if (!str) return 1;
- *str++ = '\0';
- while (isspace(*str)) ++str;
+ if (!(str = strchr(new_tz, '"'))) xvali_date(0, s);
+ *str++ = 0;
+ while (isspace(*str)) str++;
// Switch $TZ.
old_tz = getenv("TZ");
setenv("TZ", new_tz, 1);
tzset();
}
- result = parse_formats(str, t);
+ time(t);
+ xparsedate(str, t, &TT.nano);
if (new_tz) {
if (old_tz) setenv("TZ", old_tz, 1);
else unsetenv("TZ");
}
- return result;
}
// Print strftime plus %N escape(s). note: modifies fmt for %N
@@ -236,10 +130,8 @@ void date_main(void)
struct tm tm = {};
char *s = strptime(TT.d, TT.D+(*TT.D=='+'), &tm);
- if (!s || *s) goto bad_showdate;
- check_tm(&tm);
- if ((t = mktime(&tm)) == -1) goto bad_showdate;
- } else if (parse_date(TT.d, &t)) goto bad_showdate;
+ t = (s && *s) ? xvali_date(&tm, s) : xvali_date(0, TT.d);
+ } else parse_date(TT.d, &t);
} else {
struct timespec ts;
struct stat st;
@@ -264,7 +156,7 @@ void date_main(void)
} else if (setdate) {
struct timeval tv;
- if (parse_date(setdate, &t)) goto bad_setdate;
+ parse_date(setdate, &t);
tv.tv_sec = t;
tv.tv_usec = TT.nano/1000;
if (settimeofday(&tv, NULL) < 0) perror_msg("cannot set date");
@@ -279,9 +171,4 @@ void date_main(void)
}
return;
-
-bad_showdate:
- setdate = TT.d;
-bad_setdate:
- error_exit("bad date '%s'", setdate);
}
diff --git a/toys/posix/find.c b/toys/posix/find.c
index 1c35155b..5cefbf15 100644
--- a/toys/posix/find.c
+++ b/toys/posix/find.c
@@ -530,7 +530,7 @@ static int do_find(struct dirtree *new)
aa->plus = 1;
toys.exitval |= flush_exec(new, aa);
}
- } else test = flush_exec(new, aa);
+ } else test = !flush_exec(new, aa);
}
// Argument consumed, skip the check.
diff --git a/toys/posix/rm.c b/toys/posix/rm.c
index 608e1ca1..6c64e13f 100644
--- a/toys/posix/rm.c
+++ b/toys/posix/rm.c
@@ -4,19 +4,20 @@
*
* See http://pubs.opengroup.org/onlinepubs/9699919799/utilities/rm.html
-USE_RM(NEWTOY(rm, "fiRr[-fi]", TOYFLAG_BIN))
+USE_RM(NEWTOY(rm, "fiRrv[-fi]", TOYFLAG_BIN))
config RM
bool "rm"
default y
help
- usage: rm [-fiRr] FILE...
+ usage: rm [-fiRrv] FILE...
Remove each argument from the filesystem.
-f Force: remove without confirmation, no error if it doesn't exist
-i Interactive: prompt for confirmation
-rR Recursive: remove directory contents
+ -v Verbose
*/
#define FOR_rm
@@ -68,7 +69,13 @@ static int do_rm(struct dirtree *try)
}
skip:
- if (unlinkat(fd, try->name, using)) {
+ if (!unlinkat(fd, try->name, using)) {
+ if (flags & FLAG_v) {
+ char *s = dirtree_path(try, 0);
+ printf("%s%s '%s'\n", toys.which->name, dir ? "dir" : "", s);
+ free(s);
+ }
+ } else {
if (!dir || try->symlink != (char *)2) perror_msg_raw(try->name);
nodelete:
if (try->parent) try->parent->symlink = (char *)2;