aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorElliott Hughes <enh@google.com>2019-04-01 16:12:40 -0700
committerandroid-build-merger <android-build-merger@google.com>2019-04-01 16:12:40 -0700
commit03df06d931d43332eb470038be0d1f782cb688b9 (patch)
treeecc5b2a8c07db64ef129860526be8ad6035fae46
parent1722118b0c6dbca261dcfda0fa8e1076c2719911 (diff)
parent75180c5c70f571aaa2b2c9e2e383ac98095b740d (diff)
downloadandroid_external_toybox-03df06d931d43332eb470038be0d1f782cb688b9.tar.gz
android_external_toybox-03df06d931d43332eb470038be0d1f782cb688b9.tar.bz2
android_external_toybox-03df06d931d43332eb470038be0d1f782cb688b9.zip
Merge remote-tracking branch 'toybox/master' into HEAD am: 4bb9634f50
am: 75180c5c70 Change-Id: I57015d4898f1e4571f9f28e1a13ee66099268552
-rw-r--r--generated/flags.h16
-rw-r--r--generated/globals.h13
-rw-r--r--generated/newtoys.h2
-rw-r--r--scripts/runtest.sh14
-rw-r--r--tests/bc.test3
-rw-r--r--tests/tar.test61
-rw-r--r--toys/pending/tar.c213
-rw-r--r--toys/pending/vi.c352
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();