summaryrefslogtreecommitdiff
path: root/Src
diff options
context:
space:
mode:
Diffstat (limited to 'Src')
-rw-r--r--Src/Modules/curses.c4
-rw-r--r--Src/Modules/curses.mdd2
-rw-r--r--Src/Modules/db_gdbm.c9
-rw-r--r--Src/Modules/param_private.c587
-rw-r--r--Src/Modules/param_private.mdd7
-rw-r--r--Src/Modules/socket.c23
-rw-r--r--Src/Modules/tcp.c13
-rw-r--r--Src/Modules/zftp.c8
-rw-r--r--Src/Modules/zpty.c2
-rw-r--r--Src/Zle/compctl.c4
-rw-r--r--Src/Zle/complist.c13
-rw-r--r--Src/Zle/compmatch.c17
-rw-r--r--Src/Zle/computil.c11
-rw-r--r--Src/Zle/zle_hist.c8
-rw-r--r--Src/Zle/zle_keymap.c37
-rw-r--r--Src/Zle/zle_main.c15
-rw-r--r--Src/Zle/zle_misc.c11
-rw-r--r--Src/Zle/zle_params.c24
-rw-r--r--Src/Zle/zle_refresh.c3
-rw-r--r--Src/Zle/zle_thingy.c47
-rw-r--r--Src/Zle/zle_tricky.c7
-rw-r--r--Src/Zle/zle_utils.c23
-rw-r--r--Src/Zle/zle_vi.c2
-rw-r--r--Src/builtin.c100
-rw-r--r--Src/cond.c2
-rw-r--r--Src/exec.c424
-rw-r--r--Src/glob.c458
-rw-r--r--Src/hist.c18
-rw-r--r--Src/init.c6
-rw-r--r--Src/lex.c40
-rw-r--r--Src/math.c3
-rw-r--r--Src/mem.c89
-rw-r--r--Src/mkmakemod.sh10
-rw-r--r--Src/options.c31
-rw-r--r--Src/params.c110
-rw-r--r--Src/parse.c5
-rw-r--r--Src/pattern.c388
-rw-r--r--Src/subst.c131
-rw-r--r--Src/text.c4
-rw-r--r--Src/utils.c106
-rw-r--r--Src/zsh.h117
-rw-r--r--Src/zsh.mdd2
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;
}
diff --git a/Src/lex.c b/Src/lex.c
index 70f3d142a..0f260d08f 100644
--- a/Src/lex.c
+++ b/Src/lex.c
@@ -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)
diff --git a/Src/mem.c b/Src/mem.c
index b9569ea0c..e31145e1e 100644
--- a/Src/mem.c
+++ b/Src/mem.c
@@ -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.
*/
diff --git a/Src/zsh.h b/Src/zsh.h
index 4e2cb656e..d3bfcefcc 100644
--- a/Src/zsh.h
+++ b/Src/zsh.h
@@ -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 \