summaryrefslogtreecommitdiff
path: root/Src/Modules
diff options
context:
space:
mode:
authorJoe Rayhawk <jrayhawk@fairlystable.org>2025-04-30 02:07:56 -0700
committerJoe Rayhawk <jrayhawk@fairlystable.org>2025-04-30 02:07:56 -0700
commit26e09889646be3ea65b4a3dfeda26213e4bb6a27 (patch)
tree4f3c73a9416bf47ad7e125383d23cf42879e38d7 /Src/Modules
parent841bce705a58b04220b1f257abcc00ae71cbdbdc (diff)
parent001cba48ce3b964cf01fb3e2af54b20eacbc9bf5 (diff)
downloadzsh-26e09889646be3ea65b4a3dfeda26213e4bb6a27.tar.gz
zsh-26e09889646be3ea65b4a3dfeda26213e4bb6a27.zip
Merge branch 'upstream' into debian
Diffstat (limited to 'Src/Modules')
-rw-r--r--Src/Modules/curses.c8
-rw-r--r--Src/Modules/db_gdbm.c6
-rw-r--r--Src/Modules/files.c4
-rw-r--r--Src/Modules/hlgroup.c221
-rw-r--r--Src/Modules/hlgroup.mdd7
-rw-r--r--Src/Modules/ksh93.c287
-rw-r--r--Src/Modules/ksh93.mdd10
-rw-r--r--Src/Modules/mapfile.c10
-rw-r--r--Src/Modules/param_private.c151
-rw-r--r--Src/Modules/parameter.c28
-rw-r--r--Src/Modules/pcre.c327
-rw-r--r--Src/Modules/pcre.mdd2
-rw-r--r--Src/Modules/random.c326
-rw-r--r--Src/Modules/random.mdd7
-rw-r--r--Src/Modules/random_real.c213
-rw-r--r--Src/Modules/stat.c5
-rw-r--r--Src/Modules/system.c19
-rw-r--r--Src/Modules/terminfo.c4
-rw-r--r--Src/Modules/watch.c99
-rw-r--r--Src/Modules/zftp.c21
-rw-r--r--Src/Modules/zprof.c25
-rw-r--r--Src/Modules/zpty.c2
-rw-r--r--Src/Modules/zutil.c187
23 files changed, 1649 insertions, 320 deletions
diff --git a/Src/Modules/curses.c b/Src/Modules/curses.c
index e46903916..8950cc153 100644
--- a/Src/Modules/curses.c
+++ b/Src/Modules/curses.c
@@ -1302,7 +1302,7 @@ zccmd_mouse(const char *nam, char **args)
zlong delay;
if (!*++args ||
- ((delay = zstrtol(*args, &eptr, 10)), eptr != NULL)) {
+ ((delay = zstrtol(*args, &eptr, 10)), *eptr != '\0')) {
zwarnnam(nam, "mouse delay requires an integer argument");
return 1;
}
@@ -1326,7 +1326,7 @@ zccmd_mouse(const char *nam, char **args)
if (old_mask != zcurses_mouse_mask)
zcurses_flags |= ZCF_MOUSE_MASK_CHANGED;
} else {
- zwarnnam(nam, "unrecognised mouse command: %s", *arg);
+ zwarnnam(nam, "unrecognised mouse command: %s", arg);
return 1;
}
}
@@ -1426,10 +1426,10 @@ zccmd_querychar(const char *nam, char **args)
inc &= A_CHARTEXT;
if (imeta(inc)) {
instr[0] = Meta;
- instr[1] = STOUC(inc ^ 32);
+ instr[1] = (unsigned char) (inc ^ 32);
instr[2] = '\0';
} else {
- instr[0] = STOUC(inc);
+ instr[0] = (unsigned char) inc;
instr[1] = '\0';
}
attrs = inc;
diff --git a/Src/Modules/db_gdbm.c b/Src/Modules/db_gdbm.c
index 7e11ec939..3fefd412b 100644
--- a/Src/Modules/db_gdbm.c
+++ b/Src/Modules/db_gdbm.c
@@ -34,8 +34,8 @@
#include "db_gdbm.mdh"
#include "db_gdbm.pro"
-#ifndef PM_UPTODATE
-#define PM_UPTODATE (1<<19) /* Parameter has up-to-date data (e.g. loaded from DB) */
+#ifndef PM_UPTODATE /* Parameter has up-to-date data (e.g. loaded from DB) */
+#define PM_UPTODATE PM_DONTIMPORT_SUID /* Safe PM_ bit to re-use */
#endif
static Param createhash( char *name, int flags );
@@ -111,7 +111,7 @@ bin_ztie(char *nam, char **args, Options ops, UNUSED(int func))
struct gsu_scalar_ext *dbf_carrier;
char *resource_name, *pmname;
GDBM_FILE dbf = NULL;
- int read_write = GDBM_SYNC, pmflags = PM_REMOVABLE;
+ int read_write = GDBM_SYNC, pmflags = PM_REMOVABLE|PM_SINGLE;
Param tied_param;
if(!OPT_ISSET(ops,'d')) {
diff --git a/Src/Modules/files.c b/Src/Modules/files.c
index bf0e8f8a8..a3fec1daa 100644
--- a/Src/Modules/files.c
+++ b/Src/Modules/files.c
@@ -29,8 +29,8 @@
#include "files.mdh"
-typedef int (*MoveFunc) _((char const *, char const *));
-typedef int (*RecurseFunc) _((char *, char *, struct stat const *, void *));
+typedef int (*MoveFunc) (char const *, char const *);
+typedef int (*RecurseFunc) (char *, char *, struct stat const *, void *);
struct recursivecmd;
diff --git a/Src/Modules/hlgroup.c b/Src/Modules/hlgroup.c
new file mode 100644
index 000000000..6c5a4bec4
--- /dev/null
+++ b/Src/Modules/hlgroup.c
@@ -0,0 +1,221 @@
+/*
+ * hlgroup.c - Supporting parameters for highlight groups
+ *
+ * This file is part of zsh, the Z shell.
+ *
+ * Copyright (c) 2024 Oliver Kiddle
+ * 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 Oliver Kiddle 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 Oliver Kiddle and the Zsh Development Group have been advised of
+ * the possibility of such damage.
+ *
+ * Oliver Kiddle 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 Oliver Kiddle and the
+ * Zsh Development Group have no obligation to provide maintenance,
+ * support, updates, enhancements, or modifications.
+ *
+ */
+
+#include "hlgroup.mdh"
+#include "hlgroup.pro"
+
+#define GROUPVAR ".zle.hlgroups"
+
+static const struct gsu_scalar pmesc_gsu =
+{ strgetfn, nullstrsetfn, nullunsetfn };
+
+/**/
+static char *
+convertattr(char *attrstr, int sgr)
+{
+ zattr atr;
+ char *r, *s;
+ int len;
+
+ match_highlight(attrstr, &atr, NULL);
+ s = zattrescape(atr, sgr ? NULL : &len);
+
+ if (sgr) {
+ char *c = s, *t = s - 1;
+
+ while (c[0] == '\033' && c[1] == '[') {
+ for (c += 2; ; c++) {
+ if (isdigit(*c))
+ *++t = *c;
+ else if (*c == ';' || *c == ':')
+ *++t = ';';
+ else
+ break;
+ }
+ t++;
+ if (*c != 'm')
+ break;
+ *t = ';';
+ c++;
+ }
+ if (t <= s) { /* always return at least "0" */
+ *s = '0';
+ t = s + 1;
+ }
+ *t = '\0';
+ len = t - s;
+ }
+
+ r = dupstring_wlen(s, len);
+ free(s);
+ return r;
+}
+
+/**/
+static HashNode
+getgroup(const char *name, int sgr)
+{
+ Param pm = NULL;
+ HashNode hn;
+ HashTable hlg;
+ Value v;
+ struct value vbuf;
+ char *var = GROUPVAR;
+
+ pm = (Param) hcalloc(sizeof(struct param));
+ pm->gsu.s = &pmesc_gsu;
+ pm->node.nam = dupstring(name);
+ pm->node.flags = PM_SCALAR|PM_SPECIAL;
+
+ if (!(v = getvalue(&vbuf, &var, 0)) ||
+ PM_TYPE(v->pm->node.flags) != PM_HASHED ||
+ !(hlg = v->pm->gsu.h->getfn(v->pm)) ||
+ !(hn = gethashnode2(hlg, name)) ||
+ (((Param) hn)->node.flags & PM_UNSET))
+ {
+ pm->u.str = dupstring("");
+ pm->node.flags |= PM_UNSET;
+ } else {
+ pm->u.str = convertattr(((Param) hn)->u.str, sgr);
+ }
+
+ return &pm->node;
+}
+
+/**/
+static void
+scangroup(ScanFunc func, int flags, int sgr)
+{
+ struct param pm;
+ int i;
+ HashNode hn;
+ HashTable hlg;
+ Value v;
+ struct value vbuf;
+ char *var = GROUPVAR;
+
+ if (!(v = getvalue(&vbuf, &var, 0)) ||
+ PM_TYPE(v->pm->node.flags) != PM_HASHED)
+ return;
+ hlg = v->pm->gsu.h->getfn(v->pm);
+
+ memset((void *)&pm, 0, sizeof(struct param));
+ pm.node.flags = PM_SCALAR;
+ pm.gsu.s = &pmesc_gsu;
+
+ for (i = 0; i < hlg->hsize; i++)
+ for (hn = hlg->nodes[i]; hn; hn = hn->next) {
+ pm.u.str = convertattr(((Param) hn)->u.str, sgr);
+ pm.node.nam = hn->nam;
+ func(&pm.node, flags);
+ }
+}
+/**/
+static HashNode
+getpmesc(UNUSED(HashTable ht), const char *name)
+{
+ return getgroup(name, 0);
+}
+
+/**/
+static void
+scanpmesc(UNUSED(HashTable ht), ScanFunc func, int flags)
+{
+ scangroup(func, flags, 0);
+}
+
+/**/
+static HashNode
+getpmsgr(UNUSED(HashTable ht), const char *name)
+{
+ return getgroup(name, 1);
+}
+
+/**/
+static void
+scanpmsgr(UNUSED(HashTable ht), ScanFunc func, int flags)
+{
+ scangroup(func, flags, 1);
+}
+
+static struct paramdef partab[] = {
+ SPECIALPMDEF(".zle.esc", PM_READONLY_SPECIAL, 0, getpmesc, scanpmesc),
+ SPECIALPMDEF(".zle.sgr", PM_READONLY_SPECIAL, 0, getpmsgr, scanpmsgr)
+};
+
+static struct features module_features = {
+ NULL, 0,
+ NULL, 0,
+ NULL, 0,
+ partab, sizeof(partab)/sizeof(*partab),
+ 0
+};
+
+/**/
+int
+setup_(UNUSED(Module m))
+{
+ 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_(UNUSED(Module m))
+{
+ return 0;
+}
+
+/**/
+int
+cleanup_(Module m)
+{
+ return setfeatureenables(m, &module_features, NULL);
+}
+
+/**/
+int
+finish_(UNUSED(Module m))
+{
+ return 0;
+}
diff --git a/Src/Modules/hlgroup.mdd b/Src/Modules/hlgroup.mdd
new file mode 100644
index 000000000..ee3ba7260
--- /dev/null
+++ b/Src/Modules/hlgroup.mdd
@@ -0,0 +1,7 @@
+name=zsh/hlgroup
+link=either
+load=yes
+
+autofeatures="p:.zle.esc p:.zle.sgr"
+
+objects="hlgroup.o"
diff --git a/Src/Modules/ksh93.c b/Src/Modules/ksh93.c
new file mode 100644
index 000000000..fa0785cda
--- /dev/null
+++ b/Src/Modules/ksh93.c
@@ -0,0 +1,287 @@
+/*
+ * ksh93.c - support for more ksh93 features
+ *
+ * This file is part of zsh, the Z shell.
+ *
+ * Copyright (c) 2022 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 "ksh93.mdh"
+#include "ksh93.pro"
+
+/* Implementing "namespace" requires creating a new keword. Hrm. */
+
+/*
+ * Standard module configuration/linkage
+ */
+
+static struct builtin bintab[] = {
+ BUILTIN("nameref", BINF_ASSIGN, (HandlerFunc)bin_typeset, 0, -1, 0, "gpru", "n")
+};
+
+#include "zsh.mdh"
+
+static void
+edcharsetfn(Param pm, char *x)
+{
+ /*
+ * To make this work like ksh, we must intercept $KEYS before the widget
+ * is looked up, so that changing the key sequence causes a different
+ * widget to be substituted. Somewhat similar to "bindkey -s".
+ *
+ * Ksh93 adds SIGKEYBD to the trap list for this purpose.
+ */
+ ;
+}
+
+static char **
+matchgetfn(Param pm)
+{
+ char **zsh_match = getaparam("match");
+
+ /* For this to work accurately, ksh emulation should always imply
+ * that the (#m) and (#b) extendedglob operators are enabled.
+ *
+ * When we have a 0th element (ksharrays), it is $MATCH. Elements
+ * 1st and larger mirror the $match array.
+ */
+
+ if (pm->u.arr)
+ freearray(pm->u.arr);
+ if (zsh_match && *zsh_match) {
+ if (isset(KSHARRAYS)) {
+ char **ap =
+ (char **) zalloc(sizeof(char *) * (arrlen(zsh_match)+1));
+ pm->u.arr = ap;
+ *ap++ = ztrdup(getsparam("MATCH"));
+ while (*zsh_match)
+ *ap = ztrdup(*zsh_match++);
+ } else
+ pm->u.arr = zarrdup(zsh_match);
+ } else if (isset(KSHARRAYS)) {
+ pm->u.arr = mkarray(ztrdup(getsparam("MATCH")));
+ } else
+ pm->u.arr = NULL;
+
+ return arrgetfn(pm);
+}
+
+static const struct gsu_scalar constant_gsu =
+ { strgetfn, NULL, nullunsetfn };
+
+static const struct gsu_scalar sh_edchar_gsu =
+ { strvargetfn, edcharsetfn, nullunsetfn };
+static const struct gsu_scalar sh_edmode_gsu =
+ { strgetfn, nullstrsetfn, nullunsetfn };
+static const struct gsu_array sh_match_gsu =
+ { matchgetfn, arrsetfn, stdunsetfn };
+static const struct gsu_scalar sh_name_gsu =
+ { strvargetfn, nullstrsetfn, nullunsetfn };
+static const struct gsu_scalar sh_subscript_gsu =
+ { strvargetfn, nullstrsetfn, nullunsetfn };
+
+static char sh_unsetval[2]; /* Dummy to treat as NULL */
+static char *sh_name = sh_unsetval;
+static char *sh_subscript = sh_unsetval;
+static char *sh_edchar = sh_unsetval;
+static char sh_edmode[2];
+
+/*
+ * Some parameters listed here do not appear in ksh93.mdd autofeatures
+ * because they are only instantiated by ksh93_wrapper() below. This
+ * obviously includes those commented out here.
+ */
+static struct paramdef partab[] = {
+ PARAMDEF(".sh.edchar", PM_SCALAR|PM_SPECIAL,
+ &sh_edchar, &sh_edchar_gsu),
+ PARAMDEF(".sh.edmode", PM_SCALAR|PM_READONLY|PM_SPECIAL,
+ &sh_edmode, &sh_edmode_gsu),
+ PARAMDEF(".sh.file", PM_NAMEREF|PM_READONLY, "ZSH_SCRIPT", &constant_gsu),
+ PARAMDEF(".sh.lineno", PM_NAMEREF|PM_READONLY, "LINENO", &constant_gsu),
+ PARAMDEF(".sh.match", PM_ARRAY|PM_READONLY, NULL, &sh_match_gsu),
+ PARAMDEF(".sh.name", PM_SCALAR|PM_READONLY|PM_SPECIAL,
+ &sh_name, &sh_name_gsu),
+ PARAMDEF(".sh.subscript", PM_SCALAR|PM_READONLY|PM_SPECIAL,
+ &sh_subscript, &sh_subscript_gsu),
+ PARAMDEF(".sh.subshell", PM_NAMEREF|PM_READONLY, "ZSH_SUBSHELL", &constant_gsu),
+ /* SPECIALPMDEF(".sh.value", 0, NULL, NULL, NULL), */
+ PARAMDEF(".sh.version", PM_NAMEREF|PM_READONLY, "ZSH_PATCHLEVEL", &constant_gsu)
+};
+
+static struct features module_features = {
+ bintab, sizeof(bintab)/sizeof(*bintab),
+ NULL, 0,
+ NULL, 0,
+ partab, sizeof(partab)/sizeof(*partab),
+ 0
+};
+
+/**/
+static int
+ksh93_wrapper(Eprog prog, FuncWrap w, char *name)
+{
+ Funcstack f;
+ Param pm;
+ zlong num = funcstack->prev ? getiparam(".sh.level") : 0;
+
+ if (!EMULATION(EMULATE_KSH))
+ return 1;
+
+ if (num == 0)
+ for (f = funcstack; f; f = f->prev, num++);
+ else
+ num++;
+
+ queue_signals();
+ ++locallevel; /* Make these local */
+#define LOCAL_NAMEREF (PM_LOCAL|PM_UNSET|PM_NAMEREF)
+ if ((pm = createparam(".sh.command", LOCAL_NAMEREF))) {
+ pm->level = locallevel; /* Why is this necessary? */
+ /* Force scoping by assignent hack */
+ setloopvar(".sh.command", "ZSH_DEBUG_CMD");
+ pm->node.flags |= PM_READONLY;
+ }
+ /* .sh.edchar is in partab and below */
+ if (zleactive && (pm = createparam(".sh.edcol", LOCAL_NAMEREF))) {
+ pm->level = locallevel;
+ setloopvar(".sh.edcol", "CURSOR");
+ pm->node.flags |= (PM_NAMEREF|PM_READONLY);
+ }
+ /* .sh.edmode is in partab and below */
+ if (zleactive && (pm = createparam(".sh.edtext", LOCAL_NAMEREF))) {
+ pm->level = locallevel;
+ setloopvar(".sh.edtext", "BUFFER");
+ pm->node.flags |= PM_READONLY;
+ }
+
+ if ((pm = createparam(".sh.fun", PM_LOCAL|PM_UNSET))) {
+ pm->level = locallevel;
+ setsparam(".sh.fun", ztrdup(name));
+ pm->node.flags |= PM_READONLY;
+ }
+ if ((pm = createparam(".sh.level", PM_LOCAL|PM_UNSET))) {
+ pm->level = locallevel;
+ setiparam(".sh.level", num);
+ }
+ if (zleactive) {
+ extern mod_import_variable char *curkeymapname; /* XXX */
+ extern mod_import_variable char *varedarg; /* XXX */
+ /* bindkey -v forces VIMODE so this test is as good as any */
+ if (curkeymapname && isset(VIMODE) &&
+ strcmp(curkeymapname, "main") == 0)
+ strcpy(sh_edmode, "\033");
+ else
+ strcpy(sh_edmode, "");
+ if (sh_edchar == sh_unsetval)
+ sh_edchar = dupstring(getsparam("KEYS"));
+ if (varedarg) {
+ char *ie = itype_end((sh_name = dupstring(varedarg)), INAMESPC, 0);
+ if (ie && *ie) {
+ *ie++ = '\0';
+ /* Assume bin_vared has validated subscript */
+ sh_subscript = dupstring(ie);
+ ie = sh_subscript + strlen(sh_subscript);
+ *--ie = '\0';
+ } else
+ sh_subscript = sh_unsetval;
+ if ((pm = createparam(".sh.value", LOCAL_NAMEREF))) {
+ pm->level = locallevel;
+ setloopvar(".sh.value", "BUFFER"); /* Hack */
+ pm->node.flags |= PM_READONLY;
+ }
+ } else
+ sh_name = sh_subscript = sh_unsetval;
+ } else {
+ sh_edchar = sh_name = sh_subscript = sh_unsetval;
+ strcpy(sh_edmode, "");
+ /* TODO:
+ * - disciplines
+ * - special handling of .sh.value in math
+ */
+ }
+ --locallevel;
+ unqueue_signals();
+
+ return 1;
+}
+
+static struct funcwrap wrapper[] = {
+ WRAPDEF(ksh93_wrapper),
+};
+
+/**/
+int
+setup_(UNUSED(Module m))
+{
+ 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)
+{
+ return addwrapper(m, wrapper);
+}
+
+/**/
+int
+cleanup_(Module m)
+{
+ struct paramdef *p;
+
+ deletewrapper(m, wrapper);
+
+ /* Clean up namerefs, otherwise deleteparamdef() is confused */
+ for (p = partab; p < partab + sizeof(partab)/sizeof(*partab); ++p) {
+ if (p->flags & PM_NAMEREF) {
+ HashNode hn = gethashnode2(paramtab, p->name);
+ if (hn)
+ ((Param)hn)->node.flags &= ~PM_NAMEREF;
+ }
+ }
+ return setfeatureenables(m, &module_features, NULL);
+}
+
+/**/
+int
+finish_(UNUSED(Module m))
+{
+ return 0;
+}
diff --git a/Src/Modules/ksh93.mdd b/Src/Modules/ksh93.mdd
new file mode 100644
index 000000000..85e35e9cb
--- /dev/null
+++ b/Src/Modules/ksh93.mdd
@@ -0,0 +1,10 @@
+name=zsh/ksh93
+link=either
+load=yes
+
+autofeatures="b:nameref"
+autofeatures_emu="b:nameref p:.sh.command p:.sh.edcol p:.sh.edtext p:.sh.file p:.sh.lineno p:.sh.match p:.sh.subshell p:.sh.version"
+
+moddeps="zsh/zle"
+
+objects="ksh93.o"
diff --git a/Src/Modules/mapfile.c b/Src/Modules/mapfile.c
index dd86fb596..84cdfea18 100644
--- a/Src/Modules/mapfile.c
+++ b/Src/Modules/mapfile.c
@@ -170,6 +170,8 @@ get_contents(char *fname)
#ifdef USE_MMAP
caddr_t mmptr;
struct stat sbuf;
+#else
+ off_t size;
#endif
char *val;
unmetafy(fname = ztrdup(fname), &fd);
@@ -196,12 +198,8 @@ get_contents(char *fname)
close(fd);
#else /* don't USE_MMAP */
val = NULL;
- if ((fd = open(fname, O_RDONLY | O_NOCTTY)) >= 0) {
- LinkList ll;
-
- if ((ll = readoutput(fd, 1, 0)))
- val = peekfirst(ll);
- }
+ if ((size = zstuff(&val, fname)) > 0)
+ val = metafy(val, size, META_HEAPDUP);
#endif /* USE_MMAP */
free(fname);
return val;
diff --git a/Src/Modules/param_private.c b/Src/Modules/param_private.c
index c53839152..044617190 100644
--- a/Src/Modules/param_private.c
+++ b/Src/Modules/param_private.c
@@ -87,12 +87,55 @@ makeprivate(HashNode hn, UNUSED(int flags))
((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;
+ if (is_private(pm->old)) {
+ if (pm->old->node.flags & PM_READONLY) {
+ zerr("read-only variable: %s", pm->node.nam);
+ makeprivate_error = 1;
+ } else if ((pm->node.flags | pm->old->node.flags) ==
+ pm->old->node.flags) {
+ /* private called twice on same parameter */
+ Param tpm = pm;
+ pm = pm->old;
+ --locallevel;
+ /* why have a union if we need this switch anyway? */
+ switch (PM_TYPE(pm->node.flags)) {
+ case PM_SCALAR:
+ pm->gsu.s->setfn(pm, tpm->u.str);
+ tpm->u.str = NULL;
+ break;
+ case PM_INTEGER:
+ pm->gsu.i->setfn(pm, tpm->u.val);
+ break;
+ case PM_EFLOAT:
+ case PM_FFLOAT:
+ pm->gsu.f->setfn(pm, tpm->u.dval);
+ break;
+ case PM_ARRAY:
+ pm->gsu.a->setfn(pm, tpm->u.arr);
+ tpm->u.arr = NULL;
+ break;
+ case PM_HASHED:
+ pm->gsu.h->setfn(pm, tpm->u.hash);
+ tpm->u.hash = NULL;
+ break;
+ }
+ ++locallevel;
+ if (!(tpm->node.flags & PM_UNSET))
+ pm->node.flags &= ~PM_UNSET;
+ } else {
+ zerrnam("private",
+ "can't change type of private param: %s",
+ pm->node.nam);
+ makeprivate_error = 1;
+ }
+ } else {
+ zerrnam("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));
+ struct gsu_closure *gsu = zalloc(sizeof(struct gsu_closure));
switch (PM_TYPE(pm->node.flags)) {
case PM_SCALAR:
gsu->g = (void *)(pm->gsu.s);
@@ -122,6 +165,7 @@ makeprivate(HashNode hn, UNUSED(int flags))
break;
default:
makeprivate_error = 1;
+ zfree(gsu, sizeof(struct gsu_closure));
break;
}
/* PM_HIDE so new parameters in deeper scopes do not shadow */
@@ -229,7 +273,9 @@ setfn_error(Param pm)
* calling the original unsetfn. This assures that if the old unsetfn
* wants to use its getfn or setfn, they're unconditionally present.
* The "explicit" flag indicates that "unset" was called, if zero the
- * parameter is going out of scope (see params.c).
+ * parameter is going out of scope (see params.c). PM_DECLARED is
+ * asserted as if TYPESET_TO_UNSET were in use so that the private
+ * parameter is re-used rather than re-created when assigned again.
*
*/
@@ -252,7 +298,7 @@ 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)
+ if (locallevel == pm->level || locallevel > private_wraplevel)
gsu->setfn(pm, x);
else
setfn_error(pm);
@@ -267,8 +313,11 @@ pps_unsetfn(Param pm, int explicit)
pm->gsu.s = gsu;
if (locallevel <= pm->level)
gsu->unsetfn(pm, explicit);
- if (explicit)
+ if (explicit) {
+ pm->node.flags |= PM_DECLARED;
pm->gsu.s = (GsuScalar)c;
+ } else
+ zfree(c, sizeof(struct gsu_closure));
}
/**/
@@ -289,7 +338,7 @@ 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)
+ if (locallevel == pm->level || locallevel > private_wraplevel)
gsu->setfn(pm, x);
else
setfn_error(pm);
@@ -304,8 +353,11 @@ ppi_unsetfn(Param pm, int explicit)
pm->gsu.i = gsu;
if (locallevel <= pm->level)
gsu->unsetfn(pm, explicit);
- if (explicit)
+ if (explicit) {
+ pm->node.flags |= PM_DECLARED;
pm->gsu.i = (GsuInteger)c;
+ } else
+ zfree(c, sizeof(struct gsu_closure));
}
/**/
@@ -326,7 +378,7 @@ 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)
+ if (locallevel == pm->level || locallevel > private_wraplevel)
gsu->setfn(pm, x);
else
setfn_error(pm);
@@ -341,8 +393,11 @@ ppf_unsetfn(Param pm, int explicit)
pm->gsu.f = gsu;
if (locallevel <= pm->level)
gsu->unsetfn(pm, explicit);
- if (explicit)
+ if (explicit) {
+ pm->node.flags |= PM_DECLARED;
pm->gsu.f = (GsuFloat)c;
+ } else
+ zfree(c, sizeof(struct gsu_closure));
}
/**/
@@ -364,7 +419,7 @@ 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)
+ if (locallevel == pm->level || locallevel > private_wraplevel)
gsu->setfn(pm, x);
else
setfn_error(pm);
@@ -379,8 +434,11 @@ ppa_unsetfn(Param pm, int explicit)
pm->gsu.a = gsu;
if (locallevel <= pm->level)
gsu->unsetfn(pm, explicit);
- if (explicit)
+ if (explicit) {
+ pm->node.flags |= PM_DECLARED;
pm->gsu.a = (GsuArray)c;
+ } else
+ zfree(c, sizeof(struct gsu_closure));
}
static HashTable emptytable;
@@ -403,7 +461,7 @@ 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)
+ if (locallevel == pm->level || locallevel > private_wraplevel)
gsu->setfn(pm, x);
else
setfn_error(pm);
@@ -418,8 +476,11 @@ pph_unsetfn(Param pm, int explicit)
pm->gsu.h = gsu;
if (locallevel <= pm->level)
gsu->unsetfn(pm, explicit);
- if (explicit)
+ if (explicit) {
+ pm->node.flags |= PM_DECLARED;
pm->gsu.h = (GsuHash)c;
+ } else
+ zfree(c, sizeof(struct gsu_closure));
}
/*
@@ -479,18 +540,19 @@ static struct funcwrap wrapper[] = {
};
/**/
+static int private_wraplevel = 0;
+
+/**/
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;
+ if (private_wraplevel < locallevel /* && strcmp(name, "(anon)") != 0 */) {
+ int owl = private_wraplevel;
+ private_wraplevel = locallevel;
scanhashtable(paramtab, 0, 0, 0, scopeprivate, PM_UNSET);
runshfunc(prog, w, name);
scanhashtable(paramtab, 0, 0, 0, scopeprivate, 0);
- wraplevel = owl;
+ private_wraplevel = owl;
return 0;
}
return 1;
@@ -502,27 +564,50 @@ static GetNodeFunc getparamnode;
static HashNode
getprivatenode(HashTable ht, const char *nam)
{
- HashNode hn = getparamnode(ht, nam);
+ /* getparamnode() would follow namerefs, we must not do that here */
+ HashNode hn = gethashnode2(ht, nam);
Param pm = (Param) hn;
- while (!fakelevel && pm && locallevel > pm->level && is_private(pm)) {
+ /* autoload has precedence over nameref, so getparamnode() */
+ if (pm && (pm->node.flags & PM_AUTOLOAD)) {
+ hn = getparamnode(ht, nam);
+ pm = (Param) hn;
+ /* how would an autoloaded private behave? return here? */
+ }
+ while (!fakelevel && pm && is_private(pm) && locallevel > pm->level) {
+ if (pm->level == private_wraplevel + 1) {
+ /* Variable is in the current function scope */
+ break;
+ }
+#if 0
if (!(pm->node.flags & PM_UNSET)) {
/*
* private parameters are always marked PM_UNSET before we
- * increment locallevel, so the only way we get here is
- * when createparam() wants a new parameter that is not at
- * the current locallevel and it has therefore cleared the
- * PM_UNSET flag.
+ * increment locallevel, so there are three possible ways
+ * to get here:
+ * 1) createparam() wants a new parameter that is not at
+ * the current locallevel and it has therefore cleared the
+ * PM_UNSET flag
+ * 2) locallevel has been incremented (startparamscope())
+ * outside the usual function call stack (private_wraplevel)
+ * 3) dynamic scoping is fetching a value from a surrounding
+ * scope, we don't know if that's for assign or just expand
+ * The first of those is now caught in createparam() when
+ * testing PM_RO_BY_DESIGN and the second occurs only in
+ * nofork substitution or handling of ZLE specials. If the
+ * third is an assignment, the GSU setfn rejects it.
*/
DPUTS(pm->old, "BUG: PM_UNSET cleared in wrong scope");
- setfn_error(pm);
- /*
- * TODO: instead of throwing an error here, create a global
- * parameter, insert as pm->old, handle WARN_CREATE_GLOBAL.
- */
}
+#endif
pm = pm->old;
}
+
+ /* resolve nameref after skipping private parameters */
+ if (pm && (pm->node.flags & PM_NAMEREF) &&
+ (pm->u.str || (pm->node.flags & PM_UNSET)))
+ pm = (Param) resolve_nameref(pm, NULL);
+
return (HashNode)pm;
}
@@ -561,7 +646,7 @@ printprivatenode(HashNode hn, int printflags)
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:%lmprtux", "P")
+ BUILTIN("private", BINF_PLUSOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL | BINF_ASSIGN, (HandlerFunc)bin_private, 0, -1, 0, "AE:%F:%HL:%PR:%TUZ:%ahi:%lnmrtux", "P")
};
static struct features module_features = {
diff --git a/Src/Modules/parameter.c b/Src/Modules/parameter.c
index dbb61e474..7441c30b8 100644
--- a/Src/Modules/parameter.c
+++ b/Src/Modules/parameter.c
@@ -49,13 +49,15 @@ paramtypestr(Param pm)
if (pm->node.flags & PM_AUTOLOAD)
return dupstring("undefined");
- switch (PM_TYPE(f)) {
+ /* For simplicity we treat PM_NAMEREF as PM_TYPE(PM_SCALAR) */
+ switch (PM_TYPE(f)|(f & PM_NAMEREF)) {
case PM_SCALAR: val = "scalar"; break;
case PM_ARRAY: val = "array"; break;
case PM_INTEGER: val = "integer"; break;
case PM_EFLOAT:
case PM_FFLOAT: val = "float"; break;
case PM_HASHED: val = "association"; break;
+ case PM_NAMEREF: val = "nameref"; break;
}
DPUTS(!val, "BUG: type not handled in parameter");
val = dupstring(val);
@@ -103,10 +105,15 @@ getpmparameter(UNUSED(HashTable ht), const char *name)
pm->node.nam = dupstring(name);
pm->node.flags = PM_SCALAR | PM_READONLY;
pm->gsu.s = &nullsetscalar_gsu;
- if ((rpm = (Param) realparamtab->getnode(realparamtab, name)) &&
- !(rpm->node.flags & PM_UNSET))
+ if ((rpm = (Param) realparamtab->getnode2(realparamtab, name)) &&
+ !(rpm->node.flags & PM_UNSET)) {
pm->u.str = paramtypestr(rpm);
- else {
+ if ((rpm->node.flags & PM_NAMEREF) && rpm->u.str && *(rpm->u.str) &&
+ (rpm = (Param) realparamtab->getnode(realparamtab, name)) &&
+ !(rpm->node.flags & PM_UNSET)) {
+ pm->u.str = zhtricat(pm->u.str, "-", paramtypestr(rpm));
+ }
+ } else {
pm->u.str = dupstring("");
pm->node.flags |= (PM_UNSET|PM_SPECIAL);
}
@@ -302,7 +309,7 @@ setfunction(char *name, char *val, int dis)
shfunc_set_sticky(shf);
if (!strncmp(name, "TRAP", 4) &&
- (sn = getsignum(name + 4)) != -1) {
+ (sn = getsigidx(name + 4)) != -1) {
if (settrap(sn, NULL, ZSIG_FUNC)) {
freeeprog(shf->funcdef);
zfree(shf, sizeof(*shf));
@@ -1226,9 +1233,16 @@ histwgetfn(UNUSED(Param pm))
pushnode(l, getdata(n));
while (he) {
+ char *hstr = he->node.nam;
+ int len = strlen(hstr);
for (iw = he->nwords - 1; iw >= 0; iw--) {
- h = he->node.nam + he->words[iw * 2];
- e = he->node.nam + he->words[iw * 2 + 1];
+ int wbegin = he->words[iw * 2];
+ int wend = he->words[iw * 2 + 1];
+
+ if (wbegin < 0 || wbegin >= len || wend < 0 || wend > len)
+ break;
+ h = hstr + wbegin;
+ e = hstr + wend;
sav = *e;
*e = '\0';
addlinknode(l, dupstring(h));
diff --git a/Src/Modules/pcre.c b/Src/Modules/pcre.c
index 6289e003e..67157cc01 100644
--- a/Src/Modules/pcre.c
+++ b/Src/Modules/pcre.c
@@ -34,11 +34,11 @@
#define CPCRE_PLAIN 0
/**/
-#if defined(HAVE_PCRE_COMPILE) && defined(HAVE_PCRE_EXEC)
-#include <pcre.h>
+#if defined(HAVE_PCRE2_COMPILE_8) && defined(HAVE_PCRE2_H)
+#define PCRE2_CODE_UNIT_WIDTH 8
+#include <pcre2.h>
-static pcre *pcre_pattern;
-static pcre_extra *pcre_hints;
+static pcre2_code *pcre_pattern;
/**/
static int
@@ -47,8 +47,6 @@ zpcre_utf8_enabled(void)
#if defined(MULTIBYTE_SUPPORT) && defined(HAVE_NL_LANGINFO) && defined(CODESET)
static int have_utf8_pcre = -1;
- /* value can toggle based on MULTIBYTE, so don't
- * be too eager with caching */
if (have_utf8_pcre < -1)
return 0;
@@ -56,15 +54,11 @@ zpcre_utf8_enabled(void)
return 0;
if ((have_utf8_pcre == -1) &&
- (!strcmp(nl_langinfo(CODESET), "UTF-8"))) {
-
- if (pcre_config(PCRE_CONFIG_UTF8, &have_utf8_pcre))
- have_utf8_pcre = -2; /* erk, failed to ask */
+ (pcre2_config(PCRE2_CONFIG_UNICODE, &have_utf8_pcre))) {
+ have_utf8_pcre = -2; /* erk, failed to ask */
}
- if (have_utf8_pcre < 0)
- return 0;
- return have_utf8_pcre;
+ return (have_utf8_pcre == 1) && (!strcmp(nl_langinfo(CODESET), "UTF-8"));
#else
return 0;
@@ -75,47 +69,38 @@ zpcre_utf8_enabled(void)
static int
bin_pcre_compile(char *nam, char **args, Options ops, UNUSED(int func))
{
- int pcre_opts = 0, pcre_errptr, target_len;
- const char *pcre_error;
+ uint32_t pcre_opts = 0;
+ int target_len;
+ int pcre_error;
+ PCRE2_SIZE pcre_offset;
char *target;
- if(OPT_ISSET(ops,'a')) pcre_opts |= PCRE_ANCHORED;
- if(OPT_ISSET(ops,'i')) pcre_opts |= PCRE_CASELESS;
- if(OPT_ISSET(ops,'m')) pcre_opts |= PCRE_MULTILINE;
- if(OPT_ISSET(ops,'x')) pcre_opts |= PCRE_EXTENDED;
- if(OPT_ISSET(ops,'s')) pcre_opts |= PCRE_DOTALL;
+ if (OPT_ISSET(ops, 'a')) pcre_opts |= PCRE2_ANCHORED;
+ if (OPT_ISSET(ops, 'i')) pcre_opts |= PCRE2_CASELESS;
+ if (OPT_ISSET(ops, 'm')) pcre_opts |= PCRE2_MULTILINE;
+ if (OPT_ISSET(ops, 'x')) pcre_opts |= PCRE2_EXTENDED;
+ if (OPT_ISSET(ops, 's')) pcre_opts |= PCRE2_DOTALL;
if (zpcre_utf8_enabled())
- pcre_opts |= PCRE_UTF8;
-
-#ifdef HAVE_PCRE_STUDY
- if (pcre_hints)
-#ifdef PCRE_CONFIG_JIT
- pcre_free_study(pcre_hints);
-#else
- pcre_free(pcre_hints);
-#endif
- pcre_hints = NULL;
-#endif
+ pcre_opts |= PCRE2_UTF;
if (pcre_pattern)
- pcre_free(pcre_pattern);
+ pcre2_code_free(pcre_pattern);
pcre_pattern = NULL;
target = ztrdup(*args);
unmetafy(target, &target_len);
- if ((int)strlen(target) != target_len) {
- zwarnnam(nam, "embedded NULs in PCRE pattern terminate pattern");
- }
-
- pcre_pattern = pcre_compile(target, pcre_opts, &pcre_error, &pcre_errptr, NULL);
+ pcre_pattern = pcre2_compile((PCRE2_SPTR) target, (PCRE2_SIZE) target_len,
+ pcre_opts, &pcre_error, &pcre_offset, NULL);
free(target);
if (pcre_pattern == NULL)
{
- zwarnnam(nam, "error in regex: %s", pcre_error);
+ PCRE2_UCHAR buffer[256];
+ pcre2_get_error_message(pcre_error, buffer, sizeof(buffer));
+ zwarnnam(nam, "error in regex: %s", buffer);
return 1;
}
@@ -123,67 +108,76 @@ bin_pcre_compile(char *nam, char **args, Options ops, UNUSED(int func))
}
/**/
-#ifdef HAVE_PCRE_STUDY
-
-/**/
static int
bin_pcre_study(char *nam, UNUSED(char **args), UNUSED(Options ops), UNUSED(int func))
{
- const char *pcre_error;
-
if (pcre_pattern == NULL)
{
zwarnnam(nam, "no pattern has been compiled for study");
return 1;
}
-
- if (pcre_hints)
-#ifdef PCRE_CONFIG_JIT
- pcre_free_study(pcre_hints);
-#else
- pcre_free(pcre_hints);
-#endif
- pcre_hints = NULL;
- pcre_hints = pcre_study(pcre_pattern, 0, &pcre_error);
- if (pcre_error != NULL)
- {
- zwarnnam(nam, "error while studying regex: %s", pcre_error);
- return 1;
+ int jit = 0;
+ if (!pcre2_config(PCRE2_CONFIG_JIT, &jit) && jit) {
+ if (pcre2_jit_compile(pcre_pattern, PCRE2_JIT_COMPLETE) < 0) {
+ zwarnnam(nam, "error while studying regex");
+ return 1;
+ }
}
return 0;
}
-/**/
-#else /* !HAVE_PCRE_STUDY */
+static int
+pcre_callout(pcre2_callout_block_8 *block, UNUSED(void *callout_data))
+{
+ Eprog prog;
+ int ret=0;
-# define bin_pcre_study bin_notavail
+ if (!block->callout_number &&
+ ((prog = parse_string((char *) block->callout_string, 0))))
+ {
+ int ef = errflag, lv = lastval;
-/**/
-#endif /* !HAVE_PCRE_STUDY */
+ setsparam(".pcre.subject",
+ metafy((char *) block->subject, block->subject_length, META_DUP));
+ setiparam(".pcre.pos", block->current_position + 1);
+ execode(prog, 1, 0, "pcre");
+ ret = lastval | errflag;
+
+ /* Restore any user interrupt error status */
+ errflag = ef | (errflag & ERRFLAG_INT);
+ lastval = lv;
+ }
+
+ return ret;
+}
-/**/
static int
-zpcre_get_substrings(char *arg, int *ovec, int captured_count, char *matchvar,
- char *substravar, int want_offset_pair, int matchedinarr,
- int want_begin_end)
+zpcre_get_substrings(pcre2_code *pat, char *arg, pcre2_match_data *mdata,
+ int captured_count, char *matchvar, char *substravar, char *namedassoc,
+ int want_offset_pair, int matchedinarr, int want_begin_end)
{
- char **captures, *match_all, **matches;
+ PCRE2_SIZE *ovec;
+ char *match_all, **matches;
char offset_all[50];
int capture_start = 1;
+ int vec_off;
+ PCRE2_SPTR ntable; /* table of named captures */
+ uint32_t ncount, nsize;
if (matchedinarr) {
- /* bash-style captures[0] entire-matched string in the array */
+ /* bash-style ovec[0] entire-matched string in the array */
capture_start = 0;
}
- /* captures[0] will be entire matched string, [1] first substring */
- if (!pcre_get_substring_list(arg, ovec, captured_count, (const char ***)&captures)) {
- int nelem = arrlen(captures)-1;
+ /* ovec[0] will be entire matched string, [1] first substring */
+ ovec = pcre2_get_ovector_pointer(mdata);
+ if (ovec) {
+ int nelem = captured_count - 1;
/* Set to the offsets of the complete match */
if (want_offset_pair) {
- sprintf(offset_all, "%d %d", ovec[0], ovec[1]);
+ sprintf(offset_all, "%ld %ld", ovec[0], ovec[1]);
setsparam("ZPCRE_OP", ztrdup(offset_all));
}
/*
@@ -192,7 +186,7 @@ zpcre_get_substrings(char *arg, int *ovec, int captured_count, char *matchvar,
* ovec is length 2*(1+capture_list_length)
*/
if (matchvar) {
- match_all = metafy(captures[0], ovec[1] - ovec[0], META_DUP);
+ match_all = metafy(arg + ovec[0], ovec[1] - ovec[0], META_DUP);
setsparam(matchvar, match_all);
}
/*
@@ -207,21 +201,35 @@ zpcre_get_substrings(char *arg, int *ovec, int captured_count, char *matchvar,
*/
if (substravar &&
(!want_begin_end || nelem)) {
- char **x, **y;
- int vec_off, i;
- y = &captures[capture_start];
+ char **x;
+ int i;
matches = x = (char **) zalloc(sizeof(char *) * (captured_count+1-capture_start));
- for (i = capture_start; i < captured_count; i++, y++) {
+ for (i = capture_start; i < captured_count; i++) {
vec_off = 2*i;
- if (*y)
- *x++ = metafy(*y, ovec[vec_off+1]-ovec[vec_off], META_DUP);
- else
- *x++ = NULL;
+ *x++ = metafy(arg + ovec[vec_off], ovec[vec_off+1]-ovec[vec_off], META_DUP);
}
*x = NULL;
setaparam(substravar, matches);
}
+ if (namedassoc
+ && !pcre2_pattern_info(pat, PCRE2_INFO_NAMECOUNT, &ncount) && ncount
+ && !pcre2_pattern_info(pat, PCRE2_INFO_NAMEENTRYSIZE, &nsize)
+ && !pcre2_pattern_info(pat, PCRE2_INFO_NAMETABLE, &ntable))
+ {
+ char **hash, **hashptr;
+ uint32_t nidx;
+ hashptr = hash = (char **)zshcalloc((ncount+1)*2*sizeof(char *));
+ for (nidx = 0; nidx < ncount; nidx++) {
+ vec_off = (ntable[nsize * nidx] << 9) + 2 * ntable[nsize * nidx + 1];
+ /* would metafy the key but pcre limits characters in the name */
+ *hashptr++ = ztrdup((char *) ntable + nsize * nidx + 2);
+ *hashptr++ = metafy(arg + ovec[vec_off],
+ ovec[vec_off+1]-ovec[vec_off], META_DUP);
+ }
+ sethparam(namedassoc, hash);
+ }
+
if (want_begin_end) {
/*
* cond-infix rather than builtin; also not bash; so we set a bunch
@@ -253,7 +261,8 @@ zpcre_get_substrings(char *arg, int *ovec, int captured_count, char *matchvar,
setiparam("MEND", offs + !isset(KSHARRAYS) - 1);
if (nelem) {
char **mbegin, **mend, **bptr, **eptr;
- int i, *ipair;
+ int i;
+ size_t *ipair;
bptr = mbegin = zalloc(sizeof(char*)*(nelem+1));
eptr = mend = zalloc(sizeof(char*)*(nelem+1));
@@ -293,8 +302,6 @@ zpcre_get_substrings(char *arg, int *ovec, int captured_count, char *matchvar,
setaparam("mend", mend);
}
}
-
- pcre_free_substring_list((const char **)captures);
}
return 0;
@@ -320,29 +327,33 @@ getposint(char *instr, char *nam)
static int
bin_pcre_match(char *nam, char **args, Options ops, UNUSED(int func))
{
- int ret, capcount, *ovec, ovecsize, c;
+ int ret, c;
+ pcre2_match_data *pcre_mdata = NULL;
char *matched_portion = NULL;
char *plaintext = NULL;
- char *receptacle = NULL;
+ char *receptacle;
+ char *named = NULL;
int return_value = 1;
/* The subject length and offset start are both int values in pcre_exec */
int subject_len;
int offset_start = 0;
int want_offset_pair = 0;
+ int use_dfa = 0;
if (pcre_pattern == NULL) {
zwarnnam(nam, "no pattern has been compiled");
return 1;
}
- matched_portion = "MATCH";
- receptacle = "match";
- if(OPT_HASARG(ops,c='a')) {
- receptacle = OPT_ARG(ops,c);
- }
- if(OPT_HASARG(ops,c='v')) {
- matched_portion = OPT_ARG(ops,c);
+ if (!(use_dfa = OPT_ISSET(ops, 'd'))) {
+ matched_portion = OPT_HASARG(ops, c='v') ? OPT_ARG(ops, c) : "MATCH";
+ named = OPT_HASARG(ops, c='A') ? OPT_ARG(ops, c) : ".pcre.match";
+ } else if (OPT_HASARG(ops, c='v') || OPT_HASARG(ops, c='A')) {
+ zwarnnam(nam, "-d cannot be combined with -%c", c);
+ return 1;
}
+ receptacle = OPT_HASARG(ops, 'a') ? OPT_ARG(ops, 'a') : "match";
+
if(OPT_HASARG(ops,c='n')) { /* The offset position to start the search, in bytes. */
if ((offset_start = getposint(OPT_ARG(ops,c), nam)) < 0)
return 1;
@@ -350,36 +361,57 @@ bin_pcre_match(char *nam, char **args, Options ops, UNUSED(int func))
/* For the entire match, 'Return' the offset byte positions instead of the matched string */
if(OPT_ISSET(ops,'b')) want_offset_pair = 1;
- if ((ret = pcre_fullinfo(pcre_pattern, pcre_hints, PCRE_INFO_CAPTURECOUNT, &capcount)))
- {
- zwarnnam(nam, "error %d in fullinfo", ret);
- return 1;
- }
-
- ovecsize = (capcount+1)*3;
- ovec = zalloc(ovecsize*sizeof(int));
-
plaintext = ztrdup(*args);
unmetafy(plaintext, &subject_len);
+ pcre2_match_context_8 *mcontext = pcre2_match_context_create(NULL);
+ pcre2_set_callout(mcontext, &pcre_callout, 0);
+
if (offset_start > 0 && offset_start >= subject_len)
- ret = PCRE_ERROR_NOMATCH;
- else
- ret = pcre_exec(pcre_pattern, pcre_hints, plaintext, subject_len, offset_start, 0, ovec, ovecsize);
+ ret = PCRE2_ERROR_NOMATCH;
+ else if (use_dfa) {
+ PCRE2_SIZE old, wscount = 128, capcount = 128;
+ void *workspace = zhalloc(sizeof(int) * wscount);
+ pcre_mdata = pcre2_match_data_create(capcount, NULL);
+ do {
+ ret = pcre2_dfa_match(pcre_pattern, (PCRE2_SPTR) plaintext, subject_len,
+ offset_start, 0, pcre_mdata, mcontext, (int *) workspace, wscount);
+ if (ret == PCRE2_ERROR_DFA_WSSIZE) {
+ old = wscount;
+ wscount += wscount / 2;
+ workspace = hrealloc(workspace, sizeof(int) * old, sizeof(int) * wscount);
+ } else if (ret == 0) {
+ capcount += capcount / 2;
+ pcre2_match_data_free(pcre_mdata);
+ pcre_mdata = pcre2_match_data_create(capcount, NULL);
+ } else
+ break;
+ } while(1);
+ } else {
+ pcre_mdata = pcre2_match_data_create_from_pattern(pcre_pattern, NULL);
+ ret = pcre2_match(pcre_pattern, (PCRE2_SPTR) plaintext, subject_len,
+ offset_start, 0, pcre_mdata, mcontext);
+ if (ret > 0)
+ ret = pcre2_get_ovector_count(pcre_mdata);
+ }
if (ret==0) return_value = 0;
- else if (ret==PCRE_ERROR_NOMATCH) /* no match */;
+ else if (ret == PCRE2_ERROR_NOMATCH) /* no match */;
else if (ret>0) {
- zpcre_get_substrings(plaintext, ovec, ret, matched_portion, receptacle,
- want_offset_pair, 0, 0);
+ zpcre_get_substrings(pcre_pattern, plaintext, pcre_mdata, ret,
+ matched_portion, receptacle, named, want_offset_pair, use_dfa, 0);
return_value = 0;
}
else {
- zwarnnam(nam, "error in pcre_exec [%d]", ret);
+ PCRE2_UCHAR buffer[256];
+ pcre2_get_error_message(ret, buffer, sizeof(buffer));
+ zwarnnam(nam, "error in pcre matching for %s: %s", *args, buffer);
}
- if (ovec)
- zfree(ovec, ovecsize*sizeof(int));
+ if (pcre_mdata)
+ pcre2_match_data_free(pcre_mdata);
+ if (mcontext)
+ pcre2_match_context_free(mcontext);
zsfree(plaintext);
return return_value;
@@ -389,17 +421,19 @@ bin_pcre_match(char *nam, char **args, Options ops, UNUSED(int func))
static int
cond_pcre_match(char **a, int id)
{
- pcre *pcre_pat;
- const char *pcre_err;
+ pcre2_code *pcre_pat = NULL;
+ int pcre_err;
+ PCRE2_SIZE pcre_erroff;
char *lhstr, *rhre, *lhstr_plain, *rhre_plain, *avar, *svar;
- int r = 0, pcre_opts = 0, pcre_errptr, capcnt, *ov, ovsize;
+ int r = 0, pcre_opts = 0;
+ pcre2_match_data *pcre_mdata = NULL;
int lhstr_plain_len, rhre_plain_len;
int return_value = 0;
if (zpcre_utf8_enabled())
- pcre_opts |= PCRE_UTF8;
+ pcre_opts |= PCRE2_UTF;
if (isset(REMATCHPCRE) && !isset(CASEMATCH))
- pcre_opts |= PCRE_CASELESS;
+ pcre_opts |= PCRE2_CASELESS;
lhstr = cond_str(a,0,0);
rhre = cond_str(a,1,0);
@@ -407,9 +441,6 @@ cond_pcre_match(char **a, int id)
rhre_plain = ztrdup(rhre);
unmetafy(lhstr_plain, &lhstr_plain_len);
unmetafy(rhre_plain, &rhre_plain_len);
- pcre_pat = NULL;
- ov = NULL;
- ovsize = 0;
if (isset(BASHREMATCH)) {
svar = NULL;
@@ -421,27 +452,27 @@ cond_pcre_match(char **a, int id)
switch(id) {
case CPCRE_PLAIN:
- if ((int)strlen(rhre_plain) != rhre_plain_len) {
- zwarn("embedded NULs in PCRE pattern terminate pattern");
- }
- pcre_pat = pcre_compile(rhre_plain, pcre_opts, &pcre_err, &pcre_errptr, NULL);
- if (pcre_pat == NULL) {
- zwarn("failed to compile regexp /%s/: %s", rhre, pcre_err);
+ if (!(pcre_pat = pcre2_compile((PCRE2_SPTR) rhre_plain,
+ (PCRE2_SIZE) rhre_plain_len, pcre_opts,
+ &pcre_err, &pcre_erroff, NULL)))
+ {
+ PCRE2_UCHAR buffer[256];
+ pcre2_get_error_message(pcre_err, buffer, sizeof(buffer));
+ zwarn("failed to compile regexp /%s/: %s", rhre, buffer);
break;
}
- pcre_fullinfo(pcre_pat, NULL, PCRE_INFO_CAPTURECOUNT, &capcnt);
- ovsize = (capcnt+1)*3;
- ov = zalloc(ovsize*sizeof(int));
- r = pcre_exec(pcre_pat, NULL, lhstr_plain, lhstr_plain_len, 0, 0, ov, ovsize);
- /* r < 0 => error; r==0 match but not enough size in ov
+ pcre_mdata = pcre2_match_data_create_from_pattern(pcre_pat, NULL);
+ r = pcre2_match(pcre_pat, (PCRE2_SPTR8) lhstr_plain, lhstr_plain_len,
+ 0, 0, pcre_mdata, NULL);
+ /* r < 0 => error; r==0 match but not enough size in match data
* r > 0 => (r-1) substrings found; r==1 => no substrings
*/
if (r==0) {
- zwarn("reportable zsh problem: pcre_exec() returned 0");
+ zwarn("reportable zsh problem: pcre2_match() returned 0");
return_value = 1;
break;
}
- else if (r==PCRE_ERROR_NOMATCH) {
+ else if (r == PCRE2_ERROR_NOMATCH) {
return_value = 0; /* no match */
break;
}
@@ -450,9 +481,9 @@ cond_pcre_match(char **a, int id)
break;
}
else if (r>0) {
- zpcre_get_substrings(lhstr_plain, ov, r, svar, avar, 0,
- isset(BASHREMATCH),
- !isset(BASHREMATCH));
+ uint32_t ovec_count = pcre2_get_ovector_count(pcre_mdata);
+ zpcre_get_substrings(pcre_pat, lhstr_plain, pcre_mdata, ovec_count, svar, avar,
+ ".pcre.match", 0, isset(BASHREMATCH), !isset(BASHREMATCH));
return_value = 1;
break;
}
@@ -463,10 +494,10 @@ cond_pcre_match(char **a, int id)
free(lhstr_plain);
if(rhre_plain)
free(rhre_plain);
+ if (pcre_mdata)
+ pcre2_match_data_free(pcre_mdata);
if (pcre_pat)
- pcre_free(pcre_pat);
- if (ov)
- zfree(ov, ovsize*sizeof(int));
+ pcre2_code_free(pcre_pat);
return return_value;
}
@@ -488,18 +519,18 @@ static struct conddef cotab[] = {
static struct builtin bintab[] = {
BUILTIN("pcre_compile", 0, bin_pcre_compile, 1, 1, 0, "aimxs", NULL),
- BUILTIN("pcre_match", 0, bin_pcre_match, 1, 1, 0, "a:v:n:b", NULL),
+ BUILTIN("pcre_match", 0, bin_pcre_match, 1, 1, 0, "A:a:v:n:bd", NULL),
BUILTIN("pcre_study", 0, bin_pcre_study, 0, 0, 0, NULL, NULL)
};
static struct features module_features = {
bintab, sizeof(bintab)/sizeof(*bintab),
-#if defined(HAVE_PCRE_COMPILE) && defined(HAVE_PCRE_EXEC)
+#if defined(HAVE_PCRE2_COMPILE_8) && defined(HAVE_PCRE2_H)
cotab, sizeof(cotab)/sizeof(*cotab),
-#else /* !(HAVE_PCRE_COMPILE && HAVE_PCRE_EXEC) */
+#else /* !(HAVE_PCRE2_COMPILE_8 && HAVE_PCRE2_H) */
NULL, 0,
-#endif /* !(HAVE_PCRE_COMPILE && HAVE_PCRE_EXEC) */
+#endif /* !(HAVE_PCRE2_COMPILE_8 && HAVE_PCRE2_H) */
NULL, 0,
NULL, 0,
0
@@ -546,19 +577,9 @@ cleanup_(Module m)
int
finish_(UNUSED(Module m))
{
-#if defined(HAVE_PCRE_COMPILE) && defined(HAVE_PCRE_EXEC)
-#ifdef HAVE_PCRE_STUDY
- if (pcre_hints)
-#ifdef PCRE_CONFIG_JIT
- pcre_free_study(pcre_hints);
-#else
- pcre_free(pcre_hints);
-#endif
- pcre_hints = NULL;
-#endif
-
+#if defined(HAVE_PCRE2_COMPILE_8) && defined(HAVE_PCRE2_H)
if (pcre_pattern)
- pcre_free(pcre_pattern);
+ pcre2_code_free(pcre_pattern);
pcre_pattern = NULL;
#endif
diff --git a/Src/Modules/pcre.mdd b/Src/Modules/pcre.mdd
index 6eb3c691b..3e1579117 100644
--- a/Src/Modules/pcre.mdd
+++ b/Src/Modules/pcre.mdd
@@ -1,5 +1,5 @@
name=zsh/pcre
-link=`if test x$enable_pcre = xyes && (pcre-config --version >/dev/null 2>/dev/null); then echo dynamic; else echo no; fi`
+link=`if test x$enable_pcre = xyes; then echo dynamic; else echo no; fi`
load=no
autofeatures="b:pcre_compile b:pcre_study b:pcre_match"
diff --git a/Src/Modules/random.c b/Src/Modules/random.c
new file mode 100644
index 000000000..88ac9543c
--- /dev/null
+++ b/Src/Modules/random.c
@@ -0,0 +1,326 @@
+/*
+ * random.c - module to access kernel random sources.
+ *
+ * This file is part of zsh, the Z shell.
+ *
+ * Copyright (c) 2022 Clinton Bunch
+ * 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 Clinton Bunch 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 Clinton Bunch and the Zsh Development Group have been advised of
+ * the possibility of such damage.
+ *
+ * Clinton Bunch 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 Clinton Bunch and the
+ * Zsh Development Group have no obligation to provide maintenance,
+ * support, updates, enhancements, or modifications.
+ *
+ */
+
+#include "random.mdh"
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdio.h>
+
+#include <stdint.h>
+
+#ifdef HAVE_SYS_RANDOM_H
+#include <sys/random.h>
+#endif
+
+/* Simplify select URANDOM specific code */
+#if !defined(HAVE_ARC4RANDOM_BUF) && !defined(HAVE_GETRANDOM)
+#define USE_URANDOM
+#endif
+
+/* buffer to pre-load integers for SRANDOM to lessen the context switches */
+static uint32_t rand_buff[8];
+static int buf_cnt = -1;
+
+#ifdef USE_URANDOM
+/* File descriptor for /dev/urandom */
+int randfd = -1;
+#endif /* USE_URANDOM */
+
+static zlong get_srandom(UNUSED(Param p));
+
+/**/
+ssize_t
+getrandom_buffer(void *buf, size_t len)
+{
+ ssize_t ret;
+#ifndef HAVE_ARC4RANDOM_BUF
+ size_t val = 0;
+ uint8_t *bufptr = buf;
+#endif
+
+ do {
+ errno = 0;
+#ifdef HAVE_ARC4RANDOM_BUF
+ arc4random_buf(buf,len);
+ ret = len;
+#elif defined(HAVE_GETRANDOM)
+ ret=getrandom(bufptr,(len - val),0);
+#else
+ ret=read(randfd,bufptr,(len - val));
+#endif
+ if (ret < 0) {
+ if (errno != EINTR || errflag || retflag || breaks || contflag) {
+ zwarn("Unable to get random data: %e.", errno);
+ return -1;
+ }
+ }
+#ifndef HAVE_ARC4RANDOM_BUF
+ bufptr += ret;
+ val += ret;
+#endif
+ } while (ret < len);
+ return ret;
+}
+
+/*
+ * Generate count number of random 32-bit integers between 0 and max-1
+ * Got this algorithm from
+ *https://lemire.me/blog/2016/06/30/fast-random-shuffling/
+ * adapting the public domain code.
+ *
+ */
+
+/**/
+void
+get_bound_random_buffer(uint32_t *buffer, size_t count, uint32_t max)
+{
+ uint64_t multi_result;
+ uint32_t threshold;
+ uint32_t leftover;
+
+ size_t i; /* loop counter */
+
+ getrandom_buffer((void*) buffer, count*sizeof(uint32_t));
+ if (max == UINT32_MAX)
+ return;
+
+ for(i=0;i<count;i++) {
+ multi_result = ((uint64_t) buffer[i]) * (uint64_t) max;
+ leftover = (uint32_t) multi_result;
+
+ /*
+ * The following if statement should (according to Google's Gemini)
+ * only be executed with a probability of 1/2**32 or 2.33e-10
+ */
+ if(leftover < max) {
+ threshold= -max % max;
+ while (leftover < threshold) {
+ uint32_t j=get_srandom(NULL);
+ multi_result=(uint64_t) j * (uint64_t) max;
+ leftover= (uint32_t) multi_result;
+ }
+ }
+ buffer[i]=multi_result >> 32;
+ }
+}
+
+/*
+ * Provides for the SRANDOM parameter and returns an unsigned 32-bit random
+ * integer.
+ */
+
+/**/
+static zlong
+get_srandom(UNUSED(Param pm)) {
+
+ if(buf_cnt <= 0) {
+ getrandom_buffer((void*) rand_buff,sizeof(rand_buff));
+ buf_cnt=sizeof(rand_buff)/sizeof(rand_buff[0]);
+ }
+ return rand_buff[--buf_cnt];
+}
+
+/*
+ * Implements math function zrand_int takes 0 to 3 arguments an upper bound,
+ * a lower bound and a flag as to whether the range is inclusive or not. The
+ * default is exclusive. If neither upper or lower is specified this is no
+ * different than SRANDOM.
+ */
+
+/**/
+static mnumber
+math_zrand_int(UNUSED(char *name), int argc, mnumber *argv, UNUSED(int id))
+{
+ mnumber ret;
+ uint32_t i;
+ zlong lower=0, upper=UINT32_MAX,incl=0, diff;
+
+ ret.type = MN_INTEGER;
+
+ switch (argc) {
+ case 0: ret.u.l=get_srandom(NULL);
+ return ret;
+ break;
+ case 3: incl = (argv[2].u.l != 0)?1:0;
+ case 2: lower = argv[1].u.l;
+ case 1: upper = argv[0].u.l;
+ default: diff = upper-lower+incl;
+ }
+
+ if (lower < 0 || lower >= UINT32_MAX) {
+ zwarn("Lower bound (%z) out of range: 0-4294967295",lower);
+ } else if (upper < lower) {
+ zwarn("Upper bound (%z) must be greater than Lower Bound (%z)",upper,lower);
+ } else if (upper < 0 || upper >= UINT32_MAX) {
+ zwarn("Upper bound (%z) out of range: 0-4294967295",upper);
+ }
+
+ if ( diff == 0 ) {
+ ret.u.l=upper; /* still not convinced this shouldn't be an error. */
+ } else {
+ get_bound_random_buffer(&i,1,(uint32_t) diff);
+ ret.u.l=i+lower;
+ }
+ return ret;
+}
+
+/*
+ * Implements the mathfunc zrand_float and returns a random floating-point
+ * number between 0 and 1.
+ *
+ */
+
+/**/
+static mnumber
+math_zrand_float(UNUSED(char *name), UNUSED(int argc), UNUSED(mnumber *argv),
+ UNUSED(int id))
+{
+ mnumber ret;
+ double r;
+
+ r = random_real();
+ if (r < 0) {
+ zwarnnam(name, "Failed to get sufficient random data.");
+ }
+ ret.type = MN_FLOAT;
+ ret.u.d = r;
+
+ return ret;
+}
+
+static const struct gsu_integer srandom_gsu =
+{ get_srandom, nullintsetfn, stdunsetfn };
+
+static struct paramdef patab[] = {
+ {"SRANDOM", PM_INTEGER | PM_READONLY_SPECIAL | PM_HIDEVAL, NULL,
+ &srandom_gsu, NULL, NULL, NULL},
+};
+
+static struct mathfunc mftab[] = {
+ NUMMATHFUNC("zrand_float", math_zrand_float, 0, 0, 0),
+ NUMMATHFUNC("zrand_int", math_zrand_int, 0, 3, 0),
+};
+
+static struct features module_features = {
+ NULL, 0,
+ NULL, 0,
+ mftab, sizeof(mftab)/sizeof(*mftab),
+ patab, sizeof(patab)/sizeof(*patab),
+ 0
+};
+
+/**/
+int
+setup_(UNUSED(Module m))
+{
+#ifdef USE_URANDOM
+ /* Check for the existence of /dev/urandom */
+
+ struct stat st;
+
+ errno=0;
+ if (stat("/dev/urandom",&st) < 0) {
+ zwarn("Error getting kernel random pool: %e.", errno);
+ return 1;
+ }
+
+ errno=0;
+ if (!(S_ISCHR(st.st_mode)) ) {
+ zwarn("Error getting kernel random pool: %e.", errno);
+ return 1;
+ }
+#endif /* USE_URANDOM */
+ 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_(UNUSED(Module m))
+{
+#ifdef USE_URANDOM
+ /*
+ * Open /dev/urandom. Here because of a weird issue on HP-UX 11.31
+ * When opening in setup_ open returned 0. In boot_, it returns
+ * an unused file descriptor. Decided against ifdef HPUX as it works
+ * here just as well for other platforms.
+ *
+ */
+
+ int tmpfd=-1;
+
+ errno=0;
+ if ((tmpfd = open("/dev/urandom", O_RDONLY)) < 0) {
+ zwarn("Could not access kernel random pool: %e.",errno);
+ return 1;
+ }
+ randfd = movefd(tmpfd);
+ addmodulefd(randfd,FDT_MODULE);
+ if (randfd < 0) {
+ zwarn("Could not access kernel random pool.");
+ return 1;
+ }
+#endif /* USE_URANDOM */
+ return 0;
+}
+
+/**/
+int
+cleanup_(Module m)
+{
+ return setfeatureenables(m, &module_features, NULL);
+}
+
+/**/
+int
+finish_(UNUSED(Module m))
+{
+#ifdef USE_URANDOM
+ if (randfd >= 0)
+ zclose(randfd);
+#endif /* USE_URANDOM */
+ return 0;
+}
diff --git a/Src/Modules/random.mdd b/Src/Modules/random.mdd
new file mode 100644
index 000000000..7a75f29ff
--- /dev/null
+++ b/Src/Modules/random.mdd
@@ -0,0 +1,7 @@
+name=zsh/random
+link=either
+load=yes
+
+autofeatures="p:SRANDOM f:zrand_float f:zrand_int"
+
+objects="random.o random_real.o"
diff --git a/Src/Modules/random_real.c b/Src/Modules/random_real.c
new file mode 100644
index 000000000..4a8fcae19
--- /dev/null
+++ b/Src/Modules/random_real.c
@@ -0,0 +1,213 @@
+/* This file contains code under different copyrights separated by */
+/* ====@@@@@=== */
+
+/*
+ * random_real.c - module to access kernel random sources.
+ *
+ * This file is part of zsh, the Z shell.
+ *
+ * Copyright (c) 2022 Clinton Bunch
+ * 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 Clinton Bunch 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 Clinton Bunch and the Zsh Development Group have been advised of
+ * the possibility of such damage.
+ *
+ * Clinton Bunch 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 Clinton Bunch and the
+ * Zsh Development Group have no obligation to provide maintenance,
+ * support, updates, enhancements, or modifications.
+ *
+ */
+
+#include "random.mdh"
+
+#include <math.h>
+#include <stdint.h>
+#include <errno.h>
+
+
+/* Count the number of leading zeros, hopefully in gcc/clang by HW
+ * instruction */
+#if defined(__GNUC__) || defined(__clang__)
+#define clz64(x) __builtin_clzll(x)
+#else
+#define clz64(x) _zclz64(x)
+
+/**/
+int
+_zclz64(uint64_t x) {
+ int n = 0;
+
+ if (x == 0)
+ return 64;
+
+ if (!(x & 0xFFFFFFFF00000000ull)) {
+ n+=32;
+ x<<=32;
+ }
+ if (!(x & 0xFFFF000000000000ull)) {
+ n+=16;
+ x<<=16;
+ }
+ if (!(x & 0xFF00000000000000ull)) {
+ n+=8;
+ x<<=8;
+ }
+ if (!(x & 0xF000000000000000ull)) {
+ n+=4;
+ x<<=4;
+ }
+ if (!(x & 0xC000000000000000ull)) {
+ n+=2;
+ x<<=1;
+ }
+ if (!(x & 0x8000000000000000ull)) {
+ n+=1;
+ }
+ return n;
+}
+#endif /* __GNU_C__ or __clang__ */
+
+/**/
+uint64_t
+random_64bit(void) {
+ uint64_t r;
+
+ if(getrandom_buffer(&r,sizeof(r)) < 0) {
+ zwarn("zsh/random: Can't get sufficient random data.");
+ return 1; /* 0 will cause loop */
+ }
+
+ return r;
+}
+
+/* ====@@@@@=== */
+/* Following code is under the below copyright, despite changes for error
+ * handling and removing GCCisms */
+
+/*-
+ * Copyright (c) 2014 Taylor R. Campbell
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * Uniform random floats: How to generate a double-precision
+ * floating-point numbers in [0, 1] uniformly at random given a uniform
+ * random source of bits.
+ *
+ * See<http://mumble.net/~campbell/2014/04/28/uniform-random-float>
+ * for explanation.
+ *
+ * Updated 2015-02-22 to replace ldexp(x, <constant>) by x * ldexp(1,
+ * <constant>), since glibc and NetBSD libm both have slow software
+ * bit-twiddling implementations of ldexp, but GCC can constant-fold
+ * the latter.
+ */
+
+/*
+ * random_real: Generate a stream of bits uniformly at random and
+ * interpret it as the fractional part of the binary expansion of a
+ * number in [0, 1], 0.00001010011111010100...; then round it.
+ */
+
+/**/
+double
+random_real(void)
+{
+ int exponent = 0;
+ uint64_t significand = 0;
+ uint64_t r = 0;
+ unsigned shift;
+
+ /*
+ * Read zeros into the exponent until we hit a one; the rest
+ * will go into the significand.
+ */
+ while (significand == 0) {
+ exponent -= 64;
+
+ /* Get random_64bit and check for error */
+ errno = 0;
+ significand = random_64bit();
+ if (errno)
+ return -1;
+ /*
+ * If the exponent falls below -1074 = emin + 1 - p,
+ * the exponent of the smallest subnormal, we are
+ * guaranteed the result will be rounded to zero. This
+ * case is so unlikely it will happen in realistic
+ * terms only if random_64bit is broken.
+ */
+ if (exponent < -1074)
+ return 0;
+ }
+
+ /*
+ * There is a 1 somewhere in significand, not necessarily in
+ * the most significant position. If there are leading zeros,
+ * shift them into the exponent and refill the less-significant
+ * bits of the significand. Can't predict one way or another
+ * whether there are leading zeros: there's a fifty-fifty
+ * chance, if random_64bit is uniformly distributed.
+ */
+ shift = clz64(significand);
+ if (shift != 0) {
+
+ errno = 0;
+ r = random_64bit();
+ if (errno)
+ return -1;
+
+ exponent -= shift;
+ significand <<= shift;
+ significand |= (r >> (64 - shift));
+ }
+
+ /*
+ * Set the sticky bit, since there is almost surely another 1
+ * in the bit stream. Otherwise, we might round what looks
+ * like a tie to even when, almost surely, were we to look
+ * further in the bit stream, there would be a 1 breaking the
+ * tie.
+ */
+ significand |= 1;
+
+ /*
+ * Finally, convert to double (rounding) and scale by
+ * 2^exponent.
+ */
+ return ldexp((double)significand, exponent);
+}
+/* ====@@@@@=== */
diff --git a/Src/Modules/stat.c b/Src/Modules/stat.c
index 0df9b35b7..5bf201dd3 100644
--- a/Src/Modules/stat.c
+++ b/Src/Modules/stat.c
@@ -236,9 +236,8 @@ statprint(struct stat *sbuf, char *outbuf, char *fname, int iwhich, int flags)
char *optr = outbuf;
if (flags & STF_NAME) {
- sprintf(outbuf, (flags & (STF_PICK|STF_ARRAY)) ?
+ optr += sprintf(outbuf, (flags & (STF_PICK|STF_ARRAY)) ?
"%s " : "%-8s", statelts[iwhich]);
- optr += strlen(outbuf);
}
*optr = '\0';
@@ -406,7 +405,7 @@ bin_stat(char *name, char **args, Options ops, UNUSED(int func))
} else {
for (; *arg; arg++) {
if (strchr("glLnNorstT", *arg))
- ops->ind[STOUC(*arg)] = 1;
+ ops->ind[(unsigned char) *arg] = 1;
else if (*arg == 'A') {
if (arg[1]) {
arrnam = arg+1;
diff --git a/Src/Modules/system.c b/Src/Modules/system.c
index ea11ef037..929a8b002 100644
--- a/Src/Modules/system.c
+++ b/Src/Modules/system.c
@@ -74,6 +74,8 @@ bin_sysread(char *nam, char **args, Options ops, UNUSED(int func))
int infd = 0, outfd = -1, bufsize = SYSREAD_BUFSIZE, count;
char *outvar = NULL, *countvar = NULL, *inbuf;
+ errno = 0; /* Distinguish non-system errors */
+
/* -i: input file descriptor if not stdin */
if (OPT_ISSET(ops, 'i')) {
infd = getposint(OPT_ARG(ops, 'i'), nam);
@@ -238,6 +240,8 @@ bin_syswrite(char *nam, char **args, Options ops, UNUSED(int func))
int outfd = 1, len, count, totcount;
char *countvar = NULL;
+ errno = 0; /* Distinguish non-system errors */
+
/* -o: output file descriptor if not stdout */
if (OPT_ISSET(ops, 'o')) {
outfd = getposint(OPT_ARG(ops, 'o'), nam);
@@ -303,6 +307,13 @@ static struct { const char *name; int oflag; } openopts[] = {
{ "trunc", O_TRUNC }
};
+/*
+ * Return values of bin_sysopen:
+ * 0 Success
+ * 1 Error in parameters to command
+ * 2 Error on open, ERRNO set by system
+ */
+
/**/
static int
bin_sysopen(char *nam, char **args, Options ops, UNUSED(int func))
@@ -319,6 +330,8 @@ bin_sysopen(char *nam, char **args, Options ops, UNUSED(int func))
int fdflags = 0;
#endif
+ errno = 0; /* Distinguish non-system errors */
+
if (!OPT_ISSET(ops, 'u')) {
zwarnnam(nam, "file descriptor not specified");
return 1;
@@ -374,12 +387,12 @@ bin_sysopen(char *nam, char **args, Options ops, UNUSED(int func))
if (fd == -1) {
zwarnnam(nam, "can't open file %s: %e", *args, errno);
- return 1;
+ return 2;
}
moved_fd = (explicit > -1) ? redup(fd, explicit) : movefd(fd);
if (moved_fd == -1) {
zwarnnam(nam, "can't open file %s", *args);
- return 1;
+ return 2;
}
#ifdef FD_CLOEXEC
@@ -423,6 +436,8 @@ bin_sysseek(char *nam, char **args, Options ops, UNUSED(int func))
char *whence;
off_t pos;
+ errno = 0; /* Distinguish non-system errors */
+
/* -u: file descriptor if not stdin */
if (OPT_ISSET(ops, 'u')) {
fd = getposint(OPT_ARG(ops, 'u'), nam);
diff --git a/Src/Modules/terminfo.c b/Src/Modules/terminfo.c
index 4596b41d2..f9ab64fb3 100644
--- a/Src/Modules/terminfo.c
+++ b/Src/Modules/terminfo.c
@@ -160,7 +160,7 @@ getterminfo(UNUSED(HashTable ht), const char *name)
pm->node.flags |= PM_SCALAR;
pm->gsu.s = &nullsetscalar_gsu;
} else if ((tistr = (char *)tigetstr(nameu)) != NULL && tistr != (char *)-1) {
- pm->u.str = dupstring(tistr);
+ pm->u.str = metafy(tistr, -1, META_HEAPDUP);
pm->node.flags |= PM_SCALAR;
pm->gsu.s = &nullsetscalar_gsu;
} else {
@@ -280,7 +280,7 @@ scanterminfo(UNUSED(HashTable ht), ScanFunc func, int flags)
for (capname = (char **)strnames; *capname; capname++) {
if ((tistr = (char *)tigetstr(*capname)) != NULL &&
tistr != (char *)-1) {
- pm->u.str = dupstring(tistr);
+ pm->u.str = metafy(tistr, -1, META_HEAPDUP);
pm->node.nam = dupstring(*capname);
func(&pm->node, flags);
}
diff --git a/Src/Modules/watch.c b/Src/Modules/watch.c
index d45c3cf3d..acc499518 100644
--- a/Src/Modules/watch.c
+++ b/Src/Modules/watch.c
@@ -255,8 +255,10 @@ watchlog2(int inout, WATCH_STRUCT_UTMP *u, char *fmt, int prnt, int fini)
while (*fmt)
if (*fmt == '\\') {
if (*++fmt) {
- if (prnt)
+ if (prnt) {
+ applytextattributes(TSC_RAW);
putchar(*fmt);
+ }
++fmt;
} else if (fini)
return fmt;
@@ -266,8 +268,10 @@ watchlog2(int inout, WATCH_STRUCT_UTMP *u, char *fmt, int prnt, int fini)
else if (*fmt == fini)
return ++fmt;
else if (*fmt != '%') {
- if (prnt)
+ if (prnt) {
+ applytextattributes(TSC_RAW);
putchar(*fmt);
+ }
++fmt;
} else {
if (*++fmt == BEGIN3)
@@ -277,12 +281,15 @@ watchlog2(int inout, WATCH_STRUCT_UTMP *u, char *fmt, int prnt, int fini)
else
switch (*(fm2 = fmt++)) {
case 'n':
+ applytextattributes(TSC_RAW);
printf("%.*s", (int)sizeof(u->ut_name), u->ut_name);
break;
case 'a':
+ applytextattributes(TSC_RAW);
printf("%s", (!inout) ? "logged off" : "logged on");
break;
case 'l':
+ applytextattributes(TSC_RAW);
if (!strncmp(u->ut_line, "tty", 3))
printf("%.*s", (int)sizeof(u->ut_line) - 3, u->ut_line + 3);
else
@@ -290,6 +297,7 @@ watchlog2(int inout, WATCH_STRUCT_UTMP *u, char *fmt, int prnt, int fini)
break;
# ifdef WATCH_UTMP_UT_HOST
case 'm':
+ applytextattributes(TSC_RAW);
for (p = u->ut_host, i = sizeof(u->ut_host); i && *p; i--, p++) {
if (*p == '.' && !idigit(p[1]))
break;
@@ -297,6 +305,7 @@ watchlog2(int inout, WATCH_STRUCT_UTMP *u, char *fmt, int prnt, int fini)
}
break;
case 'M':
+ applytextattributes(TSC_RAW);
printf("%.*s", (int)sizeof(u->ut_host), u->ut_host);
break;
# endif /* WATCH_UTMP_UT_HOST */
@@ -343,9 +352,11 @@ watchlog2(int inout, WATCH_STRUCT_UTMP *u, char *fmt, int prnt, int fini)
len = ztrftime(buf, 40, fm2, tm, 0L);
if (len > 0)
metafy(buf, len, META_NOALLOC);
+ applytextattributes(TSC_RAW);
printf("%s", (*buf == ' ') ? buf + 1 : buf);
break;
case '%':
+ applytextattributes(TSC_RAW);
putchar('%');
break;
case 'F':
@@ -354,16 +365,20 @@ watchlog2(int inout, WATCH_STRUCT_UTMP *u, char *fmt, int prnt, int fini)
atr = match_colour((const char**)&fmt, 1, 0);
if (*fmt == '}')
fmt++;
- if (!(atr & (TXT_ERROR | TXTNOFGCOLOUR))) {
- txtunset(TXT_ATTR_FG_COL_MASK);
- txtset(atr & TXT_ATTR_FG_ON_MASK);
- set_colour_attribute(atr, COL_SEQ_FG, TSC_RAW);
+ if (atr && atr != TXT_ERROR) {
+ tsetattrs(atr);
+ break;
}
- }
- break;
+ } /* fall-through */
case 'f':
- txtunset(TXT_ATTR_FG_ON_MASK);
- set_colour_attribute(TXTNOFGCOLOUR, COL_SEQ_FG, TSC_RAW);
+ tunsetattrs(TXTFGCOLOUR);
+ break;
+ case 'H':
+ if (*fmt == '{') {
+ fmt = parsehighlight(fmt + 1, '}', &atr);
+ if (atr && atr != TXT_ERROR)
+ treplaceattrs(atr);
+ }
break;
case 'K':
if (*fmt == '{') {
@@ -371,49 +386,43 @@ watchlog2(int inout, WATCH_STRUCT_UTMP *u, char *fmt, int prnt, int fini)
atr = match_colour((const char**)&fmt, 0, 0);
if (*fmt == '}')
fmt++;
- if (!(atr & (TXT_ERROR | TXTNOBGCOLOUR))) {
- txtunset(TXT_ATTR_BG_COL_MASK);
- txtset(atr & TXT_ATTR_BG_ON_MASK);
- set_colour_attribute(atr, COL_SEQ_BG, TSC_RAW);
+ if (atr && atr != TXT_ERROR) {
+ tsetattrs(atr);
+ break;
}
- }
- break;
+ } /* fall-through */
case 'k':
- txtunset(TXT_ATTR_BG_ON_MASK);
- set_colour_attribute(TXTNOBGCOLOUR, COL_SEQ_BG, TSC_RAW);
+ tunsetattrs(TXTBGCOLOUR);
break;
case 'S':
- txtset(TXTSTANDOUT);
- tsetcap(TCSTANDOUTBEG, TSC_RAW);
+ tsetattrs(TXTSTANDOUT);
break;
case 's':
- txtunset(TXTSTANDOUT);
- tsetcap(TCSTANDOUTEND, TSC_RAW|TSC_DIRTY);
+ tunsetattrs(TXTSTANDOUT);
break;
case 'B':
- txtset(TXTBOLDFACE);
- tsetcap(TCBOLDFACEBEG, TSC_RAW|TSC_DIRTY);
+ tsetattrs(TXTBOLDFACE);
break;
case 'b':
- txtunset(TXTBOLDFACE);
- tsetcap(TCALLATTRSOFF, TSC_RAW|TSC_DIRTY);
+ tunsetattrs(TXTBOLDFACE);
break;
case 'U':
- txtset(TXTUNDERLINE);
- tsetcap(TCUNDERLINEBEG, TSC_RAW);
+ tsetattrs(TXTUNDERLINE);
break;
case 'u':
- txtunset(TXTUNDERLINE);
- tsetcap(TCUNDERLINEEND, TSC_RAW|TSC_DIRTY);
+ tunsetattrs(TXTUNDERLINE);
break;
default:
+ applytextattributes(TSC_RAW);
putchar('%');
putchar(*fm2);
break;
}
}
- if (prnt)
+ if (prnt) {
+ applytextattributes(TSC_RAW);
putchar('\n');
+ }
return fmt;
}
@@ -421,20 +430,23 @@ watchlog2(int inout, WATCH_STRUCT_UTMP *u, char *fmt, int prnt, int fini)
/* See if the watch entry matches */
static int
-watchlog_match(char *teststr, char *actual, int len)
+watchlog_match(char *teststr, char *actual, size_t buflen)
{
int ret = 0;
Patprog pprog;
char *str = dupstring(teststr);
+ size_t len = strnlen(actual, buflen);
+ char *user = metafy(actual, len,
+ len == buflen ? META_HEAPDUP : META_USEHEAP);
tokenize(str);
if ((pprog = patcompile(str, PAT_STATIC, 0))) {
queue_signals();
- if (pattry(pprog, actual))
+ if (pattry(pprog, user))
ret = 1;
unqueue_signals();
- } else if (!strncmp(actual, teststr, len))
+ } else if (!strcmp(user, teststr))
ret = 1;
return ret;
}
@@ -454,10 +466,17 @@ watchlog(int inout, WATCH_STRUCT_UTMP *u, char **w, char *fmt)
(void)watchlog2(inout, u, fmt, 1, 0);
return;
}
- if (*w && !strcmp(*w, "notme") &&
- strncmp(u->ut_name, get_username(), sizeof(u->ut_name))) {
- (void)watchlog2(inout, u, fmt, 1, 0);
- return;
+ if (*w && !strcmp(*w, "notme")) {
+ int len = strnlen(u->ut_name, sizeof(u->ut_name));
+ char *username = metafy(u->ut_name, len,
+ (len == sizeof(u->ut_name) ?
+ META_HEAPDUP /* allow for nul terminator */ :
+ META_USEHEAP));
+ if (strcmp(username, get_username())) {
+ (void)watchlog2(inout, u, fmt, 1, 0);
+ return;
+ }
+ w++;
}
for (; *w; w++) {
bad = 0;
@@ -486,7 +505,7 @@ watchlog(int inout, WATCH_STRUCT_UTMP *u, char **w, char *fmt)
for (vv = ++v; *vv && *vv != '%'; vv++);
sav = *vv;
*vv = '\0';
- if (!watchlog_match(v, u->ut_host, strlen(v)))
+ if (!watchlog_match(v, u->ut_host, sizeof(u->ut_host)))
bad = 1;
*vv = sav;
v = vv;
@@ -565,7 +584,7 @@ readwtab(WATCH_STRUCT_UTMP **head, int initial_sz)
if (sz)
qsort((void *) *head, sz, sizeof(WATCH_STRUCT_UTMP),
- (int (*) _((const void *, const void *)))ucmp);
+ (int (*) (const void *, const void *))ucmp);
return sz;
}
diff --git a/Src/Modules/zftp.c b/Src/Modules/zftp.c
index e8e239e76..230ad86f6 100644
--- a/Src/Modules/zftp.c
+++ b/Src/Modules/zftp.c
@@ -127,7 +127,7 @@ typedef int (*readwrite_t)(int, char *, off_t, int);
struct zftpcmd {
const char *nam;
- int (*fun) _((char *, char **, int));
+ int (*fun) (char *, char **, int);
int min, max, flags;
};
@@ -397,7 +397,7 @@ zfalarm(int tmout)
signal(SIGALRM, zfhandler);
oalremain = alarm(tmout);
if (oalremain)
- oaltime = time(NULL);
+ oaltime = zmonotime(NULL);
/*
* We'll leave sigtrapped, sigfuncs and TRAPXXX as they are; since the
* shell's handler doesn't get the signal, they don't matter.
@@ -431,7 +431,7 @@ zfunalarm(void)
* I love the way alarm() uses unsigned int while time_t
* is probably something completely different.
*/
- unsigned int tdiff = time(NULL) - oaltime;
+ time_t tdiff = zmonotime(NULL) - oaltime;
alarm(oalremain < tdiff ? 1 : oalremain - tdiff);
} else
alarm(0);
@@ -944,9 +944,9 @@ zfopendata(char *name, union tcp_sockaddr *zdsockp, int *is_passivep)
return 1;
}
for (i = 0; i < 4; i++)
- iaddr[i] = STOUC(nums[i]);
- iport[0] = STOUC(nums[4]);
- iport[1] = STOUC(nums[5]);
+ iaddr[i] = (unsigned char) nums[i];
+ iport[0] = (unsigned char) nums[4];
+ iport[1] = (unsigned char) nums[5];
memcpy(&zdsockp->in.sin_addr, iaddr, sizeof(iaddr));
memcpy(&zdsockp->in.sin_port, iport, sizeof(iport));
@@ -2438,7 +2438,7 @@ zftp_type(char *name, char **args, int flags)
fflush(stdout);
return 0;
} else {
- nt = toupper(STOUC(*str));
+ nt = toupper((unsigned char) *str);
/*
* RFC959 specifies other types, but these are the only
* ones we know what to do with.
@@ -2472,7 +2472,7 @@ zftp_mode(char *name, char **args, UNUSED(int flags))
fflush(stdout);
return 0;
}
- nt = str[0] = toupper(STOUC(*str));
+ nt = str[0] = toupper((unsigned char) *str);
if (str[1] || (nt != 'S' && nt != 'B')) {
zwarnnam(name, "transfer mode %s not recognised", str);
return 1;
@@ -3075,7 +3075,7 @@ bin_zftp(char *name, char **args, UNUSED(Options ops), UNUSED(int func))
if ((prefs = getsparam_u("ZFTP_PREFS"))) {
zfprefs = 0;
for (ptr = prefs; *ptr; ptr++) {
- switch (toupper(STOUC(*ptr))) {
+ switch (toupper((unsigned char) *ptr)) {
case 'S':
/* sendport */
zfprefs |= ZFPF_SNDP;
@@ -3147,6 +3147,7 @@ zftp_cleanup(void)
lastmsg = NULL;
zfunsetparam("ZFTP_SESSION");
freelinklist(zfsessions, (FreeFunc) freesession);
+ zfsessions = NULL;
zfree(zfstatusp, sizeof(int)*zfsesscnt);
zfstatusp = NULL;
}
@@ -3172,7 +3173,7 @@ static struct features module_features = {
int
setup_(UNUSED(Module m))
{
- return (require_module("zsh/net/tcp", NULL, 0) == 1);
+ return 0;
}
/**/
diff --git a/Src/Modules/zprof.c b/Src/Modules/zprof.c
index 56cdab888..f5a50effc 100644
--- a/Src/Modules/zprof.c
+++ b/Src/Modules/zprof.c
@@ -163,9 +163,9 @@ bin_zprof(UNUSED(char *nam), UNUSED(char **args), Options ops, UNUSED(int func))
*ap = NULL;
qsort(fs, ncalls, sizeof(f),
- (int (*) _((const void *, const void *))) cmpsfuncs);
+ (int (*) (const void *, const void *)) cmpsfuncs);
qsort(as, narcs, sizeof(a),
- (int (*) _((const void *, const void *))) cmpparcs);
+ (int (*) (const void *, const void *)) cmpparcs);
printf("num calls time self name\n-----------------------------------------------------------------------------------\n");
for (fp = fs, i = 1; *fp; fp++, i++) {
@@ -179,7 +179,7 @@ bin_zprof(UNUSED(char *nam), UNUSED(char **args), Options ops, UNUSED(int func))
(*fp)->name);
}
qsort(fs, ncalls, sizeof(f),
- (int (*) _((const void *, const void *))) cmptfuncs);
+ (int (*) (const void *, const void *)) cmptfuncs);
for (fp = fs; *fp; fp++) {
printf("\n-----------------------------------------------------------------------------------\n\n");
@@ -239,8 +239,7 @@ zprof_wrapper(Eprog prog, FuncWrap w, char *name)
struct sfunc sf, *sp;
Pfunc f = NULL;
Parc a = NULL;
- struct timeval tv;
- struct timezone dummy;
+ struct timespec ts;
double prev = 0, now;
char *name_for_lookups;
@@ -278,19 +277,19 @@ zprof_wrapper(Eprog prog, FuncWrap w, char *name)
stack = &sf;
f->calls++;
- tv.tv_sec = tv.tv_usec = 0;
- gettimeofday(&tv, &dummy);
- sf.beg = prev = ((((double) tv.tv_sec) * 1000.0) +
- (((double) tv.tv_usec) / 1000.0));
+ ts.tv_sec = ts.tv_nsec = 0;
+ zgettime_monotonic_if_available(&ts);
+ sf.beg = prev = ((((double) ts.tv_sec) * 1000.0) +
+ (((double) ts.tv_nsec) / 1000000.0));
}
runshfunc(prog, w, name);
if (active) {
if (zprof_module && !(zprof_module->node.flags & MOD_UNLOAD)) {
- tv.tv_sec = tv.tv_usec = 0;
- gettimeofday(&tv, &dummy);
+ ts.tv_sec = ts.tv_nsec = 0;
+ zgettime_monotonic_if_available(&ts);
- now = ((((double) tv.tv_sec) * 1000.0) +
- (((double) tv.tv_usec) / 1000.0));
+ now = ((((double) ts.tv_sec) * 1000.0) +
+ (((double) ts.tv_nsec) / 1000000.0));
f->self += now - sf.beg;
for (sp = sf.prev; sp && sp->p != f; sp = sp->prev);
if (!sp)
diff --git a/Src/Modules/zpty.c b/Src/Modules/zpty.c
index dfd2a2a7a..c2656698c 100644
--- a/Src/Modules/zpty.c
+++ b/Src/Modules/zpty.c
@@ -638,7 +638,7 @@ ptyread(char *nam, Ptycmd cmd, char **args, int noblock, int mustmatch)
readchar = cmd->read;
cmd->read = -1;
} else
- readchar = STOUC(buf[used]);
+ readchar = (unsigned char) buf[used];
if (imeta(readchar)) {
buf[used++] = Meta;
buf[used++] = (char) (readchar ^ 32);
diff --git a/Src/Modules/zutil.c b/Src/Modules/zutil.c
index 2f17c03f1..676fe1872 100644
--- a/Src/Modules/zutil.c
+++ b/Src/Modules/zutil.c
@@ -462,6 +462,28 @@ lookupstyle(char *ctxt, char *style)
}
static int
+testforstyle(char *ctxt, char *style)
+{
+ Style s;
+ Stypat p;
+ int found = 0;
+
+ s = (Style)zstyletab->getnode2(zstyletab, style);
+ if (s) {
+ MatchData match;
+ savematch(&match);
+ for (p = s->pats; p; p = p->next)
+ if (pattry(p->prog, ctxt)) {
+ found = 1;
+ break;
+ }
+ restorematch(&match);
+ }
+
+ return !found; /* 0 == success */
+}
+
+static int
bin_zstyle(char *nam, char **args, UNUSED(Options ops), UNUSED(int func))
{
int min, max, n, add = 0, list = ZSLIST_NONE, eval = 0;
@@ -570,6 +592,7 @@ bin_zstyle(char *nam, char **args, UNUSED(Options ops), UNUSED(int func))
case 't': min = 2; max = -1; break;
case 'T': min = 2; max = -1; break;
case 'm': min = 3; max = 3; break;
+ case 'q': min = 2; max = 2; break;
case 'g': min = 1; max = 3; break;
default:
zwarnnam(nam, "invalid option: %s", args[0]);
@@ -723,6 +746,15 @@ bin_zstyle(char *nam, char **args, UNUSED(Options ops), UNUSED(int func))
return 1;
}
break;
+ case 'q':
+ {
+ int success;
+ queue_signals(); /* Protect PAT_STATIC */
+ success = testforstyle(args[1], args[2]);
+ unqueue_signals();
+ return success;
+ }
+ break;
case 'g':
{
int ret = 1;
@@ -795,11 +827,11 @@ static char *zformat_substring(char* instr, char **specs, char **outp,
if (idigit(*s)) {
for (min = 0; idigit(*s); s++)
- min = (min * 10) + (int) STOUC(*s) - '0';
+ min = (min * 10) + (int) (unsigned char) *s - '0';
}
/* Ternary expressions */
- testit = (STOUC(*s) == '(');
+ testit = ((unsigned char) *s == '(');
if (testit && s[1] == '-')
{
/* Allow %(-1... etc. */
@@ -808,25 +840,25 @@ static char *zformat_substring(char* instr, char **specs, char **outp,
}
if ((*s == '.' || testit) && idigit(s[1])) {
for (max = 0, s++; idigit(*s); s++)
- max = (max * 10) + (int) STOUC(*s) - '0';
+ max = (max * 10) + (int) (unsigned char) *s - '0';
} else if (*s == '.' || testit)
s++;
- if (testit && STOUC(*s)) {
+ if (testit && (unsigned char) *s) {
int actval, testval, endcharl;
/* Only one number is useful for ternary expressions. */
testval = (min >= 0) ? min : (max >= 0) ? max : 0;
- if (specs[STOUC(*s)] && *specs[STOUC(*s)]) {
+ if (specs[(unsigned char) *s] && *specs[(unsigned char) *s]) {
if (presence) {
if (testval)
#ifdef MULTIBYTE_SUPPORT
if (isset(MULTIBYTE))
- actval = MB_METASTRWIDTH(specs[STOUC(*s)]);
+ actval = MB_METASTRWIDTH(specs[(unsigned char) *s]);
else
#endif
- actval = strlen(specs[STOUC(*s)]);
+ actval = strlen(specs[(unsigned char) *s]);
else
actval = 1;
actval = right ? (testval < actval) : (testval >= actval);
@@ -834,7 +866,7 @@ static char *zformat_substring(char* instr, char **specs, char **outp,
if (right) /* put the sign back */
testval *= -1;
/* zero means values are equal, i.e. true */
- actval = (int)mathevali(specs[STOUC(*s)]) - testval;
+ actval = (int) mathevali(specs[(unsigned char) *s]) - testval;
}
} else
actval = presence ? !right : testval;
@@ -855,7 +887,7 @@ static char *zformat_substring(char* instr, char **specs, char **outp,
return NULL;
} else if (skip) {
continue;
- } else if ((spec = specs[STOUC(*s)])) {
+ } else if ((spec = specs[(unsigned char) *s])) {
int len;
if ((len = strlen(spec)) > max && max >= 0)
@@ -950,7 +982,7 @@ bin_zformat(char *nam, char **args, UNUSED(Options ops), UNUSED(int func))
zwarnnam(nam, "invalid argument: %s", *ap);
return 1;
}
- specs[STOUC(ap[0][0])] = ap[0] + 2;
+ specs[(unsigned char) ap[0][0]] = ap[0] + 2;
}
out = (char *) zhalloc(olen = 128);
@@ -965,7 +997,7 @@ bin_zformat(char *nam, char **args, UNUSED(Options ops), UNUSED(int func))
case 'a':
{
char **ap, *cp;
- int nbc = 0, colon = 0, pre = 0, suf = 0;
+ int nbc = 0, pre = 0, suf = 0;
#ifdef MULTIBYTE_SUPPORT
int prechars = 0;
#endif /* MULTIBYTE_SUPPORT */
@@ -980,7 +1012,6 @@ bin_zformat(char *nam, char **args, UNUSED(Options ops), UNUSED(int func))
int dchars = 0;
#endif /* MULTIBYTE_SUPPORT */
- colon++;
if ((d = cp - *ap - nbc) > pre)
pre = d;
#ifdef MULTIBYTE_SUPPORT
@@ -1378,11 +1409,11 @@ rmatch(RParseResult *sm, char *subj, char *var1, char *var2, int comp)
"zregexparse-guard"), !lastval))) {
LinkNode aln;
char **mend;
- int len;
+ int len = 0;
queue_signals();
- mend = getaparam("mend");
- len = atoi(mend[0]);
+ if ((mend = getaparam("mend")))
+ len = atoi(mend[0]);
unqueue_signals();
for (i = len; i; i--)
@@ -1497,12 +1528,14 @@ struct zoptdesc {
Zoptval vals, last;
};
-#define ZOF_ARG 1
-#define ZOF_OPT 2
-#define ZOF_MULT 4
-#define ZOF_SAME 8
-#define ZOF_MAP 16
-#define ZOF_CYC 32
+#define ZOF_ARG 1
+#define ZOF_OPT 2
+#define ZOF_MULT 4
+#define ZOF_SAME 8
+#define ZOF_MAP 16
+#define ZOF_CYC 32
+#define ZOF_GNUS 64
+#define ZOF_GNUL 128
struct zoptarr {
Zoptarr next;
@@ -1537,9 +1570,29 @@ static Zoptdesc
lookup_opt(char *str)
{
Zoptdesc p;
-
for (p = opt_descs; p; p = p->next) {
- if ((p->flags & ZOF_ARG) ? strpfx(p->name, str) : !strcmp(p->name, str))
+ /*
+ * Option takes argument, with GNU-style handling of =. This should only
+ * be set for long options, though we don't care about that here. Unlike
+ * the default behaviour, matching is unambiguous
+ */
+ if (p->flags & ZOF_GNUL) {
+ if (!strcmp(p->name, str) || /* Inefficient, whatever */
+ (strpfx(p->name, str) && str[strlen(p->name)] == '='))
+ return p;
+ /*
+ * Option takes argument, default 'cuddled' style. This is weird with
+ * long options because we naively accept whatever option has a prefix
+ * match for the given parameter. Thus if you have specs `-foo:` and
+ * `-foobar:` (or even `-foobar` with no optarg), without the GNU
+ * setting, the behaviour depends on the order in which they were
+ * defined. It is documented to work this way though
+ */
+ } else if (p->flags & ZOF_ARG) {
+ if (strpfx(p->name, str))
+ return p;
+ /* Option takes no argument */
+ } else if (!strcmp(p->name, str))
return p;
}
return NULL;
@@ -1609,12 +1662,15 @@ add_opt_val(Zoptdesc d, char *arg)
v->str = NULL;
if (d->arr)
d->arr->num += (arg ? 2 : 1);
- } else if (arg) {
- char *s = (char *) zhalloc(strlen(d->name) + strlen(arg) + 2);
+ } else if (arg || d->flags & ZOF_GNUL) {
+ /* 3 here is '-' + '=' + NUL */
+ char *s = (char *) zhalloc(strlen(d->name) + strlen(arg ? arg : "") + 3);
*s = '-';
strcpy(s + 1, d->name);
- strcat(s, arg);
+ if (d->flags & ZOF_GNUL)
+ strcat(s, "=");
+ strcat(s, arg ? arg : "");
v->str = s;
if (d->arr)
d->arr->num += 1;
@@ -1682,7 +1738,8 @@ static int
bin_zparseopts(char *nam, char **args, UNUSED(Options ops), UNUSED(int func))
{
char *o, *p, *n, **pp, **aval, **ap, *assoc = NULL, **cp, **np;
- int del = 0, flags = 0, extract = 0, fail = 0, keep = 0;
+ char *paramsname = NULL, **params;
+ int del = 0, flags = 0, extract = 0, fail = 0, gnu = 0, keep = 0;
Zoptdesc sopts[256], d;
Zoptarr a, defarr = NULL;
Zoptval v;
@@ -1727,6 +1784,14 @@ bin_zparseopts(char *nam, char **args, UNUSED(Options ops), UNUSED(int func))
}
fail = 1;
break;
+ case 'G':
+ if (o[2]) {
+ args--;
+ o = NULL;
+ break;
+ }
+ gnu = 1;
+ break;
case 'K':
if (o[2]) {
args--;
@@ -1777,6 +1842,20 @@ bin_zparseopts(char *nam, char **args, UNUSED(Options ops), UNUSED(int func))
return 1;
}
break;
+ case 'v':
+ if (paramsname) {
+ zwarnnam(nam, "argv array given more than once");
+ return 1;
+ }
+ if (o[2])
+ paramsname = o + 2;
+ else if (*args)
+ paramsname = *args++;
+ else {
+ zwarnnam(nam, "missing array name");
+ return 1;
+ }
+ break;
default:
/* Anything else is an option description */
args--;
@@ -1818,6 +1897,9 @@ bin_zparseopts(char *nam, char **args, UNUSED(Options ops), UNUSED(int func))
if (*p == ':') {
f |= ZOF_ARG;
*p = '\0';
+ if (gnu) {
+ f |= o[1] ? ZOF_GNUL : ZOF_GNUS;
+ }
if (*++p == ':') {
p++;
f |= ZOF_OPT;
@@ -1864,15 +1946,17 @@ bin_zparseopts(char *nam, char **args, UNUSED(Options ops), UNUSED(int func))
d->vals = d->last = NULL;
opt_descs = d;
if (!o[1])
- sopts[STOUC(*o)] = d;
+ sopts[(unsigned char) *o] = d;
if ((flags & ZOF_MAP) && !map_opt_desc(d)) {
zwarnnam(nam, "cyclic option mapping: %s", args[-1]);
return 1;
}
}
- np = cp = pp = ((extract && del) ? arrdup(pparams) : pparams);
+ params = getaparam((paramsname = paramsname ? paramsname : "argv"));
+ np = cp = pp = ((extract && del) ? arrdup(params) : params);
for (; (o = *pp); pp++) {
- if (*o != '-') {
+ /* Not an option. With GNU style, this includes '-' */
+ if (*o != '-' || (gnu && !o[1])) {
if (extract) {
if (del)
*cp++ = o;
@@ -1880,6 +1964,7 @@ bin_zparseopts(char *nam, char **args, UNUSED(Options ops), UNUSED(int func))
} else
break;
}
+ /* '--' or (with non-GNU style, see above) '-', end parsing */
if (!o[1] || (o[1] == '-' && !o[2])) {
if (del && extract)
*cp++ = o;
@@ -1887,8 +1972,9 @@ bin_zparseopts(char *nam, char **args, UNUSED(Options ops), UNUSED(int func))
break;
}
if (!(d = lookup_opt(o + 1))) {
+ /* No match for whole param, try each character as a short option */
while (*++o) {
- if (!(d = sopts[STOUC(*o)])) {
+ if (!(d = sopts[(unsigned char) *o])) {
if (fail) {
if (*o != '-')
zwarnnam(nam, "bad option: -%c", *o);
@@ -1900,19 +1986,27 @@ bin_zparseopts(char *nam, char **args, UNUSED(Options ops), UNUSED(int func))
break;
}
if (d->flags & ZOF_ARG) {
+ /* Optarg in same parameter */
if (o[1]) {
add_opt_val(d, o + 1);
break;
+ /*
+ * Mandatory optarg or (if not GNU style) optional optarg in
+ * next parameter
+ */
} else if (!(d->flags & ZOF_OPT) ||
- (pp[1] && pp[1][0] != '-')) {
+ (!(d->flags & (ZOF_GNUL | ZOF_GNUS)) &&
+ pp[1] && pp[1][0] != '-')) {
if (!pp[1]) {
zwarnnam(nam, "missing argument for option: -%s",
d->name);
return 1;
}
add_opt_val(d, *++pp);
+ /* Missing optional optarg */
} else
add_opt_val(d, NULL);
+ /* No optarg required */
} else
add_opt_val(d, NULL);
}
@@ -1925,21 +2019,37 @@ bin_zparseopts(char *nam, char **args, UNUSED(Options ops), UNUSED(int func))
break;
}
} else {
+ /* Whole param matches a defined option */
if (d->flags & ZOF_ARG) {
char *e = o + strlen(d->name) + 1;
- if (*e)
+ /* GNU style allows an empty optarg in the same parameter */
+ if ((d->flags & ZOF_GNUL) && *e == '=') {
+ add_opt_val(d, ++e);
+ /*
+ * Non-empty optarg in same parameter. lookup_opt() test ensures
+ * that this won't be a GNU-style long option, where this would
+ * be invalid
+ */
+ } else if (*e) {
add_opt_val(d, e);
- else if (!(d->flags & ZOF_OPT) ||
- (pp[1] && pp[1][0] != '-')) {
+ /*
+ * Mandatory optarg or (if not GNU style) optional optarg in
+ * next parameter
+ */
+ } else if (!(d->flags & ZOF_OPT) ||
+ (!(d->flags & (ZOF_GNUL | ZOF_GNUS)) &&
+ pp[1] && pp[1][0] != '-')) {
if (!pp[1]) {
zwarnnam(nam, "missing argument for option: -%s",
d->name);
return 1;
}
add_opt_val(d, *++pp);
+ /* Missing optional optarg */
} else
add_opt_val(d, NULL);
+ /* No optarg required */
} else
add_opt_val(d, NULL);
}
@@ -2010,12 +2120,9 @@ bin_zparseopts(char *nam, char **args, UNUSED(Options ops), UNUSED(int func))
if (del) {
if (extract) {
*cp = NULL;
- freearray(pparams);
- pparams = zarrdup(np);
+ setaparam(paramsname, zarrdup(np));
} else {
- pp = zarrdup(pp);
- freearray(pparams);
- pparams = pp;
+ setaparam(paramsname, zarrdup(pp));
}
}
return 0;