diff options
Diffstat (limited to 'Src')
42 files changed, 2188 insertions, 733 deletions
diff --git a/Src/Modules/curses.c b/Src/Modules/curses.c index 0054aef52..64329f643 100644 --- a/Src/Modules/curses.c +++ b/Src/Modules/curses.c @@ -370,7 +370,7 @@ zcurses_colorget(const char *nam, char *colorpair) return NULL; } - cpn = (Colorpairnode)zalloc(sizeof(struct colorpairnode)); + cpn = (Colorpairnode)zshcalloc(sizeof(struct colorpairnode)); if (!cpn) { zsfree(cp); @@ -462,7 +462,7 @@ zccmd_init(const char *nam, char **args) use_default_colors(); #endif /* Initialise the default color pair, always 0 */ - cpn = (Colorpairnode)zalloc(sizeof(struct colorpairnode)); + cpn = (Colorpairnode)zshcalloc(sizeof(struct colorpairnode)); if (cpn) { cpn->colorpair = 0; addhashnode(zcurses_colorpairs, diff --git a/Src/Modules/curses.mdd b/Src/Modules/curses.mdd index 669c4f5c7..80c8f867b 100644 --- a/Src/Modules/curses.mdd +++ b/Src/Modules/curses.mdd @@ -2,7 +2,7 @@ name=zsh/curses link='if test "x$ac_cv_func_initscr" = xyes && test "x$zsh_cv_path_curses_header" != x; then echo dynamic; else echo no; fi' load=no -autobins="zcurses" +autofeatures="b:zcurses" objects="curses.o" diff --git a/Src/Modules/db_gdbm.c b/Src/Modules/db_gdbm.c index 76d4751bf..032963262 100644 --- a/Src/Modules/db_gdbm.c +++ b/Src/Modules/db_gdbm.c @@ -106,7 +106,9 @@ bin_ztie(char *nam, char **args, Options ops, UNUSED(int func)) } dbf = gdbm_open(resource_name, 0, read_write, 0666, 0); - if(!dbf) { + if(dbf) + addmodulefd(gdbm_fdesc(dbf), FDT_INTERNAL); + else { zwarnnam(nam, "error opening database file %s", resource_name); return 1; } @@ -114,6 +116,7 @@ bin_ztie(char *nam, char **args, Options ops, UNUSED(int func)) if (!(tied_param = createspecialhash(pmname, &getgdbmnode, &scangdbmkeys, pmflags))) { zwarnnam(nam, "cannot create the requested parameter %s", pmname); + fdtable[gdbm_fdesc(dbf)] = FDT_UNUSED; gdbm_close(dbf); return 1; } @@ -319,8 +322,10 @@ gdbmuntie(Param pm) GDBM_FILE dbf = (GDBM_FILE)(pm->u.hash->tmpdata); HashTable ht = pm->u.hash; - if (dbf) /* paranoia */ + if (dbf) { /* paranoia */ + fdtable[gdbm_fdesc(dbf)] = FDT_UNUSED; gdbm_close(dbf); + } ht->tmpdata = NULL; diff --git a/Src/Modules/param_private.c b/Src/Modules/param_private.c new file mode 100644 index 000000000..7f9aa7921 --- /dev/null +++ b/Src/Modules/param_private.c @@ -0,0 +1,587 @@ +/* + * param_private.c - bindings for private parameter scopes + * + * This file is part of zsh, the Z shell. + * + * Copyright (c) 2015 Barton E. Schaefer + * All rights reserved. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and to distribute modified versions of this software for any + * purpose, provided that the above copyright notice and the following + * two paragraphs appear in all copies of this software. + * + * In no event shall Barton E. Schaefer or the Zsh Development + * Group be liable to any party for direct, indirect, special, incidental, or + * consequential damages arising out of the use of this software and its + * documentation, even if Barton E. Schaefer and the Zsh + * Development Group have been advised of the possibility of such damage. + * + * Barton E. Schaefer and the Zsh Development Group + * specifically disclaim any warranties, including, but not limited to, the + * implied warranties of merchantability and fitness for a particular purpose. + * The software provided hereunder is on an "as is" basis, and + * Barton E. Schaefer and the Zsh Development Group have no + * obligation to provide maintenance, support, updates, enhancements, or + * modifications. + * + */ + +#include "param_private.mdh" +#include "param_private.pro" + +struct gsu_closure { + union { + struct gsu_scalar s; + struct gsu_integer i; + struct gsu_float f; + struct gsu_array a; + struct gsu_hash h; + } u; + void *g; +}; + +const struct gsu_scalar scalar_private_gsu = +{ pps_getfn, pps_setfn, pps_unsetfn }; + +const struct gsu_integer integer_private_gsu = +{ ppi_getfn, ppi_setfn, ppi_unsetfn }; + +const struct gsu_float float_private_gsu = +{ ppf_getfn, ppf_setfn, ppf_unsetfn }; + +const struct gsu_array array_private_gsu = +{ ppa_getfn, ppa_setfn, ppa_unsetfn }; + +const struct gsu_hash hash_private_gsu = +{ pph_getfn, pph_setfn, pph_unsetfn }; + +/* + * The trick here is: + * + * bin_private() opens a new parameter scope, then calls bin_typeset(). + * + * bin_typeset() handles the usual parameter creation and error checks. + * + * makeprivate() then finds all parameters created in the new scope and + * rejects them if they can't be "promoted" to the surrounding scope. + * Otherwise it swaps out their GSU structure and promotes them so they + * will be removed when the surrounding scope ends. + * + * bin_private() then ends the current scope, which discards any of the + * parameters rejected by makeprivate(). + * + */ + +static int makeprivate_error = 0; + +static void +makeprivate(HashNode hn, UNUSED(int flags)) +{ + Param pm = (Param)hn; + if (pm->level == locallevel) { + if (pm->ename || (pm->node.flags & PM_NORESTORE) || + (pm->old && + (pm->old->level == locallevel - 1 || + ((pm->node.flags & (PM_SPECIAL|PM_REMOVABLE)) == PM_SPECIAL && + /* typeset_single() line 2300 discards PM_REMOVABLE -- why? */ + !is_private(pm->old))))) { + zwarnnam("private", "can't change scope of existing param: %s", + pm->node.nam); + makeprivate_error = 1; + return; + } + struct gsu_closure *gsu = zhalloc(sizeof(struct gsu_closure)); + switch (PM_TYPE(pm->node.flags)) { + case PM_SCALAR: + gsu->g = (void *)(pm->gsu.s); + gsu->u.s = scalar_private_gsu; + pm->gsu.s = (GsuScalar)gsu; + break; + case PM_INTEGER: + gsu->g = (void *)(pm->gsu.i); + gsu->u.i = integer_private_gsu; + pm->gsu.i = (GsuInteger)gsu; + break; + case PM_EFLOAT: + case PM_FFLOAT: + gsu->g = (void *)(pm->gsu.f); + gsu->u.f = float_private_gsu; + pm->gsu.f = (GsuFloat)gsu; + break; + case PM_ARRAY: + gsu->g = (void *)(pm->gsu.a); + gsu->u.a = array_private_gsu; + pm->gsu.a = (GsuArray)gsu; + break; + case PM_HASHED: + gsu->g = (void *)(pm->gsu.h); + gsu->u.h = hash_private_gsu; + pm->gsu.h = (GsuHash)gsu; + break; + default: + makeprivate_error = 1; + break; + } + /* PM_HIDE so new parameters in deeper scopes do not shadow */ + pm->node.flags |= (PM_HIDE|PM_SPECIAL|PM_REMOVABLE); + pm->level -= 1; + } +} + +/**/ +static int +is_private(Param pm) +{ + switch (PM_TYPE(pm->node.flags)) { + case PM_SCALAR: + if (!pm->gsu.s || pm->gsu.s->unsetfn != pps_unsetfn) + return 0; + break; + case PM_INTEGER: + if (!pm->gsu.i || pm->gsu.i->unsetfn != ppi_unsetfn) + return 0; + break; + case PM_EFLOAT: + case PM_FFLOAT: + if (!pm->gsu.f || pm->gsu.f->unsetfn != ppf_unsetfn) + return 0; + break; + case PM_ARRAY: + if (!pm->gsu.a || pm->gsu.a->unsetfn != ppa_unsetfn) + return 0; + break; + case PM_HASHED: + if (!pm->gsu.h || pm->gsu.h->unsetfn != pph_unsetfn) + return 0; + break; + default: + /* error */ + return 0; + } + return 1; +} + +static int fakelevel; + +/**/ +static int +bin_private(char *nam, char **args, LinkList assigns, Options ops, int func) +{ + int from_typeset = 1; + makeprivate_error = 0; + + if (!OPT_ISSET(ops, 'P')) + return bin_typeset(nam, args, assigns, ops, func); + else if (OPT_ISSET(ops, 'T')) { + zwarn("bad option: -T"); + return 1; + } + + if (locallevel == 0) { + if (isset(WARNCREATEGLOBAL)) + zwarnnam(nam, "invalid local scope, using globals"); + return bin_typeset("private", args, assigns, ops, func); + } + + ops->ind['g'] = 2; /* force bin_typeset() to behave as "local" */ + + queue_signals(); + fakelevel = locallevel; + startparamscope(); + from_typeset = bin_typeset("private", args, assigns, ops, func); + scanhashtable(paramtab, 0, 0, 0, makeprivate, 0); + endparamscope(); + fakelevel = 0; + unqueue_signals(); + + return makeprivate_error | from_typeset; +} + +static void +setfn_error(Param pm) +{ + pm->node.flags |= PM_UNSET; + zerr("%s: attempt to assign private in nested scope", pm->node.nam); +} + +/* + * How the GSU functions work: + * + * The getfn and setfn family compare to locallevel and then call through + * to the original getfn or setfn. This means you can't assign at a + * deeper scope to any parameter declared private unless you first declare + * it local again at the new scope. Testing locallevel in getfn is most + * likely unnecessary given the scopeprivate() wrapper installed below. + * + * The unsetfn family compare locallevel and restore the old GSU before + * calling the original unsetfn. This assures that if the old unsetfn + * wants to use its getfn or setfn, they're unconditionally present. + * + */ + +/**/ +static char * +pps_getfn(Param pm) +{ + struct gsu_closure *c = (struct gsu_closure *)(pm->gsu.s); + GsuScalar gsu = (GsuScalar)(c->g); + + if (locallevel >= pm->level) + return gsu->getfn(pm); + else + return (char *) hcalloc(1); +} + +/**/ +static void +pps_setfn(Param pm, char *x) +{ + struct gsu_closure *c = (struct gsu_closure *)(pm->gsu.s); + GsuScalar gsu = (GsuScalar)(c->g); + if (locallevel == pm->level) + gsu->setfn(pm, x); + else + setfn_error(pm); +} + +/**/ +static void +pps_unsetfn(Param pm, int x) +{ + struct gsu_closure *c = (struct gsu_closure *)(pm->gsu.s); + GsuScalar gsu = (GsuScalar)(c->g); + pm->gsu.s = gsu; + if (locallevel <= pm->level) + gsu->unsetfn(pm, x); +} + +/**/ +static zlong +ppi_getfn(Param pm) +{ + struct gsu_closure *c = (struct gsu_closure *)(pm->gsu.i); + GsuInteger gsu = (GsuInteger)(c->g); + if (locallevel >= pm->level) + return gsu->getfn(pm); + else + return 0; +} + +/**/ +static void +ppi_setfn(Param pm, zlong x) +{ + struct gsu_closure *c = (struct gsu_closure *)(pm->gsu.i); + GsuInteger gsu = (GsuInteger)(c->g); + if (locallevel == pm->level) + gsu->setfn(pm, x); + else + setfn_error(pm); +} + +/**/ +static void +ppi_unsetfn(Param pm, int x) +{ + struct gsu_closure *c = (struct gsu_closure *)(pm->gsu.i); + GsuInteger gsu = (GsuInteger)(c->g); + pm->gsu.i = gsu; + if (locallevel <= pm->level) + gsu->unsetfn(pm, x); +} + +/**/ +static double +ppf_getfn(Param pm) +{ + struct gsu_closure *c = (struct gsu_closure *)(pm->gsu.f); + GsuFloat gsu = (GsuFloat)(c->g); + if (locallevel >= pm->level) + return gsu->getfn(pm); + else + return 0; +} + +/**/ +static void +ppf_setfn(Param pm, double x) +{ + struct gsu_closure *c = (struct gsu_closure *)(pm->gsu.f); + GsuFloat gsu = (GsuFloat)(c->g); + if (locallevel == pm->level) + gsu->setfn(pm, x); + else + setfn_error(pm); +} + +/**/ +static void +ppf_unsetfn(Param pm, int x) +{ + struct gsu_closure *c = (struct gsu_closure *)(pm->gsu.f); + GsuFloat gsu = (GsuFloat)(c->g); + pm->gsu.f = gsu; + if (locallevel <= pm->level) + gsu->unsetfn(pm, x); +} + +/**/ +static char ** +ppa_getfn(Param pm) +{ + static char *nullarray = NULL; + struct gsu_closure *c = (struct gsu_closure *)(pm->gsu.a); + GsuArray gsu = (GsuArray)(c->g); + if (locallevel >= pm->level) + return gsu->getfn(pm); + else + return &nullarray; +} + +/**/ +static void +ppa_setfn(Param pm, char **x) +{ + struct gsu_closure *c = (struct gsu_closure *)(pm->gsu.a); + GsuArray gsu = (GsuArray)(c->g); + if (locallevel == pm->level) + gsu->setfn(pm, x); + else + setfn_error(pm); +} + +/**/ +static void +ppa_unsetfn(Param pm, int x) +{ + struct gsu_closure *c = (struct gsu_closure *)(pm->gsu.a); + GsuArray gsu = (GsuArray)(c->g); + pm->gsu.a = gsu; + if (locallevel <= pm->level) + gsu->unsetfn(pm, x); +} + +static HashTable emptytable; + +/**/ +static HashTable +pph_getfn(Param pm) +{ + struct gsu_closure *c = (struct gsu_closure *)(pm->gsu.h); + GsuHash gsu = (GsuHash)(c->g); + if (locallevel >= pm->level) + return gsu->getfn(pm); + else + return emptytable; +} + +/**/ +static void +pph_setfn(Param pm, HashTable x) +{ + struct gsu_closure *c = (struct gsu_closure *)(pm->gsu.h); + GsuHash gsu = (GsuHash)(c->g); + if (locallevel == pm->level) + gsu->setfn(pm, x); + else + setfn_error(pm); +} + +/**/ +static void +pph_unsetfn(Param pm, int x) +{ + struct gsu_closure *c = (struct gsu_closure *)(pm->gsu.h); + GsuHash gsu = (GsuHash)(c->g); + pm->gsu.h = gsu; + if (locallevel <= pm->level) + gsu->unsetfn(pm, x); +} + +/* + * How visibility works: + * + * Upon entering a new function scope, we find all the private parameters + * at this locallevel. Any that we find are marked PM_UNSET. If they are + * already unset, they are also marked as PM_NORESTORE. + * + * On exit from the scope, we find the same parameters again and remove + * the PM_UNSET and PM_NORESTORE flags as appropriate. We're guaraneed + * by makeprivate() that PM_NORESTORE won't conflict with anything here. + * + */ + +static void +scopeprivate(HashNode hn, int onoff) +{ + Param pm = (Param)hn; + if (pm->level == locallevel) { + if (!is_private(pm)) + return; + if (onoff == PM_UNSET) + if (pm->node.flags & PM_UNSET) + pm->node.flags |= PM_NORESTORE; + else + pm->node.flags |= PM_UNSET; + else if (!(pm->node.flags & PM_NORESTORE)) + pm->node.flags &= ~PM_UNSET; + pm->node.flags &= ~PM_NORESTORE; + } +} + +static struct funcwrap wrapper[] = { + WRAPDEF(wrap_private) +}; + +/**/ +static int +wrap_private(Eprog prog, FuncWrap w, char *name) +{ + static int wraplevel = 0; + + if (wraplevel < locallevel /* && strcmp(name, "(anon)") != 0 */) { + int owl = wraplevel; + wraplevel = locallevel; + scanhashtable(paramtab, 0, 0, 0, scopeprivate, PM_UNSET); + runshfunc(prog, w, name); + scanhashtable(paramtab, 0, 0, 0, scopeprivate, 0); + wraplevel = owl; + return 0; + } + return 1; +} + +static HashNode (*getparamnode) _((HashTable, const char *)); + +/**/ +static HashNode +getprivatenode(HashTable ht, const char *nam) +{ + HashNode hn = getparamnode(ht, nam); + Param pm = (Param) hn; + + while (!fakelevel && pm && locallevel > pm->level && is_private(pm)) + pm = pm->old; + return (HashNode)pm; +} + +/**/ +static HashNode +getprivatenode2(HashTable ht, const char *nam) +{ + /* getparamnode() would follow autoloads, we must not do that here */ + HashNode hn = gethashnode2(ht, nam); + Param pm = (Param) hn; + + while (!fakelevel && pm && locallevel > pm->level && is_private(pm)) + pm = pm->old; + return (HashNode)pm; +} + +/**/ +static void +printprivatenode(HashNode hn, int printflags) +{ + Param pm = (Param) hn; + while (pm && (!fakelevel || + (fakelevel > pm->level && (pm->node.flags & PM_UNSET))) && + locallevel > pm->level && is_private(pm)) + pm = pm->old; + /* Ideally, we'd print the word "private" here instead of "typeset" + * when the parameter is in fact a private, but that would require + * re-implementing the entirety of printparamnode(). */ + if (pm) + printparamnode((HashNode)pm, printflags); +} + +/* + * Standard module configuration/linkage + */ + +static struct builtin bintab[] = { + /* Copied from BUILTIN("local"), "P" added */ + BUILTIN("private", BINF_PLUSOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL | BINF_ASSIGN, (HandlerFunc)bin_private, 0, -1, 0, "AE:%F:%HL:%PR:%TUZ:%ahi:%lprtux", "P") +}; + +static struct features module_features = { + bintab, sizeof(bintab)/sizeof(*bintab), + NULL, 0, + NULL, 0, + NULL, 0, + 0 +}; + +static struct builtin save_local; +static struct reswd reswd_private = {{NULL, "private", 0}, TYPESET}; + +/**/ +int +setup_(UNUSED(Module m)) +{ + HashNode hn = builtintab->getnode(builtintab, "local"); + + /* Horrible, horrible hack */ + getparamnode = realparamtab->getnode; + realparamtab->getnode = getprivatenode; + realparamtab->getnode2 = getprivatenode2; + realparamtab->printnode = printprivatenode; + + /* Even more horrible hack */ + save_local = *(Builtin)hn; + ((Builtin)hn)->handlerfunc = bintab[0].handlerfunc; + ((Builtin)hn)->optstr = bintab[0].optstr; + + reswdtab->addnode(reswdtab, reswd_private.node.nam, &reswd_private); + + return 0; +} + +/**/ +int +features_(Module m, char ***features) +{ + *features = featuresarray(m, &module_features); + return 0; +} + +/**/ +int +enables_(Module m, int **enables) +{ + return handlefeatures(m, &module_features, enables); +} + +/**/ +int +boot_(Module m) +{ + emptytable = newparamtable(1, "private"); + return addwrapper(m, wrapper); +} + +/**/ +int +cleanup_(Module m) +{ + HashNode hn = builtintab->getnode(builtintab, "local"); + *(Builtin)hn = save_local; + + removehashnode(reswdtab, "private"); + + realparamtab->getnode = getparamnode; + realparamtab->getnode2 = gethashnode2; + realparamtab->printnode = printparamnode; + + deletewrapper(m, wrapper); + return setfeatureenables(m, &module_features, NULL); +} + +/**/ +int +finish_(UNUSED(Module m)) +{ + deletehashtable(emptytable); + return 0; +} diff --git a/Src/Modules/param_private.mdd b/Src/Modules/param_private.mdd new file mode 100644 index 000000000..e6eb3228f --- /dev/null +++ b/Src/Modules/param_private.mdd @@ -0,0 +1,7 @@ +name=zsh/param/private +link=dynamic +load=yes + +autofeatures="b:private" + +objects="param_private.o" diff --git a/Src/Modules/socket.c b/Src/Modules/socket.c index 65b87d7dd..7c3fb5ebe 100644 --- a/Src/Modules/socket.c +++ b/Src/Modules/socket.c @@ -115,6 +115,8 @@ bin_zsocket(char *nam, char **args, Options ops, UNUSED(int func)) return 1; } + addmodulefd(sfd, FDT_EXTERNAL); + if (targetfd) { sfd = redup(sfd, targetfd); } @@ -127,7 +129,10 @@ bin_zsocket(char *nam, char **args, Options ops, UNUSED(int func)) return 1; } - setiparam("REPLY", sfd); + /* allow to be closed explicitly */ + fdtable[sfd] = FDT_EXTERNAL; + + setiparam_no_convert("REPLY", (zlong)sfd); if (verbose) printf("%s listener is on fd %d\n", soun.sun_path, sfd); @@ -200,18 +205,22 @@ bin_zsocket(char *nam, char **args, Options ops, UNUSED(int func)) return 1; } + addmodulefd(rfd, FDT_EXTERNAL); + if (targetfd) { sfd = redup(rfd, targetfd); if (sfd < 0) { zerrnam(nam, "could not duplicate socket fd to %d: %e", targetfd, errno); + zclose(rfd); return 1; } + fdtable[sfd] = FDT_EXTERNAL; } else { sfd = rfd; } - setiparam("REPLY", sfd); + setiparam_no_convert("REPLY", (zlong)sfd); if (verbose) printf("new connection from %s is on fd %d\n", soun.sun_path, sfd); @@ -240,15 +249,19 @@ bin_zsocket(char *nam, char **args, Options ops, UNUSED(int func)) } else { + addmodulefd(sfd, FDT_EXTERNAL); + if (targetfd) { - sfd = redup(sfd, targetfd); - if (sfd < 0) { + if (redup(sfd, targetfd) < 0) { zerrnam(nam, "could not duplicate socket fd to %d: %e", targetfd, errno); + zclose(sfd); return 1; } + sfd = targetfd; + fdtable[sfd] = FDT_EXTERNAL; } - setiparam("REPLY", sfd); + setiparam_no_convert("REPLY", (zlong)sfd); if (verbose) printf("%s is now on fd %d\n", soun.sun_path, sfd); diff --git a/Src/Modules/tcp.c b/Src/Modules/tcp.c index bc1765da1..9fc1b29a2 100644 --- a/Src/Modules/tcp.c +++ b/Src/Modules/tcp.c @@ -236,6 +236,8 @@ tcp_socket(int domain, int type, int protocol, int ztflags) if (!sess) return NULL; sess->fd = socket(domain, type, protocol); + /* We'll check failure and tidy up in caller */ + addmodulefd(sess->fd, FDT_MODULE); return sess; } @@ -298,7 +300,7 @@ tcp_close(Tcp_session sess) { if (sess->fd != -1) { - err = close(sess->fd); + err = zclose(sess->fd); if (err) zwarn("connection close failed: %e", errno); } @@ -459,7 +461,7 @@ bin_ztcp(char *nam, char **args, Options ops, UNUSED(int func)) return 1; } - setiparam("REPLY", sess->fd); + setiparam_no_convert("REPLY", (zlong)sess->fd); if (verbose) printf("%d listener is on fd %d\n", ntohs(sess->sock.in.sin_port), sess->fd); @@ -546,6 +548,9 @@ bin_ztcp(char *nam, char **args, Options ops, UNUSED(int func)) return 1; } + /* redup expects fd is already registered */ + addmodulefd(rfd, FDT_MODULE); + if (targetfd) { sess->fd = redup(rfd, targetfd); if (sess->fd < 0) { @@ -557,7 +562,7 @@ bin_ztcp(char *nam, char **args, Options ops, UNUSED(int func)) sess->fd = rfd; } - setiparam("REPLY", sess->fd); + setiparam_no_convert("REPLY", (zlong)sess->fd); if (verbose) printf("%d is on fd %d\n", ntohs(sess->peer.in.sin_port), sess->fd); @@ -676,7 +681,7 @@ bin_ztcp(char *nam, char **args, Options ops, UNUSED(int func)) } } - setiparam("REPLY", sess->fd); + setiparam_no_convert("REPLY", (zlong)sess->fd); if (verbose) printf("%s:%d is now on fd %d\n", diff --git a/Src/Modules/zftp.c b/Src/Modules/zftp.c index bd51512f9..b4081df5f 100644 --- a/Src/Modules/zftp.c +++ b/Src/Modules/zftp.c @@ -409,7 +409,7 @@ zfalarm(int tmout) /**/ static void -zfpipe() +zfpipe(void) { /* Just ignore SIGPIPE and rely on getting EPIPE from the write. */ signal(SIGPIPE, SIG_IGN); @@ -450,7 +450,7 @@ zfunalarm(void) /**/ static void -zfunpipe() +zfunpipe(void) { if (sigtrapped[SIGPIPE]) { if (siglists[SIGPIPE] || (sigtrapped[SIGPIPE] & ZSIG_FUNC)) @@ -1298,7 +1298,7 @@ zfstarttrans(char *nam, int recv, off_t sz) /**/ static void -zfendtrans() +zfendtrans(void) { zfunsetparam("ZFTP_SIZE"); zfunsetparam("ZFTP_FILE"); @@ -2834,7 +2834,7 @@ newsession(char *nm) /* Save the existing session: this just means saving the parameters. */ static void -savesession() +savesession(void) { char **ps, **pd, *val; diff --git a/Src/Modules/zpty.c b/Src/Modules/zpty.c index 9741ee287..3b8366076 100644 --- a/Src/Modules/zpty.c +++ b/Src/Modules/zpty.c @@ -464,7 +464,7 @@ newptycmd(char *nam, char *pname, char **args, int echo, int nblock) #endif errno == EINTR)); - setiparam("REPLY", master); + setiparam_no_convert("REPLY", (zlong)master); return 0; } diff --git a/Src/Zle/compctl.c b/Src/Zle/compctl.c index bac533e7e..8381867d0 100644 --- a/Src/Zle/compctl.c +++ b/Src/Zle/compctl.c @@ -2116,7 +2116,7 @@ getreal(char *str) noerrs = 1; addlinknode(l, dupstring(str)); - prefork(l, 0); + prefork(l, 0, NULL); noerrs = ne; if (!errflag && nonempty(l) && ((char *) peekfirst(l)) && ((char *) peekfirst(l))[0]) @@ -3728,7 +3728,7 @@ makecomplistflags(Compctl cc, char *s, int incmd, int compadd) errflag &= ~ERRFLAG_ERROR; zcontext_restore(); /* Fine, now do full expansion. */ - prefork(foo, 0); + prefork(foo, 0, NULL); if (!errflag) { globlist(foo, 0); if (!errflag) diff --git a/Src/Zle/complist.c b/Src/Zle/complist.c index 01bcb7cf8..29aaee82a 100644 --- a/Src/Zle/complist.c +++ b/Src/Zle/complist.c @@ -499,7 +499,7 @@ filecol(char *col) */ static void -getcols() +getcols(void) { char *s; int i, l; @@ -602,7 +602,7 @@ zcoff(void) static void -initiscol() +initiscol(void) { int i; @@ -868,7 +868,7 @@ putmatchcol(char *group, char *n) nrefs = MAX_POS - 1; if ((!pc->prog || !group || pattry(pc->prog, group)) && - pattryrefs(pc->pat, n, -1, -1, 0, &nrefs, begpos, endpos)) { + pattryrefs(pc->pat, n, -1, -1, NULL, 0, &nrefs, begpos, endpos)) { if (pc->cols[1]) { patcols = pc->cols; @@ -900,7 +900,8 @@ putfilecol(char *group, char *filename, mode_t m, int special) nrefs = MAX_POS - 1; if ((!pc->prog || !group || pattry(pc->prog, group)) && - pattryrefs(pc->pat, filename, -1, -1, 0, &nrefs, begpos, endpos)) { + pattryrefs(pc->pat, filename, -1, -1, NULL, + 0, &nrefs, begpos, endpos)) { if (pc->cols[1]) { patcols = pc->cols; @@ -1908,7 +1909,7 @@ singlecalc(int *cp, int l, int *lcp) } static void -singledraw() +singledraw(void) { Cmgroup g; int mc1, mc2, ml1, ml2, md1, md2, mcc1, mcc2, lc1, lc2, t1, t2; @@ -3300,7 +3301,7 @@ domenuselect(Hookdef dummy, Chdata dat) int len; memset(&mbs, 0, sizeof(mbs)); - len = wcrtomb(s, lastchar_wide, &mbs); + len = wcrtomb(toins, lastchar_wide, &mbs); if (len < 0) len = 0; insert[len] = '\0'; diff --git a/Src/Zle/compmatch.c b/Src/Zle/compmatch.c index 05ae43ae6..0e41ac3a5 100644 --- a/Src/Zle/compmatch.c +++ b/Src/Zle/compmatch.c @@ -338,8 +338,15 @@ add_match_str(Cmatcher m, char *l, char *w, int wl, int sfx) char *buf; buf = (char *) zalloc(blen); - memcpy(buf, matchbuf, matchbuflen); - zfree(matchbuf, matchbuflen); + if (matchbuf) { + memcpy(buf, matchbuf, matchbuflen); + zfree(matchbuf, matchbuflen); + } +#ifdef DEBUG + else { + DPUTS(matchbuflen, "matchbuflen with no matchbuf"); + } +#endif matchbuf = buf; matchbuflen = blen; } @@ -813,10 +820,12 @@ match_str(char *l, char *w, Brinfo *bpp, int bc, int *rwlp, continue; else if (mp->right) t = pattern_match(mp->right, - tl + mp->llen - mp->ralen, + /* tl + mp->llen - mp->ralen, */ + tl + mp->llen, NULL, NULL) && pattern_match(mp->right, - tw + mp->wlen - mp->ralen, + /* tw + mp->wlen - mp->ralen, */ + tw + mp->wlen, NULL, NULL) && (!mp->lalen || pattern_match(mp->left, tw + mp->wlen - diff --git a/Src/Zle/computil.c b/Src/Zle/computil.c index e5db0867b..60fb096be 100644 --- a/Src/Zle/computil.c +++ b/Src/Zle/computil.c @@ -185,7 +185,7 @@ cd_group(int maxg) * descriptions. */ static void -cd_calc() +cd_calc(void) { Cdset set; Cdstr str; @@ -236,7 +236,7 @@ cd_sort(const void *a, const void *b) } static int -cd_prep() +cd_prep(void) { Cdrun run, *runp; Cdset set; @@ -1693,10 +1693,13 @@ ca_get_opt(Cadef d, char *line, int full, char **end) for (p = d->opts; p; p = p->next) if (p->active && ((!p->args || p->type == CAO_NEXT) ? !strcmp(p->name, line) : strpfx(p->name, line))) { + int l = strlen(p->name); + if ((p->type == CAO_OEQUAL || p->type == CAO_EQUAL) && + line[l] && line[l] != '=') + continue; + if (end) { /* Return a pointer to the end of the option. */ - int l = strlen(p->name); - if ((p->type == CAO_OEQUAL || p->type == CAO_EQUAL) && line[l] == '=') l++; diff --git a/Src/Zle/zle_hist.c b/Src/Zle/zle_hist.c index c61b4ef0e..95d96c95c 100644 --- a/Src/Zle/zle_hist.c +++ b/Src/Zle/zle_hist.c @@ -1306,8 +1306,8 @@ doisearch(char **args, int dir, int pattern) * this mode. */ if (!skip_pos && - pattryrefs(patprog, zt, -1, -1, 0, NULL, NULL, - &end_pos)) + pattryrefs(patprog, zt, -1, -1, NULL, 0, + NULL, NULL, &end_pos)) t = zt; } else { if (!matchlist && !skip_pos) { @@ -1643,7 +1643,7 @@ doisearch(char **args, int dir, int pattern) } else if (cmd == Th(z_selfinsert)) { #ifdef MULTIBYTE_SUPPORT if (!lastchar_wide_valid) - if (getrestchar(lastchar) == WEOF) { + if (getrestchar(lastchar, NULL, NULL) == WEOF) { handlefeep(zlenoargs); continue; } @@ -1877,7 +1877,7 @@ getvisrchstr(void) } else { #ifdef MULTIBYTE_SUPPORT if (!lastchar_wide_valid) - if (getrestchar(lastchar) == WEOF) { + if (getrestchar(lastchar, NULL, NULL) == WEOF) { handlefeep(zlenoargs); continue; } diff --git a/Src/Zle/zle_keymap.c b/Src/Zle/zle_keymap.c index 5b4189faa..069580f8a 100644 --- a/Src/Zle/zle_keymap.c +++ b/Src/Zle/zle_keymap.c @@ -1501,6 +1501,20 @@ getkeymapcmd(Keymap km, Thingy *funcp, char **strp) * they wait till a key is pressed for the movement anyway */ timeout = !(!virangeflag && !region_active && f && f->widget && f->widget->flags & ZLE_VIOPER); +#ifdef MULTIBYTE_SUPPORT + if ((f == Th(z_selfinsert) || f == Th(z_selfinsertunmeta)) && + !lastchar_wide_valid) { + int len; + VARARR(char, mbc, MB_CUR_MAX); + ZLE_INT_T inchar = getrestchar(lastchar, mbc, &len); + if (inchar != WEOF && len) { + char *ptr = mbc; + while (len--) + addkeybuf(STOUC(*ptr++)); + lastlen = keybuflen; + } + } +#endif } if (!ispfx) break; @@ -1521,6 +1535,20 @@ getkeymapcmd(Keymap km, Thingy *funcp, char **strp) return keybuf; } +/**/ +static void +addkeybuf(int c) +{ + if(keybuflen + 3 > keybufsz) + keybuf = realloc(keybuf, keybufsz *= 2); + if(imeta(c)) { + keybuf[keybuflen++] = Meta; + keybuf[keybuflen++] = c ^ 32; + } else + keybuf[keybuflen++] = c; + keybuf[keybuflen] = 0; +} + /* * Add a (possibly metafied) byte to the key input so far. * This handles individual bytes of a multibyte string separately; @@ -1542,14 +1570,7 @@ getkeybuf(int w) if(c < 0) return EOF; - if(keybuflen + 3 > keybufsz) - keybuf = realloc(keybuf, keybufsz *= 2); - if(imeta(c)) { - keybuf[keybuflen++] = Meta; - keybuf[keybuflen++] = c ^ 32; - } else - keybuf[keybuflen++] = c; - keybuf[keybuflen] = 0; + addkeybuf(c); return c; } diff --git a/Src/Zle/zle_main.c b/Src/Zle/zle_main.c index ec3d2c354..593d636cc 100644 --- a/Src/Zle/zle_main.c +++ b/Src/Zle/zle_main.c @@ -933,7 +933,7 @@ getfullchar(int do_keytmout) int inchar = getbyte((long)do_keytmout, NULL); #ifdef MULTIBYTE_SUPPORT - return getrestchar(inchar); + return getrestchar(inchar, NULL, NULL); #else return inchar; #endif @@ -951,7 +951,7 @@ getfullchar(int do_keytmout) /**/ mod_export ZLE_INT_T -getrestchar(int inchar) +getrestchar(int inchar, char *outstr, int *outcount) { char c = inchar; wchar_t outchar; @@ -965,6 +965,8 @@ getrestchar(int inchar) */ lastchar_wide_valid = 1; + if (outcount) + *outcount = 0; if (inchar == EOF) { /* End of input, so reset the shift state. */ memset(&mbs, 0, sizeof mbs); @@ -1013,6 +1015,10 @@ getrestchar(int inchar) return lastchar_wide = WEOF; } c = inchar; + if (outstr) { + *outstr++ = c; + (*outcount)++; + } } return lastchar_wide = (ZLE_INT_T)outchar; } @@ -1396,7 +1402,8 @@ execzlefunc(Thingy func, char **args, int set_bindk) opts[XTRACE] = oxt; sfcontext = osc; endparamscope(); - lastcmd = 0; + lastcmd = w->flags; + w->flags = 0; r = 1; redup(osi, 0); } @@ -1975,7 +1982,7 @@ zle_main_entry(int cmd, va_list ap) static struct builtin bintab[] = { BUILTIN("bindkey", 0, bin_bindkey, 0, -1, 0, "evaM:ldDANmrsLRp", NULL), BUILTIN("vared", 0, bin_vared, 1, 1, 0, "aAcef:hi:M:m:p:r:t:", NULL), - BUILTIN("zle", 0, bin_zle, 0, -1, 0, "aAcCDFgGIKlLmMNrRTUw", NULL), + BUILTIN("zle", 0, bin_zle, 0, -1, 0, "aAcCDfFgGIKlLmMNrRTUw", NULL), }; /* The order of the entries in this table has to match the *HOOK diff --git a/Src/Zle/zle_misc.c b/Src/Zle/zle_misc.c index 2d1862813..0483f758d 100644 --- a/Src/Zle/zle_misc.c +++ b/Src/Zle/zle_misc.c @@ -115,8 +115,9 @@ selfinsert(UNUSED(char **args)) ZLE_CHAR_T tmp; #ifdef MULTIBYTE_SUPPORT + /* may be redundant with getkeymapcmd(), but other widgets call here too */ if (!lastchar_wide_valid) - if (getrestchar(lastchar) == WEOF) + if (getrestchar(lastchar, NULL, NULL) == WEOF) return 1; #endif tmp = LASTFULLCHAR; @@ -787,12 +788,6 @@ bracketedpaste(char **args) zmult = 1; if (region_active) killregion(zlenoargs); - /* Chop a final newline if its insertion would be hard to - * distinguish by the user from the line being accepted. */ - else if (n > 1 && zlecontext != ZLCON_VARED && - (zlecs + (insmode ? 0 : n - 1)) >= zlell && - wpaste[n-1] == ZWC('\n')) - n--; yankcs = yankb = zlecs; doinsert(wpaste, n); yanke = zlecs; @@ -1431,7 +1426,7 @@ executenamedcommand(char *prmt) else { #ifdef MULTIBYTE_SUPPORT if (!lastchar_wide_valid) - getrestchar(lastchar); + getrestchar(lastchar, NULL, NULL); if (lastchar_wide == WEOF) feep = 1; else diff --git a/Src/Zle/zle_params.c b/Src/Zle/zle_params.c index 000bc388c..b5bb288f1 100644 --- a/Src/Zle/zle_params.c +++ b/Src/Zle/zle_params.c @@ -98,9 +98,9 @@ static const struct gsu_integer undo_change_no_gsu = static const struct gsu_integer undo_limit_no_gsu = { get_undo_limit_change, set_undo_limit_change, zleunsetfn }; static const struct gsu_integer yankstart_gsu = -{ get_yankstart, NULL, zleunsetfn }; +{ get_yankstart, set_yankstart, zleunsetfn }; static const struct gsu_integer yankend_gsu = -{ get_yankend, NULL, zleunsetfn }; +{ get_yankend, set_yankend, zleunsetfn }; static const struct gsu_integer yankactive_gsu = { get_yankactive, NULL, zleunsetfn }; @@ -149,8 +149,8 @@ static struct zleparam { { "WIDGET", PM_SCALAR | PM_READONLY, GSU(widget_gsu), NULL }, { "WIDGETFUNC", PM_SCALAR | PM_READONLY, GSU(widgetfunc_gsu), NULL }, { "WIDGETSTYLE", PM_SCALAR | PM_READONLY, GSU(widgetstyle_gsu), NULL }, - { "YANK_START", PM_INTEGER | PM_READONLY, GSU(yankstart_gsu), NULL }, - { "YANK_END", PM_INTEGER | PM_READONLY, GSU(yankend_gsu), NULL }, + { "YANK_START", PM_INTEGER, GSU(yankstart_gsu), NULL }, + { "YANK_END", PM_INTEGER, GSU(yankend_gsu), NULL }, { "YANK_ACTIVE", PM_INTEGER | PM_READONLY, GSU(yankactive_gsu), NULL }, { "ZLE_STATE", PM_SCALAR | PM_READONLY, GSU(zle_state_gsu), NULL }, { NULL, 0, NULL, NULL } @@ -503,7 +503,21 @@ get_yankend(UNUSED(Param pm)) static zlong get_yankactive(UNUSED(Param pm)) { - return lastcmd & ZLE_YANK; + return !!(lastcmd & ZLE_YANK) + !!(lastcmd & ZLE_YANKAFTER); +} + +/**/ +static void +set_yankstart(UNUSED(Param pm), zlong i) +{ + yankb = i; +} + +/**/ +static void +set_yankend(UNUSED(Param pm), zlong i) +{ + yanke = i; } /**/ diff --git a/Src/Zle/zle_refresh.c b/Src/Zle/zle_refresh.c index 0c28c0a2d..6facff429 100644 --- a/Src/Zle/zle_refresh.c +++ b/Src/Zle/zle_refresh.c @@ -372,7 +372,8 @@ zle_set_highlight(void) region_highlights[1].atr = TXTUNDERLINE; if (!suffix_atr_on_set) region_highlights[2].atr = TXTBOLDFACE; - /* paste defaults to 0 */ + if (!paste_atr_on_set) + region_highlights[3].atr = TXTSTANDOUT; allocate_colour_buffer(); } diff --git a/Src/Zle/zle_thingy.c b/Src/Zle/zle_thingy.c index 7fd3a5941..271fd8efc 100644 --- a/Src/Zle/zle_thingy.c +++ b/Src/Zle/zle_thingy.c @@ -352,6 +352,7 @@ bin_zle(char *name, char **args, Options ops, UNUSED(int func)) { 'U', bin_zle_unget, 1, 1 }, { 'K', bin_zle_keymap, 1, 1 }, { 'I', bin_zle_invalidate, 0, 0 }, + { 'f', bin_zle_flags, 1, -1 }, { 'F', bin_zle_fd, 0, 2 }, { 'T', bin_zle_transform, 0, 2}, { 0, bin_zle_call, 0, -1 }, @@ -466,7 +467,7 @@ bin_zle_mesg(char *name, char **args, UNUSED(Options ops), UNUSED(char func)) static int bin_zle_unget(char *name, char **args, UNUSED(Options ops), UNUSED(char func)) { - char *b = *args, *p = b + strlen(b); + char *b = unmeta(*args), *p = b + strlen(b); if (!zleactive) { zwarnnam(name, "can only be called from widget function"); @@ -625,7 +626,7 @@ bin_zle_complete(char *name, char **args, UNUSED(Options ops), UNUSED(char func) /**/ static int -zle_usable() +zle_usable(void) { return zleactive && !incompctlfunc && !incompfunc #if 0 @@ -642,6 +643,48 @@ zle_usable() /**/ static int +bin_zle_flags(char *name, char **args, UNUSED(Options ops), UNUSED(char func)) +{ + int ret = 0; + char **flag; + + if (!zle_usable()) { + zwarnnam(name, "can only set flags from a widget"); + return 1; + } + + if (bindk) { + Widget w = bindk->widget; + if (w) { + for (flag = args; *flag; flag++) { + if (!strcmp(*flag, "yank")) { + w->flags |= ZLE_YANKAFTER; + } else if (!strcmp(*flag, "yankbefore")) + w->flags |= ZLE_YANKBEFORE; + else if (!strcmp(*flag, "kill")) + w->flags |= ZLE_KILL; + /* + * These won't do anything yet, because of how execzlefunc + * handles user widgets + } else if (!strcmp(*flag, "menucmp")) + w->flags |= ZLE_MENUCMP; + else if (!strcmp(*flag, "linemove")) + w->flags |= ZLE_LINEMOVE; + else if (!strcmp(*flag, "keepsuffix")) + w->flags |= ZLE_KEEPSUFFIX; + */ + else { + zwarnnam(name, "invalid flag `%s' given to zle -f", *flag); + ret = 1; + } + } + } + } + return ret; +} + +/**/ +static int bin_zle_call(char *name, char **args, UNUSED(Options ops), UNUSED(char func)) { Thingy t; diff --git a/Src/Zle/zle_tricky.c b/Src/Zle/zle_tricky.c index b1a6f9e7e..cc4b7d673 100644 --- a/Src/Zle/zle_tricky.c +++ b/Src/Zle/zle_tricky.c @@ -1878,6 +1878,7 @@ get_comp_string(void) if (!isset(IGNOREBRACES)) { /* Try and deal with foo{xxx etc. */ + /*}*/ char *curs = s + (isset(COMPLETEINWORD) ? offs : (int)strlen(s)); char *predup = dupstring(s), *dp = predup; char *bbeg = NULL, *bend = NULL, *dbeg = NULL; @@ -1889,6 +1890,7 @@ get_comp_string(void) * we try to get braces after a parameter expansion right, * but this may fail sometimes. sorry. */ + /*}*/ if (*p == String || *p == Qstring) { if (p[1] == Inbrace || p[1] == Inpar || p[1] == Inbrack) { char *tp = p + 1; @@ -2223,7 +2225,7 @@ doexpansion(char *s, int lst, int olst, int explincmd) else if (*ts == '\'') *ts = Snull; addlinknode(vl, ss); - prefork(vl, 0); + prefork(vl, 0, NULL); if (errflag) goto end; if (lst == COMP_LIST_EXPAND || lst == COMP_EXPAND) { @@ -2952,6 +2954,9 @@ getcurcmd(void) return s; } +/* Run '$WIDGET $commandword' and then restore the command-line using push-line. + */ + /**/ int processcmd(UNUSED(char **args)) diff --git a/Src/Zle/zle_utils.c b/Src/Zle/zle_utils.c index 9751f7a1f..6e9a98bde 100644 --- a/Src/Zle/zle_utils.c +++ b/Src/Zle/zle_utils.c @@ -166,13 +166,13 @@ zlecharasstring(ZLE_CHAR_T inchar, char *buf) } /* - * Input a line in internal zle format, possibly using wide characters, + * Input: a line in internal zle format, possibly using wide characters, * possibly not, together with its length and the cursor position. * The length must be accurate and includes all characters (no NULL * termination is expected). The input cursor position is only * significant if outcs is non-NULL. * - * Output an ordinary NULL-terminated string, using multibyte characters + * Output: an ordinary NULL-terminated string, using multibyte characters * instead of wide characters where appropriate and with the contents * metafied. * @@ -1436,6 +1436,8 @@ freeundo(void) freechanges(changes); freechanges(nextchanges); zfree(lastline, lastlinesz); + lastline = NULL; + lastlinesz = 0; } /**/ @@ -1739,9 +1741,26 @@ zlecallhook(char *name, char *arg) zlong get_undo_current_change(UNUSED(Param pm)) { + int remetafy; + + /* + * Yuk: we call this from within the completion system, + * so we need to convert back to the form which can be + * copied into undo entries. + */ + if (zlemetaline != NULL) { + unmetafy_line(); + remetafy = 1; + } else + remetafy = 0; + /* add entry for any pending changes */ mkundoent(); setlastline(); + + if (remetafy) + metafy_line(); + return undo_changeno; } diff --git a/Src/Zle/zle_vi.c b/Src/Zle/zle_vi.c index 42dc46e7e..86840bdd6 100644 --- a/Src/Zle/zle_vi.c +++ b/Src/Zle/zle_vi.c @@ -151,7 +151,7 @@ vigetkey(void) #ifdef MULTIBYTE_SUPPORT if (!lastchar_wide_valid) { - getrestchar(lastchar); + getrestchar(lastchar, NULL, NULL); } #endif return LASTFULLCHAR; diff --git a/Src/builtin.c b/Src/builtin.c index 97022addf..01eb5b84c 100644 --- a/Src/builtin.c +++ b/Src/builtin.c @@ -58,7 +58,7 @@ static struct builtin builtins[] = BUILTIN("disable", 0, bin_enable, 0, -1, BIN_DISABLE, "afmprs", NULL), BUILTIN("disown", 0, bin_fg, 0, -1, BIN_DISOWN, NULL, NULL), BUILTIN("echo", BINF_SKIPINVALID, bin_print, 0, -1, BIN_ECHO, "neE", "-"), - BUILTIN("emulate", 0, bin_emulate, 0, -1, 0, "LR", NULL), + BUILTIN("emulate", 0, bin_emulate, 0, -1, 0, "lLR", NULL), BUILTIN("enable", 0, bin_enable, 0, -1, BIN_ENABLE, "afmprs", NULL), BUILTIN("eval", BINF_PSPECIAL, bin_eval, 0, -1, BIN_EVAL, NULL, NULL), BUILTIN("exit", BINF_PSPECIAL, bin_break, 0, 1, BIN_EXIT, NULL, NULL), @@ -2090,7 +2090,9 @@ typeset_single(char *cname, char *pname, Param pm, UNUSED(int func), tc = 0; /* but don't do a normal conversion */ } } else if (!setsecondstype(pm, on, off)) { - if (asg->value.scalar && !(pm = setsparam(pname, ztrdup(asg->value.scalar)))) + if (asg->value.scalar && + !(pm = assignsparam( + pname, ztrdup(asg->value.scalar), 0))) return NULL; usepm = 1; err = 0; @@ -2202,12 +2204,13 @@ typeset_single(char *cname, char *pname, Param pm, UNUSED(int func), } else if (pm->env && !(pm->node.flags & PM_HASHELEM)) delenv(pm); DPUTS(ASG_ARRAYP(asg), "BUG: typeset got array value where scalar expected"); - if (asg->value.scalar && !(pm = setsparam(pname, ztrdup(asg->value.scalar)))) + if (asg->value.scalar && + !(pm = assignsparam(pname, ztrdup(asg->value.scalar), 0))) return NULL; } else if (asg->is_array) { - if (!(pm = setaparam(pname, asg->value.array ? + if (!(pm = assignaparam(pname, asg->value.array ? zlinklist2array(asg->value.array) : - mkarray(NULL)))) + mkarray(NULL), 0))) return NULL; } pm->node.flags |= (on & PM_READONLY); @@ -2331,7 +2334,8 @@ typeset_single(char *cname, char *pname, Param pm, UNUSED(int func), } else if ((on & PM_LOCAL) && locallevel) { *subscript = 0; pm = (Param) (paramtab == realparamtab ? - gethashnode2(paramtab, pname) : + /* getnode2() to avoid autoloading */ + paramtab->getnode2(paramtab, pname) : paramtab->getnode(paramtab, pname)); *subscript = '['; if (!pm || pm->level != locallevel) { @@ -2347,16 +2351,18 @@ typeset_single(char *cname, char *pname, Param pm, UNUSED(int func), * creating a stray parameter along the way via createparam(), * now called below in the isident() branch. */ - if (!(pm = setsparam(pname, ztrdup(asg->value.scalar ? asg->value.scalar : "")))) + if (!(pm = assignsparam( + pname, + ztrdup(asg->value.scalar ? asg->value.scalar : ""), 0))) return NULL; dont_set = 1; asg->is_array = 0; keeplocal = 0; on = pm->node.flags; } else if (PM_TYPE(on) == PM_ARRAY && ASG_ARRAYP(asg)) { - if (!(pm = setaparam(pname, asg->value.array ? - zlinklist2array(asg->value.array) : - mkarray(NULL)))) + if (!(pm = assignaparam(pname, asg->value.array ? + zlinklist2array(asg->value.array) : + mkarray(NULL), 0))) return NULL; dont_set = 1; keeplocal = 0; @@ -2432,14 +2438,30 @@ typeset_single(char *cname, char *pname, Param pm, UNUSED(int func), if (ASG_VALUEP(asg) && !dont_set) { Param ipm = pm; if (pm->node.flags & (PM_ARRAY|PM_HASHED)) { - DPUTS(!ASG_ARRAYP(asg), "BUG: inconsistent scalar value for array"); - if (!(pm=setaparam(pname, asg->value.array ? - zlinklist2array(asg->value.array) : - mkarray(NULL)))) + char **arrayval; + if (!ASG_ARRAYP(asg)) { + /* + * Attempt to assign a scalar value to an array. + * This can happen if the array is special. + * We'll be lenient and guess what the user meant. + * This is how normal assigment works. + */ + if (*asg->value.scalar) { + /* Array with one value */ + arrayval = mkarray(ztrdup(asg->value.scalar)); + } else { + /* Empty array */ + arrayval = mkarray(NULL); + } + } else if (asg->value.array) + arrayval = zlinklist2array(asg->value.array); + else + arrayval = mkarray(NULL); + if (!(pm=assignaparam(pname, arrayval, 0))) return NULL; } else { DPUTS(ASG_ARRAYP(asg), "BUG: inconsistent array value for scalar"); - if (!(pm = setsparam(pname, ztrdup(asg->value.scalar)))) + if (!(pm = assignsparam(pname, ztrdup(asg->value.scalar), 0))) return NULL; } if (pm != ipm) { @@ -2687,9 +2709,10 @@ bin_typeset(char *name, char **argv, LinkList assigns, Options ops, int func) /* Update join character */ tdp->joinchar = joinchar; if (asg0.value.scalar) - setsparam(asg0.name, ztrdup(asg0.value.scalar)); + assignsparam(asg0.name, ztrdup(asg0.value.scalar), 0); else if (asg->value.array) - setaparam(asg->name, zlinklist2array(asg->value.array)); + assignaparam( + asg->name, zlinklist2array(asg->value.array), 0); return 0; } else { zwarnnam(name, "can't tie already tied scalar: %s", @@ -2750,9 +2773,9 @@ bin_typeset(char *name, char **argv, LinkList assigns, Options ops, int func) zsfree(apm->ename); apm->ename = ztrdup(asg0.name); if (asg->value.array) - setaparam(asg->name, zlinklist2array(asg->value.array)); + assignaparam(asg->name, zlinklist2array(asg->value.array), 0); else if (oldval) - setsparam(asg0.name, oldval); + assignsparam(asg0.name, oldval, 0); unqueue_signals(); return 0; @@ -2819,11 +2842,12 @@ bin_typeset(char *name, char **argv, LinkList assigns, Options ops, int func) /* Take arguments literally. Don't glob */ while ((asg = getasg(&argv, assigns))) { HashNode hn = (paramtab == realparamtab ? - gethashnode2(paramtab, asg->name) : + /* getnode2() to avoid autoloading */ + paramtab->getnode2(paramtab, asg->name) : paramtab->getnode(paramtab, asg->name)); if (OPT_ISSET(ops,'p')) { if (hn) - printparamnode(hn, printflags); + paramtab->printnode(hn, printflags); else { zwarnnam(name, "no such variable: %s", asg->name); returnval = 1; @@ -3313,7 +3337,8 @@ bin_unset(char *name, char **argv, Options ops, int func) *ss = 0; } pm = (Param) (paramtab == realparamtab ? - gethashnode2(paramtab, s) : + /* getnode2() to avoid autoloading */ + paramtab->getnode2(paramtab, s) : paramtab->getnode(paramtab, s)); /* * Unsetting an unset variable is not an error. @@ -5419,10 +5444,11 @@ eval(char **argv) /**/ int -bin_emulate(UNUSED(char *nam), char **argv, Options ops, UNUSED(int func)) +bin_emulate(char *nam, char **argv, Options ops, UNUSED(int func)) { int opt_L = OPT_ISSET(ops, 'L'); int opt_R = OPT_ISSET(ops, 'R'); + int opt_l = OPT_ISSET(ops, 'l'); int saveemulation, savehackchar; int ret = 1, new_emulation; unsigned int savepatterns; @@ -5437,7 +5463,7 @@ bin_emulate(UNUSED(char *nam), char **argv, Options ops, UNUSED(int func)) /* without arguments just print current emulation */ if (!shname) { if (opt_L || opt_R) { - zwarnnam("emulate", "not enough arguments"); + zwarnnam(nam, "not enough arguments"); return 1; } @@ -5465,27 +5491,43 @@ bin_emulate(UNUSED(char *nam), char **argv, Options ops, UNUSED(int func)) /* with single argument set current emulation */ if (!argv[1]) { - emulate(shname, opt_R, &emulation, opts); + char *cmdopts; + if (opt_l) { + cmdopts = (char *)zhalloc(OPT_SIZE); + memcpy(cmdopts, opts, OPT_SIZE); + } else + cmdopts = opts; + emulate(shname, opt_R, &emulation, cmdopts); if (opt_L) - opts[LOCALOPTIONS] = opts[LOCALTRAPS] = opts[LOCALPATTERNS] = 1; + cmdopts[LOCALOPTIONS] = cmdopts[LOCALTRAPS] = + cmdopts[LOCALPATTERNS] = 1; + if (opt_l) { + list_emulate_options(cmdopts, opt_R); + return 0; + } clearpatterndisables(); return 0; } + if (opt_l) { + zwarnnam(nam, "too many arguments for -l"); + return 1; + } + argv++; memcpy(saveopts, opts, sizeof(opts)); memcpy(new_opts, opts, sizeof(opts)); savehackchar = keyboardhackchar; emulate(shname, opt_R, &new_emulation, new_opts); optlist = newlinklist(); - if (parseopts("emulate", &argv, new_opts, &cmd, optlist)) { + if (parseopts(nam, &argv, new_opts, &cmd, optlist)) { ret = 1; goto restore; } /* parseopts() has consumed anything that looks like an option */ if (*argv) { - zwarnnam("emulate", "unknown argument %s", *argv); + zwarnnam(nam, "unknown argument %s", *argv); goto restore; } @@ -5504,7 +5546,7 @@ bin_emulate(UNUSED(char *nam), char **argv, Options ops, UNUSED(int func)) */ if (cmd) { if (opt_L) { - zwarnnam("emulate", "option -L incompatible with -c"); + zwarnnam(nam, "option -L incompatible with -c"); goto restore2; } *--argv = cmd; /* on stack, never free()d, see execbuiltin() */ diff --git a/Src/cond.c b/Src/cond.c index df9065660..c5ab65eea 100644 --- a/Src/cond.c +++ b/Src/cond.c @@ -43,7 +43,7 @@ static void cond_subst(char **strp, int glob_ok) checkglobqual(*strp, strlen(*strp), 1, NULL)) { LinkList args = newlinklist(); addlinknode(args, *strp); - prefork(args, 0); + prefork(args, 0, NULL); while (!errflag && args && nonempty(args) && has_token((char *)peekfirst(args))) zglob(args, firstnode(args), 0); diff --git a/Src/exec.c b/Src/exec.c index 109a04a26..c0ee527b7 100644 --- a/Src/exec.c +++ b/Src/exec.c @@ -176,7 +176,8 @@ mod_export int sfcontext; /**/ struct execstack *exstack; -/* Stack with names of functions currently active. */ +/* Stack with names of function calls, 'source' calls, and 'eval' calls + * currently active. */ /**/ mod_export Funcstack funcstack; @@ -1363,7 +1364,8 @@ sublist_done: * we hit execcmd on the way down. We're now * on the way back up, so don't restore it. */ - noerrexit = (oldnoerrexit == 2) ? 0 : oldnoerrexit; + if (oldnoerrexit != 2) + noerrexit = oldnoerrexit; if (sigtrapped[SIGDEBUG] && !isset(DEBUGBEFORECMD) && !donedebug) { /* @@ -2288,7 +2290,7 @@ addvars(Estate state, Wordcode pc, int addflags) if (vl && htok) { prefork(vl, (isstr ? (PREFORK_SINGLE|PREFORK_ASSIGN) : - PREFORK_ASSIGN)); + PREFORK_ASSIGN), NULL); if (errflag) { state->pc = opc; return; @@ -2414,7 +2416,7 @@ void execsubst(LinkList strs) { if (strs) { - prefork(strs, esprefork); + prefork(strs, esprefork, NULL); if (esglob && !errflag) { LinkList ostrs = strs; globlist(strs, 0); @@ -2719,7 +2721,7 @@ execcmd(Estate state, int input, int output, int how, int last1) /* Do prefork substitutions */ esprefork = (assign || isset(MAGICEQUALSUBST)) ? PREFORK_TYPESET : 0; if (args && htok) - prefork(args, esprefork); + prefork(args, esprefork, NULL); if (type == WC_SIMPLE || type == WC_TYPESET) { int unglobbed = 0; @@ -3556,7 +3558,7 @@ execcmd(Estate state, int input, int output, int how, int last1) */ /* Unused dummy value for name */ (void)ecgetstr(state, EC_DUPTOK, &htok); - prefork(&svl, PREFORK_TYPESET); + prefork(&svl, PREFORK_TYPESET, NULL); if (errflag) { state->pc = opc; break; @@ -3582,7 +3584,7 @@ execcmd(Estate state, int input, int output, int how, int last1) } continue; } - prefork(&svl, PREFORK_SINGLE); + prefork(&svl, PREFORK_SINGLE, NULL); name = empty(&svl) ? "" : (char *)getdata(firstnode(&svl)); } @@ -3598,7 +3600,9 @@ execcmd(Estate state, int input, int output, int how, int last1) } else { if (htok) { init_list1(svl, val); - prefork(&svl, PREFORK_SINGLE|PREFORK_ASSIGN); + prefork(&svl, + PREFORK_SINGLE|PREFORK_ASSIGN, + NULL); if (errflag) { state->pc = opc; break; @@ -3620,7 +3624,7 @@ execcmd(Estate state, int input, int output, int how, int last1) EC_DUPTOK, &htok); if (asg->value.array) { - prefork(asg->value.array, PREFORK_ASSIGN); + prefork(asg->value.array, PREFORK_ASSIGN, NULL); if (errflag) { state->pc = opc; break; @@ -5065,230 +5069,230 @@ doshfunc(Shfunc shfunc, LinkList doshargs, int noreturnval) #ifdef MAX_FUNCTION_DEPTH static int funcdepth; #endif + Heap funcheap; queue_signals(); /* Lots of memory and global state changes coming */ - pushheap(); + NEWHEAPS(funcheap) { + oargv0 = NULL; + obreaks = breaks; + ocontflag = contflag; + oloops = loops; + if (trap_state == TRAP_STATE_PRIMED) + trap_return--; + oldlastval = lastval; + oldnumpipestats = numpipestats; + if (noreturnval) { + /* + * Easiest to use the heap here since we're bracketed + * immediately by a pushheap/popheap pair. + */ + size_t bytes = sizeof(int)*numpipestats; + oldpipestats = (int *)zhalloc(bytes); + memcpy(oldpipestats, pipestats, bytes); + } - oargv0 = NULL; - obreaks = breaks; - ocontflag = contflag; - oloops = loops; - if (trap_state == TRAP_STATE_PRIMED) - trap_return--; - oldlastval = lastval; - oldnumpipestats = numpipestats; - if (noreturnval) { - /* - * Easiest to use the heap here since we're bracketed - * immediately by a pushheap/popheap pair. - */ - size_t bytes = sizeof(int)*numpipestats; - oldpipestats = (int *)zhalloc(bytes); - memcpy(oldpipestats, pipestats, bytes); - } + starttrapscope(); + startpatternscope(); + + pptab = pparams; + if (!(flags & PM_UNDEFINED)) + scriptname = dupstring(name); + oldzoptind = zoptind; + oldoptcind = optcind; + if (!isset(POSIXBUILTINS)) { + zoptind = 1; + optcind = 0; + } - starttrapscope(); - startpatternscope(); - - pptab = pparams; - if (!(flags & PM_UNDEFINED)) - scriptname = dupstring(name); - oldzoptind = zoptind; - oldoptcind = optcind; - if (!isset(POSIXBUILTINS)) { - zoptind = 1; - optcind = 0; - } + /* We need to save the current options even if LOCALOPTIONS is * + * not currently set. That's because if it gets set in the * + * function we need to restore the original options on exit. */ + memcpy(saveopts, opts, sizeof(opts)); + saveemulation = emulation; + save_sticky = sticky; - /* We need to save the current options even if LOCALOPTIONS is * - * not currently set. That's because if it gets set in the * - * function we need to restore the original options on exit. */ - memcpy(saveopts, opts, sizeof(opts)); - saveemulation = emulation; - save_sticky = sticky; + if (sticky_emulation_differs(shfunc->sticky)) { + /* + * Function is marked for sticky emulation. + * Enable it now. + * + * We deliberately do not do this if the sticky emulation + * in effect is the same as that requested. This enables + * option setting naturally within emulation environments. + * Note that a difference in EMULATE_FULLY (emulate with + * or without -R) counts as a different environment. + * + * This propagates the sticky emulation to subfunctions. + */ + sticky = sticky_emulation_dup(shfunc->sticky, 1); + emulation = sticky->emulation; + restore_sticky = 1; + installemulation(emulation, opts); + if (sticky->n_on_opts) { + OptIndex *onptr; + for (onptr = sticky->on_opts; + onptr < sticky->on_opts + sticky->n_on_opts; + onptr++) + opts[*onptr] = 1; + } + if (sticky->n_off_opts) { + OptIndex *offptr; + for (offptr = sticky->off_opts; + offptr < sticky->off_opts + sticky->n_off_opts; + offptr++) + opts[*offptr] = 0; + } + /* All emulations start with pattern disables clear */ + clearpatterndisables(); + } else + restore_sticky = 0; - if (sticky_emulation_differs(shfunc->sticky)) { + if (flags & (PM_TAGGED|PM_TAGGED_LOCAL)) + opts[XTRACE] = 1; + else if (oflags & PM_TAGGED_LOCAL) + opts[XTRACE] = 0; + ooflags = oflags; /* - * Function is marked for sticky emulation. - * Enable it now. - * - * We deliberately do not do this if the sticky emulation - * in effect is the same as that requested. This enables - * option setting naturally within emulation environments. - * Note that a difference in EMULATE_FULLY (emulate with - * or without -R) counts as a different environment. - * - * This propagates the sticky emulation to subfunctions. + * oflags is static, because we compare it on the next recursive + * call. Hence also we maintain ooflags for restoring the previous + * value of oflags after the call. */ - sticky = sticky_emulation_dup(shfunc->sticky, 1); - emulation = sticky->emulation; - restore_sticky = 1; - installemulation(emulation, opts); - if (sticky->n_on_opts) { - OptIndex *onptr; - for (onptr = sticky->on_opts; - onptr < sticky->on_opts + sticky->n_on_opts; - onptr++) - opts[*onptr] = 1; - } - if (sticky->n_off_opts) { - OptIndex *offptr; - for (offptr = sticky->off_opts; - offptr < sticky->off_opts + sticky->n_off_opts; - offptr++) - opts[*offptr] = 0; - } - /* All emulations start with pattern disables clear */ - clearpatterndisables(); - } else - restore_sticky = 0; - - if (flags & (PM_TAGGED|PM_TAGGED_LOCAL)) - opts[XTRACE] = 1; - else if (oflags & PM_TAGGED_LOCAL) - opts[XTRACE] = 0; - ooflags = oflags; - /* - * oflags is static, because we compare it on the next recursive - * call. Hence also we maintain ooflags for restoring the previous - * value of oflags after the call. - */ - oflags = flags; - opts[PRINTEXITVALUE] = 0; - if (doshargs) { - LinkNode node; - - node = firstnode(doshargs); - pparams = x = (char **) zshcalloc(((sizeof *x) * - (1 + countlinknodes(doshargs)))); - if (isset(FUNCTIONARGZERO)) { - oargv0 = argzero; - argzero = ztrdup(getdata(node)); - } - /* first node contains name regardless of option */ - node = node->next; - for (; node; node = node->next, x++) - *x = ztrdup(getdata(node)); - } else { - pparams = (char **) zshcalloc(sizeof *pparams); - if (isset(FUNCTIONARGZERO)) { - oargv0 = argzero; - argzero = ztrdup(argzero); + oflags = flags; + opts[PRINTEXITVALUE] = 0; + if (doshargs) { + LinkNode node; + + node = firstnode(doshargs); + pparams = x = (char **) zshcalloc(((sizeof *x) * + (1 + countlinknodes(doshargs)))); + if (isset(FUNCTIONARGZERO)) { + oargv0 = argzero; + argzero = ztrdup(getdata(node)); + } + /* first node contains name regardless of option */ + node = node->next; + for (; node; node = node->next, x++) + *x = ztrdup(getdata(node)); + } else { + pparams = (char **) zshcalloc(sizeof *pparams); + if (isset(FUNCTIONARGZERO)) { + oargv0 = argzero; + argzero = ztrdup(argzero); + } } - } #ifdef MAX_FUNCTION_DEPTH - if(++funcdepth > MAX_FUNCTION_DEPTH) - { - zerr("maximum nested function level reached"); - goto undoshfunc; - } + if(++funcdepth > MAX_FUNCTION_DEPTH) + { + zerr("maximum nested function level reached"); + goto undoshfunc; + } #endif - fstack.name = dupstring(name); - /* - * The caller is whatever is immediately before on the stack, - * unless we're at the top, in which case it's the script - * or interactive shell name. - */ - fstack.caller = funcstack ? funcstack->name : - dupstring(oargv0 ? oargv0 : argzero); - fstack.lineno = lineno; - fstack.prev = funcstack; - fstack.tp = FS_FUNC; - funcstack = &fstack; - - fstack.flineno = shfunc->lineno; - fstack.filename = dupstring(shfunc->filename); - - prog = shfunc->funcdef; - if (prog->flags & EF_RUN) { - Shfunc shf; + fstack.name = dupstring(name); + /* + * The caller is whatever is immediately before on the stack, + * unless we're at the top, in which case it's the script + * or interactive shell name. + */ + fstack.caller = funcstack ? funcstack->name : + dupstring(oargv0 ? oargv0 : argzero); + fstack.lineno = lineno; + fstack.prev = funcstack; + fstack.tp = FS_FUNC; + funcstack = &fstack; - prog->flags &= ~EF_RUN; + fstack.flineno = shfunc->lineno; + fstack.filename = dupstring(shfunc->filename); - runshfunc(prog, NULL, fstack.name); + prog = shfunc->funcdef; + if (prog->flags & EF_RUN) { + Shfunc shf; - if (!(shf = (Shfunc) shfunctab->getnode(shfunctab, - (name = fname)))) { - zwarn("%s: function not defined by file", name); - if (noreturnval) - errflag |= ERRFLAG_ERROR; - else - lastval = 1; - goto doneshfunc; + prog->flags &= ~EF_RUN; + + runshfunc(prog, NULL, fstack.name); + + if (!(shf = (Shfunc) shfunctab->getnode(shfunctab, + (name = fname)))) { + zwarn("%s: function not defined by file", name); + if (noreturnval) + errflag |= ERRFLAG_ERROR; + else + lastval = 1; + goto doneshfunc; + } + prog = shf->funcdef; } - prog = shf->funcdef; - } - runshfunc(prog, wrappers, fstack.name); - doneshfunc: - funcstack = fstack.prev; + runshfunc(prog, wrappers, fstack.name); + doneshfunc: + funcstack = fstack.prev; #ifdef MAX_FUNCTION_DEPTH - undoshfunc: - --funcdepth; + undoshfunc: + --funcdepth; #endif - if (retflag) { - retflag = 0; - breaks = obreaks; - } - freearray(pparams); - if (oargv0) { - zsfree(argzero); - argzero = oargv0; - } - pparams = pptab; - if (!isset(POSIXBUILTINS)) { - zoptind = oldzoptind; - optcind = oldoptcind; - } - scriptname = oldscriptname; - oflags = ooflags; + if (retflag) { + retflag = 0; + breaks = obreaks; + } + freearray(pparams); + if (oargv0) { + zsfree(argzero); + argzero = oargv0; + } + pparams = pptab; + if (!isset(POSIXBUILTINS)) { + zoptind = oldzoptind; + optcind = oldoptcind; + } + scriptname = oldscriptname; + oflags = ooflags; - endpatternscope(); /* before restoring old LOCALPATTERNS */ + endpatternscope(); /* before restoring old LOCALPATTERNS */ - if (restore_sticky) { - /* - * If we switched to an emulation environment just for - * this function, we interpret the option and emulation - * switch as being a firewall between environments. - */ - memcpy(opts, saveopts, sizeof(opts)); - emulation = saveemulation; - sticky = save_sticky; - } else if (isset(LOCALOPTIONS)) { - /* restore all shell options except PRIVILEGED and RESTRICTED */ - saveopts[PRIVILEGED] = opts[PRIVILEGED]; - saveopts[RESTRICTED] = opts[RESTRICTED]; - memcpy(opts, saveopts, sizeof(opts)); - emulation = saveemulation; - } else { - /* just restore a couple. */ - opts[XTRACE] = saveopts[XTRACE]; - opts[PRINTEXITVALUE] = saveopts[PRINTEXITVALUE]; - opts[LOCALOPTIONS] = saveopts[LOCALOPTIONS]; - opts[LOCALLOOPS] = saveopts[LOCALLOOPS]; - } + if (restore_sticky) { + /* + * If we switched to an emulation environment just for + * this function, we interpret the option and emulation + * switch as being a firewall between environments. + */ + memcpy(opts, saveopts, sizeof(opts)); + emulation = saveemulation; + sticky = save_sticky; + } else if (isset(LOCALOPTIONS)) { + /* restore all shell options except PRIVILEGED and RESTRICTED */ + saveopts[PRIVILEGED] = opts[PRIVILEGED]; + saveopts[RESTRICTED] = opts[RESTRICTED]; + memcpy(opts, saveopts, sizeof(opts)); + emulation = saveemulation; + } else { + /* just restore a couple. */ + opts[XTRACE] = saveopts[XTRACE]; + opts[PRINTEXITVALUE] = saveopts[PRINTEXITVALUE]; + opts[LOCALOPTIONS] = saveopts[LOCALOPTIONS]; + opts[LOCALLOOPS] = saveopts[LOCALLOOPS]; + } - if (opts[LOCALLOOPS]) { - if (contflag) - zwarn("`continue' active at end of function scope"); - if (breaks) - zwarn("`break' active at end of function scope"); - breaks = obreaks; - contflag = ocontflag; - loops = oloops; - } + if (opts[LOCALLOOPS]) { + if (contflag) + zwarn("`continue' active at end of function scope"); + if (breaks) + zwarn("`break' active at end of function scope"); + breaks = obreaks; + contflag = ocontflag; + loops = oloops; + } - endtrapscope(); + endtrapscope(); - if (trap_state == TRAP_STATE_PRIMED) - trap_return++; - ret = lastval; - if (noreturnval) { - lastval = oldlastval; - numpipestats = oldnumpipestats; - memcpy(pipestats, oldpipestats, sizeof(int)*numpipestats); - } - popheap(); + if (trap_state == TRAP_STATE_PRIMED) + trap_return++; + ret = lastval; + if (noreturnval) { + lastval = oldlastval; + numpipestats = oldnumpipestats; + memcpy(pipestats, oldpipestats, sizeof(int)*numpipestats); + } + } OLDHEAPS; unqueue_signals(); diff --git a/Src/glob.c b/Src/glob.c index dea1bf50e..94b3f620d 100644 --- a/Src/glob.c +++ b/Src/glob.c @@ -634,8 +634,10 @@ scanner(Complist q, int shortcircuit) } else { /* if the last filename component, just add it */ insert(fn, 1); - if (shortcircuit && shortcircuit == matchct) + if (shortcircuit && shortcircuit == matchct) { + closedir(lock); return; + } } } } @@ -680,25 +682,32 @@ parsecomplist(char *instr) char *str; int compflags = gf_noglobdots ? (PAT_FILE|PAT_NOGLD) : PAT_FILE; - if (instr[0] == Star && instr[1] == Star && - (instr[2] == '/' || (instr[2] == Star && instr[3] == '/'))) { - /* Match any number of directories. */ - int follow; + if (instr[0] == Star && instr[1] == Star) { + int shortglob = 0; + if (instr[2] == '/' || (instr[2] == Star && instr[3] == '/') + || (shortglob = isset(GLOBSTARSHORT))) { + /* Match any number of directories. */ + int follow; - /* with three stars, follow symbolic links */ - follow = (instr[2] == Star); - instr += (3 + follow); + /* with three stars, follow symbolic links */ + follow = (instr[2] == Star); + /* + * With GLOBSTARSHORT, leave a star in place for the + * pattern inside the directory. + */ + instr += ((shortglob ? 1 : 3) + follow); - /* Now get the next path component if there is one. */ - l1 = (Complist) zhalloc(sizeof *l1); - if ((l1->next = parsecomplist(instr)) == NULL) { - errflag |= ERRFLAG_ERROR; - return NULL; + /* Now get the next path component if there is one. */ + l1 = (Complist) zhalloc(sizeof *l1); + if ((l1->next = parsecomplist(instr)) == NULL) { + errflag |= ERRFLAG_ERROR; + return NULL; + } + l1->pat = patcompile(NULL, compflags | PAT_ANY, NULL); + l1->closure = 1; /* ...zero or more times. */ + l1->follow = follow; + return l1; } - l1->pat = patcompile(NULL, compflags | PAT_ANY, NULL); - l1->closure = 1; /* ...zero or more times. */ - l1->follow = follow; - return l1; } /* Parse repeated directories such as (dir/)# and (dir/)## */ @@ -2084,7 +2093,7 @@ xpandredir(struct redir *fn, LinkList redirtab) /* Stick the name in a list... */ init_list1(fake, fn->name); /* ...which undergoes all the usual shell expansions */ - prefork(&fake, isset(MULTIOS) ? 0 : PREFORK_SINGLE); + prefork(&fake, isset(MULTIOS) ? 0 : PREFORK_SINGLE, NULL); /* Globbing is only done for multios. */ if (!errflag && isset(MULTIOS)) globlist(&fake, 0); @@ -2448,29 +2457,43 @@ matchpat(char *a, char *b) /* please do not laugh at this code. */ /* Having found a match in getmatch, decide what part of string - * to return. The matched part starts b characters into string s - * and finishes e characters in: 0 <= b <= e <= strlen(s) + * to return. The matched part starts b characters into string imd->ustr + * and finishes e characters in: 0 <= b <= e <= imd->ulen on input * (yes, empty matches should work). - * fl is a set of the SUB_* matches defined in zsh.h from SUB_MATCH onwards; - * the lower parts are ignored. - * replstr is the replacement string for a substitution + * + * imd->flags is a set of the SUB_* matches defined in zsh.h from + * SUB_MATCH onwards; the lower parts are ignored. + * + * imd->replstr is the replacement string for a substitution + * + * imd->replstr is metafied and the values put in imd->repllist are metafied. */ /**/ static char * -get_match_ret(char *s, int b, int e, int fl, char *replstr, - LinkList repllist) +get_match_ret(Imatchdata imd, int b, int e) { - char buf[80], *r, *p, *rr; - int ll = 0, l = strlen(s), bl = 0, t = 0, i; - + char buf[80], *r, *p, *rr, *replstr = imd->replstr; + int ll = 0, bl = 0, t = 0, add = 0, fl = imd->flags, i; + + /* Account for b and e referring to unmetafied string */ + for (p = imd->ustr; p < imd->ustr + b; p++) + if (imeta(*p)) + add++; + b += add; + for (; p < imd->ustr + e; p++) + if (imeta(*p)) + add++; + e += add; + + /* Everything now refers to metafied lengths. */ if (replstr || (fl & SUB_LIST)) { if (fl & SUB_DOSUBST) { replstr = dupstring(replstr); singsub(&replstr); untokenize(replstr); } - if ((fl & (SUB_GLOBAL|SUB_LIST)) && repllist) { + if ((fl & (SUB_GLOBAL|SUB_LIST)) && imd->repllist) { /* We are replacing the chunk, just add this to the list */ Repldata rd = (Repldata) ((fl & SUB_LIST) ? zalloc(sizeof(*rd)) : zhalloc(sizeof(*rd))); @@ -2478,30 +2501,32 @@ get_match_ret(char *s, int b, int e, int fl, char *replstr, rd->e = e; rd->replstr = replstr; if (fl & SUB_LIST) - zaddlinknode(repllist, rd); + zaddlinknode(imd->repllist, rd); else - addlinknode(repllist, rd); - return s; + addlinknode(imd->repllist, rd); + return imd->mstr; } ll += strlen(replstr); } if (fl & SUB_MATCH) /* matched portion */ ll += 1 + (e - b); if (fl & SUB_REST) /* unmatched portion */ - ll += 1 + (l - (e - b)); + ll += 1 + (imd->mlen - (e - b)); if (fl & SUB_BIND) { /* position of start of matched portion */ - sprintf(buf, "%d ", b + 1); + sprintf(buf, "%d ", MB_METASTRLEN2END(imd->mstr, 0, imd->mstr+b) + 1); ll += (bl = strlen(buf)); } if (fl & SUB_EIND) { /* position of end of matched portion */ - sprintf(buf + bl, "%d ", e + 1); + sprintf(buf + bl, "%d ", + MB_METASTRLEN2END(imd->mstr, 0, imd->mstr+e) + 1); ll += (bl = strlen(buf)); } if (fl & SUB_LEN) { /* length of matched portion */ - sprintf(buf + bl, "%d ", e - b); + sprintf(buf + bl, "%d ", MB_METASTRLEN2END(imd->mstr+b, 0, + imd->mstr+e)); ll += (bl = strlen(buf)); } if (bl) @@ -2511,7 +2536,7 @@ get_match_ret(char *s, int b, int e, int fl, char *replstr, if (fl & SUB_MATCH) { /* copy matched portion to new buffer */ - for (i = b, p = s + b; i < e; i++) + for (i = b, p = imd->mstr + b; i < e; i++) *rr++ = *p++; t = 1; } @@ -2521,12 +2546,12 @@ get_match_ret(char *s, int b, int e, int fl, char *replstr, if (t) *rr++ = ' '; /* there may be unmatched bits at both beginning and end of string */ - for (i = 0, p = s; i < b; i++) + for (i = 0, p = imd->mstr; i < b; i++) *rr++ = *p++; if (replstr) for (p = replstr; *p; ) *rr++ = *p++; - for (i = e, p = s + e; i < l; i++) + for (i = e, p = imd->mstr + e; i < imd->mlen; i++) *rr++ = *p++; t = 1; } @@ -2708,26 +2733,18 @@ set_pat_end(Patprog p, char null_me) /* * Increment *tp over character which may be multibyte. - * Return number of bytes that remain in the character after unmetafication. + * Return number of bytes. + * All unmetafied here. */ /**/ -static int iincchar(char **tp) +static int iincchar(char **tp, int left) { char *t = *tp; - int mbclen = mb_metacharlenconv(t, NULL); - int umlen = 0; - - while (mbclen--) { - umlen++; - if (*t++ == Meta) { - t++; - mbclen--; - } - } - *tp = t; + int mbclen = mb_charlenconv(t, left, NULL); + *tp = t + mbclen; - return umlen; + return mbclen; } /**/ @@ -2735,7 +2752,7 @@ static int igetmatch(char **sp, Patprog p, int fl, int n, char *replstr, LinkList *repllistp) { - char *s = *sp, *t, *tmatch; + char *s = *sp, *t, *tmatch, *send; /* * Note that ioff counts (possibly multibyte) characters in the * character set (Meta's are not included), while l counts characters in @@ -2750,36 +2767,51 @@ igetmatch(char **sp, Patprog p, int fl, int n, char *replstr, */ int ioff, l = strlen(*sp), matched = 1, umltot = ztrlen(*sp); int umlen, nmatches; - /* - * List of bits of matches to concatenate with replacement string. - * The data is a struct repldata. It is not used in cases like - * ${...//#foo/bar} even though SUB_GLOBAL is set, since the match - * is anchored. It goes on the heap. - */ - LinkList repllist = NULL; + struct patstralloc patstralloc; + struct imatchdata imd; + + (void)patallocstr(p, s, l, umltot, 1, &patstralloc); + s = patstralloc.alloced; + DPUTS(!s, "forced patallocstr failed"); + send = s + umltot; + + imd.mstr = *sp; + imd.mlen = l; + imd.ustr = s; + imd.ulen = umltot; + imd.flags = fl; + imd.replstr = replstr; + imd.repllist = NULL; /* perform must-match test for complex closures */ if (p->mustoff) { - /* - * Yuk. Probably we should rewrite this whole function to - * use an unmetafied test string. - * - * Use META_HEAPDUP because we need a terminating NULL. - */ - char *muststr = metafy((char *)p + p->mustoff, - p->patmlen, META_HEAPDUP); + char *muststr = (char *)p + p->mustoff; - if (!strstr(s, muststr)) - matched = 0; + matched = 0; + if (p->patmlen <= umltot) + { + for (t = s; t <= send - p->patmlen; t++) + { + if (!memcmp(muststr, t, p->patmlen)) { + matched = 1; + break; + } + } + } } /* in case we used the prog before... */ p->flags &= ~(PAT_NOTSTART|PAT_NOTEND); if (fl & SUB_ALL) { - int i = matched && pattry(p, s); - *sp = get_match_ret(*sp, 0, i ? l : 0, fl, i ? replstr : 0, NULL); + int i = matched && pattrylen(p, s, umltot, 0, &patstralloc, 0); + if (!i) { + /* Perform under no-match conditions */ + umltot = 0; + imd.replstr = NULL; + } + *sp = get_match_ret(&imd, 0, umltot); if (! **sp && (((fl & SUB_MATCH) && !i) || ((fl & SUB_REST) && i))) return 0; return 1; @@ -2807,25 +2839,26 @@ igetmatch(char **sp, Patprog p, int fl, int n, char *replstr, * Largest/smallest possible match at head of string. * First get the longest match... */ - if (pattry(p, s)) { - /* patmatchlen returns metafied length, as we need */ + if (pattrylen(p, s, umltot, 0, &patstralloc, 0)) { + /* patmatchlen returns unmetafied length in this case */ int mlen = patmatchlen(); if (!(fl & SUB_LONG) && !(p->flags & PAT_PURES)) { + send = s + mlen; /* * ... now we know whether it's worth looking for the * shortest, which we do by brute force. */ mb_charinit(); - for (t = s, umlen = 0; t < s + mlen; ) { + for (t = s, umlen = 0; t < send; ) { set_pat_end(p, *t); - if (pattrylen(p, s, t - s, umlen, 0)) { + if (pattrylen(p, s, umlen, 0, &patstralloc, 0)) { mlen = patmatchlen(); break; } - umlen += iincchar(&t); + umlen += iincchar(&t, send - t); } } - *sp = get_match_ret(*sp, 0, mlen, fl, replstr, NULL); + *sp = get_match_ret(&imd, 0, mlen); return 1; } break; @@ -2843,20 +2876,21 @@ igetmatch(char **sp, Patprog p, int fl, int n, char *replstr, */ mb_charinit(); tmatch = NULL; - for (ioff = 0, t = s, umlen = umltot; t < s + l; ioff++) { + for (ioff = 0, t = s, umlen = umltot; t < send; ioff++) { set_pat_start(p, t-s); - if (pattrylen(p, t, s + l - t, umlen, ioff)) + if (pattrylen(p, t, umlen, 0, &patstralloc, ioff)) tmatch = t; if (fl & SUB_START) break; - umlen -= iincchar(&t); + umlen -= iincchar(&t, send - t); } if (tmatch) { - *sp = get_match_ret(*sp, tmatch - s, l, fl, replstr, NULL); + *sp = get_match_ret(&imd, tmatch - s, umltot); return 1; } - if (!(fl & SUB_START) && pattrylen(p, s + l, 0, 0, ioff)) { - *sp = get_match_ret(*sp, l, l, fl, replstr, NULL); + if (!(fl & SUB_START) && pattrylen(p, s + umltot, 0, 0, + &patstralloc, ioff)) { + *sp = get_match_ret(&imd, umltot, umltot); return 1; } break; @@ -2866,18 +2900,19 @@ igetmatch(char **sp, Patprog p, int fl, int n, char *replstr, * move forward along string until we get a match. * * Again there's no optimisation. */ mb_charinit(); - for (ioff = 0, t = s, umlen = umltot; t < s + l; ioff++) { + for (ioff = 0, t = s, umlen = umltot; t < send ; ioff++) { set_pat_start(p, t-s); - if (pattrylen(p, t, s + l - t, umlen, ioff)) { - *sp = get_match_ret(*sp, t-s, l, fl, replstr, NULL); + if (pattrylen(p, t, umlen, 0, &patstralloc, ioff)) { + *sp = get_match_ret(&imd, t-s, umltot); return 1; } if (fl & SUB_START) break; - umlen -= iincchar(&t); + umlen -= iincchar(&t, send - t); } - if (!(fl & SUB_START) && pattrylen(p, s + l, 0, 0, ioff)) { - *sp = get_match_ret(*sp, l, l, fl, replstr, NULL); + if (!(fl & SUB_START) && pattrylen(p, send, 0, 0, + &patstralloc, ioff)) { + *sp = get_match_ret(&imd, umltot, umltot); return 1; } break; @@ -2885,17 +2920,19 @@ igetmatch(char **sp, Patprog p, int fl, int n, char *replstr, case SUB_SUBSTR: /* Smallest at start, but matching substrings. */ set_pat_start(p, l); - if (!(fl & SUB_GLOBAL) && pattry(p, s + l) && !--n) { - *sp = get_match_ret(*sp, 0, 0, fl, replstr, NULL); + if (!(fl & SUB_GLOBAL) && + pattrylen(p, send, 0, 0, &patstralloc, 0) && + !--n) { + *sp = get_match_ret(&imd, 0, 0); return 1; } /* fall through */ case (SUB_SUBSTR|SUB_LONG): /* longest or smallest at start with substrings */ t = s; if (fl & SUB_GLOBAL) { - repllist = (fl & SUB_LIST) ? znewlinklist() : newlinklist(); + imd.repllist = (fl & SUB_LIST) ? znewlinklist() : newlinklist(); if (repllistp) - *repllistp = repllist; + *repllistp = imd.repllist; } ioff = 0; /* offset into string */ umlen = umltot; @@ -2903,10 +2940,10 @@ igetmatch(char **sp, Patprog p, int fl, int n, char *replstr, do { /* loop over all matches for global substitution */ matched = 0; - for (; t < s + l; ioff++) { + for (; t < send; ioff++) { /* Find the longest match from this position. */ set_pat_start(p, t-s); - if (pattrylen(p, t, s + l - t, umlen, ioff)) { + if (pattrylen(p, t, umlen, 0, &patstralloc, ioff)) { char *mpos = t + patmatchlen(); if (!(fl & SUB_LONG) && !(p->flags & PAT_PURES)) { char *ptr; @@ -2920,18 +2957,18 @@ igetmatch(char **sp, Patprog p, int fl, int n, char *replstr, */ for (ptr = t, umlen2 = 0; ptr < mpos;) { set_pat_end(p, *ptr); - if (pattrylen(p, t, ptr - t, umlen2, ioff)) { + if (pattrylen(p, t, umlen2, 0, + &patstralloc, ioff)) { mpos = t + patmatchlen(); break; } - umlen2 += iincchar(&ptr); + umlen2 += iincchar(&ptr, mpos - ptr); } } if (!--n || (n <= 0 && (fl & SUB_GLOBAL))) { - *sp = get_match_ret(*sp, t-s, mpos-s, fl, - replstr, repllist); + *sp = get_match_ret(&imd, t-s, mpos-s); if (mpos == t) - mpos += mb_metacharlenconv(mpos, NULL); + mpos += mb_charlenconv(mpos, send - mpos, NULL); } if (!(fl & SUB_GLOBAL)) { if (n) { @@ -2941,7 +2978,7 @@ igetmatch(char **sp, Patprog p, int fl, int n, char *replstr, * the next character, even if it overlaps * with what we just found. */ - umlen -= iincchar(&t); + umlen -= iincchar(&t, send - t); continue; } else { return 1; @@ -2954,11 +2991,11 @@ igetmatch(char **sp, Patprog p, int fl, int n, char *replstr, matched = 1; while (t < mpos) { ioff++; - umlen -= iincchar(&t); + umlen -= iincchar(&t, send - t); } break; } - umlen -= iincchar(&t); + umlen -= iincchar(&t, send - t); } } while (matched); /* @@ -2968,8 +3005,8 @@ igetmatch(char **sp, Patprog p, int fl, int n, char *replstr, */ set_pat_start(p, l); if ((fl & (SUB_LONG|SUB_GLOBAL)) == SUB_LONG && - pattry(p, s + l) && !--n) { - *sp = get_match_ret(*sp, 0, 0, fl, replstr, repllist); + pattrylen(p, send, 0, 0, &patstralloc, 0) && !--n) { + *sp = get_match_ret(&imd, 0, 0); return 1; } break; @@ -2979,8 +3016,9 @@ igetmatch(char **sp, Patprog p, int fl, int n, char *replstr, /* Longest/shortest at end, matching substrings. */ if (!(fl & SUB_LONG)) { set_pat_start(p, l); - if (pattrylen(p, s + l, 0, 0, umltot) && !--n) { - *sp = get_match_ret(*sp, l, l, fl, replstr, NULL); + if (pattrylen(p, send, 0, 0, &patstralloc, umltot) && + !--n) { + *sp = get_match_ret(&imd, umltot, umltot); return 1; } } @@ -2997,13 +3035,13 @@ igetmatch(char **sp, Patprog p, int fl, int n, char *replstr, nmatches = 0; tmatch = NULL; mb_charinit(); - for (ioff = 0, t = s, umlen = umltot; t < s + l; ioff++) { + for (ioff = 0, t = s, umlen = umltot; t < send; ioff++) { set_pat_start(p, t-s); - if (pattrylen(p, t, s + l - t, umlen, ioff)) { + if (pattrylen(p, t, umlen, 0, &patstralloc, ioff)) { nmatches++; tmatch = t; } - umlen -= iincchar(&t); + umlen -= iincchar(&t, send - t); } if (nmatches) { char *mpos; @@ -3013,14 +3051,14 @@ igetmatch(char **sp, Patprog p, int fl, int n, char *replstr, */ n = nmatches - n; mb_charinit(); - for (ioff = 0, t = s, umlen = umltot; t < s + l; ioff++) { + for (ioff = 0, t = s, umlen = umltot; t < send; ioff++) { set_pat_start(p, t-s); - if (pattrylen(p, t, s + l - t, umlen, ioff) && + if (pattrylen(p, t, umlen, 0, &patstralloc, ioff) && !n--) { tmatch = t; break; } - umlen -= iincchar(&t); + umlen -= iincchar(&t, send - t); } } mpos = tmatch + patmatchlen(); @@ -3028,27 +3066,29 @@ igetmatch(char **sp, Patprog p, int fl, int n, char *replstr, if (!(fl & SUB_LONG) && !(p->flags & PAT_PURES)) { for (t = tmatch, umlen = 0; t < mpos; ) { set_pat_end(p, *t); - if (pattrylen(p, tmatch, t - tmatch, umlen, ioff)) { + if (pattrylen(p, tmatch, umlen, 0, + &patstralloc, ioff)) { mpos = tmatch + patmatchlen(); break; } - umlen += iincchar(&t); + umlen += iincchar(&t, mpos - t); } } - *sp = get_match_ret(*sp, tmatch-s, mpos-s, fl, - replstr, NULL); + *sp = get_match_ret(&imd, tmatch-s, mpos-s); return 1; } set_pat_start(p, l); - if ((fl & SUB_LONG) && pattrylen(p, s + l, 0, 0, umltot) && !--n) { - *sp = get_match_ret(*sp, l, l, fl, replstr, NULL); + if ((fl & SUB_LONG) && pattrylen(p, send, 0, 0, + &patstralloc, umltot) && + !--n) { + *sp = get_match_ret(&imd, umltot, umltot); return 1; } break; } } - if (repllist && nonempty(repllist)) { + if (imd.repllist && nonempty(imd.repllist)) { /* Put all the bits of a global search and replace together. */ LinkNode nd; Repldata rd; @@ -3056,10 +3096,15 @@ igetmatch(char **sp, Patprog p, int fl, int n, char *replstr, char *ptr, *start; int i; + /* + * Use metafied string again. + * Results from get_match_ret in repllist are all metafied. + */ + s = *sp; if (!(fl & SUB_LIST)) { lleft = 0; /* size of returned string */ - i = 0; /* start of last chunk we got from *sp */ - for (nd = firstnode(repllist); nd; incnode(nd)) { + i = 0; /* start of last chunk we got from *sp */ + for (nd = firstnode(imd.repllist); nd; incnode(nd)) { rd = (Repldata) getdata(nd); lleft += rd->b - i; /* previous chunk of *sp */ lleft += strlen(rd->replstr); /* the replaced bit */ @@ -3068,7 +3113,7 @@ igetmatch(char **sp, Patprog p, int fl, int n, char *replstr, lleft += l - i; /* final chunk from *sp */ start = t = zhalloc(lleft+1); i = 0; - for (nd = firstnode(repllist); nd; incnode(nd)) { + for (nd = firstnode(imd.repllist); nd; incnode(nd)) { rd = (Repldata) getdata(nd); memcpy(t, s + i, rd->b - i); t += rd->b - i; @@ -3083,11 +3128,14 @@ igetmatch(char **sp, Patprog p, int fl, int n, char *replstr, } return 1; } - if (fl & SUB_LIST) /* safety: don't think this can happen */ + if (fl & SUB_LIST) { /* safety: don't think this can happen */ return 0; + } /* munge the whole string: no match, so no replstr */ - *sp = get_match_ret(*sp, 0, 0, fl, 0, 0); + imd.replstr = NULL; + imd.repllist = NULL; + *sp = get_match_ret(&imd, 0, 0); return (fl & SUB_RETFAIL) ? 0 : 1; } @@ -3105,7 +3153,7 @@ static int igetmatch(char **sp, Patprog p, int fl, int n, char *replstr, LinkList *repllistp) { - char *s = *sp, *t; + char *s = *sp, *t, *send; /* * Note that ioff and uml count characters in the character * set (Meta's are not included), while l counts characters in the @@ -3113,36 +3161,48 @@ igetmatch(char **sp, Patprog p, int fl, int n, char *replstr, * lengths. */ int ioff, l = strlen(*sp), uml = ztrlen(*sp), matched = 1, umlen; - /* - * List of bits of matches to concatenate with replacement string. - * The data is a struct repldata. It is not used in cases like - * ${...//#foo/bar} even though SUB_GLOBAL is set, since the match - * is anchored. It goes on the heap. - */ - LinkList repllist = NULL; + struct patstralloc patstralloc; + struct imatchdata imd; + + (void)patallocstr(p, s, l, uml, 1, &patstralloc); + s = patstralloc.alloced; + DPUTS(!s, "forced patallocstr failed"); + send = s + uml; + + imd.mstr = *sp; + imd.mlen = l; + imd.ustr = s; + imd.ulen = uml; + imd.flags = fl; + imd.replstr = replstr; + imd.repllist = NULL; /* perform must-match test for complex closures */ if (p->mustoff) { - /* - * Yuk. Probably we should rewrite this whole function to - * use an unmetafied test string. - * - * Use META_HEAPDUP because we need a terminating NULL. - */ - char *muststr = metafy((char *)p + p->mustoff, - p->patmlen, META_HEAPDUP); + char *muststr = (char *)p + p->mustoff; - if (!strstr(s, muststr)) - matched = 0; + matched = 0; + if (p->patmlen <= uml) + { + for (t = s; t <= send - p->patmlen; t++) + { + if (!memcmp(muststr, t, p->patmlen)) { + matched = 1; + break; + } + } + } } /* in case we used the prog before... */ p->flags &= ~(PAT_NOTSTART|PAT_NOTEND); if (fl & SUB_ALL) { - int i = matched && pattry(p, s); - *sp = get_match_ret(*sp, 0, i ? l : 0, fl, i ? replstr : 0, NULL); + int i = matched && pattrylen(p, s, uml, 0, &patstralloc, 0); + if (!i) + imd.replstr = NULL; + *sp = get_match_ret(&imd, 0, i ? l : 0); if (! **sp && (((fl & SUB_MATCH) && !i) || ((fl & SUB_REST) && i))) return 0; return 1; @@ -3155,23 +3215,24 @@ igetmatch(char **sp, Patprog p, int fl, int n, char *replstr, * Largest/smallest possible match at head of string. * First get the longest match... */ - if (pattry(p, s)) { + if (pattrylen(p, s, uml, 0, &patstralloc, 0)) { /* patmatchlen returns metafied length, as we need */ int mlen = patmatchlen(); if (!(fl & SUB_LONG) && !(p->flags & PAT_PURES)) { + send = s + mlen; /* * ... now we know whether it's worth looking for the * shortest, which we do by brute force. */ for (t = s, umlen = 0; t < s + mlen; METAINC(t), umlen++) { set_pat_end(p, *t); - if (pattrylen(p, s, t - s, umlen, 0)) { + if (pattrylen(p, s, umlen, 0, &patstralloc, 0)) { mlen = patmatchlen(); break; } } } - *sp = get_match_ret(*sp, 0, mlen, fl, replstr, NULL); + *sp = get_match_ret(&imd, 0, mlen); return 1; } break; @@ -3180,17 +3241,13 @@ igetmatch(char **sp, Patprog p, int fl, int n, char *replstr, /* Smallest possible match at tail of string: * * move back down string until we get a match. * * There's no optimization here. */ - for (ioff = uml, t = s + l, umlen = 0; t >= s; + for (ioff = uml, t = send, umlen = 0; t >= s; t--, ioff--, umlen++) { - if (t > s && t[-1] == Meta) - t--; set_pat_start(p, t-s); - if (pattrylen(p, t, s + l - t, umlen, ioff)) { - *sp = get_match_ret(*sp, t - s, l, fl, replstr, NULL); + if (pattrylen(p, t, umlen, 0, &patstralloc, ioff)) { + *sp = get_match_ret(&imd, t - s, uml); return 1; } - if (t > s+1 && t[-2] == Meta) - t--; } break; @@ -3198,60 +3255,59 @@ igetmatch(char **sp, Patprog p, int fl, int n, char *replstr, /* Largest possible match at tail of string: * * move forward along string until we get a match. * * Again there's no optimisation. */ - for (ioff = 0, t = s, umlen = uml; t < s + l; - ioff++, METAINC(t), umlen--) { + for (ioff = 0, t = s, umlen = uml; t < send; + ioff++, t++, umlen--) { set_pat_start(p, t-s); - if (pattrylen(p, t, s + l - t, umlen, ioff)) { - *sp = get_match_ret(*sp, t-s, l, fl, replstr, NULL); + if (pattrylen(p, t, send - t, umlen, &patstralloc, ioff)) { + *sp = get_match_ret(&imd, t-s, uml); return 1; } - if (*t == Meta) - t++; } break; case SUB_SUBSTR: /* Smallest at start, but matching substrings. */ set_pat_start(p, l); - if (!(fl & SUB_GLOBAL) && pattry(p, s + l) && !--n) { - *sp = get_match_ret(*sp, 0, 0, fl, replstr, NULL); + if (!(fl & SUB_GLOBAL) && + pattrylen(p, send, 0, 0, &patstralloc, 0) && !--n) { + *sp = get_match_ret(&imd, 0, 0); return 1; } /* fall through */ case (SUB_SUBSTR|SUB_LONG): /* longest or smallest at start with substrings */ t = s; if (fl & SUB_GLOBAL) { - repllist = newlinklist(); + imd.repllist = newlinklist(); if (repllistp) - *repllistp = repllist; + *repllistp = imd.repllist; } ioff = 0; /* offset into string */ umlen = uml; do { /* loop over all matches for global substitution */ matched = 0; - for (; t < s + l; METAINC(t), ioff++, umlen--) { + for (; t < send; t++, ioff++, umlen--) { /* Find the longest match from this position. */ set_pat_start(p, t-s); - if (pattrylen(p, t, s + l - t, umlen, ioff)) { + if (pattrylen(p, t, send - t, umlen, &patstralloc, ioff)) { char *mpos = t + patmatchlen(); if (!(fl & SUB_LONG) && !(p->flags & PAT_PURES)) { char *ptr; int umlen2; for (ptr = t, umlen2 = 0; ptr < mpos; - METAINC(ptr), umlen2++) { + ptr++, umlen2++) { set_pat_end(p, *ptr); - if (pattrylen(p, t, ptr - t, umlen2, ioff)) { + if (pattrylen(p, t, ptr - t, umlen2, + &patstralloc, ioff)) { mpos = t + patmatchlen(); break; } } } if (!--n || (n <= 0 && (fl & SUB_GLOBAL))) { - *sp = get_match_ret(*sp, t-s, mpos-s, fl, - replstr, repllist); + *sp = get_match_ret(&imd, t-s, mpos-s); if (mpos == t) - METAINC(mpos); + mpos++; } if (!(fl & SUB_GLOBAL)) { if (n) { @@ -3271,13 +3327,13 @@ igetmatch(char **sp, Patprog p, int fl, int n, char *replstr, * which is already marked for replacement. */ matched = 1; - for ( ; t < mpos; t++, ioff++, umlen--) - if (*t == Meta) - t++; + while (t < mpos) { + ioff++; + umlen--; + t++; + } break; } - if (*t == Meta) - t++; } } while (matched); /* @@ -3287,8 +3343,8 @@ igetmatch(char **sp, Patprog p, int fl, int n, char *replstr, */ set_pat_start(p, l); if ((fl & (SUB_LONG|SUB_GLOBAL)) == SUB_LONG && - pattry(p, s + l) && !--n) { - *sp = get_match_ret(*sp, 0, 0, fl, replstr, repllist); + pattrylen(p, send, 0, 0, &patstralloc, 0) && !--n) { + *sp = get_match_ret(&imd, 0, 0); return 1; } break; @@ -3298,46 +3354,47 @@ igetmatch(char **sp, Patprog p, int fl, int n, char *replstr, /* Longest/shortest at end, matching substrings. */ if (!(fl & SUB_LONG)) { set_pat_start(p, l); - if (pattrylen(p, s + l, 0, 0, uml) && !--n) { - *sp = get_match_ret(*sp, l, l, fl, replstr, NULL); + if (pattrylen(p, send, 0, 0, &patstralloc, uml) && !--n) { + *sp = get_match_ret(&imd, uml, uml); return 1; } } - for (ioff = uml - 1, t = s + l - 1, umlen = 1; t >= s; + for (ioff = uml - 1, t = send - 1, umlen = 1; t >= s; t--, ioff--, umlen++) { - if (t > s && t[-1] == Meta) - t--; set_pat_start(p, t-s); - if (pattrylen(p, t, s + l - t, umlen, ioff) && !--n) { + if (pattrylen(p, t, send - t, umlen, &patstralloc, ioff) && + !--n) { /* Found the longest match */ char *mpos = t + patmatchlen(); if (!(fl & SUB_LONG) && !(p->flags & PAT_PURES)) { char *ptr; int umlen2; for (ptr = t, umlen2 = 0; ptr < mpos; - METAINC(ptr), umlen2++) { + ptr++, umlen2++) { set_pat_end(p, *ptr); - if (pattrylen(p, t, ptr - t, umlen2, ioff)) { + if (pattrylen(p, t, umlen2, 0, &patstralloc, + ioff)) { mpos = t + patmatchlen(); break; } } } - *sp = get_match_ret(*sp, t-s, mpos-s, fl, - replstr, NULL); + *sp = get_match_ret(&imd, t-s, mpos-s); return 1; } } set_pat_start(p, l); - if ((fl & SUB_LONG) && pattrylen(p, s + l, 0, 0, uml) && !--n) { - *sp = get_match_ret(*sp, l, l, fl, replstr, NULL); + if ((fl & SUB_LONG) && pattrylen(p, send, 0, 0, + &patstralloc, uml) && + !--n) { + *sp = get_match_ret(&imd, uml, uml); return 1; } break; } } - if (repllist && nonempty(repllist)) { + if (imd.repllist && nonempty(imd.repllist)) { /* Put all the bits of a global search and replace together. */ LinkNode nd; Repldata rd; @@ -3345,8 +3402,13 @@ igetmatch(char **sp, Patprog p, int fl, int n, char *replstr, char *ptr, *start; int i; + /* + * Use metafied string again. + * Results from get_match_ret in repllist are all metafied. + */ + s = *sp; i = 0; /* start of last chunk we got from *sp */ - for (nd = firstnode(repllist); nd; incnode(nd)) { + for (nd = firstnode(imd.repllist); nd; incnode(nd)) { rd = (Repldata) getdata(nd); lleft += rd->b - i; /* previous chunk of *sp */ lleft += strlen(rd->replstr); /* the replaced bit */ @@ -3355,7 +3417,7 @@ igetmatch(char **sp, Patprog p, int fl, int n, char *replstr, lleft += l - i; /* final chunk from *sp */ start = t = zhalloc(lleft+1); i = 0; - for (nd = firstnode(repllist); nd; incnode(nd)) { + for (nd = firstnode(imd.repllist); nd; incnode(nd)) { rd = (Repldata) getdata(nd); memcpy(t, s + i, rd->b - i); t += rd->b - i; @@ -3371,7 +3433,9 @@ igetmatch(char **sp, Patprog p, int fl, int n, char *replstr, } /* munge the whole string: no match, so no replstr */ - *sp = get_match_ret(*sp, 0, 0, fl, 0, 0); + imd.replstr = NULL; + imd.repllist = NULL; + *sp = get_match_ret(&imd, 0, 0); return 1; } diff --git a/Src/hist.c b/Src/hist.c index 9c42d85c9..007366a49 100644 --- a/Src/hist.c +++ b/Src/hist.c @@ -2254,7 +2254,7 @@ static char * getargs(Histent elist, int arg1, int arg2) { short *words = elist->words; - int pos1, nwords = elist->nwords; + int pos1, pos2, nwords = elist->nwords; if (arg2 < arg1 || arg1 >= nwords || arg2 >= nwords) { /* remember, argN is indexed from 0, nwords is total no. of words */ @@ -2263,8 +2263,22 @@ getargs(Histent elist, int arg1, int arg2) return NULL; } + /* optimization for accessing entire history event */ + if (arg1 == 0 && arg2 == nwords - 1) + return dupstring(elist->node.nam); + pos1 = words[2*arg1]; - return dupstrpfx(elist->node.nam + pos1, words[2*arg2+1] - pos1); + pos2 = words[2*arg2+1]; + + /* a word has to be at least one character long, so if the position + * of a word is less than its index, we've overflowed our signed + * short integer word range and the recorded position is garbage. */ + if (pos1 < 0 || pos1 < arg1 || pos2 < 0 || pos2 < arg2) { + herrflush(); + zerr("history event too long, can't index requested words"); + return NULL; + } + return dupstrpfx(elist->node.nam + pos1, pos2 - pos1); } /**/ diff --git a/Src/init.c b/Src/init.c index 22db4b3b2..dcce1d7ce 100644 --- a/Src/init.c +++ b/Src/init.c @@ -790,8 +790,10 @@ init_term(void) tcstr[TCCLEARSCREEN] = ztrdup("\14"); tclen[TCCLEARSCREEN] = 1; } - /* This might work, but there may be more to it */ - rprompt_indent = ((hasam && !hasbw) || hasye || !tccan(TCLEFT)); + rprompt_indent = 1; + /* The following is an attempt at a heuristic, + * but it fails in some cases */ + /* rprompt_indent = ((hasam && !hasbw) || hasye || !tccan(TCLEFT)); */ } return 1; } @@ -783,6 +783,15 @@ gettok(void) */ tokstr = NULL; return INPAR; + + case CMD_OR_MATH_ERR: + /* + * LEXFLAGS_ACTIVE means we came from bufferwords(), + * so we treat as an incomplete math expression + */ + if (lexflags & LEXFLAGS_ACTIVE) + tokstr = dyncat("((", tokstr ? tokstr : ""); + /* fall through */ default: return LEXERR; @@ -1608,7 +1617,7 @@ parsestrnoerr(char **s) mod_export char * parse_subscript(char *s, int sub, int endchar) { - int l = strlen(s), err; + int l = strlen(s), err, toklen; char *t; if (!*s || *s == endchar) @@ -1617,18 +1626,34 @@ parse_subscript(char *s, int sub, int endchar) untokenize(t = dupstring(s)); inpush(t, 0, NULL); strinbeg(0); + /* + * Warning to Future Generations: + * + * This way of passing the subscript through the lexer is brittle. + * Code above this for several layers assumes that when we tokenise + * the input it goes into the same place as the original string. + * However, the lexer may overwrite later bits of the string or + * reallocate it, in particular when expanding aliaes. To get + * around this, we copy the string and then copy it back. This is a + * bit more robust but still relies on the underlying assumption of + * length preservation. + */ lexbuf.len = 0; - lexbuf.ptr = tokstr = s; + lexbuf.ptr = tokstr = dupstring(s); lexbuf.siz = l + 1; err = dquote_parse(endchar, sub); + toklen = (int)(lexbuf.ptr - tokstr); + DPUTS(toklen > l, "Bad length for parsed subscript"); + memcpy(s, tokstr, toklen); if (err) { - err = *lexbuf.ptr; - *lexbuf.ptr = '\0'; + char *strend = s + toklen; + err = *strend; + *strend = '\0'; untokenize(s); - *lexbuf.ptr = err; + *strend = err; s = NULL; } else { - s = lexbuf.ptr; + s += toklen; } strinend(); inpop(); @@ -1997,7 +2022,9 @@ skipcomm(void) int new_lexstop, new_lex_add_raw; int save_infor = infor; struct lexbufstate new_lexbuf; + int noalias = noaliases; + noaliases = 1; infor = 0; cmdpush(CS_CMDSUBST); SETPARBEGIN @@ -2115,6 +2142,7 @@ skipcomm(void) SETPAREND cmdpop(); infor = save_infor; + noaliases = noalias; return lexstop; #endif diff --git a/Src/math.c b/Src/math.c index 977e92345..37981cf22 100644 --- a/Src/math.c +++ b/Src/math.c @@ -535,7 +535,7 @@ lexconstant(void) for (ptr2 = ptr; ptr2 < nptr; ptr2++) { if (*ptr2 == '_') { int len = nptr - ptr; - ptr = strdup(ptr); + ptr = ztrdup(ptr); for (ptr2 = ptr; len; len--) { if (*ptr2 == '_') chuck(ptr2); @@ -893,7 +893,6 @@ getcvar(char *s) return mn; } - /**/ static mnumber setmathvar(struct mathvalue *mvp, mnumber v) @@ -79,6 +79,12 @@ #include <sys/mman.h> +/* + * This definition is designed to enable use of memory mapping on MacOS. + * However, performance tests indicate that MacOS mapped regions are + * somewhat slower to allocate than memory from malloc(), so whether + * using this improves performance depends on details of zhalloc(). + */ #if defined(MAP_ANON) && !defined(MAP_ANONYMOUS) #define MAP_ANONYMOUS MAP_ANON #endif @@ -294,7 +300,7 @@ pushheap(void) #endif for (h = heaps; h; h = h->next) { - DPUTS(!h->used, "BUG: empty heap"); + DPUTS(!h->used && h->next, "BUG: empty heap"); hs = (Heapstack) zalloc(sizeof(*hs)); hs->next = h->sp; h->sp = hs; @@ -334,16 +340,20 @@ freeheap(void) * * Whenever fheap is NULL here, the loop below sweeps back over the * entire heap list again, resetting the free space in every arena to - * the amount stashed by pushheap() and finding the first arena with + * the amount stashed by pushheap() and finding the arena with the most * free space to optimize zhalloc()'s next search. When there's a lot * of stuff already on the heap, this is an enormous amount of work, * and performance goes to hell. * + * Therefore, we defer freeing the most recently allocated arena until + * we reach popheap(). + * * However, if the arena to which fheap points is unused, we want to - * free it, so we have no choice but to do the sweep for a new fheap. + * reclaim space in earlier arenas, so we have no choice but to do the + * sweep for a new fheap. */ if (fheap && !fheap->sp) - fheap = NULL; /* We used to do this unconditionally */ + fheap = NULL; /* We used to do this unconditionally */ /* * In other cases, either fheap is already correct, or it has never * been set and this loop will do it, or it'll be reset from scratch @@ -361,7 +371,11 @@ freeheap(void) memset(arena(h) + h->sp->used, 0xff, h->used - h->sp->used); #endif h->used = h->sp->used; - if (!fheap && h->used < ARENA_SIZEOF(h)) + if (!fheap) { + if (h->used < ARENA_SIZEOF(h)) + fheap = h; + } else if (ARENA_SIZEOF(h) - h->used > + ARENA_SIZEOF(fheap) - fheap->used) fheap = h; hl = h; #ifdef ZSH_HEAP_DEBUG @@ -384,6 +398,26 @@ freeheap(void) VALGRIND_MEMPOOL_TRIM((char *)h, (char *)arena(h), h->used); #endif } else { + if (fheap == h) + fheap = NULL; + if (h->next) { + /* We want to cut this out of the arena list if we can */ + if (h == heaps) + hl = heaps = h->next; + else if (hl && hl->next == h) + hl->next = h->next; + else { + DPUTS(hl, "hl->next != h when freeing"); + hl = h; + continue; + } + h->next = NULL; + } else { + /* Leave an empty arena at the end until popped */ + h->used = 0; + fheap = hl = h; + break; + } #ifdef USE_MMAP munmap((void *) h, h->size); #else @@ -441,12 +475,29 @@ popheap(void) #ifdef ZSH_VALGRIND VALGRIND_MEMPOOL_TRIM((char *)h, (char *)arena(h), h->used); #endif - if (!fheap && h->used < ARENA_SIZEOF(h)) + if (!fheap) { + if (h->used < ARENA_SIZEOF(h)) + fheap = h; + } else if (ARENA_SIZEOF(h) - h->used > + ARENA_SIZEOF(fheap) - fheap->used) fheap = h; zfree(hs, sizeof(*hs)); hl = h; } else { + if (h->next) { + /* We want to cut this out of the arena list if we can */ + if (h == heaps) + hl = heaps = h->next; + else if (hl && hl->next == h) + hl->next = h->next; + else { + DPUTS(hl, "hl->next != h when popping"); + hl = h; + continue; + } + h->next = NULL; + } #ifdef USE_MMAP munmap((void *) h, h->size); #else @@ -524,7 +575,7 @@ zheapptr(void *p) mod_export void * zhalloc(size_t size) { - Heap h; + Heap h, hp = NULL; size_t n; #ifdef ZSH_VALGRIND size_t req_size = size; @@ -543,9 +594,15 @@ zhalloc(size_t size) /* find a heap with enough free space */ - for (h = ((fheap && ARENA_SIZEOF(fheap) >= (size + fheap->used)) - ? fheap : heaps); - h; h = h->next) { + /* + * This previously assigned: + * h = ((fheap && ARENA_SIZEOF(fheap) >= (size + fheap->used)) + * ? fheap : heaps); + * but we think that nothing upstream of fheap has more free space, + * so why start over at heaps just because fheap has too little? + */ + for (h = (fheap ? fheap : heaps); h; h = h->next) { + hp = h; if (ARENA_SIZEOF(h) >= (n = size + h->used)) { void *ret; @@ -566,7 +623,6 @@ zhalloc(size_t size) } } { - Heap hp; /* not found, allocate new heap */ #if defined(ZSH_MEM) && !defined(USE_MMAP) static int called = 0; @@ -575,7 +631,6 @@ zhalloc(size_t size) #endif n = HEAP_ARENA_SIZE > size ? HEAPSIZE : size + sizeof(*h); - for (hp = NULL, h = heaps; h; hp = h, h = h->next); #ifdef USE_MMAP h = mmap_heap_alloc(&n); @@ -607,6 +662,7 @@ zhalloc(size_t size) VALGRIND_MEMPOOL_ALLOC((char *)h, (char *)arena(h), req_size); #endif + DPUTS(hp && hp->next, "failed to find end of chain in zhalloc"); if (hp) hp->next = h; else @@ -1611,8 +1667,13 @@ realloc(MALLOC_RET_T p, MALLOC_ARG_T size) int i, l = 0; /* some system..., see above */ - if (!p && size) - return (MALLOC_RET_T) malloc(size); + if (!p && size) { + queue_signals(); + r = malloc(size); + unqueue_signals(); + return (MALLOC_RET_T) r; + } + /* and some systems even do this... */ if (!p || !size) return (MALLOC_RET_T) p; diff --git a/Src/mkmakemod.sh b/Src/mkmakemod.sh index 002160910..3ccf9c5e5 100644 --- a/Src/mkmakemod.sh +++ b/Src/mkmakemod.sh @@ -21,12 +21,8 @@ # moddeps modules on which this module depends (default none) # nozshdep non-empty indicates no dependence on the `zsh/main' pseudo-module # alwayslink if non-empty, always link the module into the executable -# autobins builtins defined by the module, for autoloading -# autoinfixconds infix condition codes defined by the module, for -# autoloading (without the leading `-') -# autoprefixconds like autoinfixconds, but for prefix condition codes -# autoparams parameters defined by the module, for autoloading -# automathfuncs math functions defined by the module, for autoloading +# autofeatures features defined by the module, for autoloading +# autofeatures_emu As autofeatures, but for non-zsh emulation modes # objects .o files making up this module (*must* be defined) # proto .syms files for this module (default generated from $objects) # headers extra headers for this module (default none) @@ -189,7 +185,7 @@ if $first_stage; then for mddname in $here_mddnames; do unset name moddeps nozshdep alwayslink hasexport - unset autobins autoinfixconds autoprefixconds autoparams automathfuncs + unset autofeatures autofeatures_emu unset objects proto headers hdrdeps otherincs . $top_srcdir/$the_subdir/${mddname}.mdd q_name=`echo $name | sed 's,Q,Qq,g;s,_,Qu,g;s,/,Qs,g'` diff --git a/Src/options.c b/Src/options.c index 1fb102f1d..2678626c7 100644 --- a/Src/options.c +++ b/Src/options.c @@ -140,6 +140,7 @@ static struct optname optns[] = { {{NULL, "globassign", OPT_EMULATE|OPT_CSH}, GLOBASSIGN}, {{NULL, "globcomplete", 0}, GLOBCOMPLETE}, {{NULL, "globdots", OPT_EMULATE}, GLOBDOTS}, +{{NULL, "globstarshort", OPT_EMULATE}, GLOBSTARSHORT}, {{NULL, "globsubst", OPT_EMULATE|OPT_NONZSH}, GLOBSUBST}, {{NULL, "hashcmds", OPT_ALL}, HASHCMDS}, {{NULL, "hashdirs", OPT_ALL}, HASHDIRS}, @@ -901,3 +902,33 @@ printoptionlist_printequiv(int optno) optno *= (isneg ? -1 : 1); printf(" equivalent to --%s%s\n", isneg ? "no-" : "", optns[optno-1].node.nam); } + +/**/ +static char *print_emulate_opts; + +/**/ +static void +print_emulate_option(HashNode hn, int fully) +{ + Optname on = (Optname) hn; + + if (!(on->node.flags & OPT_ALIAS) && + ((fully && !(on->node.flags & OPT_SPECIAL)) || + (on->node.flags & OPT_EMULATE))) + { + if (!print_emulate_opts[on->optno]) + fputs("no", stdout); + puts(on->node.nam); + } +} + +/* + * List the settings of options associated with an emulation + */ + +/**/ +void list_emulate_options(char *cmdopts, int fully) +{ + print_emulate_opts = cmdopts; + scanhashtable(optiontab, 1, 0, 0, print_emulate_option, fully); +} diff --git a/Src/params.c b/Src/params.c index de151a4cd..b121bd6ad 100644 --- a/Src/params.c +++ b/Src/params.c @@ -447,7 +447,7 @@ newparamtable(int size, char const *name) ht->cmpnodes = strcmp; ht->addnode = addhashnode; ht->getnode = getparamnode; - ht->getnode2 = getparamnode; + ht->getnode2 = gethashnode2; ht->removenode = removehashnode; ht->disablenode = NULL; ht->enablenode = NULL; @@ -775,17 +775,18 @@ createparamtable(void) #endif opts[ALLEXPORT] = oae; + /* + * For native emulation we always set the variable home + * (see setupvals()). + */ + pm = (Param) paramtab->getnode(paramtab, "HOME"); if (EMULATION(EMULATE_ZSH)) { - /* - * For native emulation we always set the variable home - * (see setupvals()). - */ - pm = (Param) paramtab->getnode(paramtab, "HOME"); pm->node.flags &= ~PM_UNSET; if (!(pm->node.flags & PM_EXPORTED)) addenv(pm, home); - } + } else if (!home) + pm->node.flags |= PM_UNSET; pm = (Param) paramtab->getnode(paramtab, "LOGNAME"); if (!(pm->node.flags & PM_EXPORTED)) addenv(pm, pm->u.str); @@ -868,6 +869,7 @@ createparam(char *name, int flags) if (name != nulstring) { oldpm = (Param) (paramtab == realparamtab ? + /* gethashnode2() for direct table read */ gethashnode2(paramtab, name) : paramtab->getnode(paramtab, name)); @@ -2694,6 +2696,37 @@ gethkparam(char *s) } /**/ +static void +check_warn_create(Param pm, const char *pmtype) +{ + Funcstack i; + const char *name; + + if (pm->level != 0 || (pm->node.flags & PM_SPECIAL)) + return; + + name = NULL; + for (i = funcstack; i; i = i->prev) { + if (i->tp == FS_FUNC) { + DPUTS(!i->name, "funcstack entry with no name"); + name = i->name; + break; + } + } + + if (name) + { + zwarn("%s parameter %s created globally in function %s", + pmtype, pm->node.nam, name); + } + else + { + zwarn("%s parameter %s created globally in function", + pmtype, pm->node.nam); + } +} + +/**/ mod_export Param assignsparam(char *s, char *val, int flags) { @@ -2746,9 +2779,8 @@ assignsparam(char *s, char *val, int flags) zsfree(val); return NULL; } - if ((flags & ASSPM_WARN_CREATE) && v->pm->level == 0) - zwarn("scalar parameter %s created globally in function", - v->pm->node.nam); + if (flags & ASSPM_WARN_CREATE) + check_warn_create(v->pm, "scalar"); if (flags & ASSPM_AUGMENT) { if (v->start == 0 && v->end == -1) { switch (PM_TYPE(v->pm->node.flags)) { @@ -2827,6 +2859,15 @@ assignsparam(char *s, char *val, int flags) /**/ mod_export Param +setsparam(char *s, char *val) +{ + return assignsparam( + s, val, isset(WARNCREATEGLOBAL) && locallevel > 0 ? + ASSPM_WARN_CREATE : 0); +} + +/**/ +mod_export Param assignaparam(char *s, char **val, int flags) { struct value vbuf; @@ -2888,9 +2929,8 @@ assignaparam(char *s, char **val, int flags) return NULL; } - if ((flags & ASSPM_WARN_CREATE) && v->pm->level == 0) - zwarn("array parameter %s created globally in function", - v->pm->node.nam); + if (flags & ASSPM_WARN_CREATE) + check_warn_create(v->pm, "array"); if (flags & ASSPM_AUGMENT) { if (v->start == 0 && v->end == -1) { if (PM_TYPE(v->pm->node.flags) & PM_ARRAY) { @@ -2913,6 +2953,16 @@ assignaparam(char *s, char **val, int flags) return v->pm; } + +/**/ +mod_export Param +setaparam(char *s, char **aval) +{ + return assignaparam( + s, aval, isset(WARNCREATEGLOBAL) && locallevel >0 ? + ASSPM_WARN_CREATE : 0); +} + /**/ mod_export Param sethparam(char *s, char **val) @@ -2936,11 +2986,14 @@ sethparam(char *s, char **val) if (unset(EXECOPT)) return NULL; queue_signals(); - if (!(v = fetchvalue(&vbuf, &s, 1, SCANPM_ASSIGNING))) + if (!(v = fetchvalue(&vbuf, &s, 1, SCANPM_ASSIGNING))) { createparam(t, PM_HASHED); - else if (!(PM_TYPE(v->pm->node.flags) & PM_HASHED) && + if (isset(WARNCREATEGLOBAL) && locallevel > 0) + check_warn_create(v->pm, "associative array"); + } else if (!(PM_TYPE(v->pm->node.flags) & PM_HASHED) && !(v->pm->node.flags & PM_SPECIAL)) { unsetparam(t); + /* no WARNCREATEGLOBAL check here as parameter already existed */ createparam(t, PM_HASHED); v = NULL; } @@ -2967,6 +3020,7 @@ setnparam(char *s, mnumber val) Value v; char *t = s, *ss; Param pm; + int was_unset = 0; if (!isident(s)) { zerr("not an identifier: %s", s); @@ -2986,6 +3040,7 @@ setnparam(char *s, mnumber val) */ unset(KSHARRAYS) && !ss) { unsetparam_pm(v->pm, 0, 1); + was_unset = 1; s = t; v = NULL; } @@ -3006,6 +3061,8 @@ setnparam(char *s, mnumber val) } v = getvalue(&vbuf, &t, 1); DPUTS(!v, "BUG: value not found for new parameter"); + if (!was_unset && isset(WARNCREATEGLOBAL) && locallevel > 0) + check_warn_create(v->pm, "numeric"); } setnumvalue(v, val); unqueue_signals(); @@ -3024,6 +3081,26 @@ setiparam(char *s, zlong val) return setnparam(s, mnval); } +/* + * Set an integer parameter without forcing creation of an integer type. + * This is useful if the integer is going to be set to a parmaeter which + * would usually be scalar but may not exist. + */ + +/**/ +mod_export Param +setiparam_no_convert(char *s, zlong val) +{ + /* + * If the target is already an integer, thisgets converted + * back. Low technology rules. + */ + char buf[BDIGBUFSIZE]; + convbase(buf, val, 10); + return assignsparam( + s, ztrdup(buf), + isset(WARNCREATEGLOBAL) && locallevel > 0 ? ASSPM_WARN_CREATE : 0); +} /* Unset a parameter */ @@ -3035,7 +3112,8 @@ unsetparam(char *s) queue_signals(); if ((pm = (Param) (paramtab == realparamtab ? - gethashnode2(paramtab, s) : + /* getnode2() to avoid autoloading */ + paramtab->getnode2(paramtab, s) : paramtab->getnode(paramtab, s)))) unsetparam_pm(pm, 0, 1); unqueue_signals(); diff --git a/Src/parse.c b/Src/parse.c index 7c2d20250..83ba396b0 100644 --- a/Src/parse.c +++ b/Src/parse.c @@ -530,7 +530,7 @@ empty_eprog(Eprog p) } static void -clear_hdocs() +clear_hdocs(void) { struct heredocs *p, *n; @@ -3227,6 +3227,8 @@ build_dump(char *nam, char *dump, char **files, int ali, int map, int flags) noaliases = ali; for (hlen = FD_PRELEN, tlen = 0; *files; files++) { + struct stat st; + if (!strcmp(*files, "-k")) { flags = (flags & ~(FDHF_KSHLOAD | FDHF_ZSHLOAD)) | FDHF_KSHLOAD; continue; @@ -3235,6 +3237,7 @@ build_dump(char *nam, char *dump, char **files, int ali, int map, int flags) continue; } if ((fd = open(*files, O_RDONLY)) < 0 || + fstat(fd, &st) != 0 || !S_ISREG(st.st_mode) || (flen = lseek(fd, 0, 2)) == -1) { if (fd >= 0) close(fd); diff --git a/Src/pattern.c b/Src/pattern.c index 3b55ccf1c..9e8a80ae1 100644 --- a/Src/pattern.c +++ b/Src/pattern.c @@ -220,8 +220,10 @@ typedef union upat *Upat; #if defined(ZSH_64_BIT_TYPE) || defined(LONG_IS_64_BIT) typedef zlong zrange_t; #define ZRANGE_T_IS_SIGNED (1) +#define ZRANGE_MAX ZLONG_MAX #else typedef unsigned long zrange_t; +#define ZRANGE_MAX ULONG_MAX #endif #ifdef MULTIBYTE_SUPPORT @@ -2021,124 +2023,127 @@ pattrystart(void) } /* - * Test prog against null-terminated, metafied string. + * Fix up string length stuff. + * + * If we call patallocstr() with "force" to set things up early, it's + * done there, else it's done in pattryrefs(). The reason for the + * difference is in the latter case we may not be relying on + * patallocstr() having an effect. */ /**/ -mod_export int -pattry(Patprog prog, char *string) +static void +patmungestring(char **string, int *stringlen, int *unmetalenin) { - return pattryrefs(prog, string, -1, -1, 0, NULL, NULL, NULL); -} - -/* - * Test prog against string of given length, no null termination - * but still metafied at this point. offset gives an offset - * to include in reported match indices - */ + /* + * Special signalling of empty tokenised string. + */ + if (*stringlen > 0 && **string == Nularg) { + (*string)++; + /* + * If we don't have an unmetafied length + * and need it (we may not) we'll get it later. + */ + if (*unmetalenin > 0) + (*unmetalenin)--; + if (*stringlen > 0) + (*stringlen)--; + } -/**/ -mod_export int -pattrylen(Patprog prog, char *string, int len, int unmetalen, int offset) -{ - return pattryrefs(prog, string, len, unmetalen, offset, NULL, NULL, NULL); + /* Ensure we have a metafied length */ + if (*stringlen < 0) + *stringlen = strlen(*string); } /* - * Test prog against string with given lengths. The input - * string is metafied; stringlen is the raw string length, and - * unmetalen the number of characters in the original string (some - * of which may now be metafied). Either value may be -1 - * to indicate a null-terminated string which will be counted. Note - * there may be a severe penalty for this if a lot of matching is done - * on one string. + * Allocate memeory for pattern match. Note this is specific to use + * of pattern *and* trial string. * - * offset is the position in the original string (not seen by - * the pattern module) at which we are trying to match. - * This is added in to the positions recorded in patbeginp and patendp - * when we are looking for substrings. Currently this only happens - * in the parameter substitution code. + * Unmetafy a trial string for use in pattern matching, if needed. * - * Note this is a character offset, i.e. a metafied character - * counts as 1. + * If it is needed, returns a heap allocated string; if not needed, + * returns NULL. * - * The last three arguments are used to report the positions for the - * backreferences. On entry, *nump should contain the maximum number - * of positions to report. In this case the match, mbegin, mend - * arrays are not altered. - * - * If nump is NULL but endp is not NULL, then *endp is set to the - * end position of the match, taking into account patinstart. + * prog is the pattern to be executed. + * string is the metafied trial string. + * stringlen is it's length; it will be calculated if it's negative + * (this is a simple strlen()). + * unmetalen is the unmetafied length of the string, may be -1. + * force is 1 if we always unmetafy: this is useful if we are going + * to try again with different versions of the string. If this is + * called from pattryrefs() we don't force unmetafication as it won't + * be optimal. This option should be used if the resulting + * patstralloc is going to be passed to pattrylen() / pattryrefs(). + * In patstralloc (supplied by caller, must last until last pattry is done) + * unmetalen is the unmetafied length of the string; it will be + * calculated if the input value is negative. + * unmetalenp is the umetafied length of a path segment preceeding + * the trial string needed for file mananagement; it is calculated as + * needed so does not need to be initialised. + * alloced is the memory allocated on the heap --- same as return value from + * function. */ - /**/ -mod_export int -pattryrefs(Patprog prog, char *string, int stringlen, int unmetalen, - int patoffset, - int *nump, int *begp, int *endp) +mod_export +char *patallocstr(Patprog prog, char *string, int stringlen, int unmetalen, + int force, Patstralloc patstralloc) { - int i, maxnpos = 0, ret, needfullpath, unmetalenp; - int origlen; - char **sp, **ep, *tryalloced, *ptr; - char *progstr = (char *)prog + prog->startoff; + int needfullpath; - if (nump) { - maxnpos = *nump; - *nump = 0; - } - /* inherited from domatch, but why, exactly? */ - if (*string == Nularg) { - string++; - unmetalen--; - } + if (force) + patmungestring(&string, &stringlen, &unmetalen); - if (stringlen < 0) - stringlen = strlen(string); - origlen = stringlen; - - patflags = prog->flags; /* * For a top-level ~-exclusion, we will need the full * path to exclude, so copy the path so far and append the * current test string. */ - needfullpath = (patflags & PAT_HAS_EXCLUDP) && pathpos; + needfullpath = (prog->flags & PAT_HAS_EXCLUDP) && pathpos; /* Get the length of the full string when unmetafied. */ if (unmetalen < 0) - unmetalen = ztrsub(string + stringlen, string); - if (needfullpath) - unmetalenp = ztrsub(pathbuf + pathpos, pathbuf); + patstralloc->unmetalen = ztrsub(string + stringlen, string); else - unmetalenp = 0; + patstralloc->unmetalen = unmetalen; + if (needfullpath) { + patstralloc->unmetalenp = ztrsub(pathbuf + pathpos, pathbuf); + if (!patstralloc->unmetalenp) + needfullpath = 0; + } else + patstralloc->unmetalenp = 0; + /* Initialise cache area */ + patstralloc->progstrunmeta = NULL; + patstralloc->progstrunmetalen = 0; - DPUTS(needfullpath && (patflags & (PAT_PURES|PAT_ANY)), + DPUTS(needfullpath && (prog->flags & (PAT_PURES|PAT_ANY)), "rum sort of file exclusion"); /* * Partly for efficiency, and partly for the convenience of * globbing, we don't unmetafy pure string patterns, and * there's no reason to if the pattern is just a *. */ - if (!(patflags & (PAT_PURES|PAT_ANY)) - && (needfullpath || unmetalen != stringlen)) { + if (force || + (!(prog->flags & (PAT_PURES|PAT_ANY)) + && (needfullpath || patstralloc->unmetalen != stringlen))) { /* * We need to copy if we need to prepend the path so far * (in which case we copy both chunks), or if we have * Meta characters. */ - char *dst; - int icopy, ncopy; + char *dst, *ptr; + int i, icopy, ncopy; - dst = tryalloced = zalloc(unmetalen + unmetalenp); + dst = patstralloc->alloced = + zhalloc(patstralloc->unmetalen + patstralloc->unmetalenp); if (needfullpath) { /* loop twice, copy path buffer first time */ ptr = pathbuf; - ncopy = unmetalenp; + ncopy = patstralloc->unmetalenp; } else { /* just loop once, copy string with unmetafication */ ptr = string; - ncopy = unmetalen; + ncopy = patstralloc->unmetalen; } for (icopy = 0; icopy < 2; icopy++) { for (i = 0; i < ncopy; i++) { @@ -2153,22 +2158,136 @@ pattryrefs(Patprog prog, char *string, int stringlen, int unmetalen, break; /* next time append test string to path so far */ ptr = string; - ncopy = unmetalen; + ncopy = patstralloc->unmetalen; } + } + else + { + patstralloc->alloced = NULL; + } - if (needfullpath) { - patinstart = tryalloced + unmetalenp; - patinpath = tryalloced; - } else { - patinstart = tryalloced; - patinpath = NULL; - } - stringlen = unmetalen; - } else { + return patstralloc->alloced; +} + + +/* + * Test prog against null-terminated, metafied string. + */ + +/**/ +mod_export int +pattry(Patprog prog, char *string) +{ + return pattryrefs(prog, string, -1, -1, NULL, 0, NULL, NULL, NULL); +} + +/* + * Test prog against string of given length, no null termination + * but still metafied at this point. offset gives an offset + * to include in reported match indices + */ + +/**/ +mod_export int +pattrylen(Patprog prog, char *string, int len, int unmetalen, + Patstralloc patstralloc, int offset) +{ + return pattryrefs(prog, string, len, unmetalen, patstralloc, offset, + NULL, NULL, NULL); +} + +/* + * Test prog against string with given lengths. The input + * string is metafied; stringlen is the raw string length, and + * unmetalen the number of characters in the original string (some + * of which may now be metafied). Either value may be -1 + * to indicate a null-terminated string which will be counted. Note + * there may be a severe penalty for this if a lot of matching is done + * on one string. + * + * If patstralloc is not NULL it is used to optimise unmetafication + * of a trial string that may be passed (or any substring may be passed) to + * pattryrefs multiple times or the same pattern (N.B. so patstralloc + * depends on both prog *and* the trial string). This should only be + * done if there is no path prefix (pathpos == 0) as otherwise the path + * buffer and unmetafied string may not match. To do this, + * patallocstr() is callled (use force = 1 to ensure it is alway + * unmetafied); paststralloc points to existing storage. Memory is + * on the heap. + * + * patstralloc->alloced and patstralloc->unmetalen contain the + * unmetafied string and its length. In that case, the rules for the + * earlier arguments change: + * - string is an unmetafied string + * - stringlen is its unmetafied (i.e. actual) length + * - unmetalenin is not used. + * string and stringlen may refer to arbitrary substrings of + * patstralloc->alloced without any internal modification to patstralloc. + * + * patoffset is the position in the original string (not seen by + * the pattern module) at which we are trying to match. + * This is added in to the positions recorded in patbeginp and patendp + * when we are looking for substrings. Currently this only happens + * in the parameter substitution code. It refers to a real character + * offset, i.e. is already in the form ready for presentation to the + * general public --- this is necessary as we don't have the + * information to convert it down here. + * + * Note this is a character offset, i.e. a single possibly metafied and + * possibly multibyte character counts as 1. + * + * The last three arguments are used to report the positions for the + * backreferences. On entry, *nump should contain the maximum number + * of positions to report. In this case the match, mbegin, mend + * arrays are not altered. + * + * If nump is NULL but endp is not NULL, then *endp is set to the + * end position of the match, taking into account patinstart. + */ + +/**/ +mod_export int +pattryrefs(Patprog prog, char *string, int stringlen, int unmetalenin, + Patstralloc patstralloc, int patoffset, + int *nump, int *begp, int *endp) +{ + int i, maxnpos = 0, ret; + int origlen; + char **sp, **ep, *ptr; + char *progstr = (char *)prog + prog->startoff; + struct patstralloc patstralloc_struct; + + if (nump) { + maxnpos = *nump; + *nump = 0; + } + + if (!patstralloc) + patmungestring(&string, &stringlen, &unmetalenin); + origlen = stringlen; + + if (patstralloc) { + DPUTS(!patstralloc->alloced, + "External unmetafy didn't actually unmetafy."); + DPUTS(patstralloc->unmetalenp, + "Ooh-err: pathpos with external unmetafy. I have bad vibes."); + patinpath = NULL; patinstart = string; - tryalloced = patinpath = NULL; + /* stringlen is unmetafied length; unmetalenin is ignored */ + } else { + patstralloc = &patstralloc_struct; + if (patallocstr(prog, string, stringlen, unmetalenin, 0, patstralloc)) { + patinstart = patstralloc->alloced + patstralloc->unmetalenp; + stringlen = patstralloc->unmetalen; + } else + patinstart = string; + if (patstralloc->unmetalenp) + patinpath = patstralloc->alloced; + else + patinpath = NULL; } + patflags = prog->flags; patinend = patinstart + stringlen; /* * From now on we do not require NULL termination of @@ -2181,7 +2300,31 @@ pattryrefs(Patprog prog, char *string, int stringlen, int unmetalen, * Either we are testing against a pure string, * or we can match anything at all. */ - int ret; + int ret, pstrlen; + char *pstr; + if (patstralloc->alloced) + { + /* + * Unmetafied; we need pattern sring that's also unmetafied. + * We'll cache it in the patstralloc structure. + * Note it's on the heap. + */ + if (!patstralloc->progstrunmeta) + { + patstralloc->progstrunmeta = + dupstrpfx(progstr, (int)prog->patmlen); + unmetafy(patstralloc->progstrunmeta, + &patstralloc->progstrunmetalen); + } + pstr = patstralloc->progstrunmeta; + pstrlen = patstralloc->progstrunmetalen; + } + else + { + /* Metafied. */ + pstr = progstr; + pstrlen = (int)prog->patmlen; + } if (prog->flags & PAT_ANY) { /* * Optimisation for a single "*": always matches @@ -2193,11 +2336,11 @@ pattryrefs(Patprog prog, char *string, int stringlen, int unmetalen, * Testing a pure string. See if initial * components match. */ - int lendiff = stringlen - prog->patmlen; + int lendiff = stringlen - pstrlen; if (lendiff < 0) { /* No, the pattern string is too long. */ ret = 0; - } else if (!memcmp(progstr, patinstart, prog->patmlen)) { + } else if (!memcmp(pstr, patinstart, pstrlen)) { /* * Initial component matches. Matches either * if lengths are the same or we are not anchored @@ -2219,24 +2362,36 @@ pattryrefs(Patprog prog, char *string, int stringlen, int unmetalen, } else { /* * Remember the length in case used for ${..#..} etc. - * In this case, we didn't unmetafy the string. + * In this case, we didn't unmetafy the pattern string + * In the orignal structure, but it might be unmetafied + * for use with an unmetafied test string. */ - patinlen = (int)prog->patmlen; + patinlen = pstrlen; /* if matching files, must update globbing flags */ patglobflags = prog->globend; if ((patglobflags & GF_MATCHREF) && !(patflags & PAT_FILE)) { - char *str = ztrduppfx(patinstart, patinlen); + char *str; int mlen; - /* - * Count the characters. We're not using CHARSUB() - * because the string is still metafied. - */ - MB_METACHARINIT(); - mlen = MB_METASTRLEN2END(patinstart, 0, - patinstart + patinlen); + if (patstralloc->alloced) { + /* + * Unmetafied: pstrlen contains unmetafied + * length in bytes. + */ + str = metafy(patinstart, pstrlen, META_DUP); + mlen = CHARSUB(patinstart, patinstart + pstrlen); + } else { + str = ztrduppfx(patinstart, patinlen); + /* + * Count the characters. We're not using CHARSUB() + * because the string is still metafied. + */ + MB_METACHARINIT(); + mlen = MB_METASTRLEN2END(patinstart, 0, + patinstart + patinlen); + } setsparam("MATCH", str); setiparam("MBEGIN", @@ -2248,9 +2403,6 @@ pattryrefs(Patprog prog, char *string, int stringlen, int unmetalen, } } - if (tryalloced) - zfree(tryalloced, unmetalen + unmetalenp); - return ret; } else { int q = queue_signal_level(); @@ -2287,8 +2439,6 @@ pattryrefs(Patprog prog, char *string, int stringlen, int unmetalen, } } if (!ret) { - if (tryalloced) - zfree(tryalloced, unmetalen + unmetalenp); return 0; } @@ -2320,8 +2470,11 @@ pattryrefs(Patprog prog, char *string, int stringlen, int unmetalen, /* * Optimization: if we didn't find any Meta characters * to begin with, we don't need to look for them now. + * + * For patstralloc pased in, we want the unmetafied length. */ - if (unmetalen != origlen) { + if (patstralloc == &patstralloc_struct && + patstralloc->unmetalen != origlen) { for (ptr = patinstart; ptr < patinput; ptr++) if (imeta(*ptr)) patinlen++; @@ -2442,16 +2595,15 @@ pattryrefs(Patprog prog, char *string, int stringlen, int unmetalen, restore_queue_signals(q); - if (tryalloced) - zfree(tryalloced, unmetalen + unmetalenp); - return ret; } } /* * Return length of previous succesful match. This is - * in metafied bytes, i.e. includes a count of Meta characters. + * in metafied bytes, i.e. includes a count of Meta characters, + * unless the match was done on an unmetafied string using + * a patstralloc stuct, in which case it, too is unmetafed. * Unusual and futile attempt at modular encapsulation. */ @@ -2641,19 +2793,30 @@ patmatch(Upat prog) start = compend = patinput; comp = 0; while (patinput < patinend && idigit(*patinput)) { - if (comp) - comp *= 10; - comp += *patinput - '0'; + int out_of_range = 0; + int digit = *patinput - '0'; + if (comp > ZRANGE_MAX / (zlong)10) { + out_of_range = 1; + } else { + zrange_t c10 = comp ? comp * 10 : 0; + if (ZRANGE_MAX - c10 < digit) { + out_of_range = 1; + } else { + comp = c10; + comp += digit; + } + } patinput++; compend++; - if (comp & ((zrange_t)1 << (sizeof(comp)*8 - + if (out_of_range || + (comp & ((zrange_t)1 << (sizeof(comp)*8 - #ifdef ZRANGE_T_IS_SIGNED 2 #else 1 #endif - ))) { + )))) { /* * Out of range (allowing for signedness, which * we need if we are using zlongs). @@ -3213,6 +3376,7 @@ patmatch(Upat prog) scan[P_CT_CURRENT].l = cur + 1; if (patmatch(scan + P_CT_OPERAND)) return 1; + scan[P_CT_CURRENT].l = cur; patinput = patinput_thistime; } if (cur < min) diff --git a/Src/subst.c b/Src/subst.c index 021d23444..b7f8338c7 100644 --- a/Src/subst.c +++ b/Src/subst.c @@ -44,15 +44,23 @@ char nulstring[] = {Nularg, '\0'}; * - Brace expansion * - Tilde and equals substitution * - * PREFORK_* flags are defined in zsh.h + * "flag"s contains PREFORK_* flags, defined in zsh.h. + * + * "ret_flags" is used to return values from nested parameter + * substitions. It may be NULL in which case PREFORK_SUBEXP + * must not appear in flags; any return value from below + * will be discarded. */ /**/ mod_export void -prefork(LinkList list, int flags) +prefork(LinkList list, int flags, int *ret_flags) { LinkNode node, stop = 0; int keep = 0, asssub = (flags & PREFORK_TYPESET) && isset(KSHTYPESET); + int ret_flags_local = 0; + if (!ret_flags) + ret_flags = &ret_flags_local; /* will be discarded */ queue_signals(); for (node = firstnode(list); node; incnode(node)) { @@ -75,10 +83,8 @@ prefork(LinkList list, int flags) setdata(node, cptr); } if (!(node = stringsubst(list, node, - flags & (PREFORK_SINGLE|PREFORK_SPLIT| - PREFORK_SHWORDSPLIT| - PREFORK_NOSHWORDSPLIT), - asssub))) { + flags & ~(PREFORK_TYPESET|PREFORK_ASSIGN), + ret_flags, asssub))) { unqueue_signals(); return; } @@ -149,7 +155,8 @@ stringsubstquote(char *strstart, char **pstrdpos) /**/ static LinkNode -stringsubst(LinkList list, LinkNode node, int pf_flags, int asssub) +stringsubst(LinkList list, LinkNode node, int pf_flags, int *ret_flags, + int asssub) { int qt; char *str3 = (char *)getdata(node); @@ -235,7 +242,8 @@ stringsubst(LinkList list, LinkNode node, int pf_flags, int asssub) pf_flags |= PREFORK_SHWORDSPLIT; node = paramsubst( list, node, &str, qt, - pf_flags & (PREFORK_SINGLE|PREFORK_SHWORDSPLIT)); + pf_flags & (PREFORK_SINGLE|PREFORK_SHWORDSPLIT| + PREFORK_SUBEXP), ret_flags); if (errflag || !node) return NULL; str3 = (char *)getdata(node); @@ -413,7 +421,7 @@ singsub(char **s) init_list1(foo, *s); - prefork(&foo, PREFORK_SINGLE); + prefork(&foo, PREFORK_SINGLE, NULL); if (errflag) return; *s = (char *) ugetnode(&foo); @@ -430,11 +438,15 @@ singsub(char **s) * set to 1. Otherwise, *isarr is set to 0, and the result is put into *s, * with any necessary joining of multiple elements using sep (which can be * NULL to use IFS). The return value is true iff the expansion resulted - * in an empty list. */ + * in an empty list. + * + * *ms_flags is set to bits in the enum above as neeed. + */ /**/ static int -multsub(char **s, int pf_flags, char ***a, int *isarr, char *sep) +multsub(char **s, int pf_flags, char ***a, int *isarr, char *sep, + int *ms_flags) { int l; char **r, **p, *x = *s; @@ -450,6 +462,7 @@ multsub(char **s, int pf_flags, char ***a, int *isarr, char *sep) l++; if (!iwsep(STOUC(c))) break; + *ms_flags |= MULTSUB_WS_AT_START; } } @@ -481,8 +494,10 @@ multsub(char **s, int pf_flags, char ***a, int *isarr, char *sep) if (!WC_ZISTYPE(c, ISEP)) break; } - if (!*x) + if (!*x) { + *ms_flags |= MULTSUB_WS_AT_END; break; + } insertlinknode(&foo, n, (void *)x), incnode(n); } } @@ -509,7 +524,7 @@ multsub(char **s, int pf_flags, char ***a, int *isarr, char *sep) } } - prefork(&foo, pf_flags); + prefork(&foo, pf_flags, ms_flags); if (errflag) { if (isarr) *isarr = 0; @@ -1494,7 +1509,8 @@ check_colon_subscript(char *str, char **endp) /**/ static LinkNode -paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags) +paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags, + int *ret_flags) { char *aptr = *str, c, cc; char *s = aptr, *fstr, *idbeg, *idend, *ostr = (char *) getdata(n); @@ -1717,6 +1733,20 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags) * This is for compatibility. */ int horrible_offset_hack = 0; + /* + * Signal back from multsub: with something like + * x${:- $foo} + * with word-splitting active we need to split on that leading + * whitespace. However, if there's no "x" the whitespace is + * simply removed. + */ + int ms_flags = 0; + /* + * We need to do an extra fetch to honour the (P) flag. + * Complicated by the use of subexpressions that may have + * nested (P) flags. + */ + int fetch_needed; *s++ = '\0'; /* @@ -2265,7 +2295,8 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags) * remove the aspar test and extract a value from an array, if * necessary, when we handle (P) lower down. */ - if (multsub(&val, 0, (aspar ? NULL : &aval), &isarr, NULL) && quoted) { + if (multsub(&val, PREFORK_SUBEXP, (aspar ? NULL : &aval), &isarr, NULL, + &ms_flags) && quoted) { /* Empty quoted string --- treat as null string, not elided */ isarr = -1; aval = (char **) hcalloc(sizeof(char *)); @@ -2279,8 +2310,39 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags) */ while (inull(*s)) s++; + if (ms_flags & MULTSUB_PARAM_NAME) { + /* + * Downbelow has told us this is a parameter name, e.g. + * ${${(P)name}...}. We're going to behave as if + * we have exactly that name followed by the rest of + * the parameter for subscripting etc. + * + * See below for where we set the flag in the nested + * substitution. + */ + if (isarr) { + if (aval[0] && aval[1]) { + zerr("parameter name reference used with array"); + return NULL; + } + val = aval[0]; + isarr = 0; + } + s = dyncat(val, s); + /* Now behave po-faced as if it was always like that... */ + subexp = 0; + /* + * If this is a (P) (first test) and at the top level + * (second test) we can't rely on the caller fetching + * the result from the pending aspar. So do it below. + */ + fetch_needed = aspar && !(pf_flags & PREFORK_SUBEXP); + } else + fetch_needed = 0; /* any initial aspar fetch already done */ v = (Value) NULL; - } else if (aspar) { + } else + fetch_needed = aspar; /* aspar fetch still needed */ + if (fetch_needed) { /* * No subexpression, but in any case the value is going * to give us the name of a parameter on which we do @@ -2296,6 +2358,17 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags) } else vunset = 1; } + if (aspar && (pf_flags & PREFORK_SUBEXP)) { + /* + * This is the inner handling for the case referred to above + * where we have something like ${${(P)name}...}. + * + * Treat this as as a normal value here; all transformations on + * result are in outer instance. + */ + aspar = 0; + *ret_flags |= MULTSUB_PARAM_NAME; + } /* * We need to retrieve a value either if we haven't already * got it from a subexpression, or if the processing so @@ -2736,7 +2809,7 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags) split_flags = PREFORK_NOSHWORDSPLIT; } multsub(&val, split_flags, (aspar ? NULL : &aval), - &isarr, NULL); + &isarr, NULL, &ms_flags); copied = 1; spbreak = 0; /* Leave globsubst on if forced */ @@ -2765,13 +2838,14 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags) * behavior on caller choice of PREFORK_SHWORDSPLIT. */ multsub(&val, spbreak ? PREFORK_SINGLE : PREFORK_NOSHWORDSPLIT, - NULL, &isarr, NULL); + NULL, &isarr, NULL, &ms_flags); } else { if (spbreak) split_flags = PREFORK_SPLIT|PREFORK_SHWORDSPLIT; else split_flags = PREFORK_NOSHWORDSPLIT; - multsub(&val, split_flags, &aval, &isarr, NULL); + multsub(&val, split_flags, &aval, &isarr, NULL, + &ms_flags); spbreak = 0; } if (arrasg) { @@ -3303,6 +3377,7 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags) } if (haserr || errflag) return NULL; + ms_flags = 0; } /* * This handles taking a length with ${#foo} and variations. @@ -3341,6 +3416,7 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags) sprintf(buf, "%ld", len); val = dupstring(buf); isarr = 0; + ms_flags = 0; } /* At this point we make sure that our arrayness has affected the * arrayness of the linked list. Then, we can turn our value into @@ -3370,6 +3446,7 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags) if (isarr) { val = sepjoin(aval, sep, 1); isarr = 0; + ms_flags = 0; } if (!ssub && (spbreak || spsep)) { aval = sepsplit(val, spsep, 0, 1); @@ -3649,6 +3726,20 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags) * equivalent and only locally decide if we need to treat it * as a scalar.) */ + + /* + * If a multsub result had whitespace at the start and we're + * splitting and there's a previous string, now's the time to do so. + */ + if ((ms_flags & MULTSUB_WS_AT_START) && aptr > ostr) { + insertlinknode(l, n, dupstrpfx(ostr, aptr - ostr)), incnode(n); + ostr = aptr; + } + /* Likewise at the end */ + if ((ms_flags & MULTSUB_WS_AT_END) && *fstr) { + insertlinknode(l, n, dupstring(fstr)); /* appended, no incnode */ + *fstr = '\0'; + } if (isarr) { char *x; char *y; @@ -3727,7 +3818,7 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags) *--fstr = Marker; init_list1(tl, fstr); - if (!eval && !stringsubst(&tl, firstnode(&tl), ssub, 0)) + if (!eval && !stringsubst(&tl, firstnode(&tl), ssub, ret_flags, 0)) return NULL; *str = aptr; tn = firstnode(&tl); diff --git a/Src/text.c b/Src/text.c index 7e65f43a4..9421d70ce 100644 --- a/Src/text.c +++ b/Src/text.c @@ -632,8 +632,10 @@ gettext2(Estate state) taddstr(" in "); taddlist(state, *state->pc++); } - tindent++; taddnl(0); + taddstr("do"); + taddnl(0); + tindent++; tpush(code, 1); } else { dec_tindent(); diff --git a/Src/utils.c b/Src/utils.c index ca68eae32..0afa8c908 100644 --- a/Src/utils.c +++ b/Src/utils.c @@ -692,9 +692,23 @@ ispwd(char *s) { struct stat sbuf, tbuf; - if (stat(unmeta(s), &sbuf) == 0 && stat(".", &tbuf) == 0) - if (sbuf.st_dev == tbuf.st_dev && sbuf.st_ino == tbuf.st_ino) - return 1; + /* POSIX: environment PWD must be absolute */ + if (*s != '/') + return 0; + + if (stat((s = unmeta(s)), &sbuf) == 0 && stat(".", &tbuf) == 0) + if (sbuf.st_dev == tbuf.st_dev && sbuf.st_ino == tbuf.st_ino) { + /* POSIX: No element of $PWD may be "." or ".." */ + while (*s) { + if (s[0] == '.' && + (!s[1] || s[1] == '/' || + (s[1] == '.' && (!s[2] || s[2] == '/')))) + break; + while (*s++ != '/' && *s) + continue; + } + return !*s; + } return 0; } @@ -1878,6 +1892,37 @@ redup(int x, int y) } /* + * Add an fd opened ithin a module. + * + * fdt is the type of the fd; see the FDT_ definitions in zsh.h. + * The most likely falures are: + * + * FDT_EXTERNAL: the fd can be used within the shell for normal I/O but + * it will not be closed automatically or by normal shell syntax. + * + * FDT_MODULE: as FDT_EXTERNAL, but it can only be closed by the module + * (which should included zclose() as part of the sequence), not by + * the standard shell syntax for closing file descriptors. + * + * FDT_INTERNAL: fd is treated like others created by the shell for + * internal use; it can be closed and will be closed by the shell if it + * exec's or performs an exec with a fork optimised out. + * + * Safe if fd is -1 to indicate failure. + */ +/**/ +mod_export void +addmodulefd(int fd, int fdt) +{ + if (fd >= 0) { + check_fd_table(fd); + fdtable[fd] = fdt; + } +} + +/**/ + +/* * Indicate that an fd has a file lock; if cloexec is 1 it will be closed * on exec. * The fd should already be known to fdtable (e.g. by movefd). @@ -2798,7 +2843,7 @@ spckword(char **s, int hist, int cmd, int ask) * as used in spscan(), so that an autocd is chosen * * only when it is better than anything so far, and * * so we prefer directories earlier in the cdpath. */ - if ((thisdist = mindist(*pp, *s, bestcd)) < d) { + if ((thisdist = mindist(*pp, *s, bestcd, 1)) < d) { best = dupstring(bestcd); d = thisdist; } @@ -4043,7 +4088,8 @@ spname(char *oldname) thresh = 3; else if (thresh > 100) thresh = 100; - if ((thisdist = mindist(newname, spnameguess, spnamebest)) >= thresh) { + thisdist = mindist(newname, spnameguess, spnamebest, *old == '/'); + if (thisdist >= thresh) { /* The next test is always true, except for the first path * * component. We could initialize bestdist to some large * * constant instead, and then compare to that constant here, * @@ -4068,42 +4114,52 @@ spname(char *oldname) /**/ static int -mindist(char *dir, char *mindistguess, char *mindistbest) +mindist(char *dir, char *mindistguess, char *mindistbest, int wantdir) { int mindistd, nd; DIR *dd; char *fn; char *buf; + struct stat st; + size_t dirlen; if (dir[0] == '\0') dir = "."; mindistd = 100; - buf = zalloc(strlen(dir) + strlen(mindistguess) + 2); + if (!(buf = zalloc((dirlen = strlen(dir)) + strlen(mindistguess) + 2))) + return 0; sprintf(buf, "%s/%s", dir, mindistguess); - if (access(unmeta(buf), F_OK) == 0) { + if (stat(unmeta(buf), &st) == 0 && (!wantdir || S_ISDIR(st.st_mode))) { strcpy(mindistbest, mindistguess); free(buf); return 0; } - free(buf); - if (!(dd = opendir(unmeta(dir)))) - return mindistd; - while ((fn = zreaddir(dd, 0))) { - if (spnamepat && pattry(spnamepat, fn)) - continue; - nd = spdist(fn, mindistguess, - (int)strlen(mindistguess) / 4 + 1); - if (nd <= mindistd) { - strcpy(mindistbest, fn); - mindistd = nd; - if (mindistd == 0) - break; + if ((dd = opendir(unmeta(dir)))) { + while ((fn = zreaddir(dd, 0))) { + if (spnamepat && pattry(spnamepat, fn)) + continue; + nd = spdist(fn, mindistguess, + (int)strlen(mindistguess) / 4 + 1); + if (nd <= mindistd) { + if (wantdir) { + if (!(buf = zrealloc(buf, dirlen + strlen(fn) + 2))) + continue; + sprintf(buf, "%s/%s", dir, fn); + if (stat(unmeta(buf), &st) != 0 || !S_ISDIR(st.st_mode)) + continue; + } + strcpy(mindistbest, fn); + mindistd = nd; + if (mindistd == 0) + break; + } } + closedir(dd); } - closedir(dd); + free(buf); return mindistd; } @@ -5385,6 +5441,12 @@ quotestring(const char *s, char **e, int instring) u = s; if (instring == QT_DOLLARS) { /* + * The only way to get Nularg here is when + * it is placeholding for the empty string? + */ + if (inull(*u)) + u++; + /* * As we test for printability here we need to be able * to look for multibyte characters. */ @@ -36,6 +36,16 @@ */ #ifdef ZSH_64_BIT_TYPE typedef ZSH_64_BIT_TYPE zlong; +#if defined(ZLONG_IS_LONG_LONG) && defined(LLONG_MAX) +#define ZLONG_MAX LLONG_MAX +#else +#ifdef ZLONG_IS_LONG_64 +#define ZLONG_MAX LONG_MAX +#else +/* umm... */ +#define ZLONG_MAX ((zlong)9223372036854775807) +#endif +#endif #ifdef ZSH_64_BIT_UTYPE typedef ZSH_64_BIT_UTYPE zulong; #else @@ -44,6 +54,7 @@ typedef unsigned zlong zulong; #else typedef long zlong; typedef unsigned long zulong; +#define ZLONG_MAX LONG_MAX #endif /* @@ -395,25 +406,32 @@ enum { */ #define FDT_EXTERNAL 2 /* + * Entry visible to other processes but controlled by a module. + * The difference from FDT_EXTERNAL is that closing this using + * standard fd syntax will fail as there is some tidying up that + * needs to be done by the module's own mechanism. + */ +#define FDT_MODULE 3 +/* * Entry used by output from the XTRACE option. */ -#define FDT_XTRACE 3 +#define FDT_XTRACE 4 /* * Entry used for file locking. */ -#define FDT_FLOCK 4 +#define FDT_FLOCK 5 /* * As above, but the fd is not marked for closing on exec, * so the shell can still exec the last process. */ -#define FDT_FLOCK_EXEC 5 +#define FDT_FLOCK_EXEC 6 #ifdef PATH_DEV_FD /* * Entry used by a process substition. * This marker is not tested internally as we associated the file * descriptor with a job for closing. */ -#define FDT_PROC_SUBST 6 +#define FDT_PROC_SUBST 7 #endif /* Flags for input stack */ @@ -469,6 +487,7 @@ typedef struct heap *Heap; typedef struct heapstack *Heapstack; typedef struct histent *Histent; typedef struct hookdef *Hookdef; +typedef struct imatchdata *Imatchdata; typedef struct jobfile *Jobfile; typedef struct job *Job; typedef struct linkedmod *Linkedmod; @@ -480,6 +499,7 @@ typedef struct options *Options; typedef struct optname *Optname; typedef struct param *Param; typedef struct paramdef *Paramdef; +typedef struct patstralloc *Patstralloc; typedef struct patprog *Patprog; typedef struct prepromptfn *Prepromptfn; typedef struct process *Process; @@ -1459,6 +1479,15 @@ struct patprog { char patstartch; }; +struct patstralloc { + int unmetalen; /* Unmetafied length of trial string */ + int unmetalenp; /* Unmetafied length of path prefix. + If 0, no path prefix. */ + char *alloced; /* Allocated string, may be NULL */ + char *progstrunmeta; /* Unmetafied pure string in pattern, cached */ + int progstrunmetalen; /* Length of the foregoing */ +}; + /* Flags used in pattern matchers (Patprog) and passed down to patcompile */ #define PAT_FILE 0x0001 /* Pattern is a file name */ @@ -1572,6 +1601,31 @@ typedef struct zpc_disables_save *Zpc_disables_save; /* Range: token followed by the (possibly multibyte) start and end */ #define PP_RANGE 21 +/* + * Argument to get_match_ret() in glob.c + */ +struct imatchdata { + /* Metafied trial string */ + char *mstr; + /* Its length */ + int mlen; + /* Unmetafied string */ + char *ustr; + /* Its length */ + int ulen; + /* Flags (SUB_*) */ + int flags; + /* Replacement string (metafied) */ + char *replstr; + /* + * List of bits of matches to concatenate with replacement string. + * The data is a struct repldata. It is not used in cases like + * ${...//#foo/bar} even though SUB_GLOBAL is set, since the match + * is anchored. It goes on the heap. + */ + LinkList repllist; +}; + /* Globbing flags: lower 8 bits gives approx count */ #define GF_LCMATCHUC 0x0100 #define GF_IGNCASE 0x0200 @@ -1812,18 +1866,45 @@ enum { }; /* Flags as the second argument to prefork */ -/* argument handled like typeset foo=bar */ -#define PREFORK_TYPESET 0x01 -/* argument handled like the RHS of foo=bar */ -#define PREFORK_ASSIGN 0x02 -/* single word substitution */ -#define PREFORK_SINGLE 0x04 -/* explicitly split nested substitution */ -#define PREFORK_SPLIT 0x08 -/* SHWORDSPLIT in parameter expn */ -#define PREFORK_SHWORDSPLIT 0x10 -/* SHWORDSPLIT forced off in nested subst */ -#define PREFORK_NOSHWORDSPLIT 0x20 +enum { + /* argument handled like typeset foo=bar */ + PREFORK_TYPESET = 0x01, + /* argument handled like the RHS of foo=bar */ + PREFORK_ASSIGN = 0x02, + /* single word substitution */ + PREFORK_SINGLE = 0x04, + /* explicitly split nested substitution */ + PREFORK_SPLIT = 0x08, + /* SHWORDSPLIT in parameter expn */ + PREFORK_SHWORDSPLIT = 0x10, + /* SHWORDSPLIT forced off in nested subst */ + PREFORK_NOSHWORDSPLIT = 0x20, + /* Prefork is part of a parameter subexpression */ + PREFORK_SUBEXP = 0x40 +}; + +/* + * Bit flags passed back from multsub() to paramsubst(). + * Some flags go from a nested parmsubst() through the enclosing + * stringsubst() and prefork(). + */ +enum { + /* + * Set if the string had whitespace at the start + * that should cause word splitting against any preceeding string. + */ + MULTSUB_WS_AT_START = 1, + /* + * Set if the string had whitespace at the end + * that should cause word splitting against any following string. + */ + MULTSUB_WS_AT_END = 2, + /* + * Set by nested paramsubst() to indicate the return + * value is a parameter name, rather than a value. + */ + MULTSUB_PARAM_NAME = 4 +}; /* * Structure for adding parameters in a module. @@ -1885,9 +1966,6 @@ struct paramdef { { name, flags | PM_SPECIAL | PM_HIDE | PM_HIDEVAL, \ NULL, gsufn, getfn, scanfn, NULL } -#define setsparam(S,V) assignsparam(S,V,0) -#define setaparam(S,V) assignaparam(S,V,0) - /* * Flags for assignsparam and assignaparam. */ @@ -2164,6 +2242,7 @@ enum { GLOBASSIGN, GLOBCOMPLETE, GLOBDOTS, + GLOBSTARSHORT, GLOBSUBST, HASHCMDS, HASHDIRS, diff --git a/Src/zsh.mdd b/Src/zsh.mdd index c2e59c910..86dd58866 100644 --- a/Src/zsh.mdd +++ b/Src/zsh.mdd @@ -7,7 +7,7 @@ functions='Functions/Chpwd/* Functions/Exceptions/* Functions/Misc/* Functions/M nozshdep=1 alwayslink=1 -# autobins not specified because of alwayslink +# autofeatures not specified because of alwayslink objects="builtin.o compat.o cond.o context.o \ exec.o glob.o hashtable.o hashnameddir.o \ |