From 8f75eed7d5fe88434872172694bdbecf6e0e5e56 Mon Sep 17 00:00:00 2001 From: Rob Landley Date: Thu, 1 Apr 2021 04:01:02 -0500 Subject: Typo --- Config.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Config.in b/Config.in index 27948ad3..b626f3c1 100644 --- a/Config.in +++ b/Config.in @@ -182,6 +182,6 @@ config TOYBOX_FORCE_NOMMU to force every program to either include all nommu code in every instance ever built, or drop nommu support altogether. - Building a toolchain scripts/mcm-buildall.sh patches musl to fix this. + Building a scripts/mcm-buildall.sh toolchain patches musl to fix this. endmenu -- cgit v1.2.3 From 4e2a912dac1f4a94ac81ea9c894de3d385563a2a Mon Sep 17 00:00:00 2001 From: Rob Landley Date: Sat, 3 Apr 2021 06:01:32 -0500 Subject: Add a missing pop_block(). --- toys/pending/sh.c | 1 + 1 file changed, 1 insertion(+) diff --git a/toys/pending/sh.c b/toys/pending/sh.c index 7096d135..06dff9cf 100644 --- a/toys/pending/sh.c +++ b/toys/pending/sh.c @@ -3085,6 +3085,7 @@ static void run_lines(void) continue; } TT.ff->pl = TT.ff->pl->end; + pop_block(); dlist_add_nomalloc((void *)&pplist, (void *)pp); // handle start of block in this process -- cgit v1.2.3 From 6a73a3c9ee4b9e17704c368c27d0af34d785fa53 Mon Sep 17 00:00:00 2001 From: Rob Landley Date: Sat, 3 Apr 2021 07:34:19 -0500 Subject: Fix the next test. --- toys/pending/sh.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/toys/pending/sh.c b/toys/pending/sh.c index 06dff9cf..0f26f3fc 100644 --- a/toys/pending/sh.c +++ b/toys/pending/sh.c @@ -865,7 +865,7 @@ static int end_function(int funconly) static int run_subshell(char *str, int len) { pid_t pid; -//dprintf(2, "%d run_subshell %.*s\n", getpid(), len, str); +//dprintf(2, "%d run_subshell %.*s\n", getpid(), len, str); debug_show_fds(); // The with-mmu path is significantly faster. if (CFG_TOYBOX_FORK) { if ((pid = fork())<0) perror_msg("fork"); @@ -2290,7 +2290,10 @@ static struct sh_process *run_command(void) struct sh_vars *vv; // If prefix assignment, create temp function context to hold vars - if (!(persist = envlen==arg->c || TT.ff->blk->pipe)) call_function(); + if (envlen!=arg->c || TT.ff->blk->pipe!=-1) { + call_function(); + persist = 0; + } for (; jjv[jj]; @@ -2313,13 +2316,13 @@ static struct sh_process *run_command(void) if (pp->exit || envlen==arg->c) s = 0; // leave $_ alone else if (!pp->arg.v) s = ""; // nothing to do but blank $_ else { - struct toy_list *tl = toy_find(pp->arg.v[envlen]); + struct toy_list *tl = toy_find(*pp->arg.v); jj = tl ? tl->flags : 0; TT.pp = pp; s = pp->arg.v[pp->arg.c-1]; sss = pp->arg.v[pp->arg.c]; -//dprintf(2, "%d run command %s\n", getpid(), pp->arg.v[envlen]); +//dprintf(2, "%d run command %s\n", getpid(), *pp->arg.v); debug_show_fds(); // TODO handle ((math)): else if (!strcmp(*pp->arg.v, "((")) // TODO: call functions() FUNCTION // TODO what about "echo | x=1 | export fruit", must subshell? Test this. @@ -3329,7 +3332,7 @@ int do_source(char *name, FILE *ff) do { if ((void *)1 == (new = get_next_line(ff, more+1))) goto is_binary; -//dprintf(2, "%d getline from %p %s\n", getpid(), ff, new); +//dprintf(2, "%d getline from %p %s\n", getpid(), ff, new); debug_show_fds(); // did we exec an ELF file or something? if (!TT.LINENO++ && name && new) { wchar_t wc; -- cgit v1.2.3 From cecc41a3c52519ac1d65989b266fec32a2bd6ff7 Mon Sep 17 00:00:00 2001 From: Rob Landley Date: Mon, 5 Apr 2021 07:30:07 -0500 Subject: Any / in string makes it a path, not just absolute path. Note: toy_exec() does an exact name match so fails given a path anyway, it's just an optimization to avoid the binary search, but special casing absolute path while very cheap isn't _correct_... --- lib/xwrap.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/xwrap.c b/lib/xwrap.c index 607f3c61..3f2e3f77 100644 --- a/lib/xwrap.c +++ b/lib/xwrap.c @@ -225,8 +225,8 @@ pid_t __attribute__((returns_twice)) xvforkwrap(pid_t pid) void xexec(char **argv) { // Only recurse to builtin when we have multiplexer and !vfork context. - if (CFG_TOYBOX && !CFG_TOYBOX_NORECURSE && toys.stacktop && **argv != '/') - toy_exec(argv); + if (CFG_TOYBOX && !CFG_TOYBOX_NORECURSE) + if (toys.stacktop && !strchr(**argv,'/')) toy_exec(argv); execvp(argv[0], argv); toys.exitval = 126+(errno == ENOENT); -- cgit v1.2.3 From 2a6f03eb088b21a68ee4f5fa45b7cd4f7c7bc558 Mon Sep 17 00:00:00 2001 From: Rob Landley Date: Wed, 7 Apr 2021 07:00:13 -0500 Subject: Add cp -u --- tests/cp.test | 5 +++++ toys/posix/cp.c | 7 +++++-- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/tests/cp.test b/tests/cp.test index 3fcea3be..b0d5d3a4 100755 --- a/tests/cp.test +++ b/tests/cp.test @@ -136,6 +136,11 @@ testing "-t one arg" 'cp -t sub/ input && cat sub/input' 'yes\n' 'yes\n' '' toyonly testing "-Dt" 'cp -Dt sub2 input && cat sub2/input' 'and\n' 'and\n' '' rm -rf sub sub2 +testing '-u1' 'echo one>one; sleep .1; echo two>two; cp -u one two; cat two' \ + 'two\n' '' '' +testing '-u2' 'echo two>two; sleep .1; echo one>one; cp -u one two; cat two' \ + 'one\n' '' '' + # cp -r ../source destdir # cp -r one/two/three missing # cp -r one/two/three two diff --git a/toys/posix/cp.c b/toys/posix/cp.c index a27c200a..8eaecc9c 100644 --- a/toys/posix/cp.c +++ b/toys/posix/cp.c @@ -15,7 +15,7 @@ // options shared between mv/cp must be in same order (right to left) // for FLAG macros to work out right in shared infrastructure. -USE_CP(NEWTOY(cp, "<1(preserve):;D(parents)RHLPprdaslvnF(remove-destination)fit:T[-HLPd][-ni]", TOYFLAG_BIN)) +USE_CP(NEWTOY(cp, "<1(preserve):;D(parents)RHLPprudaslvnF(remove-destination)fit:T[-HLPd][-niu]", TOYFLAG_BIN)) USE_MV(NEWTOY(mv, "<1vnF(remove-destination)fit:T[-ni]", TOYFLAG_BIN)) USE_INSTALL(NEWTOY(install, "<1cdDpsvt:m:o:g:", TOYFLAG_USR|TOYFLAG_BIN)) @@ -38,6 +38,7 @@ config CP -L Follow all symlinks -l Hard link instead of copy -n No clobber (don't overwrite DEST) + -u Update (keep newest mtime) -P Do not follow symlinks -p Preserve timestamps, ownership, and mode -R Recurse into subdirectories (DEST must be a directory) @@ -152,7 +153,7 @@ static int cp_node(struct dirtree *try) return 0; } - // Handle -invF + // Handle -inuvF if (!faccessat(cfd, catch, F_OK, 0) && !S_ISDIR(cst.st_mode)) { char *s; @@ -165,6 +166,8 @@ static int cp_node(struct dirtree *try) error_msg("unlink '%s'", catch); return 0; } else if (flags & FLAG_n) return 0; + else if ((flags & FLAG_u) && nanodiff(&try->st.st_mtim, &cst.st_mtim)>0) + return 0; else if (flags & FLAG_i) { fprintf(stderr, "%s: overwrite '%s'", toys.which->name, s = dirtree_path(try, 0)); -- cgit v1.2.3 From 4fe080d418333afeb486f478c332bbcdace37f1b Mon Sep 17 00:00:00 2001 From: Rob Landley Date: Thu, 8 Apr 2021 11:28:13 -0500 Subject: Fix typo in the xexec tweak. --- lib/xwrap.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/lib/xwrap.c b/lib/xwrap.c index 3f2e3f77..64137da9 100644 --- a/lib/xwrap.c +++ b/lib/xwrap.c @@ -96,7 +96,6 @@ void *xrealloc(void *ptr, size_t size) char *xstrndup(char *s, size_t n) { char *ret = strndup(s, n); - if (!ret) error_exit("xstrndup"); return ret; @@ -127,8 +126,7 @@ char *xmprintf(char *format, ...) va_copy(va2, va); // How long is it? - len = vsnprintf(0, 0, format, va); - len++; + len = vsnprintf(0, 0, format, va)+1; va_end(va); // Allocate and do the sprintf() @@ -226,7 +224,7 @@ void xexec(char **argv) { // Only recurse to builtin when we have multiplexer and !vfork context. if (CFG_TOYBOX && !CFG_TOYBOX_NORECURSE) - if (toys.stacktop && !strchr(**argv,'/')) toy_exec(argv); + if (toys.stacktop && !strchr(*argv, '/')) toy_exec(argv); execvp(argv[0], argv); toys.exitval = 126+(errno == ENOENT); -- cgit v1.2.3 From f4003d5803d27519af9050ebd98d6897359c5ebe Mon Sep 17 00:00:00 2001 From: Rob Landley Date: Sat, 10 Apr 2021 01:55:00 -0500 Subject: Fix next couple of sh tests. Cached $IFS value has to be per function context because local vars. --- toys/pending/sh.c | 70 +++++++++++++++++++++++++++++++++---------------------- 1 file changed, 42 insertions(+), 28 deletions(-) diff --git a/toys/pending/sh.c b/toys/pending/sh.c index 0f26f3fc..0e7933f6 100644 --- a/toys/pending/sh.c +++ b/toys/pending/sh.c @@ -216,11 +216,11 @@ GLOBALS( } exec; }; - // keep ifs here: used to work around compiler limitation in run_command() - char *ifs, *isexec, *wcpat; + // keep SECONDS here: used to work around compiler limitation in run_command() + long long SECONDS; + char *isexec, *wcpat; unsigned options, jobcnt, LINENO; int hfd, pid, bangpid, varslen, cdcount, srclvl, recursion; - long long SECONDS; // FUNCTION transplant pipelines from place to place? // function keyword can have pointer to function struct? Still refcnt? @@ -251,6 +251,7 @@ GLOBALS( // struct sh_function *func; struct sh_pipeline *pl; + char *ifs; int varslen; struct sh_arg arg; struct arg_list *delete; @@ -399,6 +400,7 @@ static struct sh_vars *addvar(char *s, struct sh_fcall *ff) { if (!(ff->varslen&31)) ff->vars = xrealloc(ff->vars, (ff->varslen+32)*sizeof(*ff->vars)); + if (!s) return ff->vars; ff->vars[ff->varslen].flags = 0; ff->vars[ff->varslen].str = s; @@ -421,9 +423,10 @@ long long do_math(char **s) // ft static struct sh_vars *setvar(char *s) { - int len = varend(s)-s; - long flags; + struct sh_fcall *ff; struct sh_vars *var; + long flags; + int len = varend(s)-s; if (s[len] != '=') { error_msg("bad setvar %s\n", s); @@ -431,8 +434,10 @@ static struct sh_vars *setvar(char *s) return 0; } - if (!strncmp(s, "IFS=", 4)) TT.ifs = s+4; - if (!(var = findvar(s, 0))) return addvar(s, TT.ff->prev); + if (!(var = findvar(s, &ff))) ff = TT.ff->prev; + if (!strncmp(s, "IFS=", 4)) + do ff->ifs = s+4; while ((ff = ff->next) != TT.ff->prev); + if (!var) return addvar(s, TT.ff->prev); flags = (var->flags &= ~VAR_WHITEOUT); if (flags&VAR_READONLY) { @@ -483,6 +488,8 @@ static void unsetvar(char *name) if (!(var->flags&VAR_NOFREE)) free(var->str); memmove(ff->vars+ii, ff->vars+ii+1, (ff->varslen--)-ii); } + if (!strcmp(name, "IFS")) + do ff->ifs = " \t\n"; while ((ff = ff->next) != TT.ff->prev); } static struct sh_vars *setvarval(char *name, char *val) @@ -829,6 +836,7 @@ static void call_function(void) // default $* is to copy previous TT.ff->arg.v = TT.ff->next->arg.v; TT.ff->arg.c = TT.ff->next->arg.c; + TT.ff->ifs = TT.ff->next->ifs; } // returns 0 if source popped, nonzero if function popped @@ -838,7 +846,6 @@ static int end_function(int funconly) int func = ff->next!=ff && ff->vars; if (!func && funconly) return 0; - llist_traverse(ff->delete, llist_free_arg); ff->delete = 0; while (TT.ff->blk->next) pop_block(); @@ -1563,8 +1570,8 @@ barf: nosplit++; if (flags&SEMI_IFS) strcpy(sep, " "); // TODO what if separator is bigger? Need to grab 1 column of combining chars - else if (0<(dd = utf8towc(&wc, TT.ifs, 4))) - sprintf(sep, "%.*s", dd, TT.ifs); + else if (0<(dd = utf8towc(&wc, TT.ff->ifs, 4))) + sprintf(sep, "%.*s", dd, TT.ff->ifs); } // when aa proceed through entries until NULL, else process ifs once @@ -1737,7 +1744,8 @@ barf: // find end of (split) word if ((qq&1) || nosplit) ss = ifs+strlen(ifs); - else for (ss = ifs; *ss; ss += kk) if (utf8chr(ss, TT.ifs, &kk)) break; + else for (ss = ifs; *ss; ss += kk) + if (utf8chr(ss, TT.ff->ifs, &kk)) break; // when no prefix, not splitting, no suffix: use existing memory if (!oo && !*ss && !((mm==aa.c) ? str[ii] : nosplit)) { @@ -1771,7 +1779,7 @@ barf: } // Skip trailing seperator (combining whitespace) - while ((jj = utf8chr(ss, TT.ifs, &ll))) { + while ((jj = utf8chr(ss, TT.ff->ifs, &ll))) { ss += ll; if (!iswspace(jj)) break; } @@ -2290,24 +2298,28 @@ static struct sh_process *run_command(void) struct sh_vars *vv; // If prefix assignment, create temp function context to hold vars - if (envlen!=arg->c || TT.ff->blk->pipe!=-1) { + if (envlen!=arg->c || TT.ff->blk->pipe) { call_function(); + addvar(0, TT.ff); // function context (not source) so end_function deletes persist = 0; - } + } else ff = TT.ff->prev; for (; jjv[jj]; - if (!persist && (!(vv = findvar(s, &ff)) || ff != TT.ff)) { - if (vv && (vv->flags&VAR_READONLY)) { - error_msg("%.*s: readonly variable", (int)(varend(s)-s), s); - continue; - } - addvar(s, TT.ff)->flags = VAR_NOFREE|VAR_GLOBAL; + if (!(vv = findvar(s = arg->v[jj], &ff))) ff = persist?TT.ff->prev:TT.ff; + if (vv && (vv->flags&VAR_READONLY)) { + error_msg("%.*s: readonly variable", (int)(varend(s)-s), s); + continue; } + if (!vv || (!persist && ff != TT.ff)) + (vv = addvar(s, ff))->flags = VAR_NOFREE|(VAR_GLOBAL*!persist); if (!(sss = expand_one_arg(s, SEMI_IFS, persist ? 0 : &delete))) { if (!pp) pp = xzalloc(sizeof(struct sh_process)); pp->exit = 1; - } else setvar((!persist || sss != s) ? sss : xstrdup(sss)); + } else if (persist || sss != s) { + vv->flags &= ~VAR_NOFREE; + vv->str = sss==s ? xstrdup(sss) : sss; + } + if (!strncmp(vv->str, "IFS=", 4)) + do ff->ifs = s+4; while ((ff = ff->next) != TT.ff->prev); } } @@ -2322,7 +2334,7 @@ static struct sh_process *run_command(void) TT.pp = pp; s = pp->arg.v[pp->arg.c-1]; sss = pp->arg.v[pp->arg.c]; -//dprintf(2, "%d run command %s\n", getpid(), *pp->arg.v); debug_show_fds(); +//dprintf(2, "%d run command %p %s\n", getpid(), TT.ff, *pp->arg.v); debug_show_fds(); // TODO handle ((math)): else if (!strcmp(*pp->arg.v, "((")) // TODO: call functions() FUNCTION // TODO what about "echo | x=1 | export fruit", must subshell? Test this. @@ -2340,7 +2352,7 @@ static struct sh_process *run_command(void) // The compiler complains "declaration does not declare anything" if we // name the union in TT, only works WITHOUT name. So we can't // sizeof(union) instead offsetof() first thing after union to get size. - memset(&TT, 0, offsetof(struct sh_data, ifs)); + memset(&TT, 0, offsetof(struct sh_data, SECONDS)); if (!sigsetjmp(rebound, 1)) { toys.rebound = &rebound; toy_singleinit(tl, pp->arg.v); @@ -2358,6 +2370,7 @@ static struct sh_process *run_command(void) // cleanup process unredirect(pp->urd); + pp->urd = 0; if (!persist) end_function(0); if (s) setvarval("_", s); llist_traverse(delete, llist_free_arg); @@ -2798,7 +2811,9 @@ static int parse_line(char *line, struct sh_pipeline **ppl, flush: if (s) syntax_err(s); llist_traverse(*ppl, free_pipeline); + *ppl = 0; llist_traverse(*expect, free); + *expect = 0; return 0-!!s; } @@ -3427,7 +3442,7 @@ static void subshell_setup(void) initvar("PS4", "+ "); // Ensure environ copied and toys.envc set, and clean out illegal entries - TT.ifs = " \t\n"; + TT.ff->ifs = " \t\n"; for (from = pid = ppid = zpid = 0; (s = environ[from]); from++) { @@ -3453,7 +3468,7 @@ static void subshell_setup(void) shv->flags |= VAR_GLOBAL; shv->str = s; } - if (!memcmp(s, "IFS=", 4)) TT.ifs = s+4; + if (!memcmp(s, "IFS=", 4)) TT.ff->ifs = s+4; } // set/update PWD @@ -3700,7 +3715,6 @@ void unset_main(void) } // unset magic variable? - if (!strcmp(*arg, "IFS")) TT.ifs = " \t\n"; unsetvar(*arg); } } -- cgit v1.2.3 From 554565b70def39c39716ed495cafccb262a6af9a Mon Sep 17 00:00:00 2001 From: Rob Landley Date: Sat, 10 Apr 2021 04:57:10 -0500 Subject: Fix more toysh tests. --- toys/pending/sh.c | 27 ++++++++++++++++++--------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/toys/pending/sh.c b/toys/pending/sh.c index 0e7933f6..e26d2791 100644 --- a/toys/pending/sh.c +++ b/toys/pending/sh.c @@ -2288,11 +2288,11 @@ static struct sh_process *run_command(void) struct sh_process *pp = 0; struct arg_list *delete = 0; - // Count leading variable assignments and perform any assignment(s) - for (envlen = 0; envlenc; envlen++) { - s = varend(arg->v[envlen]); - if (s == arg->v[envlen] || *s != '=') break; - } + // Count leading variable assignments + for (envlen = 0; envlenc; envlen++) + if ((s = varend(arg->v[envlen])) == arg->v[envlen] || *s != '=') break; + + // perform any assignments if (envlen) { struct sh_fcall *ff; struct sh_vars *vv; @@ -2309,10 +2309,10 @@ static struct sh_process *run_command(void) error_msg("%.*s: readonly variable", (int)(varend(s)-s), s); continue; } - if (!vv || (!persist && ff != TT.ff)) + if (!vv || (!persist && ff != TT.ff && (ff = TT.ff))) (vv = addvar(s, ff))->flags = VAR_NOFREE|(VAR_GLOBAL*!persist); if (!(sss = expand_one_arg(s, SEMI_IFS, persist ? 0 : &delete))) { - if (!pp) pp = xzalloc(sizeof(struct sh_process)); + if (!pp) pp = xzalloc(sizeof(*pp)); pp->exit = 1; } else if (persist || sss != s) { vv->flags &= ~VAR_NOFREE; @@ -2323,8 +2323,17 @@ static struct sh_process *run_command(void) } } - // Expand command line and do what it says - if (!pp) pp = expand_redir(arg, envlen, 0); + // expand cmdline with _old_ var context, matching bash's order of operations + if (!pp) { + sss = persist ? 0 : dlist_pop(&TT.ff); + pp = expand_redir(arg, envlen, 0); + if (!persist) { + dlist_add_nomalloc((void *)&TT.ff, (void *)sss); + TT.ff = TT.ff->prev; + } + } + + // Do the thing if (pp->exit || envlen==arg->c) s = 0; // leave $_ alone else if (!pp->arg.v) s = ""; // nothing to do but blank $_ else { -- cgit v1.2.3 From 2d690ed7f534e19de1b113afe8d8378363849a36 Mon Sep 17 00:00:00 2001 From: Rob Landley Date: Sun, 11 Apr 2021 06:10:18 -0500 Subject: toysh: save _resolved_ variable contents in $IFS cache. --- toys/pending/sh.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/toys/pending/sh.c b/toys/pending/sh.c index e26d2791..0ccaa792 100644 --- a/toys/pending/sh.c +++ b/toys/pending/sh.c @@ -2314,12 +2314,14 @@ static struct sh_process *run_command(void) if (!(sss = expand_one_arg(s, SEMI_IFS, persist ? 0 : &delete))) { if (!pp) pp = xzalloc(sizeof(*pp)); pp->exit = 1; - } else if (persist || sss != s) { - vv->flags &= ~VAR_NOFREE; - vv->str = sss==s ? xstrdup(sss) : sss; + } else { + if (persist || sss != s) { + vv->flags &= ~VAR_NOFREE; + vv->str = sss==s ? xstrdup(sss) : sss; + } + if (!strncmp(vv->str, "IFS=", 4)) + do ff->ifs = vv->str+4; while ((ff = ff->next) != TT.ff->prev); } - if (!strncmp(vv->str, "IFS=", 4)) - do ff->ifs = s+4; while ((ff = ff->next) != TT.ff->prev); } } -- cgit v1.2.3 From 9259cb7c87191a34451c6e295994785e6b35755a Mon Sep 17 00:00:00 2001 From: Rob Landley Date: Mon, 12 Apr 2021 02:56:17 -0500 Subject: readelf: Display properly in 80 columns, don't reject .bss off end of file. --- toys/pending/readelf.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/toys/pending/readelf.c b/toys/pending/readelf.c index a8564a4d..e6e1623f 100644 --- a/toys/pending/readelf.c +++ b/toys/pending/readelf.c @@ -96,9 +96,11 @@ static int get_sh(unsigned i, struct sh *s) s->addralign = elf_long(&shdr); s->entsize = elf_long(&shdr); - if (s->offset>TT.size || s->size>TT.size || s->offset>TT.size-s->size) { - printf("Bad offset/size %llu/%llu for sh %d\n", s->offset, s->size, i); - return 0; + if (s->type != 8) { + if (s->offset>TT.size || s->size>TT.size || s->offset>TT.size-s->size) { + printf("Bad offset/size %llu/%llu for sh %d\n", s->offset, s->size, i); + return 0; + } } if (!TT.shstrtab) s->name = "?"; @@ -425,7 +427,7 @@ static void scan_elf() TT.shstrtabsz = shstr.size; } - w = 8*(TT.bits+1); + w = 8<