diff options
author | Elliott Hughes <enh@google.com> | 2019-04-01 16:12:40 -0700 |
---|---|---|
committer | android-build-merger <android-build-merger@google.com> | 2019-04-01 16:12:40 -0700 |
commit | 03df06d931d43332eb470038be0d1f782cb688b9 (patch) | |
tree | ecc5b2a8c07db64ef129860526be8ad6035fae46 | |
parent | 1722118b0c6dbca261dcfda0fa8e1076c2719911 (diff) | |
parent | 75180c5c70f571aaa2b2c9e2e383ac98095b740d (diff) | |
download | toybox-03df06d931d43332eb470038be0d1f782cb688b9.tar.gz |
Merge remote-tracking branch 'toybox/master' into HEAD am: 4bb9634f50
am: 75180c5c70
Change-Id: I57015d4898f1e4571f9f28e1a13ee66099268552
-rw-r--r-- | generated/flags.h | 16 | ||||
-rw-r--r-- | generated/globals.h | 13 | ||||
-rw-r--r-- | generated/newtoys.h | 2 | ||||
-rw-r--r-- | scripts/runtest.sh | 14 | ||||
-rw-r--r-- | tests/bc.test | 3 | ||||
-rw-r--r-- | tests/tar.test | 61 | ||||
-rw-r--r-- | toys/pending/tar.c | 213 | ||||
-rw-r--r-- | toys/pending/vi.c | 352 |
8 files changed, 444 insertions, 230 deletions
diff --git a/generated/flags.h b/generated/flags.h index 0e74cc9e..9f387ef3 100644 --- a/generated/flags.h +++ b/generated/flags.h @@ -2742,9 +2742,9 @@ #undef FLAG_f #endif -// 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] +// tar &(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] &(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 "&(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]" +#define OPTSTR_tar "&(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 @@ -2767,6 +2767,7 @@ #undef FLAG_to_command #undef FLAG_owner #undef FLAG_group +#undef FLAG_mtime #undef FLAG_exclude #undef FLAG_overwrite #undef FLAG_no_same_permissions @@ -5662,11 +5663,12 @@ #define FLAG_to_command (1<<16) #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) +#define FLAG_mtime (1<<19) +#define FLAG_exclude (1<<20) +#define FLAG_overwrite (1<<21) +#define FLAG_no_same_permissions (1<<22) +#define FLAG_numeric_owner (1<<23) +#define FLAG_no_recursion (1<<24) #endif #ifdef FOR_taskset diff --git a/generated/globals.h b/generated/globals.h index d3d76150..188d2b67 100644 --- a/generated/globals.h +++ b/generated/globals.h @@ -805,14 +805,21 @@ struct syslogd_data { struct tar_data { char *f, *C; struct arg_list *T, *X; - char *to_command, *owner, *group; + char *to_command, *owner, *group, *mtime; struct arg_list *exclude; struct double_list *incl, *excl, *seen; struct string_list *dirs; - void *inodes; char *cwd; - int fd, ouid, ggid; + int fd, ouid, ggid, hlc, warn; + time_t mtt; + + // hardlinks seen so far (hlc many) + struct { + char *arg; + ino_t ino; + dev_t dev; + } *hlx; // Parsed information about a tar header. struct { diff --git a/generated/newtoys.h b/generated/newtoys.h index 591b7233..e7b09a4f 100644 --- a/generated/newtoys.h +++ b/generated/newtoys.h @@ -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)*(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, "&(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 d08657cb..ab10bf44 100644 --- a/scripts/runtest.sh +++ b/scripts/runtest.sh @@ -66,6 +66,17 @@ optional() SKIP=1 } +skipnot() +{ + if [ -z "$VERBOSE" ] + then + eval "$@" 2>/dev/null + else + eval "$@" + fi + [ $? -eq 0 ] || SKIPNOT=1 +} + wrong_args() { if [ $# -ne 5 ] @@ -86,9 +97,10 @@ testing() [ -n "$DEBUG" ] && set -x - if [ -n "$SKIP" ] || ( [ -n "$SKIP_HOST" ] && [ -n "$TEST_HOST" ]) + if [ -n "$SKIP" -o -n "$SKIP_HOST" -a -n "$TEST_HOST" -o -n "$SKIPNOT" ] then [ ! -z "$VERBOSE" ] && echo "$SHOWSKIP: $NAME" + unset SKIPNOT return 0 fi diff --git a/tests/bc.test b/tests/bc.test index 130e049f..65969b79 100644 --- a/tests/bc.test +++ b/tests/bc.test @@ -2,7 +2,7 @@ [ -f testing.sh ] && . testing.sh -#testing "name" "command" "result" "infile" "stdin" +#testcmd "name "args" "result" "infile" "stdin" BDIR="$FILES/bc" TESTDIR="./" @@ -37,3 +37,4 @@ run_bc_test misc run_bc_test misc1 run_bc_test misc2 +testcmd "stdin" "" "2\n" "" "1+1\n" diff --git a/tests/tar.test b/tests/tar.test index 31aee386..4936320c 100644 --- a/tests/tar.test +++ b/tests/tar.test @@ -4,11 +4,56 @@ #testing "name" "command" "result" "infile" "stdin" -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" "" "" +# assumes umask 0002 + +LONG=abcdefghijklmnopqrstuvwxyz0123456789 +LONG=$LONG$LONG$LONG$LONG$LONG$LONG$LONG$LONG$LONG$LONG +OLDTZ="$TZ" +TZ=utc +OLDUMASK=$(umask) +umask 0002 + +# Reproducible tarballs: override ownership and timestamp. Also amount of +# trailing NUL padding varies (1024 bytes is minimum, gnu/dammit does more) +# so look at first 3 512-byte frames when analyzing header content. + +TAR='tar c --owner root --group root --mtime @1234567890' +SUM='head -c $((3*512)) | sha1sum | sed "s/ .*//"' +[ -n "$TARHD" ] && SUM="tee >(hd >&2) | $SUM" +SPC='sed "s/[ \t][ \t]*/ /g"' + +touch file +testing "create file" "$TAR file | $SUM" \ + "fecaecba936e604bb115627a6ef4db7c7a3a8f81\n" "" "" + +mkdir dir +testing "create dir" "$TAR dir | $SUM" \ + "05739c423d7d4a7f12b3dbb7c94149acb2bb4f8d\n" "" "" + +# note: does _not_ include dir entry in archive, just file +touch dir/file +testing "create dir and file" "$TAR dir/file | $SUM" \ + "2d7b96c7025987215f5a41f10eaa84311160afdb\n" "" "" + +# Tests recursion without worrying about content order +testing "create dir/file 2" "$TAR dir | $SUM" \ + "0bcc8005a3e07eb63c9b735267aecc5b774795d7\n" "" "" + +# / and .. only stripped from name, not symlink target. +ln -s ../name.././.. dir/link +testing "create symlink" "$TAR dir/link | $SUM" \ + "7324cafbd9aeec5036b6efc54d741f11528aeb10\n" "" "" + +# Also two explicit targets +ln dir/file dir/hardlink +testing "create hardlink" "$TAR dir/file dir/hardlink | $SUM" \ + "c5383651f8c03ec0fe15e8a9e28a4e8e5273990d\n" "" "" + +ln dir/link dir/hlink +testing "create hardlink to symlink" "$TAR dir/link dir/hlink | $SUM" \ + "3bc16f8fb6fc8b05f691da8caf989a70ee99284a\n" "" "" + +skipnot mkfifo dir/fifo +testing "create dir/fifo" "$TAR dir/fifo | $SUM" \ + "bd1365db6e8ead4c813333f9666994c1899924d9\n" "" "" + diff --git a/toys/pending/tar.c b/toys/pending/tar.c index 8fb047bf..77c03540 100644 --- a/toys/pending/tar.c +++ b/toys/pending/tar.c @@ -18,7 +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)*(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, "&(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" @@ -43,14 +43,21 @@ config TAR GLOBALS( char *f, *C; struct arg_list *T, *X; - char *to_command, *owner, *group; + char *to_command, *owner, *group, *mtime; struct arg_list *exclude; struct double_list *incl, *excl, *seen; struct string_list *dirs; - void *inodes; char *cwd; - int fd, ouid, ggid; + int fd, ouid, ggid, hlc, warn; + time_t mtt; + + // hardlinks seen so far (hlc many) + struct { + char *arg; + ino_t ino; + dev_t dev; + } *hlx; // Parsed information about a tar header. struct { @@ -82,7 +89,7 @@ static void itoo(char *str, int len, unsigned long long val) } #define ITOO(x, y) itoo(x, sizeof(x), y) -//convert octal (or base-256) to int +// convert octal (or base-256) to int static unsigned long long otoi(char *str, unsigned len) { unsigned long long val = 0; @@ -97,33 +104,6 @@ static unsigned long long otoi(char *str, unsigned len) return val; } - -struct inode_list { - struct inode_list *next; - char *arg; - ino_t ino; - dev_t dev; -}; - -static struct inode_list *seen_inode(void **list, struct stat *st, char *name) -{ - if (!S_ISDIR(st->st_mode) && st->st_nlink > 1) { - struct inode_list *new; - - for (new = *list; new; new = new->next) - if(new->ino == st->st_ino && new->dev == st->st_dev) - return new; - - new = xzalloc(sizeof(*new)); - new->ino = st->st_ino; - new->dev = st->st_dev; - new->arg = xstrdup(name); - new->next = *list; - *list = new; - } - return 0; -} - // Calculate packet checksum, with cksum field treated as 8 spaces static unsigned cksum(void *data) { @@ -176,9 +156,10 @@ static void skippy(long long len) if (lskip(TT.fd, len)) perror_exit("EOF"); } -// actually void **, but automatic typecasting doesn't work with void ** :( +// allocate and read data from TT.fd static void alloread(void *buf, int len) { + // actually void **, but automatic typecasting doesn't work with void ** :( void **b = buf; free(*b); @@ -192,32 +173,39 @@ static void add_file(char **nam, struct stat *st) struct tar_hdr hdr; struct passwd *pw = pw; struct group *gr = gr; - struct inode_list *node = node; int i, fd =-1; char *c, *p, *name = *nam, *lnk, *hname; - static int warn = 1; + // exclusion defaults to --no-anchored and --wildcards-match-slash for (p = name; *p; p++) 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); + lnk = xmprintf("%s/", name); free(name); *nam = name = lnk; } - hname = name; - //remove leading '/' or relative path '../' component - if (*hname == '/') hname++; + + // remove leading / and any .. entries from saved name + for (hname = name; *hname == '/'; hname++); + for (c = hname;;) { + if (!(c = strstr(c, ".."))) break; + if (c == hname || c[-1] == '/') { + if (!c[2]) return; + if (c[2]=='/') c = hname = c+3; + } else c+= 2; + } if (!*hname) return; - while ((c = strstr(hname, "../"))) hname = c + 3; - if (warn && hname != name) { + + if (TT.warn && hname != name) { fprintf(stderr, "removing leading '%.*s' from member names\n", (int)(hname-name), name); - warn = 0; + TT.warn = 0; } if (TT.owner) st->st_uid = TT.ouid; if (TT.group) st->st_gid = TT.ggid; + if (TT.mtime) st->st_mtime = TT.mtt; memset(&hdr, 0, sizeof(hdr)); strncpy(hdr.name, hname, sizeof(hdr.name)); @@ -227,11 +215,39 @@ static void add_file(char **nam, struct stat *st) 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))) { - hdr.type = '1'+i; - if (!(lnk = i ? xreadlink(name) : node->arg)) return perror_msg("readlink"); + // Hard link or symlink? i=0 neither, i=1 hardlink, i=2 symlink + + // Are there hardlinks to a non-directory entry? + if (st->st_nlink>1 && !S_ISDIR(st->st_mode)) { + // Have we seen this dev&ino before? + for (i = 0; i<TT.hlc; i++) { + if (st->st_ino == TT.hlx[i].ino && st->st_dev == TT.hlx[i].dev) + break; + } + if (i != TT.hlc) { + lnk = TT.hlx[i].arg; + i = 1; + } else { + // first time we've seen it, store as normal file. + if (!(TT.hlc&255)) TT.hlx = xrealloc(TT.hlx, TT.hlc+256); + TT.hlx[TT.hlc].arg = xstrdup(hname); + TT.hlx[TT.hlc].ino = st->st_ino; + TT.hlx[TT.hlc].dev = st->st_dev; + TT.hlc++; + i = 0; + } + } else i = 0; + + // !i because hardlink to a symlink is a thing. + if (!i && S_ISLNK(st->st_mode)) { + i = 2; + lnk = xreadlink(name); + } + + // Handle file types + if (i) { + hdr.type = '0'+i; + if (i==2 && !(lnk = xreadlink(name))) return perror_msg("readlink"); if (strlen(lnk) > sizeof(hdr.link)) write_longname(lnk, 'K'); strncpy(hdr.link, lnk, sizeof(hdr.link)); if (i) free(lnk); @@ -249,28 +265,22 @@ static void add_file(char **nam, struct stat *st) if (strlen(hname) > sizeof(hdr.name)) write_longname(hname, 'L'); strcpy(hdr.magic, "ustar "); 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); + if (TT.owner || (pw = bufgetpwuid(st->st_uid))) + strncpy(hdr.uname, TT.owner ? TT.owner : pw->pw_name, sizeof(hdr.uname)); + if (TT.group || (gr = bufgetgrgid(st->st_gid))) + strncpy(hdr.gname, TT.group ? TT.group : gr->gr_name, sizeof(hdr.gname)); } 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); + if (FLAG(v)) printf("%s\n", hname); - //write actual data to archive + // Write header and data to archive + xwrite(TT.fd, &hdr, 512); if (hdr.type != '0') return; //nothing to write - if ((fd = open(name, O_RDONLY)) < 0) { - perror_msg("can't open '%s'", name); - return; - } + if ((fd = open(name, O_RDONLY)) < 0) + return perror_msg("can't open '%s'", name); xsendfile_pad(fd, TT.fd, st->st_size); if (st->st_size%512) writeall(TT.fd, toybuf, (512-(st->st_size%512))); close(fd); @@ -315,7 +325,7 @@ static void extract_to_command(void) setenv("TAR_FILENAME", TT.hdr.name, 1); setenv("TAR_UNAME", TT.hdr.uname, 1); setenv("TAR_GNAME", TT.hdr.gname, 1); - sprintf(buf, "%0o", (int)TT.hdr.mtime); + sprintf(buf, "%0llo", (long long)TT.hdr.mtime); setenv("TAR_MTIME", buf, 1); sprintf(buf, "%0o", TT.hdr.uid); setenv("TAR_UID", buf, 1); @@ -336,7 +346,6 @@ static void extract_to_command(void) } } - // Do pending directory utimes(), NULL to flush all. static int dirflush(char *name) { @@ -345,8 +354,8 @@ static int dirflush(char *name) // Barf if name not in TT.cwd if (name) { ss = s = xabspath(name, -1); - if (TT.cwd && (!strstart(&ss, TT.cwd) || (*ss && *ss!='/'))) { - error_msg("'%s' not under '%s'", ss, TT.cwd); + if (TT.cwd[1] && (!strstart(&ss, TT.cwd) || *ss!='/')) { + error_msg("'%s' not under '%s'", name, TT.cwd); free(s); return 1; @@ -359,10 +368,15 @@ static int dirflush(char *name) 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", *(long long *)TT.dirs->str, TT.dirs->str+sizeof(long long)); + + if (utimes(TT.dirs->str+sizeof(long long), times)) + perror_msg("utimes %lld %s", ll, + TT.dirs->str+sizeof(long long)); free(llist_pop(&TT.dirs)); } + free(s); // name was under TT.cwd return 0; @@ -415,16 +429,16 @@ static void extract_to_disk(void) //set ownership..., --no-same-owner, --numeric-owner int u = TT.hdr.uid, g = TT.hdr.gid; - if (TT.owner) u = TT.ouid; + if (TT.owner) TT.hdr.uid = TT.ouid; else if (!FLAG(numeric_owner)) { struct passwd *pw = getpwnam(TT.hdr.uname); - if (pw && (TT.owner || !FLAG(numeric_owner))) u = pw->pw_uid; + if (pw && (TT.owner || !FLAG(numeric_owner))) TT.hdr.uid = pw->pw_uid; } - if (TT.group) g = TT.ggid; + if (TT.group) TT.hdr.gid = TT.ggid; else if (!FLAG(numeric_owner)) { struct group *gr = getgrnam(TT.hdr.gname); - if (gr) g = gr->gr_gid; + if (gr) TT.hdr.gid = gr->gr_gid; } if (lchown(name, u, g)) perror_msg("chown %d:%d '%s'", u, g, name);; @@ -458,6 +472,7 @@ static void unpack_tar(void) struct double_list *walk, *delete; struct tar_hdr tar; int i, and = 0; + unsigned maj, min; char *s; for (;;) { @@ -498,9 +513,8 @@ static void unpack_tar(void) // Posix extended record "LEN NAME=VALUE\n" format alloread(&buf, TT.hdr.size); for (p = buf; (p-buf)<TT.hdr.size; p += len) { - if ((i = sscanf(p, "%u path=%n", &len, &n))<1 || len<4 || - len>TT.hdr.size) - { + i = sscanf(p, "%u path=%n", &len, &n); + if (i<1 || len<4 || len>TT.hdr.size) { error_msg("bad header"); break; } @@ -524,11 +538,25 @@ static void unpack_tar(void) TT.hdr.uid = otoi(tar.uid, sizeof(tar.uid)); TT.hdr.gid = otoi(tar.gid, sizeof(tar.gid)); TT.hdr.mtime = otoi(tar.mtime, sizeof(tar.mtime)); - TT.hdr.device = dev_makedev(otoi(tar.major, sizeof(tar.major)), - otoi(tar.minor, sizeof(tar.minor))); + maj = otoi(tar.major, sizeof(tar.major)); + min = otoi(tar.minor, sizeof(tar.minor)); + TT.hdr.device = dev_makedev(maj, min); + + 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.owner) TT.hdr.uid = TT.ouid; + else if (!FLAG(numeric_owner)) { + struct passwd *pw = getpwnam(TT.hdr.uname); + if (pw && (TT.owner || !FLAG(numeric_owner))) TT.hdr.uid = pw->pw_uid; + } + + if (TT.group) TT.hdr.gid = TT.ggid; + else if (!FLAG(numeric_owner)) { + struct group *gr = getgrnam(TT.hdr.gname); + if (gr) TT.hdr.gid = gr->gr_gid; + } - 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) { @@ -569,14 +597,15 @@ static void unpack_tar(void) skippy(TT.hdr.size); else if (FLAG(t)) { if (FLAG(v)) { - struct tm *lc = localtime(&TT.hdr.mtime); + struct tm *lc = localtime(TT.mtime ? &TT.mtt : &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 %s/%s ", perm, TT.hdr.uname, TT.hdr.gname); + if (tar.type=='3' || tar.type=='4') printf("%u,%u", maj, min); + else printf("%9lld", (long long)TT.hdr.size); + printf(" %d-%02d-%02d %02d:%02d:%02d ", 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); @@ -619,6 +648,7 @@ void tar_main(void) if (!geteuid()) toys.optflags |= FLAG_p; if (TT.owner) TT.ouid = xgetuid(TT.owner); 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); @@ -635,8 +665,7 @@ void tar_main(void) // Get destination directory if (TT.C) xchdir(TT.C); - s = xgetcwd(); - TT.cwd = (strcmp(s, "/")) ? xabspath(s = xgetcwd(), 1) : 0; + TT.cwd = xabspath(s = xgetcwd(), 1); free(s); // Are we reading? @@ -659,7 +688,7 @@ void tar_main(void) } } - // are we writing? (Don't have to test flag here one of 3 must be set) + // are we writing? (Don't have to test flag here, one of 3 must be set) } else { struct double_list *dl = TT.incl; @@ -670,9 +699,17 @@ void tar_main(void) close(TT.fd); TT.fd = pipefd[0]; } - do dirtree_flagread(dl->data, FLAG(h)?DIRTREE_SYMFOLLOW:0, add_to_tar); - while (TT.incl != (dl = dl->next)); + do { + TT.warn = 1; + dirtree_flagread(dl->data, FLAG(h)?DIRTREE_SYMFOLLOW:0, add_to_tar); + } while (TT.incl != (dl = dl->next)); writeall(TT.fd, toybuf, 1024); } + + if (CFG_TOYBOX_FREE) { + while(TT.hlc) free(TT.hlx[--TT.hlc].arg); + free(TT.hlx); + close(TT.fd); + } } diff --git a/toys/pending/vi.c b/toys/pending/vi.c index 6a1c2349..c5c87507 100644 --- a/toys/pending/vi.c +++ b/toys/pending/vi.c @@ -31,22 +31,20 @@ GLOBALS( * * 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: x dw d$ d0 * vi: yw yy (y0 y$) * vi+ex: gg G //line movements - * ex: r + * ex: r * ex: !external programs * ex: w filename //only writes to same file now - * big file support? + * big file support? */ @@ -57,10 +55,13 @@ struct linestack_show { }; static void draw_page(); +static int draw_str_until(int *drawn, char *str, int width, int bytes); static void draw_char(char c, int x, int y, int highlight); //utf8 support +static int utf8_lnw(int* width, char* str, int bytes); static int utf8_dec(char key, char *utf8_scratch, int *sta_p) ; 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); @@ -74,7 +75,7 @@ static void adjust_screen_buffer(); struct str_line { int alloc_len; - int str_len; + int str_len; char *str_data; }; @@ -89,7 +90,7 @@ struct linelist { 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 +struct linelist *c_r;//cursor position row int modified; void dlist_insert_nomalloc(struct double_list **list, struct double_list *new) @@ -112,9 +113,9 @@ struct double_list *dlist_insert(struct double_list **list, char *data) return new; } -void linelist_unload() +void linelist_unload() { - + } void write_file(char *filename) @@ -132,7 +133,7 @@ void write_file(char *filename) fclose(fp); } -int linelist_load(char *filename) +int linelist_load(char *filename) { struct linelist *lst = c_r;//cursor position or 0 FILE *fp = 0; @@ -140,7 +141,18 @@ int linelist_load(char *filename) filename = (char*)*toys.optargs; fp = fopen(filename, "r"); - if (!fp) return 0; + if (!fp) { + char *line = xzalloc(80); + ssize_t alc = 80; + lst = (struct linelist*)dlist_add((struct double_list**)&lst, + xzalloc(sizeof(struct str_line))); + lst->line->alloc_len = alc; + lst->line->str_len = 0; + lst->line->str_data = line; + text = lst; + dlist_terminate(text->up); + return 1; + } for (;;) { char *line = xzalloc(80); @@ -176,7 +188,7 @@ int linelist_load(char *filename) } //TODO this is overly complicated refactor with lib dllist -int ex_dd(int count) +int ex_dd(int count) { struct linelist *lst = c_r; if (c_r == text && text == scr_r) { @@ -184,7 +196,7 @@ int ex_dd(int count) text->line->str_len = 1; sprintf(text->line->str_data, " "); goto success_exit; - } + } if (text->down) { text = text->down; text->up = 0; @@ -235,7 +247,7 @@ int ex_dw(int count) return 1; } -int ex_deol(int count) +int ex_deol(int count) { return 1; } @@ -252,27 +264,27 @@ int vi_x(int count) l = &c_r->line->str_len; p = &TT.cur_col; if (!(*l)) return 0; - if ((*p) == (*l)-1) { + if ((*p) == (*l)-1) { s[*p] = 0; - if (*p) (*p)--; + if (*p) (*p)--; + (*l)--; + } else { + memmove(s+(*p), s+(*p)+1, (*l)-(*p)); + s[*l] = 0; (*l)--; - } else { - memmove(s+(*p), s+(*p)+1, (*l)-(*p)); - s[*l] = 0; - (*l)--; } count--; - return (count) ? vi_x(count) : 1; + 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) +int vi_movw(int count) { if (!c_r) return 0; //could we call moveend first - while (c_r->line->str_data[TT.cur_col] > ' ') + while (c_r->line->str_data[TT.cur_col] > ' ') TT.cur_col++; while (c_r->line->str_data[TT.cur_col] <= ' ') { TT.cur_col++; @@ -282,7 +294,7 @@ int vi_movw(int count) c_r = c_r->down; TT.cur_col = 0; } - } + } count--; if (count>1) return vi_movw(count); @@ -292,7 +304,7 @@ int vi_movw(int count) return 1; } -int vi_movb(int count) +int vi_movb(int count) { if (!c_r) return 0; @@ -307,7 +319,7 @@ int vi_movb(int count) 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; @@ -322,15 +334,15 @@ exit_function: return 1; } -int vi_move(int count) +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] > ' ') + 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--; @@ -347,11 +359,11 @@ void i_insert() 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->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, + 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; @@ -401,7 +413,7 @@ struct vi_cmd_param vi_cmds[7] = {"x", &vi_x}, }; -int run_vi_cmd(char *cmd) +int run_vi_cmd(char *cmd) { int val = 0; char *cmd_e; @@ -440,11 +452,11 @@ int search_str(char *s) TT.cur_col = c-c_r->line->str_data; return 0; } - + int run_ex_cmd(char *cmd) { if (cmd[0] == '/') { - //search pattern + //search pattern if (!search_str(&cmd[1]) ) { check_cursor_bounds(); adjust_screen_buffer(); @@ -531,7 +543,8 @@ void vi_main(void) il->str_len++; break; case 'a': - TT.cur_col++; + if (c_r && c_r->line->str_len) + TT.cur_col++; case 'i': TT.vi_mode = 2; break; @@ -551,7 +564,7 @@ void vi_main(void) vi_buf_pos = 0; } - } + } break; } @@ -644,100 +657,129 @@ static void draw_page() int cy_scr = 0; int cx_scr = 0; int utf_l = 0; + + char* line = 0; + int bytes = 0; + int drawn = 0; + int x = 0; struct linelist *scr_buf= scr_r; //clear screen tty_esc("2J"); tty_esc("H"); - tty_jump(0, 0); + + //draw lines until cursor row 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"); - } + if (line && bytes) { + draw_str_until(&drawn, line, TT.screen_width, bytes); + bytes = drawn ? (bytes-drawn) : 0; + line = bytes ? (line+drawn) : 0; + y++; + tty_jump(0, y); + } else if (scr_buf && scr_buf->line->str_data && scr_buf->line->str_len) { + if (scr_buf == c_r) + break; + line = scr_buf->line->str_data; + bytes = scr_buf->line->str_len; + scr_buf = scr_buf->down; + } else { + if (scr_buf == c_r) + break; + y++; + tty_jump(0, y); + //printf(" \n"); + if (scr_buf) scr_buf = scr_buf->down; } - 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); + + } + //draw cursor row until cursor + //this is to calculate cursor position on screen and possible insert + line = scr_buf->line->str_data; + bytes = TT.cur_col; + for (; y < TT.screen_height; ) { + if (bytes) { + x = draw_str_until(&drawn, line, TT.screen_width, bytes); + bytes = drawn ? (bytes-drawn) : 0; + line = bytes ? (line+drawn) : 0; + } + if (!bytes) break; + y++; + tty_jump(0, y); + } + if (TT.vi_mode == 2 && il->str_len) { + line = il->str_data; + bytes = il->str_len; + cx_scr = x; + cy_scr = y; + x = draw_str_until(&drawn, line, TT.screen_width-x, bytes); + bytes = drawn ? (bytes-drawn) : 0; + line = bytes ? (line+drawn) : 0; + cx_scr += x; + for (; y < TT.screen_height; ) { + if (bytes) { + x = draw_str_until(&drawn, line, TT.screen_width, bytes); + bytes = drawn ? (bytes-drawn) : 0; + line = bytes ? (line+drawn) : 0; + cx_scr = x; } - y++; + if (!bytes) break; + y++; + cy_scr = y; + tty_jump(0, y); } - printf("\n"); - if (scr_buf->down) - scr_buf=scr_buf->down; - else break; + } else { + cy_scr = y; + cx_scr = x; } - for (; y < TT.screen_height; y++) { - printf("\n"); + line = scr_buf->line->str_data+TT.cur_col; + bytes = scr_buf->line->str_len-TT.cur_col; + scr_buf = scr_buf->down; + x = draw_str_until(&drawn,line, TT.screen_width-x, bytes); + bytes = drawn ? (bytes-drawn) : 0; + line = bytes ? (line+drawn) : 0; + y++; + tty_jump(0, y); + +//draw until end + for (; y < TT.screen_height; ) { + if (line && bytes) { + draw_str_until(&drawn, line, TT.screen_width, bytes); + bytes = drawn ? (bytes-drawn) : 0; + line = bytes ? (line+drawn) : 0; + y++; + tty_jump(0, y); + } else if (scr_buf && scr_buf->line->str_data && scr_buf->line->str_len) { + line = scr_buf->line->str_data; + bytes = scr_buf->line->str_len; + scr_buf = scr_buf->down; + } else { + y++; + tty_jump(0, y); + if (scr_buf) scr_buf = scr_buf->down; + } + } tty_jump(0, TT.screen_height); switch (TT.vi_mode) { case 0: - tty_esc("30;44m"); + tty_esc("30;44m"); printf("COMMAND|"); break; case 1: - tty_esc("30;42m"); + tty_esc("30;42m"); printf("NORMAL|"); break; case 2: - tty_esc("30;41m"); + tty_esc("30;41m"); printf("INSERT|"); break; } //DEBUG - tty_esc("47m"); - tty_esc("30m"); + 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}; @@ -745,26 +787,28 @@ static void draw_page() 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("37m"); tty_esc("40m"); if (!TT.vi_mode) { tty_esc("1m"); tty_jump(0, TT.screen_height+1); printf("%s", il->str_data); + tty_esc("0m"); } else tty_jump(cx_scr, cy_scr); + xflush(); } -static void draw_char(char c, int x, int y, int highlight) +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 + tty_esc("47m"); //background white } printf("%c", c); } @@ -780,7 +824,7 @@ static int draw_rune(char *c, int x, int y, int highlight) tty_esc("0m"); if (highlight) { tty_esc("30m"); //foreground black - tty_esc("47m"); //background white + tty_esc("47m"); //background white } strncpy(t, c, 5); printf("%s", t); @@ -798,7 +842,7 @@ static void check_cursor_bounds() } } -static void adjust_screen_buffer() +static void adjust_screen_buffer() { //search cursor and screen TODO move this perhaps struct linelist *t = text; @@ -821,7 +865,7 @@ static void adjust_screen_buffer() 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 + //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; @@ -839,16 +883,16 @@ static void adjust_screen_buffer() //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 +//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; + 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; @@ -860,7 +904,44 @@ static int utf8_len(char *str) return len; } -static int utf8_dec(char key, char *utf8_scratch, int *sta_p) +//get utf8 length and width at same time +static int utf8_lnw(int* width, char* str, int bytes) +{ + wchar_t wc; + int length = 1; + *width = 1; +// if (str < 0x7F) return length; + length = mbtowc(&wc, str, bytes); + switch (length) { + case -1: + mbtowc(0,0,4); + case 0: + *width = 0; + length = 0; + break; + default: + *width = wcwidth(wc); + } + return length; +} + +//try to estimate width of next "glyph" in terminal buffer +//combining chars 0x300-0x36F shall be zero width +static int utf8_width(char *str, int bytes) +{ + wchar_t wc; + switch (mbtowc(&wc, str, bytes)) { + case -1: + mbtowc(0,0,4); + case 0: + return -1; + default: + return wcwidth(wc); + } + return 0; +} + +static int utf8_dec(char key, char *utf8_scratch, int *sta_p) { int len = 0; char *c = utf8_scratch; @@ -871,17 +952,45 @@ static int utf8_dec(char key, char *utf8_scratch, int *sta_p) 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 int draw_str_until(int *drawn, char *str, int width, int bytes) +{ + int rune_width = 0; + int rune_bytes = 0; + int max_bytes = bytes; + int max_width = width; + char* end = str; + for (;width && bytes;) { + rune_bytes = utf8_lnw(&rune_width, end, 4); + if (!rune_bytes) break; + if (width - rune_width < 0) goto write_bytes; + width -= rune_width; + bytes -= rune_bytes; + end += rune_bytes; + } + for (;bytes;) { + rune_bytes = utf8_lnw(&rune_width, end, 4); + if (!rune_bytes) break; + if (rune_width) break; + bytes -= rune_bytes; + end += rune_bytes; + } +write_bytes: + fwrite(str, max_bytes-bytes, 1, stdout); + *drawn = max_bytes-bytes; + return max_width-width; +} + static void cur_left() { if (!TT.cur_col) return; @@ -892,6 +1001,7 @@ static void cur_left() static void cur_right() { + 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(); |